From 56afb803580ad274160ca878cd8aee87cc1f51fa Mon Sep 17 00:00:00 2001
From: Frederic Bouvier <fredfgfs01@free.fr>
Date: Tue, 3 Jan 2012 20:21:34 +0100
Subject: [PATCH 1/4] Local Weather 1.4 by Thorsten Renk : fixes lots of
 graphical errors and transparency issues and provides a consistent GUI.

---
 Effects/cloud-static.eff                      |   64 +
 Effects/rain-layer.eff                        |   11 +-
 Nasal/local_weather.nas                       | 4761 +++++++++++++++++
 Nasal/local_weather/cloud_definitions.nas     |  135 +-
 Nasal/local_weather/compat_layer.nas          |   70 +-
 Nasal/local_weather/local_weather.nas         |  270 +-
 Nasal/local_weather/weather_dynamics.nas      |   38 +-
 .../local_weather/weather_tile_management.nas |   21 +-
 Nasal/local_weather/weather_tiles.nas         |  358 +-
 Shaders/cloud-static.frag                     |   10 +
 Shaders/cloud-static.vert                     |   55 +
 Shaders/rain-layer.vert                       |    4 +-
 gui/dialogs/local_weather_tiles.xml           |  309 +-
 13 files changed, 5825 insertions(+), 281 deletions(-)
 create mode 100644 Effects/cloud-static.eff
 create mode 100644 Nasal/local_weather.nas
 create mode 100644 Shaders/cloud-static.frag
 create mode 100644 Shaders/cloud-static.vert

diff --git a/Effects/cloud-static.eff b/Effects/cloud-static.eff
new file mode 100644
index 000000000..52870a571
--- /dev/null
+++ b/Effects/cloud-static.eff
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PropertyList>
+  <name>Effects/rain-layer</name>
+  <parameters>
+    <texture n ="0">
+    </texture>
+  </parameters>
+  <technique n="10">
+    <predicate>
+      <and>
+        <property>/sim/rendering/clouds3d-enable</property>
+        <less-equal>
+          <value type="float">1.0</value>
+          <shader-language/>
+        </less-equal>
+      </and>
+    </predicate>
+    <pass n="0">
+      <!-- This is apparently not used, so maybe we'll blow it way soon. -->
+      <lighting>true</lighting>
+      <material>
+        <ambient type="vec4d">0.5 0.5 0.5 1.0</ambient>
+        <diffuse type="vec4d">0.5 0.5 0.5 1.0</diffuse>
+        <color-mode>off</color-mode>
+      </material>
+      <alpha-test>
+        <comparison>greater</comparison>
+        <reference type="float">0.01</reference>
+      </alpha-test>
+      <shade-model>smooth</shade-model>
+      <blend>
+        <source>src-alpha</source>
+        <destination>one-minus-src-alpha</destination>
+      </blend>
+      <depth>
+        <write-mask>false</write-mask>
+      </depth>
+      <render-bin>
+        <bin-number>9</bin-number>
+        <bin-name>DepthSortedBin</bin-name>
+      </render-bin>
+      <texture-unit>
+        <unit>0</unit>
+	<type><use>texture[0]/type</use></type>
+        <image><use>texture[0]/image</use></image>
+        <filter><use>texture[0]/filter</use></filter>
+        <wrap-s><use>texture[0]/wrap-s</use></wrap-s>
+        <wrap-t><use>texture[0]/wrap-t</use></wrap-t>
+        <!--<wrap-s>clamp</wrap-s>
+        <wrap-t>clamp</wrap-t>-->
+      </texture-unit>
+      <program>
+        <vertex-shader>Shaders/cloud-static.vert</vertex-shader>
+        <fragment-shader>Shaders/cloud-static.frag</fragment-shader>
+      </program>
+      <uniform>
+        <name>baseTexture</name>
+        <type>sampler-2d</type>
+        <value type="int">0</value>
+      </uniform>
+      <vertex-program-two-side>true</vertex-program-two-side>
+    </pass>
+  </technique>
+</PropertyList>
diff --git a/Effects/rain-layer.eff b/Effects/rain-layer.eff
index f0296c360..828237fb6 100644
--- a/Effects/rain-layer.eff
+++ b/Effects/rain-layer.eff
@@ -4,6 +4,9 @@
   <parameters>
     <texture n ="0">
     </texture>
+    <range>
+      <use>/sim/rendering/clouds3d-vis-range</use>
+    </range>   
   </parameters>
   <technique n="10">
     <predicate>
@@ -36,7 +39,7 @@
         <write-mask>false</write-mask>
       </depth>
       <render-bin>
-        <bin-number>10</bin-number>
+        <bin-number>9</bin-number>
         <bin-name>DepthSortedBin</bin-name>
       </render-bin>
       <texture-unit>
@@ -58,6 +61,12 @@
         <type>sampler-2d</type>
         <value type="int">0</value>
       </uniform>
+      <uniform>
+        <name>range</name>
+        <type>float</type>
+        <value><use>range</use></value>
+      </uniform>
+
       <vertex-program-two-side>true</vertex-program-two-side>
     </pass>
   </technique>
