//------------------------------------------------------------
// Copyright Sandlot Games, 2007
// author: Michael Felice
// file: svr_building.cs
// brief:
//    This file contains building state functionality on the
//    server side.  Anything that modifies the building's
//    state, position, or health, or anything else that needs
//    to be propagated from the server should be updated here.
//------------------------------------------------------------


// this is the size of crop plant sprouts
$SproutHeight = 0.1;

// this is the offset that is used to force buildings underground
// (extra depth to the bury element of constructing and destroying
// buildings)
$BuildingBuryOffset = 1;

$TemporaryTenants = 0;
$LastProduceCount = 0;

// this adds a shadow to an object
function SLGameObj::addShadow(%object)
{
   if (%object.hasShadow() == true)
   {
      return;
   }
   
   // can only place shadows on buildings if the building is in
   // its production state
   if (slgIsBuilding(%object))
   {
      %component = slgQueryInterface(%object, $CID_BUILDING);
      if (%component.getState() != $BuildingState::Production)
      {
         return;
      }
   }
   
   // if the object does not have a shadow decal, it has no shadow to create
   %datablock = %object.getDataBlock();
   if (%datablock.shadowDecal $= "")
   {
      return;
   }

   // this is the selectron shadow that will be created and
   // attached to the object
   %shadow = startSelectron(%object, %datablock.shadowDecal.selectionTypeStyle);
   if (%shadow != 0)
   {
      %shadow.zodiacScale = %datablock.shadowScale;
      %shadow.zodiacOffsetStatic = %datablock.shadowOffsetStatic;
      %shadow.zodiacOffsetDynamic = %datablock.shadowOffsetDynamic;
      %shadow.zodiacAngleStatic = %datablock.shadowAngleStatic;
      %shadow.zodiacAngleDynamic = %datablock.shadowAngleDynamic;
      %shadow.addConstraint(%object, "shadow");
   }

   // temporary selectron variable is used to remove the shadow later
   %object.shadow = %shadow;
}

// this function should be called every tick to ensure that the object
// has a shadow if it does not already
function SLGameObj::updateShadow(%object)
{
   // check if the shadow should be added
   if (%object.isOpaque() == true && %object.showMesh == true &&
      $pref::Shadows::Available == true && %object.hasShadow() == false)
   {
      %object.addShadow();
   }
   
   // check if the shadow should be removed
   if ((%object.isOpaque() == false || %object.showMesh == false ||
      $pref::Shadows::Available == false) && %object.hasShadow() == true)
   {
      %object.removeShadow();
   }
}

// this function returns true if the object has a shadow, false otherwise
function SLGameObj::hasShadow(%object)
{
   return %object.shadow != 0;
}

// this removes a shadow from an object
function SLGameObj::removeShadow(%object)
{
   if (isObject(%object) == false)
   {
      return;
   }
   
   if (%object.hasShadow() == false)
   {
      return;
   }
   
   if (isObject(%object.shadow) == true)
   {
      %object.shadow.stopSelectron();
   }
   %object.shadow = 0;
}

function serverCmdPlaceConstruction(%client, %ghostID)
{
   %object = %client.resolveObjectFromGhostIndex(%ghostID);
   PlaceConstruction(%object);
}

// before a building can be constructed, its construction site must
// be place-- this functionality is not linked to the building states
// in that the building should not be in any state when this function
// is called.  specifically, this function sets up construction decals,
// props, footprint, and building texture
function PlaceConstruction(%object, %loadingGame)
{
   if (%object.linkDirection == 0)
   {
      $ObjectToPlace = 0;
      $ClientToPlace = 0;
   }
   
   // this ensures that all of the bridge pieces are properly
   // positioned/aligned over the water
   if (%object.isLinkObj() == true && %object.linkDirection == 0 &&
      %loadingGame == false)
   {
      /*
      %client = ClientGroup.getObject(0);
      %ghostID = %client.getGhostID(%object);
      commandToClient(%client, 'UpdateBridgeLength', %ghostID);
      */
      
      /*
      %position = %object.position;
      %object.setPosition(getWord(%position, 0),
         getWord(%position, 1), getWord(%position, 2));
      %object.adjustLengthToWaterway();
      */
   }
   
   // save the object at this point-- it's part of the game
   if (%object.isLinkObj() == false || %object.linkStartDirection != 0)
   {
      if (%object.isLinkObj() == true)
      {
         %saveObject = %object.getFirstLinkObj();
         %saveObject.SaveObject(true);
      }
      else
      {
         %object.SaveObject(true);
      }
   }
   
   // get the object's building component-- only buildings can be placed
   // under construction, so if the object is not a building, abort
   %component = slgQueryInterface(%object, $CID_BUILDING); 
   if (!%component)
   {
      return;
   }
   %datablock = %component.getDataBlock();

   // place the building's footprint (prevents other buildings from
   // being placed on top of the construction site)
   PlaceObject(%object, true);

   // place the building's construction decal
   PlaceConstructionDecal(%object, %datablock);

   // initialize the construction position
   %component.constructionPosition = %object.position;

   // place the building's construction props
   PlaceConstructionProps(%component, %object, %datablock);

   // update the building's base construction texture
   if (%object.isLinkObj() == false)
   {
      for (%index = 0; %index < 8; %index++)
      {
         if (%index != $Texture::Road)
         {
            %object.placeTexture(%index);
         }
      }
   }

   if (isObject(%object.ring) == true)
   {
      %object.ring.stopSelectron();
      %object.ring = 0;
   }

   // we are done handling construction placement if this is  not a
   // link object  
   if (%object.isLinkObj() == false)
   {
      // initialize the object under the ground (no mesh should be visible
      // at this point, so push the building below the ground-- it will
      // rise from this position eventually anyways)
      %position = %component.constructionPosition;
      %zMax = %object.getHeight() + $BuildingBuryOffset;
      %zPos = getWord(%position, 2) - %zMax;
      %object.setPosition(getWord(%position, 0), getWord(%position, 1), %zPos);
      return;
   }

   // check if this is a continually placed object for the link object
   if (%object.linkDirection != 0)
   {
      // if this link object is continuing the path for objects being
      // created, then tell the object to start its construction state
      %component.setState($BuildingState::Construction);
      return;
   }
   
   // this is a new link object that is going under construction; first
   // determine the direction that the link object will be built in
   %direction = %object.linkStartDirection;
   
   // set the base link object direction
   if (%direction == 1) %firstLink = %object.getFirstLinkObj();
   else %firstLink = %object.getLastLinkObj();
   
   %firstLink.linkDirection = %direction;
   %firstLink.linkStart = true;
   SetLinkStart(%firstLink);
   
   // turn off all of the meshes except for the base link mesh
   %link = %firstLink;
   while (isObject(%link) == true)
   {
      if (%link.linkDirection == 0) %link.showMesh(false);

      if (%direction == 1) %link = %link.getNextLinkObj();
      else %link = %link.getPrevLinkObj();
   }
}

function SetLinkStart(%firstLink)
{
   %client = ClientGroup.getObject(0);
   %ghostID = %client.getGhostId(%firstLink);
   if (%ghostID != -1)
   {
      commandToClient(%client, 'SetLinkStart', %ghostID);
      return;
   }
   
   schedule(100, 0, "SetLinkStart", %firstLink);
}



//*************************************************************
//           SERVER-SIDE BUILDING CONSTRUCTION STATES
//*************************************************************

// this is called when the building's construction is started
function BuildingServer::onConstructionEnter(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   // start the construction effect
   startEffect(%object, "construct");
   
   if (%object.linkStart == true)
   {
      if (%object.linkDirection == 1) %link = %object.getNextLinkObj();
      else %link = %object.getPrevLinkObj();
      
      // place the first bridge construction piece
      %link.linkDirection = %object.linkDirection;
      PlaceConstruction(%link);
      
      // start the health clock for the bridge
      %client = ClientGroup.getObject(0);
      %ghostID = %client.getGhostId(%object);
      commandToClient(%client, 'CreateHealthClock', %ghostID);
      
      // create a construction timer (this is needed to save out the
      // construction time and health on the server side)
      %count = %object.getLinkCount() - 2;
      %totalTime = BridgeEndData.constructionTime;
      for (%index = 0; %index < %count; %index++)
      {
         %totalTime += BridgeMiddleData.constructionTime;
      }
      %component.timer = new SLTimer()
      {
         time = %totalTime;
      };
      return;
   }

   // set up a timer for the building, which will automatically
   // propagate the building to the production state when the timer
   // is up
   if (%datablock.constructionTime)
   {
      %component.timer = new SLTimer()
      {
         time = %datablock.constructionTime;
      };
      %component.timer.notifyOnFire(stopConstruction, %component);
   }
   
   // Put object through modifications
   osModifySLGameObject(%object);
}

// this is called on every update tick that the building is in
// the construction state
function BuildingServer::onConstructionUpdate(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();

   // if the building has a decal, we want the decal to completely
   // disappear when the construction is done (not fade out when the
   // production start), so the time is checked to predice when the
   // selectron should be turned off
   if (%datablock.constructEffect !$= "" &&
      %object.selectron && %component.timer)
   {
      %time = %component.timer.getElapsedTime();
      %checkTime = %datablock.constructionTime -
         %datablock.constructEffect.fadeOutTime;

      // if the timer is up, stop the selectron
      if (%time >= %checkTime)
      {
         %object.selectron.stopSelectron();
         %object.selectron = 0;
      }
   }
   
   // if our builder has died then stop construction
   if (%this.builder.isHealing)
   {
      %this.setState($BuildingState::Destruction);
      if (isObject(%this.timer))
      {
         %this.timer.delete();
      }
   }
}

