Abstract
A Roblox social-deduction party game for 8–12 players. Secret agents run odd missions (stand under trees, walk backwards, dodge CCTV) while civilians try to spot who's acting off. Wrong accusations help the agents; right ones take them out. Side activities, emergency meetings, cosmetics, and progression — Lua synced via Rojo.
Highlights
- —8–12 player social deduction with agent missions and civilian investigations
- —Emergency meetings, cosmetics, progression, and side activities
- —Full Roblox Studio + Rojo toolchain — works on install for local testing
README
View on GitHubSuspicious
ok so this among us type game called sus or smth idk, a social deduction party game for roblox. 8–12 players spawn into a detailed city. a few of you are secret agents with weird missions (stand under trees, walk backwards, ring bells, dodge cctv). everyone else is a civilian trying to spot who's acting off. wrong accusations help the agents. right ones take them out. there's side activities so everyone looks busy, emergency meetings, cosmetics, progression, the whole thing. if you know what to do to make this more interesting and stuff, we can do that, and feel free to change anything, add anything, or let me know what needs to be changed code wise to make stuff work if it doesnt. it should work on installation and import into roblox studio, but lmk if you need help.
this readme is your install + test guide. you'll also see a quick intro panel when you first join in-game. test mode is on by default (solo lobbies work). set TestMode = false in GameConfig.lua before you ship.
Quick Start
Prerequisites
- Roblox Studio (latest version)
- Rojo 7.x — syncs this codebase into Studio
- Aftman (optional, for tool management)
Installation
# Clone the repository
git clone <your-repo-url> suspicious
cd suspicious
# Install Rojo (via Aftman)
aftman install
# Start Rojo sync server
rojo serve
# Open Roblox Studio → Rojo plugin → Connect to localhost:34872
Once connected, all scripts and map modules sync into your place automatically.
First Test
- Open Studio with Rojo connected
- Press Play (F5) — you need 8+ players for a full round, or temporarily lower
MinPlayersinGameConfig.luafor solo testing - Watch the Output window for
[Suspicious] Server initialized
Gameplay Flow
Lobby (wait for 8–12 players)
↓ countdown
Starting (10s grace period, roles assigned)
↓
Active (8 min round — missions, accusations, events)
↕ emergency meetings (max 2/round)
Results → Intermission → Lobby
Roles
| Role | Objective | Special |
|---|---|---|
| Agent | Complete all secret missions before timer expires | Knows other Agents; gets 3–5 randomized missions |
| Civilian | Identify and eliminate all Agents | 2 accusation tokens per round; can call emergency meetings |
Agent Count by Lobby Size
| Players | Agents |
|---|---|
| 8–9 | 1 |
| 10–11 | 2 |
| 12 | 3 |
Win Conditions
- Agents win if all living Agents complete their missions, OR timer expires with Agents alive
- Civilians win if all Agents are eliminated (accusations or meeting votes)
Project Architecture
src/
├── ReplicatedStorage/
│ ├── Packages/ # Promise, Signal (internal utilities)
│ └── Shared/
│ ├── Config/ # All tunable game balance (EDIT THESE)
│ ├── Constants.lua # Enums, tags, remote names
│ ├── Types.lua # Luau type definitions
│ ├── Util/ # RandomUtil, TableUtil, PlayerUtil
│ ├── Network/ # RemoteRegistry
│ └── Map/ # MapModule registration
│
├── ServerScriptService/
│ └── Server/
│ ├── init.server.lua # Server boot
│ ├── MapLoader.server.lua
│ ├── ServiceLoader.lua # Dependency injection
│ └── Services/ # All server-authoritative logic
│ ├── GameService.lua # Round orchestrator
│ ├── RoleService.lua # Agent/Civilian assignment
│ ├── MissionService.lua # Secret mission tracking
│ ├── AccusationService.lua # Accusation tokens & rewards
│ ├── MeetingService.lua # Emergency meetings & voting
│ ├── SideActivityService.lua
│ ├── WorldEventService.lua
│ ├── SecurityService.lua # CCTV, guards, suspicion
│ ├── MapService.lua # District boundary tracking
│ ├── ProgressionService.lua
│ ├── DataService.lua # DataStore persistence
│ └── MonetizationService.lua
│
├── StarterPlayer/
│ └── StarterPlayerScripts/
│ └── Client/
│ ├── init.client.lua
│ └── Controllers/
│ ├── UIController.lua
│ ├── MissionController.lua
│ ├── InputController.lua
│ └── ProgressionController.lua
│
├── StarterGui/UI/ # Designer-editable UI assets
├── Workspace/
│ ├── Map/Modules/ # Modular map pieces (designer-built)
│ └── GameSystems/ # Meeting table, CCTV, security room
└── ServerStorage/Assets/ # Server-only assets
Design Principles
- Server authority — all gameplay logic runs on the server; clients display state only
- Config separated from code — balance changes go in
Shared/Config/without touching services - Modular services — each system is a self-contained ModuleScript registered via ServiceLoader
- CollectionService tags — map interactables use tags, not hardcoded part names
- No gameplay monetization — all Robux purchases are cosmetic-only
Extension Points
Adding a New Mission
Edit src/ReplicatedStorage/Shared/Config/MissionConfig.lua:
{
Id = "my_new_mission",
Name = "Mission Display Name",
Description = "What the Agent must do.",
Category = Constants.MissionCategory.Movement, -- Movement, Interaction, Social, Environmental, Timed
Difficulty = 2, -- 1 (easy) to 5 (hard)
Weight = 8, -- Higher = more likely to be picked
Duration = 30, -- Optional: seconds required
TargetTag = "Tree", -- Optional: CollectionService tag
TargetDistrict = "Park", -- Optional: district name
RequiredAction = "StayNear", -- Action type (see MissionService)
Target = 1, -- Optional: count required (default 1)
},
If the mission needs new tracking logic, add a handler in MissionService:StartTracking().
Adding a Side Activity
Edit src/ReplicatedStorage/Shared/Config/SideActivityConfig.lua and tag an interactable part in Studio with Interactable + SideActivityId attribute.
Adding a World Event
Edit src/ReplicatedStorage/Shared/Config/EventConfig.lua. Implement effect application in WorldEventService:_applyEffects().
Adding Cosmetics
Edit src/ReplicatedStorage/Shared/Config/CosmeticConfig.lua. Set real AssetId values from the Roblox catalog.
Adding Map Districts
See Designer Setup Guide below.
Adding NPCs
- Place NPC models under
Workspace/Map/Modules/<Module>/NPCs/ - Tag with
NPCGuardfor patrolling guards - Guards automatically increase suspicion for running players nearby
Controls
| Key | Action |
|---|---|
| E | Interact with nearest tagged object |
| M | Call emergency meeting |
Progression Systems
- Experience & Levels — earn XP from wins, missions, accusations, side activities
- Coins — soft currency for cosmetic purchases
- Achievements — milestone rewards (config in
ProgressionConfig.lua) - Daily/Weekly Challenges — rotating objectives with coin/XP rewards
- Seasonal Battle Pass — tier-based cosmetic unlocks (no gameplay advantage)
- Titles & Trophies — unlockable profile decorations
- Statistics — wins, accusations, distance, MVP awards, favorite mission type
Monetization (Cosmetic Only)
Configured in CosmeticConfig.lua:
- Hats, hairstyles, jackets, backpacks
- Emotes, idle animations, victory poses
- Trails, spawn effects, profile banners
- Trophy room decorations
- Gamepasses: VIP Pass (extra emote slot, lobby badge), Starter Bundle
- No item provides gameplay advantage
Set GamepassId and AssetId values in Studio after creating gamepasses/catalog items.
Designer Setup Guide (For Non-Programmers)
This section is written for artists and level designers who will never touch code.
Opening the Project
- Install Roblox Studio from create.roblox.com
- Ask a programmer to run
rojo serveand connect Studio via the Rojo plugin - The entire game appears in Studio automatically — you edit visually
Editing the Map
The map is built from modular pieces in Workspace → Map → Modules. Each module is a self-contained district chunk.
Creating a New Map Module
- In Studio, go to
Workspace → Map → Modules - Duplicate an existing module (e.g.,
PlazaModule) or create a new Model - Set these Attributes on the Model (Properties panel → Attributes):
DistrictName— e.g.,"Cafe","Museum","Market"ModuleId— unique ID, e.g.,"cafe_01"
- Add these child folders (create empty Folders):
DistrictZone— a large invisible Part defining the district boundaryLandmarks,Trees,Benches,Bells,Fountains,Interactables,SpawnPoints
- Place your 3D assets inside the appropriate folders
- Move/rotate the module to position it in the world
Tagging (Automatic)
When the game starts, MapLoader automatically tags parts based on their parent folder:
- Parts in
Trees/→ taggedTree - Parts in
Benches/→ taggedBench - Parts in
Landmarks/→ taggedLandmark - Parts in
Interactables/→ taggedInteractable
You do not need to add tags manually if you use the correct folder names.
Making Something Interactable
- Place the part in the
Interactablesfolder - Add an Attribute called
SideActivityIdwith value like"vending_snack"(ask programmer for valid IDs) - Players press E near it to interact
District Zones
The DistrictZone part must be:
- A Part (not a MeshPart)
- Large enough to cover the entire district
- Set to Anchored, CanCollide = false, Transparency = 0.9 (invisible in-game)
Replacing Visual Assets
- Find the part or model in the Explorer panel
- Change its Color, Material, or replace with your own Mesh/Model
- Keep the part in the same folder so tags still work
- Do not rename folders like
TreesorLandmarks
Adding CCTV Cameras
- Go to
Workspace → GameSystems → CCTVCameras - Duplicate an existing camera Part
- Position it where you want surveillance coverage
- Cameras within 30 studs of an Agent increase suspicion
Testing Locally
- Press F5 (Play) in Studio
- To test alone, ask a programmer to temporarily set
MinPlayers = 1in GameConfig - Check the Output window for errors (View → Output)
- Press Shift+F5 to stop
Publishing Updates
- File → Publish to Roblox
- Add a version note describing your changes
- Click Publish
- Test on the live game before announcing to players
What NOT to Edit
| Do NOT touch | Why |
|---|---|
ServerScriptService/Server/ | Game logic — breaks exploits protection |
StarterPlayerScripts/Client/ | Client code |
ReplicatedStorage/Shared/Config/ | Balance numbers — ask programmer |
| Script files inside models | Auto-generated |
What You CAN Edit
| Safe to edit | Examples |
|---|---|
Workspace/Map/Modules/ | Buildings, props, terrain |
Workspace/GameSystems/ | Meeting table, cameras (visuals only) |
StarterGui/UI/ | Menu layouts, colors, images |
ServerStorage/Assets/ | 3D models, textures |
| Part colors, materials, meshes | Any visual property |
Performance Notes
- Mission tracking runs at 0.5s intervals (not every frame)
- Suspicion decays over time to prevent permanent penalties
- DataStore auto-saves every 5 minutes + on player leave
- CollectionService tags are cached — avoid retagging at runtime
Live Service Checklist
Before publishing:
- Set real
AssetIdandGamepassIdvalues inCosmeticConfig.lua - Enable Studio API Services (Game Settings → Security → Enable Studio Access to API Services)
- Configure DataStore in Game Settings
- Set
MinPlayersback to 8 for production - Build out all 12 districts with proper DistrictZones
- Add NPC guard patrol paths
- Create game thumbnail and description
- Test with 8+ players in a private server
License
All rights reserved. This codebase is provided for the Suspicious Roblox game project.
Connect with Dhruv Hegde
More of Dhruv Hegde's open-source work on GitHub and LinkedIn.