--!optimize 2 --!native --!strict local Elevators = script.Parent local MainDir = Elevators.Parent local LoadDir = MainDir:WaitForChild("Load") local Storage: ReplicatedStorage = game:GetService("ReplicatedStorage") local RS: RunService = game:GetService("RunService") local Enums = require(Storage:WaitForChild("Enums")) local Leveling = require(script:WaitForChild("Leveling")) local Doors = require(script:WaitForChild("Doors")) local MovingObjects = require(script:WaitForChild("MovingObjects")) local ElevatorMover = require(Elevators:WaitForChild("Mover")) local ButtonTags = require(Elevators:WaitForChild("Buttons")) local TractionRopes = require(Elevators:WaitForChild("TractionRopes")) local Lanterns = require(Elevators:WaitForChild("Lanterns")) local PromptModule = require(MainDir:WaitForChild("Map"):WaitForChild("Prompts")) local Tags = require(LoadDir:WaitForChild("Tags")) type rbxassetid = string type Tags = Tags.ExportedTags type TagsConstructor = Tags.TagsConstructor type ClassConstructor = typeof(setmetatable({} :: Constructor_Return_Props, {} :: Impl_Constructor)) type Impl_Constructor = { __index: Impl_Constructor, constructor: Constructor_Fun, --Class functions __MoveFloors: (self: ClassConstructor, Level: number, RequestedLevel: number, GoingUp: boolean) -> (), GoToLevel: (self: ClassConstructor, RequestedLevel: number) -> () } & Impl_Static_Props type Impl_Static_Props = { Responsiveness: number, MaxVelocity: number, ButtonActivatedColor: Color3, LanternDisplayColorOn: Color3, LanternDisplayColorOff: Color3, LanternChimeDirection: rbxassetid, LanternChimeLanding: rbxassetid, FloorLevelingDistance: number, LeveledDistance: number, __Moving: boolean, __CurrentFloor: number } type Constructor_Fun = (TagsConstructor: TagsConstructor, ButtonsTags: Tags.ElevatorButtons, LanternsTags: Tags.Lanterns) -> ClassConstructor type Constructor_Return_Props = { Tags: Tags, MOConstructor: MovingObjects.MovingObjectsConstructor, LanternsConstructor: Lanterns.LanternsConstructor, ElevatorBox_1960: UnionOperation, ElevatorDoor1: BasePart, ElevatorDoor2: BasePart, ElevatorDoorSensor: Folder, BoxAttachment: Attachment, BoxAlignPosition: AlignPosition, BoxAlignOrientation: AlignOrientation, ElevatorDoors: Doors.DoorConstructor, Ropes: {Instance}, TractionRopes: TractionRopes.TractionRopesConstructor, Pulley: UnionOperation, Pulley2: UnionOperation, Governor: UnionOperation, GovernorFlyballs: Part, PieplatePulley: UnionOperation, MachineRoom: MovingObjects.MachineRoom, __MovingConnection: RBXScriptConnection?, } type ButtonFunction = (self: ClassConstructor, ButtonName: string, ButtonsConstructor: ButtonTags.ButtonsConstructor, ButtonTree: Tags.ButtonProperties) -> () type ButtonFunctions = { [Enums.ButtonTreeValues]: ButtonFunction } local Otis1960 = {} :: Impl_Constructor Otis1960.__index = Otis1960 Otis1960.FloorLevelingDistance = 2.5 Otis1960.LeveledDistance = 0.5 Otis1960.Responsiveness = 50 Otis1960.MaxVelocity = 10 Otis1960.ButtonActivatedColor = Color3.fromRGB(180,0,0) Otis1960.LanternDisplayColorOn = Color3.fromRGB(255,114,71) Otis1960.LanternDisplayColorOff = Color3.fromRGB(55,55,55) Otis1960.LanternChimeDirection = "rbxassetid://16990287228" Otis1960.LanternChimeLanding = "rbxassetid://16990290265" --*read-only* Otis1960.__Moving = false Otis1960.__CurrentFloor = 1 local ButtonFunctions: ButtonFunctions = { [Enums.ButtonTree.Landing] = function(self, ButtonName, ButtonsConstructor, ButtonTree) end, [Enums.ButtonTree.Car] = function(self, ButtonName, ButtonsConstructor, ButtonTree) local DecodedFloor = Tags.Decoders.CarTag(ButtonName) if not DecodedFloor then end local Prompt = PromptModule.constructor(ButtonTree.Prompt, ButtonTree.Inst) Prompt:AddHookTriggered(function(Player: Player) if DecodedFloor then local _ButtonThread = ButtonsConstructor:AestheticActivateButton(ButtonTree.Inst :: BasePart, false, Otis1960.ButtonActivatedColor) self:GoToLevel(DecodedFloor) end end) end, [Enums.ButtonTree.Special] = function(self, ButtonName, ButtonsConstructor, ButtonTree) end } local function HookButtons(self: ClassConstructor, ButtonsConstructor: ButtonTags.ButtonsConstructor, ButtonType: Enums.EnumValue) for ButtonNameType, ButtonList in ButtonsConstructor.Buttons do for ButtonName, ButtonTree in ButtonList do if ButtonTree.Prompt then if ButtonTree.Inst then local Button = ButtonFunctions[ButtonNameType :: Enums.ButtonTreeValues] if Button then Button(self, ButtonName, ButtonsConstructor, ButtonTree) end else warn(`{ButtonTree} is missing the field "Inst"`) end else warn(`{ButtonTree} is missing the field "Prompt"`) end end end end function Otis1960.constructor(TagsConstructor, ButtonsTags, LanternsTags) local self = {} :: Constructor_Return_Props self.ElevatorBox_1960 = TagsConstructor:Request("ElevatorMover_1960") :: UnionOperation self.ElevatorDoor1 = TagsConstructor:Request("ElevatorDoor_1960_1") :: BasePart self.ElevatorDoor2 = TagsConstructor:Request("ElevatorDoor_1960_2") :: BasePart self.ElevatorDoorSensor = TagsConstructor:Request("ElevatorDoor_Sensor_1960") :: Folder self.Ropes = TagsConstructor:Request("1960_ElevatorPulleyRope") :: {Instance} --Rotation objects self.MachineRoom = {_CFrame = {}} :: MovingObjects.MachineRoom self.MachineRoom.Pulley = TagsConstructor:Request("Otis1960_Pulley") :: UnionOperation self.MachineRoom.Pulley2 = TagsConstructor:Request("Otis1960_Pulley2") :: UnionOperation self.MachineRoom.Governor = TagsConstructor:Request("Otis1960_Governor") :: UnionOperation self.MachineRoom.GovernorFlyballs = TagsConstructor:Request("Otis1960_GovernorFlyballs") :: Part self.MachineRoom.PiePlatePulley = TagsConstructor:Request("Otis1960_PieplatePulley") :: UnionOperation self.MachineRoom.PiePlatePlates = TagsConstructor:Request("Otis1960_PiePlatePlates") :: UnionOperation self.MachineRoom.PiePlateSelector = TagsConstructor:Request("Otis1960_PiePlateSelector") :: UnionOperation self.MOConstructor = MovingObjects.constructor({MachineRoom = self.MachineRoom} :: MovingObjects.InstanceTree) --Audio local LanternDisplay = TagsConstructor:Request("Otis1960_LanternDisplayMain") :: UnionOperation self.LanternsConstructor = Lanterns.constructor(LanternDisplay, Otis1960.LanternChimeDirection, Otis1960.LanternChimeLanding, LanternsTags, { Active = Otis1960.LanternDisplayColorOn, Off = Otis1960.LanternDisplayColorOff } :: Lanterns.Colors) self.BoxAttachment, self.BoxAlignPosition, self.BoxAlignOrientation = ElevatorMover(self.ElevatorBox_1960, self.ElevatorBox_1960.Position, Otis1960.Responsiveness, Otis1960.MaxVelocity) self.ElevatorDoors = Doors.constructor(self.ElevatorBox_1960, self.ElevatorDoor1, self.ElevatorDoor2, self.ElevatorDoorSensor) self.TractionRopes = TractionRopes.constructor(self.Ropes, self.ElevatorBox_1960, Leveling) --Buttons local ButtonsConstructor = ButtonTags.constructor(TagsConstructor, ButtonsTags) local Otis1960_Buttons = ButtonsConstructor:CreatePromptButtons() local ClassConstructor = setmetatable(self, Otis1960) HookButtons(ClassConstructor, ButtonsConstructor, Enums.ButtonTree.Car) print("🔝 Otis1960 initialized and ready") return ClassConstructor end local function FloorLeveling(self: ClassConstructor, RequestedLevel: number) self.BoxAlignPosition.MaxVelocity = 1 self.LanternsConstructor:Toggle(true, RequestedLevel) end local function FloorLeveled(self: ClassConstructor, RequestedLevel: number) (self.__MovingConnection :: RBXScriptConnection):Disconnect() Otis1960.__Moving = false Otis1960.__CurrentFloor = RequestedLevel self.BoxAlignPosition.MaxVelocity = Otis1960.MaxVelocity self.LanternsConstructor:Reset() end function Otis1960:__MoveFloors(GoalLevelVEC, RequestedLevel, GoingUp) local RotationDelta = 0 local ElevatorBoxCurrentPos = self.ElevatorBox_1960.Position if self.__MovingConnection and self.__MovingConnection.Connected then self.__MovingConnection:Disconnect() end self.MOConstructor:UpdateCFrame() if GoingUp then self.LanternsConstructor:DirectionUp(true) else self.LanternsConstructor:DirectionDown(true) end --Otis1960_ShaftGovernor self.__MovingConnection = RS.Heartbeat:Connect(function(_dt) --Not safe for parallel RotationDelta+=1 Otis1960.__Moving = true local ElevatorPosition: Vector3 = self.ElevatorBox_1960.Position local ElevatorVelocityY: number = self.ElevatorBox_1960:GetVelocityAtPosition(ElevatorPosition).Y self.MOConstructor:Frame_Pullies(RotationDelta, ElevatorVelocityY) self.TractionRopes:Move(26.3, ElevatorPosition) --Kill the connection if GoingUp then if ElevatorPosition.Y>=self.BoxAlignPosition.Position.Y-Otis1960.FloorLevelingDistance then FloorLeveling(self, RequestedLevel) end if ElevatorPosition.Y>=self.BoxAlignPosition.Position.Y-Otis1960.LeveledDistance then FloorLeveled(self, RequestedLevel) end else if ElevatorPosition.Y<=self.BoxAlignPosition.Position.Y+Otis1960.FloorLevelingDistance then FloorLeveling(self, RequestedLevel) end if ElevatorPosition.Y<=self.BoxAlignPosition.Position.Y+Otis1960.LeveledDistance then FloorLeveled(self, RequestedLevel) end end end) self.BoxAlignPosition.Position = Vector3.new(ElevatorBoxCurrentPos.X, GoalLevelVEC, ElevatorBoxCurrentPos.Z) end function Otis1960:GoToLevel(RequestedLevel) local GoalLevelVEC: number = Leveling[RequestedLevel] if GoalLevelVEC and GoalLevelVEC ~= 0 and GoalLevelVEC ~= Otis1960.__CurrentFloor then local GoingUp: boolean = -(Otis1960.__CurrentFloor-RequestedLevel)>0 --My clever math function for determining if the elevator goal is to move upwards or not self:__MoveFloors(GoalLevelVEC, RequestedLevel, GoingUp) else warn(`[{Enums.Elevator.Otis1960}]: landing out of range or equals the same range as goal! requested landing: {tostring(RequestedLevel)}`) end end return Otis1960