// this is called when the building leaves the construction state
function BuildingServer::onConstructionExit(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   // stop the construction effect
   stopEffect(%object, "construct");

   // remove any props that were built on the construction site
   %component.clearProps();

   // if the decal for the building has not yet been deleted,
   // remove it here
   if (%object.selectron != 0)
   {
      %object.selectron.stopSelectron();
      %object.selectron = 0;
   }
   
   // the object should be at full health
   %object.health = %object.getDataBlock().healthMax + %object.healthMaxMod;

   if (%object.isLinkObj() == false)
   {
      // reset builder
      %this.builder = 0;
      
      // return the building to its final position
      %pos = %component.constructionPosition;
      %object.setPosition(getWord(%pos, 0), getWord(%pos, 1), getWord(%pos, 2));
      return;
   }
   
   // do not complete the construction exit process for the first part
   // of the bridge (this has already been done when the bridge was
   // first placed
   if (%object.linkDirection == 1 && %object.isFirstLinkObj() == true ||
      %object.linkDirection == -1 && %object.isLastLinkObj() == true)
   {
      return;
   }
   
   // if this is a link object, we need to turn the mesh on
   %object.showMesh(true);
   
   // then we need to move to the next mesh object and start that
   // construction state by placing the object first
   if (%object.linkDirection == 1) %link = %object.getNextLinkObj();
   else %link = %object.getPrevLinkObj();

   // construction has ended for the link object in this case   
   if (isObject(%link) == false)
   {
      if (%object.linkDirection == 1) %start = %object.getFirstLinkObj();
      else %start = %object.getLastLinkObj();
      %start.health = %start.getDataBlock().healthMax + %start.healthMaxMod;
      
      %component = slgQueryInterface(%start, $CID_BUILDING);
      %component.clearProps();
      %component.setState($BuildingState::Production);
      
      // reset builder
      %this.builder = 0;
      return;
   }
   
   // update the next object's link direction and place its construction
   %link.linkDirection = %object.linkDirection;
   PlaceConstruction(%link);
}

// this function is called when the construction timer is done,
// setting the next state for the building to the production state
// because the building is now ready to produce
function BuildingServer::stopConstruction(%component)
{
   // clear out the building timer and move to the production state
   %component.setState($BuildingState::Production);
   %component.timer = 0;
}

//*************************************************************
//           SERVER-SIDE BUILDING PRODUCTION STATES
//*************************************************************

// this is called when the production state of the building is started
function BuildingServer::onProductionEnter(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   // if this object is the front of a link object, then we
   // need to update this object's footprint with the pathing
   if (%object.isLinkObj() == true && %object.isFirstLinkObj() == true)
   {
      %object.placePathRegion();
   }
   
   if (isObject(GameResourceStack) == false)
   {
      return;
   }
   
   %gameResource = GameResourceStack.getResource();
   if (isObject(%gameResource) == false)
   {
      return;
   }
   
   if (%object.getTeam() == $OST_PLAYER)
   {
      // update the max resource values
      %gameResource.getWater().increaseMax(%datablock.produceWaterCap);
      %gameResource.getFood().increaseMax(%datablock.produceFoodCap);
      %gameResource.getPeople().increaseMax(%component.getTenantMax());
      
      %conn = ClientGroup.getObject(0);
      
      // Update requirement tech tree
      GameTechManager.updateTech(%conn, "BldgTechTree");
   }
   
   %object.updateShadow();
   
   // check if the building should have any produce (create the produce
   // where necessary)
   %produce = %component.produce;
   if (%produce $= "")
   {
      %produce = %datablock.produce;
   }
   
   if (%produce !$= "")
   {
      %component.setProduce(%produce);
   }
   
   // update happiness based on the number of buildings with this type
   // that have already been created
   if (isObject(GameResourceStack) == true && %object.isOnTeam($OST_PLAYER))
   {
      %buildingCount = %component.getDuplicateCount() - 1;
      %happiness = %datablock.getBuildHappiness(%buildingCount);
   
      %resource = GameResourceStack.getResource();
      %resource.getHappiness().increaseCount(%happiness);
      // stat screen value for happiness update
      modifyHappyModValueSvr($HMOD_BUILDINGBLDG, %happiness);
      
      if (%happiness != 0)
      {
         SendProductionToClient(%object, %happiness @ " happiness");
      }
   }
   
   // If this is a town hall then set default tax
   if (%object.isOfType("TownHall"))
   {
      %component.setCurrentTax("notax");
   }
   
   // get the percentage of health that the building is at
   %percentage = %object.health * 100 / %object.getDataBlock().healthMax;
   %component.damageHappiness = %datablock.getDamageHappiness(%percentage);
}

// this function is called on every update tick that the building is
// in the production state
function BuildingServer::onProductionUpdate(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   // check if tenants should be added to the building
   if (%component.canAddTenant() == true &&
      %object.isOnTeam($OST_PLAYER))
   {
      %component.LookForTenants();
   }
   
   // check if the produce effect needs to be run on the object
   if (%object.runningEffect("produce") == false)
   {
      // check if the effect should be turned on
      %employeeMax = %component.getEmployeeMax();
      %employeeCount = %component.getEmployeeCount();
      %tenantMax = %component.getTenantMax();
      %tenantCount = %component.getTenantCount();
      if ((%employeeMax == 0 || %employeeCount > 0) &&
         (%tenantMax == 0 || %tenantCount > 0))
      {
         startEffect(%object, "produce");
      }
   }
   // check if the produce effect needs to stop running on the object
   else
   {
      // check if the effect should be turned off
      %employeeMax = %component.getEmployeeMax();
      %employeeCount = %component.getEmployeeCount();
      %tenantMax = %component.getTenantMax();
      %tenantCount = %component.getTenantCount();
      if (%employeeMax != 0 && %employeeCount == 0 ||
         %tenantMax != 0 && %tenantCount == 0)
      {
         stopEffect(%object, "produce");
      }
   }
   
   // if the building is on fire, prevent building updates and
   // any chance that the building can produce/consume resources
   if (%component.onFire() == true)
   {
      // remove the production cycle if the building has one
      if (isObject(%component.timer) == true)
      {
         %component.timer.delete();
         %component.timer = 0;

         // loop through each produce object
         %count = %component.getPropCount();
         for (%index = 0; %index < %count; %index++)
         {
            // get the produce object
            %prop = %component.getProp(%index);
            %prop.setScaleRate("0");
            %prop.setScale("0 0 0");
         }
      }
      
      // remove the housing cycle if the building has one
      %this.deleteHouseTimer();
   }
   else
   {
      %produceTime = %component.getProductionTime();
      if (%produceTime && !%component.timer &&
         (%component.getProduction($Resource::Gold) != 0 ||
         %component.getProduction($Resource::Wood) != 0 ||
         %component.getProduction($Resource::Water) != 0 ||
         %component.getProduction($Resource::Food) != 0 ||
         %component.currentTax > 0))
      {
         if (%component.produce !$= "")
         {
            %component.setProduce(%component.produce);
         }
         else
         {
            %component.timer = new SLTimer()
            {
               time = %produceTime;
            };
            %component.timer.notifyOnFire(updateResources, %component);
         }
      }

      if (isObject(GameResourceStack) == true)
      {
         %resource = GameResourceStack.getResource();
         %people = %resource.getPeople();

         // if a new tenant can be added to the building
         %minMax = %people.getMax();
         if (%minMax > %people.getLimit()) %minMax = %people.getLimit();
         if (%object.getTeam() == $OST_PLAYER &&
            %component.canAddTenant() == true && %people.getCount() +
            $TemporaryTenants < %minMax)
         {
            if (isObject(GameResourceStack) == true)
            {
               %time = GameResourceStack.getHouseTime();
               if (GameResourceStack.canHouse() == true && %time >= 0)
               {
                  %client = ClientGroup.getObject(0);
                  %ghostID = %client.getGhostID(%component);
                  
                  if (isObject(%component.housing) == true)
                  {
                     if (%time + %datablock.housingOffset > %component.housing.time)
                     {
                        %component.housing.time = %time + %datablock.housingOffset;
                        commandToClient(%client, 'UpdateHouseBmpTimer', %ghostID, %component.housing.time);
                     }
                  }
                  else
                  {
                     %component.housing = new SLTimer()
                     {
                        time = %time + %datablock.housingOffset;
                     };
                     %component.housing.notifyOnFire(addNewTenant, %component);
                     
                     // create client timer
                     commandToClient(%client, 'CreateHouseBmpTimer', %ghostID,
                        %component.housing.time, 0, %component.getTenantCount());
                  }
               }
               else
               {
                  %this.deleteHouseTimer();
               }
            }
         }
      }
   }
   
   // get the percentage of health that the building is at
   %percentage = %object.health * 100 / %object.getDataBlock().healthMax;
   
   // update the happiness based on the change in damage states
   %damageHappiness = %datablock.getDamageHappiness(%percentage);
   if (%component.damageHappiness != %damageHappiness &&
      %object.getTeam() == $OST_PLAYER)
   {
      %resource = GameResourceStack.getResource();
      %happiness = %resource.getHappiness();
      %difference = %damageHappiness - %component.damageHappiness;
      %happiness.increaseCount(%difference);
      
      // update stat gui happiness as well
      modifyHappyModValueSvr($HMOD_DAMAGEBLDG, %difference);
      
      %component.damageHappiness = %damageHappiness;
      
      // notify client of change
      %clientCount = ClientGroup.getCount();
      for (%i = 0; %i < %clientCount; %i++)
      {
         %client   = ClientGroup.getObject(%i);
         %comGhost = %client.getGhostID(%component);
         commandToClient(%client, 'OnDamageHappinessChange', %comGhost, %damageHappiness);
      }
      if (%damageHappiness == 0)
      {
         %component.clearStatus($BuildingStatus::Damaged);
         
      }
      else
      {
         %component.setStatus($BuildingStatus::Damaged);
      }
      
      SendProductionToClient(%object, %difference @ " happiness");
   }
   
   
   // if the building has not been updated with the skin it should have,
   // then immediately update the skin with its damage level (buildings
   // should default to their full-health skin; however, buildings that
   // are loaded through the mission file will being in the production
   // update state will automatically display the right texture).
   if (%object.skin $= "")
   {
      %object.skin = %datablock.getDamageTexture(%percentage);
      %object.setSkinName(%object.skin);
   }
   // if the object has already been given a current skin, check if
   // the texture for the building should change because the damage
   // level has changed for the object.
   else
   {
      %skin = %datablock.getDamageTexture(%percentage);
      if (%object.skin !$= %skin)
      {
         %object.skin = %skin;
         %object.setSkinName(%object.skin);
      }
   }

   // start the destruction state
   if (%percentage <= 0)
   {
      // if we are an outpost or a train station, we go back to being
      // a platform
      if (%object.isOfType("trainstation") || %object.isOfType("Outpost"))
      {
         if (%component.upgraded == false)
         { 
            UpgradeBuilding(%this, "Platform");
            
            //Play the destruction sound
            %object.playSFX("destruction");
         }
      }
      
      // otherwise start destruction state
      else
      {
         %component.setState($BuildingState::Destruction);
      }
   }
   
   // update the building's shadow
   %object.updateShadow();
}

