/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

weather.c				Weather and time functionality is
					contained in this file.  Though
					based on CircleMUD2.2/Diku weather
					and time, a signifigant amount of
					changes have taken place...

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "acmd.h"
#include "db.h"
#include "lists.h"
#include "fight.h"
#include "shaman.h"
#include "global.h"
#include "weather.h"
#include "plshop.h"

// as a year passes, update some stuff
void another_year(void)
{
  mudlog("Another year has passed.", BUG, LEV_IMM, TRUE);
  time_info.month = 0;
  time_info.year++;
  sprintf(buf, "%%B%%6A new year has dawned! (%d)%%0\n\r",time_info.year);
  send_to_outdoor(buf);

  // update the plshops around the land
  yearly_plshop_update();
}

// as a month passes, update some stuff
void another_month(void)
{
  extern const char *month_names[16];
  mudlog("Another month has passed.", BUG, LEV_IMM, TRUE);

  time_info.day = 0;
  time_info.month++;

  // update the plshops around the land
  monthly_plshop_update();

  /* year passed? -roa*/
  if (time_info.month > 15)  /* now 16 months in a year -roa */ 
    another_year();
  else
  {
    /* send message stating a new month has begun */
    send_to_outdoor("As time marches steadily onward, a new month has arrived.\n\r");
    sprintf(buf, "It is known as the %%6%s%%0.\n\r", month_names[(int)time_info.month]);
    send_to_outdoor(buf);
  }

  global_weather.season = time_info.month / 4;
}

// and, again, each day, things must be done...
void another_day(void)
{  
  extern void check_char_quests(void);
  mudlog("Another day has passed.", BUG, LEV_IMM, TRUE);

  time_info.hours -= 24;
  time_info.day++;

  // subtract time from character quests in quest.c
  check_char_quests();

  // update the plshops around the land
  daily_plshop_update();

  /* has a month passed? -roa 35 (0 - 34) days in a month btw */
  if (time_info.day > 34)
    another_month();
}

void check_hourly_activity(void)
{
  dsdata *d;
  chdata *ch;

  switch (time_info.hours) {
    case 5 :
	 global_weather.sunlight = SUN_RISE;
	 send_sunrise();
	 break;
    case 6 :
	 global_weather.sunlight = SUN_LIGHT;
	 send_daytime();

	 sprintf(buf, "You feel refreshed.\n\r");
  	 for (d = descriptor_list; d; d = d->next)
    	   if (D_CHECK(d) && IS_PC(d->character)) 
	   {
                ch = d->character;

                // remove the bellow flag 4/6/98 -jtrhone
                REMOVE_BIT(CHAR_FLAGS(ch), CH_BELLOW);

     		if (IS_SHAMAN(ch) && !IS_IMMORTAL(ch))
     		{
		  act(buf, FALSE, ch, 0, 0, TO_CHAR);
                  RITES(ch) += 10;
        	  if (RITES(ch) > MIN((35+GET_WIS(ch)-13), (GET_LEVEL(ch)))) 
		    RITES(ch) = MIN((35+GET_WIS(ch)-13), (GET_LEVEL(ch)));

                  // no, let rites go negative now... 2/12/98 -jtrhone
        	  // if (RITES(ch) < 0) RITES(ch) = 0; 
     		}
	 	else
		if (!IS_IMMORTAL(ch) && IS_DROW(ch))  
		  send_to_char("You feel slightly weaker.\n\r",ch);
	   }
	 break;

    case 21 :
	 global_weather.sunlight = SUN_SET;
	 send_sunset();
	 break;
    case 22 :
	 global_weather.sunlight = SUN_DARK;
	 send_nighttime();
	 break;
    default :
	 break;
  }
}

// the driving weather/time function... called externally
// every MUD hour, do some processing...
void another_hour(void)
{
  extern void open_close_shops(void);
  extern void update_client_times(void);

  /* another hour has passed -roa */
  time_info.hours++;

  /* in mobact.c, update shopkeeps based on their hours */
  open_close_shops();

  check_hourly_activity();

  /* if a day has passed */
  if (time_info.hours > 23) 
    another_day();

  // defined below...
  hourly_weather();

  // send times to every client on the hour 3/19/98 -jtrhone
  update_client_times();
}

