skylib_mudos_v1/
skylib_mudos_v1/bin/
skylib_mudos_v1/bin/db/
skylib_mudos_v1/mudlib/banish/a/
skylib_mudos_v1/mudlib/banish/b/
skylib_mudos_v1/mudlib/banish/c/
skylib_mudos_v1/mudlib/banish/d/
skylib_mudos_v1/mudlib/banish/e/
skylib_mudos_v1/mudlib/banish/f/
skylib_mudos_v1/mudlib/banish/g/
skylib_mudos_v1/mudlib/banish/h/
skylib_mudos_v1/mudlib/banish/j/
skylib_mudos_v1/mudlib/banish/l/
skylib_mudos_v1/mudlib/banish/m/
skylib_mudos_v1/mudlib/banish/n/
skylib_mudos_v1/mudlib/banish/o/
skylib_mudos_v1/mudlib/banish/p/
skylib_mudos_v1/mudlib/banish/r/
skylib_mudos_v1/mudlib/banish/s/
skylib_mudos_v1/mudlib/banish/t/
skylib_mudos_v1/mudlib/banish/u/
skylib_mudos_v1/mudlib/banish/w/
skylib_mudos_v1/mudlib/cmds/
skylib_mudos_v1/mudlib/cmds/admin/
skylib_mudos_v1/mudlib/cmds/guild-race/
skylib_mudos_v1/mudlib/cmds/guild-race/crafts/
skylib_mudos_v1/mudlib/cmds/guild-race/magic/
skylib_mudos_v1/mudlib/cmds/guild-race/other/
skylib_mudos_v1/mudlib/cmds/living/broken/
skylib_mudos_v1/mudlib/cmds/player/group_cmds/
skylib_mudos_v1/mudlib/d/admin/
skylib_mudos_v1/mudlib/d/admin/room/
skylib_mudos_v1/mudlib/d/admin/room/we_care/
skylib_mudos_v1/mudlib/d/admin/save/
skylib_mudos_v1/mudlib/d/admin/text/
skylib_mudos_v1/mudlib/d/learning/TinyTown/buildings/
skylib_mudos_v1/mudlib/d/learning/TinyTown/map/
skylib_mudos_v1/mudlib/d/learning/TinyTown/roads/
skylib_mudos_v1/mudlib/d/learning/chars/
skylib_mudos_v1/mudlib/d/learning/functions/
skylib_mudos_v1/mudlib/d/learning/handlers/
skylib_mudos_v1/mudlib/d/learning/help_topics/
skylib_mudos_v1/mudlib/d/learning/help_topics/npcs/
skylib_mudos_v1/mudlib/d/learning/help_topics/objects/
skylib_mudos_v1/mudlib/d/learning/help_topics/rcs_demo/
skylib_mudos_v1/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/crowd/
skylib_mudos_v1/mudlib/d/learning/help_topics/rooms/situations/
skylib_mudos_v1/mudlib/d/learning/save/
skylib_mudos_v1/mudlib/d/learning/school/
skylib_mudos_v1/mudlib/d/learning/school/add_sc/
skylib_mudos_v1/mudlib/d/learning/school/characters/
skylib_mudos_v1/mudlib/d/learning/school/general/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/basic_commands/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/edtutor/
skylib_mudos_v1/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_mudos_v1/mudlib/d/learning/school/items/
skylib_mudos_v1/mudlib/d/learning/school/npc_school/
skylib_mudos_v1/mudlib/d/learning/school/room_school/
skylib_mudos_v1/mudlib/d/learning/school/room_school/room_basic/
skylib_mudos_v1/mudlib/d/learning/school/room_school/situations/
skylib_mudos_v1/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_mudos_v1/mudlib/d/learning/text/
skylib_mudos_v1/mudlib/d/liaison/
skylib_mudos_v1/mudlib/d/mudlib/
skylib_mudos_v1/mudlib/d/mudlib/changes/
skylib_mudos_v1/mudlib/d/playtesters/
skylib_mudos_v1/mudlib/d/playtesters/effects/
skylib_mudos_v1/mudlib/d/playtesters/handlers/
skylib_mudos_v1/mudlib/d/playtesters/items/
skylib_mudos_v1/mudlib/d/sage/
skylib_mudos_v1/mudlib/doc/
skylib_mudos_v1/mudlib/doc/creator/
skylib_mudos_v1/mudlib/doc/driver/
skylib_mudos_v1/mudlib/doc/driver/efuns/arrays/
skylib_mudos_v1/mudlib/doc/driver/efuns/buffers/
skylib_mudos_v1/mudlib/doc/driver/efuns/compile/
skylib_mudos_v1/mudlib/doc/driver/efuns/filesystem/
skylib_mudos_v1/mudlib/doc/driver/efuns/floats/
skylib_mudos_v1/mudlib/doc/driver/efuns/functions/
skylib_mudos_v1/mudlib/doc/driver/efuns/general/
skylib_mudos_v1/mudlib/doc/driver/efuns/mappings/
skylib_mudos_v1/mudlib/doc/driver/efuns/mixed/
skylib_mudos_v1/mudlib/doc/driver/efuns/mudlib/
skylib_mudos_v1/mudlib/doc/driver/efuns/numbers/
skylib_mudos_v1/mudlib/doc/driver/efuns/parsing/
skylib_mudos_v1/mudlib/doc/known_command/
skylib_mudos_v1/mudlib/doc/login/
skylib_mudos_v1/mudlib/doc/lpc/basic_manual/
skylib_mudos_v1/mudlib/doc/lpc/intermediate/
skylib_mudos_v1/mudlib/doc/new/add_command/
skylib_mudos_v1/mudlib/doc/new/events/
skylib_mudos_v1/mudlib/doc/new/handlers/
skylib_mudos_v1/mudlib/doc/new/living/race/
skylib_mudos_v1/mudlib/doc/new/living/spells/
skylib_mudos_v1/mudlib/doc/new/object/
skylib_mudos_v1/mudlib/doc/new/player/
skylib_mudos_v1/mudlib/doc/new/room/guild/
skylib_mudos_v1/mudlib/doc/new/room/outside/
skylib_mudos_v1/mudlib/doc/new/room/storeroom/
skylib_mudos_v1/mudlib/doc/object/
skylib_mudos_v1/mudlib/doc/playtesters/
skylib_mudos_v1/mudlib/doc/policy/
skylib_mudos_v1/mudlib/doc/weapons/
skylib_mudos_v1/mudlib/global/
skylib_mudos_v1/mudlib/global/creator/
skylib_mudos_v1/mudlib/global/handlers/
skylib_mudos_v1/mudlib/global/virtual/setup_compiler/
skylib_mudos_v1/mudlib/include/cmds/
skylib_mudos_v1/mudlib/include/effects/
skylib_mudos_v1/mudlib/include/npc/
skylib_mudos_v1/mudlib/include/room/
skylib_mudos_v1/mudlib/include/shops/
skylib_mudos_v1/mudlib/net/daemon/
skylib_mudos_v1/mudlib/net/daemon/chars/
skylib_mudos_v1/mudlib/net/inherit/
skylib_mudos_v1/mudlib/net/obj/
skylib_mudos_v1/mudlib/obj/amulets/
skylib_mudos_v1/mudlib/obj/b_day/
skylib_mudos_v1/mudlib/obj/clothes/
skylib_mudos_v1/mudlib/obj/dwarmours/plate/
skylib_mudos_v1/mudlib/obj/dwclothes/transport/horse/
skylib_mudos_v1/mudlib/obj/dwscabbards/
skylib_mudos_v1/mudlib/obj/dwweapons/axes/
skylib_mudos_v1/mudlib/obj/dwweapons/chains/
skylib_mudos_v1/mudlib/obj/faith/symbols/
skylib_mudos_v1/mudlib/obj/fungi/
skylib_mudos_v1/mudlib/obj/gatherables/
skylib_mudos_v1/mudlib/obj/instruments/
skylib_mudos_v1/mudlib/obj/magic/
skylib_mudos_v1/mudlib/obj/media/
skylib_mudos_v1/mudlib/obj/misc/player_shop/
skylib_mudos_v1/mudlib/obj/monster/godmother/
skylib_mudos_v1/mudlib/obj/monster/transport/
skylib_mudos_v1/mudlib/obj/rings/
skylib_mudos_v1/mudlib/obj/spells/
skylib_mudos_v1/mudlib/obj/stationery/
skylib_mudos_v1/mudlib/obj/stationery/envelopes/
skylib_mudos_v1/mudlib/obj/stationery/papers/
skylib_mudos_v1/mudlib/obj/toys/
skylib_mudos_v1/mudlib/obj/vessels/
skylib_mudos_v1/mudlib/obj/weapons/swords/
skylib_mudos_v1/mudlib/save/autodoc/
skylib_mudos_v1/mudlib/save/leaflets/
skylib_mudos_v1/mudlib/save/mail/
skylib_mudos_v1/mudlib/save/new_soul/data/
skylib_mudos_v1/mudlib/save/parcels/
skylib_mudos_v1/mudlib/save/playerinfo/
skylib_mudos_v1/mudlib/save/players/d/
skylib_mudos_v1/mudlib/save/random_names/
skylib_mudos_v1/mudlib/save/random_names/data/
skylib_mudos_v1/mudlib/save/terrains/
skylib_mudos_v1/mudlib/save/terrains/tutorial_desert/
skylib_mudos_v1/mudlib/save/terrains/tutorial_grassy_field/
skylib_mudos_v1/mudlib/save/terrains/tutorial_mountain/
skylib_mudos_v1/mudlib/save/todo_lists/
skylib_mudos_v1/mudlib/secure/
skylib_mudos_v1/mudlib/secure/cmds/admin/
skylib_mudos_v1/mudlib/secure/cmds/lord/
skylib_mudos_v1/mudlib/secure/config/
skylib_mudos_v1/mudlib/secure/handlers/autodoc/
skylib_mudos_v1/mudlib/secure/handlers/intermud/
skylib_mudos_v1/mudlib/secure/include/global/
skylib_mudos_v1/mudlib/secure/save/
skylib_mudos_v1/mudlib/secure/save/handlers/
skylib_mudos_v1/mudlib/secure/std/classes/
skylib_mudos_v1/mudlib/secure/std/modules/
skylib_mudos_v1/mudlib/std/commands/
skylib_mudos_v1/mudlib/std/commands/shadows/
skylib_mudos_v1/mudlib/std/creator/
skylib_mudos_v1/mudlib/std/dom/
skylib_mudos_v1/mudlib/std/effects/
skylib_mudos_v1/mudlib/std/effects/external/
skylib_mudos_v1/mudlib/std/effects/fighting/
skylib_mudos_v1/mudlib/std/effects/priest/
skylib_mudos_v1/mudlib/std/effects/room/
skylib_mudos_v1/mudlib/std/environ/
skylib_mudos_v1/mudlib/std/guilds/
skylib_mudos_v1/mudlib/std/guilds/old/
skylib_mudos_v1/mudlib/std/languages/
skylib_mudos_v1/mudlib/std/languages/BACKUPS/
skylib_mudos_v1/mudlib/std/liquids/
skylib_mudos_v1/mudlib/std/npc/
skylib_mudos_v1/mudlib/std/npc/goals/
skylib_mudos_v1/mudlib/std/npc/goals/basic/
skylib_mudos_v1/mudlib/std/npc/goals/misc/
skylib_mudos_v1/mudlib/std/npc/plans/
skylib_mudos_v1/mudlib/std/npc/plans/basic/
skylib_mudos_v1/mudlib/std/npc/types/
skylib_mudos_v1/mudlib/std/npc/types/helper/
skylib_mudos_v1/mudlib/std/npcs/
skylib_mudos_v1/mudlib/std/outsides/
skylib_mudos_v1/mudlib/std/races/shadows/
skylib_mudos_v1/mudlib/std/room/basic/topography/
skylib_mudos_v1/mudlib/std/room/controller/
skylib_mudos_v1/mudlib/std/room/inherit/topography/
skylib_mudos_v1/mudlib/std/room/topography/area/
skylib_mudos_v1/mudlib/std/room/topography/iroom/
skylib_mudos_v1/mudlib/std/room/topography/milestone/
skylib_mudos_v1/mudlib/std/shadows/curses/
skylib_mudos_v1/mudlib/std/shadows/disease/
skylib_mudos_v1/mudlib/std/shadows/fighting/
skylib_mudos_v1/mudlib/std/shadows/healing/
skylib_mudos_v1/mudlib/std/shadows/magic/
skylib_mudos_v1/mudlib/std/shadows/poison/
skylib_mudos_v1/mudlib/std/shadows/rituals/
skylib_mudos_v1/mudlib/std/shadows/room/
skylib_mudos_v1/mudlib/std/shops/controllers/
skylib_mudos_v1/mudlib/std/shops/objs/
skylib_mudos_v1/mudlib/std/shops/player_shop/
skylib_mudos_v1/mudlib/std/socket/
skylib_mudos_v1/mudlib/std/soul/
skylib_mudos_v1/mudlib/std/soul/d/
skylib_mudos_v1/mudlib/std/soul/e/
skylib_mudos_v1/mudlib/std/soul/i/
skylib_mudos_v1/mudlib/std/soul/j/
skylib_mudos_v1/mudlib/std/soul/k/
skylib_mudos_v1/mudlib/std/soul/l/
skylib_mudos_v1/mudlib/std/soul/n/
skylib_mudos_v1/mudlib/std/soul/o/
skylib_mudos_v1/mudlib/std/soul/q/
skylib_mudos_v1/mudlib/std/soul/u/
skylib_mudos_v1/mudlib/std/soul/v/
skylib_mudos_v1/mudlib/std/soul/y/
skylib_mudos_v1/mudlib/std/soul/z/
skylib_mudos_v1/mudlib/std/stationery/
skylib_mudos_v1/mudlib/w/
skylib_mudos_v1/mudlib/w/default/
skylib_mudos_v1/mudlib/w/default/armour/
skylib_mudos_v1/mudlib/w/default/clothes/
skylib_mudos_v1/mudlib/w/default/item/
skylib_mudos_v1/mudlib/w/default/npc/
skylib_mudos_v1/mudlib/w/default/room/
skylib_mudos_v1/mudlib/w/default/weapon/
skylib_mudos_v1/mudlib/www/
skylib_mudos_v1/mudlib/www/download/
skylib_mudos_v1/mudlib/www/java/
skylib_mudos_v1/mudlib/www/secure/
skylib_mudos_v1/mudlib/www/secure/lpc/advanced/
skylib_mudos_v1/mudlib/www/secure/lpc/intermediate/
skylib_mudos_v1/v22.2b14-DSv10/
skylib_mudos_v1/v22.2b14-DSv10/ChangeLog.old/
skylib_mudos_v1/v22.2b14-DSv10/Win32/
skylib_mudos_v1/v22.2b14-DSv10/compat/
skylib_mudos_v1/v22.2b14-DSv10/compat/simuls/
skylib_mudos_v1/v22.2b14-DSv10/include/
skylib_mudos_v1/v22.2b14-DSv10/mudlib/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/clone/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/command/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/data/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/etc/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/include/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/inherit/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/inherit/master/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/log/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/compiler/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/efuns/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/single/tests/operators/
skylib_mudos_v1/v22.2b14-DSv10/testsuite/u/
skylib_mudos_v1/v22.2b14-DSv10/tmp/
skylib_mudos_v1/v22.2b14-DSv10/windows/
/**
 * This is to handle a place where players can sell a number of neato
 * craft items they have made up.  It will be extended to deal with
 * books as well.
 * <p>
 * It keeps track of who sold each item and how much they will
 * receive when they pay for it.  Then tracks the royalties so
 * they can come back and collect them.
 * <p>
 * This will be run as a handler object so that npcs and rooms can
 * both run as this sort of shop.
 * <p>
 * NB: This object is not saved using save_object.  You need to set the
 * save and load functions for this system to work.
 * @author Pinkfish
 * @started Thu Feb  5 15:39:57 CST 1998
 */