// this function is called when the building leaves the production state
function BuildingServer::onProductionExit(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   %component.clearProps();
   
   %happinessChange = 0;
   
   // clear all status if any are on
   %component.clearAllStatus();
      
   // update happiness based on the number of buildings with this type
   // that have already been created (and a building that is being
   // destroyed)
   if (isObject(GameResourceStack) == true)
   {
      %buildingCount = %component.getDuplicateCount() - 1;
      %happiness = %datablock.getBuildHappiness(%buildingCount);
   
      %resource = GameResourceStack.getResource();
      %resource.getHappiness().increaseCount(-%happiness);
      // stat screen value for happiness update
      modifyHappyModValueSvr($HMOD_BUILDINGBLDG, -%happiness);
      
      %happinessChange -= %happiness;
   }
   
   // restore any happiness lost because the building was damaged
   %resource = GameResourceStack.getResource();
   %happiness = %resource.getHappiness();
   %happiness.increaseCount(-%component.damageHappiness);
   modifyHappyModValueSvr($HMOD_DAMAGEBLDG, -%component.damageHappiness);
   %happinessChange -= %component.damageHappiness;
   %component.damageHappiness = 0;
   
   if (%happinessChange != 0)
   {
      SendProductionToClient(%object, %happinessChange @ " happiness");
   }
}

// this function is called every time a production timer is complete,
// updating the resources based on the number of employees that
// the building has and the production rates for that building
function BuildingServer::updateResources(%component)
{
   if (%component.currentTax $= "") 
   {
      %component.produceResources();
   }
   else 
   {
      %component.produceTaxes();
   }
   %component.timer = 0;
}

// produces taxes if your current tax is above zero
function BuildingServer::produceTaxes(%this)
{
   // Get tax amount
   %amount = %this.getCurrentTaxAmount(slgGetServerCharacterList());
   
   // Tax count
   if (%amount > 0)
   {
      GameResourceStack.getResource().clearHappinessRates();
      IncreaseGold(%amount);
      
      %object = %this.getObjectId();
      
      %client = ClientGroup.getObject(0);
      %ghostID = %client.getGhostID(%object);
      commandToClient(%client, 'CreateResourceInfo', %ghostID, %amount @ " gold");
   }
}

// Used to update taxes if a bank has been destroyed or if the building has
// lost employees
function BuildingServer::updateTaxes(%this)
{
   if (%this.getDatablock().taxValues $= "") 
   {
      return;
   }
   
   // Update tax level from employees
   if (%this.getEmployeeCount() <= 0)
   {
      %this.setCurrentTax("notax");
      return;
   }
   
   // Update tax from banks
   %bldgs = slgGetServerBuildingList();
   if (isObject(%bldgs) == false)
   {
      return;
   }
   
   // Create game object lists for banks
   %banks = new SLGameObjList();
   %teamBanks = new SLGameObjlist();
   if (isObject(%banks) == false || isObject(%teamBanks) == false)
   {
      if (isObject(%banks)) 
      {
         %banks.delete();
      }
      if (isObject(%teamBanks))
      {
         %teamBanks.delete();
      }
      return;
   }
   
   // Get team banks
   %bldgs.getTypeList("Bank", %banks);
   %banks.getTeamList($OST_PLAYER, %teamBanks);
   
   // Get number of banks with employees
   %banks.clear();
   %teamBankSize = %teamBanks.getSize();
   for (%i = 0; %i < %teamBankSize; %i++)
   {
      %obj = %teamBanks.getID(%i);
      %component = slgQueryInterface(%obj, $CID_BUILDING);
      if (isObject(%component))
      {
         if (%component.getEmployeeCount() > 0)
         {
            %banks.addObject(%obj);
         }
      }
   }
   
   // Only one bank, set to low tax if at high tax
   if (%banks.getSize() == 1)
   {
      // Set to low tax if at high tax
      if (%this.getCurrentTax() $= "hightax")
      {
         %this.setCurrentTax("lowtax");
      }
   }
   
   // No banks
   if (%banks.getSize() == 0)
   {
      // Set to no tax if not at no tax
      if (%this.getCurrentTax() !$= "notax")
      {
         %this.setCurrentTax("notax");
      }
   }

   // Release lists
   %banks.delete();
   %teamBanks.delete();
}

// called when you wish to start repairing the building
function BuildingServer::startRepair(%this, %cmpChar)
{
   // create timer to start repair
   if (%this.repairTimer == 0)
   {
      %this.repairTimer = new SLEndlessTimer()
      {
         time = %this.getDataBlock().repairTime;
      };
      %this.repairTimer.notifyOnFire(onRepair,%this);
      %this.repairChar = %cmpChar;
      %cmpChar.setState($CharacterState::Repair);
      
      // set building to repair state
      %this.setRepair(true);
   }
}

// called each repair fire
function BuildingServer::onRepair(%component, %time)
{
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   if (isObject(%object) == false)
   {
      %component.stopRepair();
      error("Building component is not attached to an object-- aborting repair.");
      return;
   }
   
   %resource = GameResourceStack.getResource();
   if (isObject(%resource) == false)
   {
      %component.stopRepair();
      error("No resources were found-- cannot repair building.");
      return;
   }
   
   %gold = %resource.getGold();
   %wood = %resource.getWood();
   
   %maxHealth = %object.getMaxHealth();
   %healthGain = %maxHealth - %object.health;
   if (%healthGain > %datablock.repairAmount)
   {
      %healthGain = %datablock.repairAmount;
   }
   %percentage = %healthGain / %datablock.repairAmount;
   %goldCost = mCeil(%datablock.repairGold * %percentage);
   %woodCost = mCeil(%datablock.repairWood * %percentage);

   // can no longer repair a building because health is full
   // or there are not enough resources to repair the building
   if (%gold.getCount() < %goldCost || %wood.getCount() < %woodCost)
   {
      %component.stopRepair();
      %client = ClientGroup.getObject(0);
      commandToClient(%client, 'SendHudMessage', "Not enough resources to repair " @ %object.name @ ".");
      return;
   }
   
   %object.health += %healthGain;
   %gold.decreaseCount(%goldCost);
   %wood.decreaseCount(%woodCost);
   SendProductionToClient(%object, -%goldCost @ " gold " @ -%woodCost @ " wood");
   
   // check if the repair is complete
   if (%object.health >= %maxHealth)
   {
      %object.health = %maxHealth;
      %component.stopRepair();
   }
}

// called when the building is to stop repairing itself
function BuildingServer::stopRepair(%this)
{
   // stop timer
   if (%this.repairTimer != 0)
   {
      %this.repairTimer.endTimer();
      %this.repairTimer = 0;
      
      // send repair message
      if (isObject(MsgSender))
      {
         %objBldg = slgGetAttachedObjId(%this);
         %objChar = slgGetAttachedObjId(%this.repairChar);
         MsgSender.postMsg($MSG_SLGOBJREPAIR, $MRT_LOCAL, %objBldg, %objChar);
      }
         
      // end repair state
      %this.setRepair(false);
      %this.repairChar.clearState($CharacterState::Repair);
      %this.repairChar = 0;
   }
}

function InitializeProduceBadges()
{
   for (%index = 0; %index < 6; %index++)
   {
      $ProduceBadges[%index] = 0;
   }
}

function SaveProduceBadges()
{
}

function LoadProduceBadges()
{
}

