dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
/**
 * This is the inheritable bit of the pub.  The actual file you should
 * inherit is /std/shops/pub_shop.c
 * <p>
 * Based on the original "pub.c" code, this version allows you to buy an
 * actual drink (inna glass) instead of just forcefeeding you with whatever
 * you purchase.  You can also create food and serve it on plates, in boxes,
 * or whatever.
 * <p>
 * The items on sale are either cloned from the armoury, cloned from a file
 * or cloned in the "create_item()" code in the pub code itself.  Containers
 * for these items are done in the same way, using the "create_container()"
 * function.  This is the same as the "create_object()" system in
 * "item_shop.c".  You can also buy items for other people (or groups of
 * other people) in the pub.
 * <p>
 * There are several standard glasses and plates available for drinks and
 * food in the "pub_shop.h" header file, if you don't want to create the
 * glasses and plates yourself.  These plates use the "/obj/misc/plate.c"
 * inheritable file.
 * <p>
 * A "counter" will automatically be placed in the pub.  If any item cannot
 * be moved into the person who ordered it (i.e they are carrying too much )
 * it will be placed on the counter.  Empty glasses and so on will be
 * cleared from the counter after 3 seconds.  To ensure that this happens,
 * make sure that the container object you use has the property "pub item"
 * added to it.  Items will only be cleared if they are empty.
 * @author Lemming
 * @started 23/10/1999
 * @see /std/shops/pub_shop.c
 * @see /include/shops/pub_shop.h
 * @see /include/volumes.h
 */

#include <shops/pub_shop.h>
#include <armoury.h>
#include <money.h>
#include <move_failures.h>
#include <map.h>

inherit "/std/shops/inherit/open_shop";
inherit "/std/shops/inherit/shop_event";

//
// Predefinitions needed from room.c
//
void add_hidden_object(object ob);

private mapping _menu_items;
private mapping _menu_aliases;
private string _menu_header;
private string _language;
private int _display_header;
private string *_menu_subheadings;
private int _display_subheadings;
private int no_standard_alias;
private object _menu_object;
private object _counter;

void add_menu_alias( mixed alias, string actual );
string *calc_standard_aliases( string *aliases );
int do_buy( object *obs, string dir, string indir, mixed *args );
float query_discount( object ob );
object make_counter();

/** @ignore */
void create() {
   shop_event::create();
   _menu_items = ([ ]);
   _menu_aliases = ([ ]);

   _menu_header = "The menu reads:";
   _display_header = 1;

   _menu_subheadings = ({ "Appetisers", "Main Courses", "Desserts",
                         "Alcoholic Beverages", "Soft Drinks",
                         "Hot Drinks" });
   _display_subheadings = 1;

   if( base_name( this_object() ) + ".c" != __FILE__ ) {
      _menu_object = clone_object( PUB_MENU_FILE );
      _menu_object->set_pub( this_object() );
      add_hidden_object( _menu_object );

      _counter = make_counter();
   }
} /* create() */

/** @ignore */
void init() {
   this_player()->add_command( "buy", this_object(), "<string>" );
   this_player()->add_command( "buy", this_object(),
                     "<string> for <indirect:living:here>" );
   this_player()->add_command( "order", this_object(), "<string>",
                     (: do_buy( $1, $2, $3, $4 ) :) );
   this_player()->add_command( "order", this_object(),
                     "<string> for <indirect:living:here>",
                     (: do_buy( $1, $2, $3, $4 ) :) );
} /* init() */

