Development Portfolio
I am a programmer and scripter of 5 years, having worked in languages such as Luau, Java, and Python. I've mainly used my skills to create games and small projects, increasing my skill in designing and programming systems and mechanics. I've also worked for other games such as unLimited, massing around 1.2M visits overall.
I can write simple to complex, modular code for your game. Systems and sub-systems will be split into its own modules (unless asked to otherwise), with configs and proper documentation. If you would like to hire me, please refer to my contact page and message me on Discord (@saviornogame). I will give you a quote afterwards. Thank you!
I am willing to work on small to large systems. However, there are some exceptions, guidelines, and boundaries I want to be aware of.
ADVANCED SYSTEMS
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
--[=[
@author Haziel Cerroblanco (Aka. SaviorNoGame)
@version 1/30/2026
Handles the status of Players, relevant to the game itself.
@see StateService to get the Player's STATE HANDLER which affects their character state.
@see CharacterHandler to get the HANDLER tied to the player's character.
]=]
-- RESOURCES
local DataService = require(ServerScriptService.Services.DataService.Server)
local Signal = require(ReplicatedStorage.Objects.SignalPlus)
-- CLASS
local PlayerHandlerClass = {}
PlayerHandlerClass.__index = PlayerHandlerClass
PlayerHandlerClass.PlayerCache = {}
---------------------------------------------
--- CLASS FUNCTIONS
---------------------------------------------
type int = number
type PlayerHandlerObject = {
-- PROPERTIES
_Loaded: boolean,
_Data: any,
_Attackers: ,
Points: int,
IsPartcipating: boolean,
Team: Team?,
Player: Player,
Character: any,
-- EVENTS
OnKill: Signal.Signal,
OnHit: Signal.Signal,
CharacterChanged: Signal.Signal,
-- METHODS
Destroy: (_self: PlayerHandlerObject) -> nil,
AddPoints: (_self: PlayerHandlerObject, amount: int) -> nil,
GetLastAttacker: (_self: PlayerHandlerObject) -> Player?,
GetAttackers: (_self: PlayerHandlerObject) -> {Player},
AddAttackerData: (_self: PlayerHandlerObject, attacker: Player, amount: int) -> nil,
SetTeam: (_self: PlayerHandlerObject, team: Team) -> nil,
GetTeam: (_self: PlayerHandlerObject) -> Team,
IsLoaded: (_self: PlayerHandlerObject) -> boolean,
-- PROCESSORS
WhenCharacterChanging: () -> nil,
}
function PlayerHandlerClass.new(player: Player): PlayerHandler
local self = setmetatable({}, PlayerHandlerClass)
-- Properties
self.Player = player
self.Character = nil
self.Points = 0
self.IsParticipating = false
self.Team = nil
-- Private Properties
self._Data = DataService.PlayerData[player]
self._Attackers = {}
self._Loaded = false
-- Events
self.OnKill = Signal()
self.OnHit = Signal()
self.CharacterChanged = Signal()
-- Processors
self.WhenCharacterChanging = self.CharacterChanged:Connect(function()
self._Attackers = {}
end)
PlayerHandlerClass.PlayerCache[player] = self
return self
end
function PlayerHandlerClass.Get(src): PlayerHandler
return PlayerHandlerClass.PlayerCache[src]
end
---------------------------------------------
--- OBJECT FUNCTIONS
---------------------------------------------
function PlayerHandlerClass:Destroy()
self.OnKill:Destroy()
self.OnHit:Destroy()
end
function PlayerHandlerClass:AddPoints(amount: int)
self.Points += amount
self.Player.leaderstats.Points.Value = self.Points
end
--[=[
Retrieves the player who attacked most recently. However, it's possible none exist, and therefore it returns ```nil```.
@return Player?
]=]
function PlayerHandlerClass:GetLastAttacker(): Player?
local lastAttacker
local latest: number = 0
for i, v in self._Attackers do
if os.clock() - v.Time <= (os.clock() - latest) then
latest = v.Time
lastAttacker = Players:FindFirstChild(i)
end
end
return lastAttacker
end
function PlayerHandlerClass:GetAttackers(): {Player}
return self._Attackers
end
function PlayerHandlerClass:AddAttackerData(attacker: Player, damage: number)
local attackerData = self._Attackers[attacker]
if not attackerData then
self._Attackers[attacker] = {Time = 0, Damage = 0}
end
self._Attackers[attacker].Time = os.clock()
self._Attackers[attacker].Damage += damage
end
function PlayerHandlerClass:SetTeam(team: Team)
self.Player.Team = team
end
function PlayerHandlerClass:GetTeam(): Team?
return self.Player.Team
end
function PlayerHandlerClass:IsLoaded(): boolean
return self._Loaded
end
export type PlayerHandler = typeof(PlayerHandlerClass) & PlayerHandlerObject
return PlayerHandlerClass
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--[=[
@author Haziel Cerroblanco (Aka. SaviorNoGame)
@version 1/30/2026
This module controls State Machine objects used by the game to handle the state(s) of players
such as their current Action and current Bodily state.
The valid states are Enums.
]=]
--- RESOURCES
local Signal = require(ReplicatedStorage.Objects.SignalPlus)
--- CLASS
local StateMachineClass = {}
StateMachineClass.__index = StateMachineClass
--- ENUMS
StateMachineClass.ValidActionStates = {
IDLE = 'IDLE',
ACTIVE = 'ACTIVE',
SHIELDING = 'SHIELDING',
CASTING = 'CASTING',
STUNNED = 'STUNNED',
HITSTOP = 'HITSTOP',
DEAD = 'DEAD',
}
StateMachineClass.ValidBodyStates = {
NORMAL = 'NORMAL',
VULNERABLE = 'VULNERABLE', -- Able to be stunned by any attack.
SUPERARMOR = 'SUPERARMOR', -- Ignore all stuns.
INVINCIBLE = 'INVINCIBLE', -- Ignore everything except supports.
UNBOUND = 'UNBOUND', -- Ignore everything.
}
--- TRANSITIONS
StateMachineClass.ValidActionTransitions = {
IDLE = {'ACTIVE', 'DEAD'},
ACTIVE = '*',
SHIELDING = {'IDLE', 'ACTIVE', 'STUNNED', 'DEAD', 'HITSTOP'},
CASTING = {'IDLE', 'ACTIVE', 'STUNNED', 'DEAD', 'HITSTOP'},
STUNNED = {'IDLE', 'ACTIVE', 'DEAD'},
HITSTOP = {'IDLE', 'ACTIVE', 'STUNNED', 'DEAD'},
DEAD = {},
}
---------------------------------------------
-- CLASS FUNCTIONS
---------------------------------------------
type StateMachineObject = {
DEBUG: {LOG_ACTION_STATES: boolean, LOG_BODY_STATES: boolean, ActionLogs: {string}, BodyLogs: {string}},
ActionState: string,
BodyState: string,
ActionStateChanged: Signal.Signal,
BodyStateChanged: Signal.Signal,
}
-- Constructor
function StateMachineClass.new(): StateMachine
local self = setmetatable({}, StateMachineClass)
-- DEBUG PROPERTIES
self.DEBUG = {}
self.DEBUG.LOG_ACTION_STATES = false
self.DEBUG.LOG_BODY_STATES = false
self.DEBUG.ActionLogs = {}
self.DEBUG.BodyLogs = {}
-- Initialize states
self.ActionState = StateMachineClass.ValidActionStates.IDLE
self.BodyState = StateMachineClass.ValidBodyStates.NORMAL
-- Only 1 kept task can exist at a time. This is because more than 1 is unnecessary and uses excessive memory.
self._ActionKeptTask = nil
self._BodyKeptTask = nil
----------------------------- CHANGED EVENTS
self.ActionStateChanged = Signal()
self.BodyStateChanged = Signal()
self._ActionEnterEvents = {}
self._ActionLeaveEvents = {}
self._BodyEnterEvents = {}
self._BodyLeaveEvents = {}
self.ActionStateChanged:Connect(function(oldState, newState)
-- Fire all leave events for old state.
if self._ActionLeaveEvents[oldState] then
self._ActionLeaveEvents[oldState]:Fire(newState)
end
-- Fire all enter events for new state.
if self._ActionEnterEvents[newState] then
self._ActionEnterEvents[newState]:Fire(oldState)
end
end)
self.BodyStateChanged:Connect(function(oldState, newState)
-- Fire all leave events for old state.
if self._BodyLeaveEvents[oldState] then
self._BodyLeaveEvents[oldState]:Fire(newState)
end
-- Fire all enter events for new state.
if self._BodyEnterEvents[newState] then
self._BodyEnterEvents[newState]:Fire(oldState)
end
end)
---------------------------- CHANGED EVENTS END
return self
end
---------------------------------------------
-- OBJECT FUNCTIONS
---------------------------------------------
function StateMachineClass:Destroy()
self.ActionStateChanged:Destroy()
self.BodyStateChanged:Destroy()
end
function StateMachineClass:ChangeActionState(newState: string, associatedTask: thread?): boolean
assert(StateMachineClass.ValidActionStates[newState], 'Action state ['..newState..'] is not a valid state.')
-- "*" Indicated universal transition aka. can transition to any other state.
if StateMachineClass.ValidActionTransitions[self.ActionState] ~= '*' then
-- if RunService:IsStudio() then
assert(table.find(StateMachineClass.ValidActionTransitions[self.ActionState], newState),
'Current state ['..self.ActionState..'] cannot be transitioned into ['..newState..']')
-- elseif not table.find(StateMachineClass.ValidActionTransitions[self.ActionState], newState) then
-- return false
-- end
end
if self._ActionKeptTask then
task.cancel(self._ActionKeptTask)
self._ActionKeptTask = nil
end
if self.DEBUG.LOG_ACTION_STATES then
local log = 'Changed ['..self.ActionState..'] -> ['..newState..']'
if associatedTask then
log ..= ', Task Attached'
end
log ..= ' ('..os.clock()..')'
table.insert(self.DEBUG.ActionLogs, log)
end
self.ActionStateChanged:Fire(self.ActionState, newState)
self.ActionState = newState
self._ActionKeptTask = associatedTask
return true
end
function StateMachineClass:ChangeBodyState(newState: string, associatedTask: thread?): boolean
assert(StateMachineClass.ValidBodyStates[newState], 'Body state ['..newState..'] is not a valid state.')
assert(self.BodyState ~= newState, 'Body state cannot be changed into itself. ['..newState..']')
if self._BodyKeptTask then
task.cancel(self._BodyKeptTask)
self._BodyKeptTask = nil
end
if self.DEBUG.LOG_BODY_STATES then
local log = 'Changed ['..self.BodyState..'] -> ['..newState..']'
if associatedTask then
log ..= ', Task Attached'
end
log ..= ' ('..os.clock()..')'
table.insert(self.DEBUG.BodyLogs, log)
end
self.BodyStateChanged:Fire(self.BodyState, newState)
self.BodyState = newState
self._BodyKeptTask = associatedTask
return true
end
--- SIGNAL GETTERS
function StateMachineClass:GetActionStateEnteredSignal(state: string): Signal.Signal
assert(StateMachineClass.ValidActionStates[state], "Action state ["..state..'] is not a valid state.')
if not self._ActionEnterEvents[state] then
self._ActionEnterEvents[state] = Signal()
end
return self._ActionEnterEvents[state]
end
function StateMachineClass:GetBodyStateEnteredSignal(state: string): Signal.Signal
assert(StateMachineClass.ValidBodyStates[state], "Body state ["..state..'] is not a valid state.')
if not self._BodyEnterEvents[state] then
self._BodyEnterEvents[state] = Signal()
end
return self._BodyEnterEvents[state]
end
function StateMachineClass:GetActionStateLeftSignal(state: string): Signal.Signal
assert(StateMachineClass.ValidActionStates[state], "Action state ["..state..'] is not a valid state.')
if not self._ActionLeaveEvents[state] then
self._ActionLeaveEvents[state] = Signal()
end
return self._ActionLeaveEvents[state]
end
function StateMachineClass:GetBodyStateLeftSignal(state: string): Signal.Signal
assert(StateMachineClass.ValidBodyStates[state], "Body state ["..state..'] is not a valid state.')
if not self._BodyLeaveEvents[state] then
self._BodyLeaveEvents[state] = Signal()
end
return self._BodyLeaveEvents[state]
end
export type StateMachine = typeof(StateMachineClass) & StateMachineObject
return StateMachineClass
--Example of React code
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = game.Players.LocalPlayer
--[[
@author Haziel Cerroblanco (Aka. SaviorNoGame)
UI for the death screen. Right now, it only gives the player the option to respawn.
The camera will be following the person who eliminated them.
@version 6/12/2026
]]
local ReplicationService = require(ReplicatedStorage.Services.ReplicationService.Client) -- Using Packet networking library
local GuiService = require(ReplicatedStorage.Services.GuiService)
local React = require(ReplicatedStorage.Packages.React)
local ReactSpring = require(ReplicatedStorage.Packages["React-Spring"])
local DeathDecisionRemote = ReplicationService.GetRemote('DeathDecision')
DeathDecisionRemote = DeathDecisionRemote:Response(ReplicationService.Types.Boolean8)
local Module = {}
function Module.Spectate(props)
local spectating, setSpectating = React.useState(0)
local spectatedPlayer = React.useMemo(function()
if not props.Visible then
local camera = workspace.CurrentCamera
if not camera then return end
camera:SetAttribute('Override', nil)
return Players:GetPlayers()[spectating]
elseif spectating == 0 and props.Killer and props.Visible then
local camera = workspace.CurrentCamera
if not camera then return end
camera:SetAttribute('Override', props.Killer)
return nil
end
local player = Players:GetPlayers()[spectating]
if not player then return Players:GetPlayers()[spectating] end
local camera = workspace.CurrentCamera
if not camera then return Players:GetPlayers()[spectating] end
camera:SetAttribute('Override', player.Name)
return Players:GetPlayers()[spectating]
end, {spectating, props.Visible})
local eliminationText = spectatedPlayer and spectatedPlayer.Name or 'Eliminated by '..(props.Killer or 'Unknown')
local eliminationGraphemes, setEliminationGraphemes = React.useState(0)
React.useEffect(function()
if not props.Visible then return end
local conn = task.defer(function()
setEliminationGraphemes(0)
for i, _ in string.split(eliminationText, "") do
if _ == " " then continue end
task.wait(.1)
setEliminationGraphemes(i)
end
end)
return function()
pcall(task.cancel, conn)
end
end, {props.Visible, spectatedPlayer})
return React.createElement("TextLabel", {
Text = eliminationText,
TextScaled = true,
TextColor3 = Color3.new(1,0,0),
MaxVisibleGraphemes = eliminationGraphemes,
FontFace = Font.new('Code', Enum.FontWeight.SemiBold),
Size = UDim2.fromScale(1, .45),
BackgroundTransparency = 1,
}, {
React.createElement("TextButton", {
Text = "<",
TextScaled = true,
TextColor3 = Color3.new(1,1,1),
BackgroundColor3 = Color3.new(),
Size = UDim2.fromScale(.2, 1),
Position = UDim2.fromScale(-0.05, 0),
AnchorPoint = Vector2.new(1, 0),
[React.Event.Activated] = function()
local new = spectating - 1
if new <= 0 then
new = #game.Players:GetPlayers()
end
setSpectating(new)
end,
Visible = eliminationGraphemes == #string.split(eliminationText, ""),
}, {
React.createElement('UIStroke', {
Thickness = 1.2,
Color = Color3.new(1,1,1),
ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
}),
React.createElement("UIAspectRatioConstraint"),
}),
React.createElement("TextButton", {
Text = ">",
TextScaled = true,
TextColor3 = Color3.new(1,1,1),
BackgroundColor3 = Color3.new(),
Size = UDim2.fromScale(.2, 1),
Position = UDim2.fromScale(1.05, 0),
AnchorPoint = Vector2.new(0, 0),
[React.Event.Activated] = function()
local new = spectating + 1
if new > #game.Players:GetPlayers() then
new = 1
end
setSpectating(new)
end,
Visible = eliminationGraphemes == #string.split(eliminationText, ""),
}, {
React.createElement('UIStroke', {
Thickness = 1.2,
Color = Color3.new(1,1,1),
ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
}),
React.createElement("UIAspectRatioConstraint"),
}),
})
end
function Module.DeathTab(props)
local mouseHovering, setMouseHovering = React.useState(0)
local springProps = {}
for i=1, 2 do
table.insert(springProps, {
scale = if mouseHovering == i then 1.05 else 1,
})
end
local springs = ReactSpring.useSprings(2, springProps)
local timeLeft, setTimeLeft = React.useState(3)
React.useEffect(function()
if not props.Visible then return end
local conn = task.defer(function()
for i=2, 0, -1 do
task.wait(1)
setTimeLeft(i)
end
end)
return function()
pcall(task.cancel, conn)
end
end, {props.Visible})
return React.createElement("Frame", {
Size = UDim2.fromScale(.4, .15),
Position = UDim2.fromScale(.5, .9),
AnchorPoint = Vector2.new(.5, 1),
BackgroundTransparency = 1,
}, {
React.createElement('UIAspectRatioConstraint', {
AspectRatio = 3,
}),
React.createElement(Module.Spectate, props),
React.createElement('Frame', {
Size = UDim2.fromScale(1, .4),
Position = UDim2.fromScale(0, .6),
BackgroundTransparency = 1,
}, {
React.createElement('UIListLayout', {
VerticalAlignment = Enum.VerticalAlignment.Top,
HorizontalAlignment = Enum.HorizontalAlignment.Center,
FillDirection = Enum.FillDirection.Vertical,
Wraps = false,
Padding = UDim.new(0, 5),
SortOrder = Enum.SortOrder.LayoutOrder,
}),
React.createElement("TextButton", {
Text = timeLeft > 0 and tostring(timeLeft) or 'Respawn',
TextScaled = true,
TextColor3 = mouseHovering == 1 and Color3.new(0,0,1) or Color3.new(),
Size = UDim2.fromScale(.75, .8),
BackgroundTransparency = 1,
[React.Event.MouseEnter] = function()
setMouseHovering(1)
end,
[React.Event.MouseLeave] = function()
setMouseHovering(0)
end,
[React.Event.Activated] = function()
if timeLeft > 0 then return end
if DeathDecisionRemote:Fire() then
GuiService.SetUIState("InGame")
end
end,
}, {
React.createElement('UIStroke', {
Thickness = 1.2,
ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
Color = mouseHovering == 1 and Color3.new(0,0,1) or Color3.new(),
}),
React.createElement('UIScale', {
Scale = springs[1].scale,
}),
}),
-- React.createElement("TextButton", {
-- Text = 'Return To Lobby',
-- TextScaled = true,
-- TextColor3 = mouseHovering == 2 and Color3.new(1,0,0) or Color3.new(),
-- Size = UDim2.fromScale(.5, .8),
-- BackgroundTransparency = 1,
-- [React.Event.MouseEnter] = function()
-- setMouseHovering(2)
-- end,
-- [React.Event.MouseLeave] = function()
-- setMouseHovering(0)
-- end,
-- [React.Event.Activated] = function()
-- -- props.SetUIState('TitleScreen')
-- GuiService.SetUIState('TitleScreen')
-- end,
-- }, {
-- React.createElement('UIStroke', {
-- Thickness = 1.2,
-- ApplyStrokeMode = Enum.ApplyStrokeMode.Border,
-- Color = mouseHovering == 2 and Color3.new(1,0,0) or Color3.new(),
-- }),
-- React.createElement('UIScale', {
-- Scale = springs[2].scale,
-- }),
-- }),
}),
})
end
return function(props)
local spring, api = ReactSpring.useSpring(function() return {
transparency = .8,
} end)
local deathInfo, setDeathInfo = React.useState({})
props.Killer = deathInfo and deathInfo.Name or player.Name
React.useEffect(function()
local deathScreenPacket = ReplicationService.GetRemote('DeathScreen', ReplicationService.Types.Instance)
local con = deathScreenPacket.OnClientEvent:Connect(function(info)
setDeathInfo(info)
GuiService.SetUIState("DeathScreen")
end)
return function()
con:Disconnect()
end
end, {})
React.useMemo(function()
if props.Visible then
api.start({transparency = 1, config = {frequency = 2.5}})
else
api.start({transparency = .8, config = {frequency = 2.5}})
end
end, {props.Visible})
return React.createElement('Frame', {
Size = UDim2.fromScale(1, 1),
-- Position = UDim2.fromScale(.5, .5),
-- AnchorPoint = Vector2.new(.5,.5),
BackgroundTransparency = spring.transparency,
BackgroundColor3 = Color3.new(0.443137, 0, 0.603922),
-- BorderSizePixel = 5,
-- BorderColor3 = Color3.new(0,0,0),
Visible = props.Visible,
}, {
React.createElement(Module.DeathTab, {Visible = props.Visible, SetUIState = props.SetUIState, Killer = typeof(props.Killer) == "Instance" and props.Killer.Name or props.Killer})
})
end
My currently ongoing WIP game. It is a team-based 5v5 fighter based on the light novel series, Re:Zero.
Releases On: TBD
A small battlegrounds game implementing mechanics from Jujutsu Shenanigans. Development has stopped and the game is now open-source.
Released On: June 11th, 2024
A ability-grinder based off the series UnOrdinary by Uru-Chan. I worked as the Lead Scripter, having made many updates for the game.
Role: Lead Developer
or ~R$5,300 in Robux (after tax)
A standalone system or script (combat, ability framework, item, etc).
or ~R$26,500 in Robux (after tax)
Multiple systems or scripts working together (ability moveset, inventory + crafting, etc).
Please read, acknowledge, and accept these terms of service before requesting a commission.
Development Blogs
Behind the scenes explanation on how I developed a battlegrounds game from scratch.
Read