/*************************************************************************** * 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. * * * * 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 Dooley 1994 * * _/_/_/_/ _/ _/ _/ "This mud has not been * * _/ _/ _/_/_/ _/ _/ _/ tested on animals." * * * * * * 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. * ***************************************************************************/ #if defined(macintosh) #include <types.h> #else #include <sys/types.h> #endif #include <stdio.h> #include <string.h> #include <time.h> #include "merc.h" #include <sys/resource.h> #include <signal.h> extern POL_DATA politics_data; extern OBJ_DATA * quest_object; extern COUNCIL_DATA super_councils[MAX_SUPER]; /* * Local functions. */ int hit_gain args( ( CHAR_DATA *ch ) ); int mana_gain args( ( CHAR_DATA *ch ) ); int move_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 auction_update args( ( void ) ); void rooms_update args( ( void ) ); void remember_attack args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void quest_update args( ( void ) ); 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 () { extern int ssm_dup_count; extern int ssm_loops; ssm_dup_count=0; ssm_loops=0; last_checkpoint = get_user_seconds(); if (abort_threshold == BOOT_DB_ABORT_THRESHOLD) { abort_threshold = RUNNING_ABORT_THRESHOLD; fprintf (stderr, "Used %d user CPU seconds.\n", last_checkpoint); } } /* 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); } } 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]; extern int ssm_dup_count; extern int ssm_loops; extern int ssm_recent_loops; /* spec: log usage values */ logf("current usage: %d, last checkpoint: %d", usage_now, last_checkpoint); logf("SSM dups: %d, loops: %d, recent: %d", ssm_dup_count, ssm_loops, ssm_recent_loops); sprintf(buf, "%s\n\r", 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. */ } /* 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 */ } /* * Advancement stuff. */ void advance_level( CHAR_DATA *ch, int class, bool show, bool remort ) { /* class used instead of ch->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 ( class == ADVANCE_WOLF ) { add_bloodlust = ( number_range( 1, ( ( MAX_WOLF_LEVEL / 2 ) - ch->pcdata->generation ) ) ) + ( ( ( MAX_WOLF_LEVEL / 2) - ch->pcdata->generation ) /2 ); add_prac = number_range( 1, UMAX( 2, ( ( MAX_WOLF_LEVEL / 2 ) - ch->pcdata->generation ) ) ); add_max_skills = add_prac; ch->pcdata->bloodlust_max += add_bloodlust; ch->pcdata->vamp_pracs += add_prac; ch->pcdata->vamp_skill_max += add_max_skills; sprintf( buf, "@@NYou gain: %d @@rRage Ability@@N, and %d @@bWerewolf Practices. .@@N\n\r", add_bloodlust, add_prac ); send_to_char( buf, ch ); return; } if ( ( class == 16 ) ) { add_bloodlust = UMAX( ( ( MAX_VAMP_LEVEL / 2) - ( ch->pcdata->generation / 2 ) ), 1 ); add_prac =number_range( 1, UMAX( 2, ( ( MAX_VAMP_LEVEL / 2 ) - (ch->pcdata->generation ) ) ) ); add_max_skills = number_range( 1, UMAX( 2, ( ( MAX_VAMP_LEVEL / 2 ) - (ch->pcdata->generation ) ) ) ); ch->pcdata->bloodlust_max += add_bloodlust; ch->pcdata->vamp_pracs += add_prac; ch->pcdata->vamp_skill_max += add_max_skills; sprintf( buf, "You gain: %d @@eBloodlust@@N, and %d Vampyre Practices. .\n\r", add_bloodlust, add_prac ); send_to_char( buf, ch ); return; } if ( ( 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[class].hp_min, remort_table[class].hp_max ); add_mana = remort_table[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[class].hp_min, class_table[class].hp_max ); add_mana = class_table[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) ) REMOVE_BIT( ch->act, PLR_BOUGHT_PET ); sprintf( buf, "You gain: %d Hit Points, %d Mana, %d Movement and %d pracs.\n\r", add_hp, add_mana, add_move, add_prac ); if ( show ) send_to_char( buf, ch ); return; } void gain_exp( CHAR_DATA *ch, long_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) ) && !( IS_SET( ch->act, 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 ) { int gain; if ( IS_NPC(ch) && !IS_SET( ch->act, ACT_INTELLIGENT ) ) gain = ( 5 + ch->level/30); gain = ( 5 + ch->level/20 ); if ( IS_SET( ch->in_room->room_flags, ROOM_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->bloodlust < 3 ) gain = 0; else if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 8 ) gain /= 2; if ( IS_VAMP( ch ) && ch->pcdata->bloodlust == -10 ) gain = ( 5 + ch->level/25 ); } if ( IS_AFFECTED(ch, AFF_POISON) ) gain /= 4; if ( IS_SET( ch->in_room->room_flags, ROOM_COLD ) || ( IS_SET( ch->in_room->room_flags, ROOM_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 ( 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(gain, ch->max_hit - ch->hit); } int mana_gain( CHAR_DATA *ch ) { int gain; if ( IS_NPC(ch) && !IS_SET( ch->act, ACT_INTELLIGENT ) ) { gain = ( 1 + ch->level / 30 ); } else { gain = ( 5 + ch->level / 20 ); if ( IS_SET( ch->in_room->room_flags, ROOM_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->bloodlust < 3 ) gain = 0; else if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 8 ) gain /= 2; if ( IS_VAMP( ch ) && ch->pcdata->bloodlust == -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 ( !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.25; } 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; return UMIN(gain, ch->max_mana - ch->mana); } int move_gain( CHAR_DATA *ch ) { int gain; if ( IS_NPC(ch) ) { gain = ch->level; } else { gain = ( 10 + ch->level/4 ); if ( IS_SET( ch->in_room->room_flags, ROOM_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->bloodlust < 3 ) gain = 0; else if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 8 ) gain /= 2; if ( IS_VAMP( ch ) && ch->pcdata->bloodlust == -10 ) gain = ( 5 + ch->level/25 ); } if ( IS_AFFECTED(ch, AFF_POISON) ) gain /= 4; return UMIN(gain, ch->max_move - ch->move); } void gain_rage( CHAR_DATA *ch ) { sh_int rage_gain = 0; sh_int current_rage = 0; if ( IS_NPC( ch ) || !IS_WOLF( ch ) ) return; if ( IS_RAGED( ch ) ) current_rage = ch->pcdata->bloodlust_max; else if ( IS_SHIFTED( ch ) ) current_rage = UMAX( 1, ( ch->pcdata->bloodlust_max / 5 ) ); else current_rage = UMAX( 1, ( ch->pcdata->bloodlust_max / 10 ) ); rage_gain = number_range( 1, ( MAX_WOLF_LEVEL/2 - ch->pcdata->generation ) ); if ( ch->pcdata->bloodlust >= current_rage ) ch->pcdata->bloodlust = UMIN( ( ch->pcdata->bloodlust + rage_gain ), current_rage ); else ch->pcdata->bloodlust = UMIN( current_rage, ( ch->pcdata->bloodlust + 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->bloodlust; /* in case vamp bites off more than he can chew ;) -Damane- 4/26/96 */ if ( ( ch->pcdata->bloodlust + value ) > ch->pcdata->bloodlust_max ) ch->pcdata->bloodlust = ch->pcdata->bloodlust_max; else ch->pcdata->bloodlust += value; if ( ch->pcdata->bloodlust > ch->pcdata->bloodlust_max ) ch->pcdata->bloodlust = ch->pcdata->bloodlust_max; if ( ch->position == POS_BUILDING || ch->position == POS_WRITING ) return; if ( ( ch->pcdata->bloodlust < 0 ) && (ch->pcdata->bloodlust != -10 ) ) ch->pcdata->bloodlust = 0; if ( ch->pcdata->bloodlust < 2 ) send_to_char( "Your body burns with the need for blood!\n\r", ch ); else if ( ch->pcdata->bloodlust < 7 ) send_to_char( "You start to feel weaker... more blood needed!\n\r", ch ); else if ( ch->pcdata->bloodlust < 10 ) send_to_char( "You find yourself missing the taste of blood.\n\r", 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.\n\r", ch ); break; case COND_THIRST: send_to_char( "You are thirsty.\n\r", ch ); break; case COND_DRUNK: if ( condition != 0 ) send_to_char( "You are sober.\n\r", 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 *ch_next; CHAR_DATA *target; EXIT_DATA *pexit; int door; /* Examine all mobs. */ CREF( ch_next, CHAR_NEXT ); for ( ch = first_char; ch != NULL; ch = ch_next ) { ch_next = ch->next; if ( !IS_NPC(ch) || ch->in_room == NULL || IS_AFFECTED(ch, AFF_CHARM) ) continue; /* Examine call for special procedure */ if ( ch->spec_fun != 0 ) { if ( (*ch->spec_fun) ( ch ) ) continue; } /* DISABLED */ /* Intelligent mob? */ /* if ( IS_SET( ch->act, 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 ( IS_SET( ch->act, ACT_REWIELD ) ) if ( check_rewield( ch ) ) continue; if ( IS_SET( ch->act, 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; } */ /* MOBprogram random trigger */ if (ch->in_room->area->nplayer>0) { mprog_random_trigger(ch); /* If ch dies or changes position due to it's random trigger continue - Kahn */ if ( ch->position < POS_STANDING ) continue; } /* Scavenge */ if ( IS_SET(ch->act, 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 ( !IS_SET(ch->act, ACT_SENTINEL) && ch->leader == NULL && ( door = number_bits( 5 ) ) <= 5 && ( pexit = ch->in_room->exit[door] ) != NULL && pexit->to_room != NULL && !IS_SET(pexit->exit_info, EX_CLOSED) && !IS_SET(pexit->to_room->room_flags, ROOM_NO_MOB) && ( !IS_SET(ch->act, ACT_STAY_AREA) || pexit->to_room->area == ch->in_room->area ) ) { move_char( ch, door ); /* 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 && !IS_SET(pexit->exit_info, EX_CLOSED) && !IS_SET(pexit->to_room->room_flags, ROOM_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 ); } } CUREF( ch_next ); return; } /* * Update the weather. */ void weather_update( void ) { char buf[MAX_STRING_LENGTH]; char buf2[MSL]; DESCRIPTOR_DATA *d; int diff; sh_int x,y; buf[0] = '\0'; buf2[0] = '\0'; switch ( ++time_info.hour ) { case 5: weather_info.sunlight = SUN_LIGHT; strcat( buf, "The sky shows signs of daybreak.\n\r" ); break; case 6: weather_info.sunlight = SUN_RISE; strcat( buf, "The sun rises in the east.\n\r" ); 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; strcat( buf, "The sun slowly disappears in the west.\n\r" ); 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; strcat( buf, "The night has begun.\n\r" ); 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; break; } switch ( time_info.moon++ ) { case 5 : weather_info.moon_loc = MOON_RISE; sprintf( buf2, "@@NA %s @@yMoon @@Nhas risen.\n\r", get_moon_phase_name( ) ); safe_strcat( MSL, buf, buf2); break; case 10 : weather_info.moon_loc = MOON_LOW; sprintf( buf2, "@@NThe %s @@yMoon @@Nrides low on the horizon.\n\r", get_moon_phase_name( ) ); safe_strcat( MSL, buf, buf2); break; case 15 : weather_info.moon_loc = MOON_PEAK; sprintf( buf2, "@@NThe %s @@yMoon @@Nreaches it's zenith.\n\r", get_moon_phase_name( ) ); safe_strcat( MSL, buf, buf2); break; case 20 : weather_info.moon_loc = MOON_FALL; sprintf( buf2, "@@NThe %s @@yMoon @@Nfalls.\n\r", get_moon_phase_name( ) ); safe_strcat( MSL, buf, buf2); break; case 25 : weather_info.moon_loc = MOON_SET; sprintf( buf2, "@@NThe %s @@yMoon @@Nis setting.\n\r", get_moon_phase_name( ) ); safe_strcat( MSL, buf, buf2); break; case 30 : weather_info.moon_loc = MOON_DOWN; sprintf( buf2, "@@NThe %s @@yMoon @@Nhas left the sky.\n\r", get_moon_phase_name( ) ); safe_strcat( MSL, buf, buf2); 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 ) ) { strcat( buf, "The sky is getting cloudy.\n\r" ); weather_info.sky = SKY_CLOUDY; } break; case SKY_CLOUDY: if ( weather_info.mmhg < 970 || ( weather_info.mmhg < 990 && number_bits( 2 ) == 0 ) ) { strcat( buf, "It starts to rain.\n\r" ); weather_info.sky = SKY_RAINING; } if ( weather_info.mmhg > 1030 && number_bits( 2 ) == 0 ) { strcat( buf, "The clouds disappear.\n\r" ); weather_info.sky = SKY_CLOUDLESS; } break; case SKY_RAINING: if ( weather_info.mmhg < 970 && number_bits( 2 ) == 0 ) { strcat( buf, "Lightning flashes in the sky.\n\r" ); weather_info.sky = SKY_LIGHTNING; } if ( weather_info.mmhg > 1030 || ( weather_info.mmhg > 1010 && number_bits( 2 ) == 0 ) ) { strcat( buf, "The rain stopped.\n\r" ); weather_info.sky = SKY_CLOUDY; } break; case SKY_LIGHTNING: if ( weather_info.mmhg > 1010 || ( weather_info.mmhg > 990 && number_bits( 2 ) == 0 ) ) { strcat( buf, "The lightning has stopped.\n\r" ); 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; /* send wholist to web page :) Zen !-!-! ONLY RUN THIS ON ONE PORT OF YOUR SERVER !-! */ #if defined(SOE) && !defined(SOETEST) && !defined(SOEBLD) list_who_to_output(); #endif /* Update super_councils info */ { MEMBER_DATA * imember; sh_int count = 0; sh_int 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.\n\r", 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!\n\r", imember->this_member ); } } } } for ( ch = first_char; ch != NULL; ch = ch->next ) { if ( ch->position >= POS_STUNNED && !IS_SET(ch->affected_by,AFF_VAMP_HEALING) ) { if ( ( ch->hit < ch->max_hit ) && ( !IS_SET( ch->in_room->affected_by, ROOM_BV_NONE ) ) ) ch->hit += 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 += mana_gain(ch); if ( ( ch->move < ch->max_move ) && ( ch->carry_weight < can_carry_w( ch ) ) ) ch->move += move_gain(ch); else if ( ch->carry_weight >= can_carry_w( ch ) ) { send_to_char( "You are carrying so much wieght that you are @@eEXHAUSTED@@N!!\n\r", 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_next; CHAR_DATA *ch_save; CHAR_DATA *ch_quit; time_t save_time; save_time = current_time; ch_save = NULL; ch_quit = NULL; CREF( ch_next, CHAR_NEXT ); for ( ch = first_char; ch != NULL; ch = ch_next ) { AFFECT_DATA *paf; AFFECT_DATA *paf_next; if ( !IS_NPC( ch ) && IS_WOLF( ch ) ) gain_rage( ch ); ch_next = ch->next; /* * 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->pcdata->has_exp_fix == 0 ) && ( ch->level > 1 ) ) { send_to_char( "@@eWE HAVE CHANGED THE EXP SYSTEM. YOUR EXP HAS BEEN RESET TO 0.@@N\n\r", ch ); send_to_char( "@@ePLEASE CONACT AN IMMORTAL, AND YOU WILL BE ADVANCED ONE CLASS, UNLESS YOU ARE A NEW CHARACTER.@@N\n\r", ch ); send_to_char( "@@mTHANK YOU!!!!@@N NOTE: DO NOT ATTEMPT TO ABUSE THIS--WE ARE KEEPING TRACK!\n\r", ch ); ch->pcdata->has_exp_fix = 1; ch->exp = 0; } else if ( !IS_NPC( ch ) ) ch->pcdata->has_exp_fix = 1; 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; } if ( ch->position >= POS_STUNNED ) { /* -S- mod. */ if ( ch->position != POS_WRITING && ch->position != POS_BUILDING ) { if ( IS_SET( ch->in_room->room_flags, ROOM_HOT ) ) send_to_char( "You feel your skin burning.\n\r", ch ); else if ( IS_SET( ch->in_room->room_flags, ROOM_COLD ) ) send_to_char( "You feel your skin freezing.\n\r", ch ); } } /* if ( ch->stunTimer > 0 ) { ch->position = POS_STUNNED; ch->stunTimer -= 1; } else { ch->stunTimer = 0; ch->position = POS_STANDING; } */ 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 ); 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.\n\r", 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 ); } 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 ) ) { 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.\n\r", ch ); act( "You feel a surge of blood, coming from your blood leach on $N.", paf->caster, NULL, ch, TO_CHAR ); paf->caster->pcdata->bloodlust += ( 10 - paf->caster->pcdata->generation ); if ( paf->caster->pcdata->bloodlust > paf->caster->pcdata->bloodlust_max ) paf->caster->pcdata->bloodlust = paf->caster->pcdata->bloodlust_max; damage( ch, ch, paf->caster->pcdata->vamp_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.\n\r", 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.\n\r", 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( "\n\r", 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 ); multi_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.\n\r", 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 ); } } CUREF( ch_next ); /* * Autosave and autoquit. * Check that these chars still exist. */ if ( ch_save != NULL || ch_quit != NULL ) { CREF( ch_next, CHAR_NEXT ); for ( ch = first_char; ch != NULL; ch = ch_next ) { ch_next = ch->next; if ( ch == ch_save ) save_char_obj( ch ); if ( ch == ch_quit ) { send_to_char( "Idle for too long. Bye Bye!\n\r", ch ); do_quit( ch, "" ); } } CUREF( ch_next ); } 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->vamp_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->bloodlust <= -5 ) return; damage( ch, ch, dam, -1 ); } return; } /* Check for objfuns.... this is probably performance sensitive too. */ void objfun_update( void ) { OBJ_DATA *obj; for ( obj = first_obj; obj != NULL; obj = obj->next ) 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 *marker; OBJ_DATA *obj; extern OBJ_DATA * auction_item; /* * Create dummy object and add to end of list. This object is * only a marker, and will not actually be processed by this * routine. */ GET_FREE(marker, obj_free); LINK(marker, first_obj, last_obj, next, prev); /* * 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; while ((obj = first_obj) != marker) { CHAR_DATA *rch; char *message; UNLINK(obj, first_obj, last_obj, next, prev); LINK(obj, first_obj, last_obj, next, prev); 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; */ extract_obj( obj ); } /* * All objects have been processed. Remove the marker object and * put it back on the free list. */ UNLINK(marker, first_obj, last_obj, next, prev); PUT_FREE(marker, obj_free); 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 *wch_next; CHAR_DATA *ch; CHAR_DATA *ch_next; CHAR_DATA *vch; CHAR_DATA *vch_next; CHAR_DATA *victim; OBJ_DATA *wield; CREF( wch_next, CHAR_NEXT ); for ( wch = first_char; wch != NULL; wch = wch_next ) { wch_next = wch->next; if ( IS_NPC(wch) && wch->mpactnum > 0 && wch->in_room->area->nplayer > 0 ) { MPROG_ACT_LIST *mpact; while ( (mpact = wch->first_mpact) != NULL ) { mprog_wordlist_check( mpact->buf, wch, mpact->ch, mpact->obj, mpact->vo, ACT_PROG ); /* Lets hope this check works until something better is in place. * -- Alty */ if ( wch->hit < -10 ) break; wch->first_mpact = mpact->next; free_string(mpact->buf); PUT_FREE(mpact, mpact_free); } if ( wch->hit < -10 ) continue; wch->mpactnum = 0; wch->first_mpact = NULL; wch->last_mpact = NULL; } if ( (IS_NPC( wch ) ) || wch->level >= LEVEL_IMMORTAL || wch->in_room == NULL ) continue; CREF( ch_next, CHAR_NEXTROOM ); for ( ch = wch->in_room->first_person; ch != NULL; ch = ch_next ) { int count; ch_next = ch->next_in_room; if ( !IS_NPC(ch) || !IS_SET(ch->act, ACT_AGGRESSIVE) || ch->fighting != NULL || ch->hunting != NULL || IS_AFFECTED(ch, AFF_CHARM) || !IS_AWAKE(ch) || ( IS_SET(ch->act, ACT_WIMPY) && IS_AWAKE(wch) ) || !can_see( ch, wch ) ) 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; CREF( vch_next, CHAR_NEXTROOM ); for ( vch = wch->in_room->first_person; vch != NULL; vch = vch_next ) { vch_next = vch->next_in_room; if ( (!IS_NPC(vch) || IS_SET(vch->act,ACT_INTELLIGENT)) && vch->level < LEVEL_IMMORTAL && ( !IS_SET(ch->act, ACT_WIMPY) || !IS_AWAKE(vch) ) && can_see( ch, vch ) && ( !( IS_UNDEAD( ch ) && IS_VAMP( vch ) ) ) ) { if ( number_range( 0, count ) == 0 ) victim = vch; count++; } } CUREF( vch_next ); if ( victim == NULL ) { /* bug( "Aggr_update: null victim.", count ); */ continue; } if ( IS_SET( victim->in_room->room_flags, ROOM_SAFE ) ) 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_WIELD ); if ( wield != NULL && wield->item_type == ITEM_WEAPON && wield->value[3] == 11 && victim->fighting == NULL ) do_backstab( ch, victim->name ); else multi_hit( ch, victim, TYPE_UNDEFINED ); } CUREF( ch_next ); } CUREF( wch_next ); 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; MARK_LIST_MEMBER *this_mark; MARK_LIST_MEMBER *next_mark; for ( area = first_area; area != NULL; area = area->next ) { for ( thing = area->first_area_room; thing != NULL; thing = thing->next ) { room = thing->data; /* if ( room->first_room_affect == NULL ) continue; */ for ( this_mark = room->first_mark_list ; this_mark != NULL; this_mark = next_mark ) { next_mark = this_mark->next; if ( this_mark->mark->duration > 0 ) this_mark->mark->duration--; else if ( this_mark->mark->duration < 0 ) ; else { mark_from_room( room->vnum, this_mark->mark ); } } 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( "\n\r", room ); } r_affect_remove( room, raf ); } } } } } return; } extern void build_save_flush(void); /* * 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_point; static int pulse_auction; extern int saving_area; extern bool auction_flop; 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_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_WIELD ) return FALSE; if ( pickup ) { sprintf( buf, "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 ( (obj->wear_flags & ~ITEM_TAKE)==0 ) 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 ) { sprintf( buf, "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 ) { sprintf( buf, "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 ) { 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; char buf[MAX_STRING_LENGTH]; 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 ) { sprintf( buf, "@@N%s (level:%d, valued at %dGP) 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, auction_item->cost ); } else { sprintf( buf, "%s has bid %d for %s.", auction_bidder->name, auction_bid, auction_item->short_descr ); } break; case 1: if ( auction_bidder == NULL ) sprintf( buf, "Last chance to bid for %s.", auction_item->short_descr ); else sprintf( buf, "Last bid for %s was %d. Any more offers?", auction_item->short_descr, auction_bid ); break; case 2: if ( auction_bidder == NULL ) { auction( "No bidders. Auction Ended." ); for ( ach = first_char; ach != NULL; ach = ach->next ) { if ( auction_owner == ach ) good_seller = TRUE; if ( auction_bidder == ach ) good_buyer = TRUE; } if( good_seller) { auction_owner->gold = UMAX( 0, auction_owner->gold - abs(auction_reserve * .1 ) ); 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; } sprintf( buf, "%s - Going Once!", auction_item->short_descr ); break; case 3: sprintf( buf, "%s - Going TWICE!", auction_item->short_descr ); break; case 4: if ( auction_bid < auction_reserve ) { for ( ach = first_char; ach != NULL; ach = ach->next ) { if ( auction_owner == ach ) good_seller = TRUE; if ( auction_bidder == ach ) good_buyer = TRUE; } sprintf( buf, "%s - CANCELLED. Reserve price not matched.", auction_item->short_descr ); if ( good_seller ) { auction_owner->gold = UMAX( 0, auction_owner->gold - abs(auction_reserve * .1 ) ); obj_to_char( auction_item, auction_owner ); } else extract_obj ( auction_item ); if ( good_buyer ) auction_bidder->gold += auction_bid; } else { for ( ach = first_char; ach != NULL; ach = ach->next ) { if ( auction_owner == ach ) good_seller = TRUE; if ( auction_bidder == ach ) good_buyer = TRUE; } if ( good_buyer ) { sprintf( buf, "%s - SOLD! to %s.", auction_item->short_descr, auction_bidder->name ); obj_to_char( auction_item, auction_bidder ); } else { sprintf( buf, "%s - SOLD!, but the buyer has left us. Oh Well!!!", auction_item->short_descr ); extract_obj( auction_item ); } if ( good_seller ) auction_owner->gold += ( auction_bid - (auction_bid * .1 ) ); } 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: sprintf( buf, "%s returns! I shall have my revenge at last!", victim->name ); do_yell( ch, buf ); break; case 1: sprintf( buf, "%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: sprintf( buf, "%s has wronged me, and now I will seek my revenge!", victim->name ); do_gossip( ch, buf ); sprintf( buf, "Prepare to die, %s.", victim->name ); do_say( ch, buf ); break; case 4: sprintf( buf, "So, %s. You have returned. Let us finish our fight this time!", victim->name ); do_say( ch, buf ); break; case 5: sprintf( buf, "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 ); sprintf( buf, "There can only be one winner, %s.", victim->name ); do_say( ch, buf ); break; } /* Check if has intelligence, and call correct attack? */ multi_hit( ch, victim, TYPE_UNDEFINED ); /* spec- plug leak here */ if (ch->target) { free_string(ch->target); ch->target = NULL; } return; } void quest_update() { extern bool auto_quest; extern bool quest; extern CHAR_DATA *quest_mob; extern OBJ_DATA *quest_object; extern int quest_timer; extern int quest_wait; 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(); } }