diff --git a/Nasal/local_weather.nas b/Nasal/local_weather.nas
new file mode 100644
index 000000000..afb649dd2
--- /dev/null
+++ b/Nasal/local_weather.nas
@@ -0,0 +1,4761 @@
+
+########################################################
+# routines to set up, transform and manage local weather
+# Thorsten Renk, June 2011
+# thermal model by Patrice Poly, April 2010
+########################################################
+
+# function			purpose
+#
+# calc_geo			to compute the latitude to meter conversion
+# calc_d_sq			to compute a distance square in local Cartesian approximation
+# effect_volume_loop		to check if the aircraft has entered an effect volume
+# assemble_effect_array 	to store the size of the effect volume array
+# add_vectors			to add two vectors in polar coordinates
+# wind_altitude_interpolation 	to interpolate aloft winds in altitude
+# wind_interpolation		to interpolate aloft winds in altitude and position
+# get_slowdown_fraction		to compute the effect of boundary layer wind slowdown
+# interpolation_loop		to continuously interpolate weather parameters between stations 
+# thermal_lift_start		to start the detailed thermal model
+# thermal_lift_loop		to manage the detailed thermal lift model
+# thermal_lift_stop		to end the detailed thermal lift model
+# wave_lift_start		to start the detailed wave lift model
+# wave_lift_loop		to manage the detailed wave lift model
+# wave_lift_stop		to end the detailed wave lift model
+# effect_volume_start		to manage parameters when an effect volume is entered
+# effect_volume_stop		to manage parameters when an effect volume is left
+# ts_factor			(helper function for thermal lift model)
+# tl_factor			(helper function for thermal lift model)
+# calcLift_max			to calculate the maximal available thermal lift for given altitude
+# calcLift			to calculate the thermal lift at aircraft position
+# calcWaveLift			to calculate wave lift at aircraft position
+# create_cloud_vec		to place a single cloud into an array to be written later
+# clear_all			to remove all clouds, effect volumes and weather stations and stop loops
+# create_detailed_cumulus_cloud	to place multiple cloudlets into a box based on a size parameter
+# create_cumulonimbus_cloud	to place multiple cloudlets into a box 
+# create_cumulonimbus_cloud_rain to place multiple cloudlets into a box and add a rain layer beneath
+# create_cumosys		wrapper to place a convective cloud system based on terrain coverage
+# cumulus_loop			to place 25 Cumulus clouds each frame
+# create_cumulus		to place a convective cloud system based on terrain coverage
+# recreate_cumulus		to respawn convective clouds as part of the convective dynamics algorithm
+# cumulus_exclusion_layer	to create a layer with 'holes' left for thunderstorm placement
+# create_rise_clouds		to create a barrier cloud system
+# create_streak			to create a cloud streak
+# create_undulatus		to create an undulating cloud pattern
+# create_layer			to create a cloud layer with optional precipitation
+# create_hollow_layer		to create a cloud layer in a hollow cylinder (better for performance)
+# create_cloudbox		to create a sophisticated cumulus cloud with different textures (experimental)
+# terrain_presampling_start	to initialize terrain presampling
+# terrain_presampling_loop 	to sample 25 terrain points per frame
+# terrain_presampling		to sample terrain elevation at a random point within specified area
+# terrain_presampling_analysis	to analyze terrain presampling results
+# wave_detection_loop		to detect if and where wave lift should be placed (currently unfinished)
+# get_convective_altitude	to determine the altitude at which a Cumulus cloud is placed
+# manage presampling		to take proper action when a presampling call has been finished
+# set_wind_model_flag		to convert the wind model string into an integer flag
+# set_texture_mix		to determine the texture mix between smooth and rough cloud appearance
+# create_effect_volume		to create an effect volume
+# set_weather_station		to specify a weather station for interpolation
+# set_atmosphere_ipoint		to specify an interpolation point for visibility, haze and shading in the atmosphere
+# set_wind_ipoint		to set an aloft wind interpolation point
+# set_wind_ipoint_metar		to set a wind interpolation point from available ground METAR info where aloft is modelled
+# showDialog			to pop up a dialog window
+# readFlags			to read configuration flags from the property tree into Nasal variables at startup
+# streak_wrapper		wrapper to execute streak from menu
+# convection wrapper		wrapper to execute convective clouds from menu
+# barrier wrapper 		wrapper to execute barrier clouds from menu
+# single_cloud_wrapper		wrapper to create single cloud from menu
+# layer wrapper			wrapper to create layer from menu
+# box wrapper			wrapper to create a cloudbox (experimental)
+# set_aloft wrapper		wrapper to create aloft winds from menu
+# set_tile			to call a weather tile creation from menu
+# startup			to prepare the package at startup
+# test				to serve as a testbed for new functions
+
+# object			purpose
+
+# weatherStation		to store info about weather conditions
+# atmopshereIpoint		to store info about haze and light propagation in the atmosphere
+# windIpoint			to store an interpolation point of the windfield
+# effectVolume			to store effect volume info and provide methods to move and time-evolve effect volumes
+# thermalLift			to store thermal info and provide methods to move and time-evolve a thermal
+# waveLift 			to store wave info 
+
+
+
+
+###################################
+# geospatial helper functions
+###################################
+
+var calc_geo = func(clat) {
+
+lon_to_m  = math.cos(clat*math.pi/180.0) * lat_to_m;
+m_to_lon = 1.0/lon_to_m;
+
+weather_dynamics.lon_to_m = lon_to_m;
+weather_dynamics.m_to_lon = m_to_lon;
+
+}
+
+
+var calc_d_sq = func (lat1, lon1, lat2, lon2) {
+
+var x = (lat1 - lat2) * lat_to_m;
+var y = (lon1 - lon2) * lon_to_m;
+
+return (x*x + y*y);
+}
+
+
+###################################
+# effect volume management loop
+###################################
+
+var effect_volume_loop = func (index, n_active) {
+
+
+if (local_weather_running_flag == 0) {return;}
+
+var n = 25;
+
+
+var esize = n_effectVolumeArray;
+
+var viewpos = geo.aircraft_position();
+var active_counter = n_active;
+
+var i_max = index + n;
+if (i_max > esize) {i_max = esize;}
+
+for (var i = index; i < i_max; i = i+1)
+	{
+	var e = effectVolumeArray[i];
+	
+	var flag = 0; # default assumption is that we're not in the volume
+	
+	var ealt_min = e.alt_low * ft_to_m;
+	var ealt_max = e.alt_high * ft_to_m;
+
+	
+	if ((viewpos.alt() > ealt_min) and (viewpos.alt() < ealt_max)) # we are in the correct alt range
+		{
+		# so we load geometry next
+		
+		var geometry = e.geometry;
+		var elat = e.lat;
+		var elon = e.lon;
+		var rx = e.r1;
+
+		if (geometry == 1) # we have a cylinder
+			{
+			var d_sq = calc_d_sq(viewpos.lat(), viewpos.lon(), elat, elon);
+			if (d_sq < (rx*rx)) {flag =1;}
+			}
+		else if (geometry == 2) # we have an elliptic shape
+			{
+			# get orientation
+
+			var ry = e.r2;
+			var phi = e.phi;		
+
+			phi = phi * math.pi/180.0;
+						
+
+			# first get unrotated coordinates 
+			var xx = (viewpos.lon() - elon) * lon_to_m;
+			var yy = (viewpos.lat() - elat) * lat_to_m;
+			
+			# then rotate to align with the shape
+			var x = xx * math.cos(phi) - yy * math.sin(phi);
+			var y = yy * math.cos(phi) + xx * math.sin(phi); 
+
+			# then check elliptic condition
+			if ((x*x)/(rx*rx) + (y*y)/(ry*ry) <1) {flag = 1;}
+			}
+		else if (geometry == 3) # we have a rectangular shape
+			{
+			# get orientation
+
+			var ry = e.r2;
+			var phi = e.phi;
+
+			phi = phi * math.pi/180.0;
+			# first get unrotated coordinates 
+			var xx = (viewpos.lon() - elon) * lon_to_m;
+			var yy = (viewpos.lat() - elat) * lat_to_m;
+			# then rotate to align with the shape
+			var x = xx * math.cos(phi) - yy * math.sin(phi);
+			var y = yy * math.cos(phi) + xx * math.sin(phi); 
+			# then check rectangle condition
+			if ((x>-rx) and (x<rx) and (y>-ry) and (y<ry)) {flag = 1;}
+			}
+		} # end if altitude
+	
+	
+	# if flag ==1 at this point, we are inside the effect volume
+	# but we only need to take action on entering and leaving, so we check also active_flag
+	
+	# if (flag==1) {print("Inside volume");}
+	
+	var active_flag = e.active_flag;
+
+	if ((flag==1) and (active_flag ==0)) # we just entered the node
+		{
+		#print("Entered volume");		
+		e.active_flag = 1;	
+		effect_volume_start(e);
+		}
+	else if ((flag==0) and (active_flag ==1)) # we left an active node
+		{
+		#print("Left volume!");
+		e.active_flag = 0;
+		effect_volume_stop(e);
+		}
+	if (flag==1) {active_counter = active_counter + 1;} # we still count the active volumes
+	
+	} # end foreach
+
+# at this point, all active effect counters should have been set to zero if we're outside all volumes
+# however there seem to be rare configurations of overlapping volumes for which this doesn't happen
+# therefore we zero them for redundancy here so that the interpolation loop can take over
+# and set the properties correctly for outside
+
+
+if (i == esize) # we check the number of actives and reset all counters
+	{
+	if (active_counter == 0)
+		{
+		var vNode = props.globals.getNode("local-weather/effect-volumes", 1);
+		vNode.getChild("number-active-vis").setValue(0);
+		vNode.getChild("number-active-snow").setValue(0);
+		vNode.getChild("number-active-rain").setValue(0);
+		vNode.getChild("number-active-lift").setValue(0);
+		vNode.getChild("number-active-turb").setValue(0);
+		vNode.getChild("number-active-sat").setValue(0);
+		}
+	#print("n_active: ", active_counter);
+	active_counter = 0; i = 0;
+	}
+
+# and we repeat the loop as long as the control flag is set
+
+
+if (getprop(lw~"effect-loop-flag") ==1) {settimer( func {effect_volume_loop(i, active_counter); },0);}
+}
+
+
+###################################
+# assemble effect volume array
+###################################
+
+
+var assemble_effect_array = func {
+
+n_effectVolumeArray = size(effectVolumeArray);
+}
+
+
+
+###################################
+# vector addition
+###################################
+
+var add_vectors = func (phi1, r1, phi2, r2) {
+
+phi1 = phi1 * math.pi/180.0;
+phi2 = phi2 * math.pi/180.0;
+
+var x1 = r1 * math.sin(phi1);
+var x2 = r2 * math.sin(phi2);
+
+var y1 = r1 * math.cos(phi1);
+var y2 = r2 * math.cos(phi2);
+
+var x = x1+x2;
+var y = y1+y2;
+
+var phi = math.atan2(x,y) * 180.0/math.pi;
+var r = math.sqrt(x*x + y*y);
+
+var vec = [];
+
+append(vec, phi);
+append(vec,r);
+
+return vec;
+}
+
+
+###################################
+# windfield altitude interpolation
+###################################
+
+
+var wind_altitude_interpolation = func (altitude, w) {
+
+if (altitude < wind_altitude_array[0]) {var alt_wind = wind_altitude_array[0];}
+else if (altitude > wind_altitude_array[8]) {var alt_wind = 0.99* wind_altitude_array[8];}
+else {var alt_wind = altitude;}
+
+for (var i = 0; i<9; i=i+1)
+	{if (alt_wind < wind_altitude_array[i]) {break;}}
+	
+
+#var altNodeMin = w.getChild("altitude",i-1);
+#var altNodeMax = w.getChild("altitude",i);	
+
+#var vmin = altNodeMin.getNode("windspeed-kt").getValue();
+#var vmax = altNodeMax.getNode("windspeed-kt").getValue();
+
+var vmin = w.alt[i-1].v;
+var vmax = w.alt[i].v;
+
+#var dir_min = altNodeMin.getNode("wind-from-heading-deg").getValue();
+#var dir_max = altNodeMax.getNode("wind-from-heading-deg").getValue();
+
+var dir_min = w.alt[i-1].d;
+var dir_max = w.alt[i].d;
+
+var f = (alt_wind - wind_altitude_array[i-1])/(wind_altitude_array[i] - wind_altitude_array[i-1]);
+
+var res = add_vectors(dir_min, (1-f) * vmin, dir_max, f * vmax);
+
+return res;
+}
+
+
+###################################
+# windfield spatial interpolation
+###################################
+
+var wind_interpolation = func (lat, lon, alt) {
+
+var sum_norm = 0;
+var sum_wind = [0,0];
+
+var wsize = size(windIpointArray);
+	
+for (var i = 0; i < wsize; i=i+1) {
+	
+	
+	var w = windIpointArray[i];
+
+	var wpos = geo.Coord.new();
+	wpos.set_latlon(w.lat,w.lon,1000.0);
+
+	var ppos = geo.Coord.new();
+	ppos.set_latlon(lat,lon,1000.0);
+
+	var d = ppos.distance_to(wpos);
+	if (d <100.0) {d = 100.0;} # to prevent singularity at zero
+
+	sum_norm = sum_norm + (1./d) * w.weight;
+
+	var res = wind_altitude_interpolation(alt,w);
+	
+	sum_wind = add_vectors(sum_wind[0], sum_wind[1], res[0], (res[1]/d) * w.weight);	
+
+	# gradually fade in the interpolation weight of newly added points to
+	# avoid sudden jumps
+
+	if (w.weight < 1.0) {w.weight = w.weight + 0.02;}
+
+	}
+
+sum_wind[1] = sum_wind[1] /sum_norm;
+
+return sum_wind;
+}
+
+
+###################################
+# boundary layer computations
+###################################
+
+
+var get_slowdown_fraction = func {
+
+var tile_index = getprop(lw~"tiles/tile[4]/tile-index");
+var altitude_agl = getprop("/position/altitude-agl-ft");
+var altitude = getprop("/position/altitude-ft");
+
+
+
+if (presampling_flag == 0)
+	{
+	var base_layer_thickness = 600.0;	
+	var f_slow = 1.0/3.0;
+	}
+else 
+	{
+	var alt_median = alt_50_array[tile_index - 1];
+	var alt_difference = alt_median - (altitude - altitude_agl);
+	var base_layer_thickness = 150.0;	
+
+	# get the boundary layer size dependent on terrain altitude above terrain median
+
+	if (alt_difference > 0.0) # we're low and the boundary layer grows
+		{var boundary_alt = base_layer_thickness + 0.3 * alt_difference;}
+	else # the boundary layer shrinks
+		{var boundary_alt = base_layer_thickness + 0.1 * alt_difference;}
+
+	if (boundary_alt < 50.0){boundary_alt = 50.0;}
+	if (boundary_alt > 3000.0) {boundary_alt = 3000.0;}
+
+	# get the boundary effect as a function of bounday layer size
+	
+	var f_slow = 1.0 - (0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness));
+	}
+
+if (debug_output_flag == 1)
+	{
+	#print("Boundary layer thickness: ",base_layer_thickness);
+	#print("Boundary layer slowdown: ", f_slow);
+	}
+return f_slow;
+}
+
+
+###################################
+# interpolation management loop
+###################################
+
+var interpolation_loop = func {
+
+if (local_weather_running_flag == 0) {return;}
+
+var viewpos = geo.aircraft_position();
+
+
+
+var vis_before = getprop(lwi~"visibility-m");
+
+# if applicable, do some work for fps sampling
+
+if (fps_control_flag == 1)
+	{
+	fps_samples = fps_samples +1;
+	fps_sum = fps_sum + getprop("/sim/frame-rate");
+	}
+
+
+# determine at which distance we no longer keep an interpolation point, needs to be larger for METAR since points are more scarce
+
+if (metar_flag == 1)
+	{var distance_to_unload = 250000.0;}
+else 	
+	{var distance_to_unload = 120000.0;}	
+
+# if we can set environment without a reset, the loop can run a bit faster for smoother interpolation
+# so determine the suitable timing
+
+if (compat_layer.features.can_disable_environment == 1)
+	{var interpolation_loop_time = 0.2; var vlimit = 1.01;}
+else
+	{var interpolation_loop_time = 1.0; var vlimit = 1.05;} 
+
+
+# get an inverse distance weighted average from all defined weather stations
+
+var sum_alt = 0.0;
+var sum_vis = 0.0;
+var sum_T = 0.0;
+var sum_p = 0.0;
+var sum_D = 0.0;
+var sum_norm = 0.0;
+
+var n_stations = size(weatherStationArray);
+
+for (var i = 0; i < n_stations; i=i+1) {
+	
+	s = weatherStationArray[i];
+	
+
+	var stpos = geo.Coord.new();
+	stpos.set_latlon(s.lat,s.lon,0.0);
+
+	var d = viewpos.distance_to(stpos);
+	if (d <100.0) {d = 100.0;} # to prevent singularity at zero
+
+	sum_norm = sum_norm + 1./d * s.weight;
+	
+	sum_alt = sum_alt + (s.alt/d) * s.weight;
+	sum_vis = sum_vis + (s.vis/d) * s.weight;
+	sum_T = sum_T + (s.T/d) * s.weight;
+	sum_D = sum_D + (s.D/d) * s.weight;
+	sum_p = sum_p + (s.p/d) * s.weight;
+
+	# gradually fade in the interpolation weight of newly added stations to
+	# avoid sudden jumps
+
+	if (s.weight < 1.0) {s.weight = s.weight + 0.02;}
+
+	# automatically delete stations out of range
+	# take care not to unload if weird values appear for a moment
+	# never unload if only one station left
+	if ((d > distance_to_unload) and (d < (distance_to_unload + 20000.0)) and (n_stations > 1)) 
+		{
+		if (debug_output_flag == 1) 
+			{print("Distance to weather station ", d, " m, unloading ...", i);}
+		weatherStationArray = weather_tile_management.delete_from_vector(weatherStationArray,i);
+		i = i-1; n_stations = n_stations -1;
+		}
+	}
+
+setprop(lwi~"station-number", i+1);
+
+
+var ialt = sum_alt/sum_norm;
+var vis = sum_vis/sum_norm;
+var p = sum_p/sum_norm;
+var D = sum_D/sum_norm + temperature_offset;
+var T = sum_T/sum_norm + temperature_offset;
+
+
+# get an inverse distance weighted average from all defined atmospheric condition points
+
+sum_norm = 0.0;
+var sum_vis_aloft = 0.0;
+var sum_vis_alt1 = 0.0;
+var sum_vis_ovcst = 0.0;
+var sum_ovcst = 0.0;
+var sum_ovcst_alt_low = 0.0;
+var sum_ovcst_alt_high = 0.0;
+var sum_scatt = 0.0;
+var sum_scatt_alt_low = 0.0;
+var sum_scatt_alt_high = 0.0;
+
+var n_iPoints = size(atmosphereIpointArray);
+
+for (var i = 0; i < n_iPoints; i=i+1) {
+	
+	a = atmosphereIpointArray[i];
+	
+
+	var apos = geo.Coord.new();
+	apos.set_latlon(a.lat,a.lon,0.0);
+
+	var d = viewpos.distance_to(apos);
+	if (d <100.0) {d = 100.0;} # to prevent singularity at zero
+
+	sum_norm = sum_norm + 1./d * a.weight;
+	sum_vis_aloft = sum_vis_aloft + (a.vis_aloft/d) * a.weight;
+	sum_vis_alt1 = sum_vis_alt1 + (a.vis_alt1/d) * a.weight;
+	sum_vis_ovcst = sum_vis_ovcst + (a.vis_ovcst/d) * a.weight;	
+	sum_ovcst = sum_ovcst + (a.ovcst/d) * a.weight;
+	sum_ovcst_alt_low = sum_ovcst_alt_low + (a.ovcst_alt_low/d) * a.weight;
+	sum_ovcst_alt_high = sum_ovcst_alt_high + (a.ovcst_alt_high/d) * a.weight;
+	sum_scatt = sum_scatt + (a.scatt/d) * a.weight;
+	sum_scatt_alt_low = sum_scatt_alt_low + (a.scatt_alt_low/d) * a.weight;
+	sum_scatt_alt_high = sum_scatt_alt_high + (a.scatt_alt_high/d) * a.weight;
+
+	# gradually fade in the interpolation weight of newly added stations to
+	# avoid sudden jumps
+
+	if (a.weight < 1.0) {a.weight = a.weight + 0.02;}
+
+	# automatically delete stations out of range
+	# take care not to unload if weird values appear for a moment
+	# never unload if only one station left
+	if ((d > distance_to_unload) and (d < (distance_to_unload + 20000.0)) and (n_iPoints > 1)) 
+		{
+		if (debug_output_flag == 1) 
+			{print("Distance to atmosphere interpolation point ", d, " m, unloading ...", i);}
+		atmosphereIpointArray = weather_tile_management.delete_from_vector(atmosphereIpointArray,i);
+		i = i-1; n_iPoints = n_iPoints -1;
+		}
+	}
+
+setprop(lwi~"atmosphere-ipoint-number", i+1);
+
+
+
+var vis_aloft = sum_vis_aloft/sum_norm;
+var vis_alt1 = sum_vis_alt1/sum_norm;
+var vis_ovcst = sum_vis_ovcst/sum_norm;
+var ovcst_max = sum_ovcst/sum_norm;
+var ovcst_alt_low = sum_ovcst_alt_low/sum_norm;
+var ovcst_alt_high = sum_ovcst_alt_high/sum_norm;
+var scatt_max = sum_scatt/sum_norm;
+var scatt_alt_low = sum_scatt_alt_low/sum_norm;
+var scatt_alt_high = sum_scatt_alt_high/sum_norm;
+
+
+
+
+# altitude model for visibility - increase above the lowest inversion layer to simulate ground haze
+
+vis = vis * ground_haze_factor;
+
+var altitude = getprop("position/altitude-ft");
+current_mean_terrain_elevation = ialt;
+
+var alt1 = vis_alt1;
+var alt2 = alt1 + 1500.0;
+
+setprop("/environment/ground-visibility-m",vis);
+setprop("/environment/ground-haze-thickness-m",alt2 * ft_to_m);
+
+# compute the visibility gradients
+
+if (realistic_visibility_flag == 1)
+	{
+	vis_aloft = vis_aloft * 2.0;
+	vis_ovcst = vis_ovcst * 3.0;
+	}
+
+var inc1 = 0.1 * (vis_aloft - vis)/(vis_alt1 - ialt);
+var inc2 = 0.9 * (vis_aloft - vis)/1500.0;
+var inc3 = (vis_ovcst - vis_aloft)/(ovcst_alt_high - vis_alt1+1500);
+var inc4 = 0.5;
+
+
+if (realistic_visibility_flag == 1)
+	{inc4 = inc4 * 3.0;}
+
+# compute the visibility
+
+if (altitude < alt1)
+	{vis = vis + inc1 * altitude;}
+else if (altitude < alt2)
+	{
+	vis = vis + inc1 * alt1 + inc2 * (altitude - alt1); 
+	}
+else if	(altitude < ovcst_alt_high)
+	{
+	vis = vis + inc1 * alt1 + inc2 * (alt2-alt1)  + inc3 * (altitude - alt2);
+	}
+else if (altitude > ovcst_alt_high)
+	{
+	vis = vis + inc1 * alt1 + inc2 * (alt2-alt1)  + inc3 * (ovcst_alt_high - alt2) + inc4 * (altitude - ovcst_alt_high);
+	}
+
+# limit visibility (otherwise memory consumption may be very bad...)
+
+if (vis > max_vis_range)
+	{vis = max_vis_range;}
+
+# determine scattering shader parameters if scattering shader is on
+
+if (scattering_shader_flag == 1) 
+	{
+	var rayleigh = 0.0003 ;
+	var mie = 0.003;
+	var density = 0.3;
+
+
+	
+	if (altitude < 30000.0) 
+		{
+		rayleigh = 0.0004 - altitude/30000.0 * 0.0001;
+		mie = 0.004 - altitude/30000.0 * 0.001; 
+		}
+	else if (altitude < 60000.0)
+		{
+		rayleigh = 0.0003 - (altitude-30000.0)/30000.0 * 0.0001;
+		mie = 0.003 - (altitude-30000.0)/30000.0 * 0.001; 
+		}
+	else if (altitude < 85000.0)
+		{
+		rayleigh = 0.0002 - (altitude-60000.0)/25000.0 * 0.0001;
+		mie = 0.002;
+		}
+	else 
+		{rayleigh = 0.0001; mie = 0.002;}
+	}
+# otherwise compute normal skydome shader parameters
+
+	
+
+# compute the horizon shading
+
+if (altitude < scatt_alt_low)
+	{
+	var scatt = scatt_max;
+	}
+else if (altitude < scatt_alt_high)
+	{
+	var scatt = scatt_max + (0.95 - scatt_max) * (altitude - scatt_alt_low)/(scatt_alt_high-scatt_alt_low);
+	}
+else
+	{var scatt = 0.95;}
+
+
+# compute the overcast haze
+
+if (altitude < ovcst_alt_low)
+	{
+	var ovcst = ovcst_max;
+	}	
+else if (altitude < ovcst_alt_high)
+	{
+	var ovcst = ovcst_max - ovcst_max * (altitude - ovcst_alt_low)/(ovcst_alt_high-ovcst_alt_low);
+	}
+else
+	{var ovcst = 0.0;}
+
+
+
+
+# limit relative changes of the visibility, will make for gradual transitions
+
+if (vis/vis_before > vlimit)
+	{vis = vlimit * vis_before;}
+else if (vis/vis_before < (2.0-vlimit))
+	{vis = (2.0-vlimit) * vis_before;}
+
+
+
+
+# write all properties into the weather interpolation record in the property tree
+
+setprop(lwi~"mean-terrain-altitude-ft",ialt);
+if (vis > 0.0) {setprop(lwi~"visibility-m",vis);} # a redundancy check
+setprop(lwi~"temperature-degc",T);
+setprop(lwi~"dewpoint-degc",D);
+if (p > 10.0) {setprop(lwi~"pressure-sea-level-inhg",p);}
+setprop(lwi~"turbulence",0.0);
+
+
+if (scattering_shader_flag == 1)
+	{
+	local_weather.setSkydomeShader(rayleigh, mie, density);
+	}
+
+local_weather.setScattering(scatt);
+local_weather.setOvercast(ovcst);
+	
+
+# now check if an effect volume writes the property and set only if not
+
+flag = getprop("local-weather/effect-volumes/number-active-vis");
+
+if ((flag ==0) and (vis > 0.0) and (getprop(lw~"lift-loop-flag") == 0) and (compat_layer.smooth_visibility_loop_flag == 0))
+	{
+	setprop(lw~"current/visibility-m",vis);
+	compat_layer.setVisibility(vis);
+	}
+
+flag = getprop("local-weather/effect-volumes/number-active-turb");
+
+if ((flag ==0))
+	{
+	setprop(lw~"current/turbulence",0.0);		
+	compat_layer.setTurbulence(0.0);
+	}
+
+
+flag = getprop("local-weather/effect-volumes/number-active-lift");
+
+if (flag ==0) 
+	{
+	setprop(lw~"current/thermal-lift",0.0);
+	}
+
+# no need to check for these, as they are not modelled in effect volumes
+
+setprop(lw~"current/temperature-degc",T);
+compat_layer.setTemperature(T);
+
+setprop(lw~"current/dewpoint-degc", D);
+compat_layer.setDewpoint(D);
+
+if (p>0.0) 
+	{
+	setprop(lw~"current/pressure-sea-level-inhg",p);
+	compat_layer.setPressure(p);
+	}
+
+
+# now determine the local wind 
+
+
+var tile_index = getprop(lw~"tiles/tile[4]/tile-index");
+
+if (wind_model_flag ==1) # constant
+	{
+	var winddir = weather_dynamics.tile_wind_direction[0];
+	var windspeed = weather_dynamics.tile_wind_speed[0];
+
+	wind.cloudlayer = [winddir,windspeed];
+
+	}
+else if (wind_model_flag ==2) # constant in tile
+	{
+	var winddir = weather_dynamics.tile_wind_direction[tile_index-1];
+	var windspeed = weather_dynamics.tile_wind_speed[tile_index-1];
+
+	wind.cloudlayer = [winddir, windspeed];
+
+	}	
+else if (wind_model_flag ==3) # aloft interpolated, constant in tiles
+	{
+	var w = windIpointArray[0];
+	var res = wind_altitude_interpolation(altitude,w);
+	var winddir = res[0];
+	var windspeed = res[1];
+
+	wind.cloudlayer = wind_altitude_interpolation(0.0,w);
+
+	}
+else if (wind_model_flag == 5) # aloft waypoint interpolated
+	{
+	var res = wind_interpolation(viewpos.lat(), viewpos.lon(), altitude);	
+
+	var winddir = res[0];
+	var windspeed = res[1];
+
+	wind.cloudlayer = wind_interpolation(viewpos.lat(), viewpos.lon(), 0.0);	
+	}
+
+
+wind.surface = [wind.cloudlayer[0], wind.cloudlayer[1] * get_slowdown_fraction()];
+
+# now do the boundary layer computations
+
+var altitude_agl = getprop("/position/altitude-agl-ft");
+
+
+if (presampling_flag == 0)
+	{
+	var boundary_alt = 600.0;
+	var windspeed_ground = windspeed/3.0;
+	
+	var f_min = 2.0/3.0;
+
+	if (altitude_agl < boundary_alt)
+		{var windspeed_current = windspeed_ground + 2.0 * windspeed_ground * (altitude_agl/boundary_alt);}
+	else 
+		{var windspeed_current = windspeed;}
+	}
+else 
+	{
+	var alt_median = alt_50_array[tile_index - 1];
+	var alt_difference = alt_median - (altitude - altitude_agl);
+	var base_layer_thickness = 150.0;	
+
+	# get the boundary layer size dependent on terrain altitude above terrain median
+
+	if (alt_difference > 0.0) # we're low and the boundary layer grows
+		{var boundary_alt = base_layer_thickness + 0.3 * alt_difference;}
+	else # the boundary layer shrinks
+		{var boundary_alt = base_layer_thickness + 0.1 * alt_difference;}
+
+	if (boundary_alt < 50.0){boundary_alt = 50.0;}
+	if (boundary_alt > 3000.0) {boundary_alt = 3000.0;}
+
+	# get the boundary effect as a function of bounday layer size
+	
+	var f_min = 0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness);
+
+
+	if (altitude_agl < boundary_alt)
+		{
+		var windspeed_current = (1-f_min) * windspeed + f_min * windspeed * (altitude_agl/boundary_alt);
+		}
+	else 
+		{var windspeed_current = windspeed;}
+
+	}
+
+
+var windspeed_ground = (1.0-f_min) * windspeed;
+
+
+# set the wind hash before gusts, it represents mean wind
+
+wind.current = [winddir,windspeed_current];
+
+
+
+# determine gusts and turbulence in the bounday layer
+
+var gust_frequency = getprop(lw~"tmp/gust-frequency-hz");
+
+
+
+
+if (gust_frequency > 0.0)
+	{
+	var gust_relative_strength = getprop(lw~"tmp/gust-relative-strength");
+	var gust_angvar = getprop(lw~"tmp/gust-angular-variation-deg");
+	
+	var winddir_last = getprop(lwi~"wind-from-heading-deg");
+
+	var alt_scaling_factor = 1.2 * windspeed / 10.0;
+	if (alt_scaling_factor < 1.0) {alt_scaling_factor = 1.0;}
+
+	# expected mean number of gusts in time interval (should be < 1)
+	var p_gust = gust_frequency * interpolation_loop_time;
+
+	winddir_change = 0.0;
+
+	if (rand() < p_gust) # we change the offsets for windspeed and direction
+		{
+		var alt_fact = 1.0 - altitude_agl/(boundary_alt * alt_scaling_factor);
+		if (alt_fact < 0.0) {alt_fact = 0.0};
+		windspeed_multiplier =  (1.0 + ((rand()) * gust_relative_strength * alt_fact));
+		winddir_change = alt_fact * (1.0 - 2.0 * rand()) * gust_angvar;
+		winddir_change = winddir_change * 0.2; # Markov chain parameter, max. change per frame is 1/5 
+		
+		# if the Markov chain reaches the boundary, reflect
+
+		#print("Winddir: ", winddir, " winddir_last: ", winddir_last, " winddir_change: ", winddir_change);
+		if (weather_tile_management.relangle(winddir_last + winddir_change, winddir) > gust_angvar)
+			{winddir_change = -winddir_change;}
+		
+		}
+	windspeed_current = windspeed_current *  windspeed_multiplier;
+	winddir = winddir_last + winddir_change;
+	}
+
+	
+
+
+
+compat_layer.setWindSmoothly(winddir, windspeed_current);
+
+setprop(lwi~"wind-from-heading-deg", winddir);
+setprop(lwi~"wind-speed-kt",windspeed_current);
+
+setprop(lw~"current/wind-from-heading-deg",winddir);
+setprop(lw~"current/wind-speed-kt",windspeed_current);
+
+
+# hack to get access to the water shader
+
+setprop("/environment/config/boundary/entry[0]/wind-from-heading-deg",winddir);
+setprop("/environment/config/boundary/entry[0]/wind-speed-kt",windspeed_ground);
+
+# end hack
+
+
+if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, interpolation_loop_time);}
+
+}
+
+###################################
+# thermal lift loop startup
+###################################
+
+var thermal_lift_start = func (ev) {
+
+
+# if another lift loop is already running, do nothing
+if (getprop(lw~"lift-loop-flag") == 1) {return;} 
+
+# copy the properties from effect volume to the lift object
+
+l = thermalLift.new(ev.lat, ev.lon, ev.radius, ev.height, ev.cn, ev.sh, ev.max_lift, ev.f_lift_radius);
+
+l.index = ev.index;
+
+if (dynamics_flag == 1)
+	{
+	l.timestamp = weather_dynamics.time_lw;
+	if (dynamical_convection_flag == 1)
+		{
+		l.flt = ev.flt;
+		l.evolution_timestamp = ev.evolution_timestamp;
+		}
+	}
+
+
+
+thermal = l;
+
+if (debug_output_flag == 1)
+	{
+	print("Entering thermal lift...");
+	print("strength: ", thermal.max_lift, " radius: ", thermal.radius);
+	if (dynamical_convection_flag ==1)
+		{print("fractional lifetime: ", thermal.flt);}
+
+	}
+
+# and start the lift loop, unless another one is already running
+# so we block overlapping calls
+
+
+setprop(lw~"lift-loop-flag",1); 
+settimer(thermal_lift_loop,0);
+
+}
+
+###################################
+# thermal lift loop
+###################################
+
+var thermal_lift_loop = func {
+
+if (local_weather_running_flag == 0) {return;}
+
+var apos = geo.aircraft_position();
+
+var tlat = thermal.lat;
+var tlon = thermal.lon;
+
+var tpos = geo.Coord.new();
+tpos.set_latlon(tlat,tlon,0.0);
+
+var d = apos.distance_to(tpos);
+var alt = getprop("position/altitude-ft");
+
+if (dynamical_convection_flag == 1)
+	{var flt = thermal.flt;}
+else
+	{var flt = 0.5;}
+
+var lift = calcLift(d, alt, thermal.radius, thermal.height, thermal.cn, thermal.sh, thermal.max_lift, thermal.f_lift_radius, flt);
+
+if (getprop(lw~"wave-loop-flag") ==1) 
+	{
+	lift = lift + getprop(lw~"current/wave-lift");
+	}
+
+# compute a reduction in visibility when entering the cloudbase
+
+var vis = getprop(lw~"interpolation/visibility-m");
+
+if (alt > 0.9 * thermal.height)
+	{
+	var visibility_reduction = math.pow((alt - 0.9 * thermal.height)/(0.2 * thermal.height),0.1);
+	visibility_reduction = visibility_reduction * (1.0 - math.pow(d/(0.8*thermal.radius),14));
+
+	if (visibility_reduction > 1.0) {visibility_reduction = 1.0;} # this shouldn't ever happen
+	if (visibility_reduction < 0.0) {visibility_reduction = 0.0;} 
+	vis = vis * (1.0 - 0.98 * visibility_reduction);
+
+	}
+
+setprop(lw~"current/visibility-m",vis);
+compat_layer.setVisibility(vis);
+
+
+
+
+setprop(lw~"current/thermal-lift",lift);
+compat_layer.setLift(lift);
+
+# if dynamics is on, move the thermal and occasionally compute altitude and age
+
+if (dynamics_flag == 1)
+	{
+	thermal.move();
+	
+	if ((rand() < 0.01) and (presampling_flag == 1)) # check every 100 frames
+		{
+		if (dynamical_convection_flag == 1) 
+			{
+			thermal.correct_altitude_and_age();
+			if (thermal.flt > 1.1)
+				{thermal_lift_stop();}
+			}
+		else	
+			{	
+			thermal.correct_altitude();
+			}
+		}	
+	}
+
+
+if (getprop(lw~"lift-loop-flag") ==1) {settimer(thermal_lift_loop, 0);}
+}
+
+
+
+
+
+###################################
+# thermal lift loop stop
+###################################
+
+var thermal_lift_stop = func {
+
+setprop(lw~"lift-loop-flag",0);
+setprop(lw~"current/thermal-lift",0.0);
+compat_layer.setLift(0.0);
+
+if (debug_output_flag == 1)
+	{
+	print("Leaving thermal lift...");
+	}
+
+}
+
+
+###################################
+# wave lift loop startup
+###################################
+
+var wave_lift_start = func (ev) {
+
+# copy the properties from effect volume to the wave object
+
+
+w = waveLift.new (ev.lat, ev.lon, ev.r1, ev.r2, ev.phi, ev.height, ev.max_lift);
+w.index = ev.index;
+wave = w;
+
+# and start the lift loop, unless another one is already running
+# so we block overlapping calls
+
+if (getprop(lw~"wave-loop-flag") == 0) 
+{setprop(lw~"wave-loop-flag",1); settimer(wave_lift_loop,0);}
+
+}
+
+###################################
+# wave lift loop
+###################################
+
+var wave_lift_loop = func {
+
+if (local_weather_running_flag == 0) {return;}
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+var alt = getprop("position/altitude-ft");
+
+
+var phi = wave.phi * math.pi/180.0;
+
+var xx = (lon - wave.lon) * lon_to_m;
+var yy = (lat - wave.lat) * lat_to_m;
+
+var x = xx * math.cos(phi) - yy * math.sin(phi);
+var y = yy * math.cos(phi) + xx * math.sin(phi); 
+
+var lift = calcWaveLift(x,y,alt);
+
+# check if we are in a thermal, if so set wave lift and let the thermal lift loop add that
+
+if (getprop(lw~"lift-loop-flag") ==1)
+	{
+	setprop(lw~"current/wave-lift",lift);
+	}
+else
+	{
+	setprop(lw~"current/thermal-lift",lift);
+	}
+
+if (getprop(lw~"wave-loop-flag") ==1) {settimer(wave_lift_loop, 0);}
+}
+
+
+
+
+###################################
+# wave lift loop stop
+###################################
+
+var wave_lift_stop = func {
+
+setprop(lw~"wave-loop-flag",0);
+setprop(lw~"current/thermal-lift",0.0);
+}
+
+
+
+####################################
+# action taken when in effect volume
+####################################
+
+var effect_volume_start = func (ev) {
+
+var cNode = props.globals.getNode(lw~"current");
+
+
+if (ev.vis_flag ==1)
+	{
+	# first store the current setting in case we need to restore on leaving 
+	
+	var vis = ev.vis;
+	ev.vis_r = cNode.getNode("visibility-m").getValue();
+
+	# then set the new value in current and execute change
+	cNode.getNode("visibility-m").setValue(vis);
+	#compat_layer.setVisibility(vis);
+	compat_layer.setVisibilitySmoothly(vis);
+
+	# then count the number of active volumes on entry (we need that to determine
+	# what to do on exit)
+	ev.n_entry_vis = getprop(lw~"effect-volumes/number-active-vis");
+
+	# and add to the counter
+	setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")+1);
+	}
+
+if (ev.rain_flag == 1)
+	{
+	var rain = ev.rain;
+	ev.rain_r = cNode.getNode("rain-norm").getValue();
+	cNode.getNode("rain-norm").setValue(rain);
+	compat_layer.setRain(rain);
+	ev.n_entry_rain = getprop(lw~"effect-volumes/number-active-rain");
+	setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")+1);
+	}
+if (ev.snow_flag == 1)
+	{
+	var snow = ev.snow;
+	ev.snow_r = cNode.getNode("snow-norm").getValue();
+	cNode.getNode("snow-norm").setValue(snow);
+	compat_layer.setSnow(snow);
+	ev.n_entry_snow = getprop(lw~"effect-volumes/number-active-snow");
+	setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")+1);
+	}
+if (ev.turb_flag == 1)
+	{
+	var turbulence = ev.turb;
+	ev.turb_r = cNode.getNode("turbulence").getValue();
+	cNode.getNode("turbulence").setValue(turbulence);
+	compat_layer.setTurbulence(turbulence);
+	ev.n_entry_turb = getprop(lw~"effect-volumes/number-active-turb");
+	setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")+1);
+	}
+if (ev.sat_flag == 1)
+	{
+	var saturation = ev.sat;
+	ev.sat_r = getprop("/rendering/scene/saturation");
+	compat_layer.setLightSmoothly(saturation);
+	ev.n_entry_sat = getprop(lw~"effect-volumes/number-active-sat");
+	setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")+1);
+	}
+
+if (ev.lift_flag == 1)
+	{
+	var lift = ev.lift;
+	ev.lift_r = cNode.getNode("thermal-lift").getValue();
+	cNode.getNode("thermal-lift").setValue(lift);
+	compat_layer.setLift(lift);
+	ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");	
+	setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
+	}
+else if (ev.lift_flag == 2)
+	{
+	ev.lift_r = cNode.getNode("thermal-lift").getValue();
+	ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");	
+	setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
+	thermal_lift_start(ev);
+	}
+else if (ev.lift_flag == 3)
+	{
+	ev.lift_r = cNode.getNode("thermal-lift").getValue();
+	ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");	
+	setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
+	wave_lift_start(ev);
+	}
+
+}
+
+
+
+var effect_volume_stop = func (ev) {
+
+var cNode = props.globals.getNode(lw~"current");
+
+
+if (ev.vis_flag == 1)
+	{
+
+	var n_active = getprop(lw~"effect-volumes/number-active-vis");
+
+	
+	var n_entry = ev.n_entry_vis;	
+
+	# if no other nodes affecting property are active, restore to outside
+	# else restore settings as they have been when entering the volume when the number
+	# of active volumes is the same as on entry (i.e. volumes are nested), otherwise
+	# leave property at current because new definitions are already active and should not
+	# be cancelled
+	
+	if (n_active ==1){var vis = props.globals.getNode(lw~"interpolation/visibility-m").getValue();}
+	else if ((n_active -1) == n_entry) #{var vis = ev.getNode("restore/visibility-m").getValue();}
+		{var vis = ev.vis_r;}
+	else {var vis = cNode.getNode("visibility-m").getValue();}
+	cNode.getNode("visibility-m").setValue(vis);
+	#compat_layer.setVisibility(vis);
+	compat_layer.setVisibilitySmoothly(vis);
+	
+	# and subtract from the counter
+	setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")-1);
+	}
+if (ev.rain_flag == 1)
+	{
+	var n_active = getprop(lw~"effect-volumes/number-active-rain");
+	var n_entry = ev.n_entry_rain;
+
+	if (n_active ==1){var rain = props.globals.getNode(lw~"interpolation/rain-norm").getValue();}
+	else if ((n_active -1) == n_entry)# {var rain = ev.getNode("restore/rain-norm").getValue();}
+		 {var rain = ev.rain_r;}
+	else {var rain = cNode.getNode("rain-norm").getValue();}
+	cNode.getNode("rain-norm").setValue(rain);
+	compat_layer.setRain(rain);
+	setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")-1);
+	}
+
+if (ev.snow_flag == 1)
+	{
+	var n_active = getprop(lw~"effect-volumes/number-active-snow");
+	var n_entry = ev.n_entry_snow;	
+
+	if (n_active ==1){var snow = props.globals.getNode(lw~"interpolation/snow-norm").getValue();}
+	else if ((n_active -1) == n_entry)
+		{var snow = ev.snow_r;}
+	else {var snow = cNode.getNode("snow-norm").getValue();}
+	cNode.getNode("snow-norm").setValue(snow);
+	compat_layer.setSnow(snow);
+	setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")-1);
+	}
+
+if (ev.turb_flag == 1)
+	{
+	var n_active = getprop(lw~"effect-volumes/number-active-turb");
+	var n_entry = ev.n_entry_turb;
+	if (n_active ==1){var turbulence = props.globals.getNode(lw~"interpolation/turbulence").getValue();}
+	else if ((n_active -1) == n_entry) 
+		 {var turbulence = ev.turb_r;}
+	else {var turbulence = cNode.getNode("turbulence").getValue();}
+	cNode.getNode("turbulence").setValue(turbulence);
+	compat_layer.setTurbulence(turbulence);
+	setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")-1);
+	}
+
+if (ev.sat_flag == 1)
+	{
+	var n_active = getprop(lw~"effect-volumes/number-active-sat");
+	var n_entry = ev.n_entry_sat;
+	if (n_active ==1){var saturation = 1.0;}
+	else if ((n_active -1) == n_entry) 
+		 {var saturation = ev.sat_r;}
+	else {var saturation = getprop("/rendering/scene/saturation");}
+	compat_layer.setLightSmoothly(saturation);
+	setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")-1);
+	}
+
+if (ev.lift_flag == 1)
+	{
+	var n_active = getprop(lw~"effect-volumes/number-active-lift");
+	var n_entry = ev.n_entry_lift;
+	if (n_active ==1){var lift = props.globals.getNode(lw~"interpolation/thermal-lift").getValue();}
+	else if ((n_active -1) == n_entry)
+		 {var lift = ev.lift_r;}
+	else {var lift = cNode.getNode("thermal-lift").getValue();}
+	cNode.getNode("thermal-lift").setValue(lift);
+	compat_layer.setLift(lift);
+	setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
+	}
+else if (ev.lift_flag == 2)
+	{
+	thermal_lift_stop();
+	setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
+	}
+else if (ev.lift_flag == 3)
+	{
+	wave_lift_stop();
+	setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
+	}
+
+}
+
+
+
+#########################################
+# compute thermal lift in detailed model 
+#########################################
+
+var ts_factor = func (t, alt, height) {
+
+var t1 = 0.1; # fractional time at which lift is fully developed 
+var t2 = 0.9; # fractional time at which lift starts to decay
+var t3 = 1.0; # fractional time at which lift is gone
+
+# no time dependence modelled yet
+# return 1.0; 
+
+
+
+var t_a = t - (alt/height) * t1 - t1;
+
+if (t_a<0) {return 0.0;}
+else if (t_a<t1) {return 0.5 + 0.5 * math.cos((1.0-t_a/t1)* math.pi);}
+else if (t_a < t2) {return 1.0;}
+else {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);}
+}
+
+var tl_factor = func (t, alt, height) {
+
+var t1 = 0.1; # fractional time at which lift is fully developed 
+var t2 = 0.9; # fractional time at which lift starts to decay
+var t3 = 1.0; # fractional time at which lift is gone
+
+# no time dependence modelled yet
+# return 1.0; 
+
+var t_a = t - (alt/height) * t1;
+
+if (t_a<0) {return 0.0;}
+else if (t_a<t1) {return 0.5 + 0.5 * math.cos((1.0-t_a/t1)* math.pi);}
+else if (t_a < t2) {return 1.0;}
+else  {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);}      
+}
+
+
+var calcLift_max = func (alt, max_lift, height) {
+    
+alt_agl = getprop("/position/altitude-agl-ft");
+
+# no lift below ground
+if (alt_agl < 0.0) {return 0.0;}   
+    
+# lift ramps up to full within 200 m
+else if (alt_agl < 200.0*m_to_ft) 
+	{return max_lift * 0.5 * (1.0 + math.cos((1.0-alt_agl/(200.0*m_to_ft))*math.pi));}
+
+# constant max. lift in main body
+else if ((alt_agl > 200.0*m_to_ft) and (alt < height))
+	{return max_lift;}
+
+# decreasing lift from cloudbase to 10% above base
+else if ((alt > height ) and (alt < height*1.1)) 
+	{return max_lift * 0.5 * (1.0 - math.cos((1.0-10.0*(alt-height)/height)*math.pi));}
+    
+# no lift available above
+else {return 0.0;}
+}
+
+
+
+var calcLift = func (d, alt, R, height, cn, sh, max_lift, f_lift_radius, t) {
+
+# radius of slice at given altitude
+var r_total = (cn + alt/height*(1.0-cn)) * (R - R * (1.0- sh ) * (1.0 - ((2.0*alt/height)-1.0)*((2.0*alt/height)-1.0)));
+
+
+# print("r_total: ", r_total, "d: ",d);
+# print("alt: ", alt, "height: ",height);
+
+# no lift if we're outside the radius or above the thermal
+if ((d > r_total) or (alt > 1.1*height)) { return 0.0; } 
+
+# fraction of radius providing lift
+var r_lift = f_lift_radius * r_total;
+
+# print("r_lift: ", r_lift);
+
+# if we are in the sink portion, get the max. sink for this time and altitude and adjust for actual position
+if ((d < r_total ) and (d > r_lift)) 
+	{
+	var s_max = 0.5 * calcLift_max(alt, max_lift, height) * ts_factor(t, alt, height);
+	# print("s_max: ", s_max);
+	return s_max * math.sin(math.pi * (1.0 + (d-r_lift) * (1.0/(r_total - r_lift))));
+	}
+# else we are in the lift portion, get the max. lift for this time and altitude and adjust for actual position
+else
+	{  
+    	var l_max = calcLift_max(alt, max_lift, height) * tl_factor(t, alt, height);
+	# print("l_max: ", l_max);
+	return l_max * math.cos(math.pi * (d/(2.0 * r_lift)));
+	}
+}
+
+#########################################
+# compute wave lift in detailed model 
+#########################################
+
+var calcWaveLift = func (x,y, alt) {
+
+var lift = wave.max_lift * math.cos((y/wave.y) * 1.5 * math.pi);
+
+if (abs(x)/wave.x > 0.9)
+	{
+	lift = lift * (abs(x) - 0.9 * wave.x)/(0.1 * wave.x); 
+	}
+
+
+
+lift = lift * 2.71828 * math.exp(-alt/wave.height) * alt/wave.height;
+
+var alt_agl = getprop("/position/altitude-agl-ft");
+
+if (alt_agl < 1000.0)
+	{
+	lift = lift * (alt_agl/1000.0) * (alt_agl/1000.0);
+	}
+
+return lift;
+}
+	
+
+
+
+
+
+###########################################################
+# place a single cloud into a vector to be processed
+# separately
+###########################################################
+
+var create_cloud_vec = func(path, lat, long, alt, heading) {
+
+if (path == "new") # we have to switch to new cloud generating routines
+	{
+	local_weather.cloudAssembly.lat = lat;
+	local_weather.cloudAssembly.lon = long;
+	local_weather.cloudAssembly.alt = alt;	
+
+	#print(lat," ",long, " ", alt);
+
+	if (dynamics_flag == 1)
+		{
+		local_weather.cloudAssembly.mean_alt = cloud_mean_altitude;
+		local_weather.cloudAssembly.flt = cloud_fractional_lifetime;
+		local_weather.cloudAssembly.evolution_timestamp = cloud_evolution_timestamp;
+		local_weather.cloudAssembly.rel_alt = cloudAssembly.alt - cloud_mean_altitude;
+		}
+	#compat_layer.create_cloud_new(local_weather.cloudAssembly);	
+
+	append(cloudAssemblyArray,cloudAssembly);
+
+	return;
+	}
+
+append(clouds_path,path);
+append(clouds_lat,lat);
+append(clouds_lon,long);
+append(clouds_alt,alt);
+append(clouds_orientation,heading);
+
+# globals (needed for Cumulus clouds) should be set if needed by the main cloud generating call
+
+if (dynamics_flag ==1)
+	{
+	append(clouds_mean_alt, cloud_mean_altitude);
+	append(clouds_flt, cloud_fractional_lifetime);
+	append(clouds_evolution_timestamp,cloud_evolution_timestamp);
+	}
+
+}
+###########################################################
+# clear all clouds and effects
+###########################################################
+
+var clear_all = func {
+
+# clear the clouds and models
+
+var cloudNode = props.globals.getNode(lw~"clouds", 1);
+cloudNode.removeChildren("tile");
+
+var modelNode = props.globals.getNode("models", 1).getChildren("model");
+
+foreach (var m; modelNode)
+	{
+	var l = m.getNode("tile-index",1).getValue();
+	if (l != nil)
+		{
+		m.remove();
+		}
+	}
+
+
+# remove the hard-coded clouds
+
+foreach (c; weather_tile_management.cloudArray)
+	{
+	c.remove();
+	}
+setsize(weather_tile_management.cloudArray,0);
+
+# reset pressure continuity
+
+weather_tiles.last_pressure = 0.0;
+
+# stop all loops
+
+setprop(lw~"effect-loop-flag",0);
+setprop(lw~"interpolation-loop-flag",0);
+setprop(lw~"tile-loop-flag",0);
+setprop(lw~"lift-loop-flag",0);
+setprop(lw~"wave-loop-flag",0);
+setprop(lw~"dynamics-loop-flag",0);
+setprop(lw~"timing-loop-flag",0);
+setprop(lw~"buffer-loop-flag",0);
+setprop(lw~"housekeeping-loop-flag",0);
+setprop(lw~"convective-loop-flag",0);
+
+weather_dynamics.convective_loop_kill_flag = 1; # long-running loop needs a different scheme to end
+
+# also remove rain, snow, haze and light effects
+
+compat_layer.setRain(0.0);
+compat_layer.setSnow(0.0);
+compat_layer.setLight(1.0);
+
+
+# set placement indices to zero
+
+setprop(lw~"clouds/placement-index",0);
+setprop(lw~"clouds/model-placement-index",0);
+setprop(lw~"effect-volumes/effect-placement-index",0);
+setprop(lw~"effect-volumes/number",0);
+setprop(lw~"tiles/tile-counter",0);
+
+
+# remove any quadtrees and arrays
+
+settimer ( func { setsize(weather_dynamics.cloudQuadtrees,0);},0.1); # to avoid error generation in this frame
+setsize(effectVolumeArray,0);
+n_effectVolumeArray = 0;
+
+# if we have used METAR, we may no longer want to do so
+
+metar_flag = 0;
+
+
+settimer ( func {
+	setsize(weather_tile_management.modelArrays,0);
+	setsize(weather_dynamics.tile_wind_direction,0);
+	setsize(weather_dynamics.tile_wind_speed,0);
+	setsize(weather_tile_management.cloudBufferArray,0);
+	setsize(weather_tile_management.cloudSceneryArray,0);
+	setsize(alt_20_array,0);
+	setsize(alt_50_array,0);
+	setsize(alt_min_array,0);
+	setsize(alt_mean_array,0);
+	setsize(weather_dynamics.tile_convective_altitude,0);
+	setsize(weather_dynamics.tile_convective_strength,0);
+	setsize(weatherStationArray,0);
+	setsize(windIpointArray,0);
+	setsize(atmosphereIpointArray,0);
+	setprop(lw~"clouds/buffer-count",0);
+	setprop(lw~"clouds/cloud-scenery-count",0);
+	weather_tile_management.n_cloudSceneryArray = 0;
+	compat_layer.setScattering(0.8);
+	compat_layer.setOvercast(0.0);
+	setprop(lwi~"ipoint-number",0);
+	setprop(lwi~"atmosphere-ipoint-number", 0);
+	},0.1);
+
+setprop(lw~"tmp/presampling-status", "idle");
+
+# reset the random store
+
+weather_tiles.rnd_store = rand();
+
+# default 3d clouds layer wrapping back on, just in case
+
+setprop("/sim/rendering/clouds3d-wrap",1);
+
+# indicate that we are no longer running
+
+
+local_weather_running_flag = 0;
+
+}
+
+
+
+###########################################################
+# detailed Cumulus clouds created from multiple cloudlets
+###########################################################
+
+var create_detailed_cumulus_cloud = func (lat, lon, alt, size) {
+
+
+var edge_bias = convective_texture_mix;
+
+size = size + convective_size_bias;
+
+
+if (hardcoded_clouds_flag == 0)
+	{
+	if (size > 2.0)
+		{
+		if (rand() > (size - 2.0))
+			{create_cumulonimbus_cloud(lat, lon, alt, size); }
+		else
+			{create_cumulonimbus_cloud_rain(lat, lon, alt, size, 0.1 + 0.2* rand());}
+		return;
+		}
+
+	else if (size>1.5)
+		{
+		var type = "Congestus";
+		var btype = "Congestus bottom";
+		var height = 400;
+		var n = 8;
+		var n_b = 4;
+		var x = 1000.0;
+		var y = 300.0;
+		var edge = 0.3;
+		}
+
+	else if (size>1.1)
+		{
+		var type = "Cumulus (cloudlet)";
+		var btype = "Cumulus bottom";
+		var height = 200;
+		var n = 8;
+		var n_b = 1;
+		var x = 400.0;
+		var y = 200.0;
+		var edge = 0.3;
+		}
+	else if (size>0.8)
+		{
+		var type = "Cumulus (cloudlet)";
+		var btype = "Cumulus bottom";
+		var height = 150;
+		var n = 6;
+		var x = 300.0;
+		var y = 200.0;
+		var edge = 0.3;
+		}
+	else if (size>0.4)
+		{
+		var type = "Cumulus (cloudlet)";
+		var btype = "Cumulus bottom";
+		var height = 100;
+		var n = 4;
+		var x = 200.0;
+		var y = 200.0;
+		var edge = 1.0;
+		}
+	else 
+		{
+		var type = "Cumulus (whisp)";
+		var btype = "Cumulus bottom";
+		var height = 100;
+		var n = 1;
+		var x = 100.0;
+		var y = 100.0;
+		var edge = 1.0;
+		}
+
+	var alpha = rand() * 180.0;
+	edge = edge + edge_bias;
+	create_streak(type,lat,lon, alt+ 0.5* (height +cloud_vertical_size_map["Cumulus"] * ft_to_m), height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+
+	# for large clouds, add a bottom
+
+	if ((size > 1.1) and (edge < 0.4))
+		{
+
+		create_streak(btype,lat,lon, alt, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
+		}
+
+
+
+	}
+else
+	{
+	if (size > 2.0)
+		{
+		if (rand() > (size - 2.0))
+			{create_cumulonimbus_cloud(lat, lon, alt, size); }
+		else
+			{create_cumulonimbus_cloud_rain(lat, lon, alt, size, 0.1 + 0.2* rand());}
+		return;
+		}
+
+	else if (size>1.5)
+		{
+		var type = "Congestus";
+
+		var height = 400;
+		var n = 3;
+		var x = 700.0;
+		var y = 200.0;
+		var edge = 0.2;
+		
+		var alpha = rand() * 180.0;
+		edge = edge + edge_bias;		
+
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+
+		var type = "Cu (volume)";
+		var height = 400;
+		var n = 10;
+		var x = 1400.0;
+		var y = 400.0;
+		var edge = 0.2;
+		
+		edge = edge + edge_bias;		
+
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+
+		var btype = "Congestus bottom";
+		var n_b = 6;
+
+		create_streak(btype,lat,lon, alt -offset_map["Congestus"] -200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
+
+		}
+	else if (size>1.1)
+		{
+		var type = "Cumulus (cloudlet)";
+		var btype = "Cumulus bottom";
+		var height = 200;
+		var n = 6;
+		var n_b = 2;
+		var x = 900.0;
+		var y = 200.0;
+		var edge = 0.2;
+
+		var alpha = rand() * 180.0;
+		edge = edge + edge_bias;
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+
+		create_streak(btype,lat,lon, alt -offset_map["Cumulus"] - 200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
+
+		}
+	else if (size>0.8)
+		{
+		var type = "Cumulus (cloudlet)";
+		var height = 150;
+		var n = 4;
+		var x = 300.0;
+		var y = 300.0;
+		var edge = 0.3;
+
+		var alpha = rand() * 180.0;
+		edge = edge + edge_bias;
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+
+		n = 2;
+		x = 700.0;
+		y = 200.0;
+		edge = 1.0;
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		}
+
+	else if (size>0.4)
+		{
+		var type = "Cumulus (cloudlet)";
+		var height = 100;
+		var n = 2;
+		var x = 600.0;
+		var y = 100.0;
+		var edge = 1.0;
+
+		var alpha = rand() * 180.0;
+		edge = edge + edge_bias;
+		create_streak(type,lat,lon, alt+ 0.3* (height)-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		}
+	else 
+		{
+		var type = "Cumulus (whisp)";
+		var height = 100;
+		var n = 1;
+		var x = 100.0;
+		var y = 100.0;
+		var edge = 1.0;
+
+		var alpha = rand() * 180.0;
+		edge = edge + edge_bias;
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		}
+
+	}
+
+
+
+} 
+
+###########################################################
+# detailed small Cumulonimbus clouds created from multiple cloudlets
+###########################################################
+
+var create_cumulonimbus_cloud = func(lat, lon, alt, size) {
+
+if (hardcoded_clouds_flag == 1)
+	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);}
+else
+	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);}
+
+
+
+}
+
+###########################################################
+# detailed small Cumulonimbus and rain created from multiple cloudlets
+###########################################################
+
+var create_cumulonimbus_cloud_rain = func(lat, lon, alt, size, rain) {
+
+if (hardcoded_clouds_flag == 1)
+	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);}
+else
+	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);}
+
+
+# place a rain texture
+
+var path = "Models/Weather/rain2.xml";
+if (thread_flag == 1)
+				{create_cloud_vec(path, lat, lon, alt, 0.0);}
+			else 
+				{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
+
+		
+
+# and some rain underneath
+
+create_effect_volume(1, lat, lon, 2000.0, 2000.0, 0.0, 0.0, alt+1000.0, 8000.0 + 8000.0 * rand(), rain, -1, -1, -1 ,1,-1 );
+
+
+}
+
+
+###########################################################
+# wrappers for convective cloud system to distribute
+# call across several frames if needed
+###########################################################
+
+var create_cumosys = func (blat, blon, balt, nc, size) {
+
+# realistic Cumulus has somewhat larger models, so compensate to get the same coverage
+if (detailed_clouds_flag == 1) 
+	{nc = int(0.7 * nc);}
+
+nc = int(nc / cumulus_efficiency_factor);
+
+if (thread_flag ==  1)
+	{setprop(lw~"tmp/convective-status", "computing");
+	cumulus_loop(blat, blon, balt, nc, size);}
+
+else
+	{create_cumulus(blat, blon, balt, nc, size);
+	if (debug_output_flag == 1) 
+		{print("Convective system done!");}
+	}
+}
+
+
+
+var cumulus_loop = func (blat, blon, balt, nc, size) {
+
+if (local_weather_running_flag == 0) {return;}
+
+if (local_weather.features.fast_geodinfo == 0)
+	{var n = int(25/cumulus_efficiency_factor);}
+else
+	{var n = int(200/cumulus_efficiency_factor);}
+
+if (nc < 0) 
+	{
+	if (debug_output_flag == 1) 
+		{print("Convective system done!");}
+	setprop(lw~"tmp/convective-status", "idle");
+	assemble_effect_array();
+	convective_size_bias = 0.0;
+	return;
+	}
+
+create_cumulus(blat, blon, balt, n, size);
+
+settimer( func {cumulus_loop(blat, blon, balt, nc-n, size) },0);
+}
+
+###########################################################
+# place a convective cloud system
+###########################################################
+
+var create_cumulus = func (blat, blon, balt, nc, size) {
+
+
+
+var path = "Models/Weather/blank.ac";
+var i = 0;
+var p = 0.0;
+var rn = 0.0;
+var place_lift_flag = 0;
+var strength = 0.0;
+var detail_flag = detailed_clouds_flag;
+
+var alpha = getprop(lw~"tmp/tile-orientation-deg") * math.pi/180.0; # the tile orientation
+
+if (detailed_terrain_interaction_flag == 1)
+	{
+	var tile_index = getprop(lw~"tiles/tile-counter");
+	#var alt_min = alt_min_array[tile_index-1];
+	#var alt_mean = alt_mean_array[tile_index -1];
+	#var alt_median = alt_50_array[tile_index -1];
+	#var alt_base = alt_20_array[tile-index -1];
+	#var alt_min = getprop(lw~"tmp/tile-alt-min-ft");
+	#var alt_mean = getprop(lw~"tmp/tile-alt-mean-ft");
+	#var alt_median = getprop(lw~"tmp/tile-alt-median-ft");
+	#var alt_base = getprop(lw~"tmp/tile-alt-offset-ft");
+	}
+
+var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime
+
+calc_geo(blat);
+
+# get the local time of the day in seconds
+
+var t = getprop("sim/time/utc/day-seconds");
+t = t + getprop("sim/time/local-offset");
+
+# print("t is now:", t);
+
+# and make a simple sinusoidal model of thermal strength
+
+# daily variation in number of thermals, peaks at noon
+var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad))); 
+
+# daily variation in strength of thermals, peaks around 15:30
+var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9)); 
+
+
+# number of possible thermals equals overall strength times daily variation times geographic variation
+# this is a proxy for solar thermal energy
+
+nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); 
+
+var thermal_conditions = getprop(lw~"config/thermal-properties");
+
+
+while (i < nc) {
+
+	p = 0.0;
+	place_lift_flag = 0;
+	strength = 0.0;
+
+	# pick a trial position inside the tile and rotate by tile orientation angle
+	var x = (2.0 * rand() - 1.0) * size;
+	var y = (2.0 * rand() - 1.0) * size; 
+
+	var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat;
+	var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon;
+
+	# now check ground cover type on chosen spot
+	var info = geodinfo(lat, lon);
+
+	if (info != nil) {
+	var elevation = info[0] * m_to_ft;
+	if (info[1] != nil){
+         var landcover = info[1].names[0];
+	 if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];}
+	 else {print(p, " ", info[1].names[0]);}
+	}}
+	else {
+		print("No terrain loaded!");
+		continue;
+		}
+
+
+	# apply some optional corrections, biases clouds towards higher elevations
+
+	var terrain_altitude_factor = 1.0;
+	var terrain_strength_factor = 1.0;
+
+	if (detailed_terrain_interaction_flag == 1)
+		{
+		
+		terrain_altitude_factor = get_terrain_altitude_factor(tile_index, balt, elevation);
+		terrain_strength_factor = get_terrain_strength_factor(terrain_altitude_factor);
+
+		}
+
+
+	# then decide if the thermal energy at the spot generates an updraft and a cloud
+
+	if (rand() < (p * cumulus_efficiency_factor * terrain_altitude_factor)) # we decide to place a cloud at this spot
+		{
+	
+
+		# check if we have a terrain elevation analysis available and can use a 
+		# detailed placement altitude correction
+
+		if (presampling_flag == 1) 
+			{
+			
+			if (detailed_terrain_interaction_flag == 1)
+				{
+				var grad = get_terrain_gradient(lat, lon, elevation, alpha, 1000.0);
+				}
+			else 
+				{var grad = 0.0;}
+
+
+			var place_alt = get_convective_altitude(balt, elevation, getprop(lw~"tiles/tile-counter"), grad);
+			}
+		else {var place_alt = balt;}
+		
+		# no cloud placement into the ground
+		if (place_alt < elevation) {continue;}
+
+		# if we're in a lee, we may not want to place the cloud
+
+		if (detailed_terrain_interaction_flag == 1)
+				{
+				var p_lee_suppression = get_lee_bias(grad);
+				if (rand() > p_lee_suppression) {continue;} 
+				}
+
+	
+		# now decide on the strength of the thermal at the spot and on cloud size
+
+		var rn = rand();
+		strength = (1.5 * rn + (2.0 * p * terrain_strength_factor)) * t_factor2;  
+		if (strength > 1.0)  
+			{
+			# we place a large cloud, and we generate lift
+			path = select_cloud_model("Cumulus","large"); place_lift_flag = 1;
+			}
+		else {path = select_cloud_model("Cumulus","small");}
+
+		# the terrain effect cannot create Cb development, so we have to curb
+		# the strength if it would not have been Cb otherwise
+
+		if (strength > 2.0)
+			{
+			if (((1.5 * rn + (2.0 * p)) * t_factor2) < 2.0)
+				{strength = 1.7 + rand() * 0.2;}
+			}
+
+
+		cloud_mean_altitude = place_alt;
+		cloud_fractional_lifetime = rand();
+		cloud_evolution_timestamp = weather_dynamics.time_lw;
+
+		if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals
+			{		
+			if (thread_flag == 1)
+				{
+				if (detail_flag == 0){create_cloud_vec(path,lat,lon, place_alt, 0.0);}
+				else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
+				}
+			else
+				{
+				if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, place_alt, 0.0);}
+				else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
+				}
+			}	
+
+		# now see if we need to create a thermal - first check the flag
+		if (generate_thermal_lift_flag == 1) # thermal by constant
+			{
+			# now check if convection is strong
+			if (place_lift_flag == 1)
+				{
+				var lift = 3.0 + 10.0 * (strength -1.0);
+				var radius = 500 + 500 * rand();
+				#print("Lift: ", lift * ft_to_m - 1.0);
+				create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1,-1);
+				} # end if place_lift_flag		
+			} # end if generate-thermal-lift-flag	
+		else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function
+			{
+
+			if (place_lift_flag == 1)
+				{
+				var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions;
+				var radius = (500 + 500 * rand())*thermal_conditions;
+
+				create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt*1.15, -1, -1, -1, lift*0.03, lift, -2,-1);
+				} # end if place_lift_flag
+
+			} # end if generate-thermal-lift-flag
+
+
+		} # end if rand < p
+	i = i + 1;
+	} # end while
+
+}
+
+
+
+
+
+#################################################################
+# respawn convective clouds to compensate for decay
+# the difference being that new clouds get zero fractional 
+# lifetime and are placed based on terrain with a different weight
+##################################################################
+
+var recreate_cumulus = func (blat, blon, balt, alpha, nc, size, tile_index) {
+
+var path = "Models/Weather/blank.ac";
+var i = 0;
+var p = 0.0;
+var rn = 0.0;
+var place_lift_flag = 0;
+var strength = 0.0;
+var detail_flag = detailed_clouds_flag;
+
+alpha = alpha * math.pi/180.0; # the tile orientation
+
+var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime
+
+# current aircraft position
+
+var alat = getprop("position/latitude-deg");
+var alon = getprop("position/longitude-deg");
+
+# get the local time of the day in seconds
+
+var t = getprop("sim/time/utc/day-seconds");
+t = t + getprop("sim/time/local-offset");
+
+
+# and make a simple sinusoidal model of thermal strength
+
+# daily variation in number of thermals, peaks at noon
+var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad))); 
+
+# daily variation in strength of thermals, peaks around 15:30
+var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9)); 
+
+
+# number of possible thermals equals overall strength times daily variation times geographic variation
+# this is a proxy for solar thermal energy
+
+nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); 
+
+var thermal_conditions = getprop(lw~"config/thermal-properties");
+
+
+while (i < nc) {
+
+	p = 0.0;
+	place_lift_flag = 0;
+	strength = 0.0;
+
+	# pick a trial position inside the tile and rotate by tile orientation angle
+	var x = (2.0 * rand() - 1.0) * size;
+	var y = (2.0 * rand() - 1.0) * size; 
+
+	var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat;
+	var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon;
+
+	# check if the cloud would be spawned in visual range, if not don't bother
+	var d_sq = calc_d_sq(alat, alon, lat, lon);
+
+	if (math.sqrt(d_sq) > weather_tile_management.cloud_view_distance)
+		{i = i+1; continue;}
+
+	# now check ground cover type on chosen spot
+	var info = geodinfo(lat, lon);
+
+	if (info != nil) {
+	var elevation = info[0] * m_to_ft;
+	if (info[1] != nil){
+         var landcover = info[1].names[0];
+	 if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];}
+	 else {print(p, " ", info[1].names[0]);}
+	}}
+	else {continue;}
+
+
+	# apply some optional corrections, biases clouds towards higher elevations
+
+	var terrain_altitude_factor = 1.0;
+	var terrain_strength_factor = 1.0;
+
+	if (detailed_terrain_interaction_flag == 1)
+		{
+		terrain_altitude_factor = get_terrain_altitude_factor(tile_index, balt, elevation);
+		terrain_strength_factor = get_terrain_strength_factor(terrain_altitude_factor);
+		}
+
+
+
+
+	# check if to place a cloud with weight sqrt(p), the lifetime gets another sqrt(p) factor
+	
+	if (rand() > math.sqrt(p * cumulus_efficiency_factor * terrain_altitude_factor))
+		{i=i+1; continue;}
+
+
+	# then calculate the strength of the updraft
+		
+	strength = (1.5 * rand() + (2.0 * p * terrain_strength_factor)) * t_factor2; # the strength of thermal activity at the spot
+	if (strength > 1.0)  
+		{
+		path = select_cloud_model("Cumulus","large"); place_lift_flag = 1;
+		}
+	else {path = select_cloud_model("Cumulus","small");}
+
+	if (presampling_flag == 1) 
+		{
+		var place_alt = get_convective_altitude(balt, elevation, tile_index,0.0);
+		}
+	else {var place_alt = balt;}
+
+
+	# no cloud placement into the ground
+	if (place_alt < elevation) {continue;}
+
+	# if we're in a lee, we may not want to place the cloud
+
+	if (detailed_terrain_interaction_flag == 1)
+			{
+			var p_lee_suppression = get_lee_bias(grad);
+				if (rand() > math.sqrt(p_lee_suppression)) {continue;} 
+			}
+		
+	cloud_mean_altitude = place_alt;
+	cloud_fractional_lifetime = 0.0;
+	cloud_evolution_timestamp = weather_dynamics.time_lw;
+
+	compat_layer.cloud_mean_altitude = place_alt;
+	compat_layer.cloud_flt = cloud_fractional_lifetime;
+	compat_layer.cloud_evolution_timestamp = cloud_evolution_timestamp;
+
+	if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals
+		{		
+		if (thread_flag == 1)
+			{
+			thread_flag = 0; # create clouds immediately
+			if (detail_flag == 0){compat_layer.create_cloud(path,lat,lon, place_alt, 0.0);}
+			else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
+			thread_flag = 1; # and restore threading
+			}
+		else
+			{
+			if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, place_alt, 0.0);}
+			else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
+			}
+		}	
+
+	if (generate_thermal_lift_flag == 1) # thermal by constant
+		{
+		if (place_lift_flag == 1)
+			{
+			var lift = 3.0 + 10.0 * (strength -1.0);
+			var radius = 500 + 500 * rand();
+			create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1,-1);
+			} # end if place_lift_flag		
+		} # end if generate-thermal-lift-flag	
+	else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function
+		{
+			if (place_lift_flag == 1)
+			{
+			var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions;
+			var radius = (500 + 500 * rand())*thermal_conditions;
+
+			create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt*1.15, -1, -1, -1, lift*0.03, lift, -2,-1);
+			} # end if place_lift_flag
+
+		} # end if generate-thermal-lift-flag
+
+
+	i = i + 1;
+	} # end while
+
+}
+
+
+
+
+
+###########################################################
+# place a Cumulus layer with excluded regions
+# to avoid placing cumulus underneath a thunderstorm
+###########################################################
+
+var cumulus_exclusion_layer = func (blat, blon, balt, n, size_x, size_y, alpha, s_min, s_max, n_ex, exlat, exlon, exrad) {
+
+
+var strength = 0;
+var flag = 1;
+var phi = alpha * math.pi/180.0;
+
+var detail_flag = detailed_clouds_flag;
+
+if (detail_flag == 1) {var i_max = int(0.25*n);} else {var i_max = int(1.0*n);}
+
+
+
+for (var i =0; i< i_max; i=i+1)
+	{
+	var x = (2.0 * rand() - 1.0) * size_x;
+	var y = (2.0 * rand() - 1.0) * size_y; 
+
+	var lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat;
+	var lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon;
+
+	flag = 1;
+
+	for (var j=0; j<n_ex; j=j+1)
+		{
+		if (calc_d_sq(lat, lon, exlat[j], exlon[j]) < (exrad[j] * exrad[j])) {flag = 0;}
+		}
+	if (flag == 1)
+		{
+		
+		strength = s_min + rand() * (s_max - s_min);		
+	
+		if (strength > 1.0)  {var path = select_cloud_model("Cumulus","large"); }
+		else {var path = select_cloud_model("Cumulus","small");}
+
+		if (thread_flag == 1)
+			{
+			if (detail_flag == 0){create_cloud_vec(path,lat,lon, balt, 0.0);}
+			else {create_detailed_cumulus_cloud(lat, lon, balt, strength);}
+			}
+		else
+			{
+			if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, balt, 0.0);}
+			else {create_detailed_cumulus_cloud(lat, lon, balt, strength);}
+			}
+
+		} # end if flag
+
+	} # end for i
+
+}
+
+
+###########################################################
+# place a barrier cloud system 
+###########################################################
+
+var create_rise_clouds = func (blat, blon, balt, nc, size, winddir, dist) {
+
+var path = "Models/Weather/blank.ac";
+var i = 0;
+var p = 0.0;
+var rn = 0.0;
+var nsample = 10;
+var counter = 0;
+var dir = (winddir + 180.0) * math.pi/180.0;
+var step = dist/nsample;
+
+calc_geo(blat);
+
+while (i < nc) {
+
+	counter = counter + 1;
+	p = 0.0; 
+
+	var x = (2.0 * rand() - 1.0) * size;
+	var y = (2.0 * rand() - 1.0) * size; 
+
+	var lat = blat + y * m_to_lat;
+	var lon = blon + x * m_to_lon;
+
+	var elevation = compat_layer.get_elevation(lat, lon);
+	
+	#print("elevation: ", elevation, "balt: ", balt);
+
+	if ((elevation < balt) and (elevation != -1.0))
+	{
+	for (var j = 0; j<nsample; j=j+1)
+		{
+		d = j * step;
+		x = d * math.sin(dir);
+		y = d * math.cos(dir);
+		var tlat = lat + y * m_to_lat;
+		var tlon = lon + x * m_to_lon;
+		
+		#print("x: ", x, "y: ", y);
+
+		var elevation1 = compat_layer.get_elevation(tlat,tlon);	
+		#print("elevation1: ", elevation1, "balt: ", balt);
+	
+		if (elevation1 > balt)
+			{
+			p = 1.0 - j * (1.0/nsample);
+			#p = 1.0;
+			break;
+			}
+		
+		}
+	}
+	if (counter > 500) {print("Cannot place clouds - exiting..."); i = nc;}
+	if (rand() < p)
+		{
+		path = select_cloud_model("Stratus (structured)","large");
+		compat_layer.create_cloud(path, lat, lon, balt, 0.0);
+		counter = 0;
+		i = i+1;
+		}
+	
+	} # end while
+
+}
+
+
+###########################################################
+# place a cloud streak 
+###########################################################
+
+var create_streak = func (type, blat, blong, balt, alt_var, nx, xoffset, edgex, x_var, ny, yoffset, edgey, y_var, direction, tri) {
+
+var flag = 0;
+var path = "Models/Weather/blank.ac";
+calc_geo(blat);
+var dir = direction * math.pi/180.0;
+
+var ymin = -0.5 * ny * yoffset;
+var xmin = -0.5 * nx * xoffset;
+var xinc = xoffset * (tri-1.0) /ny;
+ 
+var jlow = int(nx*edgex);
+var ilow = int(ny*edgey);
+
+
+for (var i=0; i<ny; i=i+1)
+	{
+	var y = ymin + i * yoffset; 
+	
+	for (var j=0; j<nx; j=j+1)
+		{
+		var y0 = y + y_var * 2.0 * (rand() -0.5);
+		var x = xmin + j * (xoffset + i * xinc) + x_var * 2.0 * (rand() -0.5);
+		var lat = blat + m_to_lat * (y0 * math.cos(dir) - x * math.sin(dir));
+		var long = blong + m_to_lon * (x * math.cos(dir) + y0 * math.sin(dir));
+
+		var alt = balt + alt_var * 2 * (rand() - 0.5);
+		
+		flag = 0;
+		var rn = 6.0 * rand();
+
+		if (((j<jlow) or (j>(nx-jlow-1))) and ((i<ilow) or (i>(ny-ilow-1)))) # select a small or no cloud		
+			{
+			if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
+			}
+		if ((j<jlow) or (j>(nx-jlow-1)) or (i<ilow) or (i>(ny-ilow-1))) 	
+			{
+			if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
+			}
+		else	{ # select a large cloud
+			if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");}
+			}
+
+
+		if (flag==0){
+			if (thread_flag == 1)
+				{create_cloud_vec(path, lat, long, alt, 0.0);}
+			else
+				{compat_layer.create_cloud(path, lat, long, alt, 0.0);}
+			
+
+				}
+		}
+
+	} 
+
+}
+
+###########################################################
+# place an undulatus pattern 
+###########################################################
+
+var create_undulatus = func (type, blat, blong, balt, alt_var, nx, xoffset, edgex, x_var, ny, yoffset, edgey, y_var, und_strength, direction, tri) {
+
+var flag = 0;
+var path = "Models/Weather/blank.ac";
+calc_geo(blat);
+var dir = direction * math.pi/180.0;
+
+var ymin = -0.5 * ny * yoffset;
+var xmin = -0.5 * nx * xoffset;
+var xinc = xoffset * (tri-1.0) /ny;
+ 
+var jlow = int(nx*edgex);
+var ilow = int(ny*edgey);
+
+var und = 0.0;
+var und_array = [];
+
+for (var i=0; i<ny; i=i+1)
+	{
+	und = und + 2.0 * (rand() -0.5) * und_strength;
+	append(und_array,und);
+	}
+
+for (var i=0; i<ny; i=i+1)
+	{
+	var y = ymin + i * yoffset; 
+	
+	for (var j=0; j<nx; j=j+1)
+		{
+		var y0 = y + y_var * 2.0 * (rand() -0.5);
+		var x = xmin + j * (xoffset + i * xinc) + x_var * 2.0 * (rand() -0.5) + und_array[i];
+		var lat = blat + m_to_lat * (y0 * math.cos(dir) - x * math.sin(dir));
+		var long = blong + m_to_lon * (x * math.cos(dir) + y0 * math.sin(dir));
+
+		var alt = balt + alt_var * 2 * (rand() - 0.5);
+		
+		flag = 0;
+		var rn = 6.0 * rand();
+
+		if (((j<jlow) or (j>(nx-jlow-1))) and ((i<ilow) or (i>(ny-ilow-1)))) # select a small or no cloud		
+			{
+			if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
+			}
+		if ((j<jlow) or (j>(nx-jlow-1)) or (i<ilow) or (i>(ny-ilow-1))) 	
+			{
+			if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
+			}
+		else	{ # select a large cloud
+			if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");}
+			}
+
+
+		if (flag==0){
+			if (thread_flag == 1)
+				{create_cloud_vec(path, lat, long, alt, 0.0);}
+			else
+				{compat_layer.create_cloud(path, lat, long, alt, 0.0);}
+			
+
+				}
+		}
+
+	} 
+
+}
+
+
+###########################################################
+# place a cloud layer 
+###########################################################
+
+var create_layer = func (type, blat, blon, balt, bthick, rx, ry, phi, density, edge, rainflag, rain_density) {
+
+
+var i = 0;
+var area = math.pi * rx * ry;
+var circ = math.pi * (rx + ry); # that's just an approximation
+var n = int(area/80000000.0 * 100 * density);
+var m = int(circ/63000.0 * 40 * rain_density);
+var path = "Models/Weather/blank.ac";
+
+#print("density: ",n);
+
+phi = phi * math.pi/180.0;
+
+if (contains(cloud_vertical_size_map, type)) 
+		{var alt_offset = cloud_vertical_size_map[type]/2.0 * m_to_ft;}
+	else {var alt_offset = 0.0;}
+
+while(i<n)
+	{
+	var x = rx * (2.0 * rand() - 1.0); 
+	var y = ry * (2.0 * rand() - 1.0); 
+	var alt = balt + bthick * rand() + 0.8 * alt_offset;
+	var res = (x*x)/(rx*rx) + (y*y)/(ry*ry);
+
+	if (res < 1.0)
+		{
+		var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
+		var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
+		if (res > ((1.0 - edge) * (1.0- edge)))
+			{
+			if (rand() > 0.4) {
+				path = select_cloud_model(type,"small");
+			if (thread_flag == 1)
+				{create_cloud_vec(path, lat, lon, alt, 0.0);}
+			else
+				{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
+				}
+			}
+		else {
+			path = select_cloud_model(type,"large");
+			if (thread_flag == 1)
+				{create_cloud_vec(path, lat, lon, alt, 0.0);}
+			else 
+				{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
+			}
+		i = i + 1;
+		}
+	}
+
+i = 0;
+
+if (rainflag ==1){
+
+if (local_weather.hardcoded_clouds_flag == 1) {balt = balt + local_weather.offset_map[type]; }
+
+	while(i<m)
+		{
+		var alpha = rand() * 2.0 * math.pi;
+		x = 0.8 * (1.0 - edge) * (1.0-edge) * rx * math.cos(alpha);
+		y = 0.8 * (1.0 - edge) * (1.0-edge) * ry * math.sin(alpha);
+
+		lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
+		lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));	
+	
+		path = "Models/Weather/rain1.xml";
+ 		if (contains(cloud_vertical_size_map,type)) {var alt_shift = cloud_vertical_size_map[type];}
+		else {var alt_shift = 0.0;}
+		
+		if (thread_flag == 1)
+		{create_cloud_vec(path, lat, lon,balt +0.5*bthick+ alt_shift, 0.0);}		
+		else		
+		{compat_layer.create_cloud(path, lat, lon, balt + 0.5 * bthick + alt_shift, 0.0);}
+		i = i + 1;
+		} # end while	
+	} # end if (rainflag ==1)
+}
+
+
+###########################################################
+# place a cloud layer with a gap in the middle
+# (useful to reduce cloud count in large thunderstorms)
+###########################################################
+
+var create_hollow_layer = func (type, blat, blon, balt, bthick, rx, ry, phi, density, edge, gap_fraction) {
+
+
+var i = 0;
+var area = math.pi * rx * ry;
+var n = int(area/80000000.0 * 100 * density);
+var path = "Models/Weather/blank.ac";
+
+phi = phi * math.pi/180.0;
+
+if (contains(cloud_vertical_size_map, type)) 
+		{var alt_offset = cloud_vertical_size_map[type]/2.0 * m_to_ft;}
+	else {var alt_offset = 0.0;}
+
+while(i<n)
+	{
+	var x = rx * (2.0 * rand() - 1.0); 
+	var y = ry * (2.0 * rand() - 1.0); 
+	var alt = balt + bthick * rand() + 0.8 * alt_offset;
+	var res = (x*x)/(rx*rx) + (y*y)/(ry*ry);
+	
+
+	if ((res < 1.0) and (res > (gap_fraction * gap_fraction)))
+		{
+		var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
+		var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
+		if (res > ((1.0 - edge) * (1.0- edge)))
+			{
+			if (rand() > 0.4) {
+				path = select_cloud_model(type,"small");
+				compat_layer.create_cloud(path, lat, lon, alt, 0.0);
+				}
+			}
+		else {
+			path = select_cloud_model(type,"large");
+			if (thread_flag == 1)
+				{create_cloud_vec(path, lat, lon, alt, 0.0);}
+			else 
+				{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
+			}
+		i = i + 1;
+		}
+	else	# we are in the central gap region
+		{
+		i = i + 1;
+		}
+	}
+
+i = 0;
+
+
+}
+
+
+
+
+###########################################################
+# place a cloud box
+###########################################################
+
+
+var create_cloudbox = func (type, blat, blon, balt, dx,dy,dz,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom) {
+
+var phi = 0;
+
+# first get core coordinates
+
+var core_dx = dx * f_core;
+var core_dy = dy * f_core;
+var core_dz = dz * h_core;
+
+var core_x_offset = (1.0 * rand() - 0.5) *  ((dx - core_dx) * r_core);
+var core_y_offset = (1.0 * rand() - 0.5) *  ((dy - core_dy) * r_core);
+
+# get the bottom geometry
+
+var bottom_dx = dx * f_bottom;
+var bottom_dy = dy * f_bottom;
+var bottom_dz = dz * h_bottom;
+
+var bottom_offset = 400.0; # in practice, need a small shift
+
+# fill the main body of the box
+
+for (var i=0; i<n; i=i+1)
+	{
+
+	var x = 0.5 * dx * (2.0 * rand() - 1.0); 
+	var y = 0.5 * dy * (2.0 * rand() - 1.0);
+	
+	# veto in core region
+	if ((x > core_x_offset - 0.5 * core_dx) and (x < core_x_offset + 0.5 * core_dx))
+		{
+		if ((y > core_y_offset - 0.5 * core_dy) and (y < core_y_offset + 0.5 * core_dy))
+			{
+			i = i -1;
+			continue;
+			}
+		}
+	 
+	var alt = balt + bottom_dz + bottom_offset +  dz * rand();
+	
+	var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
+	var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
+
+	var path = select_cloud_model(type,"standard");
+
+	
+	create_cloud_vec(path, lat, lon, alt, 0.0);
+
+	}
+
+# fill the core region
+
+for (var i=0; i<n_core; i=i+1)
+	{
+	var x = 0.5 * core_dx * (2.0 * rand() - 1.0); 
+	var y = 0.5 * core_dy * (2.0 * rand() - 1.0);
+	var alt = balt + bottom_dz + bottom_offset + core_dz * rand();
+
+
+	var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
+	var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
+
+	var path = select_cloud_model(type,"core");
+
+	if (thread_flag == 1)
+			{create_cloud_vec(path, lat, lon, alt, 0.0);}
+		else
+			{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
+
+	}
+
+# fill the bottom region
+
+
+for (var i=0; i<n_bottom; i=i+1)
+	{
+	var x = 0.5 * bottom_dx * (2.0 * rand() - 1.0); 
+	var y = 0.5 * bottom_dy * (2.0 * rand() - 1.0);
+	var alt = balt + bottom_dz * rand();
+
+
+	var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
+	var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
+
+	var path = select_cloud_model(type,"bottom");
+
+	if (thread_flag == 1)
+			{create_cloud_vec(path, lat, lon, alt, 0.0);}
+		else
+			{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
+
+	}
+
+
+}
+
+
+
+###########################################################
+# terrain presampling initialization
+###########################################################
+
+var terrain_presampling_start = func (blat, blon, nc, size, alpha) {
+
+# terrain presampling start is always used the first time, and initializes
+# the hard-coded routine if that is available since the hard-coded routine cannot
+# be yet read out on startup
+	
+# initialize the result vector
+
+setsize(terrain_n,20);
+for(var j=0;j<20;j=j+1){terrain_n[j]=0;}
+
+if (thread_flag == 1)
+	{
+	var status = getprop(lw~"tmp/presampling-status");
+	if (status != "idle") # we try a second later
+		{
+		settimer( func {terrain_presampling_start(blat, blon, nc, size, alpha);},1.00);
+		return;
+		}
+	else	
+		{
+		setprop(lw~"tmp/presampling-status", "sampling");
+		terrain_presampling_loop (blat, blon, nc, size, alpha);
+		}
+	}
+else
+	{
+	terrain_presampling(blat, blon, nc, size, alpha);
+	terrain_presampling_analysis();
+	setprop(lw~"tmp/presampling-status", "finished");
+	}
+	
+if (compat_layer.features.terrain_presampling_active == 1)
+	{
+	print("Starting hard-coded terrain presampling");
+
+	setprop(lw~"tmp/presampling-status", "sampling");
+	setprop("/environment/terrain/area[0]/input/latitude-deg", blat );
+	setprop("/environment/terrain/area[0]/input/longitude-deg", blon );
+	setprop("/environment/terrain/area[0]/input/use-aircraft-position",1);
+	setprop("/environment/terrain/area[0]/input/radius-m",45000.0);
+
+	setprop("/environment/terrain/area[0]/output/valid", 0 );
+
+	}
+}
+
+###########################################################
+# terrain presampling loop
+###########################################################
+
+var terrain_presampling_loop = func (blat, blon, nc, size, alpha) {
+
+if ((local_weather_running_flag == 0) and (local_weather_startup_flag == 0)) {return;}
+
+
+if (local_weather.features.fast_geodinfo == 0)
+	{var n = 25;
+	var n_out = 25;
+
+	# dynamically drop accuracy if framerate is low
+
+	var dt = getprop("/sim/time/delta-sec");
+
+	if (dt > 0.2) # we have below 20 fps
+		{n = 5;}
+	else if (dt > 0.1) # we have below 10 fps
+		{n = 10;}
+	else if (dt > 0.05) # we have below 5 fps
+		{n = 15;}
+	}
+else
+	{
+	n = 250; n_out = 250;
+	}
+
+if (nc <= 0) # we're done and may analyze the result
+	{
+	terrain_presampling_analysis();
+	if (debug_output_flag == 1) 
+		{print("Presampling done!");}
+	setprop(lw~"tmp/presampling-status", "finished");
+	return;
+	}
+
+terrain_presampling(blat, blon, n, size, alpha);
+
+settimer( func {terrain_presampling_loop(blat, blon, nc-n_out, size, alpha) },0);
+}
+
+
+###########################################################
+# terrain presampling routine
+###########################################################
+
+var terrain_presampling = func (blat, blon, ntries, size, alpha) {
+
+var phi = alpha * math.pi/180.0;
+var elevation = 0.0;
+
+var lat_vec = [];
+var lon_vec = [];
+var lat_lon_vec = [];
+
+
+for (var i=0; i<ntries; i=i+1)
+	{
+	var x = (2.0 * rand() - 1.0) * size;
+	var y = (2.0 * rand() - 1.0) * size; 
+	
+	append(lat_vec, blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat);
+	append(lon_vec, blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon);
+	}
+	
+	
+var elevation_vec = compat_layer.get_elevation_array(lat_vec, lon_vec);
+	
+	
+for (i=0; i<ntries;i=i+1)
+	{
+	for(j=0;j<20;j=j+1)
+		{
+		if ((elevation_vec[i] != -1.0) and (elevation_vec[i] < 500.0 * (j+1))) 
+			{terrain_n[j] = terrain_n[j]+1;  break;}
+		}
+		
+	}
+
+
+
+}
+
+###########################################################
+# terrain presampling analysis
+###########################################################
+
+var terrain_presampling_analysis = func {
+
+if ((compat_layer.features.terrain_presampling_active == 0) or (getprop(lw~"tiles/tile-counter") == 0))
+	{
+	var sum = 0;
+	var alt_mean = 0;
+	var alt_med = 0;
+	var alt_20 = 0;
+	var alt_min = 0;
+	var alt_low_min = 0;
+
+
+	for (var i=0; i<20;i=i+1)
+		{sum = sum + terrain_n[i];}
+
+	var n_tot = sum;
+
+	sum = 0;
+	for (var i=0; i<20;i=i+1)
+		{
+		sum = sum + terrain_n[i];
+		if (sum > int(0.5 *n_tot)) {alt_med = i * 500.0; break;}		
+		}
+
+	sum = 0;
+	for (var i=0; i<20;i=i+1)
+		{
+		sum = sum + terrain_n[i];
+		if (sum > int(0.3 *n_tot)) {alt_20 = i * 500.0; break;}		
+		}
+
+
+	for (var i=0; i<20;i=i+1) {alt_mean = alt_mean + terrain_n[i] * i * 500.0;}
+	alt_mean = alt_mean/n_tot;
+
+	for (var i=0; i<20;i=i+1) {if (terrain_n[i] > 0) {alt_min = i * 500.0; break;}}
+
+	var n_max = 0;
+	sum = 0;
+
+	for (var i=0; i<19;i=i+1) 
+		{
+		sum = sum + terrain_n[i];
+		if (terrain_n[i] > n_max) {n_max = terrain_n[i];}
+		if ((n_max > terrain_n[i+1]) and (sum > int(0.3*n_tot)))
+ 			{alt_low_min = i * 500; break;}
+		}
+	}
+else
+	{
+	#print("Hard-coded sampling...");
+	var n_tot = getprop("/environment/terrain/area[0]/input/max-samples");
+	var alt_mean = getprop("/environment/terrain/area[0]/output/alt-mean-ft");
+	var alt_med = getprop("/environment/terrain/area[0]/output/alt-median-ft");
+	var alt_min = getprop("/environment/terrain/area[0]/output/alt-min-ft");
+	var alt_20 = getprop("/environment/terrain/area[0]/output/alt-offset-ft");
+	}
+
+if (debug_output_flag == 1) 
+	{print("Terrain presampling analysis results:");
+	print("total: ",n_tot," mean: ",alt_mean," median: ",alt_med," min: ",alt_min, " alt_20: ", alt_20);}
+
+
+
+setprop(lw~"tmp/tile-alt-offset-ft",alt_20);
+setprop(lw~"tmp/tile-alt-median-ft",alt_med);
+setprop(lw~"tmp/tile-alt-min-ft",alt_min);
+setprop(lw~"tmp/tile-alt-mean-ft",alt_mean);
+setprop(lw~"tmp/tile-alt-layered-ft",0.5 * (alt_min + alt_20));
+
+append(alt_50_array, alt_med);
+append(alt_20_array, alt_20);
+append(alt_min_array, alt_min);
+append(alt_mean_array, alt_mean);
+}
+
+
+
+###########################################################
+# wave conditions search
+###########################################################
+
+var wave_detection_loop = func (blat, blon, nx, alpha) {
+
+if (local_weather_running_flag == 0) {return;}
+
+var phi = alpha * math.pi/180.0;
+var elevation = 0.0;
+var ny = 20;
+
+
+for (var i=0; i<ny; i=i+1)
+	{
+	var x = 5000.0;
+	var y = -20000.0 + i * 2000.0;
+	
+	var lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat;
+	var lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon;
+
+	elevation = compat_layer.get_elevation(lat, lon);
+
+	print(elevation);	
+
+	}
+
+
+}
+
+###########################################################
+# detailed altitude determination for convective calls
+# clouds follow the terrain to some degree, but not excessively so
+###########################################################
+
+var get_convective_altitude = func (balt, elevation, tile_index, grad) {
+
+
+var alt_offset = alt_20_array[tile_index - 1];
+var alt_median = alt_50_array[tile_index - 1];
+
+# get the maximal shift
+var alt_variation = alt_median - alt_offset;
+
+# always get some amount of leeway
+if (alt_variation < 500.0) {alt_variation = 500.0;}
+
+# get the correction to the maximal shift by detailed terrain
+
+if (detailed_terrain_interaction_flag == 1)
+	{
+	var gradfact = get_gradient_factor(grad);
+	
+	if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3))
+		{
+		var windspeed = tile_wind_speed[0];
+		}
+	else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag == 4) or (local_weather.wind_model_flag == 5))
+		{
+		var windspeed = tile_wind_speed[tile_index-1];
+		}
+
+	var gradfact = ((gradfact - 1.0)  * windspeed) + 1.0;
+	#print("gradfact: ", gradfact);
+	}
+else
+	{
+	var gradfact = 1.0;
+	}
+
+var alt_variation = alt_variation * gradfact;
+
+# get the difference between offset and foot point
+var alt_diff = elevation - alt_offset;
+
+# now get the elevation-induced shift
+
+var fraction = alt_diff / alt_variation;
+
+if (fraction > 1.0) {fraction = 1.0;} # no placement above maximum shift
+if (fraction < 0.0) {fraction = 0.0;} # no downward shift
+
+# get the cloud base
+
+var cloudbase = balt - alt_offset;
+
+var alt_above_terrain = balt - elevation;
+
+# the shift strength is weakened if the layer is high above base elevation
+# the reference altitude is 1000 ft, anything higher has less sensitivity to terrain
+
+var shift_strength = 1000.0/alt_above_terrain; 
+
+if (shift_strength > 1.0) {shift_strength = 1.0;} # no enhancement for very low layers 
+if (shift_strength < 0.0) {shift_strength = 1.0;} # this shouldn't happen, but just in case...
+
+if (alt_diff > alt_variation) {alt_diff = alt_variation;} # maximal shift is given by alt_variation
+
+# print("balt: ", balt, " new alt: ", balt + shift_strength * alt_diff * fraction);
+
+return balt + shift_strength * alt_diff * fraction;
+
+}
+
+
+###########################################################
+# detailed terrain gradient determination in wind direction
+###########################################################
+
+
+var get_terrain_gradient = func (lat, lon, elevation1, phi, dist) {
+
+
+# get the first elevation
+# var elevation1 = compat_layer.get_elevation(lat,lon);
+
+# look <dist> upwind to learn about the history of the cloud
+var elevation2 = compat_layer.get_elevation(lat+weather_tiles.get_lat(0.0,dist,phi), lon+weather_tiles.get_lon(0.0,dist,phi));
+
+return (elevation2 - elevation1)/(dist * m_to_ft);
+}
+
+###########################################################
+# enhancement of the placement altitude due to terrain
+###########################################################
+
+var get_gradient_factor = func (grad) {
+
+if (grad > 0.0)
+	{return 1.0;}
+else
+	{
+	return 1.0 -2.0 * grad;
+	}
+}
+
+
+###########################################################
+# suppression of placement in lee terrain
+###########################################################
+
+var get_lee_bias = func (grad) {
+
+
+if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3))
+		{
+		var windspeed = tile_wind_speed[0];
+		}
+	else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag == 4) or (local_weather.wind_model_flag == 5))
+		{
+		var windspeed = tile_wind_speed[tile_index-1];
+		}
+
+
+if (grad < 0.0)
+	{return 1.0;}
+else
+	{
+	var lee_bias = 1.0 - (grad * 0.2 * windspeed);
+	}
+if (lee_bias < 0.2) {lee_bias = 0.2;}
+
+return lee_bias;
+}
+
+###########################################################
+# enhancement of Cumulus in above average altitude
+###########################################################
+
+
+var get_terrain_altitude_factor = func (tile_index, balt, elevation) {
+
+
+var alt_mean = alt_mean_array[tile_index -1];
+var alt_base = alt_20_array[tile_index -1];
+
+var alt_layer = balt - alt_base;
+var alt_above_terrain = balt - elevation;
+var alt_above_mean = balt - alt_mean;
+
+# the cloud may still be above terrain even if the layer altitude is negative, but we want to avoid neg. factors here
+
+if (alt_above_terrain < 0.0) {alt_above_terrain = 0.0;}
+
+var norm_alt_diff = (alt_above_mean - alt_above_terrain)/alt_layer;
+
+if (norm_alt_diff > 0.0)
+		{
+		var terrain_altitude_factor = 1.0 + 2.0 * norm_alt_diff;
+		}
+	else
+		{
+		var terrain_altitude_factor = 1.0/(1.0 - 5.0 * norm_alt_diff);
+		}
+
+if (terrain_altitude_factor > 3.0) {terrain_altitude_factor = 3.0;}
+if (terrain_altitude_factor < 0.1) {terrain_altitude_factor = 0.1;}
+
+return terrain_altitude_factor;
+}
+
+
+var get_terrain_strength_factor = func (terrain_altitude_factor) {
+
+return  1.0+ (0.5 * (terrain_altitude_factor-1.0));
+
+}
+
+
+###########################################################
+# terrain presampling listener dispatcher
+###########################################################
+
+var manage_presampling = func {
+
+
+
+var status = getprop(lw~"tmp/presampling-status");
+
+
+# we only take action when the analysis is done
+if (status != "finished") {return;} 
+
+if (getprop(lw~"tiles/tile-counter") == 0) # we deal with a tile setup call from the menu
+	{
+	set_tile();
+	}
+else	# the tile setup call came from weather_tile_management
+	{
+	var lat = getprop(lw~"tiles/tmp/latitude-deg");
+	var lon = getprop(lw~"tiles/tmp/longitude-deg");
+	var code = getprop(lw~"tiles/tmp/code");
+	var dir_index = getprop(lw~"tiles/tmp/dir-index");	
+
+	weather_tile_management.generate_tile(code, lat, lon, dir_index);
+	}
+
+
+# set status to idle again
+
+setprop(lw~"tmp/presampling-status", "idle");
+
+}
+
+
+###########################################################
+# hardcoded terrain presampling listener dispatcher
+###########################################################
+
+var manage_hardcoded_presampling = func {
+
+var status = getprop("/environment/terrain/area[0]/enabled");
+
+print("Hard-coded terrain presampling status: ", status);
+
+# no action unless the sampler has finished
+if (status ==0) {return;}
+
+# no action if the sampler hasn't been started
+
+if (getprop(lw~"tmp/presampling-status") != "sampling") {return;}
+
+terrain_presampling_analysis();
+if (debug_output_flag == 1) 
+		{print("Presampling done!");}
+setprop(lw~"tmp/presampling-status", "finished");
+
+
+}
+
+###########################################################
+# set wind model flag
+###########################################################
+
+var set_wind_model_flag = func {
+
+var wind_model = getprop(lw~"config/wind-model");
+
+if (wind_model == "constant") {wind_model_flag = 1;}
+else if (wind_model == "constant in tile") {wind_model_flag =2;}
+else if (wind_model == "aloft interpolated") {wind_model_flag =3; }
+else if (wind_model == "airmass interpolated") {wind_model_flag =4;}
+else if (wind_model == "aloft waypoints") {wind_model_flag =5;}
+else {print("Wind model not implemented!"); wind_model_flag =1;}
+
+
+}
+
+
+###########################################################
+# set texture mix for convective clouds
+###########################################################
+
+var set_texture_mix = func {
+
+var thermal_properties = getprop(lw~"config/thermal-properties");
+
+
+convective_texture_mix = -(thermal_properties - 1.0) * 0.4;
+
+if (convective_texture_mix < -0.2) {convective_texture_mix = -0.2;}
+if (convective_texture_mix > 0.2) {convective_texture_mix = 0.2;}
+}
+
+###########################################################
+# create an effect volume
+###########################################################
+
+var create_effect_volume = func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat) {
+
+
+var ev = effectVolume.new (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat);
+ev.index = getprop(lw~"tiles/tile-counter");
+ev.active_flag = 0;
+
+
+if (vis < 0.0) {ev.vis_flag = 0;} else {ev.vis_flag = 1;}
+if (rain < 0.0) {ev.rain_flag = 0;} else {ev.rain_flag = 1;}
+if (snow < 0.0) {ev.snow_flag = 0;} else {ev.snow_flag = 1;}
+if (turb < 0.0) {ev.turb_flag = 0;} else {ev.turb_flag = 1;}
+if (lift_flag ==  0.0) {ev.lift_flag = 0;} else {ev.lift_flag = 1;}
+if (sat < 0.0) {ev.sat_flag = 0;} else {ev.sat_flag = 1;}
+if (sat > 1.0) {sat = 1.0;}
+
+if (lift_flag == -2) # we create a thermal by function
+	{
+	ev.lift_flag = 2;
+	ev.radius = 0.8 * r1;
+	ev.height = alt_high * 0.87;
+	ev.cn = 0.7 + rand() * 0.2;
+	ev.sh = 0.7 + rand() * 0.2;
+	ev.max_lift = lift;
+	ev.f_lift_radius = 0.7 + rand() * 0.2;
+	if (dynamics_flag == 1) # globals set by the convective system
+		{
+		ev.flt = cloud_fractional_lifetime;
+		ev.evolution_timestamp = cloud_evolution_timestamp;
+		}
+	}
+
+if (lift_flag == -3) # we create a wave lift
+	{
+	ev.lift_flag = 3;
+	ev.height = 10000.0; # scale height in ft
+	ev.max_lift = lift;
+	ev.index = 0; # static objects are assigned tile id zero
+	}
+
+# set a timestamp if needed
+
+if (dynamics_flag == 1)
+	{
+	ev.timestamp = weather_dynamics.time_lw;
+	}
+
+# and add to the counter
+setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")+1);
+
+append(effectVolumeArray,ev);
+}
+
+
+
+
+
+###########################################################
+# set a weather station for interpolation
+###########################################################
+
+var set_weather_station = func (lat, lon, alt, vis, T, D, p) {
+
+var s = weatherStation.new (lat, lon, alt, vis, T, D, p);
+s.index = getprop(lw~"tiles/tile-counter");
+s.weight = 0.02;
+
+# set a timestamp if needed
+
+if (dynamics_flag == 1)
+	{
+	s.timestamp = weather_dynamics.time_lw;
+	}
+append(weatherStationArray,s);
+
+}
+
+
+###########################################################
+# set an atmosphere condition point for interpolation
+###########################################################
+
+var set_atmosphere_ipoint = func (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst,ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high) {
+
+var a = atmosphereIpoint.new (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst, ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high);
+a.index = getprop(lw~"tiles/tile-counter");
+a.weight = 0.02;
+
+# set a timestamp if needed
+
+if (dynamics_flag == 1)
+	{
+	a.timestamp = weather_dynamics.time_lw;
+	}
+append(atmosphereIpointArray,a);
+
+}
+
+
+###########################################################
+# set a wind interpolation point
+###########################################################
+
+var set_wind_ipoint = func (lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8) {
+
+var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
+
+append(windIpointArray, w);
+
+
+}
+
+
+###########################################################
+# set a wind interpolation point from ground METAR data
+###########################################################
+
+var set_wind_ipoint_metar = func (lat, lon, d0, v0) {
+
+# insert a plausible pattern of aloft winds based on ground info
+
+
+# direction of Coriolis deflection depends on hemisphere
+if (lat >0.0) {var dsign = -1.0;} else {var dsign = 1.0;} 
+
+
+var v1 = v0 * (1.0 + rand() * 0.1);
+var d1 = d0 + dsign * 2.0 * rand();
+
+var v2 = v0 * (1.2 + rand() * 0.2);
+var d2 = d0 + dsign * (3.0 * rand() + 2.0);
+
+var v3 = v0 * (1.3 + rand() * 0.4) + 5.0;
+var d3 = d0 + dsign * (3.0 * rand() + dsign * 4.0);
+
+var v4 = v0 * (1.7 + rand() * 0.5) + 10.0;
+var d4 = d0 + dsign * (4.0 * rand() + dsign * 8.0);
+
+var v5 = v0 * (1.7 + rand() * 0.5) + 20.0;
+var d5 = d0 + dsign * (4.0 * rand() + dsign * 10.0);
+
+var v6 = v0 * (1.7 + rand() * 0.5) + 40.0;
+var d6 = d0 + dsign * (4.0 * rand() + dsign * 12.0);
+
+var v7 = v0 * (2.0 + rand() * 0.7) + 50.0;
+var d7 = d0 + dsign * (4.0 * rand() + dsign * 13.0);
+
+var v8 = v0 * (2.0 + rand() * 0.7) + 55.0;;
+var d8 = d0 + dsign * (5.0 * rand() + dsign * 14.0);
+
+var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
+
+append(windIpointArray, w);
+
+
+
+}
+
+###########################################################
+# helper to show additional dialogs
+###########################################################
+
+var showDialog = func (name) {
+
+fgcommand("dialog-show", props.Node.new({"dialog-name":name}));
+
+}
+
+
+###########################################################
+# helper to transfer configuration flags in menu to Nasal
+###########################################################
+
+var readFlags = func {
+
+# thermal lift must be 1 for constant thermals (obsolete), 2 for thermals by model (menu default)
+# and 3 for blue thermals (set internally inside the tile only)
+
+if (getprop(lw~"config/generate-thermal-lift-flag") ==1) {generate_thermal_lift_flag = 2;}
+	else {generate_thermal_lift_flag = 0};
+
+thread_flag = getprop(lw~"config/thread-flag");
+# dynamics_flag = getprop(lw~"config/dynamics-flag");
+presampling_flag = getprop(lw~"config/presampling-flag");
+detailed_clouds_flag = getprop(lw~"config/detailed-clouds-flag");
+dynamical_convection_flag = getprop(lw~"config/dynamical-convection-flag");
+debug_output_flag = getprop(lw~"config/debug-output-flag");
+fps_control_flag = getprop(lw~"config/fps-control-flag");
+realistic_visibility_flag = getprop(lw~"config/realistic-visibility-flag");
+detailed_terrain_interaction_flag = getprop(lw~"config/detailed-terrain-interaction-flag");
+scattering_shader_flag = getprop("/sim/rendering/scattering-shader");
+
+}
+
+###########################################################
+# wrappers to call functions from the local weather menu bar 
+###########################################################
+
+var streak_wrapper = func {
+
+thread_flag = 0;
+dynamics_flag = 0;
+presampling_flag = 0;
+
+var array = [];
+append(weather_tile_management.modelArrays,array);
+setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+var type = getprop("/local-weather/tmp/cloud-type");
+var alt = getprop("/local-weather/tmp/alt");
+var nx = getprop("/local-weather/tmp/nx");
+var xoffset = getprop("/local-weather/tmp/xoffset");
+var xedge = getprop("/local-weather/tmp/xedge");
+var ny = getprop("/local-weather/tmp/ny");
+var yoffset = getprop("/local-weather/tmp/yoffset");
+var yedge = getprop("/local-weather/tmp/yedge");
+var dir = getprop("/local-weather/tmp/dir");
+var tri = getprop("/local-weather/tmp/tri");
+var rnd_alt = getprop("/local-weather/tmp/rnd-alt");
+var rnd_pos_x = getprop("/local-weather/tmp/rnd-pos-x");
+var rnd_pos_y = getprop("/local-weather/tmp/rnd-pos-y");
+
+create_streak(type,lat,lon,alt,rnd_alt,nx,xoffset,xedge,rnd_pos_x,ny,yoffset,yedge,rnd_pos_y,dir,tri);
+}
+
+
+var convection_wrapper = func {
+
+thread_flag = 0;
+dynamics_flag = 0;
+presampling_flag = 0;
+
+
+var array = [];
+append(weather_tile_management.modelArrays,array);
+setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+var alt = getprop("/local-weather/tmp/conv-alt");
+var size = getprop("/local-weather/tmp/conv-size");
+var strength = getprop("/local-weather/tmp/conv-strength");
+
+var n = int(10 * size * size * strength);
+create_cumosys(lat,lon,alt,n, size*1000.0);
+
+}
+
+var barrier_wrapper = func {
+
+
+thread_flag = 0;
+dynamics_flag = 0;
+presampling_flag = 0;
+
+
+var array = [];
+append(weather_tile_management.modelArrays,array);
+setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+var alt = getprop("/local-weather/tmp/bar-alt");
+var n = getprop("/local-weather/tmp/bar-n");
+var dir = getprop("/local-weather/tmp/bar-dir");
+var dist = getprop("/local-weather/tmp/bar-dist") * 1000.0;
+var size = getprop("/local-weather/tmp/bar-size") * 1000.0;
+
+create_rise_clouds(lat, lon, alt, n, size, dir, dist);
+
+}
+
+var single_cloud_wrapper = func {
+
+thread_flag = 0;
+dynamics_flag = 0;
+presampling_flag = 0;
+
+
+
+var array = [];
+append(weather_tile_management.modelArrays,array);
+setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+var type = getprop("/local-weather/tmp/scloud-type");
+var subtype = getprop("/local-weather/tmp/scloud-subtype");
+var lat = getprop("/local-weather/tmp/scloud-lat");
+var lon = getprop("/local-weather/tmp/scloud-lon");
+var alt = getprop("/local-weather/tmp/scloud-alt");
+var heading = getprop("/local-weather/tmp/scloud-dir");
+
+var path = select_cloud_model(type,subtype);
+
+compat_layer.create_cloud(path, lat, lon, alt, heading);
+
+}
+
+var layer_wrapper = func {
+
+thread_flag = 0;
+dynamics_flag = 0;
+presampling_flag = 0;
+
+
+var array = [];
+append(weather_tile_management.modelArrays,array);
+setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+var type = getprop(lw~"tmp/layer-type");
+var rx = getprop(lw~"tmp/layer-rx") * 1000.0;
+var ry = getprop(lw~"tmp/layer-ry") * 1000.0;
+var phi = getprop(lw~"tmp/layer-phi");
+var alt = getprop(lw~"tmp/layer-alt");
+var thick = getprop(lw~"tmp/layer-thickness");
+var density = getprop(lw~"tmp/layer-density");
+var edge = getprop(lw~"tmp/layer-edge");
+var rain_flag = getprop(lw~"tmp/layer-rain-flag");
+var rain_density = getprop(lw~"tmp/layer-rain-density");
+
+create_layer(type, lat, lon, alt, thick, rx, ry, phi, density, edge, rain_flag, rain_density);
+
+}
+
+var box_wrapper = func {
+
+thread_flag = 0;
+dynamics_flag = 0;
+presampling_flag = 0;
+
+
+setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+var alt = getprop("position/altitude-ft");
+var x = getprop(lw~"tmp/box-x-m");
+var y = getprop(lw~"tmp/box-y-m");
+var z = getprop(lw~"tmp/box-alt-ft");
+var n = getprop(lw~"tmp/box-n");
+var f_core = getprop(lw~"tmp/box-core-fraction");
+var r_core = getprop(lw~"tmp/box-core-offset");
+var h_core = getprop(lw~"tmp/box-core-height");
+var n_core = getprop(lw~"tmp/box-core-n");
+var f_bottom = getprop(lw~"tmp/box-bottom-fraction");
+var h_bottom = getprop(lw~"tmp/box-bottom-thickness");
+var n_bottom = getprop(lw~"tmp/box-bottom-n");
+
+var type = "Box_test";
+
+
+
+create_cloudbox(type, lat, lon, alt, x,y,z,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom);
+
+}
+
+
+var set_aloft_wrapper = func {
+
+
+
+var lat = getprop(lw~"tmp/ipoint-latitude-deg");
+var lon = getprop(lw~"tmp/ipoint-longitude-deg");
+
+var d0 = getprop(lw~"tmp/FL0-wind-from-heading-deg");
+var v0 = getprop(lw~"tmp/FL0-windspeed-kt");
+
+var d1 = getprop(lw~"tmp/FL50-wind-from-heading-deg");
+var v1 = getprop(lw~"tmp/FL50-windspeed-kt");
+
+var d2 = getprop(lw~"tmp/FL100-wind-from-heading-deg");
+var v2 = getprop(lw~"tmp/FL100-windspeed-kt");
+
+var d3 = getprop(lw~"tmp/FL180-wind-from-heading-deg");
+var v3 = getprop(lw~"tmp/FL180-windspeed-kt");
+
+var d4 = getprop(lw~"tmp/FL240-wind-from-heading-deg");
+var v4 = getprop(lw~"tmp/FL240-windspeed-kt");
+
+var d5 = getprop(lw~"tmp/FL300-wind-from-heading-deg");
+var v5 = getprop(lw~"tmp/FL300-windspeed-kt");
+
+var d6 = getprop(lw~"tmp/FL340-wind-from-heading-deg");
+var v6 = getprop(lw~"tmp/FL340-windspeed-kt");
+
+var d7 = getprop(lw~"tmp/FL390-wind-from-heading-deg");
+var v7 = getprop(lw~"tmp/FL390-windspeed-kt");
+
+var d8 = getprop(lw~"tmp/FL450-wind-from-heading-deg");
+var v8 = getprop(lw~"tmp/FL450-windspeed-kt");
+
+set_wind_ipoint(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
+
+if (wind_model_flag == 5)
+{setprop(lwi~"ipoint-number", getprop(lwi~"ipoint-number") + 1);}
+
+}
+
+####################################
+# tile setup call wrapper
+####################################
+
+var set_tile = func {
+
+# check if another instance of local weather is running already
+
+
+if (local_weather_running_flag == 1)
+	{
+	setprop("/sim/messages/pilot", "Local weather: Local weather is already running, use Clear/End before restarting. Aborting...");
+	return;
+	}
+
+local_weather_startup_flag = 1;
+
+
+var type = getprop("/local-weather/tmp/tile-type");
+
+# set tile center coordinates to current position
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+
+setprop(lw~"tiles/tmp/latitude-deg",lat);
+setprop(lw~"tiles/tmp/longitude-deg",lon);
+setprop(lw~"tiles/tmp/dir-index",4);
+
+readFlags();
+
+# check consistency of flags
+
+if (dynamical_convection_flag == 1)
+	{
+	if (dynamics_flag == 0) 
+		{
+		print("Dynamical convection needs weather dynamics to run! Aborting..."); 
+		setprop("/sim/messages/pilot", "Local weather: dynamical convection needs weather dynamics to run! Aborting...");
+		return;
+		}
+	if (presampling_flag == 0) 
+		{
+		print("Dynamical convection needs terrain presampling to run! Aborting..."); 
+		setprop("/sim/messages/pilot", "Local weather: dynamical convection needs terrain presampling to run! Aborting...");
+		return;
+		}
+	}
+
+if (detailed_terrain_interaction_flag == 1)
+	{
+	if (presampling_flag == 0)
+		{
+		print("Terrain effect needs terrain presampling to run! Aborting..."); 
+		setprop("/sim/messages/pilot", "Local weather: terrain effect needs terrain presampling to run! Aborting...");
+		return;
+		}
+	}
+
+
+# if we can do so, we switch global weather and METAR parsing in environment off at this point
+
+if (compat_layer.features.can_disable_environment ==1)
+	{
+	props.globals.getNode("/environment/config/enabled").setBoolValue(0);
+	props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0);
+	}
+
+
+# switch off normal 3d clouds
+
+local_weather.setDefaultCloudsOff();
+
+# now see if we need to presample the terrain
+
+if ((presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) 
+	{
+	terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg")); 
+	return;
+	}
+
+
+# indicate that we're up and running
+
+local_weather_startup_flag = 0;
+local_weather_running_flag = 1;
+
+# see if we use METAR for weather setup
+
+if ((getprop("/environment/metar/valid") == 1) and (getprop(lw~"tmp/tile-management") == "METAR"))
+	{
+	type = "METAR";
+	metar_flag = 1;	
+	
+	setprop(lw~"METAR/station-id","METAR");
+
+	
+	
+	}
+else if ((getprop("/environment/metar/valid") == 0) and (getprop(lw~"tmp/tile-management") == "METAR"))
+	{
+	print("No METAR available, aborting...");
+	setprop("/sim/messages/pilot", "Local weather: No METAR available! Aborting...");
+	return;
+	}
+
+
+# see if we need to create an aloft wind interpolation structure
+
+if ((wind_model_flag == 3) or ((wind_model_flag ==5) and (getprop(lwi~"ipoint-number") == 0))) 
+	{
+	if (metar_flag != 1)
+		{set_aloft_wrapper();}
+	}
+
+
+# prepare the first tile wind field
+
+if (metar_flag == 1) # the winds from current METAR are used
+	{
+
+	# METAR reports ground winds, we want to set aloft, so we need to compute the local boundary layer
+	# need to set the tile index for this
+	setprop(lw~"tiles/tile[4]/tile-index",1);
+
+	var boundary_correction = 1.0/get_slowdown_fraction();
+	var metar_base_wind_deg = getprop("environment/metar/base-wind-dir-deg");
+	var metar_base_wind_speed = boundary_correction * getprop("environment/metar/base-wind-speed-kt");
+
+	# set the wind hash for the new scheme
+	
+	wind.cloudlayer = [metar_base_wind_deg,metar_base_wind_speed];
+	wind.surface = [metar_base_wind_deg,metar_base_wind_speed/boundary_correction];
+	wind.current = wind.surface;
+
+
+	if ((wind_model_flag == 1) or (wind_model_flag == 2))
+		{
+		append(weather_dynamics.tile_wind_direction, metar_base_wind_deg);
+		append(weather_dynamics.tile_wind_speed, metar_base_wind_speed);
+		setprop(lw~"tmp/tile-orientation-deg",metar_base_wind_deg);
+		}
+	else if (wind_model_flag == 5) 
+		{
+		var station_lat = getprop("/environment/metar/station-latitude-deg");
+		var station_lon = getprop("/environment/metar/station-longitude-deg");
+
+		set_wind_ipoint_metar(station_lat, station_lon, metar_base_wind_deg, metar_base_wind_speed);
+
+		var res = wind_interpolation(lat,lon,0.0);
+
+		append(weather_dynamics.tile_wind_direction,res[0]);
+		append(weather_dynamics.tile_wind_speed,res[1]);
+		setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]);
+		}
+	else
+		{
+		print("Wind model currently not supported with live data!");
+		setprop("/sim/messages/pilot", "Local weather: Wind model currently not supported with live data! Aborting...");
+		return;
+		}
+	}
+else
+	{
+	setprop(lw~"tiles/tile[4]/tile-index",1);
+	var boundary_correction = get_slowdown_fraction();
+
+	if (wind_model_flag == 5) # it needs to be interpolated
+		{
+		var res = wind_interpolation(lat,lon,0.0);
+
+		append(weather_dynamics.tile_wind_direction,res[0]);
+		append(weather_dynamics.tile_wind_speed,res[1]);
+
+		# set the wind hash for the new scheme
+	
+		wind.surface = [res[0],res[1] * boundary_correction];
+		wind.cloudlayer = res;
+		wind.current = wind.surface;
+
+
+		}
+	else if (wind_model_flag == 3) # it comes from a different menu
+		{
+		append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/FL0-wind-from-heading-deg"));
+		append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/FL0-windspeed-kt"));
+		
+		# set the wind hash for the new scheme
+		wind.cloudlayer = [getprop(lw~"tmp/FL0-wind-from-heading-deg"),getprop(lw~"tmp/FL0-windspeed-kt") ];
+		wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction];
+		wind.current = wind.surface;
+
+		}
+	else # it comes from the standard menu
+		{
+		append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/tile-orientation-deg"));
+		append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/windspeed-kt"));
+
+		# set the wind hash for the new scheme
+		wind.cloudlayer = [getprop(lw~"tmp/tile-orientation-deg"),getprop(lw~"tmp/windspeed-kt")];
+		wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction];
+		wind.current = wind.surface;
+
+		}
+
+	# when the aloft wind menu is used, the lowest winds should be taken from there
+	# so we need to overwrite the setting from the tile generating menu in this case
+	# otherwise the wrong orientation is built
+
+
+	if (wind_model_flag ==3)
+		{
+		setprop(lw~"tmp/tile-orientation-deg", getprop(lw~"tmp/FL0-wind-from-heading-deg"));
+		}
+	else if (wind_model_flag == 5) 
+		{
+		setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]);
+		}
+}
+
+# create all the neighbouring tile coordinate sets
+
+weather_tile_management.create_neighbours(lat,lon,getprop(lw~"tmp/tile-orientation-deg"));
+
+
+
+
+setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+
+# see if we need to generate a quadtree structure for clouds 
+
+if (dynamics_flag ==1)
+	{
+	var quadtree = [];
+	weather_dynamics.generate_quadtree_structure(0, quadtree);
+	append(weather_dynamics.cloudQuadtrees,quadtree);
+	}
+
+
+	
+
+if (type == "High-pressure-core")
+	{weather_tiles.set_high_pressure_core_tile();}
+else if (type == "High-pressure")
+	{weather_tiles.set_high_pressure_tile();}
+else if (type == "High-pressure-border")
+	{weather_tiles.set_high_pressure_border_tile();}
+else if (type == "Low-pressure-border")
+	{weather_tiles.set_low_pressure_border_tile();}
+else if (type == "Low-pressure")
+	{weather_tiles.set_low_pressure_tile();}
+else if (type == "Low-pressure-core")
+	{weather_tiles.set_low_pressure_core_tile();}
+else if (type == "Cold-sector")
+	{weather_tiles.set_cold_sector_tile();}
+else if (type == "Warm-sector")
+	{weather_tiles.set_warm_sector_tile();}
+else if (type == "Tropical")
+	{weather_tiles.set_tropical_weather_tile();}
+else if (type == "Coldfront")
+	{weather_tiles.set_coldfront_tile();}
+else if (type == "Warmfront")
+	{weather_tiles.set_warmfront1_tile();}
+else if (type == "Warmfront-2")
+	{weather_tiles.set_warmfront2_tile();}
+else if (type == "Warmfront-3")
+	{weather_tiles.set_warmfront3_tile();}
+else if (type == "Warmfront-4")
+	{weather_tiles.set_warmfront4_tile();}
+else if (type == "METAR")
+	{weather_tiles.set_METAR_tile();}
+else if (type == "Altocumulus sky")
+	{weather_tiles.set_altocumulus_tile();setprop(lw~"tiles/code","altocumulus_sky");}
+else if (type == "Broken layers") 
+	{weather_tiles.set_broken_layers_tile();setprop(lw~"tiles/code","broken_layers");}
+else if (type == "Cold front")
+	{weather_tiles.set_coldfront_tile();setprop(lw~"tiles/code","coldfront");}
+else if (type == "Cirrus sky")
+	{weather_tiles.set_cirrus_sky_tile();setprop(lw~"tiles/code","cirrus_sky");}
+else if (type == "Fair weather")
+	{setprop(lw~"tiles/code","cumulus_sky");weather_tiles.set_fair_weather_tile();}
+else if (type == "Glider's sky")
+	{setprop(lw~"tiles/code","gliders_sky");weather_tiles.set_gliders_sky_tile();}
+else if (type == "Blue thermals")
+	{setprop(lw~"tiles/code","blue_thermals");weather_tiles.set_blue_thermals_tile();}
+else if (type == "Incoming rainfront")
+	{weather_tiles.set_rainfront_tile();setprop(lw~"tiles/code","rainfront");}
+else if (type == "8/8 stratus sky")
+	{weather_tiles.set_overcast_stratus_tile();setprop(lw~"tiles/code","overcast_stratus");}
+else if (type == "Test tile")
+	{weather_tiles.set_4_8_stratus_tile();setprop(lw~"tiles/code","test");}
+else if (type == "Summer rain")
+	{weather_tiles.set_summer_rain_tile();setprop(lw~"tiles/code","summer_rain");}
+else 
+	{print("Tile not implemented.");setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")-1);return();}
+
+
+# mark tile as active
+
+append(weather_tile_management.active_tile_list,1);
+
+# start tile management loop if needed
+
+if (getprop(lw~"tmp/tile-management") != "single tile") {
+	if (getprop(lw~"tile-loop-flag") == 0) 
+	{
+	setprop(lw~"tiles/tile[4]/code",getprop(lw~"tiles/code"));
+	setprop(lw~"tile-loop-flag",1); 
+	weather_tile_management.tile_management_loop();}
+	}
+
+# start the interpolation loop
+
+if (getprop(lw~"interpolation-loop-flag") == 0) 
+{setprop(lw~"interpolation-loop-flag",1); local_weather.interpolation_loop();}
+
+# start the effect volume loop
+
+if (getprop(lw~"effect-loop-flag") == 0) 
+{setprop(lw~"effect-loop-flag",1); local_weather.effect_volume_loop(0,0);}
+
+# start weather dynamics loops if needed
+
+if (getprop(lw~"timing-loop-flag") == 0) 
+	{setprop(lw~"timing-loop-flag",1); local_weather.timing_loop();}
+
+if (dynamics_flag ==1)
+	{
+	
+
+	if (getprop(lw~"dynamics-loop-flag") == 0) 
+		{
+		setprop(lw~"dynamics-loop-flag",1); 
+		weather_dynamics.quadtree_loop(); 
+		weather_dynamics.weather_dynamics_loop(0,0);
+		}
+	if ((getprop(lw~"convective-loop-flag") == 0) and (getprop(lw~"config/dynamical-convection-flag") ==1))
+		{
+		setprop(lw~"convective-loop-flag",1); 
+		weather_dynamics.convective_loop();
+		}
+	}
+
+
+
+
+# and start the buffer loop and housekeeping loop if needed
+
+if (buffer_flag == 1)
+	{
+	if (getprop(lw~"buffer-loop-flag") == 0) 
+		{
+		setprop(lw~"buffer-loop-flag",1); weather_tile_management.buffer_loop(0);
+		setprop(lw~"housekeeping-loop-flag",1); weather_tile_management.housekeeping_loop(0,0);
+		}
+	}
+
+
+
+# weather_tile_management.watchdog_loop();
+
+}
+
+
+#################################################
+# Anything that needs to run at startup goes here
+#################################################
+
+var startup = func {
+print("Loading local weather routines...");
+
+# get local Cartesian geometry
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+calc_geo(lat);
+
+
+# copy weather properties at startup to local weather
+
+setprop(lw~"interpolation/visibility-m",getprop(ec~"boundary/entry[0]/visibility-m"));
+setprop(lw~"interpolation/pressure-sea-level-inhg",getprop(ec~"boundary/entry[0]/pressure-sea-level-inhg"));
+setprop(lw~"interpolation/temperature-degc",getprop(ec~"boundary/entry[0]/temperature-degc"));
+setprop(lw~"interpolation/wind-from-heading-deg",getprop(ec~"boundary/entry[0]/wind-from-heading-deg"));
+setprop(lw~"interpolation/wind-speed-kt",getprop(ec~"boundary/entry[0]/wind-speed-kt"));
+setprop(lw~"interpolation/turbulence",getprop(ec~"boundary/entry[0]/turbulence/magnitude-norm"));
+
+setprop(lw~"interpolation/rain-norm",0.0);
+setprop(lw~"interpolation/snow-norm",0.0);
+setprop(lw~"interpolation/thermal-lift",0.0);
+
+
+# before interpolation starts, these are also initially current
+
+setprop(lw~"current/visibility-m",getprop(lwi~"visibility-m"));
+setprop(lw~"current/pressure-sea-level-inhg",getprop(lw~"interpolation/pressure-sea-level-inhg"));
+setprop(lw~"current/temperature-degc",getprop(lw~"interpolation/temperature-degc"));
+setprop(lw~"current/wind-from-heading-deg",getprop(lw~"interpolation/wind-from-heading-deg"));
+setprop(lw~"current/wind-speed-kt",getprop(lw~"interpolation/wind-speed-kt"));
+setprop(lw~"current/rain-norm",getprop(lw~"interpolation/rain-norm"));
+setprop(lw~"current/snow-norm",getprop(lw~"interpolation/snow-norm"));
+setprop(lw~"current/thermal-lift",getprop(lw~"interpolation/thermal-lift"));
+setprop(lw~"current/turbulence",getprop(lwi~"turbulence"));
+
+# create default properties for METAR system, should be overwritten by real-weather-fetch
+
+setprop(lw~"METAR/latitude-deg",lat); 
+setprop(lw~"METAR/longitude-deg",lon);
+setprop(lw~"METAR/altitude-ft",0.0);
+setprop(lw~"METAR/wind-direction-deg",0.0);
+setprop(lw~"METAR/wind-strength-kt",10.0);
+setprop(lw~"METAR/visibility-m",17000.0);
+setprop(lw~"METAR/rain-norm",0.0);
+setprop(lw~"METAR/snow-norm",0.0);
+setprop(lw~"METAR/temperature-degc",10.0);
+setprop(lw~"METAR/dewpoint-degc",7.0);
+setprop(lw~"METAR/pressure-inhg",29.92);
+setprop(lw~"METAR/thunderstorm-flag",0);
+setprop(lw~"METAR/layer[0]/cover-oct",4);
+setprop(lw~"METAR/layer[0]/alt-agl-ft", 3000.0);
+setprop(lw~"METAR/layer[1]/cover-oct",0);
+setprop(lw~"METAR/layer[1]/alt-agl-ft", 20000.0);
+setprop(lw~"METAR/layer[2]/cover-oct",0);
+setprop(lw~"METAR/layer[2]/alt-agl-ft", 20000.0);
+setprop(lw~"METAR/layer[3]/cover-oct",0);
+setprop(lw~"METAR/layer[3]/alt-agl-ft", 20000.0);
+setprop(lw~"METAR/available-flag",1);
+
+
+
+
+# set listeners
+
+setlistener(lw~"tmp/thread-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation);  });
+setlistener(lw~"tmp/convective-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation);  });
+setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo);  effect_placement_loop(s); });
+setlistener(lw~"tmp/presampling-status", func {manage_presampling(); });
+
+setlistener(lw~"config/wind-model", func {set_wind_model_flag();});
+setlistener(lw~"config/thermal-properties", func {set_texture_mix();});
+
+setlistener(lw~"config/clouds-in-dynamics-loop", func {weather_dynamics.max_clouds_in_loop = int(getprop(lw~"config/clouds-in-dynamics-loop"));});
+
+setlistener(lw~"config/clouds-visible-range-m", func {weather_tile_management.cloud_view_distance = getprop(lw~"config/clouds-visible-range-m");});
+setlistener(lw~"config/distance-to-load-tile-m", func {setprop(lw~"config/distance-to-remove-tile-m",getprop(lw~"config/distance-to-load-tile-m") + 500.0);});
+
+setlistener(lw~"config/fps-control-flag", func {fps_control_flag = getprop(lw~"config/fps-control-flag");});
+setlistener(lw~"config/target-framerate", func {target_framerate = getprop(lw~"config/target-framerate");});
+
+setlistener(lw~"config/small-scale-persistence", func {weather_tiles.small_scale_persistence = getprop(lw~"config/small-scale-persistence");});
+setlistener(lw~"config/ground-haze-factor", func {ground_haze_factor = getprop(lw~"config/ground-haze-factor");});
+setlistener(lw~"config/max-vis-range-m", func {max_vis_range = getprop(lw~"config/max-vis-range-m");});
+
+setlistener(lw~"config/temperature-offset-degc", func {temperature_offset = getprop(lw~"config/temperature-offset-degc");});
+
+setlistener("/sim/rendering/scattering-shader", func {scattering_shader_flag = getprop("/sim/rendering/scattering-shader"); });
+}
+
+
+#####################################################
+# Standard test call (for development and debug only)
+#####################################################
+
+var test = func {
+
+var lat = getprop("position/latitude-deg");
+var lon = getprop("position/longitude-deg");
+
+thread_flag = 0;
+dynamics_flag = 0;
+presampling_flag = 0;
+
+
+#if (compat_layer.features.can_disable_environment ==1)
+#	{
+#	props.globals.getNode("/environment/config/enabled").setBoolValue(0);
+#	props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0);
+#	}
+#
+#compat_layer.setDefaultCloudsOff();
+
+#var array = [];
+#append(weather_tile_management.modelArrays,array);
+#setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
+
+
+#var pos = geo.aircraft_position();
+
+#debug.dump(geodinfo(lat, lon));
+
+#create_cumulonimbus_cloud(lat, lon, 6000.0, 2.5);
+
+# geo.put_model("Models/Astro/Earth.ac",lat, lon);
+
+#setprop("/environment/terrain/area[0]/input/latitude-deg", lat );
+#setprop("/environment/terrain/area[0]/input/longitude-deg", lon );
+
+#setprop("/environment/terrain/area[0]/output/valid", 0 );
+
+elttest();
+
+}
+
+
+var elttest = func {
+
+var lat_uncertainty = 0.001;
+var lon_uncertainty = 0.001;
+
+#var lat = getprop("/position/latitude-deg") + lat_uncertainty * 0.5 - rand();
+#var lon = getprop("/position/longitude-deg") + lon_uncertainty * 0.5 - rand();
+var lat = getprop("/position/latitude-string");
+var lon = getprop("/position/longitude-string");
+var aircraft = getprop("sim/description");
+var callsign = getprop("sim/multiplay/callsign");
+var help_string = "ELT AutoMessage: " ~ aircraft ~ " " ~ callsign ~ " " ~lat~" LAT "~lon~" LON, requesting SAR service";
+
+setprop("/sim/multiplay/chat", help_string);
+}
+
+
+#################################################################
+# object classes
+#################################################################
+
+var weatherStation = {
+	new: func (lat, lon, alt, vis, T, D, p) {
+	        var s = { parents: [weatherStation] };
+		s.lat = lat;
+		s.lon = lon;
+		s.alt = alt;
+		s.vis = vis;
+		s.T = T;
+		s.D = D;
+		s.p = p;
+		s.scattering = 0.8;
+	        return s;
+	},
+	move: func {
+		var windfield = weather_dynamics.get_windfield(me.index);
+		var dt = weather_dynamics.time_lw - me.timestamp;
+		me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
+		me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
+		me.timestamp = weather_dynamics.time_lw;
+	},
+};
+
+
+var atmosphereIpoint = {
+	new: func (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst, ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high){
+		var a = { parents: [atmosphereIpoint] };
+		a.lat = lat;
+		a.lon = lon;
+		a.vis_aloft = vis_aloft;
+		a.vis_alt1 = vis_alt1;
+		a.vis_ovcst = vis_ovcst;
+		a.ovcst = ovcst;
+		a.ovcst_alt_low = ovcst_alt_low;
+		a.ovcst_alt_high = ovcst_alt_high;
+		a.scatt = scatt;
+		a.scatt_alt_low = scatt_alt_low;
+		a.scatt_alt_high = scatt_alt_high;
+		return a;
+	},
+	move: func {
+		var windfield = weather_dynamics.get_windfield(me.index);
+		var dt = weather_dynamics.time_lw - me.timestamp;
+		me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
+		me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
+		me.timestamp = weather_dynamics.time_lw;
+	},
+};
+
+
+var windIpoint = {
+	new: func (lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8) {
+	        var w = { parents: [windIpoint] };
+		w.lat = lat;
+		w.lon = lon;
+		
+		altvec = [];
+		
+		var wv = windVec.new(d0, v0);
+		append(altvec,wv);
+
+		wv = windVec.new(d1, v1);
+		append(altvec, wv);
+
+		wv = windVec.new(d2, v2);
+		append(altvec, wv);
+
+		wv = windVec.new(d3, v3);
+		append(altvec, wv);
+
+		wv = windVec.new(d4, v4);
+		append(altvec, wv);
+
+		wv = windVec.new(d5, v5);
+		append(altvec, wv);
+
+		wv = windVec.new(d6, v6);
+		append(altvec, wv);
+
+		wv = windVec.new(d7, v7);
+		append(altvec, wv);
+
+		wv = windVec.new(d8, v8);
+		append(altvec, wv);
+		
+		w.alt = altvec;
+		
+		w.weight = 0.02;
+	        return w;
+	},
+};
+
+var windVec = {
+	new: func (d, v) {
+	var wv = { parents: [windVec] };
+	wv.d = d;
+	wv.v = v;
+	return wv;
+	},
+
+};
+
+
+
+
+
+var effectVolume = {
+	new: func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat) {
+	        var e = { parents: [effectVolume] };
+		e.geometry = geometry;
+		e.lat = lat;
+		e.lon = lon;
+		e.r1 = r1;
+		e.r2 = r2;
+		e.phi = phi;
+		e.alt_low = alt_low;
+		e.alt_high = alt_high;
+		e.vis = vis;
+		e.rain = rain;
+		e.snow = snow;
+		e.turb = turb;
+		e.lift = lift;
+		e.lift_flag = lift_flag;
+		e.sat = sat;
+		return e;
+	},
+	move: func {
+		var windfield = weather_dynamics.get_windfield(me.index);
+		var dt = weather_dynamics.time_lw - me.timestamp;
+		me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
+		me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
+		me.timestamp = weather_dynamics.time_lw;
+	},
+	correct_altitude: func {	
+		var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1];
+		var elevation = compat_layer.get_elevation(me.lat, me.lon);
+		me.alt_high = local_weather.get_convective_altitude(convective_alt, elevation, me.index,0.0) *1.15;
+		me.height = me.alt_high * 0.87; 
+	},
+	correct_altitude_and_age: func {	
+		var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
+		var elevation = -1.0; var p_cover = 0.2;
+		var info = geodinfo(me.lat, me.lon);
+		if (info != nil) 
+			{
+			elevation = info[0] * local_weather.m_to_ft;
+			if (info[1] != nil)
+				{
+         			var landcover = info[1].names[0];
+	 			if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];}
+				else {p_cover = 0.2;}
+				}	
+			}
+		me.alt_high = get_convective_altitude(convective_alt, elevation, me.index,0.0) * 1.15;
+		me.height = me.alt_high * 0.87; 
+		var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s;
+		var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime;
+		me.flt = me.flt + fractional_increase;
+		me.evolution_timestamp = weather_dynamics.time_lw;
+	},
+	get_distance: func {
+		var lat = getprop("position/latitude-deg");
+		var lon = getprop("position/longitude-deg");
+		return math.sqrt(calc_d_sq(lat, lon, me.lat, me.lon));	
+	},
+};
+
+
+var thermalLift = {
+	new: func (lat, lon, radius, height, cn, sh, max_lift, f_lift_radius) {
+	        var l = { parents: [thermalLift] };
+		l.lat = lat;
+		l.lon = lon;
+		l.radius = radius;
+		l.height = height;
+		l.cn = cn;
+		l.sh = sh;
+		l.max_lift = max_lift;
+		l.f_lift_radius = f_lift_radius;
+		return l;
+	},
+	move: func {
+		var windfield = weather_dynamics.get_windfield(me.index);
+		var dt = weather_dynamics.time_lw - me.timestamp;
+		me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
+		me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
+		me.timestamp = weather_dynamics.time_lw;
+	},
+	correct_altitude: func {	
+		var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1];
+		var elevation = compat_layer.get_elevation(me.lat, me.lon);
+		me.height = local_weather.get_convective_altitude(convective_alt, elevation, me.index,0.0);
+	},
+	correct_altitude_and_age: func {	
+		var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
+		var elevation = -1.0; var p_cover = 0.2;
+		var info = geodinfo(me.lat, me.lon);
+		if (info != nil) 
+			{
+			elevation = info[0] * local_weather.m_to_ft;
+			if (info[1] != nil)
+				{
+         			var landcover = info[1].names[0];
+	 			if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];}
+				else {p_cover = 0.2;}
+				}	
+			}
+		me.height = get_convective_altitude(convective_alt, elevation, me.index,0.0);
+		var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s;
+		var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime;
+		me.flt = me.flt + fractional_increase;
+		me.evolution_timestamp = weather_dynamics.time_lw;
+	},
+
+};
+
+
+var waveLift = {
+	new: func (lat, lon, x, y, phi, height, max_lift) {
+		var w = { parents: [waveLift] };
+		w.lat = lat;
+		w.lon = lon;
+		w.x = x;
+		w.y = y;
+		w.phi = phi;
+		w.height = height;
+		w.max_lift = max_lift;
+		w.phi = getprop(lw~"tmp/tile-orientation-deg");
+		return w;
+	},
+
+};
+
+
+
+#################################################################
+# global variable, property creation and the startup listener
+#################################################################
+
+var rad_E = 6378138.12;	# earth radius
+var lat_to_m = 110952.0; # latitude degrees to meters
+var m_to_lat = 9.01290648208234e-06; # meters to latitude degrees
+var ft_to_m = 0.30480;
+var m_to_ft = 1.0/ft_to_m;
+
+var lon_to_m = 0.0; # needs to be calculated dynamically
+var m_to_lon = 0.0; # we do this on startup
+
+# some common abbreviations
+
+var lw = "/local-weather/";
+var lwi = "/local-weather/interpolation/";
+var ec = "/environment/config/";
+
+# a hash map of the strength for convection associated with terrain types
+
+var landcover_map = {BuiltUpCover: 0.35, Town: 0.35, Freeway:0.35, BarrenCover:0.3, HerbTundraCover: 0.25, GrassCover: 0.2, CropGrassCover: 0.2, EvergreenBroadCover: 0.2, EvergreenNeedleCover: 0.2, Sand: 0.25, Grass: 0.2, Ocean: 0.01, Marsh: 0.05, Lake: 0.01, ShrubCover: 0.15, Landmass: 0.2, CropWoodCover: 0.15, MixedForestCover: 0.15, DryCropPastureCover: 0.25, MixedCropPastureCover: 0.2, IrrCropPastureCover: 0.15, DeciduousBroadCover: 0.1, DeciduousNeedleCover: 0.1, Bog: 0.05, pa_taxiway : 0.35, pa_tiedown: 0.35, pc_taxiway: 0.35, pc_tiedown: 0.35, Glacier: 0.03, SnowCover: 0.04, DryLake: 0.3, IntermittentStream: 0.2, DryCrop: 0.2, Lava: 0.3, GolfCourse: 0.2, Rock: 0.3, Construction: 0.35};
+
+# a hash map of average vertical cloud model sizes
+
+var cloud_vertical_size_map = {Altocumulus: 700.0, Cumulus: 600.0, Congestus: 2000.0, Nimbus: 1000.0, Stratus: 800.0, Stratus_structured: 600.0, Stratus_thin: 400.0, Cirrocumulus: 200.0, Cb_box: 2000.0};
+
+# a hash map of offsets for the new cloud rendering system
+
+var offset_map = {Nimbus: 2800.0, Stratus: 2000.0, Stratus_thin: 2500.0, Cirrostratus: 4500.0, Stratus_structured: 1800.0, Stratus_alt: 600.0, Cumulus: 200.0, Congestus: 600.0 };
+
+# the array of aloft wind interpolation altitudes
+
+var wind_altitude_array = [0.0, 5000.0, 10000.0, 18000.0, 24000.0, 30000.0, 34000.0, 39000.0, 45000.0];
+
+# storage arrays for cloud generation
+
+var clouds_path = [];
+var clouds_lat = [];
+var clouds_lon = [];
+var clouds_alt = [];
+var clouds_orientation = [];
+
+
+
+
+# storage array for assembled clouds
+
+var cloudAssemblyArray = [];
+
+# additional info needed for dynamical clouds: the base altitude around which cloudlets are distributed
+# and the fractional lifetime
+
+var clouds_mean_alt = [];
+var clouds_flt = [];
+var clouds_evolution_timestamp = [];
+
+
+# storage arrays for terrain presampling and results by tile
+
+var terrain_n = [];
+var alt_50_array = [];
+var alt_20_array = [];
+var alt_min_array = [];
+var alt_mean_array = [];
+
+# array of currently existing effect volumes
+
+var effectVolumeArray = [];
+var n_effectVolumeArray = 0;
+
+# the thermal and the wave hash
+
+var thermal = {};
+var wave = {};
+
+# the wind hash stores the current winds
+
+var wind = {surface: [0.0,0.0] , cloudlayer: [0.0,0.0], current: [0.0,0.0]};
+
+
+
+# arrays of currently existing weather stations, wind interpolation and atmospheric condition points
+
+var weatherStationArray = [];
+var windIpointArray = [];
+var atmosphereIpointArray = [];
+
+
+# a flag for the wind model (so we don't have to do string comparisons all the time)
+# 1: constant 2: constant in tile 3: aloft interpolated 4: airmass interpolated
+
+var wind_model_flag = 1;
+
+# globals governing properties of the Cumulus system
+
+var convective_texture_mix = 0.0;
+var convective_size_bias = 0.0;
+var cumulus_efficiency_factor = 1.0;
+var cloud_mean_altitude = 0.0;
+
+# globals keeping track of the lifetime when building a Cumulus from individual cloudlets
+
+var cloud_fractional_lifetime = 0.0;
+var cloud_evolution_timestamp = 0.0;
+
+# globals propagating gust information inside the interpolation loop
+
+var windspeed_multiplier = 1.0;
+var winddir_change = 0.0;
+
+# global flags mirroring property tree menu settings
+
+var generate_thermal_lift_flag = 0;
+var thread_flag = 1;
+var dynamics_flag = 1;
+var presampling_flag = 1;
+var detailed_clouds_flag = 1;
+var dynamical_convection_flag = 1;
+var debug_output_flag = 1;
+var metar_flag = 0;
+var local_weather_running_flag = 0;
+var local_weather_startup_flag = 0;
+var fps_control_flag = 0;
+var buffer_flag = 1;
+var detailed_terrain_interaction_flag = 1;
+var hardcoded_clouds_flag = 1;
+var realistic_visibility_flag = 0;
+var scattering_shader_flag = 0;
+
+var ground_haze_factor = 1.0;
+var max_vis_range = 120000.0;
+var temperature_offset = 0.0;
+
+
+# globals for framerate controlled cloud management
+
+var fps_average = 0.0;
+var fps_samples = 0;
+var fps_sum = 0.0;
+var target_framerate = 25.0;
+
+# set all sorts of default properties for the menu
+
+
+setprop(lw~"tmp/scloud-lat",getprop("position/latitude-deg"));
+setprop(lw~"tmp/scloud-lon",getprop("position/longitude-deg"));
+setprop(lw~"tmp/tile-alt-median-ft",0.0);
+setprop(lw~"tmp/tile-alt-min-ft",0.0);
+setprop(lw~"tmp/last-reading-pos-del",0);
+setprop(lw~"tmp/last-reading-pos-mod",0);
+setprop(lw~"tmp/thread-status", "idle");
+setprop(lw~"tmp/convective-status", "idle");
+setprop(lw~"tmp/presampling-status", "idle");
+setprop(lw~"tmp/buffer-status", "idle");
+setprop(lw~"tmp/buffer-tile-index", 0);
+setprop(lw~"tmp/ipoint-latitude-deg",getprop("position/latitude-deg"));
+setprop(lw~"tmp/ipoint-longitude-deg",getprop("position/longitude-deg"));
+
+
+
+# set the default loop flags to loops inactive
+
+
+setprop(lw~"effect-loop-flag",0);
+setprop(lw~"interpolation-loop-flag",0);
+setprop(lw~"tile-loop-flag",0);
+setprop(lw~"lift-loop-flag",0);
+setprop(lw~"wave-loop-flag",0);
+setprop(lw~"buffer-loop-flag",0);
+setprop(lw~"housekeeping-loop-flag",0);
+setprop(lw~"convective-loop-flag",0);
+
+# create other management properties
+
+#setprop(lw~"clouds/cloud-number",0);
+setprop(lw~"clouds/placement-index",0);
+setprop(lw~"clouds/model-placement-index",0);
+setprop(lw~"effect-volumes/effect-placement-index",0);
+
+# create properties for effect volume management
+
+setprop(lw~"effect-volumes/number",0);
+setprop(lw~"effect-volumes/number-active-vis",0);
+setprop(lw~"effect-volumes/number-active-rain",0);
+setprop(lw~"effect-volumes/number-active-snow",0);
+setprop(lw~"effect-volumes/number-active-turb",0);
+setprop(lw~"effect-volumes/number-active-lift",0);
+setprop(lw~"effect-volumes/number-active-sat",0);
+
+setprop(lw~"config/max-vis-range-m", 120000.0);
+setprop(lw~"config/temperature-offset-degc", 0.0);
+
+# create properties for tile management
+
+setprop(lw~"tiles/tile-counter",0);
+
+# create properties for wind
+
+setprop(lwi~"ipoint-number",0);
+
+var updateMenu = func {
+	var isEnabled = getprop("/nasal/local_weather/enabled");
+	gui.menuEnable("local_weather", isEnabled);
+	gui.menuEnable("local_weather_tiles", isEnabled);
+}
+
+_setlistener("/nasal/local_weather/enabled", updateMenu);
+
+
diff --git a/Nasal/local_weather/cloud_definitions.nas b/Nasal/local_weather/cloud_definitions.nas
index c85db3a3c..495fd4f6b 100644
--- a/Nasal/local_weather/cloud_definitions.nas
+++ b/Nasal/local_weather/cloud_definitions.nas
@@ -485,7 +485,7 @@ else if (type == "Stratocumulus bottom"){
 		{
 		cloudAssembly = local_weather.cloud.new(type, subtype);
 
-		mult = 1.0;
+
 
 		# characterize the basic texture sheet
 		cloudAssembly.texture_sheet = "/Models/Weather/cumulus_bottom_sheet1.rgb";
@@ -520,6 +520,46 @@ else if (type == "Stratocumulus bottom"){
 	
 	}
 else if (type == "Cumulonimbus (cloudlet)"){
+
+# new code
+	
+	if (local_weather.hardcoded_clouds_flag == 1)
+		{
+		cloudAssembly = local_weather.cloud.new(type, subtype);
+
+	# characterize the basic texture sheet
+				
+
+		cloudAssembly.num_tex_x = 2;
+		cloudAssembly.num_tex_y = 2;
+		
+		if (rand() < 0.5)
+			{cloudAssembly.texture_sheet = "/Models/Weather/cumulonimbus_sheet2.rgb";}			
+		else
+			{cloudAssembly.texture_sheet = "/Models/Weather/cumulonimbus_sheet1.rgb";}
+		
+		var mult = 1.0;
+		if (subtype == "small") {mult = 0.7;}
+
+		#characterize the cloud
+		cloudAssembly.bottom_shade = 0.6;
+		cloudAssembly.n_sprites = 5;			
+		cloudAssembly.min_width = 1700.0 * mult;
+		cloudAssembly.max_width = 2200.0 * mult;
+		cloudAssembly.min_height = 1700.0 * mult;
+		cloudAssembly.max_height = 2200.0 * mult;
+		cloudAssembly.min_cloud_width = 3500.0 * mult;
+		cloudAssembly.min_cloud_height = 3500.0 * mult;
+		cloudAssembly.z_scale = 1.0;
+
+		#signal that new routines are used
+		path = "new";
+
+		}
+	else
+		{
+
+
 	if (subtype == "small") {
 		if (rn > 0.875) {path = "Models/Weather/cumulonimbus_sl1.xml";}
 		else if (rn > 0.75) {path = "Models/Weather/cumulonimbus_sl2.xml";}
@@ -540,6 +580,7 @@ else if (type == "Cumulonimbus (cloudlet)"){
 		else if (rn > 0.125) {path = "Models/Weather/cumulonimbus_sl7.xml";}
 		else  {path = "Models/Weather/cumulonimbus_sl8.xml";}
 		}
+		}
 	
 	}
 
@@ -885,24 +926,34 @@ else if (type == "Stratus") {
 		{
 		cloudAssembly = local_weather.cloud.new(type, subtype);
 
-		if (subtype == "small") {var mult = 0.8;}
-		else {var mult = 1.0;}
-
-		# characterize the basic texture sheet
-		cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb";
-		cloudAssembly.num_tex_x = 3;
-		cloudAssembly.num_tex_y = 2;
+		if (subtype == "small") 
+			{
+			var mult = 0.8;
+			cloudAssembly.texture_sheet = "/Models/Weather/cirrocumulus_sheet1.rgb";
+			cloudAssembly.num_tex_x = 3;
+			cloudAssembly.num_tex_y = 3;
+			cloudAssembly.n_sprites = 10;
+			cloudAssembly.z_scale = 0.6;
+			}
+		else 	
+			{
+			var mult = 1.0;
+			cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb";
+			cloudAssembly.num_tex_x = 3;
+			cloudAssembly.num_tex_y = 2;
+			cloudAssembly.n_sprites = 10;
+			cloudAssembly.z_scale = 0.4;
+			}
 	
 		#characterize the cloud
 		cloudAssembly.bottom_shade = 0.4;
-		cloudAssembly.n_sprites = 20;
-		cloudAssembly.min_width = 1900.0 * mult;
-		cloudAssembly.max_width = 2400.0 * mult;
-		cloudAssembly.min_height = 1900.0 * mult;
-		cloudAssembly.max_height = 2400.0 * mult;
-		cloudAssembly.min_cloud_width = 5000.0 * mult;
+		cloudAssembly.min_width = 2000.0 * mult;
+		cloudAssembly.max_width = 2500.0 * mult;
+		cloudAssembly.min_height = 2000.0 * mult;
+		cloudAssembly.max_height = 2500.0 * mult;
+		cloudAssembly.min_cloud_width = 5000.0;
 		cloudAssembly.min_cloud_height = 1.1 *  cloudAssembly.max_height;
-		cloudAssembly.z_scale = 0.4;
+
 
 		#signal that new routines are used
 		path = "new";
@@ -957,11 +1008,11 @@ else if (type == "Stratus (thin)") {
 	
 		#characterize the cloud
 		cloudAssembly.bottom_shade = 0.8;
-		cloudAssembly.min_width = 1500.0 * mult;
-		cloudAssembly.max_width = 2000.0 * mult;
-		cloudAssembly.min_height = 1500.0 * mult;
-		cloudAssembly.max_height = 2000.0 * mult;
-		cloudAssembly.min_cloud_width = 2500.0;
+		cloudAssembly.min_width = 1900.0 * mult;
+		cloudAssembly.max_width = 2400.0 * mult;
+		cloudAssembly.min_height = 1900.0 * mult;
+		cloudAssembly.max_height = 2400.0 * mult;
+		cloudAssembly.min_cloud_width = 4200.0;
 		cloudAssembly.min_cloud_height = 50.0;
 
 
@@ -1055,6 +1106,49 @@ else if (type == "Fog (thin)") {
 		}	
 	}
 else if (type == "Fog (thick)") {
+
+	# new code
+
+	if (local_weather.hardcoded_clouds_flag ==1)
+		{
+		cloudAssembly = local_weather.cloud.new(type, subtype);
+
+		if (subtype == "small") 
+			{
+			var mult = 0.8;
+			cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb";
+			cloudAssembly.num_tex_x = 3;
+			cloudAssembly.num_tex_y = 2;
+			cloudAssembly.n_sprites = 5;
+			cloudAssembly.z_scale = 1.0;
+			}
+		else 	
+			{
+			var mult = 1.0;
+			cloudAssembly.texture_sheet = "/Models/Weather/stratus_sheet1.rgb";
+			cloudAssembly.num_tex_x = 3;
+			cloudAssembly.num_tex_y = 2;
+			cloudAssembly.n_sprites = 5;
+			cloudAssembly.z_scale = 1.0;
+			}
+	
+		#characterize the cloud
+		cloudAssembly.bottom_shade = 0.4;
+		cloudAssembly.min_width = 2000.0 * mult;
+		cloudAssembly.max_width = 2500.0 * mult;
+		cloudAssembly.min_height = 2000.0 * mult;
+		cloudAssembly.max_height = 2500.0 * mult;
+		cloudAssembly.min_cloud_width = 5000.0;
+		cloudAssembly.min_cloud_height = 1.1 *  cloudAssembly.max_height;
+
+
+		#signal that new routines are used
+		path = "new";
+		}
+
+	else # old code
+		{
+
 	if (subtype == "small") {
 		if (rn > 0.8) {path = "Models/Weather/stratus_thick1.xml";}
 		else if (rn > 0.6) {path = "Models/Weather/stratus_thick2.xml";}
@@ -1068,6 +1162,7 @@ else if (type == "Fog (thick)") {
 		else if (rn > 0.4) {path = "Models/Weather/stratus_thick3.xml";}
 		else if (rn > 0.2) {path = "Models/Weather/stratus_thick4.xml";}
 		else  {path = "Models/Weather/stratus_thick5.xml";}
+		}
 		}	
 	}
 else if (type == "Test") {path="Models/Weather/single_cloud.xml";}
diff --git a/Nasal/local_weather/compat_layer.nas b/Nasal/local_weather/compat_layer.nas
index 4038a9a0e..a0b4e985d 100644
--- a/Nasal/local_weather/compat_layer.nas
+++ b/Nasal/local_weather/compat_layer.nas
@@ -170,7 +170,7 @@ else
 
 if (local_weather.hardcoded_clouds_flag == 1) 
 	{
-	# we store that information ourselves, so this should be zero
+	# we store that information ourselves, so this should be zero, but rain forces us to go for an offset
 	setprop("/environment/clouds/layer[0]/elevation-ft",0.0);
 		
 	# layer wrapping off
@@ -347,6 +347,7 @@ else
 
 var setTemperature = func (T) {
 
+
 if (features.can_disable_environment == 1)
 	{
 	setprop("/environment/temperature-sea-level-degc",T);
@@ -503,6 +504,14 @@ if (features.can_disable_environment == 1)
 	setprop("/environment/wind-from-heading-deg",dir);
 	setprop("/environment/wind-speed-kt",speed);
 	}
+
+# this is needed to trigger the cloud drift to pick up the new wind setting
+if (local_weather.hardcoded_clouds_flag == 1)
+	{
+	setprop("/environment/clouds/layer[0]/elevation-ft",0.0);
+	}
+
+
 else
 	{
 	# this is a workaround for systems which lack hard-coded support
@@ -767,7 +776,10 @@ var p = props.Node.new({ "layer" : 0,
 			 "min-sprite-height-m": c.min_height,
 			 "max-sprite-height-m": c.max_height,
 			 "num-sprites": c.n_sprites,
-			 "bottom-shade": c.bottom_shade,
+			 "min-bottom-lighting-factor": c.bottom_shade,
+			 "min-middle-lighting-factor": 0.9,
+			 "min-top-lighting-factor": 1.0,
+			 "min-shade-lighting-factor": c.bottom_shade,
 			 "texture": c.texture_sheet,
 			 "num-textures-x": c.num_tex_x,
 			 "num-textures-y": c.num_tex_y,
@@ -777,10 +789,10 @@ var p = props.Node.new({ "layer" : 0,
 			 "max-cloud-height-m": c.min_cloud_height,	
 			 "z-scale": c.z_scale,
 			 "height-map-texture": 0,
-                         "alt-ft" :  c.alt});
+                         "alt-ft" :  c.alt });
 fgcommand("add-cloud", p);
 
-# print("alt: ", c.alt);
+#print("alt: ", c.alt);
 
 # add other management properties to the hash if dynamics is on
 
@@ -799,6 +811,10 @@ append(weather_tile_management.cloudArray,c);
 
 
 
+
+
+
+
 ###########################################################
 # place a cloud layer from arrays, split across frames 
 ###########################################################
@@ -811,13 +827,22 @@ if ((i < 0) or (i==0))
 	{
 	if (local_weather.debug_output_flag == 1) 
 		{print("Cloud placement from array finished!"); }
+
+	# then place all clouds using the new rendering system
+	if (local_weather.hardcoded_clouds_flag == 1)
+		{
+		var s = size(local_weather.cloudAssemblyArray);
+		create_new_cloud_array(s,cloudAssemblyArray);
+		}
+	
 	setprop(lw~"tmp/thread-status", "idle");
 
 	# now set flag that tile has been completely processed
 	var dir_index = props.globals.getNode(lw~"tiles/tmp/dir-index").getValue();
 
-	props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("generated-flag").setValue(2);
-	
+	#props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("generated-flag").setValue(2);
+	setprop(lw~"tiles/tile["~dir_index~"]/generated-flag",2);	
+
 	return;
 	}
 
@@ -856,9 +881,42 @@ settimer( func {create_cloud_array(i - k, clouds_path, clouds_lat, clouds_lon, c
 };
 
 
+var create_new_cloud_array = func (i, cloudArray)
+{
 
 
 
+#if (getprop(lw~"tmp/thread-status") != "placing") {return;}
+#if (getprop(lw~"tmp/convective-status") != "idle") {return;}
+
+if ((i < 0) or (i==0)) 
+	{
+	if (local_weather.debug_output_flag == 1) 
+		{print("Processing add-cloud calls finished!"); }
+	return;
+	}
+
+#print("Hello world! i is now: ",i);
+
+var k_max = 20;
+var s = size(cloudArray);  
+
+if (s < k_max) {k_max = s;}
+
+for (var k = 0; k < k_max; k = k+1)
+	{
+	local_weather.create_cloud_new(cloudArray[s-k-1]);
+	#print(cloudArray[s-k-1].alt);
+	}
+
+setsize(cloudArray,s-k_max);
+
+
+
+settimer( func {create_new_cloud_array(i - k, cloudArray) }, 0 );
+}
+
+
 
 
 
diff --git a/Nasal/local_weather/local_weather.nas b/Nasal/local_weather/local_weather.nas
index 185945f77..e6548b40f 100644
--- a/Nasal/local_weather/local_weather.nas
+++ b/Nasal/local_weather/local_weather.nas
@@ -410,8 +410,8 @@ else
 
 if (debug_output_flag == 1)
 	{
-	print("Boundary layer thickness: ",base_layer_thickness);
-	print("Boundary layer slowdown: ", f_slow);
+	#print("Boundary layer thickness: ",base_layer_thickness);
+	#print("Boundary layer slowdown: ", f_slow);
 	}
 return f_slow;
 }
@@ -509,8 +509,8 @@ setprop(lwi~"station-number", i+1);
 var ialt = sum_alt/sum_norm;
 var vis = sum_vis/sum_norm;
 var p = sum_p/sum_norm;
-var D = sum_D/sum_norm;
-var T = sum_T/sum_norm;
+var D = sum_D/sum_norm + temperature_offset;
+var T = sum_T/sum_norm + temperature_offset;
 
 
 # get an inverse distance weighted average from all defined atmospheric condition points
@@ -586,6 +586,7 @@ var scatt_alt_high = sum_scatt_alt_high/sum_norm;
 
 # altitude model for visibility - increase above the lowest inversion layer to simulate ground haze
 
+vis = vis * ground_haze_factor;
 
 var altitude = getprop("position/altitude-ft");
 current_mean_terrain_elevation = ialt;
@@ -593,6 +594,8 @@ current_mean_terrain_elevation = ialt;
 var alt1 = vis_alt1;
 var alt2 = alt1 + 1500.0;
 
+setprop("/environment/ground-visibility-m",vis);
+setprop("/environment/ground-haze-thickness-m",alt2 * ft_to_m);
 
 # compute the visibility gradients
 
@@ -628,10 +631,10 @@ else if (altitude > ovcst_alt_high)
 	vis = vis + inc1 * alt1 + inc2 * (alt2-alt1)  + inc3 * (ovcst_alt_high - alt2) + inc4 * (altitude - ovcst_alt_high);
 	}
 
-# limit visibility (otherwise memory consumption is very bad...)
+# limit visibility (otherwise memory consumption may be very bad...)
 
-if (vis > 120000.0)
-	{vis = 120000.0;}
+if (vis > max_vis_range)
+	{vis = max_vis_range;}
 
 # determine scattering shader parameters if scattering shader is on
 
@@ -641,6 +644,8 @@ if (scattering_shader_flag == 1)
 	var mie = 0.003;
 	var density = 0.3;
 
+
+	
 	if (altitude < 30000.0) 
 		{
 		rayleigh = 0.0004 - altitude/30000.0 * 0.0001;
@@ -660,37 +665,37 @@ if (scattering_shader_flag == 1)
 		{rayleigh = 0.0001; mie = 0.002;}
 	}
 # otherwise compute normal skydome shader parameters
-else
+
+	
+
+# compute the horizon shading
+
+if (altitude < scatt_alt_low)
 	{
-
-	# compute the horizon shading
-
-	if (altitude < scatt_alt_low)
-		{
-		var scatt = scatt_max;
-		}
-	else if (altitude < scatt_alt_high)
-		{
-		var scatt = scatt_max + (0.95 - scatt_max) * (altitude - scatt_alt_low)/(scatt_alt_high-scatt_alt_low);
-		}
-	else
-		{var scatt = 0.95;}
+	var scatt = scatt_max;
+	}
+else if (altitude < scatt_alt_high)
+	{
+	var scatt = scatt_max + (0.95 - scatt_max) * (altitude - scatt_alt_low)/(scatt_alt_high-scatt_alt_low);
+	}
+else
+	{var scatt = 0.95;}
 
 
 # compute the overcast haze
 
-	if (altitude < ovcst_alt_low)
-		{
-		var ovcst = ovcst_max;
-		}
-	else if (altitude < ovcst_alt_high)
-		{
-		var ovcst = ovcst_max - ovcst_max * (altitude - ovcst_alt_low)/(ovcst_alt_high-ovcst_alt_low);
-		}
-	else
-		{var ovcst = 0.0;}
-
+if (altitude < ovcst_alt_low)
+	{
+	var ovcst = ovcst_max;
+	}	
+else if (altitude < ovcst_alt_high)
+	{
+	var ovcst = ovcst_max - ovcst_max * (altitude - ovcst_alt_low)/(ovcst_alt_high-ovcst_alt_low);
 	}
+else
+	{var ovcst = 0.0;}
+
+
 
 
 # limit relative changes of the visibility, will make for gradual transitions
@@ -717,11 +722,10 @@ if (scattering_shader_flag == 1)
 	{
 	local_weather.setSkydomeShader(rayleigh, mie, density);
 	}
-else
-	{
-	local_weather.setScattering(scatt);
-	local_weather.setOvercast(ovcst);
-	}
+
+local_weather.setScattering(scatt);
+local_weather.setOvercast(ovcst);
+	
 
 # now check if an effect volume writes the property and set only if not
 
@@ -766,18 +770,24 @@ if (p>0.0)
 
 # now determine the local wind 
 
-#var tile_index = props.globals.getNode(lw~"tiles").getChild("tile",4).getNode("tile-index").getValue();
+
 var tile_index = getprop(lw~"tiles/tile[4]/tile-index");
 
 if (wind_model_flag ==1) # constant
 	{
 	var winddir = weather_dynamics.tile_wind_direction[0];
 	var windspeed = weather_dynamics.tile_wind_speed[0];
+
+	wind.cloudlayer = [winddir,windspeed];
+
 	}
 else if (wind_model_flag ==2) # constant in tile
 	{
 	var winddir = weather_dynamics.tile_wind_direction[tile_index-1];
 	var windspeed = weather_dynamics.tile_wind_speed[tile_index-1];
+
+	wind.cloudlayer = [winddir, windspeed];
+
 	}	
 else if (wind_model_flag ==3) # aloft interpolated, constant in tiles
 	{
@@ -785,6 +795,9 @@ else if (wind_model_flag ==3) # aloft interpolated, constant in tiles
 	var res = wind_altitude_interpolation(altitude,w);
 	var winddir = res[0];
 	var windspeed = res[1];
+
+	wind.cloudlayer = wind_altitude_interpolation(0.0,w);
+
 	}
 else if (wind_model_flag == 5) # aloft waypoint interpolated
 	{
@@ -792,9 +805,13 @@ else if (wind_model_flag == 5) # aloft waypoint interpolated
 
 	var winddir = res[0];
 	var windspeed = res[1];
+
+	wind.cloudlayer = wind_interpolation(viewpos.lat(), viewpos.lon(), 0.0);	
 	}
 
 
+wind.surface = [wind.cloudlayer[0], wind.cloudlayer[1] * get_slowdown_fraction()];
+
 # now do the boundary layer computations
 
 var altitude_agl = getprop("/position/altitude-agl-ft");
@@ -843,6 +860,15 @@ else
 	}
 
 
+var windspeed_ground = (1.0-f_min) * windspeed;
+
+
+# set the wind hash before gusts, it represents mean wind
+
+wind.current = [winddir,windspeed_current];
+
+
+
 # determine gusts and turbulence in the bounday layer
 
 var gust_frequency = getprop(lw~"tmp/gust-frequency-hz");
@@ -897,6 +923,13 @@ setprop(lw~"current/wind-from-heading-deg",winddir);
 setprop(lw~"current/wind-speed-kt",windspeed_current);
 
 
+# hack to get access to the water shader
+
+setprop("/environment/config/boundary/entry[0]/wind-from-heading-deg",winddir);
+setprop("/environment/config/boundary/entry[0]/wind-speed-kt",windspeed_ground);
+
+# end hack
+
 
 if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, interpolation_loop_time);}
 
@@ -1472,14 +1505,18 @@ if (path == "new") # we have to switch to new cloud generating routines
 	local_weather.cloudAssembly.lon = long;
 	local_weather.cloudAssembly.alt = alt;	
 
+	#print(lat," ",long, " ", alt);
+
 	if (dynamics_flag == 1)
 		{
 		local_weather.cloudAssembly.mean_alt = cloud_mean_altitude;
 		local_weather.cloudAssembly.flt = cloud_fractional_lifetime;
 		local_weather.cloudAssembly.evolution_timestamp = cloud_evolution_timestamp;
-		local_weather.cloudAssembly.rel_alt = c.alt - c.mean_alt;
+		local_weather.cloudAssembly.rel_alt = cloudAssembly.alt - cloud_mean_altitude;
 		}
-	compat_layer.create_cloud_new(local_weather.cloudAssembly);	
+	#compat_layer.create_cloud_new(local_weather.cloudAssembly);	
+
+	append(cloudAssemblyArray,cloudAssembly);
 
 	return;
 	}
@@ -1736,7 +1773,7 @@ else
 		var alpha = rand() * 180.0;
 		edge = edge + edge_bias;		
 
-		create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
 
 		var type = "Cu (volume)";
 		var height = 400;
@@ -1747,12 +1784,12 @@ else
 		
 		edge = edge + edge_bias;		
 
-		create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
 
 		var btype = "Congestus bottom";
 		var n_b = 6;
 
-		create_streak(btype,lat,lon, alt - 1100.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
+		create_streak(btype,lat,lon, alt -offset_map["Congestus"] -200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
 
 		}
 	else if (size>1.1)
@@ -1768,9 +1805,9 @@ else
 
 		var alpha = rand() * 180.0;
 		edge = edge + edge_bias;
-		create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
 
-		create_streak(btype,lat,lon, alt -600.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
+		create_streak(btype,lat,lon, alt -offset_map["Cumulus"] - 200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
 
 		}
 	else if (size>0.8)
@@ -1784,13 +1821,13 @@ else
 
 		var alpha = rand() * 180.0;
 		edge = edge + edge_bias;
-		create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
 
 		n = 2;
 		x = 700.0;
 		y = 200.0;
 		edge = 1.0;
-		create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
 		}
 
 	else if (size>0.4)
@@ -1804,7 +1841,7 @@ else
 
 		var alpha = rand() * 180.0;
 		edge = edge + edge_bias;
-		create_streak(type,lat,lon, alt+ 0.3* (height)-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		create_streak(type,lat,lon, alt+ 0.3* (height)-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
 		}
 	else 
 		{
@@ -1817,7 +1854,7 @@ else
 
 		var alpha = rand() * 180.0;
 		edge = edge + edge_bias;
-		create_streak(type,lat,lon, alt+ 0.3* (height )-600.0, height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
+		create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
 		}
 
 	}
@@ -1833,7 +1870,7 @@ else
 var create_cumulonimbus_cloud = func(lat, lon, alt, size) {
 
 if (hardcoded_clouds_flag == 1)
-	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.9, 0.2, 8);}
+	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);}
 else
 	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);}
 
@@ -1848,7 +1885,7 @@ else
 var create_cumulonimbus_cloud_rain = func(lat, lon, alt, size, rain) {
 
 if (hardcoded_clouds_flag == 1)
-	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.9, 0.2, 8);}
+	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 3.4, 8, 0.8, 0.1, 8);}
 else
 	{create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,14, 0.2, 0.1, 1.4, 4, 0.9, 0.2, 8);}
 
