--[[#######################################################################
#  File     :  /hook/lua/DefaultUnits.lua
#  Author(s):  John Comes, Gordon Duclos
#  Summary  :  TOTAL HOOK of Default definitions of units
#  -----------------------------
#  Modif.by :  Manimal
#  Rev.Date :  14 décembre 2009
#  -----------------------------
#  Revis.by :  Manimal
#  Rev.Date :  09 juin 2010
#  Rev.Date :  20 juillet 2011 -> Refonte pour compatibilité maximale
#  -----------------------------
#  Copyright © 2005 Gas Powered Games, Inc.  All rights reserved.
#######################################################################]]--

# ORIGINAL = 1761 LIGNES

# VOIR AirUnit.OnKilled : BUG POSSIBLE VIA MobileUnit.OnKilled MANQUANT ?

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# NOTE : CONSERVER LES TRACEURS PENDANT LA MISE AU POINT DE ExpWars v 2 !!!
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


--[[
]]--
#===============================================================================
local Game = import( '/lua/game.lua' )

# VARIABLE ''GLOBALE'' ( par Manimal )
local MyModPath = Game.MyModPath

--local mesUtiles = import(  MyModPath .. '/schook/lua/system/mesUtiles.lua'  )
--local TraceurTable = mesUtiles.table.AfficheTraceur
#===============================================================================



#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# NOTE :
# HOOK DES MODULES IMPORTÉS DE /lua/defaultunits.lua 
# AFIN DE FORCER StructureUnit ET FactoryUnit À PRENDRE EN COMPTE MES MODIFS
# DE LA CLASSE Unit ( OnKilled + OnDestroy, voir /hook/lua/sim/Unit.lua )
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Unit = import(  MyModPath .. '/hook/lua/sim/Unit.lua'  ).Unit
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# NOTE : CETTE DÉFINITION DE Entity EST INUTILISÉE DANS LE FICHIER ORIGINAL !
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Entity = import( '/lua/sim/Entity.lua' ).Entity  # COMMENT OUT THIS ONE ?
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# NOTE : CETTE DÉFINITION DE Shield EST INUTILISÉE DANS LE FICHIER ORIGINAL !
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local Shield = import( '/lua/shield.lua' ).Shield
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local explosion = import( '/lua/defaultexplosions.lua' )
local EffectUtil = import( '/lua/EffectUtilities.lua' )
local EffectTemplate = import( '/lua/EffectTemplates.lua' )
local Buff = import( '/lua/sim/Buff.lua' )
local AdjacencyBuffs = import( '/lua/sim/AdjacencyBuffs.lua' )
local Util = import( '/lua/utilities.lua' )
local ScenarioUtils = import( '/lua/sim/ScenarioUtilities.lua' )




#################################################################
##  MISC UNITS
#################################################################

--# Classe NON Modifiée.
--# Forcer la prise en compte de ma version de la Classe Unit.

DummyUnit = Class( Unit ) {
    OnStopBeingBuilt = function( self,builder,layer )
        self:Destroy()
    end,
}


#################################################################
##  STRUCTURE UNITS
#################################################################

--# Classe NON Modifiée.
--# Forcer la prise en compte de ma version de la Classe Unit.

StructureUnit = Class( Unit ) {
    LandBuiltHiddenBones = {'Floatation'},
    MinConsumptionPerSecondEnergy = 1,
    MinWeaponRequiresEnergy = 0,
    
    # Stucture unit specific damage effects and smoke
    FxDamage1 = { EffectTemplate.DamageStructureSmoke01, EffectTemplate.DamageStructureSparks01 },
    FxDamage2 = { EffectTemplate.DamageStructureFireSmoke01, EffectTemplate.DamageStructureSparks01 },
    FxDamage3 = { EffectTemplate.DamageStructureFire01, EffectTemplate.DamageStructureSparks01 },    

    OnCreate = function( self )
        Unit.OnCreate( self )
        self.WeaponMod = {}
        self.FxBlinkingLightsBag = {} 
        if self:GetCurrentLayer() == 'Land' and self:GetBlueprint().Physics.FlattenSkirt then
            self:FlattenSkirt()
            # Units creating structure units tell unit to create the tarmac.
            # This left here to help with F2 unit creation and testing.
            #self:CreateTarmac( true, true, true, false, false )
        end        
    end,

    OnStopBeingBuilt = function( self, builder, layer )
        Unit.OnStopBeingBuilt( self, builder, layer )
        self:PlayActiveAnimation()
    end,

    OnFailedToBeBuilt = function( self )
        Unit.OnFailedToBeBuilt( self )
        self:DestroyTarmac()
    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 )
        if self:GetCurrentLayer() != 'Land' then return end
        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() )
        
        #I'm disabling this for now since there are so many things wrong with it.
        #SetTerrainTypeRect( self.tarmacRect, {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
        
        local GetTarmac = import( '/lua/tarmacs.lua' ).GetTarmacType
        
        local terrain = GetTerrainType( x, z )
        local terrainName
        if terrain then
            terrainName = terrain.Name
        end
        #Players and AI can build buildings outside of their faction. Get the *building's* faction to determine the correct tarrain-specific tarmac
        local factionTable = {e=1, a=2, r=3, s=4}
        local faction  = factionTable[string.sub( self:GetUnitId(),2,2 )]

        if albedo and tarmac.Albedo then
            local albedo2 = tarmac.Albedo2
            if albedo2 then 
                albedo2 = albedo2 .. GetTarmac( faction, terrain )
            end
            
            local tarmacHndl = CreateDecal( self:GetPosition(), orient, tarmac.Albedo .. GetTarmac( faction, terrainName ) , albedo2 or '', 'Albedo', w, l, fadeout, lifeTime or 0, army, 0 )
            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 .. GetTarmac( faction, terrainName ), '', 'Alpha Normals', w, l, fadeout, lifeTime or 0, army, 0 )
            
            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 .. GetTarmac( faction, terrainName ), '', 'Glow', w, l, fadeout, lifeTime or 0, army, 0 )
            
            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,
    
    HasTarmac = function( self )
        if not self.TarmacBag then return false end
        return ( table.getn( self.TarmacBag.Decals ) != 0 )
    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:PlayUnitSound( 'UpgradeStart' )
            self:DisableDefaultToggleCaps()
            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 )
            self:EnableDefaultToggleCaps()
            
            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:EnableDefaultToggleCaps()
            
            if self.AnimatorUpgradeManip then self.AnimatorUpgradeManip:Destroy() end
            
            if self:GetCurrentLayer() == 'Water' then
                self:StartRocking()
            end
            self:PlayUnitSound( 'UpgradeFailed' )
            self:PlayActiveAnimation()
            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 )
			self.BeingBuiltShowBoneTriggered = false
			if bp.General.UpgradesFrom != builder:GetUnitId() then
				self:ForkThread(  EffectUtil.CreateBuildCubeThread, builder, self.OnBeingBuiltEffectsBag  )	
			end					
		elseif FactionName == 'Aeon' then
			if bp.General.UpgradesFrom != builder:GetUnitId() then
				self:ForkThread(  EffectUtil.CreateAeonBuildBaseThread, builder, self.OnBeingBuiltEffectsBag  )
			end
		elseif FactionName == 'Cybran' then
		elseif FactionName == 'Seraphim' then
			if bp.General.UpgradesFrom != builder:GetUnitId() then
				self:ForkThread(  EffectUtil.CreateSeraphimBuildBaseThread, builder, self.OnBeingBuiltEffectsBag  )
			end		
		end
    end,
    
    StopBeingBuiltEffects = function( self, builder, layer )
        local FactionName = self:GetBlueprint().General.FactionName
        if FactionName == 'Aeon' then
            WaitSeconds(  2.0  )
        elseif FactionName == 'UEF' and not self.BeingBuiltShowBoneTriggered then 
            self:ShowBone( 0, true )
            self:HideLandBones()            
        end
		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,
    
    PlayActiveAnimation = function( self )
        
    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
    #---------------------------------------------------------------------------------------------
    
    --#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
        
        local adjBuffs = self:GetBlueprint().Adjacency
        if not adjBuffs then return end
        
        for k,v in AdjacencyBuffs[adjBuffs] do
            Buff.ApplyBuff( adjacentUnit, v, self )
        end
        self:RequestRefreshUI()
        adjacentUnit:RequestRefreshUI()
    end,
    
    --#When we're not adjacent, try to remove all the possible bonuses.
    OnNotAdjacentTo = function( self, adjacentUnit )
        local adjBuffs = self:GetBlueprint().Adjacency
        if adjBuffs and AdjacencyBuffs[adjBuffs] then 
            for k,v in AdjacencyBuffs[adjBuffs] do
                if Buff.HasBuff( adjacentUnit, v ) then
                    Buff.RemoveBuff( adjacentUnit, v )
                end
            end
        end
        self:DestroyAdjacentEffects()
        
        self:RequestRefreshUI()
        adjacentUnit:RequestRefreshUI()
    end,

    #---------
    # Add/Remove Adjacency Effects
    #---------
    
    CreateAdjacentEffect = function( self, adjacentUnit )
        #Create trashbag to hold all these entities and beams
        if not self.AdjacencyBeamsBag then
            self.AdjacencyBeamsBag = {}
        end
        
        for k,v in self.AdjacencyBeamsBag do
            if v.Unit:GetEntityId() == adjacentUnit:GetEntityId() then
                return
            end
        end
            
		self:ForkThread(  EffectUtil.CreateAdjacencyBeams, adjacentUnit, self.AdjacencyBeamsBag  )
    end,

    DestroyAdjacentEffects = function( self, adjacentUnit )
        if not self.AdjacencyBeamsBag then return end
        for k, v in self.AdjacencyBeamsBag do
            # If any of the adjacent units are destroyed or the passed in unit is found: Kill the effect
            if v.Unit:BeenDestroyed() or v.Unit:IsDead() then #or v.Unit:GetEntityId() == adjacentUnit:GetEntityId() then
                v.Trash:Destroy()
                self.AdjacencyBeamsBag[k] = nil
            end
        end
    end,
    
}



