← Roblox portfolio

Case study / Roblox gameplay systems

Nightmart

A published Roblox horror game built around one question: is the person at your window who their license says they are? This page explains the systems I wrote so the server can own the answer and the player can still check it for themselves.

Play Nightmart ↗ Hire me for Roblox work
Game
Nightmart, published on Roblox by @Jetvak009
Developer
Parker Fawcett
Scope
27 Luau scripts, about 5,300 lines, plus the open-source ProfileStore library
Stack
Luau, Roblox Studio, RemoteEvents, ViewportFrame and WorldModel rendering, ProfileStore
Credits
ProfileStore (MAD STUDIO, Apache-2.0) provides session locking and autosave. Base customer and store meshes were generated with Roblox Studio's AI tools. Sound effects come from Roblox's Creator Store audio library.

01 / The game

An observation-horror loop

You work alone at a night window. Each round a customer arrives with a license. You compare their face with the license, review the entry camera tape, serve the people who are who they say they are, and close the window on anything that is not. You rise from trainee to keeper, take on stranger closing duties, and try to keep your mistakes low. The first round is untimed so new players can learn the rules, and gear is earned through play.

Nightmart inspection screen showing a customer's live face next to the still photo on their license
The core interaction: the live customer on the left, the license photo on the right. A still photo cannot show movement, so some tells are only visible live.

02 / The engineering problem

One customer, several kinds of evidence

The game only works if a customer is the same person everywhere the player looks: moving at the window, in the still license photo, on the entry camera, and again in the review shown after a mistake. At the same time, each anomaly has to be visible in the right place. A twitching neck cannot show up in a still photo, and a printed-sideways eye cannot show up in live movement. And none of it can be decided by the client: the server has to own the answer, the progression, and the money.

03 / System one

Server-owned rounds and staged rewards

Every player gets a server-side session that owns the current customer, which clues are real, the round phase, and the rewards earned so far. The client sends small, constrained actions; the server checks them against the round state and the player's position before changing anything, then publishes a snapshot that drives what the player sees.

Rewards are escrowed. Money earned during a round sits in a pending balance and is only committed to the player's saved cash when the round succeeds and the required store work is finished. A failed round discards what was never committed. Saved cash, gear and captures are kept separate from pending round rewards, and the schedule of rounds, work duties and closing encounters is generated from a saved seed.

Nightmart payday screen listing orders, captures, store work and bonuses, with paid and saved cash totals
Payday: pending rewards are tallied from the round, then committed to saved cash once the round is complete.

04 / System two

Evidence that stays consistent across views

A customer's identity and movement come from a seed. The same seeded pose plan is used for ordinary customers and anomalous ones, so posing alone does not give an anomaly away, and each of the 12 anomaly types is only drawn in the channel where it belongs. The license is a frozen still. The entry camera replays the customer's arrival from fixed camera geometry. After a wrong decision, the review screen reuses the same identity and the same clue channel to show the player exactly what they missed, including an ordinary reference when the customer was fine.

Nightmart security camera view of a customer entering the store, with camera selection and replay controls
Entry tape: the arrival is reconstructed from game state and can be replayed, zoomed, and switched between cameras.
Nightmart review screen comparing the player's customer with an ordinary reference after a mistaken decision
Mistake review: the same customer is shown beside an ordinary reference so the player can see what was and was not a tell.

A deliberate tradeoff. The entry tape is a reconstruction, not recorded footage. Rebuilding the arrival from the seed keeps replays identical and per-player, and avoids storing frames. The cost is that the client receives enough information to draw the clue, which is a design choice I made knowingly.

05 / System three

Data-driven anomalies and a repeat-free schedule

The 12 anomaly types live in one data table, each with a visual style, the clue text shown in review, and the number of completed rounds needed to unlock it, so new tells are added by data rather than by rewriting the round logic. Contracts and ranks unlock the same way.

Work duties are scheduled from the seed like a shuffled deck: every option is used before any repeats, and the boundary between two shuffles is swapped when needed so the last night of one group never repeats as the first of the next.

-- Each saved schedule uses every option before reshuffling. Boundary swaps keep
-- the last night of one group from immediately repeating in the next group.
local function workDeck(count,block,seed,salt)
 local function shuffled(index)
  local rng=Random.new(((seed or 0)+index*104729+salt)%2147483647)
  local deck={};for i=1,count do deck[i]=i end
  for i=count,2,-1 do local j=rng:NextInteger(1,i);deck[i],deck[j]=deck[j],deck[i] end
  return deck
 end
 local deck=shuffled(block)
 if block>0 and deck[1]==shuffled(block-1)[count] then deck[1],deck[2]=deck[2],deck[1] end
 return deck
end

Shared.luau: seeded shuffled-deck scheduling. The same seed always produces the same schedule.

06 / Supporting systems

Saving, audio and testing

Save data

Player data is defined by a normalized schema with defaults and validation, loaded through ProfileStore for session locking and autosave. If saved data fails validation, the load fails closed instead of accepting a corrupted profile, and captures are synced append-only. The locking engine is ProfileStore's work; the schema, validation and integration are mine.

Audio

A small audio module handles bounded one-shot sounds, bus mixing, preloading guarded by an epoch so stale loads are ignored, and cues driven by game state.

Testing

Game logic is covered by isolated Luau fixtures that run with simulated time and fake player data: server rule checks, a seeded 20-round campaign simulation, and the review screen exercised across all 12 anomaly types and an ordinary reference in four layouts. These tests cover game logic. They are not a substitute for live multiplayer or cloud-save testing.

07 / Work with me

Need Luau gameplay systems?

I am open to well-scoped Roblox scripting contracts: round and state logic, progression and economy, UI and camera systems, and save-data integration.