/*
Original Scripts by Paul Hobbs www.44090digitalmodels.co.uk and Andi Smith http://www.ajsmith.x10.mx/trs/index.shtml with scripting help from Adam
*/

include "superstub.gs"
include "asset.gs"
include "vehicle.gs"
include "gs.gs"
include "Browser.gs"

class my_script isclass SSLoco
{
  bool debug = false;
  
   // ash dump...
  int ash_amount_max;
  int ash_amount_current;
  int ash_amount_increment;
  int ash_replenish_delay  = 360; // seconds
  int duration_ash_dump    = 25;  // seconds
  bool ScheduleSuspended, dump_BAR;

  Asset ashProduct;
  ProductQueue ashQueue;

  // ****************************************************************************/
  // start class init ACS coupler/pipes
  // ****************************************************************************/

  Asset meAsset;

  // for couplers
  void        CheckCouple(int cartype, bool direction);
  thread void DirectionMonitor();
  void        ACS_Init(void);

  // general utility functions
  int IsEndCar();
  void sniffMyTrain();

  public define int CAR_DERAILED = -1;
  public define int CAR_CENTER = 0;
  public define int CAR_FRONT = 1;
  public define int CAR_BACK = 2;
  public define int CAR_SINGLE = 3;

  int status;
  bool runDM = true;

  Asset ACS_coupler_connected;
  Asset ACS_coupler_disconnected;

  Asset ACS_vac_connected;
  Asset ACS_vac_connected_h;
  Asset ACS_vac_disconnected;
  
  Library ACSlib;

  bool bCFront = false;
  bool bCRear = false;

  float sleepRandomiser;

  Train myTrain;

  // ****************************************************************************/
  // end class init ACS coupler/pipes
  // ****************************************************************************/

  // ****************************************************************************/
  // start class init lamps
  // ****************************************************************************/

  Asset asLAMP;
  Asset webpge;
  Asset grab;
  int   headcode_1962  = 10;
  int   headcode_1962r = 10;
  bool  lamps_on = false;
  bool  lamps_disabled  = false;
  bool  lamps_disabledr = false;
  bool  setting_rear_lamps = false;
  void  Init_LampHandling(void);
  void  SetProperties_Lamps(Soup soup);
  void  GetProperties_Lamps(Soup soup);
  void  BrowserClick_HandleLamps(Message msg);
  void  HandleLightsOff(void);
  void  HandleLightsOn(void);
  void  HandleLampCouplingEvent(void);
  void  HandleLampDecouplingEvent(void);
  void  HandleHeadLight(Message msg);

  // browser for headcodes (1962 rule book)
  Browser hc1962;
  int  browser_hc1962_left;
  int  browser_hc1962_right;
  int  browser_hc1962_top;
  int  browser_hc1962_bottom;
  bool browser_hc1962_initialised = false;

  void   lampcode_all(bool setting_rear);      
  void   lampcode_none(bool setting_rear);
  void   lampcode_tail(bool setting_rear);
  void   lampcode_1962_1(bool setting_rear);
  void   lampcode_1962_2(bool setting_rear);
  void   lampcode_1962_3(bool setting_rear);
  void   lampcode_1962_4(bool setting_rear);
  void   lampcode_1962_5(bool setting_rear);
  void   lampcode_1962_6(bool setting_rear);
  void   lampcode_1962_7(bool setting_rear);
  void   lampcode_1962_8(bool setting_rear);
  void   lampcode_1962_0(bool setting_rear);
  void   set_lampcode_1962(int lampcode, bool setting_rear); 
  
  // ****************************************************************************/
  // end class init lamps
  // ****************************************************************************/

  // ****************************************************************************/
  // start class init steam effects Handling
  // ****************************************************************************/

  thread void SteamMonitor(void);
  string ViewDetails_HandlePFX(void);
  void   Toggle_pfx(void);
  bool   using_full_pfx = true;              // turns the cylinder cock effect etc on and off
  bool   drain_cocks_open = true;
  bool   drain_idletimer_active = false;
  int    nr_active_drain_timers = 0;
  int    DELAY = 0;

  // ****************************************************************************/
  // end class init steam effects Handling
  // ****************************************************************************/

  // ****************************************************************************/
  // start class init for view details browser
  // ****************************************************************************/

  public void ViewDetails(Message msg);
  void BrowserClick(Message msg);
  void BrowserClose(Message msg);

  // 'view details' browser
  Browser info;
  int  browser_left;
  int  browser_right;
  int  browser_top;
  int  browser_bottom;
  bool browser_initialised = false;

  // ****************************************************************************/
  // end class init for view details browser
  // ****************************************************************************/

  // Start of Code Block for hooks...

  void Init(void) {
    inherited();
    Asset meAsset;

    if (me.GetMyTrain()) {
      // Safe to start stuff if this is defined - will be undefined in Railyard and in the Surveyor menus
      meAsset = GetAsset();
      ashProduct = meAsset.FindAsset("loco_ash");
      ashQueue = me.GetQueue("ashpan0");
      ash_amount_max = ashQueue.GetQueueSize();
      ash_amount_current = ashQueue.GetQueueCount();
      ash_amount_increment = ash_amount_max / 5;
      // lamps
      Init_LampHandling();

      // add handlers for 'view details' browser window...
      AddHandler(me, "Browser-Closed", "", "BrowserClose");
      AddHandler(me, "Browser-URL",    "", "BrowserClick");

      // lampcode command handling
      AddHandler(me, "SetTrainReportingNumber", "", "HeadcodeCommandHandler");
	  AddHandler(me, "Train", "NotifyHeadlights", "HandleHeadLight");
	  
      // pfx
      SteamMonitor();
      AddHandler(me, "SetDrainIdleTimer", "", "DrainIdleHandler");
	  
      // This is the initialisation code for the animated couplings
      ACS_Init();

      // ash dump
	  AddHandler(me, "service_loco", "", "Message_Service_Loco_Handler");
	  AddHandler(me, "Increment_Ash_Queue", "", "Message_Ash_Queue_Increment_Handler");
    }
  }

