I have been trying to make a role system which for each role, they have a different max limit to give to people. If that doesn’t make sense here is an example: The werewolf card’s max limit is 2, so when 2 players gain the werewolf role, no other player can get the werewolf card.
The problem is that when a role reaches its max limit, it doesn’t stop. It keeps giving that same role to other people.
Scripts affiliated with role system:
-- Module script
local CardModule = {}
CardModule.MaximumRoles = {
["Werewolf"] = {
["MaximumRole"] = 2;
};
["Villager"] = {
["MaximumRole"] = 1;
};
["Hunter"] = {
["MaximumRole"] = 1;
};
["Tanner"] = {
["MaximumRole"] = 1;
};
["Seer"] = {
["MaximumRole"] = 1;
};
["Minion"] = {
["MaximumRole"] = 1;
};
["Drunk"] = {
["MaximumRole"] = 1;
};
["Mason"] = {
["MaximumRole"] = 2;
};
}
CardModule.ChooseRandomCard = function()
for i,player in pairs(game.Players:GetPlayers()) do
if player:FindFirstChildWhichIsA("IntValue").Value < 1 then
for i,intvalue in pairs(player:GetChildren()) do
if intvalue.ClassName == "IntValue" then
if intvalue.Value < 1 then
for i,v in pairs(game.ReplicatedStorage.RoleValues:GetChildren()) do
if v.Value < CardModule.MaximumRoles[v.Name].MaximumRole then
print(v.Name)
return v.Name
else
CardModule.ChooseRandomCard()
end
end
else
local folder = player
local greatestVal = 0
local greatestValObject
for I,v in pairs(folder:GetChildren()) do
if v.Value == greatestVal then
greatestVal = v.Value
greatestValObject = v
print(greatestValObject.Name)
end
end
end
end
end
end
end
end
return CardModule
-- Local script
game.ReplicatedStorage.RoleNarraration.OnClientEvent:Connect(function()
local cardModule = require(game.ReplicatedStorage.ModuleScript)
local card = cardModule.ChooseRandomCard()
wait(1)
if card == nil then
for i,v in pairs(game.Players:GetPlayers()) do
if v:FindFirstChildWhichIsA("IntValue").Value < 1 then
for i,v in pairs(game.ReplicatedStorage.RoleValues:GetChildren()) do
if v.Value < cardModule.MaximumRoles[v.Name].MaximumRole then
print(v.Name)
game.ReplicatedStorage.Role:FireServer()
game.ReplicatedStorage.Role3:FireServer(card)
game.ReplicatedStorage.Role.OnClientEvent:Connect(function(value)
game.ReplicatedStorage.RoleValues:FindFirstChild(value).Value += 1
script.Parent.Parent.Exit.Visible = true
game.ReplicatedStorage.BillboardGuie.OnClientEvent:Connect(function(text)
local clone = game.ReplicatedStorage.BillboardGui:Clone()
clone.Parent = game.Players.LocalPlayer.Character:WaitForChild("Head")
game.Players.LocalPlayer.Character.Head:WaitForChild("BillboardGui").TextLabel.Text = text
end)
end)
end
end
end
end
else
game.ReplicatedStorage.Role:FireServer()
game.ReplicatedStorage.Role3:FireServer(card)
game.ReplicatedStorage.Role.OnClientEvent:Connect(function(value)
game.ReplicatedStorage.RoleValues:FindFirstChild(value).Value += 1
script.Parent.Parent.Exit.Visible = true
game.ReplicatedStorage.BillboardGuie.OnClientEvent:Connect(function(text)
local clone = game.ReplicatedStorage.BillboardGui:Clone()
clone.Parent = game.Players.LocalPlayer.Character:WaitForChild("Head")
game.Players.LocalPlayer.Character.Head:WaitForChild("BillboardGui").TextLabel.Text = text
end)
end)
end
end)
-- Script
game.Players.PlayerAdded:Connect(function(player)
local villager = Instance.new("IntValue")
villager.Name = "Villager"
villager.Parent = player
local werewolf = Instance.new("IntValue")
werewolf.Parent = player
werewolf.Name = "Werewolf"
local tanner = Instance.new("IntValue")
tanner.Parent = player
tanner.Name = "Tanner"
local mason = Instance.new("IntValue")
mason.Parent = player
mason.Name = "Mason"
local seer = Instance.new("IntValue")
seer.Parent = player
seer.Name = "Seer"
local hunter = Instance.new("IntValue")
hunter.Parent = player
hunter.Name = "Hunter"
local minion = Instance.new("IntValue")
minion.Parent = player
minion.Name = "Minion"
local drunk = Instance.new("IntValue")
drunk.Parent = player
drunk.Name = "Drunk"
end)
game.ReplicatedStorage.GameStart.OnServerEvent:Connect(function()
wait(2)
game.ReplicatedStorage.RoleNarraration:FireAllClients()
game.ReplicatedStorage.Role.OnServerEvent:Connect(function()
wait(1)
for i,player in pairs(game.Players:GetPlayers()) do
for i,intvalue in pairs(player:GetChildren()) do
if intvalue.ClassName == "IntValue" then
if intvalue.Value > 0 then
for i,v in pairs(player:GetChildren()) do
if v.ClassName == "IntValue" then
if v.Value < 1 then
local int = intvalue.Name
game.ReplicatedStorage.Role:FireAllClients(intvalue.Name)
player:WaitForChild("PlayerGui").MainGui.Narraration.Text = "Your role is "..int.." (Click the information button for more info.)"
game.ReplicatedStorage.BillboardGuie:FireAllClients(" "..int.." (Only you can see this)")
v:Destroy()
end
end
end
end
end
end
end
end)
end)
--Local script
if not game:IsLoaded() then
game.Loaded:Wait()
screenGui.Frame.Visible = true
else
ReplicatedFirst:RemoveDefaultLoadingScreen()
screenGui:Destroy()
game.ReplicatedStorage.GameStart:FireServer()
end
Having lots of deeply-nested loops with non-descriptive variable names and no comments is a pretty sure-fire way to make hard to fix bugs. It also makes it really hard to help you when you get stuck because your code is hard to understand.
Here’s how I would do it:
-- CardModule
CardModule.RoleLimits = {
Werewolf = 2;
Villager = 1;
Hunter = 1;
Tanner = 1;
Seer = 1;
Minion = 1;
Drunk = 1;
Mason = 2;
}
function shuffle(tbl)
for i = #tbl, 2, -1 do
local j = math.random(i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
return tbl
end
function CardModule.CreateRoleDeck()
local deck = {}
for role, numCopies in pairs(CardModule.RoleLimits) do
for _ = 1, numCopies do
table.insert(deck, role)
end
end
shuffle(deck)
return deck
end
function CardModule.TakeCardFromDeck(deck)
local k = #deck
local card = deck[k]
deck[k] = nil
return k
end
-- GameController, a server script
function startGame()
local players = game.Players:GetPlayers()
local roleDeck = CardModule.CreateRoleDeck()
assert(#players <= #roleDeck, "Not enough roles for this many players!")
local playerRoles = {}
for _, player in ipairs(players) do
playerRoles[player] = CardModule.TakeCardFromDeck(roleDeck)
game.ReplicatedStorage.RoleAssigned:FireClient(player, playerRoles[player])
--Seems like you're trying to modify the player's GUI from the server. That is not a thing that can be done, you'll have to listen to the RoleAssigned RemoteEvent in a LocalScript and have that change the GUI.
end
end
Not sure what you want all those IntValues for, but if they’re important they could totally be incorporated into this solution somehow.
1 Like
Could you please explain your solution? Sorry if I am asking for a bit much here.
2 Likes
Sure! What it does is to create a new “deck of roles” every round. In the deck there’s a number of cards for each role determined by RoleLimits. So 2 werewolves, 1 villager, 2 masons, etc. The deck gets shuffled, and to assign roles it “draws cards from the deck” one at a time. This works like with a normal deck, where the top card is taken and gets removed from the deck when it’s drawn. One card is drawn per player because each player needs one role. A dictionary where keys are players and values are roles is created, and each client is notified of what role they’ve been assigned.
Also, why did you put playerRoles[player]
in this line:
game.ReplicatedStorage.RoleAssigned:FireClient(player, playerRoles[player])
playerRoles[player]
Prints as the number of roles left.
How would I find the role taken? I know it probably has something to with TakeCardFromDeck() but how?
1 Like
On the line before, I assigned CardModule.TakeCardFromDeck(roleDeck)
to playerRoles[player]
.
So game.ReplicatedStorage.RoleAssigned:FireClient(player, playerRoles[player])
sends whatever is stored in playerRoles[player]
to the player.
The thing that is stored there is the return value from CardModule.TakeCardFromDeck(roleDeck)
.
Which is a card taken from the top of the shuffled deck of roles.
Here’s a version that breaks it down a bit more:
-- GameController, a server script
function startGame()
local players = game.Players:GetPlayers()
local roleDeck = CardModule.CreateRoleDeck()
assert(#players <= #roleDeck, "Not enough roles for this many players!")
local playerRoles = {}
for _, player in ipairs(players) do
local playerRole = CardModule.TakeCardFromDeck(roleDeck) --pick a role for this player by taking the top card from the deck
playerRoles[player] = playerRole --store it for later, so we can figure out what role a player has
game.ReplicatedStorage.RoleAssigned:FireClient(player, playerRole)
--Seems like you're trying to modify the player's GUI from the server. That is not a thing that can be done, you'll have to listen to the RoleAssigned RemoteEvent in a LocalScript and have that change the GUI.
end
end
The playerRole is equal to the amount of roles left. How will that help me find the role that is taken?
This is what it prints when you put print(playerRole)
![Screen Shot 2022-01-06 at 3.59.58 PM](//devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/4X/e/4/4/e44efa1a04ce84d1010244ec407970b7d193462e.png)
Are you still here? I am still having the problem. @ThanksRoBama
Don’t do TakeCardFromDeck(CardModule.RoleLimits)
. Do TakeCardFromDeck(roleDeck)
. The role deck is just an array of roles (strings), while RoleLimits is a role to role limit dictionary (string to int dictionary)
I did do TakeCardFromDeck(roleDeck)
. Did you test the script?
Oh yeah, try this:
function CardModule.TakeCardFromDeck(deck)
local k = #deck
local card = deck[k]
deck[k] = nil
return card
end
Wait a minute. Every time a player joins, a player who had a role gets another one.
Local script:
game.ReplicatedStorage.RoleAssigned.OnClientEvent:Connect(function(player, playerRole)
script.Parent.Text = "Your role is "..player.." (Click Information for info on your role.)"
script.Parent.Parent.Exit.Visible = true
game.ReplicatedStorage.BillboardGui.TextLabel.Text = player.." (Only you can see this)"
game.ReplicatedStorage.BillboardGui.Parent = game.Players.LocalPlayer.Character:WaitForChild("Head")
end)
@ThanksRoBama
Is there any way to fix this?
1 Like
Are you calling startGame
every time a player joins? That’d explain it, it’s only supposed to be called once per game, to start the game. If you want to support players joining mid game, you’ll have to have the roleDeck
variable outside of startGame
, so that other parts of your program can access it. That way you can call a separate function when a player joins, and take another card from the deck if there are any left.
1 Like
How would I use roleDeck
when a player joins mid-game?
Also when I set it to not doing the function startGame() every time a player joins it still gives players multiple roles.
@ThanksRoBama
How would I view the roles that have not been picked? I am trying to make it so that there are always 3 cards that have not been picked.
You’d have to return cards to the deck when a player leaves, and give a player a card from the deck when they join mid game. So you’d have to connect some functions up to PlayerAdded and PlayerRemoving. Since these functions have to access the role deck, you should probably make the roleDeck a global variable in the script
1 Like
This is not what I am asking, however, if this is the correct answer to my question, please explain a little further.
@ThanksRoBama
Btw, what I actually meant was that I don’t care about the players that leave. I want to make it so that every round, there are 3 random cards that no player has obtained. I want to view these 3 random cards that nobody has obtained.
@ThanksRoBama
Are you still here? I need help.