skylib_fluffos_v3/
skylib_fluffos_v3/bin/
skylib_fluffos_v3/bin/db/
skylib_fluffos_v3/fluffos-2.9-ds2.04/
skylib_fluffos_v3/fluffos-2.9-ds2.04/ChangeLog.old/
skylib_fluffos_v3/fluffos-2.9-ds2.04/Win32/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/simuls/
skylib_fluffos_v3/fluffos-2.9-ds2.04/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/clone/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/command/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/data/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/etc/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/master/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/log/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/compiler/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/efuns/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/operators/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/u/
skylib_fluffos_v3/fluffos-2.9-ds2.04/tmp/
skylib_fluffos_v3/fluffos-2.9-ds2.04/windows/
skylib_fluffos_v3/mudlib/
skylib_fluffos_v3/mudlib/cmds/
skylib_fluffos_v3/mudlib/cmds/admin/
skylib_fluffos_v3/mudlib/cmds/guild-race/
skylib_fluffos_v3/mudlib/cmds/living/broken/
skylib_fluffos_v3/mudlib/cmds/player/group_cmds/
skylib_fluffos_v3/mudlib/cmds/playtester/
skylib_fluffos_v3/mudlib/d/admin/
skylib_fluffos_v3/mudlib/d/admin/room/
skylib_fluffos_v3/mudlib/d/admin/room/we_care/
skylib_fluffos_v3/mudlib/d/admin/save/
skylib_fluffos_v3/mudlib/d/admin/text/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/buildings/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/map/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/roads/
skylib_fluffos_v3/mudlib/d/learning/chars/
skylib_fluffos_v3/mudlib/d/learning/functions/
skylib_fluffos_v3/mudlib/d/learning/handlers/
skylib_fluffos_v3/mudlib/d/learning/help_topics/
skylib_fluffos_v3/mudlib/d/learning/help_topics/npcs/
skylib_fluffos_v3/mudlib/d/learning/help_topics/objects/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/crowd/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/situations/
skylib_fluffos_v3/mudlib/d/learning/save/
skylib_fluffos_v3/mudlib/d/learning/school/
skylib_fluffos_v3/mudlib/d/learning/school/add_sc/
skylib_fluffos_v3/mudlib/d/learning/school/characters/
skylib_fluffos_v3/mudlib/d/learning/school/general/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/basic_commands/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/edtutor/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_fluffos_v3/mudlib/d/learning/school/items/
skylib_fluffos_v3/mudlib/d/learning/school/npc_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/room_basic/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/situations/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_fluffos_v3/mudlib/d/learning/text/
skylib_fluffos_v3/mudlib/d/liaison/
skylib_fluffos_v3/mudlib/d/mudlib/
skylib_fluffos_v3/mudlib/d/mudlib/changes/
skylib_fluffos_v3/mudlib/d/playtesters/
skylib_fluffos_v3/mudlib/d/playtesters/effects/
skylib_fluffos_v3/mudlib/d/playtesters/handlers/
skylib_fluffos_v3/mudlib/d/playtesters/items/
skylib_fluffos_v3/mudlib/d/sage/
skylib_fluffos_v3/mudlib/doc/
skylib_fluffos_v3/mudlib/doc/creator/
skylib_fluffos_v3/mudlib/doc/driver/
skylib_fluffos_v3/mudlib/doc/driver/efuns/arrays/
skylib_fluffos_v3/mudlib/doc/driver/efuns/buffers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/calls/
skylib_fluffos_v3/mudlib/doc/driver/efuns/compile/
skylib_fluffos_v3/mudlib/doc/driver/efuns/filesystem/
skylib_fluffos_v3/mudlib/doc/driver/efuns/floats/
skylib_fluffos_v3/mudlib/doc/driver/efuns/functions/
skylib_fluffos_v3/mudlib/doc/driver/efuns/general/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mappings/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mixed/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mudlib/
skylib_fluffos_v3/mudlib/doc/driver/efuns/numbers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/parsing/
skylib_fluffos_v3/mudlib/doc/login/
skylib_fluffos_v3/mudlib/doc/lpc/basic_manual/
skylib_fluffos_v3/mudlib/doc/lpc/intermediate/
skylib_fluffos_v3/mudlib/doc/new/add_command/
skylib_fluffos_v3/mudlib/doc/new/events/
skylib_fluffos_v3/mudlib/doc/new/handlers/
skylib_fluffos_v3/mudlib/doc/new/living/race/
skylib_fluffos_v3/mudlib/doc/new/living/spells/
skylib_fluffos_v3/mudlib/doc/new/object/
skylib_fluffos_v3/mudlib/doc/new/player/
skylib_fluffos_v3/mudlib/doc/new/room/guild/
skylib_fluffos_v3/mudlib/doc/new/room/outside/
skylib_fluffos_v3/mudlib/doc/new/room/storeroom/
skylib_fluffos_v3/mudlib/doc/object/
skylib_fluffos_v3/mudlib/doc/playtesters/
skylib_fluffos_v3/mudlib/doc/policy/
skylib_fluffos_v3/mudlib/doc/weapons/
skylib_fluffos_v3/mudlib/global/
skylib_fluffos_v3/mudlib/global/creator/
skylib_fluffos_v3/mudlib/handlers/
skylib_fluffos_v3/mudlib/include/casino/
skylib_fluffos_v3/mudlib/include/cmds/
skylib_fluffos_v3/mudlib/include/effects/
skylib_fluffos_v3/mudlib/include/npc/
skylib_fluffos_v3/mudlib/include/room/
skylib_fluffos_v3/mudlib/include/shops/
skylib_fluffos_v3/mudlib/net/daemon/
skylib_fluffos_v3/mudlib/net/daemon/chars/
skylib_fluffos_v3/mudlib/net/inherit/
skylib_fluffos_v3/mudlib/net/obj/
skylib_fluffos_v3/mudlib/net/obj/BACKUPS/
skylib_fluffos_v3/mudlib/obj/amulets/
skylib_fluffos_v3/mudlib/obj/armours/plate/
skylib_fluffos_v3/mudlib/obj/b_day/
skylib_fluffos_v3/mudlib/obj/clothes/transport/horse/
skylib_fluffos_v3/mudlib/obj/faith/symbols/
skylib_fluffos_v3/mudlib/obj/fungi/
skylib_fluffos_v3/mudlib/obj/gatherables/
skylib_fluffos_v3/mudlib/obj/instruments/
skylib_fluffos_v3/mudlib/obj/media/
skylib_fluffos_v3/mudlib/obj/misc/player_shop/
skylib_fluffos_v3/mudlib/obj/monster/godmother/
skylib_fluffos_v3/mudlib/obj/monster/transport/
skylib_fluffos_v3/mudlib/obj/rings/
skylib_fluffos_v3/mudlib/obj/scabbards/
skylib_fluffos_v3/mudlib/obj/spells/
skylib_fluffos_v3/mudlib/obj/stationery/
skylib_fluffos_v3/mudlib/obj/stationery/envelopes/
skylib_fluffos_v3/mudlib/obj/toys/
skylib_fluffos_v3/mudlib/obj/vessels/
skylib_fluffos_v3/mudlib/obj/weapons/axes/
skylib_fluffos_v3/mudlib/obj/weapons/chains/
skylib_fluffos_v3/mudlib/obj/weapons/maces/BACKUPS/
skylib_fluffos_v3/mudlib/save/autodoc/
skylib_fluffos_v3/mudlib/save/book_handler/
skylib_fluffos_v3/mudlib/save/books/history/calarien/
skylib_fluffos_v3/mudlib/save/mail/
skylib_fluffos_v3/mudlib/save/new_soul/data/
skylib_fluffos_v3/mudlib/save/parcels/
skylib_fluffos_v3/mudlib/save/playerinfo/
skylib_fluffos_v3/mudlib/save/players/d/
skylib_fluffos_v3/mudlib/save/players/s/
skylib_fluffos_v3/mudlib/save/random_names/
skylib_fluffos_v3/mudlib/save/random_names/data/
skylib_fluffos_v3/mudlib/save/terrains/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_desert/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_grassy_field/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_mountain/
skylib_fluffos_v3/mudlib/save/todo_lists/
skylib_fluffos_v3/mudlib/secure/
skylib_fluffos_v3/mudlib/secure/cmds/admin/
skylib_fluffos_v3/mudlib/secure/cmds/lord/
skylib_fluffos_v3/mudlib/secure/config/
skylib_fluffos_v3/mudlib/secure/handlers/autodoc/
skylib_fluffos_v3/mudlib/secure/handlers/intermud/
skylib_fluffos_v3/mudlib/secure/include/global/
skylib_fluffos_v3/mudlib/secure/save/
skylib_fluffos_v3/mudlib/secure/save/handlers/
skylib_fluffos_v3/mudlib/secure/std/
skylib_fluffos_v3/mudlib/secure/std/classes/
skylib_fluffos_v3/mudlib/secure/std/modules/
skylib_fluffos_v3/mudlib/std/creator/
skylib_fluffos_v3/mudlib/std/dom/
skylib_fluffos_v3/mudlib/std/effects/
skylib_fluffos_v3/mudlib/std/effects/external/
skylib_fluffos_v3/mudlib/std/effects/fighting/
skylib_fluffos_v3/mudlib/std/effects/magic/
skylib_fluffos_v3/mudlib/std/effects/magic/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/other/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/priest/
skylib_fluffos_v3/mudlib/std/effects/room/
skylib_fluffos_v3/mudlib/std/environ/
skylib_fluffos_v3/mudlib/std/guilds/
skylib_fluffos_v3/mudlib/std/guilds/old/
skylib_fluffos_v3/mudlib/std/languages/
skylib_fluffos_v3/mudlib/std/liquids/
skylib_fluffos_v3/mudlib/std/npc/
skylib_fluffos_v3/mudlib/std/npc/goals/
skylib_fluffos_v3/mudlib/std/npc/goals/basic/
skylib_fluffos_v3/mudlib/std/npc/goals/misc/
skylib_fluffos_v3/mudlib/std/npc/plans/
skylib_fluffos_v3/mudlib/std/npc/plans/basic/
skylib_fluffos_v3/mudlib/std/npc/types/
skylib_fluffos_v3/mudlib/std/npc/types/helper/
skylib_fluffos_v3/mudlib/std/npcs/
skylib_fluffos_v3/mudlib/std/outsides/
skylib_fluffos_v3/mudlib/std/races/shadows/
skylib_fluffos_v3/mudlib/std/room/basic/BACKUPS/
skylib_fluffos_v3/mudlib/std/room/basic/topography/
skylib_fluffos_v3/mudlib/std/room/controller/
skylib_fluffos_v3/mudlib/std/room/inherit/topography/
skylib_fluffos_v3/mudlib/std/room/topography/area/
skylib_fluffos_v3/mudlib/std/room/topography/iroom/
skylib_fluffos_v3/mudlib/std/room/topography/milestone/
skylib_fluffos_v3/mudlib/std/shadows/curses/
skylib_fluffos_v3/mudlib/std/shadows/disease/
skylib_fluffos_v3/mudlib/std/shadows/fighting/
skylib_fluffos_v3/mudlib/std/shadows/healing/
skylib_fluffos_v3/mudlib/std/shadows/magic/
skylib_fluffos_v3/mudlib/std/shadows/poison/
skylib_fluffos_v3/mudlib/std/shadows/room/
skylib_fluffos_v3/mudlib/std/shops/controllers/
skylib_fluffos_v3/mudlib/std/shops/objs/
skylib_fluffos_v3/mudlib/std/shops/player_shop/
skylib_fluffos_v3/mudlib/std/socket/
skylib_fluffos_v3/mudlib/std/soul/d/
skylib_fluffos_v3/mudlib/std/soul/e/
skylib_fluffos_v3/mudlib/std/soul/i/
skylib_fluffos_v3/mudlib/std/soul/j/
skylib_fluffos_v3/mudlib/std/soul/k/
skylib_fluffos_v3/mudlib/std/soul/l/
skylib_fluffos_v3/mudlib/std/soul/n/
skylib_fluffos_v3/mudlib/std/soul/o/
skylib_fluffos_v3/mudlib/std/soul/q/
skylib_fluffos_v3/mudlib/std/soul/r/
skylib_fluffos_v3/mudlib/std/soul/u/
skylib_fluffos_v3/mudlib/std/soul/v/
skylib_fluffos_v3/mudlib/std/soul/y/
skylib_fluffos_v3/mudlib/std/soul/z/
skylib_fluffos_v3/mudlib/std/stationery/
skylib_fluffos_v3/mudlib/w/
skylib_fluffos_v3/mudlib/w/default/
skylib_fluffos_v3/mudlib/w/default/armour/
skylib_fluffos_v3/mudlib/w/default/clothes/
skylib_fluffos_v3/mudlib/w/default/item/
skylib_fluffos_v3/mudlib/w/default/npc/
skylib_fluffos_v3/mudlib/w/default/room/
skylib_fluffos_v3/mudlib/w/default/weapon/
skylib_fluffos_v3/mudlib/www/
skylib_fluffos_v3/mudlib/www/java/
skylib_fluffos_v3/mudlib/www/secure/
skylib_fluffos_v3/mudlib/www/secure/lpc/advanced/
skylib_fluffos_v3/mudlib/www/secure/lpc/intermediate/
skylib_fluffos_v3/win32/
/**
 * This room will handle craft shop like things, where you sell stuff to
 * the store and it will sell it onto other players after taking
 * a margin.  This can be used for potions, pottery, swords, anything!
 * This has a few small hooks in here to handle categories in the
 * sell process.  Most of the category handling code is dealt with in
 * the category based craft shop.
 * @author Pinkfish
 * @started Thu Feb  5 15:39:57 CST 1998
 */
