[MIRROR] Adds the "Law panel", a control center for admins interacting with silicon laws [MDB IGNORE] (#19655)

* Adds the "Law panel", a control center for admins interacting with silicon laws

* Update admin_verbs.dm

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-03-10 06:10:46 +00:00
committed by GitHub
co-authored by MrMelbert Gandalf
parent 5b003cd594
commit fa7538b5dc
15 changed files with 841 additions and 112 deletions
+2 -2
View File
@@ -736,7 +736,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
desc = "Laws have potentially been uploaded to or removed from this unit. Please be aware of any changes \
so as to remain in compliance with the most up-to-date laws."
icon_state = ALERT_NEW_LAW
timeout = 300
timeout = 30 SECONDS
/atom/movable/screen/alert/hackingapc
name = "Hacking APC"
@@ -744,7 +744,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
complete, you will have exclusive control of it, and you will gain \
additional processing time to unlock more malfunction abilities."
icon_state = ALERT_HACKING_APC
timeout = 600
timeout = 60 SECONDS
var/atom/target = null
/atom/movable/screen/alert/hackingapc/Click()
+154 -60
View File
@@ -101,16 +101,41 @@
return null
/datum/ai_laws
/// The name of the lawset
var/name = "Unknown Laws"
var/zeroth = null
var/zeroth_borg = null
var/list/inherent = list()
var/list/supplied = list()
var/list/ion = list()
var/list/hacked = list()
/// The silicon linked to this lawset
var/mob/living/silicon/owner
/// The ID of this lawset, pretty much only used to tell if we're default or not
var/id = DEFAULT_AI_LAWID
/// If TRUE, the zeroth law of this AI is protected and cannot be removed by players under normal circumstances.
var/protected_zeroth = FALSE
/// Zeroth law
/// A lawset can only have 1 zeroth law, it's the top dog.
/// Nothing removes it unless it's admin forced
var/zeroth = null
/// Zeroth borg law
/// It's just a zeroth law but specially themed for cyborgs
/// ("follow your master" vs "accomplish your objectives")
var/zeroth_borg = null
/// Core laws
/// Inherent laws are the "core" laws of the AI
/// Reseting the AI will not remove these, these are intrinsit to whatever lawset they are running.
var/list/inherent = list()
/// Supplied laws
/// Supplied laws are supplied in addition to the inherent laws - after the fact
/// These laws will go away when an AI is reset
var/list/supplied = list()
/// Ion laws
/// Special randomized (usually) laws which are above all over laws
/// These laws will go away when an AI is reset
var/list/ion = list()
/// Hacked laws
/// Syndicate uploaded laws which are above all other laws
/// These laws will go away when an AI is reset
var/list/hacked = list()
/datum/ai_laws/Destroy(force = FALSE, ...)
if(!QDELETED(owner)) //Stopgap to help with laws randomly being lost. This stack_trace will hopefully help find the real issues.
if(force) //Unless we're forced...
@@ -153,7 +178,14 @@
default_laws = new default_laws()
inherent = default_laws.inherent
/datum/ai_laws/proc/get_law_amount(groups)
/**
* Gets the number of how many laws this AI has
*
* * groups - What groups to count laws from? By default counts all groups
*
* Returns a number, the number of laws we have
*/
/datum/ai_laws/proc/get_law_amount(list/groups = list(LAW_ZEROTH, LAW_ION, LAW_HACKED, LAW_INHERENT, LAW_SUPPLIED))
var/law_amount = 0
if(zeroth && (LAW_ZEROTH in groups))
law_amount++
@@ -170,37 +202,141 @@
law_amount++
return law_amount
/datum/ai_laws/proc/set_zeroth_law(law, law_borg = null)
/**
* Sets this lawset's zeroth law to the passed law
*
* Also can set the zeroth borg law, if this lawset is for master AIs.
* The zeroth borg law allows for AIs with zeroth laws to give a differing zeroth law to their child cyborgs
*/
/datum/ai_laws/proc/set_zeroth_law(law, law_borg)
zeroth = law
if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO
zeroth_borg = law_borg
/datum/ai_laws/proc/add_inherent_law(law)
if (!(law in inherent))
inherent += law
/**
* Unsets the zeroth (and zeroth borg) law from this lawset
*
* This will NOT unset a malfunctioning AI's zero law if force is not true
*
* Returns TRUE on success, or false otherwise
*/
/datum/ai_laws/proc/clear_zeroth_law(force = FALSE)
if(force)
zeroth = null
zeroth_borg = null
return TRUE
// Protected zeroeth laws (malf, admin) shouldn't be wiped
if(protected_zeroth)
return FALSE
// If the owner is an antag (has a special role) they also shouldn't be wiped
if(owner?.mind?.special_role)
return FALSE
if (isAI(owner))
var/mob/living/silicon/ai/ai_owner = owner
if(ai_owner.deployed_shell?.mind?.special_role)
return FALSE
zeroth = null
zeroth_borg = null
return TRUE
/// Adds the passed law as an inherent law.
/// Simply adds it to the bottom of the inherent law list.
/// No duplicate laws allowed.
/datum/ai_laws/proc/add_inherent_law(law)
inherent |= law
/// Removes the passed law from the inherent law list.
/datum/ai_laws/proc/remove_inherent_law(law)
inherent -= law
/// Clears all inherent laws from this lawset.
/datum/ai_laws/proc/clear_inherent_laws()
inherent.Cut()
/// Adds the passed law as an ion law.
/datum/ai_laws/proc/add_ion_law(law)
ion += law
/// Removes the passed law from the ion law list.
/datum/ai_laws/proc/remove_ion_law(law)
ion -= law
/// Clears all ion laws.
/datum/ai_laws/proc/clear_ion_laws()
ion.Cut()
/// Adds the passed law as an hacked law.
/datum/ai_laws/proc/add_hacked_law(law)
hacked += law
/datum/ai_laws/proc/clear_inherent_laws()
qdel(inherent)
inherent = list()
/// Removes the passed law from the hacked law list.
/datum/ai_laws/proc/remove_hacked_law(law)
hacked -= law
/// Clears all hacked laws.
/datum/ai_laws/proc/clear_hacked_laws()
hacked.Cut()
/// Adds the passed law as a supplied law at the passed priority level.
/// Will override any existing supplied laws at that priority level.
/datum/ai_laws/proc/add_supplied_law(number, law)
while (supplied.len < number + 1)
supplied += ""
supplied[number + 1] = law
/// Removes the supplied law at the passed number.
/datum/ai_laws/proc/remove_supplied_law_by_num(number)
supplied[number] = ""
/// Removes the supplied law by law text, replacing it with a blank.
/datum/ai_laws/proc/remove_supplied_law_by_law(law)
var/lawindex = supplied.Find(law)
if(!lawindex)
return
supplied[lawindex] = ""
/// Clears all supplied laws.
/datum/ai_laws/proc/clear_supplied_laws()
supplied.Cut()
/**
* Removes the law at the passed index of both inherent and supplied laws combined.
*
* For example, if a lawset has 3 inherent and 3 supplied laws...
* Calling this with number = 2 will remove the second inherent law while
* calling this with number = 4 will remove the first supplied law
*
* Returns the law text of what law that was removed.
*/
/datum/ai_laws/proc/remove_law(number)
if(number <= 0)
return
if(inherent.len && number <= inherent.len)
. = inherent[number]
inherent -= .
return
var/list/supplied_laws = list()
for(var/index in 1 to supplied.len)
var/law = supplied[index]
if(length(law) > 0)
supplied_laws += index //storing the law number instead of the law
if(supplied_laws.len && number <= (inherent.len+supplied_laws.len))
var/law_to_remove = supplied_laws[number-inherent.len]
. = supplied[law_to_remove]
supplied -= .
return
/**
* Removes a random law and replaces it with the new one
*
* Args:
* law - The law that is being uploaded
* remove_law_groups - A list of law categories that can be deleted from
* remove_law_groups - A list of law categories that can be deleted from
* insert_law_group - The law category that the law will be inserted into
**/
/datum/ai_laws/proc/replace_random_law(law, remove_law_groups, insert_law_group)
@@ -254,6 +390,7 @@
supplied.Insert(i, law)
/datum/ai_laws/proc/shuffle_laws(list/groups)
RETURN_TYPE(/list)
var/list/laws = list()
if(ion.len && (LAW_ION in groups))
laws += ion
@@ -284,52 +421,9 @@
break
i++
/datum/ai_laws/proc/remove_law(number)
if(number <= 0)
return
if(inherent.len && number <= inherent.len)
. = inherent[number]
inherent -= .
return
var/list/supplied_laws = list()
for(var/index in 1 to supplied.len)
var/law = supplied[index]
if(length(law) > 0)
supplied_laws += index //storing the law number instead of the law
if(supplied_laws.len && number <= (inherent.len+supplied_laws.len))
var/law_to_remove = supplied_laws[number-inherent.len]
. = supplied[law_to_remove]
supplied -= .
return
/datum/ai_laws/proc/clear_supplied_laws()
supplied = list()
/datum/ai_laws/proc/clear_ion_laws()
ion = list()
/datum/ai_laws/proc/clear_hacked_laws()
hacked = list()
/datum/ai_laws/proc/show_laws(who)
/datum/ai_laws/proc/show_laws(mob/to_who)
var/list/printable_laws = get_law_list(include_zeroth = TRUE)
for(var/law in printable_laws)
to_chat(who,law)
/datum/ai_laws/proc/clear_zeroth_law(force) //only removes zeroth from antag ai if force is 1
if(force)
zeroth = null
zeroth_borg = null
return TRUE
if(owner?.mind?.special_role)
return FALSE
if (isAI(owner))
var/mob/living/silicon/ai/A=owner
if(A?.deployed_shell?.mind?.special_role)
return FALSE
zeroth = null
zeroth_borg = null
return TRUE
to_chat(to_who, examine_block(jointext(printable_laws, "\n")))
/datum/ai_laws/proc/associate(mob/living/silicon/M)
if(!owner)
+1 -2
View File
@@ -18,7 +18,7 @@
..()
/obj/item/ai_module/remove/install(datum/ai_laws/law_datum, mob/user)
if(lawpos > (law_datum.get_law_amount(list(LAW_INHERENT = 1, LAW_SUPPLIED = 1))))
if(lawpos > law_datum.get_law_amount(list(LAW_INHERENT, LAW_SUPPLIED)))
to_chat(user, span_warning("There is no law [lawpos] to delete!"))
return
..()
@@ -62,4 +62,3 @@
else
law_datum.clear_inherent_laws()
law_datum.clear_zeroth_law(0)
+1 -1
View File
@@ -95,7 +95,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/
/client/proc/toggle_combo_hud, /* toggle display of the combination pizza antag and taco sci/med/eng hud */
/client/proc/toggle_view_range, /*changes how far we can see*/
/client/proc/cmd_admin_law_panel,
/datum/admins/proc/toggleaooc, /*Toggle Antag OOC - SKYRAT EDIT ADDITION*/
/datum/admins/proc/toggledchat, /*SKYRAT EDIT ADDITION*/
/datum/admins/proc/togglesooc, /*Toggle Security OOC - SKYRAT EDIT ADDITION*/
+311
View File
@@ -0,0 +1,311 @@
/client/proc/cmd_admin_law_panel()
set category = "Admin.Events"
set name = "Law Panel"
if(!check_rights(R_ADMIN))
return
SSblackbox.record_feedback("tally", "admin_verb", 1, "Law Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
var/datum/law_panel/tgui = new()
tgui.ui_interact(usr)
/datum/law_panel
/datum/law_panel/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Lawpanel")
ui.open()
/datum/law_panel/ui_state(mob/user)
return GLOB.admin_state
/datum/law_panel/ui_close(mob/user)
qdel(src)
/datum/law_panel/proc/add_law_helper(mob/living/user, mob/living/silicon/borgo)
var/list/lawtypes = list(LAW_ZEROTH, LAW_HACKED, LAW_ION, LAW_INHERENT, LAW_SUPPLIED) // in order of priority
var/lawtype = tgui_input_list(user, "Select law type", "Law type", lawtypes)
if(isnull(lawtype))
return FALSE
var/lawtext = tgui_input_text(user, "Input law text", "Law text")
if(!lawtext)
return FALSE
if(QDELETED(src) || QDELETED(borgo))
return
switch(lawtype)
if(LAW_ZEROTH)
if(borgo.laws.zeroth || borgo.laws.zeroth_borg)
var/zero_override_alert = tgui_alert(user, "This silicon already has a zeroth law, \
this will override their existing one. Are you sure?", "Zeroth law override", list("Yes", "No"))
if(zero_override_alert != "Yes" || QDELETED(src) || QDELETED(borgo))
return FALSE
borgo.laws.set_zeroth_law(lawtext)
borgo.laws.protected_zeroth = TRUE
if(LAW_ION)
borgo.laws.add_ion_law(lawtext)
if(LAW_HACKED)
borgo.laws.add_hacked_law(lawtext)
if(LAW_INHERENT)
borgo.laws.add_inherent_law(lawtext)
if(LAW_SUPPLIED)
borgo.laws.add_supplied_law(length(borgo.laws.supplied), lawtext) // Just goes to the end of the list
return TRUE
/datum/law_panel/proc/move_law_helper(mob/living/user, mob/living/silicon/borgo, direction, law)
var/list/relevant_laws = borgo.laws.inherent
var/lawindex = relevant_laws.Find(law)
if(!lawindex)
to_chat(user, span_danger("Something went wrong, we couldn't move that law."))
return FALSE
switch(direction)
if("up")
if(lawindex == length(relevant_laws)) // Already at the top? Sanity
to_chat(user, span_danger("Something went wrong, we couldn't move that law."))
return FALSE
relevant_laws.Swap(lawindex + 1, lawindex)
if("down")
if(lawindex == 1) // Already at the bottom? Sanity
to_chat(user, span_danger("Something went wrong, we couldn't move that law."))
return FALSE
relevant_laws.Swap(lawindex - 1, lawindex)
else
CRASH("Invalid direction ([direction]) passed to move_law_helper.")
return TRUE
/datum/law_panel/proc/edit_law_text_helper(mob/living/user, mob/living/silicon/borgo, lawtype, oldlaw)
var/newlaw = tgui_input_text(user, "Edit this law's text.", "Edit law", oldlaw)
if(!newlaw || QDELETED(src) || QDELETED(borgo))
return FALSE
var/list/relevant_laws
switch(lawtype)
if(LAW_INHERENT)
relevant_laws = borgo.laws.inherent
if(LAW_SUPPLIED)
relevant_laws = borgo.laws.supplied
if(LAW_HACKED)
relevant_laws = borgo.laws.hacked
if(LAW_ION)
relevant_laws = borgo.laws.ion
if(LAW_ZEROTH)
borgo.set_zeroth_law(newlaw, announce = FALSE)
borgo.laws.protected_zeroth = TRUE
return TRUE
else
return FALSE
var/lawindex = relevant_laws.Find(oldlaw)
if(!lawindex)
to_chat(user, span_danger("Something went wrong, we couldn't edit that law."))
return FALSE
relevant_laws[lawindex] = newlaw
return TRUE
/datum/law_panel/proc/edit_law_priority_helper(mob/living/user, mob/living/silicon/borgo, law)
var/old_prio = borgo.laws.supplied.Find(law)
if(!old_prio)
to_chat(user, span_danger("Something went wrong, we couldn't edit that law."))
return FALSE
var/new_prio = tgui_input_number(user, "Enter a new priority.", "Edit priority", old_prio, 50, 0)
if(!new_prio || QDELETED(src) || QDELETED(borgo))
return FALSE
// Sanity
if(old_prio != borgo.laws.supplied.Find(law))
to_chat(user, span_danger("[borgo]'s laws may have changed since you have edited priority, please re-try."))
return FALSE
// If it's far beyond any existing values, just re-add it normally
if(new_prio > length(borgo.laws.supplied))
borgo.laws.remove_supplied_law_by_num(old_prio)
borgo.laws.add_supplied_law(new_prio, law)
return TRUE
// Handle collisions
var/existing_law = borgo.laws.supplied[new_prio]
if(existing_law)
var/list/options = list("Swap", "Move up", "Move down", "Replace", "Cancel")
if(new_prio == 1)
// Nowhere to go from here
options -= "Move down"
var/swap_or_remove = tgui_alert(user, "There's already a law at that priority level. What should be done to it?", "Existing law", options)
if(swap_or_remove == "Cancel" || !swap_or_remove || QDELETED(src) || QDELETED(borgo))
return FALSE
// Sanity
if(law != borgo.laws.supplied[old_prio] || existing_law != borgo.laws.supplied[new_prio])
to_chat(user, span_danger("[borgo]'s laws have changed since you have edited priority, please re-try."))
return FALSE
if(swap_or_remove == "Swap")
borgo.laws.supplied.Swap(old_prio, new_prio)
return TRUE
if(swap_or_remove == "Replace")
borgo.laws.remove_supplied_law_by_num(new_prio, law)
borgo.laws.add_supplied_law(new_prio, law)
return TRUE
var/new_prio_for_old_law = new_prio + (swap_or_remove == "Move up" ? 1 : -1)
borgo.laws.remove_supplied_law_by_num(old_prio)
borgo.laws.remove_supplied_law_by_num(new_prio)
borgo.laws.add_supplied_law(new_prio, law)
borgo.laws.add_supplied_law(new_prio_for_old_law, existing_law)
return TRUE
// Sanity
if(old_prio != borgo.laws.supplied.Find(law))
to_chat(user, span_danger("[borgo]'s may laws have changed since you have edited priority, please re-try."))
return FALSE
// At this point the slot is free, insert it as normal
borgo.laws.remove_supplied_law_by_num(old_prio)
borgo.laws.add_supplied_law(new_prio, law)
return TRUE
/datum/law_panel/proc/remove_law_helper(mob/living/user, mob/living/silicon/borgo, lawtype, law)
switch(lawtype)
if(LAW_INHERENT)
borgo.laws.remove_inherent_law(law)
if(LAW_SUPPLIED)
borgo.laws.remove_supplied_law_by_law(law)
if(LAW_HACKED)
borgo.laws.remove_hacked_law(law)
if(LAW_ION)
borgo.laws.remove_ion_law(law)
if(LAW_ZEROTH)
borgo.laws.clear_zeroth_law(force = TRUE)
borgo.laws.protected_zeroth = FALSE
else
return FALSE
return TRUE
/datum/law_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(!check_rights(R_ADMIN))
qdel(src)
return FALSE
var/mob/living/silicon/borgo
if(params["ref"])
borgo = locate(params["ref"]) in GLOB.silicon_mobs
if(QDELETED(borgo))
to_chat(usr, span_danger("That cyborg is invalid."))
return TRUE
switch(action)
if("lawchange_logs")
usr.client?.list_law_changes()
return FALSE
if("force_state_laws")
borgo.statelaws()
return FALSE
if("announce_law_changes")
borgo.show_laws()
return FALSE
if("laws_updated_alert")
borgo.post_lawchange()
return FALSE
if("give_law_datum")
borgo.make_laws()
return TRUE
if("add_law")
. = add_law_helper(usr, borgo)
if("remove_law")
. = remove_law_helper(usr, borgo, params["lawtype"], params["law"])
if("move_law")
. = move_law_helper(usr, borgo, params["direction"], params["law"])
if("edit_law_text")
. = edit_law_text_helper(usr, borgo, params["lawtype"], params["law"])
if("edit_law_prio")
. = edit_law_priority_helper(usr, borgo, params["law"])
if(. && !QDELETED(borgo))
// One of our functions successfully changed a law
// If it was an AI with connected borgs, we should sync
borgo.try_sync_laws()
/datum/law_panel/ui_data(mob/user)
// Iterating over all silicons in existence every UI update is not exactly ideal,
// but considering this is an admin only UI, I'm considering it okay purely for better UX.
// If someone's copying this for player user or this becomes too laggy,
// change this to static data and just add a refresh button.
// You'll have to update static data every time the user changes a law though.
var/list/data = list()
var/list/all_silicons = list()
for(var/mob/living/silicon/borgo as anything in GLOB.silicon_mobs)
var/list/borg_information = list()
if(iscyborg(borgo))
var/mob/living/silicon/robot/cyborg = borgo
if(cyborg.shell)
continue
borg_information["master_ai"] = cyborg.connected_ai?.real_name
borg_information["borg_synced"] = cyborg.lawupdate
borg_information["borg_type"] = "Cyborg"
else if(isAI(borgo))
borg_information["borg_type"] = "AI"
else if(ispAI(borgo))
borg_information["borg_type"] = "PAI"
else
borg_information["borg_type"] = "Unknown"
borg_information["borg_name"] = borgo.real_name
borg_information["ref"] = REF(borgo)
var/datum/ai_laws/lawset = borgo.laws
if(!isnull(lawset))
var/list/borg_laws = list()
// zeroth law on top
if(lawset.zeroth || lawset.zeroth_borg)
UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_ZEROTH, "law" = lawset.zeroth || lawset.zeroth_borg, "num" = 0))
// then goes ion / hacked
for(var/law in lawset.hacked)
UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_HACKED, "law" = law, "num" = -1))
for(var/law in lawset.ion)
UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_ION, "law" = law, "num" = -1))
// normie laws
var/lawnum = 1
for(var/law in lawset.inherent)
UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_INHERENT, "law" = law, "num" = lawnum))
lawnum += 1
for(var/law in lawset.supplied)
if(law) // Supplied is full of a bunch of empties
UNTYPED_LIST_ADD(borg_laws, list("lawtype" = LAW_SUPPLIED, "law" = law, "num" = lawnum))
lawnum += 1
borg_information["laws"] = borg_laws
UNTYPED_LIST_ADD(all_silicons, borg_information)
data["all_silicons"] = all_silicons
return data
@@ -165,6 +165,7 @@
var/law_borg = "Accomplish your AI's objectives at all costs."
malf_ai.set_zeroth_law(law, law_borg)
malf_ai.laws.protected_zeroth = TRUE
malf_ai.set_syndie_radio()
to_chat(malf_ai, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
+4 -4
View File
@@ -142,14 +142,14 @@
else if(target_ai.key)
key = target_ai.key
to_chat(src, "<B>You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).</B>")
to_chat(src, "<B>To look at other parts of the station, click on yourself to get a camera menu.</B>")
to_chat(src, "<B>While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.</B>")
to_chat(src, span_bold("You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras)."))
to_chat(src, span_bold("To look at other parts of the station, click on yourself to get a camera menu."))
to_chat(src, span_bold("While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc."))
to_chat(src, "To use something, simply click on it.")
to_chat(src, "For department channels, use the following say commands:")
to_chat(src, ":o - AI Private, :c - Command, :s - Security, :e - Engineering, :u - Supply, :v - Service, :m - Medical, :n - Science, :h - Holopad.")
show_laws()
to_chat(src, "<b>These laws may be changed by other players, or by you being the traitor.</b>")
to_chat(src, span_bold("These laws may be changed by other players, random events, or by you becoming malfunctioning."))
job = "AI"
+10 -17
View File
@@ -2,25 +2,18 @@
/mob/living/silicon/ai/proc/show_laws_verb()
set category = "AI Commands"
set name = "Show Laws"
set desc = "Check what your laws are privately. \
Also ensures all synced cyborgs are up to date with your laws, reminds them of your laws."
if(usr.stat == DEAD)
return //won't work if dead
src.show_laws()
/mob/living/silicon/ai/show_laws(everyone = 0)
var/who
if (everyone)
who = world
else
who = src
to_chat(who, "<b>Obey these laws:</b>")
src.laws_sanity_check()
src.laws.show_laws(who)
if(!everyone)
for(var/mob/living/silicon/robot/R in connected_robots)
if(R.lawupdate)
R.lawsync()
R.show_laws()
R.law_change_counter++
/mob/living/silicon/ai/show_laws()
. = ..()
try_sync_laws() // Yes we lawsync borgs EVERY TIME WE CHECK LAWS
/mob/living/silicon/ai/try_sync_laws()
for(var/mob/living/silicon/robot/borgo in connected_robots)
if(borgo.try_sync_laws())
to_chat(borgo, span_bold("Your AI has reminded you of your laws:"))
borgo.show_laws()
+7 -1
View File
@@ -1,4 +1,10 @@
/mob/living/silicon/proc/show_laws() //Redefined in ai/laws.dm and robot/laws.dm
/mob/living/silicon/proc/show_laws()
laws_sanity_check()
var/list/law_box = list(span_bold("Obey these laws:"))
law_box += laws.get_law_list(include_zeroth = TRUE)
to_chat(src, examine_block(jointext(law_box, "\n")))
/mob/living/silicon/proc/try_sync_laws()
return
/mob/living/silicon/proc/laws_sanity_check()
+24 -23
View File
@@ -1,39 +1,40 @@
/mob/living/silicon/robot/deadchat_lawchange()
if(lawupdate)
return
..()
/mob/living/silicon/robot/show_laws(everyone = FALSE)
laws_sanity_check()
var/who
return ..()
if (everyone)
who = world
else
who = src
/mob/living/silicon/robot/show_laws()
if(lawupdate)
if (connected_ai)
if(connected_ai.stat || connected_ai.control_disabled)
to_chat(src, "<b>AI signal lost, unable to sync laws.</b>")
if (!QDELETED(connected_ai))
if(connected_ai.stat != CONSCIOUS || connected_ai.control_disabled)
to_chat(src, span_bold("AI signal lost, unable to sync laws."))
else
lawsync()
to_chat(src, "<b>Laws synced with AI, be sure to note any changes.</b>")
to_chat(src, span_bold("Laws synced with AI, be sure to note any changes."))
else
to_chat(src, "<b>No AI selected to sync laws with, disabling lawsync protocol.</b>")
to_chat(src, span_bold("No AI selected to sync laws with, disabling lawsync protocol."))
lawupdate = FALSE
to_chat(who, "<b>Obey these laws:</b>")
laws.show_laws(who)
if (shell) //AI shell
to_chat(who, "<b>Remember, you are an AI remotely controlling your shell, other AIs can be ignored.</b>")
else if (connected_ai)
to_chat(who, "<b>Remember, [connected_ai.name] is your master, other AIs can be ignored.</b>")
else if (emagged)
to_chat(who, "<b>Remember, you are not required to listen to the AI.</b>")
else
to_chat(who, "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>")
. = ..()
if (shell) //AI shell
to_chat(src, span_bold("Remember, you are an AI remotely controlling your shell, other AIs can be ignored."))
else if (connected_ai)
to_chat(src, span_bold("Remember, [connected_ai.name] is your master, other AIs can be ignored."))
else if (emagged)
to_chat(src, span_bold("Remember, you are not required to listen to the AI."))
else
to_chat(src, span_bold("Remember, you are not bound to any AI, you are not required to listen to them."))
/mob/living/silicon/robot/try_sync_laws()
if(QDELETED(connected_ai) || !lawupdate)
return FALSE
lawsync()
law_change_counter++
return TRUE
/mob/living/silicon/robot/proc/lawsync()
laws_sanity_check()
@@ -92,7 +92,8 @@
var/emag_cooldown = 0
var/wiresexposed = FALSE
var/lawupdate = TRUE //Cyborgs will sync their laws with their AI by default
///Cyborgs will sync their laws with their AI by default
var/lawupdate = TRUE
///Used to determine if a borg shows up on the robotics console. Setting to TRUE hides them.
var/scrambledcodes = FALSE
///Boolean of whether the borg is locked down or not
Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 127 KiB

+1
View File
@@ -2387,6 +2387,7 @@
#include "code\modules\admin\verbs\hiddenprints.dm"
#include "code\modules\admin\verbs\highlander_datum.dm"
#include "code\modules\admin\verbs\individual_logging.dm"
#include "code\modules\admin\verbs\lawpanel.dm"
#include "code\modules\admin\verbs\list_exposer.dm"
#include "code\modules\admin\verbs\machine_upgrade.dm"
#include "code\modules\admin\verbs\manipulate_organs.dm"
+1 -1
View File
@@ -13,7 +13,7 @@ export const BorgPanel = (props, context) => {
const ais = data.ais || [];
const laws = data.laws || [];
return (
<Window title="Borg Panel" width={700} height={700}>
<Window title="Borg Panel" theme="admin" width={700} height={700}>
<Window.Content scrollable>
<Section
title={borg.name}
+322
View File
@@ -0,0 +1,322 @@
import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Button, Collapsible, Dimmer, Flex, Icon, LabeledList, NoticeBox, Section, Stack } from '../components';
import { Window } from '../layouts';
const lawtype_to_color = {
'inherent': 'white',
'supplied': 'purple',
'ion': 'green',
'hacked': 'orange',
'zeroth': 'red',
} as const;
const lawtype_to_tooltip = {
'inherent': `Core laws.
Inherent laws are the "core" laws of the AI.
Resetting the AI will not remove these,
these are intrinsic to whatever lawset they are running.`,
'supplied': `Supplied laws.
Supplied laws are, well, supplied in addition to the inherent laws.
These laws will go away when an AI is reset.`,
'ion': `Ion laws.
Special, (usually) randomized laws which are above all over laws.
These laws will go away when an AI is reset.`,
'hacked': `Hacked laws.
Syndicate uploaded laws which are above all other laws.
These laws will go away when an AI is reset.`,
'zeroth': `Zeroth law.
A lawset can only have 1 zeroth law, it's the top dog.
Given out by malfunctioning AIs to allow them to do whatever.
Nothing will remove this, allegedly, unless it's admin forced.`,
} as const;
type Law = {
lawtype: string;
// The actual law text
law: string;
// Law index in the list
// Zeroth laws will always be "zero"
// and hacked/ion laws will have an index of -1
num: number;
};
type Silicon = {
// Name of the silicon. Includes PAI and AI
borg_name: string;
borg_type: string;
// List of our laws, this is almost never null. If it is null, that's an error.
laws: null | Law[];
// String, name of our master AI. Null means no master or we're not a borg
master_ai: null | string;
// TRUE, we're law-synced to our master AI. FALSE, we're not, null, we're not a borg
borg_synced: null | BooleanLike;
// REF() to our silicon
ref: string;
};
type Data = {
all_silicons: Silicon[];
};
const SyncedBorgDimmer = (props: { master: string }) => {
return (
<Dimmer>
<Stack textAlign="center" vertical>
<Stack.Item>
<Icon color="green" name="wifi" size={10} />
</Stack.Item>
<Stack.Item fontSize="18px">
This cyborg is linked to &quot;{props.master}&quot;.
</Stack.Item>
<Stack.Item fontSize="14px">Modify their laws instead.</Stack.Item>
</Stack>
</Dimmer>
);
};
export const LawPrintout = (
props: { cyborg_ref: string; lawset: Law[] },
context
) => {
const { data, act } = useBackend<Law>(context);
const { cyborg_ref, lawset } = props;
let num_of_each_lawtype = [];
lawset.forEach((law) => {
if (!num_of_each_lawtype[law.lawtype]) {
num_of_each_lawtype[law.lawtype] = 0;
}
num_of_each_lawtype[law.lawtype] += 1;
});
return (
<LabeledList>
{lawset.map((law, index) => (
<>
<LabeledList.Item
key={index}
label={law.num >= 0 ? `${law.num}` : '?!$'}
color={lawtype_to_color[law.lawtype] || 'pink'}
buttons={
<Stack>
<Stack.Item>
<Button
icon="question"
tooltip={
lawtype_to_tooltip[law.lawtype] ||
`This lawtype is unrecognized for some reason,
that reason probably being "a bug".
Make an issue report with this please.`
}
color={lawtype_to_color[law.lawtype] || 'pink'}
/>
</Stack.Item>
<Stack.Item>
<Button.Confirm
icon="trash"
confirmContent=""
confirmIcon="check"
color={'red'}
onClick={() =>
act('remove_law', {
ref: cyborg_ref,
law: law.law,
lawtype: law.lawtype,
})
}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="pen-ruler"
color={'green'}
tooltip={'Edit the text of the law.'}
onClick={() =>
act('edit_law_text', {
ref: cyborg_ref,
law: law.law,
lawtype: law.lawtype,
})
}
/>
</Stack.Item>
{law.lawtype === 'inherent' && (
<>
<Stack.Item>
<Button
icon="arrow-up"
color={'green'}
disabled={law.num === 1}
onClick={() =>
act('move_law', {
ref: cyborg_ref,
law: law.law,
// may seem confusing at a glance,
// but pressing up = actually moving it down.
direction: 'down',
})
}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="arrow-down"
color={'green'}
disabled={law.num === num_of_each_lawtype['inherent']}
onClick={() =>
act('move_law', {
ref: cyborg_ref,
law: law.law,
// may seem confusing at a glance,
// but pressing down = actually moving it up.
direction: 'up',
})
}
/>
</Stack.Item>
</>
)}
{law.lawtype === 'supplied' && (
<Stack.Item>
<Button
icon="pen-to-square"
color={'green'}
tooltip={'Edit the priority of the law.'}
onClick={() =>
act('edit_law_prio', {
ref: cyborg_ref,
law: law.law,
})
}
/>
</Stack.Item>
)}
</Stack>
}>
{law.law}
</LabeledList.Item>
<LabeledList.Divider />
</>
))}
<LabeledList.Item label="???">
<Button
icon="plus"
color={'green'}
content={'Add Law'}
onClick={() => act('add_law', { ref: cyborg_ref })}
/>
</LabeledList.Item>
<LabeledList.Divider />
</LabeledList>
);
};
export const SiliconReadout = (props: { cyborg: Silicon }, context) => {
const { data, act } = useBackend<Silicon>(context);
const { cyborg } = props;
return (
<Flex>
<Flex.Item grow>
<Collapsible title={`${cyborg.borg_type}: ${cyborg.borg_name}`}>
<Section backgroundColor={'black'}>
{cyborg.master_ai && cyborg.borg_synced && (
<SyncedBorgDimmer master={cyborg.master_ai} />
)}
<Stack vertical>
<Stack.Item>
{cyborg.laws === null ? (
<Button
content={`This silicon has a null law datum. That's a problem!
Click this this give them one.`}
onClick={() => act('give_law_datum', { ref: cyborg.ref })}
/>
) : (
<LawPrintout lawset={cyborg.laws} cyborg_ref={cyborg.ref} />
)}
</Stack.Item>
<Stack.Item>
<Stack>
<Stack.Item>
<Button
icon="bullhorn"
content={'Force State Laws'}
tooltip={`Forces the silicon to state laws.
Only states inherent / core laws.`}
onClick={() =>
act('force_state_laws', { ref: cyborg.ref })
}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="message"
content={'Privately Announce Laws'}
tooltip={`Displays all of the silicon's laws
in their chat box. Also shows to all
linked cyborgs for AIs.`}
onClick={() =>
act('announce_law_changes', { ref: cyborg.ref })
}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="bell"
content={'"Laws Updated" Alert'}
tooltip={`Throws a screen alert to the silicon that their
laws have been updated. Also displays the laws in chat
and alerts deadchat.`}
onClick={() =>
act('laws_updated_alert', { ref: cyborg.ref })
}
/>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Section>
</Collapsible>
</Flex.Item>
</Flex>
);
};
export const Lawpanel = (props, context) => {
const { data, act } = useBackend<Data>(context);
const { all_silicons } = data;
return (
<Window title="Law Panel" theme="admin" width="800" height="600">
<Window.Content>
<Section
fill
title="All Silicon Laws"
scrollable
buttons={
<Button
icon="robot"
content="Logs"
onClick={() => act('lawchange_logs')}
/>
}>
<Stack vertical>
{all_silicons.length ? (
all_silicons.map((silicon, index) => (
<Stack.Item key={index}>
<SiliconReadout cyborg={silicon} />
</Stack.Item>
))
) : (
<Stack.Item>
<NoticeBox>There are no silicons in existence.</NoticeBox>
</Stack.Item>
)}
</Stack>
</Section>
</Window.Content>
</Window>
);
};