//------------------------------------------------------------
// Copyright Sandlot Games, 2007
// author: Michael Felice
// file: svr_savebuilding.cs
// brief:
//    This handles the saving and loading of buildings through
//    script, ensuring that the dynamic fields of objects and
//    timers are appropriately saved and initialized on load.
//------------------------------------------------------------


// SAVING BUILDINGS
function BuildingServer::SaveToFile(%component)
{
   %object = %component.getObjectId();
   %datablock = %component.getDataBlock();
   %state = %component.getState();
   
   // the link object has specific construction direction information
   // to save and load (the values will have to be sent to the other
   // links based on the direction that the bridge is being built in)
   if (%object.isLinkObj() == true)
   {
      %angle = %object.getAngle();
      slgSaveFloat(%angle);
      
      if (%object.linkStart == false && %object.getTeam() == $OST_PLAYER)
      {
         %object = %object.getLastLinkObj();
         %component = slgQueryInterface(%object, $CID_BUILDING);
         %datablock = %component.getDataBlock();
         %state = %component.getState();
      }
      
      slgSaveInt(%object.linkStartDirection);
   }
   
   // handle building no state save information
   if (%state == $BuildingState::NoState)
   {
      %component.SaveNoStateToFile(%object, %datablock);
   }
   // handle building construction save information
   else if (%state == $BuildingState::Construction)
   {
      %component.SaveConstructionToFile(%object, %datablock);
   }
   // handle building production save information
   else if (%state == $BuildingState::Production)
   {
      %component.SaveProductionToFile(%object, %datablock);
   }
   // handle building destruction save information
   else if (%state == $BuildingState::Destruction)
   {
      %component.SaveDestructionToFile(%object, %datablock);
   }
}

// construction x position
// construction y position
// construction z position
function BuildingServer::SaveNoStateToFile(%component, %object, %datablock)
{
   if (%object.isLinkObj() == true)
   {
      %link = %object.getFirstLinkObj();
      slgSaveFloat(getWord(%link.position, 0));
      slgSaveFloat(getWord(%link.position, 1));
      slgSaveFloat(getWord(%link.position, 2));
   }
   else
   {
      slgSaveFloat(getWord(%component.constructionPosition, 0));
      slgSaveFloat(getWord(%component.constructionPosition, 1));
      slgSaveFloat(getWord(%component.constructionPosition, 2));
   }
}

// construction x position
// construction y position
// construction z position
// has timer
//    if has timer, elapsed timer time
function BuildingServer::SaveConstructionToFile(%component, %object, %datablock)
{
   slgSaveFloat(getWord(%component.constructionPosition, 0));
   slgSaveFloat(getWord(%component.constructionPosition, 1));
   slgSaveFloat(getWord(%component.constructionPosition, 2));
   slgSaveFloat(getWord(%object.position, 0));
   slgSaveFloat(getWord(%object.position, 1));
   slgSaveFloat(getWord(%object.position, 2));

   %hasTimer = isObject(%component.timer);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      slgSaveFloat(%component.timer.getElapsedTime());
   }
}

// damage happiness
// has timer
//    if has timer, elapsed timer time (production)
// has timer
//    if has timer, total timer time (new tenants)
//    if has timer, elapsed timer time (new tenants)
// gunslinger data (3)
function BuildingServer::SaveProductionToFile(%component, %object, %datablock)
{
   slgSaveFloat(%component.damageHappiness);
   slgSaveBool(%component.updateProduce);

   %hasTimer = isObject(%component.timer);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      slgSaveFloat(%component.timer.getElapsedTime());
   }

   %hasTimer = isObject(%component.housing);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      slgSaveFloat(%component.housing.getTotalTime());
      slgSaveFloat(%component.housing.getElapsedTime());
   }
   
   // save respawn data
   %hasTimer = isObject(%component.respawnTimer);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      slgSaveFloat(%component.respawnTimer.getTotalTime());
      slgSaveFloat(%component.respawnTimer.getElapsedTime());
   }
   
   // save the gunslinger data
   %count = %object.slingerMaxIndex - %object.slingerMinIndex;
   slgSaveInt(%count);
   for (%index = %object.slingerMinIndex; %index < %object.slingerMaxIndex; %index++)
   {
      %slinger = %object.recruitedSlinger[%index];
      %hasSlinger = isObject(%slinger);
      slgSaveBool(%hasSlinger);
      if (%hasSlinger == true)
      {
         %saveID = %slinger.getSaveID();
         slgSaveInt(%index);
         slgSaveInt(%saveID);
      }
   }
   
   // save the deputy data
   for (%index = 0; %index < $CS_MAXDEPUTIES; %index++)
   {
      %deputy = %object.recruitedDeputy[%index];
      %hasDeputy = isObject(%deputy);
      slgSaveBool(%hasDeputy);
      if (%hasDeputy == true)
      {
         %saveID = %deputy.getSaveID();
         slgSaveInt(%saveID);
      }
   }
   
   // save the tax level data
   slgSaveString(%component.currentTax);
   %taxLevel = %component.taxLevel;
   slgSaveString(%taxLevel);
   
   // save bandit information
   slgSaveFloat(%object.banditHealth);
   slgSaveBool(%object.banditSteal);
   
   // save door state for the building
   slgSaveBool(%component.doorOpenCount);
}