#-------------------------------------------------------------
#  FACTORY  UNITS
#-------------------------------------------------------------

--# CLASSE MODIFIÉE !!!
--# Forcer la prise en compte de ma version de la Classe Unit via StructureUnit.

FactoryUnit = Class( StructureUnit ) {

    OnCreate = function( self )
        StructureUnit.OnCreate( self )
        self.BuildingUnit = false
    end,

    OnPaused = function( self )
        #When factory is paused take some action
        self:StopUnitAmbientSound(  'ConstructLoop'  )
        StructureUnit.OnPaused( self )
    end,

    OnUnpaused = function( self )
        if self.BuildingUnit then
            self:PlayUnitAmbientSound(  'ConstructLoop'  )
        end
        StructureUnit.OnUnpaused( self )
    end,

	--# ADDED BY MANIMAL

	OnStartBeingBuilt = function( self, builder, layer )
		StructureUnit.OnStartBeingBuilt(  self, builder, layer  )
	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,


--[[
]]--
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	# BUG FIX BY MANIMAL :
	# I NOTICED THAT MANY CLASSES DO NOT DEFIME OnStopBuild THE SAME WAY FOR MOST OF THE FACTORIES
	# WHEN OnStopBuild IS FIRED FROM AIR AND/OR NAVAL FACTORY UNITS, THE VALUE OF order IS nil !!!
	# THE PROBLEM LIES WITHIN xxxxunits.lua (xxxx = faction name) THAT DEFINES UNCONSISTENTLY OnStopBuild
	# FOR AIR + SEA + SUB FactoryUnit CLASSES, THAT DOES NOT MATCH AT ALL THE DEFINITION OF GENERIC FactoryUnit WITHIN defaultunits.lua !!!
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	# J'AI REMARQUÉ QUE PLUSIEURS CLASSES DÉFINISSENT OnStopBuild DIFFÉREMMENT POUR LA PLUPART DES USINES
	# PENDANT LA CONSTRUCTION D'UNITÉS AIR ET/OU NAVAL, LA VALEUR DE order EST nil !!!
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    OnStopBuild = function( self, unitBeingBuilt, order )
		--# INSPIRED BY CBFP V3
		--# the means to add a rolloff time between unit productions. Can be used to increase the time between the 
		--# construction of 2 units. Add to blueprint in general section variable name RolloffDelay and set time
		--# in seconds to halt after each produced unit.
        
        if not self.FactoryBuildFailed then
			local bp = self:GetBlueprint()
			if  bp.General.RolloffDelay  and  bp.General.RolloffDelay > 0  then
				self:ForkThread( self.PauseThread, unitBeingBuilt, order )
			else
				self.StopBuildingUnit( self, unitBeingBuilt, order )
			end
        end
    end,


	--# ADDED BY MANIMAL

	StopBuildingUnit = 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,


	--# INSPIRED BY CBFP V3
	--# takes care of the rolloff delay if required in the blueprint

    PauseThread = function( self, unitBeingBuilt, order )
        --# adds a pause between unit productions
        self:StopBuildFx()
        local productionpause = self:GetBlueprint().General.RolloffDelay
        if  productionpause  and  productionpause > 0  then
            self:SetBusy(true) 
            self:SetBlockCommandQueue(true) 
            WaitSeconds(productionpause)
            self:SetBusy(false) 
            self:SetBlockCommandQueue(false) 
        end
        self.StopBuildingUnit( self, unitBeingBuilt, order )
    end,

	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    FinishBuildThread = function( self, unitBeingBuilt, order )
        self:SetBusy( true )
        self:SetBlockCommandQueue( 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
        if unitBeingBuilt and not unitBeingBuilt:IsDead() then
            unitBeingBuilt:DetachFrom( true )
        end
        self:DetachAll( bp.Display.BuildAttachBone or 0 )
        self:DestroyBuildRotator()
        if order != 'Upgrade' then
            ChangeState( self, self.RollingOffState )
        else
            self:SetBusy( false )
            self:SetBlockCommandQueue( false )
        end
    end,


    CheckBuildRestriction = function( self, target_bp )
        if self:CanBuild( target_bp.BlueprintId ) then
            return true
        else
            return false
        end
    end,


# -> # CONTROLER LE BUT DE CE MODIF !!!

    OnFailedToBuild = function( self )
        self.FactoryBuildFailed = true        
        Unit.OnFailedToBuild( self )    --StructureUnit.OnFailedToBuild( self )
        self:DestroyBuildRotator()
        self:StopBuildFx()
        ChangeState( self, self.IdleState )
    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,


    RollOffUnit = function( self )
        local spin, x, y, z = self:CalculateRollOffPoint()
        local units = { self.UnitBeingBuilt }
        self.MoveCommand = IssueMove( units, Vector( x, y, z ) )
    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,


    RolloffBody = function( self )
        self:SetBusy( true )
        self:SetBlockCommandQueue( 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 )
        self:SetBlockCommandQueue( false )
        ChangeState( self, self.IdleState )
    end,


    IdleState = State {

        Main = function( self )
            self:ChangeBlinkingLights( 'Green' )
            self:SetBusy( false )
            self:SetBlockCommandQueue( 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:RolloffBody()
        end,
    },


	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	# BUG FIX BY MANIMAL :
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    OnKilled = function( self, instigator, type, overkillRatio )
        StructureUnit.OnKilled( self, instigator, type, overkillRatio )

		if self.UnitBeingBuilt and not self.UnitBeingBuilt:IsDead() and self.UnitBeingBuilt:GetFractionComplete() != 1 then
			# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			# MUDIF PAR MANIMAL : VOIR hook/lua/sim/unit.lua
			# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            --self.UnitBeingBuilt:Destroy()  # USELESS SINCE DIRECTLY MANAGED BY Unit.OnKilled ! ( INHERITANCE FROM StructureUnit. DOUBLE CHEKCED BY MANIMAL )
			# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        end
    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,


    OnStartBuild = function( self, unitbuilding, order )
        StructureUnit.OnStartBuild( self, unitbuilding, order )
        self:AddCommandCap( 'RULEUCC_Stop' )
        local massConsumption = self:GetConsumptionPerSecondMass()
        local massProduction = self:GetProductionPerSecondMass()
        self.UpgradeWatcher = self:ForkThread( self.WatchUpgradeConsumption, massConsumption, massProduction )
    end,

    OnStopBuild = function( self, unitbuilding, order )
        StructureUnit.OnStopBuild( self, unitbuilding, order )
        self:RemoveCommandCap( 'RULEUCC_Stop' )
        if self.UpgradeWatcher then
            KillThread( self.UpgradeWatcher )
            self:SetConsumptionPerSecondMass( 0 )
            self:SetProductionPerSecondMass( self:GetBlueprint().Economy.ProductionPerSecondMass or 0 )                  
        end  
    end,

    --# FIX BY RAGNAROK_X/COMBINE:  Pausing was handled incorrectly.  Fixed. [100]
    --# FIX BY GOWERLY: 
--[[
    # If you run out of mass, nothing happens.  If you run out of energy, your mass production from the mex 
    # will decrease as expected.  I've had to go insane to fix this. 
    # I got rid of most of the GPG code where it takes the mass production from the mass consumed, etc. 
    # That screws up your eco balance anyway, by lowering the proportion of mass in / mass out, which sucks. 
    # So I implemented a version where you keep both, giving you a better overall eco balance.  Well, it will 
    # when I find out how to implement it in less of a dark magic hack.  Yay. 
]]--
    
--[[
    --# band-aid on lack of multiple separate resource requests per unit...  
    --# If mass econ is depleted, take all the mass generated and use it for the upgrade
    WatchUpgradeConsumption = function( self, massConsumption, massProduction )
        while true do
            local fraction = self:GetResourceConsumed()
            --# If we're not getting our full request of energy and mass met...
            if fraction != 1 then
               --# check to see If our aiBrain has energy but no mass
               local aiBrain = self:GetAIBrain()
               if aiBrain then
                   local massStored = aiBrain:GetEconomyStored(  'MASS'  )
                   if massStored <= 1 then
                       self:SetConsumptionPerSecondMass( massConsumption - massProduction )
                       self:SetProductionPerSecondMass( 0 )
                   end
               end  
            elseif not self:IsPaused() then
               self:SetConsumptionPerSecondMass( massConsumption )
               self:SetProductionPerSecondMass( massProduction )
            end
            WaitSeconds( 0.2 )
        end
    end,     
]]--
    
    WatchUpgradeConsumption = function(self, massConsumption, massProduction) 
        while true do 
            if not self:IsPaused() then 
                if self:GetResourceConsumed() != 1 then 
                    local aiBrain = self:GetAIBrain() 
                    if aiBrain and aiBrain:GetEconomyStored('ENERGY') <= 1 then 
                        --# We are out of energy.  Do nothing.  Something else deals with this, it seems 
                        self:SetProductionPerSecondMass(massProduction) 
                        self:SetConsumptionPerSecondMass(massConsumption) 
                    else 
                        --[[
                        #Insanity by Gowerly, version 1. 
                        #So, if you run out of mass, there's something happening that divides the production by 
                        #how much you're missing mass by.  This sucks, and I Can't find out where this is happening 
                        #If someone knows where this happens, please let me know, so I can fix that instead.  
                        #It would be much less of a hack if I could do that. 
                        #So, I can work out how much I'm out mass by (proportionally) 
                        #and that's how much the mass production is divided by somewhere else 
                        #so if I do the OPPOSITE here, they CANCEL OUT LIKE MATTER AND ANTIMATTER AND I WIN 
                        #Um, so there's a bit when you start upgrading a mex where GetResourceConsumed is 0.  
                        #I have to bypass that, because otherwise you have Aleph-0 mass for a frame. 
                        ]]--
   
                        --[[
                        #Current Issue(s).  Mass goes wild for a second or two when you hit 0, 
                        #but levels out pretty quickly.  I would estimate you end up befitting by about 10 mass. 
                        #Looking at how to fix that, but it won't be easy.  As I end up saying 3 times in here, 
                        #if someone finds out where the mex actually calculates how much mass it's producing, 
                        #I'll give them a cookie.  Somehow. 
                        ]]--
                        if self:GetResourceConsumed() != 0 then 
                            self:SetConsumptionPerSecondMass(massConsumption) 
                            self:SetProductionPerSecondMass(massProduction / self:GetResourceConsumed()) 
                            #LOG('Out of mass, setting mex output to ', repr(massProduction / self:GetResourceConsumed()), ' to compensate') 
                        else 
                            --[[
                            #There's no logical way I can deal with you having 0 mass coming in (your ACU produces 1) 
                            #So if you're not playing Assassination and you're out of mass, I can't help you 
                            #PLEASE SOMEONE FIND ME WHERE THE MEX CALCULATES HOW MUCH MASS IT PRODUCES - GOWERLY 
                            ]]--
                            self:SetProductionPerSecondMass(0) 
                        end 
                    end                
                else 
                    --# Pause fix by R_X and Combine [100]
                    self:SetConsumptionPerSecondMass(massConsumption) 
                    self:SetProductionPerSecondMass(massProduction) 
                end 
            else 
                self:SetProductionPerSecondMass(massProduction) 
            end 
            WaitSeconds(0.2) 
        end 
    end,

    
    OnPaused = function( self )
        StructureUnit.OnPaused( self )
	end,

	OnUnpaused = function( self )
	    StructureUnit.OnUnpaused( self )
	end,

    OnProductionPaused = function( self )
        StructureUnit.OnProductionPaused( self )
        self:StopUnitAmbientSound(  'ActiveLoop'  )
    end,

    OnProductionUnpaused = function( self )
        StructureUnit.OnProductionUnpaused( self )
        self:PlayUnitAmbientSound(  'ActiveLoop'  )
    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 )
	end,

	OnUnpaused = function( self )
	    StructureUnit.OnUnpaused( self )
	end,
	
    OnProductionPaused = function( self )
        StructureUnit.OnProductionPaused( self )
        self:StopUnitAmbientSound(  'ActiveLoop'  )
    end,

    OnProductionUnpaused = function( self )
        StructureUnit.OnProductionUnpaused( self )
        self:PlayUnitAmbientSound(  'ActiveLoop'  )
    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'},
#Leave Radar on per design 11/14/06
#    # 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 )
#    OnStopBuild = function( self, unitBeingBuilt, order )
#        StructureUnit.OnStopBuild( self, unitBeingBuilt, order )
#        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:DestroyIdleEffects()
        self:DestroyBlinkingLights()
        self:CreateBlinkingLights( 'Red' )
    end,

    OnIntelEnabled = function( self )
        StructureUnit.OnIntelEnabled( self )
        self:DestroyBlinkingLights()
        self:CreateBlinkingLights( 'Green' )
        self:CreateIdleEffects()
    end,
}


#-------------------------------------------------------------
#  RADAR JAMMER 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,

	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	# BUG FIX BY MANIMAL :
	# I NOTICED THAT MANY CLASSES DO NOT DEFIME OnStopBuild THE SAME WAY FOR MOST OF THE FACTORIES
	# WHEN OnStopBuild IS FIRED FROM AIR AND/OR NAVAL FACOTRY UNITS, THE VALUE OF order IS nil !!!
	# THE PROBLEM LIES WITHIN xxxxunits.lua (xxxx = faction name) that defines unconsistently OnStopBuild
	# FOR AIR + SEA + SUB FactoryUnit CLASSES, THAT DOES NOT MATCH AT ALL THE DEFINITION OF GENERIC FactoryUnit within defaultunits.lua !!!
	# J'AI REMARQUÉ QUE PLUSIEURS CLASSES DÉFINISSENT OnStopBuild DIFFÉREMMENT POUR LA PLUPART DES USINES
	# PENDANT LA CONSTRUCTION D'UNITÉS AIR ET/OU NAVAL, LA VALEUR DE order ESR nil !!!
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # If we abort the upgrade, re-enable the intel
    --OnStopBuild = function( self, unitBeingBuilt )
    --    StructureUnit.OnStopBuild( self, unitBeingBuilt )
    OnStopBuild = function( self, unitBeingBuilt, order )
        StructureUnit.OnStopBuild( self, unitBeingBuilt, order )
        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 and not self.IntelFxOn then
			self.IntelEffectsBag = {}
			self.CreateTerrainTypeEffects(  self, self.IntelEffects, 'FXIdle',  self:GetCurrentLayer(), nil, self.IntelEffectsBag  )
			self.IntelFxOn = true
		end
    end,

    OnIntelDisabled = function( self )
        StructureUnit.OnIntelDisabled( self )
        EffectUtil.CleanupEffectBag( self,'IntelEffectsBag' )
        self.IntelFxOn = false
    end,       
}

