Welcome to My Portfolio

Exploring the digital universe

0+
Contributed to 30M+ visits
0+
5+ years experience
0+
Released 4+ projects

Description

I'm nsawill, a 16-year-old full-stack developer who's been making games since 2017. Started playing Roblox back in 2015 and turned that into a career working with large studios on projects that reached millions of players.

I code in Luau, Python, C#, HTML, CSS, JavaScript, and Java, basically whatever the project needs. I can handle everything from game mechanics and UI to backend systems.

I put in 5+ hours every day on development work. Available for both long-term partnerships and quick one-off projects.

Flexible with payment, I accept percentage deals, Robux, or USD through PayPal. Hit me up if you want to work together.

Loading 3D Model...

Payment Methods Accepted

Percentage

Revenue share partnerships for long-term projects

Robux

Robux

Direct payment through Roblox platform

USD (PayPal)

Secure international payments via PayPal

Portfolio

RoGuessr


-- Services
local Rep = game:GetService("ReplicatedStorage")
local UpdateViewSphere = require(script.Parent:WaitForChild("UpdateViewSphere"))
local SyncGame = require(script.Parent:WaitForChild("SyncGame"))
local ViewSphere = workspace.ViewSphere.PrimaryPart
local TweenService = game:GetService("TweenService")

local Camera = workspace.CurrentCamera
local Pointer = script:WaitForChild("Pointer")
local Pointers = workspace:WaitForChild("Pointers")

local RANDOM_INSTANCE = Random.new()
local POINTER_RADIUS_OFFSET = 20
local Y_OFFSET = 45
local ANIMATION_INFO = TweenInfo.new(0.22,Enum.EasingStyle.Sine,Enum.EasingDirection.Out)
local SIZE_MULTIPLIER = 1.1
local VECTOR_MAP = {
	[1] = "X",
	[2] = "Y",
	[3] = "Z"
}

local CurrentDetails = nil

-- Client
local Client = {}

local function StringToVector(String : string)
	if typeof(String) == "Vector3" then return String end
	
	local SubbedLocation = String--string.split(string.split(String,"(")[2],")")[1]
	local XYZLocation = {
		X = 0,
		Y = 0,
		Z = 0
	}

	for CoordinateIndex, Coordinate in pairs(string.split(SubbedLocation,",")) do
		local XYZ = VECTOR_MAP[CoordinateIndex]
		local Number = tonumber(Coordinate)

		XYZLocation[XYZ] = Number
	end

	return vector.create(XYZLocation.X,XYZLocation.Y,XYZLocation.Z)
end

function Client.MoveToIndex(Position : string, Rigid : boolean?)
	Pointers:ClearAllChildren()
	
	local Textures = CurrentDetails.CurrentGame.Textures
	local ActualPosition = StringToVector(Position)
	
	for StringLocation : string, Textures : {[number] : string} in pairs(Textures) do
		local Location = StringToVector(StringLocation)
		
		if StringLocation ~= Position then
			if not Pointers:FindFirstChild(StringLocation) then
				local PointerClone = Pointer:Clone()
				local Direction = (Location - ActualPosition - ViewSphere.Position).Unit
				if CurrentDetails.CurrentGame.Flipped == true then
					Direction = (ActualPosition - Location - ViewSphere.Position).Unit
				end
		
				local NewCFrame = CFrame.new(ViewSphere.Position)
				local DirectionCFrame = CFrame.new(NewCFrame.Position,ViewSphere.Position + (Direction*2))
				local X,Y,Z = DirectionCFrame:ToOrientation()
				local CompleteOrientation = CFrame.fromOrientation(0,Y,0) * CFrame.Angles(0,Y_OFFSET,0)
				local CompleteCFrame = CFrame.new(ViewSphere.Position + (CompleteOrientation.RightVector * POINTER_RADIUS_OFFSET))
				CompleteCFrame *= CompleteOrientation
				
				PointerClone.Parent = Pointers
				PointerClone.Name = StringLocation
				PointerClone:SetAttribute("CFrame",CompleteCFrame)
				
				local ClickDetector = PointerClone:WaitForChild("ClickDetector")
				local CurrentPointerTween : Tween = nil
				local OriginalSize = PointerClone.Size
				
				local Preview = PointerClone:WaitForChild("Preview"):WaitForChild("Image")
				Preview.Image = Textures[1]
				
				ClickDetector.MouseHoverEnter:Connect(function()
					if CurrentPointerTween ~= nil then
						CurrentPointerTween:Cancel()
					end
					
					Preview.Visible = true
					CurrentPointerTween = TweenService:Create(PointerClone,ANIMATION_INFO,{Size = OriginalSize * SIZE_MULTIPLIER})
					CurrentPointerTween:Play()
				end)
				ClickDetector.MouseHoverLeave:Connect(function()
					if CurrentPointerTween ~= nil then
						CurrentPointerTween:Cancel()
					end
					
					Preview.Visible = false
					CurrentPointerTween = TweenService:Create(PointerClone,ANIMATION_INFO,{Size = OriginalSize})
					CurrentPointerTween:Play()
				end)
				ClickDetector.MouseClick:Connect(function()
					Client.MoveToIndex(StringLocation,false)
				end)
			end
		end
	end

	Camera:SetAttribute("CurrentPosition",ActualPosition)
	if Rigid then
		UpdateViewSphere.ForceUpdate(Textures[Position])
	else
		UpdateViewSphere.UpdateSmooth(Textures[Position])
	end
