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 main container type object that players will use.  For
 * example, buckets and stuff would use this.  A backpack would be
 * a type of clothing and a potion bottle should be a vessel.
 * @author Who knows, Pinkfish, Ember, Jeremy
 * @see /obj/vessel.c
 * @see /obj/clothing.c
 */
/* Modified by Piecemaker 18/5/93 to add octarine descriptions.
 * Modified by Jeremy Feb 96 to skip breakage in stationary containers
 * and to add 'padded' property
 */

#include <move_failures.h>
#include <parse_command.h>
#include <potion_attrs.h>

inherit "/std/container";
inherit "/std/basic/close_lock_container";

#define MAX_INVENT 40

nosave int full_weight, leak_rate, hb_count;

/* liquid/potion stuff */
mixed *misc_attrs;   /* purely physical attributes ... all the 'inactive' parts */
nosave mixed *all_attrs;    /* misc_attrs merged with active_attrs */
nosave mixed *active_attrs; /* this is the attrs due to the position in the potion space */

int volume, max_volume;  /* total volume, and max volume for this container */
int water;    /* the 'water' part of the volume ... the volume that is used in */
              /* potion space activities.  The volume that is not 'water' is */
              /* considered 'inactive' in any potion reactions */

int *ps_coord; /* coordinate in the potion_space */
int ps_quantity; /* magic number describing quantity of active junk in this potion */

nosave mixed *potion_id, *potion_adjective;
nosave int volume_to_womble;  /* kludge for the fraction stuff */

/** @ignore yes */
void set_volume_to_womble(int n) { volume_to_womble = n; }
/** @ignore yes */
int query_volume_to_womble() { return volume_to_womble; }

/**
 * This method sets the weight of the container when it is full.
 * @param i the new weight of the containe when full.
 * @see query_full_weight()
 * @see set_weight()
 */
void set_full_weight(int i) { full_weight = i; }
/**
 * This method sets the leak rate of the container.  The leak rate is how
 * fast stuff leaks out of the container.
 * @param i the new leak rate of the container
 * @see query_leak_rate()
 */
void set_leak_rate(int i) { leak_rate = i; }
/**
 * This method returns the full weight of the container.
 * @see set_full_weight()
 * @see query_weight()
 */
int query_full_weight() { return full_weight; }
/**
 * This method returns the leak rate of the container
 * @see set_leak_rate()
 */
int query_leak_rate() { return leak_rate; }

/**
 * This method returns the current amount of liquid in the container.
 * @return the current amount of liquid in the container
 */
int query_volume() { return volume; }
/**
 * This method sets the current amount of liquid in the container.  If
 * the container has a leak rate, it starts to leak.
 * @param i the new amount of liquid in the containe
 * @see query_volume()
 * @see set_leak_rate()
 */
void set_volume(int i) {
   volume = i;
   if (leak_rate) {
      set_heart_beat(1);
   }
} /* set_volume() */

/**
 * This method returns the current maxium volume associated with this
 * container.
 * @return the current maximum volume
 */
int query_max_volume() { return max_volume; }
/*
void set_max_volume(int i) { max_volume = i; }
 */

/**
 * This method returns the potion space co-ordinates of the liquid.
 * @return the potion space co-ordinates
 * @see query_ps_quantity()
 */
int *query_ps_coord() { return ps_coord; }
/**
 * This method returns the quantity of liquid at the potion space.
 * @return the quantity of liquid
 * @see query_ps_coord()
 * @see set_ps_quantity()
 */
int query_ps_quantity() { return ps_quantity; }

void set_ps_quantity(int n) { ps_quantity = n; }

int *query_all_attrs() { return all_attrs; }
int *query_misc_attrs() { return misc_attrs; }
int *query_active_attrs() { return active_attrs; }

/**
 * This method sets the volume of water in the container.
 * @param n the new volume of water
 */
void set_water_volume(int n) { water = n; if (leak_rate) set_heart_beat(1); }
/**
 * This method returns the volume of water in the container.
 * @see set_water_volume()
 */
int query_water_volume() { return water; }

/**
 * This method sets the maximum weight of the container.  This also sets
 * the maximum volume of the container to 10 times its maximum weight.
 * (If there is no current maximum volume).
 * @param i the maxium weight of the container
 * @see set_max_volume()
 * @see add_weight()
 */
void set_max_weight(int i) {
  if (!max_volume && i) {
    max_volume = i*10;
  }
  max_weight = i;
} /* set_max_weight() */

/**
 * This method sets the maximum volume of the container.  This also
 * sets the maximum weight of the container to one tenth of the
 * volume (if there is not current maximum weight).
 * @param i the new maximum volume
 * @see set_max_weight()
 * @see add_volume()
 */
void set_max_volume(int i) {
  if (!max_weight && i) {
    max_weight = i/10;
  }
  max_volume = i;
} /* set_max_volume() */

/**
 * This method returns the amount of the liquid you can actually empty
 * from a container.
 * @return the amount of liquid that can be emptied
 */
int empty_formula() {
/* formula to work out how close to empty someone can get casually */
   int vol_lost;

   vol_lost = 2 + volume * 95 / 100;
   if (vol_lost > volume) vol_lost = volume;
   return vol_lost;
} /* empty_formula() */

/** @ignore yes */
int cull_neutral_obs( mixed ob ) {
   if ( ob->query_vect() ) {
     return 1;
   }
   return 0;
} /* cull_neutral_obs() */

/**
 * This method returns the vector of the contents of the container.
 * This is based on the vectors of all the liquid with a vector set
 * on them.
 */
int *contents_vect() {
   object *inv;
   int *v;
   int ang, str, tot, i;

   inv = all_inventory(this_object());
   inv = filter_array(inv, "cull_neutral_obs", this_object());

   if (sizeof(inv) == 0) return ({ 0, 0, 0 });

   tot = ang = str = 0;

   for (i = 0; i < sizeof(inv); i++) {
      v = inv[i]->query_vect();
      tot += v[0];
      ang += v[1] * v[0];
      str += v[2] * v[0];
   }

   return ({ ang / tot, str / tot, tot });
} /* contents_vect() */

/**
 * This method will destroy all the liquid contents of the object.
 * @return the number of object destroyed
 */
int dest_active_contents() {
   object *inv;
   int i;

   inv = all_inventory(this_object());
   inv = filter_array(inv, "cull_neutral_obs", this_object());

   for (i = 0; i < sizeof(inv); i++) {
      inv[i]->dest_me();
   }
   return sizeof(inv);
} /* dest_active_contents() */

/** @ignore yes */
int *squidge_vector(int *v, int meth_ang, int meth_mult) {
   int ca, cb;
   int da, cx, cy;
   object trig;

   "/handlers/trig"->wibble_wobble();
   trig = find_object("/handlers/trig");

   da = meth_ang - v[0];
   ca = v[1] * trig->cos(da);
   cb = v[1] * trig->sin(da);
   ca = ca * meth_mult;
   cb /= meth_mult;
   ca /= 1000000;
   cb /= 1000000;
   cx = ca * trig->cos(meth_ang) + cb * trig->sin(meth_ang);
   cy = ca * trig->sin(meth_ang) - cb * trig->cos(meth_ang);
   return ({ cx / 1000000, cy / 1000000 });
}