@@ -1939,18 +1976,21 @@ var detail_flag = detailed_clouds_flag;
 
 var alpha = getprop(lw~"tmp/tile-orientation-deg") * math.pi/180.0; # the tile orientation
 
-if (detailed_terrain_interaction_flag == 1)
-	{
-	var tile_index = getprop(lw~"tiles/tile-counter");
+var tile_index = getprop(lw~"tiles/tile-counter");
+var alt_base = alt_20_array[tile_index -1];
+
+#if (detailed_terrain_interaction_flag == 1)
+#	{
+	#var tile_index = getprop(lw~"tiles/tile-counter");
 	#var alt_min = alt_min_array[tile_index-1];
 	#var alt_mean = alt_mean_array[tile_index -1];
 	#var alt_median = alt_50_array[tile_index -1];
-	#var alt_base = alt_20_array[tile-index -1];
+	#var alt_base = alt_20_array[tile_index -1];
 	#var alt_min = getprop(lw~"tmp/tile-alt-min-ft");
 	#var alt_mean = getprop(lw~"tmp/tile-alt-mean-ft");
 	#var alt_median = getprop(lw~"tmp/tile-alt-median-ft");
 	#var alt_base = getprop(lw~"tmp/tile-alt-offset-ft");
-	}
+#	}
 
 var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime
 