/**
 * This is the method used to add items to the menu.  Only the first four
 * parameters are required, the rest are optional.
 * <p>
 * The "type" parameter is used to determine which section of the menu the
 * item should reside in, and should be selected from those in the
 * "pub_shop.h" header file.
 * <p>
 * The "item" parameter is used to generate the actual product on sale.  This
 * value can be:
 * <ul>
 * <li> a name, which is passed to the "create_item()" function in the room code
 * <li> a filename, which is cloned, or
 * <li> an armoury identifier, which is passed to the armoury handler.
 * </ul>
 * <p>
 * The "container" parameter is the same as the "item" parameter, except that
 * it refers to the container the item comes in and is passed to
 * "create_container()" instead of "create_item()".  You can also use the
 * standard containers listed in the "pub_shop.h" header file.  It is
 * optional, and setting it to 0 will cause it to be ignored.
 * <p>
 * The "volume" parameter is optional and is passed directly to
 * "set_amount()" on the object cloned from "item".  It can be used to alter
 * the volume of a liquid cloned from a file, so the file itself does not
 * have to be changed.  It is optional, and setting it to 0 will cause it to
 * be ignored.  If this is set to 0 then the item will fill up the
 * container.  Standard volume definitions can be found in the "volumes.h"
 * header file.
 * <p>
 * The last parameter, "intox", is optional and is used only by NPCs to
 * determine how alcoholic an item is.  This should be between 0 and 10,
 * with 0 being non-alcoholic and 10 being something like Suicider.  It 
 * defaults to 0.  Note:  This has no effect on the actual alcohol content
 * of the item.
 * <p>
 * The different volumes of standard containers can be taken from
 * /include/volumes.h which has defines for all the standard volumes.
 * @param name the description to be displayed on the menu
 * @param type the type of item
 * @param cost the cost of the item
 * @param item the name, filename or armoury identifier for the item itself
 * @param container the name, filename or armoury identifier for the
 * container (optional)
 * @param volume the volume that the item should be set to (optional)
 * @param intox the intoxification value, on a scale of 0 to 10 (optional)
 * @example
 * // Add a main course called "Big meat pie", cloned from the file
 * // "/obj/food/meatpie.food"
 * add_menu_item( "Big meat pie", PUB_MAINCOURSE, 1000,
 *    "/obj/food/meatpie.food" );
 * @example
 * // Same as above, but let's put the pie on a plate
 * add_menu_item( "Big meat pie", PUB_MAINCOURSE, 1000,
 *    "/obj/food/meatpie.food", PUB_STD_PLATE );
 * @example
 * // Add a glass of ale, with the ale cloned from a file and the glass
 * // cloned in the "create_container()" function in the room code
 * add_menu_item( "Pint of ale", PUB_ALCOHOL, 500, "/obj/food/ale.food",
 *    "new_pint_glass" );
 * @example
 * // The same as above, but we only want half a pint of ale in the glass, we
 * // want to use the standard glasses in the header file and we want to set
 * // the intoxification value of the ale to 5
 * add_menu_item( "Half-pint of ale in a pint glass", PUB_ALCOHOL, 300,
 *    "/obj/food/ale.food", PUB_STD_PINT, VOLUME_HALFPINT, 5 );
 * @example
 * // Create a beefburger with added vodka in the "create_item()" function in
 * // the room code and put it in a small satchel from the armoury
 * add_menu_item( "Beefburger with special sauce", PUB_MAINCOURSE, 800,
 *    "vodka_burger", "small satchel", 0, 7 );
 * @see query_menu_items()
 * @see remove_menu_item()
 * @see /include/volumes.h
 */
varargs void add_menu_item(
   string name,
   int type,
   int cost,
   string item,
   string container,
   int volume,
   int intox
) {
   class menu_item new_item;
   string noun, alias;
   string *adjectives, *aliases;

   if( intox < 0 )
      intox = 0;
   if( intox > 10 )
      intox = 10;

   new_item = new( class menu_item );
   new_item->type = type;
   new_item->cost = cost;
   new_item->item = item;
   new_item->container = container;
   new_item->volume = volume;
   new_item->intox = intox;
   _menu_items[name] = new_item;

   if( no_standard_alias ) {
      if( lower_case( name ) != name )
         add_menu_alias( lower_case( name ), name );
      return;
   }

   adjectives = explode( lower_case( name ), " " );
   noun = adjectives[sizeof(adjectives) - 1];
   adjectives = adjectives[0..sizeof(adjectives) - 2];

   aliases = calc_standard_aliases( adjectives );

   foreach( alias in aliases ) {
      add_menu_alias( implode( ({ alias, noun }), " " ), name );
   }
} /* add_menu_item() */

/** @ignore */
string *calc_standard_aliases( string *array ) {
   int i, num_aliases;
   string *new_alias, *aliases;

   if( !sizeof( array ) )
      return ({ 0 });

   if( sizeof( array ) == 1 )
      return ({ array[0], 0 });

   aliases = calc_standard_aliases( array[0..sizeof( array ) - 2] );

   num_aliases = sizeof( aliases );

   for( i = 0; i < num_aliases; i++ ) {
      new_alias = ({ aliases[i], array[ sizeof( array ) - 1] });
      aliases += ({ implode( new_alias, " " ) });
   }

   return aliases;
} /* calc_standard_aliases() */

