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/
/**
 * <BR>
 * This is the main inheritable for food objects.  It allows you to
 * create food and drink.  Virtual files may also be coded if given
 * the *.food extention.  There are many examples of these virtual
 * food files in the /obj/food directory.  Food does the following
 * things.
 * <BR><BR>
 *   - It can be eaten or drunk.
 * <BR>
 *   - Food can be changed from solid to liquid with set_liquid(),
 * and changed from liquid to solid with reset_liquid().
 * <BR>
 *   - The weight of each bite can be set with set_weight_per_bite().
 * <BR>
 *   - Effects can be added to the player when they are splashed with the food,
 * get food rubbed on them, or get food applied to them with
 * add_external_effect.
 * <BR>
 *   - Splashing, applying, and rubbing can be pk-checked with
 * set_external_pk_check()
 * <BR>
 *   - Splashing can be activated with set_splashable()
 * <BR>
 *   - Splashing can be deactivated with unset_splashable()
 * <BR>
 *   - Applying can be activated with set_applicable()
 * <BR>
 *   - Applying can be deactivated with unset_applicable()
 * <BR>
 *   - Effects can be added to the player when they consume the food
 * with add_eat_effect().
 * <BR>
 *   - Functions can be added to be carried out when the food is
 * consumed with add_eat_func().  This function can be stored
 * on another object if the set_eat_object() is used.
 * <BR>
 *   - An eat message can be set with set_eat_mess().
 * <BR>
 *   - Food can be cut up into pieces with sharp weapons.
 * <BR>
 *   - The description of the new 'pieces' can be set using
 * set_piece_description(), set_piece_short(), set_piece_plural(),
 * and set_piece_substance().
 * <BR>
 *   - Non-liquid food decays and will eventually crumble to dust.
 * <BR>
 *   - The decay speed can be set with set_decay_speed().
 * <BR>
 *   - Food can be 'pickled' to prevent decay using do_cure().
 */

#include <diseases.h>
#include <bits_controller.h>
#include <drinks.h>
#include <weapon.h>
#include <move_failures.h>

inherit "/obj/cont_medium";

#define DEFAULT_DECAY 7200 // Two hours for something to decay.

#define STATE_CHANGE ("/handlers/state_change")

private int _liquid;
private int _bites_gone;
private int _weight_per_bite;
private int _in_pieces;
private int _decay_level;
private int _decay_speed;
private int _splashable;
private int _applicable;
private int _external_pk_check;

private float _divisor = to_float(DEFAULT_DECAY) / to_float(6 * TIME_OUT);

private string _eat_object;
private string _eat_func;
private string _eat_mess;
private string _piece_desc;
private string _piece_short;
private string _piece_plural;
private string _piece_substance;

private string _food_type;

private string _eat_piece_mess;
private string _eat_amt_mess;
private string _eat_last_mess;

private mapping _eat_effects;
private mapping _external_effects;

varargs int do_cut( int num );
varargs int do_eat( int no_mess );
int do_mince();
void set_decay_speed(int decay);
int check_for_container();
private void move_check_for_container();

void create() {
    do_setup++;
    ::create();
    do_setup--;
    reset_continuous();
    _eat_effects = ([ ]);
    _external_effects = ([ ]);
    set_decay_speed(DEFAULT_DECAY);
    if( !do_setup )
        TO->setup();
} /* create() */


/**
 * This function returns true if the food is a liquid.
 * @return 1 if the food is a liquid, and 0 if it is not
 */
int query_liquid() { return _liquid; }

/**
 * This tells us if the food is edible (ie. can be
 * consumed using 'eat' instead of 'drink'.  If it is
 * edible then it is not a liquid.
 * @return 1 if the food is a liquid, and 0 if it is not
 * @see set_liquid()
 * @see query_liquid()
 */
int query_edible() { return !_liquid; }

/**
 * This changes the food object into a liquid.  Liquids are
 * automatically a 'continuous' medium, which means it will
 * combine with other liquids of the same type.
 * <p>
 * This will automatically set the decay speed to 0.  Liquids
 * do not decay.
 * @see reset_liquid()
 * @see set_decay_speed()
 * @see /obj/cont_medium->set_continuous()
 */
void set_liquid() {
    set_continuous();
    _liquid = 1;
    if( environment() )
        environment()->calc_liquid();
    set_decay_speed(0);
    set_determinate("some ");
} /* set_liquid() */

/**
 * This changes the food object into a solid.  Solids are not
 * 'continuous' and the decay speed will be set back to the
 * default.
 * @see set_liquid()
 * @see set_decay_speed()
 */
void reset_liquid() {
    reset_continuous();
    _liquid = 0;
    if( environment() )
        environment()->calc_liquid();
    set_decay_speed(DEFAULT_DECAY);
    _divisor = to_float(DEFAULT_DECAY) / to_float(6 * TIME_OUT);
} /* reset_liquid() */

/**
 * This method returns 1 if the item is a food object.
 * @return 1 when it is a food object
 */
int query_food_object() { return 1; }

/** @ignore yes */
void init() {
    if( query_continuous() ){
        add_command("rub", "<direct:object> <preposition> <indirect:living>");
        add_command("apply", "<direct:object> <preposition> <indirect:living>");
    }

    if( _liquid ) {
        add_command("drink", "<direct:object>");
        add_command("splash", "<direct:object> <preposition> <indirect:living>");
    } else {
        add_command("eat", "<direct:object>", (: do_eat(0) :) );
        add_command("cut", "<direct:object> into <number'number, eg: 3'> pieces",
            (: do_cut($4[1]) :) );
        add_command("cut", "<direct:object>", (: do_cut() :) );
        add_command("slice", "<direct:object>", (: do_cut() :) );
        add_command("mince", "<direct:object>", (: do_mince() :) );
    }
} /* init() */

/**
 * This query tells us whether the food decays.  All objects
 * which decay should have this function on them.
 * @return 1 if the object decays
 * @see set_decay_speed()
 */
int query_decays() { return _decay_speed != 0; }

