/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * Ack 2.2 improvements copyright (C) 1994 by Stephen Dooley * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * _/ _/_/_/ _/ _/ _/ ACK! MUD is modified * * _/_/ _/ _/ _/ _/ Merc2.0/2.1/2.2 code * * _/ _/ _/ _/_/ _/ (c)Stephen Zepp 1998 * * _/_/_/_/ _/ _/ _/ Version #: 4.3 * * _/ _/ _/_/_/ _/ _/ _/ * * * * http://ackmud.nuc.net/ * * zenithar@ackmud.nuc.net * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * ***************************************************************************/ /*************************************************************************** * _/_/_/_/ _/ _/ _/_/_/_/ _/_/_/_/ AckFUSS is modified ACK!MUD 4.3.1 * * _/ _/ _/ _/ _/ copyright Matt Goff (Kline) 2008 * * _/_/ _/ _/ _/_/_/_/ _/_/_/_/ * * _/ _/ _/ _/ _/ Support for this code is provided * * _/ _/_/_/_/ _/_/_/_/ _/_/_/_/ at www.ackmud.net -- check it out!* ***************************************************************************/ #include <sys/time.h> #include <sys/resource.h> #include <stdio.h> #include <string.h> #include <time.h> #include "globals.h" #include <signal.h> #ifndef DEC_ACT_COMM_H #include "h/act_comm.h" #endif #ifndef DEC_ACT_INFO_H #include "h/act_info.h" #endif #ifndef DEC_ACT_MOVE_H #include "h/act_move.h" #endif #ifndef DEC_ACT_OBJ_H #include "h/act_obj.h" #endif #ifndef DEC_ACT_WIZ_H #include "h/act_wiz.h" #endif #ifndef DEC_AREASAVE_H #include "h/areasave.h" #endif #ifndef DEC_COMM_H #include "h/comm.h" #endif #ifndef DEC_DB_H #include "h/db.h" #endif #ifndef DEC_FIGHT_H #include "h/fight.h" #endif #ifndef DEC_HANDLER_H #include "h/handler.h" #endif #ifndef DEC_MAGIC_H #include "h/magic.h" #endif #ifndef DEC_MONEY_H #include "h/money.h" #endif #ifndef DEC_SSM_H #include "h/ssm.h" #endif extern POL_DATA politics_data; extern OBJ_DATA *quest_object; extern CHAR_DATA *quest_mob; extern COUNCIL_DATA super_councils[MAX_SUPER]; extern int ssm_dup_count; extern int ssm_loops; extern int ssm_recent_loops; extern OBJ_DATA *auction_item; extern CHAR_DATA *auction_owner; extern CHAR_DATA *auction_bidder; extern int auction_bid; extern int auction_reserve; extern int auction_stage; extern int saving_area; extern bool auction_flop; extern bool auto_quest; extern bool quest; extern int quest_timer; extern int quest_wait; /* * Local functions. */ int hit_gain args( ( CHAR_DATA * ch ) ); int mana_gain args( ( CHAR_DATA * ch ) ); void mobile_update args( ( void ) ); void weather_update args( ( void ) ); void char_update args( ( void ) ); void gain_update args( ( void ) ); void obj_update args( ( void ) ); void aggr_update args( ( void ) ); void objfun_update args( ( void ) ); void rooms_update args( ( void ) ); void remember_attack args( ( CHAR_DATA * ch, CHAR_DATA * victim ) ); void quest_update args( ( void ) ); /* * IMC2 keeps quietly losing sync to the server. Going to let * it run the keepalive routine so I can stay connected. --Kline */ #ifdef IMC void imc_delete_reminfo args(( REMOTEINFO * p )); void imc_request_keepalive args(( void )); #endif int abort_threshold = BOOT_DB_ABORT_THRESHOLD; bool disable_timer_abort = FALSE; int last_checkpoint; int get_user_seconds( ) { struct rusage rus; getrusage( RUSAGE_SELF, &rus ); return rus.ru_utime.tv_sec; } /* Update the checkpoint */ void alarm_update( ) { char *strtime; char buf[MSL]; ssm_dup_count = 0; ssm_loops = 0; last_checkpoint = get_user_seconds( ); if( abort_threshold == BOOT_DB_ABORT_THRESHOLD ) { abort_threshold = RUNNING_ABORT_THRESHOLD; strtime = ctime( ¤t_time ); strtime[strlen( strtime ) - 1] = '\0'; snprintf( buf, MSL, "Used %d user CPU seconds.", last_checkpoint ); fprintf( stderr, "%s :: %s\n", strtime, buf ); } } #ifndef WIN32 /* Set the virtual (CPU time) timer to the standard setting, ALARM_FREQUENCY */ void reset_itimer( ) { struct itimerval itimer; itimer.it_interval.tv_usec = 0; /* miliseconds */ itimer.it_interval.tv_sec = ALARM_FREQUENCY; itimer.it_value.tv_usec = 0; itimer.it_value.tv_sec = ALARM_FREQUENCY; /* * start the timer - in that many CPU seconds, alarm_handler will be called */ if( setitimer( ITIMER_VIRTUAL, &itimer, NULL ) < 0 ) { perror( "reset_itimer:setitimer" ); exit( 1 ); } } #endif const char *szFrozenMessage = "Alarm_handler: Not checkpointed recently, aborting!\n"; /* Signal handler for alarm - suggested for use in MUDs by Fusion */ void alarm_handler( int signo ) { int usage_now = get_user_seconds( ); /* * Has there gone abort_threshold CPU seconds without alarm_update? */ if( !disable_timer_abort && ( usage_now - last_checkpoint > abort_threshold ) ) { /* * For the log file */ char buf[MAX_STRING_LENGTH]; /* * spec: log usage values */ log_f( "current usage: %d, last checkpoint: %d", usage_now, last_checkpoint ); log_f( "SSM dups: %d, loops: %d, recent: %d", ssm_dup_count, ssm_loops, ssm_recent_loops ); snprintf( buf, MSL, "%s\r\n", szFrozenMessage ); bug( buf, 0 ); raise( SIGABRT ); /* kill ourselves on return */ } /* * The timer resets to the values specified in it_interval * * automatically. * * * * Spec: additionally, SIGABRT is blocked in this handler, and will * * only be delivered on return. This should ensure a good core. */ } #ifndef WIN32 /* Install signal alarm handler */ void init_alarm_handler( ) { struct sigaction sa; sa.sa_handler = alarm_handler; sa.sa_flags = SA_RESTART; /* Restart interrupted system calls */ sigemptyset( &sa.sa_mask ); sigaddset( &sa.sa_mask, SIGABRT ); /* block abort() in the handler * so we can get a good coredump */ if( sigaction( SIGVTALRM, &sa, NULL ) < 0 ) /* setup handler for virtual timer */ { perror( "init_alarm_handler:sigaction" ); exit( 1 ); } last_checkpoint = get_user_seconds( ); reset_itimer( ); /* start timer */ } #endif /* * Advancement stuff. */ void advance_level( CHAR_DATA * ch, int p_class, bool show, bool remort ) { /* * class used instead of ch->p_class. -S- */ /* * show added to allow no display of gain ( when using setclass ) */ /* * remort indicates remortal class or normal mortal class */ char buf[MAX_STRING_LENGTH]; int add_hp; int add_mana; int add_move; int add_prac; int add_bloodlust, add_max_skills; /* * title no longer changed..... */ if( p_class == ADVANCE_WOLF ) { add_bloodlust = ( number_range( 1, ( ( MAX_WOLF_LEVEL / 2 ) - ch->pcdata->super->generation ) ) ) + ( ( ( MAX_WOLF_LEVEL / 2 ) - ch->pcdata->super->generation ) / 2 ); add_prac = number_range( 1, UMAX( 2, ( ( MAX_WOLF_LEVEL / 2 ) - ch->pcdata->super->generation ) ) ); add_max_skills = add_prac; ch->pcdata->super->energy_max += add_bloodlust; ch->pcdata->super->pracs += add_prac; ch->pcdata->super->skills_max += add_max_skills; snprintf( buf, MSL, "@@NYou gain: %d @@rRage Ability@@N, and %d @@bWerewolf Practices. .@@N\r\n", add_bloodlust, add_prac ); send_to_char( buf, ch ); return; } if( ( p_class == 16 ) ) { add_bloodlust = UMAX( ( ( MAX_VAMP_LEVEL / 2 ) - ( ch->pcdata->super->generation / 2 ) ), 1 ); add_prac = number_range( 1, UMAX( 2, ( ( MAX_VAMP_LEVEL / 2 ) - ( ch->pcdata->super->generation ) ) ) ); add_max_skills = number_range( 1, UMAX( 2, ( ( MAX_VAMP_LEVEL / 2 ) - ( ch->pcdata->super->generation ) ) ) ); ch->pcdata->super->energy_max += add_bloodlust; ch->pcdata->super->pracs += add_prac; ch->pcdata->super->skills_max += add_max_skills; snprintf( buf, MSL, "You gain: %d @@eBloodlust@@N, and %d Vampyre Practices. .\r\n", add_bloodlust, add_prac ); send_to_char( buf, ch ); return; } if( ( p_class == 32 ) ) { add_hp = con_app[get_curr_con( ch )].hitp + number_range( 10, 50 ); add_mana = number_range( 10, ( 3 * get_curr_int( ch ) + get_curr_wis( ch ) ) / 4 ); } else if( remort ) { add_hp = con_app[get_curr_con( ch )].hitp + number_range( remort_table[p_class].hp_min, remort_table[p_class].hp_max ); add_mana = remort_table[p_class].fMana ? number_range( 2, ( 2 * get_curr_int( ch ) + get_curr_wis( ch ) ) / 16 ) : 0; } else { add_hp = con_app[get_curr_con( ch )].hitp + number_range( class_table[p_class].hp_min, class_table[p_class].hp_max ); add_mana = class_table[p_class].fMana ? number_range( 2, ( 2 * get_curr_int( ch ) + get_curr_wis( ch ) ) / 16 ) : 0; } add_move = number_range( 2, ( get_curr_con( ch ) + get_curr_dex( ch ) ) / 5 ); add_prac = ( wis_app[get_curr_wis( ch )].practice / 2 ) + number_range( 1, 3 ); add_hp = UMAX( 1, add_hp ); add_mana = UMAX( 0, add_mana ); add_move = UMAX( 7, add_move ); ch->pcdata->mana_from_gain += add_mana; ch->pcdata->hp_from_gain += add_hp; ch->pcdata->move_from_gain += add_move; ch->max_hit += add_hp; ch->max_mana += add_mana; ch->max_move += add_move; ch->practice += add_prac; if( !IS_NPC( ch ) ) ch->act.reset(ACT_BOUGHT_PET); snprintf( buf, MSL, "You gain: %d Hit Points, %d Mana, %d Movement and %d pracs.\r\n", add_hp, add_mana, add_move, add_prac ); if( show ) send_to_char( buf, ch ); return; } void gain_exp( CHAR_DATA * ch, int gain ) { /* * Not much happens here, as no-longer auto-level... -S- */ /* * -S- Mod: mobs CAN gain exp as well as players */ if( IS_NPC( ch ) && !ch->act.test(ACT_INTELLIGENT) ) return; if( IS_IMMORTAL( ch ) ) return; /* * Changed exp system AGAIN old 'cap' was screwy!! -S- */ ch->exp += gain; return; } /* * Regeneration stuff. */ int hit_gain( CHAR_DATA * ch ) { float gain; if( ch == NULL ) return 0; if( IS_NPC( ch ) && !ch->act.test(ACT_INTELLIGENT) ) gain = ( 5 + ch->level / 30 ); gain = ( 5 + ch->level / 20 ); if( ch->in_room->room_flags.test(RFLAG_REGEN) ) gain *= 2; switch ( ch->position ) { case POS_SLEEPING: gain += get_curr_con( ch ) / 2; break; case POS_RESTING: gain += get_curr_con( ch ) / 4; break; } if( !IS_NPC( ch ) ) { if( ch->pcdata->condition[COND_FULL] == 0 ) gain /= 2; if( ch->pcdata->condition[COND_THIRST] == 0 ) gain /= 2; if( IS_VAMP( ch ) && ch->pcdata->super->energy < 3 ) gain = 0; else if( IS_VAMP( ch ) && ch->pcdata->super->energy < 8 ) gain /= 2; if( IS_VAMP( ch ) && ch->pcdata->super->energy == -10 ) gain = ( 5 + ch->level / 25 ); } if( IS_AFFECTED( ch, AFF_POISON ) ) gain /= 4; if( ch->in_room->room_flags.test(RFLAG_COLD) || ch->in_room->room_flags.test(RFLAG_HOT) ) gain *= -2; if( IS_SET( ch->in_room->affected_by, ROOM_BV_HEAL_REGEN ) ) { if( gain < 0 ) gain *= -2; else gain *= 2; } if( IS_AFFECTED( ch, AFF_CLOAK_REGEN ) ) { if( gain < 0 ) gain *= -2; else gain *= 2; } if( check_charm_aff(ch,CHARM_AFF_REGEN) ) gain *= ((100 + get_charm_bonus(ch,CHARM_AFF_REGEN)) / 100); if( IS_SET( ch->in_room->affected_by, ROOM_BV_HEAL_STEAL ) ) if( gain > 0 ) gain *= -1; if( !IS_NPC( ch ) && ( gain > 0 ) ) { if( IS_SET( race_table[ch->race].race_flags, RACE_MOD_FAST_HEAL ) ) gain = gain * 1.5; else if( IS_SET( race_table[ch->race].race_flags, RACE_MOD_SLOW_HEAL ) ) gain = gain * .75; } if( !IS_NPC( ch ) && ( gain > 0 ) ) { if( ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_WOODLAND ) ) && ( ch->in_room != NULL ) ) { if( ( ch->in_room->sector_type == SECT_FIELD ) || ( ch->in_room->sector_type == SECT_FOREST ) ) gain = gain * 1.3; else if( ( ch->in_room->sector_type == SECT_CITY ) || ( ch->in_room->sector_type == SECT_INSIDE ) ) gain = gain * .8; } else if( ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_DARKNESS ) ) && ( ch->in_room != NULL ) ) { if( ( ch->in_room->sector_type == SECT_FIELD ) || ( ch->in_room->sector_type == SECT_HILLS ) || ( ch->in_room->sector_type == SECT_AIR ) || ( ch->in_room->sector_type == SECT_DESERT ) ) gain = gain * .8; else if( ( ch->in_room->sector_type == SECT_CITY ) || ( ch->in_room->sector_type == SECT_INSIDE ) ) gain = gain * 1.3; } } return UMIN( static_cast<int>(gain), ch->max_hit - ch->hit ); } int mana_gain( CHAR_DATA * ch ) { float gain; if( ch == NULL ) return 0; if( IS_NPC( ch ) && !ch->act.test(ACT_INTELLIGENT) ) { gain = ( 1 + ch->level / 30 ); } else { gain = ( 5 + ch->level / 20 ); if( ch->in_room->room_flags.test(RFLAG_REGEN) ) gain *= 2; switch ( ch->position ) { case POS_SLEEPING: gain += get_curr_int( ch ); break; case POS_RESTING: gain += get_curr_int( ch ) / 2; break; } if( !IS_NPC( ch ) ) { if( ch->pcdata->condition[COND_FULL] == 0 ) gain /= 2; if( ch->pcdata->condition[COND_THIRST] == 0 ) gain /= 2; if( IS_VAMP( ch ) && ch->pcdata->super->energy < 3 ) gain = 0; else if( IS_VAMP( ch ) && ch->pcdata->super->energy < 8 ) gain /= 2; if( IS_VAMP( ch ) && ch->pcdata->super->energy == -10 ) gain = ( 5 + ch->level / 25 ); if( IS_WOLF( ch ) && IS_RAGED( ch ) ) gain = 0; } if( IS_SET( ch->in_room->affected_by, ROOM_BV_MANA_REGEN ) ) { if( gain < 0 ) gain *= -2; else gain *= 2; } if( IS_SET( ch->in_room->affected_by, ROOM_BV_MANA_STEAL ) ) if( gain > 0 ) gain *= -1; } if( IS_AFFECTED( ch, AFF_POISON ) ) gain /= 4; if( check_charm_aff(ch,CHARM_AFF_REGEN) ) gain *= ((100 + get_charm_bonus(ch,CHARM_AFF_REGEN)) / 100); if( !IS_NPC( ch ) && ( gain > 0 ) ) { if( IS_SET( race_table[ch->race].race_flags, RACE_MOD_NO_MAGIC ) ) gain = gain * .5; else if( IS_SET( race_table[ch->race].race_flags, RACE_MOD_WEAK_MAGIC ) ) gain = gain * .75; else if( IS_SET( race_table[ch->race].race_flags, RACE_MOD_STRONG_MAGIC ) ) gain = gain * 1.40; } if( !IS_NPC( ch ) && ( gain > 0 ) ) { if( ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_WOODLAND ) ) && ( ch->in_room != NULL ) ) { if( ( ch->in_room->sector_type == SECT_FIELD ) || ( ch->in_room->sector_type == SECT_FOREST ) ) gain = gain * 1.3; else if( ( ch->in_room->sector_type == SECT_CITY ) || ( ch->in_room->sector_type == SECT_INSIDE ) ) gain = gain * .8; } else if( ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_DARKNESS ) ) && ( ch->in_room != NULL ) ) { if( ( ch->in_room->sector_type == SECT_FIELD ) || ( ch->in_room->sector_type == SECT_HILLS ) || ( ch->in_room->sector_type == SECT_AIR ) || ( ch->in_room->sector_type == SECT_DESERT ) ) gain = gain * .8; else if( ( ch->in_room->sector_type == SECT_CITY ) || ( ch->in_room->sector_type == SECT_INSIDE ) ) gain = gain * 1.3; } } if( gain > 0 ) { gain = gain * int_app[get_curr_int( ch )].mana_regen / 10; if( MAGIC_STANCE( ch ) ) gain = gain * int_app[get_curr_int( ch )].mana_regen / 10; } return UMIN( static_cast<int>(gain), ch->max_mana - ch->mana ); } int move_gain( CHAR_DATA * ch ) { float gain; if( IS_NPC( ch ) ) { gain = ch->level; } else { gain = ( 10 + ch->level / 4 ); if( ch->in_room->room_flags.test(RFLAG_REGEN) ) gain *= 2; switch ( ch->position ) { case POS_SLEEPING: gain += get_curr_dex( ch ) / 2; break; case POS_RESTING: gain += get_curr_dex( ch ) / 4; break; } if( ch->pcdata->condition[COND_FULL] == 0 ) gain /= 2; if( ch->pcdata->condition[COND_THIRST] == 0 ) gain /= 2; if( IS_VAMP( ch ) && ch->pcdata->super->energy < 3 ) gain = 0; else if( IS_VAMP( ch ) && ch->pcdata->super->energy < 8 ) gain /= 2; if( IS_VAMP( ch ) && ch->pcdata->super->energy == -10 ) gain = ( 5 + ch->level / 25 ); } if( IS_AFFECTED( ch, AFF_POISON ) ) gain /= 4; if( check_charm_aff(ch,CHARM_AFF_REGEN) ) gain *= ((100 + get_charm_bonus(ch,CHARM_AFF_REGEN)) / 100); return UMIN( static_cast<int>(gain), ch->max_move - ch->move ); } void gain_rage( CHAR_DATA * ch ) { short rage_gain = 0; short current_rage = 0; if( IS_NPC( ch ) || !IS_WOLF( ch ) ) return; if( IS_RAGED( ch ) ) current_rage = ch->pcdata->super->energy_max; else if( IS_SHIFTED( ch ) ) current_rage = UMAX( 1, ( ch->pcdata->super->energy_max / 5 ) ); else current_rage = UMAX( 1, ( ch->pcdata->super->energy_max / 10 ) ); rage_gain = number_range( 1, ( MAX_WOLF_LEVEL / 2 - ch->pcdata->super->generation ) ); if( ch->pcdata->super->energy >= current_rage ) ch->pcdata->super->energy = UMIN( ( ch->pcdata->super->energy + rage_gain ), current_rage ); else ch->pcdata->super->energy = UMIN( current_rage, ( ch->pcdata->super->energy + rage_gain ) ); } void gain_bloodlust( CHAR_DATA * ch, int value ) { /* * Kinda like gain_condition, but handles vampires -S- */ int condition; if( value == 0 ) return; condition = ch->pcdata->super->energy; /* * in case vamp bites off more than he can chew ;) * -Damane- 4/26/96 */ if( ( ch->pcdata->super->energy + value ) > ch->pcdata->super->energy_max ) ch->pcdata->super->energy = ch->pcdata->super->energy_max; else ch->pcdata->super->energy += value; if( ch->pcdata->super->energy > ch->pcdata->super->energy_max ) ch->pcdata->super->energy = ch->pcdata->super->energy_max; if( ch->position == POS_BUILDING || ch->position == POS_WRITING ) return; if( ( ch->pcdata->super->energy < 0 ) && ( ch->pcdata->super->energy != -10 ) ) ch->pcdata->super->energy = 0; if( ch->pcdata->super->energy < 2 ) send_to_char( "Your body burns with the need for blood!\r\n", ch ); else if( ch->pcdata->super->energy < 7 ) send_to_char( "You start to feel weaker... more blood needed!\r\n", ch ); else if( ch->pcdata->super->energy < 10 ) send_to_char( "You find yourself missing the taste of blood.\r\n", ch ); return; } void gain_condition( CHAR_DATA * ch, int iCond, int value ) { int condition; if( value == 0 || IS_NPC( ch ) || ch->level >= LEVEL_HERO ) return; condition = ch->pcdata->condition[iCond]; ch->pcdata->condition[iCond] = URANGE( 0, condition + value, 48 ); if( ch->position == POS_BUILDING || ch->position == POS_WRITING ) return; if( ch->pcdata->condition[iCond] == 0 ) { switch ( iCond ) { case COND_FULL: send_to_char( "You are hungry.\r\n", ch ); break; case COND_THIRST: send_to_char( "You are thirsty.\r\n", ch ); break; case COND_DRUNK: if( condition != 0 ) send_to_char( "You are sober.\r\n", ch ); break; } } return; } /* * Mob autonomous action. * This function takes 25% to 35% of ALL Merc cpu time. * -- Furey */ void mobile_update( void ) { CHAR_DATA *ch; CHAR_DATA *target; EXIT_DATA *pexit; int door; CHAR_DATA *quitter; std::list<CHAR_DATA *>::iterator li; /* * Examine all mobs. */ for( li = char_list.begin(); li != char_list.end(); li++ ) { ch = *li; if( ch == NULL || !IS_NPC( ch ) || ch->in_room == NULL || IS_AFFECTED( ch, AFF_CHARM ) ) continue; /* * Examine call for special procedure */ if( IS_NPC(ch) && ch->npcdata->spec_fun != 0 ) { if( ( *ch->npcdata->spec_fun ) ( ch ) ) continue; } /* * Added by Aeria for mob hunting continuously */ if( ch->searching != NULL ) { if( ( quitter = get_char_world( ch, ch->searching ) ) != NULL ) set_hunt( ch, NULL, quitter, NULL, HUNT_WORLD | HUNT_OPENDOOR | HUNT_PICKDOOR | HUNT_UNLOCKDOOR | HUNT_INFORM, HUNT_CR | HUNT_MERC ); } /* * DISABLED */ /* * Intelligent mob? */ /* if ( ch->act.test(ACT_INTELLIGENT) ) int_handler( ch ); Disabled for now, for bugs. */ /* * That's all for sleeping / busy monster */ if( ch->position < POS_STANDING ) continue; /* * Check for rewield, and re-equip (specials not used anymore) */ if( ch->act.test(ACT_RE_WIELD) ) if( check_rewield( ch ) ) continue; if( ch->act.test(ACT_RE_EQUIP) ) if( check_re_equip( ch ) ) continue; /* * Check for remember victims */ if( ch->target != NULL && ( target = get_char_room( ch, ch->target ) ) != NULL ) { remember_attack( ch, target ); continue; } /* * Check to see if mob is moving somewhere */ /* if ( mob_hunt(ch) ) continue; if ( IS_SET( ch->act_hunt, ACT_HUNT_MOVE ) && ch->move_to != NO_VNUM ) { hunt_move( ch ); continue; } */ /* * Scavenge */ if( ch->act.test(ACT_SCAVENGER) && ch->in_room->first_content != NULL && number_bits( 2 ) == 0 ) { OBJ_DATA *obj; OBJ_DATA *obj_best; int max; max = 1; obj_best = 0; for( obj = ch->in_room->first_content; obj; obj = obj->next_in_room ) { if( CAN_WEAR( obj, ITEM_TAKE ) && obj->cost > max ) { obj_best = obj; max = obj->cost; } } if( obj_best ) { obj_from_room( obj_best ); obj_to_char( obj_best, ch ); act( "$n gets $p.", ch, obj_best, NULL, TO_ROOM ); } } /* * Wander */ if( !ch->act.test(ACT_SENTINEL) && ch->leader == NULL && ( door = number_bits( 5 ) ) <= 5 && ( pexit = ch->in_room->exit[door] ) != NULL && pexit->to_room != NULL && !pexit->exit_info.test(EX_CLOSED) && !pexit->to_room->room_flags.test(RFLAG_NO_MOB) && ( !ch->act.test(ACT_STAY_AREA) || pexit->to_room->area == ch->in_room->area ) ) { move_char( ch, door, FALSE ); /* * If ch changes position due * to it's or someother mob's * movement via MOBProgs, * continue - Kahn */ if( ch->position < POS_STANDING ) continue; } /* * Flee */ if( ch->hit < ( ch->max_hit / 2 ) && ( door = number_bits( 3 ) ) <= 5 && ( pexit = ch->in_room->exit[door] ) != NULL && pexit->to_room != NULL && !pexit->exit_info.test(EX_CLOSED) && !pexit->to_room->room_flags.test(RFLAG_NO_MOB) ) { CHAR_DATA *rch; bool found; found = FALSE; for( rch = pexit->to_room->first_person; rch != NULL; rch = rch->next_in_room ) { if( !IS_NPC( rch ) ) { found = TRUE; break; } } if( !found ) move_char( ch, door, FALSE ); } } return; } /* * Update the weather. */ void clean_donate_rooms( void ) { ROOM_INDEX_DATA *room = NULL; short room_count, looper, chance; int room_vnum; OBJ_DATA *object, *obj_next; /* * normal donate rooms */ if( ( room = get_room_index( ROOM_VNUM_MISC_DONATE ) ) != NULL ) { for( room_count = 0, object = room->first_content; object; room_count++, object = object->next_in_room ); if( room_count > 150 ) { for( object = room->first_content; object; object = obj_next ) { obj_next = object->next_in_room; if( count_obj_room( object->pIndexData, room->first_content ) > 5 ) chance = 50; else chance = 10; if( IS_OBJ_STAT(object,ITEM_EXTRA_RARE) || object == quest_object ) chance = 0; if( number_range( 0, 99 ) < chance ) extract_obj( object ); } } } if( ( room = get_room_index( ROOM_VNUM_WEAPON_DONATE ) ) != NULL ) { for( room_count = 0, object = room->first_content; object; room_count++, object = object->next_in_room ); if( room_count > 150 ) { for( object = room->first_content; object; object = obj_next ) { obj_next = object->next_in_room; if( count_obj_room( object->pIndexData, room->first_content ) > 5 ) chance = 50; else chance = 10; if( IS_OBJ_STAT(object,ITEM_EXTRA_RARE) || object == quest_object ) chance = 0; if( number_range( 0, 99 ) < chance ) extract_obj( object ); } } } if( ( room = get_room_index( ROOM_VNUM_ARMOR_DONATE ) ) != NULL ) { for( room_count = 0, object = room->first_content; object; room_count++, object = object->next_in_room ); if( room_count > 150 ) { for( object = room->first_content; object; object = obj_next ) { obj_next = object->next_in_room; if( count_obj_room( object->pIndexData, room->first_content ) > 5 ) chance = 50; else chance = 10; if( IS_OBJ_STAT(object,ITEM_EXTRA_RARE) || object == quest_object ) chance = 0; if( number_range( 0, 99 ) < chance ) extract_obj( object ); } } } /* * clan donate rooms */ for( looper = 1; looper < MAX_CLAN; looper++ ) { if( ( room_vnum = clan_table[looper].donat_room ) == 0 ) continue; if( ( room = get_room_index( room_vnum ) ) != NULL ) { for( room_count = 0, object = room->first_content; object; room_count++, object = object->next_in_room ); if( room_count > 150 ) { for( object = room->first_content; object; object = obj_next ) { obj_next = object->next_in_room; if( count_obj_room( object->pIndexData, room->first_content ) > 5 ) chance = 50; else chance = 10; if( IS_OBJ_STAT(object,ITEM_EXTRA_RARE) || object == quest_object ) chance = 0; if( number_range( 0, 99 ) < chance ) extract_obj( object ); } } } } } void weather_update( void ) { char buf[MAX_STRING_LENGTH]; char buf2[MSL]; DESCRIPTOR_DATA *d; int diff; short x, y; #ifdef IMC REMOTEINFO *r, *rnext; #endif buf[0] = '\0'; buf2[0] = '\0'; switch ( ++time_info.hour ) { case 5: weather_info.sunlight = SUN_LIGHT; strncat( buf, "The sky shows signs of daybreak.\r\n", MSL ); break; case 6: weather_info.sunlight = SUN_RISE; strncat( buf, "The sun rises in the east.\r\n", MSL ); for( x = 1; x < MAX_CLAN; x++ ) for( y = 1; y < MAX_CLAN; y++ ) politics_data.daily_negotiate_table[x][y] = FALSE; break; case 12: for( x = 1; x < MAX_CLAN; x++ ) for( y = 1; y < MAX_CLAN; y++ ) politics_data.daily_negotiate_table[x][y] = FALSE; break; case 19: weather_info.sunlight = SUN_SET; strncat( buf, "The sun slowly disappears in the west.\r\n", MSL ); for( x = 1; x < MAX_CLAN; x++ ) for( y = 1; y < MAX_CLAN; y++ ) politics_data.daily_negotiate_table[x][y] = FALSE; break; case 20: weather_info.sunlight = SUN_DARK; strncat( buf, "The night has begun.\r\n", MSL ); break; case 24: time_info.hour = 0; time_info.day++; for( x = 1; x < MAX_CLAN; x++ ) for( y = 1; y < MAX_CLAN; y++ ) politics_data.daily_negotiate_table[x][y] = FALSE; clean_donate_rooms( ); #ifdef IMC if( this_imcmud->autoconnect ) /* Don't attempt a re-connect if we don't want to. --Kline */ { monitor_chan("Mud list is being refreshed.",MONITOR_IMC); for( r = first_rinfo; r; r = rnext ) { rnext = r->next; imc_delete_reminfo( r ); } imc_request_keepalive( ); } #endif break; } switch ( time_info.moon++ ) { case 5: weather_info.moon_loc = MOON_RISE; snprintf( buf2, MSL, "@@NA %s @@yMoon @@Nhas risen.\r\n", get_moon_phase_name( ) ); strncat( buf, buf2, MSL ); break; case 10: weather_info.moon_loc = MOON_LOW; snprintf( buf2, MSL, "@@NThe %s @@yMoon @@Nrides low on the horizon.\r\n", get_moon_phase_name( ) ); strncat( buf, buf2, MSL ); break; case 15: weather_info.moon_loc = MOON_PEAK; snprintf( buf2, MSL, "@@NThe %s @@yMoon @@Nreaches it's zenith.\r\n", get_moon_phase_name( ) ); strncat( buf, buf2, MSL ); break; case 20: weather_info.moon_loc = MOON_FALL; snprintf( buf2, MSL, "@@NThe %s @@yMoon @@Nfalls.\r\n", get_moon_phase_name( ) ); strncat( buf, buf2, MSL ); break; case 25: weather_info.moon_loc = MOON_SET; snprintf( buf2, MSL, "@@NThe %s @@yMoon @@Nis setting.\r\n", get_moon_phase_name( ) ); strncat( buf, buf2, MSL ); break; case 30: weather_info.moon_loc = MOON_DOWN; snprintf( buf2, MSL, "@@NThe %s @@yMoon @@Nhas left the sky.\r\n", get_moon_phase_name( ) ); strncat( buf, buf2, MSL ); break; default: break; } if( time_info.moon >= 50 ) { time_info.moon = 0; weather_info.moon_loc = MOON_DOWN; } if( time_info.day >= 20 ) /* now 20 days = 1 month */ { time_info.day = 0; time_info.month++; } if( time_info.month >= 8 ) /* 8 months a year */ { time_info.month = 0; time_info.year++; } if( ( ( time_info.day ) % 4 ) == 0 ) { if( !weather_info.phase_changed ) weather_info.moon_phase++; if( weather_info.moon_phase > MOON_WAN_CRE ) weather_info.moon_phase = MOON_NEW; weather_info.phase_changed = TRUE; } else weather_info.phase_changed = FALSE; /* * Weather change. */ if( time_info.month >= 9 && time_info.month <= 16 ) diff = weather_info.mmhg > 985 ? -2 : 2; else diff = weather_info.mmhg > 1015 ? -2 : 2; weather_info.change += diff * dice( 1, 4 ) + dice( 2, 6 ) - dice( 2, 6 ); weather_info.change = UMAX( weather_info.change, -12 ); weather_info.change = UMIN( weather_info.change, 12 ); weather_info.mmhg += weather_info.change; weather_info.mmhg = UMAX( weather_info.mmhg, 960 ); weather_info.mmhg = UMIN( weather_info.mmhg, 1040 ); switch ( weather_info.sky ) { default: bug( "Weather_update: bad sky %d.", weather_info.sky ); weather_info.sky = SKY_CLOUDLESS; break; case SKY_CLOUDLESS: if( weather_info.mmhg < 990 || ( weather_info.mmhg < 1010 && number_bits( 2 ) == 0 ) ) { strncat( buf, "The sky is getting cloudy.\r\n", MSL ); weather_info.sky = SKY_CLOUDY; } break; case SKY_CLOUDY: if( weather_info.mmhg < 970 || ( weather_info.mmhg < 990 && number_bits( 2 ) == 0 ) ) { strncat( buf, "It starts to rain.\r\n", MSL ); weather_info.sky = SKY_RAINING; } if( weather_info.mmhg > 1030 && number_bits( 2 ) == 0 ) { strncat( buf, "The clouds disappear.\r\n", MSL ); weather_info.sky = SKY_CLOUDLESS; } break; case SKY_RAINING: if( weather_info.mmhg < 970 && number_bits( 2 ) == 0 ) { strncat( buf, "Lightning flashes in the sky.\r\n", MSL ); weather_info.sky = SKY_LIGHTNING; } if( weather_info.mmhg > 1030 || ( weather_info.mmhg > 1010 && number_bits( 2 ) == 0 ) ) { strncat( buf, "The rain stopped.\r\n", MSL ); weather_info.sky = SKY_CLOUDY; } break; case SKY_LIGHTNING: if( weather_info.mmhg > 1010 || ( weather_info.mmhg > 990 && number_bits( 2 ) == 0 ) ) { strncat( buf, "The lightning has stopped.\r\n", MSL ); weather_info.sky = SKY_RAINING; break; } break; } if( buf[0] != '\0' ) { for( d = first_desc; d != NULL; d = d->next ) { if( d->connected == CON_PLAYING && IS_OUTSIDE( d->character ) && ( d->character->position != POS_WRITING ) && IS_AWAKE( d->character ) ) send_to_char( buf, d->character ); } } return; } /* New update loop to handle gains for players => smaller 'ticks' for hp/mana/move gain, normal 'ticks' for objects, affects, weather, etc */ void gain_update( void ) { CHAR_DATA *ch; std::list<CHAR_DATA *>::iterator li; /* Update super_councils info */ { MEMBER_DATA *imember; short count = 0; short council_index; for( council_index = 1; council_index < MAX_SUPER; council_index++ ) { if( super_councils[council_index].council_time > 0 ) { super_councils[council_index].council_time--; if( super_councils[council_index].council_time == 1 ) { MEMBER_DATA *imember_next; for( imember = super_councils[council_index].first_member; imember != NULL; imember = imember_next ) { imember_next = imember->next; send_to_char( "The current council is disbanded.\r\n", imember->this_member ); UNLINK( imember, super_councils[council_index].first_member, super_councils[council_index].last_member, next, prev ); imember->this_member = NULL; imember->next = NULL; imember->prev = NULL; PUT_FREE( imember, member_free ); } super_councils[council_index].council_time = 0; super_councils[council_index].quorum = FALSE; } } if( !super_councils[council_index].quorum ) { super_councils[council_index].quorum = FALSE; for( imember = super_councils[council_index].first_member; imember != NULL; imember = imember->next ) { count++; } if( count >= QUORUM_NUMBER ) { super_councils[council_index].quorum = TRUE; super_councils[council_index].council_time = 10; for( imember = super_councils[council_index].first_member; imember != NULL; imember = imember->next ) send_to_char( "The Council is in Session!\r\n", imember->this_member ); } } } } for( li = char_list.begin(); li != char_list.end(); li++ ) { ch = *li; if( ch == NULL ) continue; if( ch->position >= POS_STUNNED && !IS_SET( ch->affected_by, AFF_VAMP_HEALING ) && !IS_GHOST(ch) ) { if( ( ch->hit < ch->max_hit ) && ( !IS_SET( ch->in_room->affected_by, ROOM_BV_NONE ) ) ) ch->hit += ch->position == POS_FIGHTING ? (hit_gain( ch ) / 2) : hit_gain( ch ); ch->hit = UMAX( 25, ch->hit ); if( ( ch->mana < ch->max_mana ) && ( !IS_SET( ch->in_room->affected_by, ROOM_BV_NONE ) ) ) ch->mana += ch->position == POS_FIGHTING ? (mana_gain( ch ) / 2) : mana_gain( ch ); if( ( ch->move < ch->max_move ) && ( ch->carry_weight < can_carry_w( ch ) ) ) ch->move += ch->position == POS_FIGHTING ? (move_gain( ch ) / 2) : move_gain( ch ); else if( ch->carry_weight >= can_carry_w( ch ) ) { send_to_char( "You are carrying so much weight that you are @@eEXHAUSTED@@N!!\r\n", ch ); ch->move = 0; } } if( ch->position == POS_STUNNED || IS_SET( ch->affected_by, AFF_VAMP_HEALING ) ) update_pos( ch ); } return; } /* * Update all chars, including mobs. * This function is performance sensitive. */ void char_update( void ) { CHAR_DATA *ch; CHAR_DATA *ch_save; CHAR_DATA *ch_quit; std::list<CHAR_DATA *>::iterator li; time_t save_time; save_time = current_time; ch_save = NULL; ch_quit = NULL; for( li = char_list.begin(); li != char_list.end(); li++ ) { AFFECT_DATA *paf; AFFECT_DATA *paf_next; ch = *li; if( ch == NULL ) continue; if( IS_GHOST(ch) ) { if( ch->death_cnt > 0 ) ch->death_cnt--; if( ch->death_cnt == 0 ) { char_from_room(ch); char_to_room(ch,get_room_index(ROOM_VNUM_ALTAR)); send_to_char("The gods have taken pity upon your ghostly plight...\r\n",ch); resurrect(ch); do_mpcr(ch,ch->name); do_look(ch,"auto"); } } if( !IS_NPC( ch ) && IS_WOLF( ch ) ) gain_rage( ch ); /* * Find dude with oldest save time. */ if( !IS_NPC( ch ) && ( ch->desc == NULL || ch->desc->connected == CON_PLAYING ) && ch->level >= 2 && ch->save_time < save_time ) { ch_save = ch; save_time = ch->save_time; } if( ( IS_NPC( ch ) ) && ( ch->hit < -15 ) ) raw_kill( ch, "" ); if( ch->sitting != NULL && ch->sitting->in_room != ch->in_room ) { ch->sitting->value[1]--; ch->sitting = NULL; } /* Update mquest status */ update_mquest_wait_time(ch); if( ch->position >= POS_STUNNED ) { /* * -S- mod. */ if( ch->position != POS_WRITING && ch->position != POS_BUILDING ) { if( ch->in_room->room_flags.test(RFLAG_HOT) ) send_to_char( "You feel your skin burning.\r\n", ch ); else if( ch->in_room->room_flags.test(RFLAG_COLD) ) send_to_char( "You feel your skin freezing.\r\n", ch ); } } if( ( !IS_NPC( ch ) && ch->level < LEVEL_IMMORTAL ) ) { OBJ_DATA *obj; if( ( obj = get_eq_char( ch, WEAR_LIGHT ) ) != NULL && obj->item_type == ITEM_LIGHT && obj->value[2] > 0 ) { if( --obj->value[2] == 0 && ch->in_room != NULL ) { --ch->in_room->light; act( "$p goes out.", ch, obj, NULL, TO_ROOM ); act( "$p goes out.", ch, obj, NULL, TO_CHAR ); if( obj->value[7] > 0 ) { OBJ_INDEX_DATA *new_index; OBJ_DATA *replacer; if( ( new_index = get_obj_index( obj->value[6] ) ) == NULL ) { snprintf( bug_buf, (2 * MIL), "ERROR in expiring item %s(%s %d): item has a replace vnum set (%d), but that is not a valid item.", obj->name, obj->pIndexData->area->keyword, obj->pIndexData->vnum, obj->value[6] ); monitor_chan( bug_buf, MONITOR_OBJ ); log_f( "%s", bug_buf ); } else { replacer = create_object( new_index, obj->level ); if( obj->carried_by != NULL ) obj_to_char( replacer, obj->carried_by ); else if( obj->in_room != NULL ) obj_to_room( replacer, obj->in_room ); else if( obj->in_obj != NULL ) obj_to_obj( replacer, obj->in_obj ); } } extract_obj( obj ); } } if( ++ch->timer >= 12 ) { if( ch->was_in_room == NULL && ch->in_room != NULL ) { ch->was_in_room = ch->in_room; if( ch->fighting != NULL ) stop_fighting( ch, TRUE ); act( "$n disappears into the void.", ch, NULL, NULL, TO_ROOM ); send_to_char( "You disappear into the void.\r\n", ch ); save_char_obj( ch ); char_from_room( ch ); char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) ); } } if( ch->timer > 30 ) ch_quit = ch; /* * Move this inside the if loop below to stop imms getting bloodlust */ if( ( IS_VAMP( ch ) ) && ( !IS_NPC( ch ) ) ) { gain_bloodlust( ch, 0 - number_range( 1, 2 ) ); check_vamp( ch ); } if( !check_charm_aff(ch,CHARM_AFF_HUNGERLESS) ) gain_condition( ch, COND_THIRST, 0 - number_range( 1, 2 ) ); if( ch->pcdata->condition[COND_THIRST] <= 10 ) ch->pcdata->condition[COND_THIRST] = 10; gain_condition( ch, COND_DRUNK, 0 - number_range( 1, 2 ) ); if( !IS_VAMP( ch ) ) { if( !check_charm_aff(ch,CHARM_AFF_HUNGERLESS) ) gain_condition( ch, COND_FULL, 0 - number_range( 1, 2 ) ); } } for( paf = ch->first_affect; paf != NULL; paf = paf_next ) { paf_next = paf->next; if( paf->duration > 0 ) { paf->duration--; /* * We need a check here for spells that keep working... */ if( paf->type == skill_lookup( "blood leach" ) ) { if( paf->caster != NULL && !IS_NPC( paf->caster ) ) { send_to_char( "You feel the blood leach sapping your strength.\r\n", ch ); act( "You feel a surge of blood, coming from your blood leach on $N.", paf->caster, NULL, ch, TO_CHAR ); paf->caster->pcdata->super->energy += ( 10 - paf->caster->pcdata->super->generation ); if( paf->caster->pcdata->super->energy > paf->caster->pcdata->super->energy_max ) paf->caster->pcdata->super->energy = paf->caster->pcdata->super->energy_max; damage( ch, ch, paf->caster->pcdata->super->level * 20, TYPE_UNDEFINED ); } } if( paf->type == skill_lookup( "black hand" ) ) { if( paf->caster != NULL && !IS_NPC( paf->caster ) ) { send_to_char( "You feel the Black Hand choking you.\r\n", ch ); ch->hit -= paf->modifier; } } if( ( paf->type == skill_lookup( "adrenaline bonus" ) ) && ( ch->fighting == NULL ) && ( ch->hit > 10 ) ) { ch->hit = UMAX( 10, ( ch->hit - ( paf->duration * 30 ) ) ); ch->move = UMAX( 10, ( ch->move - ( paf->duration * 80 ) ) ); send_to_char( "@@NYou feel the affects of your @@eadrenaline rush@@N wear off, leaving you exhausted.\r\n", ch ); affect_remove( ch, paf ); } } else if( paf->duration < 0 ) ; else { if( paf_next == NULL || paf_next->type != paf->type || paf_next->duration > 0 ) { if( paf->type > 0 && skill_table[paf->type].msg_off ) { send_to_char( skill_table[paf->type].msg_off, ch ); send_to_char( "\r\n", ch ); } if( paf->type > 0 && skill_table[paf->type].room_off ) act( skill_table[paf->type].room_off, ch, NULL, NULL, TO_ROOM ); } affect_remove( ch, paf ); } } /* * Careful with the damages here, * MUST NOT refer to ch after damage taken, * as it may be lethal damage (on NPC). */ if( IS_NPC( ch ) ) { if( ch->target != NULL && number_bits( 4 ) == 0 ) { free_string( ch->target ); ch->target = NULL; } if( ch->extract_timer > 0 ) { ch->extract_timer--; } else if( ch->extract_timer == 0 ) { /* if ( IS_SET( ch->affected_by, AFF_CHARM ) ) { */ if( ( ch->master == NULL ) || ( ch->master->in_room == NULL ) || ( ch->in_room != ch->master->in_room ) ) { if( ch->in_room != NULL ) { do_say( ch, "Whaa?? Where am I? How did I get here?" ); do_say( ch, "AHHH!!! Help me!!!! I'm MELTING......" ); } extract_char( ch, TRUE ); continue; } else { if( number_range( 0, 99 ) < get_psuedo_level( ch->master ) - 25 ) { CHAR_DATA *this_master; this_master = ch->master; do_say( ch, "Whaa?? Where am I? How did I get here?" ); do_scan( ch, "" ); check_social( ch, "growl", ch->master->name ); do_say( ch, "How dare you order me around!!!" ); stop_follower( ch ); one_hit( ch, this_master, TYPE_UNDEFINED ); continue; } } /* * } */ } } if( IS_AFFECTED( ch, AFF_POISON ) ) { act( "$n shivers and suffers.", ch, NULL, NULL, TO_ROOM ); send_to_char( "You shiver and suffer.\r\n", ch ); damage( ch, ch, number_range( 2, 8 ), gsn_poison ); } else if( ch->position == POS_INCAP && !IS_VAMP( ch ) ) { damage( ch, ch, number_range( 1, 4 ), TYPE_UNDEFINED ); } else if( ch->position == POS_MORTAL && !IS_VAMP( ch ) ) { damage( ch, ch, number_range( 2, 3 ), TYPE_UNDEFINED ); } else if( ch->position == POS_DEAD && !IS_VAMP( ch ) ) { damage( ch, ch, number_range( 5, 10 ), TYPE_UNDEFINED ); } else if( ch->hit < -10 && !IS_VAMP( ch ) ) { damage( ch, ch, number_range( 5, 10 ), TYPE_UNDEFINED ); } } /* * Autosave and autoquit. * Check that these chars still exist. */ if( ch_save != NULL ) save_char_obj(ch_save); if( ch_quit != NULL ) { send_to_char("Idle for too long. Bye bye!\r\n",ch_quit); do_quit(ch_quit,""); } return; } void check_vamp( CHAR_DATA * ch ) { /* * If vampire is outside, then (s)he suffers damage */ if( is_affected( ch, skill_lookup( "cloak:darkness" ) ) ) return; if( IS_OUTSIDE( ch ) && !IS_SET( ch->in_room->affected_by, ROOM_BV_SHADE ) && time_info.hour > 5 && time_info.hour < 19 ) { /* * Oh dear */ int dam; switch ( weather_info.sky ) { case SKY_CLOUDLESS: dam = 4; break; case SKY_CLOUDY: dam = 3; break; case SKY_RAINING: dam = 3; break; default: dam = 1; break; } /* * Take bloodlust into account when calculating dam! */ dam *= 40 - ch->pcdata->super->level; /* * So dam ranges from 2 (lightning;no bloodlust) * * to 200 (sunny;complete bloodlust) * * And that's each tick! */ act( "$n's skin burns with it's contact with daylight!", ch, NULL, NULL, TO_ROOM ); send_to_char( "Your skin burns with it's contact with daylight!", ch ); if( ch->pcdata->super->energy <= -5 ) return; damage( ch, ch, dam, -1 ); } return; } /* Check for objfuns.... this is probably performance sensitive too. */ void objfun_update( void ) { OBJ_DATA *obj; std::list<OBJ_DATA *>::iterator li; for( li = obj_list.begin(); li != obj_list.end(); li++ ) { obj = *li; if( obj->obj_fun != NULL ) { if( obj->carried_by != NULL ) { if( !IS_NPC( obj->carried_by ) && IS_WOLF( obj->carried_by ) && ( IS_SHIFTED( obj->carried_by ) || IS_RAGED( obj->carried_by ) ) ) { continue; } } ( *obj->obj_fun ) ( obj, obj->carried_by ); } } return; } /* * Update all objs. * This function is performance sensitive. */ void obj_update( void ) { OBJ_DATA *obj; std::list<OBJ_DATA *>::iterator li; /* * Repeatedly remove object from front of list, add to tail, and process * until the marker is at the head of the list. That will indicate all * objects have been processed. */ disable_timer_abort = FALSE; for( li = obj_list.begin(); li != obj_list.end(); li++ ) { CHAR_DATA *rch; char *message; obj = *li; monitor_chan(obj->name,MONITOR_OBJ); if( obj == auction_item ) continue; if( IS_SET( obj->item_apply, ITEM_APPLY_HEATED ) && number_range( 0, 100 ) < 25 ) { REMOVE_BIT( obj->item_apply, ITEM_APPLY_HEATED ); if( obj->carried_by != NULL ) { act( "Your $p @@acools off@@N!!", obj->carried_by, obj, NULL, TO_CHAR ); } else if( obj->in_room != NULL && ( rch = obj->in_room->first_person ) != NULL ) { act( "$p @@acools off@@N!!", rch, obj, NULL, TO_ROOM ); act( "$p @@acools off@@N!!", rch, obj, NULL, TO_CHAR ); } } if( obj->timer <= 0 || --obj->timer > 0 ) continue; switch ( obj->item_type ) { default: message = "$p vanishes."; break; case ITEM_FOUNTAIN: message = "$p dries up."; break; case ITEM_CORPSE_NPC: message = "$p decays into dust."; break; case ITEM_CORPSE_PC: message = "$p vapourises, and goes to heaven."; break; case ITEM_PORTAL: message = "$p implodes suddenly."; break; case ITEM_FOOD: message = "$p decomposes."; break; } if( obj->carried_by != NULL ) { act( message, obj->carried_by, obj, NULL, TO_CHAR ); } else if( obj->in_room != NULL && ( rch = obj->in_room->first_person ) != NULL ) { act( message, rch, obj, NULL, TO_ROOM ); act( message, rch, obj, NULL, TO_CHAR ); } /* if ( obj->in_room == NULL ) continue; if ( obj->item_type == ITEM_CORPSE_NPC ) continue; */ if( obj->value[7] > 0 ) { OBJ_INDEX_DATA *new_index; OBJ_DATA *replacer; if( ( new_index = get_obj_index( obj->value[6] ) ) == NULL ) { snprintf( bug_buf, (2 * MIL), "ERROR in expiring item %s(%s %d): item has a replace vnum set (%d), but that is not a valid item.", obj->name, obj->pIndexData->area->keyword, obj->pIndexData->vnum, obj->value[6] ); monitor_chan( bug_buf, MONITOR_OBJ ); log_f( "%s", bug_buf ); } else { replacer = create_object( new_index, obj->level ); if( obj->carried_by != NULL ) obj_to_char( replacer, obj->carried_by ); else if( obj->in_room != NULL ) obj_to_room( replacer, obj->in_room ); else if( obj->in_obj != NULL ) obj_to_obj( replacer, obj->in_obj ); } } extract_obj( obj ); } disable_timer_abort = FALSE; return; } /* * Aggress. * * for each mortal PC * for each mob in room * aggress on some random PC * * This function takes 25% to 35% of ALL Merc cpu time. * Unfortunately, checking on each PC move is too tricky, * because we don't the mob to just attack the first PC * who leads the party into the room. * * -- Furey */ void aggr_update( void ) { /* * Check to see if ch has encountered a mob with ACT_REMEMBER set, * * and with victim->target == ch->name... tbc ;) * * -- Stephen */ CHAR_DATA *wch; CHAR_DATA *ch; CHAR_DATA *ch_next; CHAR_DATA *vch; CHAR_DATA *vch_next; CHAR_DATA *victim; OBJ_DATA *wield; std::list<CHAR_DATA *>::iterator li; for( li = char_list.begin(); li != char_list.end(); li++ ) { wch = *li; if( wch == NULL ) continue; if( ( IS_NPC( wch ) ) || wch->level >= LEVEL_IMMORTAL || wch->in_room == NULL ) continue; for( ch = wch->in_room->first_person; ch != NULL; ch = ch_next ) { int count; ch_next = ch->next_in_room; if( !IS_NPC( ch ) || !ch->act.test(ACT_AGGRESSIVE) || ch->fighting != NULL || ch->hunting != NULL || IS_AFFECTED( ch, AFF_CHARM ) || !IS_AWAKE( ch ) || ( ch->act.test(ACT_WIMPY) && IS_AWAKE( wch ) ) || !can_see( ch, wch ) || ch->in_room->area->player_list.empty() ) continue; if( ( IS_AFFECTED( wch, AFF_SNEAK ) || item_has_apply( wch, ITEM_APPLY_SNEAK ) ) && ( number_percent( ) < 50 + ( 2 * ( get_psuedo_level( wch ) - get_psuedo_level( ch ) ) ) ) ) continue; /* * Ok we have a 'wch' player character and a 'ch' npc aggressor. * MAG - wch can be an intelligent NPC. * Now make the aggressor fight a RANDOM pc victim in the room, * giving each 'vch' an equal chance of selection. */ count = 0; victim = NULL; for( vch = wch->in_room->first_person; vch != NULL; vch = vch_next ) { vch_next = vch->next_in_room; if( ( !IS_NPC( vch ) || vch->act.test(ACT_INTELLIGENT) ) && vch->level < LEVEL_IMMORTAL && ( !ch->act.test(ACT_WIMPY) || !IS_AWAKE( vch ) ) && can_see( ch, vch ) && ( !( IS_UNDEAD( ch ) && IS_VAMP( vch ) ) ) ) { if( number_range( 0, count ) == 0 ) victim = vch; count++; } } if( victim == NULL ) { /* * bug( "Aggr_update: null victim.", count ); */ continue; } if( victim->in_room->room_flags.test(RFLAG_SAFE) ) continue; if( ch == quest_mob ) /* Stop the quest mob from fighting folks trying to help it! --Kline */ continue; act( "$n growls at $N!", victim, NULL, ch, TO_NOTVICT ); act( "$N growls at you! Uh-oh!!", victim, NULL, ch, TO_CHAR ); act( "You growl at $N. Get $M!!", ch, NULL, victim, TO_CHAR ); wield = get_eq_char( ch, WEAR_HOLD_HAND_L ); if( wield != NULL && wield->item_type == ITEM_WEAPON && wield->value[3] == 11 && victim->fighting == NULL ) do_backstab( ch, victim->name ); else one_hit( ch, victim, TYPE_UNDEFINED ); } } return; } /* * Check ALL rooms for affects... the ratio of affects to rooms should * be relatively low, so this shouldn't hit performance too much. * -S- */ void rooms_update( void ) { ROOM_INDEX_DATA *room; AREA_DATA *area; BUILD_DATA_LIST *thing; ROOM_AFFECT_DATA *raf; ROOM_AFFECT_DATA *raf_next; std::list<AREA_DATA *>::iterator li; std::list<MARK_DATA *>::iterator mi; for( li = area_list.begin(); li != area_list.end(); li++ ) { area = *li; for( thing = area->first_area_room; thing != NULL; thing = thing->next ) { room = (ROOM_INDEX_DATA *)thing->data; /* * if ( room->first_room_affect == NULL ) * continue; */ if( !room->mark_list.empty() ) { for( mi = room->mark_list.begin(); mi != room->mark_list.end(); mi++ ) { static_cast<MARK_DATA *>(*mi)->duration--; if( static_cast<MARK_DATA *>(*mi)->duration <= 0 ) mi = room->mark_list.erase(mi); } save_marks(); } for( raf = room->first_room_affect; raf != NULL; raf = raf_next ) { raf_next = raf->next; if( raf->duration > 0 ) raf->duration--; else if( raf->duration < 0 ) ; else { if( raf_next == NULL || raf_next->type != raf->type || raf_next->duration > 0 ) { if( raf->type > 0 && skill_table[raf->type].msg_off ) { send_to_room( skill_table[raf->type].msg_off, room ); send_to_room( "\r\n", room ); } r_affect_remove( room, raf ); } } } } } return; } /* * Handle all kinds of updates. * Called once per pulse from game loop. * Random times to defeat tick-timing clients and players. */ void update_handler( void ) { static int pulse_message; static int objfun_check; static int pulse_area; static int pulse_rooms; static int pulse_mobile; static int pulse_gain; static int pulse_violence; static int pulse_combat; static int pulse_point; static int pulse_auction; if( saving_area ) build_save( ); /* For incremental area saving */ if( --pulse_area <= 0 ) { pulse_area = number_range( PULSE_AREA / 2, 3 * PULSE_AREA / 2 ); area_update( ); build_save_flush( ); } if( --pulse_rooms <= 0 ) { pulse_rooms = PULSE_ROOMS; rooms_update( ); } if( --pulse_message <= 0 ) { pulse_message = PULSE_MESSAGE; message_update( ); } if( auction_flop ) { pulse_auction = PULSE_AUCTION; auction_flop = FALSE; } if( --pulse_auction <= 0 ) { pulse_auction = PULSE_AUCTION; auction_update( ); } if( --objfun_check <= 0 ) { objfun_check = PULSE_OBJFUN; objfun_update( ); } if( --pulse_violence <= 0 ) { alarm_update( ); pulse_violence = PULSE_VIOLENCE; violence_update( ); } if( --pulse_combat <= 0 ) { pulse_combat = PULSE_COMBAT; combat_update( ); } if( --pulse_mobile <= 0 ) { pulse_mobile = PULSE_MOBILE; mobile_update( ); } if( --pulse_gain <= 0 ) { gain_update( ); pulse_gain = PULSE_PER_SECOND * number_range( 5, 8 ); } if( --pulse_point <= 0 ) { pulse_point = PULSE_TICK; weather_update( ); char_update( ); obj_update( ); quest_update( ); /* * This will log the number of perms being used... * * fgrep the log file to get results... */ /* * perm_update( ); */ } aggr_update( ); tail_chain( ); return; } bool check_rewield( CHAR_DATA * ch ) { OBJ_DATA *obj; OBJ_DATA *weapon = NULL; int dam; int chance; bool pickup; char buf[MAX_STRING_LENGTH]; pickup = TRUE; dam = 0; chance = ( ch->fighting == NULL ? 35 : 60 ); if( number_percent( ) < chance ) { for( obj = ch->first_carry; obj != NULL; obj = obj->next_in_carry_list ) if( obj->item_type == ITEM_WEAPON && dam < obj->value[2] ) { dam = obj->value[2]; pickup = FALSE; weapon = obj; } /* * Then check inventory and room for any weapons */ for( obj = ch->in_room->first_content; obj != NULL; obj = obj->next_in_room ) { if( obj->item_type == ITEM_WEAPON ) { if( obj->value[2] > dam ) { dam = obj->value[2]; weapon = obj; pickup = TRUE; } } } if( weapon == NULL ) return FALSE; if( weapon->wear_loc == WEAR_HOLD_HAND_L ) return FALSE; if( pickup ) { snprintf( buf, MSL, "Great! %s! Just what i've always wanted!", weapon->short_descr ); do_say( ch, buf ); } if( weapon != NULL ) { /* * Now make the mob get the weapon */ if( pickup ) get_obj( ch, weapon, NULL ); do_wear( ch, weapon->name ); /* * Check is mob wielded weapon ok... */ if( weapon->wear_loc == WEAR_NONE && weapon != quest_object ) { act( "$n sniffs sadly. 'Baah! It's no good to me!'", ch, NULL, NULL, TO_ROOM ); extract_obj( weapon ); act( "$n sacrifices $p.", ch, weapon, NULL, TO_ROOM ); } return TRUE; } } return FALSE; } bool check_re_equip( CHAR_DATA * ch ) { OBJ_DATA *obj; OBJ_DATA *obj2; OBJ_DATA *armor = NULL; OBJ_DATA *light = NULL; int ac; int chance; bool pickup; bool ident; int best; char buf[MAX_STRING_LENGTH]; int objnum; best = -1; pickup = TRUE; ac = 0; chance = ( ch->fighting == NULL ? 35 : 60 ); if( number_percent( ) < chance ) { /* * Check each armor in room against ch's equipment */ ident = FALSE; for( obj = ch->in_room->first_content; obj != NULL; obj = obj->next_in_room ) { if( !can_see_obj( ch, obj ) ) continue; if( !CAN_WEAR(obj,ITEM_TAKE) ) continue; /* Check to see if item cannot be worn */ if( obj->item_type == ITEM_PIECE ) continue; if( obj->item_type == ITEM_ARMOR ) { /* * Check this object against our equiped objects */ ident = FALSE; for( obj2 = ch->first_carry; obj2 != NULL; obj2 = obj2->next_in_carry_list ) { /* * Only scan against worn objects. * * If obj2 is being worn in a position that obj can be worn in, * * and obj2->value[0] is better, then choose it. */ if( obj2->wear_loc != WEAR_NONE && obj2->item_type == ITEM_ARMOR && can_wear_at( ch, obj, obj2->wear_loc ) && obj->value[0] > obj2->value[0] ) { ident = TRUE; /* identical wear_loc */ armor = obj; break; } } /* * Found no match for locations, so get and wear. */ if( !ident ) { armor = obj; break; } } if( obj->item_type == ITEM_LIGHT && ( get_eq_char( ch, WEAR_LIGHT ) == NULL ) ) { light = obj; break; } } /* * MAG Modification. Only check one item each time, against currently * worn object. */ /* * Check one inv item against worn eq, incase we've picked up some nicer * stuff */ objnum = number_percent( ) * ch->carry_number / 100; for( obj = ch->first_carry; obj != NULL && objnum > 0; obj = obj->next_in_carry_list ) objnum--; if( obj != NULL && obj->wear_loc == WEAR_NONE && obj->item_type == ITEM_ARMOR ) { ident = FALSE; for( obj2 = ch->first_carry; obj2 != NULL; obj2 = obj2->next_in_carry_list ) { if( obj2->wear_loc != WEAR_NONE && can_wear_at( ch, obj, obj2->wear_loc ) && obj->value[0] > obj2->value[0] ) { ident = TRUE; armor = obj; break; } } if( !ident ) { armor = obj; } } if( obj != NULL && obj->item_type == ITEM_LIGHT && ( get_eq_char( ch, WEAR_LIGHT ) == NULL ) ) { light = obj; } } if( armor != NULL ) { if( armor->carried_by != ch ) { /* * Pick up off ground */ if( pickup ) { snprintf( buf, MSL, "Great! %s! Just what i've always wanted!", armor->short_descr ); do_say( ch, buf ); } /* * Now make the mob get the armor */ if( pickup ) get_obj( ch, armor, NULL ); } do_wear( ch, armor->name ); /* * Check is mob wielded weapon ok... */ if( armor->wear_loc == WEAR_NONE && armor != quest_object ) { act( "$n sniffs sadly. 'Baah! It's no good to me!'", ch, NULL, NULL, TO_ROOM ); extract_obj( armor ); act( "$n sacrifices $p.", ch, armor, NULL, TO_ROOM ); } return TRUE; } if( light != NULL ) { if( light->carried_by != ch ) { /* * Pick up off ground */ if( pickup ) { snprintf( buf, MSL, "Great! %s! Just what i've always wanted!", light->short_descr ); do_say( ch, buf ); } /* * Now make the mob get the light */ if( pickup ) get_obj( ch, light, NULL ); } do_wear( ch, light->name ); /* * Check is mob wielded weapon ok... */ if( light->wear_loc == WEAR_NONE && light != quest_object ) { act( "$n sniffs sadly. 'Baah! It's no good to me!'", ch, NULL, NULL, TO_ROOM ); extract_obj( light ); act( "$n sacrifices $p.", ch, light, NULL, TO_ROOM ); } return TRUE; } return FALSE; } void auction_update( void ) { char buf[MAX_STRING_LENGTH]; std::list<CHAR_DATA *>::iterator li; CHAR_DATA *ach; bool good_seller = false, good_buyer = false; /* * Stages: 0) No/New bid. * 1) Waiting. (If no bid here, then give up next time) * 2) Going once. * 3) Going Twice. * 4) GONE! */ if( auction_item == NULL ) return; switch ( auction_stage ) { case 0: if( auction_bidder == NULL ) { snprintf( buf, MSL, "@@N%s (level:%d, valued at %s) has been offered for auction. A @@e10%% fee@@N will be charged, the higher of the reserve price or highest bid.", auction_item->short_descr, auction_item->level, cost_to_money( auction_item->cost ) ); } else { snprintf( buf, MSL, "%s has bid %s for %s.", auction_bidder->name, cost_to_money( auction_bid ), auction_item->short_descr ); } break; case 1: if( auction_bidder == NULL ) snprintf( buf, MSL, "Last chance to bid for %s.", auction_item->short_descr ); else snprintf( buf, MSL, "Last bid for %s was %s. Any more offers?", auction_item->short_descr, cost_to_money( auction_bid ) ); break; case 2: if( auction_bidder == NULL ) { auction( "No bidders. Auction Ended." ); for( li = char_list.begin(); li != char_list.end(); li++ ) { ach = *li; if( auction_owner == ach ) good_seller = TRUE; if( auction_bidder == ach ) good_buyer = TRUE; } if( good_seller ) { int bid; char changebuf[MSL]; char *change; bid = UMIN( money_value( auction_owner->money ), static_cast<int>(auction_reserve * .1) ); change = take_best_coins( auction_owner->money, bid ); change = one_argument( change, changebuf ); money_to_value( auction_owner, change ); join_money( round_money( atoi( change ), TRUE ), auction_owner->money ); obj_to_char( auction_item, auction_owner ); } else { auction( "Oh, well..guess they didn't want it anymore, since they LEFT!! Well, it's mine now! " ); extract_obj( auction_item ); } auction_item = NULL; return; } snprintf( buf, MSL, "%s - Going Once!", auction_item->short_descr ); break; case 3: snprintf( buf, MSL, "%s - Going TWICE!", auction_item->short_descr ); break; case 4: if( auction_bid < auction_reserve ) { for( li = char_list.begin(); li != char_list.end(); li++ ) { ach = *li; if( auction_owner == ach ) good_seller = TRUE; if( auction_bidder == ach ) good_buyer = TRUE; } snprintf( buf, MSL, "%s - CANCELLED. Reserve price not matched.", auction_item->short_descr ); if( good_seller ) { int bid; char changebuf[MSL]; char *change; bid = UMIN( money_value( auction_owner->money ), static_cast<int>(auction_reserve * .1) ); change = take_best_coins( auction_owner->money, bid ); change = one_argument( change, changebuf ); money_to_value( auction_owner, change ); join_money( round_money( atoi( change ), TRUE ), auction_owner->money ); obj_to_char( auction_item, auction_owner ); } else extract_obj( auction_item ); if( good_buyer ) join_money( round_money( auction_bid, TRUE ), auction_bidder->money ); } else { for( li = char_list.begin(); li != char_list.end(); li++ ) { ach = *li; if( auction_owner == ach ) good_seller = TRUE; if( auction_bidder == ach ) good_buyer = TRUE; } if( good_buyer ) { snprintf( buf, MSL, "%s - SOLD! to %s.", auction_item->short_descr, auction_bidder->name ); obj_to_char( auction_item, auction_bidder ); } else { snprintf( buf, MSL, "%s - SOLD!, but the buyer has left us. Oh Well!!!", auction_item->short_descr ); extract_obj( auction_item ); } if( good_seller ) join_money( round_money( static_cast<int>(auction_bid - ( auction_bid * .1 )), TRUE ), auction_owner->money ); } auction_stage = 0; auction_bidder = NULL; auction_owner = NULL; auction_item = NULL; auction_reserve = 0; auction_bid = 0; break; } auction( buf ); auction_stage++; return; } void remember_attack( CHAR_DATA * ch, CHAR_DATA * victim ) { /* * Called when an NPC ch encounters a PC victim, that tried to * * kill it previously. * * --Stephen */ char buf[MAX_STRING_LENGTH]; /* * Pick a random response for ch to give, before attacking */ switch ( number_range( 0, 7 ) ) { case 0: snprintf( buf, MSL, "%s returns! I shall have my revenge at last!", victim->name ); do_yell( ch, buf ); break; case 1: snprintf( buf, MSL, "%s You should never have returned. Ye shall DIE!", victim->name ); do_whisper( ch, buf ); break; case 2: act( "$n looks at $N, remembering $S attack", ch, NULL, victim, TO_ROOM ); act( "$n looks at you, remembering your attack", ch, NULL, victim, TO_VICT ); act( "You look at $N, remembering $S attack.", ch, NULL, victim, TO_CHAR ); do_say( ch, "I SHALL HAVE MY REVENGE!!!" ); break; case 3: snprintf( buf, MSL, "%s has wronged me, and now I will seek my revenge!", victim->name ); do_gossip( ch, buf ); snprintf( buf, MSL, "Prepare to die, %s.", victim->name ); do_say( ch, buf ); break; case 4: snprintf( buf, MSL, "So, %s. You have returned. Let us finish our fight this time!", victim->name ); do_say( ch, buf ); break; case 5: snprintf( buf, MSL, "Only cowards flee from me, %s!", victim->name ); do_say( ch, buf ); break; case 6: act( "$n looks at $N, and recognizes $M!!", ch, NULL, victim, TO_ROOM ); act( "$n looks at you, and recognizes you!!", ch, NULL, victim, TO_VICT ); act( "You look at $N, and recognize $M!", ch, NULL, victim, TO_CHAR ); snprintf( buf, MSL, "There can only be one winner, %s.", victim->name ); do_say( ch, buf ); break; } /* * Check if has intelligence, and call correct attack? */ one_hit( ch, victim, TYPE_UNDEFINED ); /* * spec- plug leak here */ if( ch->target ) { free_string( ch->target ); ch->target = NULL; } return; } void quest_update( ) { if( !quest && !auto_quest ) return; if( quest ) { /* * Make sure the mobile and obj still exist! */ if( quest_mob == NULL || quest_object == NULL ) { quest_cancel( ); return; } quest_inform( ); if( quest_timer > 15 ) quest_cancel( ); return; } if( !quest ) { if( quest_wait > 0 ) { quest_wait--; return; } if( auto_quest ) generate_auto_quest( ); } }