I'm Hitchy — founder of HTC Games and a backend-focused Roblox scripter. I build the data layer, economy math, and security systems that idle games, sims, and Discord communities run on.
Roblox Studio for the game layer, Python for the community layer. Everything ships production-ready, not prototype-grade.
DataStores & ProfileStore, module-based architecture, remote security, tool physics, NPC/animation rigging, incremental & sim economies.
Multi-cog bot architecture, moderation & leveling systems, ticket/dropdown flows, translation pipelines, self-hosted deployment.
BigNumber math for idle scaling, prestige/rebirth loops, rarity & drop-rate tables, UI architecture within Lua's local-register limits.
A mix of shipped HTC Games titles, work in progress, and client commissions.
Idle/incremental Roblox game — 8 passive-mining aliens, a 7-tier prestige system with infinite ascension, rebirth sub-loop, and a 9-rarity rune system. Bioluminescent alien-tech visual direction.
HTC Games incremental — Max buy (x1/x10/Max), decillion-scale number formatting, promo codes, and click-feel juice (bounce, lucky-click flash, floating popups). Full charcoal/amber UI restyle.
Full production Discord.py bot for an ASA/ASE server — moderation, leveling/XP, security, a ticket system with map dropdowns, and a standalone translation sub-bot.
Brainrot egg-hatching simulator — 11 rarities, 55 named creatures, mutations, wave systems, and rebirth mechanics. Fixed critical economy and sell-system bugs across a large legacy codebase.
UI concepts and mockups — menu systems, panels, and in-game interface work.

Search, quick-action buttons, and a rarity-colored pet card grid with a bright cartoon UI style.

Mode-select cards with diagonal-shine gradients and hex pattern overlay, plus a Competitive toggle and player banner.
Shop, Inventory, Pets, Settings, and Trading buttons — cartoon icons on rounded halftone-textured gradient tiles.

Neon cyan-outlined dark UI with a scrollable row of rune cards — rarity tag, level, progress bar, and stat booster list.

Stacked quest cards with reward counter and progress bar, matching neon cyan glow style with a W/D toggle button.

Mode grid (1v1 through 5v5) with a larger Ranked/Unranked card and live queue count, diagonal texture matching the matchmaking panel.