  public void SetProperties(Soup soup) {
    // read the saved state and set the variables accordingly
    // if the tag doesn't have a value yet use the values we
    // already have...
    inherited(soup);
    SetProperties_Lamps(soup);
    // pfx
    using_full_pfx = soup.GetNamedTagAsBool("pfx", using_full_pfx);

  }

  public Soup GetProperties(void) {
    Soup soup = inherited();
    GetProperties_Lamps(soup);
    // pfx
    soup.SetNamedTag("pfx", using_full_pfx);
    
    return soup;
  }

  void BrowserClick(Message msg) {
    BrowserClick_HandleLamps(msg);
    // pfx +++
    if (msg.src == info) {
      if (msg.minor == "live://property/pfx") {
        Toggle_pfx();
        ViewDetails(msg);
      }
      if (msg.minor == "live://property/ash_dump_state") {
           if (ashQueue != null and ashProduct != null) {
                if (!dump_BAR and (ash_amount_current >= ash_amount_max / 10)) {
                     me.SetQueueInitialCount(ashQueue, ashProduct, 1);
                     dump_BAR = true;
                     ViewDetails(msg);
                     PostMessage(me, "service_loco", "dump_ash", 1.0);
                }
           }
      }
    }
  }

  void BrowserClose(Message msg) {
    if (msg.src == info) {
      info = null;
    }
    if (msg.src == hc1962) {
      hc1962 = null;
    }
  }

  public void ViewDetails(Message msg) {
    if (!info) {
      info = Constructors.NewBrowser();
      if (!browser_initialised) {
        browser_left   = 100;
        browser_top    = 100;
        browser_right  = 450;
        browser_bottom = 500;
        browser_initialised = true;
      }
    }
    else {
      // browser is already open, read the coordinates...
      browser_left   = info.GetWindowLeft();
      browser_top    = info.GetWindowTop();
      browser_right  = info.GetWindowRight();
      browser_bottom = info.GetWindowBottom();
    }
    StringTable strTable = GetAsset().GetStringTable();
    Train train = GetMyTrain();
    string paramOutput = "<html><body>";
    DriverCharacter driver = train.GetActiveDriver();
    //
    // Driver icon and Train name
    //
    paramOutput = paramOutput + "<table><tr height=32>";
    if (driver)
       paramOutput = paramOutput + "<td><img kuid='" + driver.GetAsset().GetKUID().GetHTMLString() + "' width=32 height=32></td>";
       paramOutput = paramOutput + "<td><b><font size=3 color=#000000>" + BrowserInterface.Quote(train.GetTrainDisplayName()) + "</font></b></td>";
       paramOutput = paramOutput + "</tr></table>";
       //
       // Total mass of all the vehicles in tow.
       //
       Vehicle[] vehicles = train.GetVehicles();
       int i;
       float totalMass = 0.0;
       for (i = 0; i < vehicles.size(); ++i) {
             if (!vehicles[i].isclass(Locomotive)) {
                totalMass = totalMass + vehicles[i].GetMass();
             }
       }
       string weightStr = ((string)(int)(totalMass / 1000)) + "t";
       paramOutput = paramOutput + "<p>" + strTable.GetString1("vehicle_view_details1", weightStr) + "</font></p>";
       //
       // Priority on track
       //
       paramOutput = paramOutput + "<p>" + strTable.GetString("interface-train-view-details-priority") + "<a href='live://property/priority-number'>" + (string)train.GetTrainPriorityNumber() + "</a></font></p>";
       //
       // optional features
       //
       // headcodes
       //
       paramOutput = paramOutput + "<p>Headcodes: <a href='live://property/hc1962'>" + "1962 rules</a></p>";
       paramOutput = paramOutput + "<p>Setting rear lamps : <a href='live://property/setrear'>" + setting_rear_lamps + "</a></p>";
       // pfx +++
       paramOutput = paramOutput + ViewDetails_HandlePFX();
       //
       // Train - Vehicle separator
       //
       paramOutput = paramOutput + "<br><table bgcolor=#000000D0 cellpadding=0 cellspacing=0 border=0><tr height=1><td width=100%></td></tr></table><br>";
       string quotedName = BrowserInterface.Quote(GetLocalisedName());
       //
       // Vehicle icon and name
       //
       KUID kuid = GetAsset().GetKUID();
       paramOutput = paramOutput + "<table><tr>";
       paramOutput = paramOutput + "<td><img kuid='" + kuid.GetHTMLString() + "' width=64 height=32></td>";
       paramOutput = paramOutput + "<td><b>" + quotedName + "</b></font></td>";
       paramOutput = paramOutput + "</tr></table>";
       paramOutput = paramOutput + "<p>" + strTable.GetString("interface-train-view-details-locomotive") + "</font></p>";
       int state = train.GetScheduleState();
       if (state != train.SS_NONE) {
          string historyString = train.GetScheduleStateString();
          paramOutput = paramOutput + "<p><i>" + BrowserInterface.Quote(historyString) + "</i></font></p>";
       }
       //
       // Product Queues
       //
       paramOutput = paramOutput + "<table>";
       bool isEmpty = true;
       ProductQueue[] vehicleQueues = GetQueues();
       for (i = 0; i < vehicleQueues.size(); i++) {
           ProductQueue vehicleQueue = vehicleQueues[i];
           Asset[] products = vehicleQueue.GetProductList();
           int l;
           for (l = 0; l < products.size(); l++) {
               paramOutput = paramOutput + HTMLWindow.GetPercentHTMLCode(null, vehicleQueue, products[l]);
               isEmpty = false;
           }
       }
       // Only report an empty queue if all queues are empty.
       /*
       if (isEmpty  and  (vehicleQueues.size() > 0))
          paramOutput = paramOutput + strTable.GetString1("vehicle_view_details2", quotedName);
*/
       // Ash dump control
       if (ashQueue != null and ashProduct != null) {
            paramOutput = paramOutput + "<tr><td>" + strTable.GetString("prompt_ash_dump")  + "</td><td><a href=live://property/ash_dump_state>";
            ash_amount_current = ashQueue.GetQueueCount();
            if (dump_BAR or (ash_amount_current < ash_amount_max / 10)) {
                 paramOutput = paramOutput + strTable.GetString("state_1") + "</a></td><br><br>";
            }
            else {
                 paramOutput = paramOutput + strTable.GetString("state_0") + "</a></td><br><br>";
            }
       paramOutput = paramOutput + "</table>";
       paramOutput = paramOutput + "</body></html>";
       info.SetWindowRect(browser_left, browser_top, browser_right, browser_bottom);
       info.LoadHTMLString(paramOutput);
    } 
  }

