------------------------------------------------------------------------------
--  File     : /mods/4DC_V0.8/lua/4D_defaultcollisionbeams.lua
--
--  Author(s): EbolaSoup, Resin Smoker, Optimus Prime, Vissroid, Eddy, Domino
--
--  Copyright © 2011 4DC_V0.8  All rights reserved.
------------------------------------------------------------------------------

local CollisionBeam = import('/lua/sim/CollisionBeam.lua').CollisionBeam
local EffectTemplate = import('/lua/EffectTemplates.lua')
local Custom_4D_EffectTemplate = import('/mods/4DC_V0.8/lua/4D_EffectTemplates.lua')
local utilities = import('/lua/utilities.lua')
EmtBpPath = '/effects/emitters/'

------------------------------
--   Base class that defines all other 4DC beam types (Do not delete)
------------------------------
CustomBeam = Class(CollisionBeam) {

    -- Show target data during debug mode (select 'true' to display)
    BeamDebug = false,
   
    -- Default beam FX
    FxImpactUnit = EffectTemplate.DefaultProjectileLandUnitImpact,
    FxUnitHitScale = 0.3,     
    FxImpactLand = EffectTemplate.DefaultProjectileLandImpact,
    FxLandHitScale = 0.3,   
    FxImpactWater = EffectTemplate.DefaultProjectileWaterImpact,
    FxImpactUnderWater = EffectTemplate.DefaultProjectileUnderWaterImpact,
    FxImpactAirUnit = EffectTemplate.DefaultProjectileAirUnitImpact,
    FxImpactProp = {},
    FxImpactShield = {},   
    FxImpactNone = {},   
   
    OnImpact = function(self, impactType, targetEntity)       
        -- Triggers Scorch effect but only if a ScorchSize is defined
        if impactType == 'Terrain' and self.ScorchSize then
            if self.Scorching == nil then
                self.Scorching = self:ForkThread( self.ScorchThread )                 
            end           
        elseif not impactType == 'Unit' and self.ScorchSize then
            KillThread(self.Scorching)
            self.Scorching = nil               
        end
        CollisionBeam.OnImpact(self, impactType, targetEntity)
    end,   
   
    DoDamage = function(self, instigator, damageData, targetEntity)
        -- Ensure we have valid data       
        if self and damageData and targetEntity then
           -- Set the lifetime of the beams
           local beamLifeTime
           local wpBp = self.Weapon:GetBlueprint()
           if wpBp.BeamLifetime > 0.25 then
               beamLifeTime = wpBp.BeamLifetime
           elseif wpBp.BeamCollisionDelay > 0.25 then
               beamLifeTime = wpBp.BeamCollisionDelay
           else
               beamLifeTime = 0.25
           end
           -- Show targetEntity during debug mode
           if self.BeamDebug then
               WARN('***')     
               WARN('   Beam Life Time', beamLifeTime)         
               WARN('   Impacted Target ID: ', targetEntity:GetEntityId() )
           end         
            -- Ensure we have an instigator of some type
            if not instigator then
                instigator = self
            end
            -- Jump beam to other targets only if ChainJumps and ChainRange are defined     
            if self.ChainJumps > 0  and self.ChainRange > 0 then                                       
               -- Check for targets in range             
               local tPos = targetEntity:GetPosition()
               local targets = {}
               targets = utilities.GetEnemyUnitsInSphere( self, tPos, self.ChainRange )
               table.removeByValue(targets, targetEntity)           
               -- Check if targets avalible beyond the first
               if table.getsize(targets) > 1 then                                     
                   -- Sort targets by range to create a valid target list
                   local army = self:GetArmy()
                   local iPos = instigator:GetPosition()                   
                   local targByDist = {}
                   for a, b in targets do
                       if not b:BeenDestroyed() then                 
                           table.insert(targByDist, {dist = utilities.XZDistanceTwoVectors(iPos, b:GetPosition()), unit = b})
                       end
                   end
                   table.sort(targByDist, sort_by('dist'))                 
                   local validTarg = {}
                   table.insert(validTarg, targetEntity)
                   for c = 1, (self.ChainJumps) do
                        table.insert(validTarg, targByDist[c].unit)
                   end                 
                   -- Show avalible targets by range during debug mode
                   if self.BeamDebug then           
                       for d, e in validTarg do
                           WARN('   Unit: ', e:GetEntityId(), ' Distance: ', utilities.XZDistanceTwoVectors(iPos, e:GetPosition()) )                           
                       end
                   end                                                           
                   -- Loop thru avalible targets
                   local num = table.getsize(validTarg)               
                   while self and num > 1 do       
                       -- Create beam and dmg FX
                       self:ForkThread(self.ChainBeamFX, validTarg[(num - 1)], validTarg[num], army, beamLifeTime)
                       self:ForkThread(self.ChainDmgFX, validTarg[num], army, self.FxBeamEndPointScale, beamLifeTime)                                                                             
                       -- Apply Damage   
                       self:ForkThread(self.ChainDamage, instigator, validTarg[num], (damageData.DamageAmount / num), damageData.DamageType)   
                       num = num - 1                             
                   end                                                                                                                   
               end
           end         
        end
        CollisionBeam.DoDamage(self, instigator, damageData, targetEntity)                     
    end,
   
    ChainBeamFX = function(self, target1, target2, army, duration)
        for f, g in self.FxBeam do
            if target1 and not target1:BeenDestroyed() and target2 and not target2:BeenDestroyed() then                     
               local beam = AttachBeamEntityToEntity(target1, -1, target2, -1, army, g)                         
               table.insert(self.BeamEffectsBag, beam)
               self.Trash:Add(beam)
               WaitSeconds(duration)
               if self and beam then
                   beam:Destroy()
               end
           end
        end   
    end,
   
    ChainDmgFX = function(self, target, army, scale, duration)
        for h, i in self.FxBeamEndPoint do
            if target and not target:BeenDestroyed() then         
                local fx = CreateAttachedEmitter(target, -1, army, i ):ScaleEmitter(scale)
                table.insert(self.BeamEffectsBag, fx)
                self.Trash:Add(fx)
               WaitSeconds(duration)
               if self and fx then
                   fx:Destroy()
               end               
            end
        end   
    end,   
   
    ChainDamage = function(self, instigator, target, dmg, dmgtype)
        if target and not target:BeenDestroyed() then   
            Damage(instigator, self:GetPosition(), target, dmg, dmgtype)
        end   
    end,     
   
    ScorchThread = function(self)
        local army = self:GetArmy()
        local CurrentPosition = self:GetPosition(1)
        local LastPosition = Vector(0,0,0)
        local skipCount = 1
        while true do
            if utilities.GetDistanceBetweenTwoVectors( CurrentPosition, LastPosition ) > 0.25 or skipCount > 100 then
                local size = utilities.GetRandomFloat( -self.ScorchSize , self.ScorchSize )
                CreateSplat( CurrentPosition, utilities.GetRandomFloat(0,2*math.pi), self.ScorchTexture, size, size, 25, 25, army )
                LastPosition = CurrentPosition
                skipCount = 1
            else
                skipCount = skipCount + self.ScorchSplatDropTime
            end               
            WaitSeconds( self.ScorchSplatDropTime or 0.1 )
            CurrentPosition = self:GetPosition(1)
        end
    end,   
   
    OnDisable = function( self )
        CollisionBeam.OnDisable(self)
        KillThread(self.Scorching)
        self.Scorching = nil           
    end,         
}

