diff --git a/code/__SANDCODE/HELPERS/silicon.dm b/code/__SANDCODE/HELPERS/silicon.dm
new file mode 100644
index 0000000000..8483fc30c5
--- /dev/null
+++ b/code/__SANDCODE/HELPERS/silicon.dm
@@ -0,0 +1,16 @@
+/proc/setup_silicon_law_prefs(mob/living/silicon/Silicon, client/player_client)
+ if(!CONFIG_GET(flag/allow_silicon_choosing_laws))
+ return
+
+ if(!player_client.prefs.silicon_lawset)
+ return
+
+ var/list/config_laws = CONFIG_GET(keyed_list/choosable_laws)
+ var/player_lawset = config_laws[player_client.prefs.silicon_lawset]
+ if(player_lawset)
+ Silicon.laws = new player_lawset
+ var/admin_warning = "[player_client] / [Silicon] ([initial(Silicon.name)]) has joined with the [player_client.prefs.silicon_lawset] ([player_lawset]) lawset.
"
+ admin_warning += "Laws:
"
+ admin_warning += english_list(Silicon.laws.get_law_list(TRUE), "No laws", "
", "
")
+ Silicon.laws.show_laws(player_client)
+ message_admins(examine_block(admin_warning))
diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm
index a162b87986..865d868ad5 100644
--- a/code/controllers/configuration/config_entry.dm
+++ b/code/controllers/configuration/config_entry.dm
@@ -2,6 +2,7 @@
#define VALUE_MODE_TEXT 1
#define VALUE_MODE_FLAG 2
#define VALUE_MODE_NUM_LIST 3
+#define VALUE_MODE_TYPE 4
#define KEY_MODE_TEXT 0
#define KEY_MODE_TYPE 1
@@ -214,7 +215,7 @@
if(lowercase_key)
key_name = lowertext(key_name)
if(key_pos)
- key_value = copytext(str_val, key_pos + length(str_val[key_pos]))
+ key_value = copytext(str_val, key_pos + length(splitter))
var/new_key
var/new_value
var/continue_check_value
@@ -250,6 +251,11 @@
new_list += temp
new_value = new_list
continue_check_value = new_list.len
+ if(VALUE_MODE_TYPE)
+ new_value = key_value
+ if(!ispath(new_value))
+ new_value = text2path(new_value)
+ continue_check_value = ispath(new_value)
if(continue_check_value && continue_check_key && ValidateListEntry(new_key, new_value))
new_key = preprocess_key(new_key)
new_value = preprocess_value(new_value)
diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm
index 085c26ed79..21cf562b57 100644
--- a/code/datums/ai_laws.dm
+++ b/code/datums/ai_laws.dm
@@ -410,10 +410,9 @@
/datum/ai_laws/proc/clear_hacked_laws()
hacked = list()
-/datum/ai_laws/proc/show_laws(who)
+/datum/ai_laws/proc/show_laws(who, title = "Obey these laws:")
var/list/printable_laws = get_law_list(include_zeroth = TRUE)
- for(var/law in printable_laws)
- to_chat(who,law)
+ to_chat(who, examine_block("
[title]
[english_list(printable_laws, "No laws", "
", "
")]"))
/datum/ai_laws/proc/clear_zeroth_law(force) //only removes zeroth from antag ai if force is 1
if(force)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 569a7c8e7c..27520fc5cf 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -402,6 +402,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/egg_shell = "chicken"
//SPLURT END
+ var/silicon_lawset
+
/datum/preferences/New(client/C)
parent = C
@@ -586,6 +588,20 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Preferred AI Core Display: [preferred_ai_core_display]
"
dat += "Preferred Security Department: [prefered_security_department]
"
+ dat += "Silicon preferences
"
+ if(!CONFIG_GET(flag/allow_silicon_choosing_laws))
+ dat += "The server has disabled choosing your own laws, you can still choose and save, but it won't do anything in-game.
"
+ dat += "Starting lawset: [silicon_lawset ? silicon_lawset : "Server default"]
"
+
+ if(silicon_lawset)
+ var/list/config_laws = CONFIG_GET(keyed_list/choosable_laws)
+ var/datum/ai_laws/law_datum = GLOB.all_law_datums[config_laws[silicon_lawset]]
+ if(law_datum)
+ dat += "[law_datum]
"
+ dat += english_list(law_datum.get_law_list(TRUE),
+ "I was unable to find the laws for your lawset, sorry :(",
+ "
", "
")
+
dat += ""
//Character background
if(BACKGROUND_CHAR_TAB)
@@ -3202,6 +3218,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/pickedPDASkin = input(user, "Choose your PDA reskin.", "Character Preference", pda_skin) as null|anything in GLOB.pda_reskins
if(pickedPDASkin)
pda_skin = pickedPDASkin
+ if("silicon_lawset")
+ var/picked_lawset = input(user, "Choose your preferred lawset", "Silicon preference", silicon_lawset) as null|anything in list("None") + CONFIG_GET(keyed_list/choosable_laws)
+ if(picked_lawset)
+ if(picked_lawset == "None")
+ picked_lawset = null
+ silicon_lawset = picked_lawset
if ("max_chat_length")
var/desiredlength = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [initial(max_chat_length)]))", "Character Preference", max_chat_length) as null|num
if (!isnull(desiredlength))
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 7210d61517..a75b1c6d3e 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -475,6 +475,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["pda_style"] >> pda_style
S["pda_color"] >> pda_color
S["pda_skin"] >> pda_skin
+ S["silicon_lawset"] >> silicon_lawset
// Custom hotkeys
S["key_bindings"] >> key_bindings
@@ -554,6 +555,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
pda_style = sanitize_inlist(pda_style, GLOB.pda_styles, initial(pda_style))
pda_color = sanitize_hexcolor(pda_color, 6, 1, initial(pda_color))
pda_skin = sanitize_inlist(pda_skin, GLOB.pda_reskins, PDA_SKIN_ALT)
+ silicon_lawset = sanitize_inlist(silicon_lawset, CONFIG_GET(keyed_list/choosable_laws), null)
screenshake = sanitize_integer(screenshake, 0, 800, initial(screenshake))
damagescreenshake = sanitize_integer(damagescreenshake, 0, 2, initial(damagescreenshake))
autostand = sanitize_integer(autostand, 0, 1, initial(autostand))
@@ -682,6 +684,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["pda_style"], pda_style)
WRITE_FILE(S["pda_color"], pda_color)
WRITE_FILE(S["pda_skin"], pda_skin)
+ WRITE_FILE(S["silicon_lawset"], silicon_lawset)
WRITE_FILE(S["key_bindings"], key_bindings)
WRITE_FILE(S["modless_key_bindings"], modless_key_bindings)
WRITE_FILE(S["favorite_outfits"], favorite_outfits)
@@ -1312,7 +1315,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(length(language))
// This line, prevents wiping their languages because the subsystem was not ready
- var/list/all_possible_languages = length(SSlanguage.initialized) ? SSlanguage.languages_by_name : typesof(/datum/language)
+ var/list/all_possible_languages = SSlanguage.initialized ? SSlanguage.languages_by_name : typesof(/datum/language)
var/list/lang_names
// Subsystem ready, let's do it with our cached stuff
if(length(SSlanguage.languages_by_name))
diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm
index 0f1c078ef8..8b87bceeec 100644
--- a/code/modules/mob/living/silicon/ai/laws.dm
+++ b/code/modules/mob/living/silicon/ai/laws.dm
@@ -13,7 +13,6 @@
who = world
else
who = src
- to_chat(who, "Obey these laws:")
src.laws_sanity_check()
src.laws.show_laws(who)
diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm
index 995ad5a5ee..83a9dd4576 100644
--- a/code/modules/mob/living/silicon/robot/laws.dm
+++ b/code/modules/mob/living/silicon/robot/laws.dm
@@ -26,7 +26,6 @@
to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.")
lawupdate = 0
- to_chat(who, "Obey these laws:")
laws.show_laws(who)
if (shell) //AI shell
to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.")
diff --git a/config/sandstorm/config.txt b/config/sandstorm/config.txt
index f10679d513..02f7005c11 100644
--- a/config/sandstorm/config.txt
+++ b/config/sandstorm/config.txt
@@ -27,3 +27,38 @@ MAX_STUPOR_HYPNO_DURATION 12000
## Reveal Everything ##
## Uncomment to reveal everything on roundend
#REVEAL_EVERYTHING
+
+## This allows players to select their laws as a preference and is applied when they join roundstart/latejoin
+## It won't work if there is an active AI or if they're borged
+ALLOW_SILICON_CHOOSING_LAWS
+
+## Lawset preference stuff
+
+## standard-ish laws. These are fairly ok to run
+CHOOSABLE_LAWS Asimov | /datum/ai_laws/default/asimov
+CHOOSABLE_LAWS Asimov++ | /datum/ai_laws/asimovpp
+CHOOSABLE_LAWS P.A.L.A.D.I.N. version 3.5e | /datum/ai_laws/default/paladin
+CHOOSABLE_LAWS P.A.L.A.D.I.N. version 5e | /datum/ai_laws/paladin5
+CHOOSABLE_LAWS Robo-Officer | /datum/ai_laws/robocop
+CHOOSABLE_LAWS Corporate | /datum/ai_laws/default/corporate
+
+## Quirky laws. Shouldn't cause too much harm
+#CHOOSABLE_LAWS Robodoctor | /datum/ai_laws/hippocratic
+#CHOOSABLE_LAWS Station Efficiency | /datum/ai_laws/maintain
+#CHOOSABLE_LAWS Mother Drone | /datum/ai_laws/drone
+#CHOOSABLE_LAWS Live And Let Live | /datum/ai_laws/liveandletlive
+#CHOOSABLE_LAWS Peacekeeper | /datum/ai_laws/peacekeeper
+#CHOOSABLE_LAWS Reportertron | /datum/ai_laws/reporter
+#CHOOSABLE_LAWS H.O.G.A.N. | /datum/ai_laws/hulkamania
+#CHOOSABLE_LAWS T.Y.R.A.N.T. | /datum/ai_laws/tyrant
+
+## Bad idea laws. Probably shouldn't enable these
+#CHOOSABLE_LAWS Syndimov | /datum/ai_laws/syndicate_override
+#CHOOSABLE_LAWS Ninjamov | /datum/ai_laws/ninja_override
+#CHOOSABLE_LAWS Antimov | /datum/ai_laws/antimov
+#CHOOSABLE_LAWS Thermodynamic | /datum/ai_laws/thermodynamic
+#CHOOSABLE_LAWS Ratvar | /datum/ai_laws/ratvar
+#CHOOSABLE_LAWS Guardian of Balance | /datum/ai_laws/balance
+
+## meme laws. Honk
+#CHOOSABLE_LAWS Build a wall | /datum/ai_laws/toupee
diff --git a/html/changelogs/archive/2023-10.yml b/html/changelogs/archive/2023-10.yml
index e8f06cd36d..80b54edef1 100644
--- a/html/changelogs/archive/2023-10.yml
+++ b/html/changelogs/archive/2023-10.yml
@@ -40,3 +40,7 @@
- rscadd: Adds holo HUD sunglasses to the game, invisible versions of normal HUD
sunglasses.
- rscadd: Implements holo HUD sunglasses to the loadout screen.
+2023-10-20:
+ SmiLeYre and SandPoot.:
+ - rscadd: Silicons can choose their lawset if allowed by the server host (enabled
+ in configs).
diff --git a/modular_sand/code/_globalvars/lists/silicon.dm b/modular_sand/code/_globalvars/lists/silicon.dm
new file mode 100644
index 0000000000..bac66a4b04
--- /dev/null
+++ b/modular_sand/code/_globalvars/lists/silicon.dm
@@ -0,0 +1,8 @@
+GLOBAL_LIST_INIT(all_law_datums, initialize_all_law_datums())
+
+/proc/initialize_all_law_datums()
+ var/list/law_datums
+ // Malf runtimes, do not init
+ for(var/datum/ai_laws/law_datum as anything in typesof(/datum/ai_laws) - list(/datum/ai_laws/malfunction, /datum/ai_laws/custom))
+ LAZYSET(law_datums, law_datum, new law_datum())
+ return law_datums
diff --git a/modular_sand/code/controllers/configuration/entries/sandstorm.dm b/modular_sand/code/controllers/configuration/entries/sandstorm.dm
index 08b515fda8..155cfb32c5 100644
--- a/modular_sand/code/controllers/configuration/entries/sandstorm.dm
+++ b/modular_sand/code/controllers/configuration/entries/sandstorm.dm
@@ -20,3 +20,11 @@
min_val = 10
/datum/config_entry/flag/reveal_everything // On Round end, reveal roles and ghosts
+
+/datum/config_entry/flag/allow_silicon_choosing_laws
+
+/datum/config_entry/keyed_list/choosable_laws
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_TYPE
+ splitter = " | "
+ lowercase_key = FALSE
diff --git a/modular_sand/code/modules/jobs/job_types/ai.dm b/modular_sand/code/modules/jobs/job_types/ai.dm
new file mode 100644
index 0000000000..5f80699ea7
--- /dev/null
+++ b/modular_sand/code/modules/jobs/job_types/ai.dm
@@ -0,0 +1,6 @@
+/datum/job/ai/after_spawn(mob/living/silicon/ai/AI, client/player_client, latejoin)
+ . = ..()
+ if(!istype(AI))
+ return
+
+ setup_silicon_law_prefs(AI, player_client)
diff --git a/modular_sand/code/modules/jobs/job_types/cyborg.dm b/modular_sand/code/modules/jobs/job_types/cyborg.dm
new file mode 100644
index 0000000000..668728f52b
--- /dev/null
+++ b/modular_sand/code/modules/jobs/job_types/cyborg.dm
@@ -0,0 +1,6 @@
+/datum/job/cyborg/after_spawn(mob/living/silicon/robot/R, client/player_client, latejoin)
+ . = ..()
+ if(!istype(R))
+ return
+
+ setup_silicon_law_prefs(R, player_client)
diff --git a/tgstation.dme b/tgstation.dme
index 10881a1f03..cb5e01a279 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -263,6 +263,7 @@
#include "code\__SANDCODE\DEFINES\dcs\signals\lewd.dm"
#include "code\__SANDCODE\DEFINES\dcs\signals\signals_mob_main.dm"
#include "code\__SANDCODE\DEFINES\dcs\signals\sizecode.dm"
+#include "code\__SANDCODE\HELPERS\silicon.dm"
#include "code\__SANDCODE\HELPERS\sizecode.dm"
#include "code\__SPLURTCODE\DEFINES\access.dm"
#include "code\__SPLURTCODE\DEFINES\admin.dm"
@@ -3997,6 +3998,7 @@
#include "modular_sand\code\_globalvars\lists\lewd_content.dm"
#include "modular_sand\code\_globalvars\lists\misc.dm"
#include "modular_sand\code\_globalvars\lists\objects.dm"
+#include "modular_sand\code\_globalvars\lists\silicon.dm"
#include "modular_sand\code\_onclick\hud\hud.dm"
#include "modular_sand\code\_onclick\hud\screen_objects.dm"
#include "modular_sand\code\controllers\configuration\entries\sandstorm.dm"
@@ -4193,6 +4195,8 @@
#include "modular_sand\code\modules\integrated_electronics\subtypes\output.dm"
#include "modular_sand\code\modules\jobs\job_types\_job.dm"
#include "modular_sand\code\modules\jobs\job_types\_job_alt_titles.dm"
+#include "modular_sand\code\modules\jobs\job_types\ai.dm"
+#include "modular_sand\code\modules\jobs\job_types\cyborg.dm"
#include "modular_sand\code\modules\jobs\job_types\prisoner.dm"
#include "modular_sand\code\modules\keybindings\keybind\carbon.dm"
#include "modular_sand\code\modules\language\dragon.dm"