//------------------------------------------------------------
// Copyright Sandlot Games, 2007
// author: Michael Felice
// file: svr_character.cs
// brief:
//    This handled character initialization, updating, and
//    removal.  In particular, resource udpates should happen
//    on the server side in order to propagate the same values
//    and changes to the client.
//------------------------------------------------------------



//*************************************************************
//           SERVER-SIDE CHARACTER STATES
//*************************************************************

// this function is called once when a character is created for
// the first time (not when it is loaded from a mission file,
// only when it is newly created)
function CharacterServer::onInitialize(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);
   %datablock = %component.getDataBlock();
   
   if (isObject(GameResourceStack) == true && %object.getTeam() == $OST_PLAYER)
   {
      %component.OnAddToTeam();
   }
   
   // Modify object as necessary
   osModifySLGameObject(%object);
   
   if (%object.tempTenant == true)
   {
      $TemporaryTenants--;
   }
}

// this function is called once on every tick for the server-side
// character object
function CharacterServer::onUpdate(%this, %input)
{
   %object = getWord(%input, 0);
   %component = getWord(%input, 1);
   %datablock = %component.getDataBlock();
   
   // update the character's shadow
   %object.updateShadow();
   
   if (%object.getTeam() != $OST_PLAYER)
   {
      return;
   }

   // handle timers for resource production
   if (%datablock.produceTime && !%component.produceTimer)
   {
      %component.produceTimer = new SLTimer()
      {
         time = %datablock.produceTime;
      };
      %component.produceTimer.notifyOnFire(onProduce, %component);
   }

   // handle timers for resource consumption
   if (%datablock.consumeTime && !%component.consumeTimer)
   {
      %component.consumeTimer = new SLTimer()
      {
         time = %datablock.consumeTime;
      };
      %component.consumeTimer.notifyOnFire(onConsume, %component);
   }
   
   // if the character is collecting resources and the collection
   // timer has not been created, create the collection timer
   if (%component.isCollecting() == true &&
      isObject(%component.collectTimer) == false)
   {
      %prop = %component.getCollectionProp();
      if (isObject(%prop) == true)
      {
         %propComponent = slgQueryInterface(%prop, $CID_PROP);
         %propData = %propComponent.getDataBlock();
         %component.collectTimer = new SLTimer()
         {
            time = %propData.collectTime / %propData.collectCount;
         };
         %component.collectTimer.notifyOnUpdate(onCheckCollect, %component);
         %component.collectTimer.notifyOnFire(onCollect, %component);
      }
   }
   // if the character is not collecting resources, but it has a
   // collection timer, make sure the collection timer is deleted
   else if (%component.isCollecting() == false &&
      isObject(%component.collectTimer) == true)
   {
      %component.collectTimer.delete();
   }
}

// this function is called when the character object is removed
function CharacterServer::onRemove(%this, %input)
{
   %component = getWord(%input, 0);
   %datablock = %component.getDataBlock();
   %object = %component.getObjectId();
   
   if (isObject(GameResourceStack) == true)
   {
      %component.OnRemoveFromTeam();
   }
   
   if (isObject(%object) == false)
   {
      return;
   }

   // remove the production timer
   if (isObject(%component.produceTimer) == true)
   {
      %component.produceTimer.delete();
   }

   // remove the consumption timer
   if (isObject(%component.consumeTimer) == true)
   {
      %component.consumeTimer.delete();
   }
   
   // remove the colleciton timer
   if (isObject(%component.collectTimer) == true)
   {
      %component.collectTimer.delete();
   }
   
   RemoveObject(%object);
}

// this function is called when the character would produce resources
function CharacterServer::onProduce(%component)
{
   %datablock = %component.getDataBlock();

   if (isObject(GameResourceStack) == true)
   {
      %gameResource = GameResourceStack.getResource();
      if (isObject(%gameResource) == true)
      {
         %gameResource.clearHappinessRates();
         %gameResource.getGold().increaseCount(%datablock.produceGold);
         %gameResource.getWood().increaseCount(%datablock.produceWood);
         %gameResource.getWater().increaseCount(%datablock.produceWater);
         %gameResource.getFood().increaseCount(%datablock.produceFood);
      }
   }

   %component.produceTimer = 0;
}