/////////////////////////////////////////////////////
// code relating more to weather below... -jtrhone //
/////////////////////////////////////////////////////
// call on bootup, after time setup
void init_weather(void)
{
  switch (time_info.hours) {
  case 0 : case 1 : case 2 : case 3 : case 4 :
      global_weather.sunlight = SUN_DARK;
      break;
  case 5 : case 6 :
      global_weather.sunlight = SUN_RISE;
      break;
  case 7 : case 8 : case 9 : case 10 : case 11 : case 12 : case 13 :
  case 14 : case 15 : case 16 : case 17 : case 18 : case 19 :
  case 20 :
      global_weather.sunlight = SUN_LIGHT;
      break;
  case 21 : case 22 :
      global_weather.sunlight = SUN_SET;
      break;
  case 23 :
  default :
      global_weather.sunlight = SUN_DARK;
      break;
  }

  global_weather.season = time_info.month / 4;
}

void send_sunrise(void)
{
  int s = global_weather.season;
  int zone;

  for (zone = 0; zone < NUM_ZONES; zone++)
    if (REAL_ZONE(zone))
    {
      if (zone_table[zone].seasons[s].sunrise)
        send_to_zone_outside(zone_table[zone].seasons[s].sunrise, zone);
      else
        send_to_zone_outside("The %3sun%0 slowly begins to rise in the east.",zone);
    }
}

void send_daytime(void)
{
  int s = global_weather.season;
  int zone;

  for (zone = 0; zone < NUM_ZONES; zone++)
    if (REAL_ZONE(zone))
    {
      if (zone_table[zone].seasons[s].daytime)
        send_to_zone_outside(zone_table[zone].seasons[s].daytime, zone);
      else
        send_to_zone_outside("The day has begun.", zone);
    }
}

void send_sunset(void)
{
  int s = global_weather.season;
  int zone;

  for (zone = 0; zone < NUM_ZONES; zone++)
    if (REAL_ZONE(zone))
    {
      if (zone_table[zone].seasons[s].sunset)
        send_to_zone_outside(zone_table[zone].seasons[s].sunset, zone);
      else
	send_to_zone_outside("The %3sun%0 slowly disappears in the west.", zone);
    }
}

void send_nighttime(void)
{
  int s = global_weather.season;
  int zone;

  for (zone = 0; zone < NUM_ZONES; zone++)
    if (REAL_ZONE(zone))
    {
      if (zone_table[zone].seasons[s].nighttime)
        send_to_zone_outside(zone_table[zone].seasons[s].nighttime, zone);
      else
	send_to_zone_outside("The %4night%0 has begun.", zone);
    }
}

void send_zone_temp_updates(int zone)
{
  chdata *ch;
  int diff, prev, curr;
  BOOL increase;

  prev = zone_table[zone].previous_gtemp;
  curr = ZONE_TEMP(zone);
  diff = curr - prev;

  /* if diff < 0 then the temp has gone down */
  increase = (diff >= 0);
  diff = abs(diff);

  if (!increase)
  {
    if (diff >= 30)
      strcpy(buf2, "%B%4much colder%0");
    else
    if (diff >= 20)
      strcpy(buf2, "%4colder%0");
    else
    if (diff >= 10)
      strcpy(buf2, "%4cooler%0");
    else
    if (diff >= 2)
      strcpy(buf2, "%4slightly cooler%0");
  }
  else
  {
    if (diff >= 30)
      strcpy(buf2, "much %B%1hotter%0");
    else
    if (diff >= 20)
      strcpy(buf2, "%B%1hotter%0");
    else
    if (diff >= 10)
      strcpy(buf2, "%B%1warmer%0");
    else
    if (diff >= 2)
      strcpy(buf2, "%B%1slightly warmer%0");
  }

  if (diff <= 1) return;

  sprintf(buf, "The temperature has gotten %s in the past hour.",buf2);
  send_to_zone_outside(buf, zone);

  sprintf(buf2, "%%6Current temperature%%0: %d degrees.",curr);
  for (ch = character_list; ch; ch=ch->next)
    if (!INVALID_ROOM(ch->in_room) && OUTSIDE(ch) && IS_IMMORTAL(ch) &&
	world[ch->in_room].zone == zone)
      act(buf2, FALSE, ch, 0, 0, TO_CHAR);
}

/* returns proper precip type */
int get_precip_type(int zone)
{
  int temp;

  temp = ZONE_TEMP(zone);

  if (temp >= 36)
    return (RAINING);
  else
  if (temp >= 32)
    return (SLEETING);
  else
    return (SNOWING);
}