void set_ps_coord_quantity(int *coord, int quantity);

/**
 * This method adds a potion space vector with a particular
 * quantity to the object.
 * @param v the vector to add
 * @param quant the quantity to add
 */
void add_ps_vect(int *v, int quant) {
   object trig;
   int ca, cb;

   if (quant <= 0) return;
   trig = find_object("/handlers/trig");
/*   ca = v[1] * trig->cos(v[0]) / 1000000;
   cb = v[1] * trig->sin(v[0]) / 1000000; */
   ca = v[0];  cb = v[1];
    ca = (int)trig->pow1_5(ps_coord[0] * 10 * ps_quantity /(ca * quant)) * ca / 1000000;
   cb = (int)trig->pow1_5(ps_coord[1] * 10 * ps_quantity /(cb * quant)) * cb / 1000000;

   set_ps_coord_quantity( ({ ps_coord[0] + ca, ps_coord[1] + cb }),
                          quant + ps_quantity);
   return;
}

/**
 * This method returns a string for how transparent the liquid is
 * based on the transparency value passed in.
 * @return the transparency string()
 * @param trans the transparency value
 */
string transparency_string(int trans)
/* return string describing transparency 'trans' */
{
   switch (trans) {
   case -10..9: return "opaque";
   case 10..24: return "milky";
   case 25..39: return "murky";
   case 40..59: return "cloudy";
   case 60..74: return "misty";
   case 75..89: return "slightly misty";
   case 90..110: return "clear";
/* if they've got the numbers stuffed up, they should _know_ about it :) */
   default: return "paisley";
   }
} /* transparency_string() */

/**
 * This method returns a string for how consistent the liquid is
 * based on the consistency value passed in.
 * @return the consistency string()
 * @param cons the consistency value
 */
string consistency_string(int cons)
/* return string describing consistency 'cons' */
{
   switch (cons) {
   case -10..9: return "watery liquid";
   case 10..24: return "slightly viscous watery liquid";
   case 25..39: return "runny syrup";
   case 40..59: return "syrup";
   case 60..74: return "jelly";  /* viscous liquid? */
   case 75..89: return "paste";
   case 90..110: return "solid";
/* see above comment in transparency */
   default: return "non-Newtonian fluid";
   }
} /* consistency_string() */

/**
 * This method updates the values used for the find_match() parseing
 * due to the liquids.
 * @see /secure/simul_efun->find_match()
 */
void update_potion_parse()
{
   int i;
   string *exploded;

   potion_adjective = query_adjectives() +
                  ({ transparency_string(all_attrs[POTION_TRANSPARENCY]) });
   potion_id = ({ query_name() }) + query_alias() +
                  ({ consistency_string(all_attrs[POTION_CONSISTENCY]) });
   for (i = 0; i < sizeof(all_attrs[POTION_NAMES]); i++) {
      exploded = explode(all_attrs[POTION_NAMES][i][0], " ");
      potion_id += ({ exploded[sizeof(exploded) - 1] });
      if (sizeof(exploded) > 1) {
         potion_adjective += exploded[0..sizeof(exploded) - 2];
      }
   }

   for (i = 0; i < sizeof(all_attrs[POTION_COLOURS]); i++)
      potion_adjective += explode(all_attrs[POTION_COLOURS][i][0], " ");
   /* plurals?  Lets forget them for now */
}

/**
 * This method zaps all the liquid stuff in the container.
 */
void void_liquid()
{
   volume = 0;
   water = 0;

   misc_attrs = allocate(POTION_ATTRS_SIZE);
   misc_attrs[POTION_CONSISTENCY] = 0;
   misc_attrs[POTION_TRANSPARENCY] = 100;
   misc_attrs[POTION_NAMES] = ({ });
   misc_attrs[POTION_COLOURS] = ({ });
   misc_attrs[POTION_SMELLS] = ({ });
   misc_attrs[POTION_FLAVOURS] = ({ });

   ps_coord = (int *)POTION_SPACE_HANDLER->neutral_coordinate();
   ps_quantity = 0;

/* Hmmm.  I wonder if having these all the same array will cause problems */
   active_attrs = all_attrs = misc_attrs;
   update_potion_parse();
}

/** @ignore yes */
void create() {
  do_setup++;
  container::create();
  close_lock_container::create();
  set_can_export_inventory();
  do_setup--;
  void_liquid();
  add_property("liquid", 1);
  add_property("watertight", 1);
  leak_rate = 1000;
  if ( !do_setup )
    this_object()->setup();
} /* create() */

/** @ignore yes */
void init() {
/*  liquid::init(); */
  (void)this_player()->add_command("drink", this_object());
  (void)this_player()->add_command("splash", this_object(), "%D %p %I");
  (void)this_player()->add_command("rub", this_object(), "%D %p %I");
  (void)this_player()->add_command("apply", this_object(), "%D %p %I");
  (void)this_player()->add_command("pour", this_object(), "%D %p %I");
  (void)this_player()->add_command("taste", this_object());
  (void)this_player()->add_command("smell", this_object());
  this_player()->add_command("fill", this_object(), "<indirect:object> <preposition> <direct:object>");
  this_player()->add_command("fill", this_object(), "%I %d/%d up %p %D");
  this_player()->add_command("fill", this_object(), "%I %d/%d full %p %D");
  (void)this_player()->add_command("empty", this_object());

  close_lock_container::init();
} /* init() */

/* This may be stuffing up other containers...
int query_weight() {
  if (!full_weight)
    return ::query_weight();
  return ::query_weight() + ((loc_weight+(max_weight*volume)/max_volume)
                             *full_weight)/max_weight;
} query_weight() */

/** @ignore yes */
int query_complete_weight() {
  return ::query_complete_weight() + ( max_weight * volume ) /
      ( max_volume + 1 );
} /* query_complete_weight() */

/**
 * This method returns the current amount of weight left that can
 * be filled on the container.
 * @return the current amount of weight left
 */
int query_weight_left() {
  if (!max_volume)
    return max_weight - loc_weight;
  return max_weight - loc_weight - (max_weight*volume)/max_volume;
} /* query_weight_left() */

/**
 * This method returns the amount of volume left for liquids to be
 * added into.
 * @return the amount of volume left
 * @see add_volume()
 * @see transfer_liquid_to()
 */
int query_volume_left() {
  if (!max_weight)
    return max_volume - volume;
  return max_volume - volume - (max_volume*loc_weight)/max_weight;
} /* query_volume_left() */