// this function is called when the character would consume resources
function CharacterServer::onConsume(%component)
{
   %datablock = %component.getDataBlock();
   %object = %component.getObjectId();

   // decrease resources that are consumed by the character
   if (isObject(GameResourceStack) == true)
   {
      %gameResource = GameResourceStack.getResource();
      if (isObject(%gameResource) == true)
      {
         %goldChange = %gameResource.getGold().getCount();
         %woodChange = %gameResource.getWood().getCount();
         %waterChange = %gameResource.getWater().getCount();
         
         %gameResource.getGold().decreaseCount(%datablock.consumeGold);
         %gameResource.getWood().decreaseCount(%datablock.consumeWood);
         %gameResource.getWater().decreaseCount(%datablock.consumeWater);

         %goldChange = %gameResource.getGold().getCount() - %goldChange;
         %woodChange = %gameResource.getWood().getCount() - %woodChange;
         %waterChange = %gameResource.getWater().getCount() - %waterChange;
         if (%goldChange != 0 || %woodChange != 0 || %waterChange != 0)
         {
            SendProductionToClient(%object, %goldChange @ " gold " @ %woodChange @ " wood " @ %waterChange @ " water");
         }

         %foodChange = %gameResource.getFood().getCount();

         // if the character was not fed because there was not enough food
         if (%gameResource.getFood().decreaseCount(%datablock.consumeFood) == false)
         {
            // determine how hungry the character is, now
            %hunger = %component.getHunger();
            %newHunger = %hunger + %datablock.hungerLoss;
            if (%newHunger > %datablock.hungerMax)
            {
               %newHunger = %datablock.hungerMax;
            }
            
            // determine if this new setting affects the hungriness of
            // the character and the happiness of the town
            if (%hunger != %newHunger)
            {
               %component.setHunger(%newHunger);
               UpdateHungerIcon(%object, %newHunger);
               UpdateUnhappinessIcon(%object);
               %gameResource.getHappiness().decreaseCount(%newHunger - %hunger);
               SendProductionToClient(%object, %hunger - %newHunger @ " happiness");
               
                // add alert
               if (%hunger == 0 && %object.isOnTeam($OST_PLAYER))
               {
                  alertSvrAddHunger(%object);
               }
            }
         }
         else
         {
            // clear all hunger issues with the character, reset the
            // happiness for the town
            %hunger = %component.getHunger();
            if (%hunger != 0)
            {
               %gameResource.getHappiness().increaseCount(%hunger);
               %component.setHunger(0);
               UpdateHungerIcon(%object, 0);
               UpdateUnhappinessIcon(%object);
               SendProductionToClient(%object, %hunger @ " happiness");
               
               // remove alert
               alertSvrRemoveHunger(%object);
            }
         }
         
         %foodChange = %gameResource.getFood().getCount() - %foodChange;
         if (%foodChange != 0)
         {
            SendProductionToClient(%object, %foodChange @ " food");
         }
      }
   }

   %component.consumeTimer = 0;
}

// this function checks if the character is still collecting from a prop
function CharacterServer::onCheckCollect(%component)
{
   if (%component.isCollecting() == false)
   {
      %component.collectTimer.delete();
   }
}

