#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(); }