/** @ignore yes */
int add_weight( int n ) {
  if ( query_weight_left() < n ) return 0;
  if ( !( ::add_weight( n ) ) ) return 0;
  if ( n >= 0 ) {
    remove_call_out( "check_breakages" );
    call_out( "check_breakages", 5 + random( 16 ) );
  }
  return 1;
#ifdef BREAK_NOW

  obs = all_inventory();
  for (i=0;i<sizeof(obs);i++)
    if ((amt = obs[i]->query_property("fragile"))) {
      if (loc_weight <= amt || ((loc_weight - amt)*100)/amt <= random(100))
          obs[i] = 0;
    } else
      obs[i] = 0;

  obs = obs - ({ 0 });
  if (sizeof(obs)) {
    say(this_player()->one_short()+" breaks "+
          (sizeof(obs)>1?"some things":"one thing")+" in "+
          query_multiple_short(({ this_object() }))+".\n");
    write("You break "+query_multiple_short(obs)+" in "+
          query_multiple_short(({ this_object() }))+".\n");
    obs->dest_me();
  }
  return 1;
#endif
} /* add_weight() */

/**
 * This method checks to see if any of the things contained in the
 * container should be broken.
 * @see /std/container->add_weight()
 */
void check_breakages() {
/* check loc_weight against fragility of every object and break some
 *   if necessary.  If the container has the "padded" property, adjust
 *   the chance of breakage.
 */
  object *obs, carrier;
  int i, amt, wt;

  // See if it's being carried by a living object
  carrier = environment(this_object());
  while (carrier && (!living(carrier))) {
    carrier = environment(carrier);
  }
  if (!carrier)
    return;
  obs = all_inventory();
  wt = loc_weight - (int)query_property("padded");
  for (i=0;i<sizeof(obs);i++) {
    if ((amt = obs[i]->query_property("fragile"))) {
      if (wt <= amt || ((wt - amt)*100)/amt <= random(100)) {
          obs[i] = 0;
      }
    } else {
      obs[i] = 0;
    }
  }

  obs = obs - ({ 0 });
  if (sizeof(obs) && environment(carrier)) {
    tell_room(environment(carrier), carrier->the_short()+" breaks "+
          (sizeof(obs)>1?"some things":"one thing")+" in "+
          query_multiple_short(({ this_object() }))+".\n", ({ carrier }));
    tell_object(carrier, "You break "+query_multiple_short(obs)+" in "+
          query_multiple_short(({ this_object() }))+".\n");
    obs->dest_me();
  }
} /* check_breakages() */

/** @ignore yes */
varargs string pretty_short( object thing ) {
   return short_status() + ::pretty_short( thing );
} /* pretty_short() */

/** @ignore yes */
varargs string pretty_plural( object thing ) {
   return short_status() + ::pretty_plural( thing );
} /* pretty_plural() */

/**
 * This method returns the name of the current liquid inside the container.
 * @return the name of the current liquid
 */
string liquid_name()
{
   string liq_name;
   mixed *names, *colours;
   string *med, *lo;   /* groups of strings with hi/med/lo intensities */
   int no_names, no_colours, i;

   names = all_attrs[POTION_NAMES];
   colours = all_attrs[POTION_COLOURS];

   no_names = (sizeof(names) < 1 || names[0][1] < VERY_SMALL_AMOUNT);
   no_colours = (sizeof(colours) < 1 || colours[0][1] < VERY_SMALL_AMOUNT);

   if (no_names && no_colours) {
      liq_name = "a colourless " +
         transparency_string(all_attrs[POTION_TRANSPARENCY]) + " " +
         consistency_string(all_attrs[POTION_CONSISTENCY]);
   } else if (no_names) {
      if (colours[0][1] < SMALL_AMOUNT) {
         liq_name = "a faint " + colours[0][0];
      } else {
         liq_name = add_a(colours[0][0]);
      }
      liq_name += " " + transparency_string(all_attrs[POTION_TRANSPARENCY]) +
                     " " + consistency_string(all_attrs[POTION_CONSISTENCY]);
      med = ({ });
      i = 1;
      while (i < sizeof(colours) && colours[i][1] >= SMALL_AMOUNT)
         med += ({ colours[i++][0] });

      lo = ({ });
      while (i < sizeof(colours) && colours[i][1] >= VERY_SMALL_AMOUNT)
         lo += ({ colours[i++][0] });

      if (sizeof(med)) {
         liq_name += " with swirls of " + query_multiple_short(med);
      }

      if (sizeof(lo)) {
         if (sizeof(med)) liq_name += " and faint streaks of " + query_multiple_short(lo);
         else liq_name += " with faint streaks of " + query_multiple_short(lo);
      }
   } else if (no_colours) {
      i = 0;
      med = ({ });
      lo = ({ });
      while (i < sizeof(names) && names[i][1] >= SMALL_AMOUNT)
         med += ({ names[i++][0] });
      while (i < sizeof(names) && names[i][1] >= VERY_SMALL_AMOUNT)
         lo += ({ names[i++][0] });

      if (sizeof(med) > 1) liq_name = "a mixture of " + query_multiple_short(med);
      else if (sizeof(med) == 1) liq_name = med[0];

      if (!sizeof(med) && sizeof(lo) > 1)
         liq_name = "a diluted mixture of " + query_multiple_short(lo);
      else if (!sizeof(med) && sizeof(lo) == 1)
         liq_name = "diluted " + lo[0];
      else if (sizeof(med) && sizeof(lo))
         liq_name += "and small quantities of " + query_multiple_short(lo);
   } else {
      if (names[0][1] > colours[0][1] / 2)
      /* arbitrary relationship ... names are 'twice' as visible as colours */
      /* if the primary name is more visible than the primary colour, */
      /* then we use this scenario: */
      /*   "chunky soup (a yellow cloudy syrup with swirls of green, orange */
      /*    and brown)" */
      /* else we use the colour scenario: */
      /*   "a yellow cloudy syrup containing chunky soup with swirls of green, */
      /*    orange and brown" */
      {
         /* this little fragment copied directly from 'no_colours' above */
         i = 0;
         med = ({ });
         lo = ({ });
         while (i < sizeof(names) && names[i][1] >= SMALL_AMOUNT)
            med += ({ names[i++][0] });
         while (i < sizeof(names) && names[i][1] >= VERY_SMALL_AMOUNT)
            lo += ({ names[i++][0] });

         if (sizeof(med) > 1) liq_name = "a mixture of " + query_multiple_short(med);
         else if (sizeof(med) == 1) liq_name = med[0];

         if (!sizeof(med) && sizeof(lo) > 1)
            liq_name = "a diluted mixture of " + query_multiple_short(lo);
         else if (!sizeof(med) && sizeof(lo) == 1)
            liq_name = "diluted " + lo[0];
         else if (sizeof(med) && sizeof(lo))
            liq_name += "and small quantities of " + query_multiple_short(lo);

         /* this little fragment copied directly from 'no_names' above */

         if (colours[0][1] < SMALL_AMOUNT) liq_name += " (a faint " + colours[0][0];
         else liq_name += " (" + add_a(colours[0][0]);
         liq_name += " " + transparency_string(all_attrs[POTION_TRANSPARENCY]) +
                        " " + consistency_string(all_attrs[POTION_CONSISTENCY]);
         med = ({ });
         i = 1;
         while (i < sizeof(colours) && colours[i][1] >= SMALL_AMOUNT)
            med += ({ colours[i++][0] });

         lo = ({ });
         while (i < sizeof(colours) && colours[i][1] >= VERY_SMALL_AMOUNT)
            lo += ({ colours[i++][0] });

         if (sizeof(med)) {
            liq_name += " with swirls of " + query_multiple_short(med);
         }

         if (sizeof(lo)) {
            if (sizeof(med)) liq_name += " and faint streaks of " + query_multiple_short(lo);
            else liq_name += " with faint streaks of " + query_multiple_short(lo);
         }
         liq_name += ")";
      } else {   /* phew, coloured liquid containing names now */
         /* this little fragment _almost_ directly copied from no_names above */
         if (colours[0][1] < SMALL_AMOUNT) liq_name = "a faint " + colours[0][0];
         else liq_name = add_a(colours[0][0]);
         liq_name += " " + transparency_string(all_attrs[POTION_TRANSPARENCY]) +
                        " " + consistency_string(all_attrs[POTION_CONSISTENCY]);

         med = ({ });
         i = 0;
         while (i < sizeof(names) && names[i][1] >= VERY_SMALL_AMOUNT)
            med += ({ names[i++][0] });

         liq_name += " containing " + query_multiple_short(med);

         med = ({ });
         i = 1;
         while (i < sizeof(colours) && colours[i][1] >= SMALL_AMOUNT)
            med += ({ colours[i++][0] });

         lo = ({ });
         while (i < sizeof(colours) && colours[i][1] >= VERY_SMALL_AMOUNT)
            lo += ({ colours[i++][0] });

         if (sizeof(med)) {
            liq_name += " with swirls of " + query_multiple_short(med);
         }

         if (sizeof(lo)) {
            if (sizeof(med)) liq_name += " and faint streaks of " + query_multiple_short(lo);
            else liq_name += " with faint streaks of " + query_multiple_short(lo);
         }
      } /* yayayayaya.  done! */
   }
   return liq_name;
}

