No sub-tasks.
Overview of the framework so a new scripter knows what is really built before quoting. Important: the README in the repo is a wishlist of planned systems, NOT a list of finished features. Based on reading the actual source. Each system marked as:
GitHub:
[https://github.com/CoffeeShoes/TenderFramework](https://github.com/CoffeeShoes/TenderFramework "smartCard-block")
The GitHub repo is the reference and source of truth for everything in this document. You need to be added to access it. Send Vizzun (CoffeeShoes) your GitHub username to be added as a collaborator. Always read the actual modules on GitHub; they are documented inline and override these notes if anything differs.
The framework is made through Rojo and stored on GitHub. The scripts in Studio are mostly a synced copy from GitHub.
Only these folders are framework-synced (everything else is free to edit in Studio):
Toolchain: Rokit. Rojo pinned at 7.6.1.
Project mapping (default.project.json): Source/Shared → ReplicatedStorage/Source, Source/Server → ServerScriptService/Source, Source/Client → StarterPlayerScripts/Source, plus Assets. Code is split Server / Client / Shared.
EconomyManager is real. Separate bank balance and hand (wallet) balance, both saved via PlayerDataSaver datastores. Get/Change/Increment methods for each. Has a security feature: only whitelisted scripts can change balances (blocks Adonis :s exploits). Client remotes for reading balances exist. What's missing vs your economy spec: deposit/withdraw/transfer flow, transfer rank-gating, the 5,000 hand cap and 1,000,000 bank cap, dropping wallet money on death, the 15-min team paycheck, and shop wiring. The backbone (storing + securing balances) is done; the gameplay around it is not.
JailManager is real. Sentences players with a duration, stores sentences in a MemoryStore hashmap, teleports jailed players to CrimeConfig.JAIL_POSITION on spawn, removes sentences, and fires a JailSentenceChanged remote to the client. Re-jails players who rejoin while serving. WarrantManager (158 lines) is also substantial. CrimeConfig, JailSentenceInfo, WarrantInfo, CivilLabourInfo, CrimeRemotes all defined. What's missing vs your arrest spec: the entire apprehension flow (cuff/grab/search proximity prompts, walking to station, the issuearrest/labourSentence UIs), the prison period clock (celltime/socialization/courtyard), work-points labour, and the records/search UIs. So jail backend exists, arrest gameplay + UI does not.
CivilLabourManager is an empty shell. It requires PlayerDataSaver and returns the table, but has NO methods. Nothing works yet.
The most complete system. Server CaptureSystem (164) + CapturePoint (219) have real logic. Client has CapturePointModel (204), CapturePointObserver, CaptureRemotesClientListener, animate. Shared config + info objects (CaptureSystemConfig, CapturePointInfo, progress, team info, RaidProgressInfo, CaptureRemotes). States (captured / under contest / spawnable) are modelled. This lines up best with your capture/power/reactor spec, though the reactor-specific mechanics (coal/coolant/meltdown/explosive) are your spec, not in here yet.
PlayerSpawner (server, 102). Client DeploymentGui (178) + SpawnPointBillboard (192). Shared SpawnConfig + spawn point info types + SpawnRemotes. The deployment board framework genuinely exists.
Faction (146), Team (144), FactionsConfig (72), PlayerTeamer, TeamRemotes. Client FactionsGui (141) + FactionJoinBox (104). Working.
Server Stat / PlayerStat / LeaderStats exist (PlayerStat is 120 lines, real). Client LeaderboardWindow is the biggest UI file (258). So in-game leaderboard is well along. Your global-stats spec (podium with rendered avatars, ACTIVITY/MONEY/KILLS tabs, Discord webhook) is NOT in here, that's new.
ToolManager only handles dropping a tool (parents it to workspace in front of you). ToolRemotes is tiny. Client ToolDragger (180) + HotbarWindow (569, the biggest client file) handle the hotbar. So hotbar/drag is built, but the inventory system with weight limits, vehicle inventories, search, pickpocket etc. (your inventory spec) is NOT built.
Client Gun + GunController, shared GunInfo, AvailableGuns (only ~23 lines so few guns defined), Projectile (135), gun + machine gun animation controllers. Shooting framework exists; the weapon loadout/quota/shop/supply systems (your weapon spec) are NOT here.
Only StreetCleaningRemotes exists: a single CleanGarbagePile remote, no manager, no logic. So the sweeper job is just a remote stub. Logging/mail/carpentry jobs: [PLANNED] only.
PlayerDataSaver (118), DataStoreAccessPoint (92), AccessPoints, GlobalDataStoreKey. Real saving/loading backbone. (test.luau is a dev scratch file and references a wrong module name "PlayerStatSaver" — ignore it.)
A half-made "GroupBotPoolConfig" class with empty New/Destroy. Looks like a leftover scratch file. Ignore.
Client UI is substantial: UIController, HudGui, and windows for Hotbar, Capture, CapturePointIcon, Info, Keybinds, Leaderboard, Map, Notifications. Plus LoadingScreenGui, FactionsGui, CustomProximityPrompts (139), UIFeedback, UISounds, UITypes. Platform handling (console/desktop/mobile), LocalSounds.
Notifications — this IS the "universal notification UI" your specs keep referencing.
```
local Notification = require(".../Source/Client/UI/Hud/Notification");
local NotificationsWindow = require(".../Source/Client/UI/Hud/NotificationsWindow");
local n = Notification.New("Paycheck received!", "Go to the bank to cash it in!", 12);
NotificationsWindow.ShowNotification(n);
```
New(title, description, timeOnScreen? = 10, soundOverrideAssetId?). Closes on click.
Keybinds — this is the "keybind universal UI" from your radio/weapon specs.
```
local Keybind = require(".../Source/Keybind");
local key = Keybind.New(consoleInput, desktopInput, mobileInput, holdDuration);
local KeybindsWindow = require(".../Source/UI/Hud/KeybindsWindow");
KeybindsWindow.ShowKeybind(key, "Reload weapon");
```
holdDuration 0 = click, higher = hold. Nil platform input = activate via the HUD keybind guide.
Genuinely built: capture system, deployment/spawns, teams/factions, datastores, economy backbone (balances only), jail backend, the HUD/UI layer (notifications, keybinds, hotbar, leaderboard window, proximity prompts).
Skeletons / stubs only: civil labour, tool/inventory beyond dropping, street-cleaning (one remote).
Not started (your specs = new work): the full arrest apprehension flow + arrest UIs, prison period clock + work labour, records/warrant UIs, inventory weight/vehicle/search/pickpocket, weapon loadout/quota/shop/supply, hotel, apartments/furniture, radio, cutscene/dialogue + AI NPC, reactor mechanics (coal/coolant/meltdown/explosive), global stats podium + webhook, and the logging/mail/sweeper jobs.
So a contractor builds new gameplay systems ON TOP of a real but partial framework, reusing the notification + keybind + proximity-prompt + capture + economy-balance + jail backends rather than rebuilding them. Coordinate with Vizzun before touching framework-synced folders; keep standalone scripts outside those folders.
(Map reference: Misc/MapReference.png and Misc/Map.psd.)
These notes are a convenience. The GitHub repo is the source of truth (
[https://github.com/CoffeeShoes/TenderFramework](https://github.com/CoffeeShoes/TenderFramework "smartCard-block")
). If anything here disagrees with the code, the code wins. Read the actual modules; they're documented inline.
All paths below are inside the Rojo-synced source. Client modules live under StarterPlayer/StarterPlayerScripts/Source/..., shared under ReplicatedStorage/Source/....
This is the "universal notification UI" referenced across the specs. Client-side. Don't rebuild it.
Modules:
Source/Client/UI/Hud/Notification (single notification class)Source/Client/UI/Hud/NotificationsWindow (the window)Normal use:
```
local Notification = require("@game/StarterPlayer/StarterPlayerScripts/Source/UI/Hud/Notification");
local NotificationsWindow = require("@game/StarterPlayer/StarterPlayerScripts/Source/UI/Hud/NotificationsWindow");
local n = Notification.New("Person arrested!", "You have been handcuffed.", 12);
NotificationsWindow.ShowNotification(n);
```
Notification.New(title, description, timeOnScreen?, soundOverrideAssetId?):
timeOnScreen nil = stays until clicked (no auto-dismiss). A number = auto-hides after that many seconds. Always dismissable by clicking.soundOverrideAssetId = optional custom sound, else default.So for specs that say "notification that doesn't go away until you press X" (arrest cuff notice, buy-out, pickpocket red alert), pass timeOnScreen = nil and keep the object so you can :Hide() it later:
```
local cuffNotice = Notification.New("YOU HAVE BEEN HANDCUFFED", "Press to dismiss.");
NotificationsWindow.ShowNotification(cuffNotice);
-- when uncuffed:
cuffNotice:Hide();
```
NotificationsWindow methods: ShowNotification(n), Enable(), Disable() (hide the whole window, useful for "no other UI when loadout active"), GetWindow(), Initialise() (wires the built-in join/leave notices).
Notification object methods if you need manual control: :GetInstance(), :Show(onFinished?), :Hide(onFinished?), :Destroy().
Server code can't call this directly. Fire a remote to the client, then show it there (JailManager already does this with JailSentenceChanged).
This is the "keybind universal UI" from the radio and weapon-loadout specs. Use the Keybind class so input is handled consistently and can be shown on the HUD.
Modules: Source/Client/Keybind, Source/Client/UI/Hud/KeybindsWindow.
Make a keybind:
```
local Keybind = require("@game/StarterPlayer/StarterPlayerScripts/Source/Keybind");
-- (consoleKey, desktopKey, mobileKey, holdDuration, ignoreGameProcessed?)
local key = Keybind.New(Enum.KeyCode.ButtonY, Enum.KeyCode.R, nil, 0);
key:GetTriggeredSignal():Connect(function()
-- run the action
end);
```
holdDuration = 0 = press/click. > 0 = must hold that many seconds before it triggers.ignoreGameProcessed = true makes it still fire even if Roblox marks the input as game-processed (e.g. while typing). Default false.Useful object methods: :GetTriggeredSignal(), :GetHoldBeganSignal(), :GetDestroyingSignal(), :GetHoldInfo() (HoldDuration / TimeHeld / TimeRemaining, good for radial fill), :IsHolding(), :GetPlatformKeyName(), :ForceTrigger(), :ForceHoldBegin() / :ForceHoldEnd(), :Destroy().
Show it in the HUD guide:
```
local KeybindsWindow = require("@game/StarterPlayer/StarterPlayerScripts/Source/UI/Hud/KeybindsWindow");
KeybindsWindow.ShowKeybind(key, "Toggle radio");
```
The second arg is the label shown in the guide. The button also lets the player trigger the keybind by clicking/holding it (handles the hold-fill animation for you). KeybindsWindow.HideKeybind(key) removes it. Enable() / Disable() toggle the whole guide.
For the radio "RADIO ON / OFF" button or any keybind-driven toggle, this is the path: make a Keybind, connect its triggered signal, and ShowKeybind it.
The arrest cuff/grab/search prompts, vehicle prompts, hotel/apartment door prompts etc. all use ProximityPrompts. The framework auto-skins any prompt set to Custom style.
Module: Source/Client/UI/ProximityPrompts/CustomProximityPrompts (call Initialise() once; it already runs in the client session).
How it works: any ProximityPrompt in workspace with Style = Enum.ProximityPromptStyle.Custom automatically gets a custom billboard + keybind button. It reads the prompt's ActionText, KeyboardKeyCode / GamepadKeyCode, and HoldDuration, and shows the platform key name with a hold-fill animation. Sounds play on shown/hold/triggered.
So to add a prompt for a spec: create a ProximityPrompt, set its Style to Custom, set ActionText (e.g. "HANDCUFF"), set HoldDuration if it should be a hold, and handle prox.Triggered on the server or client as normal. The UI is handled for you. Multiple prompts on the same part share one billboard, which is how "prompts divided by seat" / multiple prompts on a player work.
The jail backend is real (JailManager, WarrantManager). Talk to it through ReplicatedStorage/Source/Crime/CrimeRemotes:
Server -> Client events:
WantedStatusChanged (isWanted: boolean)JailSentenceChanged (JailSentenceInfo) — fired when a player is jailed or their sentence changes.Client -> Server functions:
IsWanted() -> booleanGetUserWarrants(userId) -> { WarrantInfo }GetUserJailSentence(userId) -> JailSentenceInfo? (errors "ClientNotPermitted" if the caller lacks permission)Server API (server scripts): JailManager.SentencePlayer(jailedUserId, durationInSeconds, jailingUserId, reason?, dontCreateWarrant?), JailManager.GetSentence(userId), JailManager.RemoveSentence(jailedUserId). Jailed players are teleported to CrimeConfig.JAIL_POSITION and re-jailed on rejoin.
So the arrest UIs you build should call into this existing jail/warrant backend rather than making a new one. The apprehension flow, prison clock, and records UIs are what's missing.
ServerScriptService/Source/Economy/EconomyManager (server). Two balances per player, both datastore-saved:
GetBankBalance(player), ChangeBankBalance(player, amount), IncrementBankBalance(player, increment)GetHandBalance(player), ChangeHandBalance(player, amount), IncrementHandBalance(player, increment)Security: only scripts in the whitelist inside EconomyManager can change balances (blocks Adonis :s exploits). If you add a new system that pays players (jobs, paycheck, shops), it must be whitelisted there, or call through Main.
Client reads balances via ReplicatedStorage/Source/Economy/EconomyRemotes (GetBankBalance / GetHandBalance invokes).
What's missing vs the economy spec: deposit/withdraw/transfer UI + rank-gating, the 5,000 / 1,000,000 caps, wallet-drop-on-death, the 15-min paycheck, and shop wiring. Build those on top of these methods.
ReplicatedStorage/Source/Capture/CaptureRemotes + CaptureSystemConfig; server CaptureSystem / CapturePoint. States and progress already modelled.ReplicatedStorage/Source/Spawns/SpawnRemotes + SpawnConfig; server PlayerSpawner; client DeploymentGui.ReplicatedStorage/Source/Teams/TeamRemotes + FactionsConfig; server PlayerTeamer.The reactor mechanics in the capture/power/reactor spec (coal, coolant, meltdown, explosive, city-falling) are new and would extend the capture system, not replace it.
No extra pictures yet.