#include <obj_parser.h>
#include <money.h>
#include <move_failures.h>
#include <player.h>
#include <shops/craft_shop.h>

/**
 * This keeps track of the object information itself.
 * @element cost the cost of the object when it is sold
 * @element cap_owner the capitalised owner of the object
 * @element ob_num the number of the object
 */
class craft_object {
   int cost;
   string cap_owner;
   int ob_num;
   string category;
}

/**
 * This class is the main sellable list.
 */
class craft_sellable {
   /**
    * The list of objects, it is indexed on the owners name.
    */
   mapping objects;
}

/**
 * This is the class with information about the category in it.
 */
class craft_category {
   int state;
   string* voted;
   int yes;
   int no;
   int abstain;
   int timeout;
}

#define SELL_OBJECT_NAME_PROP "sell name"
#define SELL_OBJECT_OWNER_PROP "sell owner"
#define SELL_OBJECT_CLASS_PROP "sell class"
#define SELL_OBJECT_ID_PROP "sell id"

/*
 * This will index on the name tag and will have an object, an array of owners
 * and
 * a number left count as the value in an array.  The object is
 * a dummy object to match on.  The real object will be created from
 * the save information.  
 */
private nosave mapping _sellables;
/*
 * This is the contained containingt the objects to sell.  We keep them in
 * a container so that we can do find_match matches.
 */
