//------------------------------------------------------------
// Copyright Sandlot Games, 2007
// author: Michael Felice
// file: client_building.cs
// brief:
//    This file contains building state functionality on the
//    client side.  Anything that would modify the building's
//    look that does not affect resources or any other server-
//    based data should be placed here.  The more logic on the
//    client side, the better.
//------------------------------------------------------------


$BuildingBuryOffset = 1;

function BuildingClient::onNoStateUpdate(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);
   %datablock = %component.getDataBlock();
   
   // link object
   %bIsLink = %object.isLinkObj();
   if (%bIsLink && $DoneLoading && $MissionLoaded)
   {
      if (slgIsBeingConstructed("Bridge"))
      {
         // disable bridge build button
         if (!BridgeButton.disabled)
         {
            BridgeButton.disabled = true;
         }
      }
   }
}


//*************************************************************
//           CLIENT-SIDE BUILDING CONSTRUCTION STATES
//*************************************************************

// this function is called when the client-side construction
// timer has finished running (building states are handled
// on the server-side ONLY, but the timer is zeroed here
// since it is no longer valid)
function BuildingClient::stopConstruction(%component)
{
   %component.timer = 0;
}

// this function is called when the building's construction
// is started and initializes a client-side construction
// timer, which is used to move the buildings on the client
// (causing the buildings to rise from the ground)
function BuildingClient::onConstructionEnter(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);
   %datablock = %component.getDataBlock();
   
   if (%object.linkStart == true)
   {
      %object.linkCount = %object.getLinkCount() - 1;
      %object.linkHealth = 0;
      
      %linkDirection = 1;
      if (%object.isLastLinkObj() == true)
      {
         %linkDirection = -1;
         %object = %object.getFirstLinkObj();
      }
      
      // play construction sound if builder or this building is selected
      if (isSelected(%this.builder))
      {
         playConstructionSound();
      }
      else if (isSelected(%object))
      {
         playConstructionSound();
      }
      
      while (isObject(%object) == true)
      {
         %object.linkDirection = %linkDirection;
         %object = %object.getNextLinkObj();
      }
      
      return;
   }

   // if the construction will end at a certain time, set up
   // the timer for this progression
   if (%datablock.constructionTime)
   {
      %component.timer = new SLTimer()
      {
         time = %datablock.constructionTime;
      };
      %component.timer.notifyOnFire(stopConstruction, %component);
   }

   if (%object.isLinkObj() == false)
   {
      // initialize the construction position
      %position = %object.position;
      %component.constructionPosition = %object.position;

      // 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 = %object.position;
      %zMax = %object.getHeight() + $BuildingBuryOffset;
      %zPos = getWord(%position, 2) + %zMax;
      %component.constructionPosition = getWord(%position, 0) @ " " @ getWord(%position, 1) @ " " @ %zPos;
      
      // create the health clock for the object
      CreateHealthClock(%object);
      
      // play construction sound if builder or this building is selected
      if (isSelected(%this.builder))
      {
         playConstructionSound();
      }
      else if (isSelected(%object))
      {
         playConstructionSound();
      }
   }
}

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

   if (%object.isLinkObj() == false)
   {
      // update the position of the object on the client (this is used
      // to raise the building from under the ground, and it is on the
      // client because the actual position of the buiding doesn't
      // change, it only rises-- and because it's animated, it's faster
      // on the client)
      if (%datablock.constructionPresentStart && %component.timer)
      {
         %time = %component.timer.getElapsedTime();
         %start = %datablock.constructionPresentStart;
         
         // check if the building should be rising from the ground
         if (%start <= %time)
         {
            // if no end time is provided, the animation will end when the
            // building is done constructing; otherwise, it will end when
            // the construction end time is specified
            %end = %datablock.constructionTime;
            if (%datablock.constructionPresentStop)
            {
               %end = %datablock.constructionPresentStop;
            }
            
            // determine the interpolated factor for the animation
            %offset = (%time - %start) / (%end - %start);
            if (%offset > 1) %offset = 1;

            // update the object's new position
            %position = %component.constructionPosition;         
            %zMax = %object.getHeight() + $BuildingBuryOffset;
            %zPos = getWord(%position, 2);
            %zPos = %zPos - %zMax + %offset * %zMax;
            %object.setPosition(getWord(%position, 0), getWord(%position, 1), %zPos);
         }
      }
   }
   
   // We are a link object   
   else
   {
      // disable bridge build button
      if (!BridgeButton.disabled)
      {
         BridgeButton.disabled = true;
      }
   }

   // update the health of the building (this is a visual effect because
   // the health bar will be displayed over the building, representing
   // this value (the construction will always end at full health-- set on
   // the server-side)
   if (%datablock.constructionTime && isObject(%component.timer) == true)
   {
      // find out how much health is available based on the construction time
      %percentage = %component.timer.getElapsedTime() / %datablock.constructionTime;
      if (%percentage > 1) %percentage = 1;

      // set the building's health
      %object.health = %percentage * (%object.getDataBlock().healthMax + %object.healthMaxMod);
      
      if (%object.isLinkObj() == true)
      {
         %start = %object.getFirstLinkObj();
         %linkCount = %start.getLinkCount() - 1;
         if (%start.linkStart == false)
         {
            %start = %object.getLastLinkObj();
         }
         
         %start.linkCount = %linkCount;
         %start.health = %start.linkHealth + %object.health / %start.linkCount;
      }
   }
}

