////////////////////////////////////////////////////////////////////////////////
//  Olander's Realistic Weather System
//  opw_area_weather
//  By Don Anderson
//  dandersonru@msn.com
//
//  Makes Weather Function in a Module (This is called from the Module Heartbeat
//  Event so this does not need to be placed anywhere)
//
////////////////////////////////////////////////////////////////////////////////

#include "orw_inc_weather"

void main()
{
    object oTVar = OBJECT_SELF;
    string sTVar = GetName(oTVar);
    object oMod  = GetModule();
    object oArea = GetArea(oTVar);
    string sATag = GetTag(oArea);//Each Area Tag Must be Unique!!

    //Dynamic Skyboxes Enabled
    int nSkybox = GetLocalInt(oMod,"WEATHERSKYBOX");

    //Only Natural Areas
    int nNat = GetIsAreaNatural(oArea);
    if(nNat != 1) return;

    //Fog color needs a kick in the rear =D
    int nFSet = GetLocalInt(oArea,"FOGSET");
    if(nFSet == 0)
    {
      SetFogColor(FOG_TYPE_ALL,FOG_MORNING,oArea);
      SetLocalInt(oArea,"FOGSET",1);
    }

    //An Area must be Initialized First
    int nAI = GetLocalInt(oArea,WEATHERINIAREA);
    if(nAI == 0)
    {
      InializeArea(oArea,sTVar);

      //Set Up Matching Areas if Source Waypoint Exists
      string sSource = "SOURCE_" +sATag;
      object oSource = GetNearestObjectByTag(sSource,oTVar,1);
      if(GetIsObjectValid(oSource) == TRUE)
      {
        string sMatch = "MATCH_" +sATag;//Waypoint Must Match the Source Area Tag!!

        //Now we get the areas that have the Matching Weather Waypoint
        int nMth = 0;
        object oMArea;
        object oMatch = GetObjectByTag(sMatch,nMth);
        while(GetIsObjectValid(oMatch))
        {
          oMArea = GetArea(oMatch);
          SetLocalString(oMatch,"WEATHERSOURCE",sATag);
          SetLocalString(oMArea,"WEATHERMATCH",sMatch);
          if(nMth > 50) break;
          nMth++;
          oMatch = GetObjectByTag(sMatch,nMth);
        }
      }
    }

    //Check for Season Change and Intialize Area
    int nIniSeason = GetLocalInt(oArea,WEATHERINISEASON);
    int nCurSeason = GetLocalInt(oArea,WEATHERCURSEASON);
    if(nCurSeason != nIniSeason)
    {
      SetInitializedSeason(oArea);
      InializeArea(oArea,sTVar);
    }

    int nTZ = GetLocalInt(oArea,WEATHERTZONE);
    int nPZ = GetLocalInt(oArea,WEATHERPZONE);
    int nMT = GetLocalInt(oArea,WEATHERMAXTEMP);
    int nWC = GetLocalInt(oArea,WEATHERWATER);
    int nIT = GetLocalInt(oArea,WEATHERINITEMP);
    int nIH = GetLocalInt(oArea,WEATHERINIHUMID);
    int nCW = GetCurrentWeather(oArea);
    int nHour = GetTimeHour();
    int nSeason = GetLocalInt(oArea,WEATHERCURSEASON);
    int nWarm = GetLocalInt(oArea,WEATHERWFRONT);
    int nCold = GetLocalInt(oArea,WEATHERCFRONT);
    int nCT = GetLocalInt(oArea,WEATHERCURTEMP);//Current Temp
    int nHM = GetLocalInt(oArea,WEATHERCURHUMID);//Current Humidity
    int nWet = GetCurrentWeather(oArea);

//:****************************************************************************/
//: WARM AND COLD FRONTS

    int D100 = d100(1);
    int nCF = CheckFront(oArea);
    if(D100 >= 60)
    {
      if(nCF == FALSE) InitializeWarmFront(oArea);
    }
    if(D100 <= 59)
    {
      if(nCF == FALSE) InitializeColdFront(oArea);
    }

//: WARM AND COLD FRONTS
//:****************************************************************************/

//:****************************************************************************/
//: NATURAL HEATING/COOLING CYCLE OF EACH DAY

    //Calc the Heating and Cooling part of the day
    int nHeat = 0;
    if(nHour >= 0 && nHour <=  7) nHeat = 0;
    if(nHour >  7 && nHour <= 16) nHeat = 1;
    if(nHour > 16 && nHour <= 23) nHeat = 0;

    //Warm the Air
    if(nHeat == 1)
    {
      int nMaxR = d3(1) + nMT;
      if(nCT > nMaxR) CoolAirTemp(oArea,nCW,nHeat,nCT);
      else WarmAirTemp(oArea,nCW,nHeat,nCT);
    }

    //Cool the Air
    if(nHeat == 0)
    {
      int nMaxR = nMT - 30;
      if(nCT < nMaxR) WarmAirTemp(oArea,nCW,nHeat,nCT);
      else CoolAirTemp(oArea,nCW,nHeat,nCT);
    }

    //Refresh Current Temp
    nCT = GetLocalInt(oArea,WEATHERCURTEMP);

//: NATURAL HEATING/COOLING CYCLE OF EACH DAY
//:****************************************************************************/

//:****************************************************************************/
//: NATURAL HUMIDITY ACCUMULATION

    //Humidity is Accumulated During the Heating Day and by Area Water Content
    int nIWF = GetLocalInt(oArea,WEATHERWFRONT);
    int nICF = GetLocalInt(oArea,WEATHERCFRONT);
    if(nCF == TRUE && nIWF == 1) nWC = nWC + 5;
    if(nCF == TRUE && nICF == 1) nWC = nWC - 5;

    //Day Humidity During Warm Front
    if(nHeat == 1 && nIWF == 1 && nPZ > 0)
    {
      //The chance to Raise Humidity
      int nHMAdd = d100(1) + nWC;
      if(nHMAdd >= 70) nHM = nHM + 1;
    }

    //Night Humidity During Warm Front
    if(nHeat == 0 && nIWF == 1 && nPZ > 0)
    {
      //The chance to Raise Humidity
      int nHMAdd = d100(1) + nWC;
      if(nHMAdd >= 70) nHM = nHM + 1;
    }

    //Day Humidity During Cold Front (When Not Raining)
    if(nHeat == 1 && nICF == 1 && nPZ > 0 && nWet != WEATHER_RAIN && nWet != WEATHER_SNOW)
    {
      //The chance to Lower Humidity
      int nHMAdd = d100(1) + nWC;
      if(nHMAdd <= 30) nHM = nHM - 1;
    }

    //Night Humidity During Cold Front (When Not Raining)
    if(nHeat == 0 && nICF == 1 && nPZ > 0 && nWet != WEATHER_RAIN && nWet != WEATHER_SNOW)
    {
      //The chance to Lower Humidity
      int nHMAdd = d100(1) + nWC;
      if(nHMAdd <= 30) nHM = nHM - 1;
    }

    //Now Set Our New Humidity
    if(nHM >= 100) nHM = 100;
    if(nHM < 0) nHM = 0;
    if(nPZ > 0) SetLocalInt(oArea,WEATHERCURHUMID,nHM);
    else
    {
      nHM = 0;
      SetLocalInt(oArea,WEATHERCURHUMID,nHM);
    }

//: NATURAL HUMIDITY ACCUMULATION
//:****************************************************************************/

//:****************************************************************************/
//: RAIN AND SETTING WEATHER LEVEL

    //Now Set Up the Weather Level Potential
    //Cold Fronts Will Initiate the Rain
    int nLevel = 0;//Really Clear Sky
    if(nHM >= 20) nLevel = 1;//Normal
    if(nHM >= 30) nLevel = 2;//Cloudy

    //Cold Front
    if(IsInColdFront(oArea) == TRUE && nPZ > 0)
    {
      //Now Set Up the Weather Level Potential
      if(nHM >= 40) nLevel = 3;//Raining/Snowing
      if(nHM >= 60) nLevel = 4;//Thunderstorm No Lightning
      if(nHM >= 80) nLevel = 5;//Thunderstorm With Lightning
    }

    //Spark a Rainstorm
    if(IsInWarmFront(oArea) == TRUE && nPZ > 0 && nHM >= 80)
    {
      nLevel = 3;//Raining/Snowing
      InitializeColdFront(oArea);
    }

    SetLocalInt(oArea,WEATHERLEVEL,nLevel);

    //Only if Dynamic Skyboxes are On
    if(nSkybox == 1)
    {
      //Very Clear Skies
      if(nLevel == 0 && GetSkyBox(oArea) != SKYBOX_DESERT_CLEAR)
      {
        //This Eliminates Skybox Flip Flops
        if(nHM < 15) SetSkyBox(SKYBOX_DESERT_CLEAR,oArea);
      }

      //Party Cloudy Warm Weather Skies
      if(nLevel == 1 && nCT >= 0 && GetSkyBox(oArea) != SKYBOX_GRASS_CLEAR)
      {
        if(GetSkyBox(oArea) == SKYBOX_WINTER_CLEAR || GetSkyBox(oArea) == SKYBOX_ICY)
        {
          if(nHM >= 20 && nHM < 30 && nCT >=5) SetSkyBox(SKYBOX_GRASS_CLEAR,oArea);
        }
        else
        {
          if(nHM >= 20 && nHM < 30) SetSkyBox(SKYBOX_GRASS_CLEAR,oArea);
        }
      }

      //Party Cloudy Cold Weather Skies
      if(nLevel == 1 && nCT < 0 && GetSkyBox(oArea) != SKYBOX_WINTER_CLEAR)
      {
        if(nHM >= 20 && nHM < 30) SetSkyBox(SKYBOX_WINTER_CLEAR,oArea);
      }

      //Stormy Warm Weather Skies
      if(nLevel == 2 && nCT >= 0 && GetSkyBox(oArea) != SKYBOX_GRASS_STORM)
      {
        if(nHM >= 30 && nHM < 40) SetSkyBox(SKYBOX_GRASS_STORM,oArea);

        if(GetSkyBox(oArea) == SKYBOX_ICY || GetSkyBox(oArea) == SKYBOX_WINTER_CLEAR)
        {
          if(nHM >= 30 && nHM < 40 && nCT >=5) SetSkyBox(SKYBOX_GRASS_STORM,oArea);
        }
        else
        {
          if(nHM >= 30 && nHM < 40) SetSkyBox(SKYBOX_GRASS_STORM,oArea);
        }
      }

      //Stormy Cold Weather Skies
      if(nLevel == 2 && nCT < 0 && GetSkyBox(oArea) != SKYBOX_ICY)
      {
        if(nHM >= 30 && nHM < 40) SetSkyBox(SKYBOX_ICY,oArea);
      }

      //Full Clouded
      if(nLevel >= 3 && GetSkyBox(oArea) != SKYBOX_NONE)
      {
        SetSkyBox(SKYBOX_NONE,oArea);
      }
    }

    //Now Check to see if it already Raining or Snowing and Reduce Humidity
    if(nWet == WEATHER_RAIN || nWet == WEATHER_SNOW)
    {
      //We build down by Humidity
      int nHMSub = nHM - 1;
      SetLocalInt(oArea,WEATHERCURHUMID,nHMSub);
    }

//: RAIN AND SETTING WEATHER LEVEL
//:****************************************************************************/

//:****************************************************************************/
//: WHAT TYPE OF FOG

    int nFogLight = FOG_MORNING;
    int nFogMed = FOG_LIGHTRAIN;
    int nFogDark = FOG_STORMCLOUDS;

    //Fog Dynamic Override
    object oFDO = GetNearestObjectByTag("FOG_DYNAMICOVERRIDE",oTVar,1);
    if(oFDO != OBJECT_INVALID)
    {
      string sName = GetName(oFDO);

      if(sName == "Snow")
      {
        nFogLight = FOG_LSNOW;
        nFogMed = FOG_MSNOW;
        nFogDark = FOG_DSNOW;
      }

      if(sName == "Desert")
      {
        nFogLight = FOG_LDESERT;
        nFogMed = FOG_MDESERT;
        nFogDark = FOG_DDESERT;
      }

      if(sName == "Bluish")
      {
        nFogLight = FOG_LBLUISH;
        nFogMed = FOG_MBLUISH;
        nFogDark = FOG_DBLUISH;
      }
    }

    //Is there a Fog Override?
    object oFOver = GetNearestObjectByTag("FOG_OVERRIDE",oTVar,1);
    int nFOver = GetLocalInt(oArea,"FOG_OVERRIDE");
    if(oFOver != OBJECT_INVALID && nFOver == 0)
    {
      string sFOver = GetName(oFOver);
      string sQty = GetStringLeft(sFOver,3);
      string sColor = GetSubString(sFOver,4,3);

      int nColor = FOG_MORNING;//Default

      if(sColor == "Bla") nColor = FOG_COLOR_BLACK;
      if(sColor == "Blu") nColor = FOG_COLOR_BLUE_DARK;
      if(sColor == "Bro") nColor = FOG_COLOR_BROWN_DARK;
      if(sColor == "Gre") nColor = FOG_COLOR_GREEN_DARK;
      if(sColor == "Ora") nColor = FOG_COLOR_ORANGE_DARK;
      if(sColor == "Red") nColor = FOG_COLOR_RED_DARK;
      if(sColor == "Yel") nColor = FOG_COLOR_YELLOW_DARK;

      int nQty = StringToInt(sQty);

      SetLocalInt(oArea,"FOG_OVERRIDE",1);
      SetFogAmount(FOG_TYPE_ALL,nQty,oArea);
      SetFogColor(FOG_TYPE_ALL,nColor,oArea);
    }

    int nLight = GetIsLight();
    int nFT = 0;
    if(nLight == 0) nFT = FOG_TYPE_SUN;
    if(nLight == 1) nFT = FOG_TYPE_SUN;
    if(nLight == 2) nFT = FOG_TYPE_MOON;

    int nFA = GetFogAmount(nFT,oArea);

//: WHAT TYPE OF FOG
//:****************************************************************************/

//:****************************************************************************/
//: MAKE FOG IN THE MORNING AND EVENING

    int nSI = GetLocalInt(oArea,WEATHERLEVEL);
    int nFO = GetLocalInt(oArea,"FOG_OVERRIDE");

    if(nHour >= 0 && nHour <= 6 && nSI <= 1 && nHM >= 10 && nFO == 0)
    {
      if(nCW != WEATHER_CLEAR) SetWeather(oArea,WEATHER_CLEAR);

      if(nHour >= 0 && nHour <= 6)
      {
        if(nFA <= 15) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);
      }
    }

    if(nHour >= 18 && nHour <= 23 && nSI <= 1 && nHM >= 10 && nFO == 0)
    {
      if(nCW != WEATHER_CLEAR) SetWeather(oArea,WEATHER_CLEAR);

      if(nHour >= 18 && nHour <= 23)
      {
        if(nFA <= 15) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);
      }
    }

