2026-05-02 23:53:49 +02:00

378 lines
7.7 KiB
Plaintext

local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Data = ReplicatedStorage.Data
local BotData = require(Data.BotData)
local DataTypes = require(Data.DataTypes)
local GameState = require(Data.GameState)
local sData = ServerScriptService.Data
local DataManager = require(sData.DataManager)
local ValueNames = require(sData.ValueNames)
local sModules = ServerScriptService.Modules
local Bot = require(sModules.Classes.GameObject.Bot)
local VotingHandlerServer = require(sModules.VotingHandlerServer)
local TurnManager = require(sModules.TurnManager)
local ObjectManager = require(sModules.ObjectManager)
local MapManager = require(sModules.Map.MapManager)
local FerrUtils = require(ReplicatedStorage.Shared.SharedUtils.FerrUtils)
local rBotUtils = require(ReplicatedStorage.Shared.SharedUtils.sharedBotUtils)
local BotUtils = require(sModules.Utils.BotUtils)
local Remote = ReplicatedStorage.Remote
local rev_statusRemoteEvent = Remote.StatusRemoteEvent
local rev_timerRemoteEvent = Remote.TimerRemoteEvent
local rev_UpdateGameState = Remote.UpdateGameState
local Round = {}
Round.__index = Round
function getPlaying() : {Player}
return game:GetService("CollectionService"):GetTagged("PLAYING")
end
function Round.new()
local self = setmetatable({}, Round)
if game:GetService("RunService"):IsStudio() then
self.IntermissionTime = 1
else
self.IntermissionTime = 1
end
self.MapVotingTime = 100
self.GraceTime = 1
self.DecisionTime = 15
self.Status = ""
self.RoundResults = {}
self:ChangeGameState(GameState.LOBBY)
self.Timer = 0
return self
end
function Round:ChangeStatus(newText : DataStoreListingPages)
self.Status = newText
self:UpdateState()
end
function Round:FireTo(remote,players,...)
players = players or game.Players:GetPlayers()
for i,v in pairs(players) do
remote:FireClient(v,...)
end
end
function Round:ChangeGameState(newState : number)
self.GameState = newState
shared.Phase = newState
self:FireTo(rev_UpdateGameState,getPlaying(),newState)
end
function Round:UpdateState()
rev_statusRemoteEvent:FireAllClients(self.Status .. ": " .. self.Timer)
end
function Round:EnoughPlayers()
return #Players:GetPlayers() >= 2 or game:GetService("RunService"):IsStudio() and script:GetAttribute("StudioEnoughPlayersOverride")
end
function Round:AllPlayersReady()
return #getPlaying() == TurnManager.GetReadyPlayers()
end
function Round:WaitFor(sec : number,condition,...)
for index = sec,0,-1 do
self.Timer = index
self:UpdateState()
task.wait(.5)
if self.Break then
self.Break = false
self.Timer = 0
self:UpdateState()
break
end
end
end
function Round:Halt()
repeat task.wait() until self.Break == false
end
function Round:Intermission()
self:ChangeGameState(GameState.LOBBY)
self:ChangeStatus("Intermission")
self:WaitFor(self.IntermissionTime)
end
function Round:ChooseMap()
workspace.Map:ClearAllChildren()
while task.wait() do
self:ChangeStatus("Choose Map")
VotingHandlerServer.StartVoting(1,"MapData",workspace.Lobby["Map Voting Stands"]:GetChildren())
self:WaitFor(self.MapVotingTime)
local winnerIndex,winnerData : DataTypes.VoteOptionData = VotingHandlerServer.EndVoting()
print(winnerIndex)
print(winnerData)
MapManager.SpawnMap(winnerData.Name)
break
end
self:ChangeStatus("Loading map...")
self:WaitFor(MapManager.MAP_LOAD_TIME)
end
function Round:BringPlayers()
local pick = "PlasmaBot"
for _,plr in pairs(game.Players:GetPlayers()) do
local char = plr.Character
if not char then
continue
end
char:PivotTo(workspace.BoxOfDoom.PrimaryPart.CFrame)
local newBot = Bot.new(plr,pick,char)
newBot:_Init()
plr:AddTag("PLAYING")
end
BotUtils.SpawnBots(getPlaying())
end
Round.END_TYPES = {
WIN = 1,
DRAW = 2,
}
function Round:ShouldRoundEnd()
local max = #getPlaying()
if game:GetService("RunService"):IsStudio() then
return
end
local alive = {}
for i,id in pairs(#getPlaying()) do
local b = ObjectManager.Get(id)
if b.Components.Health:IsAlive() then
table.insert(alive,id)
else
ObjectManager.Destroy(id)
end
end
if #alive == 0 then
return Round.END_TYPES.DRAW
end
if #alive == 1 then
return Round.END_TYPES.WIN,alive[1]
end
end
function Round:GracePeriod()
self:ChangeStatus("Grace period")
self:ChangeGameState(GameState.GRACE)
self:WaitFor(self.GraceTime)
end
function Round:TryStopEarlyAim()
task.spawn(function()
repeat task.wait() until self.GameState == GameState.RESOLVING or self:AllPlayersReady()
if self.GameState ~= GameState.RESOLVING then
self.Break = true
end
self:Halt()
end)
end
function Round:StartAimPhase()
--for i,v in pairs(workspace.Playing:GetChildren()) do
-- v.PrimaryPart.Anchored = true
--end
self:ChangeStatus("Decision making")
self:ChangeGameState(GameState.AIMING)
for i,plr in pairs(getPlaying()) do
local primary = rBotUtils.GetBotRoot(plr.UserId)
local rot = primary.Orientation
primary.Orientation = Vector3.new(0,0,rot.Z)
end
TurnManager.collectInputs()
self:TryStopEarlyAim()
self:WaitFor(self.DecisionTime)
end
function Round:ShouldStopEarlyResolve()
for _,gameObject in pairs(ObjectManager.GameObject) do
if not gameObject.model or not gameObject.model.Parent then
continue
end
local vel = gameObject.model.PrimaryPart.AssemblyLinearVelocity
local horizontalVel = Vector3.new(vel.X, 0, vel.Z).Magnitude
local verticalVel = math.abs(vel.Y)
if horizontalVel > 0.1 or verticalVel > 0.5 or not rBotUtils.IsObjectOnFloor(gameObject.model) then
return false
end
end
return true
end
function Round:TryStopEarlyResolve()
task.spawn(function()
local stableTime = 0
print(self.GameState)
while self.GameState == GameState.RESOLVING do
task.wait(0.1)
if self:ShouldStopEarlyResolve() then
stableTime += 0.1
if stableTime >= 0.5 then -- must be stable for 0.5s
self.Break = true
break
end
else
stableTime = 0
end
end
self:Halt()
end)
end
function Round:StartResolvePhase()
self:ChangeGameState(GameState.RESOLVING)
self:TryStopEarlyResolve()
for _, v : Model in pairs(workspace.Bots:GetChildren()) do
local root = v.PrimaryPart
root:SetNetworkOwner(nil)
v:PivotTo(v:GetPivot())
root.Velocity = Vector3.zero
root.Anchored = false
end
TurnManager.resolve()
self:ChangeStatus("Resolve")
self:WaitFor(10)
end
function Round:Start()
local endType,winner = nil,nil
while task.wait() do
endType,winner = self:ShouldRoundEnd()
if endType then
return endType,winner
end
self:StartAimPhase()
self:StartResolvePhase()
end
end
function Round:Results(endtype,winner : number)
for i,v in pairs(getPlaying()) do
v:RemoveTag("PLAYING")
end
ObjectManager.DestroyAll()
MapManager.DestroyMap()
local text
if endtype == Round.END_TYPES.WIN then
local winnerName
pcall(function()
winnerName = game.Players:GetNameFromUserIdAsync(winner)
end)
winnerName = winnerName or "Studio guy"
text = winnerName .. " has won!"
pcall(function()
DataManager.AddValue(game.Players:GetPlayerByUserId(winner),ValueNames.Wins,1)
end)
else
text = "It's a draw!"
end
for i,v in pairs(game.Players:GetPlayers()) do
v:LoadCharacterAsync()
end
self:ChangeGameState(GameState.RESULTS)
self:ChangeStatus(text)
self:WaitFor(5)
end
function Round:Initiate()
while true do
local result = self:EnoughPlayers()
if not result then
self:ChangeStatus("Not enough players")
return
end
self:Intermission()
self:ChooseMap()
self:BringPlayers()
self:GracePeriod()
local endType,winner = self:Start()
self:Results(endType,winner)
end
end
return Round