/**
 * This returns the number of bites that have been taken out
 * of the food object.
 * @return the number of bites which have been taken from
 * the object
 * @see set_bites_gone()
 * @see set_weight_per_bite()
 */
int query_bites_gone() { return _bites_gone; }

/**
 * This sets the weight of each bite of the food object.
 * This is used along with the total weight of the object
 * to calculate how many bites of the food can be taken.
 * @see /std/basic/misc->set_weight()
 * @see set_weight_per_bite()
 * @see weight
 * @return the weight of each bite
 */
int query_weight_per_bite() { return _weight_per_bite; }

/**
 * This sets which type the food is, for the disease handler.
 * @param type the food type to set
 * @see query_food_type()
 */
void set_food_type( string type ) { _food_type = type; }

/**
 * This method returns the food type.
 * @see set_food_type()
 * @return the food type
 */
string query_food_type() { return _food_type; }

/**
 * This returns the eat object on which the eat function
 * of the food is stored.
 * @see set_eat_object()
 * @see set_eat_func()
 * @see set_eat_mess()
 * @return the name of the eat object
 */
string query_eat_object() { return _eat_object; }

/**
 * This returns the name of the eat function to carry out
 * when the object is eaten.
 * @see set_eat_object()
 * @see set_eat_func()
 * @see set_eat_mess()
 * @return the name of the eat function
 */
string query_eat_func() { return _eat_func; }

/**
 * This returns the eat message of the object.
 * @see set_eat_object()
 * @see set_eat_func()
 * @see set_eat_mess()
 * @return the eat message
 */
string query_eat_mess() { return _eat_mess; }

/**
 * This returns the long description of the food object when
 * it is cut up into pieces.
 * @see set_piece_description()
 * @see query_in_pieces()
 * @return the piece description
 */
string query_piece_description() { return _piece_desc; }

/**
 * If the food has been cut up (using the 'cut' command)
 * then this will return true.
 * @see do_cut()
 * @return 1 if it is in pieces, 0 if otherwise
 */
int query_in_pieces() { return _in_pieces; }

/**
 * This returns the decay speed.  If this is 0 then the object
 * is not decaying.  This is the length of time it takes
 * for the whole object to decay.
 * @see set_decay_speed()
 * @return the decay speed
 */
int query_decay_speed() { return _decay_speed; }

/**
 * This returns the level of the decay.  This will be a number
 * between 1 and 6.
 * @see set_decay_speed()
 * @return the level of decay, which is between 1 and 6
 */
int query_decay_level() { return _decay_level; }

/**
 * This method returns which niche the food object has decayed to.
 * Should never be more than 6.
 */
int query_decay() { return ( _decay_level / _divisor ); }

/**
 * This sets the number of bites gone.
 * This helps determine the total weight of the object.
 * @see set_weight_per_bite()
 * @param number the number of bites gone
 */
void set_bites_gone(int number) { _bites_gone = number; }

/**
 * This sets weight of each bite of the food object.  This is
 * used along with the total weight of the object to calculate
 * how many bites of the food can be taken.
 * @see /std/basic/misc->set_weight()
 * @see weight
 * @param number the weight each bite should be
 */
void set_weight_per_bite(int number) { _weight_per_bite = number; }

/**
 * This sets the object on which the eat function is stored.
 * <p>
 * If the eat object is set, then the eat function needs to
 * be set as well.  If the function is not set then setting
 * the object has no effect.
 * <p>
 * The function will be called with two parameters, the first
 * being the object being eaten and the second being the
 * person eating the object.
 *
 * @see set_eat_object()
 * @see set_eat_mess()
 * @param word The object containing the eat function.
 * @example
 * void create_food() {
 *   object food;
 *   food = clone_object("/obj/food/apple.food");
 *   food->set_eat_object(TO);
 *   food->set_eat_func("eat_apple");
 * } /\* create_food() *\/
 *
 * void eat_apple(object food, object eater) {
 *   /\* Do something wonderful! *\/
 * } /\* eat_rabbit() *\/
 */
void set_eat_object(string word) { _eat_object = word; }

/**
 * This sets the eat function of the food.  This is called when
 * the food is eaten.
 * <p>
 * If the eat object is set, then the eat function needs to be
 * set as well. If the function is not set then setting the
 * object has no effect.
 * <p>
 * The function will be called with two parameters, the first being the
 * object being eaten and the second being the person eating the object.
 *
 * @see set_eat_object()
 * @see set_eat_mess()
 * @param word The function to set as the eat function.
 * @example
 * See the example for set_eat_object()
 */
void set_eat_func(string word) { _eat_func = word; }

/**
 * This sets the eat function of the food.  The function is
 * carried out when the food is consumed.  The eat message,
 * set with set_eat_mess(), is displayed when this function
 * is carried out.
 * <p>
 * The eat mess should be treated exactly the same as a
 * add_succeeded_mess(), as it is processed later as
 * add_succeeded_mess(eat_mess, ({ }));
 *
 * @see set_eat_object()
 * @see set_eat_func()
 * @see /global/new_parse->add_succeeded_mess()
 * @param word The string or array to be used as
 * the add_succeeded_mess() when eating the food.
 */
void set_eat_mess(string word) { _eat_mess = word; }

/**
 * This sets up the eat messages for pieces and stuff.  The amount message
 * will replace the $amt$ string in the message with the amount_string().
 * @param piece the eat piece message
 * @param amt the amount messages
 * @param last the message to print out when the last bit gets eaten
 */
void setup_eat_piece_messages(string piece, string amt, string last) {
    _eat_piece_mess = piece;
    _eat_amt_mess = amt;
    _eat_last_mess = last;
} /* setup_eat_piece_message() */

/**
 * This sets the long description of the food when it is has
 * 'cut' into pieces.
 * <p>
 * If this is set then the short of the object is changed
 * to 'piece of cake'.  If the piece description is not
 * set the short description of the object is not changed.
 *
 * @param word The long description to use when cut into pieces.
 * @see query_in_pieces()
 * @see set_piece_short()
 * @see set_piece_plural()
 * @see set_piece_substance()
 * @example
 * set_piece_description("The brown, soft, moist cake looks "
 *   "totally wonderful.\n");
 */