#-------------------------------------------------------------
#  SONAR UNITS
#-------------------------------------------------------------
SonarUnit = Class( StructureUnit ) {
    LandBuiltHiddenBones = {'Floatation'},
#Leave Sonar On during upgrade, per design 11/14/06
#    # 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 )
#    OnStopBuild = function( self, unitBeingBuilt, order )
#        StructureUnit.OnStopBuild( self, unitBeingBuilt, order )
#        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,    
    
    OnIntelDisabled = function( self )
        StructureUnit.OnIntelDisabled( self )
        self:DestroyBlinkingLights()
        self:CreateBlinkingLights( 'Red' )
    end,

    OnIntelEnabled = function( self )
        StructureUnit.OnIntelEnabled( self )
        self:DestroyBlinkingLights()
        self:CreateBlinkingLights( 'Green' )
    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 = 0.5,

    # 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:SetCapturable( false )
        self:SetReclaimable( false )
    end,
}


#-------------------------------------------------------------
#  WALL STRCUTURE UNITS
#-------------------------------------------------------------
WallStructureUnit = Class( StructureUnit ) {
    LandBuiltHiddenBones = {'Floatation'},
}

#-------------------------------------------------------------
#  QUANTUM GATE UNITS
#-------------------------------------------------------------
QuantumGateUnit = Class( FactoryUnit ) {
    OnKilled = function( self, instigator, type, overkillRatio )
        self:StopUnitAmbientSound(  'ActiveLoop'  )
        FactoryUnit.OnKilled( self, instigator, type, overkillRatio )
    end,

}