  public string GetDescriptionHTML(void) {
    Asset  meAsset = GetAsset();
    string htmlData = inherited();
    string s0 = "<p>Headcodes: <a href='live://property/hc1962'>" + "1962 rules</a> (close this window after selecting)</p></a><br>";
    string s1 = "<p>Setting rear lamps: <a href='live://property/setrear'>" + setting_rear_lamps + "</a></p></a><br>";
    string s2 = ViewDetails_HandlePFX();
    return htmlData + s0 + s1 + s2;
  }

  void LinkPropertyValue(string p_propertyID) {
    if (p_propertyID == "hc1962") {
      if (!hc1962) {
        hc1962 = Constructors.NewBrowser();
        if (!browser_hc1962_initialised) {
          browser_hc1962_left   = 100;
          browser_hc1962_top    = 100;
          browser_hc1962_right  = 500;
          browser_hc1962_bottom = 325;
          browser_hc1962_initialised = true;
        }
        hc1962.SetWindowRect(browser_hc1962_left, browser_hc1962_top, browser_hc1962_right, browser_hc1962_bottom);
        hc1962.LoadHTMLFile(webpge,"headcodes_1962.html");
      }
    }
    if (p_propertyID == "setrear") {
      setting_rear_lamps = !setting_rear_lamps;
    }
    // pfx
    if (p_propertyID == "pfx") {
      Toggle_pfx();
    }

    else {
      inherited (p_propertyID);
    }
  } // end LinkPropertyValue

  string GetPropertyType(string p_propertyID) {
    string s;
    if (p_propertyID == "hc1962")            {s = "link";}
    if (p_propertyID == "setrear")           {s = "link";}
    // pfx
    if (p_propertyID == "pfx")               {s = "link";}

    if ( s!= "link") {s = inherited (p_propertyID);}
    return s;
  }

  // ****************************************************************************/
  // start methods steam effects Handling
  // ****************************************************************************/

  thread void SteamMonitor(void) {
    if (using_full_pfx) {
      if(Math.Fabs(me.GetVelocity()) != 0) {
	    drain_idletimer_active = false;
	    if (drain_cocks_open) {
		  if (debug) Interface.Print("R1 Class: " + "loco is moving with drain cocks open...");
		  me.SendMessage(me, "pfx", "+2+3"); // drains on
          PostMessage(me, "pfx", "-2-3",Math.Rand(5.00f,12.00f)); // turn drains off after 5-12 seconds
		  if (debug) Interface.Print("R1 Class: " + "drain cocks will close after 5-12 seconds...");
		  drain_cocks_open = false;
		}
      }
	  else {
	    if (!drain_idletimer_active and !drain_cocks_open) {
	      DELAY = Math.Rand(20.00f,30.00f);
		  PostMessage(me, "SetDrainIdleTimer", "", DELAY);
		  nr_active_drain_timers++;
		  drain_cocks_open = false;
		  drain_idletimer_active = true;
		  if (debug) Interface.Print("R1 Class: " + "setting delay for drain cocks to open again in " + DELAY + " seconds...");	
		}
	  }
	  Sleep(2.0f);
      SteamMonitor();
    }
  }
  
  void DrainIdleHandler(Message msg) {
	if (nr_active_drain_timers > 0) {
	  if (debug) Interface.Print("R1 Class: " + "drain idle timer " + nr_active_drain_timers + " expired...");
	  nr_active_drain_timers--;
	  if(Math.Fabs(me.GetVelocity()) == 0 and nr_active_drain_timers == 0) {
	    if (debug) Interface.Print("R1 Class: " + "stopped, opening drain cocks...");
	    drain_cocks_open = true;
	  }
	}  
  }

  string ViewDetails_HandlePFX(void) {
    Asset  meAsset     = GetAsset();
    string paramOutput = "";
    paramOutput = paramOutput + "<p>" + meAsset.GetStringTable().GetString("prompt_pfx") + HTMLWindow.CheckBox("live://property/pfx", using_full_pfx) + "</a></p>";
    return paramOutput;
  }

