/**************************************************************************
*  file: weather.c , Weather and time module              Part of DIKUMUD *
*  Usage: Performing the clock and the weather                            *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
***************************************************************************/

#include <stdio.h>
#include <strings.h>

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "db.h"

/* uses */
extern struct time_info_data time_info;
extern struct zone_data *zone_table;
extern int top_of_zone_table;
extern struct char_data *character_list;
extern struct room_data *world;

/* In this part. */

void weather_and_time(int mode);
void another_hour(int mode);
void weather_change(int mode);
void calc_light_zone(struct zone_data *zone);
char get_season(struct zone_data *zone);
void blow_out_torches();

/* Here comes the code */

void weather_and_time(int mode)
{
	another_hour(mode);
	weather_change(mode);
}



void another_hour(int mode)
{
	time_info.hours++;

	if (time_info.hours > 23)  /* Changed by HHS due to bug ???*/
	{
		time_info.hours -= 24;
		time_info.day++;

		if (time_info.day>34)
		{
			time_info.day = 0;
			time_info.month++;

			if(time_info.month>16)
			{
				time_info.month = 0;
				time_info.year++;
			}
		}
	}
}

/* some magic values */
#define MAGIC_PRECIP_START 1060
#define MAGIC_PRECIP_STOP 970

void weather_change(int mode)
{
	int number(int from,int to);

	int diff, change,zon,magic,old_wind;
	signed char old_temp;
	unsigned char old_precip,season_num;
	struct climate *clime;
	struct weather_data *cond;
	
	if(mode) {

		/* Scan through each zone of the zone_table */
		for(zon=0;zon<top_of_zone_table;zon++) {
			clime=&zone_table[zon].climate;
			cond=&zone_table[zon].conditions;
			old_temp=cond->temp;
			old_precip=cond->precip_rate;
			old_wind=cond->windspeed;

			/* Which season is it? */
			season_num=get_season(&zone_table[zon]);

			/* Clear the control weather bit */
			REMOVE_BIT(cond->flags,WEATHER_CONTROLLED);

			/* Create changes for this hour */
			cond->free_energy = MAX(3000,
				clime->energy_add+cond->free_energy);
			cond->free_energy = MIN(50000,cond->free_energy);
			switch(clime->season_wind[season_num]) {
				case SEASON_CALM:
					if(cond->windspeed > 25)
						cond->windspeed -=5;
					else
						cond->windspeed += number(-2,1);
					break;
				case SEASON_BREEZY:
					if(cond->windspeed > 40)
						cond->windspeed -=5;
					else
						cond->windspeed += number(-2,2);
					break;
				case SEASON_UNSETTLED:
					if(cond->windspeed < 5)
						cond->windspeed +=5;
					else if(cond->windspeed > 60)
						cond->windspeed -=5;
					else
						cond->windspeed += number(-6,6);
					break;
				case SEASON_WINDY:
					if(cond->windspeed < 15)
						cond->windspeed +=5;
					else if(cond->windspeed > 80)
						cond->windspeed -=5;
					else
						cond->windspeed += number(-6,6);
					break;
				case SEASON_CHINOOK:
					if(cond->windspeed < 25)
						cond->windspeed +=5;
					else if(cond->windspeed > 110)
						cond->windspeed -=5;
					else
						cond->windspeed+=number(-15,15);
					break;
				case SEASON_VIOLENT:
					if(cond->windspeed < 40)
						cond->windspeed += 5;
					else
						cond->windspeed += number(-8,8);
					break;
				case SEASON_HURRICANE:
					cond->windspeed=100;
					break;
				default:
					break;
			}
			cond->free_energy += cond->windspeed;  /* + or - ? */
			if(cond->free_energy < 0)
				cond->free_energy=0;
			else if(cond->free_energy > 20000)
				cond->windspeed += number(-10,-1);
			cond->windspeed = MAX(0,cond->windspeed);
			switch(clime->season_wind_variance[season_num]) {
				case 0:
					cond->wind_dir=clime->season_wind_dir[season_num];
					break;
				case 1:
					if(dice(2,15)*1000 < cond->free_energy)
						cond->wind_dir=number(0,3);
					break;
			}
			switch(clime->season_temp[season_num]) {
				case SEASON_FROSTBITE:
					if(cond->temp > -20)
						cond->temp -= 4;
					else
						cond->temp += number(-3,3);
					break;
				case SEASON_NIPPY:
					if(cond->temp < -40)
						cond->temp += 2;
					else if(cond->temp > 5)
						cond->temp -=3;
					else
						cond->temp += number(-3,3);
					break;
				case SEASON_FREEZING:
					if(cond->temp < -20)
						cond->temp += 2;
					else if(cond->temp > 0)
						cond->temp -=2;
					else
						cond->temp += number(-2,2);
					break;
				case SEASON_COLD:
					if(cond->temp < -10)
						cond->temp += 1;
					else if(cond->temp > 5)
						cond->temp -=2;
					else
						cond->temp += number(-2,2);
					break;
				case SEASON_COOL:
					if(cond->temp < -3)
						cond->temp += 2;
					else if(cond->temp > 14)
						cond->temp -=2;
					else
						cond->temp += number(-3,3);
					break;
				case SEASON_MILD:
					if(cond->temp < 7)
						cond->temp += 2;
					else if(cond->temp > 26)
						cond->temp -=2;
					else
						cond->temp += number(-2,2);
					break;
				case SEASON_WARM:
					if(cond->temp < 19)
						cond->temp += 2;
					else if(cond->temp > 33)
						cond->temp -=2;
					else
						cond->temp += number(-3,3);
					break;
				case SEASON_HOT:
					if(cond->temp < 24)
						cond->temp += 3;
					else if(cond->temp > 46)
						cond->temp -=2;
					else
						cond->temp += number(-3,3);
					break;
				case SEASON_BLUSTERY:
					if(cond->temp < 34)
						cond->temp += 3;
					else if(cond->temp > 53)
						cond->temp -=2;
					else
						cond->temp += number(-5,5);
					break;
				case SEASON_HEATSTROKE:
					if(cond->temp < 44)
						cond->temp += 5;
					else if(cond->temp > 60)
						cond->temp -=5;
					else
						cond->temp += number(-3,3);
					break;
				case SEASON_BOILING:
					if(cond->temp < 80)
						cond->temp += 5;
					else if(cond->temp > 120)
						cond->temp -=5;
					else
						cond->temp += number(-6,6);
					break;
				default:
					break;
			}
			if(cond->flags & SUN_VISIBLE)
				cond->temp += 2;
			else if(!(clime->flags & NO_SUN_EVER))
				cond->temp -= 2;
			switch(clime->season_precip[season_num]) {
				case SEASON_NO_PRECIP_EVER:
					if(cond->precip_rate > 0)
						cond->precip_rate /= 2;
					cond->humidity=0;
					break;
				case SEASON_ARID:
					if(cond->humidity > 30)
						cond->humidity -= 3;
					else
						cond->humidity+=number(-3,2);
					if(old_precip > 20)
						cond->precip_rate -= 8;
					break;
				case SEASON_DRY:
					if(cond->humidity > 50)
						cond->humidity -= 3;
					else
						cond->humidity+=number(-4,3);
					if(old_precip > 35)
						cond->precip_rate -= 6;
					break;
				case SEASON_LOW_PRECIP:
					if(cond->humidity < 13)
						cond->humidity += 3;
					else if(cond->humidity > 91)
						cond->humidity -= 2;
					else
						cond->humidity+=number(-5,4);
					if(old_precip > 45)
						cond->precip_rate -= 10;
					break;
				case SEASON_AVG_PRECIP:
					if(cond->humidity < 30)
						cond->humidity += 3;
					else if(cond->humidity > 80)
						cond->humidity -= 2;
					else
						cond->humidity+=number(-9,9);
					if(old_precip > 55)
						cond->precip_rate -= 10;
					break;
				case SEASON_HIGH_PRECIP:
					if(cond->humidity < 40)
						cond->humidity += 3;
					else if(cond->humidity > 90)
						cond->humidity -= 2;
					else
						cond->humidity+=number(-8,8);
					if(old_precip > 65)
						cond->precip_rate -= 10;
					break;
				case SEASON_STORMY:
					if(cond->humidity < 50)
						cond->humidity += 4;
					else
						cond->humidity+=number(-6,6);
					if(old_precip > 80)
						cond->precip_rate -= 10;
					break;
				case SEASON_TORRENT:
					if(cond->humidity < 60)
						cond->humidity += 4;
					else
						cond->humidity+=number(-6,9);
					if(old_precip > 100)
						cond->precip_rate -= 15;
					break;
				case SEASON_CONSTANT_PRECIP:
					cond->humidity=100;
					if(cond->precip_rate==0)
						cond->precip_rate=number(5,12);
					break;
				default:
					break;
			}
			cond->humidity=MIN(100,cond->humidity);
			cond->humidity=MAX(0,cond->humidity);

			cond->pressure_change+=number(-3,3);
			cond->pressure_change=MIN(8,cond->pressure_change);
			cond->pressure_change=MAX(-8,cond->pressure_change);
			cond->pressure += cond->pressure_change;
			cond->pressure = MIN(cond->pressure,1040);
			cond->pressure = MAX(cond->pressure,960);

			cond->free_energy += cond->pressure_change;

			/* The numbers that follow are truly magic since  */
			/* they have little bearing on reality and are an */
			/* attempt to create a mix of precipitation which */
			/* will seem reasonable for a specified climate   */
			/* without any complicated formulae that could    */
			/* cause a performance hit. To get more specific  */
			/* or exacting would certainly not be "Diku..."   */

			magic = ((1240-cond->pressure)*cond->humidity >> 4) +
				cond->temp + old_precip*2 +
                                (cond->free_energy - 10000)/100;

			if(old_precip == 0) {
				if(magic > MAGIC_PRECIP_START) {
					cond->precip_rate += 1;
					if(cond->temp>0)
						send_to_zone_outdoor(zon,
						"It begins to rain.\n\r");
					else
						send_to_zone_outdoor(zon,
						"It starts to snow.\n\r");
				} else if(!old_wind && cond->windspeed)
					send_to_zone_outdoor(zon,
					"The wind begins to blow\n\r");
				else if(cond->windspeed - old_wind > 10)
					send_to_zone_outdoor(zon,
					"The wind picks up some.\n\r");
				else if(cond->windspeed - old_wind < -10)
					send_to_zone_outdoor(zon,
					"The wind calms down a bit.\n\r");
				else if(cond->windspeed > 60) {
					if(cond->temp > 50)
						send_to_zone_outdoor(zon,
						"A violent scorching wind "
						"blows hard in the face " 
						"of any poor travellers " 
						"in the area.\n\r");
					else if(cond->temp > 21)
						send_to_zone_outdoor(zon,
						"A hot wind gusts wildly " \
						"through the area.\n\r");
					else if(cond->temp > 0)
						send_to_zone_outdoor(zon,
						"A fierce wind cuts the air " \
						"like a razor-sharp knife.\n\r");
					else if(cond->temp > -10)
						send_to_zone_outdoor(zon,
						"A freezing gale blasts " \
						"through the area.\n\r");
					else send_to_zone_outdoor(zon,
						"An icy wind drains the " \
						"warmth from all in sight.\n\r");
				} else if(cond->windspeed > 25) {
					if(cond->temp > 50)
						send_to_zone_outdoor(zon,
						"A hot, dry breeze blows " \
						"languidly around.\n\r");
					else if(cond->temp > 22)
						send_to_zone_outdoor(zon,
						"A warm pocket of air " \
						"is rolling through here.\n\r");
					else if(cond->temp > 10)
						send_to_zone_outdoor(zon,
						"It's breezy.\n\r");
					else if(cond->temp > 2)
						send_to_zone_outdoor(zon,
						"A cool breeze wafts by.\n\r");
					else if(cond->temp > -5)
						send_to_zone_outdoor(zon,
						"A slight wind blows a " \
						"chill into living tissue.\n\r");
					else if(cond->temp > -15)
						send_to_zone_outdoor(zon,
						"A freezing wind blows " \
						"gently, but firmly " \
						"against all obstacles in " \
						"the area.\n\r");
					else
						send_to_zone_outdoor(zon,
						"The wind isn't very strong " \
						"here, but the cold makes " \
						"it quite noticeable.\n\r");
				} else if(cond->temp > 52)
					send_to_zone_outdoor(zon,
					"It's hotter than anyone could imagine.\n\r");
				else if(cond->temp > 37)
					send_to_zone_outdoor(zon,
					"It's really, really hot here. A " \
					"slight breeze would really " \
					"improve things.\n\r");
				else if(cond->temp > 25)
					send_to_zone_outdoor(zon,
					"It's hot out here.\n\r");
				else if(cond->temp > 19)
					send_to_zone_outdoor(zon,
					"It's nice and warm out.\n\r");
				else if(cond->temp > 9)
					send_to_zone_outdoor(zon,
					"It's mild out today.\n\r");
				else if(cond->temp > 1)
					send_to_zone_outdoor(zon,
					"It's cool out here.\n\r");
				else if(cond->temp > -5)
					send_to_zone_outdoor(zon,
					"It's a bit nippy here.\n\r");
				else if(cond->temp > -20)
					send_to_zone_outdoor(zon,
					"It's cold!\n\r");
				else if(cond->temp > -25)
					send_to_zone_outdoor(zon,
					"It's really c-c-c-cold!!\n\r");
				else
					send_to_zone_outdoor(zon,
					"Better get inside - this is too " \
					"cold for man or -most- beasts.\n\r");
			} else if(magic < MAGIC_PRECIP_STOP) {
				cond->precip_rate=0;
				if(old_temp > 0)
					send_to_zone_outdoor(zon,
					"The rain stops.\n\r");
				else
					send_to_zone_outdoor(zon,
					"It stops snowing.\n\r");
			} else {
				/* Still precip'ing, update the rate */
				/* Check rain->snow or snow->rain */
				if(cond->free_energy > 10000)
					cond->precip_change += number(-3,4);
				else
					cond->precip_change += number(-4,2);
				cond->precip_change=MAX(-10,cond->precip_change);
				cond->precip_change=MIN(10,cond->precip_change);
				cond->precip_rate += cond->precip_change;
				cond->precip_rate=MAX(1,cond->precip_rate);
				cond->precip_rate=MIN(100,cond->precip_rate);

				cond->free_energy -= cond->precip_rate*3-
						abs(cond->precip_change);
				
				if(old_temp > 0 && cond->temp <= 0)
					send_to_zone_outdoor(zon,
					"The rain turns to snow.\n\r");
				else if(old_temp <= 0 && cond->temp > 0)
					send_to_zone_outdoor(zon,
					"The snow turns to a cold rain.\n\r");
				else if(cond->precip_change > 5) {
					if(cond->temp > 0)
						send_to_zone_outdoor(zon,
						"It rains a bit harder.\n\r");
					else
						send_to_zone_outdoor(zon,
						"The snow is coming down " \
						"faster now.\n\r");
				} else if(cond->precip_change < -5) {
					if(cond->temp > 0)
						send_to_zone_outdoor(zon,
						"The rain is falling less " \
						"heavily now.\n\r");
					else
						send_to_zone_outdoor(zon,
						"The snow has let up a " \
						"little.\n\r");
				} else if(cond->temp > 0) {
					if(cond->precip_rate > 80) {
						if(cond->windspeed > 80)
							send_to_zone_outdoor(zon,
							"There's a hurricane " \
							"out here!\n\r");
						else if(cond->windspeed > 40)
							send_to_zone_outdoor(zon,
							"The wind and the rain"\
							" are nearly too much "\
							"to handle.\n\r");
						else
							send_to_zone_outdoor(zon,
							"It's raining really " \
							"hard right now.\n\r");
					} else if(cond->precip_rate > 50) {
						if(cond->windspeed > 60){
							send_to_zone_outdoor(zon,
							"What a rainstorm!\n\r");
						} else if(cond->windspeed > 30)
							send_to_zone_outdoor(zon,
							"The wind is lashing "\
							"this wild rain seemi"\
							"ngly straight into y"\
							"our face.\n\r");
						else
							send_to_zone_outdoor(zon,
							"It's raining pretty "\
							"hard.\n\r");
					} else if(cond->precip_rate > 30) {
						if(cond->windspeed > 50)
							send_to_zone_outdoor(zon,
							"A respectable rain "\
							"is being thrashed "\
							"about by a vicious "\
							"wind.\n\r");
						else if(cond->windspeed > 25){
							send_to_zone_outdoor(zon,
							"It's rainy and windy "\
							"but, altogether not "\
							"too uncomfortable.\n\r");
						 }else
							send_to_zone_outdoor(zon,
							"Hey, it's raining...\n\r");
					} else if(cond->precip_rate > 10) {
						if(cond->windspeed > 50)
							send_to_zone_outdoor(zon,
							"The light rain here "\
							"is nearly unnoticeab"\
							"le compared to the h"\
							"orrendous wind.\n\r");
						else if(cond->windspeed > 24)
							send_to_zone_outdoor(zon,
							"A light rain is bei"\
							"ng driven fiercely "\
							"by the wind.\n\r");
						else
							send_to_zone_outdoor(zon,
							"It's raining lightly.\n\r");
					} else if(cond->windspeed > 55)
						send_to_zone_outdoor(zon,
						"A few drops of rain are fall"\
						"ing admidst a fierce windsto"\
						"rm.\n\r");
					else if(cond->windspeed > 30)
						send_to_zone_outdoor(zon,
						"The wind and a bit of rain "\
						"hint at the possibility of "\
						"a storm.\n\r");
					else
						send_to_zone_outdoor(zon,
						"A light drizzle is falling " \
						"here.\n\r");
				} else if(cond->precip_rate > 70) {
					if(cond->windspeed > 50)
						send_to_zone_outdoor(zon,
						"This must be the worst " \
						"blizzard ever.\n\r");
					else if(cond->windspeed > 25)
						send_to_zone_outdoor(zon,
						"There's a blizzard out " \
						"here, making it quite " \
						"difficult to see.\n\r");
					else
						send_to_zone_outdoor(zon,
						"It's snowing very hard.\n\r");
				} else if(cond->precip_rate > 40) {
					if(cond->windspeed > 60)
						send_to_zone_outdoor(zon,
						"The heavily falling snow is " \
						"being whipped up to a frenzy" \
						" by a ferocious wind.\n\r");
					else if(cond->windspeed > 35)
						send_to_zone_outdoor(zon,
						"A heavy snow is being blown" \
						" randomly about by a brisk " \
						"wind.\n\r");
					else if(cond->windspeed > 18)
						send_to_zone_outdoor(zon,
						"Drifts in the snow are " \
						"being formed by the wind.\n\r");
					else
						send_to_zone_outdoor(zon,
						"The snow's coming down " \
						"pretty fast now.\n\r");
				} else if(cond->precip_rate > 19) {
					if(cond->windspeed > 70)
						send_to_zone_outdoor(zon,
						"The snow wouldn't be too " \
						"bad, except for the awful " \
						"wind blowing it in every " \
						"possible directon.\n\r");
					else if(cond->windspeed > 45)
						send_to_zone_outdoor(zon,
						"There's a minor blizzard " \
						"here, more wind than snow.\n\r");
					else if(cond->windspeed > 12)
						send_to_zone_outdoor(zon,
						"Snow is being blown about " \
						"by a stiff breeze.\n\r");
					else 
						send_to_zone_outdoor(zon,
						"It is snowing here.\n\r");
				} else if(cond->windspeed > 60)
					send_to_zone_outdoor(zon,
					"A light snow is being tossed about " \
					"by a fierce wind.\n\r");
				else if(cond->windspeed > 42)
					send_to_zone_outdoor(zon,
					"A lightly falling snow is being " \
					"driven by a strong wind.\n\r");
				else if(cond->windspeed > 18)
					send_to_zone_outdoor(zon,
					"A light snow is falling admidst " \
					"an unsettled wind.\n\r");
				else
					send_to_zone_outdoor(zon,
					"It is lightly snowing.\n\r");
			}

			/* Handle celestial objects */
			if(!(clime->flags & NO_SUN_EVER)) {
				if(time_info.hours < 6 || time_info.hours > 18
				   || cond->humidity > 90 || cond->precip_rate
				   > 80)
					cond->flags &= ~SUN_VISIBLE;
				else
					cond->flags |= SUN_VISIBLE;
			}
			if(!(clime->flags & NO_MOON_EVER)) {
				if((time_info.hours >5 && time_info.hours <19)
				   || cond->humidity > 80 || cond->precip_rate
				   > 70 || time_info.day < 3 || time_info.day
				   > 31)
					cond->flags &= ~MOON_VISIBLE;
				else if(!(cond->flags & MOON_VISIBLE)) {
					cond->flags |= MOON_VISIBLE;
					if(time_info.day==17)
						send_to_zone_outdoor(zon,
						"The full moon floods the " \
						"area with light.\n\r");
					else
						send_to_zone_outdoor(zon,
						"The moon casts a little " \
						"bit of light on the ground.\n\r");
				}
			}
			calc_light_zone(&zone_table[zon]);
		} /* End zone iteration */
		blow_out_torches();
	} /* End if(mode) */
}