void set_piece_description(string word) { _piece_desc = word; }

/**
 * This is the description used in the short for the pieces.
 * When the object is cut up this is used as part of the
 * short description.  For example, it can change
 * 'piece of cake' to 'slice of cake', 'piece of chocolate'
 * to 'square of chocolate', and so on.  If this the piece
 * short has not been set it is set to the default which is
 * simply "piece".
 *
 * @param word The short to use for the 'piece' when cut into
 * pieces.
 * @see query_in_pieces()
 * @see set_piece_description()
 * @see set_piece_plural()
 * @see set_piece_substance()
 * @example
 * set_piece_short("slice");
 */
void set_piece_short(string short) { _piece_short = short; }

/**
 * This plural description used for the pieces.  It is only
 * really of use if the piece short pluralises in an unusual
 * manner.  If it is not set the piece plural is set to what
 * the pice short has been set to with an "s" stuck on the
 * end, for example, slice to slices.
 *
 * @param word The plural used for the 'pieces' when cut into
 * pieces.
 * @see query_in_pieces()
 * @see set_piece_description()
 * @see set_piece_short()
 * @see set_piece_substance()
 * @example
 * set_piece_plural("slices");
 */
void set_piece_plural(string plural) { _piece_plural = plural; }

/**
 * This sets a new description for what there are pieces of.
 * For example, if you want the short of the food before it
 * is cut up to be 'bar of chocolate', without the piece
 * substance being set, when it is cut up it will become
 * 'a piece of bar of chocolate'.  If you set this in enables
 * you to change that unwieldly description to 'a piece of
 * chocolate'.
 *
 * @param word The description of what the 'piece' is of.
 * @see query_in_pieces()
 * @see set_piece_description()
 * @see set_piece_short()
 * @see set_piece_plural()
 * @example
 * set_piece_substance("goat cheese");
 */
void set_piece_substance(string substance) { _piece_substance = substance; }

/**
 * This sets the in_pieces flag.  This allows you to make the
 * food seem as though it has already been cut into pieces.
 * @param number the number of pieces
 * @see query_in_pieces()
 * @see set_piece_description()
 * @see set_piece_short()
 * @see set_piece_plural()
 * @see set_piece_substance()
 */
void set_in_pieces(int number) {
    _in_pieces = number;
} /* set_in_pieces() */

/**
 * This sets the decay level.  This should be anumber
 * between 1 and 6.  It lets you make a food object seem
 * as though it is already decaying.
 * @see query_decay_level()
 * @see set_decay_speed()
 * @param level The new decay level.
 */
void set_decay_level(int level) { _decay_level = level; }

/**
 * This is called by the pickling stick.  It stops the food
 * object decaying.
 * @see set_decay_speed()
 * @return always returns 1
 */
int do_cure() { set_decay_speed(0); }

/**
 * This sets the speed at which a food object decays.  The
 * speed is set in seconds and is the total amount of time
 * before the food decays.
 * <p>
 * If the decay speed is set to 0, then the object will never decay.
 *
 * @param speed The speed at which the object will decays.
 * @see set_decay_level()
 * @see query_decay_speed()
 * @example
 * /\* This will make the object decay totaly in 30 minutes *\/
 * set_decay_speed(1800);
 *
 * /\* This will stop the object from ever decaying *\/
 * set_decay_speed(0);
 */
void set_decay_speed(int decay) {
    float tmp;

    if( decay != _decay_speed ) {
        BITS_CONTROLLER->remove_bit(TO);
        _decay_speed = decay;
        if( decay ) {
            tmp = _divisor;
            _divisor = to_float(decay) / to_float( 6 * TIME_OUT );
            _decay_level = to_int(_decay_level * _divisor / tmp );
            BITS_CONTROLLER->add_bit(TO);
        }
    }
} /* set_decay_speed() */

/**
 * @ignore yes
 * This is called by the bits controller.
 */
void do_decay() {
    _decay_level++;
    if( _decay_level > 6 * _divisor ) {
        tell_room( ENV(TO), CAP( the_short() ) + " $V$0=crumbles,crumble$V$ "
            "to dust.\n");
        move("/room/rubbish");
    }
} /* do_decay() */

/** @ignore yes */
int query_weight() {
    return ::query_weight() - _bites_gone * _weight_per_bite;
} /* query_weight() */

/** @ignore yes */
string long( string words, int dark ) {
    int twelfths;
    string ret;

    ret = ::long( words, dark );

    if( _liquid )
        return ret;

    if( _weight_per_bite && _bites_gone ) {
        twelfths = ( _bites_gone * _weight_per_bite * 12 ) / ::query_weight();
        switch (twelfths) {
          case 0..1 :
            ret += "It has just been nibbled on.";
            break;
          case 2 :
            ret += "It is about one sixth gone.";
            break;
          case 3 :
            ret += "It is about one quarter gone.";
            break;
          case 4 :
            ret += "It is about one third gone.";
            break;
          case 5 :
            ret += "It is just under half gone.";
            break;
          case 6 :
            ret += "It is about half gone.";
            break;
          case 7 :
            ret += "It is just over half gone.";
            break;
          case 8 :
            ret += "It is about two thirds gone.";
            break;
          case 9 :
            ret += "It is about three quarters gone.";
            break;
          case 10 :
            ret += "It is about five sixths gone.";
            break;
          case 11 :
            ret += "It is almost all gone.";
            break;
          default :
            ret += "Schroedinger's cat has been at it.";
            break;
        }
        ret += "\n";
    }
    if( _decay_speed ) {
        switch( to_int( _decay_level / _divisor ) ) {
          case 0..1 :
            ret += "It looks nice and fresh.";
            break;
          case 2 :
            ret += "It looks a bit rotten.";
            break;
          case 3 :
            ret += "It is half rotten.";
            break;
          case 4 :
            ret += "It is mostly rotten.";
            break;
          case 5 :
            ret += "It is almost completely rotten.";
            break;
          case 6 :
            ret += "It is completely rotten.";
            break;
        }
        ret += "\n";
    }

    return ret;

} /* long() */

