diff --git a/src/server/main/Elevators/Otis1960/RelayAlgorithm.lua b/src/server/main/Elevators/Otis1960/RelayAlgorithm.lua index 2cbcb2d..e76ac6a 100644 --- a/src/server/main/Elevators/Otis1960/RelayAlgorithm.lua +++ b/src/server/main/Elevators/Otis1960/RelayAlgorithm.lua @@ -7,10 +7,9 @@ type Impl_Constructor = { __index: Impl_Constructor, constructor: Constructor_Fun, --Class functions - Sort: (self: ClassConstructor, ElevatorGoingUp: boolean) -> (), - Check: (self: ClassConstructor, ElevatorGoingUp: boolean) -> number?, - RawInsert: (self: ClassConstructor, ElevatorGoingUp: boolean, RequestedLevel: number) -> (), - AddFloor: (self: ClassConstructor, ElevatorGoingUp: boolean, RequestedLevel: number) -> boolean + Sort: (self: ClassConstructor, ElevatorGoingUp: boolean) -> (), + Check: (self: ClassConstructor, ElevatorGoingUp: boolean) -> number?, + AddFloor: (self: ClassConstructor, ElevatorGoingUp: boolean, RequestedLevel: number) -> (), } type Constructor_Fun = (BoxAlignPosition: AlignPosition, ElevatorAttributes: ElevatorAttributes, DoorAttributes: DoorAttributes) -> ClassConstructor @@ -19,10 +18,6 @@ type Constructor_Return_Props = { ElevatorAttributes: ElevatorAttributes, DoorAttributes: DoorAttributes, __FloorQueue: FloorQueue, - - Events: { - Sorted: BindableEvent - } } type ElevatorAttributes = { @@ -52,9 +47,6 @@ RelayAlgorithm.__index = RelayAlgorithm function RelayAlgorithm.constructor(BoxAlignPosition, ElevatorAttributes, DoorAttributes) return setmetatable({ - Events = { - Sorted = Instance.new("BindableEvent") - }, BoxAlignPosition = BoxAlignPosition, ElevatorAttributes = ElevatorAttributes, DoorAttributes = DoorAttributes, @@ -72,7 +64,6 @@ function RelayAlgorithm:Sort(ElevatorGoingUp) end return a>b end) - self.Events.Sorted:Fire(self.__FloorQueue) end function RelayAlgorithm:Check(ElevatorGoingUp) @@ -86,15 +77,9 @@ function RelayAlgorithm:Check(ElevatorGoingUp) return nil end -function RelayAlgorithm:RawInsert(ElevatorGoingUp, RequestedLevel) +function RelayAlgorithm:AddFloor(ElevatorGoingUp, RequestedLevel) table.insert(self.__FloorQueue, ElevatorGoingUp == self.ElevatorAttributes.GoingUp.Value and 1 or #self.__FloorQueue+1, RequestedLevel) self:Sort(ElevatorGoingUp) end -function RelayAlgorithm:AddFloor(ElevatorGoingUp, RequestedLevel) - self:RawInsert(ElevatorGoingUp, RequestedLevel) - - return not self.ElevatorAttributes.Relay.ReadyForMoving.Value and self.DoorAttributes.Relay.Open.Value -end - return RelayAlgorithm \ No newline at end of file diff --git a/src/server/main/Elevators/Otis1960/init.lua b/src/server/main/Elevators/Otis1960/init.lua index b6abdf0..16bc32d 100644 --- a/src/server/main/Elevators/Otis1960/init.lua +++ b/src/server/main/Elevators/Otis1960/init.lua @@ -30,21 +30,21 @@ local RS = game:GetService("RunService") local ButtonTags = require(TagsDir:WaitForChild("Buttons")) -local Enums = require(Storage:WaitForChild("Enums")) +local Enums = require(Storage:WaitForChild("Enums")) local SoundEnums = require(EnumsDir:WaitForChild("Sounds")) local LevelingModule = require(script:WaitForChild("Leveling")) -local Doors = require(script:WaitForChild("Doors")) -local MovingObjects = require(script:WaitForChild("MovingObjects")) +local Doors = require(script:WaitForChild("Doors")) +local MovingObjects = require(script:WaitForChild("MovingObjects")) local RelayAlgorithm = require(script:WaitForChild("RelayAlgorithm")) -local Relay = require(script:WaitForChild("Relay")) +local Relay = require(script:WaitForChild("Relay")) local PhysicalRelays = require(script:WaitForChild("PhysicalRelay")) local HallDisplays = require(Elevators:WaitForChild("HallDisplays")) local ElevatorMover = require(Elevators:WaitForChild("Mover")) local TractionRopes = require(Elevators:WaitForChild("TractionRopes")) -local Lanterns = require(Elevators:WaitForChild("Lanterns")) -local Buttons = require(Elevators:WaitForChild("ButtonsManager")) +local Lanterns = require(Elevators:WaitForChild("Lanterns")) +local Buttons = require(Elevators:WaitForChild("ButtonsManager")) local Tags = require(LoadDir:WaitForChild("Tags")) @@ -56,9 +56,8 @@ type Impl_Constructor = { __index: Impl_Constructor, constructor: Constructor_Fun, --Class functions - RequestLevelAsync: (self: ClassConstructor, RequestedLevel: number) -> boolean, - __MoveToAsync: (self: ClassConstructor, GoingUp: boolean, RequestedLevel: number) -> (), - __MovingHeartbeat: (self: ClassConstructor, GoingUp: boolean, RequestedLevel: number) -> (), + RequestLevel: (self: ClassConstructor, RequestedLevel: number) -> boolean, + __TravelToFloor: (self: ClassConstructor, LevelInt: number, LevelVec3: number, ElevatorGoingUp: boolean) -> (), } & Impl_Static_Props type Impl_Static_Props = { @@ -67,9 +66,9 @@ type Impl_Static_Props = { MaxVelocity: number, FloorLevelingDistance: number, FloorLeveling3PhaseDistance: number, - DoorOpeningDistance: number, - LeveledDistance: number, - QueueWaitTime: number, + ParkedDistance: number, + LevelingVelocity: number, + Phase3LevelingVelocity: number, Sounds: { LanternChimeDirection: SoundEnums.Otis1960LanternChimeDirection, @@ -86,11 +85,14 @@ type Impl_Static_Props = { Moving: BoolValue, GoingUp: BoolValue, Stopped: BoolValue, - - Relay: { - Goal: IntValue, - ReadyForMoving: BoolValue, - } + Goal: IntValue + }, + Events: { + CabProgression: BindableEvent, + CabTraveling: BindableEvent, + Parked: BindableEvent, + Leveling: BindableEvent, + Leveling3Phase: BindableEvent, } } @@ -129,8 +131,7 @@ type Constructor_Return_Props = { PhysicalRelays: PhysicalRelays.PhysicalRelayConstructor, __Connections: { - Moving: RBXScriptConnection?, - FloorSorted: RBXScriptConnection? + Moving: RBXScriptConnection?, } } @@ -140,11 +141,11 @@ Elevator.__index = Elevator Elevator.Name = Enums.Elevator.Otis1960 Elevator.FloorLevelingDistance = 4 Elevator.FloorLeveling3PhaseDistance = 1.5 -Elevator.DoorOpeningDistance = Elevator.FloorLeveling3PhaseDistance/2.8 -Elevator.LeveledDistance = 0.5 +Elevator.ParkedDistance = 0.2 Elevator.Responsiveness = 10 Elevator.MaxVelocity = 7 -Elevator.QueueWaitTime = 5 +Elevator.LevelingVelocity = 2 +Elevator.Phase3LevelingVelocity = .5 Elevator.Sounds = { LanternChimeDirection = SoundEnums.Otis1960.LanternChimeDirection, @@ -158,139 +159,42 @@ Elevator.Colors = { LanternDisplayOff = Color3.fromRGB(55,55,55), } +Elevator.Events = { + CabProgression = Instance.new("BindableEvent") :: BindableEvent, + CabTraveling = Instance.new("BindableEvent") :: BindableEvent, + Parked = Instance.new("BindableEvent") :: BindableEvent, + Leveling = Instance.new("BindableEvent") :: BindableEvent, + Leveling3Phase = Instance.new("BindableEvent") :: BindableEvent, +} + Elevator.Attributes = { CurrentFloor = Instance.new("IntValue") :: IntValue, Moving = Instance.new("BoolValue") :: BoolValue, GoingUp = Instance.new("BoolValue") :: BoolValue, Stopped = Instance.new("BoolValue") :: BoolValue, - - Relay = { - ReadyForMoving = Instance.new("BoolValue") :: BoolValue, - Goal = Instance.new("IntValue") :: IntValue, - }, + Goal = Instance.new("IntValue") :: IntValue, } Elevator.Attributes.CurrentFloor.Value = 1 Elevator.Attributes.Moving.Value = false Elevator.Attributes.GoingUp.Value = false - -Elevator.Attributes.Relay.Goal.Value = 1 -Elevator.Attributes.Relay.ReadyForMoving.Value = false +Elevator.Attributes.Goal.Value = 1 local Attributes = Elevator.Attributes +local Events = Elevator.Events ---Math function for determining if the elevator goal is to move upwards or not local function ElevatorGoingUpDirection(CurrentFloor: number, RequestedFloor: number): boolean -- -(CurrentFloor-RequestedFloor)>0 -- -CurrentFloor+RequestedFloor>0 return CurrentFloor(...: T...) + warn("🛗 ["..Elevator.Name.."]:", ...) end ---If the elevator is fully stopped at the floor and the hall button or the same cab button for the same floor is pressed, ---then open the doors -local function OpenElevatorDoorsSafe(self: ClassConstructor) - if not Attributes.Relay.ReadyForMoving.Value then - task.spawn(function() - self.ElevatorDoorsConstructor:ToggleElevatorDoorsAsync(true, Attributes.CurrentFloor.Value) - end) - end -end - ---Special cases inbound -local function HookButtons(self: ClassConstructor, ButtonNameType: Enums.ButtonTreeValues, ButtonID: string, ButtonTree: Tags.ButtonPropertiesSafe) - if ButtonNameType == Enums.ButtonTree.Car then - self.ButtonsConstructor:CarButton(ButtonID, ButtonTree, function(ButtonFloor: number) - _ActivatedFloorButton(self, ButtonFloor, ButtonTree) - end, function(_ButtonFloor: number) - OpenElevatorDoorsSafe(self) - end) - elseif ButtonNameType == Enums.ButtonTree.Landing then - self.ButtonsConstructor:LandingButton(ButtonID, ButtonTree, function(ButtonFloor: number) - _ActivatedFloorButton(self, ButtonFloor, ButtonTree) - end, function(_ButtonFloor: number) - OpenElevatorDoorsSafe(self) - end) - elseif ButtonNameType == Enums.ButtonTree.Special then - if Elevator.Name == Enums.Elevator.Otis1960 then - if ButtonTree.Name == Enums.SpecialButton.Stop then - ButtonTree.Attachment.Position-=Vector3.xAxis/10 - ButtonTree.Prompt.HoldDuration = 1 - end - end - - self.ButtonsConstructor:SpecialButton(ButtonTree.Name :: Enums.SpecialButtonValues, ButtonID, ButtonTree, function(Toggled: Buttons.SpecialButtonToggle) - Attributes.Stopped.Value = Toggled - - if Toggled then - (ButtonTree.Inst :: BasePart).Position+=Vector3.new(0,0,.05) - - if self.__Connections.Moving and self.__Connections.Moving.Connected then - self.__Connections.Moving:Disconnect() - end - self.BoxAlignPosition.MaxVelocity = 0 - else - (ButtonTree.Inst :: BasePart).Position-=Vector3.new(0,0,.05) - - self.LanternsConstructor:Reset() - self:__MoveToAsync(ElevatorGoingUpDirection(Attributes.CurrentFloor.Value, Attributes.Relay.Goal.Value)) - self.BoxAlignPosition.MaxVelocity = Elevator.MaxVelocity - end - end) - elseif ButtonNameType == Enums.ButtonTree.Relays then - if Elevator.Name == Enums.Elevator.Otis1960 then - ButtonTree.Attachment.Position-=Vector3.zAxis/6 - end - elseif ButtonNameType == Enums.ButtonTree.Unknown then - - else - warn(`[{Elevator.Name}]: Could not iterate a button, ButtonNameType={ButtonNameType}`) - end -end - -local function IterateButtons(self: ClassConstructor, ButtonsTagsConstructor: ButtonTags.ButtonsTagsConstructor) - for ButtonNameType, ButtonList in ButtonsTagsConstructor.Buttons[Elevator.Name] do - for ButtonID, ButtonTree in ButtonList do - if ButtonTree.Prompt and ButtonTree.Inst and ButtonTree.Attachment then - HookButtons(self, ButtonNameType :: Enums.ButtonTreeValues, ButtonID, ButtonTree :: Tags.ButtonPropertiesSafe) - else - warn(`{ButtonTree} is missing a field, {ButtonTree}`) - end - end - end -end - -local function ElevatorInit(self: ClassConstructor) - task.spawn(function() - self.LanternsConstructor:Toggle(true, Attributes.CurrentFloor.Value) - self.HallDisplaysConstructor:SetHallDisplays(Attributes.CurrentFloor.Value) - - --Open the elevator doors on server start - self.ElevatorDoorsConstructor:ToggleElevatorDoorsAsync(true, Attributes.CurrentFloor.Value) - --Some hacks - Doors.Attributes.Relay.Open.Value = true - end) +local function elevatorprint(...: T...) + print("🛗 ["..Elevator.Name.."]:", ...) end function Elevator.constructor(TagsConstructor, ButtonsTags, LanternsTags, LandingDoors) @@ -341,7 +245,7 @@ function Elevator.constructor(TagsConstructor, ButtonsTags, LanternsTags, Landin self.BoxAlignPosition, self.BoxAlignOrientation = ElevatorMover( self.ElevatorBox_1960, - Vector3.new(self.ElevatorBox_1960.Position.X, LevelingModule.Leveling[Attributes.Relay.Goal.Value] or LevelingModule.Leveling[1], self.ElevatorBox_1960.Position.Z), + Vector3.new(self.ElevatorBox_1960.Position.X, LevelingModule.Leveling[Attributes.Goal.Value] or LevelingModule.Leveling[1], self.ElevatorBox_1960.Position.Z), Elevator.Responsiveness, Elevator.MaxVelocity ) @@ -359,103 +263,128 @@ function Elevator.constructor(TagsConstructor, ButtonsTags, LanternsTags, Landin self.ElevatorBox_1960 ) - self.RelayConstructor:BulkConnect() - - self.__Connections.FloorSorted = self.RelayAlgorithmConstructor.Events.Sorted.Event:Connect(function(FloorQueue: RelayAlgorithm.FloorQueue) - print("Floors sorted") - if FloorQueue[1] then - Attributes.Relay.Goal.Value = FloorQueue[1] :: number - else - Attributes.Relay.Goal.Value = 1 - end - end) + --self.RelayConstructor:BulkConnect() local ClassConstructor = setmetatable(self, Elevator) - IterateButtons(ClassConstructor, ButtonsTagsConstructor) - ElevatorInit(self) - print(`🔝 {Elevator.Name} initialized and ready`) - + elevatorprint("Initialized and ready") return ClassConstructor end -function Elevator:__MovingHeartbeat(GoingUp, RequestedLevel) +local CurrentFloor = Attributes.CurrentFloor.Value - - local GoingTo_Y_Level = LevelingModule.Leveling[RequestedLevel] - if not GoingTo_Y_Level then - --fail safe - warn(`{Elevator.Name}: floor request "{RequestedLevel}" failed`) - GoingTo_Y_Level = LevelingModule.Leveling[1] +local function CheckFloorQueue(self: ClassConstructor) + if self.RelayAlgorithmConstructor.__FloorQueue[1] ~= Attributes.CurrentFloor.Value then + elevatorwarn("The floor queue first index did not match the elevator's current floor, CurrentFloor=", Attributes.CurrentFloor.Value, "FloorQueue[1]=", self.RelayAlgorithmConstructor.__FloorQueue[1]) end + table.remove(self.RelayAlgorithmConstructor.__FloorQueue, 1) - local Delta = 0 - - Attributes.GoingUp.Value = GoingUp - Attributes.Relay.Goal.Value = RequestedLevel - Attributes.Moving.Value = true - - self.PhysicalRelays:SetState(GoingUp and "UP" or "DOWN", true, false) - self.PhysicalRelays:SetState("240 V", true, false) - self.PhysicalRelays:SetState("440 V", true, false) - - self.MOConstructor:UpdateCFrame() - - self.BoxAlignPosition.MaxVelocity = Elevator.MaxVelocity - self.BoxAlignPosition.Position = Vector3.new( - self.ElevatorBox_1960.Position.X, - GoingTo_Y_Level, - self.ElevatorBox_1960.Position.Z - ) + local NextFloor = self.RelayAlgorithmConstructor.__FloorQueue[1] + if NextFloor then + local ElevatorGoingUp = ElevatorGoingUpDirection(Attributes.CurrentFloor.Value, NextFloor) + local LevelVec3 = LevelingModule.Leveling[NextFloor] - self.__Connections.Moving = RS.Heartbeat:Connect(function(_dt) - Delta+=1 - - end) + self:__TravelToFloor(NextFloor, LevelVec3, ElevatorGoingUp) + end end -function Elevator:__MoveToAsync(GoingUp, RequestedLevel) - if Doors.Attributes.Relay.Open.Value then - self.ElevatorDoorsConstructor:ToggleElevatorDoorsAsync(false, Attributes.CurrentFloor.Value) - end +local function CabTraveling(self: ClassConstructor, deltaTime: number, LevelVec3: number, ElevatorGoingUp: boolean) + local Elevator_Position = self.ElevatorBox_1960.Position + local Between_Levels = LevelingModule.LevelingBetween[CurrentFloor] + + if ElevatorGoingUp then + --Detecting between the floors + if Elevator_Position.Y>=Between_Levels then + local PreviousFloor = CurrentFloor + CurrentFloor+=1 + Attributes.CurrentFloor.Value = CurrentFloor - if GoingUp then - self.LanternsConstructor:DirectionUp(true) - else - self.LanternsConstructor:DirectionDown(true) - end + Events.CabProgression:Fire(PreviousFloor, CurrentFloor) + end - self:__MovingHeartbeat(GoingUp, RequestedLevel) -end + --Elevator is riding upwards towards the destination + if Elevator_Position.Y>=LevelVec3-Elevator.FloorLevelingDistance then + Events.Leveling:Fire() + self.BoxAlignPosition.MaxVelocity = Elevator.LevelingVelocity -function Elevator:RequestLevelAsync(RequestedLevel) - local FloorY_Level: number? = LevelingModule.Leveling[RequestedLevel] + if Elevator_Position.Y>=LevelVec3-Elevator.FloorLeveling3PhaseDistance then + Events.Leveling3Phase:Fire() + self.BoxAlignPosition.MaxVelocity = Elevator.Phase3LevelingVelocity - if FloorY_Level and RequestedLevel ~= Attributes.CurrentFloor.Value then - local ElevatorGoingUp = ElevatorGoingUpDirection(Attributes.CurrentFloor.Value, RequestedLevel) - local Proceeding = self.RelayAlgorithmConstructor:AddFloor(ElevatorGoingUp, RequestedLevel) + if Elevator_Position.Y>=LevelVec3-Elevator.ParkedDistance then + Events.Parked:Fire() + CheckFloorQueue(self); - --Mmm... temporary, cant distinguish here if this will be a cab or hall call - self.PhysicalRelays:SetState(`F{RequestedLevel}C`, true, true) - - if Proceeding then - self.PhysicalRelays:SetState("RUN", true, false) --This needs to be on a timer for the generator after 60 seconds - self:__MoveToAsync(ElevatorGoingUp, RequestedLevel) - else - if ElevatorGoingUp == Attributes.GoingUp.Value then - self.BoxAlignPosition.Position = Vector3.new( - self.ElevatorBox_1960.Position.X, - LevelingModule.Leveling[self.RelayAlgorithmConstructor.__FloorQueue[1] or 1], - self.ElevatorBox_1960.Position.Z - ) + (self.__Connections.Moving :: RBXScriptConnection):Disconnect() + end end end - - return true else - warn(`[{Elevator.Name}]: landing out of range or equals the same range as the goal, requested landing: {tostring(RequestedLevel)}`) + --Detecting between the floors + --leaving floor ElevatorPosition Y=62, Between_levels=75 + if Elevator_Position.Y<=Between_Levels then + local PreviousFloor = CurrentFloor + CurrentFloor-=1 + Attributes.CurrentFloor.Value = CurrentFloor + + Events.CabProgression:Fire(PreviousFloor, CurrentFloor) + end + + --Elevator is riding upwards towards the destination + if Elevator_Position.Y<=LevelVec3-Elevator.FloorLevelingDistance then + Events.Leveling:Fire() + self.BoxAlignPosition.MaxVelocity = Elevator.LevelingVelocity + + if Elevator_Position.Y<=LevelVec3-Elevator.FloorLeveling3PhaseDistance then + Events.Leveling3Phase:Fire() + self.BoxAlignPosition.MaxVelocity = Elevator.Phase3LevelingVelocity + + if Elevator_Position.Y<=LevelVec3-Elevator.ParkedDistance then + Events.Parked:Fire() + CheckFloorQueue(self); + + (self.__Connections.Moving :: RBXScriptConnection):Disconnect() + end + end + end end + Events.CabTraveling:Fire(Elevator_Position) +end + +function Elevator:__TravelToFloor(LevelInt, LevelVec3, ElevatorGoingUp) + if self.__Connections.Moving and self.__Connections.Moving.Connected then + self.__Connections.Moving:Disconnect() + end + + Attributes.Goal.Value = LevelInt + Attributes.GoingUp.Value = ElevatorGoingUp + + self.__Connections.Moving = RS.Heartbeat:Connect(function(deltaTime) + CabTraveling(self, deltaTime, LevelVec3, ElevatorGoingUp) + end) + + --Set the elevator's AlignPosition to the floor Y vector + self.BoxAlignPosition.Position = Vector3.new(self.BoxAlignPosition.Position.X, LevelVec3, self.BoxAlignPosition.Position.Z) + --Set the elevator's max velocity to its fastest speed when moving starts + self.BoxAlignPosition.MaxVelocity = Elevator.MaxVelocity +end + +function Elevator:RequestLevel(RequestedLevel) + local LevelVec3 = LevelingModule.Leveling[RequestedLevel] + + if LevelVec3 then + local ElevatorGoingUp = ElevatorGoingUpDirection(Attributes.CurrentFloor.Value, RequestedLevel) + self.RelayAlgorithmConstructor:AddFloor(ElevatorGoingUp, RequestedLevel) + + if #self.RelayAlgorithmConstructor.__FloorQueue == 1 then + self:__TravelToFloor(RequestedLevel, LevelVec3, ElevatorGoingUp) + end + + return true + end + + elevatorwarn(`Requested floor: "{RequestedLevel}" does not exist for this elevator`) return false end diff --git a/src/server/main/init.server.lua b/src/server/main/init.server.lua index 7ef1e81..bc5b4ff 100644 --- a/src/server/main/init.server.lua +++ b/src/server/main/init.server.lua @@ -53,4 +53,17 @@ print("[DEBUG] Elevator Landing Doors=", LandingDoors) local Otis1960Lanterns = Lanterns[Enums.Elevator.Otis1960] local Otis1960Buttons = Buttons[Enums.Elevator.Otis1960] local Otis1960LandingDoors = LandingDoors[Enums.Elevator.Otis1960] -local Otis1960 = Otis1960_Module.constructor(TagsConstructor, Otis1960Buttons, Otis1960Lanterns, Otis1960LandingDoors) \ No newline at end of file +local Otis1960 = Otis1960_Module.constructor(TagsConstructor, Otis1960Buttons, Otis1960Lanterns, Otis1960LandingDoors) + +task.wait(5) +Otis1960:RequestLevel(3) + +Otis1960_Module.Events.Parked.Event:Connect(function() + print("Cab parked") + task.wait(3) + Otis1960:RequestLevel(1) +end) + +Otis1960_Module.Events.CabProgression.Event:Connect(function(previousFloor: number?, nextFloor: number) + print("previousFloor=",previousFloor, "nextFloor=",nextFloor) +end) \ No newline at end of file diff --git a/src/shared/Enums.lua b/src/shared/Enums.lua index 3ef13aa..af04bb8 100644 --- a/src/shared/Enums.lua +++ b/src/shared/Enums.lua @@ -28,7 +28,8 @@ export type InteractablesValues = typeof(Enums.InteractType.LightSwitch) | typeof(Enums.InteractType.Light) | typeof(Enums.InteractType.LightSource) -export type ElevatorValues = typeof(Enums.Elevator.Otis1960) +export type ElevatorValues = typeof(Enums.Elevator.Otis1960) | + typeof(Enums.Elevator.Haughton) Enums.Button = { Car = "CarButton" :: "CarButton", @@ -50,7 +51,8 @@ Enums.SpecialButton = { } Enums.Elevator = { - Otis1960 = "Otis1960" :: "Otis1960" + Otis1960 = "Otis1960" :: "Otis1960", + Haughton = "Haughton" :: "Haughton" } Enums.InteractType = {