//: MAKE FOG IN THE MORNING AND EVENING
//:****************************************************************************/

//:****************************************************************************/
//: MAKE CLOUDS BY WEATHER LEVEL

    //Burn off Morning Fog
    if(nHour >= 7 && nHour <= 17 && nSI <= 1 && nFO == 0)
    {
      if(nCW != WEATHER_CLEAR) SetWeather(oArea,WEATHER_CLEAR);

      //Burn off Fog
      if(nFA > 0) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
    }

    //Cloudy Sky
    if(((nHour >=0 && nHour < 3) || (nHour >= 7 && nHour <= 23)) && nSI == 2 && nFO == 0)
    {
      if(nCW != WEATHER_CLEAR) SetWeather(oArea,WEATHER_CLEAR);

      //Create Clouds
      if(nFA < 5) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);

      //Waver the Cloud Shadow
      if(nFA >= 5 && nFA <= 8)
      {
        int nRC = d2(1);
        if(nRC == 1) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);
        if(nRC == 2) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
      }

      //Build Clouds Back This Stage
      if(nFA > 10) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
    }

    //Raining or Snowing
    if(nHour >= 0 && nSI == 3 && nFO == 0)
    {
      //Create Clouds
      if(nFA < 8) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);

      //Create Rain or Snow
      if(nFA >= 8)
      {
        if(nCW != WEATHER_RAIN && nCT >= 0) SetWeather(oArea,WEATHER_RAIN);
        if(nCW != WEATHER_SNOW && nCT < 0) SetWeather(oArea,WEATHER_SNOW);
      }

      //Waver the Cloud Shadow
      if(nFA >= 8 && nFA <= 12)
      {
        int nRC = d2(1);
        if(nRC == 1) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);
        if(nRC == 2) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
      }

      //Build Clouds Back This Stage
      if(nFA > 12) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
    }

    //Storm with No Lightning
    if(nHour >= 0 && nSI == 4 && nFO == 0)
    {
      //Create Clouds
      if(nFA < 10) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);

      if(nFA >= 8)
      {
        if(nCW != WEATHER_RAIN && nCT >= 0) SetWeather(oArea,WEATHER_RAIN);
        if(nCW != WEATHER_SNOW && nCT < 0) SetWeather(oArea,WEATHER_SNOW);
      }

      //Waver the Cloud Shadow
      if(nFA >= 10 && nFA <= 13)
      {
        int nRC = d2(1);
        if(nRC == 1) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);
        if(nRC == 2) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
      }

      //Build Clouds Back This Stage
      if(nFA > 13) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
    }

    //Storm with Lightning
    if(nHour >= 0 && nSI == 5 && nFO == 0)
    {
      //Create Clouds
      if(nFA < 12) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);

      if(nFA >= 8)
      {
        if(nCW != WEATHER_RAIN && nCT >= 0) SetWeather(oArea,WEATHER_RAIN);
        if(nCW != WEATHER_SNOW && nCT < 0) SetWeather(oArea,WEATHER_SNOW);
      }

      //Waver the Cloud Shadow
      if(nFA >= 12 && nFA <= 15)
      {
        int nRC = d2(1);
        if(nRC == 1) SetCloud(oArea,1,nFogLight,nFogMed,nFogDark);
        if(nRC == 2) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
      }

      //Build Clouds Back This Stage
      if(nFA > 15) SetCloud(oArea,-1,nFogLight,nFogMed,nFogDark);
    }

