How would one go about creating a team-based xp/levelling system?

So I’ve been working on a SCP game, and I was wondering how one would make a team based xp/levelling system. Or in other words, support for different levels on different teams with different ranks, and xp that is not universal, as in doing something on one team doesn’t transfer to another. I’ve done some work with xp systems for one team, but never for one with multiple teams, I also don’t have much experience with making ranks, but I’d imagine that wouldn’t be to terribly hard.

2 Likes

If you’ve already got teams down, you’re just going to be doing some simple logic!

Once you’ve got an idea of what the player’s team is, you can use Player:GetRankInGroup to get their rank.

Here’s a couple examples of ways you could go about this.

Basic Code Structure
--// Run from a server script, not a local script

local Players = game:GetService("Players")

local GroupId = 111111

--// Call this function whenever you want to give the player XP
local function AwardXP(Player)
	local PlayerRank: number = Player:GetRankInGroup(GroupId)
	local PlayerTeam: Team = Player.Team

	local XPAmount: number --// We fill this in with the amount of XP you want to give them
	
	--// Actual code goes here
end

You can do whatever you want, this is just the basic framework I was working with, which is scalable to a couple different ways of going about it

Method 1 - Conditionals (not recommended, but showing this for breadth)
--// Run from a server script, not a local script

local Players = game:GetService("Players")

local GroupId = 111111

--// Call this function whenever you want to give the player XP
local function AwardXP(Player)
	local PlayerRank: number = Player:GetRankInGroup(GroupId)
	local PlayerTeam: Team = Player.Team
	
	local XPAmount: number --// We fill this in with the amount of XP you want to give them
	
	if PlayerTeam.Name == "Team1" then
		if PlayerRank == 1 then
			XPAmount = 5
		elseif PlayerRank == 5 then
			XPAmount = 12
		elseif PlayerRank == 10 then
			XPAmount == 40
		end
	elseif PlayerTeam.Name == "Team2" then
		if PlayerRank == 1 then
			XPAmount = 2
		elseif PlayerRank == 5 then
			XPAmount = 19
		elseif PlayerRank == 10 then
			XPAmount == 33
		end
	elseif PlayerTeam.Name == "Team3" then
		if PlayerRank == 1 then
			XPAmount = 7
		elseif PlayerRank == 5 then
			XPAmount = 8
		elseif PlayerRank == 10 then
			XPAmount == 59
		end
	end
	
	if XPAmount then
		--// Then you can do something with the XPAmount (prevents an error from occurring if they don't have a rank associated with the team, in which case XPAmount is never set and remains nil)
	else
		print("Player does not have a rank associated with the team")
	end
end

You see this approach a lot, but it’s really tedious to maintain and update. It’s also a mess. A better approach is method 2 (tables) and this scales with modules.

Method 2 - Tables
--// Run from a server script, not a local script

local Players = game:GetService("Players")

local GroupId = 111111

--// Format goes [TeamName] = {[PlayerRank] = AmountOfXP}, etc.}
local TeamXPSettings = {
	["Team1"] = { --// XP settings for Team1
		[1] = 5, --// If they have the rank value of 1, they get 5 XP, etc.
		[5] = 12,
		[10] = 40,
	},
	
	["Team2"] = {
		[1] = 2, --// If they have the rank value of 1, they get 2 XP, etc.
		[5] = 19,
		[10] = 33,
	},
	
	["Team3"] = {
		[1] = 7, --// If they have the rank value of 1, they get 7 XP, etc.
		[5] = 8,
		[10] = 59,
	},
}

--// Call this function whenever you want to give the player XP
local function AwardXP(Player)
	local PlayerRank: number = Player:GetRankInGroup(GroupId)
	local PlayerTeam: Team = Player.Team
	
	local XPAmount: number = TeamXPSettings[PlayerTeam.Name][PlayerRank]
	
	if XPAmount then
		--// Then you can do something with the XPAmount (prevents an error from occurring if they don't have a rank associated with the team, in which case XPAmount is never set and remains nil)
	else
		print("Player does not have a rank associated with the team")
	end
end

As you can see, method 2 is way more clear and easy to maintain. If you wanted to use a modular set-up, it would work basically the same way, since modules are just tables. I won’t get into that here, but that’s a great next step and there are lots of great tutorials on modules on the DevForum.