/**
 * This method returns a list of all the items currently available in
 * the pub.
 * @return items available, listing type, price and intoxification value
 * @see add_menu_item()
 * @see remove_menu_item()
 */
mapping query_menu_items() {
   return _menu_items;
} /* query_menu_items() */

/**
 * This method checks to see if this is a pub.
 * @return always return 1
 */
int query_pub() {
   return 1;
} /* query_pub() */

/**
 * This method sets the language to use in the shop.
 * @param language the language to use
 */
void set_language(string language) {
   _language = language;
} /* set_language() */

/**
 * This method returns the language used in the shop.
 * @return the language used in the shop
 */
string query_language() {
   return _language;
} /* query_language() */

/**
 * This method allows you to remove an item from those currently available
 * in the pub.
 * @param name the name of the item to remove
 * @return 1 if successful, 0 if unsuccessful
 * @see add_menu_item()
 * @see query_menu_items()
 */
int remove_menu_item( string name ) {
   if( !_menu_items[name] ) {
      return 0;
   }

   map_delete( _menu_items, name );
   return 1;
} /* remove_menu_item() */

/** @ignore */
string string_menu( string *items ) {
   int loop;
   string str, place;

   str = ""; 
   place = this_object()->query_property( "place" );
   if( !place || ( place == "" ) ) {
      place = "default";
   }

   for( loop = 0; loop < sizeof(items); loop++ ) {
      str += sprintf( "    %-30s %s\n", items[loop],
             MONEY_HAND->money_value_string(
             _menu_items[items[loop]]->cost, place ) );
   }

   return str;
} /* string_menu() */

/** @ignore */
string *query_items_of_type( int type ) {
   int i;
   string *selected;
   string *items;

   selected = ({ });
   items = keys( _menu_items );

   for( i = 0; i < sizeof(items); i++ ) {
      if( _menu_items[items[i]]->type == type ) {
         selected += ({ items[i] });
      }
   }

   selected = sort_array( selected, (: _menu_items[$1]->cost -
                                       _menu_items[$2]->cost :) );

   return selected;
} /* query_items_of_type() */

/** @ignore */
string string_menu_of_type( int type ) {
   string str;
   string *items;

   items = query_items_of_type( type );
   if( !sizeof(items) ) {
      return "";
   }

   if( _display_subheadings ) {
      str = _menu_subheadings[ type ] + "\n";
   }

   str += string_menu( items );
   return sprintf( "%-=*s\n", (int)this_player()->query_cols(), str );
} /* string_menu_of_type() */

/**
 * This method produces the menu from the item information, with the menu
 * header at the top, all items available grouped by type and sorted by
 * cost.  If you don't want the menu printed this way then mask this
 * function and return your own.
 * @return the menu text
 * @see add_menu_item()
 * @see set_display_header()
 * @see set_menu_header()
 * @see set_display_subheadings()
 * @see set_menu_subheadings()
 */
string read() {
   string ret;

   ret = "\n";

   if( _display_header ) {
      ret += _menu_header + "\n";
   }

   ret += string_menu_of_type( PUB_APPETISER );
   ret += string_menu_of_type( PUB_MAINCOURSE );
   ret += string_menu_of_type( PUB_DESSERT );
   ret += string_menu_of_type( PUB_ALCOHOL );
   ret += string_menu_of_type( PUB_SOFTDRINK );
   ret += string_menu_of_type( PUB_HOTDRINK );

   return ret;
} /* read() */

/**
 * This method allows you to switch the menu header (defaults to "The menu
 * reads:") that appears at the top of the menu on and off.
 * @param value set to 1 to display header or 0 to remove them
 * @see query_display_header()
 * @see set_menu_header()
 * @see query_menu_header()
 */
void set_display_header( int value ) {
   _display_header = value;
} /* set_display_header() */

/**
 * This method returns a flag stating whether display of the menu header
 * is on or off.
 * @return 1 for header, 0 for no header
 * @see set_display_header()
 * @see set_menu_header()
 * @see query_menu_header()
 */
int query_display_header() {
   return _display_header;
} /* query_display_header() */

/**
 * This method sets the header that appears at the top of the menu.  By
 * default this is "The menu reads:".
 * @param header the new menu header
 * @see query_menu_header()
 * @see set_display_header()
 * @see query_display_header()
 */
void set_menu_header( string header ) {
   _menu_header = header;
} /* set_menu_header() */
 
/**
 * This method returns the current menu header text.
 * @return the menu header text
 * @see set_menu_header()
 * @see set_display_header()
 * @see query_display_header()
 */
