Crime and Police¶
Applejack's justice system. Note throughout how consistently it prefers detention over death.
Sources: metatables/sv_player.lua (warrants, arrest), hooks/sv_player.lua (the gating
hooks), plugins/officials/sv_init.lua (400 lines — the policy),
libraries/sh_laws.lua, plugins/prisonpoints/, sv_commands.lua,
entities/weapons/cider_lockpick/, entities/weapons/cider_tranq/.
1. The separation of mechanism and policy¶
This is the best architectural decision in the entire legacy codebase and it must survive.
metatables/sv_player.luaprovides the mechanisms:Warrant,Arrest,KnockOut,TieUp,Incapacitate,TakeWeapons.hooks/sv_player.luadeclares the gates:PlayerCanArrest,PlayerCanUnarrest,PlayerCanStun,PlayerCanKnockOut,PlayerCanWakeUp,PlayerCanWarrant,PlayerCanUnwarrant,PlayerCanDemote,PlayerCanChangeLaws.plugins/officials/supplies the policy — who is police, what they may do, whether warrants are required.
Remove the officials plugin and the mechanisms still exist; there is simply nobody
authorised to use them. A server can replace the entire concept of law enforcement by
replacing one plugin.
Design note: the new architecture reproduces this split as service interfaces plus cancellable events, not as hooks returning truthy values (Architecture/04_EVENT_BUS.md).
2. Warrants¶
meta:Warrant(class, time) / meta:UnWarrant().
Two classes: "search" and "arrest".
| Setting | Default |
|---|---|
Search Warrant Expire Time |
300 s (5 minutes) |
Arrest Warrant Expire Time |
3600 s (1 hour) |
State is published as NWString "Warrant", plus a _WarrantExpireTime CSVar so the client
can count down. Expiry is a timer.Create keyed on the player's UniqueID, which on firing
calls the PlayerWarrantExpired hook and then UnWarrant.
Because expiry is an in-memory timer and warrants are not persisted, all warrants are cleared by a server restart. Almost certainly unintended. The new design persists warrants with an absolute expiry timestamp.
Warrants are issued through /warrant (sv_commands.lua:1145) and revoked with
/unwarrant (:1189); admins have /awarrant (:134).
The design intent: a search warrant licenses a police officer to search a specific player, and an arrest warrant licenses arrest. Both are visible to the target — you know you are wanted. The short search-warrant expiry forces the police to act on information promptly.
3. Arrest¶
meta:Arrest(time). Default Arrest Time = 300 s (5 minutes).
The sequence, in order:
- Fire
PlayerArrested. - Set
cider._Arrested = true— this is persistent — andNWBool "Arrested". - Start the release timer and publish
_UnarrestTimefor the client countdown. Incapacitate()— see §5.TakeWeapons(true)— confiscate weapons, excluding items (see below).StripAmmo().- Turn the flashlight off.
UnWarrant()— the warrant is spent.UnTie(true)— you cannot be both tied and arrested.
On expiry: UnArrest(true), notify, and Spawn() the player — releasing them from jail.
UnArrest(reset) without reset also Recapacitate()s and ReturnWeapons().
Confiscation¶
TakeWeapons(noitems) stores every held weapon class in _StoredWeapons and strips them.
The noitems flag — passed as true by Arrest — means weapons that correspond to
inventory items are not stored for return. Arrest therefore destroys your item-backed
weapons rather than holding them: you lose the gun permanently.
Compare TieUp(), which calls TakeWeapons() with no flag — a tied player gets everything
back when untied. Arrest confiscates; tying merely disarms.
Jail¶
plugins/prisonpoints/ stores per-map jail positions in prisonpoints/<map>.txt and
teleports the player there on PlayerArrested.
You cannot log out of jail¶
_Arrested persists, and player.loadFunctions._Arrested calls ply:Arrest() on load
(02). Disconnecting does not shorten
a sentence.
There is a subtlety worth noting for the reimplementation: the remaining time is not saved, only the boolean. Reconnecting therefore restarts the full 5 minutes. Preserve the inescapability; fix the arithmetic.
4. Non-lethal force¶
The original's arsenal of ways to remove someone from a fight without killing them:
Knock out — meta:KnockOut(time, velocity)¶
Default Knock Out Time = 30 s.
The implementation is careful and worth describing:
- Refuses if already knocked out; exits any vehicle first ("This shit goes crazy if you ragdoll in a car").
- Captures all 71 bone matrices from the player and applies them to the spawned ragdoll, so the body is a natural continuation of the pose rather than a collapse.
- Uses
prop_ragdoll, falling back toprop_physicsfor models without a valid ragdoll. - Sets
COLLISION_GROUP_WEAPONso bodies cannot be used to crush or shove players. - Gives the ragdoll to the world via prop protection so nobody can pick it up and wave it around.
- The ragdoll carries its own health (
ply.ragdoll = {entity, health}) — an unconscious body can be killed.
And metatables/sh_player.lua overrides Player:GetPos() to return the ragdoll's position
while one exists, so an unconscious player is genuinely located at their body.
meta:WakeUp(reset) requires the player's own input to stand up.
Tie up — meta:TieUp() / meta:UnTie(reset)¶
| Setting | Default |
|---|---|
Tying Timeout |
5 s to apply |
UnTying Timeout |
5 s to remove |
Tying Struggles |
1 struggle to escape |
Tying Struggles Timeout |
30 s for the struggle meter to refill |
Rope struggles |
5 (marked unused) |
Tying incapacitates, disarms (recoverably), and kills the flashlight.
Incapacitate — meta:Incapacitate() / Recapacitate()¶
The shared primitive underneath arrest, tying and being carried:
- walk and run speed both →
Incapacitated Speed(100, against 150 walk / 275 run) - jump power → 0
NWBool "Incapacitated"→ true
Recapacitate() is gated by the PlayerCanBeRecapacitated hook — which is how the
stamina plugin prevents you standing up while exhausted. A clean example of a plugin
modifying a core mechanic through a declared gate rather than by patching it.
Bleeding — meta:Bleed(time) / StopBleeding()¶
Default Bleed Time = 5 s. Traces down from the player every 0.25 s and stamps a blood
decal on the floor. A wounded player leaves a followable trail. Cosmetic in
implementation, tactical in effect — and one of the few genuinely emergent mechanics in the
original.
Note the bug:
meta:Bleed(time)computes its repeat count fromseconds, an undefined global, not from itstimeparameter.(seconds or 0) * 4is therefore always0, which in the legacy timer library means repeat forever. Bleeding never stops on its own.
Tranquilliser¶
entities/weapons/cider_tranq/ with items/ammo/ammo_dart.lua — a non-lethal takedown
weapon feeding the knockout system. Its presence signals the design intent clearly.
5. Lockpicking and breaking in¶
entities/weapons/cider_lockpick/:
| Setting | Default |
|---|---|
Maximum Lockpick Hits |
30 hits to open a lock |
Lockpick Break Chance |
0.01 added per lock successfully picked |
The break chance is cumulative across the lockpick's life: each successful pick makes the tool 1% more likely to snap on any subsequent hit. A veteran burglar's lockpick is a liability. Lovely mechanic; preserve it.
Also present: cider_breach (breaching a door, which jams it open for 60 s — see
04) and
cider_padlock as a physical, attackable lock.
Known issue, recorded in the developer chat log preserved at the top of sh_init.lua:
"You can only lockpick the guy once, and if he moves the lockpick is unable to pick the
lock… And unable to pick the lock on the same thing again."
6. Contraband¶
Contraband entities (cider_money_printer, cider_drug_lab, cider_contraband) pay out
every Earning Interval = 300 s (init.lua:1412, hooks/sv_player.lua:308).
items/base/contraband.lua enforces per-player counts against
GM.Config.Contraband[uid].maximum via meta:AddCount / TakeCount.
Police destroy contraband for a reward, governed by two settings:
| Setting | Default | Meaning |
|---|---|---|
Officials Contraband |
true | City officials also receive contraband payments |
Need Warrant |
false | Officials need a warrant to destroy contraband |
Police Kill Drop |
true | Weapons drop when a player is killed by police |
Officials Contraband = true is striking: the police can run printers too. The framework
does not assume the state is honest.
The Applejack fork adds tiered contraband — cider_money_printer_2/3/4 and
cider_drug_lab_2/3/4 — an upgrade path the canonical copy lacks. Good input to the
crafting design.
7. Laws¶
libraries/sh_laws.lua (63 lines).
Ten laws. Free text. Editable by the Mayor. A 120 second cooldown between changes.
Synced to clients over datastream plus a legacy usermessage; displayed by
derma/cl_laws.lua. Changing them is gated by PlayerCanChangeLaws.
The laws are not enforced by code at all. Nothing reads them. They exist so that police can point at them and players can argue about them. This is the purest expression of the design philosophy in the codebase: the framework provides a noticeboard, and the players provide the legal system.
8. Commands¶
| Command | Line | Access |
|---|---|---|
/warrant |
1145 | Police |
/unwarrant |
1189 | Police |
/arrest |
114 | Admin |
/awarrant |
134 | Admin |
/mutiny |
1261 | Gang members |
Plus the officials plugin's SayRequest / SayBroadcast for police radio, and
radio recipient filtering.
9. What is missing¶
The master plan's Phase 12 lists several things the original does not have:
- Evidence. No concept. A search finds items; it does not record findings.
- Fines. No mechanism — punishment is detention only.
- Searching as a distinct action. A search warrant authorises it socially; there is no search interaction separate from just taking things from an incapacitated player.
- Persistent criminal records. Warrants are transient and die with the server.
- Bail, trials, sentencing. Arrest time is a constant.
All new design. The mechanisms that exist to build on: warrants with classes and expiry, the gate hooks, jail points, confiscation, and the laws noticeboard.
10. What survives¶
Survives: the mechanism/gate/policy split; warrant classes with different expiries; arrest as detention with confiscation; inescapable sentences; jail points per map; the incapacitate primitive shared by arrest/tying/carrying; knockout with a posed, damageable, world-owned ragdoll; position following the ragdoll; blood trails; cumulative lockpick wear; breaching with a timed jam; contraband caps and interval payouts; police optionally corruptible; ten player-authored laws that the code never reads.
Does not survive: warrants dying on restart; arrest restarting its full duration on
reconnect; the bleed timer bug; hooks that gate by truthy return; NWBool/NWString as the
state model.