/**************************************************************************/ // update.cpp - primarily update handlers /*************************************************************************** * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with all the licenses * * in licenses.txt... In particular, you may not remove this copyright * * notice. * *************************************************************************** * >> Original Diku Mud copyright (c)1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, & Katja Nyboe. * * >> Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * >> ROM 2.4 is copyright 1993-1995 Russ Taylor and has been brought to * * you by the ROM consortium: Russ Taylor(rtaylor@pacinfo.com), * * Gabrielle Taylor(gtaylor@pacinfo.com) & Brian Moore(rom@rom.efn.org) * * >> Oblivion 1.2 is copyright 1996 Wes Wagner * **************************************************************************/ #include "include.h" // dawn standard includes #include "clan.h" #include "nanny.h" #include "track.h" #include "msp.h" #include "ictime.h" #include "intro.h" #include "lockers.h" // function prototypes DECLARE_DO_FUN( do_quit ); DECLARE_DO_FUN( do_say ); DECLARE_DO_FUN( do_land ); // Kal void reboot_autosave( char_data *ch); void laston_save(char_data *ch); void room_aff_update( ROOM_INDEX_DATA *room ); /* * Local functions. */ void connections_update( void ); int hit_gain( char_data *ch ); int mana_gain( char_data *ch ); int move_gain( char_data *ch ); void mobile_update( void ); void weather_update( void ); void char_update( void ); void obj_update( void ); void aggr_update( void ); void moot_update( void ); void do_room_spec(); void dismount( char_data *); // used for saving int save_number = 0; DECLARE_DO_FUN( do_pmote ); // Include files necessary #include <signal.h> // Interval in pulses after which to abort int abort_threshold = 120; #ifdef unix #include <sys/resource.h> bool disable_timer_abort = false; int last_checkpoint; /**************************************************************************/ // find number of CPU seconds spent in user mode so far int get_user_seconds() { struct rusage rus; getrusage(RUSAGE_SELF, &rus); return rus.ru_utime.tv_sec; } bool alarm_handler_display_input_tail; /**************************************************************************/ // this function is called periodically void alarm_update() { last_checkpoint = get_user_seconds(); if (abort_threshold == BOOT_DB_ABORT_THRESHOLD) { abort_threshold = RUNNING_ABORT_THRESHOLD; logf("Used %d user CPU seconds during startup.", last_checkpoint); } alarm_handler_display_input_tail=true; } void write_last_command(void); void nasty_signal_handler(int i); /**************************************************************************/ char *get_compile_time (bool show_parent_codebase_version); /**************************************************************************/ // Signal handler for alarm - suggested for use in MUDs by Fusion void alarm_handler(int signo) { int usage_now = get_user_seconds(); if(usage_now - last_checkpoint<2){ return; // if we checked in within the last second, dont do anything } update_currenttime(); logf("Alarm handler just triggered, %d user second%s since last checkin.", usage_now - last_checkpoint, usage_now - last_checkpoint==1?"":"s"); // Has there gone abort_threshold CPU seconds without alarm_update? if (!disable_timer_abort && (usage_now - last_checkpoint > abort_threshold )) { // For the log file update_currenttime(); log_string("alarm_handler(): Not checkpointed recently, aborting!"); logf("signo=%d", signo); write_last_command(); // nasty_signal_handler(signo); signal(signo, SIG_DFL); do_abort (); return; // this will create a better core file }else if(usage_now - last_checkpoint>1){ if(alarm_handler_display_input_tail){ log_string("Last checkin longer than 1 second, displaying input tail."); // output the inputtail { bool found=false; int i; logf("======INPUTTAIL LOG"); for(i=(inputtail_index+1)%MAX_INPUTTAIL; i!=inputtail_index; i= (i+1)%MAX_INPUTTAIL){ if(!IS_NULLSTR(inputtail[i])){ log_string(inputtail[i]); found=true; } } if(!IS_NULLSTR(inputtail[inputtail_index])){ log_string(inputtail[i]); found=true; } if(!found){ log_string("No inputtail data to dump"); }else{ log_string("R = Room vnum, C = Connected state, E = olc editor mode... inputtail does not include force or ordered commands."); } log_string(get_compile_time(false)); } alarm_handler_display_input_tail=false; } } // The timer resets to the values specified in it_interval automatically } /**************************************************************************/ // Install signal alarm handler void init_alarm_handler() { #ifdef __CYGWIN__ // signaling with virtual timer unsupported by cygwin // nor is alarm handler not supported native win32... this code is in a #ifdef unix only section return; #endif log_string("Installing alarm signal handler."); abort_threshold = BOOT_DB_ABORT_THRESHOLD; // setup a SIGVTALRM signal handler struct sigaction sa; sa.sa_handler = alarm_handler; sa.sa_flags = SA_RESTART; // Restart interrupted system calls sigemptyset(&sa.sa_mask); if(sigaction(SIGVTALRM, &sa, NULL) < 0){ // setup handler for virtual timer bugf("init_alarm_handler(): sigaction(SIGVTALRM, &sa, NULL) < 0 - error %d (%s)", errno, strerror( errno)); exit(1); } last_checkpoint = get_user_seconds(); // tell the OS to send us SIGVTALRM signals periodically 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){ bugf("init_alarm_handler(): setitimer(ITIMER_VIRTUAL, &itimer, NULL) < 0 - error %d (%s)", errno, strerror( errno)); exit(1); } } #else void init_alarm_handler() { return; } void ispell_init() { return; } #endif /**************************************************************************/ // Advancement stuff. void advance_level( char_data *ch ) { if ( IS_NPC( ch )){ return; } int add_hp=0; int add_mana; int add_move; int add_prac; if(GAMESETTING3(GAMESET3_GAIN_HP_WHEN_LEVELING)){ if(ch->pcdata->perm_hit < race_table[ch->race]->max_hp){ add_hp= (ch->perm_stats[STAT_CO] + ch->perm_stats[STAT_SD] + 35) / 20; add_hp = add_hp * 9/10; add_hp = UMAX( 2, add_hp); ch->max_hit += add_hp; ch->pcdata->perm_hit += add_hp; ch->pcdata->perm_hit=UMIN(ch->pcdata->perm_hit, race_table[ch->race]->max_hp); } } add_mana = (ch->perm_stats[STAT_PR] + ch->perm_stats[STAT_EM] + ch->perm_stats[STAT_IN] + 35) / 20; if(!class_table[ch->clss].fMana){ add_mana /= 2; } add_mana = add_mana * 9/10; add_mana = UMAX( 2, add_mana ); ch->max_mana += add_mana; add_move = number_range( 1, (ch->modifiers[STAT_CO] + ch->modifiers[STAT_QU])/10); add_move = add_move * 9/10; add_move = UMAX( 6, add_move ); ch->max_move += add_move; add_prac = 3 + ( ch->modifiers[STAT_CO] + ch->modifiers[STAT_AG] + ch->modifiers[STAT_SD] + ch->modifiers[STAT_ME] + ch->modifiers[STAT_RE] ) / 50 ; ch->practice += add_prac; if(GAMESETTING3(GAMESET3_GAIN_ONE_TRAIN_WHEN_LEVELING)){ ch->train++; }else{ ch->train+=2; } ch->pcdata->perm_mana += add_mana; ch->pcdata->perm_move += add_move; if(GAMESETTING3(GAMESET3_GAIN_HP_WHEN_LEVELING)){ ch->printlnf( "Your gain is: %d/%d hp, %d/%d mana, %d/%d mv %d/%d prac.", add_hp, ch->max_hit, add_mana, ch->max_mana, add_move, ch->max_move, add_prac, ch->practice ); }else{ ch->printlnf( "Your gain is: %d/%d mana, %d/%d mv %d/%d prac.", add_mana, ch->max_mana, add_move, ch->max_move, add_prac, ch->practice ); } if( !GAMESETTING(GAMESET_NO_LETGAINING_REQUIRED) && !GAMESETTING5(GAMESET5_DEDICATED_PKILL_STYLE_MUD)) { if ( ch->level>game_settings->max_level_before_letgaining && !IS_LETGAINED(ch)) { ch->printlnf("`YNOTE: You can't get any more xp once past level %d till you are letgained!`x", game_settings->max_level_before_letgaining); ch->println("read `=Chelp letgain`x and `=Chelp requestletgain`x for more details."); return; } } ch->pcdata->last_level =( ch->played + (int) (current_time - ch->logon)); reset_char(ch); on_level_learn(ch); } /**************************************************************************/ int get_sublevels_for_level(int level) { if(!GAMESETTING(GAMESET_SUBLEVELS_ENABLED)){ return 0; } if (level>=50){ if(level>90){ return 15; } if(level>89){ return 10; } if(level>85){ return 9; } if(level>80){ return 8; } if(level>75){ return 7; } if(level>70){ return 6; } if(level>65){ return 5; } if(level>60){ return 4; } // level is between 50 and 60 switch (level){ case 60:case 59: case 58: return 3; case 57:case 56: case 55: return 2; case 54:case 53: case 52: return 1; case 51:case 50: return 1; default: bugf("get_sublevels_for_level(): level = %d!", level); return 0; } /* if(level>89){ return 15; } if(level>85){ return 10; } if(level>80){ return 9; } if(level>75){ return 8; } if(level>70){ return 7; } if(level>65){ return 6; } if(level>60){ return 5; } // level is between 50 and 60 switch (level){ case 60:case 59: case 58: return 4; case 57:case 56: case 55: return 3; case 54:case 53: case 52: return 2; case 51:case 50: return 1; default: bugf("get_sublevels_for_level(): level = %d!", level); return 0; } */ return 0; }else{ return 0; } } /**************************************************************************/ void gain_exp( char_data *ch, int gain ) { char buf[MSL]; // do nothing with mobs, imms, and gains of 0 if ( IS_NPC(ch) || ch->level >= LEVEL_IMMORTAL || gain ==0) return; if(gain>0) { if(!GAMESETTING(GAMESET_NO_LETGAINING_REQUIRED)){ // non letgained above level game_settings->max_level_before_letgaining if ( ch->level>game_settings->max_level_before_letgaining && !IS_LETGAINED(ch)){ ch->printlnf("You can't get any more xp once past level %d till you are letgained.", game_settings->max_level_before_letgaining); ch->println("read `=Chelp letgain`x and `=Chelp requestletgain`x for more details."); return; } } // award the xp ch->exp = UMAX( exp_per_level(ch,ch->pcdata->points), ch->exp + gain ); // calculate if they gained another level/sublevel uptil they are LEVEL_HERO while ( ch->level <= LEVEL_HERO // 10 sublevels for heros && ch->exp >= exp_per_level(ch,ch->pcdata->points) * (ch->level+1)) { // automatic letheroing support if(GAMESETTING3(GAMESET3_AUTOMATIC_LETHEROING) && ch->level==LEVEL_HERO-1 && ch->pcdata->sublevel>=get_sublevels_for_level(ch->level) && !IS_SET(ch->act,PLR_CAN_HERO)) { ch->println("You have been automatically letheroed!"); SET_BIT(ch->act,PLR_CAN_HERO); logf("%s has been automatically letheroed", ch->name); } // lethero system if (ch->level==LEVEL_HERO-1 && ch->pcdata->sublevel>=get_sublevels_for_level(ch->level) && !IS_SET(ch->act,PLR_CAN_HERO)) { if ( !HAS_CONFIG( ch, CONFIG_NOHEROMSG )) ch->println("You can't hero till you complete your hero quest!"); sprintf(buf,"$N would have HEROED, but isn't letheroed!"); wiznet(buf,ch,NULL,WIZ_LEVELS,0,0); ch->exp = (exp_per_level(ch,ch->pcdata->points) * (ch->level+1))-10; } else { // gaining a sublevel if(ch->pcdata->sublevel < get_sublevels_for_level(ch->level)){ ch->titlebar("`=\xa8You raise a sublevel!!`x"); ch->exp= ch->exp - exp_per_level(ch,ch->pcdata->points); ch->pcdata->sublevel++; if(++ch->pcdata->sublevel_pracs>=SUBLEVELS_PER_PRAC ){ ch->println("You gain 1 practice session for this sublevel."); ch->practice++; ch->pcdata->sublevel_pracs=0; } if(++ch->pcdata->sublevel_trains>=SUBLEVELS_PER_TRAIN){ ch->println("You gain 1 training session for this sublevel."); ch->train++; ch->pcdata->sublevel_trains=0; } if(!IS_SET(ch->dyn,DYN_NO_SUBLEVEL_SPELL_LEARN)){ on_level_learn(ch); }; }else{ // gaining a full level if (ch->level!=LEVEL_HERO){ ch->titlebar("`=\xa8You raise a level!!!`x"); ch->level++; ch->pcdata->sublevel=0; sprintf(buf,"$N has attained level %d!",ch->level); wiznet(buf,ch,NULL,WIZ_LEVELS,0,0); advance_level( ch ); // heroing if (ch->level==LEVEL_HERO){ ch->println("`?C`?o`?n`?g`?r`?a`?t`?u`?l`?a`?t`?i`?o`?n`?s`?!`?!`?!`x"); ch->println("Please read help heroref for more information"); } }else{ // gaining a full hero level ch->titlebar("`=\xa8You have gained a hero level!!!`x"); ch->println("You gain 3 pracs and 1 train."); ch->practice += 3; ch->train += 1; ch->pcdata->hero_level_count++; ch->exp= ch->exp - exp_per_level(ch,ch->pcdata->points); on_level_learn(ch); if(GAMESETTING(GAMESET_SUBLEVELS_ENABLED)){ ch->pcdata->sublevel=5; // so they have a buffer when they are killed }else{ ch->pcdata->sublevel=0; } sprintf(buf, "%s gained a hero level!!!", ch->name ); wiznet(buf, ch, NULL, WIZ_LEVELS, 0, 0 ); log_string(buf); } } REMOVE_BIT(ch->dyn,DYN_NO_SUBLEVEL_SPELL_LEARN); save_char_obj(ch); } } } else // negative gaining - death etc { bool fleeing=false; int xp_amount= gain * -1; if (gain>-400){ // lose subtrains and subpracs (prevent cheating using flee just after leveling) // (anything apart from death - energy_drain, retreat...) fleeing=true; SET_BIT(ch->dyn,DYN_NO_SUBLEVEL_SPELL_LEARN); } // handle how the loss of sublevels affects the actual // ch->exp drops, drop_level() is only called in kill_char() // use sublevels to reduce the xp_amount loss while(ch->pcdata->sublevel>0 && xp_amount>exp_per_level(ch,ch->pcdata->points)){ ch->pcdata->sublevel--; ch->println("You have lost a sublevel!"); if(fleeing){ ch->println("You lose one subtrain and one subprac."); ch->pcdata->sublevel_pracs--; ch->pcdata->sublevel_trains--; } xp_amount-=exp_per_level(ch,ch->pcdata->points); } // if they still have sublevels, and the xp_amount would cause a level drop // trade a sublevel for a levels worth of xp first if( (ch->pcdata->sublevel > 0) && (xp_amount>=XP_PAST_LEVEL(ch))){ ch->exp+=exp_per_level(ch,ch->pcdata->points); ch->println("You lose a sublevel!"); ch->pcdata->sublevel--; if(fleeing){ ch->println("You lose one subtrain and one subprac."); ch->pcdata->sublevel_pracs--; ch->pcdata->sublevel_trains--; } } // take away the xp ch->exp-=xp_amount; // can't get below level 1 ch->exp = UMAX( exp_per_level(ch,ch->pcdata->points), ch->exp); // check, shouldn't be required if(ch->pcdata->sublevel<0){ assert(ch->pcdata->sublevel); ch->pcdata->sublevel=0; } save_char_obj(ch); } return; } /**************************************************************************/ /* * Regeneration stuff. */ int hit_gain( char_data *ch ) { int gain; int number; if (ch->in_room == NULL) return 0; if ( IS_NPC(ch) ) { gain = 1 + ch->level/3; // gain *100 what it should normally be, to reduce rounding errors gain *=100; if (IS_AFFECTED(ch,AFF_REGENERATION)) gain *= 2; switch(ch->position) { default : gain /= 2; break; case POS_SLEEPING: gain = 3 * gain/2; break; case POS_RESTING: break; case POS_FIGHTING: gain /= 3; break; } } else // healing on a character { // gain *100 what it should normally be, to reduce rounding errors gain = UMAX(300, (ch->modifiers[STAT_CO]*ch->max_hit/2) ); if(gain<100) gain=100; /* fast healing code */ number = number_percent(); if (number < get_skill(ch,gsn_fast_healing)) { gain += number * gain / 100; if (ch->hit < ch->max_hit) check_improve(ch,gsn_fast_healing,true,8); } switch ( ch->position ) { default: gain /= 3; break; case POS_SLEEPING: gain = gain*3 /2; break; case POS_RESTING: break; case POS_FIGHTING: gain /= 5; break; } if (IS_AFFECTED(ch,AFF_REGENERATION)) gain *= 2; if ( ch->pcdata->condition[COND_HUNGER] == 0 ) gain /= 2; if ( ch->pcdata->condition[COND_THIRST] == 0 ) gain /= 2; } gain = gain * ch->in_room->heal_rate / 100; // support a global room hp regenerate scaling value if(game_settings->global_scale_hitpoints_regen_rate && game_settings->global_scale_hitpoints_regen_rate!=100) { gain = gain * game_settings->global_scale_hitpoints_regen_rate/ 100; } // apply healing rate of objects if (ch->on != NULL && ch->on->item_type == ITEM_FURNITURE) gain = gain * ch->on->value[3] / 100; if ( IS_AFFECTED(ch, AFF_POISON) ) gain /= 4; if (IS_AFFECTED(ch, AFF_PLAGUE)) gain /= 8; if (IS_AFFECTED(ch,AFF_HASTE) || IS_AFFECTED(ch,AFF_SLOW)) gain /=2 ; if (IS_SET(race_table[ch->race]->flags, RACEFLAG_REGEN_SLOW_IN_LIGHT) && ch->in_room!=NULL && ch->in_room->sector_type != SECT_INSIDE && ch->in_room->sector_type != SECT_CAVE && !IS_SET( ch->in_room->affected_by, ROOMAFF_UTTERDARK ) && time_info.hour>7 && time_info.hour<20 ) gain /=10; // reduce gain back down what it should normally be gain /=100; return UMIN(gain, UMAX(0, ch->max_hit - ch->hit) ); } /**************************************************************************/ int mana_gain( char_data *ch ) { int gain; int number; if (ch->in_room == NULL) return 0; if ( IS_SET( ch->in_room->room_flags, ROOM_ANTIMAGIC )) { return 0; } if ( IS_NPC(ch) ) { gain = 5 + ch->level; switch (ch->position) { default: gain /= 2; break; case POS_SLEEPING: gain = 3 * gain/2; break; case POS_RESTING: break; case POS_FIGHTING: gain /= 3; break; } } else { gain = ch->perm_stats[STAT_SD] * ch->max_mana / 400; number = number_percent(); if (number < get_skill(ch,gsn_meditation)) { gain += (number+10) * gain / 100; if (ch->mana < ch->max_mana) check_improve(ch,gsn_meditation,true,8); } if (!class_table[ch->clss].fMana) gain /= 2; switch ( ch->position ) { default: gain /= 4; break; case POS_SLEEPING: break; case POS_RESTING: gain /= 2; break; case POS_FIGHTING: gain /= 6; break; } if ( ch->pcdata->condition[COND_HUNGER] == 0 ) gain /= 2; if ( ch->pcdata->condition[COND_THIRST] == 0 ) gain /= 2; } if (IS_AFFECTED(ch,AFF_REGENERATION)) gain += gain/3; gain = gain * ch->in_room->mana_rate / 100; // support a global room mana regenerate scaling value if(game_settings->global_scale_mana_regen_rate && game_settings->global_scale_mana_regen_rate!=100) { gain = gain * game_settings->global_scale_mana_regen_rate/ 100; } if (ch->on != NULL && ch->on->item_type == ITEM_FURNITURE) gain = gain * ch->on->value[4] / 100; if ( IS_AFFECTED( ch, AFF_POISON ) ) gain /= 4; if (IS_AFFECTED(ch, AFF_PLAGUE)) gain /= 8; if (IS_AFFECTED(ch,AFF_HASTE) || IS_AFFECTED(ch,AFF_SLOW)) gain /=2 ; if (IS_SET(race_table[ch->race]->flags, RACEFLAG_REGEN_SLOW_IN_LIGHT) && ch->in_room!=NULL && ch->in_room->sector_type != SECT_INSIDE && ch->in_room->sector_type != SECT_CAVE && !IS_SET( ch->in_room->affected_by, ROOMAFF_UTTERDARK ) && time_info.hour>7 && time_info.hour<20 ) gain = 0; if(gain>30) gain=(gain-30)/3+30; return UMIN(gain, UMAX(0, ch->max_mana - ch->mana) ); } /**************************************************************************/ int move_gain( char_data *ch ) { int gain; if (ch->in_room == NULL) return 0; if ( IS_NPC(ch) ) { gain = ch->level; } else { gain = UMAX( 15, ch->level ); switch ( ch->position ) { case POS_SLEEPING: gain += ch->modifiers[STAT_CO]; break; case POS_RESTING: gain += ch->modifiers[STAT_CO]/2; break; } if ( ch->pcdata->condition[COND_HUNGER] == 0 ) gain /= 2; if ( ch->pcdata->condition[COND_THIRST] == 0 ) gain /= 2; } gain = gain * ch->in_room->heal_rate/100; // support a global room move regenerate scaling value if(game_settings->global_scale_movement_regen_rate && game_settings->global_scale_movement_regen_rate!=100) { gain = gain * game_settings->global_scale_movement_regen_rate/ 100; } if (ch->on != NULL && ch->on->item_type == ITEM_FURNITURE) gain = gain * ch->on->value[3] / 100; if ( IS_AFFECTED(ch, AFF_POISON) ) gain /= 4; if (IS_AFFECTED(ch, AFF_PLAGUE)) gain /= 8; if (IS_AFFECTED(ch,AFF_HASTE) || IS_AFFECTED(ch,AFF_SLOW)) gain /=2 ; return UMIN(gain, UMAX(0, ch->max_move - ch->move) ); } /**************************************************************************/ void gain_condition( char_data *ch, int iCond, int value ) { int condition; if ( value == 0 || IS_NPC(ch) || ch->level<=1 || ch->level >= LEVEL_IMMORTAL) return; condition = ch->pcdata->condition[iCond]; if (condition == -1) return; ch->pcdata->condition[iCond] = URANGE( 0, condition + value, 48 ); if ( ch->pcdata->condition[iCond] == 0 ) { switch ( iCond ) { case COND_HUNGER: { if(IS_SET(ch->imm_flags,IMM_HUNGER)){ ch->pcdata->condition[COND_HUNGER] = 1; }else{ ch->println("You are hungry."); if(GAMESETTING5(GAMESET5_HUNGER_AND_THIRST_CAUSES_DAMAGE)){ if(ch->level < 5){ ch->hit -= 1; }else if(ch->level < 10){ ch->hit -= 2; }else if(ch->level < 20){ ch->hit -= 3; }else if(ch->level < LEVEL_IMMORTAL){ ch->hit -= 5; } } } } break; case COND_THIRST: { if(IS_SET(ch->imm_flags,IMM_THIRST)){ ch->pcdata->condition[COND_THIRST] = 1; }else{ ch->println("You are thirsty."); if(GAMESETTING5(GAMESET5_HUNGER_AND_THIRST_CAUSES_DAMAGE)){ if(ch->level < 5){ ch->hit -= 1; }else if(ch->level < 10){ ch->hit -= 2; }else if(ch->level < 20){ ch->hit -= 3; }else if(ch->level < LEVEL_IMMORTAL){ ch->hit -= 5; } } } } break; case COND_DRUNK: if ( condition != 0 ) ch->println("You are sober."); break; } } return; } /**************************************************************************/ // Mob autonomous action // - called every PULSE_MOBILE (once every 4 seconds by default) void mobile_update( void ) { char_data *ch; char_data *ch_next; EXIT_DATA *pexit; int door; int number_in_game=mobile_count+1000; // note: number_in_game is used to reduce potential loops // with a mobprog that creates new mobs. // Examine all mobs. for ( ch = char_list; ch && --number_in_game>=0; ch = ch_next ) { ch_next = ch->next; if ( !IS_NPC(ch) || ch->in_room == NULL || IS_AFFECTED(ch,AFF_CHARM)) continue; if (ch->in_room->area->empty && !IS_SET(ch->act,ACT_UPDATE_ALWAYS)) continue; // Examine call for special procedure if ( ch->spec_fun != 0 ){ if ( (*ch->spec_fun) ( ch ) ) continue; } if (ch->pIndexData->pShop != NULL) // give showkeepers gold if they are low { if ((ch->gold * 100 + ch->silver) < ch->pIndexData->wealth*50) { ch->gold += ch->pIndexData->wealth * number_range(1,20)/5000; ch->silver += ch->pIndexData->wealth * number_range(1,20)/50; } } // Check triggers only if mobile still in default position if ( ch->position == ch->pIndexData->default_pos ) { // Delay if ( HAS_TRIGGER( ch, TRIG_DELAY) && ch->mprog_delay > 0 ) { if ( --ch->mprog_delay <= 0 ) { mp_percent_trigger( ch, NULL, NULL, NULL, TRIG_DELAY ); continue; } } if ( HAS_TRIGGER( ch, TRIG_RANDOM) ) { if( mp_percent_trigger( ch, NULL, NULL, NULL, TRIG_RANDOM ) ) continue; } } // That's all for sleeping / busy monster, and empty zones if ( ch->position != POS_STANDING || IS_CONTROLLED(ch) ) continue; // Scavenge if ( IS_SET(ch->act, ACT_SCAVENGER) && ch->in_room->contents != NULL && number_bits( 6 ) == 0 ) { OBJ_DATA *obj; OBJ_DATA *obj_best; int max; max = 1; obj_best = 0; for ( obj = ch->in_room->contents; obj; obj = obj->next_content ) { if ( CAN_WEAR(obj, OBJWEAR_TAKE) && can_loot(ch, obj) && obj->cost > max && obj->cost > 0) { 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 ); } } /* // MOB MEMORY CHECK - Kerenos { char_data *rch; for ( rch = ch->in_room->people; rch; rch = rch->next_in_room ) { if ( mobRememberCH( ch, rch ) && can_see( ch, rch )) { if ( IS_SET( ch->form, FORM_SENTIENT )) do_say( ch, "Prepare to DIE!!!" ); else act( "$n looks positively enraged and attacks you.", ch, NULL, rch, TO_CHAR ); multi_hit( ch, rch, TYPE_UNDEFINED ); break; } } } */ // Wander if ( !IS_SET(ch->act, ACT_SENTINEL) && number_bits(3) == 0 && ch->last_force+5 <= tick_counter // mobs after being forced/controlled // dont wander for a while && !ch->ridden_by) { door = number_door(); if ( ( pexit = ch->in_room->exit[door] ) != NULL && pexit->u1.to_room != NULL && !IS_SET(pexit->exit_info, EX_CLOSED) && !IS_SET(pexit->u1.to_room->room_flags, ROOM_NO_MOB) && ( !IS_SET(ch->act, ACT_STAY_AREA) || pexit->u1.to_room->area == ch->in_room->area ) && ( !IS_SET(ch->act, ACT_OUTDOORS) || !IS_SET(pexit->u1.to_room->room_flags,ROOM_INDOORS)) && ( !IS_SET(ch->act, ACT_INDOORS) || IS_SET(pexit->u1.to_room->room_flags,ROOM_INDOORS))) { bool make_move= true; // dont move a shopkeeper into a room with another shopkeeper in it if (IS_KEEPER(ch)){ char_data *kch; for ( kch = pexit->u1.to_room->people; kch; kch= kch->next_in_room){ if (IS_KEEPER(kch)){ make_move= false; } } } // dont move a non AMPHIBIAN between land/water if (!IS_SET(ch->act, ACT_AMPHIBIAN)){ if(IS_WATER_SECTOR(ch->in_room->sector_type)!= IS_WATER_SECTOR(pexit->u1.to_room->sector_type)) { make_move=false; } } if (make_move){ move_char( ch, door, false); } } } } return; } /**************************************************************************/ // normal range -9 thru 9 // peak is 11 (dual ecllipse) int get_magecastmod(void) { int cm; int sect; // 35 = 7 eclipse month 11 = 4 // 0 = 6 // months // 3 thru -3 // 18 = -6 cm = ((abs(time_info.day-17)- 9)*2/3) + ((abs(time_info.month-5)- 3)); if (time_info.day==ICTIME_DAYS_PER_MONTH-1) // peak modifier cm++; if (time_info.month==ICTIME_MONTHS_PER_YEAR-1) // peak modifier cm++; for ( sect = 0; sect < SECT_MAX; sect++ ) { if (time_info.day>(ICTIME_DAYS_PER_MONTH/2)) weather_info[sect].moon_getting_brighter = true; else weather_info[sect].moon_getting_brighter = false; } return cm; } /**************************************************************************/ void resolve_stats(); void do_save_corpses(char_data *ch, char *); void save_races(); /**************************************************************************/ void maintence_saves( void ) { if (--resaveCounter<1) { log_string("Doing maintence saves..."); log_string(">>> autosave olc"); reboot_autosave(NULL); // autosave olc work and lockers log_string(">>> autosave laston"); laston_save(NULL); // autosave laston stuff log_string(">>> autosave intro database"); save_intro_database(); // autosave laston stuff log_string(">>> autosave corpses"); do_save_corpses(NULL,""); if(IS_SET( RACEEDIT_FLAGS, OLC_CHANGED )){ log_string(">>> autosaving races"); save_races(); REMOVE_BIT( RACEEDIT_FLAGS, OLC_CHANGED ); } do_hsave(NULL,""); resolve_stats(); resort_top_roleplayers(); resaveCounter=15; // reset counter }; } /**************************************************************************/ void quit_char(char_data *ch, char *argument, bool character_deleting ); /**************************************************************************/ // Update all chars, including mobs. void char_update( void ) { char_data *ch; char_data *ch_next; char_data *ch_quit; char buf[MSL]; int xp; ch_quit = NULL; // update save counter ++save_number%=10; for ( ch = char_list; ch != NULL; ch = ch_next ) { AFFECT_DATA *paf; AFFECT_DATA *paf_next; ch_next = ch->next; ch->autologout= false; if ( IS_NPC( ch ) && HAS_TRIGGER( ch, TRIG_TICK )) mp_percent_trigger( ch, NULL, NULL, NULL, TRIG_TICK ); // Mirage countdown if ( ch->mirage_hours ) { --ch->mirage_hours; if ( ch->mirage_hours < 1 ) ch->mirage = 0; } if (!IS_NPC(ch)) { ch->idle++; if(ch->pknorecall>0) ch->pknorecall--; if(ch->pknoquit>0) ch->pknoquit--; ch->pcdata->next_lay_countdown--; if (ch->pcdata->next_lay_countdown<1) { ch->pcdata->lays=UMIN(ch->pcdata->lays+1, ch->level/8+2); ch->pcdata->next_lay_countdown=GET_NEW_LAY_COUNTER(ch); } if ( !IS_OOC(ch) && ch->idle<2 && ch->pcdata->karns<GET_MAX_KARN(ch)) { ch->pcdata->next_karn_countdown--; if (ch->pcdata->next_karn_countdown<1) { ch->println("Before you eyes a large glowing ball of light appears!\r\n" "It moves towards you and enters your body... you have a sensation\r\n" "that seems familiar but you dont recognise it, then the feeling\r\n" "slow fades to the point you are unaware of it."); ch->pcdata->next_karn_countdown=GET_NEW_KARN_COUNTER(ch); ch->pcdata->karns++; } } else { if (ch->idle>10 || (IS_OOC(ch) && ch->clan)) { ch->pcdata->next_karn_countdown++; } } // autoafk system if(!GAMESETTING3(GAMESET3_AUTOAFK_DISABLED) && !IS_NPC(ch)){ int autoafkafter=ch->pcdata->autoafkafter; if(!autoafkafter){ if(ch->level<LEVEL_IMMORTAL){ autoafkafter=5; }else{ autoafkafter=15; } } if(ch->idle >= autoafkafter && !IS_SET(TRUE_CH(ch)->comm,COMM_AFK)) { if(ch->level >= LEVEL_IMMORTAL){ ch->println("Idle 15 Ticks. Auto AFK Enabled."); }else{ ch->println("Idle 5 Ticks. Auto AFK Enabled."); } SET_BIT(TRUE_CH(ch)->comm,COMM_AFK); replace_string(ch->pcdata->afk_message, "Auto AFK"); } } if (ch->level<LEVEL_IMMORTAL) { if ( ch->idle == 15 ) { 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 ); ch->println("You disappear into the void."); if (ch->level > 1) save_char_obj( ch ); char_from_room( ch ); char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) ); } } } if (!IS_OOC(ch)) { // RPS and vote gaining systems if(!IS_NPC(ch)) { ch->pcdata->rp_count++; // update a nobles moots if(ch->pcdata->diplomacy) { ch->pcdata->voteupdate_count++; if(ch->pcdata->voteupdate_count>25){ if(ch->pcdata->voteupdate_count>(ch->pcdata->dip_points/20)){ ch->pcdata->dip_points+=ch->pcdata->diplomacy; ch->pcdata->voteupdate_count=0; if(!IS_IMMORTAL(ch)){ ch->wrapln("You just gained some more votes... " "keep giving positive moots to everyone."); } }else if(ch->pcdata->voteupdate_count>=60){ ch->pcdata->dip_points+=ch->pcdata->diplomacy; ch->pcdata->voteupdate_count=0; if(!IS_IMMORTAL(ch)){ ch->wrapln("You just gained some more votes... " "remember that those with less than 1200 votes get given votes " "more frequently so keep giving positive moots to players."); } } } } if(ch->pcdata->rp_count % 10 == 0 && ch->pcdata->rp_count != 0) { ch->pcdata->rp_count=0; if(GAMESETTING(GAMESET_DISABLE_RPS_SYSTEM)){ ch->pcdata->merit=0; } if (ch->pcdata->merit==100){ ch->pcdata->merit=85; } if (ch->pcdata->merit>65) {// anti autorps abuse system int dups=0, cdup=0; int current=0; int check=0; bool fail_audit = false; time_t timeafter = current_time - (60*20); // 20mins logf("Doing EMOTE audit on %s merit=%d (emote_index = %d)", ch->name, ch->pcdata->merit ,ch->pcdata->emote_index); for (current=0; current<RPS_AUDIT_SIZE; current++) { if (IS_NULLSTR(ch->pcdata->emotecheck[current])) continue; if (ch->pcdata->emotetimes[current]<timeafter) continue; cdup=0; for (check=current+1; check<RPS_AUDIT_SIZE; check++) { if (IS_NULLSTR(ch->pcdata->emotecheck[check])) continue; if (ch->pcdata->emotetimes[current]<timeafter) continue; if (!str_cmp(ch->pcdata->emotecheck[current], ch->pcdata->emotecheck[check])) { cdup++; } } if (cdup>5) { fail_audit=true; } dups+=cdup; } if (dups>8) { fail_audit=true; } if (fail_audit) { ch->pcdata->merit=0; logf("%s failed RPS emote audit (emote_index = %d)", ch->name, ch->pcdata->emote_index); for (current=0; current<RPS_AUDIT_SIZE && !IS_NULLSTR(ch->pcdata->emotecheck[current]); current++) { logf("%2d> emote '%s'",current, ch->pcdata->emotecheck[current]); } } } // end of emote audit if (ch->pcdata->merit>70) {// anti autorps abuse system int dups=0, cdup=0; int current=0; int check=0; time_t timeafter = current_time - (60*20); // 20mins bool fail_audit = false; logf("Doing SAY audit on %s merit=%d (say_index = %d)", ch->name, ch->pcdata->merit ,ch->pcdata->say_index); for (current=0; current<RPS_AUDIT_SIZE; current++) { if (IS_NULLSTR(ch->pcdata->saycheck[current])) continue; if (ch->pcdata->saytimes[current]<timeafter) continue; cdup=0; for (check=current+1; check<RPS_AUDIT_SIZE; check++) { if (IS_NULLSTR(ch->pcdata->saycheck[check])) continue; if (ch->pcdata->saytimes[current]<timeafter) continue; if (!str_cmp(ch->pcdata->saycheck[current], ch->pcdata->saycheck[check])) { cdup++; } } if (cdup>5) { fail_audit=true; } dups+=cdup; } if (dups>8) { fail_audit=true; } if (fail_audit) { ch->pcdata->merit=0; logf("%s failed RPS say audit (say_index = %d)", ch->name, ch->pcdata->say_index); for (current=0; current<RPS_AUDIT_SIZE && !IS_NULLSTR(ch->pcdata->saycheck[current]); current++) { logf("%2d> say '%s'",current, ch->pcdata->saycheck[current]); } } } // end of say audit if (ch->pcdata->merit<0) ch->pcdata->merit=0; // xp=ch->pcdata->merit * (125-( (ch->level) * 100 / 77)); // old dawn1.1 // xp=ch->pcdata->merit * (125-( (ch->level) * 100 / 70)); // dawn1.5 system till 14May99 // xp=ch->pcdata->merit * (130-( (ch->level) * 100 / 85)); // dawn1.5 till 12Aug99 // xp=ch->pcdata->merit * (180-( (ch->level) * 100 / 60)); // till 20 October 99 xp=ch->pcdata->merit * (210-( (ch->level) * 100 / 60)); // bonus for being letgained if(IS_LETGAINED(ch) || GAMESETTING(GAMESET_NO_LETGAINING_REQUIRED)){ xp*=3/2; } if((ch->pcdata->rp_points*2)>ch->exp){ xp*=125; // extra 25% for those who have got over half their xp from RPS xp/=100; } // reduced RPS percent system if (!IS_NPC(ch) && ch->pcdata->reduce_rps_percent>0) { xp= (xp *ch->pcdata->reduce_rps_percent)/100; } xp/=number_range(90,105); if(xp>0 && !IS_SET(ch->act,PLR_NORP)) { ch->pcdata->rp_points+=xp; if ((ch->level<=LEVEL_HERO) && (ch->pcdata->xp_penalty<1)) { ch->printlnf("`=\xa7You receive %d xp for your roleplaying efforts.`x", xp); gain_exp(ch,xp); } else { ch->printlnf("`=\xa7Your rps increases by %d for your roleplaying efforts.`x", xp); } sprintf(buf,"%s received %2d xp for RP. (merit = %2d, lvl = %2d)" ,ch->name,xp,ch->pcdata->merit, ch->level ); wiznet(buf,ch,NULL,WIZ_RPEXP,0,0); log_string(buf); // log their rps if (IS_SET(ch->act, PLR_LOG)) { append_playerlog( ch, buf); } } ch->pcdata->merit=0; } if(ch->pcdata->did_ooc || ch->pcdata->did_ic) { if(ch->pcdata->did_ic && !ch->pcdata->did_ooc) ch->pcdata->merit+=10; else if(ch->pcdata->did_ooc && !ch->pcdata->did_ic ) ch->pcdata->merit-=3; } else ch->pcdata->merit-=1; ch->pcdata->did_ooc=false; ch->pcdata->did_ic=false; }// end of RPS system // xp penalty system if(!IS_NPC(ch)&&ch->pcdata!=NULL) { if(ch->pcdata->xp_penalty>0) ch->pcdata->xp_penalty-=1; } // ****************************** // Tiredness related code below - doesn't affect on olc port if(!IS_NPC(ch) && (ch->level<LEVEL_IMMORTAL) && ch->desc!=NULL && !GAMESETTING5(GAMESET5_DEDICATED_OLC_BUILDING_MUD) && ch->level>5 && ch->pcdata!=NULL) { if(ch->position==POS_SLEEPING) { if(ch->pcdata->tired!=-1) { ch->pcdata->tired-=3; if(ch->pcdata->tired<0) ch->pcdata->tired=0; } if(ch->is_trying_sleep) { if((number_percent()<(4-ch->pcdata->tired)*25) && !IS_AFFECTED(ch, AFF_SLEEP)) { ch->position=POS_RESTING; ch->println("You awake feeling refreshed."); act( "$n awakes very chipper and refreshed.", ch, NULL, NULL, TO_ROOM ); ch->is_trying_sleep=false; } } else { if((number_percent()<(10-ch->pcdata->tired)*25) && !IS_AFFECTED(ch, AFF_SLEEP)) { ch->position=POS_RESTING; ch->println("You awake somewhat groggy and confused."); act( "$n awakes very reluctantly.", ch, NULL, NULL, TO_ROOM ); } } } else { if(ch->position>POS_RESTING && (ch->level>5) && (ch->pcdata->tired!=-1)) { if(number_range(1,3)!=1) { if (IS_SET(race_table[ch->race]->flags, RACEFLAG_NEED_TWICE_AS_MUCH_SLEEP)){ ch->pcdata->tired+=2; }else{ ch->pcdata->tired++; } } } if(ch->is_trying_sleep) { if(((number_percent()<(ch->pcdata->tired-10)*10+(ch->max_hit-ch->hit)/ch->max_hit*50)) && !IS_AFFECTED(ch, AFF_BERSERK) && !is_affected(ch, gsn_rage) ) { ch->position=POS_SLEEPING; ch->println("You drift off into the dreamscape."); act( "$n goes to sleep.", ch, NULL, NULL, TO_ROOM ); } } else { if((number_percent()<(ch->pcdata->tired-30)*7+ (ch->max_hit-ch->hit)/(ch->max_hit==0?1:ch->max_hit)*25) && !IS_AFFECTED(ch, AFF_BERSERK) && !is_affected(ch, gsn_rage) ) { if(ch->position!=POS_FIGHTING) { ch->position=POS_SLEEPING; if (ch->mounted_on!=NULL) { dismount(ch); ch->println("You are so tired you fall off your mount!"); } ch->println("You groggily collapse from exhaustion."); act( "$n collapses from exhaustion.", ch, NULL, NULL, TO_ROOM ); } else ch->println("You are very battle weary."); } else if(ch->pcdata->tired>32) ch->println("You feel extremely tired."); else if(ch->pcdata->tired>25) ch->println("You feel very tired."); else if(ch->pcdata->tired>16) ch->println("You feel tired."); } if(ch->pcdata->tired<-1) { ch->pcdata->tired=0; } } }// end of Tiredness related code } //!IS_OOC(ch) } // end of !IS_NPC(ch) // ************************************** // time doesn't pass in ooc or olconly areas if(ch->in_room && !IS_OOC(ch) && !IS_SET(ch->in_room->area->area_flags, AREA_OLCONLY) ) { if ( ch->expire_recall_inn>0 ) { ch->expire_recall_inn--; if ( ch->expire_recall_inn == 0 ) { ch->println("`YYour room at the inn has expired.`x"); ch->recall_inn_room = 0; } } if(ch->pkool>0){ ch->pkool-=1; } if(ch->pksafe>0){ ch->pksafe-=1; } // budget fix for subdue if (ch->subdued_timer>0) { ch->subdued_timer--; if (ch->subdued_timer==0) { if (ch->hit<number_range(2,5)) { ch->hit = number_range(2,5); ch->mana = 0; ch->move = number_range(1,20); } ch->subdued=false; update_pos( ch ); } } if ( ch->position >= POS_STUNNED ) { // check to see if we need to go home (mobs out of their area) if (IS_NPC(ch) && ch->zone != NULL && ch->zone != ch->in_room->area && ch->desc == NULL && ch->fighting == NULL && !IS_AFFECTED(ch,AFF_CHARM) && number_percent() < 15) { act("$n wanders on home.",ch,NULL,NULL,TO_ROOM); extract_char(ch,true); continue; } if ( ch->hit < ch->max_hit){ ch->hit+= hit_gain(ch); }else{ ch->hit = ch->max_hit; } if ( ch->mana < ch->max_mana){ ch->mana += mana_gain(ch); }else{ ch->mana = ch->max_mana; } // movement gain - lose 1 to 2 mv per tick you // are nonmagically flying if(IS_AFFECTED(ch, AFF_FLYING)){ if(IS_SET(ch->dyn,DYN_NONMAGICAL_FLYING) && (!IS_NPC(ch) || (IS_NPC(ch) && ch->master))) { ch->move-=number_range(2,5); if(ch->move<1){ ch->println("You are so exhausted from your flying you land on the ground."); do_land(ch,""); }else if(number_range(1,10)==1){ ch->println("All this flying is making you tired."); } } }else{ if ( ch->move < ch->max_move){ ch->move += move_gain(ch); }else{ ch->move = ch->max_move; } } } if ( ch->position == POS_STUNNED ) update_pos( 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 flickers and goes out.", ch, obj, NULL, TO_CHAR ); extract_obj( obj ); } else if ( obj->value[2] <= 5 && ch->in_room != NULL) act("$p flickers.",ch,obj,NULL,TO_CHAR); } if ((IS_IMMORTAL(ch) && ch->desc)|| IS_SWITCHED (ch)) ch->timer = 0; 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 ); ch->println("You disappear into the void."); if (ch->level > 1) save_char_obj( ch ); char_from_room( ch ); char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) ); } } gain_condition( ch, COND_DRUNK, -1 ); gain_condition( ch, COND_FULL, ch->size > SIZE_MEDIUM ? -4 : -2 ); gain_condition( ch, COND_THIRST, -1 ); gain_condition( ch, COND_HUNGER, ch->size > SIZE_MEDIUM ? -2 : -1); } if(ch->fighting) { update_pos(ch); if(ch->bleeding) { ch->bleeding-=1; damage( ch, ch, ch->bleeding, TYPE_UNDEFINED, DAM_NONE,false); act("$n's deteriorating condition is very apparent.", ch,NULL,NULL,TO_ROOM); ch->println("You lose even more blood."); } // lava 'fire' damage from the room if(!IS_NPC(ch) && ch->in_room && (ch->in_room->sector_type == SECT_LAVA) && !IS_AFFECTED(ch, AFF_FLYING ) && !IS_SET(TRUE_CH(ch)->act, PLR_HOLYWALK)) { if ( !IS_AFFECTED( ch, AFF_FLYING )){ ch->println("You start to burn."); damage( ch, ch, UMAX(20, 250 - (ch->modifiers[STAT_CO])), gsn_plague,DAM_FIRE,false); } } if(ch->is_stunned){ ch->is_stunned-=20; if(ch->is_stunned<=0){ ch->position=POS_RESTING; ch->is_stunned=0; ch->println("You are no longer stunned."); } } if(ch->will_die || ch->hit<-10) { ch->will_die-=20; if(ch->will_die<=0 || ch->hit<-10) { ch->will_die=0; if ( !IS_NPC(ch) ) { sprintf( log_buf, "%s died from bleeding at %d", ch->name, ch->in_room->vnum ); log_string( log_buf ); // Dying penalty: // 2/3 way back to previous level. if(!IS_NPC(ch)) if(ch->level<21) { if ( ch->exp > exp_per_level(ch,ch->pcdata->points) * ch->level ) { int lose_amount = (2 * (exp_per_level(ch,ch->pcdata->points) * ch->level - ch->exp)/3) + 50; if (lose_amount<-1000) /* dont lose more than 1000 xp */ lose_amount = -1000; gain_exp( ch,lose_amount); } } else { gain_exp( ch, -500); if(ch->exp<exp_per_level(ch,ch->pcdata->points)* ch->level ) drop_level(ch); check_perm_damage(ch); } } if (IS_NPC(ch)) wiznet(log_buf,NULL,NULL,WIZ_MOBDEATHS,0,0); else wiznet(log_buf,NULL,NULL,WIZ_DEATHS,0,0); raw_kill( ch, ch ); } } } // patch to prevent cheaters surrendering or using rescue // then backstabing straight away if (ch->cautious_about_backstab>0) ch->cautious_about_backstab--; // affects system for ( paf = ch->affected; paf != NULL; paf = paf_next ) { paf_next = paf->next; if ( paf->duration > 0 ) { paf->duration--; if (paf->level > 0 && number_range(0,4) == 0 && paf->where!=WHERE_OBJECTSPELL) { paf->level--; // spell strength fades with time } } else { // spells duration >-1 wear off checks if ( paf->duration>-1 ) { if ( paf_next == NULL || paf_next->type != paf->type || paf_next->duration > 0 ) { if ( paf->type > 0 && skill_table[paf->type].msg_off ) { ch->print( skill_table[paf->type].msg_off); ch->println(""); } if ( paf->type == gsn_immolation ) { if ( !IS_NPC(ch)) { ch->mana = 0; ch->pcdata->tired += 40; ch->pcdata->condition[COND_THIRST] = 0; ch->pcdata->condition[COND_HUNGER] = 0; } } } 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). // Drowning damage if ch->in_room sector is UNDERWATER if (ch->in_room && ( ch->in_room->sector_type == SECT_UNDERWATER || IS_SET( ch->in_room->affected_by, ROOMAFF_UNDERWATER )) && !IS_AFFECTED(ch, AFF_OTTERLUNGS ) && !IS_SET(TRUE_CH(ch)->act, PLR_HOLYWALK) && !IS_NPC(ch)) { OBJ_DATA *obj2 = (get_eq_char(ch, WEAR_LIGHT)); int dam = UMAX(20, 75 - (ch->modifiers[STAT_CO])); // Redundant?? if (IS_SET(ch->vuln_flags, VULN_DROWNING)) dam *= 2; if ( !obj2 && !IS_AFFECTED( ch, AFF_OTTERLUNGS )) { ch->println("Water fills your lungs as you start to drown."); damage( ch, ch, dam, gsn_plague,DAM_DROWNING,false); } if ( obj2 ) { if (!IS_SET( obj2->extra_flags, OBJEXTRA_OTTERLUNGS )) { ch->println("Water fills your lungs as you start to drown."); damage( ch, ch, dam, gsn_plague,DAM_DROWNING,false); } } } if (is_affected(ch, gsn_plague) && ch != NULL) { AFFECT_DATA *af, plague; char_data *vch; int dam; if (ch->in_room == NULL) continue; act("$n writhes in agony as plague sores erupt from $s skin.", ch,NULL,NULL,TO_ROOM); ch->println("You writhe in agony from the plague."); for ( af = ch->affected; af != NULL; af = af->next ) { if (af->type == gsn_plague) break; } if (af == NULL) { REMOVE_BIT(ch->affected_by,AFF_PLAGUE); continue; } if (af->level == 1) continue; plague.where = WHERE_AFFECTS; plague.type = gsn_plague; plague.level = af->level - 1; plague.duration = number_range(1,2 * plague.level); plague.location = APPLY_ST; plague.modifier = -10; plague.bitvector = AFF_PLAGUE; // spread the plague to others for ( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room) { if (!saves_spell(plague.level - 2,vch,DAM_DISEASE) && !IS_ICIMMORTAL(vch) && !IS_AFFECTED(vch,AFF_PLAGUE) && number_bits(3) == 0) { vch->println("You feel hot and feverish."); act("$n shivers and looks very ill.",vch,NULL,NULL,TO_ROOM); affect_join(vch,&plague); } } dam = UMIN(ch->level,af->level/5+1); ch->mana -= dam; ch->move -= dam; damage( ch, ch, dam, gsn_plague,DAM_DISEASE,false); } else if ( IS_AFFECTED(ch, AFF_POISON) && ch != NULL && !IS_AFFECTED(ch,AFF_SLOW)) { AFFECT_DATA *poison; poison = affect_find(ch->affected,gsn_poison); if (poison != NULL) { act( "$n shivers and suffers.", ch, NULL, NULL, TO_ROOM ); ch->println("You shiver and suffer."); damage(ch,ch,poison->level/10 + 1,gsn_poison, DAM_POISON,false); } } else if ( ch->position == POS_INCAP && number_range(0,1) == 0) { damage( ch, ch, 1, TYPE_UNDEFINED, DAM_NONE,false); } else if ( ch->position == POS_MORTAL ) { damage( ch, ch, 1, TYPE_UNDEFINED, DAM_NONE,false); }else if (ch->hit <= -50 ){ // get rid of those that have less than -50 hp // - hack till subdue is written properly if (IS_IMMORTAL(ch)){ ch->hit=ch->max_hit; }else{ damage( ch, ch, 1, TYPE_UNDEFINED, DAM_NONE,false); } } } } // Autosave // Check that these chars still exist. for ( ch = player_list; ch; ch = ch_next ) { ch_next = ch->next_player; if (ch->desc != NULL && ch->desc->connected_socket % 10 == save_number) { save_char_obj(ch); } } // replacement autoquit by Kal - June 98 for ( ch = player_list; ch; ch = ch_next ) { ch_next = ch->next_player; if (ch->idle>4) { if ((ch->idle>5) && IS_LINKDEAD(ch)){ quit_char(ch, "confirm", true); }else if ((ch->idle>29) && !IS_IMMORTAL(ch)){ quit_char(ch, "confirm", true); }else if (ch->idle>60 && !GAMESETTING2(GAMESET2_NO_AUTOLOGOFF_FOR_IMM)){ quit_char(ch, "confirm", true); } if(ch->desc && HAS_CONFIG2(ch, CONFIG2_AUTOKEEPALIVE)){ // anti 'idle timeout'/'keepalive' code // Some ADSL/Cable routers timeout inactive TCP // connections after about 30 minutes. Using IAC NOP // we can ensure data is sent over a tcp connection // while hopefully not displaying anything to the // players connection as IAC NOP should be ignored // by all telnet clients, if a telnet client doesn't // support a basic telnet option implementation // then that is there problem as the client // is too primative *grin* - Kal, Jan 02 :) #define IAC 255 // interpret as command #define NOP 241 // no operation const unsigned char nop_str [] = { IAC, NOP, '\0' }; ch->desc->write((char*)&nop_str[0],2); } } } return; } /**************************************************************************/ // drop nonplaying idle connections etc after 3 minutes void connections_update( void ) { connection_data *c, *c_next; for ( c = connection_list; c; c = c_next ) { c_next = c->next; if (c->connected_state != CON_PLAYING && c->connected_state!= CON_FTP_COMMAND && c->connected_state!= CON_FTP_DATA) { if ((current_time - c->idle_since)>3*60){ c->write("\r\n\r\nYou have been idle too long, disconnecting.\r\n",0); logf("%s %s (%d) dropped (idle too long).", CH(c)?CH(c)->name:"(unknown name)", c->remote_hostname, c->connected_socket); connection_close(c); }else if ((current_time - c->idle_since)>120){ if(c->warned_about_idle==false){ c->write("\r\nNotice: While creating a character, you can be idle for up to around 3 minutes\r\n" " before being disconnected, you have been idle for 2 minutes so far.\r\n",0); c->warned_about_idle=true; } }else{ c->warned_about_idle=false; } } } } /**************************************************************************/ // Update all objs. // This function is performance sensitive. void obj_update( void ) { OBJ_DATA *obj; OBJ_DATA *obj_next; AFFECT_DATA *paf, *paf_next; for ( obj = object_list; obj; obj = obj_next ) { char_data *rch; char *message; obj_next = obj->next; if (obj->carried_by){ if (IS_OOC(obj->carried_by)){ // no update on objects in ooc rooms continue; } } if(obj->item_type==ITEM_WEAPON || obj->item_type==ITEM_ARMOR){ if(obj->condition<obj->pIndexData->condition){ int con=obj->condition; int pcon=obj->pIndexData->condition+1; if(obj->item_type==ITEM_WEAPON){ obj->value[1]=con * obj->pIndexData->value[1]/pcon; obj->value[2]=con * obj->pIndexData->value[2]/pcon; }else{ obj->value[0]=con * obj->pIndexData->value[0]/pcon; obj->value[1]=con * obj->pIndexData->value[1]/pcon; obj->value[2]=con * obj->pIndexData->value[2]/pcon; obj->value[3]=con * obj->pIndexData->value[3]/pcon; } } } // go through affects and decrement for ( paf = obj->affected; paf; paf = paf_next ){ paf_next= paf->next; if ( paf->duration > 0 ){ paf->duration--; if (number_range(0,4) == 0 && paf->level > 0){ paf->level--; // spell strength fades with time } }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_obj ) { if (obj->carried_by != NULL) { rch = obj->carried_by; act(skill_table[paf->type].msg_obj, rch,obj,NULL,TO_CHAR); } if (obj->in_room != NULL && obj->in_room->people != NULL) { rch = obj->in_room->people; act(skill_table[paf->type].msg_obj, rch,obj,NULL,TO_ALL); } } } affect_remove_obj( obj, paf ); } } if( (obj->carried_by == NULL) && (obj->in_obj==NULL)){ if(number_percent()<20 && !IS_SET(obj->extra_flags,OBJEXTRA_NO_DEGRADE)){ obj->condition--; } } if ( (obj->timer <= 0 || --obj->timer > 0) && obj->condition!=0 ) { // FALLING CODE if ( obj->in_room && obj->in_room->sector_type == SECT_AIR && ( obj->wear_flags & OBJWEAR_TAKE ) && !IS_SET( obj->wear_flags, OBJWEAR_FLOAT ) && obj->in_room->exit[5] && obj->in_room->exit[5]->u1.to_room) { ROOM_INDEX_DATA *new_room = obj->in_room->exit[5]->u1.to_room; if (( rch = obj->in_room->people ) != NULL ) { act( "$p falls away.", rch, obj, NULL, TO_ROOM ); act( "$p falls away.", rch, obj, NULL, TO_CHAR ); } obj_from_room(obj); obj_to_room(obj, new_room); if(( rch = obj->in_room->people ) != NULL ) { if( obj->in_room && obj->in_room->sector_type == SECT_AIR && obj->in_room->exit[5] && obj->in_room->exit[5]->u1.to_room ) { act( "$p floats by.", rch, obj, NULL, TO_ROOM ); act( "$p floats by.", rch, obj, NULL, TO_CHAR ); }else{ if( obj->in_room->sector_type == SECT_AIR ){ act( "$p floats in.", rch, obj, NULL, TO_ROOM ); act( "$p floats in.", rch, obj, NULL, TO_CHAR ); }else{ act( "With a resounding thud, $p lands on the floor.", rch, obj, NULL, TO_ROOM ); act( "With a resounding thud, $p lands on the floor.", rch, obj, NULL, TO_CHAR ); } } } } continue; } switch ( obj->item_type ) { default: message = "$p crumbles into dust."; break; case ITEM_FURNITURE: { if ( obj->pIndexData->vnum == OBJ_VNUM_FIRE ) { OBJ_DATA *ashes; message = "$p dies out."; if ( get_obj_index(OBJ_VNUM_ASHES) == NULL ) { bugf("Vnum %d not found for ashes!", OBJ_VNUM_ASHES); return; } ashes = create_object( get_obj_index( OBJ_VNUM_ASHES )); ashes->timer = 12; obj_to_room( ashes, obj->in_room ); } if ( obj->pIndexData->vnum == OBJ_VNUM_DIVINE_LIGHT ) { message = "The mists dissipate."; }else{ message = "$p crumbles into dust."; } 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 decays into dust."; break; case ITEM_FOOD: message = "$p decomposes."; break; case ITEM_POTION: message = "$p has evaporated from disuse."; break; case ITEM_PORTAL: message = "$p fades out of existence."; break; case ITEM_CAULDRON: case ITEM_CONTAINER: case ITEM_FLASK: case ITEM_MORTAR: if (CAN_WEAR(obj,OBJWEAR_FLOAT)){ if (obj->contains){ message = "$p flickers and vanishes, spilling its contents on the floor."; }else{ message = "$p flickers and vanishes."; } }else{ message = "$p crumbles into dust."; } break; } if ( obj->carried_by != NULL ) { if (IS_NPC(obj->carried_by) && obj->carried_by->pIndexData->pShop != NULL) obj->carried_by->silver += obj->cost/5; else { if ( obj->item_type != ITEM_TOKEN ) act( message, obj->carried_by, obj, NULL, TO_CHAR ); if ( obj->wear_loc == WEAR_FLOAT) act(message,obj->carried_by,obj,NULL,TO_ROOM); } } else if ( obj->in_room != NULL && ( rch = obj->in_room->people ) != NULL ) { if ( !(obj->in_obj && obj->in_obj->pIndexData->vnum == OBJ_VNUM_PIT && !CAN_WEAR(obj->in_obj,OBJWEAR_TAKE))) { if ( obj->item_type != ITEM_TOKEN ) { act( message, rch, obj, NULL, TO_ROOM ); act( message, rch, obj, NULL, TO_CHAR ); } } } if ((obj->item_type == ITEM_CORPSE_PC || obj->wear_loc == WEAR_FLOAT) && obj->contains) { // save the contents OBJ_DATA *t_obj, *next_obj; for (t_obj = obj->contains; t_obj; t_obj = next_obj) { next_obj = t_obj->next_content; obj_from_obj(t_obj); if(obj->in_obj){ // in another object obj_to_obj(t_obj,obj->in_obj); }else if (obj->carried_by){ // carried if (obj->wear_loc == WEAR_FLOAT){ if (obj->carried_by->in_room == NULL){ extract_obj(t_obj); }else{ obj_to_room(t_obj,obj->carried_by->in_room); } }else{ obj_to_char(t_obj,obj->carried_by); } }else if (obj->in_room == NULL){ // destroy it extract_obj(t_obj); }else{ // to a room obj_to_room(t_obj,obj->in_room); } } } extract_obj( obj ); } return; } /**************************************************************************/ // Aggression handler - mobs attacking players // for each mortal PC // for each mob in room // aggress on some random PC void aggr_update( void ) { char_data *wch; char_data *wch_next; char_data *ch; char_data *ch_next; char_data *vch; char_data *vch_next; char_data *victim; int charmcount, charmlevels; for ( wch = player_list; wch; wch = wch_next ) { wch_next = wch->next_player; charmcount=0; charmlevels=0; if ( IS_NPC(wch) || wch->level >= LEVEL_IMMORTAL || wch->in_room == NULL || IS_SET(wch->comm, COMM_BUILDING ) || wch->in_room->area->empty || IS_SET(wch->in_room->room_flags,ROOM_SAFE) ) continue; for ( ch = wch->in_room->people; ch != NULL; ch = ch_next ) { int count; ch_next = ch->next_in_room; if( IS_NPC(ch) && ch->master==wch && wch->level<LEVEL_IMMORTAL && IS_AFFECTED(ch, AFF_CHARM)) { charmcount++; charmlevels+=ch->level; } if (// !IS_NPC(ch)|| !IS_UNSWITCHED_MOB(ch) // switched mobs && PC's dont aggy || !IS_SET(ch->act, ACT_AGGRESSIVE) || IS_SET(ch->in_room->room_flags,ROOM_SAFE) || IS_AFFECTED(ch,AFF_CALM) || ch->fighting != NULL || IS_AFFECTED(ch, AFF_CHARM) || !IS_AWAKE(ch) || ( IS_SET(ch->act, ACT_WIMPY) && IS_AWAKE(wch) ) || !can_see( ch, wch ) || number_bits(1) == 0) continue; // aggy mobs dont kill someone who was just killed // and sitting at recall if ( ch->pksafe>0 && ch->in_room->vnum == get_recallvnum(ch)){ continue; } if ( is_affected( wch, gsn_aura_of_temperance ) && (( 12 + wch->level ) > ch->level )){ continue; } /* * Ok we have a 'wch' player character and a 'ch' npc aggressor. * 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->people; vch != NULL; vch = vch_next ) { vch_next = vch->next_in_room; if ( !IS_NPC(vch) && vch->level < LEVEL_IMMORTAL && ch->level >= vch->level - 5 && ( !IS_SET(ch->act, ACT_WIMPY) || !IS_AWAKE(vch) ) && can_see( ch, vch ) ) { if ( number_range( 0, count ) == 0 ) victim = vch; count++; } } if ( victim == NULL ) continue; multi_hit( ch, victim, TYPE_UNDEFINED ); } // handle charmies turning on their masters charmlevels+= charmcount*20; if ((number_range(0,wch->fighting?3:9)==0) && number_range(0, charmlevels - ( wch->level*(wch->fighting?3:5) ) ) > 75) { for ( ch = wch->in_room->people; ch != NULL; ch = ch_next ) { ch_next = ch->next_in_room; if (IS_NPC(ch) && number_range(0,4)==0 && ch->master==wch && IS_AFFECTED(ch, AFF_CHARM)) { char buf[MIL]; // ridden mobs can't turn on you if(wch->mounted_on==ch){ if(number_range(0,get_skill(wch, gsn_riding))>30){ sprintf(buf,"'%s' attempts to turn and buck @ off but fails.", wch->name); do_pmote(ch, buf); }else{ sprintf(buf,"'%s' turns on @ and bucks off @.", wch->name); dismount(wch); do_pmote(ch, buf); affect_strip(ch,gsn_charm_person); multi_hit( ch, wch, TYPE_UNDEFINED); } }else{ affect_strip(ch,gsn_charm_person); sprintf(buf,"'%s' turns on @.", wch->name); do_pmote(ch, buf); multi_hit( ch, wch, TYPE_UNDEFINED); } } } } } return; } /**************************************************************************/ // Kal - Jan 2001 void roomecho_update( void ) { for(char_data *ch = player_list; ch != NULL; ch = ch->next_player){ if( ch->in_room && ch->in_room->echoes) { for( room_echo_data *re = ch->in_room->echoes; re; re = re->next ){ bool display_echo=false; if ( re->firsthour<=re->lasthour){ if(re->firsthour<= time_info.hour && re->lasthour>= time_info.hour && number_percent()<=re->percentage) { display_echo=true; } }else{ if(re->firsthour>= time_info.hour && re->lasthour<= time_info.hour && number_percent()<=re->percentage) { display_echo=true; } } if(display_echo){ if(IS_IMMORTAL(ch) || IS_SET(ch->comm,COMM_BUILDING)){ ch->print("RoomEcho: "); } ch->print(re->echotext); ch->println("`x"); } } } } } /**************************************************************************/ // Daos - Dec. 2001 void areaecho_update( void ) { for(char_data *ch = player_list; ch; ch = ch->next_player) { if( ch->in_room->area && ch->in_room->area->echoes && !IS_SET(ch->in_room->room2_flags, ROOM2_NO_AREA_ECHOES)) { for( area_echo_data *ae = ch->in_room->area->echoes; ae; ae = ae->next ) { bool display_echo=false; if ( ae->firsthour<=ae->lasthour) { if(ae->firsthour<= time_info.hour && ae->lasthour>= time_info.hour && number_percent()<=ae->percentage) { display_echo=true; } }else{ if(ae->firsthour>= time_info.hour && ae->lasthour<= time_info.hour && number_percent()<=ae->percentage) { display_echo=true; } } if(display_echo) { if(IS_IMMORTAL(ch) || IS_SET(ch->comm,COMM_BUILDING)) { ch->print("AreaEcho: "); } ch->print(ae->echotext); ch->println("`x"); } } } } } /**************************************************************************/ int pulse_point; void duel_update(); void dawnstat_update(); void process_mpqueue(); /**************************************************************************/ // 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_area; static int pulse_mobile; static int pulse_violence; static int pulse_affects; static int pulse_minute; static int pulse_dawnstat; if ( --pulse_area <= 0 ) { pulse_area = PULSE_AREA; log_string("Updating Areas"); //number_range( PULSE_AREA / 2, 3 * PULSE_AREA / 2 ); area_update( ); } if ( --pulse_mobile <= 0 ) { pulse_mobile = PULSE_MOBILE; mobile_update ( ); } if ( --pulse_violence <= 0 ) { pulse_violence = PULSE_VIOLENCE; violence_update ( ); duel_update(); } if ( --pulse_affects <= 0 ) // update affects like fear etc { pulse_affects = PULSE_AFFECTS; affects_update ( ); // in affects.c } moot_update(); if ( --pulse_point <= 0 ) { wiznet("TICK!",NULL,NULL,WIZ_TICKS,0,0); fulltime_log_string("Tick"); tick_counter++; pulse_point = number_range( PULSE_TICK / 2, 3 * PULSE_TICK / 2 ); char_update(); connections_update(); // drops idle connections obj_update(); roomecho_update(); areaecho_update(); maintence_saves(); // automatic saving of laston and olc etc } if ( --pulse_minute <= 0) { pulse_minute= PULSE_MINUTE; weather_update ( ); tracktime_update(); // increase the tracktime variable and calls a // reshuffle once every 4 days of continous running // when the unsigned short tracktime goes // from 65k to 0. } if ( --pulse_dawnstat <= 0) { pulse_dawnstat= PULSE_DAWNSTAT; dawnstat_update(); // update dawn statistics } // run the mob queue system process_mpqueue(); aggr_update( ); tail_chain( ); return; } /**************************************************************************/ void moot_check_bring_forward(); /**************************************************************************/ void moot_update(void) { if(moot->pulse<1){ return; } moot->pulse--; if(moot->pulse<=0){ resolve_moot(); }else{ if(moot->pulse%(20*PULSE_PER_SECOND)==0){ broadcast_moot(); } if(moot->pulse==PULSE_MOOT-16){ moot_check_bring_forward(); } } return; } /**************************************************************************/ void drop_level( char_data *ch ) { if(IS_NPC(ch)){ return; } int add_mana; int add_move; int add_prac; ch->level--; ch->pcdata->sublevel=get_sublevels_for_level(ch->level); ch->println("You have lost a LEVEL!!!!!!"); // swap sublevels for xp till reaching the balance while(ch->pcdata->sublevel>0 && XP_PAST_LEVEL(ch)<0) { ch->pcdata->sublevel--; ch->exp+=exp_per_level(ch,ch->pcdata->points); ch->println("You lose a sublevel!"); } add_mana = 1 + (ch->perm_stats[STAT_PR] + ch->perm_stats[STAT_EM] + ch->perm_stats[STAT_IN]) / 20; if (!class_table[ch->clss].fMana) add_mana /= 2; add_move = 1 + number_range( 1, (ch->modifiers[STAT_CO] + ch->modifiers[STAT_QU])/10); add_prac = 3 + ( ch->modifiers[STAT_CO] + ch->modifiers[STAT_AG] + ch->modifiers[STAT_SD] + ch->modifiers[STAT_ME] + ch->modifiers[STAT_RE] ) / 50 ; add_mana = add_mana * 9/10; add_move = add_move * 9/10; add_mana = UMAX( 2, add_mana ); add_move = UMAX( 6, add_move ); ch->max_mana -= add_mana; ch->max_move -= add_move; ch->practice -= add_prac; ch->train -= 2; ch->pcdata->perm_mana -= add_mana; ch->pcdata->perm_move -= add_move; ch->printlnf( "Your gain is: -%d/%d mana, -%d/%d mv, -%d/%d prac.", add_mana, ch->max_mana, add_move, ch->max_move, add_prac, ch->practice ); return; } /**************************************************************************/ void check_perm_damage(char_data *ch) { if ( IS_NPC( ch)){ return; } int hp_loss, mana_loss, move_loss; int stat_loss; if(ch->perm_stats[STAT_CO]<number_percent() && ch->level>10 ) { if(number_percent()<=50) { hp_loss=number_range(0,5); mana_loss=number_range(0,5); move_loss=number_range(0,5); ch->max_hit -= hp_loss; ch->max_mana -= mana_loss; ch->max_move -= move_loss; ch->pcdata->perm_hit -= hp_loss; ch->pcdata->perm_mana -= mana_loss; ch->pcdata->perm_move -= move_loss; ch->printlnf("You have lost %d hp, %d mana, and %d movement.", hp_loss, mana_loss, move_loss); } if(number_percent()<=7) { stat_loss=number_range(1,10); ch->perm_stats[STAT_ST]-=stat_loss; ch->printf("You have been weakened by your defeat by %d strength point%s.", stat_loss, stat_loss==1 ? "" : "s"); } if(number_percent()<=5) { stat_loss=number_range(1,10); ch->perm_stats[STAT_RE]-=stat_loss; ch->printf("You have been feebleminded by your defeat by %d reason point%s.", stat_loss, stat_loss==1 ? "" : "s"); } if(number_percent()<=5) { stat_loss=number_range(1,10); ch->perm_stats[STAT_IN]-=stat_loss; ch->printf("You have been disoriented by your defeat by %d intuition point%s.", stat_loss, stat_loss==1 ? "" : "s"); } if(number_percent()<=5) { stat_loss=number_range(1,10); ch->perm_stats[STAT_QU]-=stat_loss; ch->printf("You have been slowed by your defeat by %d quickness point%s.", stat_loss, stat_loss==1 ? "" : "s"); } if(number_percent()<=15) { stat_loss=number_range(1,10); ch->perm_stats[STAT_CO]-=stat_loss; ch->printf("You have been weakened by your defeat by %d constitution point%s.", stat_loss, stat_loss==1 ? "" : "s"); } }else{ ch->println("Luckily you have not suffered serious injury."); } return; } /**************************************************************************/ void do_heroxp( char_data *ch, int xp ) { char buf[MIL]; if ( IS_NPC( ch )){ return; } if(xp<0){ ch->pcdata->heroxp= UMAX(0, ch->pcdata->heroxp-xp); return; } ch->pcdata->heroxp += xp; if(ch->pcdata->heroxp > 1000 ) { ch->print("You just gained a hero level!!!"); ch->pcdata->heroxp -= 1000; ch->println("You gain 3 pracs and 1 train."); ch->practice += 3; ch->train += 1; sprintf(buf, "%s gained a hero level!!!", ch->name ); wiznet(buf, ch, NULL, WIZ_LEVELS, 0, 0 ); log_string(buf); } if ( ch->pcdata->heroxp < 0 ) ch->pcdata->heroxp = 0; return; } /**************************************************************************/ void room_update( AREA_DATA *pArea ) { ROOM_INDEX_DATA *room; int vnum; for ( vnum = pArea->min_vnum; vnum <= pArea->max_vnum; vnum ++ ) { if ( (room = get_room_index(vnum)) ) room_aff_update(room); } return; } /**************************************************************************/ void room_aff_update( ROOM_INDEX_DATA *room ) { AFFECT_DATA *paf; AFFECT_DATA *paf_next; char_data *pChar; for ( paf = room->affected; paf != NULL; paf = paf_next ) { paf_next = paf->next; if ( paf->duration > 0 ) { paf->duration--; if (number_range(0,4) == 0 && paf->level > 0) paf->level--; // spell's level fades with time } else if ( paf->duration < 0 ) ; else { if ( paf_next == NULL || paf_next->type != paf->type || paf_next->duration > 0 ) { // Display wear off message of spell if ( paf->type > 0 && skill_table[paf->type].msg_off ) { for ( pChar = room->people; pChar; pChar = pChar->next_in_room ) { act( "$t", pChar, skill_table[paf->type].msg_off, NULL, TO_CHAR ); act( "$t", pChar, skill_table[paf->type].msg_off, NULL, TO_ROOM ); break; } } } affect_remove_room( room, paf ); } } } /**************************************************************************/ // WEATHER Stuff static weather_influence_data base_weather = { SKY_CLEAR_RANGE, SKY_CLOUDY_RANGE, SKY_RAINY_RANGE, SKY_LIGHTNING_RANGE }; static weather_influence_data season_influence_data[] = { // CLEAR CLOUDY RAINY LIGHTNING { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_M2 }, // Winter { INFLUENCE_M3, INFLUENCE_M2, INFLUENCE_NONE, INFLUENCE_NONE }, // Spring { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_M2 }, // Summer { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_M3 } // Autumn }; static weather_influence_data sector_weather_table[] = { // CLEAR CLOUDY RAINY LIGHTNING { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_INSIDE { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_CITY { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_P1, INFLUENCE_P1 }, // SECT_FIELD { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_M1 }, // SECT_FOREST { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_M2 }, // SECT_HILLS { INFLUENCE_NONE, INFLUENCE_P2, INFLUENCE_P2, INFLUENCE_NONE}, // SECT_MOUNTAIN { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_WATER_SWIM { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_WATER_NOSWIM { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_SWAMP { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_M3, INFLUENCE_NONE}, // SECT_AIR { INFLUENCE_P4, INFLUENCE_M4, INFLUENCE_M4, INFLUENCE_P4 }, // SECT_DESERT { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_CAVE { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_UNDERWATER { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_SNOW { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_ICE { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE}, // SECT_TRAIL { INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE} // SECT_LAVA }; static int weather_sector_conversion[SECT_MAX][SKY_MAX] = { { 0,1,2,3}, // SECT_INSIDE { 0,1,2,3}, // SECT_CITY { 0,1,2,3}, // SECT_FIELD { 0,1,2,3}, // SECT_FOREST { 0,1,2,3}, // SECT_HILLS { 0,1,2,3}, // SECT_MOUNTAIN { 0,1,2,3}, // SECT_WATER_SWIM { 0,1,2,3}, // SECT_WATER_NOSWIM { 0,1,2,3}, // SECT_SWAMP { 0,1,2,3}, // SECT_AIR { 0,0,0,3}, // SECT_DESERT { 0,1,2,3}, // SECT_CAVE { 0,1,2,3}, // SECT_UNDERWATER { 0,1,2,3}, // SECT_SNOW { 0,1,2,3}, // SECT_ICE { 0,1,2,3}, // SECT_TRAIL { 0,1,2,3} // SECT_LAVA }; static int master_weather_table[SECT_MAX][WEATHER_SEASON_MAX][SKY_MAX]; static char * weather_message[SECT_MAX][8] = { // SECT_INSIDE (No Weather Messages) { // Cloudless "", "", // Cloudy "", "", // Rainy "", "", // Lightning "", "" }, // SECT_CITY { // Cloudless "Above the city, the clouds disperse.\r\n", "", // Cloudy "The soft drumbeat of rain on rooftops stops.\r\n", "Dark clouds gather far above the rooftops.\r\n", // Rainy "The echo of thunder rolling off nearby buildings fades.\r\n", "Rain begins to drench the city.\r\n", // Lightning "", "Bright flashes light up the sky and thunder crashes down.\r\n" }, // SECT_FIELD { // Cloudless "A clear sky pokes through the cloud cover.\r\n", "", // Cloudy "The fields are no longer bathed in rain.\r\n", "The fields darken as clouds roll in, obscuring sky.\r\n", // Rainy "The peals of thunder fade into silence.\r\n", "The rains begin, drenching you to the bone.\r\n", // Lightning "", "Thunder and lightning assault the open field.\r\n" }, // SECT_FOREST { // Cloudless "The trees breathe as the sky clears.\r\n", "", // Cloudy "The rain slows to a drizzle and then stops as suddenly as it started.\r\n", "The forest hushes as clouds gather, seeming to anticipate the rain.\r\n", // Rainy "The thunder that shook the forest rumbles one last time and is gone.\r\n", "The raindrops falling in the forest make a soothing sound.\r\n", // Lightning "", "With great force, a bolt of lightning strikes a tree near you.\r\n" }, // SECT_HILLS { // Cloudless "The hilltops are no longer obscured by clouds.\r\n", "", // Cloudy "It stops raining.\r\n", "Silence settles on the countryside as clouds loom overhead.\r\n", // Rainy "The echoes of thunder on distant hills die away.\r\n", "The thirsty ground drink the rain as it begins to pour.\r\n", // Lightning "", "Lightning lights up the sky and thunder crashes all around you.\r\n" }, // SECT_MOUNTAINS { // Cloudless "The mountains reach majestically towards a clear sky.\r\n", "", // Cloudy "The rain stops falling from blackened clouds.\r\n", "Heavy storm clouds gather.\r\n", // Rainy "The deafening thunder comes to a halt.\r\n", "Rain pours mercilessly down upon you.\r\n", // Lightning "", "Thunder roars and lightning crackles in the sky.\r\n" }, // SECT_WATER_SWIM { // Cloudless "The sky appears to clear from the cloud cover.\r\n", "", // Cloudy "The cacophony of rain dies down slowly.\r\n", "The water darkens as clouds form overhead.\r\n", // Rainy "The distant sound of thunder fades.\r\n", "The rainfall is almost overwhelming, seeming to come from every direction.\r\n", // Lightning "", "Flashes light the water around you as the air about you explodes in violent thunder.\r\n" }, // SECT_WATER_NO_SWIM { // Cloudless "The sky appears to clear from the cloud cover.\r\n", "", // Cloudy "The cacophony of rain dies down slowly.\r\n", "The water darkens as clouds form overhead.\r\n", // Rainy "The distant sound of thunder fades.\r\n", "The rainfall is almost overwhelming, seeming to come from every direction.\r\n", // Lightning "", "Flashes light the water around you as the air about you explodes in violent thunder.\r\n" }, // SECT_SWAMP { // Cloudless "The clouds overhead clear away.\r\n", "", // Cloudy "The rain stops falling.\r\n", "Clouds roll in, turning the sky dark and oppressive.\r\n", // Rainy "The distant sound of thunder fades away.\r\n", "The rain begins to fall turning the ground into a great mud pit.\r\n", // Lightning "", "Peals of thunder and arcs of lightning overwhelm your senses.\r\n", }, // SECT_AIR { // Cloudless "The clouds around you dissipate.\r\n", "", // Cloudy "The rain falling around you tapers off.\r\n", "Clouds begin to form around you.\r\n", // Rainy "Flashes of lightning and thunderous claps die down.\r\n", "Rain forms all around you and makes it's descent to the ground.\r\n", // Lightning "", "Thunder shakes you about as lightning arcs dangerously close to you.\r\n" }, // SECT_DESERT { // Cloudless "The cruel sky clears.\r\n", "", // Cloudy "", "", // Rainy "", "", // Lightning "", "Thunder rolls across the desert as an electrical storm comes to life.\r\n" }, // SECT_CAVE (No Weather Messages) { // Cloudless "", "", // Cloudy "", "", // Rainy "", "", // Lightning "", "" }, // SECT_UNDERWATER (No Weather Messages) { // Cloudless "", "", // Cloudy "", "", // Rainy "", "", // Lightning "", "" }, // SECT_SNOW { // Cloudless "A blinding glare is created as the sun shines down upon the snow.\r\n", "", // Cloudy "The clouds gather making it incredibly cold.\r\n", "", // Rainy "The rain sticks to the snow making it very slick and wet.\r\n", "", // Lightning "", "" }, // SECT_ICE { // Cloudless "The sun shines creating a glare off the ice.\r\n", "", // Cloudy "", "", // Rainy "The rain sticks to the ice, making it more slick.\r\n", "", // Lightning "", "", }, // SECT_TRAIL { // Cloudless "The sun gleams down on the open trail.\r\n", "Rays of sunlight break through the clouds.\r\n", // Cloudy "The clouds gather, darkening the trail ahead.\r\n", "The clouds darken making the trail hard to see.\r\n", // Rainy "Rain falls from the clouds above, making the trail very wet.\r\n", "High waters rise up onto the trail as a heavy downpour occurs.\r\n", // Lightning "Lightning flashes, illuminating the trail ahead of you.\r\n", "Violent roars of thunder clash as lightning flashes ahead of you.\r\n" }, // SECT_LAVA { // Cloudless "", "", // Cloudy "", "", // Rainy "Rain falls on the molten lava creating a hot steam.\r\n", "The heavy downpour turns the steaming lava into rock.\r\n", // Lightning "", "" } }; /**************************************************************************/ int calculate_season( void ) { int season = 0; int day; day = time_info.month * ICTIME_DAYS_PER_MONTH + time_info.day; if ( day < ( ICTIME_DAYS_PER_YEAR / 8 )) { season = WEATHER_SEASON_WINTER; } else if ( day < ( ICTIME_DAYS_PER_YEAR / 8 ) * 3 ) { season = WEATHER_SEASON_SPRING; } else if ( day < ( ICTIME_DAYS_PER_YEAR / 8 ) * 5 ) { season = WEATHER_SEASON_SUMMER; } else if ( day < ( ICTIME_DAYS_PER_YEAR / 8 ) * 7 ) { season = WEATHER_SEASON_AUTUMN; } else { season = WEATHER_SEASON_WINTER; } return season; } /**************************************************************************/ void update_daylight( char buf[SECT_MAX][MSL]) { int sector; ++time_info.hour; for ( sector = 0; sector < SECT_MAX; sector++ ) { switch ( time_info.hour ) { case HOUR_DAY_BEGIN: weather_info[sector].sunlight = SUN_LIGHT; strcat(buf[sector], "Light begins to spill through the misty night.\r\n"); break; case HOUR_SUNRISE: weather_info[sector].sunlight = SUN_RISE; strcat(buf[sector], "The sun rises in the east.\r\n"); break; case HOUR_SUNSET: weather_info[sector].sunlight = SUN_SET; strcat(buf[sector], "The darkness of night begins to descend.\r\n"); break; case HOUR_NIGHT_BEGIN: weather_info[sector].sunlight = SUN_DARK; strcat(buf[sector], "The day turns night and the dark mist descends choking out the stars.\r\n" ); break; } } // Mage Mod stuff here, made seperate switch to keep it more localized and readable for ( sector = 0; sector < SECT_MAX; sector++ ) { switch ( time_info.hour ) { case 1: weather_info[sector].mage_castmod = get_magecastmod(); break; case 2: weather_info[sector].mage_castmod = get_magecastmod()*3/4; break; case 3: weather_info[sector].mage_castmod = get_magecastmod()/2; break; case 4: weather_info[sector].mage_castmod = get_magecastmod()/4; break; case 5: weather_info[sector].mage_castmod =0; break; case 20: weather_info[sector].mage_castmod = get_magecastmod()/4; break; case 21: weather_info[sector].mage_castmod = get_magecastmod()/2; break; case 22: weather_info[sector].mage_castmod = get_magecastmod()*3/4; break; case 23: weather_info[sector].mage_castmod = get_magecastmod(); break; } } if ( time_info.hour == HOUR_MIDNIGHT ) { time_info.hour = 0; time_info.day++; } if ( time_info.day >= ICTIME_DAYS_PER_MONTH-1 ) { time_info.day = 0; time_info.month++; } if ( time_info.month >= ICTIME_MONTHS_PER_YEAR-1 ) { time_info.month = 0; time_info.year++; } } /**************************************************************************/ void weather_update(void) { char buf[SECT_MAX][MSL]; connection_data *d; int season; int diff; int sect; int sky; int i; bool changed = false; int dir = 0; if (++time_info.minute<60) // Does an update only once per IC hour return; time_info.minute=0; // NULLIFY it all for ( i = 0; i < SECT_MAX; i++ ) { buf[i][0] = '\0'; } update_daylight(buf); // Increment hour, check if day increments // check month increment, check year increment // get mage cast mods, handle day night messages season = calculate_season(); // Season isn't fully supported yet, possible ideas: // 1. Seasons affecting day length // 2. Seasons affecting druid season spheres? // // Season currently affects weather influence, ie rainier, drier, sunnier etc int number_in_game=mobile_count+1000; // note: number_in_game is used to reduce potential loops // with a mobprog that creates new mobs. // do hour triggers { char_data *ch; for ( ch = char_list; ch && --number_in_game>=0; ch = ch->next ) { if ( IS_NPC( ch ) && HAS_TRIGGER( ch, TRIG_HOUR )) mp_hour_trigger( ch ); } } // go through all the sector types, so now instead of 1 pass, we have like 13 :) // good thing we're not running this mud on a C64 for( sect = 0; sect < SECT_MAX; sect++ ) { int change_factor; changed = false; sky = weather_info[sect].sky; weather_info[sect].mmhg += weather_info[sect].change; // See what it's like at the moment change_factor = number_range(1,50);///////////////////////////////////// // change will be positive if(weather_info[sect].sky == 0) { if ( weather_info[sect].mmhg > master_weather_table[sect][season][weather_info[sect].sky]) { sky = weather_sector_conversion[sect][weather_info[sect].sky + 1]; changed = true; dir = 0; } } // change will be negative else if(weather_info[sect].sky == SKY_MAX - 1) { if(weather_info[sect].mmhg < master_weather_table[sect][season][weather_info[sect].sky - 1]) { sky = weather_sector_conversion[sect][weather_info[sect].sky - 1]; changed = true; dir = -1; } } else { if(weather_info[sect].mmhg > master_weather_table[sect][season][weather_info[sect].sky + 1]) { sky = weather_sector_conversion[sect][weather_info[sect].sky + 1]; changed = true; } else if(weather_info[sect].mmhg < master_weather_table[sect][season][weather_info[sect].sky - 1]) { sky = weather_sector_conversion[sect][weather_info[sect].sky - 1]; changed = true; dir = -1; } } // Sky changed, reset counters if(changed) { // Determine amount to change the weather if(sky == 0) { weather_info[sect].change = change_factor; // reset mmhg in the middle of the current sky factor // weather_info[sect].mmhg = master_weather_table[sect][season][sky] / 2; // uncomment above to lean towards more sunshine weather_info[sect].mmhg = 0; } else if(sky == SKY_MAX - 1) // SKY_LIGHTNING { weather_info[sect].change = -1 * change_factor; // reset mmhg in the middle of the current sky factor weather_info[sect].mmhg = (master_weather_table[sect][season][sky] - master_weather_table[sect][season][sky - 1]) / 2 + master_weather_table[sect][season][sky]; } else { diff = ((number_range(1,10)) < 5) ? -1 : 1; weather_info[sect].change = diff * change_factor; // reset mmhg in the middle of the current sky factor weather_info[sect].mmhg = (master_weather_table[sect][season][sky] - master_weather_table[sect][season][sky - 1]) / 2 + master_weather_table[sect][season][sky + dir]; } } // Generate a proper change message if(changed) { if( sky < weather_info[sect].sky) { strcat(buf[sect], weather_message[sect][sky * 2]); } else { strcat(buf[sect], weather_message[sect][sky * 2 + 1]); } weather_info[sect].sky = sky; } } for ( d = connection_list; d != NULL; d = d->next ) { if ( d->connected_state == CON_PLAYING && IS_OUTSIDE(d->character) && IS_AWAKE(d->character) && IS_IC(d->character) && !d->editor && buf[d->character->in_room->sector_type][0] != '\0' ) { d->character->printf( "%s", buf[d->character->in_room->sector_type] ); // lighting the world up with some sounds // msp_to_room(MSPT_WEATHER, msp_buf, 0, d->character,true,false); } } return; } /**************************************************************************/ // testing code so you don't have to wait for 12 hours to pass so you can // see how something works in the dark/light // - Kal, Aug 03 void do_setichour(char_data *ch, char *argument) { if(IS_NULLSTR(argument) || !is_number(argument)){ ch->println("syntax: setichour <0-23>"); ch->wrapln("This command makes the IC time jump to a given hour... " "the command was written to aid in developing the code and can be " "happily ignored by all non developers."); return; } int i=atoi(argument); if(i<0 || i>23){ ch->println("The numeric value must be between 0 and 23"); return; } time_info.minute=59; time_info.hour=i-1; weather_update(); ch->printlnf("The time is now: %d:%02d%s", (time_info.hour % 12 == 0) ? 12 : time_info.hour % 12, time_info.minute, time_info.hour >= 12 ? "pm" : "am"); } /**************************************************************************/ void init_weather(void) { int sect, seas; for (sect = 0; sect < SECT_MAX; sect++) { weather_info[sect].change = number_range(1,50); weather_info[sect].mmhg = 200; // Something arbitrary weather_info[sect].sky = SKY_CLOUDLESS; for(seas = 0; seas < 4; seas++) // 4 = Amount of seasons!!! { int cur = 0; // Each value is an accumulation of the preceeding cur += UMAX(base_weather.sky_clear + season_influence_data[seas].sky_clear + sector_weather_table[sect].sky_clear, 0); master_weather_table[sect][seas][SKY_CLOUDLESS] = cur; cur += UMAX(base_weather.sky_cloudy + season_influence_data[seas].sky_cloudy + sector_weather_table[sect].sky_cloudy, 0); master_weather_table[sect][seas][SKY_CLOUDY] = cur; cur += UMAX(base_weather.sky_rainy + season_influence_data[seas].sky_rainy + sector_weather_table[sect].sky_rainy, 0); master_weather_table[sect][seas][SKY_RAINING] = cur; cur += UMAX(base_weather.sky_lightning + season_influence_data[seas].sky_lightning + sector_weather_table[sect].sky_lightning, 0); master_weather_table[sect][seas][SKY_LIGHTNING] = cur; } } weather_update(); } /**************************************************************************/ /**************************************************************************/ /**************************************************************************/