void blow_out_torches()
{
	struct char_data *i;
	struct obj_data *obj;
	for(i=character_list;i;i=i->next) 
		if(!IS_SET(world[i->in_room].room_flags,INDOORS) && i->equipment[WEAR_LIGHT]) {
			obj=i->equipment[WEAR_LIGHT];
			if(obj->obj_flags.value[3]) {
				if(obj->obj_flags.value[3]<zone_table[world[i->in_room].zone].conditions.windspeed && number(0,1)) {
					act("$p goes out",TRUE,i,obj,NULL,TO_ROOM);
					act("Your $p goes out and you put it away.",TRUE,i,obj,NULL,TO_CHAR);
log("blowing out a light source");
					unequip_char(i,WEAR_LIGHT);
					obj_to_char(obj,i);
				}
			}
		}
}

void calc_light_zone(struct zone_data *zone)
{
	char light_sum=0,temp,temp2;

	if(!(zone->climate.flags & NO_SUN_EVER)) {
		temp=time_info.hours;
		if(temp > 11)
			temp=23-temp;
		temp-=5;
		if(temp > 0) {
			zone->conditions.flags |= SUN_VISIBLE;
			light_sum+=temp*10;
		} else
			zone->conditions.flags &= ~SUN_VISIBLE;
	}
	if(!(zone->climate.flags & NO_MOON_EVER)) {
		temp=abs(time_info.hours-12);
		temp-=7;
		if(temp > 0) {
			temp2=17-abs(time_info.day-17);
			temp2-=3;
			if(temp2 > 0) {
				zone->conditions.flags |= MOON_VISIBLE;
				light_sum+=temp*temp2/2;
			} else
				zone->conditions.flags &= ~MOON_VISIBLE;
		}
	}
	light_sum -= zone->conditions.precip_rate;
	light_sum = MAX(0,light_sum);
	light_sum = MIN(100,light_sum);
	zone->conditions.ambient_light=light_sum;
}