string query_menu_header() {
   return _menu_header;
} /* query_menu_header() */

/**
 * This method allows you to switch the subheadings ("Alcoholic Beverages",
 * "Meals", etc) that appear above different types of items on and off.
 * @param value set to 1 to display subheadings or 0 to remove them
 * @see query_display_subheadings()
 * @see set_menu_subheadings()
 * @see query_menu_subheadings()
 */
void set_display_subheadings( int value ) {
   _display_subheadings = value;
} /* set_display_subheadings() */

/**
 * This method returns a flag stating whether display of the menu subheadings
 * is on or off.
 * @return 1 for headings, 0 for no headings
 * @see set_display_subheadings()
 * @see set_menu_subheadings()
 * @see query_menu_subheadings()
 */
int query_display_subheadings() {
   return _display_subheadings;
} /* query_display_subheadings() */

/**
 * This method sets the subheadings that appear at the top of the menu.
 * @param subheading the subheading to change (use the #defines listed in
 * "pub_shop.h")
 * @param text the new menu subheading text
 * @see query_menu_subheadings()
 * @see set_display_subheadings()
 * @see query_display_subheadings()
 */
void set_menu_subheadings( int subheading, string text ) {
   _menu_subheadings[ subheading ] = text;
} /* set_menu_subheadings() */
 
/**
 * This method returns the current menu subheading text.
 * @return the menu subheadings
 * @see set_menu_subheadings()
 * @see set_display_subheadings()
 * @see query_display_subheadings()
 */
string *query_menu_subheadings() {
   return _menu_subheadings;
} /* query_menu_subheadings() */
 
/**
 * This method allows you to add an alias to an item sold in the pub.  Many
 * aliases are added by default so you shouldn't have to use this too
 * often.  See the help on "set_no_standard_alias()" to see what aliases are
 * added automatically.
 * @param alias the alias to add
 * @param alias the real item that the alias refers to
 * @example
 * // Allow "buy lancre ale" instead of "buy Ale from Lancre"
 * add_menu_alias( "lancre ale", "Ale from Lancre" );
 * @see add_menu_aliases()
 * @see query_menu_aliases()
 * @see remove_menu_alias()
 * @see set_no_standard_alias()
 */
void add_menu_alias( mixed alias, string actual ) {
   string bing;

   if (arrayp(alias)) {
      foreach (bing in alias) {
         add_menu_alias(bing, actual);
      }
   }
   _menu_aliases[alias] = actual;
} /* add_menu_alias() */

/**
 * This method allows you to add multiple aliases at once.  Many aliases are
 * added by default so you shouldn't have to use this too often.  See the 
 * help on "set_no_standard_alias()" to see what aliases are added 
 * automatically.
 * @param aliases an array of aliases to add
 * @param alias the real item that the aliases refer to
 * @example
 * // Add friendly aliases to "Beef burger and chips"
 * add_menu_aliases( ({ "beef burger",
 *                      "beefburger",
                        "burger" }), "Beef burger and chips" );
 * @see add_menu_alias()
 * @see query_menu_aliases()
 * @see remove_menu_alias()
 * @see set_no_standard_alias()
 */
void add_menu_aliases( string *aliases, string actual ) {
   string alias;

   foreach( alias in aliases ) {
      add_menu_alias( alias, actual );
   }
} /* add_menu_aliases() */

/**
 * This method returns a list of all the aliases currently available in
 * the pub.
 * @return alias : real name
 * @see add_menu_alias()
 * @see add_menu_aliases()
 * @see remove_menu_alias()
 * @see set_no_standard_alias()
 */
mapping query_menu_aliases() {
   return _menu_aliases;
} /* query_menu_aliases() */

/**
 * This method allows you to remove an alias from those currently available
 * in the pub.
 * @param alias the alias to remove from the list
 * @return 1 if successful, 0 if unsuccessful
 * @see add_menu_alias()
 * @see add_menu_aliases()
 * @see query_menu_aliases()
 * @see set_no_standard_alias()
 */
int remove_menu_alias( string alias ) {
   if( !_menu_aliases[alias] ) {
      return 0;
   }

   map_delete( _menu_aliases, alias );
   return 1;
} /* remove_menu_alias() */