/**
 * This method returns the short description of the liquid.
 * @return the short description of the liquid
 * @see query_liquid_name()
 */
string query_liquid_short()
{
   if (sizeof(all_attrs[POTION_NAMES]) && sizeof(all_attrs[POTION_COLOURS])) {
      if (all_attrs[POTION_NAMES][0][1] > all_attrs[POTION_COLOURS][0][1] / 2) {
         return all_attrs[POTION_NAMES][0][0];
      }
   }

   return (sizeof(all_attrs[POTION_COLOURS]) ?
           all_attrs[POTION_COLOURS][0][0] : "colourless") + " " +
             transparency_string(all_attrs[POTION_TRANSPARENCY]) + " " +
             consistency_string(all_attrs[POTION_CONSISTENCY]);
} /* query_liquid_short() */

/* ok modifyed back to using writes... */
/*        ^^^ lies!  all lies! */
/* Ok, Ok.  Sulk */
/* Please ignore the above comments.  They are wholely unjustified. */
/** @ignore yes */
string long(string str, int dark) {
  string ret;
  int bing;

  ret = ::long(str, dark);
  /*   ret += calc_extra_look(); */
  if (query_transparent() ||
      !query_closed()) {
    if ( ( dark == 2 ) || ( dark == -2 ) ) {
       if (query_contents() != "") {
          ret += "The "+ short( dark ) +" contains some items you cannot "
                 "make out.\n";
       }
    } else {
       ret += query_contents( "The "+ short( dark ) +" contains:\n" );
    }
  }

  /* what the hell is long_status, and why is it here? */
  ret += long_status();

  /* the long for the water inside of it */
  if ((query_transparent() || !query_closed()) && volume) {
    if ( ( dark == 2 ) || ( dark == -2 ) ) {
      ret += "It has some liquid of some sort in it.\n";
    } else {
      bing = (volume*8 + max_volume/16)/max_volume;
      if (bing <= 0) {
        ret += "It is slightly wet from "+liquid_name()+".\n";
      } else if (bing >= 8) {
        ret += "It is full to the brim with " + liquid_name()+".\n";
      } else {
        ret += "It is " + ({ "an eighth", "a quarter",
                "three eighths", "half", "five eighths", "three quarters",
                "seven eighths" })[bing-1]+" full of "+liquid_name()+".\n";
      }
    }
  }
  return ret;
} /* long() */

/**
 * This returns an adjective for how full the current object is with
 * liquid.  This is used in the parse command handling code.
 * @return the fullness adjective
 * @see query_max_volume()
 */
string *fullness_adjectives()
{
   if (!max_volume) {
      return ({ "totally", "empty" });
   }
   switch (100 * volume / max_volume) {
   case 0..4: return ({ "totally", "empty" });
   case 5..13: return ({ "empty" });
   case 65..94: return ({ "full" });
   case 95..100: return ({ "totally", "full" });
   default: return ({ });
   }
} /* fullness_adjectives() */

/* ok parse command stuff */
/** @@ignore yes */
string *parse_command_id_list() {
  return potion_id + ::parse_command_id_list();
} /* parse_command_id_list() */

/* string *parse_command_plural_id_list() { return plurals; } */
/** @@ignore yes */
string *parse_command_adjectiv_id_list() {
  return  fullness_adjectives() + potion_adjective + ::parse_command_adjectiv_id_list();
} /* parse_command_adjectiv_id_list() */

/** @@ignore yes */
int sort_func(int *x, int *y)
{
   if (x[1] < y[1]) return 1;
   else return 0;
}

/** @@ignore yes */
mixed *mix_liq(mixed *arr1, mixed *arr2, int vol1, int vol2, int tot_vol)
{
   int i, j;   /* general indexes into arrays */
   mixed *arr3;

   arr3 = allocate(sizeof(arr1));

   for (i = 0; i < sizeof(arr1); i++) {
      arr3[i] = ({ arr1[i][0], arr1[i][1] * vol1 / tot_vol });
      for (j = 0; j < sizeof(arr2); j++) {
         if (arr2[j][0] == arr1[i][0]) {
            arr3[i][1] += arr2[j][1] * vol2 / tot_vol;
            arr2[j][1] += 100000;         /* icky icky icky */
 /* have to be able to restore this because (due to the magic of
    arrays :( ) we are really working on the original */
            break;
         }
      }
   }

   for (i = 0; i < sizeof(arr2); i++) {
      if (arr2[i][1] >= 100000) {
         arr2[i][1] -= 100000;
      } else {
         arr3 += ({ ({ arr2[i][0], arr2[i][1] * vol2 / tot_vol }) });
      }
   }

   /* now lets sort it */
   arr3 = sort_array(arr3, "sort_func", this_object());
   /* now cull off 0's on the end */
   for (i = sizeof(arr3) - 1; i >= 0; i--) {
      if (arr3[i][1] > 0) break;
   }

   if (i < 0) arr3 = ({ });
   else arr3 = arr3[0..i];

   return arr3;
}