// this function sets the produce that a building should use when producing
// resources (set to "" for no produce)
function BuildingServer::setProduce(%component, %produce)
{
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   // update the produce that the component is using
   %oldProduce = %component.produce;
   %component.produce = %produce;
   if (%oldProduce !$= %produce)
   {
      %component.updateProduce = true;
      %component.oldProduce = %oldProduce;
   }
   
   // if the building is not in the production state, do not create the produce
   if (%component.getState() != $BuildingState::Production)
   {
      return;
   }
   
   // get the production amount (if zero, then do not create sprouts yet)
   %productionFood = %component.getProduction($Resource::Food);
   %productionGold = %component.getProduction($Resource::Gold);
   %productionWood = %component.getProduction($Resource::Wood);
   %productionWater = %component.getProduction($Resource::Water);
   if (%productionFood == 0 && %productionGold == 0 &&
      %productionWood == 0 && %productionWater == 0)
   {
      return;
   }
   
   // check for badges
   if (%component.updateProduce == true)
   {
      %component.updateProduce = false;
      
      // remove the old produce
      if (%component.oldProduce $= "chicken")
         $ProduceBadges[0] = $ProduceBadges[0] - 1;
      else if (%component.oldProduce $= "pig")
         $ProduceBadges[1] = $ProduceBadges[1] - 1;
      else if (%component.oldProduce $= "cow")
         $ProduceBadges[2] = $ProduceBadges[2] - 1;
      else if (%component.oldProduce $= "wheat")
         $ProduceBadges[3] = $ProduceBadges[3] - 1;
      else if (%component.oldProduce $= "corn")
         $ProduceBadges[4] = $ProduceBadges[4] - 1;
      else if (%component.oldProduce $= "pumpkin")
         $ProduceBadges[5] = $ProduceBadges[5] - 1;
      else if (%component.oldProduce $= "sheep")
         $ProduceBadges[6] = $ProduceBadges[6] - 1;
      
      // add the new produce
      if (%produce $= "chicken")
         $ProduceBadges[0] = $ProduceBadges[0] + 1;
      else if (%produce $= "pig")
         $ProduceBadges[1] = $ProduceBadges[1] + 1;
      else if (%produce $= "cow")
         $ProduceBadges[2] = $ProduceBadges[2] + 1;
      else if (%produce $= "wheat")
         $ProduceBadges[3] = $ProduceBadges[3] + 1;
      else if (%produce $= "corn")
         $ProduceBadges[4] = $ProduceBadges[4] + 1;
      else if (%produce $= "pumpkin")
         $ProduceBadges[5] = $ProduceBadges[5] + 1;
      else if (%produce $= "sheep")
         $ProduceBadges[6] = $ProduceBadges[6] + 1;
         
      // the production types have been changed
      UpdateProduction($ProduceBadges[3], $ProduceBadges[4],
         $ProduceBadges[5], $ProduceBadges[0], $ProduceBadges[1],
         $ProduceBadges[2], $ProduceBadges[6]);
      
      // Check ranch requirements to start Secret Quest on Campaign2
      // Check to make sure you're on campaign2 map
      if (isObject("map2check"))
      {
         %ranchAnimals = true;
         for (%index = 0; %index < 3; %index++)
         {
            if ($ProduceBadges[%index] <= 0)
            {
               %ranchAnimals = false;
               break;
            }
         }
         
         if (%ranchAnimals == true)
         {
            TaskModifier.strMarkVertex("Campaign2Tasks", "AnimalTrapMapTrigger", $TSV_AVAIL);
            //Destroy object to prevent task being reenabled.
            map2check.deleteObject();
         }
      }

      
      // check ranch requirements (to see if a badge should be awarded)
      if (tsIsBadgeAwarded("badge_rancher") == false)
      {
         %ranchBadge = true;
         for (%index = 0; %index < 3; %index++)
         {
            if ($ProduceBadges[%index] <= 0)
            {
               %ranchBadge = false;
               break;
            }
         }
         
         if (%ranchBadge == true)
         {
            AwardRanchProduceBadge();
         }
      }
      
      // check farm requirements (to see if a badge should be awarded)
      if (tsIsBadgeAwarded("badge_farmer") == false)
      {
         %farmBadge = true;
         for (%index = 3; %index < 6; %index++)
         {
            if ($ProduceBadges[%index] <= 0)
            {
               %farmBadge = false;
               break;
            }
         }
         
         if (%farmBadge == true)
         {
            AwardFarmProduceBadge();
         }
      }
      
      // check production requirements (to see if a badge should be awarded)
      if (tsIsBadgeAwarded("badge_foodking") == false)
      {
         if (tsIsBadgeAwarded("badge_rancher") == true &&
            tsIsBadgeAwarded("badge_farmer") == true)
         {
            %produceBadge = true;
            for (%index = 0; %index < 6; %index++)
            {
               if ($ProduceBadges[%index] <= 0)
               {
                  %produceBadge = false;
                  break;
               }
            }
            
            if (%produceBadge == true)
            {
               AwardAllProduceBadge();
            }
         }
      }
      
      // check sheep requirements
      if (tsIsBadgeAwarded("badge_sheep") == false)
      {
         if ($ProduceBadges[6] > 0)
         {
            AwardSheepBadge();
         }
      }
   }

   // find out how long it will take for the produce to give resources
   // and create the production timer for this
   %produceTime = %component.getProductionTime();
   if (isObject(%component.timer) == true)
   {
      %component.timer.delete();
   }
   %component.timer = new SLTimer()
   {
      time = %produceTime;
   };
   %component.timer.notifyOnUpdate(updateProduce, %component);
   %component.timer.notifyOnFire(updateResources, %component);
   
   // remove any old produce
   %name = %component.getProduceDatablock(%produce);
   if (%oldProduce !$= %produce || %name.reloadProduce == true)
   {
      %component.clearProps();
   }
   
   // MGR:
   // Changed this so that it will not affect the gold mine, since the plague
   // disaster does not affect it anyway. Otherwise, the pick upgrade will
   // be reset by this script if the if statement is not present.
   if (!%object.isOfType("Goldmine"))
   {
      %oldRate = %component.getProductionRate();
      %rate = $DisasterManager.getProductionRate(%produce);
      
      // if the building changes produce during a plague
      if (%rate < %oldRate)
      {
         startEffect(%object, "plague");
      }
      else if (%oldRate < %rate)
      {
         stopEffect(%object, "plague");
      }
      
      %component.setProductionRate(%rate);

      // building is no longer affected by a disaster   
      if (%oldRate < %rate)
      {
         %plague = $DisasterManager.getPlague(%oldProduce);
         if (isObject(%plague) == true)
         {
            %plague.onRemoveObject(%object);
            %component.clearStatus($BuildingStatus::Plague);
         }
      }
      // building is now affected by a disaster
      else if (%oldRate > %rate)
      {
         %plague = $DisasterManager.getPlague(%produce);
         if (isObject(%plague) == true)
         {
            %plague.onAddObject(%object);
            %component.setStatus($BuildingStatus::Plague);
         }
      }
   }
   
   // for each produce point, create the produce, place the produce,
   // and initialize the produce for growth
   %count = %component.getProducePointCount(%produce);
   for (%index = %component.getPropCount(); %index < %count; %index++)
   {
      %position = %component.getProducePoint(%produce, %index);
      %rotation = %component.getProduceRotation(%produce, %index);
      
      // if the production growth is 0, then the produce should start
      // out fully matured (used for livestock)
      %scale = $SproutHeight;
      if (%datablock.getProductionGrowth(%produce) == 0) %scale = 1.f;
      
      // create a prop
      %prop = new StaticShape()
      {
         canSaveDynamicFields = "1";
         position = %position;
         rotation = "0 0 1 " @ %rotation;
         scale = %scale @ " " @ %scale @ " " @ %scale;
         datablock = %name;
      };
      
      %prop.startTime = 0;
      %prop.endTime = %datablock.getProductionGrowth(%produce);
      %prop.fadeTime = %produceTime - %datablock.getProductionFade(%produce);
      %prop.fading = false;

      %scaleRate = 0;
      if (%prop.endTime <= 0)
      {
         %prop.setScale("1 1 1");
      }
      else
      {
         %scaleRate = (1 - %scale) / %prop.endTime;
      }
      %prop.setScaleRate(%scaleRate);
      
      %prop.animationOffset = getRandom(%name.animationOffset);
      
      // add the created prop to the prop list
      %component.addProp(%prop, $Detail::Low);
   }
   
   %component.UpdateProduceHappiness();
}

// this function is called on every timer produce update call (only occurs
// if produce specifically is used for the building), it handles fading for
// the produce and resizing of the produce
function BuildingServer::updateProduce(%this, %time)
{
   %component = %this;
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   // loop through each produce object
   %count = %component.getPropCount();
   for (%index = 0; %index < %count; %index++)
   {
      // get the produce object
      %prop = %component.getProp(%index);
      
      // determine if the produce should start fading
      if (%time > %prop.fadeTime)
      {
         %propDatablock = %prop.getDataBlock();
         if (%propDatablock.reloadProduce == true &&
            %prop.fading == false)
         {
            %prop.fading = true;
            
            // fade is not consistent between OpenGL and D3D
            //%prop.startFade(1000, 0, true);
         }
      }
      // determine if the produce is done growing
      else if (%time > %prop.endTime && %prop.getScaleRate() != 0)
      {
         %prop.setScaleRate("0");
         %prop.setScale("1 1 1");
      }
      
      // play the ambient animation on the prop
      if (%time >= %prop.animationOffset)
      {
         %prop.playThread(0, "ambient");
         
         if (isObject(%prop.animation) == false)
         {
            %datablock = %prop.getDatablock();
            %low = %datablock.animationLowTime;
            %high = %datablock.animationHighTime;
            %idleTime = getRandom() * (%high - %low) + %low;

            %prop.animation = new SLTimer()
            {
               time = %idleTime;
            };
            %prop.animation.prop = %prop;
            %prop.animation.notifyOnFire("IdleAnimateProp", %prop.animation);
            %prop.animation.component = %component;
         }
      }
   }
}

function SLTimer::IdleAnimateProp(%timer)
{
   %prop = %timer.prop;
   %component = %prop.animation.component;
   if (isObject(%component) == false)
   {
      return;
   }

   %found = false;
   %count = %component.getPropCount();
   for (%index = 0; %index < %count; %index++)
   {
       %compProp = %component.getProp(%index);
       if (%compProp == %prop)
       {
          %found = true;
          break;
       }
   }
   if (%found == false)
   {
      return;
   }
   %datablock = %prop.getDatablock();
   
   %prop.stopThread(0);
   %prop.playThread(0, "idle");

   %prop.animation = new SLTimer()
   {
      time = %datablock.idleTime;
   };
   %timer = %prop.animation;
   %timer.notifyOnUpdate("UpdatePropIdle", %timer);
   %timer.component = %component;
   %timer.prop = %prop;
}

function SLTimer::UpdatePropIdle(%timer)
{
   %prop = %timer.prop;
   %component = %timer.component;
   if (isObject(%component) == false)
   {
      %timer.delete();
      return;
   }

   %found = false;
   %count = %component.getPropCount();
   for (%index = 0; %index < %count; %index++)
   {
       %compProp = %component.getProp(%index);
       if (%compProp == %prop)
       {
          %found = true;
          break;
       }
   }
   if (%found == false)
   {
      %timer.delete();
      return;
   }
   
   %prop.playThread(0, "idle");
}

// this function is called when the production should stop because
// there are no people
function BuildingServer::removeProduce(%component, %object)
{
   // remove the production timer
   if (isObject(%component.timer) == true)
   {
      %component.timer.delete();
      %component.timer = 0;
   }
   
   // remove the old produce
   if (%component.produce $= "chicken")
      $ProduceBadges[0] = $ProduceBadges[0] - 1;
   else if (%component.produce $= "pig")
      $ProduceBadges[1] = $ProduceBadges[1] - 1;
   else if (%component.produce $= "cow")
      $ProduceBadges[2] = $ProduceBadges[2] - 1;
   else if (%component.produce $= "wheat")
      $ProduceBadges[3] = $ProduceBadges[3] - 1;
   else if (%component.produce $= "corn")
      $ProduceBadges[4] = $ProduceBadges[4] - 1;
   else if (%component.produce $= "pumpkin")
      $ProduceBadges[5] = $ProduceBadges[5] - 1;
   else if (%component.produce $= "sheep")
      $ProduceBadges[6] = $ProduceBadges[6] - 1;
   %component.oldProduce = "";
   %component.updateProduce = true;
   
   // the production types have been changed
   UpdateProduction($ProduceBadges[3], $ProduceBadges[4],
      $ProduceBadges[5], $ProduceBadges[0], $ProduceBadges[1],
      $ProduceBadges[2], $ProduceBadges[6]);
   
   // remove any existing produce props
   %component.clearProps();
   
   %component.UpdateProduceHappiness();
}

function BuildingServer::UpdateProduceHappiness(%component)
{
   // remove the old produce
   if (%component.produce !$= "chicken" &&
      %component.produce !$= "pig" &&
      %component.produce !$= "cow" &&
      %component.produce !$= "wheat" &&
      %component.produce !$= "corn" &&
      %component.produce !$= "pumpkin" &&
      %component.produce !$= "sheep")
   {
      return;
   }
   
   // since the production is starting for the building, check if
   // the happiness should change
   %uniqueProduceCount = %component.getUniqueProduceCount();
   if (%uniqueProduceCount != $LastProduceCount)
   {
      if (%uniqueProduceCount < $LastProduceCount)
      {
         %happiness = GameResourceData.getProduceHappiness($LastProduceCount - 1);
         %resource = GameResourceStack.getResource();
         %resource.getHappiness().decreaseCount(%happiness);
         // update stat value for happiness
         modifyHappyModValueSvr($HMOD_PRODUCE, -%happiness);
         
         SendProductionToClient(%component.getObjectId(), -%happiness @ " happiness");
      }
      else
      {
         %happiness = GameResourceData.getProduceHappiness(%uniqueProduceCount - 1);
         %resource = GameResourceStack.getResource();
         %resource.getHappiness().increaseCount(%happiness);
         // update stat value for happiness
         modifyHappyModValueSvr($HMOD_PRODUCE, %happiness);
         
         SendProductionToClient(%component.getObjectId(), %happiness @ " happiness");
      }
      $LastProduceCount = %uniqueProduceCount;
   }
}

