#**************************************************************************** #** #** File : /cdimage/lua/modules/defaultunits.lua #** Author(s): John Comes, Gordon Duclos #** #** Summary : Default definitions of units #** #** Copyright © 2005 Gas Powered Games, Inc. All rights reserved. #**************************************************************************** local Unit = import('unit.lua').Unit local Shield = import('shield.lua').Shield local explosion = import('defaultexplosions.lua') local Util = import('utilities.lua') local EffectUtil = import('EffectUtilities.lua') local EffectTemplate = import('EffectTemplates.lua') local ScenarioUtils = import('/lua/ScenarioUtilities.lua') local EffectUtil = import('EffectUtilities.lua') local CreateBuildCubeThread = EffectUtil.CreateBuildCubeThread local CreateAeonBuildBaseThread = EffectUtil.CreateAeonBuildBaseThread local Entity = import('entity.lua').Entity ################################################################# ## MISC UNITS ################################################################# DummyUnit = Class(Unit) { OnStopBeingBuilt = function(self,builder,layer) self:Destroy() end, } ################################################################# ## STRUCTURE UNITS ################################################################# StructureUnit = Class(Unit) { LandBuiltHiddenBones = {'Floatation'}, MinConsumptionPerSecondEnergy = 1, MinWeaponRequiresEnergy = 0, OnCreate = function(self) Unit.OnCreate(self) self.WeaponMod = {} if self:GetBlueprint().Display.TerrainMeshes then local pos = self:GetPosition() self.TerrainTypeOnCreate = GetTerrainType( pos[1], pos[3] ) end #CREATION OF TARMAC local flatten = self:GetBlueprint().Physics.FlattenSkirt if flatten and self:GetCurrentLayer() == 'Land' then self:FlattenSkirt(self) self:CreateTarmac(true, true, true, false, false) end self.FxBlinkingLightsBag = {} end, FlattenSkirt = function(self) local x, y, z = unpack(self:GetPosition()) local x0,z0,x1,z1 = self:GetSkirtRect() x0,z0,x1,z1 = math.floor(x0),math.floor(z0),math.ceil(x1),math.ceil(z1) FlattenMapRect(x0, z0, x1-x0, z1-z0, y) end, CreateTarmac = function(self, albedo, normal, glow, orientation, specTarmac, lifeTime) local tarmac local bp = self:GetBlueprint().Display.Tarmacs if not specTarmac then if bp and table.getn(bp) > 0 then local num = Random(1, table.getn(bp)) #LOG('*DEBUG: NUM + ', repr(num)) tarmac = bp[num] else return false end else tarmac = specTarmac end local army = self:GetArmy() local w = tarmac.Width local l = tarmac.Length local fadeout = tarmac.FadeOut local x, y, z = unpack(self:GetPosition()) local xo = x - w * 0.5 local zo = z - l * 0.5 local aiBrain = GetArmyBrain(self:GetArmy()) SetTerrainTypeRect(Rect(xo,zo,xo + w,zo + l), {TypeCode= (aiBrain:GetFactionIndex() + 189) } ) local orient = orientation if not orientation then if tarmac.Orientations and table.getn(tarmac.Orientations) > 0 then orient = tarmac.Orientations[Random(1, table.getn(tarmac.Orientations))] orient = (0.01745 * orient) else orient = 0 end end if not self.TarmacBag then self.TarmacBag = { Decals = {}, Orientation = orient, CurrentBP = tarmac, } end if albedo and tarmac.Albedo then local tarmacHndl = CreateDecal(self:GetPosition(), orient, tarmac.Albedo, tarmac.Albedo2 or '', 'Albedo', w, l, fadeout, lifeTime or 0, army) table.insert(self.TarmacBag.Decals, tarmacHndl) if tarmac.RemoveWhenDead then self.Trash:Add(tarmacHndl) end end if normal and tarmac.Normal then local tarmacHndl = CreateDecal(self:GetPosition(), orient, tarmac.Normal, '', 'Alpha Normals', w, l, fadeout, lifeTime or 0, army) table.insert(self.TarmacBag.Decals, tarmacHndl) if tarmac.RemoveWhenDead then self.Trash:Add(tarmacHndl) end end if glow and tarmac.Glow then local tarmacHndl = CreateDecal(self:GetPosition(), orient, tarmac.Glow, '', 'Glow', w, l, fadeout, lifeTime or 0, army) table.insert(self.TarmacBag.Decals, tarmacHndl) if tarmac.RemoveWhenDead then self.Trash:Add(tarmacHndl) end end end, DestroyTarmac = function(self) if not self.TarmacBag then return end for k, v in self.TarmacBag.Decals do v:Destroy() end self.TarmacBag.Orientation = nil self.TarmacBag.CurrentBP = nil end, OnMassStorageStateChange = function(self, state) end, OnEnergyStorageStateChange = function(self, state) end, CreateBlinkingLights = function(self, color) self:DestroyBlinkingLights() local bp = self:GetBlueprint().Display.BlinkingLights local bpEmitters = self:GetBlueprint().Display.BlinkingLightsFx if bp then local fxbp = bpEmitters[color] for k, v in bp do if type(v) == 'table' then local fx = CreateAttachedEmitter(self, v.BLBone, self:GetArmy(), fxbp) fx:OffsetEmitter(v.BLOffsetX or 0, v.BLOffsetY or 0, v.BLOffsetZ or 0) fx:ScaleEmitter(v.BLScale or 1) table.insert(self.FxBlinkingLightsBag, fx) self.Trash:Add(fx) end end end end, DestroyBlinkingLights = function(self) for k, v in self.FxBlinkingLightsBag do v:Destroy() end self.FxBlinkingLightsBag = {} end, CreateDestructionEffects = function( self, overKillRatio ) #LOG( bp.General.FactionName, ' ', bp.General.UnitType,' avg. bounding radius = ', explosion.GetAverageBoundingXZRadius( self ) ) #LOG( 'CurrentLayer ', self:GetCurrentLayer()) if( explosion.GetAverageBoundingXZRadius( self ) < 1.0 ) then explosion.CreateScalableUnitExplosion( self, overKillRatio ) else explosion.CreateTimedStuctureUnitExplosion( self ) WaitSeconds( 0.5 ) explosion.CreateScalableUnitExplosion( self, overKillRatio ) end end, OnStartBuild = function(self, unitBeingBuilt, order ) Unit.OnStartBuild(self,unitBeingBuilt, order) #Fix up info on the unit id from the blueprint and see if it matches the 'UpgradeTo' field in the BP. local unitid = self:GetBlueprint().General.UpgradesTo self.UnitBeingBuilt = unitBeingBuilt if unitBeingBuilt:GetUnitId() == unitid and order == 'Upgrade' then ChangeState(self, self.UpgradingState) end end, IdleState = State { Main = function(self) end, }, UpgradingState = State { Main = function(self) self:StopRocking() local bp = self:GetBlueprint().Display self:DestroyTarmac() self:DisableUnitIntel() self:PlayUnitSound('UpgradeStart') if bp.AnimationUpgrade then local unitBuilding = self.UnitBeingBuilt self.AnimatorUpgradeManip = CreateAnimator(self) self.Trash:Add(self.AnimatorUpgradeManip) local fractionOfComplete = 0 self:StartUpgradeEffects(unitBuilding) self.AnimatorUpgradeManip:PlayAnim(bp.AnimationUpgrade, false):SetRate(0) while fractionOfComplete < 1 and not self:IsDead() do fractionOfComplete = unitBuilding:GetFractionComplete() self.AnimatorUpgradeManip:SetAnimationFraction(fractionOfComplete) WaitTicks(1) end if not self:IsDead() then self.AnimatorUpgradeManip:SetRate(1) end end end, OnStopBuild = function(self, unitBuilding) Unit.OnStopBuild(self, unitBuilding) if unitBuilding:GetFractionComplete() == 1 then NotifyUpgrade(self, unitBuilding) self:StopUpgradeEffects(unitBuilding) self:PlayUnitSound('UpgradeEnd') self:Destroy() end end, OnFailedToBuild = function(self) Unit.OnFailedToBuild(self) self.AnimatorUpgradeManip:Destroy() self:EnableUnitIntel() if self:GetCurrentLayer() == 'Water' then self:StartRocking() end self:PlayUnitSound('UpgradeFailed') self:CreateTarmac(true, true, true, self.TarmacBag.Orientation, self.TarmacBag.CurrentBP) ChangeState(self, self.IdleState) end, }, StartBeingBuiltEffects = function(self, builder, layer) Unit.StartBeingBuiltEffects(self, builder, layer) local bp = self:GetBlueprint() local FactionName = bp.General.FactionName if FactionName == 'UEF' then self:HideBone(0, true) if bp.General.UpgradesFrom != builder:GetUnitId() then self.OnBeingBuiltEffectsBag:Add( self:ForkThread( CreateBuildCubeThread, builder, self.OnBeingBuiltEffectsBag ) ) end elseif FactionName == 'Aeon' then if bp.General.UpgradesFrom != builder:GetUnitId() then self.OnBeingBuiltEffectsBag:Add( self:ForkThread( CreateAeonBuildBaseThread, builder, self.OnBeingBuiltEffectsBag ) ) end elseif FactionName == 'Cybran' then end end, StopBeingBuiltEffects = function(self, builder, layer) Unit.StopBeingBuiltEffects(self, builder, layer) end, StartBuildingEffects = function(self, unitBeingBuilt, order) Unit.StartBuildingEffects(self, unitBeingBuilt, order) end, StopBuildingEffects = function(self, unitBeingBuilt) Unit.StopBuildingEffects(self, unitBeingBuilt) end, StartUpgradeEffects = function(self, unitBeingBuilt) unitBeingBuilt:HideBone(0, true) end, StopUpgradeEffects = function(self, unitBeingBuilt) unitBeingBuilt:ShowBone(0, true) end, #Adding into OnKilled the ability to destroy the tarmac but put a new one down that looks exactly like it but #will time out over the time spec'd or 300 seconds. OnKilled = function(self, instigator, type, overkillRatio) Unit.OnKilled(self, instigator, type, overkillRatio) local orient = self.TarmacBag.Orientation local currentBP = self.TarmacBag.CurrentBP self:DestroyTarmac() self:CreateTarmac(true, true, true, orient, currentBP, currentBP.DeathLifetime or 300) end, #--------------------------------------------------------------------------------------------- # Adjacency #--------------------------------------------------------------------------------------------- # Bonus Types Are: # 'MassBuildBonus' # 'EnergyBuildBonus' # 'MassProductionBonus' # 'EnergyProductionBonus' # 'EnergyWeaponBonus' # 'MassMaintenanceBonus' # 'EnergyMaintenanceBonus' #This is direct and not super flexible but considering the time, it works for ship. - 8/23/06 JC #When we're adjacent, try to all all the possible bonuses. OnAdjacentTo = function(self, adjacentUnit, triggerUnit) if self:IsBeingBuilt() then return end if adjacentUnit:IsBeingBuilt() then return end self:ApplyAdjacencyBonus(adjacentUnit, 'MassBuildBonus', false) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyBuildBonus', false) self:ApplyAdjacencyBonus(adjacentUnit, 'MassMaintenanceBonus', false) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyMaintenanceBonus', false) self:ApplyAdjacencyBonus(adjacentUnit, 'MassProductionBonus', false) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyProductionBonus', false) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyWeaponBonus', false) end, #When we're not adjacent, try to remove all the possible bonuses. OnNotAdjacentTo = function(self, adjacentUnit) self:ApplyAdjacencyBonus(adjacentUnit, 'MassBuildBonus', true) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyBuildBonus', true) self:ApplyAdjacencyBonus(adjacentUnit, 'MassMaintenanceBonus', true) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyMaintenanceBonus', true) self:ApplyAdjacencyBonus(adjacentUnit, 'MassProductionBonus', true) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyProductionBonus', true) self:ApplyAdjacencyBonus(adjacentUnit, 'EnergyWeaponBonus', true) end, #--------- # Giving adjacency bonus unit functions #--------- #This is where the adjacency bonus gets applied ApplyAdjacencyBonus = function(self, adjacentUnit, bonusType, removeBonus) local bonus = self:GetAdjacentBonus(adjacentUnit, bonusType) if bonus == 1 then return end if removeBonus then bonus = bonus * -1 end if not adjacentUnit:CanRecieveAdjacencyBonus(bonusType) then return end adjacentUnit:SetAdjacencyBonus(bonusType, bonus) if not removeBonus then self:CreateAdjacentEffect(adjacentUnit) else self:DestroyAdjacentEffects() end end, #Go through the adjacency list I have and see if I have an applicable modifier other than 1. GetAdjacentBonus = function(self, adjacentUnit, bonusType) local bp = self:GetBlueprint().Adjacency[bonusType] if not bp then return 1 end for k, v in bp do local cat = ParseEntityCategory(v.Category) if EntityCategoryContains(cat, adjacentUnit) then return v.Modifier or 1 end end return 1 end, CreateAdjacentEffect = function(self, adjacentUnit) #Create trashbag to hold all these entities and beams if not self.AdjacencyBeamsBag then self.AdjacencyBeamsBag = {} end #LOG( 'Unit:', self:GetBlueprint().Description, ' AUnit:', adjacentUnit:GetBlueprint().Description ) #LOG( 'Unit Position:', repr(self:GetPosition()) ) #LOG( 'Adjacent Unit Position:' .. repr(adjacentUnit:GetPosition()) ) #LOG( ' ' ) EffectUtil.CreateAdjacencyBeams( self, adjacentUnit, self.AdjacencyBeamsBag ) end, DestroyAdjacentEffects = function(self) if not self.AdjacencyBeamsBag then return end for k, v in self.AdjacencyBeamsBag do if v.Unit:IsDead() or v.Unit:BeenDestroyed() then v.Trash:Destroy() self.AdjacencyBeamsBag[k] = nil end end end, #--------- # Recieving adjacency bonus unit functions #--------- #Can I recieve adjacency? CanRecieveAdjacencyBonus = function(self, bonusType) local bp = self:GetBlueprint() if bonusType == 'EnergyBuildBonus' or bonusType == 'MassBuildBonus' then if bp.Economy.BuildableCategory and table.getn(bp.Economy.BuildableCategory) > 0 then return true end elseif bonusType == 'EnergyProductionBonus' then if bp.Economy.ProductionPerSecondEnergy and bp.Economy.ProductionPerSecondEnergy > 0 then return true end elseif bonusType == 'MassProductionBonus' then if bp.Economy.ProductionPerSecondMass and bp.Economy.ProductionPerSecondMass > 0 then return true end elseif bonusType == 'EnergyMaintenanceBonus' then if bp.Economy.MaintenanceConsumptionPerSecondEnergy or self.EnergyMaintenanceConsumptionOverride then return true end elseif bonusType == 'MassMaintenanceBonus' then if bp.Economy.MaintenanceConsumptionPerSecondMass then return true end elseif bonusType == 'EnergyWeaponBonus' then for i = 1, self:GetWeaponCount() do local wep = self:GetWeapon(i) if wep:WeaponUsesEnergy() then return true end end end return false end, #Set the adjacency bonus on myself. SetAdjacencyBonus = function(self, bonusType, bonus) if bonusType == 'MassBuildBonus' then self.MassBuildAdjMod = (self.MassBuildAdjMod or 1) + bonus self:UpdateConsumptionValues() return elseif bonusType == 'EnergyBuildBonus' then self.EnergyBuildAdjMod = (self.EnergyBuildAdjMod or 1) + bonus self:UpdateConsumptionValues() return elseif bonusType == 'MassMaintenanceBonus' then self.MassMaintAdjMod = (self.MassMaintAdjMod or 1) + bonus self:UpdateConsumptionValues() return elseif bonusType == 'EnergyMaintenanceBonus' then self.EnergyMaintAdjMod = (self.EnergyMaintAdjMod or 1) + bonus self:UpdateConsumptionValues() return elseif bonusType == 'MassProductionBonus' then self.MassProdAdjMod = (self.MassProdAdjMod or 1) + bonus self:UpdateProductionValues() return elseif bonusType == 'EnergyProductionBonus' then self.EnergyProdAdjMod = (self.EnergyProdAdjMod or 1) + bonus self:UpdateProductionValues() return elseif bonusType == 'EnergyWeaponBonus' then for i = 1, self:GetWeaponCount() do local wep = self:GetWeapon(i) if wep:WeaponUsesEnergy() then wep:AddAdjacentEnergyMod(bonus) end end end end, } #------------------------------------------------------------- # FACTORY UNITS #------------------------------------------------------------- FactoryUnit = Class(StructureUnit) { OnCreate = function(self) StructureUnit.OnCreate(self) self.BuildingUnit = false end, OnPaused = function(self) #When factory is paused take some action self:PlayUnitAmbientSound( nil ) StructureUnit.OnPaused(self) end, OnUnpaused = function(self) if self.BuildingUnit then self:PlayUnitAmbientSound( 'ConstructLoop' ) end StructureUnit.OnUnpaused(self) end, OnStopBeingBuilt = function(self,builder,layer) local aiBrain = GetArmyBrain(self:GetArmy()) aiBrain:ESRegisterUnitMassStorage(self) aiBrain:ESRegisterUnitEnergyStorage(self) local curEnergy = aiBrain:GetEconomyStoredRatio('ENERGY') local curMass = aiBrain:GetEconomyStoredRatio('MASS') if curEnergy > 0.11 and curMass > 0.11 then self:CreateBlinkingLights('Green') self.BlinkingLightsState = 'Green' else self:CreateBlinkingLights('Red') self.BlinkingLightsState = 'Red' end StructureUnit.OnStopBeingBuilt(self,builder,layer) end, ChangeBlinkingLights = function(self, state) local bls = self.BlinkingLightsState if state == 'Yellow' then if bls == 'Green' then self:CreateBlinkingLights('Yellow') self.BlinkingLightsState = state end elseif state == 'Green' then if bls == 'Yellow' then self:CreateBlinkingLights('Green') self.BlinkingLightsState = state elseif bls == 'Red' then local aiBrain = GetArmyBrain(self:GetArmy()) local curEnergy = aiBrain:GetEconomyStoredRatio('ENERGY') local curMass = aiBrain:GetEconomyStoredRatio('MASS') if curEnergy > 0.11 and curMass > 0.11 then if self:GetNumBuildOrders(categories.ALLUNITS) == 0 then self:CreateBlinkingLights('Green') self.BlinkingLightsState = state else self:CreateBlinkingLights('Yellow') self.BlinkingLightsState = 'Yellow' end end end elseif state == 'Red' then self:CreateBlinkingLights('Red') self.BlinkingLightsState = state end end, OnMassStorageStateChange = function(self, newState) if newState == 'EconLowMassStore' then self:ChangeBlinkingLights('Red') else self:ChangeBlinkingLights('Green') end end, OnEnergyStorageStateChange = function(self, newState) if newState == 'EconLowEnergyStore' then self:ChangeBlinkingLights('Red') else self:ChangeBlinkingLights('Green') end end, OnStartBuild = function(self, unitBeingBuilt, order ) self:ChangeBlinkingLights('Yellow') StructureUnit.OnStartBuild(self, unitBeingBuilt, order ) self.BuildingUnit = true if order != 'Upgrade' then ChangeState(self, self.BuildingState) self.BuildingUnit = false end self.FactoryBuildFailed = false end, OnStopBuild = function(self, unitBeingBuilt, order ) StructureUnit.OnStopBuild(self, unitBeingBuilt, order ) if not self.FactoryBuildFailed then if not EntityCategoryContains(categories.AIR, unitBeingBuilt) then self:RollOffUnit() end self:StopBuildFx() self:ForkThread(self.FinishBuildThread, unitBeingBuilt, order ) end self.BuildingUnit = false end, FinishBuildThread = function(self, unitBeingBuilt, order ) self:SetBusy(true) local bp = self:GetBlueprint() local bpAnim = bp.Display.AnimationFinishBuildLand if bpAnim and EntityCategoryContains(categories.LAND, unitBeingBuilt) then self.RollOffAnim = CreateAnimator(self):PlayAnim(bpAnim) self.Trash:Add(self.RollOffAnim) WaitTicks(1) WaitFor(self.RollOffAnim) end unitBeingBuilt:DetachFrom(true) self:DetachAll(bp.Display.BuildAttachBone or 0) self:DestroyBuildRotator() ChangeState(self, self.RollingOffState) end, CheckBuildRestriction = function(self, target_bp) return true end, OnFailedToBuild = function(self) self.FactoryBuildFailed = true StructureUnit.OnFailedToBuild(self) self:DestroyBuildRotator() self:StopBuildFx() ChangeState(self, self.IdleState) end, RollOffUnit = function(self) local spin, x, y, z = self:CalculateRollOffPoint() local units = { self.UnitBeingBuilt } self.MoveCommand = IssueMove(units, Vector(x, y, z)) end, CalculateRollOffPoint = function(self) local bp = self:GetBlueprint().Physics.RollOffPoints local px, py, pz = unpack(self:GetPosition()) if not bp then return 0, px, py, pz end local vectorObj = self:GetRallyPoint() local bpKey = 1 local distance, lowest = nil for k, v in bp do distance = VDist2(vectorObj[1], vectorObj[3], v.X + px, v.Z + pz) if not lowest or distance < lowest then bpKey = k lowest = distance end end local fx, fy, fz, spin local bpP = bp[bpKey] local unitBP = self.UnitBeingBuilt:GetBlueprint().Display.ForcedBuildSpin if unitBP then spin = unitBP else spin = bpP.UnitSpin end fx = bpP.X + px fy = bpP.Y + py fz = bpP.Z + pz return spin, fx, fy, fz end, StartBuildFx = function(self, unitBeingBuilt) end, StopBuildFx = function(self) end, PlayFxRollOff = function(self) end, PlayFxRollOffEnd = function(self) if self.RollOffAnim then self.RollOffAnim:SetRate(-1) WaitFor(self.RollOffAnim) self.RollOffAnim:Destroy() self.RollOffAnim = nil end end, CreateBuildRotator = function(self) if not self.BuildBoneRotator then local spin = self:CalculateRollOffPoint() local bp = self:GetBlueprint().Display self.BuildBoneRotator = CreateRotator(self, bp.BuildAttachBone or 0, 'y', spin, 10000) self.Trash:Add(self.BuildBoneRotator) end end, DestroyBuildRotator = function(self) if self.BuildBoneRotator then self.BuildBoneRotator:Destroy() self.BuildBoneRotator = nil end end, IdleState = State { Main = function(self) self:ChangeBlinkingLights('Green') self:SetBusy(false) self:DestroyBuildRotator() end, }, BuildingState = State { Main = function(self) local unitBuilding = self.UnitBeingBuilt local bp = self:GetBlueprint() local bone = bp.Display.BuildAttachBone or 0 self:DetachAll(bone) unitBuilding:AttachBoneTo(-2, self, bone) self:CreateBuildRotator() self:StartBuildFx(unitBuilding) end, }, RollingOffState = State { Main = function(self) self:SetBusy(true) self:PlayFxRollOff() # Wait until unit has left the factory while not self.UnitBeingBuilt:IsDead() and self.MoveCommand and not IsCommandDone(self.MoveCommand) do WaitSeconds(0.5) end self.MoveCommand = nil self:PlayFxRollOffEnd() self:SetBusy(false) ChangeState(self, self.IdleState) end, }, } #------------------------------------------------------------- # AIR FACTORY UNITS #------------------------------------------------------------- AirFactoryUnit = Class(FactoryUnit) { } #------------------------------------------------------------- # AIR STAGING PLATFORMS UNITS #------------------------------------------------------------- AirStagingPlatformUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) self:SetMaintenanceConsumptionActive() end, } #------------------------------------------------------------- # ENERGY CREATION UNITS #------------------------------------------------------------- ConcreteStructureUnit = Class(StructureUnit) { OnCreate = function(self) StructureUnit.OnCreate(self) self:Destroy() end } #------------------------------------------------------------- # ENERGY CREATION UNITS #------------------------------------------------------------- EnergyCreationUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, } #------------------------------------------------------------- # ENERGY STORAGE UNITS #------------------------------------------------------------- EnergyStorageUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) local aiBrain = GetArmyBrain(self:GetArmy()) aiBrain:ESRegisterUnitEnergyStorage(self) local curEnergy = aiBrain:GetEconomyStoredRatio('ENERGY') if curEnergy > 0.11 then self:CreateBlinkingLights('Yellow') else self:CreateBlinkingLights('Red') end end, OnEnergyStorageStateChange = function(self, newState) if newState == 'EconLowEnergyStore' then self:CreateBlinkingLights('Red') elseif newState == 'EconMidEnergyStore' then self:CreateBlinkingLights('Yellow') elseif newState == 'EconFullEnergyStore' then self:CreateBlinkingLights('Green') end end, } #------------------------------------------------------------- # LAND FACTORY UNITS #------------------------------------------------------------- LandFactoryUnit = Class(FactoryUnit) {} #------------------------------------------------------------- # MASS COLLECTION UNITS #------------------------------------------------------------- MassCollectionUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, OnCreate = function(self) StructureUnit.OnCreate(self) local markers = ScenarioUtils.GetMarkers() local unitPosition = self:GetPosition() for k, v in pairs(markers) do if(v.type == 'MASS') then local massPosition = v.position if( (massPosition[1] < unitPosition[1] + 1) and (massPosition[1] > unitPosition[1] - 1) and (massPosition[2] < unitPosition[2] + 1) and (massPosition[2] > unitPosition[2] - 1) and (massPosition[3] < unitPosition[3] + 1) and (massPosition[3] > unitPosition[3] - 1)) then self:SetProductionPerSecondMass(self:GetProductionPerSecondMass() * (v.amount / 100)) break end end end end, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) self:SetMaintenanceConsumptionActive() end, # Shut down production while upgrading OnStartBuild = function(self, unitbuilding, order) StructureUnit.OnStartBuild(self, unitbuilding, order) #self:SetProductionActive(false) end, OnStopBuild = function(self, unitbuilding, order) StructureUnit.OnStopBuild(self, unitbuilding, order) #self:SetProductionActive(true) end, OnPaused = function(self) StructureUnit.OnPaused(self) self:SetMaintenanceConsumptionInactive() self:SetProductionActive(false) end, OnUnpaused = function(self) StructureUnit.OnUnpaused(self) self:SetMaintenanceConsumptionActive() self:SetProductionActive(true) end, } #------------------------------------------------------------- # MASS FABRICATION UNITS #------------------------------------------------------------- MassFabricationUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) self:SetMaintenanceConsumptionActive() self:SetProductionActive(true) end, OnConsumptionActive = function(self) StructureUnit.OnConsumptionActive(self) self:SetMaintenanceConsumptionActive() self:SetProductionActive(true) end, OnConsumptionInActive = function(self) StructureUnit.OnConsumptionInActive(self) self:SetMaintenanceConsumptionInactive() self:SetProductionActive(false) end, OnPaused = function(self) StructureUnit.OnPaused(self) self:SetMaintenanceConsumptionInactive() self:SetProductionActive(false) end, OnUnpaused = function(self) StructureUnit.OnUnpaused(self) self:SetMaintenanceConsumptionActive() self:SetProductionActive(true) end, } #------------------------------------------------------------- # MASS STORAGE UNITS #------------------------------------------------------------- MassStorageUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) local aiBrain = GetArmyBrain(self:GetArmy()) aiBrain:ESRegisterUnitMassStorage(self) local curMass = aiBrain:GetEconomyStoredRatio('MASS') if curMass > 0.11 then self:CreateBlinkingLights('Yellow') else self:CreateBlinkingLights('Red') end end, OnMassStorageStateChange = function(self, newState) if newState == 'EconLowMassStore' then self:CreateBlinkingLights('Red') elseif newState == 'EconMidMassStore' then self:CreateBlinkingLights('Yellow') elseif newState == 'EconFullMassStore' then self:CreateBlinkingLights('Green') end end, } #------------------------------------------------------------- # RADAR UNITS #------------------------------------------------------------- RadarUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, # Shut down intel while upgrading OnStartBuild = function(self, unitbuilding, order) StructureUnit.OnStartBuild(self, unitbuilding, order) self:SetMaintenanceConsumptionInactive() end, # If we abort the upgrade, re-enable the intel OnStopBuild = function(self, unitBeingBuilt) StructureUnit.OnStopBuild(self, unitBeingBuilt) self:SetMaintenanceConsumptionActive() end, # If we abort the upgrade, re-enable the intel OnFailedToBuild = function(self) StructureUnit.OnStopBuild(self) self:SetMaintenanceConsumptionActive() end, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) self:SetMaintenanceConsumptionActive() end, OnIntelDisabled = function(self) StructureUnit.OnIntelDisabled(self) self:DestroyBlinkingLights() self:CreateBlinkingLights('Red') end, OnIntelEnabled = function(self) StructureUnit.OnIntelEnabled(self) self:DestroyBlinkingLights() self:CreateBlinkingLights('Green') end, } #------------------------------------------------------------- # RADAR UNITS #------------------------------------------------------------- RadarJammerUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, # Shut down intel while upgrading OnStartBuild = function(self, unitbuilding, order) StructureUnit.OnStartBuild(self, unitbuilding, order) self:SetMaintenanceConsumptionInactive() self:DisableIntel('Jammer') self:DisableIntel('RadarStealthField') end, # If we abort the upgrade, re-enable the intel OnStopBuild = function(self, unitBeingBuilt) StructureUnit.OnStopBuild(self, unitBeingBuilt) self:SetMaintenanceConsumptionActive() self:EnableIntel('Jammer') self:EnableIntel('RadarStealthField') end, # If we abort the upgrade, re-enable the intel OnFailedToBuild = function(self) StructureUnit.OnStopBuild(self) self:SetMaintenanceConsumptionActive() self:EnableIntel('Jammer') self:EnableIntel('RadarStealthField') end, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) self:SetMaintenanceConsumptionActive() end, OnIntelEnabled = function(self) StructureUnit.OnIntelEnabled(self) if self.IntelEffects then self.IntelEffectsBag = {} self.CreateTerrainTypeEffects( self, self.IntelEffects, 'FXIdle', self:GetCurrentLayer(), nil, self.IntelEffectsBag ) end end, OnIntelDisabled = function(self) StructureUnit.OnIntelDisabled(self) EffectUtil.CleanupEffectBag(self,'IntelEffectsBag') end, } #------------------------------------------------------------- # SONAR UNITS #------------------------------------------------------------- SonarUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, # Shut down intel while upgrading OnStartBuild = function(self, unitbuilding, order) StructureUnit.OnStartBuild(self, unitbuilding, order) self:SetMaintenanceConsumptionInactive() self:DisableIntel('Sonar') end, # If we abort the upgrade, re-enable the intel OnStopBuild = function(self, unitBeingBuilt) StructureUnit.OnStopBuild(self, unitBeingBuilt) self:SetMaintenanceConsumptionActive() self:EnableIntel('Sonar') end, # If we abort the upgrade, re-enable the intel OnFailedToBuild = function(self) StructureUnit.OnStopBuild(self) self:SetMaintenanceConsumptionActive() self:EnableIntel('Sonar') end, OnStopBeingBuilt = function(self,builder,layer) StructureUnit.OnStopBeingBuilt(self,builder,layer) self:SetMaintenanceConsumptionActive() end, CreateIdleEffects = function(self) StructureUnit.CreateIdleEffects(self) self.TimedSonarEffectsThread = self:ForkThread( self.TimedIdleSonarEffects ) end, TimedIdleSonarEffects = function( self ) local layer = self:GetCurrentLayer() local army = self:GetArmy() local pos = self:GetPosition() if self.TimedSonarTTIdleEffects then while not self:IsDead() do for kTypeGroup, vTypeGroup in self.TimedSonarTTIdleEffects do local effects = self.GetTerrainTypeEffects( 'FXIdle', layer, pos, vTypeGroup.Type, nil ) for kb, vBone in vTypeGroup.Bones do for ke, vEffect in effects do emit = CreateAttachedEmitter(self,vBone,army,vEffect):ScaleEmitter(vTypeGroup.Scale or 1) if vTypeGroup.Offset then emit:OffsetEmitter(vTypeGroup.Offset[1] or 0, vTypeGroup.Offset[2] or 0,vTypeGroup.Offset[3] or 0) end end end end self:PlayUnitSound('Sonar') WaitSeconds( 6.0 ) end end end, DestroyIdleEffects = function(self) self.TimedSonarEffectsThread:Destroy() StructureUnit.DestroyIdleEffects(self) end, } #------------------------------------------------------------- # SEA FACTORY UNITS #------------------------------------------------------------- SeaFactoryUnit = Class(FactoryUnit) { # Disable the default rocking behavior StartRocking = function(self) end, StopRocking = function(self) end, } #------------------------------------------------------------- # SHIELD STRCUTURE UNITS #------------------------------------------------------------- ShieldStructureUnit = Class(StructureUnit) { UpgradingState = State(StructureUnit.UpgradingState) { Main = function(self) self.MyShield:TurnOff() StructureUnit.UpgradingState.Main(self) end, OnFailedToBuild = function(self) self.MyShield:TurnOn() StructureUnit.UpgradingState.OnFailedToBuild(self) end, } } #------------------------------------------------------------- # TRANSPORT BEACON UNITS #------------------------------------------------------------- TransportBeaconUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, FxTransportBeacon = {'/effects/emitters/red_beacon_light_01_emit.bp'}, #{'/effects/emitters/red_smoke_beacon_01_emit.bp'}, FxTransportBeaconScale = 1, # invincibility! (the only way to kill a transport beacon is # to kill the transport unit generating it) OnDamage = function(self, instigator, amount, vector, damageType) end, OnCreate = function(self) StructureUnit.OnCreate(self) self:HideBone(0, true) for k, v in self.FxTransportBeacon do self.Trash:Add(CreateAttachedEmitter(self, 0,self:GetArmy(), v):ScaleEmitter(self.FxTransportBeaconScale)) #self.Trash:Add(CreateAttachedEmitter(self, 0, self:GetArmy(), v)) end end, } #------------------------------------------------------------- # WALL STRCUTURE UNITS #------------------------------------------------------------- WallStructureUnit = Class(StructureUnit) { LandBuiltHiddenBones = {'Floatation'}, } #------------------------------------------------------------- # QUANTUM GATE UNITS #------------------------------------------------------------- QuantumGateUnit = Class(FactoryUnit) { } ################################################################# ## MOBILE UNITS ################################################################# MobileUnit = Class(Unit) { StartBeingBuiltEffects = function(self, builder, layer) Unit.StartBeingBuiltEffects(self, builder, layer) end, StopBeingBuiltEffects = function(self, builder, layer) Unit.StopBeingBuiltEffects(self, builder, layer) end, StartBuildingEffects = function(self, unitBeingBuilt, order) Unit.StartBuildingEffects(self, unitBeingBuilt, order) end, StopBuildingEffects = function(self, unitBeingBuilt) Unit.StopBuildingEffects(self, unitBeingBuilt) end, CreateReclaimEffects = function( self, target ) EffectUtil.PlayReclaimEffects( self, target, self.BuildBones.BuildEffectBones or {0,}, self.ReclaimEffectsBag ) end, CreateCaptureEffects = function( self, target ) EffectUtil.PlayCaptureEffects( self, target, self.BuildBones.BuildEffectBones or {0,}, self.CaptureEffectsBag ) end, } #------------------------------------------------------------- # WALKING LAND UNITS #------------------------------------------------------------- WalkingLandUnit = Class(MobileUnit) { WalkingAnim = nil, WalkingAnimRate = 1, IdleAnim = false, IdleAnimRate = 1, DeathAnim = false, DisabledBones = {}, OnCreate = function(self) Unit.OnCreate(self) # Grab the walk anim from the unit blueprint, if it exists local myBlueprint = self:GetBlueprint() if myBlueprint.Display.AnimationWalk then self.WalkingAnim = myBlueprint.Display.AnimationWalk if myBlueprint.Display.AnimationWalkRate then self.WalkingAnimRate = myBlueprint.Display.AnimationWalkRate end end if myBlueprint.Display.AnimationIdle then self.IdleAnim = myBlueprint.Display.AnimationIdle end end, OnMotionHorzEventChange = function( self, new, old ) Unit.OnMotionHorzEventChange(self, new, old) if ( old == 'Stopped' ) then if (not self.Animator) then self.Animator = CreateAnimator(self, true) end if self.WalkingAnim then self.Animator:PlayAnim(self.WalkingAnim, true) if self.WalkingAnimRate then self.Animator:SetRate(self.WalkingAnimRate) end end elseif ( new == 'Stopped' ) then # only keep the animator around if we are dying and playing a death anim # or if we have an idle anim if(self.IdleAnim and not self:IsDead()) then self.Animator:PlayAnim(self.IdleAnim, true) elseif(not self.DeathAnim or not self:IsDead()) then self.Animator:Destroy() self.Animator = false end end end, } #------------------------------------------------------------- # SUB UNITS # These units typically float under the water and have wake when they move. #------------------------------------------------------------- SubUnit = Class(MobileUnit) { # use default spark effect until underwater damaged states are made FxDamage1 = {EffectTemplate.DamageSparks01}, FxDamage2 = {EffectTemplate.DamageSparks01}, FxDamage3 = {EffectTemplate.DamageSparks01}, # DESTRUCTION PARAMS HideUnitOnDestroy = false, PlayDestructionEffects = true, ShowUnitDestructionDebris = false, DeathThreadDestructionWaitTime = 10, OnKilled = function(self, instigator, type, overkillRatio) local layer = self:GetCurrentLayer() self:DestroyIdleEffects() if(layer == 'Water' or layer == 'Seabed' or layer == 'Sub')then self.SinkExplosionThread = self:ForkThread(self.ExplosionThread) self.SinkThread = self:ForkThread(self.SinkingThread) end Unit.OnKilled(self, instigator, type, overkillRatio) end, ExplosionThread = function(self) local maxcount = Random(17,20) # max number of above surface explosions. timed to animation local d = 0 # delay offset after surface explosions cease local sx, sy, sz = self:GetUnitSizes() local vol = sx * sy * sz local volmin = 1.5 local volmax = 15 local scalemin = 1 local scalemax = 3 local t = (vol-volmin)/(volmax-volmin) local rs = scalemin + (t * (scalemax-scalemin)) if rs < scalemin then rs = scalemin elseif rs > scalemax then rs = scalemax end local army = self:GetArmy() CreateEmitterAtEntity(self,army,'/effects/emitters/destruction_underwater_explosion_flash_01_emit.bp'):ScaleEmitter(rs) CreateEmitterAtEntity(self,army,'/effects/emitters/destruction_underwater_explosion_splash_02_emit.bp'):ScaleEmitter(rs) CreateEmitterAtEntity(self,army,'/effects/emitters/destruction_underwater_explosion_surface_ripples_01_emit.bp'):ScaleEmitter(rs) while true do local rx, ry, rz = self:GetRandomPoint(1) local rs = Random(vol/2, vol*2) / (vol*2) CreateEmitterAtEntity(self,army,'/effects/emitters/destruction_underwater_explosion_flash_01_emit.bp'):ScaleEmitter(rs):OffsetEmitter(rx, ry, rz) CreateEmitterAtEntity(self,army,'/effects/emitters/destruction_underwater_explosion_splash_01_emit.bp'):ScaleEmitter(rs):OffsetEmitter(rx, ry, rz) d = d + 1 # increase delay offset local rd = Random(30,70) / 10 WaitTicks(rd + d) end end, } #------------------------------------------------------------- # AIR UNITS #------------------------------------------------------------- AirUnit = Class(MobileUnit) { # Contrails ContrailEffects = {'/effects/emitters/contrail_polytrail_01_emit.bp',}, BeamExhaustCruise = '/effects/emitters/air_move_trail_beam_03_emit.bp', BeamExhaustIdle = '/effects/emitters/air_idle_trail_beam_01_emit.bp', # DESTRUCTION PARAMS ShowUnitDestructionDebris = false, DestructionExplosionWaitDelayMax = 0, DestroyNoFallRandomChance = 0.5, OnCreate = function(self) Unit.OnCreate(self) self:AddPingPong() end, AddPingPong = function(self) local bp = self:GetBlueprint() if bp.Display.PingPongScroller then bp = bp.Display.PingPongScroller if bp.Ping1 and bp.Ping1Speed and bp.Pong1 and bp.Pong1Speed and bp.Ping2 and bp.Ping2Speed and bp.Pong2 and bp.Pong2Speed then self:AddPingPongScroller(bp.Ping1, bp.Ping1Speed, bp.Pong1, bp.Pong1Speed, bp.Ping2, bp.Ping2Speed, bp.Pong2, bp.Pong2Speed) end end end, OnMotionVertEventChange = function( self, new, old ) Unit.OnMotionVertEventChange( self, new, old ) #LOG( 'OnMotionVertEventChange, new = ', new, ', old = ', old ) local army = self:GetArmy() if (new == 'Down') then # Turn off the ambient hover sound self:PlayUnitAmbientSound( nil ) elseif (new == 'Bottom') then # While landed, planes can only see half as far local vis = self:GetBlueprint().Intel.VisionRadius / 2 self:SetIntelRadius('Vision', vis) # Turn off the ambient hover sound # It will probably already be off, but there are some odd cases that # make this a good idea to include here as well. self:PlayUnitAmbientSound( nil ) elseif (new == 'Up' or ( new == 'Top' and ( old == 'Down' or old == 'Bottom' ))) then # Set the vision radius back to default local bpVision = self:GetBlueprint().Intel.VisionRadius if bpVision then self:SetIntelRadius('Vision', bpVision) else self:SetIntelRadius('Vision', 0) end end end, OnRunOutOfFuel = function(self) Unit.OnRunOutOfFuel(self) # penalize movement for running out of fuel self:SetSpeedMult(0.25) # change the speed of the unit by this mult self:SetAccMult(0.25) # change the acceleration of the unit by this mult self:SetTurnMult(0.25) # change the turn ability of the unit by this mult end, OnGotFuel = function(self) Unit.OnGotFuel(self) # revert these values to the blueprint values self:SetSpeedMult(1) self:SetAccMult(1) self:SetTurnMult(1) end, OnImpact = function(self, with, other) # Damage the area we have impacted with. local bp = self:GetBlueprint() local i = 1 local numWeapons = table.getn(bp.Weapon) for i, numWeapons in bp.Weapon do if(bp.Weapon[i].Label == 'DeathImpact') then DamageArea(self, self:GetPosition(), bp.Weapon[i].DamageRadius, bp.Weapon[i].Damage, bp.Weapon[i].DamageType, bp.Weapon[i].DamageFriendly) break end end if with == 'Water' then self:PlayUnitSound('AirUnitWaterImpact') EffectUtil.CreateEffects( self, self:GetArmy(), EffectTemplate.DefaultProjectileWaterImpact ) self:Destroy() else # This is a bit of safety to keep us from calling the death thread twice in case we bounce twice quickly if not self.DeathBounce then self:ForkThread(self.DeathThread, self.overkillRatio ) self.DeathBounce = 1 end end end, CreateUnitAirDestructionEffects = function( self, scale ) explosion.CreateDefaultHitExplosion( self, 1.0 ) explosion.CreateDebrisProjectiles(self, explosion.GetAverageBoundingXYZRadius(self), {self:GetUnitSizes()}) end, # ON KILLED: THIS FUNCTION PLAYS WHEN THE UNIT TAKES A MORTAL HIT. IT PLAYS ALL THE DEFAULT DEATH EFFECT # IT ALSO SPAWNS THE WRECKAGE BASED UPON HOW MUCH IT WAS OVERKILLED. UNIT WILL SPIN OUT OF CONTROL TOWARDS # GROUND AND WHEN IT IMPACTS IT WILL DESTROY ITSELF OnKilled = function(self, instigator, type, overkillRatio) if self:GetCurrentLayer() != 'Land' and Random() < self.DestroyNoFallRandomChance then self:DestroyBeamExhaust() self.overKillRatio = overkillRatio self.CreateUnitAirDestructionEffects( self, 1.0 ) self:PlayUnitSound('Killed') self:DestroyTopSpeedEffects() self:DoOnKilledCallbacks() self:OnKilledVO() if instigator and instigator:IsUnit() then instigator:OnKilledUnit(self) end else self.DeathBounce = 1 Unit.OnKilled(self, instigator, type, overkillRatio) end end, } #------------------------------------------------------------- # HOVERING LAND UNITS #------------------------------------------------------------- HoverLandUnit = Class(MobileUnit){} #------------------------------------------------------------- # LAND UNITS #------------------------------------------------------------- LandUnit = Class(MobileUnit) {} #------------------------------------------------------------- # CONSTRUCTION UNITS #------------------------------------------------------------- ConstructionUnit = Class(MobileUnit) { OnCreate = function(self) Unit.OnCreate(self) self.EffectsBag = {} if self.BuildBones then self:SetupBuildBones() end if self:GetBlueprint().Display.AnimationBuild then self.BuildingOpenAnim = self:GetBlueprint().Display.AnimationBuild end if self.BuildingOpenAnim then self.BuildingOpenAnimManip = CreateAnimator(self) self.BuildingOpenAnimManip:SetPrecedence(1) self.BuildingOpenAnimManip:PlayAnim(self.BuildingOpenAnim, false):SetRate(0) if self.BuildArmManipulator then self.BuildArmManipulator:Disable() end end self.BuildingUnit = false end, OnPaused = function(self) #When factory is paused take some action self:PlayUnitAmbientSound( nil ) MobileUnit.OnPaused(self) end, OnUnpaused = function(self) if self.BuildingUnit then self:PlayUnitAmbientSound( 'ConstructLoop' ) end MobileUnit.OnUnpaused(self) end, OnStartBuild = function(self, unitBeingBuilt, order ) Unit.OnStartBuild(self,unitBeingBuilt, order) #Fix up info on the unit id from the blueprint and see if it matches the 'UpgradeTo' field in the BP. local unitid = self:GetBlueprint().General.UpgradesTo self.UnitBeingBuilt = unitBeingBuilt self.BuildingUnit = true if unitBeingBuilt:GetUnitId() == unitid and order == 'Upgrade' then self.Upgrading = true self.BuildingUnit = false end end, OnStopBuild = function(self, unitBeingBuilt) Unit.OnStopBuild(self,unitBeingBuilt) if self.Upgrading then NotifyUpgrade(self,unitBeingBuilt) self:Destroy() end self.UnitBeingBuilt = nil if self.BuildingOpenAnimManip and self.BuildArmManipulator then self.StoppedBuilding = true elseif self.BuildingOpenAnimManip then self.BuildingOpenAnimManip:SetRate(-1) end self.BuildingUnit = false end, WaitForBuildAnimation = function(self, enable) if self.BuildArmManipulator then WaitFor(self.BuildingOpenAnimManip) #LOG( 'Finished waiting in WaitForBuildAnimation' ) if (enable) then self.BuildArmManipulator:Enable() end end end, OnPrepareArmToBuild = function(self) Unit.OnPrepareArmToBuild(self) #LOG( 'OnPrepareArmToBuild' ) if self.BuildingOpenAnimManip then self.BuildingOpenAnimManip:SetRate(1) if self.BuildArmManipulator then self.StoppedBuilding = false ForkThread( self.WaitForBuildAnimation, self, true ) end end end, OnStopBuilderTracking = function(self) Unit.OnStopBuilderTracking(self) if self.StoppedBuilding then self.StoppedBuilding = false self.BuildArmManipulator:Disable() self.BuildingOpenAnimManip:SetRate(-1) end end, CheckBuildRestriction = function(self, target_bp) return true end, } #------------------------------------------------------------- # SEA UNITS # These units typically float on the water and have wake when they move. #------------------------------------------------------------- SeaUnit = Class(MobileUnit) { DeathThreadDestructionWaitTime = 5, ShowUnitDestructionDebris = false, HideUnitOnDestroy = false, # by default, just destroy us when we are killed. OnKilled = function(self, instigator, type, overkillRatio) local layer = self:GetCurrentLayer() self:DestroyIdleEffects() if(layer == 'Water' or layer == 'Seabed' or layer == 'Sub')then self.SinkExplosionThread = self:ForkThread(self.ExplosionThread) self.SinkThread = self:ForkThread(self.SinkingThread) end Unit.OnKilled(self, instigator, type, overkillRatio) end, ExplosionThread = function(self) local maxcount = Util.GetRandomInt(6,20) # max number of above surface explosions. timed to animation local i = maxcount # initializing the above surface counter local d = 0 # delay offset after surface explosions cease local sx, sy, sz = self:GetUnitSizes() local vol = sx * sy * sz local army = self:GetArmy() local numBones = self:GetBoneCount() - 1 while true do if i > 0 then local rx, ry, rz = self:GetRandomPoint(1) local rs = Random(vol/2, vol*2) / (vol*2) explosion.CreateDefaultHitExplosionAtBone( self, Util.GetRandomInt( 0, numBones), 1.0 ) else d = d + 1 # if submerged, increase delay offset self:DestroyAllDamageEffects() end i = i - 1 local rx, ry, rz = self:GetRandomPoint(1) local rs = Random(vol/2, vol*2) / (vol*2) CreateEmitterAtEntity(self,army,'/effects/emitters/destruction_underwater_explosion_flash_01_emit.bp'):OffsetEmitter(rx, ry, rz):ScaleEmitter(rs) CreateEmitterAtEntity(self,army,'/effects/emitters/destruction_underwater_explosion_splash_01_emit.bp'):OffsetEmitter(rx, ry, rz):ScaleEmitter(rs) local rd = Util.GetRandomFloat( 0.4, 1.0) WaitSeconds(rd) end end, SinkingThread = function(self) local i = 8 # initializing the above surface counter local sx, sy, sz = self:GetUnitSizes() local vol = sx * sy * sz local army = self:GetArmy() while true do if i > 0 then local rx, ry, rz = self:GetRandomPoint(1) local rs = Random(vol/2, vol*2) / (vol*2) #previous random = (25, 100) / 100 CreateAttachedEmitter(self,-1,army,'/effects/emitters/destruction_water_sinking_ripples_01_emit.bp'):OffsetEmitter(rx * 0.5, 0, rz):ScaleEmitter(rs) local rx, ry, rz = self:GetRandomPoint(1) local rs = Random(vol/2, vol*2) / (vol*2) CreateAttachedEmitter(self,self.LeftFrontWakeBone,army, '/effects/emitters/destruction_water_sinking_wash_01_emit.bp'):OffsetEmitter(rx, 0, rz):ScaleEmitter(rs) local rx, ry, rz = self:GetRandomPoint(1) local rs = Random(vol/2, vol*2) / (vol*2) CreateAttachedEmitter(self,self.RightFrontWakeBone,army, '/effects/emitters/destruction_water_sinking_wash_01_emit.bp'):OffsetEmitter(rx, 0, rz):ScaleEmitter(rs) end local rx, ry, rz = self:GetRandomPoint(1) local rs = Random(vol/2, vol*2) / (vol*2) CreateAttachedEmitter(self,-1,army,'/effects/emitters/destruction_underwater_sinking_wash_01_emit.bp'):OffsetEmitter(rx, 0, rz):ScaleEmitter(rs) i = i - 1 WaitSeconds(1) end end, } #------------------------------------------------------------- # SHIELD HOVER UNITS #------------------------------------------------------------- ShieldHoverLandUnit = Class(HoverLandUnit) { } #------------------------------------------------------------- # SHIELD LAND UNITS #------------------------------------------------------------- ShieldLandUnit = Class(LandUnit) { }