/**
 * This method allows you to turn on or off the addition of standard aliases
 * when new menu items are added.  By default it is turned on.  Standard
 * aliases are added as follows:
 * If you added an item called "Lancre vintage wine" the aliases added would
 * be:
 * <ul>
 * <li> vintage wine
 * <li> lancre wine
 * <li> wine
 * </ul>
 * An alias of the name in lowercase is always added regardless of whether
 * or not this flag is turned on or off.  You may wish to turn this off if
 * you are adding several items which could be mistaken for each other, for
 * instance "Lancre beer" and "Morporkian beer".
 * @param flag 0 if standard aliases should be added, 1 if not
 * @see add_menu_alias()
 * @see query_menu_aliases()
 * @see remove_alias()
 * @see query_no_standard_alias()
 */
void set_no_standard_alias( int flag ) {
   no_standard_alias = flag;
} /* set_no_standard_alias() */

/**
 * This method returns a flag stating whether standard aliases will be added
 * or not.
 * @return 0 if standard aliases will be added, 1 if not
 * @see set_no_standard_alias()
 */
int query_no_standard_alias() {
   return no_standard_alias;
} /* set_no_standard_alias() */

/** @ignore */
object create_real_object( string name ) {
   object item, container;

   if( _menu_items[name]->container ) {
      container = this_object()->create_container(
                                    _menu_items[name]->container );
      if( !container ) {
         container = clone_object( _menu_items[name]->container );
      }
      if( !container ) {
         container = ARMOURY->request_item(
                                 _menu_items[name]->container, 100 );
      }
   }

   if( _menu_items[name]->item ) {
      item = this_object()->create_item( _menu_items[name]->item );
      if( !item ) {
         item = clone_object( _menu_items[name]->item );
      }
      if( !item ) {
         item = ARMOURY->request_item( _menu_items[name]->item, 100 );
      }
   }

   if( item && _menu_items[name]->volume ) {
      item->set_amount( _menu_items[name]->volume );
   } else if ( item && !_menu_items[name]->volume &&
              ( _menu_items[name]->type == PUB_ALCOHOL ||
                _menu_items[name]->type == PUB_HOTDRINK ||
                _menu_items[name]->type == PUB_SOFTDRINK ) ) {
      item->set_amount( container->query_max_volume() -
                        container->query_volume() );
   }

   if( item && container ) {
      if( (int)item->move( container ) != MOVE_OK ) {
         write( "The " + container->short() + " is too small to hold " +
            item->the_short() + ".  Please file a bug report.\n" );
         item->move( "/room/rubbish" );
      }
   }

   if( container ) {
      return container;
   }

   if( item ) {
      return item;
   }

   return 0;
} /* create_real_object() */