/** @@ignore yes */
mixed *merge_potions(mixed *potion1, mixed *potion2, int vol1, int vol2, int tot_vol)
{
   mixed *result;

   result = allocate(POTION_ATTRS_SIZE);
   result[POTION_CONSISTENCY] =
      (potion1[POTION_CONSISTENCY] * vol1 +
       potion2[POTION_CONSISTENCY] * vol2) / tot_vol;
   result[POTION_TRANSPARENCY] =
      (potion1[POTION_TRANSPARENCY] * vol1 +
       potion2[POTION_TRANSPARENCY] * vol2) / tot_vol;

   result[POTION_NAMES] = mix_liq(potion1[POTION_NAMES], potion2[POTION_NAMES],
                                  vol1, vol2, tot_vol);
   result[POTION_COLOURS] = mix_liq(potion1[POTION_COLOURS], potion2[POTION_COLOURS],
                                  vol1, vol2, tot_vol);
   result[POTION_FLAVOURS] = mix_liq(potion1[POTION_FLAVOURS], potion2[POTION_FLAVOURS],
                                  vol1, vol2, tot_vol);
   result[POTION_SMELLS] = mix_liq(potion1[POTION_SMELLS], potion2[POTION_SMELLS],
                                  vol1, vol2, tot_vol);
   return result;
}

void set_misc_attrs(mixed *new_misc_attrs, int misc_vol)
{
   misc_attrs = new_misc_attrs + ({ });
   volume = water + misc_vol;
   all_attrs = merge_potions(active_attrs, misc_attrs, water, volume - water, volume);
   update_potion_parse();
   if (leak_rate) set_heart_beat(1);
}

/**
 * This method changes the concentration of the liquid to be a new
 * and exciting concentration.
 * @param potion the potion to change the concentration of
 * @param new_conc the new concentration of the liquid
 */
mixed *change_concentration(mixed *potion, int new_conc)
{
   mixed *newpotion;
   int i;

   newpotion = potion + ({ });
   newpotion[POTION_CONSISTENCY] = newpotion[POTION_CONSISTENCY] * new_conc / 100;
   newpotion[POTION_TRANSPARENCY] =
      (100 - (100 - newpotion[POTION_TRANSPARENCY]) * new_conc / 100);
   for (i = 0; i < sizeof(newpotion[POTION_NAMES]); i++) {
      newpotion[POTION_NAMES][i][1] = newpotion[POTION_NAMES][i][1] * new_conc / 100;
   }
   for (i = 0; i < sizeof(newpotion[POTION_COLOURS]); i++) {
      newpotion[POTION_COLOURS][i][1] = newpotion[POTION_COLOURS][i][1] * new_conc / 100;
   }
   for (i = 0; i < sizeof(newpotion[POTION_SMELLS]); i++) {
      newpotion[POTION_SMELLS][i][1] = newpotion[POTION_SMELLS][i][1] * new_conc / 100;
   }
   for (i = 0; i < sizeof(newpotion[POTION_FLAVOURS]); i++) {
      newpotion[POTION_FLAVOURS][i][1] = newpotion[POTION_FLAVOURS][i][1] * new_conc / 100;
   }
   return newpotion;
}

/**
 * This method sets the quantity of the position space co-ordinate
 * to be the new quantity.
 * @param new_coord the new co-ordinate of the liquid
 * @param new_quantity the new quantity of the liquid
 */
void set_ps_coord_quantity(int *new_coord, int new_quantity)
{
   mixed *ps_attrs;

   ps_attrs = (mixed *)POTION_SPACE_HANDLER->query_attrs_at(new_coord);
   active_attrs = change_concentration(ps_attrs, new_quantity * 100 / water);
   ps_quantity = new_quantity;
   ps_coord = new_coord;
   all_attrs = merge_potions(active_attrs, misc_attrs, water, volume - water, volume);
   update_potion_parse();
   (void)POTION_SPACE_HANDLER->potion_create(this_object(), new_coord, new_quantity);
}

void begin_config()
{
   potion_id = allocate(9);
   ps_coord = allocate(9);
}

void config(int vari, mixed cont)
{
   potion_id[vari] = cont;
   ps_coord[vari] = 1;
}

void end_config()
{
   mixed *ps_attrs;

   if (ps_coord[OC_VOL]) {
      volume = potion_id[OC_VOL];
      if (ps_coord[OC_MVOL]) water = volume - potion_id[OC_MVOL];
      else water = potion_id[OC_WVOL];
   } else {
      water = potion_id[OC_WVOL];
      volume = water + potion_id[OC_MVOL];
   }
   leak_rate = potion_id[OC_LEAKRATE];
   if (leak_rate && volume) set_heart_beat(1);
   if (ps_coord[OC_MAXVOL]) max_volume = potion_id[OC_MAXVOL];
   else max_volume = volume;
   ps_quantity = potion_id[OC_PSQUANT];
   if (ps_coord[OC_MISCATTRS]) misc_attrs = potion_id[OC_MISCATTRS];
   else misc_attrs = ({ 0, 100, ({ }), ({ }), ({ }), ({ }) });
   if (ps_coord[OC_PSCOORD]) ps_coord = potion_id[OC_PSCOORD];
   else ps_coord = ({ 0, 0 });

   ps_attrs = (mixed *)POTION_SPACE_HANDLER->query_attrs_at(ps_coord);
   active_attrs = change_concentration(ps_attrs, ps_quantity * 100 / water);
   all_attrs = merge_potions(active_attrs, misc_attrs, water, volume - water, volume);
   update_potion_parse();
   (void)POTION_SPACE_HANDLER->potion_create(this_object(), ps_coord, ps_quantity);
}

/** @@ignore yes */
mixed stats() {
  return container::stats() + close_lock_container::stats() + ({
    ({ "ps_quantity", query_ps_quantity() }),
   ({ "ps_coord", (pointerp(ps_coord) && sizeof(ps_coord) == 2) ?
      "({ " + ps_coord[0] + ", " + ps_coord[1] + " })" :
      sprintf("%O", ps_coord) }),
    ({ "volume", query_volume() + "" }),
    ({ "max volume", query_max_volume() }),
    ({ "water vol", query_water_volume() }),
    ({ "volume left", query_volume_left() }),
    ({ "weight left", query_weight_left() }),
    ({ "full_weight", query_full_weight(), }),
    ({ "leak_rate", query_leak_rate() + "" }),
  });
}

/** @@ignore yes */
object query_parse_id(mixed *arr) {
  volume_to_womble = 0;
  return ::query_parse_id(arr);
} /* query_parse_id() */

/** @@ignore yes */
object query_frac_parse_id(mixed *arr) {
  int i;

  volume_to_womble = 0;
  if (arr[P_THING] < 0) {
    arr[P_THING]++;
    if (arr[P_THING] != 0)
      return 0;
    volume_to_womble = volume*arr[P_TOP]/arr[P_BOT];
    call_out("set_volume_to_womble", 0, 0);
    arr[P_THING] = -1784628;
    return this_object();
  }
  if (arr[P_THING] == 0)
    if ((i=(arr[P_MAX_NUM]*arr[P_TOP])/arr[P_BOT]) > arr[P_CUR_NUM]++)
      return this_object();
    else
      if (i+1 == arr[P_CUR_NUM]) {
        volume_to_womble = volume*arr[P_TOP]/arr[P_BOT];
        call_out("set_volume_to_womble", 0, 0);
        return this_object();
      }
  else
    return 0;