   void Toggle_pfx(void) {
     if (!using_full_pfx) {
       using_full_pfx = true;
       SteamMonitor();
     }
     else {
       using_full_pfx = false;
       // SteamLeakEjector_Stop();
       PostMessage(me, "pfx", "-2-3-5-6",0.0f);
     }
   }

  // ****************************************************************************/
  // end methods steam effects Handling
  // ****************************************************************************/

  // ****************************************************************************/
  // start methods disable unloading at industries (Wulf_9)
  // ****************************************************************************/

  bool UnloadProduct(LoadingReport report) {
    bool UnloadFlag = false;
    return UnloadFlag;
  }

  // ****************************************************************************/
  // end methods disable unloading at industries (Wulf_9)
  // ****************************************************************************/

  // ****************************************************************************/
  // start methods headcodes (lamps)     
  // ****************************************************************************/

  void Init_LampHandling(void) {
    // find the lamps
    asLAMP = GetAsset().FindAsset("lamp");
    lampcode_none(false); // clear front lamps
    lampcode_none(true);  // clear rear lamps
    grab   = GetAsset();
    webpge = grab.FindAsset("headcodes_html");
  }

  void SetProperties_Lamps(Soup soup) {
    headcode_1962   = soup.GetNamedTagAsInt("headcode_1962",  headcode_1962);
    headcode_1962r  = soup.GetNamedTagAsInt("headcode_1962r", headcode_1962r);
    lamps_disabled  = soup.GetNamedTagAsInt("fr_lamps_dis",   lamps_disabled);
    lamps_disabledr = soup.GetNamedTagAsInt("rr_lamps_dis",   lamps_disabledr);
    lamps_on        = soup.GetNamedTagAsBool("lamps_on",      lamps_on);
    set_lampcode_1962(headcode_1962,  false);
    set_lampcode_1962(headcode_1962r, true);
  }

  void GetProperties_Lamps(Soup soup) {
    soup.SetNamedTag("headcode_1962",  headcode_1962);
    soup.SetNamedTag("headcode_1962r", headcode_1962r);
    soup.SetNamedTag("fr_lamps_dis",   lamps_disabled);
    soup.SetNamedTag("rr_lamps_dis",   lamps_disabledr);
    soup.SetNamedTag("lamps_on",       lamps_on);
  }

  void lampcode_tail(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", null);
    SetFXAttachment(prefix + "lamp_br", asLAMP);
    SetFXAttachment(prefix + "lamp_bl", asLAMP);
  }

  void lampcode_none(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", null);
    SetFXAttachment(prefix + "lamp_br", null);
    SetFXAttachment(prefix + "lamp_bl", null);
  }

  void lampcode_all(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", asLAMP);
    SetFXAttachment(prefix + "lamp_bc", asLAMP);
    SetFXAttachment(prefix + "lamp_br", asLAMP);
    SetFXAttachment(prefix + "lamp_bl", asLAMP);
  }

  void lampcode_1962_1(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", null);
    SetFXAttachment(prefix + "lamp_br", asLAMP);
    SetFXAttachment(prefix + "lamp_bl", asLAMP);
  }

  void lampcode_1962_2(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", asLAMP);
    SetFXAttachment(prefix + "lamp_bc", null);
    SetFXAttachment(prefix + "lamp_br", null);
    SetFXAttachment(prefix + "lamp_bl", null);
  }

  void lampcode_1962_3(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", asLAMP);
    SetFXAttachment(prefix + "lamp_br", asLAMP);
    SetFXAttachment(prefix + "lamp_bl", null);
  }

  void lampcode_1962_4(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", asLAMP);
    SetFXAttachment(prefix + "lamp_br", asLAMP);
    SetFXAttachment(prefix + "lamp_bl", null);
  }

  void lampcode_1962_5(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", asLAMP);
    SetFXAttachment(prefix + "lamp_bc", null);
    SetFXAttachment(prefix + "lamp_br", asLAMP);
    SetFXAttachment(prefix + "lamp_bl", null);
  }

  void lampcode_1962_6(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", asLAMP);
    SetFXAttachment(prefix + "lamp_br", null);
    SetFXAttachment(prefix + "lamp_bl", asLAMP);
  }

  void lampcode_1962_7(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", asLAMP);
    SetFXAttachment(prefix + "lamp_bc", null);
    SetFXAttachment(prefix + "lamp_br", null);
    SetFXAttachment(prefix + "lamp_bl", asLAMP);
  }

  void lampcode_1962_8(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", asLAMP);
    SetFXAttachment(prefix + "lamp_bc", asLAMP);
    SetFXAttachment(prefix + "lamp_br", null);
    SetFXAttachment(prefix + "lamp_bl", null);
  }

  void lampcode_1962_9(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", null);
    SetFXAttachment(prefix + "lamp_br", null);
    SetFXAttachment(prefix + "lamp_bl", asLAMP);
  }

  void lampcode_1962_0(bool setting_rear) {
    string prefix = "";
    if (setting_rear) {
       prefix = "r";
    }
    SetFXAttachment(prefix + "lamp_tc", null);
    SetFXAttachment(prefix + "lamp_bc", asLAMP);
    SetFXAttachment(prefix + "lamp_br", null);
    SetFXAttachment(prefix + "lamp_bl", null);
  }