@@ -2003,7 +2043,13 @@ while (i < nc) {
 	 if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];}
 	 else {print(p, " ", info[1].names[0]);}
 	}}
-	else {continue;}
+	else {
+		# to avoid gaps, we create default clouds
+
+		p = p + 0.1;		
+		var elevation = alt_base;
+		# continue;
+		}
 
 
 	# apply some optional corrections, biases clouds towards higher elevations
@@ -2056,8 +2102,10 @@ while (i < nc) {
 				}
 
 	
-		# now decide on the strength of the thermal
-		strength = (1.5 * rand() + (2.0 * p * terrain_strength_factor)) * t_factor2; # the strength of thermal activity at the spot
+		# now decide on the strength of the thermal at the spot and on cloud size
+
+		var rn = rand();
+		strength = (1.5 * rn + (2.0 * p * terrain_strength_factor)) * t_factor2;  
 		if (strength > 1.0)  
 			{
 			# we place a large cloud, and we generate lift
@@ -2065,7 +2113,15 @@ while (i < nc) {
 			}
 		else {path = select_cloud_model("Cumulus","small");}
 
-	
+		# the terrain effect cannot create Cb development, so we have to curb
+		# the strength if it would not have been Cb otherwise
+
+		if (strength > 2.0)
+			{
+			if (((1.5 * rn + (2.0 * p)) * t_factor2) < 2.0)
+				{strength = 1.7 + rand() * 0.2;}
+			}
+
 
 		cloud_mean_altitude = place_alt;
 		cloud_fractional_lifetime = rand();
@@ -2168,6 +2224,7 @@ nc = t_factor1 * nc * math.cos(blat/180.0*math.pi);
 
 var thermal_conditions = getprop(lw~"config/thermal-properties");
 
+var alt_base = alt_20_array[tile_index -1];
 
 while (i < nc) {
 
@@ -2198,7 +2255,13 @@ while (i < nc) {
 	 if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];}
 	 else {print(p, " ", info[1].names[0]);}
 	}}