  if ((i=(arr[P_THING]*arr[P_TOP])/arr[P_BOT]) > arr[P_CUR_NUM]++)
    return this_object();
  else
    if (i+1 == arr[P_CUR_NUM]) {
      volume_to_womble = volume*arr[P_TOP]/arr[P_BOT];
      call_out("set_volume_to_womble", 0, 0);
      return this_object();
    }
  return 0;
} /* query_frac_parse_id() */

/**
 * This method removes some volume of liquid from the container.
 * @see add_volume()
 * @see query_volume()
 */
int remove_volume(int vol_lost)
{
   int q_lost;

   if (!volume) {
      q_lost = ps_quantity;
      water = 0;
      ps_quantity = 0;
      return q_lost;
   }
   q_lost = ps_quantity * vol_lost / volume;
   water -= water * vol_lost / volume;
   volume -= vol_lost;
   ps_quantity -= q_lost;
   return q_lost;
}

/** @ignore yes */
void heart_beat() {
  int lost, off;

   if (leak_rate == 0 || volume <= 0) {
      set_heart_beat(0);
      return;
   }
  if (hb_count--) return ;
  hb_count = 10;
  lost = leak_rate;
  if (lost > volume)
    lost = volume;
  off = lost/100;
  if (off > 10)
    off = 10;
  tell_room(environment(),
            capitalize(query_liquid_short())+({ " drips slowly",
                               " drips",
                               " dribbles",
                               " trickles slowly",
                               " trickles",
                               " trickles rapidly",
                               " pours sluggishly",
                               " pours",
                               " streams",
                               " gushes",
                               " fountains", })[off]+" out of the "+
                                                          short(1)+".\n");
  (void)remove_volume(lost);
  if (!volume) {
    set_heart_beat(0);
/*    set_liquid_name(0); */
    void_liquid();
  }
} /* heart_beat() */

/**
 * This method does the actual liquid transfer, as needed by the
 * filling, emptying and pour operations.
 * @param dest where the liquid is to go
 * @param vol_misc the volume of non-water to move
 * @param misc the attributes for the non-water
 * @param vol_water the volume of water to move
 * @param coord the co-ordinate of the liquid
 * @param quantity the quantity of the potion space liquid
 */
void transfer_liquid_to(object dest, int vol_misc, mixed *misc,
                                     int vol_water, int *coord, int quantity)
{
   mixed *their_attrs;
   int their_vol, their_water, their_quantity, new_quantity, new_misc_vol;
   int *new_coord, *old_coord;

   their_attrs = (mixed *)dest->query_misc_attrs();
   their_vol = (int)dest->query_volume();
   their_water = (int)dest->query_water_volume();
   their_quantity = (int)dest->query_ps_quantity();
   new_quantity = quantity + their_quantity;

   if (!their_attrs)  /* can't be a potion ... give up */
      return ;

   new_misc_vol = vol_misc + their_vol - their_water;

   (void)dest->set_water_volume(their_water + vol_water);
   if (new_misc_vol) {
      their_attrs = merge_potions(misc_attrs, their_attrs,
                               vol_misc, their_vol - their_water,
                               new_misc_vol);
      (void)dest->set_misc_attrs(their_attrs, new_misc_vol);
   } else   /* icck ... if we didn't do the set_misc_attrs, then their
                   volume is wrong, so we have to set it manually */
      (void)dest->set_volume(their_water + vol_water);


   old_coord = (int *)dest->query_ps_coord();
   new_coord = allocate(2);
/* simple averaging ... */
   if (pointerp(ps_coord) && new_quantity) {
      new_coord[0] = (ps_coord[0] * quantity + old_coord[0] * their_quantity) /
                     new_quantity;
      new_coord[1] = (ps_coord[1] * quantity + old_coord[1] * their_quantity) /
                     new_quantity;
      dest->set_ps_coord_quantity(new_coord, new_quantity);
   }

} /* transfer_liquid_to() */

/**
 * This method returns true if the object is open and prints a message
 * about the open status of the object.
 * @return 1 if it is open, 0 if not
 */
int ensure_open()
{
   if (query_locked()) {
      write("The " + short(1) + " is locked.\n");
      return 0;
   }
   if (query_closed()) /* has to be closed */
      if (do_open()) {
         write("You open the "+short(1)+".\n");
         return 1;
      } else {
         write("You can't open the " + short(1) + ".\n");
         return 0;
      }

   return 1;
} /* ensure_open() */

/** @ignore yes */
int do_pour(object *dest, string me, string him, string prep)
{
   int amount_poured, volume_transferred, old_water, old_volume;
   string liquid_desc;

   if (sizeof(dest) > 1) {
      write("Currently you can only pour into one object\n");
      return 0;
   }

   if (!ensure_open()) return 0;

   if (volume <= 0) {
      write("The " + short(0) + " you are trying to pour out of is empty.\n");
      return 0;
   }

   old_water = water;
   old_volume = volume;  /* so that when volume becomes 0 in this process */

   liquid_desc = query_liquid_short();

   if (volume_to_womble && volume_to_womble <= empty_formula())
      volume_transferred = volume_to_womble;
   else
      volume_transferred = empty_formula();

   amount_poured = remove_volume(volume_transferred);

   if (volume_transferred > dest[0]->query_volume_left()) {
      say(this_player()->short(0) + " attempts to pour from " + add_a(short(0))
         + " into " + add_a(dest[0]->short(0)) + " but ends up spilling "
         + liquid_desc + " all over the ground.\n");
      write(capitalize(liquid_desc) + " spills on the floor as you try to pour "
         + "from the " + short(0) + " into the " + dest[0]->short(0) + ".\n");

      amount_poured = (amount_poured * ((int)dest[0]->query_volume_left())) /
                         volume_transferred;
      volume_transferred = (int)dest[0]->query_volume_left();
   }

   transfer_liquid_to(dest[0], volume_transferred * (old_volume - old_water) / old_volume,
      misc_attrs, volume_transferred * old_water / old_volume, ps_coord, amount_poured);

   this_player()->add_succeeded(dest[0]);
   return 1;
}