//: MAKE CLOUDS BY WEATHER LEVEL
//:****************************************************************************/

//:****************************************************************************/
//: MAKE WIND BY WEATHER LEVEL

    object oWindSoft = GetNearestObjectByTag("SoftWind",oTVar);
    object oWindStrong = GetNearestObjectByTag("StrongWind",oTVar);
    int nWindSoft = GetLocalInt(oArea,"WINDSOFT");
    int nWindStrong = GetLocalInt(oArea,"WINDSTRONG");

    //Soft Winds
    if(nSI <= 3 && nWindSoft == 0)
    {
      SoundObjectPlay(oWindSoft);
      SoundObjectStop(oWindStrong);
      SetLocalInt(oArea,"WINDSOFT",1);
      SetLocalInt(oArea,"WINDSTRONG",0);
      nWindSoft = 1;
      nWindStrong = 0;
    }
    //Strong Winds
    if(nSI > 3 && nWindStrong == 0)
    {
      SoundObjectPlay(oWindStrong);
      SoundObjectStop(oWindSoft);
      SetLocalInt(oArea,"WINDSOFT",0);
      SetLocalInt(oArea,"WINDSTRONG",1);
      nWindSoft = 0;
      nWindStrong = 1;
    }

    //Wind Volume Control
    if(nSI == 1 && nWindSoft == 1) SoundObjectSetVolume(oWindSoft, 5);
    if(nSI == 2 && nWindSoft == 1) SoundObjectSetVolume(oWindSoft, 7);
    if(nSI == 3 && nWindSoft == 1) SoundObjectSetVolume(oWindSoft, 10);
    if(nSI == 4 && nWindStrong == 1) SoundObjectSetVolume(oWindStrong, 10);
    if(nSI == 5 && nWindStrong == 1) SoundObjectSetVolume(oWindStrong, 15);