#include <money.h>
#include <shops/craft_shop.h>

private nosave object _controller;
private nosave string _save_dir_name;
private nosave function _allowed_to_sell;
private nosave function _open_function;
private nosave function _when_sold_func;
private nosave int _max_sell_per_person;
private nosave int _cut;
private nosave int _minimum_cost;
private nosave function _extra_sell_check;
private nosave int _always_ask_price;

int do_sell(object *obs);
void set_controller(string name);
int do_buy(object *obs);
int do_delete(string id);
int do_browse(object *obs);
int do_change(object *obs, string str);
int do_change_name(object *obs, string str);
void set_allowed_to_sell(function f);
void set_minimum_cost(int i);
protected void sell_name(string name, object *sellable);
protected void sell_choice(string choice, object *sellable, string *names);
protected void determine_cost(string cost, string name, object *sellable);
protected void confirm_sale(string answer, int value, string name,
                            object *sellable, string category);
protected void confirm_sell_name_choice(string answer, string name,
                                        object *sellable);
protected void complete_sale(int value, string name, object *sellable, string category);
protected void do_confirm_sale(int value, string name, object *sellable);
void set_cut(int value);
int query_cut();
int check_open(object player);
mixed query_property(string name);
string the_short();

void create() {
   /* Always return true. */
   if (!_allowed_to_sell) {
      set_allowed_to_sell((: 1 :));
   }
   /* The default cut is 10% */
   if (!_cut) {
      set_cut(10);
   }
   /* Make it really 12 am pence. */
   if (!_minimum_cost) {
      set_minimum_cost(12 * 4);
   }
   if (!_controller && _save_dir_name) {
      set_controller("/std/shops/controllers/craft_shop_controller");
      _controller->load_it();
   }
} /* create() */