/**
 * This sets up the eat object and function.  This is used when
 * the food is eaten.  The eat function is called on the eat
 * object.  If successful the eat message is added to the player.
 * @see set_eat_object()
 * @see set_eat_func()
 * @param ob the object to use
 * @param func the function to use
 */
void setup_eat(string ob, string func) {
    _eat_object = ob;
    _eat_func = func;
} /* setup_eat() */

/**
 * The eat effects of the food.  The mapping contains a set of
 * strings (the effect) and a number associated with the
 * effect.
 * @see add_eat_effect()
 * @return a mapping containing the eat effects
 */
mapping query_eat_effects() {
    if( undefinedp(_eat_effects) )
        _eat_effects = ([ ]);
    return _eat_effects;
} /* query_eat_effects() */

/**
 * This sets all the eat effects.  This function should NOT
 * be used.  Please use add_eat_effect.
 * @param map the eat effects mapping
 */
void set_eat_effects(mapping map) {
    if( mappingp(map) )
        _eat_effects = map;
} /* set_eat_effects() */

/**
 * This adds an eat effect to the food.  This will be added to
 * the player or NPC when the food is eaten.
 * <p>
 * The effect is added with a number as the argument.  The number
 * is based on the amount which is eaten (in weight units), and
 * usually represents the strength of the effect.
 * If the object is continuous, the weight is calculated from
 * the amount and weight_unit array...
 * <p>
 * <br>
 * <code>
 * eff_num = (amount * number * weight_unit[0]) / weight_unit[1]
 * </code>
 * <p>
 * ...where the number is passed into the add_eat_effect() function.
 * If the effect already exists, then the number is added onto
 * the existing number.
 * @see /obj/cont_medium->set_weight_unit()
 * @see remove_eat_effect()
 * @param word the file name of the effect to add.
 * @param number the number to set to the effect to.
 * @return the current value of the effect in the mapping
 */
int add_eat_effect( mixed word, int number ) {
    if( undefinedp(_eat_effects) )
        _eat_effects= ([]);

    if( mappingp(word) ) {
        int num;
        string name;

        foreach( name, number in word ) {
           num += add_eat_effect( name, number );
        }
        return num;
    }

    if( _eat_effects[word] ) {
        _eat_effects[word] += number;
    } else {
        _eat_effects[word] = number;
    }

    return _eat_effects[word];

} /* add_eat_effect() */

/**
 * This removes the eat effect of the given name.
 * @param word the name of the effect to delete
 * @see add_eat_effect()
 */
void remove_eat_effect(string word) {
    if( mapp( _eat_effects ) )
        map_delete( _eat_effects, word );
} /* remove_eat_effect() */

/**
 * This makes the external effects pk checked.
 * @see add_external_effect(), set_applicable(), set_splashable()
 * @see unset_external_pk_check()
 */
void set_external_pk_check() { _external_pk_check = 1; }

/**
 * This makes the external effects not pk checked.
 * @see add_external_effect(), set_applicable(), set_splashable()
 * @see set_external_pk_check()
 */
void unset_external_pk_check() { _external_pk_check = 0; }

/**
 * The external (splash,apply,rub) effects of the food.  The
 * mapping contains a set of strings (the effect) and a number
 * associated with the effect.
 *
 * @see set_splashable(), set_applicable()
 * @return A mapping containing the external effects.
 */
mapping query_external_effects() {
    if( undefinedp(_external_effects) )
        _external_effects = ([ ]);
    return _external_effects + ([ ]);
} /* query_external_effects() */

/**
 * This sets all the external effects.  This function should NOT
 * be used.  Please use add_external_effect.
 * @param map the external effects mapping
 */
void set_external_effects(mapping map) { _external_effects = map; }

/**
 * This adds an external effect to the food.  This will be added to
 * the player or NPC when the liquid is splashed, applied, or rubbed on them.
 * <p>
 * The effect is added with a number as the argument.  The number
 * is based on the amount which is splashed (in weight units), and
 * usually represents the strength of the effect.
 * If the object is continuous, the weight is calculated from
 * the amount and weight_unit array...
 * <p>
 * <br>
 * <code>
 * eff_num = (amount * number * weight_unit[0]) / weight_unit[1]
 * </code>
 * <p>
 * ...where the number is passed into the add_external_effect() function.
 * If the effect already exists, then the number is added onto
 * the existing number.
 *
 * @see /obj/cont_medium->set_weight_unit()
 * @see set_splashable()
 * @see unset_splashable()
 * @see set_applicable()
 * @see unset_applicable
 * @see remove_external_effect()
 * @see set_external_pk_check()
 * @param word The file name of the effect to add.
 * @param number The number to set to the effect to.
 * @return The current value of the effect in the mapping
 */
int add_external_effect(string word, int number) {
    if( undefinedp(_external_effects) )
        _external_effects = ([]);
    if( _external_effects[word] )
        _external_effects[word] += number;
    else
        _external_effects[word] = number;
    return _external_effects[word];
} /* add_external_effect() */

/**
 * This removes the external effect of the given name.
 * @param word the name of the effect to delete
 * @see add_external_effect()
 */
void remove_external_effect(string word) {
    if( mapp( _external_effects ) )
        map_delete( _external_effects, word );
} /* remove_external_effect() */

/**
 * This sets it so the food can be splashed.
 * Note that the food can only be splashed when liquid in any case.
 * Note this is NOT the default case.
 * @see unset_splashable(), set_applicable(), unset_applicable(),
 * add_external_effect()
 */
void set_splashable() { _splashable = 1; }

/**
 * This sets it so the food cannot be splashed.
 * Note that the food can only be splashed when liquid in any case.
 * @see set_splashable()
 */
void unset_splashable() { _splashable = 0; }

/**
 * This queries splashability of the food.
 * Note that the food can only be splashed when liquid and when
 * set_splashable() has been called
 * @param splasher - Who splashes.
 * @param splashee - Who is splashed.
 * @return 1 if it can splash, 0 if it can't
 * @see set_splashable(), unset_splashable(), set_liquid(),
 * set_external_pk_check()
 */
