local Z=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local I=Z:WaitForChild("\077\111\100\117\108\101\115")local S=require(I.src.utils.Logger)local C=require(I.src.actions.toClient.clientSetGhosting)local f=require(I.src.actions.toClient.clientReset)local L=((game:GetService("\083\116\097\114\116\101\114\080\108\097\121\101\114")):WaitForChild("\083\116\097\114\116\101\114\080\108\097\121\101\114\083\099\114\105\112\116\115")).clientSrc local n=game:GetService("\080\108\097\121\101\114\115")local y=require(I.Roact)local i=require(I.Rodux)local s=require(I.RoactRodux)local U=require(I.src.utils.Dict)local k=require(I.src.commonReducers)local r=require(I.src.FlyScript)local V=require(L.clientReducers)local w=require(L.ClientApi)local N=require(L.Components.Game)local Y=require(L.Components.ApiProvider)local Q=require(I.src.AudioPlayer)local g=y.createElement local u=n.LocalPlayer return function(Z)local I=i.combineReducers(U.join(k,V))local L local n local function o()local I=y.mount(g(s.StoreProvider,{store=n},{g(Y,{api=L},{Game=g(N)})}),u.PlayerGui,"\067\108\105\101\110\116\115\077\097\105\110")table.insert(Z.destructors,function()y.unmount(I)end)S:i("\067\108\105\101\110\116 \115\116\097\114\116\101\100\033")end local function D(I)return function(S)if not S.replicated then table.insert(Z.savedActions,S)end return I(S)end end local F=function(Z)if n~=nil then n:dispatch(Z)end end local O=function()n:dispatch(C(u))S:d("\099\108\105\101\110\116\083\116\097\114\116\071\104\111\115\116\105\110\103\046 \083\116\097\114\116 \102\108\121\105\110\103")local Z=u.character local I=Z:WaitForChild("\072\117\109\097\110\111\105\100")local L=Z:WaitForChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")local y=r:create(L)y:initInput()y:startFlying()I.Died:Connect(function()y:endFlying()y=nil n:dispatch(f(u))end)end local b=function(Z,I)S:d("\099\108\105\101\110\116\080\108\097\121\083\111\117\110\100\046"..Z,I)Q.playAudio(Z,I)end local l=function(Z)S:d("\099\108\105\101\110\116\080\108\097\121\066\097\099\107\103\114\111\117\110\100\083\111\117\110\100\046"..Z)Q.playBackgroundAudio(Z)end L=w.connect({initialStoreState=function(S)for Z,C in ipairs(Z.savedActions)do S=I(S,C)end n=i.Store.new(I,S,{i.thunkMiddleware;D})table.insert(Z.destructors,function()n:destruct()end)o()end;storeAction=F;clientStartGhosting=O,clientPlaySound=b;clientPlayBackgroundSound=l})L:clientStart()S:i("\067\108\105\101\110\116 \114\101\097\100\121\033")end
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local Modules = ReplicatedStorage:WaitForChild('Modules')
local logger = require(Modules.src.utils.Logger)

local clientSetGhosting = require(Modules.src.actions.toClient.clientSetGhosting)
local clientReset = require(Modules.src.actions.toClient.clientReset)
local clientSrc = game:GetService('StarterPlayer'):WaitForChild('StarterPlayerScripts').clientSrc

local Players = game:GetService('Players')

local Roact = require(Modules.Roact)

local Rodux = require(Modules.Rodux)
local RoactRodux = require(Modules.RoactRodux)

local Dict = require(Modules.src.utils.Dict)
local commonReducers = require(Modules.src.commonReducers)
local FlyScript = require(Modules.src.FlyScript)

local clientReducers = require(clientSrc.clientReducers)
local ClientApi = require(clientSrc.ClientApi)

local Game = require(clientSrc.Components.Game)
local ApiProvider = require(clientSrc.Components.ApiProvider)
local AudioPlayer = require(Modules.src.AudioPlayer)

