@name Eli
@inputs [Floor LeftDoor RightDoor]:entity
@outputs 
@persist ElevatorName:string
@persist DistanceTarget DistanceIncrement DistanceInterval
@persist InitialDistance [LeftDirection RightDirection InitialLeftPos InitialRightPos]:vector
@persist [LeftPos RightPos]:vector
@persist InitializedMonitor HasPlayers
@persist FunctionTimers:table Players:array Elevators:gtable
@persist Open Closed TargetElevatorName:string
@persist HaltExecution
@trigger 

if (->LeftDoor & ->RightDoor)
{
    if (first())
    {   
        runOnLast(1)
        
        #ElevatorName = "Lobby"
        ElevatorName = "Room 1"
        
        DistanceTarget = 95
        DistanceIncrement = 1.25
        DistanceInterval = 100
        
        function functionTimerHandler()
        {
            if (!clk())
            {
                foreach(Timer, Function:string = FunctionTimers)
                {
                    if (clk(Timer))
                    {
                        FunctionTimers:remove(Function)
                        
                        Function()
                    }
                }
            }
        }
        
        function functionTimer(Function:string, Time)
        {
            timer(Function, Time)
            
            FunctionTimers[Function,string] = Function        
        }    
        
        function stopFunctionTimer(Function:string)
        {
            stoptimer(Function)
            
            FunctionTimers:remove(Function)
        }
        
        function number doorDistance()
        {
            LeftPos = LeftDoor:boxCenterW()
            RightPos = RightDoor:boxCenterW()
            
            return LeftPos:distance(RightPos)
        }
        
        function opened()
        {
            Open = 1
        }
        
        function openAnimation()
        {
            if (->LeftDoor & ->RightDoor)
            {
                Distance = doorDistance() - InitialDistance
                
                if (Distance < DistanceTarget)
                {
                    LeftDoor:setPos(LeftPos + LeftDirection * DistanceIncrement)
                    RightDoor:setPos(RightPos + RightDirection * DistanceIncrement)
                    
                    "functionTimer"("openAnimation", DistanceInterval)
                }
                
                else
                {
                    opened()
                }
            }
        }
        
        function open()
        {
            Closed = 0
            
            stopFunctionTimer("closeAnimation")
                
            openAnimation()        
        }
        
        function closed()
        {
            Closed = 1
        }
        
        function closeAnimation()
        {
            if (->LeftDoor & ->RightDoor)
            {
                Distance = doorDistance() - InitialDistance
                
                if (Distance > 0)
                {   
                    LeftDoor:setPos(LeftPos - LeftDirection * DistanceIncrement)
                    RightDoor:setPos(RightPos - RightDirection * DistanceIncrement)
                    
                    "functionTimer"("closeAnimation", DistanceInterval)
                }
                
                else
                {
                    closed()
                }
            }
        }
        
        function close()
        {   
            Open = 0
            
            stopFunctionTimer("openAnimation")
            
            closeAnimation()        
        }    
        
        function register()
        {
            Elevators = gTable("Elevators")
            
            Elevators[ElevatorName, entity] = Floor
        }
        
        function unregister()
        {   
            Elevators:removeEntity(ElevatorName)
            
            LeftDoor:teleport(InitialLeftPos)
            RightDoor:teleport(InitialRightPos)
        }
        
        function send()
        {
            if (HasPlayers)
            {
                Target = Elevators[TargetElevatorName,entity]
                
                for (I=1, Players:count())
                {
                    Player = Players[I,entity]
                    
                    Pos = Player:pos()
                    
                    TargetPos = Target:toWorld(Floor:toLocal(Pos))
                    
                    Player:teleport(TargetPos)
                }
            }
        }
        
        function notEmpty()
        {
            "functionTimer"("close", 1000)
        }
        
        function empty()
        {
            "functionTimer"("open", 1000)
        }
        
        function monitor()
        {
            if (!InitializedMonitor)
            {   
                Scale = Floor:aabbSize() / 2 * 0.75
                
                Corner1 = Floor:toWorld(Scale)
                Corner2 = Floor:toWorld(-Scale)
                
                Corner2 += vec(0,0,64)
                
                holoCreate(1)
                holoCreate(2)
                
                holoPos(1, Corner1)
                holoPos(2, Corner2)
                
                holoParent(1, Floor)
                holoParent(2, Floor)
                
                InitializedMonitor = 1
            }
            
            Corner1 = holoEntity(1):pos()
            Corner2 = holoEntity(2):pos()
            
            findIncludeClass("player")
            findInBox(Corner1, Corner2)
            
            Players = findToArray()
            
            if (Players:count())
            {
                HasPlayers = 1
                
                if ($HasPlayers)
                {
                    notEmpty()
                }
            }
            
            else
            {
                HasPlayers = 0
                
                if ($HasPlayers)
                {
                    empty()
                }
            }
            
            "functionTimer"("monitor", 1000)
        }
        
        InitialDistance = doorDistance()
        
        InitialLeftPos = LeftPos
        InitialRightPos = RightPos
        
        LeftDirection = (LeftPos - RightPos):normalized()
        RightDirection = -LeftDirection
        
        register()
    }
    
    if (!HaltExecution)
    {
        if (last())
        {
            unregister()
        }
        
        "functionTimerHandler"()
        
        #####################################################
        # This is the only part you need for controlling it #
        #                                                   #
        #####################################################
        
        if (first())
        {
            "functionTimer"("open", 1000)
        }
        
        if (Open & $Open)
        {
            monitor()
        }
        
        if (Open & HasPlayers & $HasPlayers)
        {
            "functionTimer"("close", 1000)
        }
        
        if (Closed & $Closed & HasPlayers)
        {
            #TargetElevatorName = "Room 1"
            TargetElevatorName = "Lobby"
            
            "functionTimer"("send", 1000)
            
            "functionTimer"("open", 2000)
        }
    }
}

else
{
    HaltExecution = 1
}