/** @ignore yes */
int do_fill(object *to, mixed *args_b, mixed *args_a, mixed *args) {
   int m, n, i, run_out, volume_needed, their_volume, their_max,
       amount_poured, ok;

   if (intp(args[1]) && intp(args[2])) {
      m = args[0];
      n = args[1];
      if (m > n || m < 0 || n <= 0) {
         notify_fail("Interesting fraction you have there!\n");
         return 0;
      }
   } else {
      m = 1;
      n = 1;
   }

   if (query_locked()) {
      notify_fail("The " + short(0) + " is locked!\n");
      return 0;
   }

   if (query_closed()) {
      if (do_open()) {
         write("You open the " + short(0) + ".\n");
      } else {
         write("You cannot open the " + short(0) + ".\n");
         return 0;
      }
   }

   if (volume <= 0) {
      write("The " + short(0) + " is bone dry!\n");
      return 0;
   }

   run_out = 0;
   for (i = 0; i < sizeof(to) && !run_out; i++) {
      their_volume = (int)to[i]->query_volume();
      their_max = (int)to[i]->query_max_volume();

      if (their_max <= 0) {
         write("The " + to[i]->short(0) +
               " doesn't look like it can be filled!\n");
         continue;
      }

      if (their_volume >= their_max) {
         write("The " + to[i]->short(0) + " is full to the brim already.\n");
         continue;
      }

      volume_needed = their_max * m / n;
      if (their_volume >= volume_needed) {
         write("The " + to[i]->short(0) + " is more than " + m + "/" + n +
           " full already.\n");
         continue;
      }

      ok++;
      volume_needed -= their_volume;

      if (volume_needed > empty_formula()) {
         write("You drain the " + short(0) + " into the " + to[i]->short(0) +
            " but it is not enough.\n");
         volume_needed = empty_formula();
         run_out = 1;
         this_player()->add_succeeded(to[i]);
      } else {
         this_player()->add_succeeded(to[i]);
/*
         write("You pour from the " + short(0) + " into the " +
                to[i]->short(0) + ".\n");
 */
      }
  /*
      say(this_player()->one_short() + " pours " + query_liquid_short() +
         " from the " + short(0) + " into the " + to[i]->short(0) + ".\n");
   */
      amount_poured = volume_needed * ps_quantity / volume;
      transfer_liquid_to(to[i], volume_needed * (volume - water) / volume,
         misc_attrs, volume_needed * water / volume, ps_coord, amount_poured);
      amount_poured = remove_volume(volume_needed);
   }
   return ok;
} /* do_fill() */

/** @ignore yes */
int do_drink(object *dest, string me, string him, string prep)
{
   int amount_drunk, amount_can_be_drunk;

   if (sizeof(dest)) {
      write("Drinking is a very simple operation - please don't complicate matters.\n");
      return 0;
   }

   if (!ensure_open()) return 0;

   if (volume <= 0) {
      write("The " + short(0) + " is bone dry!\n");
      return 0;
   }

   amount_can_be_drunk = (2000 - (int)this_player()->query_volume(2)) *
      (int)this_player()->query_con() / 12;
  /* should do some fudging to add +/- 5 mls or something */
  /* possibly skill/stat dependant */

   if (!volume_to_womble) volume_to_womble = empty_formula();
   if (volume_to_womble > empty_formula()) volume_to_womble = empty_formula();
   if (volume_to_womble > amount_can_be_drunk) {
      write("You drink some of the liquid, but simply cannot fit it all in.\n");
      volume_to_womble = amount_can_be_drunk;
   }
   amount_drunk = remove_volume(volume_to_womble);

   (void)POTION_SPACE_HANDLER->potion_drunk(this_player(), ps_coord, amount_drunk);
   this_player()->adjust_volume(2, volume_to_womble * 12 /
      (int)this_player()->query_con());
   switch ((this_player()->query_volume(2) + 100) / 200) {
   case 5:
      write("You feel mildly full of liquid.\n");
      break;
   case 6:
      write("You feel very full of liquid.\n");
      break;
   case 7:
      write("You feel pissed.\n");
      break;
   case 8:
      write("You are awash with liquid.\n");
      break;
   case 9:
      write("You are full to the brim with liquid.\n");
      break;
   case 10:
      write("You feel you would burst if you drank any more.\n");
      break;
   }
   return 1;
}

/** @ignore yes */
int do_empty(object *dest, string me, string him, string prep)
{
   if (sizeof(dest)) {
      write("Passing on to pour ... bad move.\n");
      return do_pour(dest, me, him, prep);
   }
/* this completely fails to work :( ^^^ */

   if (!ensure_open()) { write("It aint open!\n"); return 0; }

   if (volume == 0) {
      write("The " + short(0) + " is already empty.\n");
      return 0;
   }

   if (volume_to_womble && volume_to_womble <= empty_formula())
      (void)remove_volume(volume_to_womble);
   else
      (void)remove_volume(empty_formula());

/* should check spillage */
   return 1;
}

/** @ignore yes */
int do_splash(object *dest, string me, string him, string prep) {
/* note that spashing needs to be changed to make the amount that
   actually gets to the targe be related to some skill ... */
   int amount_splashed;

   if (sizeof(dest) > 1) {
      write("You can only splash one object at a time.\n");
      return 0;
   }

   if (!sizeof(dest))
     return 0;

   if (!ensure_open()) return 0;

   if (volume_to_womble && volume_to_womble <= empty_formula())
      amount_splashed = remove_volume(volume_to_womble);
   else
      amount_splashed = remove_volume(empty_formula());

   (void)POTION_SPACE_HANDLER->potion_touch(dest[0], ps_coord, amount_splashed);
   (void)this_player()->add_succeeded(dest[0]);
   return 1;
}

/** @ignore yes */
int do_rub(object *dest, string me, string him, string prep) {
   int amount_rubbed;

   if (sizeof(dest) > 1) {
      write("You can only "+query_verb()+" on to one object at a time.\n");
      return 0;
   }

   if (!sizeof(dest))
     return 0;

   if (environment(dest[0]) != this_player()) {
      write("You can only "+query_verb()+" on to an object in your inventory.\n");
      return 0;
   }

   if (!ensure_open()) return 0;

   if (volume_to_womble && volume_to_womble <= empty_formula())
      amount_rubbed = remove_volume(volume_to_womble);
   else
      amount_rubbed = remove_volume(empty_formula());

   (void)POTION_SPACE_HANDLER->potion_touch(dest[0], ps_coord, amount_rubbed);
   (void)this_player()->add_succeeded(dest[0]);
   return 1;
}

/** @ignore yes */
void do_apply(object *dest, string me, string him, string prep ) {
   do_rub( dest, me, him, prep );
}

#define TASTE_AMOUNT 5
/** @ignore yes */
int do_taste() {
   int amount_tasted;
   string desc;
   mixed *group;
   int i;
   /* be kind to tasters! */

   if (!ensure_open()) return 0;

   if (volume < TASTE_AMOUNT) {
      write("There is no liquid to taste.\n");
      return 0;
   }

   desc = "The " + query_liquid_short();
   if (!sizeof(all_attrs[POTION_FLAVOURS]) ||
     all_attrs[POTION_FLAVOURS][0][1] < VERY_SMALL_AMOUNT)
      desc += " has no discernible flavour";
   else {
      group = ({ });
      for (i = 0; i < sizeof(all_attrs[POTION_FLAVOURS]) &&
           all_attrs[POTION_FLAVOURS][i][1] >= SMALL_AMOUNT; i++)
         group += all_attrs[POTION_FLAVOURS][i][0..0];

      if (sizeof(group)) {
         desc += " tastes of " + query_multiple_short(group);
         if (i < sizeof(all_attrs[POTION_FLAVOURS]) &&
             all_attrs[POTION_FLAVOURS][i][1] >= VERY_SMALL_AMOUNT)
            desc += " with a faint hint of ";
      } else desc += " tastes faintly of ";

      group = ({ });
      for (; i < sizeof(all_attrs[POTION_FLAVOURS]) &&
           all_attrs[POTION_FLAVOURS][i][1] >= VERY_SMALL_AMOUNT; i++)
         group += all_attrs[POTION_FLAVOURS][i][0..0];

      if (sizeof(group)) desc += query_multiple_short(group);
   }
   write(desc + ".\n");
   say(this_player()->one_short() + " takes a small sip from the " +
       short(0) + ".\n");

   amount_tasted = remove_volume(TASTE_AMOUNT);
   (void)POTION_SPACE_HANDLER->potion_drunk(this_player(), ps_coord, TASTE_AMOUNT);
   return 1;
}

