/
LIB3/
LIB3/D/ADMIN/
LIB3/D/ADMIN/OBJ/
LIB3/D/ADMIN/ROOM/W/
LIB3/D/HOME/
LIB3/D/HOME/CITY/ARENA/
LIB3/D/HOME/CITY/ITEMS/
LIB3/D/HOME/CITY/POSTOFFI/
LIB3/DOC/
LIB3/GLOBAL/SPECIAL/
LIB3/GLOBAL/VIRTUAL/
LIB3/NET/
LIB3/NET/CONFIG/
LIB3/NET/DAEMON/CHARS/
LIB3/NET/GOPHER/
LIB3/NET/INHERIT/
LIB3/NET/OBJ/
LIB3/NET/SAVE/
LIB3/NET/VIRTUAL/
LIB3/OBJ/B_DAY/
LIB3/OBJ/HANDLERS/TERM_TYP/
LIB3/PLAYERS/B/
LIB3/PLAYERS/N/
LIB3/ROOM/
LIB3/SAVE/
LIB3/SAVE/BOARDS/
LIB3/SAVE/ENVIRON/
LIB3/SAVE/POST/
LIB3/STD/COMMANDS/SHADOWS/
LIB3/STD/CREATOR/
LIB3/STD/DOM/
LIB3/STD/EFFECTS/
LIB3/STD/EFFECTS/HEALING/
LIB3/STD/EFFECTS/OTHER/
LIB3/STD/EFFECTS/POISONS/
LIB3/STD/ENVIRON/
LIB3/STD/GUILDS/
LIB3/STD/LIQUIDS/
LIB3/STD/ROOM/
LIB3/STD/TRIGGER/SHADOW/
LIB3/W/
LIB3/W/BANNOR/
LIB3/W/NEWSTYLE/
#include "move_failures.h"
#include "parse_command.h"
#include "potion_attrs.h"

inherit "/std/container";
inherit "/std/basic/close_lock";

#define MAX_INVENT 40
#define C_CLOSED 1
#define C_TRANS 2
#define C_OPAQUE 1

static int full_weight, leak_rate, hb_count;

mixed  *misc_attrs;		/* purely physical attributes ... all the 'inactive' parts */
static mixed *all_attrs;	/* misc_attrs merged with active_attrs */
static 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 */

static string *potion_id, *potion_adjective;
static int volume_to_womble;	/* kludge for the fraction stuff */

void    set_volume_to_womble( int n )
{
    volume_to_womble = n;
}
int     query_volume_to_womble()
{
    return volume_to_womble;
}

void    set_full_weight( int i )
{
    full_weight = i;
}
void    set_leak_rate( int i )
{
    leak_rate = i;
}
int     query_full_weight()
{
    return full_weight;
}
int     query_leak_rate()
{
    return leak_rate;
}

int     query_volume()
{
    return volume;
}
void    set_volume( int i )
{
    volume = i;
    if( leak_rate )
	set_heart_beat( 1 );
}

int     query_max_volume()
{
    return max_volume;
}

/*
   void set_max_volume(int i) { max_volume = i; }
 */

int    *query_ps_coord()
{
    return ps_coord;
}
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;
}

void    set_water_volume( int n )
{
    water = n;
    if( leak_rate )
	set_heart_beat( 1 );
}
int     query_water_volume()
{
    return water;
}

void    set_max_weight( int i )
{
    if( !max_volume && i )
	max_volume = i * 10;
    max_weight = i;
}				/* set_max_weight() */

void    set_max_volume( int i )
{
    if( !max_weight && i )
	max_weight = i / 10;
    max_volume = i;
}				/* set_max_volume() */

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() */

cull_neutral_obs( ob )
{
    if( ob->query_vect() )
	return 1;
    return 0;
}

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 / sizeof( inv ) });
}

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 );
}

squidge_vector( v, meth_ang, meth_mult )
{
    int     ca, cb;
    int     da, cx, cy;
    object  trig;

    trig = find_object( "/obj/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 });
}

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 "plaid";
    }
}				/* transparency_string() */

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() */

void    update_potion_parse()
{
    int     i;
    string *exploded;

    potion_adjective = adjectives +
	({ transparency_string( all_attrs[ POTION_TRANSPARENCY ] ) });
    potion_id = ({ name }) +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 */
}

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

