/**************************************************************************/
// 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"
#include "olc.h"
// function prototypes
DECLARE_DO_FUN( do_quit );
DECLARE_DO_FUN( do_say );
DECLARE_DO_FUN( do_land ); // Kal
void update_roulette(void);
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;
int global_exp;
DECLARE_DO_FUN( do_pmote );
DECLARE_DO_FUN( do_gecho );
// 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(
"`cYour gain is: %d/%d hp, %d/%d mana, %d/%d mv %d/%d prac.`x",
add_hp, ch->max_hit,
add_mana, ch->max_mana,
add_move, ch->max_move,
add_prac, ch->practice );
}else{
ch->printlnf(
"`cYour gain is: %d/%d mana, %d/%d mv %d/%d prac.`x",
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("`cYou 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;
}
}
// 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("`cYou have been automatically letheroed!`x");
SET_BIT(ch->act,PLR_CAN_HERO);
logf("`c%s has been automatically letheroed.`x", 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("`cYou can't hero till you complete your hero quest!`x");
sprintf(buf,"`c$N would have HEROED, but isn't letheroed!`x");
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");
}
}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("`cYou are hungry.`x");
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("`cYou are thirsty.`x");
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("`cYou are sober.`x");
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( "`c$n gets $p.`x", 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=30;
}
}
if(ch->idle >= autoafkafter && !IS_SET(TRUE_CH(ch)->comm,COMM_AFK))
{
if(ch->level >= LEVEL_IMMORTAL){
ch->println("`cIdle 30 Ticks. Auto AFK Enabled.`x");
}else{
ch->println("`cIdle 5 Ticks. Auto AFK Enabled.`x");
}
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( "`c$n disappears into the void.`x",
ch, NULL, NULL, TO_ROOM );
ch->println("`cYou disappear into the void.`x");
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("`cYou just gained some more votes...`x "
"`ckeep giving positive moots to everyone.`x");
}
}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("`cYou just gained some more votes...`x "
"`cremember that those with less than 1200 votes get given votes`x "
"`cmore frequently so keep giving positive moots to players.`x");
}
}
}
}
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("`cYou awake feeling refreshed.`x");
act( "`c$n awakes very chipper and refreshed.`x", 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("`cYou awake somewhat groggy and confused.`x");
act( "`c$n awakes very reluctantly.`x", 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("`bHypnos `c-God of `bSleep `cflies you off to the dreamworld.`x");
act( "`c$n drifts to sleep.`x", 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("`cYou are so tired you fall off your mount!`x");
}
ch->println("`cYou groggily collapse from exhaustion.`x");
act( "`c$n collapses from exhaustion.`x", ch, NULL, NULL, TO_ROOM );
}
else
ch->println("`cYou are very battle weary.`x");
}
else if(ch->pcdata->tired>32)
ch->println("`cYou feel extremely tired.`x");
else if(ch->pcdata->tired>25)
ch->println("`cYou feel very tired.`x");
else if(ch->pcdata->tired>16)
ch->println("`cYou feel tired.`x");
}
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("`c$n wanders on home.`x",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("`cYou are so exhausted from your flying you land on the ground.`x");
do_land(ch,"");
}else if(number_range(1,10)==1){
ch->println("`cAll this flying is making you tired.`x");
}
}
}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( "`c$p goes out.`x", ch, obj, NULL, TO_ROOM );
act( "`c$p flickers and goes out.`x", ch, obj, NULL, TO_CHAR );
extract_obj( obj );
}
else if ( obj->value[2] <= 5 && ch->in_room != NULL)
act("`c$p flickers.`x",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( "`c$n disappears into the void.`x",
ch, NULL, NULL, TO_ROOM );
ch->println("`cYou disappear into the void.`x");
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("`c$n's deteriorating condition is very apparent.`x",
ch,NULL,NULL,TO_ROOM);
ch->println("`rYou lose even more blood.`x");
}
// 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("`cYou start to burn.`x");
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("`cYou are no longer stunned.`x");
}
}
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("`cWater fills your lungs as you start to drown.`x");
damage( ch, ch, dam, gsn_plague,DAM_DROWNING,false);
}
if ( obj2 ) {
if (!IS_SET( obj2->extra_flags, OBJEXTRA_OTTERLUNGS ))
{
ch->println("`cWater fills your lungs as you start to drown.`x");
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("`c$n writhes in agony as plague sores erupt from $s skin.`x",
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("`cYou feel hot and feverish.`x");
act("`c$n shivers and looks very ill.`x",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( "`c$n shivers and suffers.`x", ch, NULL, NULL, TO_ROOM );
ch->println("`cYou shiver and suffer.`x");
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);
}
}
if ( global_exp-- > 0)
{
if(IS_IMMORTAL(ch) && global_exp % 10 == 0)
{
ch->printlnf("`cThere are %d ticks left in double exp.`x", global_exp);
}
if (global_exp == 0)
{
do_gecho(ch,"`cThe Gods cause the gains of double experience to end.`x" );
double_exp = false;
return;
}
}
}
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\n`cYou have been idle too long, disconnecting.`x\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\n`cNotice: While creating a character, you can be idle for up to around 3 minutes`x\r\n"
" `cbefore being disconnected, you have been idle for 2 minutes so far.`x\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( "`c$p falls away.`x", rch, obj, NULL, TO_ROOM );
act( "`c$p falls away.`x", 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( "`c$p floats by.`x", rch, obj, NULL, TO_ROOM );
act( "`c$p floats by.`x", rch, obj, NULL, TO_CHAR );
}else{
if( obj->in_room->sector_type == SECT_AIR ){
act( "`c$p floats in.`x", rch, obj, NULL, TO_ROOM );
act( "`c$p floats in.`x", rch, obj, NULL, TO_CHAR );
}else{
act( "`cWith a resounding thud, $p lands on the floor.`x", rch, obj, NULL, TO_ROOM );
act( "`cWith a resounding thud, $p lands on the floor.`x", rch, obj, NULL, TO_CHAR );
}
}
}
}
continue;
}
switch ( obj->item_type )
{
// default: message = "`c$p crumbles to dust.`x"; break;
case ITEM_FURNITURE: {
if ( obj->pIndexData->vnum == OBJ_VNUM_FIRE ) {
OBJ_DATA *ashes;
message = "`c$p dies out.`x";
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 = "`cThe mists dissipate.`x";
}else{
message = "`c$p crumbles into dust.`x";
}
break;
}
case ITEM_FOUNTAIN: message = "`c$p dries up.`x"; break;
case ITEM_CORPSE_NPC: message = "`c$p decays into dust.`x"; break;
case ITEM_CORPSE_PC: message = "`c$p decays into dust.`x"; break;
case ITEM_FOOD: message = "`c$p decomposes.`x"; break;
case ITEM_POTION: message = "`c$p has evaporated from disuse.`x";
break;
case ITEM_PORTAL: message = "`c$p fades out of existence.`x"; break;
case ITEM_CAULDRON:
case ITEM_CONTAINER:
case ITEM_FLASK:
case ITEM_MORTAR:
if (CAN_WEAR(obj,OBJWEAR_FLOAT)){
if (obj->contains){
message = "`c$p flickers and vanishes, spilling its contents on the floor.`x";
}else{
message = "`c$p flickers and vanishes.`x";
}
}else{
message = "`c$p crumbles into dust.`x";
}
break;
default: message = "`c$p crumbles to dust.`x"; 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,"`c'%s' attempts to turn and buck @ off but fails.`x", wch->name);
do_pmote(ch, buf);
}else{
sprintf(buf,"`c'%s' turns on @ and bucks off @.`x", 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,"`c'%s' turns on @.`x", 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("`cRoomEcho: ");
}
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("`cAreaEcho: ");
}
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();
update_roulette();
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("`YYou have lost a LEVEL!!!!!!`x");
// 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(
"`cYour gain is: -%d/%d mana, -%d/%d mv, -%d/%d prac.`x",
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("`cYou have lost %d hp, %d mana, and %d movement.`x",
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("`cYou have been weakened by your defeat by %d strength point%s.`x",
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("`cYou have been feebleminded by your defeat by %d reason point%s.`x",
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("`cYou have been disoriented by your defeat by %d intuition point%s.`x",
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("`cYou have been slowed by your defeat by %d quickness point%s.`x",
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("`cYou have been weakened by your defeat by %d constitution point%s.`x",
stat_loss, stat_loss==1 ? "" : "s");
}
}else{
ch->println("`cLuckily you have not suffered serious injury.`x");
}
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("`cYou just gained a hero level!!!`x");
ch->pcdata->heroxp -= 1000;
ch->println("cYou gain 3 pracs and 1 train.`x");
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( "`c$t`x", pChar, skill_table[paf->type].msg_off, NULL, TO_CHAR );
act( "`c$t`x", 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
{ INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE, INFLUENCE_NONE} // SECT_HOME
};
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
{ 0,1,2,3} // SECT_HOME
};
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
"`cHigh above the city, Zephyrus - `BGod`c of the `BWest`c `BWinds`c clears the sky.`x\r\n",
"",
// Cloudy
"`cThe light rain trinkles briefly before ending.`x\r\n",
"`cVicious storm clouds gather over the rooftops.`x\r\n",
// Rainy
"`cThe thunderous sounds of the storm subside.`x\r\n",
"`cThe entire city is battered by a cold rain.`x\r\n",
// Lightning
"",
"`WZeus - `RKing `Wof the `RGods`c has hurled a thunderbolt somewhere. A flash streaks across the sky.`x\r\n"
},
// SECT_FIELD
{
// Cloudless
"`cA pristine sky emerges from the moving clouds.`x\r\n",
"",
// Cloudy
"`cA light shadow stretches across the land as cloud cover forms.`x\r\n",
"`cThe fields lay in a thin blanket of darkness as clouds cover the sky.`x\r\n",
// Rainy
"`cThe distant thunder ends.`x\r\n",
"`cA cold rain splatters over the fields.`x\r\n",
// Lightning
"",
"`cThunderbolts crack violently over the open fields.`x\r\n"
},
// SECT_FOREST
{
// Cloudless
"`cBrilliant light streams through the treetops, reaching the forest floor.`x\r\n",
"",
// Cloudy
"`cThe rain quickly subsides. Water runs off every plant and rock in the drenched woods.`x\r\n",
"`cThe forest wildlife becomes very still and quiet as a storm approaches.`x\r\n",
// Rainy
"`WZeus - `RKing `Wof the `RGods`c has finished. His thunder is now quiet.`x\r\n",
"`cThe forest is a large instrument played by the rain. It creates a beautiful melody.`x\r\n",
// Lightning
"",
"`cWith a loud crack, a thunderbolt hits the ground in the distance.`x\r\n"
},
// SECT_HILLS
{
// Cloudless
"`cShadows fade as a full bath of sunlight washes over the hills.`x\r\n",
"",
// Cloudy
"`cThe downpour ceases, ending the cascade of water that had rolled down the hills.`x\r\n",
"`cA harsh stillness invades the air as a bank of clouds appear overhead.`x\r\n",
// Rainy
"`cThe thunderous echos off in the distance end suddenly.`x\r\n",
"`cAs a heavy rain pours over the hills, small steams form to carry it down.`x\r\n",
// Lightning
"",
"`cThe sky is filled with a brilliant display of lightning and loud booms of thunder.`x\r\n"
},
// SECT_MOUNTAINS
{
// Cloudless
"`cAs the sky clears over the mountain, a pristine and far reaching view is created.`x\r\n",
"",
// Cloudy
"`cThe storm subsides, leaving the rocks of the mountain glittering with dampness.`x\r\n",
"`cA large storm approaches the mountain, intent to pass over it.`x\r\n",
// Rainy
"`cWithout warning, the thunder quits, leaving an almost deafening silence.`x\r\n",
"`cRain meticulously covers everything on the mountain.`x\r\n",
// Lightning
"",
"`cAcross a huge distance, numerous thunderbolts are seen crashing across the land below.`x\r\n"
},
// SECT_WATER_SWIM
{
// Cloudless
"`cThe clouds disappear as `BZephyrus`c - `BGod`c of the `BWest Winds`c carries them away.`x\r\n",
"",
// Cloudy
"`cThe storm's temperment fades quickly.`x\r\n",
"`cThe water begins to churn as a storm appears in the sky.`x\r\n",
// Rainy
"`cThe waves calm and a peaceful tone resounds over the water.`x\r\n",
"`cThe choppy water crashes against anything in its path.`x\r\n",
// Lightning
"",
"`BPoseidon `W- `BGod `Wof the `BSea`c sends out a wall of water that wreaks havoc over the sea.`x\r\n"
},
// SECT_WATER_NO_SWIM
{
// Cloudless
"`cThe clouds disappear as `BZephyrus`c - `BGod`c of the `BWest Winds`c carries them away.`x\r\n",
"",
// Cloudy
"`cThe storm's temperment fades quickly.`x\r\n",
"`cThe water begins to churn as a storm appears in the sky.`x\r\n",
// Rainy
"`cThe waves calm and a peaceful tone resounds over the water.`x\r\n",
"`cThe choppy water crashes against anything in its path.`x\r\n",
// Lightning
"",
"`BPoseidon `W- `BGod `Wof the `BSea`c sends out a wall of water that wreaks havoc over the sea.`x\r\n"
},
// SECT_SWAMP
{
// Cloudless
"`cThe sky is wiped clean of cloud cover.`x\r\n",
"",
// Cloudy
"`cThe showers cease.`x\r\n",
"`cVast clouds sweep across the horizon as a storm brews.`x\r\n",
// Rainy
"`cAll is silent as the storm subsides.`x\r\n",
"`cThe swampland absorbs the falling rain, creating a messy stew.`x\r\n",
// Lightning
"",
"`cThe sky burns as a white streak of lightning arcs through it.`x\r\n",
},
// SECT_AIR
{
// Cloudless
"`cAs the clouds thin, a sky perfect in shade replaces them.`x\r\n",
"",
// Cloudy
"`cDecreasing steadily, the rain ends.`x\r\n",
"`cA strong wind carries a batch of rain clouds.`x\r\n",
// Rainy
"`cRolling off into the distance, the storm leaves.`x\r\n",
"`cClouds coagulate, forming a storm front of massive proportions.`x\r\n",
// Lightning
"",
"`cLighning streaks in all directions here as a loud sizzle sounds near by.`x\r\n"
},
// SECT_DESERT
{
// Cloudless
"`cThe sky is devoid of any shade producing clouds.`x\r\n",
"",
// Cloudy
"",
"",
// Rainy
"",
"",
// Lightning
"",
"`cA thunderstorm quickly lays seige to the sand.`x\r\n"
},
// SECT_CAVE (No Weather Messages)
{
// Cloudless
"",
"",
// Cloudy
"",
"",
// Rainy
"",
"",
// Lightning
"",
""
},
// SECT_UNDERWATER (No Weather Messages)
{
// Cloudless
"",
"",
// Cloudy
"",
"",
// Rainy
"",
"",
// Lightning
"",
""
},
// SECT_SNOW
{
// Cloudless
"`cA sharply round sun burns the snow here, creating small wisps of steam.`x\r\n",
"",
// Cloudy
"`cA warm blanket of clouds insulates the land.`x\r\n",
"",
// Rainy
"`cFreezing rain and snow intermix to create an instand slush on the ground.`x\r\n",
"",
// Lightning
"",
""
},
// SECT_ICE
{
// Cloudless
"`cThe sun's weak attempts to warm the region fail, even as the clouds clear.`x\r\n",
"",
// Cloudy
"",
"",
// Rainy
"`cRain coats the icy terrain, making it unusually hap hazardous.`x\r\n",
"",
// Lightning
"",
"",
},
// SECT_TRAIL
{
// Cloudless
"`cRays of light steak down from the sun chariot, warming the earth of the open trail.`x\r\n",
"`cThe clouds disperse as warm sunshine shines through.`x\r\n",
// Cloudy
"`cThe path becomes enshadowed as clouds block out the sun.`x\r\n",
"`cThe open trail darkens as a storm grows overhead.`x\r\n",
// Rainy
"`cA falling rain splatters against the trail, making it wet and slippery.`x\r\n",
"`cPuddles and deep streams weave through the path as the rain continues.`x\r\n",
// Lightning
"`cA brief glipse of the trail ahead is afforded to you by a flash of lightning.`x\r\n",
"`cThunderous booms sound in the distance.`x\r\n"
},
// SECT_LAVA
{
// Cloudless
"",
"",
// Cloudy
"",
"",
// Rainy
"`cA heavy rain falls down on the lava, creating a plume of steam as Hephaestus begins his work.`x\r\n",
"`cOut of the volcano forge, rock is formed as a rain cools the lava.`x\r\n",
// Lightning
"",
""
},
// SECT_HOME
{
// Cloudless
"",
"",
// Cloudy
"",
"",
// Rainy
"",
"",
// 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], "`YThe Sun Chariot`c begins its daily journey across the sky.`x\r\n");
break;
case HOUR_SUNRISE:
weather_info[sector].sunlight = SUN_RISE;
strcat(buf[sector], "`YEos - Goddess of the Dawn`c spills light through the misty night.`x\r\n");
break;
case HOUR_SUNSET:
weather_info[sector].sunlight = SUN_SET;
strcat(buf[sector], "`BHespera`c - Goddess of `BDusk`c creates a darkness that descends across the horizon.`x\r\n");
break;
case HOUR_NIGHT_BEGIN:
weather_info[sector].sunlight = SUN_DARK;
strcat(buf[sector], "`cWith a complete departure of daylight, the `Wbrilliant stars`c of the night shine.`x\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("`csyntax: setichour <0-23>`x");
ch->wrapln("`cThis command makes the IC time jump to a given hour...`x "
"`cthe command was written to aid in developing the code and can be`x "
"`chappily ignored by all non developers.`x");
return;
}
int i=atoi(argument);
if(i<0 || i>23){
ch->println("`cThe numeric value must be between 0 and 23.`x");
return;
}
time_info.minute=59;
time_info.hour=i-1;
weather_update();
ch->printlnf("`cThe time is now: %d:%02d%s.`x",
(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();
}
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/