//------------------------------------------------------------------------------
// File: svr_alerts.cs
// This file contains the server implementation of the alert system. The server
// is responsible for initiating and removing alerts, moving the camera 
// for alerts, and maintaining the alert database.
// Copyright Sandlot Games, 2007
// Author: Matthew Rudge
//------------------------------------------------------------------------------

//-Alert Data-------------------------------------------------------------------
$AlertLocCount[$ALERT_BANDIT]  = 0;
$AlertLocCount[$ALERT_FIRE]    = 0;
$AlertLocCount[$ALERT_PLAGUE]  = 0;
$AlertLocCount[$ALERT_QUEST]   = 0;
$AlertLocCount[$ALERT_TORNADO] = 0;
$AlertLocCount[$ALERT_DROUGHT] = 0;
$AlertLocCount[$ALERT_HUNGER]  = 0;
//------------------------------------------------------------------------------

//-Alert Server Methods---------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Tests if the disaster type has an alert for the alert system
//! \param %disasterType Type of disaster
//! \retval bool True if the disaster has an alert
////////////////////////////////////////////////////////////////////////////////
function alertSvrDisasterHasAlert(%disasterType)
{
   return (alertSvrDisasterGetAlertType(%disasterType) != -1);
}

////////////////////////////////////////////////////////////////////////////////
//! Returns the alert type for a disaster type
//! \param %disasterType Type of disaster
//! \retval int Alert type ($ALERT_PLAGUE, etc.) or -1 (indicates none)
////////////////////////////////////////////////////////////////////////////////
function alertSvrDisasterGetAlertType(%disasterType)
{
   // Return the corresponding alert type based on the disaster type
   switch(%disasterType)
   {
   case $Disaster::Plague:
      return $ALERT_PLAGUE;
   case $Disaster::Bandit:
      return $ALERT_BANDIT;
   case $Disaster::Tornado:
      return $ALERT_TORNADO;
   case $Disaster::Fire:
      return $ALERT_FIRE;
   case $Disaster::Drought:
      return $ALERT_DROUGHT;
   default:
      return -1;
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Clears the alert database
////////////////////////////////////////////////////////////////////////////////
function alertSvrClearDatabase()
{
   // For each alert do
   for(%alert = 0; %alert < $ALERT_COUNT; %alert++) {
      // For each alert location do
      for(%loc = 0; %loc < $AlertLocCount[%alert]; %loc++) {
         // Remove alert location
         $AlertObjs[%alert, %loc] = "";
      }
      // Reset count
      $AlertLocCount[%alert] = 0;
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a disaster alert to the alert system. Only some of the disasters are
//! added to the alert system. Notifies client if there has been an alert 
//! location count change.
//! \param %disaster Disaster to add to alert system
//! \param %message Message to go on screen with alert change
////////////////////////////////////////////////////////////////////////////////
function alertSvrAddDisaster(%disaster, %message)
{
   if(!isObject(%disaster)) {
      return;
   }
   
   // Get alert type for disaster
   %alert = alertSvrDisasterGetAlertType(%disaster.getType());
   if(%alert == -1) {
      return;
   }

   // Bandit attack done elsewhere
   if(%alert == $ALERT_BANDIT) {
      return;
   }
   
   // Depending on alert type, add alert locations
   %objCount = 0;
   if(%alert == $ALERT_TORNADO) {
      if(alertSvrAddAlertLocation(%alert, %disaster)) {
         %objCount++;
      }
   }
   else {
      // Add objects associated with disaster
      %count = %disaster.getObjectCount();
      for(%i = 0; %i < %count; %i++) {
         %obj = %disaster.getObject(%i);
         if(isObject(%obj)) {
            if(alertSvrAddAlertLocation(%alert, %obj)) {
               %objCount++;
            }
         }
      }
   }
   
   // Notify clients of alert change
   if(%objCount != 0) {
      alertSvrNotifyClientOnAlertChange(%alert, %objCount, %message, true);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Removes a disaster alert from the alert system. Notifies client if there
//! has been an alert location count change.
//! \param %disaster Disaster to remove from alert system
//! \param %message Message to go on screen with alert change
////////////////////////////////////////////////////////////////////////////////
function alertSvrRemoveDisaster(%disaster, %message)
{
   if(!isObject(%disaster)) {
      return;
   }
   
   // Get alert type for disaster
   %alert = alertSvrDisasterGetAlertType(%disaster.getType());
   if(%alert == -1) {
      return;
   }
   
    // Bandit attack done elsewhere
   if(%alert == $ALERT_BANDIT) {
      return;
   }
   
   // Depending on alert type, remove alert locations
   %objCount = 0;
   if(%alert == $ALERT_TORNADO) {
      if(alertSvrRemoveAlertLocation(%alert, %disaster)) {
         %objCount++;
      }
   }
   else {
      // Remove objects associated with disaster
      %count = %disaster.getObjectCount();
      for(%i = 0; %i < %count; %i++) {
         %obj = %disaster.getObject(%i);
         if(isObject(%obj)) {
            if(alertSvrRemoveAlertLocation(%alert, %obj)) {
               %objCount++;
            }
         }
      }
   }   
   
   // Notify clients of change
   if(%objCount != 0) {
      alertSvrNotifyClientOnAlertChange(%alert, %objCount, %message, false);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a quest alert with a quest game object to the alert database. Notifies
//!  client if there has been an alert location count change.
//! \param %questObj Object with quest dialog
//! \param %message Message to go on screen with alert change
////////////////////////////////////////////////////////////////////////////////
function alertSvrAddQuest(%questObj, %message)
{
   if(!isObject(%questObj)) {
      return;
   }
   
   // Add and notify client
   if(alertSvrAddAlertLocation($ALERT_QUEST, %questObj)) {
      alertSvrNotifyClientOnAlertChange($ALERT_QUEST, 1, %message, true);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Removes a quest alert with a quest game object from the alert database. 
//! Notifies client if there has been an alert location count change.
//! \param %questObj Object with quest dialog
////////////////////////////////////////////////////////////////////////////////
function alertSvrRemoveQuest(%questObj)
{
   if(!isObject(%questObj)) {
      return;
   }
   
   // Remove and notify client
   if(alertSvrRemoveAlertLocation($ALERT_QUEST, %questObj)) {
      alertSvrNotifyClientOnAlertChange($ALERT_QUEST, 1, "", false);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a bandit attack alert to the alert database. Notifies client of alert
//! change.
//! \param %bandit Bandit that initiated attack
//! \param %target Target of attack
////////////////////////////////////////////////////////////////////////////////
function alertSvrAddBanditAttack(%bandit, %target)
{
   // These objects need to be alive
   if(!isObject(%target) || !isObject(%bandit) || !isObject($DisasterManager)) {
      return;
   }
   
   // Get disaster id
   %disaster = $DisasterManager.getBanditAttack(%bandit);
   if(!isObject(%disaster)) {
      return;
   }
   
   // Add alert location for disaster
   if(alertSvrAddAlertLocation($ALERT_BANDIT, %target)) {
      %msg = "";
      if(alertSvrGetAlertLocationCount($ALERT_BANDIT) == 1) {
         %msg = disasterGetCreateMsg(%disaster);
      }
      alertSvrNotifyClientOnAlertChange($ALERT_BANDIT, 1, %msg, true);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a hunger alert to the alert database. Notifies the client of the
//! alert change if the alert does not exist or updates existing alert
//! \param %hungryObj Hungry object
////////////////////////////////////////////////////////////////////////////////
function alertSvrAddHunger(%hungryObj)
{
   if(!isObject(%hungryObj)) {
      return;
   }
   
   // Just add hunger object
   if(alertSvrGetAlertLocationCount($ALERT_HUNGER) > 0) {
      alertSvrAddObject($ALERT_HUNGER, %hungryObj);
   }
   
   // Start new alert
   else {
      if(alertSvrAddAlertLocation($ALERT_HUNGER, %hungryObj)) {
         %msg = slgGetUIString("id_alert_hunger");
         alertSvrNotifyClientOnAlertChange($ALERT_HUNGER, 1, %msg, true);
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Removes a hunger alert from the alert database. Notifies the client of the
//! alert change if no more locations for the alert will exist or updates 
//! existing alert
//! \param %hungryObj Hungry object
////////////////////////////////////////////////////////////////////////////////
function alertSvrRemoveHunger(%hungryObj)
{
   if(!isObject(%hungryObj)) {
      return;
   }
   
   // Remove alert
   %msg = slgGetUIString("id_alert_hunger_end");
   alertSvrRemoveObject($ALERT_HUNGER, %hungryObj, %msg);
}

////////////////////////////////////////////////////////////////////////////////
//! Adds an alert object under an existing alert
//! \param %alert Alert type
//! \param %object Object to add to alert
////////////////////////////////////////////////////////////////////////////////
function alertSvrAddObject(%alert, %object)
{
   if(!isObject(%object)) {
      return;
   }

   // Add alert object and notify client
   if(alertSvrAddAlertLocation(%alert, %object)) {
      alertSvrNotifyClientOnLocationChange(%alert, 1, "", true);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Removes an alert object under an existing alert
//! \param %alert Alert type
//! \param %object Object to remove from alert
//! \param %message Message to output if alert has no locations left
////////////////////////////////////////////////////////////////////////////////
function alertSvrRemoveObject(%alert, %object, %message)
{
   if(!isObject(%object)) {
      return;
   }
   
   // Remove alert object and notify client
   if(alertSvrRemoveAlertLocation(%alert, %object)) {
      if(%alert == $ALERT_BANDIT) {
         if(alertSvrGetAlertLocationCount(%alert) > 0) {
            %message = "";
         }
      }
      alertSvrNotifyClientOnLocationChange(%alert, 1, %message, false);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Returns the number of alert locations stored in the alert database
//! \param %alert Alert type
//! \retval int Number of alert locations
////////////////////////////////////////////////////////////////////////////////
function alertSvrGetAlertLocationCount(%alert)
{
   return $AlertLocCount[%alert];
}

////////////////////////////////////////////////////////////////////////////////
//! Returns the alert location located at the desired slot
//! \param %alert Alert type
//! \param %slot Location slot
//! \retval Location or empty string
////////////////////////////////////////////////////////////////////////////////
function alertSvrGetAlertLocation(%alert, %slot)
{
   return $AlertObjs[%alert, %slot];
}

////////////////////////////////////////////////////////////////////////////////
//! Determines if an alert location has been stored for the alert type
//! \param %alert Alert type
//! \param %location Location of alert
//! \retval True if the location is already an alert location
////////////////////////////////////////////////////////////////////////////////
function alertSvrHasAlertLocation(%alert, %location)
{
   // Get location count for alert
   %count = alertSvrGetAlertLocationCount(%alert);
   
   // For each location do
   for(%i = 0; %i < %count; %i++) {
      // If location matches then return true
      if($AlertObjs[%alert, %i] $= %location) {
         return true;
      }
   }
   
   // No location match
   return false;
}

////////////////////////////////////////////////////////////////////////////////
//! Adds an alert location to the alert database
//! \param %alert Alert type
//! \param %location Location of alert
//! \retval bool True if the alert location was added
////////////////////////////////////////////////////////////////////////////////
function alertSvrAddAlertLocation(%alert, %location)
{
   // Only add if location is not in alert
   if(alertSvrHasAlertLocation(%alert, %location)) {
      return false;
   }
   
   // Get location count
   %count = alertSvrGetAlertLocationCount(%alert);
   
   // Add location
   $AlertObjs[%alert, %count] = %location;
   $AlertLocCount[%alert]++;
   return true;
}

////////////////////////////////////////////////////////////////////////////////
//! Removes the alert location from the alert database
//! \param %alert Alert type
//! \param %location Location of alert
//! \retval bool True if the alert location was removed
////////////////////////////////////////////////////////////////////////////////
function alertSvrRemoveAlertLocation(%alert, %location)
{
   // Get location count
   %count = alertSvrGetAlertLocationCount(%alert);
   
   // For each location do
   %bRemoved = false;
   for(%i = 0; %i < %count; %i++) {
      %cLoc = $AlertObjs[%alert, %i];
      
      // If this location matches then remove it
      if(%cLoc $= %location) {
         %bRemoved = true;
         break;
      }
   }
   
   // Shuffle the rest of the locations forward
   for(%i; %i < %count - 1; %i++) {
      $AlertObjs[%alert, %i] = $AlertObjs[%alert, %i + 1];
   }
   
   // Clear last spot and decrement alert count
   if(%bRemoved) {
      $AlertObjs[%alert, %count - 1] = "";
      $AlertLocCount[%alert]--;
      alertSvrOutputAlertMessage(%alert, "Removed successfully from database", false);
   }
   else {
      alertSvrOutputAlertMessage(%alert, "Failed to remove from database!!", true);
   }
   return %bRemoved;
}

////////////////////////////////////////////////////////////////////////////////
//! Notifies client when an alert status has changed (disaster/quest begin/end)
//! \param %alert Alert type
//! \param %amount Number of alert locations for alert
//! \param %message Message to go on screen with alert
//! \param %add True if the alert has been added, false if removed
////////////////////////////////////////////////////////////////////////////////
function alertSvrNotifyClientOnAlertChange(%alert, %amount, %message, %add)
{
   // For each client do
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      // Notify them that an alert has been changed
      commandToClient(%client, 'AlertChange', %alert, %amount, %message, %add);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Notifies client when an alert location has changed for a pre-existing alert
//! \param %alert Alert type
//! \param %amount Number of alert locations that have changed
//! \param %add True if the locations have been added, false if removed
//! \param %message Message when alert location count has reached zero
////////////////////////////////////////////////////////////////////////////////
function alertSvrNotifyClientOnLocationChange(%alert, %amount, %message, %add)
{
   // For each client do
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      // Notify them that a location for an alert has been changed
      if(alertSvrGetAlertLocationCount(%alert) == 0) {
         commandToClient(%client, 'LocationChange', %alert, %amount, %add, %message);
      }
      else {
         commandToClient(%client, 'LocationChange', %alert, %amount, %add);
      }
      
   }
}
//------------------------------------------------------------------------------

//-Alert Client Commands--------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Called by a client when an alert button has been pressed
//! \param %client Client that gave command
//! \param %alert Alert type
//! \param %select Index for slot location
////////////////////////////////////////////////////////////////////////////////
function serverCmdAlertSelect(%client, %alert, %select)
{
   // Get location
   %loc = alertSvrGetAlertLocation(%alert, %select);
   if(%loc $= "") {
      return;
   }
   
   // Get camera
   %cam = %client.getCameraObject();
   if(!isObject(%cam)) {
      return;
   }
   
   // Zoom to stored location
   %cam.moveToObject(%loc, true);
   if(%alert != $ALERT_TORNADO) {
      commandToClient(%client, 'SelectObject', %client.getGhostID(%loc));
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Called by a client when a quest object no longer has task dialog
//! \param %client Client that gave command
//! \param %ghost Ghost quest object
////////////////////////////////////////////////////////////////////////////////
function serverCmdTaskLocationDone(%client, %ghost)
{
   // Get object
   %object = %client.resolveObjectFromGhostIndex(%ghost);
   if(!isObject(%object)) {
      return;
   }
   
   // Remove alert for object
   alertSvrRemoveQuest(%object);
}

// returns true if the target object has a quest associated with it;
// otherwise false is returned
function HasQuestButton(%object)
{
   // if the target object does not exist, it has no quest button
   if (isObject(%object) == false)
   {
      return false;
   }
   
   // look through all of the alerts to find a matching target
   %count = alertSvrGetAlertLocationCount($ALERT_QUEST);
   for (%index = 0; %index < %count; %index++)
   {
      // get the alert target and ensure it is an object
      %target = alertSvrGetAlertLocation($ALERT_QUEST, %index);
      if (isObject(%target) == false)
      {
         continue;
      }

      // if both targets have the same id, we found a match
      if (%object.getId() == %target.getId())
      {
         return true;
      }
   }
   
   return false;
}

////////////////////////////////////////////////////////////////////////////////
//! Prints an error message to the console window
//! \param %alert Type of alert
//! \param %msg Message to print
//! \param %error True if its an error
////////////////////////////////////////////////////////////////////////////////
function alertSvrOutputAlertMessage(%alert, %msg, %error)
{
   switch(%alert) {
      case $ALERT_BANDIT:
         %strAlert = "Bandit Attack: ";
      case $ALERT_DROUGHT:
         %strAlert = "Drought: ";
      case $ALERT_FIRE:
         %strAlert = "Fire: ";
      case $ALERT_HUNGER:
         %strAlert = "Hunger: ";
      case $ALERT_PLAGUE:
         %strAlert = "Plague: ";
      case $ALERT_QUEST:
         %strAlert = "Quest: ";
      case $ALERT_TORNADO:
         %strAlert = "Tornado: ";
      default:
         %strAlert = "";
   }
   if(%error) {
      error(%strAlert @ %msg);
   }
   else {
      echo(%strAlert @ %msg);
      //echo(%strAlert @ %msg);
   }
}
//------------------------------------------------------------------------------

// End svr_alerts.cs