-	else {continue;}
+	else {
+		# to avoid gaps, we create default clouds
+
+		p = p + 0.1;		
+		var elevation = alt_base;
+		# continue;
+		}
 
 
 	# apply some optional corrections, biases clouds towards higher elevations
@@ -2578,6 +2641,8 @@ var n = int(area/80000000.0 * 100 * density);
 var m = int(circ/63000.0 * 40 * rain_density);
 var path = "Models/Weather/blank.ac";
 
+#print("density: ",n);
+
 phi = phi * math.pi/180.0;
 
 if (contains(cloud_vertical_size_map, type)) 
@@ -2599,7 +2664,10 @@ while(i<n)
 			{
 			if (rand() > 0.4) {
 				path = select_cloud_model(type,"small");
-				compat_layer.create_cloud(path, lat, lon, alt, 0.0);
+			if (thread_flag == 1)
+				{create_cloud_vec(path, lat, lon, alt, 0.0);}
+			else
+				{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
 				}
 			}
 		else {
@@ -2617,6 +2685,8 @@ i = 0;
 
 if (rainflag ==1){
 
+if (local_weather.hardcoded_clouds_flag == 1) {balt = balt + local_weather.offset_map[type]; }
+
 	while(i<m)
 		{
 		var alpha = rand() * 2.0 * math.pi;
@@ -3513,7 +3583,7 @@ if (getprop(lw~"config/generate-thermal-lift-flag") ==1) {generate_thermal_lift_
 	else {generate_thermal_lift_flag = 0};
 
 thread_flag = getprop(lw~"config/thread-flag");
-dynamics_flag = getprop(lw~"config/dynamics-flag");
+# dynamics_flag = getprop(lw~"config/dynamics-flag");
 presampling_flag = getprop(lw~"config/presampling-flag");
 detailed_clouds_flag = getprop(lw~"config/detailed-clouds-flag");
 dynamical_convection_flag = getprop(lw~"config/dynamical-convection-flag");
@@ -3781,6 +3851,16 @@ if (dynamical_convection_flag == 1)
 		}
 	}
 
+if (detailed_terrain_interaction_flag == 1)
+	{
+	if (presampling_flag == 0)
+		{
+		print("Terrain effect needs terrain presampling to run! Aborting..."); 
+		setprop("/sim/messages/pilot", "Local weather: terrain effect needs terrain presampling to run! Aborting...");
+		return;
+		}
+	}
+
 
 # if we can do so, we switch global weather and METAR parsing in environment off at this point
 
@@ -3851,6 +3931,12 @@ if (metar_flag == 1) # the winds from current METAR are used
 	var metar_base_wind_deg = getprop("environment/metar/base-wind-dir-deg");
 	var metar_base_wind_speed = boundary_correction * getprop("environment/metar/base-wind-speed-kt");
 
+	# set the wind hash for the new scheme
+	
+	wind.cloudlayer = [metar_base_wind_deg,metar_base_wind_speed];
+	wind.surface = [metar_base_wind_deg,metar_base_wind_speed/boundary_correction];
+	wind.current = wind.surface;
+
 
 	if ((wind_model_flag == 1) or (wind_model_flag == 2))
 		{
@@ -3880,6 +3966,8 @@ if (metar_flag == 1) # the winds from current METAR are used
 	}
 else
 	{
+	setprop(lw~"tiles/tile[4]/tile-index",1);
+	var boundary_correction = get_slowdown_fraction();
 
 	if (wind_model_flag == 5) # it needs to be interpolated
 		{
@@ -3887,16 +3975,36 @@ else
 
 		append(weather_dynamics.tile_wind_direction,res[0]);
 		append(weather_dynamics.tile_wind_speed,res[1]);
+
+		# set the wind hash for the new scheme
+	
+		wind.surface = [res[0],res[1] * boundary_correction];
+		wind.cloudlayer = res;
+		wind.current = wind.surface;
+
+
 		}
 	else if (wind_model_flag == 3) # it comes from a different menu
 		{
 		append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/FL0-wind-from-heading-deg"));
 		append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/FL0-windspeed-kt"));
+		
+		# set the wind hash for the new scheme
+		wind.cloudlayer = [getprop(lw~"tmp/FL0-wind-from-heading-deg"),getprop(lw~"tmp/FL0-windspeed-kt") ];
+		wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction];
+		wind.current = wind.surface;
+
 		}
 	else # it comes from the standard menu
 		{
 		append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/tile-orientation-deg"));
 		append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/windspeed-kt"));
+
+		# set the wind hash for the new scheme
+		wind.cloudlayer = [getprop(lw~"tmp/tile-orientation-deg"),getprop(lw~"tmp/windspeed-kt")];
+		wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction];
+		wind.current = wind.surface;
+
 		}
 
 	# when the aloft wind menu is used, the lowest winds should be taken from there
@@ -3964,6 +4072,8 @@ else if (type == "Warmfront-3")
 	{weather_tiles.set_warmfront3_tile();}
 else if (type == "Warmfront-4")
 	{weather_tiles.set_warmfront4_tile();}
+else if (type == "Thunderstorms")
+	{weather_tiles.set_thunderstorms_tile();}
 else if (type == "METAR")
 	{weather_tiles.set_METAR_tile();}
 else if (type == "Altocumulus sky")
@@ -4018,11 +4128,12 @@ if (getprop(lw~"effect-loop-flag") == 0)
 
 # start weather dynamics loops if needed
 
+if (getprop(lw~"timing-loop-flag") == 0) 
+	{setprop(lw~"timing-loop-flag",1); local_weather.timing_loop();}
 
 if (dynamics_flag ==1)
 	{
-	if (getprop(lw~"timing-loop-flag") == 0) 
-	{setprop(lw~"timing-loop-flag",1); weather_dynamics.timing_loop();}
+	
 
 	if (getprop(lw~"dynamics-loop-flag") == 0) 
 		{
@@ -4042,7 +4153,6 @@ if (dynamics_flag ==1)
 
 # and start the buffer loop and housekeeping loop if needed
 
-#if (getprop(lw~"config/buffer-flag") ==1)
 if (buffer_flag == 1)
 	{
 	if (getprop(lw~"buffer-loop-flag") == 0) 
@@ -4128,8 +4238,8 @@ setprop(lw~"METAR/available-flag",1);
 
 # set listeners
 
-setlistener(lw~"tmp/thread-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation); });
-setlistener(lw~"tmp/convective-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation); });
+setlistener(lw~"tmp/thread-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation);  });
+setlistener(lw~"tmp/convective-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation);  });
 setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo);  effect_placement_loop(s); });
 setlistener(lw~"tmp/presampling-status", func {manage_presampling(); });
 
@@ -4145,6 +4255,10 @@ setlistener(lw~"config/fps-control-flag", func {fps_control_flag = getprop(lw~"c
 setlistener(lw~"config/target-framerate", func {target_framerate = getprop(lw~"config/target-framerate");});
 
 setlistener(lw~"config/small-scale-persistence", func {weather_tiles.small_scale_persistence = getprop(lw~"config/small-scale-persistence");});
+setlistener(lw~"config/ground-haze-factor", func {ground_haze_factor = getprop(lw~"config/ground-haze-factor");});
+setlistener(lw~"config/max-vis-range-m", func {max_vis_range = getprop(lw~"config/max-vis-range-m");});
+
+setlistener(lw~"config/temperature-offset-degc", func {temperature_offset = getprop(lw~"config/temperature-offset-degc");});
 
 setlistener("/sim/rendering/scattering-shader", func {scattering_shader_flag = getprop("/sim/rendering/scattering-shader"); });
 }
@@ -4179,7 +4293,7 @@ presampling_flag = 0;
 
 #var pos = geo.aircraft_position();
 
-#debug.dump(geodinfo(lat, lon));
+debug.dump(geodinfo(lat, lon));
 
 #create_cumulonimbus_cloud(lat, lon, 6000.0, 2.5);
 
@@ -4190,7 +4304,7 @@ presampling_flag = 0;
 
 #setprop("/environment/terrain/area[0]/output/valid", 0 );
 
-elttest();
+# elttest();
 
 }
 
@@ -4471,12 +4585,16 @@ var ec = "/environment/config/";
 
 # a hash map of the strength for convection associated with terrain types
 
-var landcover_map = {BuiltUpCover: 0.35, Town: 0.35, Freeway:0.35, BarrenCover:0.3, HerbTundraCover: 0.25, GrassCover: 0.2, CropGrassCover: 0.2, EvergreenBroadCover: 0.2, EvergreenNeedleCover: 0.2, Sand: 0.25, Grass: 0.2, Ocean: 0.01, Marsh: 0.05, Lake: 0.01, ShrubCover: 0.15, Landmass: 0.2, CropWoodCover: 0.15, MixedForestCover: 0.15, DryCropPastureCover: 0.25, MixedCropPastureCover: 0.2, IrrCropPastureCover: 0.15, DeciduousBroadCover: 0.1, DeciduousNeedleCover: 0.1, Bog: 0.05, pa_taxiway : 0.35, pa_tiedown: 0.35, pc_taxiway: 0.35, pc_tiedown: 0.35, Glacier: 0.03, DryLake: 0.3, IntermittentStream: 0.2, DryCrop: 0.2, Lava: 0.3, GolfCourse: 0.2};
+var landcover_map = {BuiltUpCover: 0.35, Town: 0.35, Freeway:0.35, BarrenCover:0.3, HerbTundraCover: 0.25, GrassCover: 0.2, CropGrassCover: 0.2, EvergreenBroadCover: 0.2, EvergreenNeedleCover: 0.2, Sand: 0.25, Grass: 0.2, Ocean: 0.01, Marsh: 0.05, Lake: 0.01, ShrubCover: 0.15, Landmass: 0.2, CropWoodCover: 0.15, MixedForestCover: 0.15, DryCropPastureCover: 0.25, MixedCropPastureCover: 0.2, IrrCropPastureCover: 0.15, DeciduousBroadCover: 0.1, DeciduousNeedleCover: 0.1, Bog: 0.05, pa_taxiway : 0.35, pa_tiedown: 0.35, pc_taxiway: 0.35, pc_tiedown: 0.35, Glacier: 0.03, SnowCover: 0.04, DryLake: 0.3, IntermittentStream: 0.2, DryCrop: 0.2, Lava: 0.3, GolfCourse: 0.2, Rock: 0.3, Construction: 0.35};
 
 # a hash map of average vertical cloud model sizes
 
 var cloud_vertical_size_map = {Altocumulus: 700.0, Cumulus: 600.0, Congestus: 2000.0, Nimbus: 1000.0, Stratus: 800.0, Stratus_structured: 600.0, Stratus_thin: 400.0, Cirrocumulus: 200.0, Cb_box: 2000.0};
 
+# a hash map of offsets for the new cloud rendering system
+
+var offset_map = {Nimbus: 2800.0, Stratus: 2000.0, Stratus_thin: 2500.0, Cirrostratus: 4500.0, Stratus_structured: 1800.0, Stratus_alt: 600.0, Cumulus: 200.0, Congestus: 600.0 };
+
 # the array of aloft wind interpolation altitudes
 
 var wind_altitude_array = [0.0, 5000.0, 10000.0, 18000.0, 24000.0, 30000.0, 34000.0, 39000.0, 45000.0];
@@ -4522,6 +4640,9 @@ var n_effectVolumeArray = 0;
 var thermal = {};
 var wave = {};
 
+# the wind hash stores the current winds
+
+var wind = {surface: [0.0,0.0] , cloudlayer: [0.0,0.0], current: [0.0,0.0]};
 
 
 
@@ -4573,6 +4694,11 @@ var hardcoded_clouds_flag = 1;
 var realistic_visibility_flag = 0;
 var scattering_shader_flag = 0;
 
+var ground_haze_factor = 1.0;
+var max_vis_range = 120000.0;
+var temperature_offset = 0.0;
+
+
 # globals for framerate controlled cloud management
 
 var fps_average = 0.0;
@@ -4628,6 +4754,8 @@ setprop(lw~"effect-volumes/number-active-turb",0);
 setprop(lw~"effect-volumes/number-active-lift",0);
 setprop(lw~"effect-volumes/number-active-sat",0);
 
+setprop(lw~"config/max-vis-range-m", 120000.0);
+setprop(lw~"config/temperature-offset-degc", 0.0);
 
 # create properties for tile management
 
diff --git a/Nasal/local_weather/weather_dynamics.nas b/Nasal/local_weather/weather_dynamics.nas
index 8adc598de..49ff8dae2 100644
--- a/Nasal/local_weather/weather_dynamics.nas
+++ b/Nasal/local_weather/weather_dynamics.nas
@@ -27,7 +27,19 @@
 
 var get_windfield = func (tile_index) {
 
-var windfield = [];
+
+if (hardcoded_clouds_flag == 1)
+	{
+	var wind_direction = local_weather.wind.current[0];
+	var windspeed = local_weather.wind.current[1] * kt_to_ms;
+
+	var windfield_x = -windspeed * math.sin(wind_direction * math.pi/180.0);
+	var windfield_y = -windspeed * math.cos(wind_direction * math.pi/180.0);
+
+	return [windfield_x,windfield_y];
+	}
+
+
 
 
 
@@ -47,15 +59,17 @@ else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag =
 var windfield_x = -windspeed * math.sin(wind_direction * math.pi/180.0);
 var windfield_y = -windspeed * math.cos(wind_direction * math.pi/180.0);
 
-append(windfield,windfield_x);
-append(windfield,windfield_y);
-
-return windfield;
+return [windfield_x,windfield_y];
 }
 
 
 var get_wind_direction = func (tile_index) {
 
+if (hardcoded_clouds_flag == 1)
+	{
+	return local_weather.wind.current[0];
+	}
+
 if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3))
 	{
 	return tile_wind_direction[0];
@@ -69,6 +83,11 @@ else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag =
 
 var get_wind_speed = func (tile_index) {
 
+if (hardcoded_clouds_flag == 1)
+	{
+	return local_weather.wind.current[1];
+	}
+
 if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3))
 	{
 	return tile_wind_speed[0];
@@ -94,6 +113,15 @@ if (local_weather.local_weather_running_flag == 0) {return;}
 dt_lw = getprop("/sim/time/delta-sec");
 time_lw = time_lw + dt_lw;
 
+# this is a really ugly hack to get the sun angle information to the shaders
+# directly referencing /sim/time/sun-angle-rad as uniform doesn't
+# work since that is a tied property
+
+var sun_angle = 1.57079632675 - getprop("/sim/time/sun-angle-rad");
+
+var terminator_offset = sun_angle /  0.017451 * 110000.0 + 250000.0;
+setprop("/environment/terminator-relative-position-m",terminator_offset);
+
 if (getprop(lw~"timing-loop-flag") ==1) {settimer(timing_loop, 0);}
 
 }
diff --git a/Nasal/local_weather/weather_tile_management.nas b/Nasal/local_weather/weather_tile_management.nas
index db4483961..25b741d21 100644
--- a/Nasal/local_weather/weather_tile_management.nas
+++ b/Nasal/local_weather/weather_tile_management.nas
@@ -43,8 +43,8 @@ var code = getprop(lw~"tiles/tile[4]/code");
 var i = 0;
 var d_min = 100000.0;
 var i_min = 0;
-var distance_to_load = getprop(lw~"config/distance-to-load-tile-m");
-var distance_to_remove = getprop(lw~"config/distance-to-remove-tile-m");
+# var distance_to_load = getprop(lw~"config/distance-to-load-tile-m");
+# var distance_to_remove = getprop(lw~"config/distance-to-remove-tile-m");
 var current_visibility = getprop(lw~"interpolation/visibility-m");
 var current_heading = getprop("orientation/heading-deg");
 var loading_flag = getprop(lw~"tmp/asymmetric-tile-loading-flag");
@@ -53,10 +53,18 @@ var this_frame_action_flag = 0; # use this flag to avoid overlapping tile operat
 setsize(active_tile_list,0);
 #append(active_tile_list,0); # tile zero formally containing static objects is always active 
 
-if (distance_to_load > 3.0 * current_visibility)
-	{distance_to_load = 3.0 * current_visibility;}
-if (distance_to_load < 29000.0)
-	{distance_to_load = 29000.0;}
+var distance_to_load = current_visibility;
+
+if (distance_to_load > 65000.0) {distance_to_load = 65000.0;}
+if (distance_to_load < 29000.0) {distance_to_load = 29000.0;}
+
+
+#if (distance_to_load > 3.0 * current_visibility)
+#	{distance_to_load = 3.0 * current_visibility;}
+#if (distance_to_load < 29000.0)
+#	{distance_to_load = 29000.0;}
+
+var distance_to_remove = distance_to_load + 500.0;
 
 # check here if we have a new weather station if METAR is running
 
@@ -473,6 +481,7 @@ if (getprop(lw~"tmp/tile-management") == "repeat tile")
 	else if (code == "cold_sector") {weather_tiles.set_cold_sector_tile();}
 	else if (code == "warm_sector") {weather_tiles.set_warm_sector_tile();}
 	else if (code == "tropical_weather") {weather_tiles.set_tropical_weather_tile();}
+	else if (code == "thunderstorms") {weather_tiles.set_thunderstorms_tile();}
 	else if (code == "test") {weather_tiles.set_4_8_stratus_tile();}
 	else 
 		{
diff --git a/Nasal/local_weather/weather_tiles.nas b/Nasal/local_weather/weather_tiles.nas
index b7dea927b..bacacaed8 100644
--- a/Nasal/local_weather/weather_tiles.nas
+++ b/Nasal/local_weather/weather_tiles.nas
@@ -96,22 +96,29 @@ calc_geo(blat);
 
 # first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure)
 
-local_weather.set_weather_station(blat, blon, alt_offset, 20000.0, 14.0, 12.0, 29.78);
+local_weather.set_weather_station(blat, blon, alt_offset, 30000.0, 14.0, 12.0, 29.78);
 
-#strength = 0.5;
+alt_offset = 0.0;
+
+#var strength = 0.5;
 #local_weather.create_cumosys(blat,blon, 3000.0, get_n(strength), 20000.0);
 
 #create_2_8_altocumulus_streaks(blat, blon, 12000+alt_offset, alpha) ;
 #create_2_8_altocumulus_streaks(blat, blon, 12000+alt_offset, alpha) ;
 #create_6_8_stratus(blat, blon, 3000+alt_offset, alpha) ;
 #create_4_8_tstratus_patches(blat, blon, 5000+alt_offset, alpha) ;
-#create_4_8_sstratus_patches(blat, blon, 12000+alt_offset, alpha) ;
-#create_4_8_cirrostratus_patches(blat, blon, 20000+alt_offset, alpha) ;
+#create_4_8_sstratus_patches(blat, blon, 5000+alt_offset, alpha) ;
+#create_4_8_cirrostratus_patches(blat, blon, 5000+alt_offset, alpha) ;
 
 #create_4_8_cirrocumulus_streaks(blat, blon, 10000.0 + alt_offset, alpha);
-create_4_8_alttstratus_streaks(blat, blon, 5000+alt_offset, alpha) ;
+#create_4_8_alttstratus_streaks(blat, blon, 5000+alt_offset, alpha) ;
 #create_2_8_cirrocumulus_patches(blat, blon, 13000+alt_offset, alpha) ;
-#create_8_8_nimbus_rain(blat, blon, 5000+alt_offset, alpha, 0.3) ;
+
+#create_8_8_nimbus_rain(blat, blon, 3000+alt_offset, alpha, 0.3) ;
+#create_8_8_tstratus(blat, blon, 5000+alt_offset, alpha);
+#create_8_8_cirrostratus(blat, blon, 5000+alt_offset, alpha);
+create_thunderstorm_scenario (blat, blon, 3000.0, alpha);
+#create_big_thunderstorm (blat, blon, 3000.0, alpha);
 
 #create_4_8_altocumulus_perlucidus(blat, blon, 5000+alt_offset, alpha) ;
 
@@ -121,7 +128,7 @@ create_4_8_alttstratus_streaks(blat, blon, 5000+alt_offset, alpha) ;
 
 
 
-local_weather.set_atmosphere_ipoint(blat, blon, 45000.0, 10000.0, 45000.0, 0.0, 25000.0, 30000.0, 0.9, 10000.0, 11000.0); 
+local_weather.set_atmosphere_ipoint(blat, blon, 45000.0, 15000.0, 45000.0, 0.0, 15000.0, 17000.0, 0.8, 12000.0, 17000.0); 
 
 
 append(weather_dynamics.tile_convective_altitude,3000.0);
@@ -160,12 +167,14 @@ calc_geo(blat);
 
 # get probabilistic values for the weather parameters
 
-var vis = 30000.0 + rand() * 15000.0;
+var vis = 25000.0 + rand() * 10000.0;
 var T = 20.0 + rand() * 10.0;
 var spread = 14.0 + 8.0 * rand();
 var D = T - spread;
 var p = 1025.0 + rand() * 6.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
 
@@ -298,12 +307,14 @@ calc_geo(blat);
 
 # get probabilistic values for the weather parameters
 
-var vis = 25000.0 + rand() * 15000.0;
+var vis = 20000.0 + rand() * 10000.0;
 var T = 15.0 + rand() * 10.0;
 var spread = 10.0 + 4.0 * rand();
 var D = T - spread;
 var p = 1019.0 + rand() * 6.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
 
@@ -445,12 +456,14 @@ calc_geo(blat);
 
 # get probabilistic values for the weather parameters
 
-var vis = 20000.0 + rand() * 12000.0;
+var vis = 15000.0 + rand() * 7000.0;
 var T = 12.0 + rand() * 10.0;
 var spread = 7.0 + 4.0 * rand();
 var D = T - spread;
 var p = 1013.0 + rand() * 6.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
 
@@ -653,12 +666,14 @@ calc_geo(blat);
 
 # get probabilistic values for the weather parameters
 
-var vis = 12000.0 + rand() * 9000.0;
+var vis = 8000.0 + rand() * 8000.0;
 var T = 10.0 + rand() * 10.0;
 var spread = 6.0 + 2.0 * rand();
 var D = T - spread;
 var p = 1007.0 + rand() * 6.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
 
@@ -802,7 +817,7 @@ var alpha = getprop(lw~"tmp/tile-orientation-deg");
 var phi = alpha * math.pi/180.0;
 
 
-if (getprop(lw~"tmp/presampling-flag") == 0)
+if (local_weather.presampling_flag == 0)
 	{var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");}
 else
 	{var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");}
@@ -815,13 +830,13 @@ calc_geo(blat);
 
 # get probabilistic values for the weather parameters
 
-var vis = 9000.0 + rand() * 10000.0;
+var vis = 8000.0 + rand() * 5000.0;
 var T = 5.0 + rand() * 10.0;
 var spread = 5.0 + 2.0 * rand();
 var D = T - spread;
 var p = 1001.0 + rand() * 6.0; p = adjust_p(p);
 
-
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
 
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
@@ -832,11 +847,15 @@ var strength = 0.0;
 
 var rn = rand();
 
+
+
 if (rand() < small_scale_persistence)
 	{rn = rnd_store;}
 else
 	{rnd_store = rn;}
 
+rn = 0.1;
+
 
 if (rn > 0.8)
 	{
@@ -849,7 +868,10 @@ if (rn > 0.8)
 	y = 2.0 * (rand()-0.5) * 11000.0;
 	var beta = rand() * 360.0;
 
-	local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0);
+	var alt_eff = alt;
+	if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt_eff - 3000.0;}
+
+	local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt_eff+alt_offset, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0);
 	local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 10000.0, 6000.0, beta, 0.0, alt + alt_offset, 5000.0, 0.3, -1, -1, -1,0,-1 );
 	local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 9000.0, 5000.0, beta, 0.0, alt+alt_offset-300.0, 1500.0, 0.5, -1, -1, -1,0,-1 );
 
@@ -857,7 +879,7 @@ if (rn > 0.8)
 	y = 2.0 * (rand()-0.5) * 11000.0;
 	var beta = rand() * 360.0;
 
-	local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset, 500.0, 10000.0, 6000.0, beta, 1.0, 0.2, 1, 1.0);
+	local_weather.create_layer("Nimbus", blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt_eff+alt_offset, 500.0, 10000.0, 6000.0, beta, 1.0, 0.2, 1, 1.0);
 	local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 9000.0, 5000.0, beta, 0.0, alt + alt_offset, 5000.0, 0.3, -1, -1, -1,0 ,-1);
 	local_weather.create_effect_volume(2, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 8000.0, 4000.0, beta, 0.0, alt+alt_offset-300.0, 1500.0, 0.5, -1, -1, -1,0,-1 );
 
@@ -952,7 +974,7 @@ var lon = 0.0;
 var alpha = getprop(lw~"tmp/tile-orientation-deg");
 var phi = alpha * math.pi/180.0;
 
-if (getprop(lw~"tmp/presampling-flag") == 0)
+if (local_weather.presampling_flag == 0)
 	{var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");}
 else
 	{var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");}
@@ -971,6 +993,8 @@ var spread = 4.5 + 1.0 * rand();
 var D = T - spread;
 var p = 995.0 + rand() * 6.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
 
@@ -980,6 +1004,7 @@ local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_i
 var alt = spread * 400.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft;
 var strength = 0.0;
 
+#var alt = 3000.0;
 
 create_8_8_nimbus_rain(blat, blon, alt+alt_offset, alpha,0.4 + rand()*0.2);
 
@@ -1036,12 +1061,14 @@ calc_geo(blat);
 
 # get probabilistic values for the weather parameters
 
-var vis = 45000.0 + rand() * 20000.0;
+var vis = 40000.0 + rand() * 15000.0;
 var T = 8.0 + rand() * 8.0;
 var spread = 7.0 + 3.0 * rand();
 var D = T - spread;
 var p = 1005.0 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
 
@@ -1131,12 +1158,14 @@ calc_geo(blat);
 
 # get probabilistic values for the weather parameters
 
-var vis = 12000.0 + rand() * 10000.0;
+var vis = 8000.0 + rand() * 5000.0;
 var T = 16.0 + rand() * 10.0;
 var spread = 6.0 + 3.0 * rand();
 var D = T - spread;
 var p = 1005.0 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # and set them at the tile center
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
 
@@ -1273,6 +1302,8 @@ var spread = 8.0 + 2.0 * rand();
 var D = T - spread;
 var p = 970 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure)
 
 local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
@@ -1374,6 +1405,82 @@ tile_finished();
 }
 
 
+
+
+####################################
+# Thunderstorms
+####################################
+
+var set_thunderstorms_tile = func {
+
+setprop(lw~"tiles/code","thunderstorms");
+
+tile_start();
+
+var x = 0.0;
+var y = 0.0;
+var lat = 0.0;
+var lon = 0.0;
+
+
+var alpha = getprop(lw~"tmp/tile-orientation-deg");
+var phi = alpha * math.pi/180.0;
+var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");
+
+# get tile center coordinates
+
+var blat = getprop(lw~"tiles/tmp/latitude-deg");
+var blon = getprop(lw~"tiles/tmp/longitude-deg");
+calc_geo(blat);
+
+# get probabilistic values for the weather parameters
+
+var vis = 9000.0 + rand() * 10000.0;
+var T = 10.0 + rand() * 15.0;
+var spread = 8.0 + 2.0 * rand();
+var D = T - spread;
+var p = 1000 + rand() * 10.0; p = adjust_p(p);
+
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
+# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure)
+
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
+
+# altitude for the lowest layer
+var alt = spread * 400.0;
+var strength = 0.0;
+
+# bias Cumulus clouds towards larger sizes due to lots of water vapour
+local_weather.convective_size_bias = 0.3 + rand() * 0.3;
+
+
+# and specify the atmosphere
+	local_weather.set_atmosphere_ipoint(blat, blon, vis + 12000.0, alt+alt_offset, vis + 20000.0, 0.0, alt+alt_offset +20000.0, alt+alt_offset + 25000.0, 0.65, alt+alt_offset, alt+alt_offset + 2500.0); 
+
+var rn = rand();
+
+
+if (rand() < small_scale_persistence)
+	{rn = rnd_store;}
+else
+	{rnd_store = rn;}
+
+create_thunderstorm_scenario (blat, blon, alt + alt_offset, alpha);
+
+# store convective altitude and strength
+
+append(weather_dynamics.tile_convective_altitude,alt);
+append(weather_dynamics.tile_convective_strength,strength);
+
+tile_finished();
+
+}
+
+
+
+
+
 ####################################
 # Coldfront
 ####################################
@@ -1410,6 +1517,8 @@ var spread = 8.0 + 2.0 * rand();
 var D = T - spread;
 var p = 1005 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # first weather info for tile  (lat, lon, visibility, temperature, dew point, pressure)
 
 # after the front
@@ -1525,7 +1634,7 @@ var lon = 0.0;
 var alpha = getprop(lw~"tmp/tile-orientation-deg");
 var phi = alpha * math.pi/180.0;
 
-if (getprop(lw~"tmp/presampling-flag") == 0)
+if (local_weather.presampling_flag == 0)
 	{var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");}
 else
 	{var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");}
@@ -1544,6 +1653,8 @@ var spread = 9.0 + 4.0 * rand();
 var D = T - spread;
 var p = 1005 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # first weather info for tile  (lat, lon, visibility, temperature, dew point, pressure)
 
 # after the front
@@ -1587,7 +1698,10 @@ for (var i=0; i<6; i=i+1)
 	var x = 2.0 * (rand()-0.5) * 15000;
 	var y = 2.0 * (rand()-0.5) * 10000 + 10000;
 	var beta = (rand() -0.5) * 180.0;
-	local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 18000 + alt + alt_offset,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0);
+	var alt_shift = 0.0;
+	if (local_weather.hardcoded_clouds_flag == 1) {alt_shift = local_weather.offset_map["Cirrostratus"];}
+
+	local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 18000 + alt + alt_offset - alt_shift,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0);
 
 	}
 
@@ -1625,7 +1739,7 @@ var lon = 0.0;
 var alpha = getprop(lw~"tmp/tile-orientation-deg");
 var phi = alpha * math.pi/180.0;
 
-if (getprop(lw~"tmp/presampling-flag") == 0)
+if (local_weather.presampling_flag == 0)
 	{var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");}
 else
 	{var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");}
@@ -1644,6 +1758,8 @@ var spread = 8.0 + 2.0 * rand();
 var D = T - spread;
 var p = 1005 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # first weather info for tile  (lat, lon, visibility, temperature, dew point, pressure)
 
 # after the front
@@ -1668,12 +1784,15 @@ var strength = 0.0;
 
 # followed by random patches of Cirrostratus
 
+var alt_shift = 0.0;
+if (local_weather.hardcoded_clouds_flag == 1) {alt_shift = local_weather.offset_map["Cirrostratus"];}
+
 for (var i=0; i<3; i=i+1)
 	{
 	var x = 2.0 * (rand()-0.5) * 18000;
 	var y = 2.0 * (rand()-0.5) * 5000 - 15000;
 	var beta = (rand() -0.5) * 180.0;
-	local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 15000 + alt + alt_offset,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0);
+	local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 15000 + alt + alt_offset - alt_shift,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0);
 
 	}
 
@@ -1742,7 +1861,7 @@ var lon = 0.0;
 var alpha = getprop(lw~"tmp/tile-orientation-deg");
 var phi = alpha * math.pi/180.0;
 
-if (getprop(lw~"tmp/presampling-flag") == 0)
+if (local_weather.presampling_flag == 0)
 	{var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");}
 else
 	{var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");}
@@ -1761,6 +1880,8 @@ var spread = 7.0 + 2.0 * rand();
 var D = T - spread;
 var p = 1005 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # first weather info for tile  (lat, lon, visibility, temperature, dew point, pressure)
 
 # after the front
@@ -1849,7 +1970,7 @@ var lon = 0.0;
 var alpha = getprop(lw~"tmp/tile-orientation-deg");
 var phi = alpha * math.pi/180.0;
 
-if (getprop(lw~"tmp/presampling-flag") == 0)
+if (local_weather.presampling_flag == 0)
 	{var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");}
 else
 	{var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");}
@@ -1868,6 +1989,8 @@ var spread = 5.0 + 2.0 * rand();
 var D = T - spread;
 var p = 1005 + rand() * 10.0; p = adjust_p(p);
 
+if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+
 # first weather info for tile  (lat, lon, visibility, temperature, dew point, pressure)
 
 # after the front
@@ -1951,7 +2074,10 @@ var lon = 0.0;
 
 var alpha = getprop("/environment/metar/base-wind-dir-deg");
 var phi = alpha * math.pi/180.0;
-var metar_alt_offset = 700.0 + getprop("/environment/metar/station-elevation-ft");
+
+# it seems more recent Flightgear versions have absolute altitude
+# var metar_alt_offset = 700.0 + getprop("/environment/metar/station-elevation-ft");
+var metar_alt_offset = 700.0;
 
 # print("metar_alt_offset", metar_alt_offset);
 
@@ -2263,11 +2389,13 @@ var set_METAR_weather_station = func {
 
 	if (is_visibility_max == 1) 
 			{
-		if (p * inhg_to_hp < 1000.0) {vis = 10000.0 + 5000 * rand();}	
-		else if (p * inhg_to_hp < 1010.0) {vis = 15000.0 + 7000 * rand();}
-		else if (p * inhg_to_hp < 1020.0) {vis = 22000.0 + 14000.0 * rand();}
-		else {vis = 30000.0 + 15000.0 * rand();}
-		}
+			if (p * inhg_to_hp < 1000.0) {vis = 10000.0 + 5000 * rand();}	
+			else if (p * inhg_to_hp < 1010.0) {vis = 15000.0 + 7000 * rand();}
+			else if (p * inhg_to_hp < 1020.0) {vis = 22000.0 + 14000.0 * rand();}
+			else {vis = 30000.0 + 15000.0 * rand();}
+
+			if (realistic_visibility_flag == 1) {vis = vis * realistic_visibility_multiplyer;}
+			}
 
 
 
@@ -2385,32 +2513,31 @@ var set_METAR_weather_station = func {
 # mid-level cloud setup calls
 ####################################
 
-var create_8_8_stratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
-
-local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
-
-}
 
 var create_8_8_tstratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;}
-
-local_weather.create_streak("Stratus (thin)",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
-
+if (local_weather.hardcoded_clouds_flag == 1) 
+	{
+	alt = alt - local_weather.offset_map["Stratus_thin"];
+	
+local_weather.create_streak("Stratus (thin)",lat, lon, alt,500.0,40,1000.0,0.0,400.0,40,1000.0,0.0,400.0,alpha,1.0);
+	}
+else
+	{
+	local_weather.create_streak("Stratus (thin)",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
+	}
 }
 
 var create_8_8_cirrostratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];}
 
 local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,30,1250.0,0.0,400.0,30,1250.0,0.0,400.0,alpha,1.0);
 }
 
 var create_8_8_nimbus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
 
 local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0);
 
@@ -2419,9 +2546,11 @@ local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,
 
 var create_8_8_nimbus_var1 = func (lat, lon, alt, alpha) {
 
+if (local_weather.hardcoded_clouds_flag == 1) {var alt_eff = alt - local_weather.offset_map["Nimbus"]; }
+
 var phi = alpha * math.pi/180.0;
 
-local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0);
+local_weather.create_streak("Nimbus",lat, lon, alt_eff,500.0,35,1150.0,0.0,200.0,35,1150.0,0.0,200.0,alpha,1.0);
 
 for (var i = 0; i < 3; i=i+1)
 	{
@@ -2434,9 +2563,11 @@ for (var i = 0; i < 3; i=i+1)
 
 var create_8_8_nimbus_var2 = func (lat, lon, alt, alpha) {
 
+if (local_weather.hardcoded_clouds_flag == 1) {var alt_eff = alt - local_weather.offset_map["Nimbus"]; }
+
 var phi = alpha * math.pi/180.0;
 
-local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0);
+local_weather.create_streak("Nimbus",lat, lon, alt_eff,500.0,35,1150.0,0.0,200.0,35,1150.0,0.0,200.0,alpha,1.0);
 
 for (var i=0; i<8; i=i+1)
 	{
@@ -2452,10 +2583,12 @@ for (var i=0; i<8; i=i+1)
 
 var create_8_8_nimbus_var3 = func (lat, lon, alt, alpha) {
 
+if (local_weather.hardcoded_clouds_flag == 1) {var alt_eff = alt - local_weather.offset_map["Nimbus"]; }
+
 var phi = alpha * math.pi/180.0;
 
 
-local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0);
+local_weather.create_streak("Nimbus",lat, lon, alt_eff,500.0,35,1150.0,0.0,200.0,35,1150.0,0.0,200.0,alpha,1.0);
 
 for (var i=0; i<6; i=i+1)
 	{
@@ -2465,21 +2598,17 @@ for (var i=0; i<6; i=i+1)
 	local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt+1600.0,300.0,6,1200.0,0.2,700.0,6,1200.0,0.2,700.0,alpha+beta,1.0);
 	}
 
-# reduced visibility in layer
-#local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, alt-1500.0, alt+900.0, 2000.0, -1 , -1, -1, -1,0 ,-1);
-# cloud shade
-#local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt, -1, -1 , -1, -1, -1,0 ,0.8);
-
 }
 
 var create_8_8_nimbus_rain = func (lat, lon, alt, alpha, rain) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 3000.0;}
