/*___________________________________________________________________________* )()( DalekenMUD 1.12 (C) 2000 )()( `][' by Martin Thomson, Lee Brooks, `][' || Ken Herbert and David Jacques || || ----------------------------------------------------------------- || || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, || || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse. || || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael || || Chastain, Michael Quan, and Mitchell Tse. || || Original Diku Mud copyright (C) 1990, 1991 || || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt, || || Tom Madsen, and Katja Nyboe. || || ----------------------------------------------------------------- || || Any use of this software must follow the licenses of the || || creators. Much time and thought has gone into this software and || || you are benefitting. We hope that you share your changes too. || || What goes around, comes around. || || ----------------------------------------------------------------- || || handler.c || || General functions for manipulation of stuff. || *_/<>\_________________________________________________________________/<>\_*/ #include <math.h> #include "mud.h" #include "event.h" /* * Local functions. */ /* * Retrieve a character's trusted level for permission checking. */ int get_trust( CHAR_DATA *ch ) { if( ch->desc && ch->desc->original ) ch = ch->desc->original; if( ch->trust != 0 ) return ch->trust; if( IS_NPC( ch ) && ch->level >= LEVEL_HERO ) return LEVEL_HERO - 1; else return UMAX( 1, ch->level ); } /* * Retrieve a character's age. * (428400 = 30 secs/mud hour * 24 hours/day * 35 days/month * 17 months/year * - Kahn) */ int get_age( CHAR_DATA *ch ) { if( IS_NPC( ch ) ) return 15 + ch->level; if( race_table[ch->race].base_age ) return race_table[ch->race].base_age + ( ch->played + (int)( current_time - ch->logon ) ) / 428400; return 0; } int get_stat_mods( CHAR_DATA * ch, int apply ) { OBJ_DATA *obj; AFFECT_DATA *af; int amnt = 0; for( af = ch->affected; af; af = af->next ) { if( af->deleted || af->location != apply ) continue; if( af->type == gsn_continuous_effect || af->type == gsn_racial_fatigue || af->type == gsn_religious || af->type == gsn_delayed_effect ) continue; amnt -= af->modifier; } for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->deleted || obj->wear_loc == WEAR_NONE ) continue; for( af = obj->affected; af; af = af->next ) { if( af->deleted || af->location != apply ) continue; if( af->type == gsn_continuous_effect || af->type == gsn_racial_fatigue || af->type == gsn_religious || af->type == gsn_delayed_effect ) continue; amnt -= af->modifier; } for( af = obj->pIndexData->affected; af; af = af->next ) { if( af->deleted || af->location != apply ) continue; if( af->type == gsn_continuous_effect || af->type == gsn_racial_fatigue || af->type == gsn_religious || af->type == gsn_delayed_effect ) continue; amnt -= af->modifier; } } return amnt; } int get_max_hit( CHAR_DATA *ch ) { return ch->max_hit + get_stat_mods( ch, APPLY_HIT ); } int get_max_mana( CHAR_DATA *ch, int which ) { switch( which ) { case MAGIC_AIR: return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_AIR ); case MAGIC_EARTH: return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_EARTH ); case MAGIC_FIRE: return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_FIRE ); case MAGIC_SPIRIT: return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_SPIRIT ); case MAGIC_WATER: return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_WATER ); } bug( "Getting max mana %d for %s.", which, ch->name ); return 0; } int get_max_move( CHAR_DATA *ch ) { return ch->max_move + get_stat_mods( ch, APPLY_MOVE ); } /* * Retrieve character's current strength. */ int get_curr_str( CHAR_DATA *ch ) { int max = 5; if( IS_NPC( ch ) ) { max = ( int )log( ( double )ch->level ); if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_STR ) max += max / 2; return 15 + race_table[ch->race].str_mod + max; } if( class_table[ch->class].attr_prime == APPLY_STR ) max += 2; if( get_first_class( ch ) != CLASS_NONE && class_table[get_first_class( ch )].attr_prime == APPLY_STR ) max++; return ch->pcdata->perm_str + UMIN( ch->pcdata->mod_str, max ); } /* * Retrieve character's current intelligence. */ int get_curr_int( CHAR_DATA *ch ) { int max = 5; if( IS_NPC( ch ) ) { max = ( int )log( ( double )ch->level ); if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_INT ) max += max / 2; return 15 + race_table[ch->race].int_mod + max; } if( class_table[ch->class].attr_prime == APPLY_INT ) max += 2; if( get_first_class( ch ) != CLASS_NONE && class_table[get_first_class( ch )].attr_prime == APPLY_INT ) max++; return ch->pcdata->perm_int + UMIN( ch->pcdata->mod_int, max ); } /* * Retrieve character's current wisdom. */ int get_curr_wis( CHAR_DATA *ch ) { int max = 5; if( IS_NPC( ch ) ) { max = ( int )log( ( double )ch->level ); if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_WIS ) max += max / 2; return 15 + race_table[ch->race].wis_mod + max; } if( class_table[ch->class].attr_prime == APPLY_WIS ) max += 2; if( get_first_class( ch ) != CLASS_NONE && class_table[get_first_class( ch )].attr_prime == APPLY_WIS ) max++; return ch->pcdata->perm_wis + UMIN( ch->pcdata->mod_wis, max ); } /* * Retrieve character's current dexterity. */ int get_curr_dex( CHAR_DATA *ch ) { int max = 5; if( IS_NPC( ch ) ) { max = ( int )log( ( double )ch->level ); if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_DEX ) max += max / 2; return 15 + race_table[ch->race].dex_mod + max; } if( class_table[ch->class].attr_prime == APPLY_DEX ) max += 2; if( get_first_class( ch ) != CLASS_NONE && class_table[get_first_class( ch )].attr_prime == APPLY_DEX ) max++; return ch->pcdata->perm_dex + UMIN( ch->pcdata->mod_dex, max ); } /* * Retrieve character's current constitution. */ int get_curr_con( CHAR_DATA *ch ) { int max = 5; if( IS_NPC( ch ) ) { max = ( int )log( ( double )ch->level ); if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_CON ) max += max / 2; return 15 + race_table[ch->race].con_mod + max; } if( class_table[ch->class].attr_prime == APPLY_CON ) max += 2; if( get_first_class( ch ) != CLASS_NONE && class_table[get_first_class( ch )].attr_prime == APPLY_CON ) max++; return ch->pcdata->perm_con + UMIN( ch->pcdata->mod_con, max ); } /* * Retrieve character's current magic stat. */ int get_magic( CHAR_DATA *ch, int sphere ) { if( IS_NPC( ch ) ) return (int)log( ch->level ) + ch->level / 50; return ch->pcdata->perm_magic[sphere] + UMIN( ch->pcdata->mod_magic[sphere], 5 ); } /* array summer for magic/mana stats */ int total_mana( int *arr ) { int i, tot = 0; for( i = 0; i < MAGIC_MAX; ++i ) tot += arr[i]; return tot; } int get_magic_resist( CHAR_DATA *ch ) { return ch->saving_throw + race_table[ch->race].magic_resist; } /* * Retrieve character's current resilience. */ int get_curr_resil( CHAR_DATA *ch ) { int resil; resil = race_table[ch->race].resil + ch->resil_mod; if( !IS_NPC( ch ) ) resil -= ch->pcdata->condition[COND_DRUNK] / 10; return resil; } /* * Retrieve character's current body temperature. */ int get_curr_temp( CHAR_DATA *ch ) { return race_table[ch->race].body_temp + URANGE( -100, ch->temp_mod, 100 ); } /* * How hot/cold is it in here? */ int get_room_temp( ROOM_INDEX_DATA *room ) { int temp; if( !room ) return room->area->plane->weather.temperature; temp = room->area->ave_temp; if( !IS_SET( room->room_flags, ROOM_UNDERGROUND ) ) temp += room->area->plane->weather.temperature; if( IS_SET( room->room_flags, ROOM_HOT ) ) temp += 40; if( IS_SET( room->room_flags, ROOM_COLD ) ) temp -= 40; switch( room->sector_type ) { case SECT_MOUNTAIN: temp -= 10; break; case SECT_WATER_SWIM: case SECT_WATER_NOSWIM: case SECT_UNDERWATER: temp /= 2; break; case SECT_AIR: temp = temp * 3 / 2; break; case SECT_DESERT: temp += 15; break; case SECT_SPACE: temp -= 32; break; default: break; } if( IS_SET( room->room_flags, ROOM_FLOODED ) ) temp /= 2; return temp + dice( 2, 3 ) - 4; } /* * Retrieve character's current hitroll */ int get_hitroll( CHAR_DATA *ch ) { int hitroll = ch->hitroll; if( !IS_NPC( ch ) ) { hitroll += ch->level * 2 / 3; hitroll += power( 5, 10, get_curr_str( ch ) - 15 ) - 4; hitroll += get_speed( ch ) / 2; } if( IS_AFFECTED( ch, AFF_BLEEDING ) ) hitroll = hitroll * 9 / 10; return hitroll; } /* * Retrieve character's current damroll */ int get_damroll( CHAR_DATA *ch ) { int damroll; damroll = ch->damroll; damroll += UMAX( -1, power( 9, 9, get_curr_str( ch ) - 15 ) - 8 ); if( IS_NPC( ch ) ) damroll += ch->level / 2; if( IS_AFFECTED( ch, AFF_BLEEDING ) ) damroll -= 10; return damroll; } /* * Speed. * Bonus: dex (if awake). * Penalty: drunkeness / 10, weight carried over half maximum. * Opponent level affects your speed. */ int get_speed( CHAR_DATA *ch ) { int init; int tmp; init = ch->speed; if( IS_AWAKE( ch ) ) { init -= 15; if( IS_NPC( ch ) ) init += 12 + race_table[ch->race].dex_mod + ch->level / 3; else init += ch->pcdata->perm_dex + ch->pcdata->mod_dex; } if( ch->fighting ) init += ch->level - ch->fighting->level; if( !IS_NPC( ch ) ) { init -= ch->pcdata->condition[COND_DRUNK] / 10; tmp = ch->carry_weight * 1000 / UMAX( 1, can_carry_w( ch ) ); tmp = URANGE( 0, tmp, 2500 ); if( tmp > 600 ) /* over 60% of weight limit */ init -= ( tmp - 600 ) / 20; } return init; } int get_size( CHAR_DATA *ch ) { return race_table[ch->race].size + ch->size_mod; } int get_success( CHAR_DATA *ch, int sn, int percent ) { int hehe; if( IS_AFFECTED( ch, AFF_MIND_MIST ) ) return 0; if( IS_NPC( ch ) ) { if( ch->class == CLASS_NONE ) hehe = 85; else if( can_prac( ch, sn ) ) hehe = class_table[ch->class].skill_adept; else hehe = 0; if( number_percent( ) <= hehe ) return hehe; else return 0; } else if( IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) ) return 80; if( ch->pcdata->learned[sn] < 25 ) hehe = 0; else { hehe = 22 - ch->pcdata->perm_int + UMIN( 7, ch->pcdata->mod_int ); hehe = power( 900, 7, hehe ); hehe += skill_table[sn].usage * hehe / 650; hehe = number_range( 1, hehe ); } if( ch->pcdata->learned[sn] * percent / 100 >= number_percent( ) ) { if( hehe == 1 ) { send_to_char( "&BYou feel a faint tingling sensation.&n\n\r", ch ); act( "&BYou learned something about $t!", ch, skill_table[sn].name, NULL, TO_CHAR ); ch->pcdata->learned[sn] += 1; } return ch->pcdata->learned[sn]; } else if( hehe == 1 ) { send_to_char( "&BYou feel a faint tingling sensation.&n\n\r", ch ); act( "&BYou learn from your mistakes and you learn about $t!", ch, skill_table[sn].name, NULL, TO_CHAR ); ch->pcdata->learned[sn] += 1; } return 0; } bool can_prac( CHAR_DATA *ch, int sn ) { int i; if( IS_NPC( ch ) ) { if( ( ch->class == CLASS_NONE && skill_table[sn].skill_level[0] <= L_APP ) || ( ch->class != CLASS_NONE && skill_table[sn].skill_level[ch->class] <= ch->level ) ) return TRUE; return FALSE; } if( get_trust( ch ) >= skill_table[sn].skill_level[ch->class] || IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) ) return TRUE; for( i = 0; i < NUM_MULTI_CLASS; ++i ) { if( ch->level < skill_table[sn].skill_level[i] ) continue; if( ch->pcdata->multi_class[i] == CLASS_ASPIRING && ch->sublevel >= skill_table[sn].skill_level[i] ) return TRUE; if( ch->level == LEVEL_HERO && ch->sublevel / 4 >= skill_table[sn].skill_level[i] ) return TRUE; if( ch->pcdata->multi_class[i] >= CLASS_ADEPT && ch->level >= skill_table[sn].skill_level[i] ) return TRUE; } return FALSE; } bool can_use( CHAR_DATA *ch, int sn ) { if( ( !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) ) || ( can_prac( ch, sn ) && ( IS_NPC( ch ) || ch->pcdata->learned[sn] > 0 ) ) ) return TRUE; return FALSE; } int get_first_class( CHAR_DATA *ch ) { int i; if( IS_NPC( ch ) ) return CLASS_NONE; for( i = 0; i < AVAIL_CLASS; ++i ) { if( ch->pcdata->multi_class[i] == CLASS_FIRST ) return i; } return CLASS_NONE; } int get_second_class( CHAR_DATA *ch ) { int i; if( IS_NPC( ch ) ) return CLASS_NONE; for( i = 0; i < AVAIL_CLASS; ++i ) { if( ch->pcdata->multi_class[i] == CLASS_SECOND ) return i; } return CLASS_NONE; } int get_aspire_class( CHAR_DATA *ch ) { int i; if( IS_NPC( ch ) ) return -1; for( i = 0; i < AVAIL_CLASS; ++i ) { if( ch->pcdata->multi_class[i] == CLASS_ASPIRING ) return i; } return -1; } /* * Finds lord level, used for do_who after this */ int get_lord_level( CHAR_DATA *ch ) { int i, total; total = 0; for( i = 0; i < AVAIL_CLASS; ++i ) { if( ch->pcdata->multi_class[i] == CLASS_ADEPT || ch->pcdata->multi_class[i] == CLASS_FIRST ) total++; } return total; } /* * race_tnl(). * This function uses fine precision and a while lot of constants * to figure out how much a race should need to level. * The constants here could be jiggled a little to discretion but be * wary of the effect it would have on ALL tnls. * --Symposium */ #define TWIST( diff ) \ if( (diff) > 0 ) \ d = 1.045; \ else \ d = 1.028; \ d = pow( d, (diff) ); \ tnl = d * tnl; #define SHAKE( diff, scale ) \ d = pow( (scale), (diff) ); \ tnl = d * tnl; int race_tnl( int race ) { /* This static array allows this function to only be called once. */ static int tnls[MAX_RACE]; int i; float tnl = 1003270; /* magic number, this is the base effect */ const int race_perm[][2] = { { RACE_BREATHING, 1062 }, { RACE_INFRAVISION, 1015 }, { RACE_DETECT_INVIS, 1025 }, { RACE_DETECT_HIDDEN, 1020 }, { RACE_DETECT_ALIGN, 1007 }, { RACE_PROTECTION, 1100 }, { RACE_SANCT, 1500 }, { RACE_NO_WEAPON_WIELD, 885 }, { RACE_MUTE, 700 }, { RACE_NO_SUN, 930 }, { RACE_DUAL_WIELD, 1050 }, { RACE_DARK_SIGHT, 1042 }, { RACE_NO_POISON, 1060 }, { RACE_SWIM, 1030 }, { 0, 0 }, }; /* * Body parts. * less than 1000 will be multiplied if the part isn't there */ const int race_part[][2] = { { BODY_PART_TORSO, 875 }, { BODY_PART_HEAD, 990 }, { BODY_PART_EYES, 800 }, { BODY_PART_NOSE, 995 }, { BODY_PART_EAR_L, 995 }, { BODY_PART_EAR_R, 995 }, { BODY_PART_ARM_L, 960 }, { BODY_PART_ARM_R, 960 }, { BODY_PART_HAND_L, 910 }, { BODY_PART_HAND_R, 910 }, { BODY_PART_FINGERS_L, 910 }, { BODY_PART_FINGERS_R, 910 }, { BODY_PART_LEG_L, 940 }, { BODY_PART_LEG_R, 940 }, { BODY_PART_FOOT_L, 960 }, { BODY_PART_FOOT_R, 970 }, { BODY_PART_TAIL, 1020 }, { BODY_PART_HORNS, 1025 }, { BODY_PART_WINGS, 1040 }, { BODY_PART_LUNGS, 1050 }, { BODY_PART_GILLS, 950 }, { 0, 0 }, }; const char *sk; char buf[MAX_INPUT_LENGTH]; int sn, lowest; float d; if( tnls[race] != 0 ) return tnls[race]; sk = race_table[race].racial_skill; while( sk && sk[0] != '\0' ) { sk = one_argument( sk, buf ); sn = skill_lookup( buf ); lowest = LEVEL_HERO; for( i = 0; i < MAX_CLASS; ++i ) if( skill_table[sn].skill_level[i] < lowest ) lowest = skill_table[sn].skill_level[i]; tnl += lowest * 400; } for( i = 0; race_perm[i][0] != 0; i++ ) { if( IS_SET( race_table[race].race_abilities, race_perm[i][0] ) ) { tnl *= race_perm[i][1]; tnl /= 1000; } } for( i = 0; race_part[i][0] != 0; i++ ) { if( ( race_part[i][1] < 1000 && !IS_SET( race_table[race].body_parts, race_part[i][0] ) ) || ( race_part[i][1] > 1000 && IS_SET( race_table[race].body_parts, race_part[i][0] ) ) ) { tnl *= race_part[i][1]; tnl /= 1000; } } TWIST( race_table[race].str_mod ); TWIST( race_table[race].int_mod ); TWIST( race_table[race].wis_mod ); TWIST( race_table[race].dex_mod ); TWIST( race_table[race].con_mod ); SHAKE( race_table[race].size - 30, 1.002 ); SHAKE( race_table[race].hp_gain, 1.01 ); for( i = 0; i < MAGIC_MAX; ++i ) { if( race_table[race].mana_gain[i] > 0 ) { SHAKE( race_table[race].mana_gain[i], 1.0025 ); } else { SHAKE( race_table[race].mana_gain[i], 1.00125 ); } } SHAKE( race_table[race].move_gain, 1.003 ); SHAKE( ( race_table[race].magic_resist ) / 3.1, 1.01 ); SHAKE( ( 1000 - race_table[race].resil ) / 12.2, 1.0162 ); tnls[race] = ((int)tnl) / 1000; return tnls[race]; } #undef TWIST int get_tnl( CHAR_DATA *ch ) { int i; int tnl = race_tnl( ch->race ); if( IS_NPC( ch ) ) return tnl * 100; if( ch->class == CLASS_BUILDER || ch->class == CLASS_ANGEL ) return 100000; for( i = 0; i < 5; i++ ) { switch( ch->pcdata->multi_class[i] ) { case CLASS_FIRST: if( get_second_class( ch ) == CLASS_NONE ) tnl = ( tnl * 4 ) / 3; break; case CLASS_ADEPT: case CLASS_SECOND: tnl = ( tnl * 5 ) / 3; break; default: break; } } if( ch->level == LEVEL_HERO ) tnl += tnl * ch->sublevel / 100; return tnl * 100; } /* * Retrieve a character's carry capacity. */ int can_carry_n( CHAR_DATA *ch ) { if( !IS_NPC( ch ) && ch->level >= L_APP ) return 1000; if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_PET ) ) return 0; return MAX_WEAR + get_curr_dex( ch ) / 2 + get_curr_str( ch ); } /* * Retrieve a character's carry capacity. */ int can_carry_w( CHAR_DATA *ch ) { if( !IS_NPC( ch ) && ch->level >= L_APP ) return 1000000; if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_PET ) ) return 0; return UMIN( power( 500, 15, get_curr_str( ch ) - 20 ), 999 ); } /* checks of ch is the owner of obj */ bool is_owner( CHAR_DATA *ch, OBJ_DATA *obj ) { char buf[MAX_INPUT_LENGTH]; if( !IS_SET( obj->extra_flags, ITEM_OWNER ) ) return FALSE; sprintf( buf, " {%s}", ch->name ); if( !str_infix( buf, obj->name ) ) return TRUE; return FALSE; } /* * Special is_name for objects that considers the unique key. */ bool is_obj_name( OBJ_DATA *obj, const char *str ) { if( str[0] == '#' ) { if( LOWER( str[1] ) == 'o' ) return ( atoi( &str[2] ) == obj->unique_key ) ? TRUE : FALSE; return ( atoi( &str[1] ) == obj->unique_key ) ? TRUE : FALSE; } return is_name( str, obj->name ); } /* * Special is_name for characters that considers the unique key. */ bool is_char_name( CHAR_DATA *ch, const char *str ) { if( str[0] == '#' ) { if( LOWER( str[1] ) == 'o' ) return ( atoi( &str[2] ) == ch->unique_key ) ? TRUE : FALSE; return ( atoi( &str[1] ) == ch->unique_key ) ? TRUE : FALSE; } return is_name( str, ch->name ); } /* * See if a string is one of the names of an object. * New is_name sent in by Alander. * Modified by Symposium to allow greater definition * Now only the prefix is matched and it also allows * multiple word matches. ie "wh gu" matches "white guard" */ bool is_name( const char *str, const char *namelist ) { const char *nextkey = str; char keybuf[MAX_INPUT_LENGTH]; const char *nextpat; char patbuf[MAX_INPUT_LENGTH]; bool wordmatch; do { nextkey = one_argument( nextkey, keybuf ); if( keybuf[0] == '\0' ) return TRUE; wordmatch = FALSE; nextpat = namelist; do { nextpat = one_argument( nextpat, patbuf ); if( !str_prefix( keybuf, patbuf ) ) wordmatch = TRUE; } while( !wordmatch && patbuf[0] != '\0' ); } while( wordmatch ); return FALSE; } char *show_affect( char *outbuf, AFFECT_DATA *paf, bool extra, bool imm ) { char buf[MAX_INPUT_LENGTH]; if( paf->type == gsn_perm_spell ) sprintf( outbuf, "&gPermanent spell affect '&y%s&g'", skill_table[paf->location].name ); else if( paf->type == gsn_racial_fatigue ) sprintf( outbuf, "&gRacial &y%s&g fatigue", skill_table[paf->location].name ); else if( paf->type == gsn_religious ) sprintf( outbuf, "&gReligious &y%s&g disfavour", skill_table[paf->location].name ); else if( paf->type == gsn_clan_power ) sprintf( outbuf, "&gClan &y%s&g power fatigue", skill_table[paf->location].name ); else if( paf->type > 0 ) { strcpy( outbuf, "&gSpell:" ); /* delayed effect code by incubus */ if( paf->type == gsn_delayed_effect ) sprintf( buf, " '&y%s %s&g'", skill_table[paf->type].name, skill_table[paf->location].name ); else if( paf->type == gsn_continuous_effect ) sprintf( buf, " '&y%s&g' on the hour every hour", skill_table[paf->location].name ); else sprintf( buf, " '&y%s&g'", skill_table[paf->type].name ); strcat( outbuf, buf ); } else strcpy( outbuf, "&gAffect:" ); if( extra ) { if( paf->type == gsn_racial_fatigue || paf->type == gsn_religious || paf->type == gsn_perm_spell ) ; else if( paf->type == gsn_delayed_effect || paf->type == gsn_continuous_effect ) { sprintf( buf, " at level &y%d&g", paf->modifier ); strcat( outbuf, buf ); } else if( paf->location == APPLY_BODY_PART ) { sprintf( buf, " modifies &cbody parts&g by &c%s&g", flag_string( body_part_flags, &paf->modifier ) ); strcat( outbuf, buf ); } else if( paf->location != APPLY_NONE && paf->modifier ) { sprintf( buf, " modifies &c%s&g by &c%d&g", flag_string( apply_name_flags, &paf->location ), paf->modifier ); strcat( outbuf, buf ); } if( paf->type == gsn_perm_spell ) ; else if( paf->type == gsn_delayed_effect ) { if( paf->duration >= 0 ) sprintf( buf, " goes off in &c%d&g hours", paf->duration ); else strcpy( buf, ", indeterminate time until effect" ); strcat( outbuf, buf ); } else if( paf->duration >= 0 ) { sprintf( buf, " for &c%d&g hours", paf->duration ); strcat( outbuf, buf ); } else { strcat( outbuf, " in permanence" ); } } if( imm && !vnull( paf->bitvector ) ) { sprintf( buf, " with bits %s", flag_string( affect_flags, paf->bitvector ) ); strcat( outbuf, buf ); } strcat( outbuf, ".&n\n\r" ); return outbuf; } /* * Apply or remove an affect to a character. */ void affect_modify( CHAR_DATA *ch, AFFECT_DATA *paf, bool fAdd ) { AFFECT_DATA *af; OBJ_DATA *wield; OBJ_DATA *wield2; int mod, i; mod = paf->modifier; /* Incubus's delayed spell effect */ if( !fAdd ) { if( paf->type == gsn_delayed_effect ) { /* hit the character with the spell... */ ( *skill_table[paf->location].spell_fun ) ( paf->location, URANGE( 1, paf->modifier, LEVEL_HERO * 2 ), ch, ch ); return; } } if( paf->type == gsn_continuous_effect || paf->type == gsn_racial_fatigue || paf->type == gsn_religious || paf->type == gsn_delayed_effect ) return; if( fAdd ) { if( xIS_SET( paf->bitvector, AFF_DARKNESS ) ) ch->in_room->light -= 50; for ( i = 0; i < MAX_VECTOR; i++ ) SET_BIT( ch->affected_by[i], paf->bitvector[i] ); } else { if( xIS_SET( paf->bitvector, AFF_DARKNESS ) ) ch->in_room->light += 50; for ( i = 0; i < MAX_VECTOR; i++ ) REMOVE_BIT( ch->affected_by[i], paf->bitvector[i] ); if( paf->location != APPLY_BODY_PART ) mod = 0 - mod; } switch( paf->location ) { default: bug( "Affect_modify: unknown location %d on %s.", paf->location, ch->name ); return; case APPLY_NONE: case APPLY_GOLD: case APPLY_EXP: case APPLY_CLASS: case APPLY_LEVEL: case APPLY_AGE: case APPLY_HEIGHT: case APPLY_WEIGHT: case APPLY_APPEARANCE: break; case APPLY_STR: if( !IS_NPC( ch ) ) ch->pcdata->mod_str += mod; break; case APPLY_DEX: if( !IS_NPC( ch ) ) ch->pcdata->mod_dex += mod; break; case APPLY_INT: if( !IS_NPC( ch ) ) ch->pcdata->mod_int += mod; break; case APPLY_WIS: if( !IS_NPC( ch ) ) ch->pcdata->mod_wis += mod; break; case APPLY_CON: if( !IS_NPC( ch ) ) ch->pcdata->mod_con += mod; break; case APPLY_SEX: ch->sex += mod; break; case APPLY_RACE: ch->race += mod; break; case APPLY_HIT: ch->max_hit += mod; break; case APPLY_MOVE: ch->max_move += mod; break; case APPLY_AC: ch->armour += mod; break; case APPLY_HITROLL: ch->hitroll += mod; break; case APPLY_DAMROLL: ch->damroll += mod; break; case APPLY_RESILIENCE: ch->resil_mod += mod; break; case APPLY_BODY_TEMP: ch->temp_mod += mod; break; case APPLY_BODY_PART: if( fAdd ) SET_BIT( ch->body_parts, mod ); else REMOVE_BIT( ch->body_parts, mod ); break; case APPLY_SPEED: ch->speed += mod; break; case APPLY_SIZE: ch->size_mod += mod; break; case APPLY_AIR: if( !IS_NPC( ch ) ) ch->pcdata->mod_magic[MAGIC_AIR] += mod; break; case APPLY_EARTH: if( !IS_NPC( ch ) ) ch->pcdata->mod_magic[MAGIC_EARTH] += mod; break; case APPLY_FIRE: if( !IS_NPC( ch ) ) ch->pcdata->mod_magic[MAGIC_FIRE] += mod; break; case APPLY_SPIRIT: if( !IS_NPC( ch ) ) ch->pcdata->mod_magic[MAGIC_SPIRIT] += mod; break; case APPLY_WATER: if( !IS_NPC( ch ) ) ch->pcdata->mod_magic[MAGIC_WATER] += mod; break; case APPLY_MANA_AIR: ch->max_mana[MAGIC_AIR] += mod; break; case APPLY_MANA_EARTH: ch->max_mana[MAGIC_EARTH] += mod; break; case APPLY_MANA_FIRE: ch->max_mana[MAGIC_FIRE] += mod; break; case APPLY_MANA_SPIRIT: ch->max_mana[MAGIC_SPIRIT] += mod; break; case APPLY_MANA_WATER: ch->max_mana[MAGIC_WATER] += mod; break; case APPLY_MAGIC_RESIST: ch->saving_throw += mod; break; } if( IS_NPC( ch ) ) return; /* * Check for PC weapon wielding. * Guard against recursion (for weapons with affects). * If more than one weapon, drop weapon 2 first, then recheck. * And yes, it does work. :) --- Thelonius ( Monk ) */ if( ( wield = get_eq_char( ch, WEAR_WIELD_R ) ) || ( wield = get_eq_char( ch, WEAR_WIELD_DOUBLE ) ) ) { if( ( wield2 = get_eq_char( ch, WEAR_WIELD_L ) ) ) { if( ( ( get_obj_weight( wield ) + get_obj_weight( wield2 ) ) > str_app_wield( get_curr_str( ch ) ) ) || IS_SET( race_table[ch->race].race_abilities, RACE_NO_WEAPON_WIELD ) ) { static int depth; if( depth == 0 ) { depth++; act( "You drop $p.", ch, wield2, NULL, TO_CHAR ); act( "$n drops $p.", ch, wield2, NULL, TO_ROOM ); obj_from_char( wield2 ); obj_to_room( wield2, ch->in_room ); depth--; } } } else if( ( get_obj_weight( wield ) > str_app_wield( get_curr_str( ch ) ) ) || IS_SET( race_table[ch->race].race_abilities, RACE_NO_WEAPON_WIELD ) ) { static int depth; if( depth == 0 ) { depth++; act( "You drop $p.", ch, wield, NULL, TO_CHAR ); act( "$n drops $p.", ch, wield, NULL, TO_ROOM ); obj_from_char( wield ); obj_to_room( wield, ch->in_room ); depth--; } } } else if( ( wield2 = get_eq_char( ch, WEAR_WIELD_L ) ) && ( get_obj_weight( wield2 ) > str_app_wield( get_curr_str( ch ) ) || IS_SET( race_table[ch->race].race_abilities, RACE_NO_WEAPON_WIELD ) ) ) { static int depth; if( depth == 0 ) { depth++; act( "You drop $p.", ch, wield2, NULL, TO_CHAR ); act( "$n drops $p.", ch, wield2, NULL, TO_ROOM ); obj_from_char( wield2 ); obj_to_room( wield2, ch->in_room ); depth--; } } if( ( ch->affected_by[0] & ( AFF_BLEEDING | AFF_HIDE ) ) > 0 ) { ch->affected_by[0] &= ( AFF_BLEEDING | AFF_HIDE ); for( i = 1; i < MAX_VECTOR; i++ ) ch->affected_by[i] = 0; } else vzero( ch->affected_by ); /* put all the old AFF_* bits back on */ for( af = ch->affected; af; af = af->next ) { if( af->deleted || ( af == paf && !fAdd ) ) continue; for ( i = 0; i < MAX_VECTOR; i++ ) SET_BIT( ch->affected_by[i], af->bitvector[i] ); } for( mod = 1; mod < MAX_WEAR; mod++ ) { wield = get_eq_char( ch, mod ); if( wield ) { for( af = wield->pIndexData->affected; af; af = af->next ) { if( af->deleted || ( af == paf && !fAdd ) ) continue; for ( i = 0; i < MAX_VECTOR; i++ ) SET_BIT( ch->affected_by[i], af->bitvector[i] ); } for( af = wield->affected; af; af = af->next ) { if( af->deleted || ( af == paf && !fAdd ) ) continue; for ( i = 0; i < MAX_VECTOR; i++ ) SET_BIT( ch->affected_by[i], af->bitvector[i] ); } } } if( !fAdd && xIS_SET( paf->bitvector, AFF_FLYING ) ) char_fall_check( ch, 0 ); if( fAdd && xIS_SET( paf->bitvector, AFF_HOLD ) ) web_update( ch ); return; } /* * Give an affect to a char. */ bool affect_to_char( CHAR_DATA *ch, AFFECT_DATA *paf, CHAR_DATA *caster ) { AFFECT_DATA *paf_old; AFFECT_DATA *paf_new; double per; paf_new = new_affect( ); *paf_new = *paf; if( caster && !IS_NPC( caster ) && caster->pcdata->learned[gsn_magic_lore] > 105 ) { paf_new->duration *= caster->pcdata->learned[gsn_magic_lore]; paf_new->duration /= 100; } for( paf_old = ch->affected; paf_old; paf_old = paf_old->next ) { if( paf_old->deleted ) continue; if( paf_old->type == paf->type && paf_old->location == paf->location && vequal( paf_old->bitvector, paf->bitvector ) ) { if( paf_old->duration == -1 ) return FALSE; if( paf->duration == -1 ) break; if( caster && !IS_NPC( caster ) && caster->pcdata->learned[gsn_magic_lore] > 5 ) { if( caster->pcdata->learned[gsn_magic_lore] < 105 ) { per = caster->pcdata->learned[gsn_magic_lore] - 5; per = pow( per / 100, paf_old->duration / paf_new->duration ); per *= paf_new->duration; paf_new->duration = paf_old->duration + (int)per; } else paf_new->duration += paf_old->duration; } affect_remove( ch, paf_old ); break; } } paf_new->duration = UMIN( paf_new->duration, 250 ); paf_new->deleted = FALSE; paf_new->next = ch->affected; ch->affected = paf_new; affect_modify( ch, paf_new, TRUE ); return TRUE; } /* * Remove an affect from a char. */ void affect_remove( CHAR_DATA *ch, AFFECT_DATA *paf ) { if( !ch->affected ) { bug( "Affect_remove: no affect." ); return; } affect_modify( ch, paf, FALSE ); paf->deleted = TRUE; return; } /* * Strip all affects of a given sn. */ void affect_strip( CHAR_DATA *ch, int sn ) { AFFECT_DATA *paf; bool mesg = FALSE; if( sn == gsn_awen ) { affect_strip( ch, skill_lookup( "bless" ) ); affect_strip( ch, skill_lookup( "armour" ) ); affect_strip( ch, skill_lookup( "holy armour" ) ); affect_strip( ch, skill_lookup( "holy aura" ) ); affect_strip( ch, skill_lookup( "protection" ) ); } else if( sn == gsn_foci ) { affect_strip( ch, skill_lookup( "giant strength" ) ); affect_strip( ch, skill_lookup( "phase shift" ) ); affect_strip( ch, skill_lookup( "shield" ) ); affect_strip( ch, skill_lookup( "fly" ) ); affect_strip( ch, skill_lookup( "stone skin" ) ); affect_strip( ch, skill_lookup( "haste" ) ); } else if( sn == gsn_fortitudes ) { affect_strip( ch, skill_lookup( "thought shield" ) ); affect_strip( ch, skill_lookup( "mental barrier" ) ); affect_strip( ch, skill_lookup( "combat mind" ) ); affect_strip( ch, skill_lookup( "displacement" ) ); affect_strip( ch, skill_lookup( "flesh armour" ) ); affect_strip( ch, skill_lookup( "energy containment" ) ); } for( paf = ch->affected; paf; paf = paf->next ) { if( paf->deleted || paf->type != sn ) continue; affect_remove( ch, paf ); if( !mesg && paf->type > 0 && skill_table[paf->type].msg_off ) { act( skill_table[paf->type].msg_off, ch, NULL, NULL, TO_CHAR ); mesg = TRUE; } } return; } /* * Strip all affects matching paf. */ void affect_strip_special( CHAR_DATA *ch, AFFECT_DATA *af ) { AFFECT_DATA *paf; for( paf = ch->affected; paf; paf = paf->next ) { if( paf->deleted ) continue; if( paf->type == af->type && paf->location == af->location ) affect_remove( ch, paf ); } return; } /* * Return true if a char is affected by a spell. */ bool is_affected( CHAR_DATA *ch, int sn ) { AFFECT_DATA *paf; for( paf = ch->affected; paf; paf = paf->next ) { if( paf->deleted ) continue; if( paf->type == sn ) return TRUE; } return FALSE; } /* * Add or enhance an affect. */ void affect_join( CHAR_DATA *ch, AFFECT_DATA *paf ) { AFFECT_DATA *paf_old; for( paf_old = ch->affected; paf_old; paf_old = paf_old->next ) { if( paf_old->deleted ) continue; if( paf_old->type == paf->type && paf_old->location == paf->location && vequal( paf_old->bitvector, paf->bitvector ) ) { paf->duration += paf_old->duration; paf->modifier += paf_old->modifier; affect_remove( ch, paf_old ); break; } } affect_to_char( ch, paf, NULL ); return; } /* * Move a char out of a room. */ void char_from_room( CHAR_DATA *ch ) { OBJ_DATA *obj; if( !ch->in_room ) { bug( "Char_from_room: NULL." ); return; } /* See note below in char_to_room */ if( !IS_IMMORTAL( ch ) || !xIS_SET( ch->act, PLR_WIZINVIS ) ) { if( !IS_NPC( ch ) ) --ch->in_room->area->nplayer; else --ch->in_room->area->nmobile; if( IS_AFFECTED( ch, AFF_DARKNESS ) ) ch->in_room->light += 50; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->deleted || obj->wear_loc == WEAR_NONE ) continue; if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) { if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light += 30; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light -= 20; else ch->in_room->light -= 10; } else if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light += 3; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light -= 2; } } if( ch == ch->in_room->people ) { ch->in_room->people = ch->next_in_room; } else { CHAR_DATA *prev; for( prev = ch->in_room->people; prev; prev = prev->next_in_room ) { if( prev->next_in_room == ch ) { prev->next_in_room = ch->next_in_room; break; } } if( !prev ) bug( "Char_from_room: ch not found." ); } ch->on = NULL; ch->in_room = NULL; ch->next_in_room = NULL; return; } /* * Move a char into a room. */ void char_to_room( CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex ) { OBJ_DATA *obj; bool boat = FALSE; EXIT_DATA *pexit; if( !pRoomIndex ) { bug( "Char_to_room: NULL." ); return; } ch->in_room = pRoomIndex; ch->next_in_room = pRoomIndex->people; pRoomIndex->people = ch; /* Wizinvissed immortals leave no light trail, nor do they drown etc... * This is a security fix, no one should notice them for these slight * traces they leave. Wizinvis code has also been fixed to compensate * for this. */ if( IS_IMMORTAL( ch ) && xIS_SET( ch->act, PLR_WIZINVIS ) ) return; if( !IS_NPC( ch ) ) ++ch->in_room->area->nplayer; else ++ch->in_room->area->nmobile; if( IS_AFFECTED( ch, AFF_DARKNESS ) ) ch->in_room->light -= 50; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->item_type == ITEM_BOAT && !obj->deleted ) boat = TRUE; if( obj->deleted || obj->wear_loc == WEAR_NONE ) continue; if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) { if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light -= 30; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light += 20; else ch->in_room->light += 10; } else if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light -= 3; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light += 2; } /* falling and sinking */ if( !IS_NPC( ch ) && !IS_AFFECTED( ch, AFF_FLYING ) && !IS_SET( ch->body_parts, BODY_PART_WINGS ) ) { if( ch->in_room->sector_type == SECT_WATER_SWIM || ch->in_room->sector_type == SECT_WATER_NOSWIM || ch->in_room->sector_type == SECT_UNDERWATER || IS_SET( ch->in_room->room_flags, ROOM_FLOODED ) ) { if( ( pexit = ch->in_room->exit[boat ? DIR_UP : DIR_DOWN] ) && ( !IS_SET( pexit->exit_info, EX_CLOSED ) || ( ( IS_SET( race_table[ch->race].race_abilities, RACE_PASSDOOR ) || IS_AFFECTED( ch, AFF_PASS_DOOR ) ) && !IS_SET( pexit->exit_info, EX_PASSPROOF ) ) ) && pexit->to_room ) create_char_event( ch, evn_char_sink, percent_fuzzy( 15 * PULSE_PER_SECOND, 10 ) ); } else char_fall_check( ch, 0 ); } else if( !IS_AFFECTED( ch, AFF_BREATHING ) && ch->in_room->sector_type != SECT_WATER_SWIM && ( ( ( ch->in_room->sector_type == SECT_UNDERWATER || IS_SET( ch->in_room->room_flags, ROOM_FLOODED ) ) && !IS_SET( ch->body_parts, BODY_PART_GILLS ) ) || ( ch->in_room->sector_type != SECT_UNDERWATER && !IS_SET( ch->body_parts, BODY_PART_LUNGS ) ) ) ) create_char_event( ch, evn_char_drown, number_fuzzy( 4 * PULSE_PER_SECOND ) ); else if( ch->in_room->sector_type == SECT_SPACE && !IS_AFFECTED( ch, AFF_BREATHING ) ) create_char_event( ch, evn_char_suffocate, number_fuzzy( 3 * PULSE_PER_SECOND ) ); return; } void char_fall_check( CHAR_DATA *ch, int fallen ) { EXIT_DATA *pexit; EVENT *e; ROOM_INDEX_DATA *room = ch->in_room; int dam; if( xIS_SET( ch->affected_by, AFF_FLYING ) || IS_SET( ch->body_parts, BODY_PART_WINGS ) ) return; pexit = room->exit[DIR_DOWN]; if( IS_SET( room->room_flags, ROOM_FALL ) && pexit && ( !IS_SET( pexit->exit_info, EX_CLOSED ) || ( ( IS_SET( race_table[ch->race].race_abilities, RACE_PASSDOOR ) || IS_AFFECTED( ch, AFF_PASS_DOOR ) ) && !IS_SET( pexit->exit_info, EX_PASSPROOF ) ) ) ) { e = create_char_event( ch, evn_char_fall, UMAX( 1, 5 - fallen ) ); e->data[0] = UMIN( 25, fallen + 1 ); /* Terminal Velocity of 25 */ return; } if( fallen <= 0 ) return; dam = get_size( ch ) / 7 + 10; dam += ch->carry_weight / 10; dam *= fallen; if( room->sector_type == SECT_WATER_SWIM || room->sector_type == SECT_WATER_NOSWIM || room->sector_type == SECT_UNDERWATER || IS_SET( room->room_flags, ROOM_FLOODED ) ) { act( "$n land$% in the water with a big splash!", ch, NULL, NULL, TO_ALL ); dam = dam / 2 - 25; } else if( ( fallen = get_success( ch, gsn_catfall, 100 ) ) ) { dam -= get_curr_dex( ch ); dam = dam * 50 / UMAX( 50, fallen ); send_to_char( "You land lightly, rolling to absorb the impact.\n\r", ch ); act( "$n hits the ground and rolls over softening the landing.", ch, NULL, NULL, TO_ROOM ); } else { send_to_char( "You crash to the ground.\n\r", ch ); act( "$n crashes into the ground with a huge THUMP!", ch, NULL, NULL, TO_ROOM ); } if( dam > 0 ) damage( ch, ch, dam, skill_lookup( "earth bind" ), WEAR_NONE ); } /* * The only problem with this function is that it can change the order of * items in a character's inventory, luckily it isn't needed all that often. */ void weight_change_object( OBJ_DATA *obj, int weight ) { CHAR_DATA *tmp_ch; OBJ_DATA *tmp_obj; if( obj->in_room ) { obj->weight += weight; } else if( ( tmp_ch = obj->carried_by ) ) { obj_from_char( obj ); obj->weight += weight; obj_to_char( obj, tmp_ch ); } else if( ( tmp_obj = obj->in_obj ) ) { obj_from_obj( obj ); obj->weight += weight; obj_to_obj( obj, tmp_obj ); } else { bug( "Unknown attempt to subtract weight from an object." ); } } /* * Give an obj to a char. */ void obj_to_char( OBJ_DATA *obj, CHAR_DATA *ch ) { /* owner flagged items without an owner fuse themselves to the first person that picks them up. */ if( ( IS_SET( obj->extra_flags, ITEM_OWNER ) || ( get_trust( ch ) > L_HER && get_trust( ch ) < L_APP ) ) ) { char *p; char buf[ MAX_INPUT_LENGTH ]; if( !( p = strchr( obj->name, '{' ) ) || !strchr( p, '}' ) ) { act( "$p fuses firmly to your hand as you touch it.", ch, obj, NULL, TO_CHAR ); sprintf( buf, "%s {%s}", obj->name, ch->name ); free_string( obj->name ); obj->name = str_dup( buf ); SET_BIT( obj->extra_flags, ITEM_OWNER ); } } obj->next_content = ch->carrying; ch->carrying = obj; obj->carried_by = ch; obj->in_room = NULL; obj->in_obj = NULL; ch->carry_number++; /* += get_obj_number( obj ) */ ch->carry_weight += get_obj_weight( obj ); } /* * Take an obj from its character. */ void obj_from_char( OBJ_DATA *obj ) { CHAR_DATA *ch; if( !( ch = obj->carried_by ) ) { bug( "Obj_from_char: null ch." ); return; } if( obj->item_type == ITEM_WEAPON && ch->level < L_JUN && IS_SET( obj->extra_flags, ITEM_CHARGED ) ) { act( "$p loses it's shine.", ch, obj, NULL, TO_CHAR ); act( "$p loses it's shine.", ch, obj, NULL, TO_ROOM ); REMOVE_BIT( obj->extra_flags, ITEM_CHARGED ); } if( obj->wear_loc != WEAR_NONE ) unequip_char( ch, obj ); if( ch->carrying == obj ) { ch->carrying = obj->next_content; } else { OBJ_DATA *prev; for( prev = ch->carrying; prev; prev = prev->next_content ) { if( prev->next_content == obj ) { prev->next_content = obj->next_content; break; } } if( !prev ) bug( "Obj_from_char: obj not in list." ); } obj->carried_by = NULL; obj->next_content = NULL; ch->carry_number--; /* -= get_obj_number( obj ); */ ch->carry_weight -= get_obj_weight( obj ); return; } /* * Find the ac value of an obj, including position effect. */ int apply_ac( CHAR_DATA *ch, OBJ_DATA *obj, int iWear ) { int num = 0; if( obj->item_type != ITEM_ARMOUR ) return 0; switch( iWear ) { case WEAR_BODY: num = 3 * obj->value[0]; break; case WEAR_HEAD: case WEAR_LEGS: case WEAR_SHOULDERS: num = 2 * obj->value[0]; break; case WEAR_HORNS: case WEAR_EAR_L: case WEAR_EAR_R: case WEAR_FACE: case WEAR_NOSE: case WEAR_NECK_1: case WEAR_NECK_2: case WEAR_FLOAT_L: case WEAR_FLOAT_R: case WEAR_WINGS: case WEAR_ARMS: case WEAR_WRIST_L: case WEAR_WRIST_R: case WEAR_HANDS: case WEAR_FINGER_L: case WEAR_FINGER_R: case WEAR_HOLD_R: case WEAR_HOLD_L: case WEAR_SHIELD: case WEAR_WAIST: case WEAR_FEET: case WEAR_JUGGLED: num = obj->value[0]; break; } if( !IS_NPC( ch ) && obj->required_skill > 0 ) { num *= UMIN( ch->pcdata->learned[obj->required_skill], 100 ); num /= 100; } return num; } /* * Find a piece of eq on a character. */ OBJ_DATA *get_eq_char( CHAR_DATA *ch, int iWear ) { OBJ_DATA *obj; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->deleted ) continue; if( obj->wear_loc == iWear ) return obj; } return NULL; } OBJ_DATA *get_held( CHAR_DATA *ch, int type, bool first ) { OBJ_DATA *obj; obj = get_eq_char( ch, WEAR_HOLD_L ); if( !obj || obj->item_type != type ) { if( first ) obj = get_eq_char( ch, WEAR_HOLD_R ); } if( !obj || obj->item_type != type ) return NULL; return obj; } /* * Equip a char with an obj. */ void equip_char( CHAR_DATA *ch, OBJ_DATA *obj, int iWear ) { AFFECT_DATA *paf; if( iWear != WEAR_JUGGLED && get_eq_char( ch, iWear ) ) { bug( "Equip_char: %s already equipped at %d.", ch->name, iWear ); return; } if( ( IS_OBJ_STAT( obj, ITEM_ANTI_EVIL ) && IS_EVIL( ch ) ) || ( IS_OBJ_STAT( obj, ITEM_ANTI_GOOD ) && IS_GOOD( ch ) ) || ( IS_OBJ_STAT( obj, ITEM_ANTI_NEUTRAL ) && IS_NEUTRAL( ch ) ) ) { /* * Thanks to Morgenes for the bug fix here! */ act( "You are zapped by $p and drop it.", ch, obj, NULL, TO_CHAR ); act( "$n is zapped by $p and drops it.", ch, obj, NULL, TO_ROOM ); obj_from_char( obj ); if( ch->in_room ) obj_to_room( obj, ch->in_room ); else obj_to_room( obj, get_room_index( ROOM_VNUM_TEMPLE ) ); return; } if( !IS_NPC( ch ) && IS_OBJ_STAT( obj, ITEM_OWNER ) && !is_owner( ch, obj ) ) { act( "$p is torn from your body with extreme force.", ch, obj, NULL, TO_CHAR ); act( "$p is forcefully torn from $n's body.", ch, obj, NULL, TO_ROOM ); obj_from_char( obj ); if( ch->in_room ) obj_to_room( obj, ch->in_room ); else obj_to_room( obj, get_room_index( ROOM_VNUM_TEMPLE ) ); return; } ch->armour -= apply_ac( ch, obj, iWear ); obj->wear_loc = iWear; for( paf = obj->pIndexData->affected; paf; paf = paf->next ) { if( paf->type == gsn_perm_spell ) { if( paf->location < 0 || paf->location >= MAX_SKILL ) { bug( "Unknown perm spell location %d on vnum %d.", paf->location, obj->pIndexData->vnum ); } else if( !is_affected( ch, paf->location ) ) { AFFECT_DATA *caf; (*skill_table[paf->location].spell_fun) ( paf->location, obj->level, ch, ch ); for( caf = ch->affected; caf; caf = caf->next ) if( caf->type == paf->location ) caf->duration = -1; } } else if( paf->type > 0 && !IS_SET( skill_table[paf->type].skill_type, SKILL_TYPE_ENCHANTMENT ) ) { affect_strip_special( ch, paf ); affect_to_char( ch, paf, NULL ); } else affect_modify( ch, paf, TRUE ); } for( paf = obj->affected; paf; paf = paf->next ) { if( paf->type == gsn_perm_spell ) { if( paf->location < 0 || paf->location >= MAX_SKILL ) { bug( "Unknown perm spell location %d on obj [%d].", paf->location, obj->unique_key ); } else if( !is_affected( ch, paf->location ) ) { AFFECT_DATA *caf; (*skill_table[paf->location].spell_fun) ( paf->location, obj->level, ch, ch ); for( caf = ch->affected; caf; caf = caf->next ) if( caf->type == paf->location ) caf->duration = -1; } } else if( paf->type > 0 && !IS_SET( skill_table[paf->type].skill_type, SKILL_TYPE_ENCHANTMENT ) ) { affect_strip_special( ch, paf ); affect_to_char( ch, paf, NULL ); } else affect_modify( ch, paf, TRUE ); } if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) { if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light -= 30; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light += 20; else ch->in_room->light += 10; } else if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light -= 3; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light += 2; if( ch->desc && !IS_SET( ch->desc->interpreter->flags, INTERPRETER_NOMESSAGE ) && obj->item_type != ITEM_FOOD && obj->item_type != ITEM_LIMB && obj->action && obj->action[0] != '\0' ) send_to_char( obj->action, ch ); return; } /* * Unequip a char with an obj. */ void unequip_char( CHAR_DATA *ch, OBJ_DATA *obj ) { AFFECT_DATA *paf; if( obj->wear_loc == WEAR_NONE ) { bug( "Unequip_char: %s already unequipped with %d.", ch->name, obj->pIndexData->vnum ); return; } /* if juggling these should be replaced by one that is being juggled */ if( obj->wear_loc == WEAR_WIELD_R || obj->wear_loc == WEAR_WIELD_L ) { OBJ_DATA *juggled; if( ( juggled = get_eq_char( ch, WEAR_JUGGLED ) ) ) juggled->wear_loc = obj->wear_loc; } ch->armour += apply_ac( ch, obj, obj->wear_loc ); obj->wear_loc = WEAR_NONE; for( paf = obj->pIndexData->affected; paf; paf = paf->next ) { if( paf->type == gsn_perm_spell ) affect_strip( ch, paf->location ); else if( paf->type > 0 && !IS_SET( skill_table[paf->type].skill_type, SKILL_TYPE_ENCHANTMENT ) ) affect_strip_special( ch, paf ); else affect_modify( ch, paf, FALSE ); } for( paf = obj->affected; paf; paf = paf->next ) { if( paf->type == gsn_perm_spell ) affect_strip( ch, paf->location ); else if( paf->type > 0 && !IS_SET( skill_table[paf->type].skill_type, SKILL_TYPE_ENCHANTMENT ) ) affect_strip_special( ch, paf ); else affect_modify( ch, paf, FALSE ); } if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) { if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light += 30; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light -= 20; else ch->in_room->light -= 10; } else if( IS_SET( obj->extra_flags, ITEM_DARK ) ) ch->in_room->light += 3; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) ch->in_room->light -= 2; return; } /* * Count occurrences of an obj in a list. */ int count_obj_list( OBJ_INDEX_DATA * pObjIndex, OBJ_DATA *list ) { OBJ_DATA *obj; int nMatch; nMatch = 0; for( obj = list; obj; obj = obj->next_content ) { if( !obj->deleted && obj->pIndexData == pObjIndex ) nMatch++; } return nMatch; } /* * Move an obj out of a room. */ void obj_from_room( OBJ_DATA *obj ) { ROOM_INDEX_DATA *in_room; CHAR_DATA *rch; if( !( in_room = obj->in_room ) ) { bug( "obj_from_room: NULL." ); return; } for( rch = in_room->people; rch; rch = rch->next_in_room ) if( rch->on == obj ) rch->on = NULL; if( obj == in_room->contents ) { in_room->contents = obj->next_content; } else { OBJ_DATA *prev; for( prev = in_room->contents; prev; prev = prev->next_content ) { if( prev->next_content == obj ) { prev->next_content = obj->next_content; break; } } if( !prev ) { bug( "Obj_from_room: obj not found." ); return; } } if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) { if( IS_SET( obj->extra_flags, ITEM_DARK ) ) obj->in_room->light += 14; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) obj->in_room->light -= 10; else obj->in_room->light -= 6; } strip_events( &obj->events, evn_imp_grab ); obj->in_room = NULL; obj->next_content = NULL; return; } /* * Move an obj into a room. */ void obj_to_room( OBJ_DATA *obj, ROOM_INDEX_DATA *pRoomIndex ) { obj->next_content = pRoomIndex->contents; pRoomIndex->contents = obj; obj->in_room = pRoomIndex; obj->carried_by = NULL; obj->in_obj = NULL; set_imp_timer( obj ); if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) { if( IS_SET( obj->extra_flags, ITEM_DARK ) ) pRoomIndex->light -= 14; else if( IS_SET( obj->extra_flags, ITEM_GLOW ) ) pRoomIndex->light += 10; else pRoomIndex->light += 6; } else if( obj->item_type == ITEM_EXPLOSIVE ) create_obj_event( obj, evn_explode, percent_fuzzy( 10 * PULSE_PER_SECOND, 5 ) ); obj_fall_check( obj, 0 ); return; } void obj_fall_check( OBJ_DATA *obj, int fallen ) { EXIT_DATA *pexit; EVENT *e; CHAR_DATA *rch; pexit = obj->in_room->exit[DIR_DOWN]; if( IS_SET( obj->wear_flags, ITEM_TAKE ) && IS_SET( obj->in_room->room_flags, ROOM_FALL ) && pexit && pexit->to_room && !IS_SET( pexit->exit_info, EX_CLOSED ) ) { e = create_obj_event( obj, evn_obj_fall, UMAX( 1, 5 - fallen ) ); e->data[0] = fallen + 1; } else if( fallen > 0 ) { if( ( rch = obj->in_room->people ) != NULL ) act( "$p slams into the ground.", rch, obj, NULL, TO_ALL ); mod_item_condition( NULL, obj, 1 + fallen / 5 ); } } /* * Move an object into an object. */ void obj_to_obj( OBJ_DATA *obj, OBJ_DATA *obj_to ) { if( obj_to->deleted ) { bug( "Obj_to_obj: Obj_to already deleted" ); return; } obj->next_content = obj_to->contains; obj_to->contains = obj; obj->in_obj = obj_to; obj->in_room = NULL; obj->carried_by = NULL; for( ; obj_to; obj_to = obj_to->in_obj ) { if( obj_to->deleted ) continue; if( obj_to->item_type == ITEM_CONTAINER && IS_SET( obj_to->value[1], CONT_WEIGHTLESS ) ) break; if( obj_to->carried_by ) obj_to->carried_by->carry_weight += get_obj_weight( obj ); } return; } /* * Move an object out of an object. */ void obj_from_obj( OBJ_DATA *obj ) { OBJ_DATA *obj_from; if( !( obj_from = obj->in_obj ) ) { bug( "Obj_from_obj: null obj_from." ); return; } if( obj == obj_from->contains ) { obj_from->contains = obj->next_content; } else { OBJ_DATA *prev; for( prev = obj_from->contains; prev; prev = prev->next_content ) { if( prev->next_content == obj ) { prev->next_content = obj->next_content; break; } } if( !prev ) { bug( "Obj_from_obj: obj not found." ); return; } } obj->next_content = NULL; obj->in_obj = NULL; for( ; obj_from; obj_from = obj_from->in_obj ) { if( obj_from->deleted ) continue; if( obj_from->item_type == ITEM_CONTAINER && IS_SET( obj_from->value[1], CONT_WEIGHTLESS ) ) break; if( obj_from->carried_by ) obj_from->carried_by->carry_weight -= get_obj_weight( obj ); } return; } /* * Extract an obj from the world. */ void extract_obj( OBJ_DATA *obj ) { OBJ_DATA *obj_content; OBJ_DATA *obj_next; if( obj->deleted ) { bug( "Extract_obj: Obj already deleted" ); return; } if( obj->in_room ) obj_from_room( obj ); else if( obj->carried_by ) obj_from_char( obj ); else if( obj->in_obj ) obj_from_obj( obj ); for( obj_content = obj->contains; obj_content; obj_content = obj_next ) { obj_next = obj_content->next_content; if( obj_content->deleted ) continue; extract_obj( obj_content ); } while( obj->events ) { EVENT *tmp; tmp = obj->events; obj->events = tmp->next_local; event_remove_global( tmp ); free_event( tmp ); } obj->deleted = TRUE; delete_obj = TRUE; return; } /* * Extract a char from the world. */ void extract_char( CHAR_DATA *ch, bool fPull ) { CHAR_DATA *wch; OBJ_DATA *obj; OBJ_DATA *obj_next; if( !ch->in_room ) { bug( "Extract_char: NULL." ); return; } if( IS_NPC( ch ) && ch->pIndexData->vnum == MOB_VNUM_SUPERMOB ) { progbug( ch, "Extracting a supermob, putting it back where it should be." ); char_from_room( ch ); char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) ); clean_char( ch ); ch->hit = ch->max_hit; return; } if( ch->fighting ) stop_fighting( ch, TRUE ); if( fPull ) { char *name; if( IS_NPC( ch ) ) name = ch->short_descr; else name = ch->name; die_follower( ch, name ); /* Get rid of weapons _first_ - from Erwin Andreasen <erwin@pip.dknet.dk> */ { OBJ_DATA *obj, *obj2; obj = get_eq_char( ch, WEAR_WIELD_R ); obj2 = get_eq_char( ch, WEAR_WIELD_L ); if( !obj ) obj = get_eq_char( ch, WEAR_WIELD_DOUBLE ); if( obj ) extract_obj( obj ); /* Now kill obj2 if it exists no matter if on body or floor */ if( obj2 ) extract_obj( obj2 ); } for( obj = ch->carrying; obj; obj = obj_next ) { obj_next = obj->next_content; if( obj->deleted ) continue; extract_obj( obj ); } while( ch->events ) { EVENT *tmp; tmp = ch->events; ch->events = tmp->next_local; event_remove_global( tmp ); free_event( tmp ); } } char_from_room( ch ); if( !fPull ) { ROOM_INDEX_DATA *location; if( !( location = get_room_index( ROOM_VNUM_TEMPLE ) ) ) { bug( "Temple does not exist!" ); char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) ); } else char_to_room( ch, location ); return; } if( IS_NPC( ch ) ) --ch->pIndexData->count; if( ch->desc && ch->desc->original ) do_return( ch, "" ); for( wch = char_list; wch; wch = wch->next ) { if( wch->reply == ch ) wch->reply = NULL; } ch->deleted = TRUE; if( ch->desc ) ch->desc->character = NULL; delete_char = TRUE; return; } /* * Find a char in the room. */ CHAR_DATA *get_char_room( CHAR_DATA *ch, const char *argument ) { CHAR_DATA *rch; char arg[MAX_INPUT_LENGTH]; int number; int count; if( argument[0] == '#' ) { int uid; if( LOWER( argument[1] ) == 'c' ) uid = atoi( &argument[2] ); else uid = atoi( &argument[1] ); if( uid <= 0 ) return NULL; for( rch = ch->in_room->people; rch; rch = rch->next_in_room ) { if( uid == rch->unique_key && can_see( ch, rch ) ) return rch; } return NULL; } number = number_argument( argument, arg ); count = 0; if( number == 0 || !str_cmp( arg, "self" ) ) return ch; for( rch = ch->in_room->people; rch; rch = rch->next_in_room ) { if( !can_see( ch, rch ) || !is_name( arg, rch->name ) ) continue; if( ++count == number ) return rch; } return NULL; } /* * Find a char in the world. */ CHAR_DATA *get_char_world( CHAR_DATA *ch, const char *argument ) { CHAR_DATA *wch; char arg[MAX_INPUT_LENGTH]; int number; int count; if( ( wch = get_char_room( ch, argument ) ) ) return wch; if( argument[0] == '#' ) { int uid; if( LOWER( argument[1] ) == 'c' ) uid = atoi( &argument[2] ); else uid = atoi( &argument[1] ); if( uid <= 0 ) return NULL; for( wch = char_list; wch; wch = wch->next ) { if( uid == wch->unique_key && can_see( ch, wch ) ) return wch; } return NULL; } number = number_argument( argument, arg ); count = 0; for( wch = char_list; wch; wch = wch->next ) { if( !can_see( ch, wch ) || !is_name( arg, wch->name ) ) continue; if( ++count == number ) return wch; } return NULL; } /* * Find some object with a given index data. * Used by area-reset 'P' command. */ OBJ_DATA *get_obj_type( OBJ_INDEX_DATA * pObjIndex ) { OBJ_DATA *obj; for( obj = object_list; obj; obj = obj->next ) { if( obj->deleted ) continue; if( obj->pIndexData == pObjIndex ) return obj; } return NULL; } /* * Find an obj in a list. */ OBJ_DATA *get_obj_list( CHAR_DATA *ch, const char *argument, OBJ_DATA *list ) { OBJ_DATA *obj; char arg[MAX_INPUT_LENGTH]; int number; int count; if( argument[0] == '#' ) { int uid; if( LOWER( argument[1] ) == 'o' ) uid = atoi( &argument[2] ); else uid = atoi( &argument[1] ); if( uid <= 0 ) return NULL; for( obj = list; obj; obj = obj->next_content ) { if( uid == obj->unique_key && can_see_obj( ch, obj ) ) return obj; } return NULL; } number = number_argument( argument, arg ); count = 0; for( obj = list; obj; obj = obj->next_content ) { if( can_see_obj( ch, obj ) && is_name( arg, obj->name ) ) { if( ++count == number ) return obj; } } return NULL; } /* * Find an obj in player's inventory. */ OBJ_DATA *get_obj_carry( CHAR_DATA *ch, const char *argument ) { OBJ_DATA *obj; char arg[MAX_INPUT_LENGTH]; int number; int count; if( argument[0] == '#' ) { int uid; if( LOWER( argument[1] ) == 'o' ) uid = atoi( &argument[2] ); else uid = atoi( &argument[1] ); if( uid <= 0 ) return NULL; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->wear_loc == WEAR_NONE && uid == obj->unique_key && can_see_obj( ch, obj ) ) return obj; } return NULL; } number = number_argument( argument, arg ); count = 0; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->wear_loc == WEAR_NONE && can_see_obj( ch, obj ) && is_name( arg, obj->name ) ) { if( ++count == number ) return obj; } } return NULL; } /* * Find an obj in player's equipment. */ OBJ_DATA *get_obj_wear( CHAR_DATA *ch, const char *argument ) { OBJ_DATA *obj; char arg[MAX_INPUT_LENGTH]; int number; int count; if( argument[0] == '#' ) { int uid; if( LOWER( argument[1] ) == 'o' ) uid = atoi( &argument[2] ); else uid = atoi( &argument[1] ); if( uid <= 0 ) return NULL; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->wear_loc != WEAR_NONE && uid == obj->unique_key && can_see_obj( ch, obj ) ) return obj; } return NULL; } number = number_argument( argument, arg ); count = 0; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( obj->wear_loc != WEAR_NONE && can_see_obj( ch, obj ) && is_name( arg, obj->name ) ) { if( ++count == number ) return obj; } } return NULL; } /* * Find an obj in the room or in inventory. */ OBJ_DATA *get_obj_here( CHAR_DATA *ch, const char *argument ) { OBJ_DATA *obj; obj = get_obj_list( ch, argument, ch->in_room->contents ); if( obj ) return obj; if( ( obj = get_obj_carry( ch, argument ) ) ) return obj; if( ( obj = get_obj_wear( ch, argument ) ) ) return obj; return NULL; } /* * Find an obj in the world. */ OBJ_DATA *get_obj_world( CHAR_DATA *ch, const char *argument ) { OBJ_DATA *obj; char arg[MAX_INPUT_LENGTH]; int number; int count; if( ( obj = get_obj_here( ch, argument ) ) ) return obj; if( argument[0] == '#' ) { int uid; if( LOWER( argument[1] ) == 'o' ) uid = atoi( &argument[2] ); else uid = atoi( &argument[1] ); if( uid <= 0 ) return NULL; for( obj = object_list; obj; obj = obj->next ) { if( uid == obj->unique_key && can_see_obj( ch, obj ) ) return obj; } return NULL; } number = number_argument( argument, arg ); count = 0; for( obj = object_list; obj; obj = obj->next ) { if( can_see_obj( ch, obj ) && is_name( arg, obj->name ) ) { if( ++count == number ) return obj; } } return NULL; } /* * Create a 'money' obj. */ OBJ_DATA *create_money( int amount ) { OBJ_DATA *obj; char const * p; char buf[MAX_STRING_LENGTH]; if( amount <= 0 ) { bug( "Create_money: zero or negative money %d.", amount ); amount = 1; } if( amount == 1 ) { obj = create_object( get_obj_index( OBJ_VNUM_MONEY_ONE ), 0 ); } else { obj = create_object( get_obj_index( OBJ_VNUM_MONEY_SOME ), 0 ); if( amount <= 10 ) p = "few"; else if( amount <= 50 ) p = "small pile of"; else if( amount <= 100 ) p = "pile of"; else if( amount <= 1000 ) p = "large pile of"; else if( amount <= 10000 ) p = "bunch of"; else if( amount <= 50000 ) p = "whopping mound of"; else if ( amount <= 10000000 ) p = "mountain of"; else p = "pitiful few million"; sprintf( buf, "a %s gold coins", p ); free_string( obj->short_descr ); obj->short_descr = str_dup( buf ); buf[0] = 'A'; strcat( buf, " sits gleaming on the ground." ); free_string( obj->description ); obj->description = str_dup( buf ); } obj->value[0] = amount; return obj; } /* * Return # of objects which an object counts as. * Thanks to Tony Chamberlain for the correct recursive code here. */ int get_obj_number( OBJ_DATA *obj ) { int number; number = 0; if( obj->item_type == ITEM_CONTAINER ) for( obj = obj->contains; obj; obj = obj->next_content ) { if( obj->deleted ) continue; number += get_obj_number( obj ); } else number = 1; return number; } /* * Return weight of an object, including weight of contents. */ int get_obj_weight( OBJ_DATA *obj ) { int weight; weight = obj->weight; if( obj->item_type == ITEM_CONTAINER && IS_SET( obj->value[1], CONT_WEIGHTLESS ) ) return weight; for( obj = obj->contains; obj; obj = obj->next_content ) { if( obj->deleted ) continue; weight += get_obj_weight( obj ); } return weight; } /* * Return the number of people that are using the furniture to rest on. */ int count_users( OBJ_DATA *obj ) { CHAR_DATA *rch; int users = 0; if( !obj->in_room ) return 0; for( rch = obj->in_room->people; rch; rch = rch->next_in_room ) if( !rch->deleted && rch->on == obj ) users++; return users; } /* * True if room is dark. */ bool room_is_dark( ROOM_INDEX_DATA *pRoomIndex ) { if( IS_SET( pRoomIndex->room_flags, ROOM_INDOORS ) ) return FALSE; if( get_room_light( pRoomIndex ) < 0 ) return TRUE; return FALSE; } int get_room_light( ROOM_INDEX_DATA *pRoomIndex ) { int count; count = pRoomIndex->light; if( IS_SET( pRoomIndex->room_flags, ROOM_DARK ) ) count -= 40; /* weather wont effect underground */ if( IS_SET( pRoomIndex->room_flags, ROOM_UNDERGROUND ) ) return count - 15; if( pRoomIndex->sector_type == SECT_INSIDE || pRoomIndex->sector_type == SECT_CITY ) count += 15; if( pRoomIndex->area->plane->weather.sunlight == SUN_SET ) count -= 10; else if( pRoomIndex->area->plane->weather.sunlight == SUN_DARK ) { /* Full moon on the 4th of the month */ if( pRoomIndex->area->plane->weather.sky == SKY_CLOUDLESS ) count += abs( ( pRoomIndex->area->plane->time.day + 30 ) % 34 - 15 ); count -= 36; } else if( pRoomIndex->area->plane->weather.sunlight == SUN_RISE ) count += 8; else if( pRoomIndex->area->plane->weather.sunlight == SUN_LIGHT ) count += 32; return count; } /* * True if room is private. */ bool room_is_private( ROOM_INDEX_DATA *pRoomIndex ) { CHAR_DATA *rch; int count; count = 0; for( rch = pRoomIndex->people; rch; rch = rch->next_in_room ) { if( rch->deleted ) continue; count++; } if( IS_SET( pRoomIndex->room_flags, ROOM_PRIVATE ) && count >= 2 ) return TRUE; if( IS_SET( pRoomIndex->room_flags, ROOM_SOLITARY ) && count >= 1 ) return TRUE; return FALSE; } /* * True if char can see victim. */ bool can_see( CHAR_DATA *ch, CHAR_DATA *victim ) { int light; if( victim->deleted ) return FALSE; if( ch == victim ) return TRUE; /* All mobiles cannot see wizinvised immorts */ if( IS_NPC( ch ) && !IS_NPC( victim ) && ( xIS_SET( victim->act, PLR_WIZINVIS ) || ( victim->position == POS_MEDITATING && number_percent( ) < victim->level - ch->level + 95 ) ) ) return FALSE; if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_WIZINVIS ) && get_trust( ch ) < get_trust( victim ) ) return FALSE; /* Holylight and mobiles running programs, overrides almost all */ if( ( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_HOLYLIGHT ) ) || ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_MUDPROG ) ) ) return TRUE; if( IS_AFFECTED( ch, AFF_BLIND ) || !IS_SET( ch->body_parts, BODY_PART_EYES ) ) return FALSE; if( room_is_dark( ch->in_room ) && !IS_SET( race_table[ch->race].race_abilities, RACE_INFRAVISION ) && !IS_SET( race_table[ch->race].race_abilities, RACE_DARK_SIGHT ) && !IS_AFFECTED( ch, AFF_INFRARED ) ) return FALSE; if( victim->position == POS_DEAD ) return TRUE; if( IS_NPC( victim ) && xIS_SET( victim->act, ACT_BURIED ) ) return FALSE; if( IS_AFFECTED( victim, AFF_INVISIBLE ) && ( ( !IS_SET( race_table[ch->race].race_abilities, RACE_DETECT_INVIS ) && !IS_AFFECTED( ch, AFF_DETECT_INVIS ) ) || victim->level - 3 > ch->level ) ) return FALSE; if( victim->fighting ) return TRUE; if( IS_AFFECTED( victim, AFF_HIDE ) ) { light = get_room_light( ch->in_room ); if( IS_AFFECTED( ch, AFF_DETECT_HIDDEN ) ) light += 35; else if( IS_SET( race_table[ch->race].race_abilities, RACE_DETECT_HIDDEN ) ) light += 25; if( light < dice( 3, 6 ) ) return FALSE; } return TRUE; } /* * True if char can see obj. */ bool can_see_obj( CHAR_DATA *ch, OBJ_DATA *obj ) { if( obj->deleted ) return FALSE; /* Holylight and mobiles running programs, overrides almost all */ if( ( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_HOLYLIGHT ) ) || ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_MUDPROG ) ) ) return TRUE; if( IS_AFFECTED( ch, AFF_BLIND ) || !IS_SET( ch->body_parts, BODY_PART_EYES ) ) return FALSE; if( IS_SET( obj->extra_flags, ITEM_BURIED ) ) return FALSE; if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) return TRUE; if( room_is_dark( ch->in_room ) && !IS_SET( race_table[ch->race].race_abilities, RACE_INFRAVISION ) && !IS_SET( race_table[ch->race].race_abilities, RACE_DARK_SIGHT ) && !IS_AFFECTED( ch, AFF_INFRARED ) ) return FALSE; if( IS_SET( obj->extra_flags, ITEM_INVIS ) && !IS_SET( race_table[ch->race].race_abilities, RACE_DETECT_INVIS ) && !IS_AFFECTED( ch, AFF_DETECT_INVIS ) ) return FALSE; return TRUE; } /* * True if char can drop obj. */ bool can_drop_obj( CHAR_DATA *ch, OBJ_DATA *obj ) { if( !IS_SET( obj->extra_flags, ITEM_NODROP ) ) return TRUE; if( !IS_NPC( ch ) && ch->level >= L_APP ) return TRUE; return FALSE; } CHAR_DATA *get_char( CHAR_DATA *ch ) { if( !ch->pcdata ) return ch->desc->original; else return ch; } bool longstring( CHAR_DATA *ch, const char *argument ) { if( strlen( argument ) > 60 ) { send_to_char( "No more than 60 characters in this field.\n\r", ch ); return TRUE; } return FALSE; } bool authorized( CHAR_DATA *ch, const char *skllnm ) { char buf[MAX_STRING_LENGTH]; if( IS_NPC( ch ) || str_infix( skllnm, ch->pcdata->immskll ) ) { sprintf( buf, "Sorry, you are not authorized to use %s.\n\r", skllnm ); send_to_char( buf, ch ); return FALSE; } return TRUE; } void end_of_game( void ) { DESCRIPTOR_DATA *d; DESCRIPTOR_DATA *d_next; db_dump( ); for( d = descriptor_list; d; d = d_next ) { d_next = d->next; if( !IS_SET( d->interpreter->flags, INTERPRETER_SAFE ) ) { if( d->character->position == POS_FIGHTING ) interpret( d->character, "save" ); else interpret( d->character, "quit" ); } else close_socket( d ); } #if defined( HAVE_ISPELL ) ispell_done(); #endif return; } int race_lookup( const char *race ) { int index; for( index = 0; index < MAX_RACE; index++ ) if( !str_prefix( race, race_table[index].name ) ) return index; return -1; } int race_full_lookup( const char *race ) { int index; for( index = 0; index < MAX_RACE; index++ ) if( !str_cmp( race, race_table[index].name ) ) return index; return NO_FLAG; } int affect_lookup( const char *affectname ) { int index; for( index = 0; index < MAX_SKILL; index++ ) if( !str_cmp( affectname, skill_table[index].name ) ) return index; return -1; } int liquid_lookup( const char *name ) { int i; for( i = 0; liq_table[i].liq_name != NULL; i++ ) if( !str_cmp( liq_table[i].liq_name, name ) ) return i; return 0; } PLANE_DATA *plane_lookup( const char *name ) { PLANE_DATA *pl; for( pl = plane_list; pl; pl = pl->next ) { if( !str_prefix( name, pl->name ) ) break; } return pl; } int power( int base, int percentage, int diff ) { double jig; jig = ( 100.0 + (double)percentage ) / 100.0; jig = pow( jig, diff ); jig = jig * base; return (int)jig; } int str_app_wield( int str ) { return power( 15, 10, str - 15 ); } int get_ac( CHAR_DATA *ch ) { int ac, dex; ac = ch->armour; if( IS_NPC( ch ) || !IS_AWAKE( ch ) || IS_AFFECTED( ch, AFF_HOLD ) ) return ac; if( ( dex = get_curr_dex( ch ) ) < 6 ) return ac + ( 6 - dex ) * 5; if( dex <= 10 ) return ac; if( ch->class == CLASS_MARTIAL_ARTIST || get_first_class( ch ) == CLASS_MARTIAL_ARTIST || get_second_class( ch ) == CLASS_MARTIAL_ARTIST ) return ( ac + power( -30, 12, dex - 18 ) ); return power( -30, 10, dex - 20 ) + 11 + ac; } int total_mana_cost( CHAR_DATA *ch, int sn ) { int tot = 0, sph; for( sph = 0; sph < MAGIC_MAX + 1; ++sph ) tot += mana_cost( ch, sn, sph ); return tot; } /* * New mana cost function which is, by necessity, more complicated than the * macro previously supplied. * Finds the means by which the character can say they have known the spell * for longer then uses that number. --Symposium. */ int mana_cost( CHAR_DATA *ch, int sn, int sphere ) { int i; int level = 0; if( ( IS_NPC( ch ) && ch->class < 0 ) || ch->level >= LEVEL_IMMORTAL || ( !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) ) ) return 0; if( !IS_NPC( ch ) ) for( i = 0; ch->level < skill_table[sn].skill_level[ch->class] && i < NUM_MULTI_CLASS && level < 4; ++i ) { if( ch->pcdata->multi_class[i] == CLASS_ASPIRING && level < ch->sublevel - skill_table[sn].skill_level[i] ) level = ch->sublevel - skill_table[sn].skill_level[i]; if( ch->pcdata->multi_class[i] >= CLASS_ADEPT && level < ch->level - skill_table[sn].skill_level[i] ) level = ch->level - skill_table[sn].skill_level[i]; } level = UMAX( level, ch->level - skill_table[sn].skill_level[ch->class] ); return UMAX( skill_table[sn].min_mana[sphere], ( 6 * skill_table[sn].min_mana[sphere] ) / ( 2 + level ) ); } void mod_item_condition( CHAR_DATA *ch, OBJ_DATA *obj, int amount ) { if( ch == NULL && obj->in_room ) ch = obj->in_room->people; if( IS_SET( obj->extra_flags, ITEM_BLESS ) ) obj->condition -= UMIN( number_range( 1, obj->pIndexData->material ), number_range( 1, obj->pIndexData->material ) ) * amount; else obj->condition -= number_range( 1, obj->pIndexData->material ) * amount; if( obj->condition <= 0 || ( IS_SET( obj->extra_flags, ITEM_FRAGILE ) && number_bits( 2 ) == 0 ) ) { if( ch ) act( "&r$p is totally ruined.&n", ch, obj, NULL, TO_ALL ); extract_obj( obj ); return; } if( ch ) act( "&r$p is damaged!&n", ch, obj, NULL, TO_ALL ); if( number_bits( 7 ) == 0 ) { if( ch ) act( "&r$p becomes more fragile!", ch, obj, NULL, TO_ALL ); SET_BIT( obj->extra_flags, ITEM_FRAGILE ); } if( xIS_SET( obj->pIndexData->progtypes, DAMAGE_PROG ) ) oprog_percent_check( ch, obj, NULL, DAMAGE_PROG ); return; } char *int_to_str_special( int num ) { static char buf[25]; const char multiples[] = "KMBT"; int i, max; int places, points; i = 3 + 4 * strlen( multiples ); max = i + 1; places = points = 0; while( i > 1 ) { buf[i--] = '0' + abs( num ) % 10; places++; num /= 10; if( places % 3 == 0 && abs( num ) >= 5 ) { points++; buf[i--] = '.'; } } ++i; while( buf[i] == '0' || buf[i] == '.' ) ++i; if( buf[i] == '\0' ) return strcpy( buf, "0" ); max = UMIN( max, i + 4 ); if( points > 0 ) { if( buf[max - 1] == '.' ) max--; buf[max++] = multiples[points - 1]; } buf[max] = '\0'; if( num < 0 ) buf[--i] = '-'; return &buf[i]; } int atoi_special( const char *str ) { const char *multiples = "kKmMbBtT"; int multiply; char *p; char *q; int i; if( is_number( str ) ) return atoi( str ); q = strchr( str, '\0' ); if( !q || --q < str ) return -1; while( isspace( *q ) && q > str ) q--; p = strchr( multiples, *q ); if( !p || q == str || !isdigit( *(q-1) ) ) return -1; multiply = ( p - multiples ) / 2; multiply = pow( 1000, 1 + multiply ); *q = '\0'; if( !( p = strchr( str, '.' ) ) ) { if( is_number( str ) ) return multiply * atoi( str ); } else { int j; *p++ = '\0'; if( !is_number( str ) || !is_number( p ) ) return -1; i = atoi( p ); j = (int)log10( ( double )i ) + 1; i = multiply * i / pow( 10, j ); i += atoi( str ) * multiply; return i; } return -1; } /* * Clear all the character's stats, wipe the slate clean. */ void clean_char( CHAR_DATA *ch ) { int i; ch->armour = 100; ch->hit = UMAX( 1, ch->hit ); for( i = 0; i < MAGIC_MAX; ++i ) ch->mana[i] = UMAX( 1, ch->mana[i] ); ch->move = UMAX( 1, ch->move ); ch->hitroll = 0; ch->damroll = 0; ch->saving_throw = 0; ch->resil_mod = 0; ch->temp_mod = 0; ch->speed = 0; ch->on = NULL; if( !IS_NPC( ch ) ) { ch->pcdata->mod_str = 0; ch->pcdata->mod_int = 0; ch->pcdata->mod_wis = 0; ch->pcdata->mod_dex = 0; ch->pcdata->mod_con = 0; for( i = 0; i < MAGIC_MAX; ++i ) ch->pcdata->mod_magic[i] = 0; } return; } /* Vector manipulation */ int *vzero( int *vector ) { register int i; for( i = 0; i < MAX_VECTOR; i++ ) vector[i] = 0; return vector; } int *vcopy( int *dest, const int *src ) { register int i; for( i = 0; i < MAX_VECTOR; i++ ) dest[i] = src[i]; return dest; } int *vset( int *dest, const int bit ) { vzero( dest ); xSET_BIT( dest, bit ); return dest; } bool vequal( const int *a, const int *b ) { register int i; for( i = 0; i < MAX_VECTOR; i++ ) if( a[i] != b[i] ) return FALSE; return TRUE; } bool vnull( const int *a ) { register int i; for( i = 0; i < MAX_VECTOR; i++ ) if( a[i] ) return FALSE; return TRUE; } /* * Economy section, this is to make areas have some limit to the gold * that they have. * By Thoric of SMAUG * * Used in db.c when resetting a mob into an area -Thoric * Makes sure mob doesn't get more than 10% of that area's gold, * and reduces area economy by the amount of gold given to the mob */ void economize_mobgold( CHAR_DATA *mob ) { AREA_DATA *tarea; int scaler; /* make sure it isn't way too much */ mob->gold = UMIN( mob->gold, mob->level * mob->level * 400 ); if( !mob->in_room ) return; tarea = mob->in_room->area; if( tarea->ave && tarea->nmobile > 5 ) { scaler = tarea->economy / tarea->ave / tarea->ave / tarea->nmobile / 3 + 1; if( number_bits( 5 ) < UMIN( 5, scaler ) ) mob->gold = number_range( mob->gold, mob->gold * 4 ); } mob->gold = URANGE( 2, mob->gold, tarea->economy / 10 ); if( mob->gold ) tarea->economy -= mob->gold; }