// this function is called when the building finishes construction
function BuildingClient::onConstructionExit(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);
   %datablock = %component.getDataBlock();
   
   // the object should be at full health here (this is for life bar
   // visualization-- note that the server will set the health to 100%
   // as well, which will match server-side and client-side building
   // healths)
   %object.health = %object.getDataBlock().healthMax + %object.healthMaxMod;

   if (%object.isLinkObj() == true && %object.linkStart == false)
   {
      %start = %object.getFirstLinkObj();
      if (%start.linkStart == false)
      {
         %start = %object.getLastLinkObj();
      }
      
      %start.linkHealth += %object.health / %start.linkCount;
      %start.health = %start.linkHealth;
      return;
   }

   // since the building is done, place the road texture
   if (%object.isLinkObj() == false)
   {
      %object.placeTexture($Texture::Road);
   }
   DestroyHealthClock(%object);
}



//*************************************************************
//           CLIENT-SIDE BUILDING PRODUCTION STATES
//*************************************************************

// this function is called when the building enters the production state
function BuildingClient::onProductionEnter(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   DestroyHealthClock(%object);
   CreateHealthBar(%object);
   
   // update building panel (building happiness might have changed)
   if (isSelected(%object))
   {
      csUpdateBldgSelectionPanelStatus(%this, %object);
   }
   
   slgUpdateLocatorButtons();
   
   // Send object create message
   slgSendObjCreateMsg(%object, true);
   
   if (%object.isLinkObj()) 
   {
      if (%object.isFirstLinkObj())
      {
         playBuildingCompleteSound(%object);
         stopConstructionSound();
      }
   }
   else
   {
      playBuildingCompleteSound(%object);
      stopConstructionSound();
   }
}

// this function is called on every update tick that the building is
// in the production state
function BuildingClient::onProductionUpdate(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   // NOTE: the happiness-based updates are now happening on the
   // server with an update function call to the house timer,
   // which is only happening every time the house timer changes
   // as the result of a happiness change
}

// this function is called when the building leaves the production state
function BuildingClient::onProductionExit(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
}



//*************************************************************
//           CLIENT-SIDE BUILDING DESTRUCTION STATES
//*************************************************************