private nosave object _sell_list;
/* The royalties to be payed to each player. */
private nosave mapping _royalties;
/* The current id to use for the save files... */
private nosave int _current_save_num;
/* The categories we are using, the default is none. */
private nosave mapping _categories;
private nosave int _category_callout;
/* If not set then we add a name on by default if it doesn't exist. */
private nosave int _category_dont_use_name;


/* The functions to save and load the files... */
private nosave function _save_function;
private nosave function _load_function;
private nosave function _category_function;
private nosave int *_current_ids;
private nosave int _has_loaded;

void load_it();
void save_it();
private void update_sellable(string name);
class craft_object create_craft_object(object ob,
                                       string owner,
                                       int cost,
                                       string category);
void adjust_royalty(string player, int amt);
class craft_sellable create_craft_sellable();
void adjust_royalty(string player, int amt);
class craft_object query_class_of_shop_object(object ob);
object find_shop_object(class craft_object frog);
object *query_sell_list_obs();
string query_id_of_shop_object(object ob);
void remove_shop_id(string id);
private void setup_timeout_call();

void create() {
   /* Create a nice container to put our sell list in. */
   _sell_list = clone_object("/std/container");
   _current_save_num = 1;
   _current_ids = ({ });
   _categories = ([ ]);
} /* create() */

/**
 * This method is called by the controled object onto here to control
 * if the owners name is added to shop objects.  If this is set to 1
 * then the owners name will  not be added by default.
 * @param flag the flag to set
 */
void set_dont_use_name(int flag) {
   _category_dont_use_name = flag;
} /* set_dont_use_name() */

/**
 * This method is used to determine the status of the flag which controls
 * adding the owners name to shop objects by default.
 * If this is set to 1
 * then the owners name will  not be added by default.
 * @return 1 if the object owners name is not used, 0 if it is
 */
int query_dont_use_name() {
   return _category_dont_use_name;
} /* query_dont_use_name() */

/**
 * This method creates a save file for the specified objects autoloading
 * capability.  If the number to write to is non-null then it will
 * write to that object.
 * @param ob the object to get an autoload number for
 * @param fixed_num the file number to write to
 * @return the auto load number
 * @see save_it()
 */
