Studio told me to report this.
script:
local module = {}
local runService = game:GetService('RunService')
local Signal = require(game.ReplicatedStorage.Signal)
export type ValueFunction<T> = (T, T, number, {[any]: any}?)->T
export type StyleFunction = (number, {[any]: any}?)->number
export type DirectionFunction = {
a: (number, {[any]: any}?)->number,
b: (number, number, {[any]: any}?)->number
}
export type TweenResult<K, V> = {
tween: Tween<K, V>,
value: V,
didFinish: boolean,
elapsedTime: number,
elapsedAlpha: number,
transformedAlpha: number,
}
export type Tween<K, V> = {
lastStep: number,
Connect: ( Tween<K, V>, (TweenResult<K, V>)->() )->Signal.Connection<TweenResult<K, V>>,
Wait: (Tween<K, V>)->TweenResult<K, V>,
Step: (Tween<K, V>, number)->(),
Stop: (Tween<K, V>, boolean)->TweenResult<K, V>,
GetRawAlpha: (Tween<K, V>)->(number),
GetTransformedAlpha: (Tween<K, V>)->(number),
GetCurrentValue: (Tween<K, V>)->(V, number),
SetSteppingSignal: (Tween<K, V>, string | RBXScriptSignal)->()
}
export type TweenParams<K, V> = {
object: any?,
key: K?,
set_f: ((K, V, number)->())?,
start: V?,
start_f: ((K)->V)?,
finish: V?,
finish_f: ((K, V)->V)?,
length: number?,
length_f: (()->number)?,
style: string?,
style_f: StyleFunction?,
style_args: {[any]: any}?,
direction: string?,
direction_f: DirectionFunction?,
direction_args: {[any]: any}?,
as: string?,
value_f: ValueFunction<V>?,
value_args: {[any]: any}?,
}
local styleTransforms: {[string]: StyleFunction} = {
Linear = function(a: number) : number
return a
end,
Sine = function(a: number) : number
return math.sin(a * math.pi/2)
end,
}
local directionTransforms: {[string]: DirectionFunction} = {
In = {
a = function(raw: number) : number
return raw
end,
b = function(raw: number, styled: number) : number
return styled
end
},
Out = {
a = function(raw: number) : number
return 1-raw
end,
b = function(raw: number, styled: number) : number
return styled
end
},
InOut = {
a = function(raw: number) : number
if raw > 0.5 then
return 1-(raw-0.5)*2
else
return raw*2
end
end,
b = function(raw: number, styled: number) : number
if raw > 0.5 then
return 0.5 + styled/2
else
return styled/2
end
end
},
OutIn = {
a = function(raw: number) : number
if raw < 0.5 then
return 1-raw*2
else
return (raw-0.5)*2
end
end,
b = function(raw: number, styled: number) : number
if raw < 0.5 then
return styled/2
else
return 0.5 + styled/2
end
end
}
}
local function makeFlipFunction(value)
type T = typeof(value)
local flipFunction: ValueFunction<T> = function(a: T, b: T, alpha: number) : T
if alpha > 0.5 then
return b
end
return a
end
return flipFunction
end
local function makeMathFunction(value)
type T = typeof(value)
local mathFunction: ValueFunction<T> = function(a: T, b: T, alpha: number) : T
return (b::number - a::number)*alpha + a
end
return mathFunction
end
local valueFunctions: { [string] : ValueFunction<any> }
valueFunctions = {
number = makeMathFunction(1),
boolean = makeFlipFunction(true),
string = makeFlipFunction('a'),
['nil'] = makeFlipFunction(nil),
table = makeFlipFunction({}),
Axes = function(a: Axes, b: Axes, alpha: number) : Axes
local t: {Enum.Axis} = {}
if valueFunctions.boolean(a.X, b.X, alpha) then
table.insert(t, Enum.Axis.X)
end
if valueFunctions.boolean(a.Y, b.Y, alpha) then
table.insert(t, Enum.Axis.Y)
end
if valueFunctions.boolean(a.Z, b.Z, alpha) then
table.insert(t, Enum.Axis.Z)
end
return Axes.new(unpack(t))
end,
BrickColor = function(a: BrickColor, b: BrickColor, alpha: number) : BrickColor
return BrickColor.new( valueFunctions.Color3(a.Color, b.Color, alpha) )
end,
CFrame = function(a: CFrame, b: CFrame, alpha: number) : CFrame
return a:Lerp(b, alpha)
end,
CFrameRaw = function(a: CFrame, b: CFrame, alpha: number) : CFrame
-- CFrame.Lerp performs a quarternion interpolation which is not always ideal, especially for camera animations
local ax, ay, az = a:ToOrientation()
local bx, by, bz = b:ToOrientation()
return CFrame.new(a.Position:Lerp(b.Position, alpha)) * CFrame.fromOrientation(
(bx-ax)*alpha + ax,
(by-ay)*alpha + ay,
(bz-az)*alpha + az
)
end,
CFrameSlerp = function(a: CFrame, b: CFrame, alpha: number, args: any) : CFrame
return CFrame.new(valueFunctions.Vector3Slerp(a, b, alpha, args), args.focus)
end,
CatalogSearchParams = function(a: CatalogSearchParams, b: CatalogSearchParams, alpha: number) : CatalogSearchParams
local new = CatalogSearchParams.new()
new.SearchKeyword = valueFunctions.string(a.SearchKeyword, b.SearchKeyword, alpha)
new.MinPrice = valueFunctions.number(a.MinPrice, b.MinPrice, alpha)
new.MaxPrice = valueFunctions.number(a.MaxPrice, b.MaxPrice, alpha)
new.SortType = valueFunctions.EnumItem(a.SortType, b.SortType, alpha)
new.CategoryFilter = valueFunctions.EnumItem(a.CategoryFilter, b.CategoryFilter, alpha)
new.BundleTypes = valueFunctions.table(a.BundleTypes, b.BundleTypes, alpha)
new.AssetTypes = valueFunctions.table(a.AssetTypes, b.AssetTypes, alpha)
return new
end,
Color3 = function(a: Color3, b: Color3, alpha: number) : Color3
return a:Lerp(b, alpha)
end,
Color3HSV = function(a: Color3, b: Color3, alpha: number) : Color3
local ha, sa, va = Color3.toHSV(a)
local hb, sb, vb = Color3.toHSV(b)
if hb - 0.5 > ha then
ha = ha + 1
elseif hb + 0.5 < ha then
ha = ha - 1
end
return Color3.fromHSV(
((hb - ha)*alpha + ha)%1.000001,
(sb - sa)*alpha + sa,
(vb - va)*alpha + va
)
end,
ColorSequence = function(a: ColorSequence, b: ColorSequence, alpha: number) : ColorSequence
local times: {number} = {}
for i, sequence: ColorSequence in next, {a, b} do
for i, keypoint: ColorSequenceKeypoint in next, sequence.Keypoints do
if not table.find(times, keypoint.Time) then
table.insert(times, keypoint.Time)
end
end
end
local keys: {[ColorSequence]: {[number]: Color3}} = {}
for i, sequence: ColorSequence in next, {a, b} do
keys[sequence] = {}
for i, time: number in next, times do
local early: ColorSequenceKeypoint = nil
local late: ColorSequenceKeypoint = nil
for i, keypoint: ColorSequenceKeypoint in next, sequence.Keypoints do
if not early or keypoint.Time < time then
early = keypoint
elseif keypoint.Time == time then
early = keypoint
break
else
late = keypoint
break
end
end
if early == late or not late then
keys[sequence][time] = early.Value
else
keys[sequence][time] = early.Value:Lerp(late.Value, (time - early.Time) / (late.Time - early.Time))
end
end
end
local keypoints: {ColorSequenceKeypoint} = {}
for i, time: number in next, times do
table.insert(keypoints, ColorSequenceKeypoint.new(time, keys[a][time]:Lerp(keys[b][time], alpha)))
end
return ColorSequence.new(keypoints)
end,
ColorSequenceKeypoint = function(a: ColorSequenceKeypoint, b: ColorSequenceKeypoint, alpha: number) : ColorSequenceKeypoint
return ColorSequenceKeypoint.new(
valueFunctions.number(a.Time, b.Time, alpha),
valueFunctions.Color3(a.Value, b.Value, alpha)
)
end,
DateTime = function(a: DateTime, b: DateTime, alpha: number) : DateTime
return DateTime.fromUnixTimestamp(
valueFunctions.number(a.UnixTimestamp, b.UnixTimestamp, alpha)
)
end,
DockWidgetPluginGuiInfo = function(a: DockWidgetPluginGuiInfo, b: DockWidgetPluginGuiInfo, alpha: number) : DockWidgetPluginGuiInfo
return DockWidgetPluginGuiInfo.new(
valueFunctions.EnumItem((a::any).InitialDockState, (b::any).InitialDockState, alpha),
valueFunctions.boolean(a.InitialEnabled, b.InitialEnabled, alpha),
valueFunctions.boolean(a.InitialEnabledShouldOverrideRestore, b.InitialEnabledShouldOverrideRestore, alpha),
valueFunctions.number(a.FloatingXSize, b.FloatingXSize, alpha),
valueFunctions.number(a.FloatingYSize, b.FloatingYSize, alpha),
valueFunctions.number(a.MinWidth, b.MinWidth, alpha),
valueFunctions.number(a.MinHeight, b.MinHeight, alpha)
)
end,
EnumItem = function(a: EnumItem, b: EnumItem, alpha: number) : EnumItem
local possible: {EnumItem} = (b.EnumType::any):GetEnumItems()
table.sort(possible, function(a: EnumItem, b: EnumItem)
return a.Value < b.Value
end)
local current = 1
local current_at = table.find(possible, a)
if current_at then
current = current_at
end
local target = #possible
local target_at = table.find(possible, a)
if target_at then
target = target_at
end
return possible[math.floor((target - current) * alpha + current)]
end,
Enum = makeFlipFunction(Enum.NormalId),
Enums = makeFlipFunction(Enum),
Faces = function(a: Faces, b: Faces, alpha: number) : Faces
local t: {Enum.NormalId} = {}
for i, normalId in next, Enum.NormalId:GetEnumItems() do
local normalId = normalId::Enum.NormalId
if valueFunctions.boolean((a::any)[normalId.Name], (b::any)[normalId.Name], alpha) then
table.insert(t, normalId)
end
end
return Faces.new(unpack(t))
end,
Instance = makeFlipFunction(workspace),
NumberRange = function(a: NumberRange, b: NumberRange, alpha: number) : NumberRange
return NumberRange.new(
valueFunctions.number(a.Min, b.Min, alpha),
valueFunctions.number(a.Max, b.Max, alpha)
)
end,
NumberSequence = function(a: NumberSequence, b: NumberSequence, alpha: number) : NumberSequence
local times: {number} = {}
for i, sequence: NumberSequence in next, {a, b} do
for i, keypoint: NumberSequenceKeypoint in next, sequence.Keypoints do
if not table.find(times, keypoint.Time) then
table.insert(times, keypoint.Time)
end
end
end
local keys: {
[NumberSequence]: {
[number]: {
value: number,
envelope: number
}
}
} = {}
for i, sequence: NumberSequence in next, {a, b} do
keys[sequence] = {}
for i, time: number in next, times do
local early: NumberSequenceKeypoint = nil
local late: NumberSequenceKeypoint = nil
for i, keypoint: NumberSequenceKeypoint in next, sequence.Keypoints do
if not early or keypoint.Time < time then
early = keypoint
elseif keypoint.Time == time then
early = keypoint
break
else
late = keypoint
break
end
end
if early == late or not late then
keys[sequence][time] = {
value = early.Value,
envelope = early.Envelope
}
else
keys[sequence][time] = {
value = (late.Value - early.Value) * (time - early.Time) / (late.Time - early.Time) + early.Value,
envelope = (late.Envelope - early.Envelope) * (time - early.Time) / (late.Time - early.Time) + early.Envelope
}
end
end
end
local keypoints: {NumberSequenceKeypoint} = {}
for i, time: number in next, times do
table.insert(keypoints, NumberSequenceKeypoint.new(
time,
(keys[b][time].value - keys[a][time].value) * alpha + keys[a][time].value,
(keys[b][time].envelope - keys[a][time].envelope) * alpha + keys[a][time].envelope
))
end
return NumberSequence.new(keypoints)
end,
NumberSequenceKeypoint = function(a: NumberSequenceKeypoint, b: NumberSequenceKeypoint, alpha: number) : NumberSequenceKeypoint
return NumberSequenceKeypoint.new(
valueFunctions.number(a.Time, b.Time, alpha),
valueFunctions.number(a.Value, b.Value, alpha),
valueFunctions.number(a.Envelope, b.Envelope, alpha)
)
end,
OverlapParams = function(a: OverlapParams, b: OverlapParams, alpha: number) : OverlapParams
local params = OverlapParams.new()
params.MaxParts = valueFunctions.boolean(a.MaxParts, b.MaxParts, alpha)
params.CollisionGroup = alpha > 0.5 and b.CollisionGroup or a.CollisionGroup
params.FilterDescendantsInstances = valueFunctions.table(a.FilterDescendantsInstances, b.FilterDescendantsInstances, alpha)
return params
end,
PathWaypoint = function(a: PathWaypoint, b: PathWaypoint, alpha: number) : PathWaypoint
return PathWaypoint.new(
valueFunctions.Vector3(a.Position, b.Position, alpha),
valueFunctions.EnumItem(a.Action, b.Action, alpha)
)
end,
PhysicalProperties = function(a: PhysicalProperties, b: PhysicalProperties, alpha: number) : PhysicalProperties
return PhysicalProperties.new(
valueFunctions.number(a.Density, b.Density, alpha),
valueFunctions.number(a.Friction, b.Friction, alpha),
valueFunctions.number(a.Elasticity, b.Elasticity, alpha),
valueFunctions.number(a.FrictionWeight, b.FrictionWeight, alpha),
valueFunctions.number(a.ElasticityWeight, b.ElasticityWeight, alpha)
)
end,
Random = makeFlipFunction(Random.new()),
Ray = function(a: Ray, b: Ray, alpha: number) : Ray
return Ray.new(
valueFunctions.Vector3(a.Origin, b.Origin, alpha),
valueFunctions.Vector3(a.Direction, b.Direction, alpha)
)
end,
RaycastParams = function(a: RaycastParams, b: RaycastParams, alpha: number) : RaycastParams
local params = RaycastParams.new()
params.FilterDescendantsInstances = valueFunctions.table(a.FilterDescendantsInstances, b.FilterDescendantsInstances, alpha)
params.FilterType = valueFunctions.EnumItem(a.FilterType, b.FilterType, alpha)
params.IgnoreWater = valueFunctions.boolean(a.IgnoreWater, b.IgnoreWater, alpha)
params.CollisionGroup = alpha > 0.5 and b.CollisionGroup or a.CollisionGroup
return params
end,
Rect = function(a: Rect, b: Rect, alpha: number) : Rect
return Rect.new(
valueFunctions.number(a.Min.X, b.Min.X, alpha),
valueFunctions.number(a.Min.Y, b.Min.Y, alpha),
valueFunctions.number(a.Max.X, b.Max.X, alpha),
valueFunctions.number(a.Max.Y, b.Max.Y, alpha)
)
end,
Region3 = function(a: Region3, b: Region3, alpha: number) : Region3
return Region3.new(
valueFunctions.Vector3(a.CFrame * (-a.Size/2), b.CFrame * (-b.Size/2), alpha),
valueFunctions.Vector3(a.CFrame * (a.Size/2), b.CFrame * (b.Size/2), alpha)
)
end,
Region3int16 = function(a: Region3int16, b: Region3int16, alpha: number) : Region3int16
return Region3int16.new(
valueFunctions.Vector3int16(a.Min, b.Min, alpha),
valueFunctions.Vector3int16(a.Max, b.Max, alpha)
)
end,
TweenInfo = function(a: TweenInfo, b: TweenInfo, alpha: number) : TweenInfo
return TweenInfo.new(
valueFunctions.number(a.Time, b.Time, alpha),
valueFunctions.EnumItem(a.EasingStyle, b.EasingStyle, alpha),
valueFunctions.EnumItem(a.EasingDirection, b.EasingDirection, alpha),
valueFunctions.number(a.RepeatCount, b.RepeatCount, alpha),
valueFunctions.boolean(a.Reverses, b.Reverses, alpha),
valueFunctions.number(a.DelayTime, b.DelayTime, alpha)
)
end,
UDim = function(a: UDim, b: UDim, alpha: number) : UDim
return UDim.new(
valueFunctions.number(a.Scale, b.Scale, alpha),
valueFunctions.number(a.Offset, b.Offset, alpha)
)
end,
UDim2 = function(a: UDim2, b: UDim2, alpha: number) : UDim2
return UDim2.new(
valueFunctions.number(a.X.Scale, b.X.Scale, alpha),
valueFunctions.number(a.X.Offset, b.X.Offset, alpha),
valueFunctions.number(a.Y.Scale, b.Y.Scale, alpha),
valueFunctions.number(a.Y.Offset, b.Y.Offset, alpha)
)
end,
Vector2 = function(a: Vector2, b: Vector2, alpha: number) : Vector2
return a:Lerp(b, alpha)
end,
Vector2int16 = function(a: Vector2int16, b: Vector2int16, alpha: number) : Vector2int16
return Vector2int16.new(
valueFunctions.number(a.X, b.X, alpha),
valueFunctions.number(a.Y, b.Y, alpha)
)
end,
Vector3 = function(a: Vector3, b: Vector3, alpha: number) : Vector3
return a:Lerp(b, alpha)
end,
Vector3Slerp = function(a: Vector3, b: Vector3, alpha: number, args: any) : Vector3
local am = (a - args.focus).Magnitude
local bm = (b - args.focus).Magnitude
local midpoint = a:Lerp(b, alpha)
return (midpoint - args.focus).Unit * ((bm-am)*alpha+am)
end,
Vector3int16 = function(a: Vector3int16, b: Vector3int16, alpha: number) : Vector3int16
return Vector3int16.new(
valueFunctions.number(a.X, b.X, alpha),
valueFunctions.number(a.Y, b.Y, alpha),
valueFunctions.number(a.Z, b.Z, alpha)
)
end,
}
type ExtraParams = {
style: string?,
direction: string?,
}
local namedSteppingSignals: {[string]: RBXScriptSignal} = {
Heartbeat = runService.Heartbeat,
PostSimulation = runService.PostSimulation,
PreAnimation = runService.PreAnimation,
PreRender = runService.PreRender,
PreSimulation = runService.PreSimulation,
RenderStepped = runService.RenderStepped,
Stepped = runService.Stepped
}
local steppingSignalBindings: {[RBXScriptSignal]: {Tween<any, any>}} = {}
local tweensByObject: {
[any]: {
[any]: Tween<any, any>
}
} = {}
function module:Tween(params: TweenParams<any, any>) : Tween<any, any>
type K = any
type V = any
local object = params.object
local key = params.key
local set_f = params.set_f
local start = params.start
local start_f = params.start_f
local finish = params.finish
local finish_f = params.finish_f
local length = params.length
local length_f = params.length_f
local style = params.style
local style_f = params.style_f
local style_args = params.style_args
local direction = params.direction
local direction_f = params.direction_f
local direction_args = params.direction_args
local as = params.as
local value_f = params.value_f
local value_args = params.value_args
local function get_start() : V
if start_f then
return start_f(key)
else
return start
end
end
local function get_finish() : V
if finish_f then
return finish_f(key, get_start())
else
return finish
end
end
local function get_length() : number
if length_f then
return length_f()
elseif length then
return length
else
return 1
end
end
local function get_style_f() : StyleFunction
if style then
return styleTransforms[style]
elseif style_f then
return style_f
else
return styleTransforms.Sine
end
end
local function get_direction_f() : DirectionFunction
if direction then
return directionTransforms[direction]
elseif direction_f then
return direction_f
else
return directionTransforms.In
end
end
local function get_value_f() : ValueFunction<V>
if as then
return valueFunctions[as]
elseif value_f then
return value_f
else
return valueFunctions[typeof(start)]
end
end
local currentTime = 0
local finishSignal = Signal.new() :: Signal.Signal<TweenResult<K, V>>
local isStopping = false
local stopping_signal: Signal.Signal<TweenResult<K, V>>
local stepping_signal: RBXScriptSignal
local this
local tween: Tween<K, V> = {
lastStep = tick(),
Connect = function(self: Tween<K, V>, f: (TweenResult<K, V>)->() ) : Signal.Connection<TweenResult<K, V>>
return finishSignal:Connect(f)
end,
Wait = function(self: Tween<K, V>) : TweenResult<K, V>
return finishSignal:Wait()
end,
Step = function(self: Tween<K, V>, dt: number)
currentTime += dt
local value, alpha = self:GetCurrentValue()
if set_f then
set_f(key, value, alpha)
elseif object then
object[key] = value
end
if alpha >= 1 then
self:Stop(true)
end
end,
Stop = function(self: Tween<K, V>, finished: boolean) : TweenResult<K, V>
if isStopping then
if not stopping_signal then
stopping_signal = Signal.new()
end
return stopping_signal:Wait()
end
isStopping = true
self:Step(0)
local result: TweenResult<K, V> = {
tween = self,
value = self:GetCurrentValue(),
didFinish = finished,
elapsedAlpha = math.min(currentTime/get_length()),
transformedAlpha = self:GetTransformedAlpha(),
elapsedTime = currentTime,
}
if stepping_signal then
local bindings = steppingSignalBindings[stepping_signal]
local index = table.find(bindings, self)
if index then
table.remove(bindings, index)
end
end
tweensByObject[object][key] = nil
if next(tweensByObject[object]) == nil then
tweensByObject[object] = nil
end
if stopping_signal then
stopping_signal:Fire(result)
stopping_signal:Destroy()
end
return result
end,
GetRawAlpha = function(self: Tween<K, V>) : number
return math.clamp(currentTime / get_length(), 0, 1)
end,
GetTransformedAlpha = function(self: Tween<K, V>) : number
local raw = self:GetRawAlpha()
local direction_a = get_direction_f().a(raw, direction_args)
local styled = get_style_f()(raw, style_args)
local transformed = get_direction_f().b(raw, styled, direction_args)
return transformed
end,
GetCurrentValue = function(self: Tween<K, V>) : (V, number)
local a = get_start()
local b = get_finish()
local alpha = self:GetTransformedAlpha()
return get_value_f()(a, b, alpha, value_args), alpha
end,
SetSteppingSignal = function(self: Tween<K, V>, signal_param: string | RBXScriptSignal | Signal.Signal<any> | nil)
local signal = runService.RenderStepped
if signal_param == nil then
signal = namedSteppingSignals.RenderStepped
elseif typeof(signal_param) == 'string' then
signal = namedSteppingSignals[signal_param]
else
signal = signal_param
end
if steppingSignalBindings[signal] then
table.insert(steppingSignalBindings[signal], self)
else
steppingSignalBindings[signal] = {self}
local con; con = signal:Connect(function()
local now = tick()
for i, tween in next, {unpack(steppingSignalBindings[signal])} do
local dt = now - tween.lastStep
tween.lastStep = dt
tween:Step(dt)
end
if #steppingSignalBindings[signal] == 0 then
steppingSignalBindings[signal] = nil
con:Disconnect()
end
end)
end
stepping_signal = signal
end,
}
this = tween
if object then
if not tweensByObject[object] then
tweensByObject[object] = {}
end
if key then
if tweensByObject[object][key] then
tweensByObject[object][key]:Stop(false)
end
tweensByObject[object][key] = this
end
end
this:SetSteppingSignal()
this:Step(0)
return tween
end
function module:StopTween(object:any, key:any)
local byObject = tweensByObject[object]
if byObject then
local tween = byObject[key]
if tween then
tween:Stop(false)
end
end
end
function module:StopAllTweens(object:any)
local byObject = tweensByObject[object]
if byObject then
while next(byObject) do
next(byObject):Stop(false)
end
end
end
setmetatable(module, {
__call = function(t, params: TweenParams<any, any>) : Tween<any, any>
return module:Tween(params)
end,
})
return module
Signal module:
--!strict
--[[
Signal API
signal
Signal .new()
Signal
Connection :Connect(func)
Variant :wait()
void :Destroy()
void :fire(variant)
Connection
bool .connected
void :disconnect()
--]]
local private = {}
local public = {}
export type Connection<T> = {
connected: boolean,
f: ( (...T)->() )?,
signal: Signal<T>?,
c: RBXScriptConnection?,
traceback: string,
Disconnect: (Connection<T>)->()
}
export type Signal<T> = {
connections: {[Connection<T>]: boolean},
Connect: (Signal<T>, (...T)->())->Connection<T>,
Wait: (Signal<T>)->...T,
Fire: (Signal<T>, ...T)->(),
Destroy: (Signal<T>)->(),
GetDestroyedSignal: (Signal<T>)->Signal<nil>,
gestroyedSignal: Signal<nil>?,
GetEvent: (Signal<T>)->BindableEvent,
e: BindableEvent?,
args: {[any]: any},
connectionCount: number,
uidIndex: number,
debugMode: boolean?,
destroyedSignal: Signal<nil>?
}
--
-- Private
function private.connection_disconnect(self: Connection<any>) : ()
if self.connected then
self.connected = false
self.f = nil
local signal = self.signal
if signal then
signal.connections[self] = nil
signal.connectionCount = signal.connectionCount - 1
self.signal = nil
end
local c = self.c
if c then
c:Disconnect()
self.c = nil
end
end
end
function private.signal_connect(self: Signal<any>, f: (...any)->()) : Connection<any>
local connection: Connection<any> = {
connected = true,
f = f,
signal = self,
Disconnect = private.connection_disconnect,
c = nil,
traceback = debug.traceback()
}
self.connections[connection] = true
local dbSignalFiredAt
connection.c = self:GetEvent().Event:Connect(function(i)
if self.debugMode then
print('CONNECTION TRACEBACK',connection.traceback)
print('CALL FUNCTION',connection.f,unpack(self.args))
dbSignalFiredAt = tick()
end
local f = connection.f
if f then
f(unpack(self.args[i]))
end
if self.debugMode then
warn('time',tick()-dbSignalFiredAt)
end
end)
self.connectionCount += 1
return connection
end
function private.signal_wait(self: Signal<any>) : ...any
self.connectionCount += 1
local i = self:GetEvent().Event:Wait()
self.connectionCount -= 1
return unpack(self.args[i])
end
function private.signal_fire(self: Signal<any>, ...: any) : ()
if self.debugMode then
warn('FIRING SIGNAL IN DEBUG MODE',self,self.connectionCount,debug.traceback(),'////',...)
end
if self.connectionCount > 0 then
if self.debugMode then
print('^ firing')
end
self.uidIndex = self.uidIndex + 1
local i = self.uidIndex
self.args[i] = {...}
self:GetEvent():Fire(i)
self.args[i] = nil
end
end
function private.signal_destroy(self: Signal<any>) : ()
local e = self.e
if e then
e:Destroy()
self.e = nil
end
for c in next, self.connections do
c:Disconnect()
end
local destroyedSignal = self.destroyedSignal
if destroyedSignal then
destroyedSignal:Fire()
destroyedSignal:Destroy()
end
self.connectionCount = -1
end
function private.signal_getDestroyedSignal(self: Signal<any>) : Signal<nil>
local destroyedSignal = self.destroyedSignal
if destroyedSignal == nil then
destroyedSignal = private.new()
self.destroyedSignal = destroyedSignal
end
local destroyedSignal = destroyedSignal :: Signal<nil>
return destroyedSignal
end
function private.signal_getEvent(self: Signal<any>) : BindableEvent
local e = self.e
if e then
return e
end
local e = Instance.new('BindableEvent')
self.e = e
return e
end
function private.new() : Signal<any>
local signal: Signal<any> = {
connections = {},
Connect = private.signal_connect,
Wait = private.signal_wait,
Fire = private.signal_fire,
Destroy = private.signal_destroy,
GetDestroyedSignal = private.signal_getDestroyedSignal,
GetEvent = private.signal_getEvent,
destroyedSignal = nil,
e = nil,
args = {},
connectionCount = 0,
uidIndex = 0
}
return signal
end
function private.load()
public.new = private.new
return public
end
--
-- Module
return private.load()