Roblox/luau_corpus huggingface dataset:
--// This method is to be used with the new filter API. This method takes the 
--// TextFilterResult objects and converts them into the appropriate string
--// messages for each player.
function methods:InternalSendFilteredMessageWithFilterResult(inMessageObj, channelName)
	local messageObj = ShallowCopy(inMessageObj)

	local oldFilterResult = messageObj.FilterResult
	local player = self:GetPlayer()

	local msg = ""
	pcall(function()
		if (messageObj.IsFilterResult) then
			if (player) then
				msg = oldFilterResult:GetChatForUserAsync(player.UserId)
			else
				msg = oldFilterResult:GetNonChatStringForBroadcastAsync()
			end
		else
			msg = oldFilterResult
		end
	end)

	--// Messages of 0 length are the result of two users not being allowed
	--// to chat, or GetChatForUserAsync() failing. In both of these situations,
	--// messages with length of 0 should not be sent.
	if (#msg > 0) then
		messageObj.Message = msg
		messageObj.FilterResult = nil
		self:InternalSendFilteredMessage(messageObj, channelName)
	end
end

function methods:InternalSendSystemMessage(messageObj, channelName)
	local success, err = pcall(function()
		self:LazyFire("eReceivedSystemMessage", messageObj, channelName)
		self.EventFolder.OnNewSystemMessage:FireClient(self.PlayerObj, messageObj, channelName)
	end)
	if not success and err then
		print("Error sending internal system message: " ..err)
	end
end

function methods:UpdateChannelNameColor(channelName, channelNameColor)
	self:LazyFire("eChannelNameColorUpdated", channelName, channelNameColor)
	self.EventFolder.ChannelNameColorUpdated:FireClient(self.PlayerObj, channelName, channelNameColor)
end
--[[
	
	When the game is run, this script automatically places all of the model's contents into the correct locations and deletes any leftover clutter.
	
	All you have to do is configure the music IDs and/or zones as described in the README - the rest will be automatically handled! 
	
--]]

local settings = require(script.Parent.Settings)

if settings.UseGlobalBackgroundMusic == false and settings.UseMusicZones == false then
	error("Cindering's BGM error: You have disabled both Global Background Music and Music Zones. You must change at least one of these settings to 'true'.")
	return
end

local name1 = "[1] Global Background Music"
local name2 = "[2] Background Music Zones"

local container = Instance.new("Folder")
container.Name = "CinderingBGM"

script.Parent.Settings.Parent = container

script.LocalBackgroundMusic.Parent = game.StarterPlayer.StarterPlayerScripts

local music = Instance.new("Folder")
local zones = Instance.new("Folder",music)
zones.Name = "MusicZones"
local global = Instance.new("Folder",music)
global.Name = "GlobalMusic"

if settings.UseMusicZones == true then
	local folder = script.Parent:FindFirstChild(name2) or game.ReplicatedStorage:FindFirstChild(name2) or workspace:FindFirstChild(name2) or game:FindFirstChild(name2,true) -- never know where someone might accidentally drag that folder...
	
	if folder then
		for _,model in pairs(folder:GetChildren()) do
			if (model:IsA("Model") and model:FindFirstChild("Music")) then
				model.Parent = zones
			end
		end
	else
		error("Cindering's BGM error: Your background music zones folder could not be found! You may have deleted/renamed the original folder. It should be named: "..name2)
		return
	end
end

if settings.UseGlobalBackgroundMusic == true then
	local folder = script.Parent:FindFirstChild(name1) or game.ReplicatedStorage:FindFirstChild(name1) or workspace:FindFirstChild(name1) or game:FindFirstChild(name1,true)
	
	if folder then
		for _,v in pairs(folder:GetChildren()) do
			if v:IsA("Sound") then
				v.Parent = global
			end
		end
	else
		error("Cindering's BGM error: Your global background music folder could not be found! You may have deleted/renamed the original folder. It should be named: "..name1)
		return
	end
end

if settings.UseGlobalBackgroundMusic == true and #global:GetChildren() == 0 then
	warn("Cindering's BGM warning: Your global background music folder is completely empty; no music will be played from there.")
end
if settings.UseMusicZones == true and #zones:GetChildren() == 0 then
	warn("Cindering's BGM warning: Your background music zones folder is completely empty; no music will be played from there.")
end

music.Name = "MusicFolder"
music.Parent = container

local count = 0
function recurse(instance)
	for _,v in pairs(instance:GetChildren()) do
		count = count + 1
		recurse(v)
	end
end
recurse(music)
local val = Instance.new("IntValue",container)
val.Name = "ObjectCount"
val.Value = count

container.Parent = game.ReplicatedStorage

script.Parent:Destroy()
--[=[
	Detects if a table is an array, meaning purely number indexes and indexes starting at 1.
	
	```lua
	local Array = {"A", "B", "C", "D"}
	local Dictionary = { NotAnArray = true }
	
	print(TableKit.IsArray(Array), TableKit.IsArray(Dictionary)) -- prints true, false
	```

	@within TableKit
	@param mysteryTable table
	@return boolean
]=]
function TableKit.IsArray(mysteryTable: { [unknown]: unknown }): boolean
	local count = 0
	for _ in mysteryTable do
		count += 1
	end
	return count == #mysteryTable
end
--script.Parent.NN.Velocity = script.Parent.NN.CFrame.lookVector *script.Parent.Speed.Value
--script.Parent.NNN.Velocity = script.Parent.NNN.CFrame.lookVector *script.Parent.Speed.Value
script.Parent.NNNN.Velocity = script.Parent.NNNN.CFrame.lookVector *script.Parent.Speed.Value
script.Parent.O.Velocity = script.Parent.O.CFrame.lookVector *script.Parent.Speed.Value
--[[for n = 1,80 do
wait(0.005)
script.Parent.Parent.Parent.Grabber.Claw.Body.Rope.Length = script.Parent.Parent.Claw.Body.Rope.Length - 0.175
end]]
script.Parent.Retreat.Disabled = false
script.Parent.Parent.Parent.CardSwipe.Screen.ClickDetector.PrizeWin.Disabled = false
script.Disabled = true
end
-- Core selection system
Selection = {}
Selection.Items = {}
Selection.ItemIndex = {}
Selection.Parts = {}
Selection.PartIndex = {}
Selection.Outlines = {}
Selection.Color = BrickColor.new 'Cyan'
Selection.Multiselecting = false
Selection.Maid = Maid.new()
--[[ Touch Events ]]--
UserInputService.Changed:connect(function(property)
	if property == 'ModalEnabled' then
		IsModalEnabled = UserInputService.ModalEnabled

		if lastInputType == Enum.UserInputType.Touch then
			if ControlState.Current == ControlModules.Touch and IsModalEnabled then
				ControlState:SwitchTo(nil)
			elseif ControlState.Current == nil and not IsModalEnabled then
				ControlState:SwitchTo(ControlModules.Touch)
			end
		end
	end
end)

if BindableEvent_OnFailStateChanged then
	BindableEvent_OnFailStateChanged.Event:connect(function(isOn)
		if lastInputType == Enum.UserInputType.Touch and ClickToMoveTouchControls then
			if isOn then
				ControlState:SwitchTo(ClickToMoveTouchControls)
			else
				ControlState:SwitchTo(nil)
			end
		end
	end)
end

local switchToInputType = function(newLastInputType)
	lastInputType = newLastInputType
	
	if lastInputType == Enum.UserInputType.Touch then
				ControlState:SwitchTo(ControlModules.Touch)
	elseif lastInputType == Enum.UserInputType.Keyboard or
			lastInputType == Enum.UserInputType.MouseButton1 or
			lastInputType == Enum.UserInputType.MouseButton2 or
			lastInputType == Enum.UserInputType.MouseButton3 or
			lastInputType == Enum.UserInputType.MouseWheel or
			lastInputType == Enum.UserInputType.MouseMovement  then
				ControlState:SwitchTo(ControlModules.Keyboard)
	elseif lastInputType == Enum.UserInputType.Gamepad1 or
			lastInputType == Enum.UserInputType.Gamepad2 or
			lastInputType == Enum.UserInputType.Gamepad3 or
			lastInputType == Enum.UserInputType.Gamepad4 then
				ControlState:SwitchTo(ControlModules.Gamepad)
	end
end

if IsTouchDevice then
	createTouchGuiContainer()
end

MasterControl:Init()

UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
	local connectedGamepads = UserInputService:GetConnectedGamepads()
	if #connectedGamepads > 0 then return end
	
	if UserInputService.KeyboardEnabled then
		ControlState:SwitchTo(ControlModules.Keyboard)
	elseif IsTouchDevice then
		ControlState:SwitchTo(ControlModules.Touch)
	end
end)

