/**
* The base object which all races will be inherited from.
* @author Pinkfish
* @started 1992
* @changed Piecemaker, Ember, Presto, Deutha and Dragonkin.
* @changed Added attackable bodyparts code. - Sandoz, Nov. 2002.
*/
#define QUEST_AVE 140
#define QUEST_MAX 570
#define BASE 50
#define SCALE 10
#define NORM 15
#define EXPERT 20
#define A_TYPES ([ \
"claws" : "sharp", \
"chew" : "sharp", \
"beak" : "pierce", \
"bite" : "pierce", \
"horn" : "pierce", \
"hands" : "blunt", \
"feet" : "blunt", \
"tail" : "blunt", \
"hoof" : "blunt", \
"wrestle" : "blunt", \
])
inherit "/std/object";
private int height;
private int *sight;
private int *stats;
private string desc;
private string skin;
string *inedible;
string *unrottable;
string *pluckable;
private string *limbs;
private mixed *acs;
private mixed *attacks;
mixed *bits;
private mapping attackable_areas;
/* The acs array has element blocks:
*
* name, type, amount
*
* The attacks array has element blocks:
*
* name, chance, data relative to bonus of 200
*
* The bits array has element blocks:
*
* short, name, ({ parent, percentage weight, ({ file name (0 is "/std/bit"),
* amount }), child bit 1, child bit 2, ... })
*
* Note that if the size of ({ file name, amount }) part is less than two,
* it'll set the filename to default and the amount to 1
*/
void create() {
do_setup++;
::create();
do_setup--;
sight = ({ 5, 20, 200, 300 });
stats = allocate( 5 );
desc = "a small non-descript thing";
inedible = ({ });
unrottable = ({ });
pluckable = ({ });
acs = ({ });
attacks = ({ });
bits = ({ });
if( !do_setup )
TO->setup();
} /* create() */
/**
* This method returns the base height of the npc type.
* @return the current base height
*/
int query_height() { return height; }
/**
* This method sets the base height of the npc type.
* @param number the new height
*/
void set_height( int number ) { height = number; }
/**
* Thie method returns the values of the race which determine visibility.
* It returns an array of the format:<br>
* <pre>({
* dark
* pitch dark,
* bright light,
* bright
* })</pre>
* @return the sight array as detailed above
*/
int *query_sight() { return copy(sight); }
/**
* Thie method sets the current sight array for the npc. This sets the
* levels at which the race things things are too dark/too bright.<br>
* <pre>({
* dark,
* pitch dark,
* bright light,
* bright
* })</pre>
* @param numbers as detailed above
*/
void set_sight( int *numbers ) { sight = numbers; }
/**
* Determines if the race thinks it is dark or light.
* @return 0 for normal, -1 for dark, -2 for pitch dark, 1 for bright,
* 2 for toobright
* @see set_sight()
*/
int query_dark( int light ) {
if( light < sight[ 0 ] )
return -1;
if( light < sight[ 1 ] )
return -2;
if( light > sight[ 2 ] )
return 2;
if( light > sight[ 3 ] )
return 1;
return 0;
} /* query_dark() */
/**
* This method queries the default bonus stat values for the race.<br>
* <pre>({
* con,
* dex,
* int,
* str,
* wiz
* })</pre>
* @return the current stat offsets
*/
int *query_stats() { return copy(stats); }
/**
* This method sets the current default bonuses of the stats.
* <pre>({
* con,
* dex,
* int,
* str,
* wiz
* })</pre>
* @param numbers sets the default bonuses for the stats
*/
void set_stats( int *numbers ) { stats = numbers; }
/**
* This method returns the current skin of the race.
* @return the current skin of the race
*/
string query_skin() { return skin; }
/**
* This method sets the skin of the race. This is what is given back
* when the npc's corpse is skinned.
* @param word the skin of the race
*/
void set_skin( string word ) { skin = word; }
/**
* This method returns the current description of the npc.
* @param thing the object which is being described
* @return the description of the npc
*/
string query_desc( object thing ) {
return CAP( thing->HE )+" is "+desc+".\n";
} /* query_desc() */
/**
* This method sets the description for the npc.
* @param words the description of the npc
*/
void set_desc( string words ) { desc = words; }
/**
* This method returns the current limbs of the race.
* @return the limbs on the race
*/
string *query_limbs() {
if( !limbs )
TO->find_limbs();
return copy(limbs);
} /* query_limbs() */
/**
* This method figures out the current set of limbs on the race.
*/
void find_limbs() {
int i;
string limb;
limbs = ({ });
for( i = sizeof( bits ) - 3; i > -1; i -= 3 ) {
if( sscanf( bits[ i ], "%s hand", limb ) == 1 )
limbs += ({ bits[ i ] });
}
} /* find_limbs() */
/**
* The defaults acs for npcs when they are fighting unarmed.
* @return the default acs
*/
mixed *query_acs() { return copy(acs); }
/**
* Adds a default ac to the npc for when they fight unarmed. Please look
* add the armour object for more details on this.
* @param ac_name the name of the ac
* @param ac_type the type of the ac
* @param ac_amount the damage/roll stuff to take off
*/
void add_ac( string ac_name, string ac_type, mixed ac_amount ) {
acs += ({ ac_name, ac_type, ac_amount });
} /* add_ac() */
/**
* The default attacks for the npc when they are fighting unarmed.
* @return the default attacks
*/
mixed *query_attacks() { return copy(attacks); }
/**
* This method adds a default attack to the npc. Please see the
* help on the weapons for more details on this.
* @param attack_name the name of the attack
* @param attack_chance the chance of the attack occuring
* @param attack_data the damage roll for the attack
*/
void add_attack( string attack_name, int attack_chance, int *attack_data ) {
attacks += ({ attack_name, attack_chance, attack_data });
} /* add_attack() */
/**
* This method returns all the bits for the npc.
* @return the complete array of bits
*/
mixed *query_bits() { return bits; }
/**
* This method returns the bits which are children of the specified bit.
* @param word the bit to look for the children of
* @return the children bits
*/
mixed *query_bit( string word ) {
int i;
i = member_array( word, bits );
if( i == -1 || ( i % 3 ) )
return ({ });
return bits[i..i+2];
} /* query_bit() */
/**
* This method returns the names of all the possible bits for the creature.
* @param word the bits to look for the children under
* @return the array of possible bit names
*/
string *query_possible_bits( string word ) {
int i, j;
string *possibles;
possibles = ({ });
for( i = sizeof( bits ) - 3; i > -1; i -= 3 ) {
if( bits[ i ] == word || bits[ i + 1 ] == word || !word ) {
if( pointerp( bits[i+2][2] ) ) {
for( j = 0; j < bits[i+2][2][1]; j++ )
possibles += ({ bits[ i ] });
} else {
possibles += ({ bits[ i ] });
}
}
}
return possibles;
} /* query_possible_bits() */
/**
* This method returns the plural names for all the bits.
* @param word the bit to find the children of
* @return the plural names of all the bits
*/
string *query_possible_plural_bits( string word ) {
int i, j;
string *possibles;
possibles = ({ });
for( i = sizeof( bits ) - 3; i > -1; i -= 3 ) {
if( ( bits[i] && pluralize( bits[ i ] ) == word ) ||
( bits[i+1] && pluralize( bits[ i + 1 ] ) == word ) || !word ) {
if( pointerp( bits[i+2][2] ) ) {
for( j = 0; j < bits[ i + 2][2][1]; j++ )
possibles += ({ bits[ i ] });
} else {
possibles += ({ bits[ i ] });
}
}
}
return possibles;
} /* query_possible_plural_bits() */
/**
* This method adds a bit to the current bit setup.
* @param bit_short the short of the bit
* @param bit_name the name of the bit
* @param bit_array the array of children bits
*/
void add_bit( string bit_short, string bit_name, mixed *bit_array ) {
int i;
i = member_array( bit_short, bits );
if( !( i % 3 ) ) {
bits[ i ] = bit_short;
bits[ i + 1 ] = bit_name;
bits[ i + 2 ] = bit_array;
return;
}
bits += ({ bit_short, bit_name, bit_array });
} /* add_bit() */
/**
* This method removes a bit from the current bit array
* @param word the name of the bit to remove
*/
void remove_bit( string word ) {
int i;
i = member_array( word, bits );
if( i == -1 || i % 3 )
return;
bits = delete( bits, i, 3 );
} /* remove_bit() */
/**
* This method figures out a modifier for the height based on the base
* weight and height.
* @param number the height/weight to modify
* @return the modified heigh/weight
*/
int modifier( int number ) {
return ( number * ( roll_MdN( 20, 21 ) - 220 ) ) / 2000;
} /* modifier() */
/**
* This method is called when the npc is first created. This sets up
* the weight/height/stats etc.
* @param thing the nmpc being created
*/
void start_player( object thing ) {
if( !thing->query_base_weight() )
thing->set_base_weight( 1 + weight + modifier( weight ) );
if( !thing->query_height() )
thing->set_height( 1 + height + modifier( height ) );
thing->adjust_bonus_con( stats[ 0 ] );
thing->adjust_bonus_dex( stats[ 1 ] );
thing->adjust_bonus_int( stats[ 2 ] );
thing->adjust_bonus_str( stats[ 3 ] );
thing->adjust_bonus_wis( stats[ 4 ] );
thing->reset_all();
if( skin )
thing->set_skin( skin );
} /* start_player() */
/**
* This method figures out how many of each sort of thing
* can be worn.
* @param type the type to check for numbers worn
* @return the number of type to worn
*/
int query_number_worn( string type ) {
switch ( type ) {
case "armband" :
return 2;
case "badge" :
return 15;
case "bracelet" :
return 4;
case "earring" :
return 2;
// added by ceres since piercing isn't implemented yet.
// return (int)previous_object()->query_piercings( "ear" );
case "garter" :
return 2;
case "necklace" :
return 5;
case "ring" :
return 8;
case "sash" :
return 2;
case "shirt" :
return 2;
case "belt scabbard" :
return 2;
case "small scabbard" :
return 2;
default :
return 1;
}
} /* query_number_worn() */
int query_number_wielded( string type ) { return 1; }
int query_skill_bonus( int number, string word ) { return 0; }
int player_start( object thing ) { return 1; }
int player_quit( object thing ) { return 1; }
/**
* This method sets the level of the npc.
* @param thing the npc to set the level of
* @param level the current level
*/
void set_level( object thing, int level ) {
int i;
if( thing ) {
for( i = sizeof( acs ) - 3; i > -1; i -= 3 )
thing->add_ac( acs[ i ], acs[ i + 1 ], acs[ i + 2 ] );
}
} /* set_level() */
int *calc_attack( int *data, int number ) {
int base, num, rand, rating, scale;
base = data[ 0 ] * ( BASE + SCALE * number );
num = data[ 1 ];
rand = data[ 2 ] * ( BASE + SCALE * number );
rating = QUEST_AVE * ( base + num * rand ) +
QUEST_MAX * ( base + ( num * ( 1 + rand ) ) / 2 );
scale = BASE + SCALE * NORM;
rating /= scale;
rating = ( ( BASE + SCALE * EXPERT ) * rating + QUEST_AVE * QUEST_MAX ) /
( 2 * QUEST_AVE * QUEST_MAX );
if( rating > scale )
scale = ( rating + scale ) / 2;
return ({ base / scale, num, rand / scale });
} /* calc_attack() */
/**
* This method sets up the unarmed attacks on the npc.
* @param thing the npc to setup the attacks on
*/
void set_unarmed_attacks( object thing ) {
int i, number;
number = thing->query_skill_bonus("fighting.combat.melee.unarmed");
number = to_int( sqrt( to_float( number ) ) );
for ( i = sizeof( attacks ) - 3; i > -1; i -= 3 ) {
thing->remove_attack( attacks[ i ] );
thing->add_attack( attacks[ i ], attacks[ i + 1 ],
calc_attack( attacks[ i + 2 ], number ), A_TYPES[ attacks[ i ] ],
"unarmed", "unarmed_"+ attacks[ i ] );
}
} /* set_unarmed_attacks() */
/**
* This method checks to see if the specified bit of the npc
* is edible or not.
* @param bit the bit to check
* @return 1 if the bit is edible, 0 if not
*/
int query_eat( string bit ) {
if( member_array( bit, inedible) == -1 )
return 1;
return 0;
} /* query_eat() */
/**
* This method checks to see if the bit is unrottable. ie:
* a bit of skeleton.
* @param bit the bit to check
* @return 1 if unrottable, 0 if rottable
*/
int query_unrottable( string bit ) {
if( member_array( bit, unrottable) == -1 )
return 0;
return 1;
} /* query_unrottable() */
/**
* This method checks to see if the bit is pluckable.
* @param bit the bit to check
* @return 1 if pluckable, 0 if not
*/
int query_pluckable( string bit ) {
if( member_array( bit, pluckable) == -1 )
return 0;
return 1;
} /* query_pluckable() */
/**
* This method returns the array of all the inedible bits of the
* race.
* @return all the inedible bits
*/
string *query_all_inedible() { return inedible; }
/**
* This method returns the array of all the unrottable bits of
* the race.
* @return all the unrottable bits
*/
string *query_all_unrottable() { return unrottable; }
/**
* This method returns the array of all the pluckable vits of the
* the race.
* @return all the pluckable bits of the race
*/
string *query_all_pluckable() { return pluckable; }
/**
* This method adds an attackable are to the creatures attackable areas list.
* This is used mainly by the combat effect to get a list of bodyparts that
* can be hit on the creature. The bits argument can be either a string
* (single bit in an area), or an array of bit names.
* @param area the name of the area as used to AC calculations, etc.
* @param attackable the bits that belong to that area
* (and can be hit by weapons)
*/
void add_attackable_area( string area, mixed attackables ) {
if( !mapp(attackable_areas) )
attackable_areas = ([ ]);
if( undefinedp( attackable_areas[area] ) ) {
if( pointerp(attackables) )
attackable_areas[area] = attackables;
else
attackable_areas[area] = ({ attackables });
} else {
error("Attackable areas for "+area+" already set.\n");
}
} /* add_attackable_area() */
/**
* This method removes an attackable area from the race's list of
* attackable areas.
* @param area the name of the attackable area to remove
*/
void remove_attackable_area( string area ) {
if( !undefinedp( attackable_areas[area] ) )
map_delete( attackable_areas, area );
} /* remove_attackable_area() */
/**
* This method returns a mapping of areas that this creature has in the
* form of ([ area_name : ({ bodypart1, bodypart2 }) ]).
* @return a mapping of attackable body areas
*/
mapping query_attackable_areas() {
if( !mapp(attackable_areas) )
error("No attackable areas set.\n");
// Don't return a copy, because this would be costly,
// and it is used quite often.
return attackable_areas;
} /* query_attackable_areas() */
/**
* This method can be used to reset the attackable areas.
* It will set the attackable areas to 0.
*/
protected void reset_attackable_areas() { attackable_areas = 0; }