end

function Client.Init()
	SyncGame.AttachToSelectedGame(function(Details)
		CurrentDetails = Details
		
		local FirstPosition = Details.FirstTexture
		
		Pointers:ClearAllChildren()
		Client.MoveToIndex(FirstPosition)
	end)
end

return Client
                        

Uncomplete prototype for my own studio, couldn't secure funding so no longer working on this project.

Drone Obby Prototype


local Obstacles = require(script.Parent)
local Player = game.Players.LocalPlayer
local RunService = game:GetService("RunService")
local Movement = require(game.ReplicatedStorage:WaitForChild("Modules"):WaitForChild("Client"):WaitForChild("Movement"):WaitForChild("Walk"))
local FAN_SPEED = 100
local WIND_SPEED = vector.create(300,160,300)

local module = {}

function module:Load(WindTunnel)
	self.WindTunnel = WindTunnel
	local Hitbox : Part = WindTunnel:WaitForChild("Hitbox")
	
	local NewWindAudio = game.ReplicatedStorage:WaitForChild("Assets"):WaitForChild("Sounds"):WaitForChild("Wind"):Clone()
	NewWindAudio.Parent = Hitbox
	NewWindAudio:Play()
	
	local NewWindVFX = script:WaitForChild("Part"):WaitForChild("Attachment"):Clone()
	NewWindVFX.Parent = WindTunnel:WaitForChild("Part")
	self.VFX = NewWindVFX
	
	self.Audio = NewWindAudio
	
	self.TouchConnection = Hitbox.Touched:Connect(function(Hit)
		if Hit.Name ~= "HumanoidRootPart" then return end
		if Hit.Parent:FindFirstChild("Humanoid") then
			local HitPlayer = game.Players:GetPlayerFromCharacter(Hit.Parent)
			if HitPlayer == Player then
				Obstacles.SharedData.LastEffect = WindTunnel
				Movement.ChangeAddition("WindTunnel",-Hitbox.CFrame.LookVector * WIND_SPEED)
			end
		end
	end)
	
	self.TouchEndedConnection = Hitbox.TouchEnded:Connect(function(Hit)
		if Hit.Name ~= "HumanoidRootPart" then return end
		if Hit.Parent:FindFirstChild("Humanoid") then
			local HitPlayer = game.Players:GetPlayerFromCharacter(Hit.Parent)
			if HitPlayer == Player then
				if Obstacles.SharedData.LastEffect == WindTunnel then
					Movement.ChangeAddition("WindTunnel",nil)
				end
			end
		end
	end)
	
	if Obstacles.SharedData.AccessedWindTunnel == nil then
		Obstacles.SharedData.AccessedWindTunnel = true
		Obstacles.SharedData.WindTunnels = {}
		
		RunService:BindToRenderStep("WindTunnel",4,function(DT)
			local AllTunnels = Obstacles.SharedData.WindTunnels
			
			for _, Tunnel in pairs(AllTunnels) do
				local Fan = Tunnel.BoxFan.Fan
				
				Fan.CFrame *= CFrame.Angles(0,math.rad(15 * DT * FAN_SPEED),0)
			end
		end)
	end
	table.insert(Obstacles.SharedData.WindTunnels,WindTunnel)
end

function module:Unload()
	self.TouchEndedConnection:Disconnect()
	self.TouchConnection:Disconnect()
	self.Audio:Destroy()
	self.VFX:Destroy()
	
	if Obstacles.SharedData.AccessedWindTunnel ~= nil then
		Movement.ChangeAddition("WindTunnel", nil)
		RunService:UnbindFromRenderStep("WindTunnel")
	end
	
	self.Finished:Fire()
end

return module
                        

This is a prototype I made by myself of a drone obby, this project is no longer in the works.

Doomed 2 Die

Doomed 2 Die's Thumbnail

https://www.roblox.com/games/86340697076369/DOOMED-2-DIE I worked on updates for Doomed 2 Die, helping the CCU grow steadily up to 1.2K, whenever I left this project the game started to die down.

Movement System

Hired short-term to make a movement system for an asymmetrical horror game.

Semi Truck Obby

STO's Thumbnail

local progressionFunnel = {}

local AnalyticsService = game:GetService("AnalyticsService")
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
    AnalyticsService:LogOnboardingFunnelStepEvent(
        player, 1, "Player Joined"
    )
end)