One thing to note about this approach, is that it’s important that you set up the table completely. As it stands, it’s possible for my function to error if the player is on a team that isn’t registered in TeamXPSettings because it would try to index the team name, get a nil value (which isn’t inherently problematic), and then attempt to index the nil value (which is problematic and would throw an error). That’s not to say method 2 is worse or riskier, just that it requires attention to detail. You can also always build in a check like

Check
local TeamExists = TeamXPSettings[PlayerTeam.Name]

if TeamExists then
	XPAmount = TeamXPSettings[PlayerTeam.Name][PlayerRank]
else
	print("Oops - forgot to add a configuration for", PlayerTeam.Name)
end

To preempt a question you might have, you can also make an automated XP system like this

Method 2 automated
--// Run from a server script, not a local script

local Players = game:GetService("Players")

local GroupId = 111111
local WaitTime = 120 --// The time, in seconds, that you want to wait

--// Format goes [TeamName] = {[PlayerRank] = AmountOfXP}, etc.}
local TeamXPSettings = {
	["Team1"] = { --// XP settings for Team1
		[1] = 5, --// If they have the rank value of 1, they get 5 XP, etc.
		[5] = 12,
		[10] = 40,
	},
	
	["Team2"] = {
		[1] = 2, --// If they have the rank value of 1, they get 2 XP, etc.
		[5] = 19,
		[10] = 33,
	},
	
	["Team3"] = {
		[1] = 7, --// If they have the rank value of 1, they get 7 XP, etc.
		[5] = 8,
		[10] = 59,
	},
}

--// Call this function whenever you want to give the player XP
local function AwardXP(Player)
	local PlayerRank: number = Player:GetRankInGroup(GroupId)
	local PlayerTeam: Team = Player.Team
	
	local XPAmount: number = TeamXPSettings[PlayerTeam.Name][PlayerRank]
	
	if XPAmount then
		--// Then you can do something with the XPAmount (prevents an error from occurring if they don't have a rank associated with the team, in which case XPAmount is never set and remains nil)
	else
		print("Player does not have a rank associated with the team")
	end
end

--// The function that will give players XP over time
local function HandleXP(Player)
	while Players:FindFirstChild(Player) do --// Checks that the Player is still playing
		task.wait(WaitTime)
		
		AwardXP(Player)
	end
end

--// Handle player xp when players join
Players.PlayerAdded:Connect(function(Player)
	HandleXP(Player)
end)

--// Handle player xp for any players who joined before the code runs
for _, Player in pairs (Players:GetPlayers()) do
	HandleXP(Player)
end

Hope that clears up how you can start thinking about going about this. This isn’t meant to be an all-inclusive tutorial, but I was aiming to giving you a basic idea of different methods to go about this, visually show why a table-based approach is preferable, and hit on ways to award XP manually and automatically (both just use the AwardXP function). Sorry in advance if there’s any typos, this was all written on Notepad++.

API references

1 Like

Sorry if I was a bit confusing, when I said ranks I meant each individual team would have different ranks that you could earn (for example, let’s say there’s a researcher team, with the lowest rank being Intern and the highest being Head of Research). I’d still image your could use a system similar to this, no?

1 Like

Yeah, same idea. You’re just going to need to modify the PlayerRank variable I was using and the table structure to facilitate that. It’s the same basic concept though!

1 Like

How would you recommend going about structuring PlayerRank?

It really just depends on the specifics of your project.

If you are trying to save rank, then just save the value and retrieve it as needed (that’s pretty much just a 1:1 swap for the way PlayerRank is currently configured).

If you plan to make rank reset each time so that players can grind it out, I would just recommend checking each time you add XP to see if player’s hit some minimum amount of XP, and then adjust their rank accordingly. You could set up a big table for each player and record their rank like

local Table = {
     [https_KingPie] = "Rank 1",
     [AnotherPlayerName] = "Rank 2",
     --// etc
}

Then, just pull their rank out whenever you add XP and give them the amount of XP designated for each rank. Once they hit the minimum amount of XP for the next rank, just adjust their rank ex:

Table[https_KingPie] = "Rank 2"

Hope that clarifies things. I want to avoid being too specific, since a lot will just depend on the nature of your project. If you want to talk in more detail, feel free to hit me up on Discord

1 Like

Alright, I sent over a friend request so we can hopefully talk a bit more in-depth. Edit: Discord Username is the same as my Roblox one.