#################################################################
##  MOBILE UNITS
#################################################################
MobileUnit = Class( Unit ) {

    OnKilled = function( self, instigator, type, overkillRatio )
        #Add unit's threat to our influence map
        local threat = 5
        local decay = 0.1
        local currentLayer = self:GetCurrentLayer()
        if instigator then
            local unit = false
            if IsUnit( instigator ) then
                unit = instigator
            elseif IsProjectile( instigator ) or IsCollisionBeam( instigator ) then
                unit = instigator.unit
            end
            
            if unit then    
                local unitPos = unit:GetCachePosition()
                if EntityCategoryContains( categories.STRUCTURE, unit ) then
                    decay = 0.01
                end
                 
                if unitPos then
                    if currentLayer == 'Sub' then
                        threat = self:GetAIBrain():GetThreatAtPosition( unitPos, 0, true, 'AntiSub' )
                    elseif currentLayer == 'Air' then
                        threat = self:GetAIBrain():GetThreatAtPosition( unitPos, 0, true, 'AntiAir' )
                    else
                        threat = self:GetAIBrain():GetThreatAtPosition( unitPos, 0, true, 'AntiSurface' )
                    end
                    threat = threat / 2
                end
            end
        end
    
        if currentLayer == 'Sub' then
            self:GetAIBrain():AssignThreatAtPosition( self:GetPosition(), threat, decay*10, 'AntiSub' )
        elseif currentLayer == 'Air' then
            self:GetAIBrain():AssignThreatAtPosition( self:GetPosition(), threat, decay, 'AntiAir' )
        elseif currentLayer == 'Water' then
            self:GetAIBrain():AssignThreatAtPosition( self:GetPosition(), threat, decay*10, 'AntiSurface' )
        else
            self:GetAIBrain():AssignThreatAtPosition( self:GetPosition(), threat, decay, 'AntiSurface' )
        end    
    
        Unit.OnKilled( self, instigator, type, overkillRatio )
    end,
    
    StartBeingBuiltEffects = function( self, builder, layer )
        Unit.StartBeingBuiltEffects( self, builder, layer )
        local bp = self:GetBlueprint()
        local FactionName = bp.General.FactionName

        if FactionName == 'UEF' then
            EffectUtil.CreateUEFUnitBeingBuiltEffects(  self, builder, self.OnBeingBuiltEffectsBag  )
        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,
    
    CreateReclaimEffects = function( self, target )
        EffectUtil.PlayReclaimEffects(  self, target, self:GetBlueprint().General.BuildBones.BuildEffectBones or {0,}, self.ReclaimEffectsBag  )
    end,
    
    CreateReclaimEndEffects = function( self, target )
        EffectUtil.PlayReclaimEndEffects(  self, target  )
    end,         
    
    CreateCaptureEffects = function(  self, target  )
        EffectUtil.PlayCaptureEffects(  self, target, self:GetBlueprint().General.BuildBones.BuildEffectBones or {0,}, self.CaptureEffectsBag  )
    end,       
}