function progressionFunnel.LogCheckpointStep(player, checkpointIndex)
    AnalyticsService:LogOnboardingFunnelStepEvent(
        player, checkpointIndex, "Checkpoint " .. checkpointIndex .. " Unlocked"
    )
end

return progressionFunnel
                        

https://www.roblox.com/games/110345898280451/Semi-Truck-Obby I worked on updates for Semi Truck Obby, helping the CCU grow steadily to around 300. I no longer work on this project.

Terrain + Camera System


							local Helpers = game.ReplicatedStorage.Modules.Terrain.Helpers
local T = require(Helpers.T)
local TerrainService = workspace.Terrain
local RunService = game:GetService("RunService")

local AllBiomes : {[number] : {
	Name : string,
	HeatThresholds : Vector2,
	HumidityThresholds : Vector2,
	LightThresholds : Vector2,
	Callback : (X : number, Y : number, Z : number, Settings : T.GenerationSettings) -> nil	
}} = {}
for _, Biome in pairs(script:GetChildren()) do
	table.insert(AllBiomes,require(Biome))
end

return function(Terrain : T.Terrain, BiomeSettings : T.BiomeSettings) : T.MaterialisedTerrain
	local PerlinNoise = require(Helpers.PerlinNoise)
	
	local HeatNoise = PerlinNoise("3D")
	local HumidityNoise = PerlinNoise("3D")
	local LightNoise = PerlinNoise("3D")
	
	for X = 1, #Terrain do
		for Y = 1, #Terrain[X] do
			for Z = 1, #Terrain[X][Y] do
				local Heat = HeatNoise(X * BiomeSettings.HeatAmplifier, Y * BiomeSettings.HeatAmplifier, Z * BiomeSettings.HeatAmplifier) * BiomeSettings.BiomeSize
				local Humidity = HumidityNoise(X * BiomeSettings.HumidityAmplifier, Y * BiomeSettings.HumidityAmplifier, Z * BiomeSettings.HumidityAmplifier) * BiomeSettings.BiomeSize
				local Light = LightNoise(X * BiomeSettings.LightAmplifier, Y * BiomeSettings.LightAmplifier, Z * BiomeSettings.LightAmplifier) * BiomeSettings.BiomeSize
				
				local BiomeChosen = nil
				for _, Biome in pairs(AllBiomes) do
					local MinHeat = Biome.HeatThresholds.X
					local MaxHeat = Biome.HeatThresholds.Y
					local MinHumidity = Biome.HumidityThresholds.X
					local MaxHumidity = Biome.HumidityThresholds.Y
					local MinLight = Biome.LightThresholds.X
					local MaxLight = Biome.LightThresholds.Y
					
					local HeatThreshold = (Heat >= MinHeat and Heat <= MaxHeat)
					local HumidityThreshold = (Humidity >= MinHumidity and Humidity <= MaxHumidity)
					local LightThreshold = (Light >= MinLight and Light <= MaxLight)
					
					if HeatThreshold and HumidityThreshold and LightThreshold then
						BiomeChosen = Biome
						break
					end
				end
				
				if BiomeChosen then  
					BiomeChosen.Callback(X,Y,Z,BiomeSettings)
				end
			end
		end
	end
	
	return Terrain
end
                        

Hired short-term to script a procedural terrain generation system for underground caves along with a camera system that works alongside the terrain system.

Sessions


-- Services
local Players = game:GetService("Players")
local SessionManager = require(script.Parent.SessionManager)
local PlayerManager = require(script.Parent.PlayerManager)
local HTTPS = game:GetService("HttpService")

-- Variables
local PlayerRanks : {[Player] : string} = {}
local AllRanks : {[string] : Rank} = {}
local LastOwner : Player = nil

-- Constants
local DEFAULT_RANK = "Visitor"
local DEFAULT_OWNER_RANK = "Owner"

-- Default/Base rank
local BaseRank : Rank = {}
BaseRank.PermissionLevel = 0
BaseRank.Name = ""
function BaseRank:Apply(Player : Player)
	Player:SetAttribute("PermissionLevel",self.PermissionLevel)
	Player:SetAttribute("Rank",self.Name)
	
	PlayerManager.UpdateData(Player,{Rank = self.Name})
	PlayerRanks[Player] = self.Name
end
function BaseRank:Remove(Player : Player)
	
end


local Ranks = {}

local function New(Rank)
	return setmetatable(Rank,{__index = BaseRank})
end

function Ranks.GetPlayersRank(Player : Player) : string
	return PlayerRanks[Player]
end

function Ranks.ChangePlayerRank(Player : Player, Rank : string)
	PlayerRanks[Player] = Rank
end

function Ranks.RankPlayer(Player : Player, Rank : string)
	if PlayerRanks[Player] ~= nil then
		local OldPlayerRank = PlayerRanks[Player]
		AllRanks[OldPlayerRank]:Remove(Player)
	end
	AllRanks[Rank]:Apply(Player)
end