void    create()
{
    container::create();
    close_lock::create();

    void_liquid();

    add_property( "liquid", 1 );
    add_property( "watertight", 1 );

    leak_rate = 1000;
}

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() );
/*
   add_action("fill", "fill");
 */
    this_player()->add_command( "fill", this_object(), "%I %p %D" );
    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() );

    container::init();		/* ? */
    close_lock::init();
}

int     query_weight()
{
    if( !full_weight )
	return ::query_weight();
    return ::query_weight() + ((loc_weight + (max_weight * volume) /
				max_volume) * full_weight) / max_weight;
}

int     query_weight_left()
{
    if( !max_volume )
	return max_weight - loc_weight;
    return max_weight - loc_weight - (max_weight * volume) / max_volume;
}

int     query_volume_left()
{
    if( !max_weight )
	return max_volume - volume;
    return max_volume - volume - (max_volume * loc_weight) / max_weight;
}

int     add_weight( int n )
{
    int     old, new, amt;

    if( query_weight_left() < n )
	return 0;
    if( !full_weight )
	return ::add_weight( n );
    old = query_weight();
    loc_weight += n;
    new = query_weight();
    if( new != old )
	if( environment() && !environment()->add_weight( new - old ) )
	{
	    loc_weight -= n;
	    return 0;
	}
    return 1;
}

string  pretty_short( int dark )
{
    string  s;

    if( trans == C_TRANS && closed != C_CLOSED )
    {
	s = query_multiple_short( all_inventory() );
	return this_object()->short( dark ) + short_status() +
	        (s != "" ? ", it contains " + s : s);
    }
    return( string ) this_object()->short( dark ) + short_status();
}

string  pretty_plural()
{
    string  s;

    if( trans != C_TRANS && closed != C_CLOSED )
	return ::pretty_plural();
    s = query_multiple_short( all_inventory() );
    return ::pretty_plural() + short_status() +
	(s != "" ? ", they contain " + s : s);
}

int     transfer_all_to( object dest )
{
    object *ob;
    int     i;

    /* potion stuff?  Who knows */
    ob = all_inventory( this_object() );
    for( i = 0; i < sizeof( ob ); i++ )
	ob[ i ]->move( dest );
    if( first_inventory( this_object() ) )
	return 0;
    return 1;
}

string  comma_list( string *lst )
/* I am sure this is done elsewhere, however I am not going to find it */
/* just now */
{
    int     i;
    string  result;

    if( sizeof( lst ) < 1 )
	return "";
    if( sizeof( lst ) == 1 )
	return lst[ 0 ];
    result = "";
    for( i = 0; i < sizeof( lst ) - 2; i++ )
	result += lst[ i ] + ", ";
    return result + lst[ i ] + " and " + lst[ i + 1 ];
}

string  liquid_name()
{
    string  liq_name;
    mixed * names, *colours;
    string *hi, *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 " + comma_list( med );
	    }

	    if( sizeof( lo ) )
	    {
		if( sizeof( med ) )
		    liq_name += " and faint streaks of " + comma_list( lo );
		else
		    liq_name += " with faint streaks of " + comma_list( 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 " + comma_list( med );
		else
		    if( sizeof( med ) == 1 )
			liq_name = med[ 0 ];

		if( !sizeof( med ) && sizeof( lo ) > 1 )
		    liq_name = "a diluted mixture of " + comma_list( 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 " + comma_list( 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 " + comma_list( med );
		    else
			if( sizeof( med ) == 1 )
			    liq_name = med[ 0 ];

		    if( !sizeof( med ) && sizeof( lo ) > 1 )
			liq_name = "a diluted mixture of " + comma_list( 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 " + comma_list( 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 " + comma_list( med );
		    }

		    if( sizeof( lo ) )
		    {
			if( sizeof( med ) )
			    liq_name += " and faint streaks of " + comma_list( lo );
			else
			    liq_name += " with faint streaks of " + comma_list( 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 " + comma_list( 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 " + comma_list( med );
		    }

		    if( sizeof( lo ) )
		    {
			if( sizeof( med ) )
			    liq_name += " and faint streaks of " + comma_list( lo );
			else
			    liq_name += " with faint streaks of " + comma_list( lo );
		    }
		}		/* yayayayaya.  done! */
	    }
    return liq_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 ] );
}