// has timer
//    if has timer, elapsed timer time
function BuildingServer::SaveDestructionToFile(%component, %object, %datablock)
{
   %hasTimer = isObject(%component.timer);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      slgSaveFloat(%component.timer.getElapsedTime());
   }
}



// LOADING BUILDINGS
function BuildingServer::LoadFromFile(%component)
{
   %object = %component.getObjectId();
   %datablock = %component.getDataBlock();
   %state = %component.getState();
   
   if (%object.isLinkObj() == true)
   {
      %saveObject = %object.getFirstLinkObj();
      %saveObject.SaveObject(true);
      %link = %saveObject.getNextLinkObj();
      while (isObject(%link) == true)
      {
         %link.SaveObject(false);
         %link = %link.getNextLinkObj();
      }
      
      %angle = slgLoadFloat();
      /*
      %object.rotateZ(%angle);
      if (%angle != 0)
      {
         %object.clearInitialAngle();
      }
      */
      
      %linkStartDirection = slgLoadInt();
      %object.linkStartDirection = %linkStartDirection;
      if (%linkStartDirection == -1)
      {
         %object = %object.getLastLinkObj();
         %component = slgQueryInterface(%object, $CID_BUILDING);
         %datablock = %component.getDataBlock();
      }
      
      %object.linkStart = true;
      %object.linkStartDirection = %linkStartDirection;
      %state = %component.getState();
      if (%state == $BuildingState::NoState) %object.linkDirection = 0;
      else %object.linkDirection = %linkStartDirection;
   }
   
   // handle building no state load information
   if (%state == $BuildingState::NoState)
   {
      %component.LoadNoStateFromFile(%object, %datablock);
   }
   // handle building construction load information
   else if (%state == $BuildingState::Construction)
   {
      %component.LoadConstructionFromFile(%object, %datablock);
   }
   // handle building production load information
   else if (%state == $BuildingState::Production)
   {
      %component.LoadProductionFromFile(%object, %datablock);
   }
   // handle building destruction load information
   else if (%state == $BuildingState::Destruction)
   {
      %component.LoadDestructionFromFile(%object, %datablock);
   }
}

// construction x position
// construction y position
// construction z position
function BuildingServer::LoadNoStateFromFile(%component, %object, %datablock)
{
   if (%object.isLinkObj() == true)
   {
      // load the object's construction position
      %link = %object.getFirstLinkObj();
      
      %xPos = slgLoadFloat();
      %yPos = slgLoadFloat();
      %zPos = slgLoadFloat();
      %link.setPosition(%xPos, %yPos, %zPos);
      PlaceConstruction(%object, true);
   }
   else
   {
      %xPos = slgLoadFloat();
      %yPos = slgLoadFloat();
      %zPos = slgLoadFloat();
      %object.setPosition(%xPos, %yPos, %zPos);
      PlaceConstruction(%object, true);
   }
}

