/************************************************************************
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 */
}