// this function is called when the tenant timer indicates that new
// tenants should be created
function BuildingServer::addNewTenant(%component)
{
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   if (%object.getTeam() != $OST_PLAYER)
   {
      return;
   }

   // do not use this timer any longer (should delete itself)
   %component.housing = 0;
   
   // if there is no room in the town for new tenants, do not add
   // a new tenant to this building
   %resource = GameResourceStack.getResource();
   %people = %resource.getPeople();
   %minMax = %people.getMax();
   if (%minMax > %people.getLimit()) %minMax = %people.getLimit();
   if (%people.getCount() + $TemporaryTenants >= %minMax)
   {
      return;
   }
   
   // create the new tenant and add it to the building as a tenant
   %position = %component.getDoor();
   if (getRandom(0, 1) == 0)
   {
      %tenant = CreateSaveObject(MaleCiv, "", $OST_PLAYER);
   }
   else
   {
      %tenant = CreateSaveObject(FemaleCiv, "", $OST_PLAYER);
   }
   
   %tenant.health = %tenant.getDataBlock().healthMax + %tenant.healthMaxMod;
   %tenant.setPosition(getWord(%position, 0), getWord(%position, 1));
   %tenant.startFade(1000, 0, false);
   %tenant.tempTenant = true;
   %tenant.showMesh(false);
   slgHouseObject(%tenant, %object);
   
   %ai = slgQueryInterface(%tenant, $CID_AI);
   %ai.performAction("shelter", %object);
   
   SendProductionToClient(%object, "1 people");
   
   %client = ClientGroup.getObject(0);
   %ghostID = %client.getGhostId(%object);
   commandToClient(%client, 'updateHealthBar', %ghostID, %object.getTeam(), %component.getTenantCount());
   
   // send hud notification
   %msg = slgGetUIString("id_character_arrive");
   %msg = slgFormatUIString(%msg, %tenant.name);
   commandToClient(%client, 'SendHudMessage', %msg); 
   
   $TemporaryTenants++;
}

// This function is used to update the happiness based on a tax
// bracket change.
// \param %oldTax previous tax (string)
// \param %newTax the new tax being set from the previous tax (string)
////////////////////////////////////////////////////////////////////////////////
function updateTaxHappiness(%oldTax, %newTax)
{
   // if the tax has not changed, do nothing
   if (%oldTax $= %newTax)
   {
      return;
   }
   
   // get the resource object to update happiness
   %resource = GameResourceStack.getResource();
   if (isObject(%resource) == false)
   {
      return;
   }

   // get the happiness object that will be updated
   %happiness = %resource.getHappiness();
   
   //taxHappiness = "15 45";
   %change = 0;
   %lowTax = getWord(GameResourceData.taxHappiness, 0);
   %highTax = getWord(GameResourceData.taxHappiness, 1);

   // moving from no taxes to taxes
   if (%oldTax $= "notax")
   {
      if (%newTax $= "lowtax") %change = %lowTax;
      else %change = %highTax;
   }
   // moving from low taxes to a different tax bracket
   else if (%oldTax $= "lowtax")
   {
      %change -= %lowTax;
      if (%newTax $= "hightax") %change += %highTax;
   }
   // moving from high taxes to a different tax bracket
   else
   {
      %change -= %highTax;
      if (%newTax $= "lowtax") %change += %lowTax;
   }
   
   // update the happiness based on the tax change
   %happiness.increaseCount(%change);
   modifyHappyModValueSvr($HMOD_TAXES, %change);
   return %change;
}

// use to set the current tax for a building. If set, resource production will
// use this value and not its normal production
// \param %taxlevel level of tax ("notax", "lowtax", "hightax")
function BuildingServer::setCurrentTax(%component, %taxlevel)
{
   // update the happiness based on the tax change
   %change = updateTaxHappiness(%component.getCurrentTax(), %taxlevel);
   if (%change != 0)
   {
      SendProductionToClient(%component.getObjectId(), %change @ " happiness");
   }
   
   // Default to highest level
   %level = 2;
   if (%taxlevel $= "notax") 
   {
      %level = 0;
   }
   else if (%taxlevel $= "lowtax")
   {
      %level = 1;
   }
   
   %currenttax = getWord(%component.getDatablock().taxValues, %level);
   %component.currentTax = %currenttax;
   %component.taxLevel = %taxLevel;
   
   // Notify clients
   %clientSize = ClientGroup.getCount();
   for (%i = 0; %i < %clientSize; %i++) 
   {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'OnTaxSet', %client.getGhostId(%component), %taxlevel, %currenttax);
   }
}

function BuildingServer::getCurrentTax(%this)
{
   // No tax
   if (%this.currentTax == getWord(%this.getDatablock().taxValues, 0))
   {
      return "notax";
   }
   
   // Low tax
   if (%this.currentTax == getWord(%this.getDatablock().taxValues, 1))
   {
      return "lowtax";
   }
   
   // High tax
   if (%this.currentTax == getWord(%this.getDatablock().taxValues, 2))
   {
      return "hightax";
   }
}

function BuildingServer::LookForTenants(%component)
{
   // double-check if we can look for tenants for the building
   %tenantCount = %component.getTenantCount();
   %tenantMax = %component.getTenantMax();
   if (%tenantCount >= %tenantMax)
   {
      return;
   }
   
   // get the character list
   %list = slgGetServerCharacterList();
   if (isObject(%list) == false)
   {
      return;
   }
   
   // look for unemployed characters to employ
   %size = %list.getSize();
   %initialCount = %tenantCount;
   for (%index = 0; %index < %size; %index++)
   {
      // check if character is on the player's team
      %object = %list.getID(%index);
      if (%object.getTeam() != $OST_PLAYER)
      {
         continue;
      }
      
      // check if the character can be given a home
      %character = slgQueryInterface(%object, $CID_CHARACTER);
      if (%character.isShelterable() == false)
      {
         continue;
      }
      
      // check if the character already had a home
      if (%character.hasHome() == true)
      {
         continue;
      }
      
      // check if character is drunk
      if (%character.inState($CharacterState::Drunk))
      {
         %drunk = %object;
         continue;
      }
      
      // make this character a tenant
      slgHouseObject(%object, %component.getObjectId());
      
      // check if we should look for any more tenants
      %tenantCount++;
      if (%tenantCount >= %tenantMax)
      {
         break;
      }
   }
   
   // if there were no other characters to house except a drunk character then
   // house the drunk character (going to house one drunk at a time to avoid
   // excessive new/delete or double for loops)
   if (%initialCount == %tenantCount && %tenantCount < %tenantMax)
   {
      if (isObject(%drunk))
      {
         slgHouseObject(%drunk, %component.getObjectId());
         %tenantCount++;
      }
   }
   
   // stop housing timer if running
   if (%initialCount != %tenantCount) 
   {
      %component.deleteHouseTimer();
   }
}



//*************************************************************
//           SERVER-SIDE BUILDING DESTRUCTION STATES
//*************************************************************

// this is called when the building enters its destruction state
function BuildingServer::onDestructionEnter(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   if (%component.updateProduce == false)
   {
      %component.removeProduce(%object);
   }
   
   if (isObject(%component.burnTimer) == true)
   {
      %component.burnTimer.delete();
      %component.burnTimer = 0;
   }
   
   // If this is a town hall then reset to default tax
   if (%object.isOnTeam($OST_PLAYER))
   {
      if (%object.isOfType("TownHall"))
      {
         %component.setCurrentTax("notax");
      }
   }
      
   if (%object.runningEffect("fire") == true)
   {
      stopEffect(%object, "fire");
   }
   
   // check if the production effect needs to be turned off
   if (%object.runningEffect("produce") == true)
   {
      stopEffect(%object, "produce");
   }
   
   // start the destruction effect
   startEffect(%object, "destruct");
   
   %object.onDestroy();
   
   if (isObject($DisasterManager) == true)
   {
      $DisasterManager.updateFires();
   }
   
   // added to remove building from plague disaster once it is destroyed
   %plague = $DisasterManager.getPlague(%component.produce);
   if (isObject(%plague) == true)
   {
      %plague.onRemoveObject(%object);
   }
   
   // if a timer is still attached, it is the production timer-- kill
   // the timer because the building can no longer produce anything;
   // this guarantees that production stops
   if (isObject(%component.timer) == true)
   {
      %component.timer.delete();
      %component.timer = 0;
   }
   if (isObject(%component.housing) == true)
   {
      %component.housing.delete();
      %component.housing = 0;
   }

   // remove all of the employees
   while (%component.getEmployeeCount() > 0)
   {
      // check the hotspot for the employee and if it's occupied,
      // make sure the object stops moving if no actions are queued
      %employee = %component.getEmployee(0);
      %employee.playThread(0, "root");
      slgUnemployObject(%employee);
      %ai = slgQueryInterface(%employee, $CID_AI);
      RestoreCharacterOutfit(%ai);
      StopMoving(%ai);
   }
   
   // remove all of the tenants
   while (%component.getTenantCount() > 0)
   {
      %tenant = %component.getTenant(0);
      slgUnhouseObject(%tenant);
   }
   
   //r emove all of the hiders
   while (%component.getHiderCount() > 0)
   {
      %hider = %component.getHider(0);
      slgUnhideObject(%hider);
   }

   if (isObject(GameResourceStack) == false)
   {
      return;
   }
   
   if (%object.isOnTeam($OST_PLAYER))
   {
      %gameResource = GameResourceStack.getResource();
      if (isObject(%gameResource) == false)
      {
         return;
      }

      %gameResource.getWater().decreaseMax(%datablock.produceWaterCap);
      %gameResource.getFood().decreaseMax(%datablock.produceFoodCap);
      %gameResource.getPeople().forceDecreaseMax(%component.getTenantMax());
   }
   
   // render the building's destruction decal
   PlaceDestructionDecal(%object, %datablock);

   // place the buiding's construction prop
   PlaceDestructionProps(%component, %object, %datablock);

   // if the construction will end at a certain time, set up
   // the timer for this progression
   if (%datablock.destructionTime)
   {
      %component.timer = new SLTimer()
      {
         time = %datablock.destructionTime;
      };
      %component.timer.notifyOnFire(stopDestruction, %component);
   }
   // if not destruction time is present, set up for immediate
   // destruction (for the building)
   else
   {
      %component.timer = new SLTimer()
      {
         time = 0;
      };
      %component.timer.notifyOnFire(stopDestruction, %component);
   }

   // remove the object's shadow
   %object.removeShadow();
   
   // hide the building
   %object.showMesh(false);

   %object.health = 0;
   
   //Play the destruction sound
   %object.playSFX("destruction");
   
   if ($PlayingGame == true) 
   {
      if (%object.isOnTeam($OST_PLAYER))
      {
         %conn = ClientGroup.getObject(0);
         
         // Update requirement tech tree
         GameTechManager.updateTech(%conn, "BldgTechTree");
      }
      
      // Send out destruction message
      slgSendObjDestroyMsgLocal(slgGetAttachedObjId(%this));
   }
   
   // if the building has a health bar, we don't want it visible
   DestroyHealthBar(%object);
   
   // if it is not a link object, we are done
   if (%object.isLinkObj() == false)
   {
      return;
   }
   
   // if this object is not the first object in the list, then
   // we have already set the states appropriately
   if (%object.isFirstLinkObj() == false)
   {
      return;
   }
   
   // set all of the remaining link objects into the destruction state
   %link = %object;
   while (isObject(%link) == true)
   {
      RemoveObject(%link);
      %link.showMesh(false);
      %linkComponent = slgQueryInterface(%link, $CID_BUILDING);
      %linkComponent.setState($BuildingState::Destruction);
      %link = %link.getNextLinkObj();
   }
   
   // remove the path region
   %object.removePathRegion();
}

