/*___________________________________________________________________________*
)()( 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;
}