Law (experimental for Arrest)
The game keeps a record of the crimes the player has committed, and it can send the player to prison. Law lets your mod read that record, add and clear crimes, change or cancel a crime as it is recorded, send the player to prison, and react to the player being spotted, fined, arrested or released.
Currency: RM is Real Money (see RM).
Law read, record and clear crimes, and arrests
Use Commit to record a crime, Clear to remove records, and Intercept to change or cancel a crime before it is recorded. Arrest sends the player to prison, and it is experimental.
public static class Law
{
public static event Action<CommittedArgs> Committed;
public static event Action Spotted;
public static event Action<FineArgs> Fined;
public static event Action Arrested;
public static event Action Released;
public static bool IsAvailable { get; }
public static float Total { get; }
public static float TotalFromRecords { get; }
public static int FineRM { get; }
public static int SentenceHours { get; }
public static int TimesInJail { get; }
public static bool IsArrestInProgress { get; }
public static bool HasStolenGoods { get; }
public static int StolenGoodsValue { get; }
public static float GetGeneric(CrimeKind kind);
public static float GetAgainst(int npcId);
public static bool Commit(CrimeKind kind, int severity, int victimNpcId, bool silent);
public static bool Clear();
public static bool Clear(int victimNpcId);
public static bool Clear(CrimeKind kind);
public static bool Clear(int victimNpcId, CrimeKind kind);
public static bool Arrest(float severityFactor);
public static void Intercept(string modId, CrimeHandler handler);
public static void RemoveAll(string modId);
}
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | true when the Law API works. |
Total | float | All the player’s crimes, including carried stolen goods. |
TotalFromRecords | float | The crime records only. |
FineRM | int | The fine in RM, rounded. |
SentenceHours | int | The prison sentence in hours, rounded. |
TimesInJail | int | How many times the player has been in jail. |
IsArrestInProgress | bool | true while an arrest is happening. |
HasStolenGoods | bool | true when the player carries stolen goods. false when the inventory is not loaded. |
StolenGoodsValue | int | The value of carried stolen goods, minus permitted items. From 0 to int.MaxValue. |
| Method | Returns | What it does |
|---|---|---|
GetGeneric(kind) | float | The amount of the generic records of that kind. |
GetAgainst(npcId) | float | The amount recorded against one NPC. 0 for ids of 0 or less. |
Commit(kind, severity, victimNpcId, silent) | bool | Records a crime. Returns true when it was recorded. |
Clear() | bool | Clears all crime records. |
Clear(victimNpcId) | bool | Clears one victim’s records, across every crime type. |
Clear(kind) | bool | Clears the first generic record of that kind. |
Clear(victimNpcId, kind) | bool | Clears the first record of that kind against one victim. |
Arrest(severityFactor) | bool | Sends the player to prison (experimental). |
Intercept(modId, handler) | void | Runs your code before every crime is recorded. |
RemoveAll(modId) | void | Removes your mod’s crime handlers. |
| Event | Gives you | When it fires |
|---|---|---|
Committed | CommittedArgs | A crime was recorded. |
Spotted | Nothing | A crime witness saw the player. At most once per frame. |
Fined | FineArgs | The player paid a fine. args.Amount is the RM that was due before payment, rounded. |
Arrested | Nothing | The player was sent to prison. |
Released | Nothing | The player left prison. |
CommittedArgs a crime that was recorded
Committed hands you this after a crime is recorded. Kind, Severity and VictimId say what was recorded.
public sealed class CommittedArgs
{
public CrimeKind Kind { get; }
public int Severity { get; }
public int VictimId { get; }
}
| Property | Type | What it means |
|---|---|---|
Kind | CrimeKind | The kind of crime. |
Severity | int | The recorded severity, at least 1. |
VictimId | int | The victim’s NPC id. -1 when there is no victim. |
FineArgs what Fined tells you
Fined hands you this when the player pays a fine. Amount is the RM that was due before the payment.
public sealed class FineArgs
{
public float Amount { get; }
}
| Property | Type | What it means |
|---|---|---|
Amount | float | The RM that was due before payment. |
CrimeKind what sort of crime it is
This is the Kind of a crime, used by Commit, GetGeneric, Clear and CrimeArgs. Each value is one of the game’s crime types, and the framework refuses None.
public enum CrimeKind
{
Unspecified = 0,
Theft = 1,
Violence = 2,
Fraud = 3,
PrisonEscape = 4,
Administrative = 5,
AnimalCruelty = 6,
Trespassing = 7,
Burglary = 8,
None = 9,
Harassment = 10,
Vandalism = 11,
Terrorism = 12
}
| Value | Meaning |
|---|---|
Unspecified | The game’s Unspecified crime type. |
Theft | The game’s Theft crime type. |
Violence | The game’s Violence crime type. |
Fraud | The game’s Fraud crime type. |
PrisonEscape | The game’s PrisonEscape crime type. |
Administrative | The game’s Administrative crime type. |
AnimalCruelty | The game’s AnimalCruelty crime type. |
Trespassing | The game’s Trespassing crime type. |
Burglary | The game’s Burglary crime type. |
None | The game’s None crime type. The framework refuses this one. |
Harassment | The game’s Harassment crime type. |
Vandalism | The game’s Vandalism crime type. |
Terrorism | The game’s Terrorism crime type. |
CrimeHandler your code that runs before a crime is recorded
This is the delegate you pass to Law.Intercept. The framework calls it before each crime is recorded, with the CrimeArgs for that crime.
public delegate void CrimeHandler(CrimeArgs args);
| Parameter | Type | What it means |
|---|---|---|
args | CrimeArgs | The crime about to be recorded. |
CrimeArgs a crime that is about to be recorded
Each CrimeHandler gets one of these for the crime. Change Kind, Severity or Silent, or set Cancel to drop the crime.
public sealed class CrimeArgs
{
public CrimeKind Kind { get; set; }
public int Severity { get; set; }
public int VictimId { get; }
public bool Silent { get; set; }
public bool Cancel { get; set; }
}
| Property | Type | What it means |
|---|---|---|
Kind | CrimeKind | The kind of crime. You may change it. |
Severity | int | How bad it is. You may change it. |
VictimId | int | The victim’s NPC id. -1 when there is no victim. |
Silent | bool | true hides the crime notification. |
Cancel | bool | true drops the crime. |
Putting it together
Refuses to record small thefts, then records a vandalism crime and logs the fine.
using ObenseuerCore;
[ModInfo("example.lawtour", "Law Tour", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class LawTour : FrameworkMod
{
protected override void OnLoad()
{
Law.Intercept("mymod", args => { if (args.Kind == CrimeKind.Theft && args.Severity < 3) args.Cancel = true; });
Events.WhenReady(() =>
{
bool recorded = Law.Commit(CrimeKind.Vandalism, 5, 0, false);
int owed = Law.FineRM;
Log.Info("recorded " + recorded + ", fine " + owed + " RM");
});
}
}
| Step | What it does |
|---|---|
Law.Intercept("mymod", ...) | Cancels any theft with a severity below 3 before it is recorded. |
Law.Commit(CrimeKind.Vandalism, 5, 0, false) | Records a vandalism crime of severity 5 with no victim, and shows the notification. |
Law.FineRM | Reads the fine in RM. |
Things to know
Reading
- Reads give
0orfalsewhen the crime system is not loaded (on the main menu) or when the call fails. Totalincludes the stolen goods the player carries, andTotalFromRecordscounts crime records only.FineRMis the fine, rounded.HasStolenGoodsandStolenGoodsValueread the stolen goods the player carries.StolenGoodsValueleaves out permitted items, and it is0when the inventory is not loaded.SentenceHoursis the game’s own sentence rule, worked out from the crime figures. It takes the number of crimes without stolen goods, divides it by the game’s crimes-per-prison-hour value, rounds it, and keeps the result between 1 and 100, plus 12 hours for each earlier jail term.TimesInJailcounts the earlier jail terms. (The divisor iscrimePerPrisonHour.)
Recording and clearing crimes
Commit(kind, severity, victimNpcId, silent)records a crime the way the game does, and returnstruewhen it was recorded.victimNpcIdabove 0 names the victim, and 0 means no victim. Every crime in the game goes through the same step, so yours is treated like any other. (It calls the game’sCommitCrime.)Clear()clears all crime records, andClear(victimNpcId)clears one victim’s records across every crime type.Clear(kind)clears the first generic record of that kind, and leaves victim records alone. The generic record is the oneGetGenericadds up.Clear(victimNpcId, kind)clears the first record of that kind against that victim, and leaves generic records alone.- Both of those return
falsefor aNoneor unknown kind.Clear(victimNpcId, kind)also returnsfalsefor an id of 0 or less. Both refuse under the same conditions asClear().
Changing a crime as it is recorded
Intercept(modId, handler)runs for every crime, before it is recorded. Handlers run in the order they were added. Adding the same handler for the same mod again does nothing.- Your handler can change
Kind,SeverityandSilent, or setCancel. AKindofNoneor an unknown value keeps the game’s kind.Severityis kept between 1 and 1,000,000 only when the handler changed it.Silenthides the crime notification. - Any
Cancelwins: once a handler sets it, the later handlers still run and seeCancelastrue, and setting it back tofalsehas no effect.Kind,SeverityandSilentare last-writer-wins: the last handler to set them counts, and they are ignored once the crime is cancelled. Committedfires for each crime that was recorded. ItsSeverityis the recorded value, at least 1. A cancelled crime is not recorded, andCommitteddoes not fire.
Arrest (experimental)
Arrest(severityFactor)sends the player to prison.severityFactormust be above 0 and at most 100.- A successful arrest has these effects, all done by the game:
- A theft record of 10% of the stolen goods the player carries is added, when there are any.
- Every crime record’s amount becomes
severityFactortimes its value, rounded. This cannot be undone. - The count of jail terms goes up by one, which raises the sentence limit by 12 hours. (
timesInJailincreases by one. The achievement isARRESTED.) - Police chases stop, the arrest achievement is given, tiredness is set to at least 10, the player passes out, and the arrest sleep scene is scheduled 10 seconds later.
Events
Spottedfires when a crime witness sees the player, at most once per frame. (It fires whenWitness.IsWitnessedreturns true for a crime witness, only for calls withinvokeset.)Finedfires when the player pays a fine.Amountis the rounded RM amount that was due before payment. (It fires when the game’sPayFinesucceeds.)Arrestedfires when the game sends the player to prison, andReleasedfires when the player leaves prison. (Arrestedfires fromCrime.OnGoingToPrison.)
Limits
Commitreturnsfalseand records nothing in these cases: the crime system is not loaded,kindisNoneor not a real kind,severityis outside 1 through 1,000,000, the achievements are not loaded, an intercept cancels the crime, or the game throws.Clear()andClear(victimNpcId)returnfalsewhile an arrest is in progress or a level change is active.Clear(victimNpcId)returnsfalsefor an id of 0 or less.- A
truefromClear(kind)orClear(victimNpcId, kind)means the game call ran, not that a record was cleared. They reset the game’s bribe flag even when no record matches. Arrestreturnsfalseand does nothing when:- an arrest is already in progress, or a level change is active;
- a conversation is open;
- the player is already in prison;
- the achievements, player stats, waiting controller or inventory are not loaded;
TotaltimesseverityFactorwould not fit in a whole number. (A conversation is open whenDialogueManager.isConversationActiveorDialogueController.instance.DialogueActiveis true. “Already in prison” isPrison.inPrison, read through reflection. It calls the game’sCrime.GoToPrison.)
- If the game throws during the call, the framework clears the arrest-in-progress flag, and
Arrestreturnsfalse.