void stop_precip(int zone)
{
  extern char *precip_names_ing[];
  extern char *precip_names[];
  int prev, curr;

  prev = zone_table[zone].previous_status = zone_table[zone].current_status;
  curr = zone_table[zone].current_status = number(0, 2);

  switch (number(0, 4)) {
  case 0:
    sprintf(buf, "The sky slowly brightens as it stops %s.\n\r",
	    precip_names_ing[prev]);
    break;
  case 1:
    sprintf(buf, "The clouds slowly drift away as it stops %s.\n\r",
	    precip_names_ing[prev]);
    break;
  case 2:
    sprintf(buf, "The %%6sky%%0 stops dropping the %s.\n\r", precip_names[prev]);
    break;
  case 3:
    sprintf(buf, "All around you, the %s stops falling.\n\r", precip_names[prev]);
    break;
  default:
    sprintf(buf, "The %s comes to an end.\n\r", precip_names[prev]);
    break;
  }
  sprintf(buf+strlen(buf), "The sky is now %%B%s%%0.",precip_names[curr]);
  send_to_zone_outside(buf, zone);
}

void begin_precip(int zone)
{
  int temp;
  extern char *precip_names_ing[];
  extern char *precip_names[];

  temp = ZONE_TEMP(zone);

  if (temp >= 36)
    zone_table[zone].current_status = RAINING;
  else
  if (temp >= 32)
    zone_table[zone].current_status = SLEETING;
  else
    zone_table[zone].current_status = SNOWING;

  switch (number(0, 4)) {
  case 0:
    sprintf(buf, "The area %%4darkens%%0 slightly as it starts %s.",
	    precip_names_ing[zone_table[zone].current_status]);
    break;
  case 1:
    sprintf(buf, "A breeze accompanies the %s as it begins to fall.",
	    precip_names[zone_table[zone].current_status]);
    break;
  case 2:
    sprintf(buf, "The %%6sky%%0 begins to drop %s.",
	    precip_names[zone_table[zone].current_status]);
    break;
  case 3:
    sprintf(buf, "All around you, %s begins to fall.",
	    precip_names[zone_table[zone].current_status]);
    break;
  default:
    sprintf(buf, "Some %s starts to fall.",
	    precip_names[zone_table[zone].current_status]);
    break;
  }

  send_to_zone_outside(buf, zone);
}

void send_precip_arrival(int zone)
{
  switch (number(0, 3))
  {
    case 0:
      sprintf(buf, "Dark clouds begin to move in from the west.");
    break;

    case 1:
      sprintf(buf, "You notice dark weather approaching from the west.");
    break;

    case 2:
      sprintf(buf, "The wind begins to pick up slightly.");
    break;

    default:
      sprintf(buf, "Clouds begin to rush in from the west.");
    break;
  }

  send_to_zone_outside(buf, zone);
}

void send_precip_departure(int zone)
{
  switch (number(0, 3))
  {
    case 0:
      sprintf(buf, "Clear skies approach from the west.");
    break;

    case 1:
      sprintf(buf, "Dark clouds move slowly away eastwards.");
    break;

    case 2:
      sprintf(buf, "The air around you becomes very still.");
    break;

    default:
      sprintf(buf, "Clouds, hanging low to the ground, move away east.");
    break;
  }

  send_to_zone_outside(buf, zone);
}

void send_lightning_mesg(int room)
{
  if (WEATHER_ROOM(room))
  {
    if (DARK_OUTSIDE)
    {
      switch (number(1,5)) {
        case 1:
	  send_to_room_not_busy("%B%3Magnificent lightning lights up the sky%0.",room);
	break;
        case 2:
	  send_to_room_not_busy("The sky %B%3glows%0 with streaks of lightning as thunder crashes down.",room);
	break;
        case 3:
	  send_to_room_not_busy("Your vision blurs momentarily as %B%3lightning%0 fills the sky.",room);
	break;
        case 4:
	  send_to_room_not_busy("Streaks of %B%3brilliant lightning%0 stream through the night sky.",room);
	break;
	default:
	  send_to_room_not_busy("A %B%3sharp flash of lightning%0 leaves imprints on your vision.",room);
	break;
      }
    }
    else // some light, day or dawn
    {
      switch (number(1,5)) {
        case 1:
	  send_to_room_not_busy("Thunder crashes down in the distance.",room);
	break;
        case 2:
	  send_to_room_not_busy("The area is shaken by distant thunder.",room);
	break;
        case 3:
	  send_to_room_not_busy("A barely visible flash of lightning streaks across the sky.",room);
	break;
        case 4:
  	  send_to_room_not_busy("The area is jolted by rumbling thunder in the distance.",room);
	break;
	default:
  	  send_to_room_not_busy("The area is jolted by rumbling thunder in the distance.",room);
	break;
      }
    } 
  }
  else // inside room perhaps, but no weather regardless
  {
    switch (number(1, 5)) {
      case 1:
  	send_to_room_not_busy("The area around you shakes violently as thunder echos in the distance.",room);
	break;
      case 2:
  	send_to_room_not_busy("Thunder shakes everything around you.",room);
	break;
      case 3:
  	send_to_room_not_busy("The sound of thunder reverberates through the area.",room);
	break;
      case 4:
  	send_to_room_not_busy("In the distance, you hear lightning crash down.",room);
	break;
      default:
  	send_to_room_not_busy("The area is jolted by rumbling thunder in the distance.",room);
	break;
    }
  }
}

