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 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 <move_failures.h>

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

private nosave function _open_function;

private mapping _menu_items;
private mapping _menu_aliases;
private string _menu_header;
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( string 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 );
int check_open( object player, int type );

/** @ignore */
void 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( TO ) + ".c" != __FILE__ ) {
       _menu_object = clone_object( PUB_MENU_FILE );
       _menu_object->set_pub( TO );
       add_hidden_object( _menu_object );
       _counter = clone_object( PUB_COUNTER_FILE );
       add_hidden_object( _counter );
   }
} /* create() */

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

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

/**
 * 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 ) {
   string str, place, tmp;

   str = "";

   if( !( place = TO->query_property( "place" ) ) || place == "" )
       place = "default";

   foreach( tmp in items ) {
      str += sprintf( "    %-30s %s\n", tmp,
             MONEY_H->money_value_string(
             _menu_items[tmp]->cost, place ) );
   }

   return str;

} /* string_menu() */

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

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

   foreach( tmp in items )
      if( _menu_items[tmp]->type == type )
          selected += ({ tmp });

   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, *items;

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

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

   str += string_menu( items );
   return sprintf( "%-=*s\n", (int)TP->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; }

/**
 * 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; }

/**
 * 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; }

/**
 * 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; }

/**
 * 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; }

/**
 * 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; }

/**
 * 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; }

/**
 * 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( string alias, string 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; }

/**
 * 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; }

/**
 * 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; }

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

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

   if( _menu_items[name]->item ) {
       item = TO->create_item( _menu_items[name]->item );
       if( !item )
           item = clone_object( _menu_items[name]->item );
       if( !item )
           item = ARMOURY_H->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( container->the_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;

   str = args[0];

   if( TP->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] ) {
       add_failed_mess("Sorry, "+str+" is not on the menu.\n");
       return 0;
   }

   // Check to make sure the pub is open.
   if( !check_open( TP, _menu_items[str]->type ) )
       return 0;

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

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

   place = TO->query_property( "place" );
   if( !place || place == "" )
       place = "default";

   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( TP );

      value = (int)TP->query_value_in( place );
      if( place != "default" )
          value += (int)TP->query_value_in( "default" );

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

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

      TP->pay_money( (mixed)MONEY_H->create_money_array(
                     cost, place ), place );

      succeededpeople += ({ person });

      if( (int)thing->move( person ) != MOVE_OK ) {
          if( (int)thing->move( _counter ) != MOVE_OK ) {
              thing->move( TO );
              tell_object( person, "You cannot pick "+thing->a_short()+" up.  "
                  "It's left on the floor for you.\n" );
          } else {
              tell_object( person, "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("");

   if( sizeof( succeededpeople ) ) {
      if( !MAP_H->compare_arrays( succeededpeople, ({ TP }) ) ) {
          add_succeeded_mess("$N $V "+add_a(str)+".\n");
      } else {
          tell_object( TP, "You buy "+add_a(str)+" for "+
              query_multiple_short( ( member_array( TP,
              succeededpeople ) != -1 ? succeededpeople - ({ TP }) +
              ({"yourself"}) : succeededpeople ), "one" ) + ".\n");
          tell_room( TO, TP->one_short()+" buys "+add_a(str)+" for "+
              query_multiple_short( ( member_array( TP,
              succeededpeople ) != -1 ? succeededpeople - ({ TP }) +
              ({ TP->HIM+"self" }) : succeededpeople ), "one" ) + ".\n",
              ({ TP }) );
      }
   }

   if( sizeof( deadpeople ) ) {
       tell_object( TP, "What use "+( sizeof( deadpeople ) > 1 ?
           "have" : "has")+" "+query_multiple_short( deadpeople,
           "one")+" got for "+add_a(str)+"?\n");
   }

   if( sizeof( failedpeople ) ) {
       tell_object( TP, "You can't buy anything for "+
           query_multiple_short( failedpeople, "one")+".\n");
   }

   if( sizeof( poorpeople ) ) {
       tell_object( TP, "You cannot afford to order "+add_a(str)+" for "+
           query_multiple_short( ( member_array( TP, poorpeople ) != -1 ?
           poorpeople - ({ TP }) + ({"yourself"}) : poorpeople ),
           "one")+".\n");
   }

   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 age of
 * 'ob' and give a discount to newbies, 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 people under 1 day of age a 10% discount.
 * float query_discount( object ob ) {
 *    if( -( ob->query_time_on() ) < 60*60*24 )
 *        return 0.9;
 *    else
 *        return 1.0;
 * }
 */
float query_discount( object ob ) {
   return 1.0;
} /* query_discount() */

/**
 * This method sets the function to be called to check to see if the pub
 * is open.  If this is not set then the pub is always assumed to be
 * open.  The function returns a non-zero value then the pub is considered
 * closed, if the function returns a value other than 1 then the default
 * fail message will not be added.  The function will be passed two
 * parameters - the customer and the type of item being bought (as defined
 * in the "pub_shop.h" header file).
 * @param func the function to use for open tests
 * @see query_open_function()
 * @see check_open()
 * @example
 * // The pub is only open if the barman is alive.
 * set_open_function( (: ( barman ? 1 : 0 ) :) );
 * @example
 * void setup() {
 *    ...
 *    // Setup the open function.
 *    set_open_function( (: do_open_check :) );
 * } /\* setup() *\/
 *
 * int do_open_check( object player, int type ) {
 *    if( !barman ) {
 *       add_failed_mess( "There appears to be no barman here!\n" );
 *       return 2;
 *    }
 *    if( type <= PUB_DESSERT && !chef ) {
 *       add_failed_mess( "The chef isn't here at the moment.\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 pub is
 * open or not.
 * @return the pub open function
 * @see set_open_function()
 * @see check_open()
 */
function query_open_function() {
   return _open_function;
} /* query_open_function() */

/**
 * This method checks to see if the pub is open or not.  It will setup the
 * default fail message if this is what is needed.
 * @param player the player attempting to buy something
 * @param type the type of item being bought
 * @see set_open_function()
 * @see query_open_function()
 */
int check_open( object player, int type ) {
   int val;

   val = evaluate( _open_function, player, type );

   if( val == 1 ) {
       add_failed_mess("The pub is not selling that kind of item at this "
           "time.\n" );
       return 0;
   }

   return !val;

} /* check_open() */