#****************************************************************************
#**
#**  File     :  /lua/unit.lua
#**  Author(s):  John Comes, David Tomandl, Gordon Duclos
#**


do
local oldUnit = Unit
Unit = Class(oldUnit) {
    OnCreate = function(self)
        oldUnit.OnCreate(self)
		self.VeteranTracker = 0
	end,

   CreateDestructionEffects = function( self, overKillRatio )
        local scale = self:GetBlueprint().SizeX * self:GetBlueprint().SizeY * self:GetBlueprint().SizeZ
    	local category = self:GetBlueprint().Categories
    	local island = 0
    	local isnaval = 0
		local isair = 0
		    	
    	if table.find(category, 'LAND') then 
    	island = 1
    	end
		if table.find(category, 'NAVAL') then 
    	isnaval = 1
    	end
		if table.find(category, 'AIR') then 
    	isair = 1
    	end
    	   	
    	self:PlayUnitSound('Destroyed')
        
    	randomized = Random( 1, 20)
        if scale > 0.2 and randomized > 14 and randomized < 19 and island == 1 then
        CreateAttachedEmitter(self, 0, self:GetArmy(), '/effects/emitters/destruction_explosion_concussion_ring_03_emit.bp'):ScaleEmitter(scale * 0.45)
        end	
        if scale > 0.26 and randomized > 17 and island == 1 then
        CreateAttachedEmitter(self, 0, self:GetArmy(), '/effects/emitters/nuke_concussion_ring_01_emit.bp'):ScaleEmitter(scale * 0.25)
        end	

        randomized = Random( 1, 7)
        if randomized > 3 and isair == 1 then
        explosion.CreateTimedStuctureUnitExplosion3( self )
		WaitSeconds( Random( 0, 0.5 ) )  
		end     
                            	
        explosion.CreateScalableUnitExplosion( self, overKillRatio )
        
        randomized = Random( 1, 6)
        if randomized > 4 and isnaval == 0 then
        explosion.CreateTimedStuctureUnitExplosion2( self )
		WaitSeconds( Random( 0, 0.5 ) )  
		end     
	 	
        randomized = Random( 1, 6)
        self:PlayUnitSound('Destroyed')
        if randomized > 5 and isnaval == 0 then
        explosion.CreateTimedStuctureUnitExplosion2( self )  
        WaitSeconds( Random( 0.5, 1.5 ) ) 
        end            
        if randomized > 3 then
        WaitSeconds( Random( 0.2, 1.0 ) )
        self:PlayUnitSound('Destroyed')        
        explosion.CreateScalableUnitExplosion( self, overKillRatio )        
        WaitSeconds( Random( 0.5, 1.5 ) )
        end
        
    end,	

    DeathWeaponDamageThread = function( self , damageRadius, damage, damageType, damageFriendly)
        DamageArea(self, self:GetPosition(), damageRadius * 0.6 or 1, damage * 0.4 or 1, damageType or 'Normal', damageFriendly or true)
        WaitSeconds( 0.3 )
		DamageArea(self, self:GetPosition(), damageRadius * 0.7 or 1, damage * 0.185 or 1, damageType or 'Normal', damageFriendly or true)
		WaitSeconds( 0.3 )
		DamageArea(self, self:GetPosition(), damageRadius * 0.8 or 1, damage * 0.15 or 1, damageType or 'Normal', damageFriendly or true)
		WaitSeconds( 0.3 )
		DamageArea(self, self:GetPosition(), damageRadius * 0.9 or 1, damage * 0.1 or 1, damageType or 'Normal', damageFriendly or true)
		WaitSeconds( 0.3 )
		DamageArea(self, self:GetPosition(), damageRadius * 1.0 or 1, damage * 0.085 or 1, damageType or 'Normal', damageFriendly or true)
		WaitSeconds( 0.3 )
		DamageArea(self, self:GetPosition(), damageRadius * 1.1 or 1, damage * 0.085 or 1, damageType or 'Normal', damageFriendly or true)
		WaitSeconds( 0.3 )
		DamageArea(self, self:GetPosition(), damageRadius * 1.2 or 1, damage * 0.05 or 1, damageType or 'Normal', damageFriendly or true)
    end,

    DeathThread = function( self, overkillRatio, instigator)

        #LOG('*DEBUG: OVERKILL RATIO = ', repr(overkillRatio))

        WaitSeconds( utilities.GetRandomFloat( self.DestructionExplosionWaitDelayMin, self.DestructionExplosionWaitDelayMax) )
        self:DestroyAllDamageEffects()

        if self.PlayDestructionEffects then
            self:CreateDestructionEffects( self, overkillRatio )
        end

        #MetaImpact( self, self:GetPosition(), 0.1, 0.5 )
        if self.DeathAnimManip then
            WaitFor(self.DeathAnimManip)
            if self.PlayDestructionEffects and self.PlayEndAnimDestructionEffects then
                self:CreateDestructionEffects( self, overkillRatio )
            end
        end

        self:CreateWreckage( overkillRatio )

        # CURRENTLY DISABLED UNTIL DESTRUCTION
        # Create destruction debris out of the mesh, currently these projectiles look like crap,
        # since projectile rotation and terrain collision doesn't work that great. These are left in
        # hopes that this will look better in the future.. =)
        if( self.ShowUnitDestructionDebris and overkillRatio ) then
            if overkillRatio <= 1 then
                self.CreateUnitDestructionDebris( self, true, true, false )
            elseif overkillRatio <= 2 then
                self.CreateUnitDestructionDebris( self, true, true, false )
            elseif overkillRatio <= 3 then
                self.CreateUnitDestructionDebris( self, true, true, true )
            else #VAPORIZED
                self.CreateUnitDestructionDebris( self, true, true, true )
            end
        end

        #LOG('*DEBUG: DeathThread Destroying in ',  self.DeathThreadDestructionWaitTime )
        WaitSeconds(self.DeathThreadDestructionWaitTime)

        self:PlayUnitSound('Destroyed')
        self:Destroy()
    end,
        		
    OnKilledUnit = function(self, unitKilled)
		local deadUnit = unitKilled:GetBlueprint().Categories
		for i, cat in deadUnit do
			if cat == 'EXPERIMENTAL' then
				self.VeteranTracker = self.VeteranTracker + 60
			elseif cat == 'COMMAND' then
				self.VeteranTracker = self.VeteranTracker + 30
			elseif cat == 'TECH3' then
				self.VeteranTracker = self.VeteranTracker + 6
			elseif cat == 'TECH2' then
				self.VeteranTracker = self.VeteranTracker + 3
			elseif cat == 'TECH1' then
				self.VeteranTracker = self.VeteranTracker + 1
			end
		end
		self.Sync.VeteranTracker = self.VeteranTracker
		oldUnit.OnKilledUnit(self, unitKilled)
   end,

	PlayAnimationThread = function(self, anim)
        local bp = self:GetBlueprint().Display[anim]
        if bp then
            local totWeight = 0
            for k, v in bp do
                if v.Weight then
                    totWeight = totWeight + v.Weight
                end
            end
            local val = 1
            local num = Random(0, totWeight)
            for k, v in bp do
                if v.Weight then
                    val = val + v.Weight
                end
                if num < val then
                    bp = v
                    break
                end
            end
            if bp.Mesh then
                self:SetMesh(bp.Mesh)
            end
            if bp.Animation then
                local sinkAnim = CreateAnimator(self)
                self:StopRocking()
                self.DeathAnimManip = sinkAnim
                sinkAnim:PlayAnim(bp.Animation)
                local rate = 1
                if bp.AnimationRateMax and bp.AnimationRateMin then
                    rate = Random(bp.AnimationRateMin * 10, bp.AnimationRateMax * 10) / 20
                end
                sinkAnim:SetRate(rate)
                self.Trash:Add(sinkAnim)
                WaitFor(sinkAnim)
            end
        end
    end,   

	CloakEffectControlThread = function(self)
		local bp = self:GetBlueprint()
		local bpDisplay = bp.Display
		while not (self == nil or self:GetHealth() <= 0 or self:IsDead()) do
			WaitSeconds(0.1)
			self:UpdateCloakEffect()
			local CloakFieldIsActive = self:IsIntelEnabled('CloakField')
			if CloakFieldIsActive then
				local position = self:GetPosition(0)
				-- Range must be (radius - 2) because it seems GPG did that for the actual field for some reason.
				-- Anything beyond (radius - 2) is not cloaked by the cloak field
				local range = bp.Intel.CloakFieldRadius - 2
				local brain = self:GetAIBrain()
				local UnitsInRange = brain:GetUnitsAroundPoint( categories.ALLUNITS, position, range, 'Ally' )
				for num, unit in UnitsInRange do
					unit:MarkUnitAsInCloakField()
				end
			end
		end
	end,

	-- Fork the thread that will deactivate the cloak effect, killing any previous threads that may be running
	MarkUnitAsInCloakField = function(self)
		self.InCloakField = true
		if self.InCloakFieldThread then
			KillThread(self.InCloakFieldThread)
			self.InCloakFieldThread = nil
		end
		self.InCloakFieldThread = self:ForkThread(self.InCloakFieldWatchThread)
	end,

	-- Will deactive the cloak effect if it is not renewed by the cloak field
	InCloakFieldWatchThread = function(self)
		WaitSeconds(0.2)
		self.InCloakField = false
	end,

	-- This is the core of the entire mod. The effect is actually applied here.
	UpdateCloakEffect = function(self)
		local bp = self:GetBlueprint()
		local bpDisplay = bp.Display
		local cloaked = self:IsIntelEnabled('Cloak') or self.InCloakField
		if (not cloaked and self.CloakEffectEnabled) or self:GetHealth() <= 0 then
			self:SetMesh(bpDisplay.MeshBlueprint, true)
		elseif cloaked and not self.CloakEffectEnabled then
			self:SetMesh(bpDisplay.CloakMeshBlueprint , true)
			self.CloakEffectEnabled = true
		end
	end,

	--------------------------------------------------------------------------------------------------
	--                                          Overridden Functions                                          --
	--------------------------------------------------------------------------------------------------
	
	-- Overridden Function:
	-- Fork our CloakEffectControlThread
	OnStopBeingBuilt = function(self,builder,layer)
		oldUnit.OnStopBeingBuilt(self,builder,layer)
		self:ForkThread(self.CloakEffectControlThread)
	end,

	-- Overridden Function:
	-- Overrode this so that there will be no doubt if the cloak effect is active or not
	SetMesh = function(self, meshBp, keepActor)
		oldUnit.SetMesh(self, meshBp, keepActor)
		self.CloakEffectEnabled = false;
	end,
	
	-- Overridden Function:
	-- Just an attempt to ensure that a dying unit decloaks, may not be 100% effective
	OnKilled = function(self)
		local bpDisplay = self:GetBlueprint().Display
		self:SetMesh(bpDisplay.MeshBlueprint, true)
		oldUnit.OnKilled(self)
	end,

	-- Overridden Function:
	-- While the CloakEffectControlThread will activate the cloak effect eventually,
	-- this method tries to provide faster a response time to intel changes
	OnIntelEnabled = function(self)
		oldUnit.OnIntelEnabled(self)
		self:UpdateCloakEffect()
	end,

	-- Overridden Function:
	-- While the CloakEffectControlThread will deactivate the cloak effect eventually,
	-- this method tries to provide faster a response time to intel changes
	OnIntelDisabled = function(self)
		oldUnit.OnIntelDisabled(self)
		self:UpdateCloakEffect()
	end,

	-- Overridden Function:
	-- Fixes a problem with the default code and multiple types of intel on a unit.
	IntelWatchThread = function(self)
		WaitSeconds(0.5)
		local energy = self:GetConsumptionPerSecondEnergy()
		local mass = self:GetConsumptionPerSecondMass()
		local fraction = self:GetResourceConsumed()
		if energy == 0 and mass == 0 then
			fraction = 1
		end
		while fraction == 1 do
			WaitSeconds(0.5)
			energy = self:GetConsumptionPerSecondEnergy()
			mass = self:GetConsumptionPerSecondMass()
			if energy == 0 and mass == 0 then
				fraction = 1
			else
				fraction = self:GetResourceConsumed()
			end
		end
		self:DisableUnitIntel()
	end,
}
end