diff --git a/code/modules/admin/modify_robot.dm b/code/modules/admin/modify_robot.dm
index c02457152f..429f99805d 100644
--- a/code/modules/admin/modify_robot.dm
+++ b/code/modules/admin/modify_robot.dm
@@ -12,9 +12,18 @@
/datum/eventkit/modify_robot
var/mob/living/silicon/robot/target
var/mob/living/silicon/robot/source
+ var/ion_law = "IonLaw"
+ var/zeroth_law = "ZerothLaw"
+ var/inherent_law = "InherentLaw"
+ var/supplied_law = "SuppliedLaw"
+ var/supplied_law_position = MIN_SUPPLIED_LAW_NUMBER
+ var/list/datum/ai_laws/law_list
/datum/eventkit/modify_robot/New()
. = ..()
+ law_list = new()
+ init_subtypes(/datum/ai_laws, law_list)
+ law_list = dd_sortedObjectList(law_list)
/datum/eventkit/modify_robot/tgui_close()
if(source)
@@ -97,6 +106,27 @@
continue
all_robots += list(list("displayText" = "[R]", "value" = "\ref[R]"))
.["all_robots"] = all_robots
+ // Law data
+ .["ion_law_nr"] = ionnum()
+ .["ion_law"] = ion_law
+ .["zeroth_law"] = zeroth_law
+ .["inherent_law"] = inherent_law
+ .["supplied_law"] = supplied_law
+ .["supplied_law_position"] = supplied_law_position
+
+ package_laws(., "zeroth_laws", list(target.laws.zeroth_law))
+ package_laws(., "ion_laws", target.laws.ion_laws)
+ package_laws(., "inherent_laws", target.laws.inherent_laws)
+ package_laws(., "supplied_laws", target.laws.supplied_laws)
+
+ .["isAI"] = isAI(target)
+
+ var/list/channels = list()
+ for(var/ch_name in target.law_channels())
+ channels[++channels.len] = list("channel" = ch_name)
+ .["channel"] = target.lawchannel
+ .["channels"] = channels
+ .["law_sets"] = package_multiple_laws(law_list)
/datum/eventkit/modify_robot/tgui_state(mob/user)
@@ -356,6 +386,96 @@
target.idcard.access -= get_all_station_access()
target.idcard.access -= access_synth
return TRUE
+ if("law_channel")
+ if(params["law_channel"] in target.law_channels())
+ target.lawchannel = params["law_channel"]
+ return TRUE
+ if("state_law")
+ var/datum/ai_law/AL = locate(params["ref"]) in target.laws.all_laws()
+ if(AL)
+ var/state_law = text2num(params["state_law"])
+ target.laws.set_state_law(AL, state_law)
+ return TRUE
+ if("add_zeroth_law")
+ if(zeroth_law && !target.laws.zeroth_law)
+ target.set_zeroth_law(zeroth_law)
+ return TRUE
+ if("add_ion_law")
+ if(ion_law)
+ target.add_ion_law(ion_law)
+ return TRUE
+ if("add_inherent_law")
+ if(inherent_law)
+ target.add_inherent_law(inherent_law)
+ return TRUE
+ if("add_supplied_law")
+ if(supplied_law && supplied_law_position >= 1 && MIN_SUPPLIED_LAW_NUMBER <= MAX_SUPPLIED_LAW_NUMBER)
+ target.add_supplied_law(supplied_law_position, supplied_law)
+ return TRUE
+ if("change_zeroth_law")
+ var/new_law = sanitize(params["val"])
+ if(new_law && new_law != zeroth_law)
+ zeroth_law = new_law
+ return TRUE
+ if("change_ion_law")
+ var/new_law = sanitize(params["val"])
+ if(new_law && new_law != ion_law)
+ ion_law = new_law
+ return TRUE
+ if("change_inherent_law")
+ var/new_law = sanitize(params["val"])
+ if(new_law && new_law != inherent_law)
+ inherent_law = new_law
+ return TRUE
+ if("change_supplied_law")
+ var/new_law = sanitize(params["val"])
+ if(new_law && new_law != supplied_law)
+ supplied_law = new_law
+ return TRUE
+ if("change_supplied_law_position")
+ var/new_position = tgui_input_number(usr, "Enter new supplied law position between 1 and [MAX_SUPPLIED_LAW_NUMBER], inclusive. Inherent laws at the same index as a supplied law will not be stated.", "Law Position", supplied_law_position, MAX_SUPPLIED_LAW_NUMBER, 1)
+ if(isnum(new_position))
+ supplied_law_position = CLAMP(new_position, 1, MAX_SUPPLIED_LAW_NUMBER)
+ return TRUE
+ if("edit_law")
+ var/datum/ai_law/AL = locate(params["edit_law"]) in target.laws.all_laws()
+ if(AL)
+ var/new_law = sanitize(tgui_input_text(usr, "Enter new law. Leaving the field blank will cancel the edit.", "Edit Law", AL.law))
+ if(new_law && new_law != AL.law)
+ log_and_message_admins("has changed a law of [target] from '[AL.law]' to '[new_law]'")
+ AL.law = new_law
+ return TRUE
+ if("delete_law")
+ var/datum/ai_law/AL = locate(params["delete_law"]) in target.laws.all_laws()
+ if(AL)
+ target.delete_law(AL)
+ return TRUE
+ if("state_laws")
+ target.statelaws(target.laws)
+ return TRUE
+ if("state_law_set")
+ var/datum/ai_laws/ALs = locate(params["state_law_set"]) in law_list
+ if(ALs)
+ target.statelaws(ALs)
+ return TRUE
+ if("transfer_laws")
+ var/datum/ai_laws/ALs = locate(params["transfer_laws"]) in law_list
+ if(ALs)
+ log_and_message_admins("has transfered the [ALs.name] laws to [target].")
+ ALs.sync(target, 0)
+ return TRUE
+
+ if("notify_laws")
+ to_chat(target, "Law Notice")
+ target.laws.show_laws(target)
+ if(isAI(target))
+ var/mob/living/silicon/ai/AI = target
+ for(var/mob/living/silicon/robot/R in AI.connected_robots)
+ to_chat(R, "Law Notice")
+ R.laws.show_laws(R)
+ if(usr != target)
+ to_chat(usr, "Laws displayed.")
+ return TRUE
/datum/eventkit/modify_robot/proc/get_target_items(var/mob/user)
var/list/target_items = list()
@@ -490,3 +610,22 @@
var/datum/robot_component/C = target.components[entry]
components += list(list("name" = C.name, "ref" = "\ref[C]", "brute_damage" = C.brute_damage, "electronics_damage" = C.electronics_damage, "max_damage" = C.max_damage, "installed" = C.installed, "exists" = (C.wrapped ? TRUE : FALSE)))
return components
+
+/datum/eventkit/modify_robot/proc/package_laws(var/list/data, var/field, var/list/datum/ai_law/laws)
+ var/list/packaged_laws = list()
+ for(var/datum/ai_law/AL in laws)
+ packaged_laws[++packaged_laws.len] = list("law" = AL.law, "index" = AL.get_index(), "state" = target.laws.get_state_law(AL), "ref" = "\ref[AL]")
+ data[field] = packaged_laws
+ data["has_[field]"] = packaged_laws.len
+
+/datum/eventkit/modify_robot/proc/package_multiple_laws(var/list/datum/ai_laws/laws)
+ var/list/law_sets = list()
+ for(var/datum/ai_laws/ALs in laws)
+ var/list/packaged_laws = list()
+ package_laws(packaged_laws, "zeroth_laws", list(ALs.zeroth_law, ALs.zeroth_law_borg))
+ package_laws(packaged_laws, "ion_laws", ALs.ion_laws)
+ package_laws(packaged_laws, "inherent_laws", ALs.inherent_laws)
+ package_laws(packaged_laws, "supplied_laws", ALs.supplied_laws)
+ law_sets[++law_sets.len] = list("name" = ALs.name, "header" = ALs.law_header, "ref" = "\ref[ALs]","laws" = packaged_laws)
+
+ return law_sets
diff --git a/tgui/packages/tgui/interfaces/LawManager.tsx b/tgui/packages/tgui/interfaces/LawManager.tsx
index 3632166b60..2b2838c7d0 100644
--- a/tgui/packages/tgui/interfaces/LawManager.tsx
+++ b/tgui/packages/tgui/interfaces/LawManager.tsx
@@ -1,4 +1,7 @@
+import { filter } from 'common/collections';
+import { flow } from 'common/fp';
import { BooleanLike } from 'common/react';
+import { createSearch } from 'common/string';
import { useBackend, useSharedState } from '../backend';
import {
@@ -76,30 +79,12 @@ export const LawManager = (props) => {
};
const LawManagerContent = (props) => {
+ const { data } = useBackend();
const [tabIndex, setTabIndex] = useSharedState('lawsTabIndex', 0);
-
- const tab: React.JSX.Element[] = [];
-
- tab[0] = ;
- tab[1] = ;
-
- return (
- <>
-
- setTabIndex(0)}>
- Law Management
-
- setTabIndex(1)}>
- Law Sets
-
-
- {tab[tabIndex]}
- >
+ const [searchLawName, setSearchLawName] = useSharedState(
+ 'searchLawName',
+ '',
);
-};
-
-const LawManagerLaws = (props) => {
- const { act, data } = useBackend();
const {
ion_law_nr,
@@ -121,8 +106,108 @@ const LawManagerLaws = (props) => {
isAdmin,
channel,
channels,
+ law_sets,
} = data;
+ const tab: React.JSX.Element[] = [];
+
+ tab[0] = (
+
+ );
+ tab[1] = (
+
+ );
+
+ return (
+ <>
+
+ setTabIndex(0)}>
+ Law Management
+
+ setTabIndex(1)}>
+ Law Sets
+
+
+ {tab[tabIndex]}
+ >
+ );
+};
+
+export const LawManagerLaws = (props: {
+ ion_law_nr: string;
+ ion_law: string;
+ zeroth_law: string;
+ inherent_law: string;
+ supplied_law: string;
+ supplied_law_position: number;
+ zeroth_laws: law[];
+ ion_laws: law[];
+ inherent_laws: law[];
+ supplied_laws: law[];
+ has_zeroth_laws: number;
+ has_ion_laws: number;
+ has_inherent_laws: number;
+ has_supplied_laws: number;
+ isAI: BooleanLike;
+ isMalf: BooleanLike;
+ isAdmin: BooleanLike;
+ channel: string;
+ channels: { channel: string }[];
+ hasScroll?: boolean;
+ sectionHeight?: string;
+}) => {
+ const { act } = useBackend();
+ const {
+ ion_law_nr,
+ ion_law,
+ zeroth_law,
+ inherent_law,
+ supplied_law,
+ supplied_law_position,
+ zeroth_laws,
+ has_zeroth_laws,
+ ion_laws,
+ has_ion_laws,
+ inherent_laws,
+ has_inherent_laws,
+ supplied_laws,
+ has_supplied_laws,
+ isAI,
+ isMalf,
+ isAdmin,
+ channel,
+ channels,
+ hasScroll,
+ sectionHeight,
+ } = props;
+
let allLaws = zeroth_laws
.map((law) => {
law.zero = true;
@@ -131,19 +216,37 @@ const LawManagerLaws = (props) => {
.concat(inherent_laws);
return (
-
+
{has_ion_laws ? (
-
+
) : (
''
)}
{has_zeroth_laws || has_inherent_laws ? (
-
+
) : (
''
)}
{has_supplied_laws ? (
-
+
) : (
''
)}
@@ -283,12 +386,12 @@ const LawsTable = (props: {
title: string;
noButtons?: BooleanLike;
[rest: string]: any;
+ isMalf: BooleanLike;
+ isAdmin: BooleanLike;
}) => {
- const { act, data } = useBackend();
+ const { act } = useBackend();
- const { isMalf, isAdmin } = data;
-
- const { laws, title, noButtons, ...rest } = props;
+ const { laws, title, noButtons, isMalf, isAdmin, ...rest } = props;
return (
@@ -360,10 +463,24 @@ const LawsTable = (props: {
);
};
-const LawManagerLawSets = (props) => {
- const { act, data } = useBackend();
+export const LawManagerLawSets = (props: {
+ law_sets: law_pack[];
+ ion_law_nr: string;
+ searchLawName: string;
+ onSearchLawName: Function;
+ isAdmin: BooleanLike;
+ isMalf: BooleanLike;
+}) => {
+ const { act } = useBackend();
- const { isMalf, law_sets, ion_law_nr } = data;
+ const {
+ law_sets,
+ ion_law_nr,
+ searchLawName,
+ onSearchLawName,
+ isMalf,
+ isAdmin,
+ } = props;
return (
<>
@@ -371,8 +488,14 @@ const LawManagerLawSets = (props) => {
Remember: Stating laws other than those currently loaded may be grounds
for decommissioning! - NanoTrasen
+ onSearchLawName(value)}
+ />
{law_sets.length
- ? law_sets.map((laws) => (
+ ? prepareSearch(law_sets, searchLawName).map((laws) => (
{
noButtons
laws={laws.laws.ion_laws}
title={ion_law_nr + ' Laws:'}
+ isAdmin={isAdmin}
+ isMalf={isMalf}
/>
) : (
''
@@ -412,6 +537,8 @@ const LawManagerLawSets = (props) => {
noButtons
laws={laws.laws.zeroth_laws.concat(laws.laws.inherent_laws)}
title={laws.header}
+ isAdmin={isAdmin}
+ isMalf={isMalf}
/>
) : (
''
@@ -421,6 +548,8 @@ const LawManagerLawSets = (props) => {
noButtons
laws={laws.laws.supplied_laws}
title="Supplied Laws"
+ isAdmin={isAdmin}
+ isMalf={isMalf}
/>
) : (
''
@@ -431,3 +560,23 @@ const LawManagerLawSets = (props) => {
>
);
};
+
+const prepareSearch = (
+ laws: law_pack[],
+ searchText: string = '',
+): law_pack[] => {
+ const testSearch = createSearch(
+ searchText,
+ (law: law_pack) => law.name + law.header,
+ );
+ return flow([
+ (laws: law_pack[]) => {
+ // Optional search term
+ if (!searchText) {
+ return laws;
+ } else {
+ return filter(laws, testSearch);
+ }
+ },
+ ])(laws);
+};
diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx
index cd48cc8aef..dba66ce648 100644
--- a/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx
+++ b/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx
@@ -1,18 +1,19 @@
import { useEffect, useState } from 'react';
import { useBackend } from 'tgui/backend';
import {
- Box,
Button,
Divider,
Dropdown,
Input,
LabeledList,
NoticeBox,
+ Section,
Stack,
Tabs,
} from 'tgui/components';
import { Window } from 'tgui/layouts';
+import { LawManagerLaws, LawManagerLawSets } from '../LawManager';
import { ModifyRobotNoModule } from './ModifyRobotNoModule';
import { ModifyRobotAccess } from './ModifyRobotTabs/ModifyRobotAccess';
import { ModifyRobotComponent } from './ModifyRobotTabs/ModifyRobotComponent';
@@ -34,10 +35,29 @@ export const ModifyRobot = (props) => {
cell_options,
id_icon,
access_options,
+ ion_law_nr,
+ ion_law,
+ zeroth_law,
+ inherent_law,
+ supplied_law,
+ supplied_law_position,
+ zeroth_laws,
+ ion_laws,
+ inherent_laws,
+ supplied_laws,
+ has_zeroth_laws,
+ has_ion_laws,
+ has_inherent_laws,
+ has_supplied_laws,
+ isAI,
+ channel,
+ channels,
+ law_sets,
} = data;
const [tab, setTab] = useState(0);
const [robotName, setRobotName] = useState(target ? target.name : '');
+ const [searchLawName, setSearchLawName] = useState('');
useEffect(() => {
if (target?.name) {
@@ -67,8 +87,43 @@ export const ModifyRobot = (props) => {
all_access={access_options!}
/>
);
- tabs[6] = ;
- tabs[7] = ;
+ tabs[6] = (
+
+ );
+ tabs[7] = (
+
+ );
return (
diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/types.ts b/tgui/packages/tgui/interfaces/ModifyRobot/types.ts
index bdf75f31c6..02b840948b 100644
--- a/tgui/packages/tgui/interfaces/ModifyRobot/types.ts
+++ b/tgui/packages/tgui/interfaces/ModifyRobot/types.ts
@@ -9,6 +9,24 @@ export type Data = {
cell_options: Record;
id_icon: string;
access_options: Access[] | undefined;
+ ion_law_nr: string;
+ ion_law: string;
+ zeroth_law: string;
+ inherent_law: string;
+ supplied_law: string;
+ supplied_law_position: number;
+ zeroth_laws: law[];
+ ion_laws: law[];
+ inherent_laws: law[];
+ supplied_laws: law[];
+ has_zeroth_laws: number;
+ has_ion_laws: number;
+ has_inherent_laws: number;
+ has_supplied_laws: number;
+ isAI: BooleanLike;
+ channel: string;
+ channels: { channel: string }[];
+ law_sets: law_pack[];
};
export type DropdownEntry = {
@@ -89,3 +107,27 @@ export type Cell = {
};
export type Access = { id: number; name: string };
+
+type law_pack = {
+ name: string;
+ header: string;
+ ref: string;
+ laws: {
+ zeroth_laws: law[];
+ has_zeroth_laws: number;
+ ion_laws: law[];
+ has_ion_laws: number;
+ inherent_laws: law[];
+ has_inherent_laws: number;
+ supplied_laws: law[];
+ has_supplied_laws: number;
+ };
+};
+
+type law = {
+ law: string;
+ index: number;
+ state: number;
+ ref: string;
+ zero: boolean; // Local UI var
+};