
//$MissionName is the file name of the mission
//$MapName is the displayed name(no underscore,spaces)
//$GameType (CTF,Hunters)

// These are the globals that control how points are assigned by damage
$FriendlyFireFactor = -2.0;
$FriendlyFireFlagFactor = -2.0;
$FriendlyKillBonus = -0.2;
$EnemyFireFactor = 1.0;
$EnemyFireFlagFactor = 1.0;
$EnemyKillBonus = 0.1;
$TurretsScore = true;
$RespawnDelay = 2;
$SuicideRespawnDelay = 10;
$VehicleTimeOut = 15;

$VoteMessage["voteIncreaseTime"] = "increase time";

function DefaultGame::activatePackages(%game)
{
   // activate the default package for the game type
   activatePackage(DefaultGame);
   if(isPackage(%game.class) && %game.class !$= DefaultGame)
      activatePackage(%game.class);
}

function DefaultGame::deactivatePackages(%game)
{
   deactivatePackage(DefaultGame);
   if(isPackage(%game.class) && %game.class !$= DefaultGame)
      deactivatePackage(%game.class);
}

package DefaultGame {

function FlipFlop::objectiveInit(%data, %flipflop)
{
   // add this flipflop to missioncleanup
   %flipflopSet = nameToID("MissionCleanup/FlipFlops");
   if(%flipflopSet <= 0) {
      %flipflopSet = new SimSet("FlipFlops");
      MissionCleanup.add(%flipflopSet);
   }
   %flipflopSet.add(%flipflop);

   // see if there's a holo projector associated with this flipflop
   // search the flipflop's folder for a holo projector
   // if one exists, associate it with the flipflop

   %flipflop.projector = 0;
   %folder = %flipflop.getGroup();
   for(%i = 0; %i < %folder.getCount(); %i++) 
   {
      %proj = %folder.getObject(%i);
      // weird, but line below prevents console error
      if(%proj.getClassName() !$= "SimGroup" && %proj.getClassName() !$= "InteriorInstance")
         if(%proj.getDatablock().getName() $= "LogoProjector") 
         {
            %flipflop.projector = %proj;
            %flipflop.projector.holo = 0;
            break;
         }
   }

   // may have been hidden
   %target = %flipFlop.getTarget();
   if(%target != -1)
   {
      // set flipflop to base skin
      setTargetSkin(%target, $teamSkin[0]);

      // make this always visible in the commander map
      setTargetAlwaysVisMask(%target, 0xffffffff);

      // make this always visible in the commander list
      setTargetRenderMask(%target, getTargetRenderMask(%target) | $TargetInfo::CommanderListRender);
   }
}

function FlipFlop::playerTouch(%data, %flipflop, %player)
{
   %client = %player.client;
   %flipTeam = %flipflop.team;

   if(%flipTeam == %client.team)
      return false;
   
   %teamName = game.getTeamName(%client.team);
   // Let the observers know:
   messageTeam( 0, 'MsgClaimFlipFlop', '\c2%1 claimed %2 for %3.~wfx/misc/flipflop_taken.wav', %client.name, Game.cleanWord( %flipflop.name ), %teamName );
   // Let the teammates know:
   messageTeam( %client.team, 'MsgClaimFlipFlop', '\c2%1 claimed %2 for %3.~wfx/misc/flipflop_taken.wav', %client.name, Game.cleanWord( %flipflop.name ), %teamName );
   // Let the other team know:
   %losers = %client.team == 1 ? 2 : 1;
   messageTeam( %losers, 'MsgClaimFlipFlop', '\c2%1 claimed %2 for %3.~wfx/misc/flipflop_lost.wav', %client.name, Game.cleanWord( %flipflop.name ), %teamName );

   logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") claimed flipflop "@%flipflop@" for team "@%client.team);

   //change the skin on the switch to claiming team's logo
   setTargetSkin(%flipflop.getTarget(), game.getTeamSkin(%player.team));
   setTargetSensorGroup(%flipflop.getTarget(), %player.team);

   // if there is a "projector" associated with this flipflop, put the claiming team's logo there
   if(%flipflop.projector > 0)
   {
      %projector = %flipflop.projector;
      // axe the old projected holo, if one exists
      if(%projector.holo > 0)
         %projector.holo.delete();
          
      %newHolo = getTaggedString(game.getTeamSkin(%client.team)) @ "Logo";

      %projTransform = %projector.getTransform();
      // below two functions are from deployables.cs
      %projRot = rotFromTransform(%projTransform);
      %projPos = posFromTransform(%projTransform);
      // place the holo above the projector (default 10 meters)
      %hHeight = %projector.holoHeight;
      if(%hHeight $= "")
         %hHeight = 10;
      %holoZ = getWord(%projPos, 2) + %hHeight;
      %holoPos = firstWord(%projPos) SPC getWord(%projPos,1) SPC %holoZ;

      %holo = new StaticShape() 
      {
         rotation = %projRot;
         position = %holoPos;
         dataBlock = %newHolo;
      };
      // dump the hologram into MissionCleanup
      MissionCleanup.add(%holo);
      // associate the holo with the projector
      %projector.holo = %holo;
   }

   // convert the resources associated with the flipflop
   Game.claimFlipflopResources(%flipflop, %client.team);
   
   if(Game.countFlips())
      for(%i = 1; %i <= Game.numTeams; %i++)
      {
         %teamHeld = Game.countFlipsHeld(%i);
         messageAll('MsgFlipFlopsHeld', "", %i, %teamHeld);
      }
   
   //call the ai function
   Game.AIplayerCaptureFlipFlop(%player, %flipflop);
   return true;
}

};

//--------- DEFAULT SCORING, SUPERCEDE IN GAMETYPE FILE ------------------

function DefaultGame::initGameVars(%game)
{
    %game.SCORE_PER_SUICIDE = 0; 
   %game.SCORE_PER_TEAMKILL = 0;
   %game.SCORE_PER_DEATH = 0;  

   %game.SCORE_PER_KILL = 0; 

   %game.SCORE_PER_TURRET_KILL = 0;
}

//-- tracking  ---
// .deaths .kills .suicides .teamKills .turretKills 

function DefaultGame::claimFlipflopResources(%game, %flipflop, %team)
{
   %group = %flipflop.getGroup();
   %group.setTeam(%team);
   
   // make this always visible in the commander map (gets reset when sensor group gets changed)
   setTargetAlwaysVisMask(%flipflop.getTarget(), 0xffffffff);
}

//------------------------------------------------------------------------------
function DefaultGame::selectSpawnSphere(%game, %team)
{ 
   // - walks the objects in the 'teamdrops' group for this team
   // - find a random spawn point which has a running sum less more than
   //   0->total sphere weight       
   
   %teamDropsGroup = "MissionCleanup/TeamDrops" @ %team;
   
   %group = nameToID(%teamDropsGroup);
   if (%group != -1)
   {
      %count = %group.getCount();
      if (%count != 0)
      {
         // Get total weight of those spheres not filtered by mission types list- 
         %overallWeight = 0;
         for (%i = 0; %i < %count; %i++) 
         {
            %sphereObj = %group.getObject(%i);
            if ( ! %sphereObj.isHidden() )
               %overallWeight += %sphereObj.sphereWeight;
         }
         
         if (%overallWeight > 0)
         {
            // Subtract a little from this as hedge against any rounding offness- 
            %randSum = getRandom(%overallWeight) - 0.05;
            // echo("randSum = " @ %randSum);
         
            for (%i = 0; %i < %count; %i++)
            {
               %sphereObj = %group.getObject(%i);
               if (! %sphereObj.isHidden())
               {
                  %randSum -= %sphereObj.sphereWeight;
                  if (%randSum <= 0)
                  {
                     // echo("Chose sphere " @ %i);
                     return %group.getObject(%i);     // Found our sphere
                  }
               }
            }
            error("Random spawn sphere selection didn't work");
         }
         else
            error("No non-hidden spawnspheres were found in " @ %teamDropsGroup);
      }
      else
         error("No spawnspheres found in " @ %teamDropsGroup);
   }
   else
      error(%teamDropsGroup @ " not found in selectSpawnSphere().");
      
   return -1;
}

function DefaultGame::selectSpawnZone(%game, %sphere)
{
   // determines if this should spawn inside or outside
   %overallWeight = %sphere.indoorWeight + %sphere.outdoorWeight;
   %index = mFloor(getRandom() * (%overallWeight - 0.1)) + 1;
   if ((%index - %sphere.indoorWeight) > 0)
      return false; //do not pick an indoor spawn
   else 
      return true;  //pick an indoor spawn
}     

function DefaultGame::selectSpawnFacing(%game, %src, %target, %zone)
{   
   //this used only when spawn loc is not on an interior.  This points spawning player to the ctr of spawnshpere
   %target = setWord(%target, 2, 0);      
   %src = setWord(%src, 2, 0);      

   if(VectorDist(%target, %src) == 0)
      return " 0 0 1 0  ";
   %vec = VectorNormalize(VectorSub(%target, %src));
   %angle = mAcos(getWord(%vec, 1));

   if(%src < %target)
     return(" 0 0 1 " @ %angle);
   else                            
     return(" 0 0 1 " @ -%angle);   
}  

function DefaultGame::pickTeamSpawn(%game, %team) 
{
   // early exit if no nav graph
   if (!navGraphExists())
   {
      echo("No navigation graph is present.  Build one.");
      return -1;
   }
   
   for (%attempt = 0; %attempt < 20; %attempt++)
   {
      //  finds a random spawn sphere
      //  selects inside/outside on this random sphere
      //  if the navgraph exists, then uses it to grab a random node as spawn 
      //   location/rotation
      %sphere = %game.selectSpawnSphere(%team);
      if (%sphere == -1)
      {
         echo("No spawn spheres found for team " @ %team);   
         return -1;
      }

      %zone = %game.selectSpawnZone(%sphere);
      %useIndoor = %zone;
      %useOutdoor = !%zone;
      if (%zone)
         %area = "indoor";
      else
         %area = "outdoor";
                           
      %radius = %sphere.radius;
      %sphereTrans = %sphere.getTransform();
      %sphereCtr = getWord(%sphereTrans, 0) @ " " @ getWord(%sphereTrans, 1) @ " " @ getWord(%sphereTrans, 2);   //don't need full transform here, just x, y, z
      //echo("Selected Sphere is " @ %sphereCtr @ " with a radius of " @ %radius @ " meters.  Selecting from " @ %area @ " zone.");

      %avoidThese = $TypeMasks::VehicleObjectType  | $TypeMasks::MoveableObjectType |
                    $TypeMasks::PlayerObjectType   | $TypeMasks::TurretObjectType;

      for (%tries = 0; %tries < 10; %tries++)
      {
         %nodeIndex = navGraph.randNode(%sphereCtr, %radius, %useIndoor, %useOutdoor);
         if (%nodeIndex >= 0)
         {
            %loc = navGraph.randNodeLoc(%nodeIndex);
            %adjUp = VectorAdd(%loc, "0 0 1.0");   // don't go much below
            
            if (ContainerBoxEmpty( %avoidThese, %adjUp, 2.0))
               break;
         }
      }
      
      if (%nodeIndex >= 0)
      {
         %loc = navGraph.randNodeLoc(%nodeIndex);
         if (%zone)
         {
            %trns = %loc @ " 0 0 1 0";
            %spawnLoc = whereToLook(%trns);
         }
         else
         {
            %rot = %game.selectSpawnFacing(%loc, %sphereCtr, %zone);               
            %spawnLoc = %loc @ %rot;
         }          
         return %spawnLoc;            
      }
   }
}            
                     
//------------------------------------------------------------

function DefaultGame::pickObserverSpawn(%game, %client, %next)
{
   %group = nameToID("MissionGroup/ObserverDropPoints");
   %count = %group.getCount();
   if(!%count || %group == -1)
   {
      echo("no observer spawn points found");
      return -1;
   }    
    
   if(%client.lastObserverSpawn == -1)
   {
      %client.lastObserverSpawn = 0;
      return(%group.getObject(%client.lastObserverSpawn));
   }

   if(%next == true)
      %spawnIdx = %client.lastObserverSpawn + 1;
   else
      %spawnIdx = %client.lastObserverSpawn - 1;

   if(%spawnIdx < 0)
      %spawnIdx = %count - 1;
   else if(%spawnIdx >= %count)
      %spawnIdx = 0;
      
   %client.lastObserverSpawn = %spawnIdx;
   //echo("Observer spawn point found");
   return %group.getObject(%spawnIdx);
}

//------------------------------------------------------------
function DefaultGame::spawnPlayer( %game, %client, %respawn )
{
   %client.lastSpawnPoint = %game.pickPlayerSpawn( %client, false );
   %client.suicidePickRespawnTime = getSimTime() + 20000;
   %game.createPlayer( %client, %client.lastSpawnPoint, %respawn );
}
                        
//------------------------------------------------------------
function DefaultGame::playerSpawned(%game, %player)
{
   if( %player.client.respawnTimer )
      cancel(%player.client.respawnTimer);
   
   %player.client.observerStartTime = "";
   %game.equip(%player);

   //set the spawn time (for use by the AI system)
   %player.client.spawnTime = getSimTime();

// jff: this should probably be checking the team of the client
   //update anyone observing this client
   %count = ClientGroup.getCount();
   for (%i = 0; %i < %count; %i++)
   {
      %cl = ClientGroup.getObject(%i);
      if (%cl.camera.mode $= "observerFollow" && %cl.observeClient == %player.client)
      {
         %transform = %player.getTransform();
         %cl.camera.setOrbitMode(%player, %transform, 0.5, 4.5, 4.5);
         %cl.camera.targetObj = %player;
      }
   }
}

// This function gives the player thier starting equipment
function DefaultGame::equip(%Game, %Player)
{
   // Loop through and start the players HUD
   %i = 0;
   while (%i<$InventoryHudCount)
   {
      %Player.client.setInventoryHudItem($InventoryHudData[%i, itemDataName], 0, 1);
      %i++;
   }

   // Equip the player with the items they spawn with
   %Player.setInventory(Disc, 1);
   %Player.setInventory(DiscAmmo, 20);
   %Player.setInventory(Chaingun, 1);
   %Player.setInventory(ChaingunAmmo, 250);
   %Player.setInventory(Plasma, 1);
   %Player.setInventory(PlasmaAmmo, 36);
   %Player.setInventory(TargetingLaser, 1);
   %Player.setInventory(FlashLight, 1);
   %Player.setInventory(RepairKit, 1);
   %Player.setInventory(Grenade, 4);
   %Player.setInventory(Beacon, 1);
   %Player.Special = "SpeedBooster";
   %Player.weaponCount = 3;

   switch$ ($Host::DefaultPack)
   {
      case "AmmoPack" :
         %Player.setInventory(AmmoPack, 1);

         %Player.setInventory(DiscAmmo, 40);
         %Player.setInventory(ChaingunAmmo, 350);
         %Player.setInventory(PlasmaAmmo, 48);
         %Player.setInventory(RepairKit, 2);
         %Player.setInventory(Grenade, 6);

      case "MapDependant" :
         if ($RepairPacks > 0)
            %Player.setInventory(EnergyPack, 1);
         else
            %Player.setInventory(RepairPack, 1);

      case "RepairPack" :
         %Player.setInventory(RepairPack, 1);

      case "ShieldPack" :
         %Player.setInventory(ShieldPack, 1);

      default :
         %Player.setInventory(EnergyPack, 1);
   }
   
   // Put the Weapon in the Player's hand
   %Player.use("Disc");
}