int query_splashable(object splasher, object splashee) {
    if( splasher == splashee || !_external_pk_check )
        return ( _splashable && _liquid );
    return ( _splashable && _liquid && !pk_check( splasher, splashee ) );
} /* query_splashable() */

/**
 * This sets it so the food can be applied.
 * Note this is the default case.
 * @see unset_applicable()
 */
void set_applicable() { _applicable = 1; }

/**
 * This sets it so the food cannot be applied.
 * @see set_applicable()
 */
void unset_applicable() { _applicable = 0; }
/**
 * This queries applicability of the food. (rubbing on someone)
 * Note that the food can only be applied when
 * set_applicable() has been called
 * @param applier - Who applies it
 * @param appliee - Who it is applied to.
 * @return 1 if it can be applied, 0 if it can't
 * @see set_applicable(), unset_applicable(), set_external_pk_check()
 */
int query_applicable(object applier, object appliee) {
    if( applier == appliee || !_external_pk_check )
        return _applicable;
    return ( _applicable && !pk_check(applier, appliee) );
} /* query_applicable() */

/** @ignore yes */
void being_joined_by(object thing) {
    int that, this;
    string *words, word;
    mapping new_effects;

    that = (int)thing->query_amount();
    this = query_amount();

    if( !( this + that ) )
        return;

    if( thing->query_splashable( 0, 0 ) && !_splashable )
        set_splashable();
    if( thing->query_applicable( 0, 0 ) && !_applicable )
        set_applicable();
    if( thing->query_external_pk_check() && !_external_pk_check )
        set_external_pk_check();

    new_effects = (mapping)thing->query_eat_effects();
    // If new_effects isn't a mapping, then there's something
    // wrong, so it should give an error so that someone knows
    // and can get it fixed.
    words = keys(_eat_effects);
    foreach( word in words ) {
        if( new_effects[word] ) {
            new_effects[word] *= that;
            new_effects[word] += this * _eat_effects[word];
        } else {
            new_effects[word] = this * _eat_effects[word];
        }
    }
    words = keys(new_effects);
    foreach( word in words )
        new_effects[word] /= this + that;

    _eat_effects = new_effects;

    new_effects = (mapping)thing->query_external_effects();
    // If new_effects isn't a mapping, then there's something
    // wrong, so it should give an error so that someone knows
    // and can get it fixed.
    words = keys(_external_effects);
    foreach( word in words ) {
        if( _external_effects[word] ) {
            new_effects[word] *= that;
            new_effects[word] += this * _external_effects[word];
        } else {
            new_effects[word] = this * _external_effects[word];
        }
    }

    words = keys(new_effects);
    foreach( word in words )
        new_effects[word] /= this + that;

    _external_effects = new_effects;

} /* being_joined_by() */

/** @ignore yes */
void consume_effect( object who, string what, int amount ) {
    if( who )
        who->add_effect( what, amount );
} /* consume_effect() */

/**
 * This consumes some of the food.
 * @param consumer The person doing the consumption.
 * @param amount The amount being consumed.
 * @param type Use "splash" or "apply" or "external" to use external
 * effects, anything else for eat effects.
 */
varargs void consume( object consumer, int amount, string type ) {
    int denominator, numerator, wholes;
    string *words, word;

    switch(type){
      case "splash" :
      case "apply" :
      case "external" :
         words = keys(_external_effects);
         break;
      default :
         words = keys(_eat_effects);
         break;
    }

    if( !amount && query_continuous() )
        amount = query_amount();

    if( amount ) {
        numerator = amount * query_weight_unit()[0];
        denominator = query_weight_unit()[1];
        if( type != "apply" && type != "splash" && type != "external" ) {
            DISEASE_HAND->consume_food( consumer, query_decay(),
                        ( numerator / denominator ), _food_type );
            consumer->adjust_volume( ( _liquid? D_DRINK : D_FOOD ),
                        ( 50 * numerator ) / denominator );
        }
        if( !TO->query_property("playtester") || playtesterp(consumer) ) {
            foreach( word in words ) {
                switch( type ) {
                  case "splash" :
                  case "apply" :
                  case "external" :
                    numerator = _external_effects[word] * amount *
                                query_weight_unit()[0];
                    break;
                  default :
                    numerator = _eat_effects[word] * amount *
                                query_weight_unit()[0];
                    break;
                }
                wholes = numerator / denominator;
                numerator %= denominator;
                if( random(denominator) < numerator )
                    wholes++;
                call_out( (: consume_effect :), 0, consumer, word, wholes );
            }
        }
        adjust_amount(-amount);
    } else if( _weight_per_bite ) {
        _bites_gone++;
        if( type != "apply" && type != "splash" && type != "external" ) {
            DISEASE_HAND->consume_food( consumer, query_decay(),
                                       _weight_per_bite, _food_type );
            consumer->adjust_volume( D_FOOD, 50 * _weight_per_bite );
        }
        if( !TO->query_property("playtester") || playtesterp(consumer) ) {
            foreach( word in words ) {
                switch(type) {
                  case "splash" :
                  case "apply" :
                  case "external" :
                    call_out( (: consume_effect :), 0, consumer,
                        word, _external_effects[word] * _weight_per_bite );
                  break;
                  default :
                    call_out( (: consume_effect :), 0, consumer,
                        word, _eat_effects[word] * _weight_per_bite );
                  break;
                }
            }
        }
    } else {
        DISEASE_HAND->consume_food( consumer, query_decay(),
               _weight_per_bite, _food_type );
        consumer->adjust_volume( D_FOOD, 50 * weight );
        if( !TO->query_property("playtester") || playtesterp(consumer) ) {
            foreach( word in words ) {
                switch( type ) {
                  case "apply" :
                  case "splash" :
                  case "external" :
                    call_out( (: consume_effect :), 0, consumer,
                        word, _external_effects[word] * weight );
                    break;
                  default :
                    call_out( (: consume_effect :), 0, consumer,
                        word, _eat_effects[word] * weight );
                    break;
                }
            }
        }
    }

    if( type != "apply" && type != "splash" && type != "external" ) {
        if( _eat_func )
            call_other( _eat_object || TO, _eat_func, TO, consumer );
    }

} /* consume() */