function Ranks.Initialize()
	for _, Module in pairs(script:GetChildren()) do
		local RequiredRank = require(Module)
		RequiredRank = New(RequiredRank)
		RequiredRank.Name = Module.Name
		AllRanks[Module.Name] = RequiredRank
	end
	
	PlayerManager.BindToData({"JoinTime","Rank"},function(Player, Key, NewData)
		if NewData == nil then return end
		local SessionData = SessionManager.GetSessionData()
		if SessionData == nil then return end
		
		if Key == "Rank" then
			SessionData.Ranks[Player.UserId] = NewData
			
			if NewData == DEFAULT_OWNER_RANK then
				SessionData.Host = {
					DisplayName = Player.DisplayName,
					UserId = Player.UserId,
					Name = Player.Name,
					HasVerifiedBadge = Player.HasVerifiedBadge
				}
				SessionManager.UpdateSession(SessionData)
			end
		elseif Key == "JoinTime" then
			if SessionData.Host.UserId == Player.UserId then
				Ranks.RankPlayer(Player,DEFAULT_OWNER_RANK)
			else
				Ranks.RankPlayer(Player,DEFAULT_RANK)
			end
		end
	end)
	Players.PlayerRemoving:Connect(function(Player)
		if SessionManager.GetSessionData() == nil then return end
		if SessionManager.GetSessionData().Host.UserId == Player.UserId then
			LastOwner = nil
			
			local HighestPermissionPlayer = {
				Player = nil,
				PermissionLevel = 0,
				JoinTime = nil,
			}
			for Player, Rank in pairs(PlayerRanks) do
				if Rank == DEFAULT_OWNER_RANK then continue end
				local PlayerRank = AllRanks[Rank]
				local PlayerPermissionLevel = PlayerRank.PermissionLevel
				
				if PlayerPermissionLevel >= HighestPermissionPlayer.PermissionLevel then
					local PlayerJoinTime = PlayerManager.GetProfile(Player).JoinTime
					if PlayerJoinTime < (HighestPermissionPlayer.JoinTime or os.time()) then
						HighestPermissionPlayer = {
							JoinTime = PlayerJoinTime,
							Player = Player,
							PermissionLevel = PlayerPermissionLevel
						}
					end
				end
			end
			
			if HighestPermissionPlayer.Player then
				LastOwner = HighestPermissionPlayer.Player
				Ranks.RankPlayer(HighestPermissionPlayer.Player,DEFAULT_OWNER_RANK)
			else
				warn("Closing session -- no player to inherit session.")
			end
		end
	end)
	SessionManager.BindToSessionData(function(NewSessionData)
		if NewSessionData == nil then return end
		if NewSessionData.Host == nil then return end
		
		local OwnerID = NewSessionData.Host.UserId or 0
		local Owner = Players:GetPlayerByUserId(OwnerID)
		
		if LastOwner ~= Owner then
			if LastOwner then
				Ranks.RankPlayer(LastOwner,DEFAULT_RANK)
			end
			if Owner then
				LastOwner = Owner
				Ranks.RankPlayer(Owner,DEFAULT_OWNER_RANK)
			end
		end
	end)
end


export type Rank = {
	PermissionLevel : number,
	Apply : (self : {}, Player : Player) -> nil,
	Remove : (self : {}, Player : Player) -> nil
}
return Ranks
                        

This is a project I no longer work on, left the project after owner and I had some disputes. This was meant to be a social game where players can join sessions with other users.

Pet Simulator System


--Services
local Knit = require(game.ReplicatedStorage.Packages.Knit)
local RunService = game:GetService("RunService")
local DataService = nil
local InventoryHandlerService = nil
local Modules = game.ReplicatedStorage:WaitForChild("Modules")

--player related variables
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local PlayersData = nil
local MouseClickedConnection = nil
local MouseMovedConnection = nil
local SelectedTemplate = nil

--pet related variables
local AllPetModules = {
	Pets = "nil",
	Types = "nil",
	Rarities = "nil",
	GuiFunctions = "nil"
}

local ExistingPets = {}

--Gui related variables
local Gui = Player.PlayerGui:WaitForChild("Inventory")
local Inventory = Gui:WaitForChild("Main")
local PetsFrame = Inventory:WaitForChild("Pets"):WaitForChild("Container")
local EquippedFrame = PetsFrame:WaitForChild("Equipped"):WaitForChild("Container")
local UnequippedFrame = PetsFrame:WaitForChild("Unequipped"):WaitForChild("Container")
local TemplateIcon = Gui:WaitForChild("TemplateIcon")
local PetDetails = Gui:WaitForChild("TemplateDetails")
local Open : ImageButton = Gui:WaitForChild("Open")
local Exit : ImageButton = Inventory:WaitForChild("Exit"):WaitForChild("Button")

local NonEquippedPetsSorting = nil
local EquippedPetsSorting = nil

local EquipConnections = {}


local InventoryHandler = Knit.CreateController({
	Name = "InventoryHandler"
})