#-------------------------------------------------------------
#  WALKING LAND UNITS
#-------------------------------------------------------------
WalkingLandUnit = Class( MobileUnit ) {
    WalkingAnim = nil,
    WalkingAnimRate = 1,
    IdleAnim = false,
    IdleAnimRate = 1,
    DeathAnim = false,
    DisabledBones = {},

    OnMotionHorzEventChange = function( self, new, old )
        MobileUnit.OnMotionHorzEventChange(self, new, old)
        
        if ( old == 'Stopped' ) then
            if (not self.Animator) then
                self.Animator = CreateAnimator(self, true)
            end
            local bpDisplay = self:GetBlueprint().Display
            if bpDisplay.AnimationWalk then
                self.Animator:PlayAnim(bpDisplay.AnimationWalk, true)
                self.Animator:SetRate(bpDisplay.AnimationWalkRate or 1)
            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
    PlayDestructionEffects = true,
    ShowUnitDestructionDebris = false,
    DeathThreadDestructionWaitTime = 10,


    OnKilled = function( self, instigator, type, overkillRatio )
        local layer = self:GetCurrentLayer()
        self:DestroyIdleEffects()
        local bp = self:GetBlueprint()
        
        if ( layer == 'Water' or layer == 'Seabed' or layer == 'Sub' ) and bp.Display.AnimationDeath then
            self.SinkExplosionThread = self:ForkThread( self.ExplosionThread )
            self.SinkThread = self:ForkThread( self.SinkingThread )
        end
        MobileUnit.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:GetRandomOffset( 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 )
        MobileUnit.OnCreate( self )
        self:AddPingPong()
    end,
    
    OnStopBeingBuilt = function( self, builder, layer )
        MobileUnit.OnStopBeingBuilt( self,builder,layer )
        local bp = self:GetBlueprint()
        if bp.SizeSphere then
            self:SetCollisionShape( 
                'Sphere', 
                bp.CollisionSphereOffsetX or 0, 
                bp.CollisionSphereOffsetY or 0, 
                bp.CollisionSphereOffsetZ or 0, 
                bp.SizeSphere
             )
        end        
    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 )
        MobileUnit.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:StopUnitAmbientSound(  'ActiveLoop'  )
        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:StopUnitAmbientSound(  'ActiveLoop'  )
        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 )
        MobileUnit.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 )
        MobileUnit.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, explosion.GetAverageBoundingXZRadius( self ) )
        explosion.CreateDebrisProjectiles( self, explosion.GetAverageBoundingXYZRadius( self ), {self:GetUnitSizes()} )
    end,