protected int create_auto_load_file(mixed ob, int fixed_num) {
   mixed *auto_load;
   string tmp;

   if (objectp(ob)) {
      if (this_player()) {
         catch(auto_load = this_player()->create_auto_load(({ ob })));
      }
      if (!auto_load) {
         catch(auto_load = PLAYER_OB->create_auto_load(({ ob })));
      }
      if (!auto_load) {
         // Make it error on the last one...
         auto_load = AUTO_LOAD_OB->create_auto_load(({ ob }));
      }
   } else if (pointerp(ob)) {
      auto_load = ob;
   } else {
      printf("Error!  Dammit!\n");
   }
   if (!fixed_num) {
      do {
         tmp = evaluate(_load_function,
                        CRAFT_SHOP_DATA_SAVE_FILE,
                        "" + _current_save_num);
         if (tmp) {
            _current_save_num++;
         }
      } while (tmp);
      fixed_num = _current_save_num;
   }
   evaluate(_save_function,
            CRAFT_SHOP_DATA_SAVE_FILE,
            auto_load,
            "" + fixed_num);
   return fixed_num;
} /* create_auto_load_file() */

/**
 * This method creates a real object from the save file number.
 * @param num the save file number
 * @return the nice shiny new object
 * @see create_auto_load_file()
 */
protected object create_real_auto_load_object(int num, object player) {
   mixed *auto_load;
   object *obs;

   auto_load = evaluate(_load_function,
                        CRAFT_SHOP_DATA_SAVE_FILE,
                        "" + num);
   if (userp(player)) {
      obs = player->load_auto_load_to_array(auto_load, player);
   } else if (this_player()) {
      obs = this_player()->load_auto_load_to_array(auto_load, player);
   } else {
      obs = PLAYER_OB->load_auto_load_to_array(auto_load, player);
   }
   if (sizeof(obs)) {
      return obs[0];
   }
   return clone_object("/std/object");
} /* create_object_from_auto_load() */

/**
 * This method removes the auto load stuff after it is no longer needed.
 * @param num the file number to remove
 * @see create_object_from_auto_load()
 */
protected void remove_auto_load_file(int num) {
   evaluate(_save_function,
            CRAFT_SHOP_REMOVE_DATA_SAVE_FILE,
            0,
            "" + num);
} /* remove_auto_load_file() */

/**
 * This method adds a selable into the current sellable array.
 * @param name the name the object willb elisted under
 * @param owner the owner of the object being sold
 * @param ob the objects being sold
 * @see add_sell_list_object()
 * @see change_name_of_object()
 */
protected void add_to_sellables(string name,
                                string owner,
                                class craft_object *ob) {
   class craft_sellable craft_sell;

   if (_sellables[name]) {
      craft_sell = (class craft_sellable)_sellables[name];
   } else {
      craft_sell = create_craft_sellable();
      _sellables[name] = craft_sell;
   }

   /*
    * This seemingly weird way of handling the craft objects is so we have
    * an easily returnable handle to the class which we can pass to the
    * dummy shop objects.
    */
   if (!craft_sell->objects[owner]) {
      craft_sell->objects[owner] = ({ });
   }
   craft_sell->objects[owner] += ob;
   
   update_sellable(name);
} /* add_to_selllist() */

/**
 * This will allow us to add an object into the inventory of the
 * craft shop.  If the name already exists in the inventory we
 * just increment the number left by the number passed in here.
 * If the number left goes below 0 then the item is remove
 * from the inventory.
 * @param ob the object to add
 * @param name the tag to use for it
 * @param num the number to add
 * @param category the category to add the item to
 * @see remove_list_object()
 * @see add_sell_list_name()
 */
int add_list_object(object ob,
                    string name,
                    int cost,
                    string owner,
                    string category) {
   class craft_object craft_ob;
   string cap_owner;

   if (!objectp(ob) || !stringp(name) || !intp(cost)) {
      return 0;
   }

   cap_owner = owner;
   owner = lower_case(owner);
   add_to_sellables(name,
                    owner,
                    ({ create_craft_object(ob, cap_owner, cost, category) }));
   foreach (craft_ob in ((class craft_sellable)_sellables[name])->objects[owner]) {
      craft_ob->cost = cost;
      craft_ob->category = category;
   }
   save_it();
   return 1;
} /* add_list_object() */

/**
 * This method removes a single object from the current object list.
 * @param name the name of object to remove
 * @param owner the owner of the object to remove
 * @param ob the craft object pointer itself we want removed
 * @see add_list_object()
 */
void remove_list_object(string name, string owner, class craft_object ob) {
   int i;
   object us;
   class craft_object *data;

   if (_sellables[name] &&
       ((class craft_sellable)_sellables[name])->objects[owner]) {
      data = ((class craft_sellable)_sellables[name])->objects[owner];
      for (i = 0; i < sizeof(data); i++) {
         if (data[i] == ob) {
            break;
         }
      }
      //i = member_array(ob, (class craft_object)_sellables[name]->objects[owner]);
      if (i < sizeof(data)) {
         ((class craft_sellable)_sellables[name])->objects[owner] -= ({ ob });
         if (!sizeof(((class craft_sellable)_sellables[name])->objects[owner])) {
            map_delete(((class craft_sellable)_sellables[name])->objects, owner);
            if (!sizeof(((class craft_sellable)_sellables[name])->objects)) {
               map_delete(_sellables, name);
               us = find_shop_object(ob);
               remove_shop_id(query_id_of_shop_object(us));
            }
         }
         save_it();
         remove_auto_load_file(ob->ob_num);
         us = find_shop_object(ob);
         us->dest_me();
      } else {
         printf("Unable to find the object to remove? %O, %O\n", name, owner);
      }
   }
} /* remove_list_object() */

/**
 * This method turns a craft_object class into an actual object.
 * @param craft the craft object to create
 * @return the newly formed craft object
 * @see create_all_real_objects()
 */
object create_real_object(object player, class craft_object craft) {
   return create_real_auto_load_object(craft->ob_num, player);
} /* create_real_object() */

/**
 * This method creates real objects for all the passed in dummy objects.
 * Remember to dest the objects after you are finished with them.
 * @param obs the dummy objects to find real ones of
 * @return the real object values
 * @see create_real_object()
 * @example
 * real_obs = create_real_objects(this_player(), obs);
 * foreach (ob in real_obs) {
 *    ret += ob->the_short + ":\n" + ob->long() + "\n";
 * }
 */
object *create_all_real_objects(object player, object *obs) {
   object *ret;
   object ob;
   class craft_object craft;

   ret = ({ });
   foreach (ob in obs) {
      craft = query_class_of_shop_object(ob);
      ret += ({ create_real_object(player, craft) });
   }
   return ret;
} /* create_all_real_objects() */

