do


local BuffFieldBlueprints = import(Game.CBFP_Path..'/lua/sim/BuffField.lua').BuffFieldBlueprints
local RRBC = import(Game.CBFP_Path..'/lua/sim/RebuildBonusCallback.lua').RegisterRebuildBonusCheck


local CBFP_oldUnit = Unit
Unit = Class(CBFP_oldUnit) {

    OnPreCreate = function(self)
        CBFP_oldUnit.OnPreCreate(self)
        self.EventCallbacks = {
                OnKilled = {},
                OnUnitBuilt = {},
                OnStartBuild = {},
                OnReclaimed = {},
                OnStartReclaim = {},
                OnStopReclaim = {},
                OnStopBeingBuilt = {},
                OnHorizontalStartMove = {},
                OnCaptured = {},
                OnCapturedNewUnit = {},
                OnDamaged = {},
                OnStartCapture = {},
                OnStopCapture = {},
                OnFailedCapture = {},
                OnStartBeingCaptured = {},
                OnStopBeingCaptured = {},
                OnFailedBeingCaptured = {},
                OnFailedToBuild = {},
                OnVeteran = {},
                ProjectileDamaged = {},
                SpecialToggleEnableFunction = false,
                SpecialToggleDisableFunction = false,

                # new eventcallbacks. returns only 'self' as argument unless otherwise noted
                OnCreated = {},
                OnTransportAttach = {},
                OnTransportDetach = {},
                OnShieldIsUp = {},
                OnShieldIsDown = {},
                OnShieldIsCharging = {},
                OnPaused = {}, # pause button
                OnUnpaused = {},
                OnProductionPaused = {}, # production button for f.e. mass fab
                OnProductionUnpaused = {},
                OnHealthChanged = {}, # returns self, newHP, oldHP   
                OnTMLAmmoIncrease = {}, # use AddOnMLammoIncreaseCallback function. uses 6 sec interval polling so not accurate
                OnTMLaunched = {},
                OnSMLAmmoIncrease = {}, # use AddOnMLammoIncreaseCallback function. uses 6 sec interval polling so not accurate
                OnSMLaunched = {},
                OnStartRefueling = {},
                OnRunOutOfFuel = {},
                OnGotFuel = {}, # fires when the meter isnt empty anymore.
                OnCmdrUpgradeFinished = {}, # happens when a commander unit is upgraded. doesnt work for factories
                OnCmdrUpgradeStart = {},
                OnTeleportCharging = {}, # returns self, location
                OnTeleported = {}, # returns self, location

                # new in v3
                OnTimedEvent = {}, # returns self, variable (can be antyhing, value is determined when adding event callback)
                OnAttachedToTransport = {}, # returns self, transport unit
                OnDetachedToTransport = {}, # returns self, transport unit

                # new in v4
                OnBeforeTransferingOwnership = {},
                OnAfterTransferingOwnership = {},
        }
    end,

    OnCreate = function(self)
        CBFP_oldUnit.OnCreate(self)

        local bp = self:GetBlueprint()
        if bp.Transport and bp.Transport.DontUseForcedAttachPoints then
            self:RemoveTransportForcedAttachPoints()
        end
        self:InitBuffFields()    # buff field initialization changed in v4   (read docs)
        self:DisableRestrictedWeapons()    # added by brute51 [119]
        self:OnCreated()
    end,

    OnCreated = function(self)   # new in v3
        self:DoUnitCallbacks('OnCreated')
    end,

    InitBuffFields = function(self)  # buff field stuff
        # creates all buff fields
        local bp = self:GetBlueprint()
        if self.BuffFields and bp.BuffFields then
            for scriptName, field in self.BuffFields do

                # getting buff field blueprint
                local BuffFieldBp = BuffFieldBlueprints[bp.BuffFields[scriptName]]

                if not BuffFieldBp or type(BuffFieldBp) != 'table' then
                    WARN('BuffField: no blueprint data for buff field '..repr(scriptName))
                    continue
                end

                # we need a different buff field instance for each unit. this takes care of that.
                if not self.MyBuffFields then
                    self.MyBuffFields = {}
                end
                self.MyBuffFields[scriptName] = self:CreateBuffField(scriptName, BuffFieldBp)

            end
        end
    end,

    CreateBuffField = function(self, name, buffFieldBP)  # buff field stuff
        local spec = {
            Name = buffFieldBP.Name,
            Owner = self,
        }
        return ( self.BuffFields[name](spec) )
    end,


    ##########################################################################################
    ## MISC FUNCTIONS
    ##########################################################################################

    # removes engine forced attachment bones for transports
    RemoveTransportForcedAttachPoints = function(self)
        # this cancels the weird attachment bone manipulations, so transported units attach to the correct positions
        # (probably only useful for custom transport units only). By brute51, this is not a bug fix.
        local nBones = self:GetBoneCount() - 1
        for k = 1, nBones do
            if string.find(self:GetBoneName(k), 'Attachpoint_') then
                self:EnableManipulators(k, false)
            end
        end
    end,

    # buff field function
    GetBuffFieldByName = function(self, name)
        if self.BuffFields and self.MyBuffFields then
            for k, field in self.MyBuffFields do
                local fieldBP = field:GetBlueprint()
                if fieldBP.Name == name then
                    return field
                end
            end
        end
    end,

    # disables some weapons as defined in the unit restriction list, if unit restrictions enabled ofcrouse. [119]
    # why not do this using a blueprint mod? removing the weapon in the blueprint would break compatibility with other
    # mods that might change nuke weapons. So I'm just disabling it and removing the icons, the next best thing.
    DisableRestrictedWeapons = function(self)
        local noNukes = Game.NukesRestricted()
        local noTacMsl = Game.TacticalMissilesRestricted()
        for i = 1, self:GetWeaponCount() do
            local wep = self:GetWeapon(i)
            local bp = wep:GetBlueprint()
            if bp.CountedProjectile then
                if noNukes and bp.NukeWeapon then
                    wep:SetWeaponEnabled(false)
                    self:RemoveCommandCap('RULEUCC_Nuke')
                    self:RemoveCommandCap('RULEUCC_SiloBuildNuke')
                elseif noTacMsl then
                    wep:SetWeaponEnabled(false)
                    # todo: this may not be sufficient for all units, ACUs with a tactical missile enhancements may by-pass this
                    self:RemoveCommandCap('RULEUCC_Tactical')
                    self:RemoveCommandCap('RULEUCC_SiloBuildTactical')
                end
            end
        end

    end,

    ##########################################################################################
    ## MISC EVENTS
    ##########################################################################################

    OnCaptured = function(self, captor)
        if self and not self:IsDead() and captor and not captor:IsDead() and self:GetAIBrain() ~= captor:GetAIBrain() then

            if not self:IsCapturable() then
                self:Kill()
                return
            end
            # kill non capturable things on a transport
            if EntityCategoryContains( categories.TRANSPORTATION, self ) then
                local cargo = self:GetCargo()
                for _,v in cargo do
                    if not v:IsDead() and not v:IsCapturable() then
                        v:Kill()
                    end
                end
            end 
            self:DoUnitCallbacks('OnCaptured', captor)
            local newUnitCallbacks = {}
            if self.EventCallbacks.OnCapturedNewUnit then
                newUnitCallbacks = self.EventCallbacks.OnCapturedNewUnit
            end
            local entId = self:GetEntityId()
            local unitEnh = SimUnitEnhancements[entId]
            local captorArmyIndex = captor:GetArmy()
            local captorBrain = false
            
            # For campaigns:
            # We need the brain to ignore army cap when transfering the unit
            # do all necessary steps to set brain to ignore, then un-ignore if necessary the unit cap
            
            if ScenarioInfo.CampaignMode then
                captorBrain = captor:GetAIBrain()
                SetIgnoreArmyUnitCap(captorArmyIndex, true)
            end

             # added by brute51 - bugfix when capturing an enemy it should retain its data [120]
            local newUnits = import('/lua/SimUtils.lua').TransferUnitsOwnership( {self}, captorArmyIndex)
           
            if ScenarioInfo.CampaignMode and not captorBrain.IgnoreArmyCaps then
                SetIgnoreArmyUnitCap(captorArmyIndex, false)
            end

            # the unit transfer function returns a table of units. since we transfered 1 unit the table contains 1
            # unit (the new unit).
            if table.getn(newUnits) != 1 then
                return
            end
            local newUnit
            for k, unit in newUnits do
                newUnit = unit
                break
            end
            
            # no need for this anymore
            #if unitEnh then
            #    for k,v in unitEnh do
            #        newUnit:CreateEnhancement(v)
            #    end
            #end
            # Because the old unit is lost we cannot call a member function for newUnit callbacks
            for k,cb in newUnitCallbacks do
                if cb then
                    cb(newUnit, captor)
                end
            end
        end
    end,

    OnPaused = function(self)
        CBFP_oldUnit.OnPaused(self)
        # added by brute51
        self:DoUnitCallbacks('OnPaused')
    end,

    OnUnpaused = function(self)
        CBFP_oldUnit.OnUnpaused(self)
        # added by brute51
        self:DoUnitCallbacks('OnUnpaused')
    end,

    # added by brute51 - timed event, fired every X seconds.
    TimedEventThread = function(self, interval, passData)
        WaitSeconds(interval)
        while not self:IsDead() do
            WaitSeconds(interval)
            self:OnTimedEvent(interval, passData)
        end
    end,

    OnTimedEvent = function(self, interval, passData)
        self:DoOnTimedEventCallbacks(interval, passData)
    end,

    OnBeforeTransferingOwnership = function(self, ToArmy)
        self:DoUnitCallbacks('OnBeforeTransferingOwnership')
    end,

    OnAfterTransferingOwnership = function(self, FromArmy)
        self:DoUnitCallbacks('OnAfterTransferingOwnership')
    end,

    ##########################################################################################
    ## ECONOMY
    ##########################################################################################

    OnProductionPaused = function(self)
        CBFP_oldUnit.OnProductionPaused(self)
        # added by brute51
        self:DoUnitCallbacks('OnProductionPaused')
    end,

    OnProductionUnpaused = function(self)
        CBFP_oldUnit.OnProductionUnpaused(self)
        # added by brute51
        self:DoUnitCallbacks('OnProductionUnpaused')
    end,

    UpdateConsumptionValues = function(self)
        # added by brute51 - to make sure we use the proper consumption values. [132]
        if self.ActiveConsumption then
            local focus = self:GetFocusUnit()
            if focus and self.WorkItem and self.WorkProgress < 1 and (focus:IsUnitState('Enhancing') or focus:IsUnitState('Building')) then
                self.WorkItem = focus.WorkItem    # set our workitem to the focus unit work item, is specific for enhancing
            end
        end
        CBFP_oldUnit.UpdateConsumptionValues(self)
    end,

    ##########################################################################################
    ## DAMAGE
    ##########################################################################################

    DoTakeDamage = function(self, instigator, amount, vector, damageType)
        # added by brute51 - bug fix for bug [128] - units with 0 health don't die (because they have 0.001 health
        # sometimes). Revised this bug fix in CBFP v4 for better mod compatibility.
        CBFP_oldUnit.DoTakeDamage(self, instigator, amount, vector, damageType)
        if self:GetHealth() < 1 then  # check if there's very little health left. If so help the unit over the edge
            CBFP_oldUnit.DoTakeDamage(self, instigator, 1, vector, damageType)
        end
    end,

    OnHealthChanged = function(self, new, old)
        CBFP_oldUnit.OnHealthChanged(self, new, old)
        # added by brute51
        self:DoOnHealthChangedCallbacks(self, new, old)
    end,

    OnCountedMissileLaunch = function(self, missileType)
        # is called in defaultweapons.lua when a counted missile (tactical, nuke) is launched
        if missileType == 'nuke' then
            self:OnSMLaunched()
        else
            self:OnTMLaunched()
        end
    end,

    OnTMLaunched = function(self)
        self:DoUnitCallbacks('OnTMLaunched')
    end,

    OnSMLaunched = function(self)
        self:DoUnitCallbacks('OnSMLaunched')
    end,

    CheckCountedMissileAmmoIncrease = function(self)  # be sure to fork this
        # polls the ammo count every 6 secs 
        local nukeCount = self:GetNukeSiloAmmoCount() or 0
        local lastTimeNukeCount = nukeCount
        local tacticalCount = self:GetTacticalSiloAmmoCount() or 0
        local lastTimeTacticalCount = tacticalCount
        while not self:IsDead() do
            nukeCount = self:GetNukeSiloAmmoCount() or 0
            tacticalCount = self:GetTacticalSiloAmmoCount() or 0

            if nukeCount > lastTimeNukeCount then
                self:OnSMLAmmoIncrease()
            end
            if tacticalCount > lastTimeTacticalCount then
                self:OnTMLAmmoIncrease()
            end

            lastTimeNukeCount = nukeCount 
            lastTimeTacticalCount = tacticalCount 
            WaitSeconds(6)
        end
    end,

    OnTMLAmmoIncrease = function(self)
        self:DoUnitCallbacks('OnTMLAmmoIncrease')
    end,

    OnSMLAmmoIncrease = function(self)
        self:DoUnitCallbacks('OnSMLAmmoIncrease')
    end,

    CreateWreckageProp = function( self, overkillRatio )
        local wreck = CBFP_oldUnit.CreateWreckageProp( self, overkillRatio )
        if wreck then
            wreck.IsWreckage = true
        end
        return wreck
    end,

    #############################################################################################
    ## CONSTRUCTING - BUILDING - REPAIR
    #############################################################################################

    OnStartBuild = function(self, unitBeingBuilt, order)
        # added by brute51 - to make sure we use the proper consumption values. [132]
        self:UpdateConsumptionValues()
        CBFP_oldUnit.OnStartBuild(self, unitBeingBuilt, order)
        self.CurrentBuildOrder = order
    end,

    OnStartBeingBuilt = function(self, builder, layer)
        # here 'self' is the unit being built, builder is the engineer doing that

        self:CheckUnitRestrictions()  # [157]
        CBFP_oldUnit.OnStartBeingBuilt(self, builder, layer)

        # this section is rebuild bonus check 1 [159]
        # GPG if you read this I can explain what this does. So if you need me to, contact me (Brute51).
        local builderUpgradesTo = builder:GetBlueprint().General.UpgradesTo or false
        if not builderUpgradesTo or self:GetUnitId() != builderUpgradesTo then    # avoid upgrades
            if EntityCategoryContains( categories.STRUCTURE, self) then
                builder:ForkThread( builder.CheckFractionComplete, self )  # [159]
            end

            # this section is rebuild bonus check 2, it also requires the above IF statement to work OK [159]
            if builder.VerifyRebuildBonus then
                builder.VerifyRebuildBonus = nil
                self:ForkThread( self.CheckRebuildBonus )  # [159]
            end
        end
    end,

    GetRebuildBonus = function(self, rebuildUnitBP)
        # here 'self' is the engineer building the structure
        self.InitialFractionComplete = CBFP_oldUnit.GetRebuildBonus(self, rebuildUnitBP)  # rebuild bonus check 1 [159]
        self.VerifyRebuildBonus = true    # rebuild bonus check 2 [159]
        return self.InitialFractionComplete
    end,

    CheckFractionComplete = function(self, unitBeingBuilt, threadCount)
        # rebuild bonus check 1 [159]
        # This code checks if the unit is allowed to be accelerate-built. If not the unit is destroyed (for lack 
        # of a SetFractionComplete() function). Added by brute51
        local fraction = unitBeingBuilt:GetFractionComplete()
        if fraction > (self.InitialFractionComplete or 0) then
            unitBeingBuilt:OnRebuildBonusIsIllegal()
        end
        self.InitialFractionComplete = nil
    end,

    CheckRebuildBonus = function(self)
        # this section is rebuild bonus check 2 [159]
        # This code checks if the unit is allowed to be accelerate-built. If not the unit is destroyed (for lack 
        # of a SetFractionComplete() function). Added by brute51
        if self:GetFractionComplete() > 0 then
            local cb = function(bpUnitId)
                            if self:GetUnitId() == bpUnitId then 
                                self:OnRebuildBonusIsLegal()
                            end
                        end
            RRBC( self:GetPosition(), cb)
            self.RebuildBonusIllegalThread = self:ForkThread(
                function(self) 
                    WaitTicks(1)
                    self:OnRebuildBonusIsIllegal()
                end
            )
        end
    end,

    OnRebuildBonusIsLegal = function(self)
        # rebuild bonus check 2 [159]
        # this doesn't always run. In fact, in most of the time it doesn't.
        if self.RebuildBonusIllegalThread then
            KillThread(self.RebuildBonusIllegalThread)
        end
    end,

    OnRebuildBonusIsIllegal = function(self)
        # rebuild bonus check 1 and 2 [159]
        # this doesn't always run. In fact, in most of the time it doesn't.
        self:Destroy()
    end,

    CheckUnitRestrictions = function(self)
        # added by brute51 - to make sure stuff that shouldn't be available (unit restriction) can't be build [157]
        # in this statement GetFractionComplete() is used to filter nukes building a missile or ACU upgrading (and
        # probably more). Those situations bug out otherwise.
        if Game.UnitRestricted(self) and IsUnit(self) and self:GetFractionComplete() != 1 then
                # I need a way to cancel a build command. Ordering the unit to stop works but it's a mickey mouse 
                # workaround, not a real fix. Fortunately only AI players and cheating players (those using an UI 
                # mod that shows restricted unit icons) are affected by this. And the AI doesn't have build 
                # queues anyway so it's ok cancelling the orders (I hope!)
                # If someone from GPG reads this: a better fix is to call CheckBuildRestriction() in the unit class
                # each time a new build/upgrade command is given internally, to any unit. Right now that function
                # is only used for command units (ACU or SCU) and only when they build, not when they assist
                # an upgrade of a unit.
            self:Destroy()
        end
    end,

    OnStopBuild = function(self, unitBeingBuilt)
        CBFP_oldUnit.OnStopBuild(self, unitBeingBuilt)
        if self.CurrentBuildOrder == 'MobileBuild' then  # prevents false positives by assisted enhancing
            if self.OnStopBuildWasRun then
                if unitBeingBuilt and not unitBeingBuilt:BeenDestroyed() then
                    unitBeingBuilt:Destroy()  # [164]
                end
            else
                self.OnStopBuildWasRun = true
                self:ForkThread( function(self) WaitTicks(2) self.OnStopBuildWasRun = nil end )
            end
        end
    end,

    ##########################################################################################
    ## INTEL
    ##########################################################################################

    #Watch the economy.  If this unit doesn't get all it needs, shut off the intel.
    #FIX BY GOWERLY - Intel should only care about energy, let's not die if we run out of mass while upgrading, ok? 
    # Brute51: This is bug fix [158] and was present since v1. I've enhanced the Gowerlys code for v4
    IntelWatchThread = function(self)
        local aiBrain = self:GetAIBrain() 
        local bp = self:GetBlueprint()
        local recharge = bp.Intel.ReactivateTime or 10
        while self:ShouldWatchIntel() do
            WaitSeconds(0.5)
            if aiBrain:GetEconomyStored( 'ENERGY' ) < 1 then  # checking for less than 1 cause sometimes there's more
                self:DisableUnitIntel(nil)                    # than 0 and less than 1 in stock and that last bit of
                WaitSeconds(recharge)                         # energy isn't used. This results in the radar being
                self:EnableUnitIntel(nil)                     # on even though there's no energy to run it. Shields
            end                                               # have a similar bug with a similar fix.
        end
        if self.IntelThread then 
            self.IntelThread = nil
        end
    end,

    ##########################################################################################
    ## GENERIC WORK
    ##########################################################################################


    ##########################################################################################
    ## FUEL EVENTS
    ##########################################################################################

    OnStartRefueling = function(self)
        CBFP_oldUnit.OnStartRefueling(self)
        #added by brute51
        self:DoUnitCallbacks('OnStartRefueling')
    end,

    OnRunOutOfFuel = function(self)
        CBFP_oldUnit.OnRunOutOfFuel(self)
        #added by brute51
        self:DoUnitCallbacks('OnRunOutOfFuel')
    end,

    OnGotFuel = function(self)
        CBFP_oldUnit.OnGotFuel(self)
        #added by brute51
        self:DoUnitCallbacks('OnGotFuel')
    end,

    ##########################################################################################
    ## UNIT CALLBACKS
    ##########################################################################################

    # use addunitcallback and dounitcallback for normal callback handling. these are special cases

    DoOnHealthChangedCallbacks = function(self, newHP, oldHP) # use normal add callback function
        local type = 'OnHealthChanged'
        if ( self.EventCallbacks[type] ) then
            for num,cb in self.EventCallbacks[type] do
                if cb then
                    cb( self, newHP, oldHP )
                end
            end
        end
    end,

    AddOnMLammoIncreaseCallback = function(self, fn) # specialized cause this starts the ammo check thread
        if not fn then
            error('*ERROR: Tried to add a callback type - OnTMLAmmoIncrease with a nil function')
            return
        end
        table.insert( self.EventCallbacks.OnTMLAmmoIncrease, fn )
        if not self.MLAmmoCheckThread then
            self.MLAmmoCheckThread = self:ForkThread(self.CheckCountedMissileAmmoIncrease)
        end
    end,

    AddOnTimedEventCallback = function(self, fn, interval, passData) 
        # specialized because this starts a timed even thread (interval = secs between events, passData can be
        # anything, is passed to callback when event is fired)
        if not fn then
            error('*ERROR: Tried to add a callback type - OnTimedEvent with a nil function')
            return
        end
        table.insert( self.EventCallbacks.OnTimedEvent, {fn = fn, interval = interval} )
        self:ForkThread(self.TimedEventThread, interval, passData)
    end,

    DoOnTimedEventCallbacks = function(self, interval, passData)
        local type = 'OnTimedEvent'
        if ( self.EventCallbacks[type] ) then
            for num,cb in self.EventCallbacks[type] do
                if cb and cb['fn'] and cb['interval'] == interval then
                    cb['fn']( self, passData )
                end
            end
        end
    end,

    ##########################################################################################
    ## STATES
    ##########################################################################################

    WorkingState = State(CBFP_oldUnit.WorkingState) {
        Main = function(self)
            #added by brute51
            self:OnCmdrUpgradeStart()
            CBFP_oldUnit.WorkingState.Main(self)
        end,

        OnWorkEnd = function(self, work)
            CBFP_oldUnit.WorkingState.OnWorkEnd(self, work)
            #added by brute51
            self:OnCmdrUpgradeFinished()
        end,
    },

    ##########################################################################################
    ## BUFFS
    ##########################################################################################


    ##########################################################################################
    ## VETERANCY
    ##########################################################################################
    

    ##########################################################################################
    ## SHIELDS
    ##########################################################################################

    # Added by brute51, fires shield events for units. The other shield events dont run at the correct times
    # (to early)

    OnShieldIsUp = function(self)
        self:DoUnitCallbacks('OnShieldIsUp')
    end,

    OnShieldIsDown = function(self)
        self:DoUnitCallbacks('OnShieldIsDown')
    end,

    OnShieldIsCharging = function(self)
        self:DoUnitCallbacks('OnShieldIsCharging')
    end,

    ##########################################################################################
    ## TRANSPORTING
    ##########################################################################################

    OnTransportAttach = function(self, attachBone, unit)
        CBFP_oldUnit.OnTransportAttach(self, attachBone, unit)
        # added by brute51
        unit:OnAttachedToTransport(self)
        self:DoUnitCallbacks( 'OnTransportAttach', unit )
    end,

    OnAttachedToTransport = function(self, transport)
        self:DoUnitCallbacks( 'OnAttachedToTransport', transport )
    end,

    OnTransportDetach = function(self, attachBone, unit)
        CBFP_oldUnit.OnTransportDetach(self, attachBone, unit)
        # added by brute51
        unit:OnDetachedToTransport(self)
        self:DoUnitCallbacks( 'OnTransportDetach', unit )
    end,

    OnDetachedToTransport = function(self, transport)
        self:DoUnitCallbacks( 'OnDetachedToTransport', transport )
    end,

    ##########################################################################################
    ## TELEPORTING
    ##########################################################################################

    InitiateTeleportThread = function(self, teleporter, location, orientation)
        # added by brute51
        self:OnTeleportCharging(location)
        CBFP_oldUnit.InitiateTeleportThread(self, teleporter, location, orientation)
        self:OnTeleported(location)
    end,

    OnTeleportCharging = function(self, location)
        self:DoUnitCallbacks('OnTeleportCharging', location)
    end,

    OnTeleported = function(self, location)
        self:DoUnitCallbacks('OnTeleported', self, location)
    end,

    #########################################################################################
    ## COMMANDER ENHANCING
    ##########################################################################################

    # Events that happen when a commander unit (ACU or SCU) is enhanced with a new toy

    OnCmdrUpgradeFinished = function(self)
        self:DoUnitCallbacks('OnCmdrUpgradeFinished')
    end,

    OnCmdrUpgradeStart = function(self)
        self:DoUnitCallbacks('OnCmdrUpgradeStart')
    end,
}

end