/**
 * This attempts to figure out how much the person is telling us the
 * item costs.
 * @param str the string to get the value of
 */
int value_from_string(string str, string place) {
   return MONEY_HAND->value_from_string(str, place);
} /* value_from_string() */

/**
 * This method handles the saving of the file to the disk.
 * @param type the type of file to save
 * @param value the value to save to it
 * @param fname the file name is optional and only used for data files
 */
protected void do_save_file(int type, string value, string fname) {
   string tmp;

   switch (type) {
      case CRAFT_SHOP_MAIN_SAVE_FILE :
         tmp = save_variable(value);
         if (file_size(_save_dir_name + ".o") != -1) {
            unguarded( (: rename(_save_dir_name + ".o",
                                 _save_dir_name + ".o.bak") :));
         }
         if (unguarded( (: write_file(_save_dir_name + ".o", $(tmp), 1) :) )) {
            unguarded( (: rm(_save_dir_name + ".o.bak") :));
         } else {
            unguarded( (: rename(_save_dir_name + ".o.bak",
                                 _save_dir_name + ".o") :));
         }
         break;
      case CRAFT_SHOP_DATA_SAVE_FILE :
         tmp = save_variable(value);
         unguarded( (: write_file(_save_dir_name + "/" + $(fname),
                                  $(tmp), 1) :));
         break;
      case CRAFT_SHOP_REMOVE_DATA_SAVE_FILE :
         tmp = save_variable(value);
         unguarded( (: rm(_save_dir_name + "/" + $(fname)) :));
         break;
      default :
         printf("Unknown file type to write in %O, of %O\n", __FILE__, type);
         break;
   }
} /* do_save_file() */

/**
 * This method handles the reading of the file to the disk.
 * @param type the type of file to read
 * @param fname the file name is optional and only used for data files
 * @return the value read
 */
protected string do_read_file(int type, string fname) {
   string tmp;

   switch (type) {
      case CRAFT_SHOP_MAIN_SAVE_FILE :
         tmp = unguarded( (: read_file(_save_dir_name + ".o") :) );
         if (tmp) {
            return restore_variable(tmp);
         }
         return 0;
      case CRAFT_SHOP_DATA_SAVE_FILE :
         tmp = unguarded( (: read_file(_save_dir_name + "/" + $(fname)) :) );
         if (tmp) {
            return restore_variable(tmp);
         }
         return 0;
      default :
         printf("Unknown file type to read in %O, of %O\n", __FILE__, type);
         break;
   }
} /* do_read_file() */