string long( string str, int dark )
{
    string  ret;
    int     bing;

    ret = query_long();
    ret += calc_extra_look();
    if( trans != C_OPAQUE && closed != C_CLOSED )
	ret += query_contents( 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( trans != C_OPAQUE && closed != C_CLOSED && volume )
    {
	bing = (volume * 8 + max_volume / 16) / max_volume;
	if( bing <= 0 )
	    return ret + "It is slightly wet from " + liquid_name() + ".\n";
	if( bing >= 8 )
	    return ret + "It is full to the brim with " + liquid_name() + ".\n";
	return 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;
}

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

/* string *parse_command_plural_id_list() { return plurals; } */
string *parse_command_adjectiv_id_list()
{
    return potion_adjective + ::parse_command_adjectiv_id_list();
}				/* parse_command_adjectiv_id_list() */

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

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


#ifdef BINGLEBONGLE
mixed * mix_liq( mixed *arr1, mixed *arr2, int vol1, int vol2, int tot_vol )
{
    int     i, j;		/* general indexes into arrays */

    arr1 += ({ });		/* stop anything silly happening */
    arr2 += ({ });

    for( i = 0; i < sizeof( arr1 ); i++ )
    {
	arr1[ i ][ 1 ] = arr1[ i ][ 1 ] * vol1 / tot_vol;
	for( j = 0; j < sizeof( arr2 ); j++ )
	{
	    if( arr2[ j ][ 0 ] == arr1[ i ][ 0 ] )
	    {
		arr1[ i ][ 1 ] += arr2[ j ][ 1 ] * vol2 / tot_vol;
		arr2[ j ][ 0 ] = 0;
		break;
	    }
	}
    }

    for( i = 0; i < sizeof( arr2 ); i++ )
    {
	if( arr2[ i ][ 0 ] )
	{
	    arr1 += ({ ({ arr2[ i ][ 0 ], arr2[ i ][ 1 ] * vol2 / tot_vol }) });
	}
    }

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

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

    return arr1;
}
#endif

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 );
}

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

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 );
}

mixed   stats()
{
    return container ::stats() + close_lock ::stats() + ({ 
							      ({ "ps_quantity", query_ps_quantity() }),
							      ({ "ps_coord", 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() }),
							       });
}

object  query_parse_id( mixed *arr )
{
    volume_to_womble = 0;
    return ::query_parse_id( arr );
}

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

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

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

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 );
    }

    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 );
    }

}

int     ensure_open()
{
    if( locked )
    {
	write( "The " + short( 1 ) + " is locked.\n" );

	return 0;
    }
    if( 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;
}

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

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( locked )
    {
	notify_fail( "The " + short( 0 ) + " is locked!\n" );

	return 0;
    }

    if( 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()->query_cap_name() + " 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() */

int     fill( string s )
{
    string  x, y, z;
    int     m, n, i;
    object *from, *to;
    int     their_volume, their_max, amount_poured, run_out, volume_needed;

    notify_fail( "fill container [m/n up|full] from|with container\n" );
    if( !s )
	return 0;

    if( sscanf( s, "%s from %s", x, y ) != 2 )
	if( sscanf( s, "%s with %s", x, y ) != 2 )
	    return 0;

    if( sscanf( x, "%s %d/%d up", z, m, n ) != 3 )
	if( sscanf( x, "%s %d/%d full", z, m, n ) != 3 )
	{
	    z = x;
	    m = n = 1;
	}

    if( m > n || m < 0 || n <= 0 )
    {
	notify_fail( "Interesting fraction you have there!\n" );
	return 0;
    }

    from = find_match( y, ({ this_player(), environment( this_player() ) }) );
    to = find_match( z, ({ this_player(), environment( this_player() ) }) );

    if( member_array( this_object(), from ) < 0 )
    {
	notify_fail( "Can't find " + y + ".\n" );
	return 0;
    }

    if( sizeof( from ) > 1 )
    {
	notify_fail( "Can only pour from one container at a time.\n" );
	return 0;
    }

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

	return 0;
    }

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

	    return 1;
	}
    }

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

	return 1;
    }

    if( sizeof( to ) == 0 )
    {
	write( "Can't find " + z + ".\n" );
	return 1;
    }

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

	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;
	    if( i + 2 == sizeof( to ) )
		write( "The " + to[ i + 1 ] + " remains unfilled.\n" );
	    else
		if( i + 2 < sizeof( to ) )
		    write( "The " + query_multiple_short( to[ i + 1..sizeof( to ) - 1 ] ) +
			   " remain unfilled.\n" );
	}
	else
	{
	    write( "You pour from the " + short( 0 ) + " into the " + to[ i ]->short( 0 ) + ".\n" );
	}

	say( this_player()->query_cap_name() + " 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 1;
}

