diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUi.cs b/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUi.cs
new file mode 100644
index 00000000000000..ea5aa3cf256988
--- /dev/null
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUi.cs
@@ -0,0 +1,35 @@
+using Robust.Client.UserInterface;
+using Content.Client.UserInterface.Fragments;
+using Content.Shared.DeltaV.CartridgeLoader.Cartridges;
+using Content.Shared.CartridgeLoader;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.DeltaV.CartridgeLoader.Cartridges;
+
+public sealed partial class CrimeAssistUi : UIFragment
+{
+ private CrimeAssistUiFragment? _fragment;
+
+ public override Control GetUIFragmentRoot()
+ {
+ return _fragment!;
+ }
+
+ public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
+ {
+ _fragment = new CrimeAssistUiFragment();
+
+ _fragment.OnSync += _ => SendSyncMessage(userInterface);
+ }
+
+ private void SendSyncMessage(BoundUserInterface userInterface)
+ {
+ var syncMessage = new CrimeAssistSyncMessageEvent();
+ var message = new CartridgeUiMessage(syncMessage);
+ userInterface.SendMessage(message);
+ }
+
+ public override void UpdateState(BoundUserInterfaceState state)
+ {
+ }
+}
diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiFragment.xaml b/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiFragment.xaml
new file mode 100644
index 00000000000000..8186986d8fb70e
--- /dev/null
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiFragment.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiFragment.xaml.cs b/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiFragment.xaml.cs
new file mode 100644
index 00000000000000..e3163975d1213d
--- /dev/null
+++ b/Content.Client/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiFragment.xaml.cs
@@ -0,0 +1,98 @@
+using Content.Client.Message;
+using Content.Shared.DeltaV.CartridgeLoader.Cartridges;
+using Robust.Client.AutoGenerated;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+using static Content.Client.DeltaV.CartridgeLoader.Cartridges.CrimeAssistUi;
+
+namespace Content.Client.DeltaV.CartridgeLoader.Cartridges;
+
+[GenerateTypedNameReferences]
+public sealed partial class CrimeAssistUiFragment : BoxContainer
+{
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IResourceCache _resourceCache = default!;
+
+ public event Action? OnSync;
+ private CrimeAssistPage _currentPage;
+ private List? _pages;
+
+ public CrimeAssistUiFragment()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ Orientation = LayoutOrientation.Vertical;
+ HorizontalExpand = true;
+ VerticalExpand = true;
+
+ _pages = new List(_prototypeManager.EnumeratePrototypes());
+
+ _currentPage = FindPageById("mainmenu");
+ UpdateUI(_currentPage);
+
+ StartButton.OnPressed += _ => UpdateUI(FindPageById(FindPageById("mainmenu").OnStart!));
+ HomeButton.OnPressed += _ => UpdateUI(FindPageById("mainmenu"));
+ YesButton.OnPressed += _ => AdvanceState(_currentPage!, true);
+ NoButton.OnPressed += _ => AdvanceState(_currentPage!, false);
+ }
+
+ public void AdvanceState(CrimeAssistPage currentPage, bool yesPressed)
+ {
+ UpdateUI(yesPressed ? FindPageById(currentPage.OnYes!) : FindPageById(currentPage.OnNo!));
+ }
+
+ public void UpdateUI(CrimeAssistPage page)
+ {
+ _currentPage = page;
+ bool isResult = page.LocKeyPunishment != null;
+
+ StartButton.Visible = page.OnStart != null;
+ YesButton.Visible = page.OnYes != null;
+ NoButton.Visible = page.OnNo != null;
+ HomeButton.Visible = page.OnStart == null;
+ Explanation.Visible = page.OnStart == null;
+
+ Subtitle.Visible = page.LocKeySeverity != null;
+ Punishment.Visible = page.LocKeyPunishment != null;
+
+ if (!isResult)
+ {
+ string question = $"\n[font size=15]{Loc.GetString(page.LocKey!)}[/font]";
+
+ if (question.ToLower().Contains("sophont"))
+ {
+ string sophontExplanation = Loc.GetString("crime-assist-sophont-explanation");
+ question += $"\n[font size=8][color=#999999]{sophontExplanation}[/color][/font]";
+ }
+
+ Title.SetMarkup(question);
+ Subtitle.SetMarkup(string.Empty);
+ Explanation.SetMarkup(string.Empty);
+ Punishment.SetMarkup(string.Empty);
+ }
+ else
+ {
+ string color = page.LocKeySeverity! switch
+ {
+ "crime-assist-crimetype-innocent" => "#39a300",
+ "crime-assist-crimetype-misdemeanour" => "#7b7b30",
+ "crime-assist-crimetype-felony" => "#7b5430",
+ "crime-assist-crimetype-capital" => "#7b2e30",
+ _ => "#ff00ff"
+ };
+
+ Title.SetMarkup("\n[bold][font size=23][color=#a4885c]" + Loc.GetString(page.LocKeyTitle!) + "[/color][/font][/bold]");
+ Subtitle.SetMarkup($"\n[font size=19][color={color}]" + Loc.GetString(page.LocKeySeverity!) + "[/color][/font]");
+ Explanation.SetMarkup("\n[title]" + Loc.GetString(page.LocKeyDescription!) + "[/title]\n");
+ Punishment.SetMarkup("[bold][font size=15]" + Loc.GetString(page.LocKeyPunishment!) + "[/font][/bold]");
+ }
+ }
+
+ private CrimeAssistPage FindPageById(string id)
+ {
+ return _pages?.Find(o => o.ID == id)!;
+ }
+}
diff --git a/Content.IntegrationTests/Tests/DeltaV/CrimeassistTest.cs b/Content.IntegrationTests/Tests/DeltaV/CrimeassistTest.cs
new file mode 100644
index 00000000000000..2e5c7605fa8c6d
--- /dev/null
+++ b/Content.IntegrationTests/Tests/DeltaV/CrimeassistTest.cs
@@ -0,0 +1,79 @@
+using System.Linq;
+using Content.Shared.DeltaV.CartridgeLoader.Cartridges;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Localization;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+
+namespace Content.IntegrationTests.Tests.DeltaV;
+
+[TestFixture]
+public sealed class CrimeAssistTest
+{
+ [Test]
+ public async Task CrimeAssistValid()
+ {
+ await using var pair = await PoolManager.GetServerClient();
+ var server = pair.Server;
+ await server.WaitIdleAsync();
+
+ var prototypeManager = server.ResolveDependency();
+ var allProtos = prototypeManager.EnumeratePrototypes().ToArray();
+
+ await server.WaitAssertion(() =>
+ {
+ foreach (var proto in allProtos)
+ {
+ if (proto.LocKey != null)
+ {
+ Assert.That(Loc.TryGetString(proto.LocKey, out var _),
+ $"CrimeAssistPage {proto.ID} has invalid LocKey {proto.LocKey}!");
+ }
+
+ if (proto.LocKeyTitle != null)
+ {
+ Assert.That(Loc.TryGetString(proto.LocKeyTitle, out var _),
+ $"CrimeAssistPage {proto.ID} has invalid LocKeyTitle {proto.LocKeyTitle}!");
+ }
+
+ if (proto.LocKeyDescription != null)
+ {
+ Assert.That(Loc.TryGetString(proto.LocKeyDescription, out var _),
+ $"CrimeAssistPage {proto.ID} has invalid LocKeyDescription {proto.LocKeyDescription}!");
+ }
+
+ if (proto.LocKeySeverity != null)
+ {
+ Assert.That(Loc.TryGetString(proto.LocKeySeverity, out var _),
+ $"CrimeAssistPage {proto.ID} has invalid LocKeySeverity {proto.LocKeySeverity}!");
+ }
+
+ if (proto.LocKeyPunishment != null)
+ {
+ Assert.That(Loc.TryGetString(proto.LocKeyPunishment, out var _),
+ $"CrimeAssistPage {proto.ID} has invalid LocKeyPunishment {proto.LocKeyPunishment}!");
+ }
+
+ if (proto.OnStart != null)
+ {
+ Assert.That(allProtos.Any(p => p.ID == proto.OnStart),
+ $"CrimeAssistPage {proto.ID} has invalid OnStart {proto.OnStart}!");
+ }
+
+ if (proto.OnYes != null)
+ {
+ Assert.That(allProtos.Any(p => p.ID == proto.OnYes),
+ $"CrimeAssistPage {proto.ID} has invalid OnYes {proto.OnYes}!");
+ }
+
+ if (proto.OnNo != null)
+ {
+ Assert.That(allProtos.Any(p => p.ID == proto.OnNo),
+ $"CrimeAssistPage {proto.ID} has invalid OnNo {proto.OnNo}!");
+ }
+ }
+ });
+
+ await pair.CleanReturnAsync();
+ }
+}
diff --git a/Content.Server/DeltaV/CartridgeLoader/CrimeAssistCartridgeComponent.cs b/Content.Server/DeltaV/CartridgeLoader/CrimeAssistCartridgeComponent.cs
new file mode 100644
index 00000000000000..741a61345808d6
--- /dev/null
+++ b/Content.Server/DeltaV/CartridgeLoader/CrimeAssistCartridgeComponent.cs
@@ -0,0 +1,5 @@
+namespace Content.Server.DeltaV.CartridgeLoader.Cartridges;
+
+[RegisterComponent]
+public sealed partial class CrimeAssistCartridgeComponent : Component
+{ }
diff --git a/Content.Server/DeltaV/CartridgeLoader/CrimeAssistCartridgeSystem.cs b/Content.Server/DeltaV/CartridgeLoader/CrimeAssistCartridgeSystem.cs
new file mode 100644
index 00000000000000..06732c2c5349e4
--- /dev/null
+++ b/Content.Server/DeltaV/CartridgeLoader/CrimeAssistCartridgeSystem.cs
@@ -0,0 +1,16 @@
+using Content.Shared.CartridgeLoader;
+using Content.Server.DeltaV.CartridgeLoader;
+using Content.Server.CartridgeLoader.Cartridges;
+using Content.Server.CartridgeLoader;
+
+namespace Content.Server.DeltaV.CartridgeLoader.Cartridges;
+
+public sealed class CrimeAssistCartridgeSystem : EntitySystem
+{
+ [Dependency] private readonly CartridgeLoaderSystem? _cartridgeLoaderSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ }
+}
diff --git a/Content.Shared/DeltaV/CartridgeLoader/Cartridges/CrimeAssistPage.cs b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/CrimeAssistPage.cs
new file mode 100644
index 00000000000000..8fb9a131a827bc
--- /dev/null
+++ b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/CrimeAssistPage.cs
@@ -0,0 +1,35 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.DeltaV.CartridgeLoader.Cartridges;
+
+[Prototype("crimeAssistPage")]
+public sealed partial class CrimeAssistPage : IPrototype
+{
+ [ViewVariables]
+ [IdDataField]
+ public string ID { get; private set; } = "";
+
+ [DataField("onStart")]
+ public string? OnStart { get; private set; }
+
+ [DataField("locKey")]
+ public string? LocKey { get; private set; }
+
+ [DataField("onYes")]
+ public string? OnYes { get; private set; }
+
+ [DataField("onNo")]
+ public string? OnNo { get; private set; }
+
+ [DataField("locKeyTitle")]
+ public string? LocKeyTitle { get; private set; }
+
+ [DataField("locKeyDescription")]
+ public string? LocKeyDescription { get; private set; }
+
+ [DataField("locKeySeverity")]
+ public string? LocKeySeverity { get; private set; }
+
+ [DataField("locKeyPunishment")]
+ public string? LocKeyPunishment { get; private set; }
+}
diff --git a/Content.Shared/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiState.cs b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiState.cs
new file mode 100644
index 00000000000000..dd820f1a0b3f70
--- /dev/null
+++ b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/CrimeAssistUiState.cs
@@ -0,0 +1,18 @@
+using Content.Shared.CartridgeLoader;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.DeltaV.CartridgeLoader.Cartridges;
+
+[Serializable, NetSerializable]
+public sealed class CrimeAssistUiState : BoundUserInterfaceState
+{
+ public CrimeAssistUiState()
+ { }
+}
+
+[Serializable, NetSerializable]
+public sealed class CrimeAssistSyncMessageEvent : CartridgeMessageEvent
+{
+ public CrimeAssistSyncMessageEvent()
+ { }
+}
diff --git a/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl
new file mode 100644
index 00000000000000..9b4c59d001d98f
--- /dev/null
+++ b/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl
@@ -0,0 +1,120 @@
+crime-assist-program-name = Crime Assist
+crime-assist-yes-button = Yes
+crime-assist-no-button = No
+crime-assist-crimetype-innocent = Innocent
+crime-assist-crimetype-misdemeanour = Misdemeanour
+crime-assist-crimetype-felony = Felony
+crime-assist-crimetype-capital = Capital
+crime-assist-crime-innocent = No crime was committed
+crime-assist-crime-animalcruelty = Code 101: Animal Cruelty
+crime-assist-crime-theft = Code 102: Theft
+crime-assist-crime-trespass = Code 110: Trespass
+crime-assist-crime-vandalism = Code 111: Vandalism
+crime-assist-crime-hooliganism = Code 112: Hooliganism
+crime-assist-crime-manslaughter = Code 201: Manslaughter
+crime-assist-crime-grandtheft = Code 202: Grand Theft
+crime-assist-crime-blackmarketeering = Code 203: Black Marketeering
+crime-assist-crime-sabotage = Code 204: Sabotage
+crime-assist-crime-mindbreaking = Code 205: Mindbreaking
+crime-assist-crime-assault = Code 206: Assault
+crime-assist-crime-abuseofpower = Code 207: Abuse of Power
+crime-assist-crime-possession = Code 208: Possession
+crime-assist-crime-endangerment = Code 209: Endangerment
+crime-assist-crime-breakingandentering = Code 210: Breaking and Entering
+crime-assist-crime-rioting = Code 211: Rioting
+crime-assist-crime-contemptofcourt = Code 212: Contempt of Court
+crime-assist-crime-perjuryfalsereport = Code 213: Perjury or False Report
+crime-assist-crime-obstructionofjustice = Code 214: Obstruction of Justice
+crime-assist-crime-murder = Code 301: Murder
+crime-assist-crime-terrorism = Code 303: Terrorism
+crime-assist-crime-grandsabotage = Code 304: Grand Sabotage
+crime-assist-crime-decorporealisation = Code 305: Decorporealisation
+crime-assist-crime-kidnapping = Code 309: Kidnapping
+crime-assist-crime-sedition = Code 311: Sedition
+crime-assist-crime-sexualharassment = Code 314: Sexual Harassment
+crime-assist-mainmenu = Welcome to Crime Assist!
+crime-assist-question-isitterrorism = Did the suspect hold hostages, cause many deaths or major destruction to force compliance from the crew?
+crime-assist-question-wassomeoneattacked = Was an entity attacked?
+crime-assist-question-wasitsophont = Was the victim in question a sophont?
+crime-assist-question-didvictimdie = Did the victim die as a result to the attack?
+crime-assist-question-isvictimremovedfrombody = Is the victim alive, but intentionally and permanently removed from the body?
+crime-assist-question-wasdeathintentional = Did the suspect attack the victim with intent to kill?
+crime-assist-question-forcedmindbreakertoxin = Was the victim forced to take Mindbreaker Toxin?
+crime-assist-question-hadillicititem = Did the suspect have an illicit or controlled item, substance or entity?
+crime-assist-question-wasitaperson = Did the suspect hold another sophont against its will?
+crime-assist-question-wassuspectselling = Was the suspect selling or distributing the illicit items?
+crime-assist-question-wassuspectseentaking = Can it be proven that the suspect took the item knowing that it was illegal for the suspect to possess the item?
+crime-assist-question-isitemextremelydangerous = Is the item in question dangerous to other sophonts or the station as a whole?
+crime-assist-question-wassuspectinarestrictedlocation = Was the suspect in a restricted location, or anywhere the suspect did not have legal access to?
+crime-assist-question-wasentrancelocked = Did the suspect force through a locked access point to get to the location?
+crime-assist-question-didsuspectbreaksomething = Did the suspect break something?
+crime-assist-question-weretheremanysuspects = Were there many suspects in a group causing a disturbance?
+crime-assist-question-wasdamagesmall = Was the damage minor, or easily undone?
+crime-assist-question-wasdestroyeditemimportanttostation = Was the damaged or destroyed item important to the functioning of the station?
+crime-assist-question-islargepartofstationdestroyed = Was a large part of the station destroyed or made uninhabitable?
+crime-assist-question-wascrimesexualinnature = Was the crime sexual in nature?
+crime-assist-question-wassuspectanuisance = Was the suspect being a nuisance to other sophonts?
+crime-assist-question-falselyreportingtosecurity = Has the suspect made a report, in person or through radio channels, to security that was proven to be intentionally false?
+crime-assist-question-happenincourt = Was the suspect a nuisance in court?
+crime-assist-question-duringactiveinvestigation = Was the suspect a nuisance during an active investigation, and hindered the investigation as a result?
+crime-assist-question-tocommandstaff = Did the suspect overthrow or compromise a lawfully established Chain of Command, or attempt to do so?
+crime-assist-question-wasitcommanditself = Was a command staff or department head abusing authority over another sophont?
+crime-assist-crimedetail-innocent = Crime could not be determined. Use your best judgement to resolve the situation.
+crime-assist-crimedetail-animalcruelty = To inflict unnecessary suffering on a sapient being with malicious intent.
+crime-assist-crimedetail-theft = To unlawfully take property or items without consent.
+crime-assist-crimedetail-trespass = To enter into an area where one is not authorised nor invited.
+crime-assist-crimedetail-vandalism = To deface or superficially damage public property, or property belonging to another person.
+crime-assist-crimedetail-hooliganism = To intentionally and maliciously engage in disruptive conduct, where one refuses to cease.
+crime-assist-crimedetail-manslaughter = To effect violence upon a sophont, resulting in their death, without the apparent intent to kill them.
+crime-assist-crimedetail-grandtheft = To unlawfully take highly restricted, dangerous, valuable, or highly sensitive property or items without consent.
+crime-assist-crimedetail-blackmarketeering = To sell, distribute, or otherwise circulate restricted items or substances to unauthorised sophonts or entities.
+crime-assist-crimedetail-sabotage = To engage in malicious actions that directly or indirectly hinder the operation of a vessel or its part, modify and/or damage technology one is not authorised to access, or endanger multiple sophonts.
+crime-assist-crimedetail-mindbreaking = To unlawfully and maliciously rid a psionic sophont of their powers.
+crime-assist-crimedetail-assault = To cause physical harm or to effect unwanted physical contact on a sophont, without the apparent intent to kill them, or to threaten such actions with both capability and intent to do so.
+crime-assist-crimedetail-abuseofpower = To intentionally misuse or wrongfully exercise one’s own authority, influence, or control, resulting in harm, unjust treatment, or demonstrable loss to a separate party due to violation of ethical and professional standards.
+crime-assist-crimedetail-possession = To be in unauthorised possession of restricted items or items of particular danger.
+crime-assist-crimedetail-endangerment = To recklessly abandon obligations involving the continued wellbeing and/or protection of life and property, through malpractice, action, or inaction.
+crime-assist-crimedetail-breakingandentering = To break and enter into a high security area where one is not authorised nor invited, with intent to commit a crime within.
+crime-assist-crimedetail-rioting = To partake in an unauthorised riotous, tumultuous, and disruptive public assembly that refuses to disperse after warning.
+crime-assist-crimedetail-contemptofcourt = To conduct oneself disruptively and disrespectfully before the court.
+crime-assist-crimedetail-perjuryfalsereport = To wilfully and maliciously tell an untruth either in court or in the process of making an actionable report to law enforcement.
+crime-assist-crimedetail-obstructionofjustice = To wilfully disobey, interfere with, or refuse a decree of the court, warrant, or arrest.
+crime-assist-crimedetail-murder = To kill a sophont with malicious intent and premeditation.
+crime-assist-crimedetail-terrorism = To commit an act with the intent to cause injury or death, or to take hostages, in order to provoke a state of terror in, intimidate, or compel a group of sophonts to do or to abstain from doing any act.
+crime-assist-crimedetail-grandsabotage = To engage in malicious actions that directly or indirectly make uninhabitable or inoperable a vessel or its part, or irreversibly modify and/or damage technology one is not authorised to access, causing severe bodily harm or death to multiple sophonts.
+crime-assist-crimedetail-decorporealisation = To unlawfully, maliciously, and permanently strip a sophont’s mind from their body.
+crime-assist-crimedetail-kidnapping = To unlawfully confine or restrict the free movement of a sophont against their will.
+crime-assist-crimedetail-sedition = To act to overthrow a lawfully established Chain of Command or governing body without lawful or legitimate cause.
+crime-assist-crimedetail-sexualharassment = To sexually harass, attempt to coerce into sexual relations, or effect unwanted sexual contact with an unwilling sophont.
+crime-assist-crimepunishment-innocent = No punishment may be necessary
+crime-assist-crimepunishment-animalcruelty = Punishment: 3 minutes
+crime-assist-crimepunishment-theft = Punishment: 2 minutes
+crime-assist-crimepunishment-trespass = Punishment: 2 minutes
+crime-assist-crimepunishment-vandalism = Punishment: 2 minutes
+crime-assist-crimepunishment-hooliganism = Punishment: As necessary
+crime-assist-crimepunishment-manslaughter = Punishment: 8 minutes
+crime-assist-crimepunishment-grandtheft = Punishment: 8 minutes
+crime-assist-crimepunishment-blackmarketeering = Punishment: 6 minutes
+crime-assist-crimepunishment-sabotage = Punishment: 6 minutes
+crime-assist-crimepunishment-mindbreaking = Punishment: 5 minutes
+crime-assist-crimepunishment-assault = Punishment: 5 minutes
+crime-assist-crimepunishment-abuseofpower = Punishment: 5 minutes
+crime-assist-crimepunishment-possession = Punishment: Up to 5 minutes
+crime-assist-crimepunishment-endangerment = Punishment: 4 minutes
+crime-assist-crimepunishment-breakingandentering = Punishment: 4 minutes
+crime-assist-crimepunishment-rioting = Punishment: 4 minutes
+crime-assist-crimepunishment-contemptofcourt = Punishment: 4 minutes
+crime-assist-crimepunishment-perjuryfalsereport = Punishment: 3 minutes
+crime-assist-crimepunishment-obstructionofjustice = Punishment: 2 minutes
+crime-assist-crimepunishment-murder = Punishment: Capital
+crime-assist-crimepunishment-terrorism = Punishment: Capital
+crime-assist-crimepunishment-grandsabotage = Punishment: Capital
+crime-assist-crimepunishment-decorporealisation = Punishment: Capital
+crime-assist-crimepunishment-kidnapping = Punishment: Capital
+crime-assist-crimepunishment-sedition = Punishment: Capital
+crime-assist-crimepunishment-sexualharassment = Punishment: Capital
+crime-assist-sophont-explanation = A sophont is described as any entity with the capacity to display the following attributes:
+ • [bold]Sapience[/bold]: the entity possesses basic logic and problem-solving skills, or at a minimum some level of significant intelligence.
+ • [bold]Sentience[/bold]: the entity has the capacity to process an emotion or lack thereof, or at a minimum the ability to recognise its own pain.
+ • [bold]Self-awareness[/bold]: the entity is capable of altering its behaviour in a reasonable fashion as a result of stimuli, or at a minimum is capable of recognising its own sapience and sentience.
+ Any sophont is considered a legal person, regardless of origin or prior cognitive status. Much like any other intelligent organic, a sophont may press charges against crew and be tried for crimes.
diff --git a/Resources/Prototypes/Datasets/tips.yml b/Resources/Prototypes/Datasets/tips.yml
index 53429e879ab902..32798370651e18 100644
--- a/Resources/Prototypes/Datasets/tips.yml
+++ b/Resources/Prototypes/Datasets/tips.yml
@@ -136,3 +136,5 @@
- "Lottery crates can very rarely contain The Throngler."
- "You can tell if an area with firelocks up is spaced by looking to see if the firelocks have lights beside them."
- "Instead of picking it up, you can alt-click food to eat it. This also works for mice and other creatures without hands."
+ # DeltaV - Specific tips below
+ - "As a Security Officer, if you're not sure how to determine the severity of a crime or the appropriate punishment, take a look at the Crime Assist application in your PDA!"
diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/cartridges.yml
new file mode 100644
index 00000000000000..e3d5e9d2138daa
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/cartridges.yml
@@ -0,0 +1,20 @@
+- type: entity
+ parent: BaseItem
+ id: CrimeAssistCartridge
+ name: crime assist cartridge
+ description: A cartridge that helps identify crimes and see appropriate punishment.
+ components:
+ - type: Sprite
+ sprite: DeltaV/Objects/Devices/cartridge.rsi
+ state: cart-cri
+ - type: Icon
+ sprite: DeltaV/Objects/Devices/cartridge.rsi
+ state: cart-cri
+ - type: UIFragment
+ ui: !type:CrimeAssistUi
+ - type: Cartridge
+ programName: crime-assist-program-name
+ icon:
+ sprite: DeltaV/Icons/cri.rsi
+ state: cri
+ - type: CrimeAssistCartridge
diff --git a/Resources/Prototypes/DeltaV/cartridges/crimeassistflow.yml b/Resources/Prototypes/DeltaV/cartridges/crimeassistflow.yml
new file mode 100644
index 00000000000000..c8258ba598cc1e
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/cartridges/crimeassistflow.yml
@@ -0,0 +1,349 @@
+- type: crimeAssistPage
+ id: "mainmenu"
+ locKey: "crime-assist-mainmenu"
+ onStart: "questionIsItTerrorism"
+
+- type: crimeAssistPage
+ id: "questionIsItTerrorism"
+ locKey: "crime-assist-question-isitterrorism"
+ onYes: "resultTerrorism"
+ onNo: "questionWasSomeoneAttacked"
+
+- type: crimeAssistPage
+ id: "questionWasSomeoneAttacked"
+ locKey: "crime-assist-question-wassomeoneattacked"
+ onYes: "questionWasItSophont"
+ onNo: "questionForcedMindbreakerToxin"
+
+- type: crimeAssistPage
+ id: "questionWasItSophont"
+ locKey: "crime-assist-question-wasitsophont"
+ onYes: "questionDidVictimDie"
+ onNo: "resultAnimalCruelty"
+
+- type: crimeAssistPage
+ id: "questionDidVictimDie"
+ locKey: "crime-assist-question-didvictimdie"
+ onYes: "questionIsVictimRemovedFromBody"
+ onNo: "resultAssault"
+
+- type: crimeAssistPage
+ id: "questionIsVictimRemovedFromBody"
+ locKey: "crime-assist-question-victimremovedfrombody"
+ onYes: "resultDecorporealisation"
+ onNo: "questionWasDeathIntentional"
+
+- type: crimeAssistPage
+ id: "questionWasDeathIntentional"
+ locKey: "crime-assist-question-wasdeathintentional"
+ onYes: "resultMurder"
+ onNo: "resultManslaughter"
+
+- type: crimeAssistPage
+ id: "questionForcedMindbreakerToxin"
+ locKey: "crime-assist-question-forcedmindbreakertoxin"
+ onYes: "resultMindbreaking"
+ onNo: "questionHadRestrictedItem"
+
+- type: crimeAssistPage
+ id: "questionHadRestrictedItem"
+ locKey: "crime-assist-question-hadillicititem"
+ onYes: "questionWasItAPerson"
+ onNo: "questionWasSuspectInARestrictedLocation"
+
+- type: crimeAssistPage
+ id: "questionWasItAPerson"
+ locKey: "crime-assist-question-wasitaperson"
+ onYes: "resultKidnapping"
+ onNo: "questionWasSuspectSelling"
+
+- type: crimeAssistPage
+ id: "questionWasSuspectSelling"
+ locKey: "crime-assist-question-wassuspectselling"
+ onYes: "resultBlackMarketeering"
+ onNo: "questionWasSuspectSeenTaking"
+
+- type: crimeAssistPage
+ id: "questionWasSuspectSeenTaking"
+ locKey: "crime-assist-question-wassuspectseentaking"
+ onYes: "questionIsItemExtremelyDangerous"
+ onNo: "resultPossession"
+
+- type: crimeAssistPage
+ id: "questionIsItemExtremelyDangerous"
+ locKey: "crime-assist-question-isitemextremelydangerous"
+ onYes: "resultGrandTheft"
+ onNo: "resultTheft"
+
+- type: crimeAssistPage
+ id: "questionWasSuspectInARestrictedLocation"
+ locKey: "crime-assist-question-wassuspectinarestrictedlocation"
+ onYes: "questionWasEntranceLocked"
+ onNo: "questionDidSuspectBreakSomething"
+
+- type: crimeAssistPage
+ id: "questionWasEntranceLocked"
+ locKey: "crime-assist-question-wasentrancelocked"
+ onYes: "resultBreakingAndEntering"
+ onNo: "resultTrespass"
+
+- type: crimeAssistPage
+ id: "questionDidSuspectBreakSomething"
+ locKey: "crime-assist-question-didsuspectbreaksomething"
+ onYes: "questionWereThereManySuspects"
+ onNo: "questionWasCrimeSexualInNature"
+
+- type: crimeAssistPage
+ id: "questionWereThereManySuspects"
+ locKey: "crime-assist-question-weretheremanysuspects"
+ onYes: "resultRioting"
+ onNo: "questionWasDamageSmall"
+
+- type: crimeAssistPage
+ id: "questionWasDamageSmall"
+ locKey: "crime-assist-question-wasdamagesmall"
+ onYes: "resultVandalism"
+ onNo: "questionWasDestroyedItemImportantToStation"
+
+- type: crimeAssistPage
+ id: "questionWasDestroyedItemImportantToStation"
+ locKey: "crime-assist-question-wasdestroyeditemimportanttostation"
+ onYes: "questionIsLargePartOfStationDestroyed"
+ onNo: "resultEndangerment"
+
+- type: crimeAssistPage
+ id: "questionIsLargePartOfStationDestroyed"
+ locKey: "crime-assist-question-islargepartofstationdestroyed"
+ onYes: "resultGrandSabotage"
+ onNo: "resultSabotage"
+
+- type: crimeAssistPage
+ id: "questionWasCrimeSexualInNature"
+ locKey: "crime-assist-question-wascrimesexualinnature"
+ onYes: "resultSexualHarassment"
+ onNo: "questionWasSuspectANuisance"
+
+- type: crimeAssistPage
+ id: "questionWasSuspectANuisance"
+ locKey: "crime-assist-question-wassuspectanuisance"
+ onYes: "questionFalselyReportingToSecurity"
+ onNo: "resultInnocent"
+
+- type: crimeAssistPage
+ id: "questionFalselyReportingToSecurity"
+ locKey: "crime-assist-question-falselyreportingtosecurity"
+ onYes: "resultPerjuryOrFalseReport"
+ onNo: "questionHappenInCourt"
+
+- type: crimeAssistPage
+ id: "questionHappenInCourt"
+ locKey: "crime-assist-question-happenincourt"
+ onYes: "resultContemptOfCourt"
+ onNo: "questionDuringActiveInvestigation"
+
+- type: crimeAssistPage
+ id: "questionDuringActiveInvestigation"
+ locKey: "crime-assist-question-duringactiveinvestigation"
+ onYes: "resultObstructionOfJustice"
+ onNo: "questionToCommandStaff"
+
+- type: crimeAssistPage
+ id: "questionToCommandStaff"
+ locKey: "crime-assist-question-tocommandstaff"
+ onYes: "resultSedition"
+ onNo: "questionWasItCommandItself"
+
+- type: crimeAssistPage
+ id: "questionWasItCommandItself"
+ locKey: "crime-assist-question-wasitcommanditself"
+ onYes: "resultAbuseOfPower"
+ onNo: "resultHooliganism"
+
+- type: crimeAssistPage
+ id: "resultInnocent"
+ locKeyTitle: "crime-assist-crime-innocent"
+ locKeyDescription: "crime-assist-crimedetail-innocent"
+ locKeySeverity: "crime-assist-crimetype-innocent"
+ locKeyPunishment: "crime-assist-crimepunishment-innocent"
+
+- type: crimeAssistPage
+ id: "resultAnimalCruelty"
+ locKeyTitle: "crime-assist-crime-animalcruelty"
+ locKeyDescription: "crime-assist-crimedetail-animalcruelty"
+ locKeySeverity: "crime-assist-crimetype-misdemeanour"
+ locKeyPunishment: "crime-assist-crimepunishment-animalcruelty"
+
+- type: crimeAssistPage
+ id: "resultTheft"
+ locKeyTitle: "crime-assist-crime-theft"
+ locKeyDescription: "crime-assist-crimedetail-theft"
+ locKeySeverity: "crime-assist-crimetype-misdemeanour"
+ locKeyPunishment: "crime-assist-crimepunishment-theft"
+
+- type: crimeAssistPage
+ id: "resultTrespass"
+ locKeyTitle: "crime-assist-crime-trespass"
+ locKeyDescription: "crime-assist-crimedetail-trespass"
+ locKeySeverity: "crime-assist-crimetype-misdemeanour"
+ locKeyPunishment: "crime-assist-crimepunishment-trespass"
+
+- type: crimeAssistPage
+ id: "resultVandalism"
+ locKeyTitle: "crime-assist-crime-vandalism"
+ locKeyDescription: "crime-assist-crimedetail-vandalism"
+ locKeySeverity: "crime-assist-crimetype-misdemeanour"
+ locKeyPunishment: "crime-assist-crimepunishment-vandalism"
+
+- type: crimeAssistPage
+ id: "resultHooliganism"
+ locKeyTitle: "crime-assist-crime-hooliganism"
+ locKeyDescription: "crime-assist-crimedetail-hooliganism"
+ locKeySeverity: "crime-assist-crimetype-misdemeanour"
+ locKeyPunishment: "crime-assist-crimepunishment-hooliganism"
+
+- type: crimeAssistPage
+ id: "resultManslaughter"
+ locKeyTitle: "crime-assist-crime-manslaughter"
+ locKeyDescription: "crime-assist-crimedetail-manslaughter"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-manslaughter"
+
+- type: crimeAssistPage
+ id: "resultGrandTheft"
+ locKeyTitle: "crime-assist-crime-grandtheft"
+ locKeyDescription: "crime-assist-crimedetail-grandtheft"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-grandtheft"
+
+- type: crimeAssistPage
+ id: "resultSabotage"
+ locKeyTitle: "crime-assist-crime-sabotage"
+ locKeyDescription: "crime-assist-crimedetail-sabotage"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-sabotage"
+
+- type: crimeAssistPage
+ id: "resultMindbreaking"
+ locKeyTitle: "crime-assist-crime-mindbreaking"
+ locKeyDescription: "crime-assist-crimedetail-mindbreaking"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-mindbreaking"
+
+- type: crimeAssistPage
+ id: "resultAssault"
+ locKeyTitle: "crime-assist-crime-assault"
+ locKeyDescription: "crime-assist-crimedetail-assault"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-assault"
+
+- type: crimeAssistPage
+ id: "resultAbuseOfPower"
+ locKeyTitle: "crime-assist-crime-abuseofpower"
+ locKeyDescription: "crime-assist-crimedetail-abuseofpower"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-abuseofpower"
+
+- type: crimeAssistPage
+ id: "resultPossession"
+ locKeyTitle: "crime-assist-crime-possession"
+ locKeyDescription: "crime-assist-crimedetail-possession"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-possession"
+
+- type: crimeAssistPage
+ id: "resultEndangerment"
+ locKeyTitle: "crime-assist-crime-endangerment"
+ locKeyDescription: "crime-assist-crimedetail-endangerment"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-endangerment"
+
+- type: crimeAssistPage
+ id: "resultBreakingAndEntering"
+ locKeyTitle: "crime-assist-crime-breakingandentering"
+ locKeyDescription: "crime-assist-crimedetail-breakingandentering"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-breakingandentering"
+
+- type: crimeAssistPage
+ id: "resultRioting"
+ locKeyTitle: "crime-assist-crime-rioting"
+ locKeyDescription: "crime-assist-crimedetail-rioting"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-rioting"
+
+- type: crimeAssistPage
+ id: "resultContemptOfCourt"
+ locKeyTitle: "crime-assist-crime-contemptofcourt"
+ locKeyDescription: "crime-assist-crimedetail-contemptofcourt"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-contemptofcourt"
+
+- type: crimeAssistPage
+ id: "resultBlackMarketeering"
+ locKeyTitle: "crime-assist-crime-blackmarketeering"
+ locKeyDescription: "crime-assist-crimedetail-blackmarketeering"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-blackmarketeering"
+
+- type: crimeAssistPage
+ id: "resultPerjuryOrFalseReport"
+ locKeyTitle: "crime-assist-crime-perjuryfalsereport"
+ locKeyDescription: "crime-assist-crimedetail-perjuryfalsereport"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-perjuryfalsereport"
+
+- type: crimeAssistPage
+ id: "resultObstructionOfJustice"
+ locKeyTitle: "crime-assist-crime-obstructionofjustice"
+ locKeyDescription: "crime-assist-crimedetail-obstructionofjustice"
+ locKeySeverity: "crime-assist-crimetype-felony"
+ locKeyPunishment: "crime-assist-crimepunishment-obstructionofjustice"
+
+- type: crimeAssistPage
+ id: "resultMurder"
+ locKeyTitle: "crime-assist-crime-murder"
+ locKeyDescription: "crime-assist-crimedetail-murder"
+ locKeySeverity: "crime-assist-crimetype-capital"
+ locKeyPunishment: "crime-assist-crimepunishment-murder"
+
+- type: crimeAssistPage
+ id: "resultTerrorism"
+ locKeyTitle: "crime-assist-crime-terrorism"
+ locKeyDescription: "crime-assist-crimedetail-terrorism"
+ locKeySeverity: "crime-assist-crimetype-capital"
+ locKeyPunishment: "crime-assist-crimepunishment-terrorism"
+
+- type: crimeAssistPage
+ id: "resultGrandSabotage"
+ locKeyTitle: "crime-assist-crime-grandsabotage"
+ locKeyDescription: "crime-assist-crimedetail-grandsabotage"
+ locKeySeverity: "crime-assist-crimetype-capital"
+ locKeyPunishment: "crime-assist-crimepunishment-grandsabotage"
+
+- type: crimeAssistPage
+ id: "resultDecorporealisation"
+ locKeyTitle: "crime-assist-crime-decorporealisation"
+ locKeyDescription: "crime-assist-crimedetail-decorporealisation"
+ locKeySeverity: "crime-assist-crimetype-capital"
+ locKeyPunishment: "crime-assist-crimepunishment-decorporealisation"
+
+- type: crimeAssistPage
+ id: "resultKidnapping"
+ locKeyTitle: "crime-assist-crime-kidnapping"
+ locKeyDescription: "crime-assist-crimedetail-kidnapping"
+ locKeySeverity: "crime-assist-crimetype-capital"
+ locKeyPunishment: "crime-assist-crimepunishment-kidnapping"
+
+- type: crimeAssistPage
+ id: "resultSedition"
+ locKeyTitle: "crime-assist-crime-sedition"
+ locKeyDescription: "crime-assist-crimedetail-sedition"
+ locKeySeverity: "crime-assist-crimetype-capital"
+ locKeyPunishment: "crime-assist-crimepunishment-sedition"
+
+- type: crimeAssistPage
+ id: "resultSexualHarassment"
+ locKeyTitle: "crime-assist-crime-sexualharassment"
+ locKeyDescription: "crime-assist-crimedetail-sexualharassment"
+ locKeySeverity: "crime-assist-crimetype-capital"
+ locKeyPunishment: "crime-assist-crimepunishment-sexualharassment"
diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
index 72942cbb916a30..6ce0d54e284a21 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
@@ -173,6 +173,12 @@
accentVColor: "#A32D26"
- type: Icon
state: pda-interncadet
+ - type: CartridgeLoader # DeltaV - Crime Assist
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - CrimeAssistCartridge
- type: entity
parent: BasePDA
@@ -410,6 +416,12 @@
borderColor: "#6f6192"
- type: Icon
state: pda-lawyer
+ - type: CartridgeLoader # DeltaV - Crime Assist
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - CrimeAssistCartridge
- type: entity
parent: BasePDA
@@ -605,6 +617,12 @@
accentHColor: "#447987"
- type: Icon
state: pda-hos
+ - type: CartridgeLoader # DeltaV - Crime Assist
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - CrimeAssistCartridge
- type: entity
parent: BasePDA
@@ -620,6 +638,12 @@
accentVColor: "#949137"
- type: Icon
state: pda-warden
+ - type: CartridgeLoader # DeltaV - Crime Assist
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - CrimeAssistCartridge
- type: entity
parent: BasePDA
@@ -634,6 +658,12 @@
borderColor: "#A32D26"
- type: Icon
state: pda-security
+ - type: CartridgeLoader # DeltaV - Crime Assist
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - CrimeAssistCartridge
- type: entity
parent: BasePDA
@@ -910,6 +940,12 @@
borderColor: "#774705"
- type: Icon
state: pda-detective
+ - type: CartridgeLoader # DeltaV - Crime Assist
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - CrimeAssistCartridge
- type: entity
parent: BaseMedicalPDA
@@ -1011,6 +1047,12 @@
accentVColor: "#DFDFDF"
- type: Icon
state: pda-seniorofficer
+ - type: CartridgeLoader # DeltaV - Crime Assist
+ preinstalled:
+ - CrewManifestCartridge
+ - NotekeeperCartridge
+ - NewsReadCartridge
+ - CrimeAssistCartridge
- type: entity
parent: SyndiPDA
diff --git a/Resources/Textures/DeltaV/Icons/cri.rsi/cri.png b/Resources/Textures/DeltaV/Icons/cri.rsi/cri.png
new file mode 100644
index 00000000000000..b31d02763ca9a8
Binary files /dev/null and b/Resources/Textures/DeltaV/Icons/cri.rsi/cri.png differ
diff --git a/Resources/Textures/DeltaV/Icons/cri.rsi/meta.json b/Resources/Textures/DeltaV/Icons/cri.rsi/meta.json
new file mode 100644
index 00000000000000..edeccd8f6d6da2
--- /dev/null
+++ b/Resources/Textures/DeltaV/Icons/cri.rsi/meta.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC0-1.0",
+ "copyright": "IcedQuinn",
+ "states": [
+ {
+ "name": "cri"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/cart-cri.png b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/cart-cri.png
new file mode 100644
index 00000000000000..4da4343e58455a
Binary files /dev/null and b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/cart-cri.png differ
diff --git a/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/meta.json b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/meta.json
new file mode 100644
index 00000000000000..293870d3a3bd27
--- /dev/null
+++ b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/meta.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Timfa",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "cart-cri"
+ }
+ ]
+}
\ No newline at end of file