+
 
 if (local_weather.detailed_clouds_flag == 0)
 	{local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0);}
 else
 	{
+	#print(local_weather.offset_map["Nimbus"]);
 	var rn = rand();
 	if (rn > 0.66) {create_8_8_nimbus_var1(lat, lon, alt, alpha);}
 	else if (rn > 0.33) {create_8_8_nimbus_var2(lat, lon, alt, alpha);}
@@ -2499,12 +2628,19 @@ else
 
 }
 
+var create_8_8_stratus = func (lat, lon, alt, alpha) {
+
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];}
+
+local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
+}
+
 
 var create_8_8_stratus_rain = func (lat, lon, alt, alpha, rain) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
 
-local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
+create_8_8_stratus(lat, lon, alt, alpha);
+
 
 if (rain > 0.1)	
 	{
@@ -2520,9 +2656,22 @@ else
 
 var create_6_8_stratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
+if (local_weather.hardcoded_clouds_flag == 1) 
+	{
+	alt = alt - local_weather.offset_map["Stratus"];
+	
+	for (var i = 0; i < 20; i = i + 1)
+		{
+		var phi = alpha * math.pi/180.0;
+		var x = 2.0 * (rand()-0.5) * 18000;
+		var y = 2.0 * (rand()-0.5) * 18000;
 
-local_weather.create_streak("Stratus",lat, lon, alt,500.0,20,0.0,0.2,20000.0,20,0.0,0.2,20000.0,alpha,1.0);
+		local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,500.0,5,2300.0,0.2,500.0,5,2300.0,0.2,500.0,alpha,1.0);
+		}
+
+	}
+else
+	{local_weather.create_streak("Stratus",lat, lon, alt,500.0,20,0.0,0.2,20000.0,20,0.0,0.2,20000.0,alpha,1.0);}
 }
 
 