/**
 * This is the eat command call.  This is setup with add_command()
 * and does the actual eating.
 * @param no_mess don't use success messages
 * @return Return 1 on success, 2 on success + last bit eaten and return 0 on failure.
 * @see /global/new_parse->add_command()
 * @see /global/new_parse->add_succeeded_mess()
 * @see set_eat_mess()
 */
varargs int do_eat( int no_mess ) {
    int ret;

    if( weight <= 0 && !query_continuous() ) {
        if( _eat_mess )
            add_succeeded_mess( _eat_mess );
        move("/room/rubbish");
        return 1;
    }

    if( ( weight <= ( _bites_gone + 1 ) * _weight_per_bite ) ||
        !_weight_per_bite ) {
        if( !no_mess ) {
            if( !query_continuous() ) {
                if( _weight_per_bite ) {
                    add_succeeded_mess( _eat_last_mess || "$N $V the last of $D.\n");
                } else if( _eat_mess ) {
                    // This only gets done if the whole thing is eaten.
                    add_succeeded_mess( _eat_mess );
                }
            } else {
                if( _eat_amt_mess )
                    add_succeeded_mess( replace_string( _eat_amt_mess,
                        "$amt$", amount_size() ) );
                else
                    add_succeeded_mess("$N $V "+amount_size()+" of $D.\n");
            }
        }
        ret = 2;
        move("/room/rubbish");
    } else {
        if( !no_mess )
            add_succeeded_mess( _eat_piece_mess || "$N $V a piece of $D.\n");
        ret = 1;
    }

    consume( TP, 0 ); // This increases bites gone too.
    return ret;

} /* do_eat() */

/**
 * This checks to see if a weapon is sharp.  This is used later
 * to determine whather an object can be cut up with the weapon.
 * @param weapon the weapon to check to see if it is sharp
 * @return 1 if it is sharp, or 0 if not
 */
int check_sharp( object weapon ) {
    mixed data;
    int i;

    if( !weapon )
        return 0;

    data = weapon->query_attack_data();
    for( i = W_TYPE; i < sizeof(data); i += W_ARRAY_SIZE ) {
        if( data[i] == "sharp" )
            return 1;
    }
    return 0;
} /* check_sharp() */

/**
 * This cuts the food up into bits.  This is the command called
 * with add_command() and does the actual cutting up into bits.  If
 * the num_pieces parameter is undefined, the food is cut in half.
 * If the food is continuous, the cutting is handled by
 * the state_change handler (all continuous objects must be cut this
 * way).
 * @see set_piece_description()
 * @see query_in_pieces()
 * @see query_piece_short()
 * @see query_piece_plural()
 * @see query_piece_substance()
 * @param num_pieces the number of pieces to cut the food into
 * @return 1 if it succeeded, 0 if not.
 */
varargs int do_cut( int num_pieces ) {
    object bing, *obs, with;
    string name, *exploded_short;
    int i, j, k, size_of_each, gone;
    int portion_of_whole;
    int portion_of_parent;
    string temp_short, temp_plural;
    mixed rabbit;

    if( query_liquid() ) {
        add_failed_mess("You cannot $V $D.\n");
        return -1;
    }

    // First, check to see if they have a sharp weapon.
    obs = TP->query_holding() - ({ 0 });
    foreach( bing in obs ) {
        if( check_sharp(bing) ) {
            if( !with || ( with &&
                with->query_weight() > bing->query_weight() ) ) {
                with = bing;
            }
        }
    }

    if( !with ) {
        add_failed_mess("You have to be holding a sharp object to $V $D.\n");
        return -1;
    }

    if( undefinedp(num_pieces) )
        num_pieces = 2;

    if( query_continuous() )
        return( do_mince() );

    if( !_weight_per_bite ) {
        add_failed_mess("$D cannot be cut up.\n");
        return 0;
    }

    if( num_pieces < 2 ||
        num_pieces > ( ( query_weight() / _weight_per_bite ) - _bites_gone ) ) {
        if( num_pieces == 2 || ( ( query_weight() / _weight_per_bite ) -
            _bites_gone ) < 2 )
            add_failed_mess("$D cannot be cut up.\n");
        else
            add_failed_mess("You can't $V $D into that many pieces.\n");
        return -1;
    }

    // Now, do the cutting.
    name = base_name(TO);
    size_of_each = ( query_weight() - _weight_per_bite * _bites_gone ) /
                     num_pieces;
    gone = ( ::query_weight() - size_of_each ) / _weight_per_bite;
    for( i = 0; i < num_pieces; i++ ) {
        // Duplicate ourselves.
        bing = clone_object(name);
        bing->init_static_arg( query_static_auto_load() );
        bing->init_dynamic_arg( query_dynamic_auto_load() );
        bing->set_weight(size_of_each);
        bing->add_property("whole:weight", query_property("whole:weight")
              || query_weight() );
        bing->add_property("whole:short", query_property("whole:short")
              || TP->convert_message( a_short() ) );

        portion_of_whole = bing->query_property("whole:weight") / size_of_each;
        portion_of_parent = query_weight() / size_of_each;

        if( !_in_pieces ) {
            if( _piece_desc ) {
                bing->set_long( _piece_desc );
                if( !_piece_short ) {
                    if( portion_of_whole == 1 )
                        temp_short = "piece";
                    else if( portion_of_whole == 2 )
                        temp_short = "half";
                    else if( portion_of_whole == 4 )
                        temp_short = "quarter";
                    else
                        temp_short = word_ordinal( portion_of_whole );
                }

                if( !_piece_plural ) {
                    if( _piece_short )
                        _piece_plural = pluralize( _piece_short );
                    else
                        temp_plural = pluralize( temp_short );
                }

                rabbit = _piece_substance || bing->query_property("whole:short");
                bing->set_short( ( _piece_short || temp_short )+
                    " of "+rabbit );
                bing->set_main_plural( ( _piece_plural || temp_plural ) +
                    " of " + rabbit );
            }
        } else {
            bing->set_bites_gone( gone );
        }

        /* These next two might not be valid if _in_pieces isn't 0.  Good
         * thing it always is! */
        bing->add_alias( _piece_short || temp_short );
        bing->add_plural( _piece_plural || temp_plural );

        exploded_short = explode( bing->query_short(), " " );
        j = sizeof( exploded_short );
        bing->set_name( exploded_short[ j - 1 ] );
        for( k = 0; k < sizeof( exploded_short ) - 1; k++ )
            bing->add_adjective( exploded_short[k] );
        bing->set_in_pieces( 1 );  // This doesn't do anything!
        if( _piece_desc )
            bing->set_long(_piece_desc);
        bing->move( environment( TO ) );
    }

    move("/room/rubbish");

    // Use the default message.
    if( with->query_weight() < 20 ) {
        add_succeeded_mess( ({
            sprintf("$N $V $D into %s pieces with %s%s.\n",
            query_num( num_pieces ), with->poss_short(),
            ( num_pieces * size_of_each ) < query_weight() ?
             ", carelessly dribbling a bit while you do so" : "" ),
            sprintf("$N $V $D into %s pieces with %s.\n",
            query_num( num_pieces ), with->poss_short() ) }) );
    } else {
        add_succeeded_mess( ({
            sprintf("$N stand back and hack $D into %s pieces with %s%s.\n",
            query_num( num_pieces ), with->poss_short(),
            ( num_pieces * size_of_each ) < query_weight() ?
            ", carelessly obliterating a bit when you do so" : "" ),
            sprintf("$N stand$s back and hack$s $D into %s pieces with %s.\n",
            query_num( num_pieces ), with->poss_short() ) }) );

    }

    return 1;

} /* do_cut() */