/** @ignore yes */
int do_smell()
{
   string desc;
   string *group;
   int i;
   /* be kind to smellers! */

   if (!ensure_open()) return 0;

   if (volume < 1) {
      write("There is no liquid to smell.\n");
      return 0;
   }

   desc = "The " + query_liquid_short();
   if (!sizeof(all_attrs[POTION_SMELLS]) ||
     all_attrs[POTION_SMELLS][0][1] < VERY_SMALL_AMOUNT)
      desc += " has no distinct aroma";
   else {
      group = ({ });
      for (i = 0; i < sizeof(all_attrs[POTION_SMELLS]) &&
           all_attrs[POTION_SMELLS][i][1] >= SMALL_AMOUNT; i++)
         group += all_attrs[POTION_SMELLS][i][0..0];

      if (sizeof(group)) {
         desc += " smells of " + query_multiple_short(group);
         if (i < sizeof(all_attrs[POTION_SMELLS]) &&
             all_attrs[POTION_SMELLS][i][1] >= VERY_SMALL_AMOUNT)
            desc += " with a faint hint of ";
      } else desc += " smells faintly of ";

      group = ({ });
      for (; i < sizeof(all_attrs[POTION_SMELLS]) &&
           all_attrs[POTION_SMELLS][i][1] >= VERY_SMALL_AMOUNT; i++)
         group += all_attrs[POTION_SMELLS][i][0..0];

      if (sizeof(group)) desc += query_multiple_short(group);
   }
   write(desc + ".\n");
   say(this_player()->one_short() + " takes a whiff of the " +
       short(0) + ".\n");

   (void)POTION_SPACE_HANDLER->potion_smell(this_player(), ps_coord, ps_quantity * 100 / volume);
   return 1;
}

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

  tmp = ::int_query_static_auto_load();
  return ([ "::" : tmp,
            "leak rate" : leak_rate,
            "full weight" : full_weight,
            "trans" : query_transparent(),
            "difficulty" : query_difficulty(),
            "key" : query_key(),
            "trap open func" : query_open_trap_func(),
            "trap lock func" : query_lock_trap_func(),
            "trap open ob" : query_open_trap_ob(),
            "trap lock ob" : query_lock_trap_ob(),
            "max volume" : max_volume,
          ]);
} /* int_query_static_auto_load() */

/** @ignore yes */
mapping query_dynamic_auto_load() {
  if (!query_name() || query_name() == "object") {
    return 0;
  }
  return ([
           "::" : ::query_dynamic_auto_load(),
           "locked" : query_locked(),
           "stuck" : query_stuck(),
           "closed" : query_closed(),
           "volume" : volume,
           "water" : water,
           "misc attrs" : misc_attrs,
           "ps coord" : ps_coord,
           "ps quantity" : ps_quantity,
          ]);
} /* query_dynamic_auto_load() */

/** @ignore yes */
void init_dynamic_arg(mapping map) {
  mixed *ps_attrs;
  object money;

  if (map["::"])
    ::init_dynamic_arg(map["::"]);
  if (sizeof(map["money"])) {
     money = clone_object("/obj/money");
     money->set_money_array(map["money"]);
     money->move(this_object());
  }

  if (map["locked"]) {
     set_locked();
  } else {
     set_unlocked();
  }
  set_stuck(map["stuck"]);
  if (map["closed"]) {
     set_closed();
  } else {
     set_open();
  }

  volume = map["volume"];
  set_water_volume(map["water"]);
  if (volume == 0) {
    void_liquid();
    update_potion_parse();
    return;
  }
  misc_attrs = map["misc attrs"];
  ps_quantity = map["ps quantity"];
  ps_coord = map["ps coord"];
  ps_attrs = (mixed *)POTION_SPACE_HANDLER->query_attrs_at(ps_coord);
  if(!water) water = 1;
  active_attrs = change_concentration(ps_attrs, ps_quantity * 100 / water);
  all_attrs = merge_potions(active_attrs, misc_attrs, water, volume - water, volume);
  update_potion_parse();
} /* init_dynamic_arg() */

/** @ignore yes */
void init_static_arg(mapping args) {
  if (args["::"]) {
    ::init_static_arg(args["::"]);
  }
  if (!undefinedp(args["leak rate"])) {
    leak_rate = args["leak rate"];
  }
  if (!undefinedp(args["full weight"])) {
    full_weight = args["full weight"];
  }

  if (!undefinedp(args["trans"])) {
    if (args["trans"]) {
       set_transparent();
    } else {
       set_opaque();
    }
  }
  if (!undefinedp(args["difficulty"])) {
    set_difficulty(args["difficulty"]);
  }
  if (!undefinedp(args["key"])) {
    set_key(args["key"]);
  }
  if (!undefinedp(args["trap open func"])) {
    set_open_trap(args["trap open ob"], args["trap open func"]);
  }
  if (!undefinedp(args["trap lock func"])) {
    set_lock_trap(args["trap lock ob"], args["trap lock func"]);
  }

  if (!undefinedp(args["max volume"])) {
    max_volume = args["max volume"];
  }
} /* init_static_arg() */

/** @ignore yes
 * Do it all the time for containers...  They could have things in them.
 * too hard to andle rhe special cases you know...
 */
mixed query_static_auto_load() {
  if (!query_name() || query_name() == "object") return 0;
  if (file_name(this_object())[0..13] == "/obj/container")
    return int_query_static_auto_load();
  return ([ ]);
} /* query_static_auto_load() */

/**
 * This method stops the container being added when it is
 * closed.  It also handles the same flags as the liing
 * object.  We assume this is not the same
 * as the default containers used
 * by things like item rooms and such.
 * @param ob the object being added
 * @param flag the addition flag
 * @see /std/basic/move.c
 */
int test_add(object ob, int flag) {
  return !query_closed() && !flag;
} /* test_addd() */

/** @ignore yes */
int can_find_match_recurse_into(object looker) {
   if( query_closed() )
       return 0;
   return ::can_find_match_recurse_into(looker);
} /* can_find_match_recurse_into() */

/** @ignore yes */
int test_remove( object thing, int flag, mixed dest ) {
    return !query_closed();
} /* test_remove() */

/** @ignore yes */
varargs int move(mixed dest, string messin, string messout) {
   int result;
   object from;
   object to;

   from = environment();
   result = ::move(dest, messin, messout);
   if (result == MOVE_OK) {
      to = environment();
      we_moved(from, to);
   }
   return result;
} /* move() */

/** @ignore yes */
void dest_me() {
   close_lock_container::dest_me();
   container::dest_me();
} /* dest_me() */