//------------------------------------------------------------------------------
// File: client_alerts.cs
// This file contains the client implementation of the alert system. The client
// is responsible for the displaying and moving of alert text and buttons.
// Copyright Sandlot Games, 2007
// Author: Matthew Rudge
//------------------------------------------------------------------------------

//-Alert Hud Globals------------------------------------------------------------
$AlertBtnInitPos = "279 52";
$AlertBtnSpeed = 1000;
$AlertBtnWait  = 2;

// Alert text location values. Alert text values will determine alert message
// layout in alert hud
$AlertMsgSpacing = 20;
$AlertMsgDestroy = 4.3;
$AlertMsgFade    = 3.5;
$AlertMsgCount = 0;
$AlertMsg = 0;

// For updating alert system
$AlertUpdateTimer  = 0;
//------------------------------------------------------------------------------

//-Alert Containers-------------------------------------------------------------
// Array of indexes for existing buttons snapped in place
$AlertIdx[$ALERT_BANDIT]  = "";
$AlertIdx[$ALERT_FIRE]    = "";
$AlertIdx[$ALERT_PLAGUE]  = "";
$AlertIdx[$ALERT_QUEST]   = "";
$AlertIdx[$ALERT_TORNADO] = "";
$AlertIdx[$ALERT_DROUGHT] = "";
$AlertIdx[$ALERT_HUNGER]  = "";

$AlertOpenIdx = 0;

// Locked alert button group
$AlertBtnLock      = "AlertBtnLock";
$AlertBtnLockCount = 0;

// Alert transition button group
$AlertBtnMove      = "AlertBtnMove";
$AlertBtnMoveCount = 0;
//------------------------------------------------------------------------------