// construction x position
// construction y position
// construction z position
// has timer
//    if has timer, elapsed timer time
function BuildingServer::LoadConstructionFromFile(%component, %object, %datablock)
{
   // load the object's construction position
   %component.constructionPosition = slgLoadFloat() @ " " @
      slgLoadFloat() @ " " @ slgLoadFloat();
      
   %object.position = slgLoadFloat() @ " " @ slgLoadFloat() @
      " " @ slgLoadFloat();

   // set up the timer and clock on the object
   %hasTimer = slgLoadBool();
   %elapsedTime = 0;
   if (%hasTimer == true)
   {
      %elapsedTime = slgLoadFloat();
      %component.timer = new SLTimer()
      {
         time = %datablock.constructionTime;
      };
      %component.timer.setElapsedTime(%elapsedTime);
   }
   
   // set up a list of data that will be sent to the client to
   // ensure that the client-side object is reporting accurate
   // data, timers, health clocks, etc
   %data = "";
   if (%object.isLinkObj() == true)
   {
      // send the number of links and the direction of the links
      %linkCount = %object.getLinkCount() - 1;
      %data = "1 " @ %object.linkDirection;
      %data = %data @ " " @ %linkCount @ " ";

      // determine the total time it will take to make the bridge
      %totalTime = 0;
      for (%index = 0; %index < %linkCount; %index++)
      {
         %totalTime += BridgeMiddleData.constructionTime;
      }
      
      // get the fraction of the bridge that is complete
      %fraction = 1.0 / %linkCount;
      %totalFraction = %elapsedTime / %totalTime;
      %fullHealth = 0;
      
      %linkObject = %object;
      PlaceObject(%linkObject);
      if (%object.linkDirection == 1) %linkObject = %linkObject.getNextLinkObj();
      else %linkObject = %linkObject.getPrevLinkObj();
      while (%totalFraction > %fraction)
      {
         // link object is in production state
         %fullHealth += Bridge.healthMax / %linkCount;
         %linkObject.linkDirection = %object.linkDirection;
         %linkObject.health = %linkObject.getMaxHealth();
         %linkComp = slgQueryInterface(%linkObject, $CID_BUILDING);
         %linkComp.setState($BuildingState::Production);
         PlaceObject(%linkObject);
         
         if (%object.linkDirection == 1) %linkObject = %linkObject.getNextLinkObj();
         else %linkObject = %linkObject.getPrevLinkObj();
         %totalFraction -= %fraction;
      }
      
      %data = %data @ %fullHealth @ " ";

      // link object is in construction state (tell the client-side
      // link object which state it is in)
      %linkObject.linkDirection = %object.linkDirection;
      %linkObject.showMesh(false);
      %linkComp = slgQueryInterface(%linkObject, $CID_BUILDING);
      %linkComp.forceState($BuildingState::Construction);
      %linkComp.constructionPosition = %linkObject.position;
      %linkDatablock = %linkComp.getDataBlock();
      PlaceObject(%linkObject);
      PlaceConstructionProps(%linkComp, %linkObject, %linkDatablock);
      
      %newData = "0 " @ %object.linkDirection @ " " @ %linkCount @ " " @
         %fullHealth @ " " @ %totalFraction * %elapsedTime @ " " @
         %linkObject.position;

      %linkComp.timer = new SLTimer()
      {
         time = %datablock.constructionTime;
      };
      %linkComp.timer.setElapsedTime(%totalFraction * %elapsedTime);
      %linkComp.timer.notifyOnFire(stopConstruction, %linkComp);
         
      %client = ClientGroup.getObject(0);
      ClientLoadBuildingConstruction(%client, %linkComp, %newData);

      // any remaining link objects have not been placed
      if (%object.linkDirection == 1) %linkObject = %linkObject.getNextLinkObj();
      else %linkObject = %linkObject.getPrevLinkObj();
      while (isObject(%linkObject) == true)
      {
         %linkObject.health = %linkObject.getMaxHealth();
         %linkObject.showMesh(false);
         %linkComp = slgQueryInterface(%linkObject, $CID_BUILDING);
         %linkComp.forceState($BuildingState::NoState);
         
         if (%object.linkDirection == 1) %linkObject = %linkObject.getNextLinkObj();
         else %linkObject = %linkObject.getPrevLinkObj();
      }
   }
   
   %data = %data @ %elapsedTime @ " ";
   %data = %data @ %component.constructionPosition;
   
   %client = ClientGroup.getObject(0);
   ClientLoadBuildingConstruction(%client, %component, %data);
   
   if (%object.linkStart == false)
   {
      %component.timer.notifyOnFire(stopConstruction, %component);
   }
   
   // when constructing, the object does not have a shadow yet
   %object.removeShadow();
   
   // place the construction decal
   PlaceConstructionDecal(%object, %datablock);
   
   // place the construction props
   if (%object.linkStart == true)
   {
      %linkDirection = %object.linkDirection;
      %object.linkDirection = 0;
      PlaceConstructionProps(%component, %object, %datablock);
      %object.linkDirection = %linkDirection;
   }
   else
   {
      PlaceConstructionProps(%component, %object, %datablock);
   }
   
   // place the building's textures (excluding road)
   if (%object.isLinkObj() == false)
   {
      for (%index = 0; %index < 8; %index++)
      {
         if (%index != $Texture::Road)
         {
            %object.placeTexture(%index);
         }
      }
   }
   
   /*
   // if this is a bridge, we will need to place all of the bridge links
   if (%object.isLinkObj() == true)
   {
      %direction = %object.linkDirection;
      while (isObject(%object) == true)
      {
         PlaceObject(%object);
         if (%direction == 1) %object = %object.getNextLinkObj();
         else %object = %object.getPrevLinkObj();
      }
   }
   */
}