/**
 * This method creates a craft object from the input actual object.
 * @param ob the real input object
 * @param owner the owner of the object
 * @param cost the cost of the object
 * @param category the category of the object
 * @return a nice craft object
 */
class craft_object create_craft_object(object ob,
                                       string owner,
                                       int cost,
                                       string category) {
   class craft_object craft;
   int num;

   craft = new (class craft_object);
   num = create_auto_load_file(ob, 0);
   craft->ob_num = num;
   craft->cost = cost;
   craft->cap_owner = owner;
   craft->category = category;
   return craft;
} /* create_craft_object() */

/**
 * This method creates a sellable class.
 * @return a new sellable class
 */
class craft_sellable create_craft_sellable() {
   class craft_sellable craft;

   craft = new(class craft_sellable);
   craft->objects = ([ ]);
   return craft;
} /* create_craft_sellable() */

/**
 * This method returns the owner associated with the shop
 * object.
 * @param ob the shop object to query the owner of
 * @return the owner of the shop object
 */
string query_owner_of_shop_object(object ob) {
   return ob->query_property(SELL_OBJECT_OWNER_PROP);
} /* query_owner_of_shop_object() */

/**
 * This method returns the owner associated with the shop
 * object.
 * @param ob the shop object to query the owner of
 * @return the owner of the shop object
 */
string query_name_of_shop_object(object ob) {
   return ob->query_property(SELL_OBJECT_NAME_PROP);
} /* query_owner_of_shop_object() */

/**
 * This method returns the id associated with the shop object.
 * This is the one letter id used for buying selling etc.
 * @param ob the shop object to query the id of
 * @return the id of the shop object
 */
string query_id_of_shop_object(object ob) {
   return ob->query_property(SELL_OBJECT_ID_PROP);
} /* query_id_of_shop_object() */

/**
 * This method sets the current id of the shop objects.
 * @param obs the objects to set the id for
 * @param id the new id for them all
 */
void set_id_of_shop_objects(object *obs, string id) {
   obs->add_property(SELL_OBJECT_ID_PROP, id);
   obs->add_alias(id);
} /* set_id_of_shop_objects() */

/**
 * This method returns the category of the speficied shop object.
 * @param ob the object to find the category of
 * @return the category of the object
 */
string query_category_of_shop_object(object ob) {
   class craft_object craft;

   craft = query_class_of_shop_object(ob);
   if (craft) {
      return craft->category;
   }
   return 0;
} /* query_category_of_shop_object() */

/**
 * This method updates the internal representation of the specified
 * craft object.  It replaces the saved auto load info with the new
 * stuff.
 * @param craft the craft object containing the info to replace
 * @param replacement_ob the new object
 */
void update_craft_object(object craft_ob, object replacement_ob) {
   class craft_object craft;

   craft = query_class_of_shop_object(craft_ob);
   if (craft) {
      create_auto_load_file(replacement_ob, craft->ob_num);
   }
} /* update_craft_object() */

/**
 * This method attempts to figure out what the next id would
 * be for the object.  It will allocate this id as well as returning.
 * @return the next id for the shop
 * @see remove_shop_id()
 * @see update_sellables()
 */
string query_next_shop_id() {
   int num;

   num = 0;
   while (member_array(num, _current_ids) != -1) {
      num++;
   }
   _current_ids += ({ num });
   return sprintf("%c%c", (num / 26) + 'a',
                          (num % 26) + 'a');
} /* query_next_shop_id() */

/**
 * This method removes an id when that book has been taken from the
 * shop.
 * @param id the id to remove
 * @see query_next_shop_id()
 */
void remove_shop_id(string id) {
   int real_id;

   real_id = (id[0] * 26) - 'a' + id[1] - 'a';
   _current_ids -= ({ real_id });
} /* remove_shop_id() */

/**
 * This method searches the current sell list to see if the object
 * passed corresponds to any of the existing things in our
 * sell list.
 * @param ob the object to check
 * @return the craft_object class pointed to by the object
 * @see add_object()
 * @see remove_object()
 * @see create_real_object()
 */
class craft_object query_class_of_shop_object(object ob) {
   return ob->query_property(SELL_OBJECT_CLASS_PROP);
} /* query_class_of_shop_object() */

/**
 * This method figured out what the real object is for the class.
 * @param frog the class to find the object for
 * @return the dummy object for the class
 */
object find_shop_object(class craft_object frog) {
   object ob;

   foreach (ob in query_sell_list_obs()) {
      if (query_class_of_shop_object(ob) == frog) {
         return ob;
      }
   }
   return 0;
} /* find_shop_object() */

/**
 * This method changes the value of the shop object.
 * @param ob the shop object
 * @param value the new value
 */
void change_value_of_shop_object(object ob, int value) {
   string owner;
   string name;
   class craft_object wombat;
   object frog;

   owner = query_owner_of_shop_object(ob);
   name = query_name_of_shop_object(ob);
   foreach (wombat in ((class craft_sellable)_sellables[name])->objects[owner]) {
      wombat->cost = value;
      frog = find_shop_object(wombat);
      frog->set_value(value);
   }
   save_it();
} /* change_value_of_shop_object() */

/**
 * This method changes the name of the shop object.
 * @param ob the shop object to change
 * @param new_name the new name of the shop object
 */
int change_name_of_shop_object(object ob, string new_name) {
   string owner;
   string name;

   owner = query_owner_of_shop_object(ob);
   name = query_name_of_shop_object(ob);
   if (!_sellables[new_name] ||
       !((class craft_sellable)_sellables[new_name])->objects[owner]) {
      add_to_sellables(new_name, owner, ((class craft_sellable)_sellables[name])->objects[owner]);
      map_delete(((class craft_sellable)_sellables[name])->objects, owner);
      if (!sizeof(((class craft_sellable)_sellables[name])->objects)) {
         map_delete(_sellables, name);
      }
      update_sellable(name);
      save_it();
      return 1;
   }
   return 0;
} /* change_name_of_shop_object() */

/**
 * This method changes the category of the shop object.
 * @param ob the shop object to change
 * @param new_category the new category of the shop object
 */
int change_category_of_shop_object(object ob, string new_category) {
   string owner;
   string name;
   class craft_object bing;

   owner = query_owner_of_shop_object(ob);
   name = query_name_of_shop_object(ob);
   if (!_sellables[name] ||
       !((class craft_sellable)_sellables[name])->objects[owner]) {
      return 0;
   }
   foreach (bing in ((class craft_sellable)_sellables[name])->objects[owner]) {
      bing->category = new_category;
      save_it();
   }
   return 1;
} /* change_name_of_shop_object() */