  void set_lampcode_1962(int lampcode, bool setting_rear) {
    switch (lampcode) {
      case  0: lampcode_1962_0(setting_rear); break;
      case  1: lampcode_1962_1(setting_rear); break;
      case  2: lampcode_1962_2(setting_rear); break;
      case  3: lampcode_1962_3(setting_rear); break;
      case  4: lampcode_1962_4(setting_rear); break;
      case  5: lampcode_1962_5(setting_rear); break;
      case  6: lampcode_1962_6(setting_rear); break;
      case  7: lampcode_1962_7(setting_rear); break;
      case  8: lampcode_1962_8(setting_rear); break;
      case  9: lampcode_1962_9(setting_rear); break;
      case 10: lampcode_none(setting_rear)  ; break;
      case 11: lampcode_tail(setting_rear)  ; break;
      default: Interface.Print("Oops, Error in set_lampcode_1962!");
    }
    bool lights = me.GetMyTrain().GetHeadlightState();
    if (lights) {
      HandleLightsOn();
    }
    else {
      HandleLightsOff();
    }
  }

  void LampsAllOff(void) {
    MeshObject mo = GetFXAttachment("lamp_tc");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
    mo = GetFXAttachment("lamp_bc");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
    mo = GetFXAttachment("lamp_br");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
    mo = GetFXAttachment("lamp_bl");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
	
	mo = GetFXAttachment("rlamp_tc");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
    mo = GetFXAttachment("rlamp_bc");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
    mo = GetFXAttachment("rlamp_br");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
    mo = GetFXAttachment("rlamp_bl");
    if (mo) {
      mo.SetFXCoronaTexture("corona", null);
    }
	
  }