varargs int do_mince() {
    object *obs, with, transformed;
    int rem_pieces;

    if( query_liquid() ) {
        add_failed_mess("You cannot $V $D.\n");
        return -1;
    }

    // First, check to see if they have a sharp weapon.
    obs = TP->query_holding() - ({ 0 });
    foreach( transformed in obs ) {
        if( check_sharp( transformed ) ) {
            if( !with || ( with &&
                with->query_weight() > transformed->query_weight() ) ) {
                with = transformed;
            }
        }
    }
    if( !with ) {
        add_failed_mess( "You have to be holding a sharp object to $V "
            "$D.\n" );
        return -1;
    }

    transformed = STATE_CHANGE->transform( TO, "slice" );

    // if it doesn't mince, cut into the remaining number of pieces
    if( !objectp( transformed )) {
        if( query_continuous() ) {
            add_failed_mess("$D cannot be cut up any further.\n");
            return -1;
        }
        if( _weight_per_bite ) {
            rem_pieces = ( query_weight() / _weight_per_bite ) - _bites_gone;
            return( do_cut( rem_pieces ) );
        }
        return -1;
    }

    TO->move( "/room/rubbish" );

    if( transformed->move( TP ) != MOVE_OK ) {
        call_out( "tell_object", 0, TP,
            "You drop "+transformed->the_short()+".\n");
        call_out( "tell_room", 0, ENV( TP ), TP->the_short()+" drops "+
            transformed->the_short()+".\n", TP );
        transformed->move( environment( TP ) );
    }

    add_succeeded_mess("$N $V $D with "+with->the_short()+" into $I.\n",
        ({ transformed }) );
    return 1;

} /* do_mince() */

/**
 * This gives the appropriate message for drinking things.
 * @see do_eat()
 * @see consume(*)
 * @see set_liquid()
 * @return Return 1 if it succeeded, or return 0 if it failed.
 */
int do_drink() {
    if( !check_for_container() ) {
        add_failed_mess("You go to try and drink $D, but it suddenly "
            "remembers what happens when it is not in a container and "
            "drains away to dust.\n");
        move("/room/rubbish");
        return 0;
    }

    TP->add_succeeded_mess( TO, "$N $V "+amount_size()+" of $D.\n", ({ }) );

    consume( TP, 0 );
    return 1;

} /* do_drink() */

/**
 * This handles applying things.
 * @see set_applicable()
 * @see consume(*)
 * @return Return 1 if it succeeded, or return 0 if it failed.
 */
int do_apply(object *things) {
    if( sizeof(things) > 1 ) {
        add_failed_mess("You can $V $D to only one person.\n", ({ }) );
        return 0;
    }

    if( !_applicable ) {
         add_failed_mess("You cannot $V $D to anybody.\n");
         return 0;
    }
    if( _external_pk_check && pk_check(TP, things[0]) ) {
        add_failed_mess("You feel it would be wrong to rub $D on $I.\n",
            things );
        return 0;
    }

    TP->add_succeeded_mess( TO, "$N $V "+amount_size()+" of $D to $I.\n",
        ({ things[0] }) );
    consume( things[0], 0, "apply" );
    return 1;

} /* do_apply() */

/** @ignore yes */
int do_rub(object *things) {
    /* identical to do_apply, except for one little syntax thing */
    if( sizeof(things) > 1 ){
        add_failed_mess("You can $V $D on only one person.\n", ({ }) );
        return 0;
    }

    if( !_applicable ) {
        add_failed_mess("You cannot $V $D on anybody.\n");
        return 0;
    }

    if( _external_pk_check && pk_check( TP, things[0] ) ) {
        add_failed_mess("You feel it would be wrong to rub $D on $I.\n",
            things);
        return 0;
    }

    TP->add_succeeded_mess( TO, "$N $V "+amount_size()+" of $D on $I.\n",
        ({ things[0] }) );
    consume( things[0], 0, "apply" );
    return 1;

} /* do_rub() */