@@ -2532,7 +2681,9 @@ var create_6_8_nimbus_rain = func (lat, lon, alt, alpha, rain) {
 
 var phi = alpha * math.pi/180.0;
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 3000.0;}
+var alt_cloud = alt;
+
+if (local_weather.hardcoded_clouds_flag == 1) {alt_cloud = alt_cloud - 3000.0;}
 
 for (var i = 0; i < 3; i = i + 1)
 	{
@@ -2540,7 +2691,7 @@ for (var i = 0; i < 3; i = i + 1)
 	var y = 2.0 * (rand()-0.5) * 12000.0;
 	var beta = rand() * 360.0;
 
-	local_weather.create_layer("Nimbus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0);
+	local_weather.create_layer("Nimbus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt_cloud, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0);
 
 	if (rain > 0.1)
 		{
@@ -2562,7 +2713,9 @@ var create_6_8_stratus_rain = func (lat, lon, alt, alpha, rain) {
 
 var phi = alpha * math.pi/180.0;
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
+var alt_cloud = alt;
+
+if (local_weather.hardcoded_clouds_flag == 1) {alt_cloud = alt_cloud - local_weather.offset_map["Stratus"];}
 
 for (var i = 0; i < 3; i = i + 1)
 	{
@@ -2570,7 +2723,7 @@ for (var i = 0; i < 3; i = i + 1)
 	var y = 2.0 * (rand()-0.5) * 12000.0;
 	var beta = rand() * 360.0;
 
-	local_weather.create_layer("Stratus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 0, 0.0);
+	local_weather.create_layer("Stratus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt_cloud, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 0, 0.0);
 
 	if (rain > 0.1)
 		{
@@ -2589,21 +2742,21 @@ for (var i = 0; i < 3; i = i + 1)
 
 var create_6_8_stratus_undulatus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];}
 
 local_weather.create_undulatus("Stratus",lat, lon, alt,300.0,10,4000.0,0.1,400.0,50,800.0,0.1,100.0, 1000.0, alpha,1.0);
 }
 
 var create_6_8_tstratus_undulatus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];}
 
 local_weather.create_undulatus("Stratus (thin)",lat, lon, alt,300.0,10,4000.0,0.1,400.0,50,800.0,0.1,100.0, 1000.0, alpha,1.0);
 }
 
 var create_6_8_cirrostratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];}
 
 local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,24,1500.0,0.0,900.0,24,1500.0,0.0,900.0,alpha,1.0);
 }