//------------------------------------------------------------
function DefaultGame::pickPlayerSpawn(%game, %client, %respawn)
{
   // place this client on his own team, '%respawn' does not ever seem to be used
   //we no longer care whether it is a respawn since all spawns use same points.
   return %game.pickTeamSpawn(%client.team);
}

function DefaultGame::displayDeathMessages(%game, %Victim, %Killer, %DamageType, %Implement)
{
   echo("Death Message");
   echo("Victim: " @ %Victim);
   echo("Killer: " @ %Killer);
   echo("Damage Type: " @ %DamageType);
   echo("Implement: " @ %Implement);

   %VictimName = getTaggedString(%Victim.name);

   // Load the Him/Her and His/Hers part of the Victim
   if (%Victim.sex $= "Male")
   {
      %VictimGender = "him";
      %VictimPossesive = "his";
   } else {
      %VictimGender = "her";
      %VictimPossesive = "her";
   }

   // If no one is the killer, we do that now
   if (%Killer == 0 || %Killer $= "")
   {
      // Nearby Explosion
      if (%damageType == $DamageType::Explosion)
      {
         %Random = mFloor(getRandom() * $DeathMessageExplosionCount);
         %Message = $DeathMessageExplosion[%Random];
         messageAll('msgExplosionKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by a nearby explosion.");
         return;
      }

      // Lightning
      if (%damageType == $DamageType::Lightning)
      {
         %Random = mFloor(getRandom() * $DeathMessageLightningCount);
         %Message = $DeathMessageLightning[%Random];
         messageAll('msgLightningKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by lightning.");
         return;
      }

      // Fire
      if (%damageType == $DamageType::Fire)
      {
         %Random = mFloor(getRandom() * $DeathMessageFireCount);
         %Message = $DeathMessageFire[%Random];
         messageAll('msgExplosionKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by fire.");
         return;
      }

      // Vehicle Spawning
      if (%damageType == $DamageType::VehicleSpawn)
      {
         %Random = mFloor(getRandom() * $DeathMessageVehPadCount);
         %Message = $DeathMessageVehPad[%Random];
         messageAll('msgExplosionKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by a vehicle spawning.");
         return;
      }
   }

   // If it was self inflicted
   if (%Victim == %Killer)
   { 
      // Suicide Button?
      if (%damageType == $DamageType::Default)
      {
         %Random = mFloor(getRandom() * $DeathMessageSuicideCount);
         %Message = $DeathMessageSuicide[%Random];
         messageAll('msgSuicide', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " kills themself.");
         return;
      }

      // Lava
      if (%damageType == $DamageType::Lava)
      {
         %Random = mFloor(getRandom() * $DeathMessageLavaCount);
         %Message = $DeathMessageLava[%Random];
         messageAll('msgLavaKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by lava.");
         return;
      }

      // Vehicle Spawning
      if (%damageType == $DamageType::VehicleSpawn)
      {
         %Random = mFloor(getRandom() * $DeathMessageVehPadCount);
         %Message = $DeathMessageVehPad[%Random];
         messageAll('msgExplosionKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by a vehicle spawning.");
         return;
      }

      // Nexus Camping
      if (%damageType == $DamageType::NexusCamping)
      {
         %Random = mFloor(getRandom() * $DeathMessageNexusCount);
         %Message = $DeathMessageNexus[%Random];
         messageAll('msgCampKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed for Nexus Camping.");
         return;
      }

      // Out of the Mission Area
      if (%damageType == $DamageType::OutOfBounds)
      {
         %Random = mFloor(getRandom() * $DeathMessageOOBCount);
         %Message = $DeathMessageOOB[%Random];
         messageAll('msgOOBKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed for Nexus Camping.");
         return;
      }

      // Slamming into the Ground or something else
      if (%damageType == $DamageType::Ground)
      {
         %Random = mFloor(getRandom() * $DeathMessageSelfKillCount);
         %Message = $DeathMessageSelfKill[%damageType, %Random];
         messageAll('msgSelfKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by slamming into something.");
         return;
      }

      // Slamming into Water
      if (%damageType == $DamageType::Water)
      {
         %Random = mFloor(getRandom() * $DeathMessageSelfKillCount);
         %Message = $DeathMessageSelfKill[%damageType, %Random];
         messageAll('msgSelfKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by slamming into water.");
         return;
      }

      // If not a weapon was used
      %Random = mFloor(getRandom() * $DeathMessageSelfKillCount);
      %Message = $DeathMessageSelfKill[%damageType, %Random];
      messageAll('msgSuicide', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
      logEcho(%VictimName @ " kills themself (damage type: " @ $DamageTypeName[%damageType] @ ").");
      return;
   }
      
   %KillerName = getTaggedString(%Killer.name);

   // Load the Him/Her and His/Hers part of the Killer
   if (%Killer.sex $= "Male")
   {
      %KillerGender = "him";
      %KillerPossesive = "his";
   } else {
      %KillerGender = "her";
      %KillerPossesive = "her";
   }

   // If it was a Team Kill
   if (%Victim.Team == %Killer.Team)
   {
      %Killer.TKs++;
      %TKs = %Killer.TKs;
      switch (%TKs)
      {
         case 1 :
            %TKTime = "1st";

         case 2 :
            %TKTime = "2nd";

         case 3 :
            %TKTime = "3rd";

         default :
            %TKTime = %TKs @ "th";
      }

      %Message = %KillerName @ " TEAMKILLED " @ %VictimName @ "! (" @ %KillerPossesive @ " " @ %TKTime @ " this map.)";
      messageAll('msgTeamKill', %Message, "", "", "", "", "", "", %DamageType);
      logEcho(%KillerName @ " team kills " @ %VictimName @ " (damage type: " @ $DamageTypeName[%damageType] @ ").");
      return;
   }

   // If it was a vehicle Impact, we do all sorts of extra checks
   if (%DamageType == $DamageType::Impact)
   {
      // Load the Pilot of the Craft
      %Controller = %Implement.getControllingClient();
      if (%Controller $= "")
      {
         // If there is no pilot, load the last pilot since they must have just bailed
         %Player = %Implement.lastPilot;
         if (%Player.client !$= "")
            %Controller = %Player;

         %Unmanned = true;
      }

      // If there is/was a pilot we do that now
      if(%Controller > 0)
      {
         %Killer = %Controller;

         // Load the Him/Her and His/Hers part of the Killer
         if (%Killer.sex $= "Male")
         {
            %KillerGender = "him";
            %KillerPossesive = "his";
         } else {
            %KillerGender = "her";
            %KillerPossesive = "her";
         }

         %killerName = %Killer.name;

         // Player was hit by thier vehicle
         if (%Victim == %Killer)
         {
            %Random = mFloor(getRandom() * $DeathMessageSelfKillCount);
            %Message = $DeathMessageSelfKill[$DamageType::Impact, %Random];
            messageAll('msgVehicleKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
            logEcho(%VictimName @ " kills themself with a vehicle (damage type: " @ $DamageTypeName[%damageType] @ ").");
            return;
         }

         // If it was a Team Kill
         if (%Victim.Team == %Killer.Team)
         {
            %Killer.TKs++;
            %TKs = %Killer.TKs;
            switch (%TKs)
            {
               case 1 :
                  %TKTime = "1st";

               case 2 :
                  %TKTime = "2nd";
 
               case 3 :
                  %TKTime = "3rd";

               default :
                  %TKTime = %TKs @ 'th';
            }

            %Message = %KillerName @ " TEAMKILLED " @ %VictimName @ " with a vehicle! (" @ %KillerPossesive @ " " @ %TKTime @ " team kill this map.)";
            messageAll('msgTeamKill', %Message, "", "", "", "", "", "", %DamageType);
            logEcho(%KillerName @ " team kills " @ %VictimName @ " with a vehicle (damage type: " @ $DamageTypeName[%damageType] @ ").");
            return;
         }

         // Death Message if the Pilot bailed and let the vehicle hit the victim
         if (%Unmanned == true)
         {
            %Random = mFloor(getRandom() * $DeathMessageVehicleCount);
            %Message = $DeathMessageVehicle[%DamageType, %Random];
            messageAll('msgVehicleKill', %Message, %victimName, %VictimGender, %VictimPossesive, %KillerName, %KillerGender, %KillerPossesive, %DamageType);
            logEcho(%KillerName @ " kills " @ %VictimName @ " with a vehicle (damage type: " @ $DamageTypeName[%damageType] @ ").");
            return;
         }

         // Death Message if the Pilot was in Control and ran them down
         %Random = mFloor(getRandom() * $DeathMessageImpactCount);
         %Message = $DeathMessageVehicle[%DamageType, %Random];
         messageAll('msgVehicleKill', %Message, %victimName, %VictimGender, %VictimPossesive, %KillerName, %KillerGender, %KillerPossesive, %DamageType);
         logEcho(%KillerName @ " kills " @ %VictimName @ " with a vehicle (damage type: " @ $DamageTypeName[%damageType] @ ").");
         return;

      } else {

         // Death Message if no Pilot is in Control
         %Random = mFloor(getRandom() * $DeathMessageVehicleUnmannedCount);
         %Message = $DeathMessageVehicleUnmanned[%Random];
         messageAll('msgVehicleKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
         logEcho(%VictimName @ " is killed by an unmanned vehicle (damage type: " @ $DamageTypeName[%damageType] @ ").");
         return;
      }
   }

   echo("3.");

   // If it was a headshot, then note that now
   if (%Victim.headShot == true)
   {
      %Victim.headShot = "";
      %Random = mFloor(getRandom() * $DeathMessageHeadshotCount);
      %Message = $DeathMessageHeadshot[%Random];
      messageAll('MsgHeadshotKill', %Message, %victimName, %VictimGender, %VictimPossesive, %KillerName, %KillerGender, %KillerPossesive, %DamageType);
      logEcho(%KillerName @ " kills " @ %VictimName @ " with a headshot (damage type: " @ $DamageTypeName[%damageType] @ ").");
      return;
   }

   // If a turret or weapon on a vehicle
   if (isObject(%Implement) && (%Implement.getClassName() $= "Turret" || %Implement.getClassName() $= "VehicleTurret" || %Implement.getDataBlock().catagory $= "Vehicles"))
   {
      %Controller = %Implement.getControllingClient();
      if (%Controller !$= "0")
      {
         %Killer = %Controller;

         // Load the Him/Her and His/Hers part of the Killer
         if (%Killer.sex $= "Male")
         {
            %KillerGender = "him";
            %KillerPossesive = "his";
         } else {
            %KillerGender = "her";
            %KillerPossesive = "her";
         }

         %killerName = %Killer.name;

         // Player shot themselves with the turret
         if (%Victim == %Killer)
         {
            %Random = mFloor(getRandom() * $DeathMessageTurretSelfKillCount);
            %Message = $DeathMessageTurretSelfKill[%Random];
            messageAll('msgTurretSelfKill', %Message, %victimName, %VictimGender, %VictimPossesive, "", "", "", %DamageType);
            logEcho(%VictimName @ " kills themself with a turret (damage type: " @ $DamageTypeName[%damageType] @ ").");
            return;
         }

         // If it was a Team Kill
         if (%Victim.Team == %Killer.Team)
         {
            %Killer.TKs++;
            %TKs = %Killer.TKs;
            switch (%TKs)
            {
               case 1 :
                  %TKTime = "1st";

               case 2 :
                  %TKTime = "2nd";
 
               case 3 :
                  %TKTime = "3rd";

               default :
                  %TKTime = %TKs @ 'th';
            }

            %Message = %KillerName @ " TEAMKILLED " @ %VictimName @ " with a turret! (" @ %KillerPossesive @ " " @ %TKTime @ " team kill this map.)";
            messageAll('msgTeamKill', %Message, "", "", "", "", "", "", %DamageType);
            logEcho(%KillerName @ " team kills " @ %VictimName @ " with a turret (damage type: " @ $DamageTypeName[%damageType] @ ").");
            return;
         }

         %Random = mFloor(getRandom() * $DeathMessageCount);
         %Message = $DeathMessage[%DamageType, %Random];
         messageAll('msgTurretKill', %Message, %victimName, %VictimGender, %VictimPossesive, %KillerName, %KillerGender, %KillerPossesive, %DamageType);
         logEcho(%KillerName @ " kills " @ %VictimName @ " with a turret (damage type: " @ $DamageTypeName[%damageType] @ ").");
         return;

      } else {
         %Message = %VictimName @ " is killed by a turret!";
         messageAll('msgTurretKill', %Message, "", "", "", "", "", "", %DamageType);
         logEcho(%VictimName @ " killed by a turret (damage type: " @ $DamageTypeName[%damageType] @ ").");
         return;
      }
   }

   %Random = mFloor(getRandom() * $DeathMessageCount);
   %Message = $DeathMessage[%DamageType, %Random];
   messageAll('MsgLegitKill', %Message, %victimName, %VictimGender, %VictimPossesive, %KillerName, %KillerGender, %KillerPossesive, %DamageType);
   logEcho(%KillerName @ " kills " @ %VictimName @ " (damage type: " @ $DamageTypeName[%damageType] @ ").");
   return;
}

//------------------------------------------------------------
function DefaultGame::createPlayer(%game, %client, %spawnLoc, %respawn)
{
   // clients and cameras can exist in team 0, but players should not
   if(%client.team == 0)
      error("Players should not be added to team0!");

   if( %client.player > 0 )
      error( "Attempting to create an angus ghost!" );
   
   // defaultplayerarmor is in 'players.cs'
   if(%spawnLoc == -1)      
      %spawnLoc = "0 0 300 1 0 0 0";
   //else
   //  echo("Spawning player at " @ %spawnLoc);

   // copied from player.cs
   if (%client.race $= "Bioderm")
      // Only have male bioderms.
      %armor = $DefaultPlayerArmor @ "Male" @ %client.race @ Armor;
   else
      %armor = $DefaultPlayerArmor @ %client.sex @ %client.race @ Armor;
   %client.armor = $DefaultPlayerArmor;

   %player = new Player()
   {
      dataBlock = %armor;
   };
   
   if(%respawn)
   {
      %player.setInvincible(true);
      %player.setCloaked(true);
      %player.setInvincibleMode($InvincibleTime,0.02);
      %player.respawnCloakThread = %player.schedule($InvincibleTime * 1000, "setRespawnCloakOff");
      %player.schedule($InvincibleTime * 1000, "setInvincible", false);
   } else {
      if (%client.HavocClient != true)
      {
         %message = "You do not have the Havoc Client, it provides you with many more features and corrects several HUDS.  You can download it at www.havoctribe.org";
         CenterPrint(%client, %message, 10, 3);
         echo("Center Print Help");
      }
   }
   
   %player.setTransform( %spawnLoc );
   MissionCleanup.add(%player);

   // setup some info
   %player.setOwnerClient(%client);
   %player.team = %client.team;
   %client.outOfBounds = false;
   %player.setEnergyLevel(60);
   %client.player = %player;

   // updates client's target info for this player
   %player.setTarget(%client.target);
   setTargetDataBlock(%client.target, %player.getDatablock());
   setTargetSensorData(%client.target, PlayerSensor);
   setTargetSensorGroup(%client.target, %client.team);
   %client.setSensorGroup(%client.team);

   //make sure the player has been added to the team rank array...
   %game.populateTeamRankArray(%client);

   %game.playerSpawned(%client.player);
}

function Player::setRespawnCloakOff(%player)
{
   %player.setCloaked(false);
   %player.respawnCloakThread = "";
}

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

function DefaultGame::startMatch(%game)
{
   echo("START MATCH");
   MessageAll('MsgMissionStart', "\c2Match started!");

   //the match has been started, clear the team rank array, and repopulate it...
   for (%i = 0; %i < 32; %i++)
      %game.clearTeamRankArray(%i);

   //used in BountyGame, prolly in a few others as well...
   $matchStarted = true;

   $missionStartTime = getSimTime();
   %curTimeLeftMS = ($Host::TimeLimit * 60 * 1000);

   // schedule first timeLimit check for 20 seconds
   if(%game.class !$= "SiegeGame")
   {
      %game.timeCheck = %game.schedule(20000, "checkTimeLimit");
   }

   //schedule the end of match countdown
   EndCountdown($Host::TimeLimit * 60 * 1000);

   //reset everyone's score and add them to the team rank array
   for (%i = 0; %i < ClientGroup.getCount(); %i++)
   {
      %cl = ClientGroup.getObject(%i);
      %game.resetScore(%cl);
      %game.populateTeamRankArray(%cl);
   }
   
   // set all clients control to their player
   %count = ClientGroup.getCount();
   for( %i = 0; %i < %count; %i++ )
   {
      %cl = ClientGroup.getObject(%i);
      
      // Siege game will set the clock differently
      if(%game.class !$= "SiegeGame")
         messageClient(%cl, 'MsgSystemClock', "", $Host::TimeLimit, %curTimeLeftMS);
      
      if( !$Host::TournamentMode && %cl.matchStartReady && %cl.camera.mode $= "pre-game")
      {
         commandToClient(%cl, 'setHudMode', 'Standard');
         %cl.setControlObject( %cl.player );
      }
      else
      {
         if( %cl.matchStartReady )
         {
            if(%cl.camera.mode $= "pre-game")
            {
               %cl.observerMode = "";
               commandToClient(%cl, 'setHudMode', 'Standard');
               
               if(isObject(%cl.player))
                  %cl.setControlObject( %cl.player );
               else
                  echo("can't set control for client: " @ %cl @ ", no player object found!");
            }
            else
               %cl.observerMode = "observerFly";
         }
      }
   }
   
   // on with the show this is it!
   AISystemEnabled( true );
}

function DefaultGame::gameOver( %game )
{
   //set the bool
   $missionRunning = false;

   CancelCountdown();
   CancelEndCountdown();
   
   //loop through all the clients, and do any cleanup...
   %count = ClientGroup.getCount();
   for (%i = 0; %i < %count; %i++)
   {
      %client = ClientGroup.getObject(%i);
      %player = %client.player;
      %client.lastTeam = %client.team;
      
      if ( !%client.isAiControlled() )
      {
         %client.endMission();
         messageClient( %client, 'MsgClearDebrief', "" );
         %game.sendDebriefing( %client );

         //clear the score hud...
         messageClient( %client, 'SetScoreHudHeader', "", "" );
         messageClient( %client, 'SetScoreHudSubheader', "", "");
         messageClient( %client, 'ClearHud', "", 'scoreScreen', 0 );
      }
   }

   // Default game does nothing...  except lets the AI know the mission is over
   AIMissionEnd();
}

//------------------------------------------------------------------------------
function DefaultGame::sendDebriefing( %game, %client )
{
   if ( %game.numTeams == 1 )
   {
      // Mission result:
      %winner = $TeamRank[0, 0];
      if ( %winner.score > 0 )
         messageClient( %client, 'MsgDebriefResult', "", '<just:center>%1 wins!', $TeamRank[0, 0].name );
      else
         messageClient( %client, 'MsgDebriefResult', "", '<just:center>Nobody wins.' );

      // Player scores:
      %count = $TeamRank[0, count];
      messageClient( %client, 'MsgDebriefAddLine', "", '<tab:150,210><spush><color:00dc00><font:univers condensed:18>PLAYER\tSCORE\tKILLS<spop>' );
      for ( %i = 0; %i < %count; %i++ )
      {
         %cl = $TeamRank[0, %i];
         if ( %cl.score $= "" )
            %score = 0;
         else
            %score = %cl.score;
         if ( %cl.kills $= "" )
            %kills = 0;
         else
            %kills = %cl.kills;
         messageClient( %client, 'MsgDebriefAddLine', "", '<tab:5,155,215><clip:150>\t%1</clip>\t%2\t%3', %cl.name, %score, %kills );
      }
   }
   else
   {
      %topScore = "";
      %topCount = 0;
      for ( %team = 1; %team <= %game.numTeams; %team++ )
      {
         if ( %topScore $= "" || $TeamScore[%team] > %topScore )
         {
            %topScore = $TeamScore[%team];
            %firstTeam = %team;
            %topCount = 1;
         }
         else if ( $TeamScore[%team] == %topScore )
         {
            %secondTeam = %team;
            %topCount++;
         }
      }

      // Mission result:
      if ( %topCount == 1 )
         messageClient( %client, 'MsgDebriefResult', "", '<just:center>Team %1 wins!', game.getTeamName(%firstteam) );
      else if ( %topCount == 2 )
         messageClient( %client, 'MsgDebriefResult', "", '<just:center>Team %1 and Team %2 tie!', game.getTeamName(%firstTeam), game.getTeamName(%secondTeam));
      else
         messageClient( %client, 'MsgDebriefResult', "", '<just:center>The mission ended in a tie.' );

      // Team scores:
      messageClient( %client, 'MsgDebriefAddLine', "", '<tab:150><spush><color:00dc00><font:univers condensed:18>TEAM\tSCORE<spop>' );
      for ( %team = 1; %team - 1 < %game.numTeams; %team++ )
      {
         if ( $TeamScore[%team] $= "" )
            %score = 0;
         else
            %score = $TeamScore[%team];
         messageClient( %client, 'MsgDebriefAddLine', "", '<tab:5,155><clip:150>\t%1</clip>\t%2', game.getTeamName(%team), %score );
      }

      // Player scores:
      messageClient( %client, 'MsgDebriefAddLine', "", '\n<tab:120,180,230><spush><color:00dc00><font:univers condensed:18>PLAYER\tTEAM\tSCORE\tKILLS<spop>' );
      for ( %team = 1; %team - 1 < %game.numTeams; %team++ )
         %count[%team] = 0;

      %notDone = true;
      while ( %notDone )
      {
         // Get the highest remaining score:
         %highScore = "";
         for ( %team = 1; %team <= %game.numTeams; %team++ )
         {
            if ( %count[%team] < $TeamRank[%team, count] && ( %highScore $= "" || $TeamRank[%team, %count[%team]].score > %highScore ) )
            {
               %highScore = $TeamRank[%team, %count[%team]].score;
               %highTeam = %team;
            }
         }

         // Send the debrief line:
         %cl = $TeamRank[%highTeam, %count[%highTeam]];
         %score = %cl.score $= "" ? 0 : %cl.score;
         %kills = %cl.kills $= "" ? 0 : %cl.kills;
         messageClient( %client, 'MsgDebriefAddLine', "", '<tab:5,125,185,235><clip:120>\t%1</clip>\t<clip:55>%2</clip>\t%3\t%4', %cl.name, game.getTeamName(%cl.team), %score, %kills );

         %count[%highTeam]++;
         %notDone = false;
         for ( %team = 1; %team - 1 < %game.numTeams; %team++ )
         {
            if ( %count[%team] < $TeamRank[%team, count] )
            {
               %notDone = true;
               break;
            }
         }
      }
   }

   //now go through an list all the observers:
   %count = ClientGroup.getCount();
   %printedHeader = false;
   for (%i = 0; %i < %count; %i++)
   {
      %cl = ClientGroup.getObject(%i);
      if (%cl.team <= 0)
      {
         //print the header only if we actually find an observer
         if (!%printedHeader)
         {
            %printedHeader = true;
            messageClient(%client, 'MsgDebriefAddLine', "", '\n<spush><color:00dc00><font:univers condensed:18><tab:150,210>OBSERVERS\tSCORE<spop>');
         }

         //print out the client
         %score = %cl.score $= "" ? 0 : %cl.score;
         messageClient( %client, 'MsgDebriefAddLine', "", '<tab:5,155,215><clip:150>\t%1</clip>\t%2', %cl.name, %score);
      }
   }
}

//------------------------------------------------------------
// jff: 'numDeathMsgLines' is set in 'message.cs' which seems to conflict with this
$numDeathMsgs = 2;

function DefaultGame::clearDeployableMaxes(%game)
{
   // Handled already before this
}

// called from player scripts
function DefaultGame::onClientDamaged(%Game, %TargetClient, %AttackerClient, %DamageType, %SourceObject, %Amount)
{ 
   // If who shot the player was not another player or turret, ignore it
   %SourceClassType = %SourceObject.getDataBlock().getClassName();
   if (%SourceClassType $= "TurretData" || %SourceClassType $= "PlayerData")
     echo('Found');
   else
      return;

   // If there was no damage done, just ignore the user
   if (%Amount $= "" || %Amount == 0)
      return;

   %TargetTeam = %TargetClient.team;
   %AttackerTeam = %AttackerClient.team;
   
   // If a turret shot the player and it was also a deployable, then we let the AI know so 
   // they can shoot at it, otherwise we let them run by it to avoid taking damage.
   if (%SourceClassType $= "TurretData")
   { 
      %DeployedTurret = %SourceObject.getDataBlock().deployedObject;
      if (%DeployedTurret == true)
      {
         %TargetClient.lastDamageTurretTime = getSimTime();
         %TargetClient.lastDamageTurret = %SourceObject;
      }

      // If they injured someone on thier team, regardless of Score by Turrets or not
      if (%TargetTeam == %AttackerTeam)
      {
         // Dock the players score for harming a friendly
         %AttackerClient.DamageDone += $FriendlyFireFactor * %Amount;

         if (%TargetClient.holdingFlag == true)
            %AttackerClient.DamageDone += $FriendlyFireFlagFactor * %Amount;
      } else {

         // If someone was controlling the turret or they placed it credit them.
         if ($TurretsScore == true)
         {
            // Increase the players score for harming an enemy
            %AttackerClient.DamageDone += $EnemyFireFactor * %Amount;

            if (%TargetClient.holdingFlag == true)
               %AttackerClient.DamageDone += $EnemyFireFlagFactor * %Amount;
         }

      }
   }

   if (%SourceClassType $= "PlayerData")
   {
      // If there are 2 teams and the people are on the same team, the dock the attacker
      if(%AttackerClient > 0 && %TargetTeam == %AttackerClient.team && %Game.numTeams > 1)
      {   
         // If the Player was hurt by someone other than himself on his own team, tell them to stop
         if (%AttackerClient != %TargetClient)
         {
            if (%AttackerClient.player.causedRecentDamage != %TargetClient.player)
            {
               %AttackerClient.player.causedRecentDamage = %TargetClient.player;
               %AttackerClient.player.schedule(1000, "causedRecentDamage", "");   //allow friendly fire message every x ms
               %Game.friendlyFireMessage(%TargetClient, %AttackerClient);          
            }
         }

         // Dock the players score for harming a friendly
         %AttackerClient.DamageDone += $FriendlyFireFactor * %Amount;

         if (%TargetClient.holdingFlag == true)
            %AttackerClient.DamageDone += $FriendlyFireFlagFactor * %Amount;

      } else {

         // Increase the players score for harming an enemy
         %AttackerClient.DamageDone += $EnemyFireFactor * %Amount;

         if (%TargetClient.holdingFlag == true)
            %AttackerClient.DamageDone += $EnemyFireFlagFactor * %Amount;

         %TargetClient.lastDamageTime = getSimTime();
         %TargetClient.lastDamageClient = %AttackerClient;
         if (%TargetClient.isAIControlled())
            %TargetClient.clientDetected(%AttackerClient);
      }
   }

   if (%AttackerClient.damageDone >= 1 || %AttackerClient.damageDone <= -1)
      ScoreUpdate(%AttackerClient);

   //call the game specific AI routines...
   if (isObject(%TargetClient) && %TargetClient.isAIControlled())
      %Game.onAIDamaged(%TargetClient, %AttackerClient, %DamageType, %SourceObject);

   if (isObject(%AttackerClient) && %AttackerClient.isAIControlled())
      %Game.onAIFriendlyFire(%TargetClient, %AttackerClient, %DamageType, %SourceObject);
}

function DefaultGame::friendlyFireMessage(%game, %damaged, %damager)
{
   messageClient(%damaged, 'MsgDamagedByTeam', '\c1You were harmed by teammate %1', %damager.name);
   messageClient(%damager, 'MsgDamagedTeam', '\c1You just harmed teammate %1.', %damaged.name);
}

function DefaultGame::clearWaitRespawn(%game, %client)
{
   %client.waitRespawn = 0;
}

// When the player Dies
function DefaultGame::onClientKilled(%Game, %TargetClient, %AttackerClient, %DamageType, %Implement, %DamageLocation)
{     
   %TargetPlayer = %TargetClient.player;
   %AttackerPlayer = %AttackerClient.player;

   %TargetClient.plyrPointOfDeath = %TargetPlayer.position;
   %TargetClient.plyrDiedHoldingFlag = %TargetPlayer.holdingFlag;
   %TargetClient.waitRespawn = $RespawnDelay - 1;

   // Cancel any pending checks
   %TargetPlayer.CancelTimers();

   %TargetClient.respawnTimer = %Game.schedule(($Host::PlayerRespawnTimeout * 1000), "forceObserver", %TargetClient, "spawnTimeout" );

   // reset the alarm for out of bounds
   if(%TargetClient.outOfBounds)
      messageClient(%TargetClient, 'EnterMissionArea', "");

   // Based on the method of death, we set the respawn delay
   if (%DamageType == $DamageType::suicide)
      %respawnDelay = $SuicideRespawnDelay;
   else
      %respawnDelay = $RespawnDelay;
   %Game.schedule(%respawnDelay * 1000, "clearWaitRespawn", %TargetClient);

   // if victim had an undetonated satchel charge pack, get rid of it
   if(%TargetPlayer.thrownChargeId != 0)
      if(!%TargetPlayer.thrownChargeId.kaboom)
         %TargetPlayer.thrownChargeId.delete();

   // Any vehicle attacked to the player will decay when the Vehicle Timeout hits unless someone else claims it.
   if (%TargetPlayer.lastVehicle !$= "" && %TargetPlayer.lastVehicle == %TargetPlayer.lastVehicle.lastPilot)
   {
      schedule($VehicleTimeOut * 1000, %TargetPlayer.lastVehicle,"vehicleAbandonTimeOut", %TargetPlayer.lastVehicle);
      %TargetPlayer.lastVehicle.lastPilot = "";
   }   
   
   // Unmount from Vehicle
   if(%TargetPlayer.isMounted())
   {
      if(%TargetPlayer.vehicleTurret)
         %TargetPlayer.vehicleTurret.getDataBlock().playerDismount(%TargetPlayer.vehicleTurret);
      else
      {
         %TargetPlayer.getDataBlock().doDismount(%TargetPlayer, true);
         %TargetPlayer.mountVehicle = false;
      }
   }

   // If at a station, enable the inventory screen
   if(%TargetPlayer.inStation)
      commandToClient(%TargetPlayer.client,'setStationKeys', false);

   // reset who triggered this station and cancel outstanding armor switch thread   
   if(%TargetPlayer.station)
   {
      %TargetPlayer.station.triggeredBy = "";
      %TargetPlayer.station.getDataBlock().stationTriggered(%TargetPlayer.station, 0);
   }   

   // Let the player see thier body, or what is left of it.
   %TargetClient.camera.mode = "playerDeath";

   // Close any other HUDs and bring up the standard, then clear that
   messageClient(%TargetClient, 'CloseHud', "", 'inventoryScreen');
   messageClient(%TargetClient, 'CloseHud', "", 'vehicleHud');
   commandToClient(%TargetClient, 'setHudMode', 'Standard', "", 0);
   %TargetClient.SetWeaponsHudClearAll();
   %TargetClient.SetInventoryHudClearAll();
   %TargetClient.setAmmoHudCount(-1);
   messageClient(%TargetClient, 'msgDeploySensorOff', "");
   messageClient(%TargetClient, 'msgPackIconOff', "");

   %TargetPlayer.client.deployPack = false;
   deactivateDeploySensor(%TargetPlayer);

   // Stop any repairing as they are dead.
   %TargetPlayer.setRepairRate(0);
   %TargetPlayer.setImageTrigger($WeaponSlot, false);

   playDeathAnimation(%TargetPlayer, %damageLocation, %DamageType);
   playDeathCry(%TargetPlayer);

   %Game.displayDeathMessages(%TargetClient, %AttackerClient, %DamageType, %implement);
   %Game.updateKillScores(%TargetClient, %AttackerClient, %DamageType, %implement);

   // Toss whatever is being carried.  There are 9 slots but we only use 
   for(%index = 0 ; %index <= $ArmorSlots; %index++)
   {
      %image = %TargetPlayer.getMountedImage(%index);
      if(%image)
      {
         if(%index == $FlagSlot)
            %TargetPlayer.throwObject(%TargetPlayer.holdingFlag);
         else 
            %TargetPlayer.throw(%image.item);
      }
   } 

   // Make the player an observer that nothing can target
   setTargetDataBlock(%TargetClient.target, 0);
   setTargetSensorData(%TargetClient.target, 0);

   //if the killer was an AI...
   if (isObject(%AttackerClient) && %AttackerClient.isAIControlled())
      %Game.onAIKilledClient(%TargetClient, %AttackerClient, %DamageType, %implement);

   // reset control object on this player: also sets 'playgui' as content
   serverCmdResetControlObject(%TargetClient);

   // set control object to the camera   
   %TargetClient.player = 0;
   %Transform = %TargetPlayer.getTransform();

   //note, AI's don't have a camera...
   if (isObject(%TargetClient.camera))
   {
      %TargetClient.camera.setTransform(%Transform);
      %TargetClient.camera.setOrbitMode(%TargetPlayer, %Transform, 0.5, 4.5, 4.5);
      %TargetClient.setControlObject(%TargetClient.camera);
   }

   //hook in the AI specific code for when a client dies
   if (%TargetClient.isAIControlled())
   {
      aiReleaseHumanControl(%TargetClient.controlByHuman, %TargetClient);
      %Game.onAIKilled(%TargetClient, %AttackerClient, %DamageType, %implement);
   }
   else
      aiReleaseHumanControl(%TargetClient, %TargetClient.controlAI);

   //used to track corpses so the AI can get ammo, etc...
   AICorpseAdded(%TargetPlayer);

   //if the death was a suicide, prevent respawning for 5 seconds...
   %TargetClient.lastDeathSuicide = false;
   if (%DamageType == $DamageType::Suicide)
   {
      %TargetClient.lastDeathSuicide = true;
      %TargetClient.suicideRespawnTime = getSimTime() + ($SuicideRespawnDelay * 1000);
   }
}

function DefaultGame::forceObserver( %game, %client, %reason )
{
   //make sure we have a valid client...
   if (%client <= 0)
      return;

   // first kill this player
   if(%client.player)
      %client.player.scriptKill(0);
   
   if( %client.respawnTimer )
      cancel(%client.respawnTimer);
   
   %client.respawnTimer = "";
   
   // remove them from the team rank array
   %game.removeFromTeamRankArray(%client);

   // place them in observer mode
   %client.lastObserverSpawn = -1;
   %client.observerStartTime = getSimTime();
   %adminForce = 0;
   
   switch$ ( %reason )
   {
      case "playerChoose":
         %client.camera.getDataBlock().setMode( %client.camera, "observerFly" );
         messageClient(%client, 'MsgClientJoinTeam', '\c2You have become an observer.', %client.name, $teamName[0], %client, 0 );
         logEcho(%client.nameBase@" (cl "@%client@") entered observer mode");
         %client.lastTeam = %client.team;
      
      case "AdminForce":
         %client.camera.getDataBlock().setMode( %client.camera, "observerFly" );
         messageClient(%client, 'MsgClientJoinTeam', '\c2You have been forced into observer mode by the admin.', %client.name, $teamName[0], %client, 0 );
         logEcho(%client.nameBase@" (cl "@%client@") was forced into observer mode by admin");
         %client.lastTeam = %client.team;
         %adminForce = 1;
         
         if($Host::TournamentMode)
         {
            if(!$matchStarted)
            {   
               if(%client.camera.Mode $= "pickingTeam")
               {
                  commandToClient( %client, 'processPickTeam');
                  clearBottomPrint( %client );
               }
               else
               {
                  clearCenterPrint(%client);
                  %client.notReady = true;
               }
            }
         }
         
      case "spawnTimeout":
         %client.camera.getDataBlock().setMode( %client.camera, "observerTimeout" );
         messageClient(%client, 'MsgClientJoinTeam', '\c2You have been placed in observer mode due to delay in respawning.', %client.name, $teamName[0], %client, 0 );
         logEcho(%client.nameBase@" (cl "@%client@") was placed in observer mode due to spawn delay");
         // save the team the player was on - only if this was a delay in respawning
         %client.lastTeam = %client.team;
   }
   
   // switch client to team 0 (observer)
   %client.team = 0;
   %client.player.team = 0;
   setTargetSensorGroup( %client.target, %client.team );
   %client.setSensorGroup( %client.team );

   // set their control to the obs. cam
   %client.setControlObject( %client.camera );
   commandToClient(%client, 'setHudMode', 'Observer');
   
   // display the hud
   //displayObserverHud(%client, 0);
   updateObserverFlyHud(%client);

   
   // message everyone about this event
   if( !%adminForce )
      messageAllExcept(%client, -1, 'MsgClientJoinTeam', '\c2%1 has become an observer.', %client.name, $teamName[0], %client, 0 );
   else
      messageAllExcept(%client, -1, 'MsgClientJoinTeam', '\c2The admin has forced %1 to become an observer.', %client.name, 0 );

   updateCanListenState( %client );
   
   // call the onEvent for this game type
   %game.onClientEnterObserverMode(%client);  //Bounty uses this to remove this client from others' hit lists 
}

function DefaultGame::assignClientTeam(%game, %client, %respawn )
{
   // this function is overwritten in non-team mission types (e.g. DM)
   // so these lines won't do anything
   //if(!%game.numTeams)
   //{
   //   setTargetSkin(%client.target, %client.skin);
   //   return;
   //}
      
   //  camera is responsible for creating a player
   //  - counts the number of players per team
   //  - puts this player on the least player count team
   //  - sets the client's skin to the servers default

   %numPlayers = ClientGroup.getCount();
   for(%i = 0; %i <= %game.numTeams; %i++)
      %numTeamPlayers[%i] = 0;

   for(%i = 0; %i < %numPlayers; %i = %i + 1)
   {
      %cl = ClientGroup.getObject(%i);
      if(%cl != %client)
         %numTeamPlayers[%cl.team]++;
   }
   %leastPlayers = %numTeamPlayers[1];
   %leastTeam = 1;
   for(%i = 2; %i <= %game.numTeams; %i++)
   {
      if( (%numTeamPlayers[%i] < %leastPlayers) || 
         ( (%numTeamPlayers[%i] == %leastPlayers) && 
         ($teamScore[%i] < $teamScore[%leastTeam] ) ))
      {
         %leastTeam = %i;
         %leastPlayers = %numTeamPlayers[%i];
      }
   }
   
   %client.team = %leastTeam;
   %client.lastTeam = %team;

   // Assign the team skin:
   if ( %client.isAIControlled() )
   {
      if ( %leastTeam & 1 )
      {
         %client.skin = addTaggedString( "basebot" );
         setTargetSkin( %client.target, 'basebot' );
      }
      else
      {
         %client.skin = addTaggedString( "basebbot" );
         setTargetSkin( %client.target, 'basebbot' );
      }
   }
   else
      setTargetSkin( %client.target, $teamSkin[%client.team] );
      //setTargetSkin( %client.target, %client.skin );

   // might as well standardize the messages
   //messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined %2.', %client.name, game.getTeamName(%leastTeam), %client, %leastTeam );
   //messageClient( %client, 'MsgClientJoinTeam', '\c1You joined the %2 team.', $client.name, game.getTeamName(%client.team), %client, %client.team );
   messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined %2.', %client.name, game.getTeamName(%client.team), %client, %client.team );
   messageClient( %client, 'MsgClientJoinTeam', '\c1You joined the %2 team.', %client.name, game.getTeamName(%client.team), %client, %client.team );

   updateCanListenState( %client );

   logEcho(%client.nameBase@" (cl "@%client@") joined team "@%client.team);
}

function DefaultGame::clientJoinTeam( %game, %client, %team, %respawn )
{
   if ( %team < 1 || %team > %game.numTeams )
      return;

   if( %respawn $= "" )
      %respawn = 1;
      
   %client.team = %team;
   %client.lastTeam = %team;
   setTargetSkin( %client.target, $teamSkin[%team] );
   setTargetSensorGroup( %client.target, %team );
   %client.setSensorGroup( %team );

   // Spawn the player:
   %game.spawnPlayer( %client, %respawn );

   messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined %2.', %client.name, game.getTeamName(%team), %client, %team );
   messageClient( %client, 'MsgClientJoinTeam', '\c1You joined the %2 team.', $client.name, game.getTeamName(%client.team), %client, %client.team );
   
   updateCanListenState( %client );

   logEcho(%client.nameBase@" (cl "@%client@") joined team "@%client.team);
}

function DefaultGame::AIHasJoined(%game, %client)
{
   //defined to prevent console spam
}

function DefaultGame::AIChangeTeam(%game, %client, %newTeam)
{
   //make sure we're trying to drop an AI
   if (!isObject(%client) || !%client.isAIControlled())
      return;

   //clear the ai from any objectives, etc...
	AIUnassignClient(%client);
   %client.stop();
	%client.clearTasks();
   %client.clearStep();
   %client.lastDamageClient = -1;
   %client.lastDamageTurret = -1;
   %client.shouldEngage = -1;
   %client.setEngageTarget(-1);
   %client.setTargetObject(-1);
	%client.pilotVehicle = false;
   %client.defaultTasksAdded = false;

   //kill the player, which should cause the Game object to perform whatever cleanup is required.
   if (isObject(%client.player))
      %client.player.scriptKill(0);

   //clean up the team rank array
   %game.removeFromTeamRankArray(%client);

   //assign the new team
   %client.team = %newTeam;
   if (%newTeam < 0)
      Game.assignClientTeam(%client);
   else
   {
      if ( %client.team & 1 )
      {
         %client.skin = addTaggedString( "basebot" );
         setTargetSkin( %client.target, 'basebot' );
      }
      else
      {
         %client.skin = addTaggedString( "basebbot" );
         setTargetSkin( %client.target, 'basebbot' );
      }
   }
   
   messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1bot %1 has switched to team %2.', %client.name, game.getTeamName(%client.team), %client, %client.team );
}

function DefaultGame::clientChangeTeam(%game, %client, %team, %fromObs)
{
//error("DefaultGame::clientChangeTeam");
   //first, remove the client from the team rank array
   //the player will be added to the new team array as soon as he respawns...
   %game.removeFromTeamRankArray(%client);
   
   %pl = %client.player;
   if(isObject(%pl))
   {
      if(%pl.isMounted())
         %pl.getDataBlock().doDismount(%pl);
      %pl.scriptKill(0);
   }

   // reset the client's targets and tasks only
   clientResetTargets(%client, true);

   // give this client a new handle to disassociate ownership of deployed objects
   if( %team $= "" && (%team > 0 && %team <= %game.numTeams))
   {
      if( %client.team == 1 )
         %client.team = 2;
      else
         %client.team = 1;
   }
   else
      %client.team = %team;

   // Set the client's skin:
   if (!%client.isAIControlled())
      setTargetSkin( %client.target, %game.getTeamSkin(%client.team) );
   setTargetSensorGroup( %client.target, %client.team );
   %client.setSensorGroup( %client.team );

   // Spawn the player:
   %client.lastSpawnPoint = %game.pickPlayerSpawn( %client );
   
   %game.createPlayer( %client, %client.lastSpawnPoint, $MatchStarted );

   if($MatchStarted)
      %client.setControlObject(%client.player);
   else
   {
      %client.camera.getDataBlock().setMode(%client.camera, "pre-game", %client.player);
      %client.setControlObject(%client.camera);
   }
   
   // call the onEvent for this game type
   %game.onClientEnterObserverMode(%client);  //Bounty uses this to remove this client from others' hit lists 

   if(%fromObs $= "" || !%fromObs)
   {   
      messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 switched to team %2.', %client.name, %game.getTeamName(%client.team), %client, %client.team );
      messageClient( %client, 'MsgClientJoinTeam', '\c1You switched to team %2.', $client.name, %game.getTeamName(%client.team), %client, %client.team );
   }
   else
   {
      messageAllExcept( %client, -1, 'MsgClientJoinTeam', '\c1%1 joined team %2.', %client.name, %game.getTeamName(%client.team), %client, %team );
      messageClient( %client, 'MsgClientJoinTeam', '\c1You joined team %2.', $client.name, %game.getTeamName(%client.team), %client, %client.team );
   }

   updateCanListenState( %client );
   
   // MES - switch objective hud lines when client switches teams
   messageClient(%client, 'MsgCheckTeamLines', "", %client.team);
   logEcho(%client.nameBase@" (cl "@%client@") switched to team "@%client.team);
}

//  missioncleanup and missiongroup are checked prior to entering game code
function DefaultGame::missionLoadDone(%game)
{
   //  walks through the mission group and sets the power stuff up
   //   - groups get initialized with power count 0 then iterated to 
   //     increment powercount if an object within is powered
   //   - powers objects up/down
   //MissionGroup.objectiveInit();
   MissionGroup.clearPower();
   MissionGroup.powerInit(0);

   %game.initGameVars();  //set up scoring variables and other game specific globals

   // make team0 visible/friendly to all
   setSensorGroupAlwaysVisMask(0, 0xffffffff);
   setSensorGroupFriendlyMask(0, 0xffffffff);

   // update colors:
   // - enemy teams are red
   // - same team is green
   // - team 0 is white
   for(%i = 0; %i < 32; %i++)
   {
      %team = (1 << %i);
      setSensorGroupColor(%i, %team, "0 255 0 255");
      setSensorGroupColor(%i, ~%team, "255 0 0 255");
      setSensorGroupColor(%i, 1, "255 255 255 255");

      // setup the team targets (alwyas friendly and visible to same team)
      setTargetAlwaysVisMask(%i, %team);
      setTargetFriendlyMask(%i, %team);
   }

   //set up the teams
   %game.setUpTeams();

   //clear out the team rank array...
   for (%i = 0; %i < 32; %i++)
      $TeamRank[%i, count] = "";

   // objectiveInit has to take place after setupTeams -- objective HUD relies on flags
   // having their team set
   MissionGroup.objectiveInit();

   //initialize the AI system
   %game.aiInit();
   
   //need to reset the teams if we switch from say, CTF to Bounty...
   // assign the bots team
   if ($currentMissionType !$= $previousMissionType)
   {
      $previousMissionType = $currentMissionType;
      for(%i = 0; %i < ClientGroup.getCount(); %i++)
      {
         %cl = ClientGroup.getObject(%i);
         if (%cl.isAIControlled())
            %game.assignClientTeam(%cl);
      }
   }
   
   //Save off respawn or Siege Team switch information...
   if(%game.class !$= "SiegeGame")
      MissionGroup.setupPositionMarkers(true);
   echo("Default game mission load done.");
}

function DefaultGame::onClientLeaveGame(%game, %client)
{
   // if there is a player attached to this client, kill it
   if( isObject(%client.player))
      %client.player.scriptKill(0);

   //cancel a scheduled call...
   cancel(%client.respawnTimer);
   %client.respawnTimer = "";

   //remove them from the team rank arrays
   %game.removeFromTeamRankArray(%client);
   logEcho(%client.nameBase@" (cl "@%client@") dropped");
}

function DefaultGame::clientMissionDropReady(%game, %client)
{
   //synchronize the clock HUD
   messageClient(%client, 'MsgSystemClock', "", 0, 0);
   
   %game.sendClientTeamList( %client );
   %game.setupClientHuds( %client );
   
   if($CurrentMissionType $= "SinglePlayer")
   {  
      //CommandToClient( %client, 'setPlayContent');
      return;
   }
      
   %observer = false;
   if( !$Host::TournamentMode )
   {
      if( %client.camera.mode $= "observerFly" || %client.camera.mode $= "justJoined")
      {
         %observer = true;
         commandToClient(%client, 'setHudMode', 'Observer');
         %client.setControlObject( %client.camera );
         //displayObserverHud( %client, 0 );
         updateObserverFlyHud(%client);
      }
         
      if( !%observer )
      {
         if(!$MatchStarted && !$CountdownStarted) // server has not started anything yet
         {   
            %client.setControlObject( %client.camera );
            commandToClient(%client, 'setHudMode', 'Observer');
         }
         else if(!$MatchStarted && $CountdownStarted) // server has started the countdown
         {   
            commandToClient(%client, 'setHudMode', 'Observer');
            %client.setControlObject( %client.camera );
         }
         else
         {
            commandToClient(%client, 'setHudMode', 'Standard'); // the game has already started
            %client.setControlObject( %client.player );
         }   
      }   
   }
   else
   {                                                                                                                      
      // set all players into obs mode. setting the control object will handle further procedures...
      %client.camera.getDataBlock().setMode( %client.camera, "ObserverFly" );
      commandToClient(%client, 'setHudMode', 'Observer');
      %client.setControlObject( %client.camera );   
   }
   
   //make sure the objective HUD indicates your team on top and in green...
   if (%client.team > 0)
      messageClient(%client, 'MsgCheckTeamLines', "", %client.team);

   // were ready to go.
   %client.matchStartReady = true;
   echo("Client" SPC %client SPC "is ready.");
}

function DefaultGame::sendClientTeamList(%game, %client)
{
   // Send the client the current team list:
   %teamCount = %game.numTeams;
   for ( %i = 0; %i < %teamCount; %i++ )
   {
      if ( %i > 0 )
         %teamList = %teamList @ "\n";

      %teamList = %teamList @ detag( getTaggedString( game.getTeamName(%i + 1)) );
   }
   messageClient( %client, 'MsgTeamList', "", %teamCount, %teamList );
}

function DefaultGame::setupClientHuds(%game, %client)
{
   // tell the client to setup the huds...
   for(%i =0; %i<$WeaponsHudCount; %i++)
      %client.setWeaponsHudBitmap(%i, $WeaponsHudData[%i, itemDataName], $WeaponsHudData[%i, bitmapName]);
   for(%i =0; %i<$InventoryHudCount; %i++)
      %client.setInventoryHudBitmap($InventoryHudData[%i, slot], $InventoryHudData[%i, itemDataName], $InventoryHudData[%i, bitmapName]);
      
   %client.setWeaponsHudBackGroundBmp("gui/hud_new_panel.png");
   %client.setWeaponsHudHighLightBmp("gui/hud_new_weaponselect.png");
   %client.setWeaponsHudInfiniteAmmoBmp("gui/hud_infinity.png");
   %client.setInventoryHudBackGroundBmp("gui/hud_new_panel.png");
 
   // tell the client if we are protecting statics (so no health bar will be displayed)
   commandToClient(%client, 'protectingStaticObjects', %game.allowsProtectedStatics());
   commandToClient(%client, 'setPowerAudioProfiles', sPowerUp.getId(), sPowerDown.getId());
}

function DefaultGame::testDrop( %game, %client )
{
   %game.clientJoinTeam( %client, 1, false );
   %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player );
   %client.setControlObject( %client.camera );
   CommandToClient( %client, 'setPlayContent' );
}

function DefaultGame::onClientEnterObserverMode( %game, %client )
{
   // Default game doesn't care...
}

// from 'item.cs'
function DefaultGame::playerTouchFlag(%game, %player, %flag)
{
   messageAll('MsgPlayerTouchFlag', 'Player %1 touched flag %2', %player, %flag);
}

// from 'item.cs'
function DefaultGame::playerDroppedFlag(%game, %player, %flag)
{
   messageAll('MsgPlayerDroppedFlag', 'Player %1 dropped flag %2', %player, %flag);
}

// from 'staticShape.cs'
function DefaultGame::flagStandCollision(%game, %dataBlock, %obj, %colObj)
{
   // for retreiveGame
}

function DefaultGame::notifyMineDeployed(%game, %mine)
{
   //do nothign in the default game...
}

// from 'staticshape.cs'
function DefaultGame::findProjector(%game, %flipflop)
{
   // search the flipflop's folder for a holo projector
   // if one exists, associate it with the flipflop
   %flipflop.projector = 0;
   %folder = %flipflop.getGroup();
   for(%i = 0; %i < %folder.getCount(); %i++) 
   {
      %proj = %folder.getObject(%i);
      if(%proj.getDatablock().getName() $= "LogoProjector") 
      {
         %flipflop.projector = %proj;
         %flipflop.projector.holo = 0;
         break;
      }
   }
}

//******************************************************************************
//*   DefaultGame Trigger  -  Functions                                        *
//******************************************************************************

/// -Trigger- //////////////////////////////////////////////////////////////////
//Function -- onEnterTrigger (%game, %name, %data, %obj, %colObj)
//                %game = Current game type object
//                %name = Trigger name - defined when trigger is created
//                %data = Trigger Data Block 
//                %obj = Trigger Object 
//                %colObj = Object that collided with the trigger
//Decription -- Called when trigger has been triggered 
////////////////////////////////////////////////////////////////////////////////
// from 'trigger.cs'
function DefaultGame::onEnterTrigger(%game, %triggerName, %data, %obj, %colobj)
{
   //Do Nothing
}

/// -Trigger- //////////////////////////////////////////////////////////////////
//Function -- onLeaveTrigger (%game, %name, %data, %obj, %colObj)
//                %game = Current game type object
//                %name = Trigger name - defined when trigger is created
//                %data = Trigger Data Block 
//                %obj = Trigger Object 
//                %colObj = Object that collided with the trigger
//Decription -- Called when trigger has been untriggered
////////////////////////////////////////////////////////////////////////////////
// from 'trigger.cs'
function DefaultGame::onLeaveTrigger(%game, %triggerName, %data, %obj, %colobj)
{
   //Do Nothing
}

/// -Trigger- //////////////////////////////////////////////////////////////////
//Function -- onTickTrigger(%game, %name, %data, %obj)
//                %game = Current game type object
//                %name = Trigger name - defined when trigger is created
//                %data = Trigger Data Block 
//                %obj = Trigger Object 
//Decription -- Called every tick if triggered
////////////////////////////////////////////////////////////////////////////////
// from 'trigger.cs'
function DefaultGame::onTickTrigger(%game, %triggerName, %data, %obj)
{
   //Do Nothing
}


function DefaultGame::setUpTeams(%game)
{  
   %group = nameToID("MissionGroup/Teams");
   if(%group == -1)
      return;
   
   // create a team0 if it does not exist
   %team = nameToID("MissionGroup/Teams/team0");
   if(%team == -1)
   {
      %team = new SimGroup("team0");
      %group.add(%team);
   }
   
   // 'team0' is not counted as a team here
   %game.numTeams = 0;
   while(%team != -1)
   {
      // create drop set and add all spawnsphere objects into it
      %dropSet = new SimSet("TeamDrops" @ %game.numTeams);
      MissionCleanup.add(%dropSet);

      %spawns = nameToID("MissionGroup/Teams/team" @ %game.numTeams @ "/SpawnSpheres");
      if(%spawns != -1)
      {
         %count = %spawns.getCount();
         for(%i = 0; %i < %count; %i++)
            %dropSet.add(%spawns.getObject(%i));
      }

      // set the 'team' field for all the objects in this team
      %team.setTeam(%game.numTeams);

      clearVehicleCount(%team+1);
      // get next group
      %team = nameToID("MissionGroup/Teams/team" @ %game.numTeams + 1);
      if (%team != -1)
         %game.numTeams++;
   }

   // set the number of sensor groups (including team0) that are processed
   setSensorGroupCount(%game.numTeams + 1);
}

function SimGroup::setTeam(%this, %team)
{
   for (%i = 0; %i < %this.getCount(); %i++)
   {
      %obj = %this.getObject(%i);
      switch$ (%obj.getClassName())
      {
         case SpawnSphere     :
            if($MatchStarted)
            {
               // find out what team the spawnsphere used to belong to
               %found = false;
               for(%l = 1; %l <= Game.numTeams; %l++)
               {
                  %drops = nameToId("MissionCleanup/TeamDrops" @ %l);
                  for(%j = 0; %j < %drops.getCount(); %j++)
                  {
                     %current = %drops.getObject(%j);
                     if(%current == %obj)
                        %found = %l;
                  }
               }
               if(%team != %found)
                  Game.claimSpawn(%obj, %team, %found);
               else
                  error("spawn "@%obj@" is already on team "@%team@"!");
            }
            else
               Game.claimSpawn(%obj, %team, "");
         case SimGroup        :  %obj.setTeam(%team);
         default              :  %obj.team = %team;
      }

      if(%obj.getType() & $TypeMasks::GameBaseObjectType)
      {
         // eeck.. please go away when scripts get cleaned...
         if(%obj.getDataBlock().getName() $= "StationVehiclePad")
            %obj = %obj.station;

         %target = %obj.getTarget();
         if(%target != -1)
            setTargetSensorGroup(%target, %team);
      }
   }
} 
 
function DefaultGame::claimSpawn(%game, %obj, %newTeam, %oldTeam)
{
   if(%newTeam == %oldTeam)
      return;

   %newSpawnGroup = nameToId("MissionCleanup/TeamDrops" @ %newTeam);
   if(%oldTeam !$= "")
   {
      %oldSpawnGroup = nameToId("MissionCleanup/TeamDrops" @ %oldTeam);
      %oldSpawnGroup.remove(%obj);
   }
   %newSpawnGroup.add(%obj);   
}

// recursive function to assign teams to all mission objects        

function SimGroup::swapTeams(%this)
{
   // used in Siege only
   Game.groupSwapTeams(%this);
}

function ShapeBase::swapTeams(%this)
{
   // used in Siege only
   Game.objectSwapTeams(%this);
}

function GameBase::swapTeams(%this)
{
   // used in Siege only
   Game.objectSwapTeams(%this);
}

function TSStatic::swapTeams(%this)
{
   // used in Siege only
   // do nothing
}

function InteriorInstance::swapTeams(%this)
{
   // used in Siege only
   // do nothing -- interiors don't switch teams
}

function SimGroup::swapVehiclePads(%this)
{
   // used in Siege only
   Game.groupSwapVehiclePads(%this);
}

function ShapeBase::swapVehiclePads(%this)
{
   // used in Siege only
   Game.objectSwapVehiclePads(%this);
}

function GameBase::swapVehiclePads(%this)
{
   // used in Siege only
   // do nothing -- only searching for vehicle pads
}

function InteriorInstance::swapVehiclePads(%this)
{
   // used in Siege only
   // do nothing -- only searching for vehicle pads
}

function SimSet::swapVehiclePads(%this)
{
   // used in Siege only
   // do nothing -- only searching for vehicle pads
}

function PhysicalZone::swapVehiclePads(%this)
{
   // used in Siege only
   // do nothing -- only searching for vehicle pads
}

function SimGroup::objectRestore(%this)
{
   // used in Siege only
   Game.groupObjectRestore(%this);
}

function ShapeBase::objectRestore(%object)
{
   // only used for Siege
   Game.shapeObjectRestore(%object);
}

function Turret::objectRestore(%object)
{
   // only used for Siege
   Game.shapeObjectRestore(%object);
}

function AIObjective::objectRestore(%object)
{
   // only used for Siege
   // don't do anything for AI Objectives
}

function DefaultGame::checkObjectives(%game)  
{  
   //any special objectives that can be met by gametype   
   //none for default game
}

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

function DefaultGame::checkTimeLimit(%game, %forced)
{
   // Don't add extra checks:
   if ( %forced )
      cancel( %game.timeCheck );

   // if there is no time limit, check back in a minute to see if it's been set
   if(($Host::TimeLimit $= "") || $Host::TimeLimit == 0)
   {
      %game.timeCheck = %game.schedule(20000, "checkTimeLimit");
      return;
   }
   
   %curTimeLeftMS = ($Host::TimeLimit * 60 * 1000) + $missionStartTime - getSimTime();

   if (%curTimeLeftMS <= 0)
   {
      // time's up, put down your pencils
      %game.timeLimitReached();
   }
   else
   {
      if(%curTimeLeftMS >= 20000)
         %game.timeCheck = %game.schedule(20000, "checkTimeLimit");
      else
         %game.timeCheck = %game.schedule(%curTimeLeftMS + 1, "checkTimeLimit");

      //now synchronize everyone's clock
      messageAll('MsgSystemClock', "", $Host::TimeLimit, %curTimeLeftMS);
   }
}

function listplayers()
{
   %i = 0;
   %Count = ClientGroup.getCount();

   while (%i < %Count)
   {
      %Client = ClientGroup.getObject(%i);
      %Status = "";

      if (%Client.isAIControlled() == true)
         %Status = "Bot ";

      if (%Client.isSmurf == true)
         %Status = "Alias ";

      if (isMasterAdmin(%Client) == true)
      {
         %Status = %Status @ "Master Admin ";
      } else
      {
         if (isSuperAdmin(%Client) == true)
         {
            %Status = %Status @ "Super Admin ";
         } else
         {
            if (isAdmin(%Client) == true)
            {
               %Status = %Status @ "Admin ";
            }
         }
      }

      if(%Status $= "")
         %Status = "<normal>";

      if (IS(%Client) == true)
      {
         %Display = "Name: " @ " " @ %Client.nameBase @ "GUID: "  @ "Client ID: " @ %Client @ " Player ID: " @ %Client.player @ " team: " @ %Client.team @ " Status: ";
      } else {
         %Display = "Name: " @ " " @ %Client.nameBase @ "GUID: " @ %Client.GUID @ "Client ID: " @ %Client @ " Player ID: " @ %Client.player @ " team: " @ %Client.team @ " Status: " @ %Status;
      }

      echo(%Display);

      %i++;
   }
}

function logEcho(%msg)
{
	if($LogEchoEnabled)
		echo("LOG: " @ %msg);
}

function DefaultGame::clearTeamRankArray(%game, %team)
{
   %count = $TeamRank[%team, count];
   for (%i = 0; %i < %count; %i++)
      $TeamRank[%team, %i] = "";
   $TeamRank[%team, count] = 0;
}

function DefaultGame::populateTeamRankArray(%game, %client)
{
   //this function should be called *after* the client has been added to a team...
   if (%client <= 0 || %client.team <= 0)
      return;

   //find the team
   if (%game.numTeams == 1)
      %team = 0;
   else
      %team = %client.team;

   //find the number of teammates already ranked...
   %count = $TeamRank[%team, count];
   if (%count $= "")
   {
      $TeamRank[%team, count] = 0;
      %count = 0;
   }

   //make sure we're not already in the array
   for (%i = 0; %i < %count; %i++)
   {
      if ($TeamRank[%team, %i] == %client)
         return;
   }

   //add the client in at the bottom of the list, and increment the count
   $TeamRank[%team, %count] = %client;
   $TeamRank[%team, count] = $TeamRank[%team, count] + 1;

   //now recalculate the team rank for this player
   %game.recalcTeamRanks(%client);
}

function DefaultGame::removeFromTeamRankArray(%game, %client)
{
   //note, this should be called *before* the client actually switches teams or drops...
   if (%client <= 0 || %client.team <= 0)
      return;

   //find the correct team
   if (%game.numTeams == 1)
      %team = 0;
   else
      %team = %client.team;

   //now search throught the team rank array, looking for this client
   %count = $TeamRank[%team, count];
   for (%i = 0; %i < %count; %i++)
   {
      if ($TeamRank[%team, %i] == %client)
      {
         //we've found the client in the array, now loop through, and move everyone else up a rank
         for (%j = %i + 1; %j < %count; %j++)
         {
            %cl = $TeamRank[%team, %j];
            $TeamRank[%team, %j - 1] = %cl;
            messageClient(%cl, 'MsgYourRankIs', "", %j);
         }

         //now decriment the team rank array count, and break
         $TeamRank[%team, count] = $TeamRank[%team, count] - 1;
         break;
      }
   }
}

function DefaultGame::recalcTeamRanks(%game, %client)
{
   if (%client <= 0 || %client.team <= 0)
      return;

   // this is a little confusing -- someone's actual numerical rank is always
   // one number higher than his index in the $TeamRank array
   // (e.g. person ranked 1st has index of 0)

   // TINMAN:  I'm going to remove the %client.teamRank field - the index in the
   // $TeamRank array already contains their rank - safer to search the array than
   // to maintiain the information in a separate variable...

   //find the team, the client in the team array
   if (%game.numTeams == 1)
      %team = 0;
   else
      %team = %client.team;

   %count = $TeamRank[%team, count];
   %index = -1;
   for (%i = 0; %i < %count; %i++)
   {
      if ($TeamRank[%team, %i] == %client)
      {
         %index = %i;
         break;
      }
   }

   //if they weren't found in the array, return
   if (%index < 0)
      return;

   //make sure far down the array as they should be...
   %tempIndex = %index;
   %swapped = false;
   while (true)
   {
      if (%tempIndex <= 0)
         break;

      %tempIndex--;
      %tempClient = $TeamRank[%team, %tempIndex];

      //see if we should swap the two
      if (%client.score > %tempClient.score)
      {
         %swapped = true;
         %index = %tempIndex;
         $TeamRank[%team, %tempIndex] = %client;
         $TeamRank[%team, %tempIndex + 1] = %tempClient;
         messageClient(%tempClient, 'MsgYourRankIs', "", %tempIndex + 2);
      }
   }

   //if we've swapped up at least once, we obviously won't need to swap down as well...
   if (%swapped)
   {
      messageClient(%client, 'MsgYourRankIs', "", %index + 1);
      return;
   }

   //since we didnt' swap up, see if we need to swap down...
   %tempIndex = %index;
   %swapped = false;
   while (true)
   {
      if (%tempIndex >= %count - 1)
         break;

      %tempIndex++;
      %tempClient = $TeamRank[%team, %tempIndex];

      //see if we should swap the two
      if (%client.score < %tempClient.score)
      {
         %swapped = true;
         %index = %tempIndex;
         $TeamRank[%team, %tempIndex] = %client;
         $TeamRank[%team, %tempIndex - 1] = %tempClient;
         messageClient(%tempClient, 'MsgYourRankIs', "", %tempIndex);
      }
   }

   //send the message (regardless of whether a swap happened or not)
   messageClient(%client, 'MsgYourRankIs', "", %index + 1);
}

function DefaultGame::recalcScore(%game, %cl)
{
   %game.recalcTeamRanks(%cl);
}  

function DefaultGame::testKill(%game, %victimID, %killerID)
{
   return ((%killerID !=0) && (%victimID.team != %killerID.team));
}

function DefaultGame::testSuicide(%game, %victimID, %killerID, %damageType)
{     
   return ((%victimID == %killerID) || (%damageType == $DamageType::Ground) || (%damageType == $DamageType::Suicide));      
}
   
function DefaultGame::testTeamKill(%game, %victimID, %killerID)
{
   return (%killerID.team == %victimID.team);   
}

function DefaultGame::testTurretKill(%game, %implement)
{
   if(%implement == 0)
      return false;
   else     
      return (%implement.getClassName() $= "Turret");
}

// function DefaultGame::awardScoreFlagCap(%game, %cl)
// {
//    %cl.flagCaps++;
//    $TeamScore[%cl.team] += %game.SCORE_PER_TEAM_FLAG_CAP;
//    messageAll('MsgCTFTeamScore', "", %cl.team, $TeamScore[%cl.team]);
// 
//    if (%game.SCORE_PER_PLYR_FLAG_CAP > 1)
//      %plural = "s";
//    else
//      %plural = "";
// 
//    if (%game.SCORE_PER_PLYR_FLAG_CAP != 0)
//         messageClient(%cl, 'scoreFlaCapMsg', 'You received %1 point%2 for capturing the flag.', %game.SCORE_PER_PLYR_FLAG_CAP, %plural);
//    %game.recalcScore(%cl);
// }


function DefaultGame::testOOBDeath(%game, %damageType)
{
   return (%damageType == $DamageType::OutOfBounds);
}

function DefaultGame::awardScoreTurretKill(%game, %victimID, %implement)
{
   if ((%killer = %implement.getControllingClient()) != 0) //award whoever might be controlling the turret
   {
      if (%killer == %victimID)
           %game.awardScoreSuicide(%victimID);
      else if (%killer.team == %victimID.team) //player controlling a turret killed a teammate     
      {
           %killer.teamKills++;
        %game.awardScoreTurretTeamKill(%victimID, %killer);
        %game.awardScoreDeath(%victimID);
      }
      else
      {
         %killer.turretKills++;
         %game.recalcScore(%killer);
         %game.awardScoreDeath(%victimID);
      }     
   }   
   else if ((%killer = %implement.owner) != 0) //if it isn't controlled, award score to whoever deployed it
   {
       if (%killer.team == %victimID.team)       
       {
            %game.awardScoreDeath(%victimID);
       }
       else       
       {
          %killer.turretKills++;
         %game.recalcScore(%killer);
         %game.awardScoreDeath(%victimID);
      }
   }   
   //default is, no one was controlling it, no one owned it.  No score given.   
}

function DefaultGame::awardScoreDeath(%game, %victimID)
{
   %victimID.deaths++;
   if ( %game.SCORE_PER_DEATH != 0 )
   {      
//       %plural = (abs(%game.SCORE_PER_DEATH) != 1 ? "s" : "");
//       messageClient(%victimID, 'MsgScoreDeath', '\c0You have been penalized %1 point%2 for dying.', abs(%game.SCORE_PER_DEATH), %plural);
      %game.recalcScore(%victimID);
   }
}

function DefaultGame::awardScoreKill(%game, %killerID)
{
   %killerID.kills++;   
   %game.recalcScore(%killerID);    
} 

function DefaultGame::awardScoreSuicide(%game, %victimID)
{
   %victimID.suicides++;
//    if (%game.SCORE_PER_SUICIDE != 0)
//       messageClient(%victimID, 'MsgScoreSuicide', '\c0You have been penalized for killing yourself.');      
   %game.recalcScore(%victimID);
}
   
function DefaultGame::awardScoreTeamkill(%game, %victimID, %killerID)
{
   %killerID.teamKills++;
   if (%game.SCORE_PER_TEAMKILL != 0)   
      messageClient(%killerID, 'MsgScoreTeamkill', '\c0You have been penalized for killing teammate %1.', %victimID.name); 
   %game.recalcScore(%killerID);
}
 
function DefaultGame::awardScoreTurretTeamKill(%game, %victimID, %killerID)
{
   %killerID.teamKills++;
   if (%game.SCORE_PER_TEAMKILL != 0)   
      messageClient(%killerID, 'MsgScoreTeamkill', '\c0You have been penalized for killing your teammate %1, with a turret.', %victimID.name);  
   %game.recalcScore(%killerID);
}


function DefaultGame::objectRepaired(%game, %obj, %objName)
{     
   %item = %obj.getDataBlock().getName();
   //echo("Item repaired is a " @ %item); 
   switch$ (%item)
   {
      case generatorLarge :
         %game.genOnRepaired(%obj, %objName);
      case stationInventory :
         %game.stationOnRepaired(%obj, %objName);
      case sensorMediumPulse : 
         %game.sensorOnRepaired(%obj, %objName);
      case sensorLargePulse : 
         %game.sensorOnRepaired(%obj, %objName);
      case turretBaseLarge : 
         %game.turretOnRepaired(%obj, %objName);
      case stationVehicle : %game.vStationOnRepaired(%obj, %objName);
      default:  //unused by current gametypes.  Add more checks here if desired
   }
}

function DefaultGame::allowsProtectedStatics(%game)
{
   return false;
}

// jff: why is game object doing this?
//Return a simple string with no extras
function DefaultGame::cleanWord(%game, %this)
{
   %length = strlen(%this);
   for(%i = 0; %i < %length; %i++)
   {
      %char = getSubStr(%this, %i, 1);
      if(%char $= "_")
      {
         %next =  getSubStr(%this, (%i+1), 1);
         if(%next $= "_") 
         {
            %char = "'";   //apostrophe (2 chars)
            %i++;
         }
         else
            %char = " ";   //space
      }
      %clean = (%clean @ %char);
   }
}

function DefaultGame::stationOnEnterTrigger(%game, %data, %obj, %colObj)
{
   return true;
}

function DefaultGame::WeaponOnUse(%game, %data, %obj)
{
   return true;
}

function DefaultGame::HandInvOnUse(%game, %data, %obj)
{
   return true;
}

function DefaultGame::WeaponOnInventory(%game, %this, %obj, %amount)
{
   return true;
}

function DefaultGame::ObserverOnTrigger(%game, %data, %obj, %trigger, %state)
{
   return true;
}

// jff: why is the game being notified that a weapon is being thrown? hot potato gametype?
function DefaultGame::ShapeThrowWeapon(%game, %this)
{
   return true;
}

function DefaultGame::leaveMissionArea(%game, %playerData, %player)
{
   %player.client.outOfBounds = true;
   messageClient(%player.client, 'LeaveMissionArea', '\c1You left the mission area.~wfx/misc/warning_beep.wav');
}

function DefaultGame::enterMissionArea(%game, %playerData, %player)
{
   %player.client.outOfBounds = false; 
   messageClient(%player.client, 'EnterMissionArea', '\c1You are back in the mission area.');
}

//------------------------------------------------------------------------------
// AI stubs:
//------------------------------------------------------------------------------

function DefaultGame::onAIDamaged(%game, %clVictim, %clAttacker, %damageType, %sourceObject)
{
}

function DefaultGame::onAIFriendlyFire(%game, %clVictim, %clAttacker, %damageType, %sourceObject)
{
}

function DefaultGame::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement)
{
   //unassign the client from any objectives
   AIUnassignClient(%clVictim);

   //break the link, if this ai is controlled
   aiReleaseHumanControl(%clVictim.controlByHuman, %clVictim);

   //and schedule the respawn
   %clVictim.respawnThread = schedule(5000, %clVictim, "onAIRespawn", %clVictim);
}

function DefaultGame::onAIKilledClient(%game, %clVictim, %clAttacker, %damageType, %implement)
{
   %clAttacker.setVictim(%clVictim, %clVictim.player);
}

//------------------------------------------------------------------------------
// Voting stuff:
//------------------------------------------------------------------------------
function DefaultGame::sendGamePlayerPopupMenu( %game, %client, %targetClient, %key )
{
   if( !%targetClient.matchStartReady )
      return;

   %isAdmin = ( %client.isAdmin || %client.isSuperAdmin );

   %isTargetSelf = ( %client == %targetClient );
   %isTargetAdmin = ( %targetClient.isAdmin || %targetClient.isSuperAdmin );
   %isTargetBot = %targetClient.isAIControlled();
   %isTargetObserver = ( %targetClient.team == 0 );
   %outrankTarget = false;
   if ( %client.isSuperAdmin )
      %outrankTarget = !%targetClient.isSuperAdmin;
   else if ( %client.isAdmin )
      %outrankTarget = !%targetClient.isAdmin;
   
   if( %client.isSuperAdmin && %targetClient.guid != 0 && !isDemo() && !isDemoServer())
   {   
      messageClient( %client, 'MsgPlayerPopupItem', "", %key, "addAdmin", "", 'Add to Server Admin List', 10);
      messageClient( %client, 'MsgPlayerPopupItem', "", %key, "addSuperAdmin", "", 'Add to Server SuperAdmin List', 11);
   }
   
   //mute options
   if ( !%isTargetSelf )
   {
      if ( %client.muted[%targetClient] )
         messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MutePlayer", "", 'Unmute Text Chat', 1);
      else
         messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MutePlayer", "", 'Mute Text Chat', 1);

      if ( !%isTargetBot && %client.canListenTo( %targetClient ) )
      {
         if ( %client.getListenState( %targetClient ) )
            messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ListenPlayer", "", 'Disable Voice Com', 9 );
         else
            messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ListenPlayer", "", 'Enable Voice Com', 9 );
      }
   }

   if( !%client.canVote && !%isAdmin )
      return;
   
   // regular vote options on players
   if ( %game.scheduleVote $= "" && !%isAdmin && !%isTargetAdmin )
   {
      if ( $Host::allowAdminPlayerVotes && !%isTargetBot && !isDemo() && !isDemoServer())
         messageClient( %client, 'MsgPlayerPopupItem', "", %key, "AdminPlayer", "", 'Vote to Make Admin', 2 );
      
      if ( !%isTargetSelf )
      {   
         messageClient( %client, 'MsgPlayerPopupItem', "", %key, "KickPlayer", "", 'Vote to Kick', 3 );
      }
   }


   // Admin only options on players:
   else if ( %isAdmin && !isDemo() && !isDemoServer())
   {
      if ( !%isTargetBot && !%isTargetAdmin )
         messageClient( %client, 'MsgPlayerPopupItem', "", %key, "AdminPlayer", "", 'Make Admin', 2 );
      
      if ( !%isTargetSelf && %outrankTarget )
      {
         messageClient( %client, 'MsgPlayerPopupItem', "", %key, "KickPlayer", "", 'Kick', 3 );

         if ( !%isTargetBot )
         {
            if( %client.isSuperAdmin )
               messageClient( %client, 'MsgPlayerPopupItem', "", %key, "BanPlayer", "", 'Ban', 4 );

            if ( !%isTargetObserver )
               messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ToObserver", "", 'Force observer', 5 );
         }
      }


      if ( %isTargetSelf || %outrankTarget )
      {
         if ( %game.numTeams > 1 )
         {   
            if ( %isTargetObserver )
            {
               %action = %isTargetSelf ? "Join " : "Change to ";
               %str1 = %action @ getTaggedString(%game.getTeamName(1));      
               %str2 = %action @ getTaggedString(%game.getTeamName(2));      
            
               messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ChangeTeam", "", %str1, 6 );
               messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ChangeTeam", "", %str2, 7 );
            }
            else
            {
               %changeTo = %targetClient.team == 1 ? 2 : 1;   
               %str = "Switch to " @ getTaggedString( %game.getTeamName(%changeTo) );
               %caseId = 5 + %changeTo;      
            
               messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ChangeTeam", "", %str, %caseId );
            }
         }
         else if ( %isTargetObserver )
         {
            %str = %isTargetSelf ? 'Join the Game' : 'Add to Game';
            messageClient( %client, 'MsgPlayerPopupItem', "", %key, "JoinGame", "", %str, 8 );
         }
      }
   }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function DefaultGame::sendGameVoteMenu( %game, %client, %key )
{
   %isAdmin = isAdmin(%Client);
   %isSuperAdmin = isSuperAdmin(%Client);
   %isMasterAdmin = isMasterAdmin(%Client);

   if (%game.numTeams > 1)
      %multipleTeams = true;
   else
      %multipleTeams = false;

   // no one is going anywhere until this thing starts
   if($MatchStarted == true)
   {
      // Client options:
      if (%client.team != 0)
      {
         if (%client.TeamLocked != true)
            messageClient( %client, 'MsgVoteItem', "", %key, 'MakeObserver', "", 'Become an Observer' );
      }
      else 
      {   
         if (%multipleTeams == true && $Host::TournamentMode == false)
            messageClient( %client, 'MsgVoteItem', "", %key, 'JoinGame', "", 'Join the Game' );
      }
      
   }

   if (%client.canVote == false)
      return;
   
   if (%game.scheduleVote $= "")
   {
      if (%isAdmin == false)
      {
         // Actual vote options:
         if ($Havoc::allowMissionChangeVotes == true)
            messageClient(%client, 'MsgVoteItem', "", %key, 'VoteChangeMission', 'change the mission to', 'Vote to Change the Mission');
      
         if ($Host::TournamentMode == true)
         {   
            messageClient(%client, 'MsgVoteItem', "", %key, 'VoteFFAMode', 'Change server to Free For All.', 'Vote Free For All Mode');
            
            if (!$MatchStarted && !$CountdownStarted)
               messageClient(%client, 'MsgVoteItem', "", %key, 'VoteMatchStart', 'Start Match', 'Vote to Start the Match');
         } else
            if ($Havoc::allowTournamentVotes == true)
               messageClient(%client, 'MsgVoteItem', "", %key, 'VoteTournamentMode', 'Change server to Tournament.', 'Vote Tournament Mode');
         
         if (%multipleTeams == true)
         {
            if ($Host::TournamentMode == false && %Client.TeamLocked != true)
               messageClient(%client, 'MsgVoteItem', "", %key, 'ChooseTeam', "", 'Change your Team');

            if ($Havoc::allowTeamDamageVotes == true)
            {
               if ($teamDamage == true)
                  messageClient(%client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'disable team damage', 'Vote to Disable Team Damage');
               else
                  messageClient(%client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'enable team damage', 'Vote to Enable Team Damage');
            }
         }
         if ($Havoc::allowTimeIncreaseVotes == true && %game.canVoteMoreTime() == true)
            messageClient( %client, 'MsgVoteItem', "", %key, 'voteIncreaseTime', 'increase time', 'Vote to Increase Time' );

      } else {

         // Actual Admin options:
         if ($Host::TournamentMode == true)
         {   
            messageClient(%client, 'MsgVoteItem', "", %key, 'VoteFFAMode', 'Change server to Free For All.', 'Free For All Mode');
            
            if ($MatchStarted == false && $CountdownStarted == false)
               messageClient(%client, 'MsgVoteItem', "", %key, 'VoteMatchStart', 'Start Match', 'Start Match');
         } else
            messageClient(%client, 'MsgVoteItem', "", %key, 'VoteTournamentMode', 'Change server to Tournament.', 'Tournament Mode');
      
         if (%multipleTeams == true)
         {
            messageClient(%client, 'MsgVoteItem', "", %key, 'ChooseTeam', "", 'Change your Team');

            if ($teamDamage == true)
               messageClient(%client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'disable team damage', 'Disable Team Damage');
            else
               messageClient(%client, 'MsgVoteItem', "", %key, 'VoteTeamDamage', 'enable team damage', 'Enable Team Damage');

         }

         if (%game.canVoteMoreTime() == true)
            messageClient( %client, 'MsgVoteItem', "", %key, 'voteIncreaseTime', 'increase time', 'Increase Mission Time' );

         messageClient(%client, 'MsgVoteItem', "", %key, 'VoteChangeMission', 'change the mission to', 'Change the Mission');
         messageClient( %client, 'MsgVoteItem', "", %key, 'VoteChangeTimeLimit', 'change the time limit', 'Change the Time Limit' );

         if (%isSuperAdmin == true)
            messageClient( %client, 'MsgVoteItem', "", %key, 'VoteResetServer', 'reset server defaults', 'Reset the Server' );

      }
   }
}

//------------------------------------------------------------------------------
function DefaultGame::sendGameTeamList( %game, %client, %key )
{
   %teamCount = %game.numTeams;
   if ( %teamCount < 2 )
   {
      warn( "Team menu requested for one-team game!" );
      return;
   }

   for ( %team = 1; %team - 1 < %teamCount; %team++ )
      messageClient( %client, 'MsgVoteItem', "", %key, %team, "", detag( getTaggedString( game.getTeamName(%team) ) ) );
}

//------------------------------------------------------------------------------
function DefaultGame::sendTimeLimitList( %game, %client, %key )
{
   messageClient( %client, 'MsgVoteItem', "", %key, 10, "", '10 minutes' );
   messageClient( %client, 'MsgVoteItem', "", %key, 15, "", '15 minutes' );
   messageClient( %client, 'MsgVoteItem', "", %key, 20, "", '20 minutes' );
   messageClient( %client, 'MsgVoteItem', "", %key, 25, "", '25 minutes' );
   messageClient( %client, 'MsgVoteItem', "", %key, 30, "", '30 minutes' );
   messageClient( %client, 'MsgVoteItem', "", %key, 45, "", '45 minutes' );
   messageClient( %client, 'MsgVoteItem', "", %key, 60, "", '60 minutes' );
   messageClient( %client, 'MsgVoteItem', "", %key, 999, "", 'No time limit' );
}

//------------------------------------------------------------------------------
// all global votes here
function DefaultGame::voteChangeMission(%game, %admin, %missionDisplayName, %typeDisplayName, %missionId, %missionTypeId)
{
   %mission = $HostMissionFile[%missionId];
   if ( %mission $= "" )
   {
      error( "Invalid mission index passed to DefaultGame::voteChangeMission!" );
      return;
   }

   %missionType = $HostTypeName[%missionTypeId];
   if ( %missionType $= "" )
   {
      error( "Invalid mission type id passed to DefaultGame::voteChangeMission!" );
      return;
   }

   if(%admin) 
   {
      messageAll('MsgAdminChangeMission', '\c2The Admin has changed the mission to %1 (%2).', %missionDisplayName, %typeDisplayName );   
      logEcho("mission changed to "@%missionDisplayName@"/"@%typeDisplayName@" (admin)");
      %game.gameOver();
      loadMission( %mission, %missionType, false );   
   }
   else 
   {
      %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
      if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100))
      {
         messageAll('MsgVotePassed', '\c2The mission was changed to %1 (%2) by vote.', %missionDisplayName, %typeDisplayName ); 
         logEcho("mission changed to "@%missionDisplayName@"/"@%typeDisplayName@" (vote)");
         %game.gameOver();
         loadMission( %mission, %missionType, false );   
      }
      else
         messageAll('MsgVoteFailed', '\c2Change mission vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); 
   }
}

//------------------------------------------------------------------------------
function DefaultGame::voteTeamDamage(%game, %admin)
{
   %setto = "";
   %cause = "";
   if(%admin) 
   {
      if($teamDamage)
      {
         messageAll('MsgAdminForce', '\c2The Admin has disabled team damage.');   
         $Host::TeamDamageOn = $TeamDamage = 0;
         %setto = "disabled";
      }
      else 
      {
         messageAll('MsgAdminForce', '\c2The Admin has enabled team damage.');   
         $Host::TeamDamageOn = $TeamDamage = 1;
         %setto = "enabled";
      }
      %cause = "(admin)";
   }
   else 
   {
      %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
      if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100))
      {
         if($teamDamage) 
         {
            messageAll('MsgVotePassed', '\c2Team damage was disabled by vote.'); 
            $Host::TeamDamageOn = $TeamDamage = 0;
            %setto = "disabled";
         }
         else 
         {
            messageAll('MsgVotePassed', '\c2Team damage was enabled by vote.');  
            $Host::TeamDamageOn = $TeamDamage = 1;
            %setto = "enabled";
         }
         %cause = "(vote)";
      }
      else 
      {
         if($teamDamage)
            messageAll('MsgVoteFailed', '\c2Disable team damage vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100));  
         else 
            messageAll('MsgVoteFailed', '\c2Enable team damage vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100));   
      }
   }
   if(%setto !$= "")
      logEcho("team damage "@%setto SPC %cause);
}

//------------------------------------------------------------------------------
function DefaultGame::voteTournamentMode( %game, %admin, %missionDisplayName, %typeDisplayName, %missionId, %missionTypeId )
{
   %mission = $HostMissionFile[%missionId];
   if ( %mission $= "" )
   {
      error( "Invalid mission index passed to DefaultGame::voteTournamentMode!" );
      return;
   }

   %missionType = $HostTypeName[%missionTypeId];
   if ( %missionType $= "" )
   {
      error( "Invalid mission type id passed to DefaultGame::voteTournamentMode!" );
      return;
   }

   %cause = "";
   if (%admin) 
   {
      messageAll( 'MsgAdminForce', '\c2The Admin has switched the server to Tournament mode (%1).', %missionDisplayName );
      setModeTournament( %mission, %missionType );
      %cause = "(admin)";
   }
   else 
   {
      %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
      if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) 
      {
         messageAll('MsgVotePassed', '\c2Server switched to Tournament mode by vote (%1): %2 percent.', %missionDisplayName, mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); 
         setModeTournament( %mission, %missionType );
         %cause = "(vote)";
      }
      else
         messageAll('MsgVoteFailed', '\c2Tournament mode vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100));
   }
   if(%cause !$= "")
      logEcho("tournament mode set "@%cause);
}

//------------------------------------------------------------------------------
function DefaultGame::voteMatchStart( %game, %admin)
{
   %cause = "";
   %ready = forceTourneyMatchStart();
   if(%admin)
   {
      if(!%ready)
      {   
         messageClient( %client, 'msgClient', '\c2No players are ready yet.');
         return;
      }
      else
      {
         messageAll('msgMissionStart', '\c2The admin has forced the match to start.');
         %cause = "(admin)";
         startTourneyCountdown();
      }
   }
   else
   {
      if(!%ready)
      {
         messageAll( 'msgClient', '\c2Vote passed to start match, but no players are ready yet.');
         return;
      }
      else
      {  
         %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
         if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) 
         {
            messageAll('MsgVotePassed', '\c2The match has been started by vote: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100));  
            startTourneyCountdown();
         } 
         else
            messageAll('MsgVoteFailed', '\c2Start Match vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); 
      }
   }
   
   if(%cause !$= "")
      logEcho("start match "@%cause);
}

//------------------------------------------------------------------------------
function DefaultGame::voteFFAMode( %game, %admin, %client )
{
   %cause = "";
   %name = getTaggedString(%client.name);
   
   if (%admin) 
   {
      messageAll('MsgAdminForce', '\c2The Admin has switched the server to Free For All mode.', %client);   
      setModeFFA($CurrentMission, $CurrentMissionType); 
      %cause = "(admin)";
   }
   else 
   {
      %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
      if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) 
      {
         messageAll('MsgVotePassed', '\c2Server switched to Free For All mode by vote.', %client); 
         setModeFFA($CurrentMission, $CurrentMissionType); 
         %cause = "(vote)";
      }
      else 
         messageAll('MsgVoteFailed', '\c2Free For All mode vote did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); 
   }
   if(%cause !$= "")
      logEcho("free for all set "@%cause);
}

//------------------------------------------------------------------------------
function DefaultGame::voteIncreaseTime(%Game, %Admin)
{
   if (%Game.canVoteMoreTime() == false)
      return;

   %MoreTime = $Host::TimeIncreaseFactor;
   %Cause = "";
   if (%Admin !$= "")
   {
      %Name = getTaggedString(%Admin.name);
      if (%Name $= "")
         %Message = "The Admin increased the time for this game by " @ %MoreTime @ " minutes.";
      else
         %Message = %Name @ " increased the time for this game by " @ %MoreTime @ " minutes.";

      messageAll('MsgAdminForce', '\c2%1', %Message);

      $Host::TimeLimit += %MoreTime;
      %Cause = "(admin)";
   } else {
      // If no one is on it any longer, just forget it.
      %PlayerCount = ClientGroup.getCount() - $HostGameBotCount;
      if(%PlayerCount <= 0)
         return;
         
      %TotalVotes = %Game.totalVotesFor + %Game.totalVotesAgainst;
      %ForPercentage = %Game.totalVotesFor / %PlayerCount;
      %Required = $Host::VotePasspercent / 100;

      // If the game was forced to pass
      if (%Game.ForcePass == true || %ForPercentage >= %Required)
      {
         %Message = "Time for this game was increased by " @ %MoreTime @ " by vote.";
         messageAll('MsgVotePassed', '\c2%1', %Message);

         $Host::TimeLimit += %MoreTime;
         %Cause = "(vote)";

         %Game.ForcePass = "";
      } else {
         %Percentage = mFloor(%Game.totalVotesFor / %PlayerCount);
         %Message = "The vote to increase the time for this game by " @ %MoreTime @ " minutes did not pass: " @ %Percentage @ " percent.";

         messageAll('MsgVoteFailed', '\c2%1', %Message);
         return;
      }
   }

   logEcho("Time increased by " @ %MoreTime @ " minutes. " @ %cause);

   //if the match has been started, reset the end of match countdown
   if ($matchStarted == true)
   {
      //schedule the end of match countdown
      %elapsedTimeMS = getSimTime() - $missionStartTime;
      %TimeLeftMS = ($Host::TimeLimit * 60000) - %elapsedTimeMS;

      CancelEndCountdown();
      EndCountdown(%TimeLeftMS);
      %Game.checkTimeLimit(true);
   }
}

//------------------------------------------------------------------------------
function DefaultGame::voteChangeTimeLimit( %game, %admin, %newLimit )
{
   %cause = "";
   if ( %admin )
   {
      messageAll( 'MsgAdminForce', '\c2The Admin changed the mission time limit to %1 minutes.', %newLimit );
      $Host::DefaultTimeLimit = %newLimit;
      $Host::TimeLimit = %newLimit;
      %cause = "(admin)";
   }
   else
   {
      %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
      if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) 
      {
         messageAll('MsgVotePassed', '\c2The mission time limit was set to %1 minutes by vote.', %newLimit);   
         $Host::DefaultTimeLimit = %newLimit;
         $Host::TimeLimit = %newLimit;
         %cause = "(vote)";
      }
      else 
         messageAll('MsgVoteFailed', '\c2The vote to change the mission time limit did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100));   
   }

   //if the time limit was actually changed...
   if(%cause !$= "")
   {
      logEcho("time limit set to "@%newLimit SPC %cause);

      //if the match has been started, reset the end of match countdown
      if ($matchStarted)
      {
         //schedule the end of match countdown
         %elapsedTimeMS = getSimTime() - $missionStartTime;
         %curTimeLeftMS = ($Host::TimeLimit * 60 * 1000) - %elapsedTimeMS;
			//error("time limit="@$Host::TimeLimit@", elapsed="@(%elapsedTimeMS / 60000)@", curtimeleftms="@%curTimeLeftMS);
         CancelEndCountdown();
         EndCountdown(%curTimeLeftMS);
         %game.checkTimeLimit(true);
      }
   }
}

//------------------------------------------------------------------------------
function DefaultGame::voteResetServer( %game, %admin, %client )
{
   %cause = "";
   if ( %admin )
   {
      messageAll( 'AdminResetServer', '\c2The Admin has reset the server.' );
      resetServerDefaults();
      %cause = "(admin)";
   }
   else
   {
      %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
      if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) 
      {
         messageAll('MsgVotePassed', '\c2The Server has been reset by vote.' );  
         resetServerDefaults();
         %cause = "(vote)";
      }
      else 
         messageAll('MsgVoteFailed', '\c2The vote to reset Server to defaults did not pass: %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100));  
   }
   if(%cause !$= "")
      logEcho("server reset "@%cause);
}

//------------------------------------------------------------------------------
// all team based votes here
function DefaultGame::voteKickPlayer(%game, %admin, %client)
{
   %cause = "";
   %name = %client.nameBase;

   if(%admin) 
   {
      kick(%client, %admin, %client.guid );
      %cause = "(admin)";
   }
   else 
   {
      //%team = %client.team;
      %totalVotes = %game.votesFor[%game.kickTeam] + %game.votesAgainst[%game.kickTeam];
      if(%totalVotes > 0 && (%game.votesFor[%game.kickTeam] / %totalVotes) > ($Host::VotePasspercent / 100)) 
      {
         kick(%client, %admin, Game.kickGuid);
         %cause = "(vote)";
      }
      else
         messageAll('MsgVoteFailed', '\c2Kick player vote did not pass');
   }
   
   %game.kickTeam = "";
   %game.kickGuid = "";

   if(%cause !$= "")
      logEcho(%name@" (cl "@%client@") kicked "@%cause);
}

//------------------------------------------------------------------------------
function DefaultGame::banPlayer(%game, %admin, %client)
{
   %cause = "";
   %name = %client.nameBase;
   if( %admin ) 
   {
      ban( %client, %admin );
      %cause = "(admin)";
   }
   
   if(%cause !$= "")
      logEcho(%name@" (cl "@%client@") banned "@%cause);
}

//------------------------------------------------------------------------------
function DefaultGame::voteAdminPlayer(%game, %admin, %client)
{
   %cause = "";
   
   if (%admin) 
   {
      messageAll('MsgAdminAdminPlayer', '\c2The Admin made %2 an admin.', %client, %client.name);  
      %client.isAdmin = 1;
      %cause = "(admin)";
   }
   else 
   {
      %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst;
      if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) 
      {
         messageAll('MsgAdminPlayer', '\c2%2 was made an admin by vote.', %client, %client.name);  
         %client.isAdmin = 1;
         %cause = "(vote)";
      }
      else
         messageAll('MsgVoteFailed', '\c2Vote to make %1 an admin did not pass.', %client.name);
   }
   if(%cause !$= "")
      logEcho(%client.nameBase@" (cl "@%client@") made admin "@%cause);
}

//------------------------------------------------------------------------------
function DefaultGame::processGameLink(%game, %client, %arg1, %arg2, %arg3, %arg4, %arg5)
{
   //the default behavior when clicking on a game link is to start observing that client
   %targetClient = %arg1;
   if ((%client.team == 0) && isObject(%targetClient) && (%targetClient.team != 0))
   {
      %prevObsClient = %client.observeClient;
      serverCmdObserveClient(%client, %targetClient);
      displayObserverHud(%client, %targetClient);
      if (%targetClient != %prevObsClient)
      {
         messageClient(%targetClient, 'Observer', '\c1%1 is now observing you.', %client.name);  
         messageClient(%prevObsClient, 'ObserverEnd', '\c1%1 is no longer observing you.', %client.name);  
      }
   }
}

//------------------------------------------------------------------------------
$ScoreHudMaxVisible = 19;
function DefaultGame::updateScoreHud(%game, %client, %tag)
{
   if (Game.numTeams > 1)
   {
      // Send header:
      messageClient( %client, 'SetScoreHudHeader', "", '<tab:15,315>\t%1<rmargin:260><just:right>%2<rmargin:560><just:left>\t%3<just:right>%4', 
            game.getTeamName(1), $TeamScore[1], game.getTeamName(2), $TeamScore[2] );

      // Send subheader:
      messageClient( %client, 'SetScoreHudSubheader', "", '<tab:15,315>\tPLAYERS (%1)<rmargin:260><just:right>SCORE<rmargin:560><just:left>\tPLAYERS (%2)<just:right>SCORE',
            $TeamRank[1, count], $TeamRank[2, count] );

      %index = 0;
      while ( true )
      {
         if ( %index >= $TeamRank[1, count] && %index >= $TeamRank[2, count] )
            break;

         //get the team1 client info
         %team1Client = "";
         %team1ClientScore = "";
         if ( %index < $TeamRank[1, count] )
         {
            %team1Client = $TeamRank[1, %index];
            %team1ClientScore = %team1Client.score $= "" ? 0 : %team1Client.score;
            %col1Style = %team1Client == %client ? "<color:dcdcdc>" : "";
         }

         //get the team2 client info
         %team2Client = "";
         %team2ClientScore = "";
         if ( %index < $TeamRank[2, count] )
         {
            %team2Client = $TeamRank[2, %index];
            %team2ClientScore = %team2Client.score $= "" ? 0 : %team2Client.score;
            %col2Style = %team2Client == %client ? "<color:dcdcdc>" : "";
         }

         //if the client is not an observer, send the message
         if (%client.team != 0)
         {
            messageClient( %client, 'SetLineHud', "", %tag, %index, '<tab:20,320>\t<spush>%5<clip:200>%1</clip><rmargin:260><just:right>%2<spop><rmargin:560><just:left>\t%6<clip:200>%3</clip><just:right>%4', 
                  %team1Client.name, %team1ClientScore, %team2Client.name, %team2ClientScore, %col1Style, %col2Style );
         }
         //else for observers, create an anchor around the player name so they can be observed
         else
         {
            messageClient( %client, 'SetLineHud', "", %tag, %index, '<tab:20,320>\t<spush>%5<clip:200><a:gamelink\t%7>%1</a></clip><rmargin:260><just:right>%2<spop><rmargin:560><just:left>\t%6<clip:200><a:gamelink\t%8>%3</a></clip><just:right>%4', 
                  %team1Client.name, %team1ClientScore, %team2Client.name, %team2ClientScore, %col1Style, %col2Style, %team1Client, %team2Client );
         }

         %index++;
      }
   }
   else
   {
      //tricky stuff here...  use two columns if we have more than 15 clients...
      %numClients = $TeamRank[0, count];
      if ( %numClients > $ScoreHudMaxVisible )
         %numColumns = 2;

      // Clear header:
      messageClient( %client, 'SetScoreHudHeader', "", "" );

      // Send header:
      if (%numColumns == 2)
         messageClient(%client, 'SetScoreHudSubheader', "", '<tab:15,315>\tPLAYER<rmargin:270><just:right>SCORE<rmargin:570><just:left>\tPLAYER<just:right>SCORE');
      else
         messageClient(%client, 'SetScoreHudSubheader', "", '<tab:15>\tPLAYER<rmargin:270><just:right>SCORE');

      %countMax = %numClients;
      if ( %countMax > ( 2 * $ScoreHudMaxVisible ) )
      {
         if ( %countMax & 1 )
            %countMax++;
         %countMax = %countMax / 2;
      }
      else if ( %countMax > $ScoreHudMaxVisible )
         %countMax = $ScoreHudMaxVisible;

      for ( %index = 0; %index < %countMax; %index++ )
      {
         //get the client info
         %col1Client = $TeamRank[0, %index];
         %col1ClientScore = %col1Client.score $= "" ? 0 : %col1Client.score;
         %col1Style = %col1Client == %client ? "<color:dcdcdc>" : "";

         //see if we have two columns
         if ( %numColumns == 2 )
         {
            %col2Client = "";
            %col2ClientScore = "";
            %col2Style = "";

            //get the column 2 client info
            %col2Index = %index + %countMax;
            if ( %col2Index < %numClients )
            {
               %col2Client = $TeamRank[0, %col2Index];
               %col2ClientScore = %col2Client.score $= "" ? 0 : %col2Client.score;
               %col2Style = %col2Client == %client ? "<color:dcdcdc>" : "";
            }
         }

         //if the client is not an observer, send the message
         if (%client.team != 0)
         {
            if ( %numColumns == 2 )
               messageClient(%client, 'SetLineHud', "", %tag, %index, '<tab:25,325>\t<spush>%5<clip:195>%1</clip><rmargin:260><just:right>%2<spop><rmargin:560><just:left>\t%6<clip:195>%3</clip><just:right>%4', 
                     %col1Client.name, %col1ClientScore, %col2Client.name, %col2ClientScore, %col1Style, %col2Style );
            else
               messageClient( %client, 'SetLineHud', "", %tag, %index, '<tab:25>\t%3<clip:195>%1</clip><rmargin:260><just:right>%2', 
                     %col1Client.name, %col1ClientScore, %col1Style );
         }
         //else for observers, create an anchor around the player name so they can be observed
         else
         {
            if ( %numColumns == 2 )
               messageClient(%client, 'SetLineHud', "", %tag, %index, '<tab:25,325>\t<spush>%5<clip:195><a:gamelink\t%7>%1</a></clip><rmargin:260><just:right>%2<spop><rmargin:560><just:left>\t%6<clip:195><a:gamelink\t%8>%3</a></clip><just:right>%4', 
                     %col1Client.name, %col1ClientScore, %col2Client.name, %col2ClientScore, %col1Style, %col2Style, %col1Client, %col2Client );
            else
               messageClient( %client, 'SetLineHud', "", %tag, %index, '<tab:25>\t%3<clip:195><a:gamelink\t%4>%1</a></clip><rmargin:260><just:right>%2', 
                     %col1Client.name, %col1ClientScore, %col1Style, %col1Client );
         }
      }

   }

   // Tack on the list of observers:
   %observerCount = 0;
   for (%i = 0; %i < ClientGroup.getCount(); %i++)
   {
      %cl = ClientGroup.getObject(%i);
      if (%cl.team == 0)
         %observerCount++;
   }

   if (%observerCount > 0)
   {
	   messageClient( %client, 'SetLineHud', "", %tag, %index, "");
      %index++;
		messageClient(%client, 'SetLineHud', "", %tag, %index, '<tab:10, 310><spush><font:Univers Condensed:22>\tOBSERVERS (%1)<rmargin:260><just:right>TIME<spop>', %observerCount);
      %index++;
      for (%i = 0; %i < ClientGroup.getCount(); %i++)
      {
         %cl = ClientGroup.getObject(%i);
         //if this is an observer
         if (%cl.team == 0)
         {
            %obsTime = getSimTime() - %cl.observerStartTime;
            %obsTimeStr = %game.formatTime(%obsTime, false);
		      messageClient( %client, 'SetLineHud', "", %tag, %index, '<tab:20, 310>\t<clip:150>%1</clip><rmargin:260><just:right>%2',
		                     %cl.name, %obsTimeStr );
            %index++;
         }
      }
   }

   //clear the rest of Hud so we don't get old lines hanging around...
   messageClient( %client, 'ClearHud', "", %tag, %index );
}

//------------------------------------------------------------------------------
function UpdateClientTimes(%time)
{
    %secondsLeft = %time / 1000;
    messageAll('MsgSystemClock', "", (%secondsLeft / 60), %time);
}

//------------------------------------------------------------------------------
function notifyMatchStart(%time)
{
   %seconds = mFloor(%time / 1000);
   if (%seconds > 2)
      MessageAll('MsgMissionStart', '\c2Match starts in %1 seconds.~wfx/misc/hunters_%1.wav', %seconds);
   else if (%seconds == 2)
      MessageAll('MsgMissionStart', '\c2Match starts in 2 seconds.~wvoice/announcer/ann.match_begins.wav');
   else if (%seconds == 1)
      MessageAll('MsgMissionStart', '\c2Match starts in 1 second.');
   UpdateClientTimes(%time);
}

//------------------------------------------------------------------------------
function notifyMatchEnd(%time)
{
   %seconds = mFloor(%time / 1000);
   if (%seconds > 1)
      MessageAll('MsgMissionEnd', '\c2Match ends in %1 seconds.~wfx/misc/hunters_%1.wav', %seconds);
   else if (%seconds == 1)
      MessageAll('MsgMissionEnd', '\c2Match ends in 1 second.~wfx/misc/hunters_1.wav');
   UpdateClientTimes(%time);
}

function DefaultGame::formatTime(%game, %tStr, %includeHundredths)
{
   %timeInSeconds = %tStr / 1000;
   %mins = mFloor(%timeInSeconds / 60);
   if(%mins < 1)
      %timeString = "00:";
   else if(%mins < 10)
      %timeString = "0" @ %mins @ ":";
   else
      %timeString = %mins @ ":";

   %timeInSeconds -= (%mins * 60);
   %secs = mFloor(%timeInSeconds);
   if(%secs < 1)
      %timeString = %timeString @ "00";
   else if(%secs < 10)
      %timeString = %timeString @ "0" @ %secs;
   else
      %timeString = %timeString @ %secs;

   if (%includeHundredths)
   {
      %timeString = %timeString @ ".";
      %timeInSeconds -= %secs;
      %hSecs = mFloor(%timeInSeconds * 100); // will be between 0 and 999
      if(%hSecs < 1)
         %timeString = %timeString @ "00";
      else if(%hSecs < 10)
         %timeString = %timeString @ "0" @ %hSecs;
      else
         %timeString = %timeString @ %hSecs;
   }

   return %timeString;
}

//------------------------------------------------------------------------------
//AI FUNCTIONS
function DefaultGame::AIChooseGameObjective(%game, %client)
{
   AIChooseObjective(%client);
}

//------------------------------------------------------------------------------
function DefaultGame::getServerStatusString(%game)
{
   %status = %game.numTeams;
   for ( %team = 1; %team - 1 < %game.numTeams; %team++ )
   {
      %score = isObject( $teamScore[%team] ) ? $teamScore[%team] : 0;
      %teamStr = getTaggedString( game.getTeamName(%team) ) TAB %score;
      %status = %status NL %teamStr;
   }

   %status = %status NL ClientGroup.getCount();
   for ( %i = 0; %i < ClientGroup.getCount(); %i++ )
   {
      %cl = ClientGroup.getObject( %i );
      %score = %cl.score $= "" ? 0 : %cl.score;
      %playerStr = getTaggedString( %cl.name ) TAB getTaggedString( game.getTeamName(%cl.team) ) TAB %score;
      %status = %status NL %playerStr;
   }
   return( %status );
}

//------------------------------------------------------------------------------
function DefaultGame::OptionsDlgSleep( %game )
{
   // ignore in the default game...
}

function DefaultGame::canVoteMoreTime(%Game)
{
   return(true);
}