Files
Roblox-Elevator-Game/src/server/main/Elevators/Otis1960/init.lua
2024-05-24 01:39:47 -04:00

463 lines
18 KiB
Lua

--!optimize 2
--!native
--!strict
--Set relay tags through studio command line
--[[
local s = game.Selection:Get()[1]
for i,v in s:GetChildren() do
if v:IsA("Model") then
local main = v:FindFirstChild("main")
if main then
local label = v:FindFirstChildWhichIsA("TextLabel", true)
if label then
main:AddTag(`Otis1960_RelayButton_{label.Text}`)
end
end
end
end
]]
local Elevators = script.Parent
local MainDir = Elevators.Parent
local EnumsDir = MainDir:WaitForChild("Enums")
local LoadDir = MainDir:WaitForChild("Load")
local TagsDir = LoadDir:WaitForChild("Tags")
local Storage = game:GetService("ReplicatedStorage")
local RS = game:GetService("RunService")
local ButtonTags = require(TagsDir:WaitForChild("Buttons"))
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 RelayAlgorithm = require(script:WaitForChild("RelayAlgorithm"))
local Relay = require(script:WaitForChild("Relay"))
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 Tags = require(LoadDir:WaitForChild("Tags"))
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
Leveled: (self: ClassConstructor, RequestedLevel: number) -> (),
Leveling: (self: ClassConstructor, RequestedLevel: number) -> (),
FloorPassingUp: (self: ClassConstructor, ElevatorPositionY: number, RequestedLevel: number) -> (),
FloorPassingDown: (self: ClassConstructor, ElevatorPositionY: number, RequestedLevel: number) -> (),
RequestLevel: (self: ClassConstructor, RequestedLevel: number) -> boolean,
__MoveTo: (self: ClassConstructor, GoingUp: boolean, GoalFloor_Y: number) -> (),
__MovingHeartbeat: (self: ClassConstructor, GoingUp: boolean, GoalFloor_Y: number) -> (),
} & Impl_Static_Props
type Impl_Static_Props = {
Name: Enums.ElevatorValues,
Responsiveness: number,
MaxVelocity: number,
FloorLevelingDistance: number,
DoorOpeningDistance: number,
LeveledDistance: number,
QueueWaitTime: number,
Sounds: {
LanternChimeDirection: SoundEnums.Otis1960LanternChimeDirection,
LanternChimeLanding: SoundEnums.Otis1960LanternChimeLanding,
},
Colors: {
ButtonActivated: Color3,
ButtonDeactivated: Color3,
LanternDisplayOn: Color3,
LanternDisplayOff: Color3,
},
Attributes: {
CurrentFloor: IntValue,
Moving: BoolValue,
GoingUp: BoolValue,
Stopped: BoolValue,
Relay: {
Goal: IntValue,
GoalYLevel: NumberValue,
Ready: BoolValue,
}
},
Events: {
ButtonActivated: BindableEvent
}
}
type Constructor_Fun = (TagsConstructor: TagsConstructor, ButtonsTags: Tags.ElevatorButtons, LanternsTags: Tags.Lanterns, LandingDoors: Tags.LandingTags) -> ClassConstructor
type Constructor_Return_Props = {
Tags: Tags,
MOConstructor: MovingObjects.MovingObjectsConstructor,
LanternsConstructor: Lanterns.LanternsConstructor,
HallDisplaysConstructor: HallDisplays.HallDisplaysConstructor,
ElevatorBox_1960: UnionOperation,
ElevatorDoor1: BasePart,
ElevatorDoor2: BasePart,
ElevatorDoorSensor: Folder,
BoxAttachment: Attachment,
BoxAlignPosition: AlignPosition,
BoxAlignOrientation: AlignOrientation,
ElevatorDoorsConstructor: Doors.DoorConstructor,
Ropes: {Instance},
TractionRopesConstructor: TractionRopes.TractionRopesConstructor,
Pulley: UnionOperation,
Pulley2: UnionOperation,
Governor: UnionOperation,
GovernorFlyballs: Part,
PieplatePulley: UnionOperation,
MachineRoom: MovingObjects.MachineRoom,
HallDisplays: {Instance},
ButtonsConstructor: Buttons.ButtonsConstructor,
RelayAlgorithmConstructor: RelayAlgorithm.RelayAlgorithmConstructor,
RelayConstructor: Relay.RelayConstructor,
__MovingConnection: RBXScriptConnection?,
}
local Elevator = {} :: Impl_Constructor
Elevator.__index = Elevator
Elevator.Name = Enums.Elevator.Otis1960
Elevator.FloorLevelingDistance = 2.5
Elevator.DoorOpeningDistance = Elevator.FloorLevelingDistance/2.8
Elevator.LeveledDistance = 0.5
Elevator.Responsiveness = 10
Elevator.MaxVelocity = 10
Elevator.QueueWaitTime = 5
Elevator.Sounds = {
LanternChimeDirection = SoundEnums.Otis1960.LanternChimeDirection,
LanternChimeLanding = SoundEnums.Otis1960.LanternChimeLanding
}
Elevator.Colors = {
ButtonActivated = Color3.fromRGB(180,0,0),
ButtonDeactivated = Color3.fromRGB(139,139,139),
LanternDisplayOn = Color3.fromRGB(255,114,71),
LanternDisplayOff = Color3.fromRGB(55,55,55),
}
Elevator.Attributes = {
CurrentFloor = Instance.new("IntValue") :: IntValue,
Moving = Instance.new("BoolValue") :: BoolValue,
GoingUp = Instance.new("BoolValue") :: BoolValue,
Stopped = Instance.new("BoolValue") :: BoolValue,
Relay = {
Ready = Instance.new("BoolValue") :: BoolValue,
Goal = Instance.new("IntValue") :: IntValue,
GoalYLevel = Instance.new("NumberValue") :: NumberValue,
},
}
Elevator.Events = {
ButtonActivated = Instance.new("BindableEvent") :: BindableEvent
}
Elevator.Attributes.CurrentFloor.Value = 1
Elevator.Attributes.Moving.Value = false
Elevator.Attributes.GoingUp.Value = false
Elevator.Attributes.Relay.Goal.Value = 1
Elevator.Attributes.Relay.Ready.Value = false
local Attributes = Elevator.Attributes
--My clever math function for determining if the elevator goal is to move upwards or not
local function ElevatorGoingUpDirection(Floor: number, RequestedFloor: number): boolean
return -(Floor-RequestedFloor)>0
end
local function _ActivatedFloorButton(self: ClassConstructor, ButtonFloor: number, ButtonTree: Tags.ButtonPropertiesSafe)
ButtonTree.Prompt.Enabled = false
local Some = self:RequestLevel(ButtonFloor)
if Some then
local FloorTracker: RBXScriptConnection
FloorTracker = Attributes.CurrentFloor:GetPropertyChangedSignal("Value"):Connect(function()
if Attributes.CurrentFloor.Value == ButtonFloor and Attributes.Relay.Goal.Value == ButtonFloor then
FloorTracker:Disconnect()
self.ButtonsConstructor:__DeactivateButton(ButtonTree.Inst :: BasePart, (ButtonTree.Inst :: BasePart):FindFirstChild("Glass") :: BasePart?)
ButtonTree.Prompt.Enabled = true
end
end)
else
warn(`Failed to call floor: {ButtonFloor}`)
ButtonTree.Prompt.Enabled = true
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)
elseif ButtonNameType == Enums.ButtonTree.Landing then
self.ButtonsConstructor:LandingButton(ButtonID, ButtonTree, function(ButtonFloor: number)
_ActivatedFloorButton(self, ButtonFloor, ButtonTree)
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.__MovingConnection and self.__MovingConnection.Connected then
self.__MovingConnection:Disconnect()
end
self.BoxAlignPosition.MaxVelocity = 0
else
(ButtonTree.Inst :: BasePart).Position-=Vector3.new(0,0,.05)
self.LanternsConstructor:Reset()
self:__MoveTo(ElevatorGoingUpDirection(Attributes.CurrentFloor.Value, Attributes.Relay.Goal.Value), LevelingModule.Leveling[self.RelayAlgorithmConstructor.__FloorQueue[1]])
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 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
function Elevator.constructor(TagsConstructor, ButtonsTags, LanternsTags, LandingDoors)
local self = {} :: Constructor_Return_Props
self.MachineRoom = {_CFrame = {}} :: MovingObjects.MachineRoom
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}
self.HallDisplays = TagsConstructor:Request("Otis1960_LandingFloorDisplay") :: {Instance}
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
local LanternDisplay = TagsConstructor:Request("Otis1960_LanternDisplayMain") :: UnionOperation
self.MOConstructor = MovingObjects.constructor({
MachineRoom = self.MachineRoom
} :: MovingObjects.InstanceTree)
self.HallDisplaysConstructor = HallDisplays.constructor(Attributes.CurrentFloor, self.HallDisplays)
self.ElevatorDoorsConstructor = Doors.constructor(LandingDoors, self.ElevatorBox_1960, self.ElevatorDoor1, self.ElevatorDoor2, self.ElevatorDoorSensor)
self.TractionRopesConstructor = TractionRopes.constructor(self.Ropes, self.ElevatorBox_1960, LevelingModule.Leveling)
self.LanternsConstructor = Lanterns.constructor(LanternDisplay, LanternsTags, Elevator.Sounds, Elevator.Colors)
local ButtonsTagsConstructor = ButtonTags.constructor(TagsConstructor, ButtonsTags)
local Otis1960_Buttons = ButtonsTagsConstructor:CreatePromptButtons()
self.ButtonsConstructor = Buttons.constructor(Elevator.Attributes, Elevator.Events, Elevator.Colors)
self.HallDisplaysConstructor:BindHallDisplays()
self.BoxAttachment,
self.BoxAlignPosition,
self.BoxAlignOrientation = ElevatorMover(self.ElevatorBox_1960, self.ElevatorBox_1960.Position, Elevator.Responsiveness, Elevator.MaxVelocity)
self.RelayAlgorithmConstructor = RelayAlgorithm.constructor(self.BoxAlignPosition, Attributes, Doors.Attributes.Relay)
self.RelayConstructor = Relay.constructor(self.RelayAlgorithmConstructor, Attributes, Doors.Attributes, LevelingModule, self.BoxAlignPosition, self.ElevatorBox_1960)
self.RelayConstructor:BulkConnect()
local ClassConstructor = setmetatable(self, Elevator)
IterateButtons(ClassConstructor, ButtonsTagsConstructor)
--Open the elevator doors on server start
task.spawn(function()
self.LanternsConstructor:Toggle(true, Attributes.CurrentFloor.Value)
self.HallDisplaysConstructor:SetHallDisplays(Attributes.CurrentFloor.Value)
self.ElevatorDoorsConstructor:ToggleElevatorDoorsAsync(true, Attributes.CurrentFloor.Value)
--Some hacks
Doors.Attributes.Relay.Open.Value = true
end)
print(LevelingModule.LevelingBetween)
print(`🔝 {Elevator.Name} initialized and ready`)
return ClassConstructor
end
function Elevator:Leveled(RequestedLevel)
(self.__MovingConnection :: RBXScriptConnection):Disconnect()
Attributes.Moving.Value = false
Attributes.CurrentFloor.Value = RequestedLevel
self.BoxAlignPosition.MaxVelocity = Elevator.MaxVelocity
self.LanternsConstructor:Reset(Attributes.CurrentFloor.Value)
task.wait(Elevator.QueueWaitTime)
local ElevatorGoingUp = ElevatorGoingUpDirection(Attributes.CurrentFloor.Value, RequestedLevel)
if self.RelayAlgorithmConstructor:Check(ElevatorGoingUp) then
--More floors in the queue
self:__MoveTo(ElevatorGoingUp, LevelingModule.Leveling[self.RelayAlgorithmConstructor.__FloorQueue[1]])
end
end
function Elevator:Leveling(RequestedLevel)
self.BoxAlignPosition.MaxVelocity = 1
self.LanternsConstructor:Toggle(true, RequestedLevel)
end
function Elevator:FloorPassingUp(ElevatorPositionY, RequestedLevel)
if ElevatorPositionY>=LevelingModule.Leveling[Attributes.CurrentFloor.Value+1] then
Attributes.CurrentFloor.Value+=1
self.LanternsConstructor:Toggle(true, Attributes.CurrentFloor.Value)
self.LanternsConstructor:Toggle(false, Attributes.CurrentFloor.Value-1)
end
end
function Elevator:FloorPassingDown(ElevatorPositionY, RequestedLevel)
if ElevatorPositionY<=LevelingModule.Leveling[Attributes.CurrentFloor.Value-1] then
Attributes.CurrentFloor.Value-=1
self.LanternsConstructor:Toggle(true, Attributes.CurrentFloor.Value)
self.LanternsConstructor:Toggle(false, Attributes.CurrentFloor.Value+1)
end
end
function Elevator:__MovingHeartbeat(GoingUp, GoalFloor_Y)
Attributes.GoingUp.Value = GoingUp
if self.__MovingConnection and self.__MovingConnection.Connected then
self.__MovingConnection:Disconnect()
end
self.MOConstructor:UpdateCFrame()
local Delta = 0
local DoorsOpeningEvent = false
self.__MovingConnection = RS.Heartbeat:Connect(function(_dt)
Delta+=1
local FloorGoal: number = self.RelayAlgorithmConstructor.__FloorQueue[1]
Attributes.Moving.Value = true
Attributes.Relay.Goal.Value = FloorGoal
local ElevatorPosition: Vector3 = self.ElevatorBox_1960.Position
local ElevatorPositionY: number = ElevatorPosition.Y
local BoxAlignY: number = self.BoxAlignPosition.Position.Y
local ElevatorVelocityY: number = self.ElevatorBox_1960:GetVelocityAtPosition(ElevatorPosition).Y
self.TractionRopesConstructor:Move(27, self.ElevatorBox_1960.Position)
self.MOConstructor:Frame_Pullies(Delta, ElevatorVelocityY)
--Kill the connection
if GoingUp then
self:FloorPassingUp(ElevatorPositionY, FloorGoal)
if ElevatorPositionY>=BoxAlignY-Elevator.FloorLevelingDistance then
self:Leveling(FloorGoal)
if not DoorsOpeningEvent and ElevatorPositionY>=BoxAlignY-Elevator.DoorOpeningDistance then
DoorsOpeningEvent = true
self.ElevatorDoorsConstructor:ToggleElevatorDoorsAsync(true, FloorGoal)
end
end
if ElevatorPositionY>=BoxAlignY-Elevator.LeveledDistance then
self:Leveled(FloorGoal)
end
else
self:FloorPassingDown(ElevatorPositionY, FloorGoal)
if ElevatorPositionY<=BoxAlignY+Elevator.FloorLevelingDistance then
self:Leveling(FloorGoal)
if not DoorsOpeningEvent and ElevatorPositionY>=BoxAlignY-Elevator.DoorOpeningDistance then
DoorsOpeningEvent = true
self.ElevatorDoorsConstructor:ToggleElevatorDoorsAsync(true, FloorGoal)
end
end
if ElevatorPositionY<=BoxAlignY+Elevator.LeveledDistance then
self:Leveled(FloorGoal)
end
end
end)
end
function Elevator:__MoveTo(GoingUp, GoalFloor_Y)
if Doors.Attributes.Relay.Open.Value then
self.ElevatorDoorsConstructor:ToggleElevatorDoorsAsync(false, Attributes.CurrentFloor.Value)
end
if GoingUp then
self.LanternsConstructor:DirectionUp(true)
else
self.LanternsConstructor:DirectionDown(true)
end
self:__MovingHeartbeat(GoingUp, GoalFloor_Y)
end
function Elevator:RequestLevel(RequestedLevel)
local GoalFloor_Y: number? = LevelingModule.Leveling[RequestedLevel]
if GoalFloor_Y and RequestedLevel ~= Attributes.CurrentFloor.Value then
local ElevatorGoingUp = ElevatorGoingUpDirection(Attributes.CurrentFloor.Value, RequestedLevel)
local Proceeding = self.RelayAlgorithmConstructor:AddFloor(ElevatorGoingUp, RequestedLevel)
if Proceeding then
self:__MoveTo(ElevatorGoingUp, GoalFloor_Y)
end
else
warn(`[{Elevator.Name}]: landing out of range or equals the same range as the goal, requested landing: {tostring(RequestedLevel)}`)
return false
end
return true
end
return Elevator