//: MAKE WIND BY WEATHER LEVEL
//:****************************************************************************/

//:****************************************************************************/
//: MAKE THUNDER BY WEATHER LEVEL

    object oThunder = GetNearestObjectByTag("Thunder",oTVar);
    int nThunder = GetLocalInt(oArea,"THUNDER");

    //Stop Thunder
    if(nCW != WEATHER_RAIN && nSI < 3 && nThunder == 1)
    {
      SoundObjectStop(oThunder);
      SetLocalInt(oArea,"THUNDER",1);
    }
    //Start Thunder
    if(nCW == WEATHER_RAIN && nSI >= 3 && nCT >= 0 && nThunder == 0)
    {
      SoundObjectPlay(oThunder);
      SetLocalInt(oArea,"THUNDER",1);
    }

//: MAKE THUNDER BY WEATHER LEVEL
//:****************************************************************************/

//:****************************************************************************/
//: MAKE LIGHTNING BY WEATHER LEVEL

    //Start Lightning
    if(nCW == WEATHER_RAIN && nSI == 5 && nCT >= 0 && nThunder == 1)
    {
      int nAir = d2(1);
      location lStrike = RandomLocation(oArea,nAir);
      LightningStrike(oArea,lStrike,nAir);
    }

//: MAKE LIGHTNING BY WEATHER LEVEL
//:****************************************************************************/

