/*
* Wonderflug, 1997.
* - Fixed weight once and for all. We have the invariant that
* contents_weight is ALWAYS correct, so we need not recalculate it
* constantly, we only need update it constantly. Also, max_weight
* may never be negative.
*/
#include "move_failures.h"
inherit "std/object";
inherit "std/basic/cute_look";
int max_weight; /* maximum allowable weight, 0 = no limit */
static int contents_weight; /* current contained weight */
void create()
{
max_weight = 0;
contents_weight = 0;
object::create();
} /* create() */
int query_max_weight() { return max_weight; }
int query_contents_weight() { return contents_weight; }
int query_empty_weight() { return ::query_weight(); }
int query_weight()
{
return contents_weight + ::query_weight();
}
/****** The following functions may, in effect, have force_ preceding their
****** names. They adjust things no matter what, and then remove any
****** inconsistencies afterward.
******/
void set_max_weight( int newmax )
{
if ( newmax < 0 )
newmax = 1;
else
max_weight = newmax;
this_object()->rebalance( ({ }) );
}
void adjust_max_weight( int amount )
{
/* if it's adjusting, and hitting 0, we have it be 1, in fact, since
* 0 is a reserved value for max_weight. It's not likely someone is
* setting the object to have unlimited weight by using adjust_max_weight
*/
if ( max_weight + amount == 0 )
return set_max_weight( 1 );
return set_max_weight( max_weight + amount );
}
/* NOT to be used in any domain code */
void adjust_contained_weight(int n)
{
contents_weight += n;
/* propogate the change to the environment */
if ( environment() )
environment()->adjust_contained_weight( n );
}
/* NOT to be used in any domain code */
void mudlib_adjust_contained_weight(int n)
{
contents_weight += n;
}
/****** This function is only to be used rarely, it is expensive, but
****** rebalances things completely. Excess objects get expelled to
****** the environment, and then the environment gets rebalanced.
****** Preferred are objects already expelled somewhere or
****** suspected to be causing the inconsistency (eg, they've just
****** changed weight). However they must already have been moved
****** into this_object(). Note cursed objects (in use and unable
****** to remove from use) will never be expelled, regardless of
****** preferred status.
******/
void rebalance( object* preferred )
{
object* stuff;
int i;
int curr_weight = contents_weight;
if ( !max_weight || max_weight >= contents_weight )
return ;
if ( !preferred )
preferred = ({ });
/* first check if throwing out some preferred stuff rebalances things */
preferred = filter_array( preferred, "check_rebalanceable", this_object() );
for ( i=0; i<sizeof( preferred ); i++ )
{
curr_weight -= preferred[i]->query_weight();
if ( max_weight >= curr_weight )
{
/* we can let everything else stay */
this_object()->rebalance_move( preferred[0..i] );
return ;
}
}
/* preferred didn't cut it, we must throw out some additional stuff now.
*/
stuff = all_inventory(this_object()) - preferred;
stuff = filter_array( stuff, "check_rebalanceable", this_object() );
for(i=0;i<sizeof(stuff);i++)
{
curr_weight -= stuff[i]->query_weight();
if ( max_weight >= curr_weight )
{
this_object()->rebalance_move( preferred + stuff[0..i] );
return ;
}
}
/* if we reach here.. we're throwing out everything and still not good
* enough. This is probably a bad sign, but may just mean they're
* overloaded with cursed items. In any case, we'll let them keep on.
*/
this_object()->rebalance_move( preferred + stuff );
}
int check_rebalanceable( object ob )
{
if ( !ob->droppable() ||
(ob->query_in_use() && !ob->set_in_use(0)) )
return 0;
return 1;
}
/* Dumps all the objects to the environment, or crushes them
* if there's no environment
*/
void rebalance_move( object* obs )
{
if ( environment() )
{
obs->mudlib_move( environment() );
environment()->rebalance( obs + ({ this_object() }) );
}
else
this_object()->rebalance_crush( obs );
}
/* Destroys all the objects; this happens when there isn't enough
* contents space for all the contents, and there is no environment
* to expell the objects to.
*/
void rebalance_crush( object* obs )
{
int i;
tell_room( this_object(), query_multiple_short(obs)+" are crushed!\n");
for( i=0; i<sizeof(obs); i++ )
if ( living(obs[i]) )
obs[i]->do_death(this_object());
else
obs[i]->dest_me();
}
/****** The permission functions. Determine whether the proposed action
****** would cause inconsistency.
******/
/*
* test_add/remove_object; these perform all the necessary checks as to
* whether an object may be added/removed from the container. Return
* a move_failures.h return code
*
* NOTE the default here, is that non-droppable items may
* be removed, and non-gettable items may be added.
* This gets overridden in living objects. (see /std/living/living.c)
*/
int test_remove_object(object ob)
{
if ( !max_weight )
return MOVE_OK;
if ( contents_weight - ob->query_weight() > max_weight )
{
/* it might be cursed objects that are the real cause of the
* overbalance; in that case, we allow objects to be removed
*/
if ( contents_weight < max_weight )
/* the object has negative weight and would overbalance us */
return MOVE_TOO_HEAVY_FOR_SOURCE;
}
if ( environment() &&
!environment()->test_adjust_contained_weight( -ob->query_weight() ) )
return MOVE_TOO_HEAVY_FOR_SOURCE;
else
return MOVE_OK;
}
int test_add_object(object ob)
{
if ( !max_weight )
return MOVE_OK;
if ( contents_weight + ob->query_weight() > max_weight )
return MOVE_TOO_HEAVY_FOR_DEST;
if ( environment() &&
!environment()->test_adjust_contained_weight( ob->query_weight() ) )
return MOVE_TOO_HEAVY_FOR_DEST;
else
return MOVE_OK;
}
/*
* test_swallow/expell_object; these work much as add/remove, but imply
* that the object is being received/moved from/to the immediate environment,
* so the weight constraints aren't the same; ie, if we asked the environment
* if adding the additional weight was ok, we would (faultily) require
* extra free space in the environment to perform the move; but in effect, the
* environment sees no change of weight. Note this is different, if there
* is a weight reduction applied. See /obj/container.c
*/
int test_swallow_object( object ob )
{
if ( !max_weight )
return MOVE_OK;
if ( contents_weight + ob->query_weight() > max_weight )
return MOVE_TOO_HEAVY_FOR_DEST;
return MOVE_OK;
}
int test_expell_object( object ob )
{
if ( !max_weight )
return MOVE_OK;
if ( contents_weight - ob->query_weight() > max_weight
&& contents_weight <= max_weight )
return MOVE_TOO_HEAVY_FOR_SOURCE;
return MOVE_OK;
}
/*
* test_adjust_contained_weight; test if the given change to the contained
* weight would cause an inconsistency. 1=no, ie allowable, 0=yes
*/
int test_adjust_contained_weight( int n )
{
if ( contents_weight + n > max_weight )
return 0;
if ( environment() )
return environment()->test_adjust_contained_weight( n );
else
return 1;
}
/****** Miscellaneous stuff */
object *find_inv_match(string str)
{
return all_inventory(this_object());
} /* find_inv_match() */
mixed stats()
{
return ::stats() +
({
({ "contents_weight", query_contents_weight(), }),
({ "max_weight", query_max_weight(), }),
});
} /* stats() */
int transfer_all_to(object dest)
{
object *ob;
int i;
ob = all_inventory(this_object());
ob -= ({ dest });
for (i=0;i<sizeof(ob);i++)
ob[i]->move(dest);
if (first_inventory(this_object()))
return 0;
return 1;
} /* transfer_all_to() */
string long(string str, int dark)
{
string ret;
ret = ::long(str, dark);
ret += calc_extra_look();
/* this is stupid. corpse.c should redefine long() itself.
*if(this_object()->query_corpse())
* ret += (string)this_object()->query_living_contents(0);
*
* else
*/
ret += query_contents(short(dark)+" contains:\n", all_inventory() -
query_labels());
return ret;
} /* long() */
/****** Autoload stuff */
/*
* Derived can be:
* -1 = unknown, check
* 0 = not derived
* 1 = derived
*/
mapping query_auto_load_attributes( int derived )
{
mapping tmp;
if ( derived < 0 )
{
if (file_name(this_object())[0..13] == "/std/container")
derived = 0;
else
derived = 1;
}
tmp = ::query_auto_load_attributes( derived );
if ( !derived )
return
([
"::" : tmp,
"max weight" : max_weight,
"inv" : "/global/auto_load"->create_auto_load(
all_inventory(this_object()) - labels ),
]);
else
return
([
"::" : tmp,
"inv" : "/global/auto_load"->create_auto_load(
all_inventory(this_object()) - labels ),
]);
}
void init_auto_load_attributes( mapping attribute_map )
{
if (attribute_map["::"])
::init_auto_load_attributes(attribute_map["::"]);
if (!undefinedp(attribute_map["max weight"]))
max_weight = attribute_map["max weight"];
if (attribute_map["inv"])
"/global/mortal"->load_auto_load(attribute_map["inv"], this_object());
} /* init_dynamic_arg() */