// this is called on every tick that the building is in its destruction state
function BuildingServer::onDestructionUpdate(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();

   if (%datablock.destructEffect !$= "" && %object.selectron && %component.timer)
   {
      %time = %component.timer.getElapsedTime();
      %checkTime = %datablock.destructionTime - %datablock.destructEffect.fadeOutTime;
      if (%time >= %checkTime)
      {
         %object.selectron.stopSelectron();
         %object.selectron = 0;
      }
   }
}

// this function is called when the destruction timer is up, handling the
// final destruction step and destroying the building permenantly
function BuildingServer::stopDestruction(%component)
{
   %object = %component.getObject();
   
   // stop the destruction effect
   stopEffect(%object, "destruct");
   
   if (%object.selectron)
   {
      %object.selectron.stopSelectron();
      %object.selectron = 0;
   }
   
   if (%object.isLinkObj() == true && %object.isFirstLinkObj() == false)
   {
      return;
   }
   
   if (%object.isLinkObj() == true)
   {
      %link = %object;
      while (isObject(%link) == true)
      {
         %linkComponent = slgQueryInterface(%link, $CID_BUILDING);
         %linkComponent.clearProps();
         %link = %link.getNextLinkObj();
      }
   }
   else
   {
      %component.clearProps();
      RemoveObject(%object);
   }

   %object.DeleteObject();
}


//*************************************************************
//           SERVER-SIDE BUILDING SOURCE CALLBACK METHODS
//*************************************************************
function BuildingServer::onDestroyMsg(%this, %param)
{
   // Get object list
   %objs = getWord(%param, 0);
         
   if (slgGetAttachedObjId(%this).isOfType("TownHall")) 
   {
      // Need to have a bank destroyed to update taxes
      if (%objs.containsType("Bank"))
      {
         // Update taxes
         %this.updateTaxes();
      }
   }
}

function BuildingServer::onQuitMsg(%this, %param)
{
   // Get object list
   %objs = getWord(%param, 0);
   
   // Need to have employment change for bank or town hall
   if (slgGetAttachedObjId(%this).isOfType("TownHall")) 
   {      
      if (%objs.containsType("Bank") || %objs.containsType("TownHall"))
      {
         // Update tax
         %this.updateTaxes();
      }
   }
}

// call to start fire on building
function BuildingServer::startFire(%this, %fire)
{
   // Apply fire to building
   if (%fire.applyFire(%this))
   {
      if (alertSvrDisasterHasAlert(%fire.getType()))
      {
         alertSvrAddObject($ALERT_FIRE, slgGetAttachedObjId(%this));
      }
   }
}

// called when a building first catches fire
function BuildingServer::onCatchFire(%component)
{
   %object = %component.getObjectId();
   startEffect(%object, "fire");
   
   %component.setStatus($BuildingStatus::Fire);
   
   // start the timer for burning the building further
   %fireDatablock = $DisasterManager.getDisasterDatablock($Disaster::Fire);
   %timer = new SLTimer()
   {
      time = %fireDatablock.burnTime;
   };
   %timer.notifyOnFire("onBurn", %component);
   %component.burnTimer = %timer;
}

function BuildingServer::onBurn(%component)
{
   if (%component.onFire() == false)
   {
      %component.burnTimer = 0;
      return;
   }
   
   %fireDatablock = $DisasterManager.getDisasterDatablock($Disaster::Fire);
   %strength = %component.getFireStrength();
   if (%strength < %fireDatablock.burnMax)
   {
      %component.catchFire(%fireDatablock.burnStrength);
   }

   %timer = new SLTimer()
   {
      time = %fireDatablock.burnTime;
   };
   %timer.notifyOnFire("onBurn", %component);
   %component.burnTimer = %timer;
}

// called when a building's fire is extinguished
function BuildingServer::onExtinguishFire(%component)
{
   %datablock = %component.getDatablock();
   %object = %component.getObjectId();
   stopEffect(%object, "fire");
   
   %component.clearStatus($BuildingStatus::Fire);
   
   if (isObject(%component.burnTimer) == true)
   {
      %component.burnTimer.delete();
   }
   
   // reset the production cycle if the building has one
   %count = %component.getPropCount();
   for (%index = 0; %index < %count; %index++)
   {
      // get the produce object
      %prop = %component.getProp(%index);
      
      // if the production growth is 0, then the produce should start
      // out fully matured (used for livestock)
      %scale = $SproutHeight;
      %produce = %component.getProductionName();
      if (%datablock.getProductionGrowth(%produce) == 0) %scale = 1.f;
      
      %prop.endTime = %datablock.getProductionGrowth(%produce);

      %scaleRate = 0;
      if (%prop.endTime <= 0)
      {
         %prop.setScale("1 1 1");
      }
      else
      {
         %prop.setScale(%scale @ " " @ %scale @ " " @ %scale);
         %scaleRate = (1 - %scale) / %prop.endTime;
      }
      %prop.setScaleRate(%scaleRate);
   }
}




// additional building functionality
function serverCmdCreateForPlacement(%client, %type, %name, %team)
{
   if (isObject(GameResourceStack) == false)
   {
      csCmdStatusFail(%client, $CSC_BUILD);
      return;
   }
   
   %gameResource = GameResourceStack.getResource();
   if (isObject(%gameResource) == false)
   {
      csCmdStatusFail(%client, $CSC_BUILD);
      return;
   }
   
   if (isObject($ObjectToPlace) == true)
   {
      csCmdStatusFail(%client, $CSC_BUILD);
      return;
   }
   
   %costGold = rsGetResourceCount($Resource::Gold);
   %costWood = rsGetResourceCount($Resource::Wood);
   
   if(%type.goldCost <= %costGold &&
      %type.woodCost <= %costWood)
   {
      %gameResource.getGold().decreaseCount(%type.goldCost);
      %gameResource.getWood().decreaseCount(%type.woodCost);
      
      // after the object is created, send the ghost to the client
      // to be attached to the cursor, which will tell the command
      // system that the building is ready for placement
      %object = CreateObject(%type, %name, %team);
      
      schedule(100, 0, "SendGhostToClient", %client, 'CreateForPlacement', %object);

      $ObjectToPlace = %object;
      $ClientToPlace = %client;
   }
   else 
   {
      // Output error string and notify command system of failure
      %bldgString = slgGetObjectString(%type);
      %errorString = slgGetUIString("id_errbuild_lack_resource");
      %goldString  = slgGetUIString("id_resource_gold");
      %woodString  = slgGetUIString("id_resource_wood");
      %errorString = slgFormatUIString(%errorString, %type.goldCost, %goldString, %type.woodCost, %woodString, %bldgString);
      csCmdStatusFail(%client, $CSC_BUILD, %errorString);
   }
}

// this function is called when placing the building has been cancelled
function serverCmdCancelBuilding(%client)
{
   if (isObject(GameTechManager))
   {
      GameTechManager.resetTech(%client);
   }
   
   if (isObject(GameResourceStack) == false)
   {
      return;
   }
   
   %gameResource = GameResourceStack.getResource();
   if (isObject(%gameResource) == false)
   {
      return;
   }
   
   if ($ObjectToPlace && $ClientToPlace == %client)
   {
      %gameResource.clearHappinessRates();
      %gameResource.getGold().increaseCount($ObjectToPlace.getDataBlock().goldCost);
      %gameResource.getWood().increaseCount($ObjectToPlace.getDataBlock().woodCost);

      $ObjectToPlace.DeleteObject();
   }
   
   $ObjectToPlace = 0;
   $ClientToPlace = 0;
}

// this is a helper function used to send the ghost id of a server
// object to a client's function when the ghost id is first available
function SendGhostToClient(%client, %function, %object)
{
   %ghostID = slgGetGhostIndex(%object);
   if (%ghostID != -1)
   {
      commandToClient(%client, %function, %ghostID);
   }
   else
   {
      schedule(100, 0, "SendGhostToClient", %client, %function, %object);
   }
}

// this function is called from the client to tell the attached object
// to the cursor to be rotated to the left by 90 degrees
function serverCmdRotateBuildingLeft(%client, %ghostID, %position)
{
   %object = %client.resolveObjectFromGhostIndex(%ghostID);
   if (isObject(%object) == false)
   {
      return;
   }
   
   %object.rotateZ(-90);
   if (%object.isLinkObj() == true)
   {
      %offset = %object.getCenterOffset();
      %xPos = getWord(%position, 0) - getWord(%offset, 0);
      %yPos = getWord(%position, 1) - getWord(%offset, 1);
      %position = %xPos @ " " @ %yPos @ " " @ getWord(%position, 2);
   }
   %object.setPosition(getWord(%position, 0),
      getWord(%position, 1), getWord(%position, 2));
}