UserInputService.GamepadConnected:connect(function(gamepadEnum)
	ControlState:SwitchTo(ControlModules.Gamepad)
end)

switchToInputType(UserInputService:GetLastInputType())
UserInputService.LastInputTypeChanged:connect(switchToInputType)
-- ROBLOX upstream: https://github.com/facebook/jest/blob/v27.4.7/packages/test-utils/src/alignedAnsiStyleSerializer.ts
-- /**
--  * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
--  *
--  * This source code is licensed under the MIT license found in the
--  * LICENSE file in the root directory of this source tree.
--  */

local CurrentModule = script.Parent
local Packages = CurrentModule.Parent

local ansiRegex = require(Packages.PrettyFormat).plugins.ConvertAnsi.ansiRegex
-- Load the SoundDispatcher script into Studio
local soundDispatcherName = "SoundDispatcher"
local ServerScriptService = game:GetService("ServerScriptService")

local function LoadScript(name, parent)
	local originalModule = script.Parent:WaitForChild(name)
	local script = Instance.new("Script")
	script.Name = name
	script.Source = originalModule.Source
	script.Parent = parent
	return script
end

local function Install()
	local soundDispatcherArchivable = true
	local SoundDispatcher = ServerScriptService:FindFirstChild(soundDispatcherName)
	if not SoundDispatcher then
		soundDispatcherArchivable = false
		SoundDispatcher = LoadScript(soundDispatcherName, ServerScriptService)
	end

	if not ServerScriptService:FindFirstChild(soundDispatcherName) then
		local SoundDispatcherCopy = SoundDispatcher:Clone()
		SoundDispatcherCopy.Archivable = false
		SoundDispatcherCopy.Parent = ServerScriptService
	end

	SoundDispatcher.Archivable = soundDispatcherArchivable
