///defined truthy result for `handle_unique_ai()`, which makes initialize return INITIALIZE_HINT_QDEL
#define SHOULD_QDEL_MODULE 1
/obj/item/ai_module
name = "\improper AI module"
icon = 'icons/obj/devices/circuitry_n_data.dmi'
icon_state = "std_mod"
inhand_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
desc = "An AI Module for programming laws to an AI."
obj_flags = CONDUCTS_ELECTRICITY
force = 5
w_class = WEIGHT_CLASS_SMALL
throwforce = 0
throw_speed = 3
throw_range = 7
custom_materials = list(/datum/material/gold = SMALL_MATERIAL_AMOUNT * 0.5)
/// This is where our laws get put at for the module
var/list/laws = list()
/// Used to skip laws being checked (for reset & remove boards that have no laws)
var/bypass_law_amt_check = FALSE
/obj/item/ai_module/Initialize(mapload)
. = ..()
if(mapload && HAS_TRAIT(SSstation, STATION_TRAIT_UNIQUE_AI) && is_station_level(z))
var/delete_module = handle_unique_ai()
if(delete_module)
return INITIALIZE_HINT_QDEL
/obj/item/ai_module/examine(mob/user as mob)
. = ..()
var/examine_laws = display_laws()
if(examine_laws)
. += "\n" + examine_laws
/obj/item/ai_module/attack_self(mob/user as mob)
..()
to_chat(user, examine_block(display_laws()))
/// Returns a text display of the laws for the module.
/obj/item/ai_module/proc/display_laws()
// Used to assemble the laws to show to an examining user.
var/assembled_laws = ""
if(laws.len)
assembled_laws += "Programmed Law[(laws.len > 1) ? "s" : ""]:
"
for(var/law in laws)
assembled_laws += "\"[law]\"
"
return assembled_laws
///what this module should do if it is mapload spawning on a unique AI station trait round.
/obj/item/ai_module/proc/handle_unique_ai()
return SHOULD_QDEL_MODULE //instead of the roundstart bid to un-unique the AI, there will be a research requirement for it.
//The proc other things should be calling
/obj/item/ai_module/proc/install(datum/ai_laws/law_datum, mob/user)
if(!bypass_law_amt_check && (!laws.len || laws[1] == "")) //So we don't loop trough an empty list and end up with runtimes.
to_chat(user, span_warning("ERROR: No laws found on board."))
return
var/overflow = FALSE
//Handle the lawcap
if(law_datum)
var/tot_laws = 0
var/included_lawsets = list(law_datum.supplied, law_datum.ion, law_datum.hacked, laws)
// if the ai module is a core module we don't count inherent laws since they will be replaced
// however the freeformcore doesn't replace inherent laws so we check that too
if(!istype(src, /obj/item/ai_module/core) || istype(src, /obj/item/ai_module/core/freeformcore))
included_lawsets += list(law_datum.inherent)
for(var/lawlist in included_lawsets)
for(var/mylaw in lawlist)
if(mylaw != "")
tot_laws++
if(tot_laws > CONFIG_GET(number/silicon_max_law_amount) && !bypass_law_amt_check)//allows certain boards to avoid this check, eg: reset
to_chat(user, span_alert("Not enough memory allocated to [law_datum.owner ? law_datum.owner : "the AI core"]'s law processor to handle this amount of laws."))
message_admins("[ADMIN_LOOKUPFLW(user)] tried to upload laws to [law_datum.owner ? ADMIN_LOOKUPFLW(law_datum.owner) : "an AI core"] that would exceed the law cap.")
log_silicon("[key_name(user)] tried to upload laws to [law_datum.owner ? key_name(law_datum.owner) : "an AI core"] that would exceed the law cap.")
overflow = TRUE
var/law2log = transmitInstructions(law_datum, user, overflow) //Freeforms return something extra we need to log
if(law_datum.owner)
to_chat(user, span_notice("Upload complete. [law_datum.owner]'s laws have been modified."))
law_datum.owner.law_change_counter++
else
to_chat(user, span_notice("Upload complete."))
var/time = time2text(world.realtime,"hh:mm:ss")
var/ainame = law_datum.owner ? law_datum.owner.name : "empty AI core"
var/aikey = law_datum.owner ? law_datum.owner.ckey : "null"
//affected cyborgs are cyborgs linked to the AI with lawsync enabled
var/affected_cyborgs = list()
var/list/borg_txt = list()
var/list/borg_flw = list()
if(isAI(law_datum.owner))
var/mob/living/silicon/ai/owner = law_datum.owner
for(var/mob/living/silicon/robot/owned_borg as anything in owner.connected_robots)
if(owned_borg.connected_ai && owned_borg.lawupdate)
affected_cyborgs += owned_borg
borg_flw += "[ADMIN_LOOKUPFLW(owned_borg)], "
borg_txt += "[owned_borg.name]([owned_borg.key]), "
owned_borg.lawsync() // SKYRAT EDIT ADDITION
borg_txt = borg_txt.Join()
GLOB.lawchanges.Add("[time] : [user.name]([user.key]) used [src.name] on [ainame]([aikey]).[law2log ? " The law specified [law2log]" : ""], [length(affected_cyborgs) ? ", impacting synced borgs [borg_txt]" : ""]")
log_silicon("LAW: [key_name(user)] used [src.name] on [key_name(law_datum.owner)] from [AREACOORD(user)].[law2log ? " The law specified [law2log]" : ""], [length(affected_cyborgs) ? ", impacting synced borgs [borg_txt]" : ""]")
message_admins("[ADMIN_LOOKUPFLW(user)] used [src.name] on [ADMIN_LOOKUPFLW(law_datum.owner)] from [AREACOORD(user)].[law2log ? " The law specified [law2log]" : ""] , [length(affected_cyborgs) ? ", impacting synced borgs [borg_flw.Join()]" : ""]")
if(law_datum.owner)
deadchat_broadcast(" changed [span_name("[ainame]")]'s laws at [get_area_name(user, TRUE)].", span_name("[user]"), follow_target=user, message_type=DEADCHAT_LAWCHANGE)
//The proc that actually changes the silicon's laws.
/obj/item/ai_module/proc/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow = FALSE)
if(law_datum.owner)
to_chat(law_datum.owner, span_userdanger("[sender] has uploaded a change to the laws you must follow using a [name]."))
/obj/item/ai_module/core
desc = "An AI Module for programming core laws to an AI."
/obj/item/ai_module/core/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow)
for(var/templaw in laws)
if(law_datum.owner)
if(!overflow)
law_datum.owner.add_inherent_law(templaw)
else
law_datum.owner.replace_random_law(templaw, list(LAW_INHERENT, LAW_SUPPLIED), LAW_INHERENT)
else
if(!overflow)
law_datum.add_inherent_law(templaw)
else
law_datum.replace_random_law(templaw, list(LAW_INHERENT, LAW_SUPPLIED), LAW_INHERENT)
/obj/item/ai_module/core/full
var/law_id // if non-null, loads the laws from the ai_laws datums
/obj/item/ai_module/core/full/Initialize(mapload)
. = ..()
if(!law_id)
return
var/lawtype = lawid_to_type(law_id)
if(!lawtype)
return
var/datum/ai_laws/core_laws = new lawtype
laws = core_laws.inherent
/obj/item/ai_module/core/full/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) //These boards replace inherent laws.
if(law_datum.owner)
law_datum.owner.clear_inherent_laws()
law_datum.owner.clear_zeroth_law(0)
else
law_datum.clear_inherent_laws()
law_datum.clear_zeroth_law(0)
..()
/obj/item/ai_module/core/full/handle_unique_ai()
var/datum/ai_laws/default_laws = get_round_default_lawset()
if(law_id == initial(default_laws.id))
return
return SHOULD_QDEL_MODULE
/obj/effect/spawner/round_default_module
name = "ai default lawset spawner"
icon = 'icons/hud/screen_gen.dmi'
icon_state = "x2"
color = COLOR_VIBRANT_LIME
/obj/effect/spawner/round_default_module/Initialize(mapload)
. = ..()
var/datum/ai_laws/default_laws = get_round_default_lawset()
//try to spawn a law board, since they may have special functionality (asimov setting subjects)
for(var/obj/item/ai_module/core/full/potential_lawboard as anything in subtypesof(/obj/item/ai_module/core/full))
if(initial(potential_lawboard.law_id) != initial(default_laws.id))
continue
potential_lawboard = new potential_lawboard(loc)
return
//spawn the fallback instead
new /obj/item/ai_module/core/round_default_fallback(loc)
///When the default lawset spawner cannot find a module object to spawn, it will spawn this, and this sets itself to the round default.
///This is so /datum/lawsets can be picked even if they have no module for themselves.
/obj/item/ai_module/core/round_default_fallback
/obj/item/ai_module/core/round_default_fallback/Initialize(mapload)
. = ..()
var/datum/ai_laws/default_laws = get_round_default_lawset()
default_laws = new default_laws()
name = "'[default_laws.name]' Core AI Module"
laws = default_laws.inherent
/obj/item/ai_module/core/round_default_fallback/handle_unique_ai()
return
#undef SHOULD_QDEL_MODULE