char get_season(struct zone_data *zone)
{
	char season_num;
	char buf[MAX_STRING_LENGTH];

	switch(zone->climate.season_pattern) {
		case ONE_SEASON:
			season_num= 0;
			break;
		case TWO_SEASONS_EQUAL:
			season_num=(time_info.month<9)? 0 : 1;
			break;
		case TWO_SEASONS_FIRST_LONG:
			season_num=(time_info.month<11)? 0 : 1;
			break;
		case TWO_SEASONS_SECOND_LONG:
			season_num=(time_info.month<7)? 0 : 1;
			break;
		case THREE_SEASONS_EQUAL:
			season_num=(time_info.month<6)? 0 : 
			           (time_info.month<11)? 1 : 2;
			break;
		case FOUR_SEASONS_EQUAL:
			season_num=(time_info.month<5)? 0 : 
			           (time_info.month<9)? 1 :
			           (time_info.month<13)? 2 : 3;
			break;
		case FOUR_SEASONS_EVEN_LONG:
			season_num=(time_info.month<4)? 0 : 
			           (time_info.month<9)? 1 :
			           (time_info.month<11)? 2 : 3;
			break;
		case FOUR_SEASONS_ODD_LONG:
			season_num=(time_info.month<6)? 0 : 
			           (time_info.month<9)? 1 :
			           (time_info.month<12)? 2 : 3;
			break;
		default:      /* Hmmm?!? */
			log("Bad Season spec in get_season!");
			sprintf(buf,"--> %d in zone %s",
				zone->climate.season_pattern,
				zone->name);
			log(buf);
			season_num=0;
			break;
	}
	return(season_num);
}