/**
 * This method sets the controller object for this shop.
 * @param controller the controller name as a string
 * @see query_controller()
 */
void set_controller(string name) {
   if (_controller) {
      _controller->dest_me();
   }
   _controller = clone_object(name);
   _controller->set_save_function((: do_save_file($1, $2, $3) :));
   _controller->set_load_function((: do_read_file($1, $2) :) );
} /* set_controller() */

/**
 * This method returns the current value of the controller.
 * @return the current controller
 * @see set_controller()
 */
object query_controller() {
   return _controller;
} /* query_controller() */

/**
 * This method sets the save file associated with the controller.  The save
 * file should actually be a directory, the name of the directory plus a
 * .o will be used for the save files.  The directory must exist or weird
 * errors will occur.
 * This should always be set in the setup function of the room.
 * @param fname the save file
 * @see set_controller()
 * @see query_save_dir()
 */
void set_save_dir(string fname) {
   _save_dir_name = fname;
} /* set_save_dir() */

/**
 * This method returns the file name of the save file.
 * @return the file name of the save file
 * @see set_save_dir()
 */
string query_save_dir() {
   return _save_dir_name;
} /* query_save_dir() */

/**
 * This method sets the function to use to check to see if the object
 * is able to be sold here or not.  The function will be called with
 * one parameter, that is the object being check for buyability.
 * @param func the function to check objects with
 * @see query_allowed_to_sell()
 */
void set_allowed_to_sell(function func) {
   _allowed_to_sell = func;
} /* set_allowed_to_sell() */

/**
 * This method returns the function used to check to see if an object
 * is able to be sold here or not.
 * @return func the function to use for checking
 * @see set_allowed_to_sell()
 */
function query_allowed_to_sell() {
   return _allowed_to_sell;
} /* query_allowed_to_sell() */

/**
 * This method sets the maxium allowed number of objects to be sold
 * by each person into the shop.
 * @param num the maximum number to be sold
 * @see query_max_sell_per_person()
 */
void set_max_sell_per_person(int num) {
   _max_sell_per_person = num;
} /* set_max_sell_per_person() */

/**
 * This methods returns the maximum number of allowed objects to be
 * sold by each person into the shop.
 * @return the maximum number to be sold
 * @see set_max_sell_per_person()
 */
int query_max_sell_per_person() {
   return _max_sell_per_person;
} /* query_max_sell_per_person() */

/**
 * This method sets the flag which makes the shop always ask for a price
 * when it attempts to save something.
 * @param ask_price the flag
 */
void set_always_ask_price(int ask_price) {
   _always_ask_price = ask_price;
} /* set_always_ask_price() */

/**
 * This method returns the flag on this object that tells us if the shop
 * will always ask for a price
 * @return the flag
 */
int query_always_ask_price() {
   return _always_ask_price;
} /* query_always_ask_price() */

/**
 * This method will return the maximum price this item is allowed to
 * be sold for.  This will return 0 if there is no maximum price
 * @return 0 if there is no maximum price, or maximum price
 */
int query_maximum_sale_value(string person, object *obs) {
   return 0;
} /* query_maximum_sale_value() */

/**
 * This method returns any extra information about prices that the
 * shop wants to display before entering a cost.  This can be
 * overridden in higher inherits to control this more effectively.
 * @return an extra string to print
 */
string query_extra_price_information(string person, object *obs) {
   return "";
} /* query_extra_price_information() */

/**
 * @ignore yes
 * Sets this room as a shop, to interface with the shoplift command.
 * @return always returns 1
 */
int query_shop() {
   return 1;
} /* query_shop() */

/**
 * @ignore yes
 * This method returns the list of things that can be shoplifted from this
 * shop based on the input list.
 */
object *query_shop_lift_items(string str, object player) {
   object *stuff;
   object *pk_ok;
   object ob;

   stuff = match_objects_for_existence(str, ({ _controller->query_sell_list() }));
   pk_ok = ({ });
   foreach (ob in stuff) {
      if (!pk_check(_controller->query_owner_of_shop_object(ob),
                      player, 1)) {
         pk_ok += ({ ob });
      }
   }
   if (sizeof(pk_ok) != sizeof(stuff)) {
      player->add_failed_mess(previous_object(),
                      "You cannot shoplift $I since you cannot "
                      "shoplift from non-player killers.\n", stuff);
   }
   return pk_ok;
} /* query_shop_lift_items() */

/**
 * This method is called when something is bought and tells us how much
 * has been spent.
 * @param value of the the items sold
 * @param obs the objects bought
 * @param player who bought them
 * @param sellers the people whose items were bought
 * @param names the names of the items bought
 * @param cats the categories of the items bought
 * @param values the values of each item
 */
void inform_of_buy(int value, object *obs, object player, string *sellers,
                   string *names, string *cats, int *values) {
} /* inform_of_buy() */

/**
 * @ignore yes
 * This place is always marked as 'no steal' for now.
 */
int query_no_steal() {
   return 1;
} /* query_no_steal() */

/**
 * This allows the system to override this for player run shops so the
 * owner can discount peoples items.
 * @param ob the object to test
 * @return 1 if they can modify the item, 0 if not
 */
int is_able_to_change(object ob) {
   return _controller->query_owner_of_shop_object(ob) ==
       this_player()->query_name();
} /* is_able_change_ob() */

/**
 * This method checks to see if this item is allowed to be sold in this shop.
 * It assumes it is called from a add_command() method and sets up fail
 * messages accordingly.
 * @param obs the objects to check
 * @param name the name of the person doing the selling
 * @param sellable_names the names of the items to sell
 */
