Welcome to My Portfolio
Exploring the digital universe
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.
Payment Methods Accepted
Percentage
Revenue share partnerships for long-term projects
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
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
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.