// this function is called from the client to tell the attached object
// to the cursor to be rotated to the right by 90 degrees
function serverCmdRotateBuildingRight(%client, %ghostID, %position)
{
   %object = %client.resolveObjectFromGhostIndex(%ghostID);
   if (isObject(%object) == false)
   {
      return;
   }
   
   %object.rotateZ(90);
   if (%object.isLinkObj() == true)
   {
      %offset = %object.getCenterOffset();
      %xPos = getWord(%position, 0) - getWord(%offset, 0);
      %yPos = getWord(%position, 1) - getWord(%offset, 1);
      %position = %xPos @ " " @ %yPos @ " " @ getWord(%position, 2);
   }
   %object.setPosition(getWord(%position, 0),
      getWord(%position, 1), getWord(%position, 2));
}


// this function sends production information to the client to have
// the resource information appear above a target object's location
function SendProductionToClient(%object, %production)
{
   if (isObject(%object) == false)
   {
      return;
   }
   
   %client = ClientGroup.getObject(0);
   %ghostID = %client.getGhostID(%object);
   if (%ghostID == -1)
   {
      schedule(100, 0, "SendProductionToClient", %object, %production);
      return;
   }

   commandToClient(%client, 'CreateResourceInfo', %ghostID, %production);
}

// this function translates a long list of resources that the building
// can and has produced, eliminating the reosurces that were not
// produced
function BuildingServer::SendProduction(%component, %production, %consumeFactor)
{
   // set status for building
   if (%consumeFactor !$= "")
   {
      if (%consumeFactor < 1)
      {
         %component.setStatus($BuildingStatus::NeedWater);
      }
      else
      {
         %component.clearStatus($BuildingStatus::NeedWater);
      }
   }
   
   %gold = getWord(%production, 0);
   %wood = getWord(%production, 1);
   %water = getWord(%production, 2);
   %food = getWord(%production, 3);
   
   %data = "";
   if (%gold != 0)
   {
      %data = %data @ %gold @ " gold ";
   }
   if (%wood != 0)
   {
      %data = %data @ %wood @ " wood ";
   }
   if (%water != 0)
   {
      %data = %data @ %water @ " water ";
   }
   if (%food != 0)
   {
      %data = %data @ %food @ " food ";
   }
   
   %object = %component.getObjectId();
   SendProductionToClient(%object, %data);
}

function BuildingServer::deleteHouseTimer(%this)
{
   if (isObject(%this.housing) == true)
   {
      %this.housing.delete();
      %this.housing = 0;
      
      // notify clients of change
      %clCount = ClientGroup.getCount();
      for (%i = 0; %i < %clCount; %i++)
      {
         %client = ClientGroup.getObject(%i);
         %ghost  = %client.getGhostID(%this);
         commandToClient(%client, 'DestroyHouseBmpTimer', %ghost);
      }
   }
}

// BUILDING HELPER FUNCTIONS
function PlaceConstructionDecal(%object, %datablock)
{
   // render the building's construction decal (if there is a decal to add)
   if (%datablock.constructDecal !$= "")
   {
      // create the selectron and scale it for this particular
      // construction site
      %selectron = startSelectron(%object, %datablock.constructDecal.selectionTypeStyle);
      if (%selectron != 0)
      {
         %selectron.zodiacScale = %datablock.constructionScale;
         %selectron.zodiacOffsetDynamic = %datablock.constructionOffset;
         %selectron.zodiacAngleDynamic = true;
         %selectron.addConstraint(%object, "construction");
      }
      // temporary selectron variable is used to remove the decal later
      %object.selectron = %selectron;
   }
}

function PlaceDestructionDecal(%object, %datablock)
{
   // render the building's construction decal
   if (%datablock.destructDecal !$= "")
   {
      %selectron = startSelectron(%object, %datablock.destructDecal.selectionTypeStyle);
      if (%selectron != 0)
      {
         %selectron.zodiacScale = %datablock.destructionScale;
         %selectron.zodiacOffsetDynamic = %datablock.destructionOffset;
         %selectron.addConstraint(%object, "destruction");
      }
      %object.selectron = %selectron;
   }
}

function PlaceConstructionProps(%component, %object, %datablock)
{
   %useObjList = (%object.isLinkObj() == true && %object.linkDirection == 0);
   %index = 0;
   %count = %datablock.getConstructionPropCount();
   if (%useObjList == true) %index = %datablock.constructionPropCount;
   else if (%object.isLinkObj() == true) %count = %datablock.constructionPropCount;

   // place the buiding's construction props by iterating through the
   // prop list, creating the prop, offsetting it, and adding it to
   // the prop list (for prop removal later)
   for (%index; %index < %count; %index++)
   {
      %offset = %datablock.getConstructionPropOffset(%index);
      %rotation = %datablock.getConstructionPropRotation(%index);
      %scale = %datablock.getConstructionPropScale(%index);
      %file = %datablock.getConstructionPropFile(%index);
      %level = %datablock.getConstructionPropLevel(%index);
      
      %offset = %object.transformPoint(getWord(%offset, 0), getWord(%offset, 1));

      // offset the prop's position
      %xPos = getWord(%offset, 0);
      %yPos = getWord(%offset, 1);
      %position = %xPos @ " " @ %yPos @ " " @ getWord(%component.constructionPosition, 2);

      // create a prop
      if (%useObjList == true && %index >= %datablock.constructionPropCount)
      {
         %prop = new SLGameObj()
         {
            position = %position;
            rotation = "0 0 1 0";
            scale = %scale @ " " @ %scale @ " " @ %scale;
            datablock = %file;
         };
         %prop.rotateZ(getWord(%object.rotation, 3) + %rotation);
         PlaceObject(%prop);
      }
      else
      {
         %prop = new TSStatic()
         {
            position = %position;
            rotation = "0 0 1 " @ getWord(%object.rotation, 3) + %rotation;
            scale = %scale @ " " @ %scale @ " " @ %scale;
            shapeName = %file;
         };
      }

      // add the created prop to the prop list
      %component.addProp(%prop, %level);
   }
   %component.updateProps($pref::LevelOfDetail);
}

function PlaceDestructionProps(%component, %object, %datablock)
{
   // place the buiding's construction prop
   for (%index = 0; %index < %datablock.getDestructionPropCount(); %index++)
   {
      %offset = %datablock.getDestructionPropOffset(%index);
      %rotation = %datablock.getDestructionPropRotation(%index);
      %scale = %datablock.getDestructionPropScale(%index);
      %file = %datablock.getDestructionPropFile(%index);
      %level = %datablock.getDestructionPropLevel(%index);
      
      %offset = %object.transformPoint(getWord(%offset, 0), getWord(%offset, 1));

      // offset the prop's position
      %xPos = getWord(%offset, 0);
      %yPos = getWord(%offset, 1);
      %position = %xPos @ " " @ %yPos @ " " @ getWord(%object.position, 2);

      //%object.constructionProp = new TSStatic()
      %prop = new TSStatic()
      {
         canSaveDynamicFields = "1";
         position = %position;
         rotation = "0 0 1 " @ getWord(%object.rotation, 3) + %rotation;
         scale = %scale @ " " @ %scale @ " " @ %scale;
         shapeName = %file;
      };
      %component.addProp(%prop, %level);
   }
   %component.updateProps($pref::LevelOfDetail);
}

function UpgradeBuilding(%component, %type)
{
   %oldCmpBldg = %component;
   %building  = slgGetAttachedObjId(%component);
   slgSendObjDestroyMsgLocal(%building);
   
    // create new building
   %upgrade = CreateSaveObject(%type, %building.getName(), %building.getTeam());
   if (!isObject(%upgrade)) 
   {
      return;
   }
   // this should be a building
   %newCmpBldg = slgQueryInterface(%upgrade, $CID_BUILDING);
   if (!isObject(%newCmpBldg)) 
   {
      %upgrade.DeleteObject();
      return;
   }
   
   %rotation = %building.getZRotation();
   %upgrade.rotateZ(%rotation);
   
   // save position of new object
   %pos = %building.getPosition();
   %upgrade.setPosition(getWord(%pos, 0), getWord(%pos, 1), getWord(%pos, 2));
   
   // set state of new object
   %newCmpBldg.setState(%oldCmpBldg.getState());
   
   // set health of new object
   %upgrade.health = %upgrade.getMaxHealth();
   
   // do not show upgrade until old building is gone
   %upgrade.showMesh(false);
   
   // to prevent future calls to this function
   %oldCmpBldg.upgraded = true;
   
   // handle the footprint swap
   RemoveObject(%building);
   PlaceObject(%upgrade);
   
   // repopulate hud
   %clCount = ClientGroup.getCount();
   for (%i = 0; %i < %clCount; %i++)
   {
      %client = ClientGroup.getObject(%i);
      commandToClient(%client, 'RemoveFromSelection', %client.getGhostID(%building));
   }
   ServerSelectObject(%upgrade);
      
   // we have to wait until the new building is in the production state
   schedule(100, 0, "checkForPeopleSwap", %building, %upgrade);
}

function ServerSelectObject(%object)
{
   %client = ClientGroup.getObject(0);
   %ghostID = %client.getGhostID(%object);
   
   if (%ghostID != -1)
   {
      commandToClient(%client, 'SelectObject', %ghostID);
      return;
   }
   
   schedule(100, 0, ServerSelectObject, %object);
}

