/**
* This file contains all the armour related code for the living
* objects.
* @author Pinkfish
*/
inherit "/std/armour_logic";
#define IMMUNE_TYPES ({"sharp", "blunt", "pierce", "magic", "fire", \
"ice", "self"})
nosave string skin;
nosave object *wearing;
nosave mixed stopped;
private string *_immuneto;
void create() {
skin = "morphological field";
wearing = ({ });
::create();
} /* create() */
/**
* This method returns the types of attacks that this thing is immune to.
* @return the list of attacks this thing is immune to
* @see add_immune(), remove_immune()
*/
string *query_immune() { return _immuneto || ({ }); }
/**
* This method removes an immune type from the list of damage types
* this creature is immune to.
* @param str the damage type to remove from the immunity list
* @see query_immune(), add_immune()
*/
void remove_immune( string str ) {
if( !sizeof(_immuneto) )
return;
_immuneto -= ({ str });
} /* remove_immune() */
/**
* This method adds a type of attack to the list of things this
* object is immune to.
* @param type the type or array of types of damage to add to
* the immune list
* @see query_immune(), remove_immune()
*/
void add_immune( mixed type ) {
if( !pointerp( _immuneto ) )
_immuneto = ({ });
if( pointerp( type ) )
foreach( string str in type )
add_immune( str );
if( stringp( type ) && member_array( type, IMMUNE_TYPES ) != -1 &&
member_array( type, _immuneto ) == -1 )
_immuneto += ({ type });
} /* add_immune() */
/**
* This method returns the current skin of the living object.
* @return the skin of the object
* @see set_skin()
*/
string query_skin() { return skin; }
/**
* This method sets the current skin of the living object. This can
* be done by the race object and by specific spells or effects.
* @param word the new skin type
* @see query_skin()
*/
void set_skin( string word ) { skin = word; }
/**
* This method returns all the objects you are currently wearing.
* @return the current array of worn objects
* @return the array of worn stuff
* @see query_armours()
*/
object *query_wearing() { return copy( wearing -= ({ 0 }) ); }
/**
* This method returns all the armours that the is currently being
* worn. This will always return the same value as query_wearing()
* @see query_wearing()
* @return the array of armours
*/
object *query_armours() { return query_wearing(); }
/**
* This method will make sure all the armours are unworn by the living
* object.
* @see query_wearing()
*/
void clear_armours() {
wearing->set_worn_by( 0 );
wearing = ({ });
} /* clear_armours() */
/**
* This returns the object which stopped the blow. This is only valid
* inside and after a query_ac() call.
* @return the object which stopped the call
*/
mixed query_stopped() {
return stopped;
} /* query_stopped() */
/**
* This method sets the object which stops the call. This should be
* used to do weird stuff. I have no idea what it should be used for
* at all and I am just rambling.
* @param arg the new value of the stopped object
*/
void set_stopped( mixed arg ) { stopped = arg; }
/**
* This method checks to see if the two armour types are
* equivilant or not.
* @param comp the first type
* @param type the second type
*/
protected int equivalent_armour_types( mixed comp, string type ) {
string word;
if( stringp( comp ) ) {
if( CLOTHING_H->query_equivilant_type( comp ) )
return CLOTHING_H->query_equivilant_type( comp ) == type;
return comp == type;
}
foreach( word in comp ) {
if( equivalent_armour_types( word, type ) )
return 1;
}
return 0;
} /* equivalent_armour_types() */
/**
* This is the method used to get the living object to wear a piece of
* armour or clothing. This is called from inside the armour or clothing
* code itself. It will call the function set_worn_by() on the
* armour or clothing and if this returns a non-zero result then it
* will add it into the current list of worn types. Assuming the
* type of the armour fits into the allowed list. It will also
* call the functon hold_item on the object if it is required to be
* held as well (ie: shield).
* @param armour the armour to wear
* @param doing_hold if this is called by the hold command
* @return the failure message, or 0 if ok
* @see remove_armour()
* @see query_armours()
* @see query_wearing()
*/
string wear_armour( object armour, int doing_hold ) {
string type, word;
mapping things;
object thing;
int no_limbs;
mixed types;
wearing -= ({ 0 });
types = armour->query_type();
if( word = TO->not_allowed_to_wear( types ) )
return word;
if( member_array( armour, wearing ) != -1 )
return "already wearing";
if( stringp(types) )
types = ({ types });
if( !pointerp( types ) )
return "oh dear";
things = ([ ]);
foreach( type in types ) {
if( CLOTHING_H->query_equivilant_type( type ) )
type = CLOTHING_H->query_equivilant_type( type );
things[ type ] = ({ });
foreach( thing in wearing ) {
if( equivalent_armour_types( (mixed)thing->query_type(), type ) )
things[ type ] += ({ thing });
}
}
word = TO->query_race_ob();
foreach( type in keys( things ) ) {
if( word->query_number_worn(type) <= sizeof( things[type] ) ) {
return "since you are already wearing "+
query_multiple_short( things[ type ] );
}
}
if( ( no_limbs = armour->query_no_limbs() ) && !doing_hold ) {
if( member_array( armour, (object *)TO->query_holding() ) != -1 )
return "already wearing";
if( sizeof( (int *)armour->hold_item( TO, -1 ) ) )
return 0;
else
return "already holding";
}
if( !armour->set_worn_by( TO ) )
return "oh dear";
wearing += ({ armour });
call_out("calc_burden", 1 );
return 0;
} /* wear_armour() */
/**
* This method will remove the armour from the living object.
* @param thing the armour to remove
* @return 1 if was unsuccessful and 0 if it was successful
* @see wear_armour()
* @see query_armours()
* @see query_wearing()
*/
int remove_armour( object thing ) {
int i;
wearing -= ({ 0 });
if( ( i = member_array( thing, wearing ) ) == -1 )
return 1;
if( !thing->set_worn_by( 0 ) )
return 1;
if( member_array( thing, TO->query_holding() ) != -1 )
if( !sizeof( TO->set_unhold(thing) ) )
return 1;
wearing = delete( wearing, i, 1 );
call_out("calc_burden", 1 );
return 0;
} /* remove_armour() */
/**
* This function returns the amount of damage that is blocked
* my the armour on a specified zone. It automatically
* damages the armour, and sets the stopped object to be the
* the object (piece of armour) that stopped the blow.
*
* @param type the type of damage, eg: "sharp", "blunt", "pierce"
* @param amount the amount of damage that is being done
* @param zone the zone which the damage is being done through, eg: "head"
* @return the amount of damage that will be blocked
* @see query_wearing()
*/
varargs int query_ac( string type, int amount, string zone ) {
object thing, *defenses;
int part, total;
string word;
stopped = 0;
if( sizeof(_immuneto) && member_array( type, _immuneto ) != -1 ) {
stopped = skin;
return amount;
}
// This is used by magic and poisons, etc. so the identity of
// "stopped" is irrelevant, since it bypasses all armour.
if( zone == "self")
return ::query_ac( type, amount, zone );
if( !zone ) {
switch( random(20) ) {
case 0..1: zone = "head"; break;
case 2: zone = "neck"; break;
case 3..6: zone = "chest"; break;
case 7..9: zone = "abdomen"; break;
case 10..12: zone = "arms"; break;
case 13..14: zone = "hands"; break;
case 15..18: zone = "legs"; break;
case 19: zone = "feet"; break;
}
}
if(CLOTHING_H->query_clothing_zone( zone )){
foreach ( word in CLOTHING_H->query_clothing_zone( zone ) ) {
defenses = wearing -= ({ 0 });
defenses += uniq_array( filter( TO->query_holding(),
(: $1 && $1->query_armour() :) ) );
foreach( thing in defenses ) {
if( equivalent_armour_types( thing->query_type(), word ) ) {
part = thing->query_ac( type, amount, zone );
if( !stopped && part )
stopped = thing;
total += part;
amount -= part;
}
if( amount < 1 )
break;
}
if( amount < 1 )
break;
}
}
if( amount > 0 ) {
if( !stopped )
stopped = skin;
if( type == "fire" && TO->effects_matching("body.wetness") &&
sizeof( TO->effects_matching("body.wetness") ) )
TO->add_effect("/std/effects/other/wetness", -amount );
part = ::query_ac( type, amount, zone );
total += part;
amount -= part;
}
// This is to not let the AC become higher than the amount.
if( amount < 0 )
total += amount;
return total;
} /* query_ac() */
/** @ignore yes */
mixed stats() {
return ::stats() + ({ ({"skin", skin }) });
} /* stats() */