//:****************************************************************************/
//: RESET WEATHER FRONTS

   int nFCount = GetLocalInt(oArea,WEATHERCOUNTER);
   int nSDurat = GetLocalInt(oArea,WEATHERDURATION);

   //Currently in a Weather Front
   if(nCF == TRUE)
   {
     if(nFCount == nSDurat)
     {
       SetLocalInt(oArea,WEATHERAFRONT,0);
       SetLocalInt(oArea,WEATHERWFRONT,0);
       SetLocalInt(oArea,WEATHERCFRONT,0);
       SetLocalInt(oArea,WEATHERCOUNTER,0);
       SetLocalInt(oArea,WEATHERDURATION,0);
     }
   }

//: RESET WEATHER FRONTS
//:****************************************************************************/

//:****************************************************************************/
//: MESSAGES TO PLAYERS IN THE AREA

   int nNth = 1;
   object oPC = GetNearestObject(OBJECT_TYPE_CREATURE,oTVar,nNth);
   while (GetIsObjectValid(oPC) == TRUE)
   {
      if(GetIsPC(oPC) == TRUE || GetIsDM(oPC) == TRUE || GetIsDMPossessed(oPC) == TRUE)
      {
        ExecuteScript("orw_pc_message",oPC);
      }
      nNth++;
      oPC = GetNearestObject(OBJECT_TYPE_CREATURE,oTVar,nNth);
   }

//: MESSAGES TO PLAYERS IN THE AREA
//:****************************************************************************/

//:****************************************************************************/
//: FRONT COUNTER

    //Only Count When a Front is in the Area
    if(nSDurat > 0)
    {
      nFCount = nFCount + 1;
      SetLocalInt(oArea,WEATHERCOUNTER,nFCount);
    }

//: FRONT COUNTER
//:****************************************************************************/

}