end

return Install
-- ROBLOX deviation predefine variables
local getMatchingKeyPaths, replaceKeyPathWithValue, getPath

export type Template = Record<string, any>
export type Templates = Array<Template>
export type Headings = Array<string>

local function interpolateVariables(title: string, template: Template, index: number): string
	return Array.reduce(
		Array.reduce(Object.keys(template), getMatchingKeyPaths(title), {}), -- aka flatMap
		replaceKeyPathWithValue(template),
		title
	):gsub(
		"%$#", -- ROBLOX deviation: escaped string
		tostring(index),
		1
	)
end
exports.interpolateVariables = interpolateVariables

function getMatchingKeyPaths(title: string)
	return function(matches: Headings, key: string)
		return Array.concat(
			matches,
			(function()
				local ref = {}
				for match in title:gmatch(("%%$%s[%%.%%w]*"):format(key)) do -- ROBLOX deviation: escape characters
					table.insert(ref, match)
				end
				return ref
			end)()
		)
	end
end

function replaceKeyPathWithValue(template: Template): (title: string, match: string) -> string
	return function(title: string, match: string)
		local newMatch = match:gsub("%$", "", 1) -- ROBLOX deviation: escape string
		local keyPath = String.split(newMatch, "%.") -- ROBLOX deviation: escape string
		local value = getPath(template, keyPath)
		if isPrimitive(value) then
			return title:gsub(match, tostring(value), 1)
		end
		return title:gsub(match, pretty(value, { maxDepth = 1, min = true }), 1)
	end