local createElement = Roact.createElement

local LocalPlayer = Players.LocalPlayer

-- The Rodux DevTools aren't available yet! Check the README for more details.
-- local RoduxVisualizer = require(Modules.RoduxVisualizer)

return function(context)
	local reducer = Rodux.combineReducers(Dict.join(commonReducers, clientReducers))

	local api
	local store

	-- We define our main function explicitly instead of running it right away
	-- because we want to make sure we're connected and synced with the server
	-- first.
	local function main()
		local ui =
			Roact.mount(
				createElement(
					RoactRodux.StoreProvider,
					{ store = store },
					{ createElement(ApiProvider, { api = api }, { Game = createElement(Game) }) }
				),
				LocalPlayer.PlayerGui,
				'ClientsMain'
			)

		table.insert(context.destructors, function()
			Roact.unmount(ui)
		end)

		logger:i('Client started!')
	end

	-- This is a custom Rodux middleware that automatically saves any local
	-- actions that are dispatched in order to replay them when we hot-reload.
	local function saveActionsMiddleware(nextDispatch)
		return function(action)
			if not action.replicated then
				table.insert(context.savedActions, action)
			end

			return nextDispatch(action)
		end
	end

	-- Once the Rodux DevTools are available publicly, this will be revisited.
	-- When I was working on this project, I used this config:

	-- local devTools = RoduxVisualizer.createDevTools({
	-- 	mode = RoduxVisualizer.Mode.Integrated,
	-- 	toggleHotkey = Enum.KeyCode.Y,
	-- 	visibleOnStartup = false,
	-- 	attachTo = LocalPlayer:WaitForChild("PlayerGui"),
	-- })

	local storeAction = function(action)
		if store ~= nil then
			store:dispatch(action)
		end
	end
	local clientStartGhosting = function()
		store:dispatch(clientSetGhosting(LocalPlayer))
		logger:d('clientStartGhosting. Start flying')
		local character = LocalPlayer.character
		local humanoid = character:WaitForChild('Humanoid')
		local humanoidRoot = character:WaitForChild('HumanoidRootPart')

		local flyScript = FlyScript:create(humanoidRoot)
		flyScript:initInput()
		flyScript:startFlying()

		humanoid.Died:Connect(function()
			flyScript:endFlying()
			flyScript = nil
			store:dispatch(clientReset(LocalPlayer))
		end)
	end

	local clientPlaySound = function(soundName, triggerPart)
		logger:d('clientPlaySound.' .. soundName, triggerPart)
		AudioPlayer.playAudio(soundName, triggerPart)
	end

	local clientPlayBackgroundSound = function(soundName)
		logger:d('clientPlayBackgroundSound.' .. soundName)

		AudioPlayer.playBackgroundAudio(soundName)
	end

	api = ClientApi.connect({
	-- Apply any saved actions from the last reload.
	-- The actions in this list are only those that were triggered on
	-- the client, since the shared state should already be populated
	-- correctly from the server.
	-- Thunks are functions that we dispatch to the store. It's a
	-- handy way to get a reference to the store and have
	-- side-effects while still looking like regular actions!

	-- This is our custom hot-reloading middleware defined above.

	-- The Redux DevTools middleware, retrieved above.
	-- devTools.middleware,
	--, Rodux.loggerMiddleware
		initialStoreState = function(initialState)
			for _, action in ipairs(context.savedActions) do
				initialState = reducer(initialState, action)
			end

			store =
				Rodux.Store.new(
					reducer,
					initialState,
					{ Rodux.thunkMiddleware, saveActionsMiddleware }
				)

			table.insert(context.destructors, function()
				store:destruct()
			end)

			main()
		end,
		storeAction = storeAction,
		clientStartGhosting = clientStartGhosting,
		clientPlaySound = clientPlaySound,
		clientPlayBackgroundSound = clientPlayBackgroundSound,
	})

	api:clientStart()

	logger:i('Client ready!')
end