int is_allowed_to_sell(object* obs, string name, string *sellable_names) {
   if ((sizeof(sellable_names)+sizeof(obs)) > query_max_sell_per_person() &&
       query_max_sell_per_person()) {
      add_failed_mess("Cannot sell $I, since you already have " +
                      query_num(sizeof(sellable_names)) +
                      " items listed.\n", obs);
      return 0;
   }
   return 1;
} /* is_allowed_to_sell() */

/** @ignore yes */
void init() {
   string storeroom;

   storeroom = file_name(_controller->query_sell_list());
   add_command("sell", "<indirect:object:me>");
   add_command("list", "");
   add_command("buy", "<indirect:object:" + storeroom + ">",
               (: do_buy($1) :));
   add_command("browse", "<indirect:object:" + storeroom + ">",
               (: do_browse($1) :));
   add_command("change", "price of <indirect:object:" + storeroom +
                         "> to <string>", (: do_change($1, $4[1]) :));
   add_command("change", "name of <indirect:object:" + storeroom +
                         "> to <string>", (: do_change_name($1, $4[1]) :));
   add_command("collect", "royalties");
   add_command("delete", "<string'sell id'>",
               (: do_delete($4[0]) :));
} /* init() */

/**
 * This method sells an object into the craft shop.
 * @param obs the objects to sell
 * @return 1 on success, 0 on failure
 * @see do_list()
 * @see do_buy()
 */
int do_sell(object *obs) {
   object *sellable;
   string name;
   string *sellable_names;
   int i;
   int cost;
   string place;

   // Check to make sure the shop is open.
   if (!check_open(this_player())) {
      return 0;
   }

   sellable = filter(obs, query_allowed_to_sell());
   if (!sizeof(sellable)) {
      add_failed_mess("This shop does not deal in $I.\n", obs);
      return 0;
   }
   if (query_cut() != 100) {
      write("Selling an item to this shop does not automatically give you the "
            "money.  You will get a cut of " + (100 - query_cut()) +
            "% of the sale price once the item has been sold by the shop.\n");
   }

   name = this_player()->query_name();
   if (!is_allowed_to_sell(sellable, name, sellable_names)) {
      return 0;
   }

   sellable_names = _controller->query_owner_sellables(name);
   sellable_names = uniq_array(sellable_names);
   if (!sizeof(sellable_names)) {
      write("You are not currently selling any items.  What name "
            "would you like to list " + query_multiple_short(sellable) +
            " under?  (Blank line to quit)\nList name: ");
      input_to((: sell_name :), 0, sellable);
   } else {
      place = query_property("place");
      if (!place) {
         place = "default";
      }
      write("You currently have the following listed for sale:\n");
      for (i = 0; i < sizeof(sellable_names); i++) {
         cost = _controller->query_list_object_cost(sellable_names[i],
                                   this_player()->query_name());
         write(sprintf("  %c) %s (%s)\n", 'a' + i, sellable_names[i],
                       MONEY_HAND->money_value_string(cost, place)));
      }
      write("Select the letter of the choice to add to the item, or type "
            "'create' to create a new listing name.\n"
            "Your choice ([a-" + sprintf("%c", 'a' + sizeof(sellable_names) -1) +
            "],create) ? ");
      input_to((: sell_choice :), 0, sellable, sellable_names);
   }
   add_succeeded_mess(({ "", "$N starts to sell $I to the shop.\n" }),
                      sellable);
   return 1;
} /* do_sell() */


/**
 * This method makes sure a name is not too long and contains
 * no colour codes.
 * @param name the name to list the object as
 * @return 0 if name is unacceptable, 1 if it's OK
 */
protected int check_sell_name( string name ) {

   if (strlen(name) > CRAFT_SHOP_MAX_NAME_LENGTH) {
      write("Sorry, that name is too long.  It can be a maximum of " +
            CRAFT_SHOP_MAX_NAME_LENGTH + " characters.\n");
      return 0;
   }

   if (strsrch(name, "%^") != -1) {
      write("Sorry, you cannot use colour sequences in names.\n");
      return 0;
   }

   // Name was OK
   return 1;

} // check_sell_name()

/**
 * This method determines the name to list the object under.  This is
 * called when a new name is used.
 * @param name the name to list the object as
 * @param sellable the list of sellable objects
 */
protected void sell_name(string name, object *sellable) {
   if (name == "" || name == 0) {
      write("Ok, cancelling the sale of " + query_multiple_short(sellable) +
            ".\n");
      return ;
   }

   if ( !check_sell_name( name ) ) {
      input_to((: sell_name :), 0, sellable);
      write("What name would you like to list " +
            query_multiple_short(sellable) + " under? ");
      return;
   }

   write("You wish to sell " + query_multiple_short(sellable) +
         " as '" + name + "' (y/n)? ");
   input_to((: confirm_sell_name_choice :), 0, name, sellable);

} /* sell_name() */


/**
 * This method is used to confirm the choice of name to use when
 * selling the object.
 * @param answer the confirmation choice
 * @param name the name to sell it as
 * @param sellable the list of objects to sell
 */
protected void confirm_sell_name_choice(string answer, string name,
                                        object *sellable) {
   answer = lower_case(answer);
   if (strlen(answer) > 0 && ( answer[0] == 'n' || answer[0] == 'q' ) ) {
      write("Ok, cancelling the sale of " + query_multiple_short(sellable) +
            ".\n");
      return ;
   }
   if (!strlen(answer) || answer[0] != 'y') {
      write("Unknown selection.\n");
      write("You wish to sell " + query_multiple_short(sellable) +
            " as '" + name + "' (y/n)? ");
      input_to( (: confirm_sell_name_choice :), 0, name, sellable);
      return ;
   }
   write(query_extra_price_information(this_player()->query_name(), sellable));
   write("How much money do you wish to list the item '" + name + "' for? ");
   input_to((: determine_cost :), 0, name, sellable);
} /* confirm_sell_name_choice() */

/**
 * This method is used when there is a list of object already listed.  It
 * will check to see if the choice entered is valid and then use that
 * name when selling the object if it is.
 * @param choice the selected choice
 * @param sellable the array of sellable objects
 * @param names the names of choices for name
 */