--[[
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # ON KILLED:
	# THIS FUNCTION PLAYS (INCLUDING ALL THE DEFAULT DEATH EFFECT) WHEN THE UNIT TAKES A MORTAL HIT.
    # 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
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]]--

	#================================================================================================
	# NOTES BY MANIMAL FOR POSSIBLE BUG IN : OnKilled ( ORIGINE = GPG )
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	# THERE IS ONLY ONE CALL OF MobileUnit.OnKilled( self, instigator, type, overkillRatio )
	# THAT IS INVOKED INSIDE THE CONDITIONAL CODE, SO THIS MAKES THE OTHER CONDITIONAl part of this 
	# SCRIPT NEVER FIRES MobileUnit.OnKilled => SHOULD BE INVOKED FROM OUTSIDE THE CONDITIONAL CODE ?
	#================================================================================================

    OnKilled = function( self, instigator, type, overkillRatio )
        local bp = self:GetBlueprint()
        if ( self:GetCurrentLayer() == 'Air' and Random() < self.DestroyNoFallRandomChance ) then
            
            self.CreateUnitAirDestructionEffects(  self, 1.0  )
            self:DestroyTopSpeedEffects()
            self:DestroyBeamExhaust()
            self.OverKillRatio = overkillRatio
            self:PlayUnitSound( 'Killed' )
            self:DoUnitCallbacks( 'OnKilled' )
            self:OnKilledVO()
            if instigator and IsUnit( instigator ) then
                instigator:OnKilledUnit( self )
            end
        else
            self.DeathBounce = 1
            if instigator and IsUnit( instigator ) then
                instigator:OnKilledUnit( self )
            end
            MobileUnit.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 )
        MobileUnit.OnCreate( self ) 
    
        self.EffectsBag = {}
        if self:GetBlueprint().General.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:StopUnitAmbientSound(  'ConstructLoop'  )
        MobileUnit.OnPaused( self )
        if self.BuildingUnit then
            MobileUnit.StopBuildingEffects( self, self:GetUnitBeingBuilt() )
        end    
    end,
    
    OnUnpaused = function( self )
        if self.BuildingUnit then
            self:PlayUnitAmbientSound(  'ConstructLoop'  )
            MobileUnit.StartBuildingEffects( self, self:GetUnitBeingBuilt(), self.UnitBuildOrder )
        end
        MobileUnit.OnUnpaused( self )
    end,
    
    OnStartBuild = function( self, unitBeingBuilt, order )
        MobileUnit.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.
        self.UnitBeingBuilt = unitBeingBuilt
        self.UnitBuildOrder = order
        self.BuildingUnit = true
        if unitBeingBuilt:GetUnitId() == self:GetBlueprint().General.UpgradesTo and order == 'Upgrade' then
            self.Upgrading = true
            self.BuildingUnit = false
        end
    end,

	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	# BUG FIX BY MANIMAL :
	# I NOTICED THAT MANY CLASSES DO NOT DEFIME OnStopBuild THE SAME WAY FOR MOST OF THE FACTORIES
	# WHEN OnStopBuild IS FIRED FROM AIR AND/OR NAVAL FACOTRY UNITS, THE VALUE OF order IS nil !!!
	# THE PROBLEM LIES WITHIN xxxxunits.lua (xxxx = faction name) that defines unconsistently OnStopBuild
	# FOR AIR + SEA + SUB FactoryUnit CLASSES, THAT DOES NOT MATCH AT ALL THE DEFINITION OF GENERIC FactoryUnit within defaultunits.lua !!!
	# J'AI REMARQUÉ QUE PLUSIEURS CLASSES DÉFINISSENT OnStopBuild DIFFÉREMMENT POUR LA PLUPART DES USINES
	# PENDANT LA CONSTRUCTION D'UNITÉS AIR ET/OU NAVAL, LA VALEUR DE order ESR nil !!!
	#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    --OnStopBuild = function( self, unitBeingBuilt )
    --    MobileUnit.OnStopBuild( self, unitBeingBuilt )
    OnStopBuild = function( self, unitBeingBuilt, order )
        MobileUnit.OnStopBuild( self, unitBeingBuilt, order )
        if self.Upgrading then
            NotifyUpgrade( self, unitBeingBuilt )
            self:Destroy()
        end
        self.UnitBeingBuilt = nil
        self.UnitBuildOrder = 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 )
            if ( enable ) then
                self.BuildArmManipulator:Enable()
            end
        end
    end,

    OnPrepareArmToBuild = function( self )
        MobileUnit.OnPrepareArmToBuild( self )

        #LOG(  'OnPrepareArmToBuild'  )
        if self.BuildingOpenAnimManip then
            self.BuildingOpenAnimManip:SetRate( self:GetBlueprint().Display.AnimationBuildRate or 1 )
            if self.BuildArmManipulator then
                self.StoppedBuilding = false
                ForkThread(  self.WaitForBuildAnimation, self, true  )
            end
        end
    end,

    OnStopBuilderTracking = function( self )
        MobileUnit.OnStopBuilderTracking( self )

        if self.StoppedBuilding then
            self.StoppedBuilding = false
            self.BuildArmManipulator:Disable()
            self.BuildingOpenAnimManip:SetRate( -( self:GetBlueprint().Display.AnimationBuildRate or 1 ) )
        end
    end,
    

    CheckBuildRestriction = function( self, target_bp )
        if self:CanBuild( target_bp.BlueprintId ) then
            return true
        else
            return false
        end
    end,
}