// each parameter is the number of that produce that is currently
// in production (farms and ranches are guaranteed to have
// employees and this function will always be called whenever
// these values change)
function UpdateProduction(%wheat, %corn, %pumpkins, %chickens, %pigs, %cows, %sheep)
{
   // if the general store buttons have not been activated yet, skip
   if (isObject(GeneralStorePumpkin) == false)
   {
      return;
   }
   
   // check if pumpkin pie is enabled
   if (GeneralStorePumpkin.isAvailable() == true &&
      %wheat >= 2 && %pumpkins >= 2)
   {
      // enable the pumpkin pie button
      tsEnableCommand(PumpkinpieCmdData, true);
      
      // enable the pumpkin pie vertex
      TaskModifier.strMarkVertex("GeneralStore", "GeneralStorePumpkin", $TSV_CMPLETE);
      
      // if this is the first resource for the general store, restart profit
      if (GeneralStoreProfit.isAvailable() == true &&
         GeneralStoreHoneyCreate.isComplete() == false &&
         GeneralStoreSheep.isComplete() == false)
      {
         TaskModifier.strResetVertex("GeneralStore", "GeneralStoreRestart");
         TaskModifier.strMarkVertex("GeneralStore", "GeneralStoreRestart", $TSV_AVAIL);
      }
   }
   // check if pumpkin pie is disabled
   else if (GeneralStorePumpkin.isComplete() == true &&
      (%wheat < 2 || %pumpkins < 2))
   {
      // disable the pumpkin pie button
      tsEnableCommand(PumpkinpieCmdData, false);
      
      // disable the pumpkin pie vertex
      TaskModifier.strResetVertex("GeneralStore", "GeneralStorePumpkin");
      TaskModifier.strMarkVertex("GeneralStore", "GeneralStorePumpkin", $TSV_AVAIL);
   }
   
   // check if wool is enabled
   if (GeneralStoreSheep.isAvailable() == true && %sheep >= 2)
   {
      // enable the wool button
      tsEnableCommand(woolCmdData, true);
      
      // enable the wool vertex
      TaskModifier.strMarkVertex("GeneralStore", "GeneralStoreSheep", $TSV_CMPLETE);
      
      // if this is the first resource for the general store, restart profit
      if (GeneralStoreProfit.isAvailable() == true &&
         GeneralStoreHoneyCreate.isComplete() == false &&
         GeneralStorePumpkin.isComplete() == false)
      {
         TaskModifier.strResetVertex("GeneralStore", "GeneralStoreRestart");
         TaskModifier.strMarkVertex("GeneralStore", "GeneralStoreRestart", $TSV_AVAIL);
      }
   }
   // check if wool is disabled
   else if (GeneralStoreSheep.isComplete() == true && %sheep < 2)
   {
      // disable the wool button
      tsEnableCommand(woolCmdData, false);
      
      // disable the wool vertex
      TaskModifier.strResetVertex("GeneralStore", "GeneralStoreSheep");
      TaskModifier.strMarkVertex("GeneralStore", "GeneralStoreSheep", $TSV_AVAIL);
   }
}

function BuildingServer::onRemove(%component)
{
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   // if the object could not be found, we cannot look-up team
   if (isObject(%object) == false)
   {
      return;
   }
   
   // if the game resource does not exist, nothing needs to be updated
   if (isObject(GameResourceStack) == false)
   {
      return;
   }
    
   // if the game resources are not found, nothing can be updated
   %gameResource = GameResourceStack.getResource();
   if (isObject(%gameResource) == false)
   {
      return;
   }
   
   // remove max resources if the building is on the player
   // team and the building is in the production state
   if (%object.getTeam() == $OST_PLAYER &&
      %component.getState() == $BuildingState::Production)
   {
      %gameResource.getWater().decreaseMax(%datablock.produceWaterCap);
      %gameResource.getFood().decreaseMax(%datablock.produceFoodCap);
      %gameResource.getPeople().forceDecreaseMax(%component.getTenantMax());
   }
}

// each function to use to update the health bar on the client
function serverUpdateHealthBar(%object, %tenantCount)
{
   if (isObject(%object) == true)
   {
      %client = ClientGroup.getObject(0);
      %ghostID = %client.getGhostId(%object);
      commandToClient(%client, 'updateHealthBar', %ghostID,
         %object.getTeam(), %tenantCount);
   }
}

function BuildingServer::OnAddToTeam(%component)
{
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   // update the health bar
   serverUpdateHealthBar(%object, %component.getTenantCount());
   
   // set the default production
   %component.produce = %datablock.produce;
   
   if (%component.getState() != $BuildingState::Production)
   {
      return;
   }
   
   if (isObject(GameResourceStack) == false)
   {
      return;
   }
   
   %resource = GameResourceStack.getResource();
   %resource.getWater().increaseMax(%datablock.produceWaterCap);
   %resource.getFood().increaseMax(%datablock.produceFoodCap);
   %resource.getPeople().increaseMax(%component.getTenantMax());
   
   // update the tech tree
   %conn = ClientGroup.getObject(0);
   %objectDatablock = %object.getDataBlock();
   GameTechManager.updateTech(%conn, "BldgTechTree");
   
   // update happiness based on building count
   %buildingCount = %component.getDuplicateCount() - 1;
   %happiness = %datablock.getBuildHappiness(%buildingCount);
   %resource = GameResourceStack.getResource();
   %resource.getHappiness().increaseCount(%happiness);
   modifyHappyModValueSvr($HMOD_BUILDINGBLDG, %happiness);
   
   // we want production to continue updating happiness
   %component.updateProduce = true;
}

function BuildingServer::OnRemoveFromTeam(%component)
{
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   // update the health bar
   serverUpdateHealthBar(%object, %component.getTenantCount());
   
   if (%component.getState() != $BuildingState::Production)
   {
      return;
   }
   
   if (isObject(GameResourceStack) == false)
   {
      return;
   }
   
   // Remove all characters working, living, and hiding there
   while (%component.getEmployeeCount() > 0)
   {
      %employee = %component.getEmployee(0);
      slgUnemployObject(%employee);
   }
   while (%component.getTenantCount() > 0)
   {
      %tenant = %component.getTenant(0);
      slgUnhouseObject(%tenant);
   }
   while (%component.getHiderCount() > 0)
   {
      %hider = %component.getHider(0);
      slgUnhideObject(%hider);
   }
   
   // if the door is open, close the door
   while (%component.doorOpenCount > 0)
   {
      %component.doorOpenCount = 0;
      %component.playThread(1, "closedoor");
   }
   
   // update resource maxes
   %resource = GameResourceStack.getResource();
   %resource.getWater().decreaseMax(%datablock.produceWaterCap);
   %resource.getFood().decreaseMax(%datablock.produceFoodCap);
   %tenantMax = %component.getTenantMax();
   %resource.getPeople().forceDecreaseMax(%tenantMax);
   
   %damageHappiness = %datablock.getDamageHappiness(100);
   if (%damageHappiness != %component.damageHappiness)
   {
      %happiness = %resource.getHappiness();
      %difference = %damageHappiness - %component.damageHappiness;
      %happiness.increaseCount(%difference);
      
      // update stat gui happiness as well
      modifyHappyModValueSvr($HMOD_DAMAGEBLDG, %difference);
      
      %component.damageHappiness = %damageHappiness;
      
      // notify client of change
      %clientCount = ClientGroup.getCount();
      for (%i = 0; %i < %clientCount; %i++)
      {
         %client   = ClientGroup.getObject(%i);
         %comGhost = %client.getGhostID(%component);
         commandToClient(%client, 'OnDamageHappinessChange', %comGhost, %damageHappiness);
      }
      if (%damageHappiness == 0)
      {
         %component.clearStatus($BuildingStatus::Damaged);
         
      }
      else
      {
         %component.setStatus($BuildingStatus::Damaged);
      }
      
      SendProductionToClient(%object, %difference @ " happiness");
   }
   
   // update the tech tree
   %conn = ClientGroup.getObject(0);
   %objectDatablock = %object.getDataBlock();
   GameTechManager.updateTech(%conn, "BldgTechTree");
   
   // update happiness based on building count
   %buildingCount = %component.getDuplicateCount() - 1;
   %happiness = %datablock.getBuildHappiness(%buildingCount);
   %resource = GameResourceStack.getResource();
   %resource.getHappiness().increaseCount(-%happiness);
   modifyHappyModValueSvr($HMOD_BUILDINGBLDG, -%happiness);
}

function BuildingServer::onStatusChange(%this, %stat, %set)
{
   %object = slgGetAttachedObjId(%this);
   
   if(%stat == $BuildingStatus::NoGold
   || %stat == $BuildingStatus::NoWood) {
      // Fire all employees if true
      if(%set) {
         // Target buildings will switch their statuses to no gold or no wood
         // after the first employee is hired there and there is no gold or wood
         // in its radius. Due to this, an added if check was necessary (employ
         // is being called, and the character hasn't been hired yet when this 
         // method is called). We do not wish to fire the employee in this case.
         if(!%object.employing) {
            while(%this.getEmployeeCount() > 0) {
               slgUnemployObject(%this.getEmployee(0));
            }
         }
         
         if(%stat == $BuildingStatus::NoWood) {
            %object.mountImage(forestdepletesign, 0);
         }
         if(%stat == $BuildingStatus::NoGold) {
            %object.mountImage(golddepletedsign, 0);
         }
      }
      
      // Remove mounts
      else {
         %object.unmountImage(0);
      }
   }
}

function BuildingServer::RemoveTenant(%component)
{
   %object = slgGetAttachedObjId(%component);
   %datablock = %component.getDataBlock();
   
   // if the respawn timer does not exist, nothing needs to be done
   if (%datablock.respawnTime <= 0)
   {
      return;
   }
   
   %client = ClientGroup.getObject(0);
   %ghostID = %client.getGhostID(%component);
   
   // if the housing clock already exists, update it
   if (isObject(%component.housing))
   {
      // update the house timer
      %component.housing.time += %datablock.respawnTime;
      commandToClient(%client, 'DestroyHouseBmpTimer', %ghostID);
      commandToClient(%client, 'CreateHouseBmpTimer', %ghostID,
         %component.housing.time, %component.housing.getElapsedTime(),
         %component.getTenantCount());
   }
   // if the housing close does not exist, create it
   else
   {
      // this is happiness-based time
      %time = GameResourceStack.getHouseTime();
      
      // create the housing clock that will be used
      %component.housing = new SLTimer()
      {
         time = %time + %datablock.housingOffset + %datablock.respawnTime;
      };
      %component.housing.notifyOnFire(addNewTenant, %component);
      
      // create client timer
      commandToClient(%client, 'CreateHouseBmpTimer', %ghostID,
         %component.housing.time, 0, %component.getTenantCount());
   }
}

// award the 3 unique ranches badge
function AwardRanchProduceBadge()
{
   tsAwardBadge("badge_rancher");
}

// award the 3 unique farms badge
function AwardFarmProduceBadge()
{
   tsAwardBadge("badge_farmer");
}

// award the 6 unique produce items badge
function AwardAllProduceBadge()
{
   tsAwardBadge("badge_foodking");
}

// award for creating a ranch with sheep
function AwardSheepBadge()
{
   tsAwardBadge("badge_sheep");
}

function areTeamBuildingsOnFire(%team)
{
   // Get buildings on team
   %teamlist  = new SLGameObjList();
   %bldgs     = slgGetServerBuildingList();
   %bldgs.getTeamList(%team, %teamlist);
   
   // Check for any building on fire
   %onFire = false;
   %count = %teamlist.getSize();
   for(%i = 0; %i < %count; %i++) {
      %bldgcmp = slgQueryInterface(%teamlist.getID(%i), $CID_BUILDING);
      if(%bldgcmp.onFire()) {
         %onFire = true;
         break;
      }
   }
   
   %teamlist.delete();
   return %onFire;
}