// this function is called every time the character should take
// resources from a prop that it was collecting from over time
function CharacterServer::onCollect(%component)
{
   // do not collect anything if the character is told not to
   // collect anything
   if (%component.isCollecting() == false)
   {
      return;
   }
   
   // the collection timer will be deleted after this function call,
   // so there is no need to track it anymore
   %component.collectTimer = 0;
   
   // check the collection prop, and if it is there, then the character
   // is currently collecting a resource (which is needed for this to work)
   %prop = %component.getCollectionProp();
   if (isObject(%prop) == false)
   {
      return;
   }
   
   // get the prop component that makes the prop a prop (if there
   // is no prop component, this is not a prop, and there is a problem)
   %propComponent = slgQueryInterface(%prop, $CID_PROP);
   if (isObject(%propComponent) == false)
   {
      error("Character is attempting to collect from a prop (that is not a prop)-- aborting collection.");
      return;
   }

   if (isObject(GameResourceStack) == true)
   {
      %gameResource = GameResourceStack.getResource();
      if (isObject(%gameResource) == true)
      {
         // update the resources with the resources that were taken from the prop
         %gameResource.clearHappinessRates();
         %gameResource.getGold().increaseCount(%propComponent.takeGold());
         %gameResource.getWood().increaseCount(%propComponent.takeWood());
         %gameResource.getWater().increaseCount(%propComponent.takeWater());
         %gameResource.getFood().increaseCount(%propComponent.takeFood());
         %gameResource.getHappiness().increaseCount(%propComponent.takeHappiness());
      }
   }

   // if there are no more resources to take from the prop, delete it
   if (%propComponent.isEmpty() == true)
   {
      %component.stopCollecting();
      %prop.onDestroy();
      return;
   }

   // otherwise, if there are more resources to take from the prop, create
   // a new collection timer for the next set of resources of the quick job
   %propData = %propComponent.getDataBlock();
   %component.collectTimer = new SLTimer()
   {
      time = %propData.collectTime / %propData.collectCount;
   };
   %component.collectTimer.notifyOnUpdate(onCheckCollect, %component);
   %component.collectTimer.notifyOnFire(onCollect, %component);
}

function CharacterServer::OnAddToTeam(%component)
{
   %datablock = %component.getDataBlock();
   %object = %component.getObjectId();
   
   // update the health bar
   serverUpdateHealthBar(%object, 0);
   
   if (%component.uninitialize == true)
   {
      return;
   }
   
   if (isObject(GameResourceStack) == false)
   {
      return;
   }
   
   %gameResource = GameResourceStack.getResource();
   if (isObject(%gameResource) == true)
   {
      %people = %gameResource.getPeople();
      %oldPopulation = %people.getCount();
      
      %gameResource.clearHappinessRates();
      %gameResource.getGold().increaseCount(%datablock.initGold);
      %gameResource.getWood().increaseCount(%datablock.initWood);
      %gameResource.getWater().increaseCount(%datablock.initWater);
      %gameResource.getFood().increaseCount(%datablock.initFood);
      %gameResource.getPeople().forceIncreaseCount(%datablock.initPeople);
      %gameResource.getHappiness().increaseCount(%datablock.initHappiness);
      
      // update the happiness based on any population changes
      %newPopulation = %people.getCount();
      %happiness = GameResourceData.getPopulationHappiness(%oldPopulation, %newPopulation);
      %gameResource.getHappiness().increaseCount(%happiness);
      
      %happinessGain = %datablock.initHappiness + %happiness;
      SendProductionToClient(%object, %happinessGain @ " happiness");
      %component.uninitialize = true;
   }
   
   // check if a hunger and or angry icon should be added
   %hunger = %component.getHunger();
   UpdateHungerIcon(%object, %hunger);
   UpdateUnhappinessIcon(%object);
   
   // update the portrait camera image
   %camera = PortraitCamera.getCameraCmp();
   if (isObject(%camera) == true)
   {
      %camera.updateTarget();
   }
}

