/** * The base object which all races will be inherited from. * @author Pinkfish * @started 1992 sometime */ #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"; /* Original by who knows. * Modifications by Piecemaker, Ember, Presto, Deutha and Dragonkin. */ 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; private mapping _fixed_bits; mixed *bits; /* 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 ) this_object()->setup(); } /* create() */ /** * This method returns the base height of the npc type. * @return the current base height * @see set_height() */ int query_height() { return height; } /** * This method sets the base height of the npc type. * @param number the new height * @see query_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 * @see set_sight() * @see query_dark() */ 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>({ * pitch dark, * dark, * bright, * bright light * })</pre> * @param numbers as detailed above * @see query_sight() * @see query_dark() */ 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() * @see query_sight() */ int query_dark( int light ) { if ( light < sight[ 0 ] ) { return -2; } if ( light < sight[ 1 ] ) { return -1; } if ( light > sight[ 3 ] ) { return 2; } if ( light > sight[ 2 ] ) { 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 * @see set_stats() */ int *query_stats() { return copy(stats); } /** * This method sets the current default bonuses of the stats. The parameter * is an array of numbers, with the numbers corresponding to the stats as * shown below. It is highly inadvisable to set any stat bonus to less than * -6, since in conjunction with the guild stat settings this could combine * to give a negative total for that stat. Negative stats can cause * permanent unconsciousness. * <pre>({ * con, * dex, * int, * str, * wis * })</pre> * @param numbers sets the default bonuses for the stats * @see query_stats() */ void set_stats( int *numbers ) { stats = numbers; } /** * This method returns the current skin of the race. * @return the current skin of the race * @see set_skin() */ 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 * @see query_skin() */ 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 * @see query_desc() */ string query_desc( object thing ) { return capitalize( (string)thing->query_pronoun() ) +" is "+ desc +".\n"; } /* query_desc() */ /** * This method sets the description for the npc. * @param words the description of the npc * @see set_desc() */ void set_desc( string words ) { desc = words; } /** * This method returns the current limbs of the race. * @return the limbs on the race * @see find_limbs() */ string *query_limbs() { if ( !limbs ) { this_object()->find_limbs(); } return copy(limbs); } /* query_limbs() */ /** * This method figures out the current set of limbs on the race. A creature * is assigned one "limb" for every "hand" found in the body part array. The * number of limbs is the number of weapons that can be held. * @see query_limbs() */ void find_limbs() { int i; string limb; limbs = ({ }); for ( i = 0; i < sizeof( bits ); 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 * @see add_ac() */ 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 * @see query_acs() */ 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 * @see add_attack() */ 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 * @see query_attacks() */ 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 * @see add_bit() * @see query_bit() * @see query_possible_bits() * @see query_all_inedible() * @see query_all_unrottable() * @see query_all_pluckable() * @see remove_bit() */ 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 * @see add_bit() * @see query_bits() * @see remove_bit() */ 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() */ private void fixup_bits() { string* str_bits; string pl; int i; if (_fixed_bits) { return ; } _fixed_bits = ([ ]); for ( i = sizeof( bits ) - 3; i > -1; i -= 3 ) { str_bits = explode(bits[i], " "); pl = pluralize(str_bits[<1]); if (!_fixed_bits[str_bits[<1]]) { _fixed_bits[str_bits[<1]] = ({ }); } _fixed_bits[str_bits[<1]] += ({ ({ str_bits[0..<2], i, 0 }) }); if (!_fixed_bits[pl]) { _fixed_bits[pl] = ({ }); } _fixed_bits[pl] += ({ ({ str_bits[0..<2], i, 1 }) }); } } /* fixup_bits() */ /** @ignore yes * For backwards compatibility, as corpses are currently broken in * the way they call the query_possible_bits() method. */ private string *query_old_possible_bits( string word ) { int i; int j; string *possibles; possibles = ({ }); for ( i = sizeof( bits ) - 3; i > -1; i -= 3 ) { if ( ( bits[ i ] == word ) || ( bits[ i + 1 ] == word ) || !word) { if (arrayp( bits[i+2][2] )) { for ( j = 0; j < bits[i + 2][2][1]; j++) { possibles += ({ bits[ i ] }); } } else { possibles += ({ bits[ i ] }); } } } return possibles; } /* query_old_possible_bits() */ /** * This method returns the names of all the possible bits for the * npc. * @param word the bits to look for the children under * @return the array of possible bit names * @see add_bit() * @see query_bits() * @see query_possible_plural_bits() * @see remove_bit() */ string *query_possible_bits( string word ) { string* str_bits; string* adj; string *possibles; mixed* bing; if(!word) { return query_old_possible_bits( word ); } fixup_bits(); str_bits = explode(word, " "); possibles = ({ }); if (_fixed_bits[str_bits[<1]]) { adj = str_bits[0..<2]; foreach (bing in _fixed_bits[str_bits[<1]]) { if (!bing[2] && sizeof(adj) == sizeof(adj & bing[0])) { possibles += ({ bits[bing[1]] }); } } } 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 * @see add_bit() * @see query_bits() * @see query_possible_bits() * @see remove_bit() */ string *query_possible_plural_bits( string word ) { string* str_bits; string* adj; string *possibles; mixed* bing; fixup_bits(); str_bits = explode(word, " "); possibles = ({ }); if (_fixed_bits[str_bits[<1]]) { adj = str_bits[0..<2]; foreach (bing in _fixed_bits[str_bits[<1]]) { if (bing[2] && sizeof(adj) == sizeof(adj & bing[0])) { possibles += ({ bits[bing[1]] }); } } } 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 * @see query_bits() * @see remove_bit() */ 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 * @see add_bit() * @see query_bits() */ 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 npc 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 "shoulder" : 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; case "bandaid" : return 5; 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 ) { return; } 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; int num; int rand; int rating; int 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 = (int)thing->query_skill_bonus( "fighting.combat.melee.unarmed" ); number = sqrt( 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 * @see add_bit() * @see query_bits() * @see query_all_inedible() * @see remove_bit() */ 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 * @see add_bit() * @see query_bits() * @see query_all_unrottable() * @see remove_bit() */ 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 * @see add_bit() * @see query_bits() * @see query_all_pluckable() * @see remove_bit() */ 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 * @see add_bit() * @see query_bits() * @see query_eat() * @see remove_bit() */ string *query_all_inedible() { return inedible; } /* query_all_inedible() */ /** * This method returns the array of all the unrottable bits of the race. * @return all the unrottable bits * @see add_bit() * @see query_bits() * @see query_unrottable() * @see remove_bit() */ string *query_all_unrottable() { return unrottable; } /* query_all_unrottable() */ /** * This method returns the array of all the pluckable bits of the race. * @return all the pluckable bits of the race * @see add_bit() * @see query_bits() * @see query_pluckable() * @see remove_bit() */ string *query_all_pluckable() { return pluckable; } /* query_all_pluckable() */ // Very generic. These are the zones string *query_target_zones() { return ({ "head", "head", "neck", "chest", "back", "back", "back", "lower back", "left front leg", "left front leg", "stomach", "stomach", "right front leg", "right front leg", "left rear leg", "left rear leg", "right rear leg", "right rear leg",}); } string map_target_zone(string zone) { switch(zone) { case "abdomen": return "stomach"; case "hands": case "arms": return (random(2) ? "left " : "right ") + "front leg"; case "feet": case "legs": return (random(2) ? "left " : "right ") + "rear leg"; default: return zone; } } string map_armour_zone(string zone) { switch(zone) { case "thorax": case "body": case "breast": case "trunk": return "chest"; case "tail": return "back"; case "stomach": return "abdomen"; case "left arm": case "right arm": case "left front leg": case "right front leg": case "left petral fin": case "right petral fin": case "left wing": case "right wing": case "branches": return "arms"; case "left hand": case "right hand": case "left middle leg": case "right middle leg": case "left front paw": case "right front paw": return "hands"; case "left leg": case "right leg": case "left back leg": case "right back leg": case "left rear leg": case "right rear leg": case "dorsal fin": return "legs"; case "left foot": case "right foot": case "left rear paw": case "left rear paw": case "left rear foot": case "left rear foot": case "left claw": case "right claw": case "root": return "feet"; default: return zone; } }