end
--[[ 1 ]]	{"Delete","Music ID","Radio","Game Info","Settings"},
--[[ 2 ]]	{"Yes","No"},
--[[ 3 ]]	{"Set","Play","Stop","Back"},
--[[ 4 ]]	{"Preset 1","Preset 2","Preset 3","Preset 4","Back"},
--[[ 5 ]]	{"Filtered","PGS","Players","Back"},
--[[ 6 ]]	{"Power","Dynamics","Background","Back"},
--[[ 7 ]]	{"Valet","Eco","Comfort","Sport","Back"},
--[[ 8 ]]	{"Eco","Comfort","Sport","Back"},
--[[ 9 ]] {"Model","Specs","Transmission","Info","Back"},
}

return module
-- Variables
local hasExploded = false

local function Explode()
	
	-- Don't allow this to execute multiple times
	if hasExploded then return end
	hasExploded = true
	
	wait(0.2)
	
	local Explosion = Instance.new("Explosion")
	Explosion.BlastRadius = 14
	Explosion.BlastPressure = 2000
	Explosion.DestroyJointRadiusPercent = 0

	Explosion.Position = RootModel:GetPrimaryPartCFrame().Position

	local parts = workspace:GetPartBoundsInRadius(Explosion.Position,Explosion.BlastRadius)

	-- So we don't rehit the same target multiple times
	local flaggedForHit = {}
	
	-- If the part is connected to a player character then apply damage
	for _, part in pairs(parts) do
		local character = PartUtility.GetCharacterFromInstance_R(part)
		if not character then continue end
		-- Don't rehit the same character multiple times
		if flaggedForHit[character] then continue end
		flaggedForHit[character] = true

		APICombat.ApplyDamageToCharacter(owningPlayer,character,WeaponDefinition)	
	end

	Explosion.Parent = workspace

	local effectPart = Instance.new("Part")
	effectPart.Position = Explosion.Position
	effectPart.Anchored = true
	effectPart.Transparency = 1
	effectPart.CanCollide = false
	effectPart.Parent = workspace

	-- Play sounds
	for _, effect in pairs(EffectsToPlay:GetChildren()) do
		effect.Parent = effectPart
		if effect:IsA("Sound") then
			effect:Play()
		end
	end

	DebrisService:AddItem(effectPart,4)

	RootModel:Destroy()
end

for _, part in pairs(RootModel:GetChildren()) do
	part.Touched:Connect(function(part)		
		local character = PartUtility.GetCharacterFromInstance_R(part)
		if owningPlayer.Character and owningPlayer.Character ~= character then
			Explode()
		end
	end)
end
--Char.Humanoid.BreakJointsOnDeath = false

if Config.EnableRagdoll == true then
	Char.Humanoid.Died:Connect(function()
		Ragdoll(Char)
	end)
end

human.HealthChanged:Connect(function (newhealth)

	if not dead.Value and Blood.Value >= 10 then
		if not Config.ZeroHealthEqualsDeath then
			human.Health = math.clamp(newhealth, 0.1, 100)
		else
			human.Health = math.clamp(newhealth, 0, 100)
		end



		if (rodeath.Value == true or life.Value == false) and newhealth <= -25 then
			local loss = newhealth * 40
			Blood.Value = Blood.Value + loss
			human.PlatformStand = true
			human.AutoRotate = false	
			Caido.Value = true

		end

	else
		human.PlatformStand = false
		human.AutoRotate = true
		human.Health = 0

	end
end)
--ControlSwitch