function CharacterServer::OnRemoveFromTeam(%component)
{
   %datablock = %component.getDataBlock();
   %object = %component.getObjectId();
   
   // update the health bar
   serverUpdateHealthBar(%object, 0);
   
   if (%component.uninitialize == false)
   {
      return;
   }
   
   %gameResource = GameResourceStack.getResource();
   if (isObject(%gameResource) == true)
   {
      %people = %gameResource.getPeople();
      %oldPopulation = %people.getCount();
      
      %gameResource.getGold().decreaseCount(%datablock.initGold);
      %gameResource.getWood().decreaseCount(%datablock.initWood);
      %gameResource.getWater().decreaseCount(%datablock.initWater);
      %gameResource.getFood().decreaseCount(%datablock.initFood);
      %gameResource.getPeople().decreaseCount(%datablock.initPeople);
      %gameResource.getHappiness().decreaseCount(%datablock.initHappiness);

      // reset any happiness lost by the character due to hunger         
      %hunger = %component.getHunger();
      if (%hunger != 0)
      {
         %gameResource.getHappiness().increaseCount(%hunger);
         %component.setHunger(0);
         UpdateHungerIcon(%object, 0);
         UpdateUnhappinessIcon(%object);
         
         // remove alert
         alertSvrRemoveHunger(%object);
      }

      // update the happiness based on any population changes
      %newPopulation = %people.getCount();
      %happiness = GameResourceData.getPopulationHappiness(%oldPopulation, %newPopulation);
      %gameResource.getHappiness().increaseCount(%happiness);
      
      %happinessGain = %happiness - %datablock.initHappiness;
      if (isObject(%object) == true)
      {
         SendProductionToClient(%object, %happinessGain @ " happiness");
      }
      %component.uninitialize = false;
   }
   
   if (isObject(%component.consumeTimer))
   {
      %component.consumeTimer.delete();
      %component.consumeTimer = 0;
   }
   
   // remove any hunger and angry icons that are present
   if (isObject(%object.hungerIcon) == true)
   {
      %object.hungerIcon.delete();
      %object.hungerIcon = 0;
   }
   if (isObject(%object.unhappinessIcon) == true)
   {
      %object.unhappinessIcon.delete();
      %object.unhappinessIcon = 0;
   }
   CheckIconStacking(%object);
   
   // remove from employment and home
   if (%component.hasWork())
   {
      slgUnemployObject(%object);
   }
   if (%component.hasHome())
   {
      slgUnhouseObject(%object);
   }
   
   // update the portrait camera image
   %camera = PortraitCamera.getCameraCmp();
   if (isObject(%camera) == true)
   {
      %camera.updateTarget();
   }
}

//-State Change Methods---------------------------------------------------------
function CharacterServer::onStateChange(%this, %state, %set)
{
   %object = slgGetAttachedObjId(%this);
   
   // Output text message
   if (%state == $CharacterState::Drunk) 
   {
      if (%set == true)
      {
         %msg = slgGetUIString("id_character_drunk");
      }
      else
      {
         %msg = slgGetUIString("id_character_sober");
      }
      %msg = slgFormatUIString(%msg, %object.name);
      tsSendHudMessage(%msg);
   }
}

function CharacterServer::onSetWork(%this, %char, %bldg)
{
    // Send employ message
   if(isObject(MsgSender)) {
      MsgSender.postMsg($MSG_SLGOBJEMPLOY, $MRT_LOCAL, %char, %bldg);
   }
   
   // Update the employement icons on the health bar
   SendGhostToClient(ClientGroup.getObject(0), 'AddEmployeeToMeter', %bldg);
   

   if(%char.isOnTeam($OST_PLAYER)) {
      %msg = slgGetUIString("id_character_employ");
      %msg = slgFormatUIString(%msg, %char.name, %bldg.name);
      tsSendHudMessage(%msg);
   }
}

function CharacterServer::onLeaveHome(%this, %char, %bldg)
{
}

function CharacterServer::onLeaveWork(%this, %char, %bldg)
{
   // Send message
   if(isObject(MsgSender)) {
      MsgSender.sendMsg($MSG_SLGOBJQUIT, $MRT_LOCAL, %char, %bldg);
   }
   
   if(%char.isOnTeam($OST_PLAYER)) {
      %msg = slgGetUIString("id_character_quit");
      %msg = slgFormatUIString(%msg, %char.name, %bldg.name);
      tsSendHudMessage(%msg);
   }
   
   // Notify AI
   AICmd_QuitJob(%char);
}