------------------------------
--   Chain Lighting beam for the Seraphem transformable heavy bot
------------------------------

xsl031a_LightningBeam = Class(CustomBeam) {

    ChainJumps = 3,
    ChainRange = 10,
    FxBeamStartPoint = EffectTemplate.SExperimentalUnstablePhasonLaserMuzzle01,
    FxBeam = Custom_4D_EffectTemplate.LightingStrikeBeam,
    FxBeamEndPoint = EffectTemplate.OthuyElectricityStrikeHit,
    FxBeamEndPointScale = 0.1,
    ScorchSize = 0.5,
    ScorchSplatDropTime = 0.1,     
    ScorchTexture = 'czar_mark01_albedo',
    TerrainImpactType = 'LargeBeam01',
    TerrainImpactScale = 1,   

}

------------------------------
--   Green beam for the Cybran Vampire
------------------------------

VampireGreenLaserCollisionBeam = Class(CustomBeam) {

    FxBeam = {'/mods/4DC_V0.8/effects/Emitters/green_laser_beam_02_emit.bp'},
    FxImpactUnit = {
        '/mods/4DC_V0.8/effects/Emitters/green_hit_unit_emit.bp',
        '/mods/4DC_V0.8/effects/Emitters/green_beam_hit_sparks_emit.bp',
        EmtBpPath .. 'beam_hit_smoke_01_emit.bp',
    },
    FxUnitHitScale = 0.5,
    FxImpactLand = {
        EmtBpPath .. 'dirtchunks_01_emit.bp',
        EmtBpPath .. 'dust_cloud_05_emit.bp',
        EmtBpPath .. 'beam_hit_smoke_01_emit.bp',
    },
    FxLandHitScale = 0.5,   
    ScorchSplatDropTime = 1,
    ScorchSize = 0.25,
    ScorchTexture = 'czar_mark01_albedo',
    TerrainImpactType = 'LargeBeam01',
    TerrainImpactScale = 1,     
       
}