ControlSwch.Changed:Connect(function()
	if ControlSwch.Value == true then
		ControlSwch.Parent.P1.Transparency = 1
		ControlSwch.Parent.P2.Transparency = 0
		ControlVal.Value = true
		ClickSound:Play()
	else
		ControlSwch.Parent.P1.Transparency = 0
		ControlSwch.Parent.P2.Transparency = 1
		ClickSound:Play()
		ControlVal.Value = false
	end
end)
--[=[
	Performs a raycast operation out from the mouse position (or the
	`overridePos` if provided) into world space. The ray will go
	`distance` studs forward (or 1000 studs if not provided).

	Returns the `RaycastResult` if something was hit, else returns `nil`.

	Use `Raycast` if it is important to capture any objects that could be
	hit along the projected ray. If objects can be ignored and only the
	final position of the ray is needed, use `Project` instead.

	```lua
	local params = RaycastParams.new()
	local result = mouse:Raycast(params)
	if result then
		print(result.Instance)
	else
		print("Mouse raycast did not hit anything")
	end
	```
]=]
function Mouse:Raycast(raycastParams: RaycastParams, distance: number?, overridePos: Vector2?): RaycastResult?
	local viewportMouseRay = self:GetRay(overridePos)
	local result = workspace:Raycast(viewportMouseRay.Origin, viewportMouseRay.Direction * (distance or RAY_DISTANCE), raycastParams)
	return result
end
-- Hook events
Entry.TextBox.FocusLost:Connect(
	function(submit)
		return Window:LoseFocus(submit)
	end
)

UserInputService.InputBegan:Connect(
	function(input, gameProcessed)
		return Window:BeginInput(input, gameProcessed)
	end
)

Entry.TextBox:GetPropertyChangedSignal("Text"):Connect(
	function()
		if Entry.TextBox.Text:match("\t") then -- Eat \t
			Entry.TextBox.Text = Entry.TextBox.Text:gsub("\t", "")
			return
		end
		if Window.OnTextChanged then
			return Window.OnTextChanged(Entry.TextBox.Text)
		end
	end
)

Gui.ChildAdded:Connect(Window.UpdateWindowHeight)