//-Protesting Methods-----------------------------------------------------------
// starts the character protesting home
function CharacterServer::protestHome(%this)
{
   %this.setState($CharacterState::HomeProtest);
   
   // if already protesting work then swap sign
   if (%this.isProtestingWork()) 
   {
      %this.mountProtestSign(true);
   }
}

// starts the character protesting work
function CharacterServer::protestWork(%this)
{
   %this.setState($CharacterState::WorkProtest);
}

// stops the character from protesting about home
function CharacterServer::endProtestingHome(%this)
{
   %this.clearState($CharacterState::HomeProtest);
   
   // if protesting work then swap sign
   if (%this.isProtestingWork()) 
   {
      %this.mountProtestSign(true);
   }
}

// stops the character from protesting about work
function CharacterServer::endProtestingWork(%this)
{
   %this.clearState($CharacterState::WorkProtest);
}

// tests if character is protesting home
function CharacterServer::isProtestingHome(%this)
{
   return %this.inState($CharacterState::HomeProtest);
}

// tests if character is protesting work
function CharacterServer::isProtestingWork(%this)
{
   return %this.inState($CharacterState::WorkProtest);
}

// mounts the protest sign
function CharacterServer::mountProtestSign(%this, %bMount)
{
   %obj = slgGetAttachedObjId(%this);
   
   %homeMountInfo = %obj.getDataBlock().homeProtestMount;
   %workMountInfo = %obj.getDataBlock().workProtestMount;
   
   // home overrides work
   if (%bMount == true)
   {
      if (%this.isProtestingHome())
      {
         %obj.mountImage(getWord(%homeMountInfo, 0), getWord(%homeMountInfo, 1));
      }
      else if (%this.isProtestingWork()) 
      {
         %obj.mountImage(getWord(%workMountInfo, 0), getWord(%workMountInfo, 1));
      }
   }
   else
   {
      %obj.unmountImage(getWord(%homeMountInfo, 1));
      %obj.unmountImage(getWord(%workMountInfo, 1));
   }
}

// plays the protest animation
function CharacterServer::playProtestAnimation(%this)
{
   %obj = slgGetAttachedObjId(%this);
   
   // home overrides work
   if (%this.isProtestingHome()) 
   {
      %animation = %obj.getDataBlock().homeProtestAnim;
   }
   else if (%this.isProtestingWork()) 
   {
      %animation = %obj.getDataBlock().workProtestAnim;
   }
   
   // play animation
   if (%animation !$= "") 
   {
      %cmpAI = slgQueryInterface(%obj, $CID_AI);
      %obj.playThread(0, %animation);
      %this.playingProtestAnimation = true;
      %obj.setRotationZ(%cmpAI.getDataBlock().protestAnimAngle);
      
      %timer = new SLTimer()
      {
         time = 6;
      };
      %timer.notifyOnFire(PicketResetAnimation, %cmpAI);
      %cmpAI.protestTimer = %timer;
   }
}

function CharacterServer::stopProtestAnimation(%this)
{
   %object = slgGetAttachedObjId(%this);
   %cmpAI = slgQueryInterface(%object, $CID_AI);
   if (isObject(%cmpAI.protestTimer) == true)
   {
      %cmpAI.protestTimer.delete();
   }
   %object.stopThread(0);
   %this.playingProtestAnimation = false;
}

// tests if the character is playing a protest animation
function CharacterServer::isPlayingProtestAnimation(%this)
{
   return (%this.playingProtestAnimation);
}

function ClearDestination(%object)
{
   %object.ClearDestination();
   
   %client = ClientGroup.getObject(0);
   %ghostID = %client.getGhostId();
   commandToClient(%client, 'ClearDestination', %ghostID);
}

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