/** @ignore */
int do_buy( object *obs, string dir, string indir, mixed *args ) {
   int value, cost;
   string str, place;
   object person, thing;
   object *succeededpeople, *deadpeople, *failedpeople, *poorpeople;

   succeededpeople = ({ });
   deadpeople = ({ });
   failedpeople = ({ });
   poorpeople = ({ });

   str = args[0];

   if( this_player()->query_property( "dead" ) ) {
      add_failed_mess( "How can you expect to buy " + str + " when you're "
         "dead?\n" );
      return 0;
   }

   if( _menu_aliases[str] ) {
      str = _menu_aliases[str];
   }

   if( !_menu_items[str] ) {
      if (!broadcast_shop_event(PUB_EVENT_NOT_AVAILABLE, this_player(), str)) {
         add_failed_mess( "Sorry, " + str + " is not on the menu.\n" );
      }
      return 0;
   }

   // Check to make sure the pub is open.
   if ( !is_open( this_player(), _menu_items[str]->type ) ) {
      broadcast_shop_event(PUB_EVENT_NOT_OPEN, this_player());
      return 0;
   }

   if( !sizeof( obs ) ) {
      obs = ({ this_player() });
   }

   foreach( person in obs ) {
      if( person->query_property( "dead" ) ) {
         deadpeople += ({ person });
         continue;
      }

      if( !living( person ) ||
          !interactive( person ) && !person->query_property( "npc" ) ) {
         failedpeople += ({ person });
         continue;
      }

      cost = ( _menu_items[str]->cost ) * query_discount( this_player() );
      place = this_object()->query_property( "place" );
      if( !place || ( place == "" ) ) {
         place = "default";
      }
      value = (int)this_player()->query_value_in( place );
      if( place != "default" ) {
         value += (int)this_player()->query_value_in( "default" );
      }

      if( cost > value ) {
         poorpeople += ({ person });
         continue;
      }

      thing = create_real_object( str );
      if( !thing ) {
         add_failed_mess( "Something is buggered.  Please file a bug report.  "
            "Thank you.\n" );
         return 0;
      }

      this_player()->pay_money( (mixed *)MONEY_HAND->create_money_array(
                        cost, place ), place );

      succeededpeople += ({ person });

      if( (int)thing->move( person ) != MOVE_OK ) {
         if( (int)thing->move( _counter ) != MOVE_OK ) {
            thing->move( this_object() );
            write("You cannot pick " + thing->a_short() +
               " up.  It's left on the floor for you.\n" );
         } else {
           write("You cannot pick " + thing->a_short() +
              " up.  It's left on the counter for you.\n" );
         }
      }
   }

   /* This will be overidden if necessary */
   //add_succeeded_mess( this_object(), "", ({ }) );

   if( sizeof( succeededpeople ) ) {
      // Quite why compare_arrays() isn't an efun is beyond me...
      if (!broadcast_shop_event(PUB_EVENT_BOUGHT_STUFF, this_player(),
                           succeededpeople, str)) {
         if( !MAP_HANDLER->compare_arrays( succeededpeople,
                                           ({ this_player() }) ) ) {
            this_player()->add_succeeded_mess( this_object(),
               "$N $V $I.\n", ({ add_a(str) }) );
         } else {
            add_succeeded_mess("$N $V "
               + query_multiple_short( ({ thing }), "a" ) + " for $I.\n",
               succeededpeople);
         }
      }
   } else {
      if( sizeof( deadpeople ) ) {
         if (!broadcast_shop_event(PUB_EVENT_DEAD_PEOPLE, this_player(),
                              deadpeople, str)) {
            add_failed_mess("What use " +
               ( sizeof( deadpeople ) > 1?"have ":"has " ) +
               query_multiple_short( deadpeople, "one" ) + " got for " + str +
               "?\n" );
         }
      }
   
      if( sizeof( failedpeople ) ) {
         if (!broadcast_shop_event(PUB_EVENT_FAILED_PEOPLE, this_player(),
                              failedpeople, str)) {
            add_failed_mess("You can't buy anything for " +
               query_multiple_short( failedpeople, "one" ) + ".\n" );
         }
      }
   
      if( sizeof( poorpeople ) ) {
         if (!broadcast_shop_event(PUB_EVENT_POOR_PERSON, this_player(),
                              poorpeople, str)) {
            add_failed_mess("You cannot afford to order " + str +
               " for " + query_multiple_short( poorpeople, "one" ) + ".\n" );
         }
      }
      return 0;
   }
   
   return 1;
} /* do_buy() */

/** @ignore */
void dest_me() {
   if( _menu_object ) {
      _menu_object->dest_me();
   }
} /* dest_me() */

/** 
 * This function can be masked and used to determine a discount that is
 * applied to all items sold, so for instance you could check the guild of
 * 'ob' and give a discount to Witches, or something.  By default the
 * discount is zero, so query_discount returns 1.0
 * @param ob the object doing the buying
 * @return a float to multiply the price by
 * @example
 * // Give Priests a 10% discount
 * float query_discount( object ob ) {
 *    if( ob->query_guild_ob() == "/std/guilds/priest.c" )
 *       return 0.9;
 *    else
 *       return 1.0;
 * }
 */
float query_discount( object ob ) {
   return 1.0;
} /* query_discount() */

/**
 * This function creates the counter for the pub.  It defaults to cloning
 * PUB_COUNTER_FILE and making it a hidden object.
 * If you are creating your own counter object then making it hidden is a
 * good idea.  If it's not hidden, make sure it at least cannot be moved or
 * buried.
 * The counter should clear empty objects with the "pub item" property if
 * they are placed on it.  Making it clear non-empty objects is a bad idea,
 * since items will be placed here if the purchaser is unable to carry
 * them.
 * @return the new counter object
 * @see query_counter()
 * @see /include/shops/pub_shop.h
 */
object make_counter() {
   object ob;

   ob = clone_object( PUB_COUNTER_FILE );
   add_hidden_object( ob );

   return ob;
} /* make_counter() */

/**
 * This method returns the object currently being used as a counter in the
 * pub.
 * @return the file name of the counter
 * @see make_counter()
 */
object query_counter() {
   return _counter;
} /* query_counter() */


/**
 * This method returns the object currently being used as a menu in the
 * pub.
 * @return the file name of the menu
 */
object query_menu() {
   return _menu_object;
} /* query_menu() */