Timed boost and starter bundle offers with cartoon icons, item preview slots, and gem-cost pricing on a halftone-textured card style.
Representative scripts showing how I structure server-authoritative systems, economy math, and bot logic. Full projects available on request.
Distance, angle, rate-limit, and position-mismatch checks for a melee combat system — the kind of pattern used in the Last Dance combat rewrite.
-- CombatValidator.lua
-- Server-authoritative hit validation for a melee/ranged combat system.
-- Rejects client-reported hits that fail distance, angle, or rate checks.
local RunService = game:GetService("RunService")
local CombatValidator = {}
CombatValidator.__index = CombatValidator
local MAX_HIT_DISTANCE = 12
local MAX_ANGLE_DEGREES = 65
local MIN_HIT_INTERVAL = 0.35 -- seconds, matches weapon swing cooldown
local lastHitTimestamps = {} -- [player.UserId] = os.clock()
function CombatValidator.new(config)
local self = setmetatable({}, CombatValidator)
self.hitboxPadding = config.hitboxPadding or 1.5
return self
end
function CombatValidator:IsRateLimited(attacker)
local last = lastHitTimestamps[attacker.UserId]
if last and (os.clock() - last) < MIN_HIT_INTERVAL then
return true
end
lastHitTimestamps[attacker.UserId] = os.clock()
return false
end
function CombatValidator:ValidateHit(attacker, target, reportedHitPosition)
local attackerRoot = attacker.Character and attacker.Character:FindFirstChild("HumanoidRootPart")
local targetRoot = target.Character and target.Character:FindFirstChild("HumanoidRootPart")
if not attackerRoot or not targetRoot then
return false, "MissingCharacter"
end
if self:IsRateLimited(attacker) then
return false, "RateLimited"
end
local distance = (targetRoot.Position - attackerRoot.Position).Magnitude
if distance > MAX_HIT_DISTANCE + self.hitboxPadding then
return false, "OutOfRange"
end
local toTarget = (targetRoot.Position - attackerRoot.Position).Unit
local facing = attackerRoot.CFrame.LookVector
local angle = math.deg(math.acos(math.clamp(facing:Dot(toTarget), -1, 1)))
if angle > MAX_ANGLE_DEGREES then
return false, "BadAngle"
end
-- Sanity-check the client-reported hit position against the target's
-- actual position, catching spoofed RemoteEvent payloads.
local reportedDelta = (reportedHitPosition - targetRoot.Position).Magnitude
if reportedDelta > self.hitboxPadding * 2 then
return false, "PositionMismatch"
end
return true
end
return CombatValidator
Mantissa/exponent number type for incremental games — keeps precision past 1e300+ and formats for display, same approach used in Alien Incremental's economy.
-- BigNumber.lua
-- Compact arbitrary-scale number type for idle/incremental economies.
-- Stores values as {mantissa, exponent} to stay accurate past 1e300+
-- without floating point precision loss, and formats for display.
local BigNumber = {}
BigNumber.__index = BigNumber
local SUFFIXES = { "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No" }
local function normalize(mantissa, exponent)
while mantissa >= 10 do
mantissa /= 10
exponent += 1
end
while mantissa < 1 and mantissa > 0 do
mantissa *= 10
exponent -= 1
end
return mantissa, exponent
end
function BigNumber.new(value)
local mantissa, exponent = normalize(value, 0)
return setmetatable({ mantissa = mantissa, exponent = exponent }, BigNumber)
end
function BigNumber.__add(a, b)
local diff = a.exponent - b.exponent
if math.abs(diff) > 15 then
return diff > 0 and a or b -- smaller value is negligible at this scale
end
local mantissa = a.mantissa + b.mantissa * (10 ^ -diff)
local m, e = normalize(mantissa, a.exponent)
return setmetatable({ mantissa = m, exponent = e }, BigNumber)
end
function BigNumber.__lt(a, b)
if a.exponent ~= b.exponent then
return a.exponent < b.exponent
end
return a.mantissa < b.mantissa
end
function BigNumber:ToString(decimals)
decimals = decimals or 2
if self.exponent < 3 then
return string.format("%." .. decimals .. "f", self.mantissa * 10 ^ self.exponent)
end
local suffixIndex = math.floor((self.exponent - 3) / 3) + 1
local suffix = SUFFIXES[suffixIndex] or ("e" .. self.exponent)
local shortMantissa = self.mantissa * 10 ^ ((self.exponent - 3) % 3)
return string.format("%." .. decimals .. "f%s", shortMantissa, suffix)
end
return BigNumber
Prevents double-claiming support tickets, with an auto-release on timeout — part of the ARK community moderation bot's ticket cog.
# ticket_claims.py
# Admin claim system for the ARK ticket bot — prevents two staff members
# from double-claiming the same ticket, with an auto-release on timeout.
import discord
from discord.ext import commands, tasks
from datetime import datetime, timedelta
CLAIM_TIMEOUT_MINUTES = 30
class TicketClaims(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.active_claims: dict[int, dict] = {} # channel_id -> {staff_id, claimed_at}
self.release_stale_claims.start()
def cog_unload(self):
self.release_stale_claims.cancel()
@commands.hybrid_command(name="claim", description="Claim this support ticket")
async def claim(self, ctx: commands.Context):
channel_id = ctx.channel.id
existing = self.active_claims.get(channel_id)
if existing and existing["staff_id"] != ctx.author.id:
claimer = ctx.guild.get_member(existing["staff_id"])
await ctx.send(
f"This ticket is already claimed by {claimer.mention if claimer else 'another staff member'}.",
ephemeral=True,
)
return
self.active_claims[channel_id] = {
"staff_id": ctx.author.id,
"claimed_at": datetime.utcnow(),
}
await ctx.channel.edit(name=f"claimed-{ctx.channel.name.split('-', 1)[-1]}")
await ctx.send(f"🎫 Claimed by {ctx.author.mention}.")
@commands.hybrid_command(name="unclaim", description="Release your claim on this ticket")
async def unclaim(self, ctx: commands.Context):
existing = self.active_claims.get(ctx.channel.id)
if not existing or existing["staff_id"] != ctx.author.id:
await ctx.send("You haven't claimed this ticket.", ephemeral=True)
return
del self.active_claims[ctx.channel.id]
await ctx.send(f"Ticket released by {ctx.author.mention}.")
@tasks.loop(minutes=5)
async def release_stale_claims(self):
cutoff = datetime.utcnow() - timedelta(minutes=CLAIM_TIMEOUT_MINUTES)
stale = [cid for cid, data in self.active_claims.items() if data["claimed_at"] < cutoff]
for channel_id in stale:
del self.active_claims[channel_id]
async def setup(bot):
await bot.add_cog(TicketClaims(bot))
Whitelists remote payload shapes, rate-limits requests per player, and flags speed/teleport anomalies — the anti-exploit layer that sits in front of gameplay logic.
-- ExploitGuard.lua
-- Server-side remote validation layer — sanity-checks every RemoteEvent
-- payload before it reaches gameplay logic, rejecting malformed or
-- speed-hacked client input.
local ExploitGuard = {}
ExploitGuard.__index = ExploitGuard
local MAX_WALKSPEED = 32
local TELEPORT_DISTANCE_THRESHOLD = 60 -- studs per heartbeat, flags fly/noclip
local requestLog = {} -- [player.UserId] = { count, windowStart }
function ExploitGuard.new(remoteWhitelist)
local self = setmetatable({}, ExploitGuard)
self.remoteWhitelist = remoteWhitelist -- table of { [remoteName] = expectedArgTypes }
self.lastPosition = {}
return self
end
function ExploitGuard:ValidatePayload(remoteName, player, ...)
local expected = self.remoteWhitelist[remoteName]
if not expected then
return false, "UnknownRemote"
end
local args = { ... }
if #args ~= #expected then
return false, "ArgCountMismatch"
end
for i, expectedType in ipairs(expected) do
if typeof(args[i]) ~= expectedType then
return false, "ArgTypeMismatch:" .. i
end
end
return self:CheckRateLimit(player, remoteName)
end
function ExploitGuard:CheckRateLimit(player, remoteName, limit, windowSeconds)
limit = limit or 20
windowSeconds = windowSeconds or 1
local key = player.UserId .. ":" .. remoteName
local entry = requestLog[key]
local now = os.clock()
if not entry or (now - entry.windowStart) > windowSeconds then
requestLog[key] = { count = 1, windowStart = now }
return true
end
entry.count += 1
if entry.count > limit then
return false, "RateLimitExceeded"
end
return true
end
function ExploitGuard:CheckMovementIntegrity(player)
local character = player.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
local root = character and character:FindFirstChild("HumanoidRootPart")
if not humanoid or not root then
return true -- character not loaded, nothing to validate yet
end
if humanoid.WalkSpeed > MAX_WALKSPEED then
return false, "SpeedModified"
end
local last = self.lastPosition[player.UserId]
self.lastPosition[player.UserId] = root.Position
if last and (root.Position - last).Magnitude > TELEPORT_DISTANCE_THRESHOLD then
return false, "SuspiciousTeleport"
end
return true
end
return ExploitGuard
Reusable tweened open/close controller for UI panels — debounced input and animation-state tracking so a menu can never get stuck half-open.
-- MenuController.lua
-- Reusable UI controller — tweened open/close, debounced input, and
-- automatic cleanup so panels never get stuck mid-animation.
local TweenService = game:GetService("TweenService")
local MenuController = {}
MenuController.__index = MenuController
local OPEN_TWEEN_INFO = TweenInfo.new(0.28, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
local CLOSE_TWEEN_INFO = TweenInfo.new(0.18, Enum.EasingStyle.Quint, Enum.EasingDirection.In)
local DEBOUNCE_SECONDS = 0.3
function MenuController.new(frame)
local self = setmetatable({}, MenuController)
self.frame = frame
self.isOpen = false
self.isAnimating = false
self.lastToggle = 0
self.frame.Visible = false
self.frame.Position = self.frame.Position + UDim2.new(0, 0, 0.05, 0)
self.frame.BackgroundTransparency = 1
return self
end
function MenuController:Open()
if self.isOpen or self.isAnimating then
return
end
self.isAnimating = true
self.isOpen = true
self.frame.Visible = true
local targetPosition = self.frame.Position - UDim2.new(0, 0, 0.05, 0)
local tween = TweenService:Create(self.frame, OPEN_TWEEN_INFO, {
Position = targetPosition,
BackgroundTransparency = 0,
})
tween.Completed:Once(function()
self.isAnimating = false
end)
tween:Play()
end
function MenuController:Close()
if not self.isOpen or self.isAnimating then
return
end
self.isAnimating = true
self.isOpen = false
local tween = TweenService:Create(self.frame, CLOSE_TWEEN_INFO, {
Position = self.frame.Position + UDim2.new(0, 0, 0.05, 0),
BackgroundTransparency = 1,
})
tween.Completed:Once(function()
self.frame.Visible = false
self.isAnimating = false
end)
tween:Play()
end
function MenuController:Toggle()
local now = os.clock()
if (now - self.lastToggle) < DEBOUNCE_SECONDS then
return -- ignore rapid double-clicks / spam taps
end
self.lastToggle = now
if self.isOpen then
self:Close()
else
self:Open()
end
end
return MenuController
Snippets are representative examples of systems I've built, written for portfolio display rather than pulled verbatim from client deliverables (which stay private). Happy to walk through real project code on a call.
Backend and systems work first — I can take a UI brief end to end too, but data, economy, and security are where I spend most of my time.
Priced in Robux. Final quote depends on scope — send a brief and I'll confirm a number before starting.