/**
 * This method removes the real data behind the specified shop object.
 * The object passed in must be one of the dummy objects used for
 * listing the shops inventory.
 * @param ob the object to remove
 * @see sell_objects()
 * @see query_class_of_shop_object()
 */
void remove_shop_object(object ob) {
   string name;
   string owner;

   owner = query_owner_of_shop_object(ob);
   name = query_name_of_shop_object(ob);
   remove_list_object(name, owner, query_class_of_shop_object(ob));
} /* remove_shop_object() */

/**
 * This method finds the objects which matched the passed in pattern.
 * Note, this returns the dummy pointer objects not the actual
 * objects, they need to be changed into real objects before they
 * are really sold.
 * @param str the pattern to match the objects on
 * @return the matching objects
 * @see query_class_of_shop_object()
 * @see value_of_objects()
 * @see sell_objects()
 */
class obj_match find_matching_objects(string str) {
   class obj_match obs;

   obs = match_objects_in_environments(str, ({ _sell_list }));
   return obs;
} /* find_matching_objects() */

/**
 * This method determines the price of all the shop objects in the
 * array.
 * @return the price of the shop objects
 * @param obs the objects to price
 * @see find_matching_objects()
 * @see query_class_of_shop_object()
 * @see sell_objects()
 */
int value_of_objects(object *obs) {
   object ob;
   class craft_object craft;
   int value;

   foreach (ob in obs) {
      craft = query_class_of_shop_object(ob);
      if (craft) {
         value += craft->cost;
      }
   }
   return value;
} /* value_of_shop_objects() */

/**
 * This method sells the specified shop objects to the player.  This
 * will make the payments to the player.  The objects will all attempt
 * to be moved into the player.  The cut must be between 0 and 100.
 * <p>
 * The royalties from selling the object will be placed into the
 * correct place by the function, so they can be picked up at a 
 * later date.  The 'cut' will be taken out of the sale.
 * @param obs the objects to sell
 * @param place the place to sell the objects in
 * @param player the player to sell the objects too
 * @param cut the cut the manager takes from the sale (percentage)
 * @return the array of sold objects
 * @see value_of_objects()
 * @see find_matching_objects()
 * @see adjust_royalty()
 */
object *sell_objects(object *obs, string place, object player, int cut) {
   object ob;
   object this_ob;
   object *sold;
   class craft_object craft;

   sold = ({ });
   if (cut < 0) {
      cut = 0;
   }
   if (cut > 100) {
      cut = 100;
   }
   foreach (ob in obs) {
      craft = query_class_of_shop_object(ob);
      if (craft) {
         if (player->query_value_in(place) >= craft->cost) {
            // Create the object.
            this_ob = create_real_object(player, craft);
            if (this_ob->move(player) == MOVE_OK) {
               sold += ({ this_ob });
               // Make the player pay for it.
               player->pay_money(
                       MONEY_HAND->create_money_array(craft->cost, place),
                       place);
               // Adjust the players current royalties.
               adjust_royalty(query_owner_of_shop_object(ob),
                              craft->cost - (craft->cost * cut) / 100);
               // Remove the object from our inventory.
               remove_shop_object(ob);
            }
         }
      }
   }
   return sold;
} /* sell_objects() */


/**
 * This method deletes the specified list entry.  The player
 * owner will not be payed.
 * @param id The list entry to delete
 */
void delete_objects( string id ) {
   object ob, *obs;

   obs = all_inventory( _sell_list );

   obs = filter( obs,
         (: $1->query_property( SELL_OBJECT_ID_PROP ) == $(id) :) );
   
   foreach( ob in obs ) {
      remove_shop_object( ob );
   }
   
} /* delete_objects() */

/**
 * This method will buy the specified objects and place them into our
 * current potential sell list.  This will destroy the objects after
 * it has been added into the sell list correctly.
 * @param obs the object to sell
 * @param name the name of the object to sell
 * @param cost the cost of the object being sold
 * @param owner the person to who the object belongs
 * @param category the category of the object
 * @see sell_objects()
 */
object *buy_objects(object *obs,
                    string name,
                    int cost,
                    string owner,
                    string category) {
   object ob;
   object *bought;

   bought = ({ });
   foreach (ob in obs) {
      if (add_list_object(ob, name, cost, owner, category)) {
         ob->move("/room/rubbish");
         bought += ({ ob });
      }
   }
   return bought;
} /* buy_objects() */

/**
 * This method creates a dummy object for use when selling items.  This
 * allows use to use alias names for the potions when buying/selling
 * and not the real name of the container.  When bought these names
 * will be added to the thingy as aliases.
 * @param owner the owner of the object
 * @param name the sell list name of the object
 * @return a dummy object to place into the sell list
 */
protected object create_dummy_object(string owner, string name, int cost,
                                     class craft_object craft) {
   object new_name;
   string *bits;

   new_name = clone_object("/std/object");
   new_name->reset_get();
   bits = explode(lower_case(name), " ");
   new_name->set_name(bits[<1]);
   new_name->add_adjective(bits[0..<2]);
   if (member_array(lower_case(owner), bits) == -1) {
      new_name->add_adjective(owner);
   }
   if (!_category_dont_use_name &&
       member_array(lower_case(owner) + "'s", bits) == -1) {
      new_name->add_adjective(lower_case(owner) + "'s");
      new_name->set_short(craft->cap_owner + "'s " + name);
   } else {
      new_name->set_short(capitalize(name));
   }
   new_name->add_property(SELL_OBJECT_NAME_PROP, name);
   new_name->add_property(SELL_OBJECT_OWNER_PROP, owner);
   new_name->add_property(SELL_OBJECT_CLASS_PROP, craft);
   new_name->add_property("determinate", "");
   new_name->set_value(cost);
   return new_name;
} /* create_dummy_object() */

/**
 * This method adds an object into the current sell list.
 * @param ob the object to add
 */
private void add_sell_object(object ob) {
   if (ob->move(_sell_list) != MOVE_OK) {
      printf("Unable to move %O into the sell list (%O).\n", ob, _sell_list);
   }
} /* add_sell_object() */

/**
 * This method returns all the current objects in the sell list.
 * @return all the current objects in the sell list
 */
object *query_sell_list_obs() {
   return all_inventory(_sell_list);
} /* query_sell_list_obs() */

/**
 * This method returns the container associated with the sell list.
 * @return the container for the sell list
 */
object query_sell_list() {
   return _sell_list;
} /* query_sell_list() */