void do_zone_blizzard(int zone)
{
  return;
}

void do_zone_storm(int zone)
{
  int rroom = 0, room = 0;
  chdata *ch, *next_ch;
  int tarroom;
  BOOL will_spread;

  tarroom = number(zone * 100, zone * 100 + 99);

  rroom = real_room(tarroom);
    
  for (room = 0; room < top_of_world; room++)
    if (world[room].zone == zone && room != rroom)
      send_lightning_mesg(room);

  if (INVALID_ROOM(rroom) || !WEATHER_ROOM(rroom))
    return;

  sprintf(buf, "SYSUPD: Lightning strike, room #%d.",tarroom);
  mudlog(buf, BUG, LEV_IMM, FALSE);

  send_to_room_not_busy("%B%1LIGHTNING crashes down with a thunderous boom!%0", rroom);

  will_spread = (world[rroom].terrain_type == TERRAIN_WATER_SWIM ||
       		 world[rroom].terrain_type == TERRAIN_WATER_NOSWIM ||
       		 world[rroom].terrain_type == TERRAIN_UWATER);

  for (ch = world[rroom].people; ch; ch = next_ch)
  {
    next_ch = ch->next_in_room;
    if (!number(0, 99) && !IS_IMMORTAL(ch))
    {
      sprintf(buf, "SYSUPD: %s struck by lightning, room %d.",GET_NAME(ch), tarroom);
      mudlog(buf, BUG, LEV_IMM, TRUE);

      act("%B%1AAAHHHHH!!  The lightning strikes you!!%%0",FALSE, ch, 0, 0, TO_CHAR);
      act("%B%1Lightning strikes $n!!!%0",FALSE, ch, 0, 0, TO_ROOM);
      GET_HIT(ch) -= number(100, 30000);

      update_pos(ch);

      if (GET_POS(ch) == POS_DEAD)
	die(ch, FALSE);

      if (!will_spread)
        break;
    }    
  }
}

void calc_accums(int zone)
{
  int type;

  /* first, add/subtract to accums for last hour's precip */
  if (IS_PRECIPPING(zone)) 
  {
      /* add to accums */
      type = get_precip_type(zone);  /* returns proper precip type */
      ZONE_ACCUM_TYPE(zone) = type;

      /* accums based on inches of rain/snow */
      switch(type) {
        case RAINING:
        case SLEETING:
	  ZONE_ACCUM(zone) += number(0, 2);
	  break;
        case HAILING:
	  ZONE_ACCUM(zone) += number(0, 6);
	  break;
        case SNOWING:
	  ZONE_ACCUM(zone) += number(0, 12);
	  break;
      };
  }
  else /* its clear and not precipping remove some accum */
  {
      if (ZONE_TEMP(zone) >= 60)
      {
	switch(ZONE_ACCUM_TYPE(zone)) {
          case SLEETING:
	  case RAINING: 
	    ZONE_ACCUM(zone) -= number(2, 5);
	    ZONE_ACCUM(zone) = MAX(0, ZONE_ACCUM(zone));
	    break;	
          case HAILING:
	  case SNOWING: 
	    ZONE_ACCUM(zone) -= number(8, 15);
	    ZONE_ACCUM(zone) = MAX(0, ZONE_ACCUM(zone));
	    break;	
	};
      }
      else
      if (ZONE_TEMP(zone) >= 45)
      {
	switch(ZONE_ACCUM_TYPE(zone)) {
          case SLEETING:
	  case RAINING: 
	    ZONE_ACCUM(zone) -= number(1, 2);
	    ZONE_ACCUM(zone) = MAX(0, ZONE_ACCUM(zone));
	    break;	
          case HAILING:
	  case SNOWING: 
	    ZONE_ACCUM(zone) -= number(3, 6);
	    ZONE_ACCUM(zone) = MAX(0, ZONE_ACCUM(zone));
	    break;	
	};
      }
      else
      if (ZONE_TEMP(zone) >= 32)
      {
	switch(ZONE_ACCUM_TYPE(zone)) {
          case SLEETING:
	  case RAINING: 
	    ZONE_ACCUM(zone) -= number(0, 1);
	    ZONE_ACCUM(zone) = MAX(0, ZONE_ACCUM(zone));
	    break;	
          case HAILING:
	  case SNOWING: 
	    ZONE_ACCUM(zone) -= number(1, 4);
	    ZONE_ACCUM(zone) = MAX(0, ZONE_ACCUM(zone));
	    break;	
	};
    } 
  }  /* else we dont lose any precip if less than freezing */
}

