Restructure and get a working state for the Haughton elevator

This commit is contained in:
2024-08-13 01:45:03 -04:00
parent a1a455fb78
commit 9d081a2084
18 changed files with 96 additions and 440 deletions

View File

@@ -0,0 +1,387 @@
--!optimize 2
--!native
--!strict
local Elevators = script.Parent
local MainDir = Elevators.Parent
local LoadDir = MainDir:WaitForChild("Load")
local RunService = game:GetService("RunService")
local StorageService = game:GetService("ReplicatedStorage")
local Enums = require(StorageService:WaitForChild("Enums"))
local Out = require(StorageService:WaitForChild("Output"))
local Algebra = require(StorageService:WaitForChild("Algebra"))
local ElevatorTypes = require(MainDir:WaitForChild("Types"):WaitForChild("Elevator"))
local Tags = require(LoadDir:WaitForChild("Tags"))
local RelayAlgorithm = require(script:WaitForChild("RelayAlgorithm"))
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
GetLevel: (self: ClassConstructor, Level: number) -> Vector3?,
RequestLevelAsync: (self: ClassConstructor, RequestedLevel: number, Direction: Enums.ElevatorCallDirectionValues) -> boolean,
StartTraveling: (self: ClassConstructor) -> (),
__TravelToFloorAsync: (self: ClassConstructor, LevelInt: number, VEC3_Y_WRAP: Vector3) -> (),
}
type FloorLevelingPositions = {number}
type Constructor_Fun = (ElevatorBoxModel: BasePart, ElevatorConfigurationTable: ElevatorTypes.ElevatorConfigurationTable, FloorLevelingPositions: FloorLevelingPositions) -> ClassConstructor
type Constructor_Return_Props = {
RelayAlgorithm: RelayAlgorithm.RelayAlgorithmConstructor,
FloorLevelingPositions: FloorLevelingPositions,
eprint: <T...>(T...) -> (),
ewarn: <T...>(T...) -> (),
eprintStudio: <T...>(T...) -> (),
ewarnStudio: <T...>(T...) -> (),
Elevator: {
TravelingDirection: Enums.ElevatorCallDirectionValues,
BoxModel: UnionOperation,
AlignPosition: AlignPosition,
Configuration: ElevatorTypes.ElevatorConfigurationTable
},
Attributes: {
PreviousFloor: IntValue,
CurrentFloor: IntValue,
NextFloor: IntValue,
Goal: IntValue,
TravelingUpwards: BoolValue,
Stopped: BoolValue
},
Events: {
Progression: RBXScriptSignal<number, number, number>,
Traveling: RBXScriptSignal<number, Vector3>,
Parked: RBXScriptSignal,
Leveling: RBXScriptSignal,
Leveling3Phase: RBXScriptSignal,
__eventInstances__: {
Progression: BindableEvent,
Traveling: BindableEvent,
Parked: BindableEvent,
Leveling: BindableEvent,
Leveling3Phase: BindableEvent,
}
},
__functionEvents: {
StartTraveling: BindableEvent
},
__Connections: {
Moving: RBXScriptConnection?,
}
}
local Elevator = {} :: Impl_Constructor
Elevator.__index = Elevator
local function ElevatorGoingUpDirection(CurrentFloor: number, RequestedFloor: number): boolean
-- -(CurrentFloor-RequestedFloor)>0
-- -CurrentFloor+RequestedFloor>0
return CurrentFloor<RequestedFloor
end
local function Mover(ElevatorBoxModel: BasePart, Responsiveness: number, MaxVelocity: number): (Attachment, AlignPosition, AlignOrientation)
local BoxAttachment = Instance.new("Attachment")
BoxAttachment.Parent = ElevatorBoxModel
local BoxAlignPosition = Instance.new("AlignPosition")
BoxAlignPosition.Mode = Enum.PositionAlignmentMode.OneAttachment
BoxAlignPosition.Attachment0 = BoxAttachment
BoxAlignPosition.MaxForce = 1e6
BoxAlignPosition.Position = ElevatorBoxModel.Position
-- BoxAlignPosition.RigidityEnabled = true
-- Lines below are disabled with RigidityEnabled true
BoxAlignPosition.Responsiveness = Responsiveness
BoxAlignPosition.MaxVelocity = MaxVelocity
--
BoxAlignPosition.Parent = ElevatorBoxModel
local BoxAlignOrientation = Instance.new("AlignOrientation")
BoxAlignOrientation.Mode = Enum.OrientationAlignmentMode.OneAttachment
BoxAlignOrientation.Attachment0 = BoxAttachment
BoxAlignOrientation.RigidityEnabled = true
BoxAlignOrientation.CFrame = ElevatorBoxModel.CFrame
BoxAlignOrientation.Parent = ElevatorBoxModel
return BoxAttachment, BoxAlignPosition, BoxAlignOrientation
end
function Elevator.constructor(ElevatorBoxModel, ElevatorConfigurationTable, FloorLevelingPositions)
assert(#FloorLevelingPositions>1, `"{ElevatorConfigurationTable.Name}" requires more floors to operate. Floors={FloorLevelingPositions}, #Floors={#FloorLevelingPositions}.`)
local function eprint<T...>(...: T...)
print(`[{ElevatorConfigurationTable.Name}]:`, ...)
end
local function ewarn<T...>(...: T...)
warn(`[{ElevatorConfigurationTable.Name}]:`, ...)
warn(debug.traceback())
end
local function eprintStudio<T...>(...: T...)
Out.printStudio(`[{ElevatorConfigurationTable.Name}]:`, ...)
end
local function ewarnStudio<T...>(...: T...)
Out.warnStudio(`[{ElevatorConfigurationTable.Name}]:`, ...)
end
local _BoxAttachment,
BoxAlignPosition,
_BoxAlignOrientation = Mover(ElevatorBoxModel, ElevatorConfigurationTable.Responsiveness, ElevatorConfigurationTable.MaxVelocity)
local RelayAlgorithmConstructor = RelayAlgorithm.constructor(BoxAlignPosition)
local CabProgression = Instance.new("BindableEvent") :: BindableEvent
local CabTraveling = Instance.new("BindableEvent") :: BindableEvent
local Parked = Instance.new("BindableEvent") :: BindableEvent
local Leveling = Instance.new("BindableEvent") :: BindableEvent
local Leveling3Phase = Instance.new("BindableEvent") :: BindableEvent
local Attributes = {
PreviousFloor = Instance.new("IntValue") :: IntValue,
CurrentFloor = Instance.new("IntValue") :: IntValue,
NextFloor = Instance.new("IntValue") :: IntValue,
Goal = Instance.new("IntValue") :: IntValue,
TravelingUpwards = Instance.new("BoolValue") :: BoolValue,
Stopped = Instance.new("BoolValue") :: BoolValue
}
Attributes.CurrentFloor.Value = 1
Attributes.PreviousFloor.Value = Attributes.CurrentFloor.Value
Attributes.NextFloor.Value = Attributes.CurrentFloor.Value+1
Attributes.TravelingUpwards.Value = true
Attributes.Goal.Value = 1
local ElevatorClass = setmetatable({
RelayAlgorithm = RelayAlgorithmConstructor,
FloorLevelingPositions = FloorLevelingPositions,
eprint = eprint,
ewarn = ewarn,
eprintStudio = eprintStudio,
ewarnStudio = ewarnStudio,
Elevator = {
TravelingDirection = Enums.ElevatorCallDirection.Up,
BoxModel = ElevatorBoxModel,
AlignPosition = BoxAlignPosition,
Configuration = ElevatorConfigurationTable,
},
Events = {
Progression = CabProgression.Event,
Traveling = CabTraveling.Event,
Parked = Parked.Event,
Leveling = Leveling.Event,
Leveling3Phase = Leveling3Phase.Event,
__eventInstances__ = {
Progression = CabProgression,
Traveling = CabTraveling,
Parked = Parked,
Leveling = Leveling,
Leveling3Phase = Leveling3Phase,
}
},
Attributes = Attributes,
__functionEvents = {
StartTraveling = Instance.new("BindableEvent") :: BindableEvent
},
__Connections = {}
}, Elevator)
RelayAlgorithmConstructor.Events.Sorted:Connect(function(AddedFloorDirection: Enums.ElevatorCallDirectionValues, FloorDirectionQueue: RelayAlgorithm.FloorDirectionQueue)
if AddedFloorDirection == (ElevatorClass.Attributes.TravelingUpwards.Value and Enums.ElevatorCallDirection.Up or Enums.ElevatorCallDirection.Down) then
local NextFloorAsTraveling = FloorDirectionQueue[1]
if NextFloorAsTraveling then
local Level = FloorLevelingPositions[NextFloorAsTraveling]
ElevatorClass:__TravelToFloorAsync(Level, Vector3.new(0, Level, 0))
end
ElevatorClass.eprintStudio(`Floors sorted in proceeding direction. direction={AddedFloorDirection}, FloorDirectionQueue={FloorDirectionQueue}`)
end
end)
print(`🛗 [{ElevatorConfigurationTable.Name}]: Initialized and ready`)
return ElevatorClass
end
local function ProceedToNextLevel(self: ClassConstructor, Level_Int: number): Vector3?
local VEC3_Y_WRAP = self:GetLevel(Level_Int)
if VEC3_Y_WRAP then
self:__TravelToFloorAsync(Level_Int, VEC3_Y_WRAP)
self.eprintStudio(`Traveling to Level={Level_Int} VEC3_Y_WRAP={VEC3_Y_WRAP}`)
else
self.ewarn(`Failed to get the requested level's Y position. VEC3_Y_WRAP={VEC3_Y_WRAP} Level={Level_Int}`)
end
return VEC3_Y_WRAP
end
local function GoingUpDirectionToDirectionEnum(CurrentFloor: number, RequestedFloor: number): Enums.ElevatorCallDirectionValues
return ElevatorGoingUpDirection(CurrentFloor, RequestedFloor) and Enums.ElevatorCallDirection.Up or Enums.ElevatorCallDirection.Down
end
local function CheckFloorQueue(self: ClassConstructor)
local DirectionToDirectionQueue = self.Attributes.TravelingUpwards.Value and self.RelayAlgorithm.FloorQueue.Up or self.RelayAlgorithm.FloorQueue.Down
local DirectionToOppositeDirectionQueue = self.Attributes.TravelingUpwards.Value and self.RelayAlgorithm.FloorQueue.Down or self.RelayAlgorithm.FloorQueue.Up
if DirectionToDirectionQueue[1] ~= self.Attributes.CurrentFloor.Value then
self.ewarn("The floor queue first index did not match the elevator's current floor, CurrentFloor=", self.Attributes.CurrentFloor.Value, "FloorQueue[1]=", DirectionToDirectionQueue[1])
end
table.remove(DirectionToDirectionQueue, 1)
local IdenticalOpposite = table.find(DirectionToOppositeDirectionQueue, self.Attributes.CurrentFloor.Value)
if IdenticalOpposite then
table.remove(DirectionToOppositeDirectionQueue, IdenticalOpposite)
self.eprintStudio(`Removed the current floor from the opposite direction, ElevatorTravelingUpwards={self.Attributes.TravelingUpwards.Value} CurrentFloor={self.Attributes.CurrentFloor.Value}`)
end
self.eprintStudio("Checking more floors in direction queue. DirectionToDirectionQueue=", DirectionToDirectionQueue)
local NextFloorInQueue = DirectionToOppositeDirectionQueue[1]
if NextFloorInQueue then
local NewDirection = GoingUpDirectionToDirectionEnum(self.Attributes.CurrentFloor.Value, NextFloorInQueue)
self.RelayAlgorithm:Sort(NewDirection :: Enums.ElevatorCallDirectionValues)
local ProceedingToTheNextLevel = ProceedToNextLevel(self, NextFloorInQueue)
if not ProceedingToTheNextLevel then
self.RelayAlgorithm:Sort(Enums.ElevatorCallDirection.Down)
ProceedToNextLevel(self, 1)
end
else
--No more floors in the current direction?
--Check the opposite
self.eprintStudio(`No more floors in the direction TravelingUpwards="{self.Attributes.TravelingUpwards.Value}" checking the opposite direction floors`)
if #DirectionToOppositeDirectionQueue ~= 0 then
local NextLevelOpposite = DirectionToOppositeDirectionQueue[1]
if NextLevelOpposite then
local NewDirection = GoingUpDirectionToDirectionEnum(self.Attributes.CurrentFloor.Value, NextLevelOpposite)
self.RelayAlgorithm:Sort(NewDirection :: Enums.ElevatorCallDirectionValues)
self.eprint("Floors found in the opposite direction. Direction=", Enums.ElevatorCallDirection.Down, "NextLevel=", NextLevelOpposite)
ProceedToNextLevel(self, NextLevelOpposite)
end
end
end
end
local function FloorsClamp(self: ClassConstructor, n: number): number
return Algebra.minmax(1, n, #self.FloorLevelingPositions)
end
local function CabTraveling(self: ClassConstructor, deltaTime: number, LEVEL_VEC3_Y_WRAP: Vector3)
local ElevatorPosition = self.Elevator.BoxModel.Position
local AtFloorY = self.FloorLevelingPositions[FloorsClamp(self, self.Attributes.TravelingUpwards.Value and self.Attributes.CurrentFloor.Value+1 or self.Attributes.CurrentFloor.Value-1)]
if self.Attributes.TravelingUpwards.Value then
--Detecting between the floors
if ElevatorPosition.Y>=AtFloorY-self.Elevator.Configuration.FloorLevelingDistance then
self.Attributes.PreviousFloor.Value = self.Attributes.CurrentFloor.Value
self.Attributes.CurrentFloor.Value+=1
self.Attributes.NextFloor.Value = FloorsClamp(self, self.Attributes.CurrentFloor.Value+1)
self.Events.__eventInstances__.Progression:Fire(self.Attributes.PreviousFloor.Value, self.Attributes.CurrentFloor.Value, self.Attributes.NextFloor.Value)
end
--Elevator is riding upwards towards the destination
if ElevatorPosition.Y>=LEVEL_VEC3_Y_WRAP.Y-self.Elevator.Configuration.FloorLevelingDistance then
self.Events.__eventInstances__.Leveling:Fire()
self.Elevator.AlignPosition.MaxVelocity = self.Elevator.Configuration.LevelingVelocity
if ElevatorPosition.Y>=LEVEL_VEC3_Y_WRAP.Y-self.Elevator.Configuration.FloorLeveling3PhaseDistance then
self.Events.__eventInstances__.Leveling3Phase:Fire()
self.Elevator.AlignPosition.MaxVelocity = self.Elevator.Configuration.Phase3LevelingVelocity
if ElevatorPosition.Y>=LEVEL_VEC3_Y_WRAP.Y-self.Elevator.Configuration.ParkedDistance then
self.Events.__eventInstances__.Parked:Fire()
CheckFloorQueue(self);
(self.__Connections.Moving :: RBXScriptConnection):Disconnect()
end
end
end
else
if ElevatorPosition.Y<=AtFloorY+self.Elevator.Configuration.FloorLevelingDistance then
self.Attributes.PreviousFloor.Value = self.Attributes.CurrentFloor.Value
self.Attributes.CurrentFloor.Value-=1
self.Attributes.NextFloor.Value = FloorsClamp(self, self.Attributes.CurrentFloor.Value-1)
self.Events.__eventInstances__.Progression:Fire(self.Attributes.PreviousFloor.Value, self.Attributes.CurrentFloor.Value, self.Attributes.NextFloor.Value)
end
--Elevator is riding upwards towards the destination
if ElevatorPosition.Y<=LEVEL_VEC3_Y_WRAP.Y+self.Elevator.Configuration.FloorLevelingDistance then
self.Events.__eventInstances__.Leveling:Fire()
self.Elevator.AlignPosition.MaxVelocity = self.Elevator.Configuration.LevelingVelocity
if ElevatorPosition.Y<=LEVEL_VEC3_Y_WRAP.Y+self.Elevator.Configuration.FloorLeveling3PhaseDistance then
self.Events.__eventInstances__.Leveling3Phase:Fire()
self.Elevator.AlignPosition.MaxVelocity = self.Elevator.Configuration.Phase3LevelingVelocity
if ElevatorPosition.Y<=LEVEL_VEC3_Y_WRAP.Y+self.Elevator.Configuration.ParkedDistance then
self.Events.__eventInstances__.Parked:Fire()
CheckFloorQueue(self);
(self.__Connections.Moving :: RBXScriptConnection):Disconnect()
end
end
end
end
self.Events.__eventInstances__.Traveling:Fire(deltaTime, ElevatorPosition)
end
function Elevator:GetLevel(Level_Int)
local Level = self.FloorLevelingPositions[Level_Int]
if Level then
--local VEC3_Y_WRAP_LOSSY = Vector3.yAxis*Level
return Vector3.new(0, Level, 0)
end
return nil
end
function Elevator:__TravelToFloorAsync(Level_Int, LEVEL_VEC3_Y_WRAP)
if self.Elevator.Configuration.Functions.ManualTravelStart then
self.__functionEvents.StartTraveling.Event:Wait()
end
if self.__Connections.Moving and self.__Connections.Moving.Connected then
self.__Connections.Moving:Disconnect()
end
assert(not self.Elevator.BoxModel.Anchored, "The elevator cannot move! Its box model is Anchored.")
local ElevatorTravelingUpwards = ElevatorGoingUpDirection(self.Attributes.CurrentFloor.Value, Level_Int)
self.Attributes.Goal.Value = Level_Int
self.Attributes.TravelingUpwards.Value = ElevatorTravelingUpwards
self.__Connections.Moving = RunService.Heartbeat:Connect(function(deltaTime: number)
CabTraveling(self, deltaTime, LEVEL_VEC3_Y_WRAP)
end)
--Set the elevator's AlignPosition to the floor Y vector
self.Elevator.AlignPosition.Position = Vector3.new(self.Elevator.AlignPosition.Position.X, LEVEL_VEC3_Y_WRAP.Y, self.Elevator.AlignPosition.Position.Z)
--Set the elevator's max velocity to its fastest speed when moving starts
self.Elevator.AlignPosition.MaxVelocity = self.Elevator.Configuration.MaxVelocity
end
function Elevator:RequestLevelAsync(RequestedLevel, Direction)
local Level = self:GetLevel(RequestedLevel)
if Level then
if (RequestedLevel == 1 and Direction == Enums.ElevatorCallDirection.Down) or (RequestedLevel == #self.FloorLevelingPositions and Direction == Enums.ElevatorCallDirection.Up) then
self.ewarn(`Impossible direction requested, Direction={Direction}, RequestedLevel={Level}`)
return false
else
local DirectionQueue = Direction == Enums.ElevatorCallDirection.Up and self.RelayAlgorithm.FloorQueue.Up or self.RelayAlgorithm.FloorQueue.Down
-- local OppositeQueue = Direction == Enums.ElevatorCallDirection.Up and self.RelayAlgorithm.FloorQueue.Down or self.RelayAlgorithm.FloorQueue.Up
self.RelayAlgorithm:AddFloor(Direction :: Enums.ElevatorCallDirectionValues, RequestedLevel)
if #DirectionQueue == 1 then
self:__TravelToFloorAsync(RequestedLevel, Level)
end
end
return true
end
self.ewarn(`Requested floor: "{RequestedLevel}" does not exist for this elevator`)
return false
end
function Elevator:StartTraveling()
self.__functionEvents.StartTraveling:Fire()
end
return Elevator