int     do_drink( object *dest, string me, string him, string prep )
{
    int     amount_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;
    }

    /* should do some fudging to add +/- 5 mls or something */
    /* possibly skill/stat dependant */

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

    (void)POTION_SPACE_HANDLER->potion_drunk( this_player(), ps_coord, amount_drunk );
    return 1;
}

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

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

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

do_apply( x, y, z, a, b, c )
{
    do_rub( x, y, z, a, b, c );
}

#define TASTE_AMOUNT 5
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 " + comma_list( 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 += comma_list( group );
    }
    write( desc + ".\n" );
    say( this_player()->query_cap_name() + " 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;
}

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 " + comma_list( 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 += comma_list( group );
    }
    write( desc + ".\n" );
    say( this_player()->query_cap_name() + " takes a whiff of the " +
	 short   ( 0 ) + ".\n" );

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

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": trans, "difficulty":difficulty,
  "key": key, "trap open func":trap_open_func,
  "trap lock func": trap_lock_func, "unlock skill":unlock_skill,
  "trap open ob": trap_open_ob, "trap lock ob":trap_lock_ob,
  "max volume":max_volume,
	      ]);
}

mapping query_dynamic_auto_load()
{
  return([ "::": ::query_dynamic_auto_load(),
  "locked": locked,
  "stuck":  stuck,
  "closed": closed,
  "volume": volume,
  "water":  water,
  "misc attrs":misc_attrs,
  "ps coord":ps_coord,
  "ps quantity":ps_quantity,
	      ]);
}				/* query_dynamic_auto_load() */

void    init_dynamic_arg( mapping map )
{
    if( map[ "::" ] )
	::init_dynamic_arg( map[ "::" ] );
    locked = map[ "locked" ];
    stuck = map[ "stuck" ];
    closed = map[ "closed" ];
    volume = map[ "volume" ];
    set_water_volume( map[ "water" ] );
    if( volume == 0 )
    {
	void_liquid();
	update_potion_parse();
	return;
    }
    if( map[ "misc attrs" ] )
	set_misc_attrs( map[ "misc attrs" ], volume - water );
    if( map[ "ps coord" ] )
	set_ps_coord_quantity( map[ "ps coord" ], map[ "ps quantity" ] );
    update_potion_parse();
}				/* init_dynamic_arg() */

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" ] ) )
	trans = args[ "trans" ];
    if( !undefinedp( args[ "difficuly" ] ) )
	difficulty = args[ "difficulty" ];
    if( !undefinedp( args[ "key" ] ) )
	key = args[ "key" ];
    if( !undefinedp( args[ "trap open func" ] ) )
	trap_open_func = args[ "trap open func" ];
    if( !undefinedp( args[ "trap lock func" ] ) )
	trap_lock_func = args[ "trap lock func" ];
    if( !undefinedp( args[ "unlock skill" ] ) )
	unlock_skill = args[ "unlock skill" ];
    if( !undefinedp( args[ "trap open ob" ] ) )
	trap_open_ob = args[ "trap open ob" ];
    if( !undefinedp( args[ "trap lock ob" ] ) )
	trap_lock_ob = args[ "trap lock ob" ];
    if( !undefinedp( args[ "max volume" ] ) )
	max_volume = args[ "max volume" ];
}				/* init_static_arg() */

/*
 * 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( file_name( this_object() )[ 0..13 ] == "/obj/container" )
	return int_query_static_auto_load();
    return([ ]);
}				/* query_static_auto_load() */