/***************************************************************************** * DikuMUD (C) 1990, 1991 by: * * Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen, * * and Katja Nyboe. * *---------------------------------------------------------------------------* * MERC 2.1 (C) 1992, 1993 by: * * Michael Chastain, Michael Quan, and Mitchell Tse. * *---------------------------------------------------------------------------* * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider. * * Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, * * gorog, Grishnakh, Nivek, Tricops, and Fireblade. * *---------------------------------------------------------------------------* * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community. * * Their contributions are greatly appreciated. * *---------------------------------------------------------------------------* * LoP (C) 2006, 2007, 2008 by: the LoP team. * *---------------------------------------------------------------------------* * Main structure manipulation module * *****************************************************************************/ #include <ctype.h> #include <stdio.h> #include <string.h> #include <time.h> #include <sys/stat.h> #include <unistd.h> #include "h/mud.h" extern int top_exit; extern int top_ed; extern int top_affect; extern int cur_qchars; extern CHAR_DATA *gch_prev; extern OBJ_DATA *gobj_prev; extern REL_DATA *first_relation, *last_relation; OBJ_DATA *get_eq_location( CHAR_DATA *ch, int iWear ); void check_chareq( CHAR_DATA *ch ); void remove_char_from_group( CHAR_DATA *ch ); CHAR_DATA *cur_char; bool cur_char_died; ch_ret global_retcode; int cur_obj; bool cur_obj_extracted; obj_ret global_objcode; void update_room_reset( CHAR_DATA *ch ); OBJ_DATA *group_object( OBJ_DATA *obj1, OBJ_DATA *obj2 ); bool in_magic_container( OBJ_DATA *obj ); void delete_reset( RESET_DATA *pReset ); bool stop_obj_fishing( OBJ_DATA *obj ); bool stop_fishing( CHAR_DATA *ch ); void remove_file( const char *filename ) { unlink( filename ); } bool check_parse_name( char *name, bool newchar ); bool valid_pfile( const char *filename ) { char buf[MSL]; struct stat fst; if( !filename || filename[0] == '\0' ) return false; snprintf( buf, sizeof( buf ), "%s%c/%s", PLAYER_DIR, tolower( filename[0] ), capitalize( filename ) ); if( stat( buf, &fst ) == -1 || !check_parse_name( capitalize( filename ), false ) ) return false; return true; } char *shorttime( time_t updated ) { static char buf[MIL]; struct tm *time; if( !updated ) return (char *)""; time = localtime( &updated ); snprintf( buf, sizeof( buf ), "%2d/%2d/%4.4d", ( time->tm_mon + 1 ), time->tm_mday, ( time->tm_year + 1900 ) ); return buf; } char *distime( time_t updated ) { char wday_name[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; char month_name[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char buf[MIL]; struct tm *time; bool am = true; int hour; if( !updated ) return (char *)""; time = localtime( &updated ); hour = time->tm_hour; if( hour >= 12 ) { am = false; hour -= 12; } if( hour == 0 ) hour = 12; snprintf( buf, sizeof( buf ), "%3.3s %3.3s %2d %2d:%.2d:%.2d%2.2s %4.4d", wday_name[time->tm_wday], month_name[time->tm_mon], time->tm_mday, hour, time->tm_min, time->tm_sec, am ? "AM" : "PM", ( time->tm_year + 1900 ) ); return buf; } void explore_room( CHAR_DATA *ch, ROOM_INDEX_DATA *room ) { EXP_DATA *fexp; int exp, cnt = 0; if( !ch || !ch->pcdata || is_npc( ch ) || !room || !xIS_SET( room->room_flags, ROOM_EXPLORER ) ) return; for( fexp = ch->pcdata->first_explored; fexp; fexp = fexp->next ) { if( fexp->vnum == room->vnum ) return; cnt++; /* The more of them they find the more exp they gain */ } CREATE( fexp, EXP_DATA, 1 ); fexp->vnum = room->vnum; LINK( fexp, ch->pcdata->first_explored, ch->pcdata->last_explored, next, prev ); exp = ( 100 * cnt ); gain_exp( ch, exp ); } /* Retrieve a character's permission for checking. */ short get_trust( CHAR_DATA *ch ) { if( !ch ) return 0; return ch->trust; } /* Retrieve a character's age. */ short get_age( CHAR_DATA *ch ) { if( is_npc( ch ) ) return 0; if( time_info.month > ch->pcdata->birth_month ) return ( time_info.year - ch->pcdata->birth_year ); if( time_info.month == ch->pcdata->birth_month && time_info.day >= ch->pcdata->birth_day ) return ( time_info.year - ch->pcdata->birth_year ); return ( ( time_info.year - 1 ) - ch->pcdata->birth_year ); } /* Update characters maxes */ void update_maxes( CHAR_DATA *ch ) { /* Start again with race's base hit */ ch->max_hit = race_table[ch->race]->hit; /* Toss in affect from constitution */ ch->max_hit += get_curr_con( ch ); /* Start again with race's base move */ ch->max_move = race_table[ch->race]->move; /* Toss in affect from constitution and dexterity */ ch->max_move += ( ( get_curr_con( ch ) + get_curr_dex( ch ) ) / 4 ); /* Start again with race's base mana */ ch->max_mana = race_table[ch->race]->mana; /* Toss in affect from intelligence and wisdom */ ch->max_mana += ( ( ( get_curr_int( ch ) * 2 ) + get_curr_wis( ch ) ) / 8 ); } short get_perm_stat( int stat, CHAR_DATA *ch ) { if( stat < 0 || stat >= STAT_MAX ) return 0; return ch->perm_stats[stat]; } short get_mod_stat( int stat, CHAR_DATA *ch ) { if( stat < 0 || stat >= STAT_MAX ) return 0; return ch->mod_stats[stat]; } short get_curr_stat( int stat, CHAR_DATA *ch ) { OBJ_DATA *obj; short eqcheck, cha; if( stat < 0 || stat >= STAT_MAX ) return 0; if( stat == STAT_CHA ) { cha = ( ch->perm_stats[stat] + ch->mod_stats[stat] ); /* Decrease based on blood on character */ cha = UMAX( 0, ( cha - ch->bsplatter ) ); /* Decrease for no/bloody/stained equipment on locations */ for( eqcheck = 0; eqcheck < WEAR_MAX; eqcheck++ ) { if( !( obj = get_eq_location( ch, eqcheck ) ) ) /* Decrease 1 if no eq on the location */ { /* Limit the locations we check to see if they are naked */ if( eqcheck != WEAR_BODY && eqcheck != WEAR_ARMS && eqcheck != WEAR_LEGS ) continue; cha = UMAX( 0, ( cha - 1 ) ); } else /* Decrease if blood/stain on any worn object, the more blood/stains the uglier they are */ cha = UMAX( 0, ( cha - ( obj->bsplatter + obj->bstain ) ) ); } return cha; } return ( ch->perm_stats[stat] + ch->mod_stats[stat] ); } /* Retrieve character's current strength. */ short get_curr_str( CHAR_DATA *ch ) { return get_curr_stat( STAT_STR, ch ); } /* Retrieve character's current intelligence. */ short get_curr_int( CHAR_DATA *ch ) { return get_curr_stat( STAT_INT, ch ); } /* Retrieve character's current wisdom. */ short get_curr_wis( CHAR_DATA *ch ) { return get_curr_stat( STAT_WIS, ch ); } /* Retrieve character's current dexterity. */ short get_curr_dex( CHAR_DATA *ch ) { return get_curr_stat( STAT_DEX, ch ); } /* Retrieve character's current constitution. */ short get_curr_con( CHAR_DATA *ch ) { return get_curr_stat( STAT_CON, ch ); } /* Retrieve character's current charisma. */ short get_curr_cha( CHAR_DATA *ch ) { return get_curr_stat( STAT_CHA, ch ); } /* Retrieve character's current luck. */ short get_curr_lck( CHAR_DATA *ch ) { return get_curr_stat( STAT_LCK, ch ); } /* Retrieve the number of items a character can carry */ int can_carry_n( CHAR_DATA *ch ) { int penalty = 0; if( !is_npc( ch ) && get_trust( ch ) >= PERM_IMM ) return get_trust( ch ) * 200; if( is_npc( ch ) && xIS_SET( ch->act, ACT_IMMORTAL ) ) return ch->level * 200; if( get_eq_char( ch, WEAR_HOLD_B ) ) ++penalty; if( get_eq_char( ch, WEAR_HOLD_L ) ) ++penalty; if( get_eq_char( ch, WEAR_HOLD_R ) ) ++penalty; return URANGE( 5, ( ch->level + 15 ) / 5 + get_curr_dex( ch ) - 13 - penalty, 20 ); } /* Retrieve a character's carry capacity. */ int can_carry_w( CHAR_DATA *ch ) { int carry; if( !is_npc( ch ) && get_trust( ch ) >= PERM_IMM ) return 1000000; if( is_npc( ch ) && xIS_SET( ch->act, ACT_IMMORTAL ) ) return 1000000; carry = get_curr_str( ch ); return URANGE( carry, carry * ch->level, 1000000 ); } /* See if a player/mob can take a piece of prototype eq - Thoric */ bool can_take_proto( CHAR_DATA *ch ) { if( is_immortal( ch ) ) return true; else if( is_npc( ch ) && xIS_SET( ch->act, ACT_PROTOTYPE ) ) return true; else return false; } /* See if a string is one of the names of an object. */ bool is_name( const char *str, char *namelist ) { char name[MIL]; while( namelist && namelist[0] != '\0' ) { namelist = one_argument( namelist, name ); if( !str_cmp( str, name ) ) return true; } return false; } bool is_name_prefix( const char *str, char *namelist ) { char name[MIL]; while( namelist && namelist[0] != '\0' ) { namelist = one_argument( namelist, name ); if( !str_prefix( str, name ) ) return true; } return false; } /* * See if a string is one of the names of an object. - Thoric * Treats a dash as a word delimiter as well as a space */ bool is_name2( const char *str, char *namelist ) { char name[MIL]; while( namelist && namelist[0] != '\0' ) { namelist = one_argument2( namelist, name ); if( !str_cmp( str, name ) ) return true; } return false; } bool is_name2_prefix( const char *str, char *namelist ) { char name[MIL]; while( namelist && namelist[0] != '\0' ) { namelist = one_argument2( namelist, name ); if( !str_prefix( str, name ) ) return true; } return false; } /* Checks if str is a name in namelist supporting multiple keywords - Thoric */ bool nifty_is_name( char *str, char *namelist ) { char name[MIL]; while( str && str[0] != '\0' ) { str = one_argument2( str, name ); if( !is_name2( name, namelist ) ) return false; } return true; } bool nifty_is_name_prefix( char *str, char *namelist ) { char name[MIL]; while( str && str[0] != '\0' ) { str = one_argument2( str, name ); if( !is_name2_prefix( name, namelist ) ) return false; } return true; } /* Apply or remove an affect to a character. */ void affect_modify( CHAR_DATA *ch, AFFECT_DATA *paf, bool fAdd ) { SKILLTYPE *skill; int mod, x; ch_ret retcode; mod = paf->modifier; if( fAdd ) { if( ( paf->location % REVERSE_APPLY ) == APPLY_EXT_AFFECT ) { if( !xIS_EMPTY( paf->bitvector ) ) xSET_BITS( ch->affected_by, paf->bitvector ); } } else { if( ( paf->location % REVERSE_APPLY ) == APPLY_EXT_AFFECT ) xREMOVE_BITS( ch->affected_by, paf->bitvector ); switch( paf->location % REVERSE_APPLY ) { case APPLY_EXT_AFFECT: xREMOVE_BIT( ch->affected_by, mod ); return; case APPLY_RESISTANT: xREMOVE_BIT( ch->resistant, mod ); return; case APPLY_IMMUNE: xREMOVE_BIT( ch->immune, mod ); return; case APPLY_ABSORB: xREMOVE_BIT( ch->absorb, mod ); return; case APPLY_SUSCEPTIBLE: xREMOVE_BIT( ch->susceptible, mod ); return; default: break; } mod = 0 - mod; } switch( paf->location % REVERSE_APPLY ) { default: bug( "%s: unknown location %d.", __FUNCTION__, paf->location ); return; case APPLY_NONE: break; case APPLY_HIT: ch->max_hit += mod; break; case APPLY_MOVE: ch->max_move += mod; break; case APPLY_MANA: ch->max_mana += mod; break; case APPLY_ARMOR: ch->armor += mod; break; case APPLY_HITROLL: ch->hitroll += mod; break; case APPLY_DAMROLL: ch->damroll += mod; break; case APPLY_WAITSTATE: wait_state( ch, mod ); break; case APPLY_STAT: for( x = 0; x < STAT_MAX; x++ ) { if( xIS_SET( paf->bitvector, x ) ) ch->mod_stats[x] += mod; } break; /* Bitvector modifying apply types */ case APPLY_EXT_AFFECT: if( mod < 0 || mod >= AFF_MAX ) bug( "%s: unknown affect: mod (%d) paf->modifier (%d)", __FUNCTION__, mod, paf->modifier ); else if( xIS_EMPTY( paf->bitvector ) || ( !xIS_EMPTY( paf->bitvector ) && mod != 0 ) ) xSET_BIT( ch->affected_by, mod ); break; case APPLY_RESISTANT: if( mod < 0 || mod >= RIS_MAX ) bug( "%s: unknown resistant: mod (%d) paf->modifier (%d)", __FUNCTION__, mod, paf->modifier ); else xSET_BIT( ch->resistant, mod ); break; case APPLY_IMMUNE: if( mod < 0 || mod >= RIS_MAX ) bug( "%s: unknown immune: mod (%d) paf->modifier (%d)", __FUNCTION__, mod, paf->modifier ); else xSET_BIT( ch->immune, mod ); break; case APPLY_ABSORB: if( mod < 0 || mod >= RIS_MAX ) bug( "%s: unknown absorb: mod (%d) paf->modifier (%d)", __FUNCTION__, mod, paf->modifier ); else xSET_BIT( ch->absorb, mod ); break; case APPLY_SUSCEPTIBLE: if( mod < 0 || mod >= RIS_MAX ) bug( "%s: unknown susceptible: mod (%d) paf->modifier (%d)", __FUNCTION__, mod, paf->modifier ); else xSET_BIT( ch->susceptible, mod ); break; case APPLY_WEAPONSPELL: /* see fight.c */ break; case APPLY_STRIPSN: if( is_valid_sn( mod ) ) affect_strip( ch, mod ); else bug( "%s: APPLY_STRIPSN invalid sn %d", __FUNCTION__, mod ); break; case APPLY_WEARSPELL: case APPLY_REMOVESPELL: if( xIS_SET( ch->in_room->room_flags, ROOM_NO_MAGIC ) || xIS_SET( ch->immune, RIS_MAGIC ) || ( ( paf->location % REVERSE_APPLY ) == APPLY_WEARSPELL && !fAdd ) || ( ( paf->location % REVERSE_APPLY ) == APPLY_REMOVESPELL && fAdd ) || saving_char == ch /* so save/quit doesn't trigger */ || loading_char == ch ) /* so loading doesn't trigger */ return; mod = abs( mod ); if( is_valid_sn( mod ) && ( skill = skill_table[mod] ) && skill->type == SKILL_SPELL ) { if( skill->target == TAR_IGNORE || skill->target == TAR_OBJ_INV ) { bug( "APPLY_WEARSPELL/APPLY_REMOVESPELL trying to apply bad target spell. SN is %d.", mod ); return; } if( ( retcode = ( *skill->spell_fun ) ( mod, ch->level, ch, ch ) ) == rCHAR_DIED || char_died( ch ) ) return; } break; } check_chareq( ch ); } /* Give an affect to a char. */ void affect_to_char( CHAR_DATA *ch, AFFECT_DATA *paf ) { AFFECT_DATA *paf_new, *paf_check; SKILLTYPE *skill, *skill_new; bool inserted = false; if( !ch ) { bug( "%s: (NULL, %d)", __FUNCTION__, paf ? paf->type : 0 ); return; } if( !paf ) { bug( "%s: (%s, NULL)", __FUNCTION__, ch->name ); return; } CREATE( paf_new, AFFECT_DATA, 1 ); paf_new->type = paf->type; paf_new->duration = paf->duration; paf_new->location = paf->location; paf_new->modifier = paf->modifier; paf_new->bitvector = paf->bitvector; skill_new = get_skilltype( paf_new->type ); for( paf_check = ch->first_affect; paf_check; paf_check = paf_check->next ) { skill = get_skilltype( paf_check->type ); if( paf_new->duration < paf_check->duration ) { INSERT( paf_new, paf_check, ch->first_affect, next, prev ); inserted = true; break; } else if( paf_new->duration == paf_check->duration && skill_new && skill && strcmp( skill_new->name, skill->name ) <= 0 ) { INSERT( paf_new, paf_check, ch->first_affect, next, prev ); inserted = true; break; } } if( !inserted ) LINK( paf_new, ch->first_affect, ch->last_affect, next, prev ); affect_modify( ch, paf_new, true ); } /* Remove an affect from a char. */ void affect_remove( CHAR_DATA *ch, AFFECT_DATA *paf ) { if( !ch->first_affect ) { bug( "%s: (%s, %d): no affect.", __FUNCTION__, ch->name, paf ? paf->type : 0 ); return; } affect_modify( ch, paf, false ); UNLINK( paf, ch->first_affect, ch->last_affect, next, prev ); DISPOSE( paf ); } /* Strip all affects of a given sn. */ void affect_strip( CHAR_DATA *ch, int sn ) { AFFECT_DATA *paf, *paf_next; for( paf = ch->first_affect; paf; paf = paf_next ) { paf_next = paf->next; if( paf->type == sn ) affect_remove( ch, paf ); } } /* Return true if a char is affected by a spell. */ bool is_affected( CHAR_DATA *ch, int sn ) { AFFECT_DATA *paf; for( paf = ch->first_affect; paf; paf = paf->next ) if( paf->type == sn ) return true; return false; } /* * Add or enhance an affect. * Limitations put in place by Thoric, they may be high... but at least * they're there :) */ void affect_join( CHAR_DATA *ch, AFFECT_DATA *paf ) { AFFECT_DATA *paf_old; for( paf_old = ch->first_affect; paf_old; paf_old = paf_old->next ) if( paf_old->type == paf->type ) { paf->duration = UMIN( 1000000, paf->duration + paf_old->duration ); if( paf->modifier ) paf->modifier = UMIN( 5000, paf->modifier + paf_old->modifier ); else paf->modifier = paf_old->modifier; affect_remove( ch, paf_old ); break; } affect_to_char( ch, paf ); } /* Apply only affected and RIS on a char */ void aris_affect( CHAR_DATA *ch, AFFECT_DATA *paf ) { if( ( paf->location % REVERSE_APPLY ) == APPLY_EXT_AFFECT ) { if( !xIS_EMPTY( paf->bitvector ) ) xSET_BITS( ch->affected_by, paf->bitvector ); } switch( paf->location % REVERSE_APPLY ) { case APPLY_EXT_AFFECT: xSET_BIT( ch->affected_by, paf->modifier ); break; case APPLY_RESISTANT: xSET_BIT( ch->resistant, paf->modifier ); break; case APPLY_IMMUNE: xSET_BIT( ch->immune, paf->modifier ); break; case APPLY_ABSORB: xSET_BIT( ch->absorb, paf->modifier ); break; case APPLY_SUSCEPTIBLE: xSET_BIT( ch->susceptible, paf->modifier ); break; } } void update_ris( CHAR_DATA *ch ) { int ris; if( !ch ) return; /* We will toss through the resistant, immune, susceptible, and absorbs and remove ones unneeded */ for( ris = 0; ris < RIS_MAX; ris++ ) { /* Take care of ones they shouldn't have first */ if( xIS_SET( ch->no_absorb, ris ) ) xREMOVE_BIT( ch->absorb, ris ); if( xIS_SET( ch->no_immune, ris ) ) xREMOVE_BIT( ch->immune, ris ); if( xIS_SET( ch->no_resistant, ris ) ) xREMOVE_BIT( ch->resistant, ris ); if( xIS_SET( ch->no_susceptible, ris ) ) xREMOVE_BIT( ch->susceptible, ris ); /* If we have it in absorb don't need it anywhere else */ if( xIS_SET( ch->absorb, ris ) ) { xREMOVE_BIT( ch->immune, ris ); xREMOVE_BIT( ch->resistant, ris ); xREMOVE_BIT( ch->susceptible, ris ); } /* Same for if immune */ if( xIS_SET( ch->immune, ris ) ) { xREMOVE_BIT( ch->resistant, ris ); xREMOVE_BIT( ch->susceptible, ris ); } /* If they are resistant and susceptible they cancel each other out */ if( xIS_SET( ch->resistant, ris ) && xIS_SET( ch->susceptible, ris ) ) { xREMOVE_BIT( ch->resistant, ris ); xREMOVE_BIT( ch->susceptible, ris ); } } } /* * Update affecteds and RIS for a character in case things get messed. * This should only really be used as a quick fix until the cause * of the problem can be hunted down. - FB * Last modified: June 30, 1997 * * Quick fix? Looks like a good solution for a lot of problems. */ /* Temp mod to bypass immortals so they can keep their mset affects, * just a band-aid until we get more time to look at it -- Blodkai */ void update_aris( CHAR_DATA *ch ) { AFFECT_DATA *paf; OBJ_DATA *obj; int hiding; if( is_npc( ch ) || is_immortal( ch ) ) return; /* So chars using hide skill will continue to hide */ hiding = IS_AFFECTED( ch, AFF_HIDE ); xCLEAR_BITS( ch->affected_by ); xCLEAR_BITS( ch->no_affected_by ); xCLEAR_BITS( ch->resistant ); xCLEAR_BITS( ch->no_resistant ); xCLEAR_BITS( ch->immune ); xCLEAR_BITS( ch->no_immune ); xCLEAR_BITS( ch->absorb ); xCLEAR_BITS( ch->no_absorb ); xCLEAR_BITS( ch->susceptible ); xCLEAR_BITS( ch->no_susceptible ); /* Add in effects from race */ if( race_table[ch->race] ) { if( !xIS_EMPTY( race_table[ch->race]->affected ) ) xSET_BITS( ch->affected_by, race_table[ch->race]->affected ); if( !xIS_EMPTY( race_table[ch->race]->resist ) ) xSET_BITS( ch->resistant, race_table[ch->race]->resist ); if( !xIS_EMPTY( race_table[ch->race]->suscept ) ) xSET_BITS( ch->susceptible, race_table[ch->race]->suscept ); } /* Add in effects from deities */ if( ch->pcdata->deity ) { if( ch->pcdata->favor > ch->pcdata->deity->affectednum ) if( !xIS_EMPTY( ch->pcdata->deity->affected ) ) xSET_BITS( ch->affected_by, ch->pcdata->deity->affected ); if( ch->pcdata->favor > ch->pcdata->deity->resistnum ) if( !xIS_EMPTY( ch->pcdata->deity->resist ) ) xSET_BITS( ch->resistant, ch->pcdata->deity->resist ); if( ch->pcdata->favor < ch->pcdata->deity->susceptnum ) if( !xIS_EMPTY( ch->pcdata->deity->suscept ) ) xSET_BITS( ch->susceptible, ch->pcdata->deity->suscept ); } /* Add in effect from spells */ for( paf = ch->first_affect; paf; paf = paf->next ) aris_affect( ch, paf ); /* Add in effects from equipment */ for( obj = ch->first_carrying; obj; obj = obj->next_content ) { if( obj->wear_loc != WEAR_NONE ) { for( paf = obj->first_affect; paf; paf = paf->next ) aris_affect( ch, paf ); for( paf = obj->pIndexData->first_affect; paf; paf = paf->next ) aris_affect( ch, paf ); } } /* Add in effects for polymorph */ if( ch->morph ) { if( !xIS_EMPTY( ch->morph->affected_by ) ) xSET_BITS( ch->affected_by, ch->morph->affected_by ); if( !xIS_EMPTY( ch->morph->no_affected_by ) ) xSET_BITS( ch->no_affected_by, ch->morph->no_affected_by ); if( !xIS_EMPTY( ch->morph->immune ) ) xSET_BITS( ch->immune, ch->morph->immune ); if( !xIS_EMPTY( ch->morph->no_immune ) ) xSET_BITS( ch->no_immune, ch->morph->no_immune ); if( !xIS_EMPTY( ch->morph->absorb ) ) xSET_BITS( ch->absorb, ch->morph->absorb ); if( !xIS_EMPTY( ch->morph->no_absorb ) ) xSET_BITS( ch->no_absorb, ch->morph->no_absorb ); if( !xIS_EMPTY( ch->morph->resistant ) ) xSET_BITS( ch->resistant, ch->morph->resistant ); if( !xIS_EMPTY( ch->morph->no_resistant ) ) xSET_BITS( ch->no_resistant, ch->morph->no_resistant ); if( !xIS_EMPTY( ch->morph->suscept ) ) xSET_BITS( ch->susceptible, ch->morph->suscept ); if( !xIS_EMPTY( ch->morph->no_suscept ) ) xSET_BITS( ch->no_susceptible, ch->morph->no_suscept ); } /* If they were hiding before, make them hiding again */ if( hiding ) xSET_BIT( ch->affected_by, AFF_HIDE ); update_ris( ch ); } /* Adjust the rooms light for this ch */ void adjust_room_light( CHAR_DATA *ch, bool increase ) { OBJ_DATA *obj; for( obj = ch->first_carrying; obj; obj = obj->next_content ) { if( obj->wear_loc != WEAR_NONE && obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) { if( increase ) ++ch->in_room->light; else if( ch->in_room->light > 0 ) --ch->in_room->light; } } } /* Move a char out of a room. */ void char_from_room( CHAR_DATA *ch ) { if( !ch->in_room ) { bug( "%s: NULL.", __FUNCTION__ ); return; } if( !is_npc( ch ) ) --ch->in_room->area->nplayer; adjust_room_light( ch, false ); ch->in_room->charcount--; UNLINK( ch, ch->in_room->first_person, ch->in_room->last_person, next_in_room, prev_in_room ); ch->was_in_room = ch->in_room; ch->in_room = NULL; ch->next_in_room = NULL; ch->prev_in_room = NULL; if( !is_npc( ch ) && get_timer( ch, TIMER_SHOVEDRAG ) > 0 ) remove_timer( ch, TIMER_SHOVEDRAG ); } typedef struct teleport_data TELEPORT_DATA; /* Delayed teleport type. */ struct teleport_data { TELEPORT_DATA *next, *prev; ROOM_INDEX_DATA *room; short timer; }; TELEPORT_DATA *first_teleport, *last_teleport; void tele_update( void ) { TELEPORT_DATA *tele, *tele_next; if( !first_teleport ) return; for( tele = first_teleport; tele; tele = tele_next ) { tele_next = tele->next; if( --tele->timer <= 0 ) { if( tele->room->first_person ) { if( xIS_SET( tele->room->room_flags, ROOM_TELESHOWDESC ) ) teleport( tele->room->first_person, tele->room->tele_vnum, TELE_SHOWDESC | TELE_TRANSALL ); else teleport( tele->room->first_person, tele->room->tele_vnum, TELE_TRANSALL ); } UNLINK( tele, first_teleport, last_teleport, next, prev ); DISPOSE( tele ); } } } /* Move a char into a room. */ void char_to_room( CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex ) { if( !ch ) { bug( "%s: NULL ch!", __FUNCTION__ ); return; } if( !pRoomIndex || !get_room_index( pRoomIndex->vnum ) ) { if( ch->was_in_room && get_room_index( ch->was_in_room->vnum ) ) { bug( "%s: Trying to put (%s) in a NULL room! Putting char back in previous room!", __FUNCTION__, ch->name ); pRoomIndex = ch->was_in_room; } else if( !( pRoomIndex = get_room_index( sysdata.room_limbo ) ) ) bug( "%s: Trying to put %s in a NULL room! Putting char in limbo (%d)", __FUNCTION__, ch->name, sysdata.room_limbo ); else bug( "%s: Trying to put %s in a NULL room! Limbo [%d] doesn't exist prepare for a crash!", __FUNCTION__, ch->name, sysdata.room_limbo ); } stop_fishing( ch ); ch->in_room = pRoomIndex; LINK( ch, pRoomIndex->first_person, pRoomIndex->last_person, next_in_room, prev_in_room ); pRoomIndex->charcount++; if( !is_npc( ch ) ) if( ++pRoomIndex->area->nplayer > pRoomIndex->area->max_players ) pRoomIndex->area->max_players = pRoomIndex->area->nplayer; adjust_room_light( ch, true ); explore_room( ch, pRoomIndex ); if( !is_npc( ch ) && xIS_SET( pRoomIndex->room_flags, ROOM_SAFE ) && get_timer( ch, TIMER_SHOVEDRAG ) <= 0 ) add_timer( ch, TIMER_SHOVEDRAG, 10, NULL, 0 ); /* * Delayed Teleport rooms -Thoric * Should be the last thing checked in this function */ if( xIS_SET( pRoomIndex->room_flags, ROOM_TELEPORT ) && pRoomIndex->tele_delay > 0 ) { TELEPORT_DATA *tele; for( tele = first_teleport; tele; tele = tele->next ) if( tele->room == pRoomIndex ) return; CREATE( tele, TELEPORT_DATA, 1 ); LINK( tele, first_teleport, last_teleport, next, prev ); tele->room = pRoomIndex; tele->timer = pRoomIndex->tele_delay; } if( !ch->was_in_room ) ch->was_in_room = ch->in_room; } void free_teleports( void ) { TELEPORT_DATA *tele, *tele_next; for( tele = first_teleport; tele; tele = tele_next ) { tele_next = tele->next; UNLINK( tele, first_teleport, last_teleport, next, prev ); DISPOSE( tele ); } } /* Give an obj to a char. */ OBJ_DATA *obj_to_char( OBJ_DATA *obj, CHAR_DATA *ch ) { OBJ_DATA *otmp, *oret = obj; bool skipgroup = false, grouped = false; int oweight = get_obj_weight( obj ); int onum = get_obj_number( obj ); int wear_loc = obj->wear_loc; EXT_BV extra_flags = obj->extra_flags; if( is_obj_stat( obj, ITEM_PROTOTYPE ) ) { if( !is_immortal( ch ) && ( !is_npc( ch ) || !xIS_SET( ch->act, ACT_PROTOTYPE ) ) ) return obj_to_room( obj, ch->in_room ); } if( loading_char == ch ) if( obj->wear_loc != -1 || obj->t_wear_loc != -1 ) skipgroup = true; if( is_npc( ch ) && ch->pIndexData->pShop ) skipgroup = true; if( !skipgroup ) { for( otmp = ch->first_carrying; otmp; otmp = otmp->next_content ) { if( ( oret = group_object( otmp, obj ) ) == otmp ) { grouped = true; break; } } } if( !grouped ) { if( !is_npc( ch ) || !ch->pIndexData->pShop ) { LINK( obj, ch->first_carrying, ch->last_carrying, next_content, prev_content ); obj->carried_by = ch; obj->in_room = NULL; obj->in_obj = NULL; } else { /* If ch is a shopkeeper, add the obj using an insert sort */ for( otmp = ch->first_carrying; otmp; otmp = otmp->next_content ) { if( obj->level > otmp->level ) { INSERT( obj, otmp, ch->first_carrying, next_content, prev_content ); break; } else if( obj->level == otmp->level && strcmp( obj->short_descr, otmp->short_descr ) < 0 ) { INSERT( obj, otmp, ch->first_carrying, next_content, prev_content ); break; } } if( !otmp ) LINK( obj, ch->first_carrying, ch->last_carrying, next_content, prev_content ); obj->carried_by = ch; obj->in_room = NULL; obj->in_obj = NULL; } } if( wear_loc == WEAR_NONE ) { ch->carry_number += onum; ch->carry_weight += oweight; } else if( !xIS_SET( extra_flags, ITEM_MAGIC ) ) ch->carry_weight += oweight; return ( oret ? oret : obj ); } /* Take an obj from its character. */ void obj_from_char( OBJ_DATA *obj ) { CHAR_DATA *ch; if( !( ch = obj->carried_by ) ) { bug( "%s: null ch.", __FUNCTION__ ); return; } if( obj->wear_loc != WEAR_NONE ) unequip_char( ch, obj ); /* obj may drop during unequip... */ if( !obj->carried_by ) return; UNLINK( obj, ch->first_carrying, ch->last_carrying, next_content, prev_content ); if( is_obj_stat( obj, ITEM_COVERING ) && obj->first_content ) empty_obj( obj, NULL, NULL ); obj->in_room = NULL; obj->carried_by = NULL; ch->carry_number -= get_obj_number( obj ); ch->carry_weight -= get_obj_weight( obj ); } /* Uses a 0 and higher now, higher is better */ void apply_ac( CHAR_DATA *ch, OBJ_DATA *obj, int iWear, bool remove ) { if( obj->item_type != ITEM_ARMOR ) return; switch( iWear ) { case WEAR_HEAD: case WEAR_EARS: case WEAR_EAR_L: case WEAR_EAR_R: case WEAR_EYES: case WEAR_EYE_L: case WEAR_EYE_R: case WEAR_FACE: case WEAR_NECK: case WEAR_SHOULDERS: case WEAR_SHOULDER_L: case WEAR_SHOULDER_R: case WEAR_ABOUT: case WEAR_BODY: case WEAR_BACK: case WEAR_ARMS: case WEAR_ARM_L: case WEAR_ARM_R: case WEAR_HANDS: case WEAR_HAND_L: case WEAR_HAND_R: case WEAR_FINGERS: case WEAR_FINGER_L: case WEAR_FINGER_R: case WEAR_HOLD_B: case WEAR_HOLD_L: case WEAR_HOLD_R: case WEAR_WAIST: case WEAR_LEGS: case WEAR_LEG_L: case WEAR_LEG_R: case WEAR_ANKLES: case WEAR_ANKLE_L: case WEAR_ANKLE_R: case WEAR_FEET: case WEAR_FOOT_L: case WEAR_FOOT_R: if( remove ) ch->armor -= obj->value[0]; else ch->armor += obj->value[0]; if( ch->armor < 0 ) ch->armor = 0; break; } } /* * Find a piece of eq on a character. * Will pick the top layer if clothing is layered. -Thoric */ OBJ_DATA *get_eq_char( CHAR_DATA *ch, int iWear ) { OBJ_DATA *obj, *maxobj = NULL; for( obj = ch->first_carrying; obj; obj = obj->next_content ) if( obj->wear_loc == iWear ) { if( !obj->pIndexData->layers ) return obj; else if( !maxobj || obj->pIndexData->layers > maxobj->pIndexData->layers ) maxobj = obj; } return maxobj; } /* Equip a char with an obj. */ void equip_char( CHAR_DATA *ch, OBJ_DATA *obj, int iWear ) { MCLASS_DATA *mclass; AFFECT_DATA *paf; OBJ_DATA *otmp; int stat; if( obj->carried_by != ch ) { bug( "%s: obj not being carried by (%s)[%d]!", __FUNCTION__, ch->name, ch->pIndexData ? ch->pIndexData->vnum : 0 ); return; } if( !is_obj_stat( obj, ITEM_LODGED ) && ( otmp = get_eq_char( ch, iWear ) ) && ( !otmp->pIndexData->layers || !obj->pIndexData->layers ) ) { bug( "%s: already equipped on (%d) (%s) by (%s)[%d].", __FUNCTION__, iWear, wear_locs[iWear], ch->name, ch->pIndexData ? ch->pIndexData->vnum : 0 ); return; } separate_obj( obj ); if( !is_obj_stat( obj, ITEM_LODGED ) && ch->level < obj->level ) { ch_printf( ch, "You must be level %d to use this object.\r\n", obj->level ); act( AT_ACTION, "$n tries to use $p, but is too inexperienced.", ch, obj, NULL, TO_ROOM ); return; } for( stat = 0; stat < STAT_MAX; stat++ ) { if( !is_obj_stat( obj, ITEM_LODGED ) && get_curr_stat( stat, ch ) < obj->stat_reqs[stat] ) { ch_printf( ch, "You need to have %d %s to use this object.\r\n", obj->stat_reqs[STAT_STR], stattypes[stat] ); act( AT_ACTION, "$n tries to use $p, but lacks something to do so.", ch, obj, NULL, TO_ROOM ); return; } } if( !is_obj_stat( obj, ITEM_LODGED ) && !is_npc( ch ) && xIS_SET( obj->race_restrict, ch->race ) ) { send_to_char( "Your race can't wear this object.\r\n", ch ); act( AT_ACTION, "$n tries to use $p, but is forbidden to do so.", ch, obj, NULL, TO_ROOM ); return; } if( !is_obj_stat( obj, ITEM_LODGED ) && !is_npc( ch ) ) { for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next ) { if( mclass->wclass >= 0 && xIS_SET( obj->class_restrict, mclass->wclass ) ) { send_to_char( "Your class can't wear this object.\r\n", ch ); act( AT_ACTION, "$n tries to use $p, but is forbidden to do so.", ch, obj, NULL, TO_ROOM ); return; } } } if( !is_obj_stat( obj, ITEM_LODGED ) && ( ( 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 ) ) ) ) { if( loading_char != ch ) { act( AT_MAGIC, "You're zapped by $p.", ch, obj, NULL, TO_CHAR ); act( AT_MAGIC, "$n is zapped by $p.", ch, obj, NULL, TO_ROOM ); } oprog_zap_trigger( ch, obj ); if( xIS_SET( sysdata.save_flags, SV_ZAPDROP ) && !char_died( ch ) ) save_char_obj( ch ); return; } apply_ac( ch, obj, iWear, false ); obj->wear_loc = iWear; ch->carry_number -= get_obj_number( obj ); if( is_obj_stat( obj, ITEM_MAGIC ) ) ch->carry_weight -= get_obj_weight( obj ); for( paf = obj->pIndexData->first_affect; paf; paf = paf->next ) affect_modify( ch, paf, true ); for( paf = obj->first_affect; paf; paf = paf->next ) affect_modify( ch, paf, true ); if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 && ch->in_room ) ++ch->in_room->light; } /* 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( "%s: already unequipped.", __FUNCTION__ ); return; } ch->carry_number += get_obj_number( obj ); if( is_obj_stat( obj, ITEM_MAGIC ) ) ch->carry_weight += get_obj_weight( obj ); apply_ac( ch, obj, obj->wear_loc, true ); obj->wear_loc = -1; obj->t_wear_loc = -1; for( paf = obj->pIndexData->first_affect; paf; paf = paf->next ) affect_modify( ch, paf, false ); if( obj->carried_by ) for( paf = obj->first_affect; paf; paf = paf->next ) affect_modify( ch, paf, false ); update_aris( ch ); if( !obj->carried_by ) return; if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 && ch->in_room && ch->in_room->light > 0 ) --ch->in_room->light; } /* Move an obj out of a room. */ void write_corpses( OBJ_DATA *obj, bool dontsave ); int falling; void obj_from_room( OBJ_DATA *obj ) { ROOM_INDEX_DATA *in_room; if( !( in_room = obj->in_room ) ) { bug( "%s: NULL in_room.", __FUNCTION__ ); return; } in_room->objcount--; UNLINK( obj, in_room->first_content, in_room->last_content, next_content, prev_content ); /* uncover contents */ if( is_obj_stat( obj, ITEM_COVERING ) && obj->first_content ) empty_obj( obj, NULL, obj->in_room ); if( obj->item_type == ITEM_FIRE ) obj->in_room->light -= obj->count; obj->carried_by = NULL; obj->in_obj = NULL; obj->in_room = NULL; if( obj->pIndexData->vnum == OBJ_VNUM_CORPSE_PC && falling < 1 ) write_corpses( obj, true ); } /* Move an obj into a room. */ OBJ_DATA *obj_to_room( OBJ_DATA *obj, ROOM_INDEX_DATA *pRoomIndex ) { OBJ_DATA *otmp, *oret; short count = obj->count; short item_type = obj->item_type; for( otmp = pRoomIndex->first_content; otmp; otmp = otmp->next_content ) { if( ( oret = group_object( otmp, obj ) ) == otmp ) { if( item_type == ITEM_FIRE ) pRoomIndex->light += count; return oret; } } LINK( obj, pRoomIndex->first_content, pRoomIndex->last_content, next_content, prev_content ); pRoomIndex->objcount++; obj->in_room = pRoomIndex; obj->carried_by = NULL; obj->in_obj = NULL; obj->room_vnum = pRoomIndex->vnum; /* hotboot tracker */ if( item_type == ITEM_FIRE ) pRoomIndex->light += count; falling++; obj_fall( obj, false ); falling--; if( obj->pIndexData->vnum == OBJ_VNUM_CORPSE_PC && falling < 1 ) write_corpses( obj, false ); return obj; } /* Who's carrying an item -- recursive for nested objects - Thoric */ CHAR_DATA *carried_by( OBJ_DATA *obj ) { if( obj->in_obj ) return carried_by( obj->in_obj ); return obj->carried_by; } /* Move an object into an object. */ OBJ_DATA *obj_to_obj( OBJ_DATA *obj, OBJ_DATA *obj_to ) { OBJ_DATA *otmp, *oret; CHAR_DATA *who; if( obj == obj_to ) { bug( "%s: trying to put object inside itself: vnum %d", __FUNCTION__, obj->pIndexData->vnum ); return obj; } if( !in_magic_container( obj_to ) && ( who = carried_by( obj_to ) ) ) who->carry_weight += get_obj_weight( obj ); for( otmp = obj_to->first_content; otmp; otmp = otmp->next_content ) if( ( oret = group_object( otmp, obj ) ) == otmp ) return oret; LINK( obj, obj_to->first_content, obj_to->last_content, next_content, prev_content ); obj->in_obj = obj_to; obj->in_room = NULL; obj->carried_by = NULL; return obj; } /* Move an object out of an object. */ void obj_from_obj( OBJ_DATA *obj ) { OBJ_DATA *obj_from; bool magic; if( !( obj_from = obj->in_obj ) ) { bug( "%s: null obj_from.", __FUNCTION__ ); return; } magic = in_magic_container( obj_from ); UNLINK( obj, obj_from->first_content, obj_from->last_content, next_content, prev_content ); /* uncover contents */ if( is_obj_stat( obj, ITEM_COVERING ) && obj->first_content ) empty_obj( obj, obj->in_obj, NULL ); obj->in_obj = NULL; obj->in_room = NULL; obj->carried_by = NULL; if( !magic ) for( ; obj_from; obj_from = obj_from->in_obj ) if( obj_from->carried_by ) obj_from->carried_by->carry_weight -= get_obj_weight( obj ); } /* Extract an obj from the world. */ void extract_obj( OBJ_DATA *obj ) { OBJ_DATA *obj_content; REL_DATA *RQueue, *rq_next; if( !obj ) return; if( obj->item_type == ITEM_FISHINGPOLE ) stop_obj_fishing( obj ); if( obj->item_type == ITEM_PORTAL ) remove_portal( obj ); if( obj->carried_by ) obj_from_char( obj ); if( obj->in_room ) obj_from_room( obj ); if( obj->in_obj ) obj_from_obj( obj ); while( ( obj_content = obj->last_content ) ) extract_obj( obj_content ); /* remove affects */ { AFFECT_DATA *paf, *paf_next; for( paf = obj->first_affect; paf; paf = paf_next ) { paf_next = paf->next; DISPOSE( paf ); top_affect--; } obj->first_affect = obj->last_affect = NULL; } /* remove extra descriptions */ { EXTRA_DESCR_DATA *ed, *ed_next; for( ed = obj->first_extradesc; ed; ed = ed_next ) { ed_next = ed->next; STRFREE( ed->description ); STRFREE( ed->keyword ); DISPOSE( ed ); } obj->first_extradesc = obj->last_extradesc = NULL; } if( obj == gobj_prev ) gobj_prev = obj->prev; for( RQueue = first_relation; RQueue; RQueue = rq_next ) { rq_next = RQueue->next; if( RQueue->Type == relOSET_ON ) { if( obj == RQueue->Subject ) ( ( CHAR_DATA * ) RQueue->Actor )->dest_buf = NULL; else continue; UNLINK( RQueue, first_relation, last_relation, next, prev ); DISPOSE( RQueue ); } } UNLINK( obj, first_object, last_object, next, prev ); if( obj->pIndexData->vnum == OBJ_VNUM_CORPSE_PC ) { UNLINK( obj, first_corpse, last_corpse, next_corpse, prev_corpse ); --num_corpses; } UNLINK( obj, obj->pIndexData->first_copy, obj->pIndexData->last_copy, next_index, prev_index ); obj->pIndexData->count -= obj->count; numobjsloaded -= obj->count; --physicalobjects; STRFREE( obj->name ); STRFREE( obj->short_descr ); STRFREE( obj->description ); STRFREE( obj->action_desc ); STRFREE( obj->owner ); DISPOSE( obj ); } void extract_character_obj( OBJ_DATA *obj ) { OBJ_DATA *obj_content, *obj_content_next; for( obj_content = obj->first_content; obj_content != NULL; obj_content = obj_content_next ) { obj_content_next = obj_content->next_content; extract_character_obj( obj_content ); } if( is_obj_stat( obj, ITEM_QUEST ) ) return; if( obj->item_type != ITEM_CONTAINER || !obj->first_content ) extract_obj( obj ); } /* Extract a char from the world. */ void extract_char( CHAR_DATA *ch, bool fPull ) { CHAR_DATA *wch; OBJ_DATA *obj, *nextobj; char buf[MSL]; ROOM_INDEX_DATA *location; REL_DATA *RQueue, *rq_next; if( !ch ) { bug( "%s: NULL ch.", __FUNCTION__ ); return; } if( !ch->in_room ) { bug( "%s: %s in NULL room.", __FUNCTION__, ch->name ? ch->name : "???" ); return; } if( ch == supermob ) { bug( "%s: ch == supermob!", __FUNCTION__ ); return; } if( char_died( ch ) ) { bug( "%s: %s already died!", __FUNCTION__, ch->name ); return; } if( ch == cur_char ) cur_char_died = true; /* shove onto extraction queue */ queue_extracted_char( ch, fPull ); for( RQueue = first_relation; RQueue; RQueue = rq_next ) { rq_next = RQueue->next; if( fPull && RQueue->Type == relMSET_ON ) { if( ch == RQueue->Subject ) ( ( CHAR_DATA * ) RQueue->Actor )->dest_buf = NULL; else if( ch != RQueue->Actor ) continue; UNLINK( RQueue, first_relation, last_relation, next, prev ); DISPOSE( RQueue ); } } if( gch_prev == ch ) gch_prev = ch->prev; if( fPull ) die_follower( ch ); stop_fighting( ch, true ); if( ch->mount ) { xREMOVE_BIT( ch->mount->act, ACT_MOUNTED ); ch->mount = NULL; ch->position = POS_STANDING; } if( ch->mounter ) { ch->mounter = NULL; } /* If player goes link dead and was editing something update it all */ if( ch->dest_buf ) { if( ( wch = ( CHAR_DATA * )ch->dest_buf ) ) { if( wch->editing ) { stop_editing( ch ); /* This is correct as ch and not wch */ wch->editing = NULL; } } ch->dest_buf = NULL; ch->spare_ptr = NULL; } /* Update the one who was editing the ch */ if( ch->editing ) { stop_editing( ch->editing ); ch->editing->substate = SUB_NONE; ch->editing->dest_buf = NULL; ch->editing->spare_ptr = NULL; send_to_char( "What you were editing has been extracted.\r\n", ch->editing ); ch->editing = NULL; } /* check if this NPC was a mount, pet or in a group */ if( is_npc( ch ) ) { update_room_reset( ch ); for( wch = first_char; wch; wch = wch->next ) { if( wch->mount == ch && wch != quitting_char ) { wch->mount = NULL; wch->position = POS_STANDING; if( wch->in_room == ch->in_room ) { act( AT_SOCIAL, "Your faithful mount, $N collapses beneath you...", wch, NULL, ch, TO_CHAR ); act( AT_SOCIAL, "Sadly you dismount $M for the last time.", wch, NULL, ch, TO_CHAR ); act( AT_PLAIN, "$n sadly dismounts $N for the last time.", wch, NULL, ch, TO_ROOM ); } } if( wch->pcdata ) { CHAR_DATA *pet; for( pet = wch->pcdata->first_pet; pet; pet = pet->next_pet ) { if( pet != ch ) continue; UNLINK( pet, wch->pcdata->first_pet, wch->pcdata->last_pet, next_pet, prev_pet ); if( wch->in_room == ch->in_room ) act( AT_SOCIAL, "You mourn for the loss of $N.", wch, NULL, ch, TO_CHAR ); } } } xREMOVE_BIT( ch->act, ACT_MOUNTED ); if( ch->group ) remove_char_from_group( ch ); } if( fPull ) { while( ( obj = ch->last_carrying ) ) extract_obj( obj ); } else { for( obj = ch->first_carrying; obj; obj = nextobj ) { nextobj = obj->next_content; extract_character_obj( obj ); } } char_from_room( ch ); if( !fPull ) { location = NULL; if( !is_npc( ch ) && ch->pcdata->nation ) location = get_room_index( ch->pcdata->nation->recall ); if( !location && !is_npc( ch ) && ch->pcdata->clan ) location = get_room_index( ch->pcdata->clan->recall ); if( !location ) location = get_room_index( sysdata.room_altar ); if( !location ) location = get_room_index( sysdata.room_limbo ); if( !location ) bug( "No location to send (%s) to...this could cause alot of issues!!!!", ch->name ); char_to_room( ch, location ); /* Make things a little fancier - Thoric */ if( ( wch = get_char_room( ch, (char *)"healer" ) ) ) { act( AT_MAGIC, "$n mutters a few incantations, waves $s hands and points $s finger.", wch, NULL, NULL, TO_ROOM ); act( AT_MAGIC, "$n appears from some strange swirling mists!", ch, NULL, NULL, TO_ROOM ); snprintf( buf, sizeof( buf ), "say Welcome back to the land of the living, %s", capitalize( ch->name ) ); interpret( wch, buf ); } else act( AT_MAGIC, "$n appears from some strange swirling mists!", ch, NULL, NULL, TO_ROOM ); ch->position = POS_RESTING; return; } if( is_npc( ch ) ) { --ch->pIndexData->count; --nummobsloaded; UNLINK( ch, ch->pIndexData->first_copy, ch->pIndexData->last_copy, next_index, prev_index ); } for( wch = first_char; wch; wch = wch->next ) { if( wch->reply == ch ) wch->reply = NULL; if( wch->retell == ch ) wch->retell = NULL; } UNLINK( ch, first_char, last_char, next, prev ); if( ch->desc ) { if( ch->desc->character != ch ) bug( "%s: char's descriptor points to another char", __FUNCTION__ ); else { ch->desc->character = NULL; close_socket( ch->desc, false ); ch->desc = NULL; } } } /* Find a char in the room. */ CHAR_DATA *get_char_room( CHAR_DATA *ch, char *argument ) { char arg[MIL]; CHAR_DATA *rch; int number, count, vnum; number = number_argument( argument, arg ); if( !str_cmp( arg, "self" ) ) return ch; if( get_trust( ch ) >= PERM_BUILDER && is_number( arg ) ) vnum = atoi( arg ); else vnum = -1; count = 0; for( rch = ch->in_room->first_person; rch; rch = rch->next_in_room ) { if( !can_see( ch, rch ) ) continue; if( vnum != -1 ) { if( !is_npc( rch ) || vnum != rch->pIndexData->vnum ) continue; } else if( !nifty_is_name( arg, rch->name ) ) continue; if( number == 0 && !is_npc( rch ) ) return rch; else if( ++count == number ) return rch; } if( vnum != -1 ) return NULL; /* * If we didn't find an exact match, run through the list of characters * again looking for prefix matching, ie gu == guard. * Added by Narn, Sept/96 */ count = 0; for( rch = ch->in_room->first_person; rch; rch = rch->next_in_room ) { if( !can_see( ch, rch ) ) continue; if( !nifty_is_name_prefix( arg, rch->name ) ) continue; if( number == 0 && !is_npc( rch ) ) return rch; else if( ++count == number ) return rch; } return NULL; } /* Find a char in the world. */ CHAR_DATA *get_char_world( CHAR_DATA *ch, char *argument ) { char arg[MIL]; CHAR_DATA *wch; int number, count, vnum; number = number_argument( argument, arg ); count = 0; if( !str_cmp( arg, "self" ) ) return ch; /* Allow reference by vnum for saints+ -Thoric */ if( ch && get_trust( ch ) >= PERM_BUILDER && is_number( arg ) ) vnum = atoi( arg ); else vnum = -1; /* check the room for an exact match */ if( ch ) for( wch = ch->in_room->first_person; wch; wch = wch->next_in_room ) if( can_see( ch, wch ) && ( nifty_is_name( arg, wch->name ) || ( is_npc( wch ) && vnum == wch->pIndexData->vnum ) ) ) { if( number == 0 && !is_npc( wch ) ) return wch; else if( ++count == number ) return wch; } count = 0; /* check the world for an exact match */ for( wch = first_char; wch; wch = wch->next ) if( ( !ch || can_see( ch, wch ) ) && ( nifty_is_name( arg, wch->name ) || ( is_npc( wch ) && vnum == wch->pIndexData->vnum ) ) ) { if( number == 0 && !is_npc( wch ) ) return wch; else if( ++count == number ) return wch; } /* bail out if looking for a vnum match */ if( vnum != -1 ) return NULL; /* * If we didn't find an exact match, check the room for * for a prefix match, ie gu == guard. * Added by Narn, Sept/96 */ count = 0; if( ch ) for( wch = ch->in_room->first_person; wch; wch = wch->next_in_room ) { if( !can_see( ch, wch ) || !nifty_is_name_prefix( arg, wch->name ) ) continue; if( number == 0 && !is_npc( wch ) ) return wch; else if( ++count == number ) return wch; } /* * If we didn't find a prefix match in the room, run through the full list * of characters looking for prefix matching, ie gu == guard. * Added by Narn, Sept/96 */ count = 0; for( wch = first_char; wch; wch = wch->next ) { if( ( ch && !can_see( ch, wch ) ) || !nifty_is_name_prefix( arg, wch->name ) ) continue; if( number == 0 && !is_npc( wch ) ) return wch; else if( ++count == number ) return wch; } return NULL; } /* Find an obj in a list. */ OBJ_DATA *get_obj_list( CHAR_DATA *ch, char *argument, OBJ_DATA *list ) { char arg[MIL]; OBJ_DATA *obj; int number, count; number = number_argument( argument, arg ); count = 0; for( obj = list; obj; obj = obj->next_content ) if( can_see_obj( ch, obj ) && nifty_is_name( arg, obj->name ) ) if( ( count += obj->count ) >= number ) return obj; /* * If we didn't find an exact match, run through the list of objects * again looking for prefix matching, ie swo == sword. * Added by Narn, Sept/96 */ count = 0; for( obj = list; obj; obj = obj->next_content ) if( can_see_obj( ch, obj ) && nifty_is_name_prefix( arg, obj->name ) ) if( ( count += obj->count ) >= number ) return obj; return NULL; } /* Find an obj in a list...going the other way -Thoric */ OBJ_DATA *get_obj_list_rev( CHAR_DATA *ch, char *argument, OBJ_DATA *list ) { char arg[MIL]; OBJ_DATA *obj; int number, count; number = number_argument( argument, arg ); count = 0; for( obj = list; obj; obj = obj->prev_content ) if( can_see_obj( ch, obj ) && nifty_is_name( arg, obj->name ) ) if( ( count += obj->count ) >= number ) return obj; /* * If we didn't find an exact match, run through the list of objects * again looking for prefix matching, ie swo == sword. * Added by Narn, Sept/96 */ count = 0; for( obj = list; obj; obj = obj->prev_content ) if( can_see_obj( ch, obj ) && nifty_is_name_prefix( arg, obj->name ) ) if( ( count += obj->count ) >= number ) return obj; return NULL; } /* Find an obj in player's inventory or wearing via a vnum -Shaddai */ OBJ_DATA *get_obj_vnum( CHAR_DATA *ch, int vnum ) { OBJ_DATA *obj; for( obj = ch->last_carrying; obj; obj = obj->prev_content ) if( can_see_obj( ch, obj ) && obj->pIndexData->vnum == vnum ) return obj; return NULL; } /* Find an obj in player's inventory. */ OBJ_DATA *get_obj_carry( CHAR_DATA *ch, char *argument ) { char arg[MIL]; OBJ_DATA *obj; int number, count, vnum; number = number_argument( argument, arg ); if( get_trust( ch ) >= PERM_BUILDER && is_number( arg ) ) vnum = atoi( arg ); else vnum = -1; count = 0; for( obj = ch->last_carrying; obj; obj = obj->prev_content ) if( obj->wear_loc == WEAR_NONE && can_see_obj( ch, obj ) && ( nifty_is_name( arg, obj->name ) || obj->pIndexData->vnum == vnum ) ) if( ( count += obj->count ) >= number ) return obj; if( vnum != -1 ) return NULL; /* * If we didn't find an exact match, run through the list of objects * again looking for prefix matching, ie swo == sword. * Added by Narn, Sept/96 */ count = 0; for( obj = ch->last_carrying; obj; obj = obj->prev_content ) if( obj->wear_loc == WEAR_NONE && can_see_obj( ch, obj ) && nifty_is_name_prefix( arg, obj->name ) ) if( ( count += obj->count ) >= number ) return obj; return NULL; } /* Find an obj in player's equipment. */ OBJ_DATA *get_obj_wear( CHAR_DATA *ch, char *argument ) { char arg[MIL]; OBJ_DATA *obj; int number, count, vnum; number = number_argument( argument, arg ); if( get_trust( ch ) >= PERM_BUILDER && is_number( arg ) ) vnum = atoi( arg ); else vnum = -1; count = 0; for( obj = ch->last_carrying; obj; obj = obj->prev_content ) if( obj->wear_loc != WEAR_NONE && can_see_obj( ch, obj ) && ( nifty_is_name( arg, obj->name ) || obj->pIndexData->vnum == vnum ) ) if( ++count == number ) return obj; if( vnum != -1 ) return NULL; /* * If we didn't find an exact match, run through the list of objects * again looking for prefix matching, ie swo == sword. * Added by Narn, Sept/96 */ count = 0; for( obj = ch->last_carrying; obj; obj = obj->prev_content ) if( obj->wear_loc != WEAR_NONE && can_see_obj( ch, obj ) && nifty_is_name_prefix( 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, char *argument ) { OBJ_DATA *obj; obj = get_obj_list_rev( ch, argument, ch->in_room->last_content ); 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, char *argument ) { char arg[MIL]; OBJ_DATA *obj; int number, count, vnum; if( ( obj = get_obj_here( ch, argument ) ) ) return obj; number = number_argument( argument, arg ); /* Allow reference by vnum for saints+ -Thoric */ if( get_trust( ch ) >= PERM_BUILDER && is_number( arg ) ) vnum = atoi( arg ); else vnum = -1; count = 0; for( obj = first_object; obj; obj = obj->next ) if( can_see_obj( ch, obj ) && ( nifty_is_name( arg, obj->name ) || vnum == obj->pIndexData->vnum ) ) if( ( count += obj->count ) >= number ) return obj; /* bail out if looking for a vnum */ if( vnum != -1 ) return NULL; /* * If we didn't find an exact match, run through the list of objects * again looking for prefix matching, ie swo == sword. * Added by Narn, Sept/96 */ count = 0; for( obj = first_object; obj; obj = obj->next ) if( can_see_obj( ch, obj ) && nifty_is_name_prefix( arg, obj->name ) ) if( ( count += obj->count ) >= number ) return obj; return NULL; } /* * How mental state could affect finding an object -Thoric * Used by get/drop/put/quaff/recite/etc * Increasingly freaky based on mental state and drunkeness */ bool ms_find_obj( CHAR_DATA *ch ) { const char *t; int ms = ch->mental_state; int drunk = is_npc( ch ) ? 0 : ch->pcdata->condition[COND_DRUNK]; /* * we're going to be nice and let nothing weird happen unless * you're a tad messed up */ drunk = UMAX( 1, drunk ); if( abs( ms ) + ( drunk / 3 ) < 30 ) return false; if( ( number_percent( ) + ( ms < 0 ? 15 : 5 ) ) > abs( ms ) / 2 + drunk / 4 ) return false; if( ms > 15 ) /* range 1 to 20 -- feel free to add more */ { switch( number_range( UMAX( 1, ( ms / 5 - 15 ) ), ( ms + 4 ) / 5 ) ) { default: case 1: t = "As you reach for it, you forgot what it was...\r\n"; break; case 2: t = "As you reach for it, something inside stops you...\r\n"; break; case 3: t = "As you reach for it, it seems to move out of the way...\r\n"; break; case 4: t = "You grab frantically for it, but can't seem to get a hold of it...\r\n"; break; case 5: t = "It disappears as soon as you touch it!\r\n"; break; case 6: t = "You would if it would stay still!\r\n"; break; case 7: t = "Whoa! It's covered in blood! Ack! Ick!\r\n"; break; case 8: t = "Wow... trails!\r\n"; break; case 9: t = "You reach for it, then notice the back of your hand is growing something!\r\n"; break; case 10: t = "As you grasp it, it shatters into tiny shards which bite into your flesh!\r\n"; break; case 11: t = "What about that huge dragon flying over your head?!?!?\r\n"; break; case 12: t = "You stratch yourself instead...\r\n"; break; case 13: t = "You hold the universe in the palm of your hand!\r\n"; break; case 14: t = "You're too scared.\r\n"; break; case 15: t = "Your mother smacks your hand... 'NO!'\r\n"; break; case 16: t = "Your hand grasps the worst pile of revoltingness that you could ever imagine!\r\n"; break; case 17: t = "You stop reaching for it as it screams out at you in pain!\r\n"; break; case 18: t = "What about the millions of burrow-maggots feasting on your arm?!?!\r\n"; break; case 19: t = "That doesn't matter anymore... you've found the true answer to everything!\r\n"; break; case 20: t = "A supreme entity has no need for that.\r\n"; break; } } else { int sub = URANGE( 1, abs( ms ) / 2 + drunk, 60 ); switch( number_range( 1, sub / 10 ) ) { default: case 1: t = "In just a second...\r\n"; break; case 2: t = "You can't find that...\r\n"; break; case 3: t = "It's just beyond your grasp...\r\n"; break; case 4: t = "...but it's under a pile of other stuff...\r\n"; break; case 5: t = "You go to reach for it, but pick your nose instead.\r\n"; break; case 6: t = "Which one?!? I see two... no three...\r\n"; break; } } send_to_char( t, ch ); return true; } /* * Generic get obj function that supports optional containers. -Thoric * currently only used for "eat" and "quaff". */ OBJ_DATA *find_obj( CHAR_DATA *ch, char *argument, bool carryonly ) { char arg1[MIL]; char arg2[MIL]; OBJ_DATA *obj = NULL; argument = one_argument( argument, arg1 ); argument = one_argument( argument, arg2 ); if( !str_cmp( arg2, "from" ) && argument[0] != '\0' ) argument = one_argument( argument, arg2 ); if( arg2[0] == '\0' ) { if( carryonly && !( obj = get_obj_carry( ch, arg1 ) ) ) { send_to_char( "You do not have that item.\r\n", ch ); return NULL; } else if( !carryonly && !( obj = get_obj_here( ch, arg1 ) ) ) { act( AT_PLAIN, "I see no $T here.", ch, NULL, arg1, TO_CHAR ); return NULL; } return obj; } else { OBJ_DATA *container = NULL; if( carryonly && !( container = get_obj_carry( ch, arg2 ) ) && !( container = get_obj_wear( ch, arg2 ) ) ) { send_to_char( "You do not have that item.\r\n", ch ); return NULL; } if( !carryonly && !( container = get_obj_here( ch, arg2 ) ) ) { act( AT_PLAIN, "I see no $T here.", ch, NULL, arg2, TO_CHAR ); return NULL; } if( !is_obj_stat( container, ITEM_COVERING ) && IS_SET( container->value[1], CONT_CLOSED ) ) { act( AT_PLAIN, "The $d is closed.", ch, NULL, container->name, TO_CHAR ); return NULL; } obj = get_obj_list( ch, arg1, container->first_content ); if( !obj ) act( AT_PLAIN, is_obj_stat( container, ITEM_COVERING ) ? "I see nothing like that beneath $p." : "I see nothing like that in $p.", ch, container, NULL, TO_CHAR ); return obj; } } int get_obj_number( OBJ_DATA *obj ) { return obj->count; } /* Return true if an object is, or nested inside a magic container */ bool in_magic_container( OBJ_DATA *obj ) { if( obj->item_type == ITEM_CONTAINER && is_obj_stat( obj, ITEM_MAGIC ) ) return true; if( obj->in_obj ) return in_magic_container( obj->in_obj ); return false; } /* Return weight of an object, including weight of contents (unless magic). */ int get_obj_weight( OBJ_DATA *obj ) { int weight; weight = obj->count * obj->weight; /* magic containers */ if( obj->item_type != ITEM_CONTAINER || !is_obj_stat( obj, ITEM_MAGIC ) ) for( obj = obj->first_content; obj; obj = obj->next_content ) weight += get_obj_weight( obj ); return weight; } const char *dis_class_name( int Class ) { if( Class < 0 || Class >= MAX_PC_CLASS || !class_table[Class] || !class_table[Class]->name ) return "Unknown"; return class_table[Class]->name; } /* Display the main class name for the character */ const char *dis_main_class_name( CHAR_DATA *ch ) { MCLASS_DATA *mclass; int uclass = -1, ulevel = -1; double uexp = 0; if( !ch || is_npc( ch ) ) return (char *)"Unknown"; for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next ) { if( mclass->wclass < 0 || mclass->wclass >= MAX_PC_CLASS || !class_table[mclass->wclass] || !class_table[mclass->wclass]->name ) continue; if( uclass == -1 || ulevel < mclass->level || ( ulevel == mclass->level && uexp < mclass->exp ) ) { uclass = mclass->wclass; ulevel = mclass->level; uexp = mclass->exp; } } if( uclass < 0 || uclass >= MAX_PC_CLASS || !class_table[uclass] || !class_table[uclass]->name ) return "Unknown"; return class_table[uclass]->name; } const char *dis_race_name( int race ) { if( race < 0 || race >= MAX_PC_RACE || !race_table[race] || !race_table[race]->name ) return (char *)"Unknown"; return race_table[race]->name; } /* Return real weight of an object, including weight of contents. */ int get_real_obj_weight( OBJ_DATA *obj ) { int weight; weight = obj->count * obj->weight; for( obj = obj->first_content; obj; obj = obj->next_content ) weight += get_real_obj_weight( obj ); return weight; } /* True if room is dark. */ bool room_is_dark( ROOM_INDEX_DATA *pRoomIndex ) { if( !pRoomIndex ) { bug( "%s:: NULL pRoomIndex", __FUNCTION__ ); return true; } if( pRoomIndex->light > 0 ) return false; if( xIS_SET( pRoomIndex->room_flags, ROOM_DARK ) ) return true; if( pRoomIndex->sector_type == SECT_INSIDE || pRoomIndex->sector_type == SECT_CITY ) return false; if( time_info.sunlight == SUN_SET || time_info.sunlight == SUN_DARK ) return true; return false; } /* * If room is "do not disturb" return the pointer to the imm with dnd flag * NULL if room is not "do not disturb". */ CHAR_DATA *room_is_dnd( CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex ) { CHAR_DATA *rch; if( !pRoomIndex ) { bug( "%s: NULL pRoomIndex", __FUNCTION__ ); return NULL; } if( !xIS_SET( pRoomIndex->room_flags, ROOM_DND ) ) return NULL; for( rch = pRoomIndex->first_person; rch; rch = rch->next_in_room ) { if( !is_npc( rch ) && rch->pcdata && is_immortal( rch ) && xIS_SET( rch->pcdata->flags, PCFLAG_DND ) && get_trust( ch ) < get_trust( rch ) && can_see( ch, rch ) ) return rch; } return NULL; } /* True if room is private. */ bool room_is_private( ROOM_INDEX_DATA *pRoomIndex ) { CHAR_DATA *rch; int count; if( !pRoomIndex ) { bug( "%s: NULL pRoomIndex", __FUNCTION__ ); return false; } count = 0; for( rch = pRoomIndex->first_person; rch; rch = rch->next_in_room ) count++; if( xIS_SET( pRoomIndex->room_flags, ROOM_PRIVATE ) && count >= 2 ) return true; if( xIS_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 ) { if( !victim ) return false; if( !ch ) { if( IS_AFFECTED( victim, AFF_INVISIBLE ) || IS_AFFECTED( victim, AFF_HIDE ) || xIS_SET( victim->act, PLR_WIZINVIS ) ) return false; else return true; } if( ch == victim ) return true; /* Wizinvis goes by get_trust */ if( !is_npc( victim ) && xIS_SET( victim->act, PLR_WIZINVIS ) && get_trust( ch ) < victim->pcdata->wizinvis ) return false; /* Mobinvis goes by level but shows to all immortals */ if( is_npc( victim ) && xIS_SET( victim->act, ACT_MOBINVIS ) && ch->level < victim->mobinvis && !is_immortal( ch ) ) return false; /* Deadlies link-dead over 2 ticks aren't seen by mortals -- Blodkai */ if( !is_immortal( ch ) && !is_npc( ch ) && !is_npc( victim ) && is_pkill( victim ) && victim->timer > 1 && !victim->desc ) return false; if( !is_npc( ch ) && xIS_SET( ch->act, PLR_HOLYLIGHT ) ) return true; /* The miracle cure for blindness? -- Altrag */ if( !IS_AFFECTED( ch, AFF_TRUESIGHT ) ) { if( IS_AFFECTED( ch, AFF_BLIND ) ) return false; if( room_is_dark( ch->in_room ) && !IS_AFFECTED( ch, AFF_INFRARED ) ) return false; if( IS_AFFECTED( victim, AFF_INVISIBLE ) && !IS_AFFECTED( ch, AFF_DETECT_INVIS ) ) return false; if( IS_AFFECTED( victim, AFF_HIDE ) && !IS_AFFECTED( ch, AFF_DETECT_HIDDEN ) && !victim->fighting && ( is_npc( ch ) ? !is_npc( victim ) : is_npc( victim ) ) ) return false; } /* * Redone by Narn to let newbie council members see pre-auths. */ if( not_authed( victim ) ) { if( not_authed( ch ) || is_immortal( ch ) || is_npc( ch ) ) return true; if( ch->pcdata->council && !str_cmp( ch->pcdata->council->name, "Newbie Council" ) ) return true; return false; } return true; } /* True if char can see obj. */ bool can_see_obj( CHAR_DATA *ch, OBJ_DATA *obj ) { if( !is_npc( ch ) && xIS_SET( ch->act, PLR_HOLYLIGHT ) ) return true; if( is_npc( ch ) && ch->pIndexData->vnum == MOB_VNUM_SUPERMOB ) return true; if( is_obj_stat( obj, ITEM_BURIED ) ) return false; if( is_obj_stat( obj, ITEM_HIDDEN ) ) return false; if( IS_AFFECTED( ch, AFF_TRUESIGHT ) ) return true; if( IS_AFFECTED( ch, AFF_BLIND ) ) return false; /* can see lights in the dark */ if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 ) return true; if( room_is_dark( ch->in_room ) ) { if( is_obj_stat( obj, ITEM_GLOW ) ) return true; if( !IS_AFFECTED( ch, AFF_INFRARED ) ) return false; } if( is_obj_stat( obj, ITEM_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_obj_stat( obj, ITEM_NODROP ) ) return true; if( !is_npc( ch ) && get_trust( ch ) >= PERM_IMM ) return true; if( is_npc( ch ) && ch->pIndexData->vnum == MOB_VNUM_SUPERMOB ) return true; return false; } /* Return ascii name of an affect location. */ char *affect_loc_name( int location ) { if( location >= 0 && location < APPLY_MAX ) return capitalize( a_types[location] ); bug( "%s: unknown location %d.", __FUNCTION__, location ); return (char *)"(Unknown)"; } /* Return ascii name of pulltype exit setting. */ const char *pull_type_name( int pulltype ) { if( pulltype >= PT_FIRE ) return ex_pfire[pulltype - PT_FIRE]; if( pulltype >= PT_AIR ) return ex_pair[pulltype - PT_AIR]; if( pulltype >= PT_EARTH ) return ex_pearth[pulltype - PT_EARTH]; if( pulltype >= PT_WATER ) return ex_pwater[pulltype - PT_WATER]; if( pulltype < 0 ) return "ERROR"; return ex_pmisc[pulltype]; } /* Set off a trap (obj) upon character (ch) -Thoric */ ch_ret spring_trap( CHAR_DATA *ch, OBJ_DATA *obj ) { int dam, typ, lev; const char *txt; char buf[MSL]; ch_ret retcode; typ = obj->value[1]; lev = obj->value[2]; retcode = rNONE; switch( typ ) { default: txt = "hit by a trap"; break; case TRAP_TYPE_POISON_GAS: txt = "surrounded by a green cloud of gas"; break; case TRAP_TYPE_POISON_DART: txt = "hit by a dart"; break; case TRAP_TYPE_POISON_NEEDLE: txt = "pricked by a needle"; break; case TRAP_TYPE_POISON_DAGGER: txt = "stabbed by a dagger"; break; case TRAP_TYPE_POISON_ARROW: txt = "struck with an arrow"; break; case TRAP_TYPE_BLINDNESS_GAS: txt = "surrounded by a red cloud of gas"; break; case TRAP_TYPE_SLEEPING_GAS: txt = "surrounded by a yellow cloud of gas"; break; case TRAP_TYPE_FLAME: txt = "struck by a burst of flame"; break; case TRAP_TYPE_EXPLOSION: txt = "hit by an explosion"; break; case TRAP_TYPE_ACID_SPRAY: txt = "covered by a spray of acid"; break; case TRAP_TYPE_ELECTRIC_SHOCK: txt = "suddenly shocked"; break; case TRAP_TYPE_BLADE: txt = "sliced by a razor sharp blade"; break; } dam = number_range( obj->value[2], obj->value[2] * 2 ); snprintf( buf, sizeof( buf ), "You're %s!", txt ); act( AT_HITME, buf, ch, NULL, NULL, TO_CHAR ); snprintf( buf, sizeof( buf ), "$n is %s.", txt ); act( AT_ACTION, buf, ch, NULL, NULL, TO_ROOM ); --obj->value[0]; if( obj->value[0] <= 0 ) extract_obj( obj ); switch( typ ) { default: case TRAP_TYPE_POISON_DART: case TRAP_TYPE_POISON_NEEDLE: case TRAP_TYPE_POISON_DAGGER: case TRAP_TYPE_POISON_ARROW: retcode = obj_cast_spell( gsn_poison, lev, ch, ch, NULL ); if( retcode == rNONE ) retcode = damage( ch, ch, dam, TYPE_UNDEFINED, true ); break; case TRAP_TYPE_POISON_GAS: retcode = obj_cast_spell( gsn_poison, lev, ch, ch, NULL ); break; case TRAP_TYPE_BLINDNESS_GAS: retcode = obj_cast_spell( gsn_blindness, lev, ch, ch, NULL ); break; case TRAP_TYPE_SLEEPING_GAS: retcode = obj_cast_spell( skill_lookup( "sleep" ), lev, ch, ch, NULL ); break; case TRAP_TYPE_ACID_SPRAY: retcode = obj_cast_spell( skill_lookup( "acidshot" ), lev, ch, ch, NULL ); break; case TRAP_TYPE_FLAME: case TRAP_TYPE_EXPLOSION: retcode = obj_cast_spell( skill_lookup( "fireball" ), lev, ch, ch, NULL ); break; case TRAP_TYPE_ELECTRIC_SHOCK: case TRAP_TYPE_BLADE: retcode = damage( ch, ch, dam, TYPE_UNDEFINED, true ); break; } return retcode; } /* Check an object for a trap - Thoric */ ch_ret check_for_trap( CHAR_DATA *ch, OBJ_DATA *obj, int flag ) { OBJ_DATA *check; ch_ret retcode; if( !obj->first_content ) return rNONE; retcode = rNONE; for( check = obj->first_content; check; check = check->next_content ) if( check->item_type == ITEM_TRAP && IS_SET( check->value[3], flag ) ) { retcode = spring_trap( ch, check ); if( retcode != rNONE ) return retcode; } return retcode; } ch_ret check_room_for_traps( CHAR_DATA *ch, int flag ) { OBJ_DATA *check; ch_ret retcode; retcode = rNONE; if( !ch ) return rERROR; if( !ch->in_room || !ch->in_room->first_content ) return rNONE; for( check = ch->in_room->first_content; check; check = check->next_content ) { if( check->item_type == ITEM_TRAP && IS_SET( check->value[3], flag ) ) { retcode = spring_trap( ch, check ); if( retcode != rNONE ) return retcode; } } return retcode; } /* return true if an object contains a trap - Thoric */ bool is_trapped( OBJ_DATA *obj ) { OBJ_DATA *check; if( !obj->first_content ) return false; for( check = obj->first_content; check; check = check->next_content ) if( check->item_type == ITEM_TRAP ) return true; return false; } /* If an object contains a trap, return the pointer to the trap - Thoric */ OBJ_DATA *get_trap( OBJ_DATA *obj ) { OBJ_DATA *check; if( !obj->first_content ) return NULL; for( check = obj->first_content; check; check = check->next_content ) if( check->item_type == ITEM_TRAP ) return check; return NULL; } /* * Return a pointer to the first object of a certain type found that * a player is carrying/wearing */ OBJ_DATA *get_objtype( CHAR_DATA *ch, short type ) { OBJ_DATA *obj; for( obj = ch->first_carrying; obj; obj = obj->next_content ) if( obj->item_type == type ) return obj; return NULL; } /* Remove an exit from a room - Thoric */ void extract_exit( ROOM_INDEX_DATA *room, EXIT_DATA *pexit ) { UNLINK( pexit, room->first_exit, room->last_exit, next, prev ); if( pexit->rexit ) pexit->rexit->rexit = NULL; STRFREE( pexit->keyword ); STRFREE( pexit->description ); DISPOSE( pexit ); } void clean_room( ROOM_INDEX_DATA *room ) { EXTRA_DESCR_DATA *ed, *ed_next; EXIT_DATA *pexit, *pexit_next; MPROG_DATA *mprog, *mprog_next; STRFREE( room->description ); STRFREE( room->name ); for( mprog = room->mudprogs; mprog; mprog = mprog_next ) { mprog_next = mprog->next; STRFREE( mprog->arglist ); STRFREE( mprog->comlist ); DISPOSE( mprog ); } for( ed = room->first_extradesc; ed; ed = ed_next ) { ed_next = ed->next; STRFREE( ed->description ); STRFREE( ed->keyword ); DISPOSE( ed ); top_ed--; } room->first_extradesc = NULL; room->last_extradesc = NULL; for( pexit = room->first_exit; pexit; pexit = pexit_next ) { pexit_next = pexit->next; extract_exit( room, pexit ); top_exit--; } room->first_exit = NULL; room->last_exit = NULL; xCLEAR_BITS( room->room_flags ); room->sector_type = 0; room->light = 0; } void clean_obj( OBJ_INDEX_DATA *obj ) { AFFECT_DATA *paf, *paf_next; EXTRA_DESCR_DATA *ed, *ed_next; MPROG_DATA *mprog, *mprog_next; STRFREE( obj->name ); STRFREE( obj->short_descr ); STRFREE( obj->description ); STRFREE( obj->action_desc ); obj->item_type = 0; xCLEAR_BITS( obj->extra_flags ); xCLEAR_BITS( obj->wear_flags ); obj->count = 0; obj->weight = 0; obj->cost = 0; obj->value[0] = 0; obj->value[1] = 0; obj->value[2] = 0; obj->value[3] = 0; obj->value[4] = 0; obj->value[5] = 0; for( paf = obj->first_affect; paf; paf = paf_next ) { paf_next = paf->next; DISPOSE( paf ); top_affect--; } obj->first_affect = NULL; obj->last_affect = NULL; for( ed = obj->first_extradesc; ed; ed = ed_next ) { ed_next = ed->next; STRFREE( ed->description ); STRFREE( ed->keyword ); DISPOSE( ed ); top_ed--; } obj->first_extradesc = NULL; obj->last_extradesc = NULL; for( mprog = obj->mudprogs; mprog; mprog = mprog_next ) { mprog_next = mprog->next; STRFREE( mprog->arglist ); STRFREE( mprog->comlist ); DISPOSE( mprog ); } } /* clean out a mobile (index) (leave list pointers intact ) - Thoric */ void clean_mob( MOB_INDEX_DATA *mob ) { MPROG_DATA *mprog, *mprog_next; STRFREE( mob->name ); STRFREE( mob->short_descr ); STRFREE( mob->long_descr ); STRFREE( mob->description ); mob->spec_fun = NULL; mob->pShop = NULL; mob->rShop = NULL; xCLEAR_BITS( mob->progtypes ); for( mprog = mob->mudprogs; mprog; mprog = mprog_next ) { mprog_next = mprog->next; STRFREE( mprog->arglist ); STRFREE( mprog->comlist ); DISPOSE( mprog ); } mob->count = 0; mob->killed = 0; mob->sex = 0; mob->level = 0; xCLEAR_BITS( mob->act ); xCLEAR_BITS( mob->affected_by ); mob->alignment = 0; mob->ac = 0; mob->minhit = 0; mob->maxhit = 0; mob->gold = 0; mob->mgold = 0; mob->position = 0; mob->defposition = 0; mob->height = 0; mob->weight = 0; xCLEAR_BITS( mob->attacks ); xCLEAR_BITS( mob->defenses ); } extern int top_reset; /* Remove all resets from a room -Thoric */ void clean_resets( ROOM_INDEX_DATA *room ) { RESET_DATA *pReset, *pReset_next; for( pReset = room->first_reset; pReset; pReset = pReset_next ) { pReset_next = pReset->next; delete_reset( pReset ); --top_reset; } room->first_reset = NULL; room->last_reset = NULL; } /* Set up players base stats */ void set_base_stats( CHAR_DATA *ch ) { int stat; if( !ch ) return; for( stat = 0; stat < STAT_MAX; stat++ ) { if( race_table[ch->race] ) ch->perm_stats[stat] = race_table[ch->race]->base_stats[stat]; else ch->perm_stats[stat] = 13; } if( race_table[ch->race] ) { ch->max_hit = race_table[ch->race]->hit; ch->max_mana = race_table[ch->race]->mana; ch->max_move = race_table[ch->race]->move; ch->height = number_range( race_table[ch->race]->minheight, race_table[ch->race]->maxheight ); ch->weight = number_range( race_table[ch->race]->minweight, race_table[ch->race]->maxweight ); ch->alignment = URANGE( race_table[ch->race]->minalign, race_table[ch->race]->alignment, race_table[ch->race]->maxalign ); ch->armor = race_table[ch->race]->ac_plus; } else { ch->max_hit = 100; ch->max_mana = 100; ch->max_move = 100; ch->height = number_range( 50, 100 ); ch->weight = number_range( 150, 300 ); ch->alignment = 0; ch->armor = 0; } ch->hit = UMAX( 1, ch->max_hit ); ch->mana = UMAX( 1, ch->max_mana ); ch->move = UMAX( 1, ch->max_move ); } bool is_obj_stat( OBJ_DATA *obj, int stat ) { if( xIS_SET( obj->extra_flags, stat ) ) return true; return false; } bool can_wear( OBJ_DATA *obj, int part ) { if( xIS_SET( obj->wear_flags, part ) ) return true; return false; } bool is_retired( CHAR_DATA *ch ) { if( ch->pcdata && xIS_SET( ch->pcdata->flags, PCFLAG_RETIRED ) ) return true; return false; } bool is_guest( CHAR_DATA *ch ) { if( ch->pcdata && xIS_SET( ch->pcdata->flags, PCFLAG_GUEST ) ) return true; return false; } bool is_idle( CHAR_DATA *ch ) { if( ch->pcdata && xIS_SET( ch->pcdata->flags, PCFLAG_IDLE ) ) return true; return false; } bool is_pkill( CHAR_DATA *ch ) { if( ch->pcdata && xIS_SET( ch->pcdata->flags, PCFLAG_DEADLY ) ) return true; return false; } bool can_pkill( CHAR_DATA *ch ) { if( is_pkill( ch ) && ch->level >= 5 && get_age( ch ) >= 18 ) return true; return false; } bool is_awake( CHAR_DATA *ch ) { if( ch->position > POS_SLEEPING ) return true; return false; } bool is_outside( CHAR_DATA *ch ) { if( !xIS_SET( ch->in_room->room_flags, ROOM_INDOORS ) && !xIS_SET( ch->in_room->room_flags, ROOM_TUNNEL ) ) return true; return false; } bool no_weather_sect( ROOM_INDEX_DATA *room ) { if( room->sector_type == SECT_INSIDE || room->sector_type == SECT_UNDERWATER || room->sector_type == SECT_OCEANFLOOR || room->sector_type == SECT_UNDERGROUND ) return true; return false; } bool is_drunk( CHAR_DATA *ch, int drunk ) { if( number_percent( ) < ( ( ch->pcdata->condition[COND_DRUNK] * 2 ) / drunk ) ) return true; return false; } bool is_devoted( CHAR_DATA *ch ) { if( !is_npc( ch ) && ch->pcdata->deity ) return true; return false; } bool is_vampire( CHAR_DATA *ch ) { if( !is_npc( ch ) && race_table[ch->race] && race_table[ch->race]->uses == 2 ) return true; return false; } bool is_good( CHAR_DATA *ch ) { if( ch->alignment >= 350 ) return true; return false; } bool is_evil( CHAR_DATA *ch ) { if( ch->alignment <= -350 ) return true; return false; } bool is_neutral( CHAR_DATA *ch ) { if( !is_good( ch ) && !is_evil( ch ) ) return true; return false; } bool can_cast( CHAR_DATA *ch ) { if( race_table[ch->race] && ( race_table[ch->race]->uses == 1 || race_table[ch->race]->uses == 2 ) ) return true; return false; } bool in_clan( CHAR_DATA *ch ) { if( !is_npc( ch ) && ch->pcdata->clan ) return true; return false; } bool is_clanned( CHAR_DATA *ch ) { if( in_clan( ch ) && ch->pcdata->clan->clan_type == CLAN_PLAIN ) return true; return false; } bool in_nation( CHAR_DATA *ch ) { if( !is_npc( ch ) && ch->pcdata->nation ) return true; return false; } bool is_nationed( CHAR_DATA *ch ) { if( in_nation( ch ) && ch->pcdata->nation->clan_type == CLAN_NATION ) return true; return false; } bool in_council( CHAR_DATA *ch ) { if( !is_npc( ch ) && ch->pcdata->council ) return true; return false; } bool is_counciled( CHAR_DATA *ch ) { if( in_council( ch ) ) return true; return false; } bool not_authed( CHAR_DATA *ch ) { if( !is_npc( ch ) && ch->pcdata->auth_state <= 3 && xIS_SET( ch->pcdata->flags, PCFLAG_UNAUTHED ) ) return true; return false; } bool is_waiting_for_auth( CHAR_DATA *ch ) { if( !is_npc( ch ) && ch->desc && ch->pcdata->auth_state == 1 && xIS_SET( ch->pcdata->flags, PCFLAG_UNAUTHED ) ) return true; return false; } bool is_pacifist( CHAR_DATA *ch ) { if( is_npc( ch ) && xIS_SET( ch->act, ACT_PACIFIST ) ) return true; return false; } bool is_valid_herb( int sn ) { if( sn < 0 || sn >= MAX_HERB ) return false; if( !herb_table[sn] || !herb_table[sn]->name ) return false; return true; } bool is_valid_pers( int sn ) { if( sn < 0 || sn >= MAX_PERS ) return false; if( !pers_table[sn] || !pers_table[sn]->name ) return false; return true; } bool is_valid_sn( int sn ) { if( sn < 0 || sn >= MAX_SKILL ) return false; if( !skill_table[sn] || !skill_table[sn]->name ) return false; return true; } void wait_state( CHAR_DATA *ch, int npulse ) { if( !is_npc( ch ) && is_immortal( ch ) ) ch->wait = 1; else ch->wait = UMAX( ch->wait, npulse ); return; } bool can_go( ROOM_INDEX_DATA *room, int door ) { EXIT_DATA *exit; if( !room || !( exit = get_exit( room, door ) ) ) return false; if( !exit->to_room || xIS_SET( exit->exit_info, EX_CLOSED ) ) return false; return true; } bool is_floating( CHAR_DATA *ch ) { if( !IS_AFFECTED( ch, AFF_FLYING ) && !IS_AFFECTED( ch, AFF_FLOATING ) ) return false; return true; } /* Show an affect verbosely to a character -Thoric */ void showaffect( CHAR_DATA *ch, AFFECT_DATA *paf, bool extdisplay ) { char buf[MSL]; if( !paf ) { bug( "%s: NULL paf", __FUNCTION__ ); return; } if( paf->location != APPLY_NONE ) { switch( paf->location ) { default: snprintf( buf, sizeof( buf ), "Affects %s by %d.", affect_loc_name( paf->location ), paf->modifier ); break; case APPLY_STAT: snprintf( buf, sizeof( buf ), "Affects %s by %d.", ext_flag_string( &paf->bitvector, stattypes ), paf->modifier ); break; case APPLY_EXT_AFFECT: snprintf( buf, sizeof( buf ), "Affects %s by %s.", affect_loc_name( paf->location ), a_flags[paf->modifier] ); break; case APPLY_WEAPONSPELL: case APPLY_WEARSPELL: case APPLY_REMOVESPELL: snprintf( buf, sizeof( buf ), "Casts spell '%s'.", is_valid_sn( paf->modifier ) ? skill_table[paf->modifier]->name : "unknown" ); break; case APPLY_RESISTANT: case APPLY_IMMUNE: case APPLY_ABSORB: case APPLY_SUSCEPTIBLE: snprintf( buf, sizeof( buf ), "Affects %s by %s.", affect_loc_name( paf->location ), ris_flags[paf->modifier] ); break; } if( extdisplay ) mudstrlcat( buf, " (Extra)\r\n", sizeof( buf ) ); else mudstrlcat( buf, "\r\n", sizeof( buf ) ); send_to_char( buf, ch ); } } /* Set the current global character to ch - Thoric */ void set_cur_char( CHAR_DATA *ch ) { cur_char = ch; cur_char_died = false; global_retcode = rNONE; } /* Check to see if ch died recently - Thoric */ bool char_died( CHAR_DATA *ch ) { EXTRACT_CHAR_DATA *ccd; if( ch == cur_char && cur_char_died ) return true; for( ccd = extracted_char_queue; ccd; ccd = ccd->next ) if( ccd->ch == ch ) return true; return false; } /* Add ch to the queue of recently extracted characters - Thoric */ void queue_extracted_char( CHAR_DATA *ch, bool extract ) { EXTRACT_CHAR_DATA *ccd; if( !ch ) { bug( "%s: ch = NULL", __FUNCTION__ ); return; } CREATE( ccd, EXTRACT_CHAR_DATA, 1 ); ccd->ch = ch; ccd->room = ch->in_room; ccd->extract = extract; if( ch == cur_char ) ccd->retcode = global_retcode; else ccd->retcode = rCHAR_DIED; ccd->next = extracted_char_queue; extracted_char_queue = ccd; cur_qchars++; } /* clean out the extracted character queue */ void clean_char_queue( void ) { EXTRACT_CHAR_DATA *ccd; for( ccd = extracted_char_queue; ccd; ccd = extracted_char_queue ) { extracted_char_queue = ccd->next; if( ccd->extract ) free_char( ccd->ch ); DISPOSE( ccd ); --cur_qchars; } } /* * Add a timer to ch -Thoric * Support for "call back" time delayed commands */ void add_timer( CHAR_DATA *ch, short type, int count, DO_FUN *fun, int value ) { TIMER *timer; for( timer = ch->first_timer; timer; timer = timer->next ) { if( timer->type == type ) { timer->count = count; timer->do_fun = fun; timer->value = value; break; } } if( !timer ) { CREATE( timer, TIMER, 1 ); timer->count = count; timer->type = type; timer->do_fun = fun; timer->value = value; LINK( timer, ch->first_timer, ch->last_timer, next, prev ); } return; } TIMER *get_timerptr( CHAR_DATA *ch, short type ) { TIMER *timer; for( timer = ch->first_timer; timer; timer = timer->next ) if( timer->type == type ) return timer; return NULL; } short get_timer( CHAR_DATA *ch, short type ) { TIMER *timer; if( ( timer = get_timerptr( ch, type ) ) ) return timer->count; else return 0; } void extract_timer( CHAR_DATA *ch, TIMER *timer ) { if( !ch ) { bug( "%s: NULL ch", __FUNCTION__ ); return; } if( !timer ) { bug( "%s: NULL timer", __FUNCTION__ ); return; } UNLINK( timer, ch->first_timer, ch->last_timer, next, prev ); DISPOSE( timer ); return; } void remove_timer( CHAR_DATA *ch, short type ) { TIMER *timer; for( timer = ch->first_timer; timer; timer = timer->next ) if( timer->type == type ) break; if( timer ) extract_timer( ch, timer ); } bool in_soft_range( CHAR_DATA *ch, AREA_DATA *tarea ) { if( is_immortal( ch ) ) return true; else if( is_npc( ch ) ) return true; else if( ch->level >= tarea->low_soft_range || ch->level <= tarea->hi_soft_range ) return true; else return false; } bool can_astral( CHAR_DATA *ch, CHAR_DATA *victim ) { if( victim == ch || !victim->in_room || xIS_SET( victim->in_room->room_flags, ROOM_PRIVATE ) || xIS_SET( victim->in_room->room_flags, ROOM_SOLITARY ) || xIS_SET( victim->in_room->room_flags, ROOM_NO_ASTRAL ) || xIS_SET( victim->in_room->room_flags, ROOM_DEATH ) || xIS_SET( ch->in_room->room_flags, ROOM_NO_RECALL ) || victim->level >= ch->level + 15 || ( can_pkill( victim ) && !is_npc( ch ) && !can_pkill( ch ) ) || ( is_npc( victim ) && xIS_SET( victim->act, ACT_PROTOTYPE ) ) || ( is_npc( victim ) && saves_spell_staff( ch->level, victim ) ) || ( xIS_SET( victim->in_room->area->flags, AFLAG_NOPKILL ) && is_pkill( ch ) ) ) return false; else return true; } bool in_hard_range( CHAR_DATA *ch, AREA_DATA *tarea ) { if( is_immortal( ch ) ) return true; else if( is_npc( ch ) ) return true; else if( ch->level >= tarea->low_hard_range && ch->level <= tarea->hi_hard_range ) return true; else return false; } /* Scryn, standard luck check 2/2/96 */ bool chance( CHAR_DATA *ch, short percent ) { short deity_factor, ms; if( !ch ) { bug( "%s: null ch!", __FUNCTION__ ); return false; } /* * Mental state bonus/penalty: Your mental state is a ranged value with * zero (0) being at a perfect mental state (bonus of 10). * negative values would reflect how sedated one is, and * positive values would reflect how stimulated one is. * In most circumstances you'd do best at a perfectly balanced state. */ if( is_devoted( ch ) ) deity_factor = ch->pcdata->favor / -500; else deity_factor = 0; ms = 10 - abs( ch->mental_state ); if( ( ( number_percent( ) * 3 ) - get_curr_lck( ch ) - ms ) + deity_factor <= percent ) return true; else return false; } /* Make a simple clone of an object (no extras...yet) -Thoric */ OBJ_DATA *clone_object( OBJ_DATA *obj ) { OBJ_DATA *clone; CREATE( clone, OBJ_DATA, 1 ); clone->pIndexData = obj->pIndexData; clone->name = QUICKLINK( obj->name ); clone->short_descr = QUICKLINK( obj->short_descr ); clone->description = QUICKLINK( obj->description ); clone->action_desc = QUICKLINK( obj->action_desc ); clone->owner = QUICKLINK( obj->owner ); clone->item_type = obj->item_type; clone->extra_flags = obj->extra_flags; clone->wear_flags = obj->wear_flags; clone->wear_loc = obj->wear_loc; clone->t_wear_loc = obj->t_wear_loc; clone->weight = obj->weight; clone->cost = obj->cost; clone->level = obj->level; clone->timer = obj->timer; clone->value[0] = obj->value[0]; clone->value[1] = obj->value[1]; clone->value[2] = obj->value[2]; clone->value[3] = obj->value[3]; clone->value[4] = obj->value[4]; clone->value[5] = obj->value[5]; clone->count = 1; ++obj->pIndexData->count; ++numobjsloaded; ++physicalobjects; LINK( clone, first_object, last_object, next, prev ); if( clone->pIndexData->vnum == OBJ_VNUM_CORPSE_PC ) { LINK( clone, first_corpse, last_corpse, next_corpse, prev_corpse ); ++num_corpses; } LINK( clone, clone->pIndexData->first_copy, clone->pIndexData->last_copy, next_index, prev_index ); return clone; } /* * If possible group obj2 into obj1 -Thoric * This code, along with clone_object, obj->count, and special support * for it implemented throughout handler.c and save.c should show improved * performance on MUDs with players that hoard tons of potions and scrolls * as this will allow them to be grouped together both in memory, and in * the player files. */ OBJ_DATA *group_object( OBJ_DATA *obj1, OBJ_DATA *obj2 ) { if( !obj1 || !obj2 ) return NULL; if( obj1 == obj2 ) return obj1; if( obj1->pIndexData == obj2->pIndexData && !str_cmp( obj1->name, obj2->name ) && !str_cmp( obj1->short_descr, obj2->short_descr ) && !str_cmp( obj1->description, obj2->description ) && !str_cmp( obj1->action_desc, obj2->action_desc ) && !str_cmp( obj1->owner, obj2->owner ) && obj1->bsplatter == obj2->bsplatter && obj1->bstain == obj2->bstain && obj1->item_type == obj2->item_type && xSAME_BITS( obj1->extra_flags, obj2->extra_flags ) && xSAME_BITS( obj1->wear_flags, obj2->wear_flags ) && obj1->wear_loc == obj2->wear_loc && obj1->t_wear_loc == obj2->t_wear_loc && obj1->weight == obj2->weight && obj1->cost == obj2->cost && obj1->level == obj2->level && obj1->timer == obj2->timer && obj1->value[0] == obj2->value[0] && obj1->value[1] == obj2->value[1] && obj1->value[2] == obj2->value[2] && obj1->value[3] == obj2->value[3] && obj1->value[4] == obj2->value[4] && obj1->value[5] == obj2->value[5] && !obj1->first_extradesc && !obj2->first_extradesc && !obj1->first_affect && !obj2->first_affect && !obj1->first_content && !obj2->first_content && obj1->count + obj2->count > 0 ) /* prevent count overflow */ { obj1->count += obj2->count; obj1->pIndexData->count += obj2->count; /* to be decremented in */ numobjsloaded += obj2->count; /* extract_obj */ extract_obj( obj2 ); return obj1; } return obj2; } /* * Split off a grouped object - Thoric * decreased obj's count to num, and creates a new object containing the rest */ void split_obj( OBJ_DATA *obj, int num ) { int count; OBJ_DATA *rest; if( !obj ) return; count = obj->count; if( count <= num || num == 0 ) return; rest = clone_object( obj ); --obj->pIndexData->count; /* since clone_object() ups this value */ --numobjsloaded; rest->count = obj->count - num; obj->count = num; if( obj->carried_by ) { LINK( rest, obj->carried_by->first_carrying, obj->carried_by->last_carrying, next_content, prev_content ); rest->carried_by = obj->carried_by; rest->in_room = NULL; rest->in_obj = NULL; } else if( obj->in_room ) { LINK( rest, obj->in_room->first_content, obj->in_room->last_content, next_content, prev_content ); obj->in_room->objcount++; rest->carried_by = NULL; rest->in_room = obj->in_room; rest->in_obj = NULL; } else if( obj->in_obj ) { LINK( rest, obj->in_obj->first_content, obj->in_obj->last_content, next_content, prev_content ); rest->in_obj = obj->in_obj; rest->in_room = NULL; rest->carried_by = NULL; } } void separate_obj( OBJ_DATA *obj ) { if( !obj ) return; split_obj( obj, 1 ); } /* Empty an obj's contents... optionally into another obj, or a room */ bool empty_obj( OBJ_DATA *obj, OBJ_DATA *destobj, ROOM_INDEX_DATA *destroom ) { OBJ_DATA *otmp, *otmp_next; CHAR_DATA *ch = obj->carried_by; bool movedsome = false; if( !obj ) { bug( "%s: NULL obj", __FUNCTION__ ); return false; } if( destobj || ( !destroom && !ch && ( destobj = obj->in_obj ) ) ) { for( otmp = obj->first_content; otmp; otmp = otmp_next ) { otmp_next = otmp->next_content; /* only keys on a keyring */ if( destobj->item_type == ITEM_KEYRING && otmp->item_type != ITEM_KEY ) continue; if( destobj->item_type == ITEM_QUIVER && otmp->item_type != ITEM_PROJECTILE ) continue; if( ( destobj->item_type == ITEM_CONTAINER || destobj->item_type == ITEM_KEYRING || destobj->item_type == ITEM_QUIVER ) && get_real_obj_weight( otmp ) + get_real_obj_weight( destobj ) > destobj->value[0] ) continue; obj_from_obj( otmp ); obj_to_obj( otmp, destobj ); movedsome = true; } return movedsome; } if( destroom || ( !ch && ( destroom = obj->in_room ) ) ) { for( otmp = obj->first_content; otmp; otmp = otmp_next ) { otmp_next = otmp->next_content; if( ch && HAS_PROG( otmp->pIndexData, DROP_PROG ) && otmp->count > 1 ) { separate_obj( otmp ); obj_from_obj( otmp ); if( !otmp_next ) otmp_next = obj->first_content; } else obj_from_obj( otmp ); otmp = obj_to_room( otmp, destroom ); if( ch ) { oprog_drop_trigger( ch, otmp ); /* mudprogs */ if( char_died( ch ) ) ch = NULL; } movedsome = true; } return movedsome; } if( ch ) { for( otmp = obj->first_content; otmp; otmp = otmp_next ) { otmp_next = otmp->next_content; obj_from_obj( otmp ); obj_to_char( otmp, ch ); movedsome = true; } return movedsome; } bug( "%s: could not determine a destination for vnum %d", __FUNCTION__, obj->pIndexData->vnum ); return false; } /* Improve mental state - Thoric */ void better_mental_state( CHAR_DATA *ch, int mod ) { int c = URANGE( 0, abs( mod ), 20 ); int con = get_curr_con( ch ); c += number_percent( ) < con ? 1 : 0; if( ch->mental_state < 0 ) ch->mental_state = URANGE( -100, ch->mental_state + c, 0 ); else if( ch->mental_state > 0 ) ch->mental_state = URANGE( 0, ch->mental_state - c, 100 ); } /* Deteriorate mental state - Thoric */ void worsen_mental_state( CHAR_DATA *ch, int mod ) { int c = URANGE( 0, abs( mod ), 20 ); int con = get_curr_con( ch ); c -= number_percent( ) < con ? 1 : 0; if( c < 1 ) return; if( ch->mental_state < 0 ) ch->mental_state = URANGE( -100, ch->mental_state - c, 100 ); else if( ch->mental_state > 0 ) ch->mental_state = URANGE( -100, ch->mental_state + c, 100 ); else ch->mental_state -= c; } /* * Add another notch on that there belt... ;) * Keep track of the last so many kills by vnum - Thoric */ void add_kill( CHAR_DATA *ch, CHAR_DATA *mob ) { int x, vnum; short track; if( is_npc( ch ) ) { bug( "%s: trying to add kill to npc", __FUNCTION__ ); return; } if( !is_npc( mob ) ) { bug( "%s: trying to add kill non-npc", __FUNCTION__ ); return; } vnum = mob->pIndexData->vnum; track = URANGE( 2, ( ( ch->level + 3 ) * MAX_KILLTRACK ) / MAX_LEVEL, MAX_KILLTRACK ); /* Already in the history? */ for( x = 0; x < track; x++ ) { if( ch->pcdata->killed[x].vnum == vnum ) { if( ch->pcdata->killed[x].count < 50 ) ++ch->pcdata->killed[x].count; return; } else if( ch->pcdata->killed[x].vnum == 0 ) break; } /* Move the history down so 0 is empty */ memmove( (char *)ch->pcdata->killed + sizeof( KILLED_DATA ), ch->pcdata->killed, (track - 1) * sizeof( KILLED_DATA ) ); /* Set the new kill history (in 0 spot) */ ch->pcdata->killed[0].vnum = vnum; ch->pcdata->killed[0].count = 1; /* Clear out the rest from track to MAX_KILLTRACK */ while( track < MAX_KILLTRACK ) { ch->pcdata->killed[track].vnum = 0; ch->pcdata->killed[track].count = 0; track++; } } /* * Return how many times this player has killed this mob -Thoric * Only keeps track of so many (MAX_KILLTRACK), and keeps track by vnum */ int times_killed( CHAR_DATA *ch, CHAR_DATA *mob ) { int vnum, x; if( is_npc( ch ) ) { bug( "%s: ch is not a player", __FUNCTION__ ); return 0; } if( !is_npc( mob ) ) { bug( "%s: mob is not a mobile", __FUNCTION__ ); return 0; } vnum = mob->pIndexData->vnum; for( x = 0; x < MAX_KILLTRACK; x++ ) { if( !ch->pcdata->killed[x].vnum ) continue; if( ch->pcdata->killed[x].vnum == vnum ) return ch->pcdata->killed[x].count; } return 0; } /* * returns area with name matching input string * Last Modified : July 21, 1997 * Fireblade */ AREA_DATA *get_area( char *name ) { AREA_DATA *pArea; if( !name ) { bug( "%s: NULL input string.", __FUNCTION__ ); return NULL; } for( pArea = first_area; pArea; pArea = pArea->next ) { if( nifty_is_name( name, pArea->name ) ) break; } if( !pArea ) { for( pArea = first_build; pArea; pArea = pArea->next ) { if( nifty_is_name( name, pArea->name ) ) break; } } return pArea; } AREA_DATA *get_area_obj( OBJ_INDEX_DATA *pObjIndex ) { AREA_DATA *pArea; if( !pObjIndex ) { bug( "%s: pObjIndex is NULL.", __FUNCTION__ ); return NULL; } for( pArea = first_area; pArea; pArea = pArea->next ) { if( pObjIndex->vnum >= pArea->low_vnum && pObjIndex->vnum <= pArea->hi_vnum ) break; } return pArea; } int umin( int check, int ncheck ) { if( check < ncheck ) return check; return ncheck; } int umax( int check, int ncheck ) { if( check > ncheck ) return check; return ncheck; } int urange( int mincheck, int check, int maxcheck ) { if( check < mincheck ) return mincheck; if( check > maxcheck ) return maxcheck; return check; } double dumin( double check, double ncheck ) { if( check < ncheck ) return check; return ncheck; } double dumax( double check, double ncheck ) { if( check > ncheck ) return check; return ncheck; } double durange( double mincheck, double check, double maxcheck ) { if( check < mincheck ) return mincheck; if( check > maxcheck ) return maxcheck; return check; } char *pers( CHAR_DATA *ch, CHAR_DATA *looker ) { if( !ch || !looker ) return (char *)"Someone"; if( can_see( looker, ch ) ) { if( is_npc( ch ) ) return ch->short_descr; else return ch->name; } return (char *)"Someone"; } char *morphpers( CHAR_DATA *ch, CHAR_DATA *looker ) { if( ch && looker && ch->morph && ch->morph->morph && ch->morph->morph->short_desc ) return ch->morph->morph->short_desc; return (char *)"Someone"; } /* Things change sometimes and need to be rechecked as they go */ void check_chareq( CHAR_DATA *ch ) { MCLASS_DATA *mclass; OBJ_DATA *obj, *obj_next; int stat; bool scontinue; for( obj = ch->first_carrying; obj; obj = obj_next ) { obj_next = obj->next_content; if( obj->wear_loc == WEAR_NONE || is_obj_stat( obj, ITEM_LODGED ) ) continue; /* Check to see if it should zap them */ 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 ) ) ) { act( AT_MAGIC, "You're zapped by $p.", ch, obj, NULL, TO_CHAR ); act( AT_MAGIC, "$n is zapped by $p.", ch, obj, NULL, TO_ROOM ); unequip_char( ch, obj ); oprog_zap_trigger( ch, obj ); /* mudprogs */ if( char_died( ch ) ) break; continue; } /* Check level to see if they can still use it */ if( ch->level < obj->level ) { act( AT_OBJECT, "Your level is no longer high enough to use $p.", ch, obj, NULL, TO_CHAR ); act( AT_OBJECT, "$n's level is no longer high enough to use $p.", ch, obj, NULL, TO_ROOM ); unequip_char( ch, obj ); continue; } /* Check all stats to see if they can still use it */ scontinue = false; for( stat = 0; stat < STAT_MAX; stat++ ) { if( get_curr_stat( stat, ch ) < obj->stat_reqs[stat] ) { act_printf( AT_OBJECT, ch, obj, NULL, TO_CHAR, "Your %s is no longer high enough to use $p.", stattypes[stat] ); act_printf( AT_OBJECT, ch, obj, NULL, TO_ROOM, "$n no longer has the %s needed to use $p.", stattypes[stat] ); unequip_char( ch, obj ); scontinue = true; break; } } if( scontinue ) continue; /* Only check pc's after this */ if( is_npc( ch ) ) continue; /* Check PC race */ if( xIS_SET( obj->race_restrict, ch->race ) ) { act( AT_OBJECT, "Your race isn't able to use $p.", ch, obj, NULL, TO_CHAR ); act( AT_OBJECT, "$n can no longer use $p because of race.", ch, obj, NULL, TO_ROOM ); unequip_char( ch, obj ); continue; } /* Check all PC classes */ scontinue = false; for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next ) { if( mclass->wclass >= 0 && xIS_SET( obj->class_restrict, mclass->wclass ) ) { act( AT_OBJECT, "Your class isn't able to wear $p.", ch, obj, NULL, TO_CHAR ); act( AT_OBJECT, "$n can no longer use $p because of class.", ch, obj, NULL, TO_ROOM ); unequip_char( ch, obj ); scontinue = true; break; } } if( scontinue ) continue; } }