Guest User

Chain Lightning with overkill

a guest
May 18th, 2015
303
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.45 KB | None | 0 0
  1. ------------------------------------------------------------------------------
  2. --  File        :   /mods/4DC_FAF_Release/lua/4D_defaultcollisionbeams.lua
  3. --  Author(s)   :   EbolaSoup, Resin Smoker, Optimus Prime, Vissroid, Eddy, Domino
  4. --  Copyright © 2015 4DC_FAF_Release, All rights reserved.
  5. ------------------------------------------------------------------------------
  6.  
  7. local CollisionBeam = import('/lua/sim/CollisionBeam.lua').CollisionBeam
  8. local EffectTemplate = import('/lua/EffectTemplates.lua')
  9. local Custom_4D_EffectTemplate = import('/mods/4DC_FAF_Release/lua/4D_EffectTemplates.lua')
  10. local utilities = import('/lua/utilities.lua')
  11. EmtBpPath = '/effects/emitters/'
  12.  
  13. ------------------------------
  14. --   Base class that defines all other 4DC beam types (Do not delete)
  15. ------------------------------
  16. CustomBeam = Class(CollisionBeam) {
  17.  
  18.     -- Default beam FX
  19.     FxImpactUnit = EffectTemplate.DefaultProjectileLandUnitImpact,
  20.     FxUnitHitScale = 0.3,
  21.     FxImpactLand = EffectTemplate.DefaultProjectileLandImpact,
  22.     FxLandHitScale = 0.3,
  23.     FxImpactWater = EffectTemplate.DefaultProjectileWaterImpact,
  24.     FxImpactUnderWater = EffectTemplate.DefaultProjectileUnderWaterImpact,
  25.     FxImpactAirUnit = EffectTemplate.DefaultProjectileAirUnitImpact,
  26.     FxImpactProp = {},
  27.     FxImpactShield = {},
  28.     FxImpactNone = {},
  29.  
  30.     OnImpact = function(self, impactType, targetEntity)
  31.         local myDebug = true
  32.         if myDebug then LOG('*** OnImpact ***') end
  33.         if self then
  34.             if impactType == 'Terrain' and self.ScorchSize then
  35.                 if self.Scorching == nil then
  36.                     self.Scorching = self:ForkThread( self.ScorchThread )
  37.                 end
  38.             elseif not impactType == 'Unit' and self.ScorchSize then
  39.                 KillThread(self.Scorching)
  40.                 self.Scorching = nil
  41.             end
  42.             self.ImpactPos = self:GetPosition()
  43.             CollisionBeam.OnImpact(self, impactType, targetEntity)
  44.         end
  45.     end,
  46.  
  47.     DoDamage = function(self, instigator, damageData, targetEntity)
  48.         local myDebug = true
  49.         if myDebug then LOG('*** DoDamage ***') end
  50.         if self and damageData then
  51.             if not instigator then
  52.                 instigator = self
  53.             end
  54.             if myDebug then LOG('   instigator: ', instigator:GetEntityId()) end
  55.             if self.Weapon:GetBlueprint().ChainLighningSpec and targetEntity then
  56.                 if myDebug then LOG('   Calling Chain Lightning') end
  57.                 if self.ChainLightningThread then
  58.                     KillThread(self.ChainLightningThread)
  59.                     self.ChainLightningThread = nil
  60.                 end
  61.                 self.Bp = self.Weapon:GetBlueprint()
  62.                 self.ChainSpec = self.Bp.ChainLighningSpec
  63.                 self.ChainFx = {}
  64.                 self.DmgUnitsTable = {}
  65.                 self.ChainLightningThread = self:ForkThread( self.DoChainLightning, instigator, damageData, targetEntity )
  66.             else
  67.                 if myDebug then LOG('   Normal beam, calling CollisionBeam.DoDamage') end
  68.                 CollisionBeam.DoDamage(self, instigator, damageData, targetEntity)
  69.             end
  70.         else
  71.             if myDebug then LOG('   "self" or Damage.Data is Invalid') end
  72.         end
  73.     end,
  74. ------------------------------------------------------------------------------------------
  75. --Three types of chain lightning damage spread "form"
  76. --Absolute: All targets receive the same base amount
  77. --Division: All targets receive damage = damage / num targets
  78. --Overkill: Targets only receive damage is there is overkill avalible from the prior target
  79. --Add this to the beam units weapon blueprint      
  80. --  ChainLighningSpec = {
  81. --      Branches = 3,
  82. --      ChainRadius = 5,
  83. --      DamageSpread = 'Overkill',
  84. --      Forks = 3,
  85. --  },
  86. --  Note: Strongly suggest keeping the collision and beam delays > than 0.3 seconds, otherwise the effects may not be visable.
  87. ------------------------------------------------------------------------------------------
  88.  
  89.     DoChainLightning = function(self, instigator, damageData, targetEntity)
  90.         local myDebug = true
  91.         if myDebug then LOG('*** DoChainLightning ***') end
  92.         local army = self:GetArmy()
  93.         local beamLifeTime
  94.         if self.Bp.BeamLifetime > 0.25 then
  95.             beamLifeTime = self.Bp.BeamLifetime
  96.         elseif self.Bp.BeamCollisionDelay > 0.25 then
  97.             beamLifeTime = self.Bp.BeamCollisionDelay
  98.         else
  99.             beamLifeTime = 0.25
  100.         end
  101.         if not self.ChainSpec.Forks or self.ChainSpec.Forks < 1 then
  102.             self.ChainSpec.Forks = Random(2, 6)
  103.         end
  104.         if not self.ChainSpec.ChainRadius or self.ChainSpec.ChainRadius < 1 then
  105.             self.ChainSpec.ChainRadius = Random(4, 10)
  106.         end
  107.         local chainDamage = damageData.DamageAmount
  108.         local oldTarget = targetEntity
  109.         if not oldTarget:BeenDestroyed() then
  110.             local oldHealth = oldTarget:GetHealth()
  111.             if oldHealth and oldHealth > 0 then
  112.                 if oldHealth > chainDamage then
  113.                     table.insert(self.DmgUnitsTable, {unit = oldTarget, damage = chainDamage})
  114.                     chainDamage = 0
  115.                 else
  116.                     table.insert(self.DmgUnitsTable, {unit = oldTarget, damage = oldHealth})
  117.                     chainDamage = chainDamage - oldHealth          
  118.                 end
  119.             end
  120.         end
  121.         local forkCounter = 1
  122.         if myDebug then LOG('   starting chainDamage: ', chainDamage) end
  123.         while self and oldTarget and forkCounter <= self.ChainSpec.Forks and chainDamage > 0 do
  124.             if myDebug then LOG('   While do ForkLightning loop') end
  125.             local targets = self:GetValidTargetsAlongScaledVector(instigator, oldTarget, (self.ChainSpec.ChainRadius))
  126.             local tblSize =  table.getsize(targets)
  127.             if myDebug then LOG('       tblSize: ', tblSize) end
  128.             if tblSize > 0 then
  129.                 local currentTarget
  130.                 if not self.ChainSpec.Branches or self.ChainSpec.Branches < 1 then
  131.                     self.ChainSpec.Branches = Random(2, 6)
  132.                 end
  133.                 for i = 0, self.ChainSpec.Branches do
  134.                     if myDebug then LOG('       for i = 0, self.ChainSpec.Branches do: ', i) end
  135.                     --local ran = Random(1,tblSize)
  136.                     currentTarget = targets[i]
  137.                     if currentTarget.unit != nil and not currentTarget.unit:BeenDestroyed() and currentTarget.unit != instigator and currentTarget.unit != targetEntity and currentTarget.unit != oldTarget and chainDamage > 0 then           
  138.                         if myDebug then LOG('           CurrentTarget Id: ', currentTarget.id, '    chainDamage avalible: ', chainDamage) end
  139.                         self:ForkThread(self.ChainLightningFX, oldTarget, currentTarget.unit, army, beamLifeTime)                      
  140.                         if self.ChainSpec.DamageSpread == 'Absolute' then
  141.                             if myDebug then LOG('       DamageSpread == Absolute') end
  142.                             table.insert(self.DmgUnitsTable, {unit = currentTarget.unit, damage = chainDamage})
  143.                             if myDebug then LOG('       Damage Amount: ', chainDamage) end                         
  144.                         elseif self.ChainSpec.DamageSpread == 'Division' then
  145.                             if myDebug then LOG('       DamageSpread == Division') end
  146.                             local num = table.getsize(self.DmgUnitsTable) + 1
  147.                             local dmgMod = chainDamage / num
  148.                             table.insert(self.DmgUnitsTable, {unit = currentTarget.unit, damage = dmgMod})
  149.                             if myDebug then LOG('       Damage Amount: ', dmgMod) end                                                                              
  150.                         else
  151.                             if myDebug then LOG('       DamageSpread == Overkill') end
  152.                             local targetHealth = currentTarget.unit:GetHealth()
  153.                             if chainDamage > targetHealth then
  154.                                 table.insert(self.DmgUnitsTable, {unit = currentTarget.unit, damage = targetHealth})
  155.                                 if myDebug then LOG('       Damage Amount: ', targetHealth) end                            
  156.                                 chainDamage = chainDamage - targetHealth                               
  157.                             else
  158.                                 table.insert(self.DmgUnitsTable, {unit = currentTarget.unit, damage = chainDamage})
  159.                                 if myDebug then LOG('       Damage Amount: ', chainDamage) end                                                     
  160.                                 chainDamage = 0
  161.                             end                                        
  162.                         end                                                
  163.                     end        
  164.                 end
  165.                 oldTarget = currentTarget.unit
  166.             end
  167.             targets = nil
  168.             forkCounter = forkCounter + 1
  169.         end
  170.         local numTargets = table.getsize(self.DmgUnitsTable)
  171.         if numTargets > 0 then
  172.             for a, b in self.DmgUnitsTable do
  173.                 if b and not b.unit:BeenDestroyed() then
  174.                     if myDebug then
  175.                         LOG('   Unit to damage ID: ', b.unit:GetEntityId(), '   Instigator: ', instigator:GetEntityId(), '  Damage Spread: ', self.ChainSpec.DamageSpread, '    Amount: ', b.damage)
  176.                     end            
  177.                     Damage(instigator, self:GetPosition(), b.unit, b.damage, damageData.DamageType)
  178.                 end
  179.             end        
  180.         end
  181.     end,
  182.  
  183.     GetValidTargetsAlongScaledVector = function(self, instigator, oldTarget, scale)
  184.         local myDebug = true
  185.         if myDebug then LOG('GetValidTargetsAlongScaledVector') end
  186.         if instigator and oldTarget and scale then
  187.             --Get a list of avlaible targets in the direction the beam was originally fired
  188.             local tPos = oldTarget:GetPosition()
  189.             local vPos = utilities.GetScaledDirectionVector(tPos, self.ImpactPos, scale)
  190.             local modPos = Vector(vPos[1] + tPos[1], tPos[2], vPos[3] + tPos[3])
  191.             if myDebug then
  192.                 LOG('impact x: ', self.ImpactPos[1],'impact y: ', self.ImpactPos[2],'impact z: ', self.ImpactPos[3])
  193.                 LOG('tPos x: ', tPos[1],'tPos y: ', tPos[2],'tPos z: ', tPos[3])
  194.                 LOG('vPos x: ', vPos[1],'vPos y: ', vPos[2],'vPos z: ', vPos[3])
  195.                 LOG('modPos x: ', modPos[1],'modPos y: ', modPos[2],'modPos z: ', modPos[3], 'scale: ', scale)
  196.             end
  197.             local targets = utilities.GetEnemyUnitsInSphere( self, modPos, scale )
  198.             --Filter out targets we've already chained to if any
  199.             if table.getsize(self.DmgUnitsTable) > 0 then
  200.                 for a, b in self.DmgUnitsTable do
  201.                     table.remove(targets, self.DmgUnitsTable[b])
  202.                 end
  203.             end
  204.             if myDebug then LOG('   Number of "filtered" targets found at range: ', scale, 'table size: ', table.getsize(targets)) end
  205.             if table.getsize(targets) < 1 then return nil end
  206.             --Sort the remaining targets by range
  207.             local sortTargByDist = {}
  208.             for a, b in targets do
  209.                 if not b:BeenDestroyed() and b:GetHealth() > 0 and b != instigator and b != oldTarget then
  210.                     table.insert(sortTargByDist, {dist = utilities.XZDistanceTwoVectors(self.ImpactPos, b:GetPosition()),id = b:GetEntityId() ,unit = b})
  211.                 end
  212.             end
  213.             table.sort(sortTargByDist, sort_by('dist'))
  214.             if myDebug then
  215.                 LOG('   Number of sorted targets: ', table.getsize(sortTargByDist))
  216.                 for c, d in sortTargByDist do
  217.                     LOG('       Unit: ', d.id, ' Distance: ', utilities.XZDistanceTwoVectors(self.ImpactPos, d.unit:GetPosition()) )
  218.                 end
  219.             end
  220.             return sortTargByDist
  221.         else
  222.             if myDebug then LOG('   Data passed is missing or invalid, returning nil') end
  223.             return nil
  224.         end
  225.     end,
  226.  
  227.     ChainLightningFX = function(self, target1, target2, army, duration)
  228.         local myDebug = true
  229.         if myDebug then LOG('*** ChainLightningFX ***') end
  230.         for a, b in self.FxBeamEndPoint do
  231.             if myDebug then LOG('   Impacts') end
  232.             local fx = CreateAttachedEmitter(target2, -1, army, b ):ScaleEmitter(self.FxBeamEndPointScale)
  233.             table.insert( self.BeamEffectsBag, fx)
  234.             table.insert( self.ChainFx, fx)
  235.             self.Trash:Add(fx)
  236.             target2.Trash:Add(fx)          
  237.         end
  238.         for c, d in self.FxBeam do
  239.             if target1 and not target1:BeenDestroyed() and target2 and not target2:BeenDestroyed() then
  240.                 if myDebug then LOG('   Beams') end
  241.                 local beam = AttachBeamEntityToEntity(target1, -1, target2, -1, army, d)
  242.                 local offset = 0.1
  243.                 if IsUnit(target2) then
  244.                     offSet = (target2:GetBlueprint().Physics.SizeY or 1) / 2
  245.                 end
  246.                 beam:OffsetEmitter(0, offset, 0)               
  247.                 table.insert(self.BeamEffectsBag, beam)
  248.                 table.insert(self.ChainFx, beam)               
  249.                 self.Trash:Add(beam)
  250.                 target2.Trash:Add(beam)
  251.             end
  252.         end
  253.         WaitSeconds(duration)
  254.         if self.ChainFx then
  255.             for e, f in self.ChainFx do
  256.                 f:Destroy()
  257.             end
  258.         end
  259.     end,
  260.  
  261.     ScorchThread = function(self)
  262.         local myDebug = true
  263.         if myDebug then LOG('*** ScorchThread ***') end
  264.         local army = self:GetArmy()
  265.         local CurrentPosition = self:GetPosition(1)
  266.         local LastPosition = Vector(0,0,0)
  267.         local skipCount = 1
  268.         while true do
  269.             if utilities.GetDistanceBetweenTwoVectors( CurrentPosition, LastPosition ) > 0.25 or skipCount > 100 then
  270.                 local size = utilities.GetRandomFloat( -self.ScorchSize , self.ScorchSize )
  271.                 CreateSplat( CurrentPosition, utilities.GetRandomFloat(0,2*math.pi), self.ScorchTexture, size, size, 25, 25, army )
  272.                 LastPosition = CurrentPosition
  273.                 skipCount = 1
  274.             else
  275.                 skipCount = skipCount + self.ScorchSplatDropTime
  276.             end
  277.             WaitSeconds( self.ScorchSplatDropTime or 0.1 )
  278.             CurrentPosition = self:GetPosition(1)
  279.         end
  280.     end,
  281.  
  282.     OnDisable = function( self )
  283.         local myDebug = true
  284.         if myDebug then LOG('*** OnDisable ***') end
  285.         CollisionBeam.OnDisable(self)
  286.         KillThread(self.Scorching)
  287.         KillThread(self.ChainLightningThread)
  288.         self.Scorching = nil
  289.     end,
  290. }
  291.  
  292. ------------------------------
  293. --  Chain Lighting beam for the Seraphem transformable heavy bot
  294. ------------------------------
  295.  
  296. xsl0310_LightningBeam = Class(CustomBeam) {
  297.     FxBeamStartPoint = EffectTemplate.SExperimentalUnstablePhasonLaserMuzzle01,
  298.     FxBeam = Custom_4D_EffectTemplate.LightingStrikeBeam,
  299.     FxBeamEndPoint = EffectTemplate.OthuyElectricityStrikeHit,
  300.     FxBeamEndPointScale = 0.1,
  301.     ScorchSize = 0.5,
  302.     ScorchSplatDropTime = 0.1,
  303.     ScorchTexture = 'czar_mark01_albedo',
  304.     TerrainImpactType = 'LargeBeam01',
  305.     TerrainImpactScale = 1,
  306. }
  307.  
  308. ------------------------------
  309. --  Green beam for the Cybran Vampire
  310. ------------------------------
  311.  
  312. VampireGreenLaserCollisionBeam = Class(CustomBeam) {
  313.  
  314.     FxBeam = {'/mods/4DC_FAF_Release/effects/Emitters/green_laser_beam_02_emit.bp'},
  315.     FxImpactUnit = {
  316.         '/mods/4DC_FAF_Release/effects/Emitters/green_hit_unit_emit.bp',
  317.         '/mods/4DC_FAF_Release/effects/Emitters/green_beam_hit_sparks_emit.bp',
  318.         EmtBpPath .. 'beam_hit_smoke_01_emit.bp',
  319.     },
  320.     FxUnitHitScale = 0.5,
  321.     FxImpactLand = {
  322.         EmtBpPath .. 'dirtchunks_01_emit.bp',
  323.         EmtBpPath .. 'dust_cloud_05_emit.bp',
  324.         EmtBpPath .. 'beam_hit_smoke_01_emit.bp',
  325.     },
  326.     FxLandHitScale = 0.5,
  327.     ScorchSplatDropTime = 1,
  328.     ScorchSize = 0.25,
  329.     ScorchTexture = 'czar_mark01_albedo',
  330.     TerrainImpactType = 'LargeBeam01',
  331.     TerrainImpactScale = 1,
  332.  
  333. }
Advertisement
Add Comment
Please, Sign In to add comment