int do_splash(object *things) {
    if( sizeof(things) > 1 ) {
        add_failed_mess("You can $V $D on only one person.\n", ({ }) );
        return 0;
    }

    if( !_splashable || !_liquid ) {
        add_failed_mess("You cannot $V $D on anybody.\n", ({ }) );
        return 0;
    }

    if( _external_pk_check && pk_check( TP, things[0] ) ) {
        add_failed_mess("You feel it would be wrong to splash $D on $I.\n",
            things);
        return 0;
    }

    TP->add_succeeded_mess( TO, "$N $V "+amount_size()+" of $D on $I.\n",
        ({ things[0] }) );
    consume( things[0], 0, "splash" );
    return 1;

} /* do_splash() */

/** @ignore yes */
mapping int_query_static_auto_load() {
    mapping map;

    map = ([ "::" : ::int_query_static_auto_load() ]);

    if( _liquid )
        map["liquid"] = _liquid;
    if( _weight_per_bite )
        map["weight_per_bite"] = _weight_per_bite;
    if( _eat_object )
        map["eat_object"] = _eat_object;
    if( _eat_func )
        map["eat_func"] = _eat_func;
    if( _eat_mess )
        map["eat_mess"] = _eat_mess;
    if( _splashable )
        map["splashable"] = _splashable;
    if( _applicable )
        map["applicable"] = _applicable;
    if( _external_pk_check )
        map["external_pk_check"] = _external_pk_check;

    return map;

} /* int_query_static_auto_load() */

/** @ignore yes */
mapping query_static_auto_load() {
    if( !query_name() || query_name() == "object" )
        return 0;

    if( base_name(TO) + ".c" == __FILE__ || query_continuous() )
        return int_query_static_auto_load();

    return 0;

} /* query_static_auto_load() */

/** @ignore yes */
void init_static_arg( mapping map ) {
    if( !mapp(map) )
        return;

    if( map["::"] )
        ::init_static_arg(map["::"]);

    _liquid = map["liquid"];
    _weight_per_bite = map["weight_per_bite"];
    _eat_object = map["eat_object"];
    _eat_func = map["eat_func"];
    _eat_mess = map["eat_mess"];
    _splashable = map["splashable"];
    _applicable = map["applicable"];
    _external_pk_check = map["external_pk_check"];

} /* init_static_arg() */

/** @ignore yes */
mapping query_dynamic_auto_load() {
    mapping map;

    map = ([ "::" : ::query_dynamic_auto_load() ]);

    if( _bites_gone )
        map["bites_gone"] = _bites_gone;
    if( sizeof(_eat_effects) )
        map["eat_effects"] = _eat_effects;
    if( sizeof(_external_effects) )
        map["external_effects"] = _external_effects;
    if( _in_pieces )
        map["in pieces"] = _in_pieces;
    if( _piece_desc )
        map["piece desc"] = _piece_desc;
    if( _piece_short )
        map["piece short"] = _piece_short;
    if( _piece_plural )
        map["piece plural"] = _piece_plural;
    if( _piece_substance )
        map["piece substance"] = _piece_substance;
    if( _decay_speed )
        map["decay speed"] = _decay_speed;
    if( _decay_level )
        map["decay level"] = _decay_level;
    if( _divisor )
        map["divisor"] = _divisor;

    return map;

} /* query_dynamic_auto_load() */

/** @ignore yes */
void init_dynamic_arg( mapping map ) {
    if( !mapp(map) )
        return;

    if( map["::"] )
        ::init_dynamic_arg(map["::"]);

    _bites_gone = map["bites_gone"];
    _eat_effects = map["eat_effects"] || ([ ]);
    _external_effects = map["external_effects"] || ([ ]);

    _in_pieces = map["in pieces"];
    _piece_desc = map["piece desc"];
    _piece_short = map["piece short"];
    _piece_plural = map["piece plural"];
    _piece_substance = map["piece substance"];

    if( _in_pieces && _piece_desc )
        set_long(_piece_desc);

    set_decay_speed(map["decay speed"]);
    _decay_level = map["decay level"];
    _divisor = map["divisor"];

} /* init_dynamic_arg() */

/** @ignore yes */
mixed stats() {
    string word, *words;
    mixed args;
    int i;

    args = ::stats() + ({
         ({"liquid", _liquid }),
         ({"bites gone", _bites_gone }),
         ({"weight per bite", _weight_per_bite }),
         ({"eat object", _eat_object }),
         ({"eat function", _eat_func }),
         ({"eat message", _eat_mess }),
         ({"in pieces", _in_pieces }),
         ({"decay speed", _decay_speed }),
         ({"decay level", _decay_level }),
         ({"food type", _food_type }),
         });

    words = keys(_eat_effects);
    foreach( word in words )
        args += ({ ({"eat effect#"+i++, word+" ("+_eat_effects[word]+")"}) });

    args += ({ ({"applicable", _applicable }) });
    args += ({ ({"splashable", _splashable }) });

    if( _splashable && !_liquid )
        args += ({ ({"***not splashable***", "because not a liquid"}) });

    args += ({ ({"external_pk_check", _external_pk_check }) });

    if( _external_effects ) {
        words = keys(_external_effects);
        i = 0;
        foreach( word in words )
            args += ({ ({"external effect#"+i++, word+" ("+
                         _external_effects[word]+")"}) });
    }

    return args;

} /* stats() */

/**
 * This checks the outside of us for a container and then destroys ourselves
 * if it is not a container.
 */
int check_for_container() {
    if( query_liquid() ) {
        if( !environment()->query_max_volume() || living(environment()) ) {
            return 0;
        }
    }
    return 1;
} /* check_for_container() */

private void move_check_for_container() {
    if( !check_for_container() ) {
        // Opps, not a container.  Destroy ourselves.
        tell_room( environment(), the_short()+" dribbles all the place "
            "and disappears into the dust.\n");
        move("/room/rubbish");
    }
} /* check_for_container() */

/**
 * @ignore yes
 * Check to see if the thing outside us can handle liquids.
 */
varargs int move(mixed dest, string mess1, string mess2) {
   int ret;

   ret = ::move(dest, mess1, mess2);

   if( ret == MOVE_OK && dest != "/room/rubbish")
       call_out("move_check_for_container", 2 );

   return ret;

} /* move() */