//------------------------------------------------------------------------------
// File: svr_taskhelpers.cs
// This file consolidates various methods into one location that allow the
// task scripter to perform many different tasks
// Author: Matthew Rudge
//------------------------------------------------------------------------------

$BadgeExperience = 4;

//-Task System Methods----------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Starts a task by activating it and marking all vertexes in the vertex list
//! as available
//! \param %taskname Name of task
//! \param %vertexlist Name of vertexes separated by spaces
////////////////////////////////////////////////////////////////////////////////
function tsStartTask(%taskname, %vertexlist)
{
   tsEndTask(%taskname);
   slgActivateTask(%taskname);
   %count = getWordCount(%vertexlist);
   for(%i = 0; %i < %count; %i++) {
      %vertex = getWord(%vertexlist, %i);
      TaskModifier.strMarkVertex(%taskname, %vertex, $TSV_AVAIL);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Ends a task through deactivation
//! \param %taskname Name of task
////////////////////////////////////////////////////////////////////////////////
function tsEndTask(%taskname)
{
   slgDeactivateTask(%taskname);
}

////////////////////////////////////////////////////////////////////////////////
//! Marks all vertexes as complete
//! \param %taskname Name of task
//! \param %vertexlist Name of vertexes separated by spaces
////////////////////////////////////////////////////////////////////////////////
function tsCompleteVertexes(%taskname, %vertexlist)
{
   %count = getWordCount(%vertexlist);
   for(%i = 0; %i < %count; %i++) {
      %vertex = getWord(%vertexlist, %i);
      TaskModifier.strMarkVertex(%taskname, %vertex, $TSV_CMPLETE);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Marks all vertexes as available
//! \param %taskname Name of task
//! \param %vertexlist Name of vertexes separated by spaces
////////////////////////////////////////////////////////////////////////////////
function tsActivateVertexes(%taskname, %vertexlist)
{
   %count = getWordCount(%vertexlist);
   for(%i = 0; %i < %count; %i++) {
      %vertex = getWord(%vertexlist, %i);
      TaskModifier.strMarkVertex(%taskname, %vertex, $TSV_AVAIL);
   }
}
//------------------------------------------------------------------------------

//-Task Dialog Methods----------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Sends a task popup dialog
//! \param %taskNameId Task name string id
//! \param %dialogId Dialog string id
//! \param %dialogType Type of dialog (More, Accept, Ok)
//! \param %dialogObj Object associated with dialog (object, image, or nothing)
//! (optional)
//! \param %dialogDataId Dialog data object string id (optional)
////////////////////////////////////////////////////////////////////////////////
function tsSendTaskPopUpDlg(%taskNameId, %dialogId, %dialogType, %dialogObj, %dialogDataId)
{
   %dlgData = tsCreateTaskDlgData(%taskNameId, false, %dialogType, %dialogDataId);
   tsAddTaskDialog(%dialogId, %dlgData);
   
   // If it is not an building, prop, or character then
   if(isObject(%dialogObj) &&
      !slgIsBuilding(%dialogObj) &&
      !slgIsCharacter(%dialogObj) && 
      !slgIsProp(%dialogObj)) {
      // Add it as an image
      tsSetTaskDialogImage(%dialogObj, %dlgData);
      tsSendTaskDlgMsg(%dlgData);
   }
   else {
      tsSendTaskDlgMsg(%dlgData, %dialogObj);
   }
   
   tsDestroyTaskDlgData(%dlgData);
}

////////////////////////////////////////////////////////////////////////////////
//! Sends a task click dialog associated with an object
//! \param %taskNameId Task name string id
//! \param %dialogId Dialog string id
//! \param %dialogType Type of dialog (More, Accept, Ok)
//! \param %dialogObj Object associated with dialog
//! \param %dialogDataId Dialog data object string id (optional)
////////////////////////////////////////////////////////////////////////////////
function tsSendTaskClickDlg(%taskNameId, %dialogId, %dialogType, %dialogObj, %dialogDataId)
{
   %dlgData = tsCreateTaskDlgData(%taskNameId, true, %dialogType, %dialogDataId);
   tsAddTaskDialog(%dialogId, %dlgData);
   tsSendTaskDlgMsg(%dlgData, %dialogObj);
   tsDestroyTaskDlgData(%dlgData);
}

////////////////////////////////////////////////////////////////////////////////
//! Used to create task dialog data that is sent with a task dialog message
//! \param %taskNameId Task name id
//! \param %bOnClick True if the dialog is generated from a click
//! \param %bAccept True if the dialog has an accept button
//! \param %identifier Task dialog data identifier
////////////////////////////////////////////////////////////////////////////////
function tsCreateTaskDlgData(%taskNameId, %bOnClick, %bAccept, %identifier)
{
   %dlgData = new SLTaskDlgData() {
      onClickDlg = %bOnClick;
      acceptDlg  = %bAccept;
      dlgDataId  = slgHashString(%identifier);
   };
   %dlgData.setTaskName(%taskNameId);
   return %dlgData;
}

////////////////////////////////////////////////////////////////////////////////
//! Used to destroy the task dialog data object sent with a task dialog message.
//! Destroy the task dialog data object after it has been sent with a call to
//! tsSendTaskDlgMsg
//! \param %dlgData Task dialog data object
////////////////////////////////////////////////////////////////////////////////
function tsDestroyTaskDlgData(%dlgData)
{
   %dlgData.delete();
}

////////////////////////////////////////////////////////////////////////////////
//! Used to clear the task dialog data object of various information
//! \param %bTaskName True if name of task is cleared
//! \param %bTaskDialog True if task dialog is cleared
//! \param %bTaskAccept True if resource acceptance cost is cleared
//! \param %dlgData Id of task dialog data object to clear
////////////////////////////////////////////////////////////////////////////////
function tsClearTaskDlgData(%bTaskName, %bTaskDialog, %bTaskAccept, %dlgData)
{
   if(%bTaskName) {
      %dlgData.clearTaskName();
   }
   if(%bTaskDialog) {
      %dlgData.clearTaskDialog();
   }
   if(%bTaskAccept) {
      %dlgData.clearAcceptCost();
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds task dialog to the task dialog data object
//! \param %dlgId Id of dialog
//! \param %dlgData Task dialog data object
////////////////////////////////////////////////////////////////////////////////
function tsAddTaskDialog(%dlgId, %dlgData)
{
   %dlgData.addTaskDialog(%dlgId);
}

////////////////////////////////////////////////////////////////////////////////
//! Adds resource acceptance cost to the task dialog data object. Only used if
//! the dialog has an accept button
//! \param %rsrc Resource type ($Resource::Gold, $Resource::Wood, etc.)
//! \param %cost Amount of resource type needed
//! \param %dlgData Task dialog data object
////////////////////////////////////////////////////////////////////////////////
function tsAddAcceptCost(%rsrc, %cost, %dlgData)
{
   %dlgData.addAcceptCost(%rsrc, %cost);
}

////////////////////////////////////////////////////////////////////////////////
//! Sets an image path in the task dialog data object to be shown in the task
//! dialog box.
//! \param %image the id/object SLImage to attach to the dialog
////////////////////////////////////////////////////////////////////////////////
function tsSetTaskDialogImage(%image, %dlgData)
{
   %dlgData.setTaskImage(%image);
}

////////////////////////////////////////////////////////////////////////////////
//! Sets the okay button text
//! \param %textId String id of text for button
//! \param %dlgData Task dialog data object
////////////////////////////////////////////////////////////////////////////////
function tsSetOkayButtonText(%textId, %dlgData)
{
   %dlgData.setOkayButtonText(%textId);
}

////////////////////////////////////////////////////////////////////////////////
//! Sets the accept button text
//! \param %textId String id of text for button
//! \param %dlgData Task dialog data object
////////////////////////////////////////////////////////////////////////////////
function tsSetAcceptButtonText(%textId, %dlgData)
{
   %dlgData.setAcceptButtonText(%textId);
}

////////////////////////////////////////////////////////////////////////////////
//! Sets the cancel button text
//! \param %textId String id of text for button
//! \param %dlgData Task dialog data object
////////////////////////////////////////////////////////////////////////////////
function tsSetCancelButtonText(%textId, %dlgData)
{
   %dlgData.setCancelButtonText(%textId);
}

////////////////////////////////////////////////////////////////////////////////
//! Used to send a task dialog message to a client (currently only works with
//! local client)
//! \param %dlgData Task dialog data id generated from taskCreateTaskDlgData
//! \param %dlgObj(optional) Id of object that will generate task dialog. If not
//! specified, then the task dialog will appear without a portrait
////////////////////////////////////////////////////////////////////////////////
function tsSendTaskDlgMsg(%dlgData, %dlgObj)
{
   if(isObject(%dlgObj)) {
      // Get id of object
      %dlgObj = %dlgObj.getId();
      
      // Add alert
      if(%dlgData.onClickDlg) {
         %message = slgGetUIString("id_alert_quest");
         %message = slgFormatUIString(%message, %dlgObj.name);
         alertSvrAddQuest(%dlgObj, %message);
      }
   }
   
   // Send message
   MsgSender.sendMsg($MSG_TASKDLG, $MRT_NETWORK, %dlgData, %dlgObj);
}

////////////////////////////////////////////////////////////////////////////////
//! Destroys a task dialog icon and alert on the client given a task dialog data
//! object id and the dialog speaker
//! \param %identifier Task dialog data object id
//! \param %dlgObj Dialog speaker object
////////////////////////////////////////////////////////////////////////////////
function tsDestroyTaskDlgIcon(%identifier, %dlgObj)
{
   %dlgObj = %dlgObj.getId();
   
   // Removes task dialog bubble on object
   echo("!*!*!* In tsDestroyTaskDlgIcon");
   echo("!*!*!* Dialog object name in tsDestroyTaskDlgIcon: ", %dlgObj.name);
   if(%dlgObj.getName() !$= "") {
      echo("!*!*!* Torque object mame in tsDestroyTaskDlgIcon: ", %dlgObj.getName());
   }
   %dlgId = slgHashString(%identifier);
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      %ghost = %client.getGhostID(%dlgObj);
      commandToClient(%client, 'DestroyTaskDlg', %dlgId, %ghost);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Destroys all task dialog on an object that has click dialog
//! \param %dlgObj Object that has dialog
//! \param %bNoMsg True if you don't want an alert message appearing about the
//! dialog loss
////////////////////////////////////////////////////////////////////////////////
function tsDestroyAllTaskDlg(%dlgObj, %bNoMsg)
{
   %dlgObj = %dlgObj.getId();
   
   // Destroy dialog on character
   echo("!*!*!* In tsDestroyAllTaskDlg");
   echo("!*!*!* Dialog object name in tsDestroyAllTaskDlg: ", %dlgObj.name);
   if(%dlgObj.getName() !$= "") {
      echo("!*!*!* Torque object mame in tsDestroyAllTaskDlg: ", %dlgObj.getName());
   }
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'DestroyAllTaskDlg', %client.getGhostID(%dlgObj), %bNoMsg);
   }
}
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
//! Attempts to get the id of the desired task object (by name or internal name)
//! \param %taskObj Task object whose id is desired
//! \retval int Task object id or 0
////////////////////////////////////////////////////////////////////////////////
function tsGetTaskObjectId(%taskObj)
{
   if(isObject(%taskObj)) {
      return %taskObj.getId();
   }
   else {
      return MissionGroup.findObjectByInternalName(%internalName);
   }
}

//-Sound Methods----------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Starts the playing of ambient sounds
////////////////////////////////////////////////////////////////////////////////
function tsPlayAmbientSound()
{
   //tsNotifyAllClients('PlayAmbientSound');
}

////////////////////////////////////////////////////////////////////////////////
//! Stops ambient sounds from playing
////////////////////////////////////////////////////////////////////////////////
function tsStopAmbientSound()
{
   //tsNotifyAllClients('StopAmbientSound');
}

////////////////////////////////////////////////////////////////////////////////
//! Pushes ambient sound on stack
//! \param %sound Name of sound AudioProfile datablock to play
////////////////////////////////////////////////////////////////////////////////
function tsPushAmbientSound(%sound)
{
   //tsNotifyAllClients('PushAmbientSound', %sound);
}

////////////////////////////////////////////////////////////////////////////////
//! Pops last ambient sound on stack
////////////////////////////////////////////////////////////////////////////////
function tsPopAmbientSound()
{
   //tsNotifyAllClients('PopAmbientSound');
}

////////////////////////////////////////////////////////////////////////////////
//! Pops all ambient sounds on stack
////////////////////////////////////////////////////////////////////////////////
function tsPopAllAmbientSounds()
{
   //tsNotifyAllClients('PopAllAmbientSounds');
}

//-Resource Methods-------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Sets the production rate for a particular resource
//! \param %rsrc Resource to change ($Resource::Gold, $Resource::Wood, etc.)
//! \param %rate New float rate (rate defaults to one)
////////////////////////////////////////////////////////////////////////////////
function tsSetResourceProductionRate(%rsrc, %rate)
{
   %rsrcObj = rsGetResource(%rsrc);
   if(isObject(%rsrcObj)) {
      %rsrcObj.setProductionRate(%rate);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds or subtracts resources
//! \param %rsrc Resource to modify ($Resource::Gold, $Resource::Wood, etc.)
//! \param %amnt Integer amount to modify the resource (must be above zero)
//! \param %bAdd True to increase amount, false to decrease
////////////////////////////////////////////////////////////////////////////////
function tsModifyResource(%rsrc, %amnt, %bAdd)
{
   // Error check
   if(%rsrc $= "") {
      return;
   }
   
   switch(%rsrc) {
         // Gold
      case $Resource::Gold:
         if(%bAdd) {
            IncreaseGold(%amnt);
         }
         else {
            DecreaseGold(%amnt);
         }
         
         // Wood
      case $Resource::Wood:
         if(%bAdd) {
            IncreaseWood(%amnt);
         }
         else {
            DecreaseWood(%amnt);
         }
         
         // Food
      case $Resource::Food:
         if(%bAdd) {
            IncreaseFood(%amnt);
         }
         else {
            DecreaseFood(%amnt);
         }
      
         // Water
      case $Resource::Water:
         if(%bAdd) {
            IncreaseWater(%amnt);
         }
         else {
            DecreaseWater(%amnt);
         }
         
         // Happiness
      case $Resource::Happiness:
         if(%bAdd) {
            IncreaseHappiness(%amnt);
         }
         else {
            DecreaseHappiness(%amnt);
         }
         
         // Experience
      case $Resource::Experience:
         if(%bAdd) {
            IncreaseExperience(%amnt);
            tsNotifyAllClients('OnXpAward');
         }
         else {
            DecreaseExperience(%amnt);
         }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds or subtracts max resources
//! \param %rsrc Resource to modify ($Resource::Gold, $Resource::Wood, etc.)
//! \param %amnt Integer amount to modify the resource (must be above zero)
//! \param %bAdd True to increase amount, false to decrease
////////////////////////////////////////////////////////////////////////////////
function tsModifyResourceMax(%rsrc, %amnt, %bAdd)
{
   // Error check
   if(%rsrc $= "") {
      return;
   }
   
   switch(%rsrc) {         
         // Food
      case $Resource::Food:
         if(%bAdd) {
            IncreaseFoodMax(%amnt);
         }
         else {
            DecreaseFoodMax(%amnt);
         }
      
         // Water
      case $Resource::Water:
         if(%bAdd) {
            IncreaseWaterMax(%amnt);
         }
         else {
            DecreaseWaterMax(%amnt);
         }
   }
}

function tsActivateBuildingXP(%building)
{
   ActivateBuildingExperience(%building);
}

function tsActivateButtonXP(%button)
{
   ActivateButtonExperience(%button);
}

//------------------------------------------------------------------------------

//-Object Methods---------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Tests if an object has the specified name
//! \param %obj Object to test
//! \param %name Name to test for
//! \retval bool True if the object has the name
////////////////////////////////////////////////////////////////////////////////
function tsIsNameOfObj(%obj, %name)
{
   return (%obj.name $= %name);
}

////////////////////////////////////////////////////////////////////////////////
//! Tests if an object has the specified internal name
//! \param %obj Object to test
//! \param %intName Internal name to test for
//! \retval bool True if the object has the internal name
////////////////////////////////////////////////////////////////////////////////
function tsIsInternalNameOfObj(%obj, %intName)
{
   // In case name still has curly braces
   if(tsIsInternalName(%intName)) {
      %intName = tsGetInternalName(%intName);
   }
   return (%obj.getInternalName() $= %intName);
}

////////////////////////////////////////////////////////////////////////////////
//! Returns the object list id from the message parameters if an object list is
//! available
//! \param %msg Message type
//! \param %param Message parameters
////////////////////////////////////////////////////////////////////////////////
function tsGetObjListFromMsg(%msg, %param)
{
   if(%msg == $MSG_TOOLUSE || %msg == $MSG_TOOLPURCHASE) {
      return 0;
   }
   else {
      return getWord(%param, 0);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Returns a named object from the message parameters if available
//! \param %name Name of object or internal name of object
//! \param %msg Message type
//! \param %param Message parameters
//! \retval int Id of named object or 0 if not found
////////////////////////////////////////////////////////////////////////////////
function tsGetObjFromMsg(%name, %msg, %param)
{   
   %objs = tsGetObjListFromMsg(%msg, %param);
   if(%objs == 0) {
      return %objs;
   }
   
   // Get internally named object
   if(tsIsInternalName(%name)) {
      %name = tsGetInternalName(%name);
      return %objs.getObjectByInternalName(%name);
   }
   
   // Get normal named object
   else {
      return %objs.getObjectByName(%name);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Returns the first object that matches type from the message parameters
//! if available
//! \param %type Object type to search for
//! \param %msg Message type
//! \param %param Message parameters
//! \retval int Id of first object that matches object type or 0 if not found
////////////////////////////////////////////////////////////////////////////////
function tsGetObjTypeFromMsg(%type, %msg, %param)
{
   %objs = tsGetObjListFromMsg(%msg, %param);
   if(%objs == 0) {
      return %objs;
   }
   
   // Search for object of type in object list
   return %objs.getObjectByType(%type);
}

////////////////////////////////////////////////////////////////////////////////
//! Creates an object list that can be used with different helper methods. Call
//! tsDestroyObjList() after you are finished using the object list. Note that
//! this allocates an object for you, so do not call in updates. It is
//! performance intensive to continually allocate objects
//! \retval int id of object list created or 0
////////////////////////////////////////////////////////////////////////////////
function tsCreateObjList()
{
   return (new SLGameObjList());
}

////////////////////////////////////////////////////////////////////////////////
//! Destroys an allocated object list. Only use on objects that you allocated 
//! with tsCreateObjList.
//! \param %list Id of list to destroy
////////////////////////////////////////////////////////////////////////////////
function tsDestroyObjList(%list)
{
   if(isObject(%list)) {
      %list.delete();
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Returns a globally used task helper object list. Cuts down on the number of
//! times to create an object list dynamically
//! \retval integer Task helper object list id
////////////////////////////////////////////////////////////////////////////////
function tsGetTaskHelperObjList()
{
   if(!isObject($TaskHelperObjList)) {
      $TaskHelperObjList = new SLGameObjList();
   }
   return $TaskHelperObjList;
}

////////////////////////////////////////////////////////////////////////////////
//! Finds a group of objects on the map that are of type. May be slightly 
//! performance intensive, depending on how many objects are on the map.
//! \param %type Type of objects desired
//! \param %category Category of object ("prop", "bldg", or "char")
//! \param %list Id of object list that this function will fill
////////////////////////////////////////////////////////////////////////////////
function tsFindObjTypeOnMap(%type, %category, %list)
{
   if(!isObject(%list)) {
      return;
   }
   
   // Get server list of objects
   %catlist = 0;
   if(%category $= "bldg") {
      %catlist = slgGetServerBuildingList();
   }
   else if(%category $= "char") {
      %catlist = slgGetServerCharacterList();
   }
   else if(%category $= "prop") {
      %catlist = slgGetServerPropList();
   }
   
   // Get type list
   if(isObject(%catList)) {
      %catlist.getTypeList(%type, %list);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Converts an object to a new team
//! \param %obj Object to convert
//! \param %newTeam New team number for object ($OST_PLAYER, $OST_NEUTRAL, or 
//! other)
////////////////////////////////////////////////////////////////////////////////
function tsConvertObjTeam(%obj, %newTeam)
{
   if(isObject(%obj)) {
      %obj.setTeam(%newTeam);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Converts a list of objects to a new team
//! \param %objs List of objects to convert
//! \param %newTeam New team number for objects ($OST_PLAYER, $OST_NEUTRAL, or 
//! other)
////////////////////////////////////////////////////////////////////////////////
function tsConvertObjListTeam(%objs, %newTeam)
{
   if(isObject(%objs)) {
      %count = %objs.getSize();
      for(%i = 0; %i < %count; %i++) {
         tsConvertObjectTeam(%objs.getID(%i), %newTeam);
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Moves a group of objects to a target position or target object
//! \param %obj Object to move
//! \param %target Target location in world coordinates or target object
////////////////////////////////////////////////////////////////////////////////
function tsMoveObjToTarget(%obj, %tgt)
{
   if(isObject(%obj)) {
      %objlist = tsGetTaskHelperObjList();
      %objlist.clear();
      %objlist.addObject(%obj.getId());
      tsMoveObjListToTarget(%objlist, %tgt);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Moves a group of objects to a target position or target object
//! \param %obj Object to move
//! \param %target Target location in world coordinates or target object
////////////////////////////////////////////////////////////////////////////////
function tsMoveObjListToTarget(%objs, %tgt)
{
   if(isObject(%objs)) { 
      MsgSender.sendMsg(
         $MSG_CMDREQUEST,
         $MRT_LOCAL,
         $CSC_MOVE,
         %tgt,
         %objs,
         false,
         ClientGroup.getObject(0)
      );
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Changes the skin on an object
//! \param %obj Object to reskin
//! \param %skin Name of skin
////////////////////////////////////////////////////////////////////////////////
function tsSkinObj(%obj, %skin)
{
   if(isObject(%obj)) {
      %obj.setSkinName(%skin);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Turns on/off the mesh of an object. Do not use this function right after an
//! object is created and has not been ghosted yet!
//! \param %obj Object to switch
//! \param %show True to turn on, false to turn off
////////////////////////////////////////////////////////////////////////////////
function tsShowMesh(%obj, %show)
{
   if(isObject(%obj)) {
      %obj.showMesh(%show);
      %client = ClientGroup.getObject(0);
      tsNotifyClient(%client, 'OnMeshChange', %client.getGhostID(%obj) SPC %show);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Mounts an image on an object
//! \param %obj Object to receive mount
//! \param %img Name of ShapeBaseImageData to mount on object
////////////////////////////////////////////////////////////////////////////////
function tsMountImageOnObj(%obj, %img)
{
   if(isObject(%obj) && isObject(%img))
   {
      if (%img.mountPoint != 0 || $HatEnabled == true)
      {
         %obj.mountImage(%img, %img.mountPoint);
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Starts a particle effect on an object
//! \param %obj Object id or name
//! \param %keyword Particle effect keyword
////////////////////////////////////////////////////////////////////////////////
function tsStartObjParticleFx(%obj, %keyword)
{
   if(isObject(%obj)) {
      startEffect(%obj.getId(), %keyword);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Stops a particle effect on an object
//! \param %obj Object id or name
//! \param %keyword Particle effect keyword
////////////////////////////////////////////////////////////////////////////////
function tsStopObjParticleFx(%obj, %keyword)
{
   if(isObject(%obj)) {
      stopEffect(%obj.getId(), %keyword);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Moves the bandit to attack a target
//! \param %bandit Bandit
//! \param %target Target of attack
////////////////////////////////////////////////////////////////////////////////
function tsStartBanditAttack(%bandit, %target)
{
   if(!isObject(%bandit) || !isObject(%target)) {
      return;
   }
   
   %cmpAI = slgQueryInterface(%bandit.getId(), $CID_AI);
   if(isObject(%cmpAI)) {
      %cmpAI.performAction("bandit_attack", %target.getId());
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Starts an attack on the target
//! \param %obj Object ordered to attack
//! \param %tgt Target of attack
////////////////////////////////////////////////////////////////////////////////
function tsStartObjAttack(%obj, %tgt)
{
   %objlist = tsGetTaskHelperObjList();
   %objlist.clear();
   %objlist.addObject(%obj);
   tsStartObjListAttack(%objlist, %tgt);
}

////////////////////////////////////////////////////////////////////////////////
//! Starts an attack on the target with a list of objects
//! \param %objs Objects ordered to attack
//! \param %tgt Target of attack
////////////////////////////////////////////////////////////////////////////////
function tsStartObjListAttack(%objs, %tgt)
{
   MsgSender.postMsg(
         $MSG_CMDREQUEST,
         $MRT_LOCAL,
         $CSC_ATTACK,
         %tgt,
         %objs,
         false,
         ClientGroup.getObject(0)
      );
}

////////////////////////////////////////////////////////////////////////////////
//! Creates a new object at the specified world position (object should only
//! be a character or prop)
//! \param %type Type of object
//! \param %name Name of object
//! \param %team Team of object
//! \param %pos World position of object
//! \param %intname Internal name of object (optional)
//! \retval int Id of new object or 0
////////////////////////////////////////////////////////////////////////////////
function tsCreateObj(%type, %name, %team, %pos, %intname)
{
   // Create object
   %obj = CreateSaveObject(%type, %name, %team);
   if(isObject(%obj)) {
      if(%name !$= "") {
         %obj.name = %name;
      }
      %obj.setPosition(getWord(%pos, 0), getWord(%pos, 1));
      if(%intname !$= "") {
         %obj.setInternalName(%intname);
      }
      %obj.health = %obj.getMaxHealth();
      
      // Place footprint down
      if(slgIsProp(%obj)) {
         PlaceObject(%obj);
      }
   }
   
   return %obj;
}

////////////////////////////////////////////////////////////////////////////////
//! Removes the game object (character, building, or prop)
//! \param %obj Object name, id, or internal name
////////////////////////////////////////////////////////////////////////////////
function tsRemoveObj(%obj)
{
   if(isObject(%obj)) {
      %objid = %obj.getId();
      // If it is a character, tell them to leave
      if(slgIsCharacter(%objid)) {
         %objid.performAction("leave");
      }
      // If it is a prop, destroy it
      else if(slgIsProp(%objid)) {
         %cmpProp = slgQueryInterface(%objid, $CID_PROP);
         %objid.onDestroy();
      }
      // If it is a building, go to the destruction state
      else if(slgIsBuilding(%objid)) {
         %cmpBldg = slgQueryInterface(%objid, $CID_BUILDING);
         %objid.health = 0;
      }
      // Otherwise simply destroy it
      else {
         %objid.onDestroy();
      }
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Gives an object a desired name
//! \param %obj Object to name
//! \param %name Name for object
////////////////////////////////////////////////////////////////////////////////
function tsNameObj(%obj, %name)
{
   if(isObject(%obj)) {
      %obj.name = %name;
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Gives an object a desired internal name
//! \param %obj Object to name
//! \param %intname Internal name for object
////////////////////////////////////////////////////////////////////////////////
function tsInternalNameObj(%obj, %intname)
{
   if(isObject(%obj)) {
      %obj.setInternalName(%intName);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Sets an individual building's production rate
//! \param %bldg Id or name of building object
//! \param %rate New rate for building (default is 1.0f)
////////////////////////////////////////////////////////////////////////////////
function tsSetBldgProductionRate(%bldg, %rate)
{
   if(isObject(%bldg)) {
      %cmpBldg = slgQueryInterface(%bldg.getId(), $CID_BUILDING);
      if(!isObject(%cmpBldg)) {
         return;
      }
      %cmpBldg.setProductionRate(%rate);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Stops a train in its tracks (heh)
////////////////////////////////////////////////////////////////////////////////
function tsStopTrain()
{
   slgTrain_StopTrain();
}

////////////////////////////////////////////////////////////////////////////////
//! Starts the train running again
////////////////////////////////////////////////////////////////////////////////
function tsStartTrain()
{
   slgTrain_StartTrain();
}

////////////////////////////////////////////////////////////////////////////////
//! Returns true if any combat characters (excluding the hero) on the specified 
//! team exist on the map
//! \param %team Team to check
//! \retval bool True if a combat unit is on the map
////////////////////////////////////////////////////////////////////////////////
function tsCombatUnitsExist(%team)
{
   %objlist = tsGetTaskHelperObjList();
   tsGetCombatUnits(%team, %objlist);
   return (%objlist.getSize() > 0);
}

////////////////////////////////////////////////////////////////////////////////
//! Returns a list of objects that are combat units, excluding the hero
//! \param %team Team of combat units desired
//! \param %objlist Object list that will be filled with combat units
////////////////////////////////////////////////////////////////////////////////
function tsGetCombatUnits(%team, %objlist)
{
   %objlist.clear();
   
   %chars = slgGetServerCharacterList();
   %count = %chars.getSize();
   for(%i = 0; %i < %count; %i++) {
      %char = %chars.getID(%i);
      if(!isObject(%char)) {
         continue;
      }
      
      // Unit needs to be on specified team
      if(!%char.isOnTeam(%team)) {
         continue;
      }
      
      // Add to list if one of these types
      if(%char.isOfType("Malegunslinger") ||
         %char.isOfType("Femalegunslinger") ||
         %char.isOfType("Nicegunslinger") ||
         %char.isOfType("Toughgunslinger") ||
         %char.isOfType("Nicesheriff") ||
         %char.isOfType("Toughsheriff") ||
         %char.isOfType("Deputy")) 
      {
         %objlist.addObject(%char);
      }
   }
}
//------------------------------------------------------------------------------

//-Gui Methods------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Enables/disables game input
//! \param %enable True to enable, false to disable
////////////////////////////////////////////////////////////////////////////////
function tsEnableInput(%enable)
{
   tsNotifyAllClients('EnableInput', %enable);
}

////////////////////////////////////////////////////////////////////////////////
//! Changes the visibility on a gui element
//! \param %gui Gui name
//! \param %visible True for visible, false for hidden
////////////////////////////////////////////////////////////////////////////////
function tsSetGuiVisibility(%gui, %visible)
{
   tsNotifyAllClients('SetGuiVisibility', %gui SPC %visible);
}

////////////////////////////////////////////////////////////////////////////////
//! Enables/disables a command button on all clients
//! \param %cmd Name of command data to enable/disable
//! \param %enable True to enable, false to disable
////////////////////////////////////////////////////////////////////////////////
function tsEnableCommand(%cmd, %enable)
{
   tsNotifyAllClients('EnableCommand', %cmd SPC %enable);
}

////////////////////////////////////////////////////////////////////////////////
//! Blinks a button in the HUD
//! \param %btn         Button name
//! \param %timeUp      Duration for up image state
//! \param %timeFocus   Duration for focus image state
//! \param %count       Number of blinks total
////////////////////////////////////////////////////////////////////////////////
function tsBlinkButton(%btn, %timeUp, %timeFocus, %count)
{
   tsNotifyAllClients('BlinkButton', %btn SPC %timeUp SPC %timeFocus SPC %count);
}

////////////////////////////////////////////////////////////////////////////////
//! Shows/hides a dialog gui
//! \param %dlg Dialog name
//! \param %show True to show dialog, false to hide it
////////////////////////////////////////////////////////////////////////////////
function tsShowDialog(%dlg, %show)
{
   tsNotifyAllClients('ShowDialog', %dlg SPC %show);
}

////////////////////////////////////////////////////////////////////////////////
//! Places a message on the Hud Message gui
//! \param %msg Message to place
////////////////////////////////////////////////////////////////////////////////
function tsSendHudMessage(%msg)
{
   tsNotifyAllClients('SendHudMessage', %msg);
}

////////////////////////////////////////////////////////////////////////////////
//! Places a message on the Alert Message gui
//! \param %msg Message to place
////////////////////////////////////////////////////////////////////////////////
function tsSendAlertMessage(%msg)
{
   tsNotifyAllClients('SendAlertMessage', %msg);
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a new quest to the quest hud to which tasks can be added and checked
//! \param %required the list to add the quest to (required/true or optional/false)
//! \param %quest the name of the quest
//! \param %trigger what is required to activate the quest
//! \param %description the text description of the quest
//! \param %reward the text description of the reward
////////////////////////////////////////////////////////////////////////////////
function tsAddQuestToTracker(%required, %quest, %trigger, %description, %reward)
{
    // this function should NOT be networked-- because replace task
   // functionality cannot be networked, we need to guarantee that
   // tasks added to the tracker are added immediately, so replace
   // task does not accidentally dual add tasks to the tracker
   CreateQuest(%required, %quest, %trigger, %description, %reward);
   
   /*
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'CreateQuest', %required, %quest, %trigger, %description, %reward);
   }
   */
}

////////////////////////////////////////////////////////////////////////////////
//! Makes an unavailable quest available.
//! \param %quest the name of the quest
////////////////////////////////////////////////////////////////////////////////
function tsReadyQuest(%quest)
{
   // this function should NOT be networked-- because replace task
   // functionality cannot be networked, we need to guarantee that
   // tasks added to the tracker are added immediately, so replace
   // task does not accidentally dual add tasks to the tracker
   ReadyQuest(%quest);
   
   /*
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'ReadyQuest', %quest);
   }
   */
}

////////////////////////////////////////////////////////////////////////////////
//! Disables a quest.
//! \param %quest the name of the quest
////////////////////////////////////////////////////////////////////////////////
function tsDisableQuest(%quest)
{
   // this function should NOT be networked-- because replace task
   // functionality cannot be networked, we need to guarantee that
   // tasks added to the tracker are added immediately, so replace
   // task does not accidentally dual add tasks to the tracker
   DisableQuest(%quest);
   
   /*
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'DisableQuest', %quest);
   }
   */
}

function tsSelectQuestButton(%quest)
{
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'SelectQuestButton', %quest);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Adds a task to the quest stored in the quest hud
//! \param %quest the name of the quest
//! \param %brief the brief description of the task
//! \param %long the long description of the task
////////////////////////////////////////////////////////////////////////////////
function tsAddTaskToTracker(%quest, %brief, %long)
{
   // this function should NOT be networked-- because replace task
   // functionality cannot be networked, we need to guarantee that
   // tasks added to the tracker are added immediately, so replace
   // task does not accidentally dual add tasks to the tracker
   CreateQuestTask(%quest, %brief, %long);
   
   /*
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'CreateQuestTask', %quest, %brief, %long);
   }
   */
}

////////////////////////////////////////////////////////////////////////////////
//! Replaces a task in the quest stored in the quest hud
//! \param %quest the name of the quest
//! \param %oldbrief the old brief description to look for
//! \param %newbrief the new brief description to add
//! \param %newlong the new long description to add
////////////////////////////////////////////////////////////////////////////////
function tsReplaceTaskInTracker(%quest, %oldbrief, %newbrief, %newlong)
{
   // this function should NOT be networked-- there are problems
   // that can be caused if trying to replace a task in the tracker
   // and the task is replaced by something else or changed elsewhere
   // while the message is being sent; this must happen immediately
   ReplaceQuestTask(%quest, %oldbrief, %newbrief, %newlong);
   
   /*
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'ReplaceQuestTask', %quest, %oldbrief, %newbrief, %newlong);
   }
   */
}

////////////////////////////////////////////////////////////////////////////////
//! Completes a task in a quest in the quest hud. Once all tasks are completed,
//! then the quest is completed, removed from the quest hud, and announced as
//! complete.
//! \param %quest the name of the quest
//! \param %brief the brief description of the task
////////////////////////////////////////////////////////////////////////////////
function tsCompleteTaskInTracker(%quest, %brief)
{
   // this function should NOT be networked-- because replace task
   // functionality cannot be networked, we need to guarantee that
   // tasks added to the tracker are added immediately, so replace
   // task does not accidentally dual add tasks to the tracker
   CompleteQuestTask(%quest, %brief);
   
   /*
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'CompleteQuestTask', %quest, %brief);
   }
   */
}

////////////////////////////////////////////////////////////////////////////////
//! Awards a badge to the player
//! \param %badgeId String id of badge
//! \param %sandboxOK true if badge can be awarded in sandbox, false otherwise
////////////////////////////////////////////////////////////////////////////////
function tsAwardBadge(%badgeId, %sandboxOK)
{
   // Do not award any badges in sandbox mode
   if(slgUseSandbox() && %sandboxOK == false) {
      return;
   }
   
   // Do not award any badges if the badge is already awarded
   if(tsIsBadgeAwarded(%badgeId)) {
      return;
   }
   
   tsFlagBadgeAwarded(%badgeId);
   
   // Save the current profile
   %file = slgGetSaveProfilePath();
   slgSaveCurrentProfile(%file);
   
   // Output message of badge award
   %msg  = slgGetUIString("id_badge_awardmsg");
   %name = tsGetBadgeName(%badgeId);
   %msg  = slgFormatUIString(%msg, %name);
   tsNotifyAllClients('OnBadgeAward', %msg);
   
   // when a badge is awarded, give the player experience
   SendProductionToClient(HeroCharacter, $BadgeExperience @ " experience");
   %resource = GameResourceStack.getResource();
   %experience = %resource.getExperience();
   %experience.increaseCount($BadgeExperience);
}

////////////////////////////////////////////////////////////////////////////////
//! Unlocks a hidden badge for the player
//! \param %badgeId String id of badge
////////////////////////////////////////////////////////////////////////////////
function tsUnlockBadge(%badgeId)
{
   // do not unlock any badges in sandbox mode
   if (slgUseSandbox() == true)
   {
      return;
   }
   
   // do not unlock any badges if the badge is already awarded
   if (tsIsBadgeUnlocked(%badgeId) == true)
   {
      return;
   }
   
   tsFlagBadgeUnlocked(%badgeId);
   
   // save the current profile
   %file = slgGetSaveProfilePath();
   slgSaveCurrentProfile(%file);
}

////////////////////////////////////////////////////////////////////////////////
//! Modifies quest task in quest hud
//! \param %quest Quest name
//! \param %task Task text
//! \param %mod Mod
////////////////////////////////////////////////////////////////////////////////
function tsModQuestTask(%quest, %task, %mod)
{
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'ModifyTaskInQuestHud', %quest, %task, %mod);
   }
}
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
//! Moves a camera to a position
//! \param %pos Position
//! \param %snap True to snap to the position, false to fly
////////////////////////////////////////////////////////////////////////////////
function tsMoveCameraToPosition(%pos, %snap)
{
   $serverCamera.moveToPosition(%pos, !%snap);
}

////////////////////////////////////////////////////////////////////////////////
//! Moves a camera to an object
//! \param %obj Object
//! \param %snap True to snap to position, false to fly
////////////////////////////////////////////////////////////////////////////////
function tsMoveCameraToObject(%obj, %snap)
{
   tsMoveCameraToPosition(%obj.getPosition(), %snap);
}

////////////////////////////////////////////////////////////////////////////////
//! Checks if the camera is moving
//! \retval bool True if moving
////////////////////////////////////////////////////////////////////////////////
function tsIsCameraMoving()
{
   return $serverCamera.isMovingToPos();
}

////////////////////////////////////////////////////////////////////////////////
//! Handles the tutorial state of the game.  If no parameters are passed to the
//! function, the current tutorial state is returned.
//! \param %use (optional) if set, will update the state of the tutorial map,
//! where true means the tutorial map should be used and false otherwise
//! \retval (optional) if no parameter is set, true is returned if the tutorial
//! mode has not been completed and false otherwise
////////////////////////////////////////////////////////////////////////////////
function tsUseTutorialMap(%use)
{
   if (%use $= "")
   {
      return slgUseTutorial();
   }
   else
   {
      slgSetTutorial(%use);
      %file = slgGetSaveProfilePath();
      slgSaveCurrentProfile(%file);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Pushes the dialog that will restart or quit the scenario if a lose condition
//! has been met
//! \param %textStringId Task string id for text
//! \param %restartStringId Task string id for restart button
//! \param %quitStringId Task string id for quit button
////////////////////////////////////////////////////////////////////////////////
function tsRestartScenario(%textStringId, %restartStringId, %quitStringId)
{
   slgStackPause();
   
   // If there are task string ids, use them
   if(%textStringId !$= "") {
      EndScenarioText.stateUp = tsGetTaskString(%textStringId);
   }
   if(%restartStringId !$= "") {
      %string = tsGetTaskString(%restartStringId);
      EndScenarioRestartBtn.textUp       = %string;
      EndScenarioRestartBtn.textDown     = %string;
      EndScenarioRestartBtn.textFocus    = %string;
      EndScenarioRestartBtn.textDisabled = %string;
   }
   if(%quitStringId !$= "") {
      %string = tsGetTaskString(%quitStringId);
      EndScenarioQuitBtn.textUp       = %string;
      EndScenarioQuitBtn.textDown     = %string;
      EndScenarioQuitBtn.textFocus    = %string;
      EndScenarioQuitBtn.textDisabled = %string;
   }
   
   // check if a game can be loaded
   EndScenarioLoadBtn.disabled = (!slgCanLoadGame());
   $MissionLoaded = false;
   
   // show the options at the lost scenario
   Canvas.pushDialog(EndScenarioGui);
}

////////////////////////////////////////////////////////////////////////////////
//! Loads a scenario
//! \param %scenario Scenario object id
////////////////////////////////////////////////////////////////////////////////
function tsLoadScenario(%scenario)
{
   LoadScenario(%scenario);
}

////////////////////////////////////////////////////////////////////////////////
//! Generalized method to notify all clients of an event
//! \param %tag Tagged name of method to call on client
//! \param %param Spaced parameters of method
////////////////////////////////////////////////////////////////////////////////
function tsNotifyAllClients(%tag, %param)
{
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++) {
      %client = ClientGroup.getObject(%i);
      tsNotifyClient(%client, %tag, %param);
   }
}

////////////////////////////////////////////////////////////////////////////////
//! Generalized method to notify a client of an event
//! \param %client Client to notify
//! \param %tag Tagged name of method to call on client
//! \param %param Spaced parameters of method
////////////////////////////////////////////////////////////////////////////////
function tsNotifyClient(%client, %tag, %param)
{
   commandToClient(%client, %tag, %param);
}


//-Client Commands to Server----------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
//! Received on the server when a client accepts or clicks okay on a task dialog
//! \param %client Client that sent command
//! \param %ghost Object that spoke
//! \param %dlgid Dialog data identifier
//! \param %count Number of dialogs left for object
////////////////////////////////////////////////////////////////////////////////
function serverCmdOnClickDlgDone(%client, %ghost, %dlgid, %count)
{
   // Get object
   %object = %client.resolveObjectFromGhostIndex(%ghost);
   if(!isObject(%object)) {
      return;
   }
   
   // Remove alert for object
   if(%count <= 0) {
      alertSvrRemoveQuest(%object);
   }
   
   // Send task dialog accept message
   MsgSender.postMsg($MSG_DIALOGDONE, $MRT_LOCAL, %object, %dlgid);
}

////////////////////////////////////////////////////////////////////////////////
//! Received on the server when a client accepts or clicks okay on a task dialog
//! \param %client Client that sent command
//! \param %ghost Object that spoke
//! \param %dlgid Dialog data identifier
////////////////////////////////////////////////////////////////////////////////
function serverCmdOnPopUpDlgDone(%client, %ghost, %dlgid)
{
   // Get object
   %object = %client.resolveObjectFromGhostIndex(%ghost);
   
   // Send task dialog accept message
   MsgSender.postMsg($MSG_DIALOGDONE, $MRT_LOCAL, %object, %dlgid);
}

////////////////////////////////////////////////////////////////////////////////
//! Received on the server when a task dialog is removed from the object
//! \param %client Client that sent command
//! \param %ghost Object that had task dialog
//! \param %count Number of dialogs left for object
////////////////////////////////////////////////////////////////////////////////
function serverCmdOnDlgDestroy(%client, %ghost, %count)
{
   // Get object
   %object = %client.resolveObjectFromGhostIndex(%ghost);
   if(!isObject(%object)) {
      return;
   }
   
    // Remove alert for object
   if(%count <= 0) {
      alertSvrRemoveQuest(%object);
   }
}

function SendTimerFireMsg(%vertexName)
{
   MsgSender.postMsg($MSG_TIMERFIRE, $MRT_LOCAL, %vertexName);
}

// End svr_taskhelpers.cs
