#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <ctype.h>
#include <merc.h>
#include <tables.h>
#include <lookup.h>
#include <recycle.h>
/*
* Update the weather.
*/
/* PaB: Handle daylight changes
* Accepts a pointer to a buffer into which it outputs a string - if neccessary
* to decribe changes in day/night
* Also keeps track of month/year changes
*/
/* PaB: Constants in weather calcs */
#define TOTAL_WEATHER_PROBABILITY 1000
#define CLEAR_SKY_RANGE (TOTAL_WEATHER_PROBABILITY / SEASON_MAX)
#define CLOUDY_SKY_RANGE (TOTAL_WEATHER_PROBABILITY / SEASON_MAX)
#define RAINY_SKY_RANGE (TOTAL_WEATHER_PROBABILITY / SEASON_MAX)
#define LIGHTNING_RANGE (TOTAL_WEATHER_PROBABILITY / SEASON_MAX)
/* Influence factors
* For each season/sector we keep a list of influences. These are applied
* to calculations of what the current weather is like. For example,
* springtime is a stormy period of transition, so we increase the chances
* of rain and lightning accordingly. Deserts don't see much rain at all,
* so we seriously crank down the chances of those happening in desert
* sectors.
* The following constants will either fully open a weather chance to happening
* or fully close it off.
*/
#define BASE_INFLUENCE ((TOTAL_WEATHER_PROBABILITY / SEASON_MAX) / 4)
#define PLUS4 (BASE_INFLUENCE * 4)
#define PLUS3 (BASE_INFLUENCE * 3)
#define PLUS2 (BASE_INFLUENCE * 2)
#define PLUS1 (BASE_INFLUENCE * 1)
#define NOINFLUENCE (BASE_INFLUENCE * 0)
#define MINUS1 (BASE_INFLUENCE * -1)
#define MINUS2 (BASE_INFLUENCE * -2)
#define MINUS3 (BASE_INFLUENCE * -3)
#define MINUS4 (BASE_INFLUENCE * -4)
/* PaB: Influence of seasons on weather changes.
* Notes: Because we could be dealing with all kinds of 'climates' which
* aren't properly reflected in the areas - exactly how minute of detail
* do we want to track here, anyway?? - we should keep things fairly
* mediocre. Will prevent snow in what should be a tropical climate...
*/
typedef struct _weather_influence_data
{
int clear_sky;
int cloudy_sky;
int rainy_sky;
int lightning_sky;
int max_probability;
} weather_influence_data;
/* Notes:
* Base weather table. Even chances of all weather types, then seasonal
* and sector influences are applied.
*/
static weather_influence_data base_weather =
{
/* clear cloudy raining lightning */
CLEAR_SKY_RANGE, CLOUDY_SKY_RANGE, RAINY_SKY_RANGE, LIGHTNING_RANGE
};
/* Notes:
* In simple terms: (Thanks, Eladriel! - PaB)
* Winter - its cloudy quite a bit, but doesn't rain often and lightnings even less.
* Spring - clouds up with some frequency and when it does, it rains lot, but
* not a lot of lightning.
* Summer - Sunny a bit, but when it rains, it lightnings frequently.
* Fall - Sunny a lot, but doesn't rain and rarely lightnings when cloudy.
*/
static weather_influence_data seasonal_weather_table[] =
{
/* clear cloudy raining lightning */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, MINUS2 }, /* Winter */
{MINUS3, MINUS2, NOINFLUENCE, NOINFLUENCE}, /* Spring */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, MINUS2 }, /* Summer */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, MINUS2 } /* Fall */
};
/* Notes: (Thanks, Eladriel! - PaB)
* Inside and City - baseline of the weather.
* Field - sunny a lot, but when it gets cloudy, it rains and often lightnings
* Forest - sunny a bit, rains when cloudy but doesn't lightning often.
* Hills - gets overcast a bit, rains with some frequency but no big lightning
* Mountains - overcast a lot, rains a lot but not a lot of lightning.
* Water (swim)-sun and clouds normal, but when cloudy it rains and lightnings.
* water (no) -sunny a bit and little clouds, but when there, it rains a lot
* and lightnings quite a bit too.
* Tundra - will tell you once I've got it set :P DEC
* air - most everything normal, though when it rains, it lightnings like
* hell.
* Desert - Rarely, if ever, gets cloudy. When it does, it rains and
* lightnings to make up for lost time.
*/
static weather_influence_data sector_weather_table[] =
{
/* clear cloudy raining lightning */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, NOINFLUENCE}, /* SECT_INSIDE */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, NOINFLUENCE}, /* SECT_CITY */
{NOINFLUENCE, NOINFLUENCE, PLUS1, PLUS1 }, /* SECT_FIELD */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, MINUS1 }, /* SECT_FOREST */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, MINUS2 }, /* SECT_HILLS */
{NOINFLUENCE, PLUS2, PLUS2, NOINFLUENCE}, /* SECT_MOUNTAIN */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, NOINFLUENCE}, /* SECT_WATER_SWIM */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, NOINFLUENCE}, /* SECT_WATER_NOSWIM */
{NOINFLUENCE, NOINFLUENCE, NOINFLUENCE, NOINFLUENCE}, /* SECT_TUNDRA */
{NOINFLUENCE, NOINFLUENCE, MINUS3, NOINFLUENCE}, /* SECT_AIR */
{PLUS4, MINUS4, MINUS4, PLUS4 } /* SECT_DESERT */
};
/* Weather conversion table. Some sectors don't have normal weather
* patterns, e.g. it doesn't rain in the desert. This enables the logic
* to neatly step over unwanted states.
*/
static int weather_conversion_table[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_TUNDRA */
{0, 1, 2, 3}, /* SECT_AIR */
{0, 0, 0, 3} /* SECT_DESERT */
};
/* Master weather table. At startup, all of the above rubbish is
* coalesced into this table, which we then use when figuring out
* what the weather is like.
*/
static int master_weather_table[SECT_MAX][SEASON_MAX][SKY_MAX];
static char * moon_rise[5] =
{
"{wAbove your head the New Moon Rises.{x\n\r\n\r",
"{wAbove your head the 1st Qtr Moon Rises.{x\n\r\n\r",
"{wAbove your head the Half Moon Rises.{x\n\r\n\r",
"{wAbove your head the Last Qtr Moon Rises.{x\n\r\n\r",
"{wAbove your head the Full Moon Rises.{x\n\r\n\r"
};
/* PaB: String tables by sector. If we really wanted to be extravagant,
* we could fill them in for seasons, as well..
*/
static char* weather_strings[11][8] =
{
{
/* SECT_INSIDE */
/* cloudless */
"",
"",
/* cloudy */
"",
"",
/* raining */
"",
"",
/* lightning */
"",
""
},
{
/* SECT_CITY */
/* cloudless */
"The clouds break up over the city.\r\n",
"",
/* cloudy */
"The drumbeats of rain on rooftops dies down.\r\n",
"Above the rooftops, you see clouds rolling in.\r\n",
/* raining */
"The thunder rolling off of nearby buildings fades and is quiet.\r\n",
"Rain makes a melodic drumbeat as it falls on the rooftops.\r\n",
/* lightning */
"",
"Flashes light up the sky and thunder crashes off the buildings.\r\n"
},
{
/* SECT_FIELD */
/* cloudless */
"The clear sky opens up over the fields.\r\n",
"",
/* cloudy */
"The fields are no longer being bathed in rainfall.\r\n",
"The fields darken as clouds roll in, obscuring the sky.\r\n",
/* raining */
"The rolls of thunder rolling across the fields fades into silence.\r\n",
"The thirsty fields drink up the rain as it begins falling.\r\n",
/* lightning */
"",
"Thunder clashes and roars across the fields. Shouldn't you seek cover..?\r\n"
},
{
/* SECT_FOREST */
/* cloudless */
"The forest breathes again as the sky clears.\r\n",
"",
/* cloudy */
"The drumbeat of rain on leaves fades..\r\n",
"The forest hushes as clouds darken the sky.\r\n",
/* raining */
"The thunder shaking the forest rumbles one last and is gone.\r\n",
"The raindrops falling on the forest around you makes a soothing sound.\r\n",
/* lightning */
"",
"The denizens of the forest run for cover as the elements vent their fury!\r\n"
},
{
/* SECT_HILLS */
/* cloudless */
"The hilltops are no longer obscured by clouds.\r\n",
"",
/* cloudy */
"It stops raining.\r\n",
"An eerie silence settles over the hills as clouds roll in.\r\n",
/* raining */
"The echoes of thunder on distant hills dies down.\r\n",
"The thirsty fields drink up the rain as it begins falling.\r\n",
/* lightning */
"",
"Lightning flashes and thunder rolls from distant hilltops.\r\n"
},
{
/* SECT_MOUNTAIN */
/* cloudless */
"The moutaintops reach majestically towards the clear sky.\r\n",
"",
/* cloudy */
"The clouds, lightened of their load of rain, stop pouring it upon you.\r\n",
"Heavy clouds roll in around the mountain.\r\n",
/* raining */
"The booming thunder quiets and dies.\r\n",
"Rain pours mercilessly down upon you.\r\n",
/* lightning */
"",
"Thunder roars and reverberates from the surrounding peaks.\r\n"
},
{
/* SECT_WATER_SWIM */
/* cloudless */
"The sky far above seems to be clearing.\r\n",
"",
/* cloudy */
"The chaotic noise of rain from above dies down.\r\n",
"The water darkens as clouds roll in.\r\n",
/* raining */
"The distant sound of thunder fades.\r\n",
"The rain beats chaotic on the surface above.\r\n",
/* lightning */
"",
"Flashes light up the water around you as the air above storms and thunders.\r\n"
},
{
/* SECT_WATER_NOSWIM */
/* cloudless */
"The sky above seems to be clearing.\r\n",
"",
/* cloudy */
"The chaotic noise of rain on water dies down.\r\n",
"The water around you darkens as clouds roll in.\r\n",
/* raining */
"The distant sound of thunder fades.\r\n",
"The rain beats chaotic on the surface above.\r\n",
/* lightning */
"",
"Flashes light up the water around you as the air above storms and thunders.\r\n"
},
{
/* SECT_TUNDRA */
/* cloudless */
"The clouds clear and the sun reflects blindingly off the snow.",
"",
/* cloudy */
"The hiss of snow falling comes to a halt... Silence.",
"The inevitable clouds roll in, turning the sky low and oppressive.",
/* raining */
"At last, the blizzard dies down and visibility improves.",
"Big, fat, fluffy flakes fall from a heavy-laden sky.",
/* lightning */
"",
"Brutal winds rise, blasting flakes of snow seemingly to your bones."
},
{
/* SECT_AIR */
/* cloudless */
"The clouds around you dissipate.\r\n",
"",
/* cloudy */
"The rain falling around you tapers off..\r\n",
"Clouds begin rolling in around you.\r\n",
/* raining */
"The flashes of lightning and impacts of thunder die off..\r\n",
"Rain begins forming around you and falling to the ground.\r\n",
/* lightning */
"",
"Thunder shakes you about as lightning crashes around you!\r\n"
},
{
/* SECT_DESERT */
/* cloudless */
"The cruel sky opens above you.\r\n",
"",
/* cloudy */
"A single cloud floats in the sky, providing no relief from the sun.",
"",
/* raining */
"",
"",
/* lightning */
"",
"Thunder rolls across the desert as an electrical storm comes to life.\r\n"
}
};
/* PaB: Which season are we in?
* Notes: Simply figures out which season the current month falls into
* and returns a proper value.
*/
int calc_season(void)
{
int season = 0;
/* How far along in the year are we, measured in days? */
/* PaB: Am doing this in days to minimize roundoff impact */
int day = time_info.month * DAYS_PER_MONTH + time_info.day;
if(day < (DAYS_PER_YEAR / 8))
{
season = SEASON_WINTER;
}
else if(day < (DAYS_PER_YEAR / 8) * 3)
{
season = SEASON_SPRING;
}
else if(day < (DAYS_PER_YEAR / 8) * 5)
{
season = SEASON_SUMMER;
}
else if(day < (DAYS_PER_YEAR / 8) * 7)
{
season = SEASON_FALL;
}
else
{
season = SEASON_WINTER;
}
return season;
}
void update_daylight(char buf[SECT_MAX][MAX_STRING_LENGTH])
{
int sect;
CHAR_DATA *ch,*ch_next;
bool char_up = FALSE;
AFFECT_DATA *paf;
time_info.half_hour += 30;
if ( time_info.half_hour != 30 )
{
++time_info.hour;
time_info.half_hour = 0;
}
else
return;
for(sect = 0; sect < SECT_MAX; sect++)
{
switch(time_info.hour)
{
case HOUR_DAY_BEGIN:
weather_info[sect].sunlight = SUN_LIGHT;
strcat(buf[sect], "{wThe day has begun.{x\n\r");
break;
case HOUR_SUNRISE:
weather_info[sect].sunlight = SUN_RISE;
strcat(buf[sect], "{wThe moon slowly disappears in the west.{x\n\r");
strcat(buf[sect], "{wThe sun rises in the east.{x\n\r");
break;
case HOUR_SUNSET:
if (weather_info[sect].moon_count == 4)
{
weather_info[sect].moon_count = 0;
if (weather_info[sect].moon == 4)
weather_info[sect].moon = 0;
else
weather_info[sect].moon++;
}
else
weather_info[sect].moon_count++;
strcat(buf[sect],moon_rise[weather_info[sect].moon]);
weather_info[sect].sunlight = SUN_SET;
strcat(buf[sect], "{wThe sun slowly disappears in the west.{x\n\r");
break;
case HOUR_NIGHT_BEGIN:
weather_info[sect].sunlight = SUN_DARK;
strcat(buf[sect], "{wThe night has begun.{x\n\r");
break;
}
}
if(time_info.hour == HOUR_MIDNIGHT)
{
time_info.hour = 0;
time_info.day++;
for ( ch = char_list; ch != NULL; ch = ch_next )
{
ch_next = ch->next;
mudsetting->last_proc_logged = 3706;
if (IS_AFFECTED(ch,AFF_AGGDAM))
{
if ((paf = find_affect(ch,AFF_AGGDAM)) != NULL)
affect_remove(ch, paf);
}
if (!IS_NPC(ch) && IS_SET(ch->added, ADDED_CALM))
{
send_to_char("You no longer feel so apathetic.\n\r",ch);
REMOVE_BIT(ch->added, ADDED_CALM);
}
if (!IS_NPC(ch) && IS_CLASS(ch, CLASS_VAMPIRE) )
{
if (ch->hit < ch->max_hit)
{ ch->hit = ch->max_hit; char_up = TRUE; }
if (ch->mana < ch->max_mana)
{ ch->mana = ch->max_mana; char_up = TRUE; }
if (ch->move < ch->max_move)
{ ch->move = ch->max_move; char_up = TRUE; }
if (char_up) send_to_char( "{RYou feel the strength of the kindred flow through your veins!{x\n\r", ch );
}
}
}
if(time_info.day >= DAYS_PER_MONTH)
{
time_info.day = 0;
time_info.month++;
}
if(time_info.month >= MONTHS_PER_YEAR)
{
time_info.month = 0;
time_info.year++;
}
}
void weather_update(void)
{
char buf[SECT_MAX][MAX_STRING_LENGTH];
DESCRIPTOR_DATA *d;
int season;
int diff;
int sect;
int sky;
int i;
int changed = 0;
int dir = 0;
GSList *desc_list;
for(i = 0; i < SECT_MAX; i++)
{
buf[i][0] = '\0';
}
/* Day/Night */
update_daylight(buf);
if ( time_info.half_hour == 30 )
return;
desc_list = descriptor_list;
while ( desc_list != NULL )
{
d = (DESCRIPTOR_DATA*)desc_list->data;
if ( d->connected == CON_PLAYING &&
d->character->in_room != NULL &&
strcmp(buf[d->character->in_room->sector_type],"") &&
d->character->in_room->sector_type == SECT_INSIDE )
{
send_to_char(buf[d->character->in_room->sector_type], d->character);
}
mudsetting->last_proc_logged = 3782;
desc_list = g_slist_next(desc_list);
}
g_slist_free(desc_list);
season = calc_season();
for(sect = 0; sect < SECT_MAX; sect++)
{
changed = 0;
sky = weather_info[sect].sky;
weather_info[sect].mmhg += weather_info[sect].change;
/* See what it's like at the moment */
#define CHANGE_FACTOR ((number_percent() % 25) + 15)
if(weather_info[sect].sky == 0)
{
if(weather_info[sect].mmhg > master_weather_table[sect][season][weather_info[sect].sky])
{
sky = weather_conversion_table[sect][weather_info[sect].sky + 1];
changed = 1;
dir = 0;
}
}
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_conversion_table[sect][weather_info[sect].sky - 1];
changed = 1;
dir = -1;
}
}
else
{
if(weather_info[sect].mmhg > master_weather_table[sect][season][weather_info[sect].sky + 1])
{
sky = weather_conversion_table[sect][weather_info[sect].sky + 1];
changed = 1;
}
else if(weather_info[sect].mmhg < master_weather_table[sect][season][weather_info[sect].sky - 1])
{
sky = weather_conversion_table[sect][weather_info[sect].sky - 1];
changed = 1;
dir = -1;
}
}
/* Sky changed, reset counters */
if(changed)
{
/* Figure how much to change the weather */
if(sky == 0)
{
/* Is always going to go up */
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;*/
/* PaB: Cheat for a little more sunshine. ;-) */
weather_info[sect].mmhg = 0;
}
else if(sky == SKY_MAX - 1) /* Right now, SKY_LIGHTNING */
{
/* Is always going to come back down */
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
{
/* Might go up or down */
diff = ((number_percent() % 100) < 50) ? -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)
{
// TODO : Gotta fix this for windows port
if (strlen(buf[sect]) > 1024){
bug("Problems with the weather",0);
}
else
strcat(buf[sect], weather_strings[sect][sky * 2]);
}
else
{
if (strlen(buf[sect]) > 1024){
bug("Problems with the weather",0);
}
strcat(buf[sect], weather_strings[sect][sky * 2 + 1]);
}
weather_info[sect].sky = sky;
}
}
desc_list = descriptor_list;
while ( desc_list != NULL )
{
d = (DESCRIPTOR_DATA*)desc_list->data;
if(d->connected == CON_PLAYING &&
IS_OUTSIDE(d->character) &&
IS_AWAKE(d->character) &&
d->character &&
d->character->in_room &&
d->character->in_room->sector_type != SECT_INSIDE &&
buf[d->character->in_room->sector_type][0] != '\0')
{
send_to_char(buf[d->character->in_room->sector_type], d->character);
}
mudsetting->last_proc_logged = 3781;
desc_list = g_slist_next(desc_list);
}
/* See if Divine Intervention is really going to piss on someone's
* parade.
*/
while ( desc_list != NULL )
{
d = (DESCRIPTOR_DATA*)desc_list->data;
if(d->connected == CON_PLAYING &&
(d->character->in_room) &&
d->character &&
d->character->in_room->sector_type != SECT_INSIDE )
{
if(weather_info[d->character->in_room->sector_type].sky == SKY_LIGHTNING)
{
if((number_percent() % 10000) == 0)
{
int sn = skill_lookup("lightning");
send_to_char("{YZZAAAAAAAPP! {WYou are nailed by a bolt from the sky..{n", d->character);
if ( ( sn = skill_lookup( "call lightning" ) ) < 0)
{
send_to_char("Whoops error in the code.. please report to Spiral/Dominion.\n\r",d->character);
return;
}
damage(d->character, d->character,
number_range(500, 1000), sn);
}
}
}
mudsetting->last_proc_logged = 3788;
desc_list = g_slist_next(desc_list);
}
g_slist_free(desc_list);
return;
}
void init_weather(void)
{
int sect, seas;
for(sect = 0; sect < SECT_MAX; sect++)
{
weather_info[sect].change = CHANGE_FACTOR;
weather_info[sect].mmhg = 200; /* Something arbitrary */
weather_info[sect].sky = SKY_CLOUDLESS;
for(seas = 0; seas < SEASON_MAX; seas++)
{
int cur = 0;
/* Each value is an accumulation of the preceeding */
cur += UMAX(base_weather.clear_sky +
seasonal_weather_table[seas].clear_sky +
sector_weather_table[sect].clear_sky, 0);
master_weather_table[sect][seas][SKY_CLOUDLESS] = cur;
cur += UMAX(base_weather.cloudy_sky +
seasonal_weather_table[seas].cloudy_sky +
sector_weather_table[sect].cloudy_sky, 0);
master_weather_table[sect][seas][SKY_CLOUDY] = cur;
cur += UMAX(base_weather.rainy_sky +
seasonal_weather_table[seas].rainy_sky +
sector_weather_table[sect].rainy_sky, 0);
master_weather_table[sect][seas][SKY_RAINING] = cur;
cur += UMAX(base_weather.lightning_sky +
seasonal_weather_table[seas].lightning_sky +
sector_weather_table[sect].lightning_sky, 0);
master_weather_table[sect][seas][SKY_LIGHTNING] = cur;
}
}
weather_update();
}