@@ -2633,7 +2786,7 @@ else
 
 var create_4_8_stratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];}
 
 var phi = alpha * math.pi/180.0;
 var x = 2.0 * (rand()-0.5) * 15000;
@@ -2662,7 +2815,7 @@ var create_4_8_stratus_patches = func (lat, lon, alt, alpha) {
 
 var phi = alpha * math.pi/180.0;
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];}
 
 for (var i=0; i<16; i=i+1)
 	{
@@ -2679,7 +2832,7 @@ var create_4_8_tstratus_patches = func (lat, lon, alt, alpha) {
 
 var phi = alpha * math.pi/180.0;
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];}
 
 for (var i=0; i<22; i=i+1)
 	{
@@ -2701,7 +2854,7 @@ var create_4_8_sstratus_patches = func (lat, lon, alt, alpha) {
 
 var phi = alpha * math.pi/180.0;
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];}
 
 for (var i=0; i<22; i=i+1)
 	{
@@ -2719,7 +2872,7 @@ for (var i=0; i<22; i=i+1)
 
 var create_4_8_cirrostratus_patches = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -2736,7 +2889,7 @@ for (var i=0; i<6; i=i+1)
 
 var create_4_8_cirrostratus_undulatus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];}
 
 local_weather.create_undulatus("Cirrostratus",lat, lon, alt,300.0,5,8000.0,0.1,400.0,40,1000.0,0.1,100.0, 1500.0, alpha,1.0);
 }
@@ -2744,7 +2897,7 @@ local_weather.create_undulatus("Cirrostratus",lat, lon, alt,300.0,5,8000.0,0.1,4
 
 var create_4_8_stratus_undulatus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];}
 
 var phi = alpha * math.pi/180.0;
 var x = 2.0 * (rand()-0.5) * 5000;
@@ -2760,7 +2913,7 @@ local_weather.create_streak("Stratus",lat+get_lat(x,y-4000,phi), lon+get_lon(x,y
 
 var create_4_8_tstratus_undulatus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];}
 
 var phi = alpha * math.pi/180.0;
 var x = 2.0 * (rand()-0.5) * 5000;
@@ -2776,7 +2929,7 @@ local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y-4000,phi), lon+get_
 
 var create_4_8_sstratus_undulatus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];}
 
 var phi = alpha * math.pi/180.0;
 var x = 2.0 * (rand()-0.5) * 5000;
@@ -2858,7 +3011,7 @@ for (var i=0; i<20; i=i+1)
 
 var create_4_8_alttstratus_streaks = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 300.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_alt"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -2878,7 +3031,7 @@ for (var i=0; i<10; i=i+1)
 
 var create_4_8_alttstratus_patches = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 300.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_alt"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -2909,7 +3062,7 @@ else
 
 var create_2_8_stratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1500.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -2926,7 +3079,7 @@ for (var i=0; i<8; i=i+1)
 
 var create_2_8_tstratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 650.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_thin"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -2944,7 +3097,7 @@ for (var i=0; i<8; i=i+1)
 
 var create_2_8_sstratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -2967,7 +3120,7 @@ for (var i=0; i<8; i=i+1)
 
 var create_2_8_sstratus_streak = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_structured"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -2981,7 +3134,7 @@ local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get
 
 var create_2_8_cirrostratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -3027,7 +3180,7 @@ local_weather.create_streak("Cirrus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi),
 
 var create_2_8_alttstratus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 300.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Stratus_alt"];}
 
 var phi = alpha * math.pi/180.0;
 
@@ -3113,7 +3266,7 @@ local_weather.create_streak("Cirrus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi),
 
 var create_1_8_cirrostratus_undulatus = func (lat, lon, alt, alpha) {
 
-if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - 1800.0;}
+if (local_weather.hardcoded_clouds_flag == 1) {alt = alt - local_weather.offset_map["Cirrostratus"];}
 
 local_weather.create_undulatus("Cirrostratus",lat, lon, alt,300.0,1,8000.0,0.0,400.0,40,1000.0,0.1,100.0, 1500.0, alpha,1.0);
 }
@@ -3265,9 +3418,12 @@ var create_small_thunderstorm = func(lat, lon, alt, alpha) {
 
 var scale = 0.7 + rand() * 0.3;
 
-local_weather.create_layer("Stratus", lat, lon, alt, 1000.0, 4000.0 * scale, 4000.0 * scale, 0.0, 1.0, 0.3, 1, 1.0);
+var alt_eff = alt;
+if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt - local_weather.offset_map["Stratus"] - 2000.0; }
 
-local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt+2000, 15000.0, 3000.0 * scale, 3000.0 * scale, 0.0, 2.0, 0.0, 0, 0.0);
+local_weather.create_layer("Stratus", lat, lon, alt_eff, 1000.0, 4000.0 * scale, 4000.0 * scale, 0.0, 1.0, 0.3, 1, 1.0);
+
+local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt_eff+3000.0, 12000.0, 3000.0 * scale, 3000.0 * scale, 0.0, 2.0, 0.0, 0, 0.0);
 
 # set the exclusion region for the Cumulus layer
 append(elat, lat); append(elon, lon); append(erad, 4000.0 * scale * 1.2);
@@ -3282,15 +3438,20 @@ var create_medium_thunderstorm = func(lat, lon, alt, alpha) {
 
 var scale = 0.7 + rand() * 0.3;
 
-local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 6000.0 * scale, 6000.0 * scale, 0.0, 1.0, 0.3, 1, 1.5);
+var alt_eff = alt;
+if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt - local_weather.offset_map["Nimbus"] - 2000.0; }
 
-#local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
-local_weather.create_hollow_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0.5);
+local_weather.create_layer("Nimbus", lat, lon, alt_eff, 500.0, 6000.0 * scale, 6000.0 * scale, 0.0, 1.0, 0.3, 1, 1.5);
 
-local_weather.create_layer("Fog (thick)", lat, lon, alt+4000, 6000.0, 3400.0 * scale, 3400.0 * scale, 0.0, 1.5, 0.3, 0, 0.0);
+#local_weather.create_layer("Stratus", lat, lon, alt_eff+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
 
 
-local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt+10000, 10000.0, 3600.0 * scale, 3600.0 * scale, 0.0, 1.2, 0.0, 0, 0.0);
+local_weather.create_hollow_layer("Stratus", lat, lon, alt_eff+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0.5);
+
+local_weather.create_layer("Fog (thick)", lat, lon, alt_eff+4000, 6000.0, 3400.0 * scale, 3400.0 * scale, 0.0, 1.5, 0.3, 0, 0.0);
+
+
+local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt_eff+10000, 10000.0, 3600.0 * scale, 3600.0 * scale, 0.0, 1.2, 0.0, 0, 0.0);
 
 # set the exclusion region for the Cumulus layer
 append(elat, lat); append(elon, lon); append(erad, 6000.0 * scale * 1.2);
@@ -3307,23 +3468,26 @@ var phi = alpha * math.pi/180.0;
 
 var scale = 0.8;
 
-local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.25, 1, 1.5);
+var alt_eff = alt;
+if (local_weather.hardcoded_clouds_flag == 1) {alt_eff = alt - local_weather.offset_map["Nimbus"]; }
+
+local_weather.create_layer("Nimbus", lat, lon, alt_eff, 500.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.25, 1, 1.5);
 
 #local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
-local_weather.create_hollow_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0.7);
+local_weather.create_hollow_layer("Stratus", lat, lon, alt_eff+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0.7);
 
-local_weather.create_layer("Fog (thick)", lat, lon, alt+5000, 3000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 0.7, 0.3, 0, 0.0);
+local_weather.create_layer("Fog (thick)", lat, lon, alt_eff+5000, 3000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 0.7, 0.3, 0, 0.0);
 
 
-local_weather.create_layer("Fog (thick)", lat+get_lat(0,-1000,phi), lon+get_lon(0,-1000,phi), alt+12000, 4000.0, 6300.0 * scale, 6300.0 * scale, 0.0, 0.7, 0.3, 0, 0.0);
+local_weather.create_layer("Fog (thick)", lat+get_lat(0,-1000,phi), lon+get_lon(0,-1000,phi), alt_eff+12000, 4000.0, 6300.0 * scale, 6300.0 * scale, 0.0, 0.7, 0.3, 0, 0.0);
 
 #local_weather.create_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
-local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0.5);
+local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt_eff+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0.5);
 
-#local_weather.create_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
-local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0.5);
+#local_weather.create_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt_eff+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
+local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt_eff+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0.5);
 
-local_weather.create_layer("Stratus (thin)", lat+get_lat(0,-4000,phi), lon+get_lon(0,-4000,phi), alt+24000, 1000.0, 11500.0 * scale, 11500.0 * scale, 0.0, 2.0, 0.3, 0, 0.0);
+local_weather.create_layer("Stratus (thin)", lat+get_lat(0,-4000,phi), lon+get_lon(0,-4000,phi), alt_eff+24000, 1000.0, 11500.0 * scale, 11500.0 * scale, 0.0, 2.0, 0.0, 0, 0.0);
 
 # set the exclusion region for the Cumulus layer
 append(elat, lat); append(elon, lon); append(erad, 7500.0 * scale * 1.2);
@@ -3420,6 +3584,8 @@ var lon_to_m = 0.0; # needs to be calculated dynamically
 var m_to_lon = 0.0; # we do this on startup
 var lw = "/local-weather/";
 
+var realistic_visibility_multiplyer = 1.5;
+
 var small_scale_persistence = getprop(lw~"config/small-scale-persistence");
 var rnd_store = rand();
 
diff --git a/Shaders/cloud-static.frag b/Shaders/cloud-static.frag
new file mode 100644
index 000000000..b51ff3af4
--- /dev/null
+++ b/Shaders/cloud-static.frag
@@ -0,0 +1,10 @@
+uniform sampler2D baseTexture;
+varying float fogFactor;
+
+void main(void)
+{
+      vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);
+      vec4 finalColor = base * gl_Color;
+      gl_FragColor.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor );
+      gl_FragColor.a = mix(0.0, finalColor.a, fogFactor);
+}
diff --git a/Shaders/cloud-static.vert b/Shaders/cloud-static.vert
new file mode 100644
index 000000000..96c2ec80a
--- /dev/null
+++ b/Shaders/cloud-static.vert
@@ -0,0 +1,55 @@
+// -*-C++-*-
+#version 120
+
+varying float fogFactor;
+
+
+float shade = 0.8;
+float cloud_height = 1000.0;
+
+void main(void)
+{	
+
+  gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+  //gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);
+  vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);
+  vec4 l  = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);
+  vec3 u = normalize(ep.xyz - l.xyz);
+
+  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+  gl_Position.x = gl_Vertex.x;
+  gl_Position.y += gl_Vertex.y;
+  gl_Position.z += gl_Vertex.z;
+  gl_Position.xyz += gl_Color.xyz;
+
+
+
+  // Determine a lighting normal based on the vertex position from the
+  // center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
+  float n = dot(normalize(-gl_LightSource[0].position.xyz),
+                normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));;
+
+  // Determine the position - used for fog and shading calculations
+  vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);
+  float fogCoord = abs(ecPosition.z);
+  float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);
+
+
+  gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
+
+// Determine the shading of the sprite based on its vertical position and position relative to the sun.
+  n = min(smoothstep(-0.5, 0.0, n), fract);
+// Determine the shading based on a mixture from the backlight to the front
+  vec4 backlight = gl_LightSource[0].diffuse * shade;
+
+  gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);
+  gl_FrontColor += gl_FrontLightModelProduct.sceneColor;
+
+  // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
+  gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1.0 - smoothstep(70000.0, 75000.0, fogCoord));
+  gl_BackColor = gl_FrontColor;
+
+  // Fog doesn't affect rain as much as other objects.
+  fogFactor = exp( -gl_Fog.density * fogCoord * 0.4);
+  fogFactor = clamp(fogFactor, 0.0, 1.0);
+}
diff --git a/Shaders/rain-layer.vert b/Shaders/rain-layer.vert
index 52c4dbd5c..ef72bf493 100644
--- a/Shaders/rain-layer.vert
+++ b/Shaders/rain-layer.vert
@@ -3,6 +3,8 @@
 
 varying float fogFactor;
 
+uniform float range; // From /sim/rendering/clouds3d-vis-range
+
 float shade = 0.8;
 float cloud_height = 1000.0;
 
@@ -45,7 +47,7 @@ void main(void)
   gl_FrontColor += gl_FrontLightModelProduct.sceneColor;
 
   // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
-  gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1.0 - smoothstep(40000.0, 45000.0, fogCoord));
+  gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1.0 - smoothstep(range*0.9, range, fogCoord));
   gl_BackColor = gl_FrontColor;
 
   // Fog doesn't affect rain as much as other objects.
diff --git a/gui/dialogs/local_weather_tiles.xml b/gui/dialogs/local_weather_tiles.xml
index aacc2d387..cbf4acfe1 100644
--- a/gui/dialogs/local_weather_tiles.xml
+++ b/gui/dialogs/local_weather_tiles.xml
@@ -5,7 +5,7 @@
  <!-- Weather tiles -->
 
  <name>local_weather_tiles</name>
- <width>310</width>
+ <width>470</width>
  <height>385</height>
  <modal>false</modal>
 
@@ -35,6 +35,7 @@
    <value>Coldfront</value>
    <value>Warmfront</value>
    <value>Tropical</value>
+   <value>Thunderstorms</value>
    <value>Test tile</value>
    <binding>
     <command>dialog-apply</command>
@@ -49,6 +50,24 @@
    </enable>-->
   </combo>
 
+
+<text>
+   <x>300</x>
+   <y>330</y>
+   <label>alt. offset (ft)</label>
+  </text>
+
+
+
+<input>
+   <x>390</x>
+   <y>330</y>
+   <width>50</width>
+   <height>25</height>
+   <property>/local-weather/tmp/tile-alt-offset-ft</property>
+  </input>
+
+
  <text>
    <x>5</x>
    <y>295</y>
@@ -77,34 +96,19 @@
    <property>/local-weather/tmp/windspeed-kt</property>
   </input>
 
- <text>
-   <x>155</x>
-   <y>295</y>
-   <label>alt. offset (ft)</label>
-  </text>
-
-
-
-<input>
-   <x>240</x>
-   <y>295</y>
-   <width>50</width>
-   <height>25</height>
-   <property>/local-weather/tmp/tile-alt-offset-ft</property>
-  </input>
+ 
   
-
 <text>
-   <x>5</x>
-   <y>265</y>
+   <x>157</x>
+   <y>295</y>
    <label>gust freq.</label>
   </text>
 
 
 
 <slider>
-   <x>65</x>
-   <y>265</y>
+   <x>225</x>
+   <y>295</y>
    <width>50</width>
    <height>20</height>
    <min>0.0</min>
@@ -118,15 +122,15 @@
 
 
 <text>
-   <x>112</x>
-   <y>265</y>
+   <x>275</x>
+   <y>295</y>
    <label>strength</label>
   </text>
 
 
 <slider>
-   <x>167</x>
-   <y>265</y>
+   <x>333</x>
+   <y>295</y>
    <width>50</width>
    <height>20</height>
    <min>0.0</min>
@@ -138,15 +142,15 @@
 </slider>
 
 <text>
-   <x>215</x>
-   <y>265</y>
+   <x>380</x>
+   <y>295</y>
    <label>dir.</label>
   </text>
 
 
 <slider>
-   <x>240</x>
-   <y>265</y>
+   <x>410</x>
+   <y>295</y>
    <width>50</width>
    <height>20</height>
    <min>0.0</min>
@@ -162,14 +166,14 @@
 
 <text>
    <x>5</x>
-   <y>235</y>
+   <y>265</y>
    <label>wind model</label>
   </text>
 
 
 <combo>
    <x>150</x>
-   <y>235</y>
+   <y>265</y>
    <width>140</width>
    <height>25</height>
    <live>true</live>
@@ -185,15 +189,32 @@
   </combo>
 
 
+<text>
+   <x>300</x>
+   <y>265</y>
+   <label>T offset (C)</label>
+  </text>
+
+
+
+<input>
+   <x>390</x>
+   <y>265</y>
+   <width>50</width>
+   <height>25</height>
+   <property>/local-weather/config/temperature-offset-degc</property>
+  </input>
+
+
 <text>
    <x>5</x>
-   <y>205</y>
+   <y>235</y>
    <label>tile selection mode</label>
 </text>
 
 <combo>
    <x>150</x>
-   <y>205</y>
+   <y>235</y>
    <width>140</width>
    <height>25</height>
    <live>true</live>
@@ -207,33 +228,10 @@
    </binding>
   </combo>
 
-<checkbox>
-   <x>10</x>
-   <y>175</y>
-   <width>15</width>
-   <height>15</height>
-   <label>terrain presampling</label>
-   <property>/local-weather/config/presampling-flag</property>
-   <binding>
-    <command>dialog-apply</command>
-   </binding>
-</checkbox>
-
-<checkbox>
-   <x>150</x>
-   <y>175</y>
-   <width>15</width>
-   <height>15</height>
-   <label>generate thermals</label>
-   <property>/local-weather/config/generate-thermal-lift-flag</property>
-   <binding>
-    <command>dialog-apply</command>
-   </binding>
-</checkbox>
 
 <checkbox>
    <x>10</x>
-   <y>150</y>
+   <y>205</y>
    <width>15</width>
    <height>15</height>
    <label>debug output</label>
@@ -243,7 +241,7 @@
    </binding>
 </checkbox>
 
-<checkbox>
+<!--<checkbox>
    <x>150</x>
    <y>150</y>
    <width>15</width>
@@ -253,9 +251,9 @@
    <binding>
     <command>dialog-apply</command>
    </binding>
-</checkbox>
+</checkbox>-->
 
-<checkbox>
+<!--<checkbox>
    <x>10</x>
    <y>125</y>
    <width>15</width>
@@ -265,9 +263,9 @@
    <binding>
     <command>dialog-apply</command>
    </binding>
-</checkbox>
+</checkbox>-->
 
-<checkbox>
+<!--<checkbox>
    <x>150</x>
    <y>125</y>
    <width>15</width>
@@ -277,11 +275,11 @@
    <binding>
     <command>dialog-apply</command>
    </binding>
-</checkbox>
+</checkbox>-->
 
 <checkbox>
-   <x>10</x>
-   <y>100</y>
+   <x>150</x>
+   <y>205</y>
    <width>15</width>
    <height>15</height>
    <label>terrain effects</label>
@@ -292,8 +290,8 @@
 </checkbox>
 
 <checkbox>
-   <x>150</x>
-   <y>100</y>
+   <x>290</x>
+   <y>205</y>
    <width>15</width>
    <height>15</height>
    <label>realistic visibility</label>
@@ -303,21 +301,46 @@
    </binding>
 </checkbox>
 
-<text>
+<checkbox>
    <x>10</x>
-   <y>75</y>
+   <y>180</y>
+   <width>15</width>
+   <height>15</height>
+   <label>generate thermals</label>
+   <property>/local-weather/config/generate-thermal-lift-flag</property>
+   <binding>
+    <command>dialog-apply</command>
+   </binding>
+</checkbox>
+
+<checkbox>
+   <x>150</x>
+   <y>180</y>
+   <width>15</width>
+   <height>15</height>
+   <label>terrain presampling</label>
+   <property>/local-weather/config/presampling-flag</property>
+   <binding>
+    <command>dialog-apply</command>
+   </binding>
+</checkbox>
+
+
+<text>
+   <x>5</x>
+   <y>150</y>
    <label>Thermal properties:</label>
 </text>
 
 <text>
-   <x>10</x>
-   <y>50</y>
+   <x>160</x>
+   <y>150</y>
    <label>rough day</label>
 </text>
 
 <slider>
-   <x>80</x>
-   <y>50</y>
+   <x>230</x>
+   <y>150</y>
    <width>90</width>
    <height>20</height>
    <min>0.3</min>
@@ -329,11 +352,147 @@
 </slider>
 
 <text>
-   <x>180</x>
-   <y>50</y>
+   <x>330</x>
+   <y>150</y>
    <label>low convection</label>
 </text>
 
+
+<text>
+   <x>5</x>
+   <y>125</y>
+   <label>Ground haze:</label>
+</text>
+
+<text>
+   <x>160</x>
+   <y>125</y>
+   <label>thick</label>
+</text>
+
+<slider>
+   <x>230</x>
+   <y>125</y>
+   <width>90</width>
+   <height>20</height>
+   <min>0.1</min>
+   <max>1.0</max>
+   <property>/local-weather/config/ground-haze-factor</property>
+   <binding>
+     <command>dialog-apply</command>
+   </binding>
+</slider>
+
+<text>
+   <x>330</x>
+   <y>125</y>
+   <label>thin</label>
+</text>
+
+
+<text>
+   <x>5</x>
+   <y>100</y>
+   <label>Max. visibility:</label>
+</text>
+
+<text>
+   <x>160</x>
+   <y>100</y>
+   <label>30 km</label>
+</text>
+
+<slider>
+   <x>230</x>
+   <y>100</y>
+   <width>90</width>
+   <height>20</height>
+   <min>30000.0</min>
+   <max>140000.0</max>
+   <property>/local-weather/config/max-vis-range-m</property>
+   <binding>
+     <command>dialog-apply</command>
+   </binding>
+</slider>
+
+<text>
+   <x>330</x>
+   <y>100</y>
+   <label>140 km</label>
+</text>
+
+
+<text>
+  <x>5</x>
+  <y>75</y>
+  <label>Weather pattern scales</label>
+</text>
+
+
+<text>
+   <x>15</x>
+   <y>45</y>
+   <label>small</label>
+</text>
+
+<text>
+   <x>78</x>
+   <y>60</y>
+   <label>airmass</label>
+</text>
+
+<slider>
+   <x>60</x>
+   <y>45</y>
+   <width>100</width>
+   <height>20</height>
+   <min>0.5</min>
+   <max>2.0</max>
+   <property>/local-weather/config/large-scale-persistence</property>
+   <binding>
+     <command>dialog-apply</command>
+   </binding>
+</slider>
+
+<text>
+   <x>165</x>
+   <y>45</y>
+   <label>large</label>
+</text>
+
+
+<text>
+   <x>205</x>
+   <y>45</y>
+   <label>small</label>
+</text>
+
+<text>
+   <x>257</x>
+   <y>60</y>
+   <label>cloud patterns</label>
+</text>
+
+<slider>
+   <x>250</x>
+   <y>45</y>
+   <width>100</width>
+   <height>20</height>
+   <min>0.0</min>
+   <max>1.0</max>
+   <property>/local-weather/config/small-scale-persistence</property>
+   <binding>
+     <command>dialog-apply</command>
+   </binding>
+</slider>
+
+<text>
+   <x>355</x>
+   <y>45</y>
+   <label>large</label>
+</text>
+
+
 <group>
   <x>10</x>
   <y>10</y>

From b426b4dbed805964f178bc00796ca89822336ba6 Mon Sep 17 00:00:00 2001
From: Gijs de Rooy <gijsrooy@hotmail.com>
Date: Tue, 3 Jan 2012 21:35:26 +0100
Subject: [PATCH 2/4] Transmit door-positions over MP.

---
 Aircraft/c172p/Models/c172p.xml | 9 +++++++++
 Aircraft/c172p/c172p-set.xml    | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/Aircraft/c172p/Models/c172p.xml b/Aircraft/c172p/Models/c172p.xml
index 94eb6af57..2f8f51e54 100644
--- a/Aircraft/c172p/Models/c172p.xml
+++ b/Aircraft/c172p/Models/c172p.xml
@@ -7,6 +7,15 @@
    <pitch-deg>-3.0</pitch-deg>
    <z-m> -0.065 </z-m>
  </offsets>
+ 
+ <nasal>
+   <load>
+     var rplayer = cmdarg();  
+     rplayer.getNode("sim/model/door-positions/rightDoor/position-norm", 1).alias(rplayer.getNode("sim/multiplay/generic/float[0]")); 
+     rplayer.getNode("sim/model/door-positions/leftDoor/position-norm", 1).alias(rplayer.getNode("sim/multiplay/generic/float[1]")); 
+     rplayer.getNode("sim/model/door-positions/baggageDoor/position-norm", 1).alias(rplayer.getNode("sim/multiplay/generic/float[2]")); 
+   </load>
+ </nasal> 
 
  <!-- Normal shader effect. Separate effects required for each normal map texture -->
 
diff --git a/Aircraft/c172p/c172p-set.xml b/Aircraft/c172p/c172p-set.xml
index 8a5eeb9b9..36c7867cd 100644
--- a/Aircraft/c172p/c172p-set.xml
+++ b/Aircraft/c172p/c172p-set.xml
@@ -100,6 +100,9 @@ Started October 23 2001 by John Check, fgpanels@rockfish.net
   <multiplay>
       <chat_display>1</chat_display>
       <generic>
+        <float n="0" alias="/sim/model/door-positions/rightDoor/position-norm"/>
+        <float n="1" alias="/sim/model/door-positions/leftDoor/position-norm"/>
+        <float n="2" alias="/sim/model/door-positions/baggageDoor/position-norm"/>
         <int type="int">0</int>
         <int type="int">0</int>
         <int type="int">0</int>

From 66e7d103f1a596b8907b0b2215a7c4278b3e2399 Mon Sep 17 00:00:00 2001
From: Stuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Date: Tue, 3 Jan 2012 23:33:26 +0000
Subject: [PATCH 3/4] Update multiplayer settings dialog: - Add list of current
 (as of 3 Jan 2012) MP servers - Add Connect/Disconnect buttons

---
 gui/dialogs/multiplayer.xml | 96 ++++++++++++++++++++++++++++++-------
 1 file changed, 80 insertions(+), 16 deletions(-)

diff --git a/gui/dialogs/multiplayer.xml b/gui/dialogs/multiplayer.xml
index 713336ada..24b8d18a4 100644
--- a/gui/dialogs/multiplayer.xml
+++ b/gui/dialogs/multiplayer.xml
@@ -4,10 +4,23 @@
 <PropertyList>
     <name>multiplayer</name>
     <layout>vbox</layout>
-    <resizable>true</resizable>
+    <resizable>false</resizable>
 
     <nasal>
         <open>
+            if ((getprop("/sim/multiplay/selected-server") == nil) or 
+                (getprop("/sim/multiplay/selected-server") == "" )   ){
+                var tx = getprop("/sim/multiplay/txhost");
+                var dlg = cmdarg();
+                var servers = cmdarg().getChildren("group")[1].getChildren("combo")[0].getChildren("value");
+                foreach (var s; servers) {
+                    var server = s.getValue();
+                    var host = split(" ", server)[0];
+                    if (host == tx) {
+                        setprop("/sim/multiplay/selected-server", server);
+                    }
+                }
+            }
         </open>
         
         <close>
@@ -50,6 +63,7 @@
         </text>
         <input>
           <row>0</row><col>1</col>
+          <halign>left</halign>
           <property>/sim/multiplay/callsign</property>
         </input>
         <text>
@@ -60,23 +74,31 @@
         <combo>
             <name>host</name>
             <row>1</row><col>1</col>
-            <pref-width>120</pref-width>
-            <property>/sim/multiplay/txhost</property>
-            <editable>true</editable>
-            <properties>/sim/multiplay/servers</properties>
+            <pref-width>300</pref-width>
+            <property>/sim/multiplay/selected-server</property>
+            <editable>false</editable>
+            <value>mpserver01.flightgear.org (Frankfurt/Germany)</value>
+            <value>mpserver02.flightgear.org (Kansas/USA)</value>
+            <value>mpserver03.flightgear.org (Germany)</value>
+            <value>mpserver04.flightgear.org (United Kingdom)</value>
+            <value>mpserver05.flightgear.org (Chicago/USA)</value>
+            <value>mpserver07.flightgear.org (Wisconsin, USA)</value>
+            <value>mpserver08.flightgear.org (Germany)</value>
+            <value>mpserver09.flightgear.org (Germany)</value>
+            <value>mpserver10.flightgear.org (Montpellier, France)</value>
+            <value>mpserver11.flightgear.org (Vilnius, Lithuania)</value>
+            <value>mpserver12.flightgear.org (Amsterdam, Netherlands)</value>
+            <value>mpserver13.flightgear.org (Grenoble, France)</value>
         </combo>
-        <input>
-          <row>1</row><col>2</col>
-          <property>/sim/multiplay/txport</property>
-        </input>
         
     <!-- status area -->
         <text>
             <visible>
                 <not><property>/sim/multiplay/online</property></not>
             </visible>
-            <row>2</row>
+            <row>3</row>
             <col>1</col>
+            <halign>left</halign>
             <label>Not connected</label>
         </text>
         
@@ -84,9 +106,9 @@
             <visible>
                 <property>/sim/multiplay/online</property>
             </visible>
-            <row>2</row>
+            <row>3</row>
             <col>1</col>
-            
+            <halign>left</halign>
             <label>MMMMMMMMMMMMMMMMM</label>
             <format>Connected to %s</format>
             <property>/sim/multiplay/txhost</property>
@@ -94,23 +116,65 @@
         </text>
     </group>
     
+    <hrule/>
     <group>
         <layout>hbox</layout>
         <default-padding>10</default-padding>
         <empty><stretch>true</stretch></empty>
 
         <button>
-          <legend>Apply</legend>
-          <default>true</default>
-          <equal>true</equal>
+          <legend>Connect</legend>
+          <enable>
+            <not>
+              <property>/sim/multiplay/online</property>
+            </not>
+          </enable>
           <binding>
             <command>dialog-apply</command>
           </binding>
+          <binding>
+            <command>nasal</command>
+            <script>
+              var server = getprop("/sim/multiplay/selected-server");
+              # Get the server name by splitting on the space between
+              # the hostname and the comment.
+              server = split(" ", server)[0];
+              setprop("/sim/multiplay/txhost", server);
+            </script> 
+          </binding>
           <binding>
             <command>reinit</command>
             <subsystem>mp</subsystem>
           </binding>
         </button>
+        <button>
+          <legend>Disconnect</legend>
+          <enable>
+            <property>/sim/multiplay/online</property>
+          </enable>
+          <binding>
+            <command>dialog-apply</command>
+          </binding>
+          <binding>
+            <command>nasal</command>
+            <script>
+              setprop("/sim/multiplay/txhost", "0.0.0.0");
+            </script> 
+          </binding>
+          <binding>
+            <command>reinit</command>
+            <subsystem>mp</subsystem>
+          </binding>
+        </button>
+        <button>
+          <legend>Close</legend>
+          <equal>true</equal>
+          <binding>
+            <command>dialog-close</command>
+          </binding>
+        </button>
+
+        <empty><stretch>true</stretch></empty>
     </group>
     
-</PropertyList>
\ No newline at end of file
+</PropertyList>

From 316cf855576831cc1f847f06e5fa0011a23f6998 Mon Sep 17 00:00:00 2001
From: Stuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Date: Tue, 3 Jan 2012 23:38:26 +0000
Subject: [PATCH 4/4] Update MP Server descriptions

---
 gui/dialogs/multiplayer.xml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/gui/dialogs/multiplayer.xml b/gui/dialogs/multiplayer.xml
index 24b8d18a4..6ee1c17cd 100644
--- a/gui/dialogs/multiplayer.xml
+++ b/gui/dialogs/multiplayer.xml
@@ -74,17 +74,17 @@
         <combo>
             <name>host</name>
             <row>1</row><col>1</col>
-            <pref-width>300</pref-width>
+            <pref-width>350</pref-width>
             <property>/sim/multiplay/selected-server</property>
             <editable>false</editable>
-            <value>mpserver01.flightgear.org (Frankfurt/Germany)</value>
-            <value>mpserver02.flightgear.org (Kansas/USA)</value>
+            <value>mpserver01.flightgear.org (Frankfurt, Germany)</value>
+            <value>mpserver02.flightgear.org (Kansas, USA)</value>
             <value>mpserver03.flightgear.org (Germany)</value>
             <value>mpserver04.flightgear.org (United Kingdom)</value>
-            <value>mpserver05.flightgear.org (Chicago/USA)</value>
+            <value>mpserver05.flightgear.org (Chicago, USA)</value>
             <value>mpserver07.flightgear.org (Wisconsin, USA)</value>
-            <value>mpserver08.flightgear.org (Germany)</value>
-            <value>mpserver09.flightgear.org (Germany)</value>
+            <value>mpserver08.flightgear.org (Frankfurt am Main, Germany)</value>
+            <value>mpserver09.flightgear.org (Koln, Germany)</value>
             <value>mpserver10.flightgear.org (Montpellier, France)</value>
             <value>mpserver11.flightgear.org (Vilnius, Lithuania)</value>
             <value>mpserver12.flightgear.org (Amsterdam, Netherlands)</value>