// this function is called when the building enters the destruction state
function BuildingClient::onDestructionEnter(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);   
   %datablock = %component.getDataBlock();
   
   DestroyHealthBar(%object);
   
   // destroy house bitmap timer
   if (isObject(%this.houseTimer)) 
   {
      %this.houseTimer.delete();
      %this.houseTimer = 0;
   }
   
   // update building panel if selected (building happiness might have changed)
   if (isSelected(%object))
   {
      csUpdateBldgSelectionPanelStatus(%this, %object);
   }
   slgUpdateLocatorButtons();
   
   // Remove from selection and disable collision
   removeFromSelection(%object);
   %object.disableCollision();
}

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


//*************************************************************
//           CLIENT-SIDE BUILDING CALLBACKS
//*************************************************************

// this is called when a hider has entered or left the building
function BuildingClient::onHiderChange(%this)
{
}

// this is called when a tenant has appeared or left the building
function BuildingClient::onTenantChange(%this)
{
   // update building selection panel if necessary
   %object = slgGetAttachedObjId(%this);
   if (isSelected(%object))
   {
      csUpdateBldgSelectionPanelEH(%object);
   }
}

// this is called when an employee has been hired or quit
function BuildingClient::onEmployeeChange(%this)
{
   // update building selection panel if necessary
   %object = slgGetAttachedObjId(%this);
   if (isSelected(%object))
   {
      csUpdateBldgSelectionPanelEH(%object);
      csUpdateBldgSelectionPanelPU(%this);
   }
}

// this is called when the last produce value has changed
function BuildingClient::onLastProduceChange(%this)
{
   %object = slgGetAttachedObjId(%this);
   if (isSelected(%object))
   {
      csUpdateBldgSelectionPanelPU(%this);
   }
}

// this is called when the last consume value has changed
function BuildingClient::onLastConsumeChange(%this)
{
   %object = slgGetAttachedObjId(%this);
   if (isSelected(%object))
   {
      csUpdateBldgSelectionPanelPU(%this);
   }
}

// this is called when the status of the building has been changed
function BuildingClient::onStatusChange(%this, %stat, %set)
{
   %object = slgGetAttachedObjId(%this);
   if (isSelected(%object))
   {
      csUpdateBldgSelectionPanelStatus(%this, %object);
   }
   
   // Pop messages to alert hud if true
   if(%set) {
      if(%stat == $BuildingStatus::NoGold) {
         %msg = slgGetUIString("id_gold_deplete");
         %msg = slgFormatUIString(%msg, %object.name);
         alertPushMsg(%msg);
      }
      else if(%stat == $BuildingStatus::NoWood) {
         %msg = slgGetUIString("id_wood_deplete");
         %msg = slgFormatUIString(%msg, %object.name);
         alertPushMsg(%msg);
      }
   }
}

// this is called when the bitmap timer for creating a settler from a house ends
function BuildingClient::onHouseBmpTimerEnd(%this)
{
   // Clear timer
   %this.houseTimer = 0;
}

// additional building functionality
function clientCmdCreateForPlacement(%ghostID)
{
   csEndWaitForCmd();
   %object = slgResolveGhost(%ghostID);
   if (isObject(%object) == true)
   {
      %placerList = gSelection.getSelectedGroup($SELECT_ID);
      if (%placerList.getSize() == 0)      
      {
         return;
      }
      %placer = %placerList.getId(0);
      PlayGui.attachObject(%placer, %object);
      
      %object.clearHighlight();
      csCmdOnButton($CSC_BUILD);
      
      // clear all rotation buttons
      BuildingRotate1.visible = true;
      BuildingRotate2.visible = true;
      BuildingRotate3.visible = true;
      BuildingRotate1.disabled = true;
      BuildingRotate2.disabled = true;
      BuildingRotate3.disabled = true;
      
      // get the building component to determine which direction
      // the building can be rotated in
      %building = slgQueryInterface(%object, $CID_BUILDING);
      if (isObject(%building) == true)
      {
         %direction = %building.getFacingDir();
         if (%direction != 0)
         {
            BuildingRotate1.disabled = false;
            BuildingRotate2.disabled = false;
            BuildingRotate3.disabled = false;
            
            BuildingRotate1.direction = %direction;
            BuildingRotate2.direction = %direction;
            BuildingRotate3.direction = %direction;
         }
         
         /*
         %type = %object.getType();
         if (%type $= "Woodhut")
         {
            %selectron = startSelectron(%object, $RING_EFFECT_ID);
            if (isObject(%selectron) == true)
            {
               %selectron.zodiacScale = WoodhutData.targetRadius;
               %selectron.addConstraint(%object, "ring");
               %object.ring = %selectron;
            }
         }
         */
      }
      
      // special case for bridge - disable build button
      if (%object.isOfType("Bridge"))
      {
         BridgeButton.disabled = true;
      }
   }
}