function ClientLoadBuildingConstruction(%client, %object, %data)
{
   %ghostID = %client.getGhostId(%object);
   if (%ghostID == -1)
   {
      schedule(100, 0, ClientLoadBuildingConstruction, %client, %object, %data);
      return;
   }
   
   commandToClient(%client, 'LoadBuildingConstruction', %ghostID, %data);
}

// damage happiness
// has timer
//    if has timer, elapsed timer time (production)
// has timer
//    if has timer, total timer time (new tenants)
//    if has timer, elapsed timer time (new tenants)
// gunslinger data (3)
function BuildingServer::LoadProductionFromFile(%component, %object, %datablock)
{
   // handle file loading here
   %component.damageHappiness = slgLoadFloat();
   %component.updateProduce = slgLoadBool();

   // load the production rate for the building (and production
   // objects if a ranch or farm)
   %hasTimer = slgLoadBool();
   if (%hasTimer == true)
   {
      %elapsedTime = slgLoadFloat();

      %component.timer = 0;      
      if (%component.produce !$= "")
      {
         %component.setProduce(%component.produce);
      }
      else
      {
         %component.timer = new SLTimer()
         {
            time = %datablock.produceTime;
         };
         %component.timer.notifyOnFire(updateResources, %component);
      }
      %component.timer.setElapsedTime(%elapsedTime);
   }

   // load the tenant production rate for the building
   %hasTimer = slgLoadBool();
   if (%hasTimer == true)
   {
      %totalTime = slgLoadFloat();
      %elapsedTime = slgLoadFloat();
      %component.housing = new SLTimer()
      {
         time = %totalTime;
      };
      %component.housing.notifyOnFire(addNewTenant, %component);
      %component.housing.setElapsedTime(%elapsedTime);
      
      // create client timers
      %clCount = ClientGroup.getCount();
      for (%i = 0; %i < %clCount; %i++)
      {
         %client = ClientGroup.getObject(%i);
         %ghost  = %client.getGhostID(%this);
         commandToClient(%client, 'CreateHouseBmpTimer', %ghost,
            %totalTime, %elapsedTime, %component.getTenantCount());
      }
   }
   
   // load respawn data
   %hasTimer = slgLoadBool();
   if (%hasTimer == true)
   {
      %totalTime = slgLoadFloat();
      %elapsedTime = slgLoadFloat();
      %component.respawnTimer = new SLTimer()
      {
         time = %totalTime;
      };
      %component.respawnTimer.setElapsedTime(%elapsedTime);
   }

   // reload all of the terrain textures
   if (%object.isLinkObj() == false)
   {
      for (%index = 0; %index < 8; %index++)
      {
         %object.placeTexture(%index);
      }
   }
   
   // if this is a bridge, we will need to place all of the bridge links
   if (%object.isLinkObj() == true)
   {
      %direction = %object.linkDirection;
      %link = %object;
      while (isObject(%link) == true)
      {
         PlaceObject(%link);
         if (%direction == 1) %link = %link.getNextLinkObj();
         else %link = %link.getPrevLinkObj();
      }
   }
   
   // load the gunslinger data
   %count = slgLoadInt();
   for (%index = 0; %index < %count; %index++)
   {
      %hasSlinger = slgLoadBool();
      if (%hasSlinger == true)
      {
         %useIndex = slgLoadInt();
         %saveID = slgLoadInt();
         %slinger = slgGetGameObject(%saveID);
         
         // if the slinger is not found, no need to load the slinger
         // information (because the slinger has died)
         if (isObject(%slinger) == true)
         {
            %object.recruitedSlinger[%useIndex] = %slinger;
            
            // send the message that load the gunslinger on the client
            %client = ClientGroup.getObject(0);
            %ghostIDer = %client.getGhostID(%object);
            %ghostIDee = %client.getGhostID(%slinger);
            commandToClient(%client, 'RecruitGunslinger', %ghostIDer, %useIndex, %ghostIDee);
         }
      }
   }
   
   // load the deputy data
   for (%index = 0; %index < $CS_MAXDEPUTIES; %index++)
   {
      %hasDeputy = slgLoadBool();
      if (%hasDeputy == true)
      {
         %saveID = slgLoadInt();
         %deputy = slgGetGameObject(%saveID);
         
         // if the deputy is not found, no need to load the deputy
         // information (because the deputy has died)
         if (isObject(%deputy) == true)
         {
            %object.recruitedDeputy[%index] = %deputy;
            
            // send the message that loads the gunslinger on the client
            %client = ClientGroup.getObject(0);
            %ghostIDer = %client.getGhostID(%object);
            %ghostIDee = %client.getGhostID(%deputy);
            commandToClient(%client, 'RecruitDeputy', %ghostIDer, %index, %ghostIDee);
         }
      }
   }
   
   // load the tax level data
   %component.currentTax = slgLoadString();
   %taxLevel = slgLoadString();
   %component.taxLevel = %taxLevel;
   if (%taxLevel !$= "")
   {
      %client = ClientGroup.getObject(0);
      %ghostID = %client.getGhostID(%component);
      CommandToClient(%client, 'OnTaxSet', %ghostID, %taxLevel, %component.currentTax);
   }
   
   // load bandit information
   %object.banditHealth = slgLoadFloat();
   %object.banditSteal = slgLoadBool();
   
   // load door state for the building
   %component.doorOpenCount = slgLoadBool();
}