protected void sell_choice(string answer, object *sellables, string *names) {
   int cost;
   string place;
   int pos;

   if (answer == "" || (answer[0] == 'q' || answer[0] == 'Q')) {
      write("Ok, cancelling the sale of " + query_multiple_short(sellables) +
            ".\n");
      return ;
   }
   answer = lower_case(answer);
   if (answer == "create") {
      write("Please enter the name you wish to list " +
            query_multiple_short(sellables) + " as.\nList name: ");
      input_to((: sell_name :), 0, sellables);
      return ;
   }
   if (strlen(answer) > 1 ||
       (answer[0] < 'a' || answer[0] >= 'a' + sizeof(names))) {
      write(sprintf("Your response must be between 'a' and '%c' or "
                    "'create'.\nChoice? ", 'a' + sizeof(names) - 1));
      input_to((: sell_choice :), 0, sellables, names);
      return ;
   }
   pos = answer[0] - 'a';
   cost = _controller->query_list_object_cost(names[pos],
                                              this_player()->query_name());
   if (!cost || _always_ask_price) {
      /* Hmm, the cost is not working, we need to set a cost as well. */
      write("How much money do you wish to list the item '" + names[pos] +
            "' for? ");
      write(query_extra_price_information(this_player()->query_name(),
                                          sellables));
      input_to((: determine_cost :), 0, names[pos], sellables);
   } else {
      place = query_property("place");
      if (!place) {
         place = "default";
      }
      do_confirm_sale(cost, names[pos], sellables);
   }
} /* sell_choice() */

/**
 * This method figured out the cost of the item based on the value they
 * type in.
 * @param cost the cost of the item
 * @param name the name of the item
 * @param sellable the array of objects to sell
 */
protected void determine_cost(string cost, string name, object *sellable) {
   int max;
   int value;
   string place;

   place = query_property("place");
   if (!place) {
      place = "default";
   }
   value = value_from_string(cost, place);
   if (value < _minimum_cost) {
      if (!strlen(cost) ||
          lower_case(cost)[0] == 'q') {
         write("Quiting.\n");
         return ;
      }
      write("You must list your item as costing more than " +
            MONEY_HAND->money_value_string(_minimum_cost, place) + ".\n");
      write(query_extra_price_information(this_player()->query_name(),
                                          sellable));
      write("What do you wish to list " + name + " for? ");
      input_to("determine_cost", 0, name, sellable);
      return ;
   }

   max = query_maximum_sale_value(this_player()->query_name(), sellable);
   if (value > max && max) {
      write("You must list your item as costing less than " +
            MONEY_HAND->money_value_string(max, place) + ".\n");
      write(query_extra_price_information(this_player()->query_name(),
                                          sellable));
      write("What do you wish to list " + name + " for? ");
      input_to("determine_cost", 0, name, sellable);
      return ;
   }

   do_confirm_sale(value, name, sellable);
} /* determine_cost() */

private void confirm_sale_question(int value,
                                   string name,
                                   object *sellables,
                                   string category) {
   string place;

   place = query_property("place");
   if (!place) {
      place = "default";
   }
   write("Confirming your sale of " + query_multiple_short(sellables) +
         " as '" + name +
         "' " + (category?"in category " + category + " ":"") +
         "for " + MONEY_HAND->money_value_string(value, place) + " (y/n)? ");
   input_to((: confirm_sale :), 0, value, name, sellables, category);
} /* confirm_sale_question() */

/**
 * This method will ask the person to confirm the sale.  It will check
 * the extra_sell_check function and then call the extra sale thing.
 * @param value the value of the thing being sold
 * @param name the name the thing is sold for
 * @param sellables the things to sell
 */
protected void do_confirm_sale(int value, string name, object *sellables) {
   if (_extra_sell_check) {
      evaluate(_extra_sell_check,
               (: confirm_sale_question :),
               value, name, sellables);
   } else {
      confirm_sale_question(value, name, sellables, 0);
   }
} /* do_confirm_sale() */

/**
 * This method confirms the sale after the cost and name have been
 * determined.
 * @param answer the answer to confirm with
 * @param value the value of the object
 * @param name the name of the object
 * @param sellable the list of sellable objects
 * @param category the category of the object, 0 if none
 */
protected void confirm_sale(string answer, int value, string name,
                            object *sellable,
                            string category) {
   if (answer == "" || answer[0] == 'q' || answer[0] == 'Q' ||
       answer[0] == 'n' || answer[0] == 'N') {
      write("Ok, aborting sale of " + query_multiple_short(sellable) + ".\n");
      return 0;
   }
   if (answer[0] != 'y' && answer[0] != 'Y') {
      write("Please answer yes or no.\n");
      confirm_sale_question(value, name, sellable, category);
      return 0;
   }
   complete_sale(value, name, sellable, category);
} /* confirm_sale() */

/**
 * This method is called to complete the sale completely.  It is split
 * up into a second function to allow the extra sell stuff
 * to work neatly.
 * @param value the value of the objects to sell
 * @param name the list name
 * @param sellable the list of objects to sell
 * @param category the category of the object, 0 if none
 */
protected void complete_sale(int value, string name, object *sellable,
                             string category) {
   object *bought;
   string place;
   object ob;

   /* First run them through the when sold function. */
   if (_when_sold_func) {
      foreach (ob in sellable) {
         evaluate(_when_sold_func, ob);
      }
   }
   bought = _controller->buy_objects(sellable, name, value,
                                   this_player()->query_cap_name(), category);
   place = query_property("place");
   if (!place) {
      place = "default";
   }
   if (sizeof(bought)) {
      write("Sold " + query_multiple_short(bought) + ", listed as '" +
            name + "' " + (category?" in category " + category + " ":"") +
            "for " + MONEY_HAND->money_value_string(value, place) +
            ".\nYou will need to come back later to pick up your royalties "
            "for the sale.\n");
      say(this_player()->query_cap_name() + " sold " +
          query_multiple_short(bought) + " to " + the_short() + ".\n");
   } else {
      write("Unable to sell " + query_multiple_short(sellable) + ".\n");
   }
} /* confirm_sale() */