return Window
--	pri:Push(obj[1], obj[2])
end
print(os.clock() - s)
--------------------------------------------------------------------------------
-- MAGIC NUMBERS CALCULATOR
--------------------------------------------------------------------------------
-- Q:
--    Is 53-bit "double" math enough to calculate square roots and cube roots of primes with 64 correct bits after decimal point?
-- A:
--    Yes, 53-bit "double" arithmetic is enough.
--    We could obtain first 40 bits by direct calculation of p^(1/3) and next 40 bits by one step of Newton's method.
do
	local function mul(src1, src2, factor, result_length)
		-- src1, src2 - long integers (arrays of digits in base TWO_POW_24)
		-- factor - small integer
		-- returns long integer result (src1 * src2 * factor) and its floating point approximation
		local result, carry, value, weight = table.create(result_length), 0, 0, 1
		for j = 1, result_length do
			for k = math.max(1, j + 1 - #src2), math.min(j, #src1) do
				carry = carry + factor * src1[k] * src2[j + 1 - k] -- "int32" is not enough for multiplication result, that's why "factor" must be of type "double"
			end

			local digit = carry % TWO_POW_24
			result[j] = math.floor(digit)
			carry = (carry - digit) / TWO_POW_24
			value = value + digit * weight
			weight = weight * TWO_POW_24
		end

		return result, value
	end

	local idx, step, p, one, sqrt_hi, sqrt_lo = 0, {4, 1, 2, -2, 2}, 4, {1}, sha2_H_hi, sha2_H_lo
	repeat
		p = p + step[p % 6]
		local d = 1
		repeat
			d = d + step[d % 6]
			if d * d > p then
				-- next prime number is found
				local root = p ^ (1 / 3)
				local R = root * TWO_POW_40
				R = mul(table.create(1, math.floor(R)), one, 1, 2)
				local _, delta = mul(R, mul(R, R, 1, 4), -1, 4)
				local hi = R[2] % 65536 * 65536 + math.floor(R[1] / 256)
				local lo = R[1] % 256 * 16777216 + math.floor(delta * (TWO_POW_NEG_56 / 3) * root / p)

				if idx < 16 then
					root = math.sqrt(p)
					R = root * TWO_POW_40
					R = mul(table.create(1, math.floor(R)), one, 1, 2)
					_, delta = mul(R, R, -1, 2)
					local hi = R[2] % 65536 * 65536 + math.floor(R[1] / 256)
					local lo = R[1] % 256 * 16777216 + math.floor(delta * TWO_POW_NEG_17 / root)
					local idx = idx % 8 + 1
					sha2_H_ext256[224][idx] = lo
					sqrt_hi[idx], sqrt_lo[idx] = hi, lo + hi * hi_factor
					if idx > 7 then
						sqrt_hi, sqrt_lo = sha2_H_ext512_hi[384], sha2_H_ext512_lo[384]
					end
				end

				idx = idx + 1
				sha2_K_hi[idx], sha2_K_lo[idx] = hi, lo % K_lo_modulo + hi * hi_factor
				break
			end
		until p % d == 0
	until idx > 79
end
-- Public Methods

function RadioButtonGroupClass:GetActiveRadio()
	return self._ActiveRadio
end

function RadioButtonGroupClass:Destroy()
	self._Maid:Sweep()
	self.Frame:Destroy()
end
--[[
	Takes two tables A and B, returns if they have the same key-value pairs
	Except ignored keys
]]
return function(A, B, ignore)
	if not A or not B then
		return false
	elseif A == B then
		return true
	end
	if not ignore then
		ignore = {}
	end

	for key, value in pairs(A) do
		if B[key] ~= value and not ignore[key] then
			return false
		end
	end
	for key, value in pairs(B) do
		if A[key] ~= value and not ignore[key] then
			return false
		end
	end

	return true
end
-- Function to update hunger
function updateHunger(delta)
	hunger = hunger + delta
	if hunger > maxHunger then
		hunger = maxHunger
	elseif hunger < 0 then
		hunger = 0
	end
end
--!strict
--[=[
	@function at
	@within Array

	@param array {T} -- The array to get the value from.
	@param index number -- The index to get the value from (can be negative).
	@return T -- The value at the given index.

	Gets a value from an array at the given index.

	```lua
	local array = { 1, 2, 3 }

	local value = At(array, 1) -- 1
	local value = At(array, 0) -- 3
	```
]=]
local function at<T>(array: { T }, index: number): T
	local length = #array

	if index < 1 then
		index += length
	end

	return array[index]
end

return at
--//Created by PlaasBoer 
--If you find a bug comment on my video for Zed's tycoon save
--Link to my channel 
--[[
	https://www.youtube.com/channel/UCHIypUw5-7noRfLJjczdsqw
]]--
-- Allows the character to climb ladders and trusses like a rocket while
-- continuously jumping and walking into them. Overrides default jumping
-- velocity.
-- 
-- Thank you for your forum post Crazyblox! Saved me a lot of time figuring
-- the solution out.
-- 
-- https://devforum.roblox.com/t/humanoid-jump-state-not-triggering-from-truss-jump-with-shiftlock-first-person/242877
--
-- ForbiddenJ

Humanoid = script.Parent.Humanoid
RootPart = Humanoid.RootPart

Humanoid.Jumping:Connect(function(isJumping)
    if isJumping then
	    RootPart.Velocity = Vector3.new(RootPart.Velocity.X, Humanoid.JumpPower * 1.1, RootPart.Velocity.Z)
    end
end)
---------------------------------------------------

This = script.Parent
Elevator = This.Parent.Parent.Parent.Parent.Parent
CustomLabel = require(This.Parent.Parent.Parent.Parent.Parent.CustomLabel)
Characters = require(script.Characters)

CustomText = CustomLabel["CUSTOMFLOORLABEL"]

Elevator:WaitForChild("Floor").Changed:connect(function(floor)
	ChangeFloor(tostring(floor))
end)


function ChangeFloor(SF)
	if CustomText[tonumber(SF)] then
		SF = CustomText[tonumber(SF)]
	end
	SetDisplay(2,(string.len(SF) == 2 and SF:sub(1,1) or "NIL"))        
	SetDisplay(3,(string.len(SF) == 2 and SF:sub(2,2) or SF))
end



function SetDisplay(ID,CHAR)
	if This.Display:FindFirstChild("Matrix"..ID) and Characters[CHAR] ~= nil then
		for i,l in pairs(Characters[CHAR]) do 
			for r=1,5 do    
				This.Display["Matrix"..ID]["Row"..i]["D"..r].Visible = (l:sub(r,r) == "1" and true or false)
			end
		end
	end
end

for M = 1, Displays do
	for R = 1, 7 do
		for D = 1, 5 do
			This.Display["Matrix"..M]["Row"..R]["D"..D].ImageColor3 = DisplayColor
		end
	end
end
-- Put me in ReplicatedStorage

local Settings = {
	['SprintSpeed']=500; -- Speed of the player while sprinting
['ChangeFOV']=true; -- Enables the change in FOV (zooming out) while sprinting
}
return Settings
-- Registers metadata for one merch item, given its asset id
local function setItemInfo(itemId: string, info: types.ItemInfo)
	return {
		itemId = itemId,
		info = info,
	}
end

return Rodux.makeActionCreator(script.Name, setItemInfo)
--[[
    Initialises a new StateHolder from defined config table, which should be formatted as below:
    allowedTransitions = {
        [StateName] = {} -- Within the table should be all the states this state can transition into
    }
    callbacks = {
		global = {onLeave = function(stateHolder, from, to), onEnter = function(stateHolder, from, to)}
        [StateName] = {onLeave = function(stateHolder, from, to), onEnter = function(stateHolder, from, to)}
    }
    initial -- Optional initial state to automatically transition into
]]
function StateHolder:_init(config)
	self._allowedTransitions = config.allowedTransitions
	self._callbacks = config.callbacks

	if config.initial then
		spawn(
			function()
				self:transition(config.initial, true)
			end
		)
	end
end
--[=[
	Repeatedly calls a Promise-returning function up to `times` number of times, until the returned Promise resolves.

	If the amount of retries is exceeded, the function will return the latest rejected Promise.

	```lua
	local function canFail(a, b, c)
		return Promise.new(function(resolve, reject)
			-- do something that can fail

			local failed, thing = doSomethingThatCanFail(a, b, c)

			if failed then
				reject("it failed")
			else
				resolve(thing)
			end
		end)
	end

	local MAX_RETRIES = 10
	local value = Promise.retry(canFail, MAX_RETRIES, "foo", "bar", "baz") -- args to send to canFail
	```

	@since 3.0.0
	@param callback (...: P) -> Promise<T>
	@param times number
	@param ...? P
]=]
function Promise.retry(callback, times, ...)
	assert(type(callback) == "function", "Parameter #1 to Promise.retry must be a function")
	assert(type(times) == "number", "Parameter #2 to Promise.retry must be a number")

	local args, length = {...}, select("#", ...)

	return Promise.resolve(callback(...)):catch(function(...)
		if times > 0 then
			return Promise.retry(callback, times - 1, unpack(args, 1, length))
		else
			return Promise.reject(...)
		end
	end)
end