Development Portfolio

About Me

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!

Scope

I am willing to work on small to large systems. However, there are some exceptions, guidelines, and boundaries I want to be aware of.

  • Combat, hitboxes, abilities, dashes, movement
  • DataStores, saving systems, in-game data management, leaderboards
  • UI, menus, settings
  • Items, pickups, interactables
  • Mini-games, round-based modes
  • Simple building involved
  • Placeholder animations involved (low-quality)

ADVANCED SYSTEMS

  • Enemy AI, state machines/managers, NPCs
  • Custom classes and objects
  • Ability/combat frameworks
  • React-Lua UI coding
  • Custom Networking Implementation (i.e. Packet, ByteNet, etc.)

  • Custom physics
  • Realistic Vehicle Systems
  • Multi-subsystem systems without notice
  • Libraries from scratch (like Promise or Signal)

What I've Made

Code Samples

 
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 Games

Placeholder

Re:Theatre

My currently ongoing WIP game. It is a team-based 5v5 fighter based on the light novel series, Re:Zero.

Releases On: TBD

Placeholder

Re:Battlegrounds

A small battlegrounds game implementing mechanics from Jujutsu Shenanigans. Development has stopped and the game is now open-source.

Released On: June 11th, 2024

Games I've Worked On

Placeholder

unLimited

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

Want To Hire Me?

...
Single System
From $20 USD

or ~R$5,300 in Robux (after tax)

A standalone system or script (combat, ability framework, item, etc).

  • Revisions included
  • Completion in 2-6 days
...
Multi System
From $100 USD

or ~R$26,500 in Robux (after tax)

Multiple systems or scripts working together (ability moveset, inventory + crafting, etc).

  • 2-4 full systems
  • User Guide included
  • Revisions included
  • Completion in 1-3 weeks

Terms of Service

Please read, acknowledge, and accept these terms of service before requesting a commission.

Contact Me

Development Blogs

...
Developing Re:Battlegrounds

Behind the scenes explanation on how I developed a battlegrounds game from scratch.

Read