/**
 * This method will list the current set of objects which are
 * available to buy.
 * @return 1 on success, 0 on failure
 * @see do_sell()
 * @see do_buy()
 */
int do_list() {
   string place;
   object *obs;
   object ob;
   mixed *stuff;
   string ret;

   // Check to make sure the shop is open.
   if (!check_open(this_player())) {
      return 0;
   }

   /* Do a list of the wombles... */
   obs = _controller->query_sell_list_obs();
   if (!sizeof(obs)) {
      add_failed_mess("The shop is empty.\n", ({ }));
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   stuff = unique_array(obs, (: $1->short() :) );
   stuff = sort_array(stuff, (:
                strcmp(_controller->query_id_of_shop_object($1[0]),
                       _controller->query_id_of_shop_object($2[0])) :) );
   ret = "";
   foreach (obs in stuff) {
      ob = obs[0];
      ret += "$I$9=$C$" + _controller->query_id_of_shop_object(ob) +
            ") $C$" + ob->short() + " for " +
            MONEY_HAND->money_value_string(ob->query_value(), place) + "; " +
            query_num(sizeof(obs)) + " left.\n";
   }
   write("$P$Shop list$P$" + ret);
   add_succeeded_mess( ({ "", "$N browses through the inventory.\n" }) );
   return 1;
} /* do_list() */

/**
 * This method will attempt to buy an object from the shops inventory.
 * @param str the string to match an object with
 * @return 1 on success, 0 on failure
 * @see do_sell()
 * @see do_list()
 */
int do_buy(object *obs) {
   int value;
   string place;
   int player_money;
   object *bought;
   string *sellers;
   string *names;
   string *cats;
   int *values;

   // Check to make sure the shop is open.
   if (!check_open(this_player())) {
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }
   player_money = this_player()->query_value_in(place);
   value = _controller->value_of_objects(obs);
   if (value > player_money) {
      add_failed_mess("You cannot buy $I as "+
                      ( sizeof( obs ) > 1 ? "they cost ":"it costs " )+
                      MONEY_HAND->money_value_string(value, place) +
                      " and you do not have that much money.\n", obs );
      return 0;
   }

   sellers = map(obs, (: _controller->query_owner_of_shop_object($1) :));
   names = map(obs, (: _controller->query_name_of_shop_object($1) :));
   cats = map(obs, (: _controller->query_category_of_shop_object($1) :));
   values = map(obs, (: $1->query_value() :));

   bought = _controller->sell_objects(obs, place, this_player(),
                                     query_cut());
   if (!sizeof(bought)) {
      if (sizeof(bought) > 1) {
         add_failed_mess("Cannot move any of $I into your inventory, nothing "
                         "bought.\n", obs);
      } else {
         add_failed_mess("Cannot move $I into your inventory, nothing "
                         "bought.\n", obs);
      }
      return 0;
   }
   inform_of_buy(value, bought, this_player(), sellers, names, cats, values);
   add_succeeded_mess(({ "You buy $I for " +
                       MONEY_HAND->money_value_string(value, place) + ".\n",
                         "$N buys $I.\n" }), bought);
   return 1;
} /* do_buy() */


/**
 * This method will delete a list entry without the owner getting paid.
 * @param str the string to match an object with
 * @return 1 on success, 0 on failure
 */
int do_delete(string id ) {

   if( !lordp(this_player()) ) {
      add_failed_mess( "This command is reserved for lords.\n", ({ }) );
      return 0;
   }

   if( !id || id == "" ) {
      add_failed_mess( "You have to choose a valid sell id.\n", ({ }) );
      return 0;
   }

   _controller->delete_objects(id);

   add_succeeded_mess( "$N $V some things.\n", ({ }) );

   return 1;
} /* do_delete() */


/**
 * This function browses through the current selected list of items.
 * @param obs the items to browse through
 */
int do_browse(object *obs) {
   object *real_obs;
   object ob;
   string mess;
   string read;

   // Check to make sure the shop is open.
   if (!check_open(this_player())) {
      return 0;
   }

   if (sizeof(obs) > 10) {
      add_failed_mess("You cannot browse more than 10 things at once.\n");
      return 0;
   }

   real_obs = _controller->create_all_real_objects(this_player(), obs);
   mess = "$P$Browse$P$";
   foreach (ob in real_obs) {
      mess += ob->the_short() + ":\n" + ob->long();
      read = ob->query_readable_message();
      if (read) {
         mess += "You read " +
                 replace(ob->query_read_short(this_player()), "$name$",
                         ob->a_short()) + ":\n" +
                 ob->query_readable_message();
      }
   }
   real_obs->move("/room/rubbish");
   add_succeeded_mess(({ mess, "$N browses $I.\n" }), obs);
   return 1;
} /* do_browse() */

/**
 * This function changes the list price of an object.
 * @param obs the object to change the list price of
 * @param change the value to change the list price to
 */
int do_change(object *obs, string change) {
   object *frog;
   int value;
   string place;

   // Check to make sure the shop is open.
   if (!check_open(this_player())) {
      return 0;
   }

   frog = filter(obs, (: $1->short() == $2->short() :), obs[0]);
   if (sizeof(frog) != sizeof(obs)) {
      add_failed_mess("You cannot change the value of more than one "
                      "type of object at once.\n", obs);
      return 0;
   }

   if (!is_able_to_change(obs[0])) {
      add_failed_mess("You do not own $I.\n", obs);
      return 0;
   }

   place = query_property("place");
   if (!place) {
      place = "default";
   }

   value = value_from_string(change, place);
   if (value < _minimum_cost) {
      add_failed_mess("You cannot set $I to a value less than " +
                 MONEY_HAND->money_value_string(_minimum_cost, place) + ".\n",
                 obs);
      return 0;
   }

   _controller->change_value_of_shop_object(obs[0], value);
   add_succeeded_mess( ({ "You change the value of $I to " +
         MONEY_HAND->money_value_string(value, place) + ".\n",
         "$N changes the value of $I.\n" }), obs[0..0]);
   return 1;
} /* do_change() */

/**
 * This function changes the list name of an object.
 * @param obs the object to change the list name of
 * @param new_name the value to change the list name to
 */
int do_change_name(object *obs, string new_name) {
   object *frog;

   // Check to make sure the shop is open.
   if (!check_open(this_player())) {
      return 0;
   }

   frog = filter(obs, (: $1->short() == $2->short() :), obs[0]);
   if (sizeof(frog) != sizeof(obs)) {
      add_failed_mess("You cannot change the value of more than one "
                      "type of object at once.\n", obs);
      return 0;
   }

   if (!is_able_to_change(obs[0])) {
      add_failed_mess("You do not own $I.\n", obs);
      return 0;
   }

   if( !check_sell_name( new_name ) ) {
      add_failed_mess( "Try another name.\n", ({ }) );
      return 0;
   }

   if (_controller->change_name_of_shop_object(obs[0], new_name)) {
      add_succeeded_mess(({ "You change the name of $I to " +
                               new_name + ".\n",
                               "$N changes the name of $I.\n" }),
                         ({ obs[0] }) );
   } else {
      add_failed_mess("The name " + new_name + " is already in use.\n", obs);
   }

   return 1;
} /* do_change_name() */

/**
 * This method collects any royalties the current player is owed.
 * @return 1 on succes, 0 on failure
 */
int do_collect() {
   string name;
   string place;
   int value;

   // Check to make sure the shop is open.
   if (!check_open(this_player())) {
      return 0;
   }

   name = this_player()->query_name();
   value = _controller->query_royalty(name);
   if (!value) {
      add_failed_mess("You have no money owed to you here.\n");
      return 0;
   }

   if (value < 0) {
      add_failed_mess("You owe money to " + the_short() + ".\n");
      return 0;
   }


   place = query_property("place");
   if (!place) {
      place = "default";
   }
   _controller->pay_out_royalty(name, place);
   add_succeeded_mess(({ "You collect " +
                      MONEY_HAND->money_value_string(value, place) +
                      " in royalties.\n",
                      "$N collects some money in royalties.\n" }));
   return 1;
} /* do_collect() */

/**
 * This method sets the cut for the object.
 * @param new_cut the new cut of the object
 * @see query_cut()
 */
void set_cut(int new_cut) {
   _cut = new_cut;
} /* set_cut() */

/**
 * This method queries the current cut defined for this shop.
 * @return the current cut for the shop
 * @see set_cut()
 */
int query_cut() {
   return _cut;
} /* query_cut() */

/**
 * This method sets the minimum allowed cost for the item in the
 * shop inventory.
 * @param cost the minimum cost
 * @see query_minimum_cost()
 */
void set_minimum_cost(int cost) {
   _minimum_cost = cost;
} /* set_minimum_cost() */

/**
 * This method returns the minimum allowed cost for an item in the
 * shop inventory.
 * @see set_minimum_cost()
 */
int query_minimum_cost() {
   return _minimum_cost;
} /* query_minimum_cost() */

/**
 * This method sets the function to be called on all the objects when
 * they are sold.  For instance this can be used to close all books
 * etc.
 * @param func the function to use
 * @example
 * // Close the book when it is sold.
 * set_when_sold_function( (: $1->set_open_page(0) :) );
 */
void set_when_sold_function(function func) {
   _when_sold_func = func;
} /* set_when_sold_function() */

/**
 * This method returns the when sold function.
 * @return the when sold function
 * @see set_when_sold_function()
 */
function query_when_sold_function() {
   return _when_sold_func;
} /* query_when_sold_function() */

/**
 * This method sets the function to be called to check to see if the shop
 * is open.  If this is not set then the shop is always assumed to be
 * open.  The function returns a non-zero value then the shop is considered
 * closed, if the function returns a value other than 1 then the default
 * fail message will not be added.
 * @param func the function to use for open tests
 * @example
 * // The shop is only open if gammer is alive.
 * set_open_function( (: (gammer?1:0) :) );
 * @see check_open()
 * @example
 * void setup() {
 *    ...
 *    // Setup the open function.
 *    set_open_function( (: do_open_check :) );
 * } /\* setup() *\/
 *
 * int do_open_check(object player) {
 *    if (!gammer) {
 *       add_failed_mess("There appears to be no shopkeeper here!\n");
 *       return 2;
 *    }
 *    return 0;
 * } /\* do_open_check() *\/
 */
void set_open_function(function func) {
   _open_function = func;
} /* set_open_function() */

/**
 * This method returns the function used to check to see if the shop is
 * open or not.
 * @return the shop open function
 * @see set_open_function()
 */
function query_open_function() {
   return _open_function;
} /* query_open_function() */

/**
 * This method checks to see if the shop is open or not.  It will setup the
 * default fail message if this is what is needed.
 * @param player
 * @see set_open_function()
 */
int check_open(object player) {
   int val;

   val = evaluate(_open_function, player);
   if (val == 1) {
      add_failed_mess("The shop is not open.\n");
      return 0;
   }
   return !val;
} /* check_open() */

/**
 * This method sets the extra sell function for the shop.  The extra sell
 * function is called after the sell process is almost finished.  It can
 * check for extra things needed in the sell process, like which page of
 * the book to browse.
 * @param func the extra sell function
 * @see query_extra_sell_check()
 * @see complete_sell()
 */
void set_extra_sell_check(function func) {
   _extra_sell_check = func;
} /* set_extra_sell_check() */

/**
 * This method returns the extra sell check function.
 * @return the extra sell check function
 * @see set_extra_sell_check()
 */
function query_extra_sell_check() {
   return _extra_sell_check;
} /* query_extra_sell_check() */

void dest_me() {
  if(_controller) {
     _controller->dest_me();
  }
} /* dest_me() */