#-------------------------------------------------------------
#  SEA UNITS
#  These units typically float on the water and have wake when they move.
#-------------------------------------------------------------
SeaUnit = Class( MobileUnit ) {
    DeathThreadDestructionWaitTime = 5,
    ShowUnitDestructionDebris = false,
    PlayEndAnimDestructionEffects = false,


    OnStopBeingBuilt = function( self, builder, layer )
        MobileUnit.OnStopBeingBuilt( self,builder,layer )
        self:SetMaintenanceConsumptionActive()
    end,

    # 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
        MobileUnit.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:GetRandomOffset( 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:GetRandomOffset( 0.25 )
            local rs = Random( vol/2, vol*2 ) / ( vol*2 )
            local randBone = Util.GetRandomInt(  0, numBones )
            
            CreateEmitterAtBone(  self, randBone, army, '/effects/emitters/destruction_underwater_explosion_flash_01_emit.bp' ):OffsetEmitter( rx, ry, rz ):ScaleEmitter( rs )
            CreateEmitterAtBone(  self, randBone, 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:GetRandomOffset( 1 )
                local rs = Random( vol/2, vol*2 ) / ( vol*2 ) 
                CreateAttachedEmitter( self,-1,army,'/effects/emitters/destruction_water_sinking_ripples_01_emit.bp' ):OffsetEmitter( rx, 0, rz ):ScaleEmitter( rs )

                local rx, ry, rz = self:GetRandomOffset( 1 )
                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:GetRandomOffset( 1 )
                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:GetRandomOffset( 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 ) {
}

#-------------------------------------------------------------
#  SHIELD SEA UNITS
#-------------------------------------------------------------
ShieldSeaUnit = Class( SeaUnit ) {
}