// has timer
//    if has timer, elapsed timer time
function BuildingServer::LoadDestructionFromFile(%component, %object, %datablock)
{
   // if this is a link object, we want to set up the timers for the
   // first link object, not the end one if the bridge is made backwards
   if (%object.isLinkObj() == true)
   {
      %object = %object.getFirstLinkObj();
      %component = slgQueryInterface(%object, $CID_BUILDING);
      %datablock = %component.getDataBlock();
   }
   
   // load the tenant production rate for the building
   %hasTimer = slgLoadBool();
   if (%hasTimer == true)
   {
      %elapsedTime = slgLoadFloat();
      %component.timer = new SLTimer()
      {
         time = %datablock.destructionTime;
      };
      %component.timer.notifyOnFire(stopDestruction, %component);
      %component.timer.setElapsedTime(%elapsedTime);
   }
   
   // when destructing, the object's shadow is removed
   %object.removeShadow();
   
   // reload all of the terrain textures
   if (%object.isLinkObj() == false)
   {
      for (%index = 0; %index < 8; %index++)
      {
         %object.placeTexture(%index);
      }
   }
   
   // place destruction decal
   PlaceDestructionDecal(%object, %datablock);
   
   // place destruction props
   PlaceDestructionProps(%component, %object, %datablock);

   // if this is a link object, we need to set   
   if (%object.isLinkObj() == true)
   {
      %object = %object.getNextLinkObj();
      while (isObject(%object) == true)
      {
         %object.showMesh(false);
         %component = slgQueryInterface(%object, $CID_BUILDING);
         %datablock = %component.getDataBlock();
         
         // place destruction decal
         PlaceDestructionDecal(%object, %datablock);
         
         // place destruction props
         PlaceDestructionProps(%component, %object, %datablock);
         
         %object = %object.getNextLinkObj();
      }
   }
}