function clientCmdRemoveFromCursor(%ghostID)
{
   %object = PlayGui.removeObject();
   %object.clearHighlight();
   if (isObject(%object.ring) == true)
   {
      %object.ring.stopSelectron();
      %object.ring = 0;
   }
   
   BuildingRotate1.visible = false;
   BuildingRotate2.visible = false;
   BuildingRotate3.visible = false;
   
   if (%object.isLinkObj() == true)
   {
      %ghostID = ServerConnection.getGhostID(%object);
      clientCmdRemoveAllClientNodes(%ghostID);
   }
   
   csUpdateCursor($CSC_NONE);
   csEndWaitForCmd();
}

function clientCmdDisableCollision(%ghostID)
{
   %object = slgResolveGhost(%ghostID);
   if (isObject(%object) == true && %object.isCollisionEnabled() == true)
   {
      %object.disableCollision();
   }
}

function clientCmdEnableCollision(%ghostID)
{
   %object = slgResolveGhost(%ghostID);
   if (isObject(%object) == true)
   {
      %object.enableCollision();
   }
}

// this is called when the building should be rotated to the left
function LeftBuildingSelect()
{
   %object = PlayGui.getObject();
   commandToServer('RotateBuildingLeft', ServerConnection.getGhostID(%object), %object.position);
}

// this is calledwhen the building should be rotated to the right
function RightBuildingSelect()
{
   %object = PlayGui.getObject();
   commandToServer('RotateBuildingRight', ServerConnection.getGhostID(%object), %object.position);
}

// rotates the attached building depending on which rotate button
// is available for selection (if none are available, nothing happens)
function shortcutBuildingRotate()
{
   if (BuildingRotate1.disabled == false)
   {
      BuildingRotate1.buttonSelect();
   }
   else if (BuildingRotate2.disabled == false)
   {
      BuildingRotate2.buttonSelect();
   }
   else if (BuildingRotate3.disabled == false)
   {
      BuildingRotate3.buttonSelect();
   }
}

function BuildingRotate1::buttonSelect(%button)
{
   if (%button.direction > 0) LeftBuildingSelect();
   else RightBuildingSelect();
   
   %button.direction = -%button.direction;
}

function BuildingRotate2::buttonSelect(%button)
{
   if (%button.direction > 0) LeftBuildingSelect();
   else RightBuildingSelect();
   
   %button.direction = -%button.direction;
}

function BuildingRotate3::buttonSelect(%button)
{
   if (%button.direction > 0) LeftBuildingSelect();
   else RightBuildingSelect();
   
   %button.direction = -%button.direction;
}


function clientCmdSetLinkStart(%ghostID)
{
   %object = ServerConnection.resolveGhostId(%ghostID);
   %object.linkStart = true;
}

// tests if the building is disabled for any reason
function BuildingClient::isDisabled(%this)
{
   // must be in production state
   if (%this.getState() != $BuildingState::Production)
   {
      return true;
   }
   
   // default is false
   return false;
}

// Called when the damage happiness for a building changes on the server
// (this updates client object)
function clientCmdOnDamageHappinessChange(%comGhost, %damageHappiness)
{
   %component = ServerConnection.resolveGhost(%comGhost);
   %component.damageHappiness = %damageHappiness;
}