/**
 * This method updates the specified named set of sellables.  This
 * removes and creates the nessessary dummy objects needed to
 * handle the selling code.  It will also set the ids for objects
 * which need them, creating a new id if nessessary.
 * @param name the name of the sellables list to update
 * @see query_sell_list_obs()
 */
private void update_sellable(string name) {
   string owner;
   class craft_sellable bing;
   mixed *data;
   class craft_object womble;
   class craft_object new_womble;
   object shop_ob;
   string id;
   object *need_ids;
   int i;

   if (_sellables[name]) {
      bing = _sellables[name];
      foreach (owner, data in bing->objects) {
         id = 0;
         need_ids = ({ });
         foreach (womble in data) {
            if (sizeof(womble) == 3) {
               //
               // Make sure we zap the old shop object first.
               //
               shop_ob = find_shop_object(womble);
               if (shop_ob) {
                  shop_ob->dest_me();
               }
               new_womble = new(class craft_object);
               new_womble->cap_owner = womble->cap_owner;
               new_womble->cost = womble->cost;
               new_womble->ob_num = womble->ob_num;
               for (i = 0; i < sizeof(data); i++) {
                  if (data[i] == womble) {
                     data[i] = new_womble;
                  }
               }
               womble = new_womble;
            }
            shop_ob = find_shop_object(womble);
            if (!shop_ob ||
                query_name_of_shop_object(shop_ob) != name) {
               if (shop_ob) {
                  shop_ob->move("/room/rubbish");
               }
               add_sell_object(create_dummy_object(owner, name,
                                                   womble->cost, womble));
            }
            shop_ob = find_shop_object(womble);
            if (!id) {
               id = query_id_of_shop_object(shop_ob);
            }
            if (!query_id_of_shop_object(shop_ob)) {
               need_ids += ({ shop_ob });
            }
         }
         if (!id) {
            id = query_next_shop_id();
         }
         set_id_of_shop_objects(need_ids, id);
      }
   }
} /* update_sellables() */

/**
 * This method returns the list of sellables that the owner currently
 * has in the shop.  This will potentially return more than one of the
 * same name if the owner has more than one of the same sort of object
 * being sold.
 * @param owner the owner to check
 * @return the array of all the possible sellables
 * @see query_list_object_cost()
 */
string *query_owner_sellables(string owner) {
   class craft_sellable sell;
   mixed *womble;
   string *ret;
   string name;

   ret = ({ });
   foreach (name, sell in _sellables) {
      if (sell->objects[owner]) {
         womble = sell->objects[owner];
         ret += allocate(sizeof(womble), (: $(name) :) );
      }
   }
   return ret;
} /* query_owner_sellables() */

/**
 * This method returns the cost of the specified type of object.
 * @param name the name of the object
 * @param owner the owner of the object
 * @return the cost of the object, 0 if it is not known
 * @see query_owner_sellables()
 */
int query_list_object_cost(string name, string owner) {
   class craft_sellable sell;
   class craft_object wombat;

   owner = lower_case(owner);
   if (_sellables[name]) {
      sell = _sellables[name];
      if (sell->objects[owner]) {
         wombat = sell->objects[owner][0];
         return wombat->cost;
      }
   }
   return 0;
} /* query_list_object_cost() */

/**
 * This method sets the save function on the class.  The save function will
 * be called with two parameters, one is the file name to be saved and
 * the other is the text to be saved to it.
 * @param func the new save function
 */
void set_save_function(function func) {
   _save_function = func;
} /* set_save_function() */

/**
 * This method sets the load function on the class.  The restore function
 * will be called with one parameter and is expected to return a string.
 * The parameter is the file name to load.
 * @param func the new load function
 */
void set_load_function(function func) {
   _load_function = func;
} /* set_load_function() */

/**
 * This method is called when categories complete each of their phases to
 * see if the idea was passed by enough majority or whatever to go onto
 * the next one.
 * @param func the new category function
 */
void set_category_function(function func) {
   _category_function = func;
} /* set_category_function() */

/**
 * This method saves the current state of the system.
 * @see set_save_function()
 * @see load_it()
 */
void save_it() {
   if (_has_loaded) {
      evaluate(_save_function, CRAFT_SHOP_MAIN_SAVE_FILE,
            ({ _sellables, _royalties, _current_save_num, _categories }) );
   }
} /* save_it() */

/**
 * This method loads the current state of the system.
 * @see set_load_function()
 */
void load_it() {
   mixed *map;
   string name;

   _has_loaded = 1;
   map = evaluate(_load_function, CRAFT_SHOP_MAIN_SAVE_FILE);
   if (pointerp(map) && sizeof(map) == 3) {
      _sellables = map[0];
      _royalties = map[1];
      _current_save_num = map[2];
      foreach (name in keys(_sellables)) {
         reset_eval_cost();
         update_sellable(name);
      }
   } else if (pointerp(map) && sizeof(map) == 4) {
      _sellables = map[0];
      _royalties = map[1];
      _current_save_num = map[2];
      _categories = map[3];
      foreach (name in keys(_sellables)) {
         reset_eval_cost();
         update_sellable(name);
      }
   } else {
      _sellables = ([ ]);
      _royalties = ([ ]);
   }
   setup_timeout_call();
} /* load_it() */

/**
 * This method adjusts the current royalties for the player.
 * @param player the name of the player to pay the royalties to
 * @param amt the amount to adjust the value by
 * @see query_royalties()
 * @see pay_out_royalties()
 */
void adjust_royalty(string player, int amt) {
   _royalties[player] += amt;
   save_it();
} /* adjust_royalities() */

/**
 * This method returns the current royalties for the specified person.
 * @param player the player to get the royalties for
 * @return the current royalties
 * @see adjust_royalties()
 * @see pay_out_royalties()
 */
int query_royalty(string player) {
  return _royalties[player];
} /* query_royalties() */

/**
 * This method returns the royalty mapping on the object.  This is only
 * use for debug information.
 * @return the royalties mapping
 */
mapping query_all_royalties() {
   return copy(_royalties);
} /* query_all_royalties() */

/**
 * This method pays out the royalties owed to the specified player.
 * @param player the player to pay out
 * @param place the place in which the royalties are to occur
 * @see query_royalties()
 * @see adjust_royalties()
 */
void pay_out_royalty(string player, string place) {
   object pl;
 
   pl = find_player(player);
   if (pl && _royalties[player]) {
      pl->adjust_money(MONEY_HAND->create_money_array(_royalties[player],
                                                         place));
      map_delete(_royalties, player);
      save_it();
   }
} /* pay_out_royalties() */

//
//  All the category handling code goes after this point.
//

/**
 * This method checks to make sure that the category is valid.  This
 * means a category that has been properly accepted.
 * @param category the category to check
 */