local function WipeFrame(Frame)
	for _, Pet in pairs(Frame:GetChildren()) do -- remove pets from frame
		if Pet:IsA("ImageButton") or Pet:IsA("ImageLabel") then
			Pet:Destroy() -- remove every single pet in frame
		end
	end
end

function InventoryHandler.AddEquipConnection(Func)
	table.insert(EquipConnections,Func)
end

function InventoryHandler.RemoveEquipConnection(Func)
	table.remove(EquipConnections,table.find(EquipConnections,Func))
end

local function WipeFrames(Frames : {Frame : Frame})
	for _, Frame in pairs(Frames) do -- remove pets from frame
		WipeFrame(Frame)
	end
end

function InventoryHandler.GetNonItemedPet(Pet,Proc : string?,Args : {}?)
	local NonItemedPet = Pet
	for _, Statistic in pairs(AllPetModules.Types.All) do
		if string.find(Pet,Statistic.Prefix) then
			NonItemedPet = Pet:gsub(Statistic.Prefix,"")

			if Proc ~= nil then
				if Args ~= nil then
					Statistic[Proc](Statistic,PetDetails,Args)
				else
					Statistic[Proc](Statistic,PetDetails)
				end
			end
			NonItemedPet = InventoryHandler.GetNonItemedPet(NonItemedPet) -- if pet is a proc of another proc then get the non itemed pet
		end
	end

	return NonItemedPet
end

local function MakeNewTemplate(Pet,Parent)
	local TemplateClone = TemplateIcon:Clone() -- new pet icon
	TemplateClone.Visible = true
	TemplateClone.Name = Pet
	
	local NonItemedPet = InventoryHandler.GetNonItemedPet(Pet,"InventoryInit",{TemplateClone})
	
	local PetData = AllPetModules.Pets.All[NonItemedPet]
	TemplateClone.Parent = Parent
	TemplateClone.Item.Image = PetData.Image
	TemplateClone.Multiplier.Text = PetData.Multiplier
	TemplateClone.Type.BackgroundColor3 = AllPetModules.Rarities.All[PetData.Type].Color
	
	TemplateClone.MouseLeave:Connect(function()
		InventoryHandler.LeaveInspectedPet(TemplateClone)
	end)
	TemplateClone.MouseEnter:Connect(function()
		InventoryHandler.InspectPet(TemplateClone)
	end)
	
	AllPetModules.Rarities.All[PetData.Type]:InventoryInit(TemplateClone)
	
	if Parent.Parent.Name == "Equipped" then
		if EquippedPetsSorting == nil then
			EquippedPetsSorting = {}
			EquippedPetsSorting[1] = {TemplateClone,PetData.Multiplier}
			return TemplateClone
		end
		
		for Index, Value in ipairs(EquippedPetsSorting) do
			if PetData.Multiplier > Value[2] then
				table.insert(EquippedPetsSorting, Index, {TemplateClone, PetData.Multiplier})
				return TemplateClone
			end
		end
		table.insert(EquippedPetsSorting,{TemplateClone,PetData.Multiplier})
	elseif Parent.Parent.Name == "Unequipped" then
		if NonEquippedPetsSorting == nil then
			NonEquippedPetsSorting = {}
			NonEquippedPetsSorting[1] = {TemplateClone,PetData.Multiplier}
			return TemplateClone
		end
		for Index, Value in ipairs(NonEquippedPetsSorting) do
			if PetData.Multiplier > Value[2] then
				table.insert(NonEquippedPetsSorting, Index, {TemplateClone, PetData.Multiplier})
				return TemplateClone
			end
		end
		table.insert(NonEquippedPetsSorting,{TemplateClone,PetData.Multiplier})
	end
	
	return TemplateClone
end

function InventoryHandler.MouseMoved()
	if SelectedTemplate == nil then
		if MouseMovedConnection ~= nil then
			MouseMovedConnection:Disconnect()
			MouseMovedConnection = nil
		else
			return
		end
	end
	local MousePos = Vector2.new(Mouse.X, Mouse.Y)
	local ScreenSize = workspace.CurrentCamera.ViewportSize

	local PetSize = PetDetails.AbsoluteSize
	local Offset = Vector2.new(0, 0.1) * ScreenSize
	local ClampedX = math.clamp(MousePos.X + Offset.X, 0, ScreenSize.X - PetSize.X)
	local ClampedY = math.clamp(MousePos.Y + Offset.Y, 0, ScreenSize.Y - PetSize.Y)

	PetDetails.Position = UDim2.fromOffset(ClampedX,ClampedY)
	
	--[[
	local AbsolutePosition = PetDetails.AbsolutePosition
	local AbsoluteSize = PetDetails.AbsoluteSize
	local ScreenSize = workspace.CurrentCamera.ViewportSize
	
	PetDetails.Position = UDim2.fromOffset(Mouse.X,Mouse.Y) +UDim2.fromScale(0,0.1) -- update position of pet details to mouse position]]
end