// here are the main changes in weather, now OLCable, all be it based
// more on random numbers than actual barometric pressure...
// it is no longer MUD-WIDE, but each zone now has its own weather
// structure with average temperatures and chances for precip etc...
// jtrhone -roa
void hourly_weather(void)
{
  int season, zone, diff, target_temp;
  int perc, temp_change = 0;
  BOOL daytime;
  struct season_data *zseas = NULL;

  season = global_weather.season;

  daytime = (time_info.hours >= 8 && time_info.hours <= 20); /*8am-8pm*/

  for (zone = 0; zone < NUM_ZONES; zone++)
  {
    if (!REAL_ZONE(zone) || ZONE_IDLE(zone) || ZONE_FREED(zone))
      continue;

    zseas = &zone_table[zone].seasons[season];

    if (zseas->avg_day_temp <= -999)  /* this is the IGNORE trigger */
      continue;

    /* if daytime, progress towards avg daytime temp for this season */
    if (daytime)
    {
      if (number(0, 1))
	target_temp = zseas->avg_day_temp - number(0, zseas->avg_day_temp_var);
      else
	target_temp = zseas->avg_day_temp + number(0, zseas->avg_day_temp_var);
    }
    else
    if (!daytime)
    {
      if (number(0, 1))
	target_temp = zseas->avg_night_temp - number(0,zseas->avg_night_temp_var);
      else
	target_temp = zseas->avg_night_temp + number(0,zseas->avg_night_temp_var);
    }    

    diff = target_temp - ZONE_TEMP(zone);
    zone_table[zone].previous_gtemp = ZONE_TEMP(zone);

    temp_change = (diff < 0) ? (0 - number(0, (abs(diff)/2))) : number(0, (diff/2));
    ZONE_TEMP(zone) += temp_change;
    temp_change = abs(temp_change);

    /* tell everybody in the zone about their temp changes */
    send_zone_temp_updates(zone);

    calc_accums(zone);

    if (IS_PRECIPPING(zone))  /* calc perc it will stop */
    {
      perc = 100 - zseas->precip_percentage;
      if (perc > number(1, 100))  /* stop raining */
        stop_precip(zone);
    }
    else  /* calc percentage it will begin precipping */
    {
      // this percentage is based on a daily percentage, not hourly
      // at hour 0, we will set the hour_to_rain based on
      // this percentage, this is only calculated on bootup and
      // at hour 0
      if (!time_info.hours)	// time to determine if it will rain
      {
        perc = zseas->precip_percentage;
	if (perc > number(1, 100))
	{
	  zone_table[zone].hour_to_rain = number (0,23);
	  zone_table[zone].has_rained = FALSE;
	}
	else
	{
	  zone_table[zone].hour_to_rain = -1;
          zone_table[zone].has_rained = FALSE;
	}
      }

      if (!zone_table[zone].has_rained)
      {
	if (time_info.hours == zone_table[zone].hour_to_rain)
	{
	  begin_precip(zone);
	  zone_table[zone].has_rained = TRUE;
	}
	else
	if (time_info.hours == zone_table[zone].hour_to_rain - 1)
	  send_precip_arrival(zone);
	else
	if (time_info.hours == zone_table[zone].hour_to_rain + 1)
	  send_precip_departure(zone);
      }
    }

    REMOVE_BIT(ZONE_FLAGS(zone), Z_TSTORM | Z_BLIZZARD);
    if (IS_PRECIPPING(zone))  /* if still precip, check for storms */
    {
      if (temp_change >= 3)
      {
	perc = temp_change * zseas->atmosphere_type;
	if (perc > number(1, 50))
        {
	  if (ZONE_TEMP(zone) > 32)
	  {
	    SET_BIT(ZONE_FLAGS(zone), Z_TSTORM);  // storm for next hour
	    do_zone_storm(zone);
	  }
	  else
	  {
	    SET_BIT(ZONE_FLAGS(zone), Z_BLIZZARD);
	    do_zone_blizzard(zone);
	  }
        }
      }
    }
  }  /* end for each zone */
}