int is_valid_category(string category) {
   if (_categories[category] &&
       (_categories[category]->state == CRAFT_CATEGORY_STATE_ACCEPTED ||
        _categories[category]->state == CRAFT_CATEGORY_STATE_DELETEING)) {
      return 1;
   }
   return 0;
} /* is_valid_category() */

/**
 * Add a request for a new category.
 * @param name the name of the category
 * @param player the person who suggested it
 * @param timeout the initial timeout for the nomination period
 * @return 1 if successful added, 0 if not
 */
int add_category_request(string name,
                         object player,
                         int timeout) {
   if (!_categories[name]) {
      _categories[name] = new(class craft_category);
      _categories[name]->state = CRAFT_CATEGORY_STATE_NOMINATING;
      _categories[name]->voted = ({ player->query_name() });
      _categories[name]->yes = 0;
      _categories[name]->no = 0;
      _categories[name]->timeout = time() + timeout;
      save_it();
      return 1;
   }
   return 0;
} /* add_category_request() */

/**
 * This method removes a category immediately.
 * @param name the name of the category
 */
int remove_category_accepted(string category) {
   if (_categories[category]) {
      map_delete(_categories, category);
      save_it();
      return 1;
   }
   return 0;
} /* remove_category_accepted() */

/**
 * This method adds a category and sets it as already being accepted.
 * @param name the name of the category to add
 */
int add_category_accepted(string name) {
   if (!_categories[name]) {
      _categories[name] = new(class craft_category);
      _categories[name]->state = CRAFT_CATEGORY_STATE_ACCEPTED;
      _categories[name]->voted = ({ });
      _categories[name]->yes = 0;
      _categories[name]->no = 0;
      _categories[name]->timeout = 0;
      save_it();
      return 1;
   }
   return 0;
} /* add_category_accepted() */

/**
 * This method returns the category associated with this name/category
 * set.
 */
string query_category_of(string name, string owner) {
   if (_sellables[name] &&
       sizeof( ((class craft_sellable)_sellables[name])->objects[owner])) {
      return ((class craft_sellable)_sellables[name])->objects[owner][0]->category;
   }
   return 0;
} /* query_category_of() */

/**
 * This method checks to see if the player has voted (or nominated) for a
 * specified category already.
 * @param name the name of the category
 * @param player the player being checked
 * @return 1 if they have voted/nominated 0 if they have not
 */
int has_voted_for_category(string name,
                           object player) {
   return _categories[name] &&
       member_array(player->query_name(), _categories[name]->voted) != -1;
} /* has_player_voted() */

/**
 * This method returns the time the current category has left before it
 * times out.
 * @param name the name of the cateogyr
 * @return the time it timesout
 */
int query_category_timeout(string name) {
   if (_categories[name]) {
      return _categories[name]->timeout;
   }
   return CRAFT_CATEGORY_ERROR;
} /* query_category_timeout() */

/**
 * This method allows the player to vote for the specified category.
 * @param name the name of the category
 * @param player the player voting
 * @param choice their choice
 * @return 1 if the vote was successful, 0 if not
 */
int vote_for_category(string name,
                      object player,
                      int choice) {
   if (_categories[name]) {
      if (!has_voted_for_category(name, player)) {
         if (choice == CRAFT_CATEGORY_CHOICE_YES ||
             choice == CRAFT_CATEGORY_CHOICE_NO ||
             choice == CRAFT_CATEGORY_CHOICE_ABSTAIN) {
            switch (choice) {
            case CRAFT_CATEGORY_CHOICE_YES :
               _categories[name]->yes++;
               break;
            case CRAFT_CATEGORY_CHOICE_NO :
               _categories[name]->no++;
               break;
            case CRAFT_CATEGORY_CHOICE_ABSTAIN :
               _categories[name]->abstain++;
               break;
            }
            _categories[name]->voted += ({ player->query_name() });
            save_it();
         }
      }
   }
   return 0;
} /* vote_for_category() */

/**
 * This method returns the list of categories for the current shop.
 * @return the list of categories
 */
string* query_categories() {
   return filter(keys(_categories), (: is_valid_category($1) :));
} /* query_categories() */

/**
 * This method is used to go through the categories and see which ones
 * need to timeout.
 */
private void do_timeout_categories() {
   string name;
   class craft_category cat;
   int timeout;

   foreach (name, cat in _categories) {
      if (cat->timeout &&
          cat->timeout < time()) {
         //
         // Timed out!
         //
         timeout = evaluate(_category_function,
                            cat->state,
                            cat->voted,
                            cat->yes,
                            cat->no,
                            cat->abstain);
         if (timeout) {
            //
            // Ok, do something groovy based on the state.
            //
            cat->voted = ({ });
            cat->yes = 0;
            cat->no = 0;
            switch (cat->state) {
            case CRAFT_CATEGORY_STATE_NOMINATING :
               cat->state = CRAFT_CATEGORY_STATE_VOTING;
               cat->timeout = time() + timeout;
               break;
            case CRAFT_CATEGORY_STATE_VOTING :
               cat->state = CRAFT_CATEGORY_STATE_ACCEPTED;
               cat->timeout = 0;
               break;
            case CRAFT_CATEGORY_STATE_DELETEING :
               map_delete(_categories, name);
               break;
            }
         } else {
            switch (cat->state) {
            case CRAFT_CATEGORY_STATE_VOTING :
            case CRAFT_CATEGORY_STATE_NOMINATING :
               map_delete(_categories, name);
               break;
            case CRAFT_CATEGORY_STATE_DELETEING :
               cat->state = CRAFT_CATEGORY_STATE_ACCEPTED;
               cat->timeout = 0;
               cat->voted = ({ });
               cat->yes = 0;
               cat->no = 0;
               break;
            }
         }
      }
   }
   save_it();
   setup_timeout_call();
} /* do_timeout_categories() */

/**
 * This method sets up a timeout call based on the next thing to timeout.
 */
private void setup_timeout_call() {
   string name;
   class craft_category cat;
   int bing;

   foreach (name, cat in _categories) {
      if (cat->timeout != 0 &&
          cat->timeout < bing) {
         bing = cat->timeout;
      }
   }
   if (_category_callout) {
      remove_call_out(_category_callout);
   }
   if (bing) {
      _category_callout = call_out((: do_timeout_categories() :), bing);
   }
} /* setup_timeout_call() */

/** @ignore yes */
void dest_me() {
  if(_sell_list) {
    all_inventory(_sell_list)->move("/room/rubbish");
    _sell_list->dest_me();
  }
  destruct(this_object());
} /* dest_me() */