function InventoryHandler.InspectPet(Pet)
	local NonItemedPet = InventoryHandler.GetNonItemedPet(Pet.Name)
	if PetDetails.PetName.Text == NonItemedPet and PetDetails.Visible then
		return -- if pet is already hovered then return
	end
	for _, Detail in pairs(PetDetails:GetChildren()) do
		if Detail:HasTag("Specific") then
			Detail:Destroy()
		end
	end
	
	NonItemedPet = InventoryHandler.GetNonItemedPet(Pet.Name,"DisplayInit",{PetDetails})
	local PetData = AllPetModules.Pets.All[NonItemedPet]
	AllPetModules.Rarities.All[PetData.Type]:DisplayInit(Pet)
	PetDetails.Visible = true
	PetDetails.PetName.Text = PetData.DisplayName
	PetDetails.Descriptions.Text = PetData.Description
	PetDetails.Type.Text = PetData.Type
	PetDetails.Type.TextColor3 = AllPetModules.Rarities.All[PetData.Type].Color
	PetDetails.GlobalAmount.Text = tostring(ExistingPets[NonItemedPet] or 1).." exist"
	PetDetails.Multiplier.Text = PetData.Multiplier
	PetDetails.Position = UDim2.fromOffset(Mouse.X,Mouse.Y) -- update position of pet details to mouse position
	SelectedTemplate = Pet
	
	local Layout = PetDetails:FindFirstChildWhichIsA("UIListLayout")
	if Layout then
		local TotalHeight = 0
		for _, Child in ipairs(PetDetails:GetChildren()) do
			if Child:IsA("GuiObject") and Child.Visible and Child ~= Layout then
				TotalHeight += Child.Size.Y.Offset + Layout.Padding.Offset
			end
		end
		PetDetails.Size = UDim2.new(PetDetails.Size.X.Scale, PetDetails.Size.X.Offset, 0, TotalHeight)
	end
	
	MouseClickedConnection = Pet.InputBegan:Connect(function(Input) -- listen for when player clicks on pet
		if Input.UserInputType == Enum.UserInputType.MouseButton1 then -- make sure its a left click
			InventoryHandler.SelectPet(Pet)
		end
	end)
	
	MouseMovedConnection = Mouse.Move:Connect(InventoryHandler.MouseMoved) -- listen for when mouse moves
end

local function RemoveMouseConnections()
	for _, Detail in pairs(PetDetails:GetChildren()) do
		if Detail:HasTag("Specific") then
			Detail:Destroy()
		end
	end
	
	if MouseClickedConnection ~= nil then
		MouseClickedConnection:Disconnect()
		MouseClickedConnection = nil
	end
	if MouseMovedConnection ~= nil then
		MouseMovedConnection:Disconnect()
		MouseMovedConnection = nil
	end
end

function InventoryHandler.LeaveInspectedPet(Pet)
	local NonItemedPet = InventoryHandler.GetNonItemedPet(Pet.Name)
	if NonItemedPet == PetDetails.PetName.Text  then -- make sure pet is actually hovered
		PetDetails.Visible = false
		SelectedTemplate = nil
		InventoryHandler.GetNonItemedPet(Pet.Name,"RemoveDisplay",{PetDetails})
		RemoveMouseConnections()
	end
end

function InventoryHandler.SelectPet(Pet)
	for _, Connection in pairs(EquipConnections) do
		if Connection(Pet) == false then
			return -- make sure we WANT to equip/unequip the pet
		end
	end
	if Pet.Parent.Parent.Name == "Equipped" then -- if pet is already equipped then unequip it
		InventoryHandlerService:UnequipPet(Pet.Name)
	else
		InventoryHandlerService:EquipPet(Pet.Name)
	end
end

function InventoryHandler.PreloadAddExistingPet(Pet)
	local Name = InventoryHandler.GetNonItemedPet(Pet.Name or Pet)
	local Success, Error = pcall(function()
		ExistingPets[Name] = ExistingPets[Name] + 1 -- add to existing pets
	end)
	if not Success then
		if ExistingPets == nil then
			ExistingPets = {}
		end
		ExistingPets[Name] = 1 -- if pet doesnt exist then make it 1
	end
end

function InventoryHandler.PreloadRemoveExistingPet(Pet)
	local Name = InventoryHandler.GetNonItemedPet(Pet.Name or Pet)
	local Success, Error = pcall(function()
		ExistingPets[Name] = (ExistingPets[Name] or 1) - 1 -- remove from existing pets
	end)
	if not Success then
		if ExistingPets == nil then
			ExistingPets = {}
		end
		ExistingPets[Name] = 0 -- if pet doesnt exist then make it 0
	end
end

