Files
Bubberstation/code/modules/admin/verbs/lawpanel.dm
The Sharkening 8e236c4693 The Law Panel now logs admin law changes. (#87083)
## About The Pull Request

Adds proper law logging to the law panel.

I asked Melbert if he wanted it inside silicon.log, admin.log, or both.
He said admin.log

## Why It's Good For The Game

Logging is good for adminbus and what can be high impact adminbus. 

## Changelog
🆑
admin: The Law Panel now properly logs and communicates law edits.
/🆑
2024-11-14 15:58:09 -08:00

322 lines
11 KiB
Plaintext

ADMIN_VERB(law_panel, R_ADMIN, "Law Panel", "View the AI laws.", ADMIN_CATEGORY_EVENTS)
if(!isobserver(user) && SSticker.HasRoundStarted())
message_admins("[key_name_admin(user)] checked AI laws via the Law Panel.")
var/datum/law_panel/tgui = new
tgui.ui_interact(user.mob)
BLACKBOX_LOG_ADMIN_VERB("Law Panel")
/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") // admin verb so no max length and also any user-level input is config based already so ehhhh
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
log_admin("[key_name(user)] has UPLOADED a [lawtype] law to [key_name(borgo)] stating: [lawtext]")
message_admins("[key_name(user)] has UPLOADED a [lawtype] law to [key_name(borgo)] stating: [lawtext]")
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
log_admin("[key_name(user)] has EDITED [key_name(borgo)] [lawtype] law. OLD LAW: [oldlaw] \
NEW LAW: [newlaw]")
message_admins("[key_name(user)] has EDITED a [lawtype] law on [key_name(borgo)]")
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)
log_admin("[key_name(user)] has SWAPPED [key_name(borgo)] law [old_prio] and [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)
log_admin("[key_name(user)] has REPLACED [key_name(borgo)] law: [law] with priority [new_prio]")
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)
log_admin("[key_name(user)] has changed the priority of an existing law on [key_name(borgo)]. LAW: [law] PRIORITY: [new_prio]")
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)
log_admin("[key_name(user)] has UPLOADED a supplied law to [key_name(borgo)] stating: [law]") // Normal insertion, I.E upload
message_admins("[key_name(user)] has UPLOADED a supplied law to [key_name(borgo)] stating: [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
log_admin("[key_name(user)] has REMOVED a law from [key_name(borgo)]. LAW: [law]")
message_admins("[key_name(user)] has REMOVED a law from [key_name(borgo)]. LAW: [law]")
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")
ui.user?.client?.holder?.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))
// Whoopsie something wrong wrong this isn't supposed to happen
borg_information["laws"] = null
else
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