//******************************************************************************
//* Alert System Routines
//******************************************************************************
////////////////////////////////////////////////////////////////////////////////
//! Sends a message to the alert hud and adds an alert hud button
//! \param %alert Alert that occurred
//! \param %count Count for alert that occurred. Used by server to move and 
//! position camera.
//! \param %message Message to put on screen (if any)
////////////////////////////////////////////////////////////////////////////////
function alertAdd(%alert, %count, %message)
{
   // Create alert update timer if necessary
   alertCreateUpdateTimer();
   
   // Play sound for quest alerts
   if(%alert == $ALERT_QUEST) {
      playSFXSound("audio_questavailable");
   }
   // Play sound effects for alerts
   else if(%alert == $ALERT_FIRE) {
      playSFXSound("audio_fire");
   }
   else if(%alert == $ALERT_TORNADO) {
      playSFXSound("audio_tornado");
   }
   else if(%alert == $ALERT_DROUGHT) {
      playSFXSound("audio_drought");
   }
   else if(%alert == $ALERT_PLAGUE) {
      playSFXSound("audio_plague");
   }
   else if(%alert == $ALERT_HUNGER) {
      playSFXSound("audio_hunger");
   }
   
   // If there is a message then
   if(%message !$= "") {
      // Output message
      alertPushMsg(%message);
      
      // Create and initialize alert button
      %btn = alertCreateBtn(%alert, %count);
   
      // Start the button moving
      alertStartBtnMove(%btn, true);
   }
   
   // No message just change locked or moving buttons already created
   else {
      // Update locked button if there is one
      %btn = alertGetBtnInLockedGroup(%alert);
      if(isObject(%btn)) {
         %btn.addAlertNum(%count);
      }
      
      // If there isn't a locked button then it might be in the moving group
      else {
         %btn = alertGetBtnInMovingGroup(%alert);
         if(isObject(%btn)) {
            %btn.addAlertNum(%count);
         }
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Sends a message to the alert hud and removes the alert hud button
//! \param %alert Alert that occurred
//! \param %count Count for alert that occurred
//! \param %message Message to put on screen (if any)
////////////////////////////////////////////////////////////////////////////////
function alertRemove(%alert, %count, %message)
{   
   // Remove from moving group first
   %btn = alertGetBtnInMovingGroup(%alert);
   while(isObject(%btn) && %count > 0) {      
      // Update amount left
      %btnAmount = %btn.alertCount;
      
      // Decrement alert count
      %btn.subAlertNum(%count);
      %count -= %btnAmount;
      
      // If count is zero then
      if(%btn.alertCount <= 0) {
         // Stop button from moving and destroy it
         alertStopBtnMove(%btn);
         alertDestroyBtn(%btn);
      }
      
      // Get next moving button
      %btn = alertGetBtnInMovingGroup(%alert);
   }
      
   // Remove from locked group next
   %btn = alertGetBtnInLockedGroup(%alert);   
   if(isObject(%btn)) {
      if(%count > 0) {
         // Decrement alert count
         %btn.subAlertNum(%count);
         
         // If alert count is zero then
         if(%btn.alertCount <= 0) {
            // Unlock and destroy button
            alertUnlockBtn(%btn);
            alertDestroyBtn(%btn);
            
            // Update moving button list indexes
            alertResetMovingBtnIndexes();
            
            // Update locked button list
            alertUpdateLockedBtns();
         }
      }
   }
   
   if(!isObject(alertGetBtnInMovingGroup(%alert)) &&
      !isObject(alertGetBtnInLockedGroup(%alert))) {
      // Clear button index if no buttons are left
      alertClearBtnIndex(%alert);
   }
   
   // Output message if necessary
   if(%message !$= "") {
      alertPushMsg(%message);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Destroys and resets the entire alert system
////////////////////////////////////////////////////////////////////////////////
function alertDestroySystem()
{
   // Destroy all locked buttons
   for(%i = 0; %i < $AlertBtnLockCount; %i++) {
      alertDestroyBtn($AlertBtnLock[%i]);
      $AlertBtnLock[%i] = "";
   }
   $AlertBtnLockCount = 0;
   
   // Destroy all moving buttons
   for(%i = 0; %i < $AlertBtnMoveCount; %i++) {
      alertDestroyBtn($AlertBtnMove[%i]);
      $AlertBtnMove[%i] = "";
   }
   $AlertBtnMoveCount = 0;
   
   // Destroy timer
   if(isObject($AlertUpdateTimer)) {
      $AlertUpdateTimer.endTimer();
      $AlertUpdateTimer = 0;
   }
   
   // Destroy messages
   for(%i = 0; %i < $AlertMsgCount; %i++) {
      alertDestroyMsg($AlertMsg[%i], true);
      $AlertMsg[%i] = "";
   }
   $AlertMsgCount = 0;
   
   // Destroy indexes
   for(%i = 0; %i < $ALERT_COUNT; %i++) {
      $AlertIdx[%i] = "";
   }
   $AlertOpenIdx = 0;
}

////////////////////////////////////////////////////////////////////////////////
//! Updates the alert system
//! \param %fTime Time since last update
////////////////////////////////////////////////////////////////////////////////
function alertUpdate(%fTime)
{   
   // Update moving buttons
   alertUpdateMovingBtns(%fTime);
}

////////////////////////////////////////////////////////////////////////////////
//! Creates the alert system update timer if not created yet. This method is
//! called once one alert is added to the system.
////////////////////////////////////////////////////////////////////////////////
function alertCreateUpdateTimer()
{
   // Create alert timer if necessary
   if(!isObject($AlertUpdateTimer)) {
      $AlertUpdateTimer = new SLEndlessTimer() {
         time = 0;
      };
      $AlertUpdateTimer.notifyOnUpdate(alertUpdate);
   }
}

//******************************************************************************
//* Alert Button Routines
//******************************************************************************
////////////////////////////////////////////////////////////////////////////////
//! Called when an alert button has been pressed in the alert hud. This method
//! is responsible for centering on the next object in its object group.
////////////////////////////////////////////////////////////////////////////////
function AlertBtn::buttonSelect(%this)
{
   // Nothing to select
   if(%this.alertCount == 0) {
      return;
   }
   
   // Notify server of selection
   commandToServer('AlertSelect', %this.alertType, %this.alertSel);
   
   // Update selection for next button click
   %this.alertSel++;
   if(%this.alertSel >= %this.alertCount) {
      %this.alertSel = 0;
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds to the current alert count under the button
//! \param %num Amount to add
////////////////////////////////////////////////////////////////////////////////
function AlertBtn::addAlertNum(%this, %num)
{
   // Add to count
   %this.alertCount += %num;
   
   // Update alert text if locked
   if(%this.lock) {
      %text = alertGetBtnText(alertGetBtnIndex(%this.alertType));
      %text.stateUp = %this.alertCount;
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Subtracts from the current alert count under the button
//! \param %num Amount to subtract
////////////////////////////////////////////////////////////////////////////////
function AlertBtn::subAlertNum(%this, %num)
{
   //Subtract from count
   %this.alertCount -= %num;
      
   // Reset alert selection if necessary
   if(%this.alertSel >= %this.alertCount) {
      %this.alertSel = 0;
   }
      
   // Update alert text if locked
   if(%this.lock) {
      %text = alertGetBtnText(alertGetBtnIndex(%this.alertType));
      %text.stateUp = %this.alertCount;
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Creates an alert button given an alert
//! \param %alert Alert button to create
//! \param %count Number for alert occurrence
//! \retval int Id of alert button
////////////////////////////////////////////////////////////////////////////////
function alertCreateBtn(%alert, %count)
{
   if(firstword(getRes()) > 800)
   {
      $AlertBtnInitPos = "260 52";
   }
   else
   {
      $AlertBtnInitPos = "208 43";
   }
   %btn = new SLImage(AlertBtn : AlertBtnTemp0) {
      Visible    = true;
      Position   = $AlertBtnInitPos;
      Extent = Extent.AlertBtnTemp0;
      // Dynamic fields
      alertType  = %alert;
      alertCount = %count;
      alertSel   = 0;
   };
   
   // Alert-specific button field set
   switch(%alert) {
      case $ALERT_BANDIT:
         %btn.stateUp     = "assets/client/ui/icons/dot.png";
         %btn.stateFocus  = "assets/client/ui/icons/alertbtnfocus.png";
         %btn.iconEnabled = "assets/client/ui/icons/alertattack.png";
      case $ALERT_FIRE:
         %btn.stateUp     = "assets/client/ui/icons/dot.png";
         %btn.stateFocus  = "assets/client/ui/icons/alertbtnfocus.png";
         %btn.iconEnabled = "assets/client/ui/icons/alertfire.png";
      case $ALERT_PLAGUE:
         %btn.stateUp     = "assets/client/ui/icons/dot.png";
         %btn.stateFocus  = "assets/client/ui/icons/alertbtnfocus.png";
         %btn.iconEnabled = "assets/client/ui/icons/alertplague.png";
      case $ALERT_QUEST:
         %btn.stateUp     = "assets/client/ui/icons/dot.png";
         %btn.stateFocus  = "assets/client/ui/icons/alertbtnfocus.png";
         %btn.iconEnabled = "assets/client/ui/icons/alertquest.png";
      case $ALERT_TORNADO:
         %btn.stateUp     = "assets/client/ui/icons/dot.png";
         %btn.stateFocus  = "assets/client/ui/icons/alertbtnfocus.png";
         %btn.iconEnabled = "assets/client/ui/icons/alerttornado.png";
      case $ALERT_DROUGHT:
         %btn.stateUp     = "assets/client/ui/icons/dot.png";
         %btn.stateFocus  = "assets/client/ui/icons/alertbtnfocus.png";
         %btn.iconEnabled = "assets/client/ui/icons/alertdrought.png";
      case $ALERT_HUNGER:
         %btn.stateUp     = "assets/client/ui/icons/dot.png";
         %btn.stateFocus  = "assets/client/ui/icons/alertbtnfocus.png";
         %btn.iconEnabled = "assets/client/ui/icons/alerthunger.png";
   }
   
   // Add to gui
   AlertButtonGroup.addGuiControl(%btn);
   //AlertHud.addGuiControl(%btn);
   
   return %btn;
}

////////////////////////////////////////////////////////////////////////////////
//! Destroys an alert button and its allocated memory
//! \param %btn Button to destroy
////////////////////////////////////////////////////////////////////////////////
function alertDestroyBtn(%btn)
{
   if(!isObject(%btn)) {
      return;
   }
   
   // Clear button text
   if(%btn.lock) {
      %txt = alertGetBtnText(alertGetBtnIndex(%btn.alertType));
      %txt.stateUp = "";
   }
   
   // Delete alert type
   %btn.alertType = "";
   
   // Delete timer
   %btn.alertTime = "";
   
   // Delete button
   %btn.delete();
}

////////////////////////////////////////////////////////////////////////////////
//! Updates the moving button list after a button has been removed from the 
//! locked list
////////////////////////////////////////////////////////////////////////////////
function alertResetMovingBtnIndexes()
{
   // Go through moving buttons
   for(%i = 0; %i < $AlertBtnMoveCount; %i++) {
      // Get moving button
      %moveBtn = $AlertBtnMove[%i];
      
      // If alert is not in locked button group do
      %lockBtn = alertGetBtnInLockedGroup(%moveBtn.alertType);
      if(!isObject(%lockBtn)) {
         // Clear button index
         alertClearBtnIndex(%moveBtn.alertType);
         
         // Assign a new one
         alertAssignBtnIndex(%moveBtn.alertType);
      }
   }
   
}


////////////////////////////////////////////////////////////////////////////////
//! Updates the locked button list after a button has been removed
////////////////////////////////////////////////////////////////////////////////
function alertUpdateLockedBtns()
{
   for(%i = 0; %i < $AlertBtnLockCount; %i++) {
      // Get alert button
      %btn = $AlertBtnLock[%i];
      
      // Don't move alert if lower than open index
      if(alertGetBtnIndex(%btn.alertType) < $AlertOpenIdx) {
         continue;
      }
      else {
         // Unlock button
         alertUnlockBtn(%btn);
         
         // Start button moving
         alertStartBtnMove(%btn, false);
         %i--;
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Updates the moving button list
//! \param %fTime Elapsed time since update timer started
////////////////////////////////////////////////////////////////////////////////
function alertUpdateMovingBtns(%fTime)
{
   // For now, snap button into place
   for(%i = 0; %i < $AlertBtnMoveCount; %i++) {
      %btn = $AlertBtnMove[%i];
      if(!isObject(%btn)) {
         continue;
      }
      
      // Get elapsed time since last update
      %elapsed = %fTime - %btn.alertTime;
      
      // Add to total elapsed time
      %btn.totalTime += %elapsed;
      
      // Reset alert time
      %btn.alertTime = %fTime;
      
      if(%btn.totalTime < $AlertBtnWait) {
         continue;
      }
      
      // Get current position of button
      %position = %btn.actualPosition;
      if(%position $= "") {
         %position = %btn.Position;
      }
      
      %curPosX = getWord(%position, 0);
      %curPosY = getWord(%position, 1);
      
      // Get destination button
      %alertTempBtn = alertGetTemplateBtn(alertGetBtnIndex(%btn.alertType));
      
      // Set new vector towards final position if necessary
      if(%btn.finalPosition !$= %alertTempBtn.Position) {
         // Get distance to final position
         %desPosX = getWord(%alertTempBtn.Position, 0);
         %desPosY = getWord(%alertTempBtn.Position, 1);
         %vX = %curPosX - %desPosX;
         %vY = %curPosY - %desPosY;
         
         %btn.finalPosition = %alertTempBtn.Position;
         %btn.trajectory    = VectorNormalize(%vX SPC %vY);
         %btn.trajectory    = VectorScale(%btn.trajectory, $AlertBtnSpeed);
         %btn.totalDistance = VectorLen(%vX SPC %vY);
      }
      
      // Move button towards destination
      %dis  = $AlertBtnSpeed * %elapsed;
      %vLoc = VectorScale(%btn.trajectory, %elapsed);
      %vLoc = VectorSub(%curPosX SPC %curPosY, %vLoc);
      %btn.setPosition(getWord(%vLoc, 0), getWord(%vLoc, 1));
      %btn.actualPosition = %vLoc;
      %btn.totalDistance -= (%dis);
      
      // Close enough to stop the button from moving
      if(mAbs(%btn.totalDistance) <= %dis || %btn.totalDistance <= 0) {
         alertStopBtnMove(%btn);
         %i--;
         alertLockBtn(%btn);
         continue;
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a button to a specific group (moving or locked group)
//! \param %group Group to add to
//! \param %groupCnt Number of buttons in group
//! \param %btn Button to add to group
//! \retval int New size for group
////////////////////////////////////////////////////////////////////////////////
function alertAddBtnToBtnGroup(%group, %groupCnt, %btn)
{
   // Add button to group
   if(%group $= $AlertBtnLock) {
      $AlertBtnLock[%groupCnt] = %btn;
   }
   else {
      $AlertBtnMove[%groupCnt] = %btn;
   }
   
   // Return new count
   return %groupCnt + 1;
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a button to the lock group
//! \param %btn Button to add
////////////////////////////////////////////////////////////////////////////////
function alertAddBtnToLockGroup(%btn)
{
   $AlertBtnLockCount = alertAddBtnToBtnGroup($AlertBtnLock, $AlertBtnLockCount, %btn);
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a button to the move group
//! \param %btn Button to add
////////////////////////////////////////////////////////////////////////////////
function alertAddBtnToMoveGroup(%btn)
{
   $AlertBtnMoveCount = alertAddBtnToBtnGroup($AlertBtnMove, $AlertBtnMoveCount, %btn);
}

////////////////////////////////////////////////////////////////////////////////
//! Removes a button from a specific group (moving or locked group)
//! \param %group Group to remove from
//! \param %groupCnt Number of buttons in group
//! \param %btn Button to remove from group
//! \retval int New size of group
////////////////////////////////////////////////////////////////////////////////
function alertRemBtnFromBtnGroup(%group, %groupCnt, %btn)
{
   // Are we dealing with lock or move group?
   %bLockGroup = false;
   if(%group $= $AlertBtnLock) {
      %bLockGroup = true;
   }
   
   // Loop through button group and remove button 
   for(%index = 0; %index < %groupCnt; %index++) {
      // Look at lock group
      if(%bLockGroup) {
         if($AlertBtnLock[%index] == %btn) {
            $AlertBtnLock[%index] = "";
         }
      }
      // Move group
      else {
         if($AlertBtnMove[%index] == %btn) {
            $AlertBtnMove[%index] = "";
         }
      }
   }
   
   // Collapse button group list
   %next = 0;
   for(%index = 0; %index < %groupCnt; %index++) {
      // Lock group
      if(%bLockGroup) {
         $AlertBtnLock[%next] = $AlertBtnLock[%index];
         if($AlertBtnLock[%next] !$= "") {
            %next++;
         }
      }
      // Move group
      else {
         $AlertBtnMove[%next] = $AlertBtnMove[%index];
         if($AlertBtnMove[%next] !$= "") {
            %next++;
         }
      }
   }
   
   // Return remaining count
   return %next;
}

////////////////////////////////////////////////////////////////////////////////
//! Removes a button from the lock group
//! \param %btn Button to remove
////////////////////////////////////////////////////////////////////////////////
function alertRemBtnFromLockGroup(%btn)
{
   $AlertBtnLockCount = alertRemBtnFromBtnGroup($AlertBtnLock, $AlertBtnLockCount, %btn);
}

////////////////////////////////////////////////////////////////////////////////
//! Removes a button from the move group
//! \param %btn Button to remove
////////////////////////////////////////////////////////////////////////////////
function alertRemBtnFromMoveGroup(%btn)
{
   $AlertBtnMoveCount = alertRemBtnFromBtnGroup($AlertBtnMove, $AlertBtnMoveCount, %btn);
}

////////////////////////////////////////////////////////////////////////////////
//! Gets the alert button in the locked group that corresponds to the alert type
//! \param %alert Alert type
//! \retval int Id of button or 0 if the alert is not in locked group
////////////////////////////////////////////////////////////////////////////////
function alertGetBtnInLockedGroup(%alert)
{
   // For each button in locked group do
   for(%i = 0; %i < $AlertBtnLockCount; %i++) {
      %btn = $AlertBtnLock[%i];
      if(!isObject(%btn)) {
         continue;
      }
      
      // If this button is of alert type then return it
      if(%btn.alertType == %alert) {
         return %btn;
      }
   }
   
   // Return default (no button that has alert type)
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
//! Gets the alert button in the moving group that corresponds to the alert type
//! \param %alert Alert type
//! \retval int Id of button or 0 if the alert is not in moving group
////////////////////////////////////////////////////////////////////////////////
function alertGetBtnInMovingGroup(%alert)
{
   // For each button in locked group do
   for(%i = 0; %i < $AlertBtnMoveCount; %i++) {
      %btn = $AlertBtnMove[%i];
      if(!isObject(%btn)) {
         continue;
      }
      
      // If this button is of alert type then return it
      if(%btn.alertType == %alert) {
         return %btn;
      }
   }
   
   // Return default (no button that has alert type)
   return 0;
}

////////////////////////////////////////////////////////////////////////////////
//! Gets the template alert button
//! \param %index Index of template alert button
//! \retval int Id of alert button
////////////////////////////////////////////////////////////////////////////////
function alertGetTemplateBtn(%index)
{
   return ("AlertBtnTemp" @ %index);   
}

////////////////////////////////////////////////////////////////////////////////
//! Returns the button index for an alert type
//! \param %alert Alert type
//! \retval int Index for button
////////////////////////////////////////////////////////////////////////////////
function alertGetBtnIndex(%alert)
{
   return $AlertIdx[%alert];
}

////////////////////////////////////////////////////////////////////////////////
//! Clears the button index for the alert type
//! \param %alert Alert type
////////////////////////////////////////////////////////////////////////////////
function alertClearBtnIndex(%alert)
{
   $AlertIdx[%alert] = "";
   alertUpdateOpenIndex();
}

////////////////////////////////////////////////////////////////////////////////
//! Assigns a button index for the alert type if it does not already have one
//! \param %alert Alert type
////////////////////////////////////////////////////////////////////////////////
function alertAssignBtnIndex(%alert)
{
   if($AlertIdx[%alert] $= "") {
      $AlertIdx[%alert] = $AlertOpenIdx;
      alertUpdateOpenIndex();
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Updates the open index to point to the next open slot for the next alert
//! button
////////////////////////////////////////////////////////////////////////////////
function alertUpdateOpenIndex()
{
   // Update open index to next open
   $AlertOpenIdx = 0;
   for(%i = 0; %i < $ALERT_COUNT; %i++) {
      if($AlertIdx[%i] $= $AlertOpenIdx) {
         $AlertOpenIdx++;
         // To restart at zero next loop
         %i = -1;
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Gets the alert button text number
//! \param %index Index of template alert button that this text is attached to
//! \retval int Id of alert text
////////////////////////////////////////////////////////////////////////////////
function alertGetBtnText(%index)
{
   return ("AlertNum" @ %index);
}


//******************************************************************************
//* Alert Message Routines
//******************************************************************************
////////////////////////////////////////////////////////////////////////////////
//! Pushes a new alert message onto the alert message queue
//! \param %text Text for alert message
////////////////////////////////////////////////////////////////////////////////
function alertPushMsg(%text)
{
   // Create message
   %newMsg = new SLTextImage()
   {
      textUp = %text;
      visible = true;
      input = false;
      applyAlphaToText = true;
      stateUp = AlertMsgTemp.stateUp;
      profile = AlertMsgTemp.profile;
      horizSizing = "relative";
      vertSizing = "relative";
      position = AlertMsgTemp.position;
      extent = AlertMsgTemp.extent;
   };

   // Add to gui
   AlertHudMsg.addGuiControl(%newMsg);
   
   // Distance to next message
   %yDiff = (getWord(AlertMsgTemp.Extent, 1) + $AlertMsgSpacing);
   
   // For each message in existence do   
   for(%i = $AlertMsgCount; %i > 0; %i--) {
      // Move it up one in the array of messages
      $AlertMsg[%i] = $AlertMsg[%i - 1];
      
      // Move its position up by extent and offset
      %msg = $AlertMsg[%i];
      if(isObject(%msg)) {
         %xMsg = getWord(%msg.Position, 0);
         %yMsg = getWord(%msg.Position, 1) + %yDiff;
         %msg.Position = %xMsg SPC %yMsg;
      }
   }
   
   // Store new message
   $AlertMsg[0] = %newMsg;
   $AlertMsgCount++;
   
   // Create destroy and fade timers for the message
   %timer = new SLTimer() {
      time = $AlertMsgDestroy;
   };
   %timer.notifyOnFire(alertPopMsg);
   %newMsg.destroytimer = %timer;
   %timer = new SLTimer() {
      time = $AlertMsgFade;
   };
   %timer.notifyOnFire(alertFadeMsg, %newMsg);
   // Need to save timer for save/load
   %newMsg.fadetimer = %timer;
   
   // For each button in moving group do
   for(%i = 0; %i < $AlertBtnMoveCount; %i++) {
      %btn = $AlertBtnMove[%i];
      if(!isObject(%btn)) {
         continue;
      }
      
      // If button's total time is less than time then
      if(%btn.totalTime < $AlertBtnWait) {
         // Push button down
         %xBtn = getWord(%btn.Position, 0);
         %yBtn = getWord(%btn.Position, 1) + %yDiff;
         %btn.Position = %xBtn SPC %yBtn;
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Pops a message at the front of the queue
////////////////////////////////////////////////////////////////////////////////
function alertPopMsg()
{
   // Occasionally, the message count happens to be zero or less when this
   // function is called. Putting a stop to it by checking for zero and setting
   // the message count explicitly to zero.
   if($AlertMsgCount <= 0) {
      $AlertMsgCount = 0;
      return;
   }
   
   // Decrement message count
   $AlertMsgCount--;
   
   // Get last message in queue and clear it
   %msg = $AlertMsg[$AlertMsgCount];
   $AlertMsg[$AlertMsgCount] = "";
   
   // Delete message
   if(isObject(%msg)) {
      alertDestroyMsg(%msg, false);
   }   
}

////////////////////////////////////////////////////////////////////////////////
//! Starts the fading process for the message
////////////////////////////////////////////////////////////////////////////////
function SLTextImage::alertFadeMsg(%this)
{
   // Start fade of this message
   %this.fadetimer = 0;
   %this.FadeOut($AlertMsgDestroy - $AlertMsgFade);
}

////////////////////////////////////////////////////////////////////////////////
//! Destroys a message and its allocated memory
//! \param %msg Message to destroy
//! \param %deltimers True to delete the timers
////////////////////////////////////////////////////////////////////////////////
function alertDestroyMsg(%msg, %deltimers)
{
   if(!isObject(%msg)) {
      return;
   }
   
   if(%deltimers) {   
      if(isObject(%msg.destroytimer)) {
         %msg.destroytimer.delete();
      }
      if(isObject(%msg.fadetimer)) {
         %msg.fadetimer.delete();
      }
   }
    
   // Destroy message
   %msg.delete();
}

//******************************************************************************
//* Hud Positioning Routines
//******************************************************************************
////////////////////////////////////////////////////////////////////////////////
//! Called to start a button moving by placing it in the button move group and
//! assigning it a new button index
//! \param %btn Button to start moving
//! \param %wait True if the button is to wait for a set amount of time before
//! moving
////////////////////////////////////////////////////////////////////////////////
function alertStartBtnMove(%btn, %wait)
{
   // Assigns an alert index
   alertAssignBtnIndex(%btn.alertType);
   
   // Adds button to move group
   alertAddBtnToMoveGroup(%btn);
   
   // Store starting time
   %btn.alertTime = $AlertUpdateTimer.getElapsedTime();
   %btn.stateUp = "";
   
   // Set waiting time
   if(!%wait) {
      %btn.totalTime = $AlertBtnWait;
   }
   else {
      %btn.totalTime = 0;
   }
   
   // Reset dynamic fields used in move update
   %btn.finalPosition  = "";
   %btn.actualPosition = "";
   %btn.trajectory     = "";
   %btn.totalDistance  = "";
}

////////////////////////////////////////////////////////////////////////////////
//! Called to stop a button moving by removing it from the button move group.
//! \param %btn Button to stop moving
////////////////////////////////////////////////////////////////////////////////
function alertStopBtnMove(%btn)
{
   // Removes button from move group
   alertRemBtnFromMoveGroup(%btn);
}

////////////////////////////////////////////////////////////////////////////////
//! Called to unlock a button from its locked position in the button lock group.
//! \param %btn Button to unlock
////////////////////////////////////////////////////////////////////////////////
function alertUnlockBtn(%btn)
{
   // Reset button text
   %index = alertGetBtnIndex(%btn.alertType);
   %text  = alertGetBtnText(%index);
   %text.stateUp = "";
   
   // Remove button from lock
   alertRemBtnFromLockGroup(%btn);
   
   // Remove alert index
   alertClearBtnIndex(%btn.alertType);
   
   // Disallow input and unlock
   %btn.input = false;
   %btn.lock  = false;
}

////////////////////////////////////////////////////////////////////////////////
//! Called to lock a button into place once it has reached its final index
//! \param %btn Button to lock into place
////////////////////////////////////////////////////////////////////////////////
function alertLockBtn(%btn)
{
   if(!isObject(%btn)) {
      return;
   }
   
   // Check for button existence in lock group
   %alertBtn = alertGetBtnInLockedGroup(%btn.alertType);
   %index    = alertGetBtnIndex(%btn.alertType);
   
   // Lock button in its place
   if(%alertBtn == 0) {
      %btn.Position = alertGetTemplateBtn(%index).Position;
      alertAddBtnToLockGroup(%btn);
      
      // Allow for input and lock
      %btn.input = true;
      %btn.lock  = true;
      
      // Update text for button
      %txt = alertGetBtnText(%index);
      %txt.stateUp = %btn.alertCount;
      // Add background
      %btn.stateUp = "assets/client/ui/icons/alertbtn.png";
   }
   
   // Release button and increment alert count
   else {
      %alertBtn.addAlertNum(%btn.alertCount);
      alertDestroyBtn(%btn);
   }
}

//******************************************************************************
//* Server Command Routines
//******************************************************************************
////////////////////////////////////////////////////////////////////////////////
//! Server command notifying client that an alert has changed status
//! \param %alert Alert type
//! \param %amount Amount of alert locations that have changed
//! \param %message Message to place on screen if any
//! \param %add True if the alert has been added, false otherwise
////////////////////////////////////////////////////////////////////////////////
function clientCmdAlertChange(%alert, %amount, %message, %add)
{
   if(%add) {
      alertAdd(%alert, %amount, %message);
   }
   else {
      alertRemove(%alert, %amount, %message);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Server command notifying client that one or more alert locations have 
//! changed
//! \param %alert Alert type
//! \param %amount Amount of alert locations that have changed
//! \param %add True if the alert locations have been added, false otherwise
//! \param %message Message to place on screen if any
////////////////////////////////////////////////////////////////////////////////
function clientCmdLocationChange(%alert, %amount, %add, %message)
{
   if(%add) {
      alertAdd(%alert, %amount, "");
   }
   else {
      alertRemove(%alert, %amount, %message);
   }
}

// End client_alerts.cs