function InventoryHandler.ReloadInventory(Data)
	local Success, Error = pcall(function()
		for _, Detail in pairs(PetDetails:GetChildren()) do
			if Detail:HasTag("Specific") then
				Detail:Destroy()
			end
		end
		NonEquippedPetsSorting = nil
		EquippedPetsSorting = nil
		PlayersData = Data
		PetDetails.Visible = false
		local Pets = Data.Pets
		local EquippedPets = Data.EquippedPets

		-- Remove unused connections to save resources
		RemoveMouseConnections()
		SelectedTemplate = nil

		EquippedFrame.Parent.Title.Text = string.format("Equipped Pets (%d/%d)",#EquippedPets,Data.MaxEquippedPets)

		WipeFrames({EquippedFrame,UnequippedFrame})

		for _, Pet in pairs(Pets) do -- load in new pets to unequipped frame
			if UnequippedFrame:FindFirstChild(Pet) then
				if UnequippedFrame[Pet].Amount.Visible == false then UnequippedFrame[Pet].Amount.Visible = true end
				UnequippedFrame[Pet].Amount.Text = tostring(tonumber(string.split(UnequippedFrame[Pet].Amount.Text,"x")[1]) +1).."x" -- display amount of same pet in inventory

				local PetData = AllPetModules.Pets.All[InventoryHandler.GetNonItemedPet(Pet)]
				AllPetModules.Rarities.All[PetData.Type]:InventoryInit(UnequippedFrame:FindFirstChild(Pet))
				continue
			end
			MakeNewTemplate(Pet,UnequippedFrame)
		end
		if NonEquippedPetsSorting ~= nil then
			for Index, Template in ipairs(NonEquippedPetsSorting) do
				Template[1].LayoutOrder = Index
			end
		end


		for _, Pet in pairs(EquippedPets) do -- load in new pets to equipped frame
			local TemplateClone = MakeNewTemplate(Pet,EquippedFrame)
			TemplateClone.Amount.Text = "" -- no amount needed as its only 1
		end
		if EquippedPetsSorting ~= nil then
			for Index, Template in ipairs(EquippedPetsSorting) do
				Template[1].LayoutOrder = Index
			end
		end
	end)
end

function InventoryHandler:KnitStart()
	DataService = Knit.GetService("PlayerHandler")
	InventoryHandlerService = Knit.GetService("InventoryHandler")
	
	for Name, Misc in pairs(AllPetModules) do
		local Required = require(Modules:WaitForChild(Name))
		Required.All = Required.Load()
		AllPetModules[Name] = Required
	end
	
	Open.Activated:Connect(function() -- check for when player wants to open/close inventory
		Inventory.Visible = not Inventory.Visible
	end)
	
	Exit.Activated:Connect(function()
		Inventory.Visible = false
	end)
	

	DataService.DataChanged:Connect(InventoryHandler.ReloadInventory) -- check when players data changes
	InventoryHandlerService.ExistingPetsChanged:Connect(function(Data)
		ExistingPets = Data
	end)
	
	InventoryHandlerService:GetExistingPets():andThen(function(Data) -- get existing pets
		ExistingPets = Data
	end)
	DataService:GetPlayerData():andThen(function(Data) -- get current players data whenever they join
		InventoryHandler.ReloadInventory(Data) -- refresh inventory
	end)
end


Inventory.Visible = false
return InventoryHandler
                        

Got hired short-term to make a pet simulator inventory and egg system.

Uncomplete Brainrot Game


-- Services
local Players = game:GetService("Players")
local Rep = game:GetService("ReplicatedStorage")
local Communication = Rep.Communication
local AllInventoryModules = {
	Eggs = "nil",
	Brainrots = "nil"
}

local RetrieveData = Communication.Player.RetrieveData
local ChangedData = Communication.Player.UpdateData

local ProfileStore = require(game.ServerStorage.Modules.ProfileStore)
local ProfileTemplate = {
	Cash = 0,
	Wave = 0,
	WaveSpeed = 1,
	Id = 0,
	Storage = {},
	MaxStorage = 6,
	Index = {},
	Inventory = {},
	LastLogin = 0
}

local PlayerDataStore = ProfileStore.New("PlayerData4", ProfileTemplate)
local Profiles : {[Player]: typeof(PlayerDataStore:StartSessionAsync())} = {}

local PlayerData = {}
PlayerData.Profiles = Profiles

function PlayerData:PlayerLoaded(Player : Player)
	local Profile = PlayerDataStore:StartSessionAsync(`{Player.UserId}`, {
		Cancel = function()
			return Player.Parent ~= Players
		end,
	})
	if Profile ~= nil then
		Profile:AddUserId(Player.UserId)
		Profile:Reconcile()

		Profile.OnSessionEnd:Connect(function()
			self.Profiles[Player] = nil
			Player:Kick(`Profile session end - Please rejoin`)
		end)

		if Player.Parent == Players then
			Profile.Data.Cash = 5000
			self.Profiles[Player] = Profile
			
			local FriendBoost = Instance.new("IntValue")
			FriendBoost.Value = 0
			FriendBoost.Name = "FriendBoost"
			FriendBoost.Parent = Player
			
			local SpawnPart = nil
			for _, Slot in pairs(workspace.Players:GetChildren()) do
				if Slot:GetAttribute("Occupied") == nil then
					Slot:SetAttribute("Occupied",Player.Name)
					SpawnPart = Slot:FindFirstChild("Spawn")
					Player:SetAttribute("Slot",Slot.Name)
					break
				end
			end
			
			for _, Friend in pairs(Players:GetPlayers()) do
				if Player:IsFriendsWith(Friend.UserId) and Friend ~= Player then
					Player:SetAttribute(Friend.Name,true)
					Friend:SetAttribute(Player.Name,true)
					--print(Player,"is friends with",Friend)
					FriendBoost.Value += 10
					Friend:WaitForChild("FriendBoost",2).Value += 10
				end
			end
			
			local Root = Player.Character:WaitForChild("HumanoidRootPart",1)
			if Root then
				Root.CFrame = SpawnPart.CFrame
			end
			for _, Item in pairs(Profile.Data.Inventory) do
				local Type = Item.Type
				if Type then
					if AllInventoryModules[Type] ~= nil then
						AllInventoryModules[Type].All[Item.Item]:InventorySpawn(Player,Item.Id)
					end
				end
			end
			
			Player.CharacterAdded:Connect(function(Character)
				local Root = Character:WaitForChild("HumanoidRootPart",1)
				
				if Root then
					Root.CFrame = SpawnPart.CFrame
				end
				
				for _, Item in pairs(Profile.Data.Inventory) do
					local Type = Item.Type
					if Type then
						if AllInventoryModules[Type] ~= nil then
							AllInventoryModules[Type].All[Item.Item]:InventorySpawn(Player,Item.Id)
						end
					end
				end
			end)
			print(Profile)
		else
			Profile:EndSession()
		end
	else
		Player:Kick(`Profile load fail - Please rejoin`)
	end
end

function PlayerData:PlayerRemoved(Player : Player)
	local Profile = self.Profiles[Player]
	Profile.Data.LastLogin = os.clock()
	
	local Success, Error = pcall(function()
		for _, Friend in pairs(Players:GetPlayers()) do
			if Friend:GetAttribute(Player.Name) == true then
				Friend:SetAttribute(Player.Name, nil)
				Friend.FriendBoost.Value -= 10
			end
		end
	end)
	
	for _, Slot in pairs(workspace.Players:GetChildren()) do
		if Slot:GetAttribute("Occupied") == Player.Name then
			Slot:SetAttribute("Occupied",nil)
			return
		end
	end
	
	if Profile ~= nil then
		Profile:EndSession()
	end
end

function PlayerData:ChangeData(Player : Player, Data : {})
	local Profile = self.Profiles[Player]
	if Profile ~= nil then
		for Key, Value in pairs(Data) do
			Profile.Data[Key] = Value
		end
	end
	
	self.Profiles[Player] = Profile
	ChangedData:FireClient(Player, Profile.Data)
end

function PlayerData:InsertValue(Player : Player, Key : string, Value : any)
	local Profile = self.Profiles[Player]
	
	if Key == "Inventory" then
		Value.Id = Profile.Data.Id + 1
		Profile.Data.Id += 1
		
		local Type = Value.Type
		if Type then
			if AllInventoryModules[Type] ~= nil then
				AllInventoryModules[Type].All[Value.Item]:InventorySpawn(Player,Value.Id)
			end
		end
	end
	if Profile ~= nil then
		table.insert(Profile.Data[Key],Value)
	end

	self.Profiles[Player] = Profile
	ChangedData:FireClient(Player, Profile.Data)
end

function PlayerData:RemoveValue(Player : Player, Key : string, Value : any, ExtraKey : string)
	local Profile = self.Profiles[Player]
	if Profile ~= nil then
		for Index, Val in pairs(Profile.Data[Key]) do
			if Val[ExtraKey] == Value then
				Profile.Data[Key][Index] = nil
			end
		end
	end
	
	self.Profiles[Player] = Profile
	ChangedData:FireClient(Player, Profile.Data)
end

function PlayerData:RetrieveData(Player : Player)
	return self.Profiles[Player]
end

function PlayerData:Load()
	for Key, _ in pairs(AllInventoryModules) do
		AllInventoryModules[Key] = require(Rep.Modules.ServerModules:FindFirstChild(Key))
	end
	
	
	for _, Player in pairs(Players:GetPlayers()) do
		self:PlayerLoaded(Player)
	end
	
	Players.PlayerAdded:Connect(function(Player)
		self:PlayerLoaded(Player)
	end)
	Players.PlayerRemoving:Connect(function(Player)
		self:PlayerRemoved(Player)
	end)
	RetrieveData.OnServerInvoke = function(Player)
		local Success, Data = pcall(function()
			return self:RetrieveData(Player).Data
		end)
		return if Success then Data else nil
	end
end

return PlayerData
                        

This is an uncomplete project for the group OwlVision, https://www.roblox.com/communities/33741749/OwlVision#!/about No longer work on this project as the owner quit.

Contact

Get in touch! Feel free to reach out through any of the platforms below.