  void LampsWhiteOn(void) {
    MeshObject mo;
    Asset asCoronaWhite = me.GetAsset().FindAsset("corona_white");
	if (!lamps_disabled) {
		mo = GetFXAttachment("lamp_tc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
		mo = GetFXAttachment("lamp_bc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
		mo = GetFXAttachment("lamp_br");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
		mo = GetFXAttachment("lamp_bl");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
    }
	
	if (!lamps_disabledr) {
	    mo = GetFXAttachment("rlamp_tc");
    	if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
		mo = GetFXAttachment("rlamp_bc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
		mo = GetFXAttachment("rlamp_br");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
		mo = GetFXAttachment("rlamp_bl");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaWhite);
		}
	}
  }

  void LampsRedOn(void) {
    MeshObject mo;
    Asset asCoronaRed = me.GetAsset().FindAsset("corona_red");   
	if (!lamps_disabled) {
      if (headcode_1962 == 11) {
	    mo = GetFXAttachment("lamp_tc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
		mo = GetFXAttachment("lamp_bc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
		mo = GetFXAttachment("lamp_br");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
		mo = GetFXAttachment("lamp_bl");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
	  }
	}  
	
	if (!lamps_disabledr) {
      if (headcode_1962r == 11) {
	    mo = GetFXAttachment("rlamp_tc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
		mo = GetFXAttachment("rlamp_bc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
		mo = GetFXAttachment("rlamp_br");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
		mo = GetFXAttachment("rlamp_bl");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", asCoronaRed);
		}
	  }
    }	  
  }

  // end lamp code routines

  void HandleLightsOn(void) {
    MeshObject mo;
	lamps_on = true;
    if (!lamps_disabled) {
      if (headcode_1962 == 11) {
        LampsRedOn();       }
      else {
        LampsWhiteOn();
      }
    }
	if (!lamps_disabledr) {
      if (headcode_1962r == 11) {
        LampsRedOn();       }
      else {
        LampsWhiteOn();
      }
    }
  }

  void HandleLightsOff(void) {
    lamps_on = false;
    LampsAllOff();
  }

  void HandleLampCouplingEvent(void) {
    lamps_disabled  = bCFront;
    lamps_disabledr = bCRear;

	MeshObject mo; 
    if (lamps_disabled) {
		mo = GetFXAttachment("lamp_tc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", null);
		}
		mo = GetFXAttachment("lamp_bc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", null);
		}
		mo = GetFXAttachment("lamp_br");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", null);
		}
		mo = GetFXAttachment("lamp_bl");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", null);
		}
	  }	
 
	if (lamps_disabledr) {
      if (mo) {
	      mo = GetFXAttachment("rlamp_tc");
		  mo.SetFXCoronaTexture("corona", null);
		}
		mo = GetFXAttachment("rlamp_bc");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", null);
		}
		mo = GetFXAttachment("rlamp_br");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", null);
		}
		mo = GetFXAttachment("rlamp_bl");
		if (mo) {
		  mo.SetFXCoronaTexture("corona", null);
		}
    }
  }
  
  void HandleLampDecouplingEvent(void) {
    lamps_disabled  = bCFront;
    lamps_disabledr = bCRear;
	// turn the lamps back on if they were deactivated by a couple event...
	if (lamps_on) {
	  HandleLightsOn();
	}
    
  }

  void HandleHeadLight(Message msg) {
    if (debug) Interface.Print("R1 Class: in HeadLamp handler...");
        bool lights = me.GetMyTrain().GetHeadlightState();
		if (debug) Interface.Print("R1 Class: Headlamp state is " + lights);
        if (lights) {
		  if (debug) Interface.Print("R1 Class: Turning lights on...");
		  lamps_on = true;
          HandleLightsOn();
        }
        else {
		  if (debug) Interface.Print("R1 Class: Turning lights off...");
		  lamps_on = false;
          HandleLightsOff();
        }
  }
  
  void BrowserClick_HandleLamps(Message msg) {
    if (msg.src == info) {
      // handle click messages on the 'view details' browser here

      if (msg.minor == "live://property/setrear") {
        setting_rear_lamps = !setting_rear_lamps;
        ViewDetails(msg);
      }

      if (msg.minor == "live://property/hc1962") {
        if (!hc1962) {
          hc1962 = Constructors.NewBrowser();
          if (!browser_hc1962_initialised) {
            browser_hc1962_left   = 100;
            browser_hc1962_top    = 100;
            browser_hc1962_right  = 500;
            browser_hc1962_bottom = 325;
            browser_hc1962_initialised = true;
          }
          hc1962.SetWindowRect(browser_hc1962_left, browser_hc1962_top, browser_hc1962_right, browser_hc1962_bottom);
          hc1962.LoadHTMLFile(webpge,"headcodes_1962.html");
        }
      }
      else if (msg.minor == "live://property/priority-number") {
       GetMyTrain().SetTrainPriorityNumber( GetMyTrain().GetTrainPriorityNumber() % 3 + 1 );
       ViewDetails(msg);
      }
    }

    if (msg.src == hc1962) {
       if (msg.minor == "live://property/hc1962_1") {
         if (!setting_rear_lamps) {
           headcode_1962 = 1;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 1;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }

       }
       if (msg.minor == "live://property/hc1962_2") {
         if (!setting_rear_lamps) {
           headcode_1962 = 2;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 2;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_3") {
         if (!setting_rear_lamps) {
           headcode_1962 = 3;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 3;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_4") {
         if (!setting_rear_lamps) {
           headcode_1962 = 4;
		   set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 4;
		   set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_5") {
         if (!setting_rear_lamps) {
           headcode_1962 = 5;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 5;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_6") {
         if (!setting_rear_lamps) {
           headcode_1962 = 6;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 6;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_7") {
         if (!setting_rear_lamps) {
           headcode_1962 = 7;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 7;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_8") {
         if (!setting_rear_lamps) {
           headcode_1962 = 8;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 8;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_9") {
         if (!setting_rear_lamps) {
           headcode_1962 = 9;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 9;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_0") {
         if (!setting_rear_lamps) {
           headcode_1962 = 0;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 0;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_10") {
         if (!setting_rear_lamps) {
           headcode_1962 = 10;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 10;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       if (msg.minor == "live://property/hc1962_11") {
         if (!setting_rear_lamps) {
           headcode_1962 = 11;
           set_lampcode_1962(headcode_1962, setting_rear_lamps);
         }
         else {
           headcode_1962r = 11;
           set_lampcode_1962(headcode_1962r, setting_rear_lamps);
         }
       }
       bool lights = me.GetMyTrain().GetHeadlightState();
       if (lights) {
         if (!lamps_disabled) {
           if (!setting_rear_lamps) {
             if (headcode_1962 == 11) {
               set_lampcode_1962(headcode_1962, setting_rear_lamps);
               LampsRedOn();
             }
             else {
               set_lampcode_1962(headcode_1962, setting_rear_lamps);
               LampsWhiteOn();
             }
           }
           else {
             if (headcode_1962r == 11) {
               set_lampcode_1962(headcode_1962r, setting_rear_lamps);
               LampsRedOn();
             }
             else {
               set_lampcode_1962(headcode_1962r, setting_rear_lamps);
               LampsWhiteOn();
             }
           }
         }
       }
       // remember the window position...
       browser_hc1962_left   = hc1962.GetWindowLeft();
       browser_hc1962_top    = hc1962.GetWindowTop();
       browser_hc1962_right  = hc1962.GetWindowRight();
       browser_hc1962_bottom = hc1962.GetWindowBottom();
    }
  }

  // ****************************************************************************/
  // end methods headcodes (lamps)     
  // ****************************************************************************/

  // ****************************************************************************/
  // start methods headcode command Handling
  // ****************************************************************************/

  void HeadcodeCommandHandler(Message msg) {

    // read the message strng from the headcode driver command and set the loco lamps
    // accordingly...
    // for steam locos we are only interested in the first character after the first ':'
    // in the message...

    int   code;
    int   i;
    int   iIndex = -1;     // if still -1 after scanning the string then no ":" character found...
    bool  bFound = false;

    // scan msg.minor for the first ":" character...
    for (i = 0; i < msg.minor.size(); i++) {
      if (msg.minor[i] == ':') {
        if (!bFound) {
          iIndex = i + 1;
          bFound = true;
        }
      }
    }

    if (iIndex != -1) {
      // found a valid headcode in the command string...
      code =  msg.minor[iIndex] - 48;
      if (!bCFront) {
        // not coupled at the front, set the front lamps...
        headcode_1962 = code;
        set_lampcode_1962(headcode_1962, false);
      }
      if (!bCRear) {
        // not coupled at the rear, set the rear lamps...
        headcode_1962r = code;
        set_lampcode_1962(headcode_1962r, true);
      }
    }
    else {
      // this shouldn't happen...
      Interface.Print("Oops, wrongly formatted command string received (headcode)");
    }

  }

  // ****************************************************************************/
  // end methods headcode command Handling
  // ****************************************************************************/

  // ****************************************************************************/
  // start functions for ACS couplers/pipes
  // ****************************************************************************/

  // init ACS...

  void ACS_Init(void) {
    // Get the coupler assets to attach later on
    Asset meAsset = GetAsset();
    ACS_coupler_connected    =  meAsset.FindAsset("coupler_c");
    ACS_coupler_disconnected =  meAsset.FindAsset("coupler_i");

    ACS_vac_connected        =  meAsset.FindAsset("vac_c");
    ACS_vac_disconnected     =  meAsset.FindAsset("vac_i");

    ACSlib = World.GetLibrary(meAsset.LookupKUIDTable("acslib"));

    // Initialise couplers
    SetFXAttachment("coupler_front", ACS_coupler_disconnected);
    SetFXAttachment("coupler_back", ACS_coupler_disconnected);
    SetFXAttachment("vac_front",    ACS_vac_disconnected);
    SetFXAttachment("vac_back",     ACS_vac_disconnected);

    // randomise sleep values - should smooth out gameplay a bit if there are lots of cars
    sleepRandomiser = Math.Rand(2.25,3.75);

    // message handlers

    AddHandler(me, "Vehicle", "Coupled", "VehicleCoupleHandler");
    AddHandler(me, "Vehicle", "Decoupled", "VehicleDecoupleHandler");
    AddHandler(me, "Vehicle", "Derailed", "VehicleDerailHandler");

    // not sure this is necessary - I believe that BadCouple comes in addition to Coupled,
    // but what the hey, it isn't going to hurt to call it twice...
    AddHandler(me, "Vehicle", "BadCouple", "VehicleCoupleHandler");

    // lashed on as it happens to do the right thing
    AddHandler(me, "World", "ModuleInit", "VehicleDecoupleHandler");

    // ACS handler
    AddHandler(me, "ACScallback", "", "ACShandler");
  }

  // handle being derailed

  void VehicleDerailHandler(Message msg) {
    if(msg.src == me) {
      status = CAR_DERAILED;
    }
    if(myTrain) {
      Sniff(myTrain, "Train", "", false);
    }
  }

  // handler for couple events
  void VehicleCoupleHandler(Message msg) {

    status = IsEndCar();

    // two vehicles that couple generate two couple events, one from each
    // so we can just act on ones that come from ourself
    if(msg.src == me) {
      CheckCouple(IsEndCar(), GetDirectionRelativeToTrain());
    }

    if(status and runDM) {
      runDM = false;
      DirectionMonitor();
    }
    sniffMyTrain();
	HandleLampCouplingEvent();
  }

  // handler for decouple events
  void VehicleDecoupleHandler(Message msg) {

    status = IsEndCar();

    // only one decouple event is generated for each decouple,
    // not one for each half of train, so in this case have to check anyway
    // this is still reasonably efficient, as we can at least be confident
    // the decouple happened somewhere in our train

    // this forces a full train recalc on decouple. Temporary debugging code.
    //    bCFront = false;
    //    bCRear = false;

    CheckCouple(IsEndCar(), GetDirectionRelativeToTrain());

    if(status and runDM) {
      runDM = false;
      DirectionMonitor();
    }
    sniffMyTrain();
	HandleLampDecouplingEvent();
  }

  // utility functions

  // determine our position in this consist
  int IsEndCar() {
    Train consist;
    Vehicle[] cars;
    int rval = CAR_CENTER;

    consist = me.GetMyTrain();
    cars = consist.GetVehicles();
    if(me == cars[0]) {
      rval = rval + CAR_FRONT;
    }
    if(me == cars[cars.size() - 1]) {
      rval = rval + CAR_BACK;
    }
    return rval;
  }

  // listen to messages from my train only - also handles clearing up sniffs on old train

  void sniffMyTrain() {

    Train oldTrain = myTrain;

    myTrain = me.GetMyTrain();
    Sniff(myTrain,"SetTrainReportingNumber","", true);
	Sniff(myTrain,"Train","NotifyHeadlights", true);
    if(oldTrain) {
      if(oldTrain != myTrain) {
        Sniff(oldTrain, "Train", "", false);
        Sniff(myTrain, "Train", "", true);
      }
    } else {
      Sniff(myTrain, "Train", "", true);
    }
  }

      // The Direction Monitor checks every x seconds, in which direction our Vehicle is facing
  thread void DirectionMonitor() {
//    Interface.Log("Direction Monitor start");
    bool direction;

    int oldstatus = CAR_CENTER;
    bool olddirection = !GetDirectionRelativeToTrain();

    while(status > CAR_CENTER) // Exit when we are no longer an end car
    {
//      Interface.Log("Direction Monitor loop");
      status = IsEndCar();
      direction = GetDirectionRelativeToTrain();

      if((olddirection != direction) or (oldstatus != status)) {
        olddirection = direction;
        oldstatus = status;
      }

      Sleep(sleepRandomiser);
    }

    direction = GetDirectionRelativeToTrain();

    runDM = true;

    // Interface.Log("Direction Monitor end");
  }
  
  // these two are for controlling the bluestar couplings and hoses
  void CheckCouple(int cartype, bool direction) {
    // Interface.Log("CheckCouple called with cartype: \"" + cartype + "\" and direction: \"" + direction + "\"");

    GSObject[] veh = new GSObject[1];
    veh[0] = me;
    string[] front = new string[1];
    front[0] = "front";
    string[] back = new string[1];
    back[0] = "back";

    switch(cartype) {
      case CAR_DERAILED:
      case CAR_SINGLE:

        // Call ACS
        ACSlib.LibraryCall("ACSrecalc", front, veh);
        bCFront = false;

        ACSlib.LibraryCall("ACSrecalc", back, veh);
        bCRear = false;

        break;

      case CAR_CENTER:
        // call ACS on the appropriate end *note: this is IMPORTANT!*
        // Bugs will be introduced if you don't remember which ends
        // were coupled and only call ACS when the state has changed!

        if(!bCFront) {
          ACSlib.LibraryCall("ACSrecalc", front, veh);
          bCFront = true;
        }
        if(!bCRear) {
          ACSlib.LibraryCall("ACSrecalc", back, veh);
          bCRear = true;
        }
        break;

      case CAR_FRONT:
        if(direction) {
          if(bCFront) {
            ACSlib.LibraryCall("ACSrecalc", front, veh);
            bCFront = false;
          }
          if(!bCRear) {
            ACSlib.LibraryCall("ACSrecalc", back, veh);
            bCRear = true;
          }
        } else {
          if(bCRear) {
            ACSlib.LibraryCall("ACSrecalc", back, veh);
            bCRear = false;
          }
          if(!bCFront) {
            ACSlib.LibraryCall("ACSrecalc", front, veh);
            bCFront = true;
          }
        }
        break;

      case CAR_BACK:
        if(direction) {
          if(bCRear) {
            ACSlib.LibraryCall("ACSrecalc", back, veh);
            bCRear = false;
          }
          if(!bCFront) {
            ACSlib.LibraryCall("ACSrecalc", front, veh);
            bCFront = true;
          }
        } else {
          if(bCFront) {
            ACSlib.LibraryCall("ACSrecalc", front, veh);
            bCFront = false;
          }
          if(!bCRear) {
            ACSlib.LibraryCall("ACSrecalc", back, veh);
            bCRear = true;
          }
        }
        break;

      default:
        // this can't happen
        Interface.Log("Broken Code alert, someone called CheckCouple with a cartype of \"" + cartype + "\" and direction of \"" + direction + "\"");
        break;

    }

  }


  void ACShandler(Message msg) {
    // receive callbacks from ACS and act upon them

    // tokenise msg.minor into pipe ('|') separated strings
    string[] callback = Str.Tokens(msg.minor, "|");
    //Interface.Print(msg.minor);
    if(callback.size() >= 3) {

      if(callback[0] == "coupler") {
        if(callback[2] == "screwlink") {
          SetFXAttachment("coupler_" + callback[1], ACS_coupler_connected);
        } else {
          SetFXAttachment("coupler_" + callback[1], ACS_coupler_disconnected);
        }
      }
      else if(callback[0] == "vacbrake") {
        if (callback[2]== "high" or callback[2]== "single") {
          // connecting to a vehicle with a high or low vac hose...
          SetFXAttachment("vac_" + callback[1], ACS_vac_connected);
        }
        else {
          SetFXAttachment("vac_" + callback[1], ACS_vac_disconnected);
        }
      }
    } else {
      Interface.Log("ERROR: ACS Library returned invalid callback \"" + msg.minor + "\" - not divisible into at least 3 parts!");
    }
  }

  // ****************************************************************************/
  // end functions for ACS couplers/pipes
  // ****************************************************************************/
  // ****************************************************************************/
  // start functions for Service_Loco messages
  // ****************************************************************************/
  
  void Message_Ash_Queue_Increment_Handler(Message msg) {
       if (Math.Fabs(me.GetVelocity()) > 0) {



	    if (ashQueue   == null) return;
	    if (ashProduct == null) return;
            ash_amount_current = ashQueue.GetQueueCount() + ash_amount_increment;
	    me.SetQueueInitialCount(ashQueue, ashProduct, ash_amount_current);

       }
       if (ash_amount_current < ash_amount_max) {
	    PostMessage(me, "Increment_Ash_Queue", "", ash_replenish_delay);
       }
  }


  void Message_Service_Loco_Handler(Message msg) {

    if (msg.minor == "dump_ash") {
      ScheduleSuspended = false;
      DriverCharacter myDriver = GetMyTrain().GetActiveDriver();

      if(myDriver.IsRunningCommand()) {
        myDriver.SetRunningCommands(false);  // suspend schedule if a schedule is running
        ScheduleSuspended = true;
      }
	
	  // me.SetMeshAnimationSpeed("ashdoors",0.5);
      // TestandPlay(GetWAVFile("DoorsOpen"), 0.7, 75.0, "a.AshBack");

      // TestandPlay(GetWAVFile("BulkDump"), 0.5, 50.0, "a.AshBack");
      PostMessage(me, "pfx", "+7+8+9+10",1.0);
      PostMessage(me, "service_loco", "dump_ash_cease", duration_ash_dump);
    }

    if (msg.minor == "dump_ash_cease") {


      PostMessage(me, "pfx", "-7-8-9-10",0.0);
      Sleep(Math.Rand(2.5, 5.0));
      // me.SetMeshAnimationSpeed("ashdoors",-1.0);
      Sleep(0.5);

      // TestandPlaySleep(GetWAVFile("DoorsShut"), 0.9, 100.0);

/*
	  // tell the sender of the message how much ash we have dumped...THIS IS UNNECESSARY AS ASH PIT FACILITY IS NOW A BASE INDUSTRY
      PostMessage(msg.src, "ash_amount", ash_amount_current,0.0);

*/
      // refill queue by incrementing the amount every 'ash_replenish_delay' seconds...


      ash_amount_current = 1;
      me.SetQueueInitialCount(ashQueue, ashProduct, 1);
      PostMessage(me, "Increment_Ash_Queue", "", ash_replenish_delay);
      
      dump_BAR = false; // release block on user-initiated ash dumps

      // resume schedule...

      if(ScheduleSuspended) {
           DriverCharacter myDriver = GetMyTrain().GetActiveDriver();
           myDriver.GetDriverCommands().ProceedToNextCommand();
           myDriver.SetRunningCommands(true);
      }

    }
	
  }
  
  // ****************************************************************************/
  // end functions for Service_Loco messages
  // ****************************************************************************/
};

