Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218local Controller = {}function Controller.start() if game.ReplicatedStorage:FindFirstChild("ArenaState") then return game.ReplicatedStorage.ArenaState endlocal Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local RunService = game:GetService("RunService")local Match = require(script.Parent.Match)local World = require(script.Parent.World)local GrenadeMath = require(ReplicatedStorage.GrenadeMath)local Grenades = require(script.Parent.Grenades)local Bot = require(script.Parent.Bot)local Avatars = require(script.Parent.Avatars)Players.CharacterAutoLoads=falselocal bot = nillocal botLoading = falselocal soloSince, soloId = nil, nillocal world = World.build()local remote = Instance.new("RemoteEvent")remote.Name, remote.Parent = "ArenaAction", ReplicatedStoragelocal state = Instance.new("Folder")state.Name, state.Parent = "ArenaState", ReplicatedStoragerequire(script.Parent.Updates).start(state)require(script.Parent.MediaBridge).start(world,state)local match = Match.new()local attacks, requests, staggered = {}, {}, {}local function player(id) return id ~= Bot.ID and Players:GetPlayerByUserId(id) or nil endlocal function body(id) local p = player(id) local c = id == Bot.ID and bot or (p and p.Character) return c and c:FindFirstChild("HumanoidRootPart"), c and c:FindFirstChildOfClass("Humanoid")endlocal function move(id, position, frozen, serverOwned) local root, humanoid = body(id) if not root or not humanoid then return end root.Anchored = false root.CFrame = CFrame.lookAt(position, Vector3.new(0, position.Y, 0)) root.AssemblyLinearVelocity, root.AssemblyAngularVelocity = Vector3.zero, Vector3.zero if serverOwned then root:SetNetworkOwner(nil) else root:SetNetworkOwnershipAuto() end root.Anchored = frozen humanoid.WalkSpeed, humanoid.JumpPower = GrenadeMath.runSpeed, 45endlocal function alive(id) local root, h = body(id) if not root or not h or h.Health <= 0 then return false end local v = root.Position return v.Y > -8 and math.abs(v.X) < 60 and math.abs(v.Z) < 60endlocal grenades = Grenades.new(function(position, shotId) if match.phase ~= "Battle" then return end remote:FireAllClients("blast", position, shotId) for _, id in match.fighters do local target, h = body(id) local velocity = target and GrenadeMath.blast(position,target.Position) if velocity and h and h.Health>0 then local duration = id == Bot.ID and 0.55 or 0.45 staggered[id] = os.clock()+duration h.WalkSpeed=0; h:Move(Vector3.zero) if id==Bot.ID then velocity*=1.5 end local owner=target:GetNetworkOwner() if owner then remote:FireClient(owner,"knockback",id,velocity,duration,match.round) else h:ChangeState(Enum.HumanoidStateType.Physics) target:ApplyImpulse(velocity*target.AssemblyMass) end task.delay(duration,function() if h.Parent and os.clock()>=(staggered[id] or 0) then h.WalkSpeed=id==Bot.ID and GrenadeMath.botSpeed or GrenadeMath.runSpeed if not owner and h.Health>0 then h:ChangeState(Enum.HumanoidStateType.Freefall) end end end) end endend)local function throw(id, now, aim, sequence, destination) local p=player(id) if now<(staggered[id] or 0) or not match:canAttack(id,now,attacks[id]) then if p then remote:FireClient(p,"throw-rejected",sequence) end return end local root,h=body(id) if not root or not h or h.Health<=0 or not alive(id) then return end aim=GrenadeMath.aim(aim) if not aim then return end attacks[id]=now local origin=root.Position+Vector3.new(0,2,0)+aim*3 local _, flight=grenades:throw(origin,destination or (root.Position+aim*GrenadeMath.range),now,match.round,root.Parent,root.AssemblyLinearVelocity) remote:FireAllClients("thrown",id,sequence,flight)endlocal function action(p, command, packet) local now,id=os.clock(),p.UserId if now-(requests[id] or -math.huge)<0.1 then return end requests[id]=now if command=="throw" and type(packet)=="table" and type(packet.sequence)=="number" and packet.sequence%1==0 and packet.sequence>0 and packet.sequence<1e9 then throw(id,now,packet.aim,packet.sequence) endendremote.OnServerEvent:Connect(action)local function added(p) local stats = Instance.new("Folder") stats.Name, stats.Parent = "leaderstats", p local wins = Instance.new("IntValue") wins.Name, wins.Parent = "Wins", stats local loading=false local function spawn() if loading or not p.Parent then return end loading=true local old=p.Character local c=Avatars.create(p.Name,Color3.fromRGB(65,235,235)) c.Humanoid.DisplayName=p.DisplayName c:PivotTo(CFrame.new(World.lobby)) c.Parent=workspace p.Character=c if old then old:Destroy() end c.Humanoid.Died:Connect(function() task.delay(3,spawn) end) loading=false end p.CharacterAdded:Connect(function(c) if not c:WaitForChild("HumanoidRootPart", 10) or p.Character ~= c or not p.Parent then return end -- A reset forfeits the current match, even if the new character spawns quickly. if table.find(match.fighters, p.UserId) and (match.phase == "Countdown" or match.phase == "Battle") then local other = match.fighters[1] == p.UserId and match.fighters[2] or match.fighters[1] match:finish((other == Bot.ID or player(other)) and other or nil, os.clock()) end move(p.UserId, World.lobby, false, false) end) task.defer(spawn)endPlayers.PlayerAdded:Connect(added)for _, p in Players:GetPlayers() do added(p) endPlayers.PlayerRemoving:Connect(function(p) match:leave(p.UserId) attacks[p.UserId], requests[p.UserId], staggered[p.UserId] = nil, nil, nilend)local previous, accumulator = "Lobby", 0RunService.Heartbeat:Connect(function(dt) grenades:tick(os.clock(), match.round) accumulator += dt if accumulator < 0.1 then return end accumulator = 0 local now = os.clock() -- Queue membership follows physical occupancy, never a client toggle. for _,p in Players:GetPlayers() do local root,h=body(p.UserId) local onPad=root and h and h.Health>0 and World.onPad(world.QueuePad,root.Position) if onPad then match:join(p.UserId) else match:leave(p.UserId) end end if match.phase == "Lobby" and #match.queue == 1 then if soloId ~= match.queue[1] then soloId, soloSince = match.queue[1], now end if not bot and not botLoading then botLoading = true task.spawn(function() local ok, model = pcall(Bot.create) if ok then bot = model else warn("Practice bot failed to load; retrying"); task.wait(3) end botLoading = false end) end if now - soloSince >= 3 and bot and bot.Humanoid.Health > 0 then match:join(Bot.ID) end else soloId, soloSince = nil, nil end local queueSeconds=soloSince and math.max(0,math.ceil(3-(now-soloSince))) or 3 state:SetAttribute("QueueSeconds",queueSeconds) world.QueuePad.Color=#match.queue>0 and Color3.fromRGB(65,235,235) or Color3.fromRGB(255,84,172) world.QueuePad.SurfaceGui.Status.Text=#match.queue==0 and "STEP ON TO PLAY" or (match.phase~="Lobby" and "QUEUED · NEXT ROUND" or (queueSeconds>0 and "STARTING "..queueSeconds or "LOADING DUMMY")) match:tick(now, alive) if match.phase ~= previous then if match.phase == "Battle" then if table.find(match.fighters, Bot.ID) then bot.Parent = workspace end for i, id in match.fighters do move(id, World.starts[i], false, true); attacks[id] = nil; staggered[id] = nil end for _, id in match.fighters do local root = body(id) if root then root.Anchored = false if id == Bot.ID then local other = match.fighters[1] == Bot.ID and match.fighters[2] or match.fighters[1] root:SetNetworkOwner(player(other)) else root:SetNetworkOwnershipAuto() end end end elseif match.phase == "Result" then grenades:clear() local winner = match.winner and player(match.winner) if winner then winner.leaderstats.Wins.Value += 1 end for i, id in match.fighters do if id ~= Bot.ID then move(id, World.lobby + Vector3.new(i * 4 - 6, 0, 0), false, false) end end if bot then bot:Destroy(); bot = nil end end previous = match.phase end if match.phase == "Battle" and table.find(match.fighters, Bot.ID) then local root, h = body(Bot.ID) local other = match.fighters[1] == Bot.ID and match.fighters[2] or match.fighters[1] local target = body(other) if root and h and target and alive(Bot.ID) and now >= (staggered[Bot.ID] or 0) then Bot.step(root, h, target.Position) if now - (attacks[Bot.ID] or -math.huge) >= 2.6 then throw(Bot.ID,now,target.Position-root.Position,0,target.Position) end end end state:SetAttribute("Round", match.round) state:SetAttribute("BotMatch", table.find(match.fighters, Bot.ID) ~= nil) state:SetAttribute("Phase", match.phase) state:SetAttribute("Seconds", math.max(0, math.ceil(match.deadline - now))) state:SetAttribute("QueueCount", #match.queue) state:SetAttribute("FighterA", match.fighters[1] or 0) state:SetAttribute("FighterB", match.fighters[2] or 0) local winner = match.winner and player(match.winner) state:SetAttribute("Winner", match.winner == Bot.ID and "Dummy" or (winner and winner.DisplayName or "Draw")) for _, p in Players:GetPlayers() do p:SetAttribute("ArenaQueued", table.find(match.queue, p.UserId) ~= nil) endend)
return stateendreturn Controller