boom-bots/src/server/Modules/VotingHandlerServer.luau
2026-05-02 23:53:49 +02:00

201 lines
4.5 KiB
Plaintext

local VotingHandlerServer = {}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Data = ReplicatedStorage:WaitForChild("Data")
local OptionsData = require(Data:WaitForChild("OptionsData"))
local Remote = ReplicatedStorage:WaitForChild("Remote")
local rev_Vote = Remote:WaitForChild("Vote")
local rev_StartVote = Remote:WaitForChild("StartVote")
local rev_EndVote = Remote:WaitForChild("EndVote")
local alreadyVoted = {} -- userId -> option
local voteCounts = {} -- option -> count
local currentOptions = {}
local moduleData = nil
local activeConnections = {}
local touchDebounce = {}
-- cleanup
local function cleanupConnections()
for _, conn in ipairs(activeConnections) do
if conn then conn:Disconnect() end
end
table.clear(activeConnections)
end
-- pick random KEYS from dictionary
local function pickRandomOptions(source, count)
local keys = {}
for key in pairs(source) do
table.insert(keys, key)
end
local selected = {}
for i = 1, math.min(count, #keys) do
local index = math.random(1, #keys)
table.insert(selected, keys[index])
table.remove(keys, index)
end
return selected
end
-- results
local function calculateResults()
local results = {}
for _, option in ipairs(currentOptions) do
results[option] = voteCounts[option] or 0
end
return results
end
-- update stands
local function updateVotingStands(votingStands, results)
if not votingStands then return end
for index, stand in ipairs(votingStands) do
local option = currentOptions[index]
if not option then continue end
local data = moduleData[option]
local gui = stand:FindFirstChild("IndicatorPart")
if gui then gui = gui:FindFirstChild("VoteGui") end
if not gui then continue end
local votesLabel = gui:FindFirstChild("Votes")
local mapLabel = gui:FindFirstChild("MapName")
if mapLabel then
mapLabel.Text = (data and data.DisplayName) or option
end
if votesLabel then
votesLabel.Text = tostring(results[option] or 0)
end
local imagePart = stand:FindFirstChild("MapImagePart")
if imagePart and imagePart:FindFirstChild("ImageGui") then
local img = imagePart.ImageGui:FindFirstChild("Picture")
if img then
img.Image = (data and data.DisplayImage) or ""
end
end
end
end
local function broadcast(votingStands)
local results = calculateResults()
rev_Vote:FireAllClients(results)
updateVotingStands(votingStands, results)
end
-- voting logic
local function registerVote(player, option, votingStands)
if not player or not option then return end
voteCounts[option] = voteCounts[option] or 0
local userId = player.UserId
if alreadyVoted[userId] == option then
return
end
local previous = alreadyVoted[userId]
if previous and voteCounts[previous] then
voteCounts[previous] -= 1
end
alreadyVoted[userId] = option
voteCounts[option] += 1
broadcast(votingStands)
end
-- START
function VotingHandlerServer.StartVoting(optionCount, moduleKey, votingStands)
cleanupConnections()
alreadyVoted = {}
voteCounts = {}
touchDebounce = {}
moduleData = OptionsData.Get(moduleKey)
if not moduleData then
warn("No moduleData for:", moduleKey)
return
end
currentOptions = pickRandomOptions(moduleData, optionCount)
-- remote voting
table.insert(activeConnections,
rev_Vote.OnServerEvent:Connect(function(player, option)
registerVote(player, option, votingStands)
end)
)
-- stand voting
if votingStands then
for index, stand in ipairs(votingStands) do
local option = currentOptions[index]
if option then
local conn = stand.ToVotePart.Touched:Connect(function(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if not player then return end
local userId = player.UserId
if touchDebounce[userId] then return end
touchDebounce[userId] = true
registerVote(player, option, votingStands)
task.delay(0.5, function()
touchDebounce[userId] = nil
end)
end)
table.insert(activeConnections, conn)
end
end
end
updateVotingStands(votingStands, calculateResults())
rev_StartVote:FireAllClients(currentOptions, moduleKey)
end
-- END
function VotingHandlerServer.EndVoting()
cleanupConnections()
rev_EndVote:FireAllClients()
local results = calculateResults()
local winner
local highest = -1
for option, votes in pairs(results) do
if votes > highest then
highest = votes
winner = option
end
end
if not winner and #currentOptions > 0 then
winner = currentOptions[math.random(1, #currentOptions)]
end
return winner, moduleData and moduleData[winner]
end
return VotingHandlerServer