Creating Moods for a player

This module is called upon a player entering the game

local MoodService = {}

local Players = game:GetService('Players')
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local ServerStorage = game:GetService('ServerStorage')

local Remotes = ReplicatedStorage:WaitForChild('Remotes')
local Events = Remotes:WaitForChild('Events')
local Mood = Events:WaitForChild('Mood')
local UpdateMood = Events:WaitForChild('UpdateMood')

local PlayerData = require(ServerStorage.PlayerData)

local Updating = false
local UpdatingMood

local function update(player, active, mood)
	if active == Updating then return end
	
	Updating = active
	UpdatingMood = mood
end

local function getMood(player)
	local User = PlayerData[player.UserId]
	if not User then return end
	
	Mood:FireClient(player, User.Moods)
end

function MoodService:Groggy(player)
	local User = PlayerData[player.UserId]
	if not User then return end
	
	local TotalMood = 0
			
	for _, v in pairs(User.Moods) do
		TotalMood = TotalMood + v
	end
	
	local Character = player.Character
	if not Character then return end
	
	local Humanoid = Character:WaitForChild('Humanoid')
	if not Humanoid then return end
	
	if TotalMood <= 100 then
		Humanoid.WalkSpeed = 10
	else
		Humanoid.WalkSpeed = 16
	end
end

function MoodService:ChangeMood(player, mood, amount)
	local User = PlayerData[player.UserId]
	if not User then return end
	
	if User.Moods[mood] < 5 then return end
	
	if amount > 0 then
		if User.Moods[mood] < 100 then
			User.Moods[mood] = User.Moods[mood] + amount
		end
	else
		User.Moods[mood] = User.Moods[mood] + amount
	end
	
	self:Groggy(player)
	getMood(player)
end

function MoodService:FindNearestPlayer(player)
	local PlayersCharacter = player.Character
	if not PlayersCharacter then return end
	
	for _, v in pairs(Players:GetPlayers()) do
		if v ~= player then
			local NearCharacter = v.Character
			if not NearCharacter then return end
			
			local Distance = (NearCharacter.PrimaryPart.Position).magnitude - (PlayersCharacter.PrimaryPart.Position).magnitude
			if Distance <= 10 then
				update(player, {}, 'Happiness')
			else
				update(player, false)	
			end
		end
	end
end

function MoodService:SetUp(player)
	local User = PlayerData[player.UserId]
	if not User then return end
	
	coroutine.wrap(function()
		while player:IsDescendantOf(Players) do
			if Updating and UpdatingMood then
				MoodService:ChangeMood(player, UpdatingMood, 5)
			end
			wait(3)
		end
	end)()
	
	spawn(function()
		local i = 0
		
		while User do
			i = i + wait()
			
			MoodService:Groggy(player)
			
			MoodService:FindNearestPlayer(player)
			
			if i >= 5 then
				i = 0
				
				local Elements = {}
				for i, v in pairs(User.Moods) do
					table.insert(Elements, i)
				end
				
				local RandomMood, RandomIndex
				repeat
					RandomIndex = math.random(1, #Elements)
					RandomMood = table.remove(Elements, RandomIndex)
				until #Elements == 0 or RandomMood ~= UpdatingMood
				print(RandomMood)
				if RandomMood ~= UpdatingMood then 
					MoodService:ChangeMood(player, RandomMood, -5)
				end
			end
		end
	end)
end

Mood.OnServerEvent:Connect(getMood)
UpdateMood.OnServerEvent:Connect(update)

return MoodService

Now I feel like this is very clunky, and will more than likely become a problem in the future.

Basically, a player has ‘moods’ (Energy, Happiness, Hunger, Hygiene) A random mood is picked every 5 seconds, and decreases that set mood (moods start at 100, lose 5 points to that when random mood is chosen)

The problems I feel will face in the future is the ChangeMood function. From the client, I have multiple ways for a players mood to go up. Code below checks for which mood should be updating.

coroutine.wrap(function()
		while player:IsDescendantOf(Players) do
			if Updating and UpdatingMood then
				MoodService:ChangeMood(player, UpdatingMood, 5)
			end
			wait(3)
		end
	end)()

Now, I’ve just implemented a new function, which will give the player Happiness when near other players.

for _, v in pairs(Players:GetPlayers()) do
		if v ~= player then
			local NearCharacter = v.Character
			if not NearCharacter then return end
			
			local Distance = (NearCharacter.PrimaryPart.Position).magnitude - (PlayersCharacter.PrimaryPart.Position).magnitude
			if Distance <= 10 then
				update(player, {}, 'Happiness')
			else
				update(player, false)	
			end
		end
	end

Problem with this, is if you aren’t close enough, update gets fired to false, stopping the points from going up. Now, for example, when you sit, that fires the UpdateMood RemoteEvent, which then fires update function to give you Energy. But if the Energy is being given and then a player comes near you, it can’t give you both Energy and Happiness. And then if that player leaves while your still sitting, it’ll run

update(player, false)	

Which will mean your still sitting, but you won’t be getting Energy

Is there a cleaner way I can do this? I’m guessing the use of tables or something? To somehow add like

UpdatingMoods = {}
-- and then insert/remove the speicific mood when it needs to
UpdatingMoods = {'Hunger', 'Energy'}
-- this way all 4 moods could be increased at once (if the player was doing 4 things at once to increase all 4, which will be unlikely, but at least 2 or 3 moods at once

I wouldn’t know how to implement this table system into what I have

2 Likes

You could use event-based programming rather than having a while loop running a conditional statement every three seconds, so you would replace

with something like this:

game:GetService("ReplicatedStorage").UpdateMood.OnServerEvent:Connect(function(player, moods, values)
    for i,mood in ipairs(moods) do
        MoodService:ChangeMood(player, mood, values[i])
    end
end)

Which would change your proximity-based happiness script to something like:

while true do
    for _,v in pairs(Players:GetPlayers()) do
        if not v == player then
            local nearCharacter = v.Character
            if not nearCharacter then return end

            local Distance = nearCharacter.PrimaryPart.Position.magnitude) - PlayersCharacter.PrimaryPart.Position.magnitude
            if distance <= 10 then
                game:GetService("ReplicatedStorage").UpdateMood:FireServer(["Happiness"], [5])
            end
        end
    end
wait(3)
end

This allows you to

  • Update multiple moods at the same time
  • Update different moods by differing amounts, rather than by 5 or any number n
  • Reduce use of co-routines which can be more difficult to use properly
  • Reduce server loads by putting any infinite loops on the client

Sounds something like

My script is server side tho. So like the player proximity is server side,

Then you can use a BindableEvent in ServerStorage, replacing game:GetService("ReplicatedStorage").UpdateMood:FireServer(["Happiness"], [5]) with game:GetService("ServerStorage").UpdateMood:Fire(["Happiness"], [5])