diff --git a/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm b/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm
index 57ff783e409..63d2dfb30c1 100644
--- a/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm
+++ b/_maps/RandomRuins/SpaceRuins/skyrat/interdynefob.dmm
@@ -1246,7 +1246,7 @@
"fd" = (
/mob/living/basic/bot/medbot/stationary{
faction = list("Syndicate");
- maints_access_required = list("syndicate");
+ req_one_access = list("syndicate");
name = "Insurgent Care";
radio_channel = "Syndicate"
},
diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm
index c71d9e95939..afafc393a12 100644
--- a/_maps/map_files/KiloStation/KiloStation.dmm
+++ b/_maps/map_files/KiloStation/KiloStation.dmm
@@ -23448,7 +23448,7 @@
/obj/machinery/power/apc/auto_name/directional/west,
/obj/structure/cable,
/mob/living/basic/bot/cleanbot/medbay{
- maints_access_required = list(22);
+ req_one_access = list(22);
name = "Deacon Scrubsy"
},
/turf/open/floor/iron/smooth_large,
diff --git a/_maps/map_files/generic/CentCom_skyrat_z2.dmm b/_maps/map_files/generic/CentCom_skyrat_z2.dmm
index 30f99667b44..80018710ef6 100644
--- a/_maps/map_files/generic/CentCom_skyrat_z2.dmm
+++ b/_maps/map_files/generic/CentCom_skyrat_z2.dmm
@@ -9446,7 +9446,7 @@
/obj/structure/closet/cardboard,
/mob/living/basic/bot/medbot{
faction = list("Syndicate");
- maints_access_required = list(150);
+ req_one_access = list(150);
name = "Kahn";
radio_channel = "Syndicate"
},
diff --git a/_maps/shuttles/skyrat/goldeneye_cruiser.dmm b/_maps/shuttles/skyrat/goldeneye_cruiser.dmm
index 474d5f97910..4bcb2d3e845 100644
--- a/_maps/shuttles/skyrat/goldeneye_cruiser.dmm
+++ b/_maps/shuttles/skyrat/goldeneye_cruiser.dmm
@@ -882,7 +882,7 @@
/obj/structure/closet/crate/medical,
/mob/living/basic/bot/medbot{
faction = list("Syndicate");
- maints_access_required = list(150);
+ req_one_access = list(150);
name = "James T. Kirk";
radio_channel = "Syndicate"
},
diff --git a/_maps/templates/battlecruiser_starfury.dmm b/_maps/templates/battlecruiser_starfury.dmm
index 2c70afabbdf..2315ef85c71 100644
--- a/_maps/templates/battlecruiser_starfury.dmm
+++ b/_maps/templates/battlecruiser_starfury.dmm
@@ -4784,7 +4784,7 @@
faction = list("neutral","silicon","turret","Syndicate");
name = "Syndicate Medibot";
skin = "bezerk";
- maints_access_required = list("syndicate")
+ req_one_access = list("syndicate")
},
/turf/open/floor/iron,
/area/shuttle/sbc_starfury)
diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm
index ec7ad8f98a4..8597092fbf6 100644
--- a/code/__DEFINES/_flags.dm
+++ b/code/__DEFINES/_flags.dm
@@ -212,11 +212,16 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define ZAP_FUSION_FLAGS ZAP_OBJ_DAMAGE | ZAP_MOB_DAMAGE | ZAP_MOB_STUN
#define ZAP_SUPERMATTER_FLAGS ZAP_GENERATES_POWER
-//EMP protection
+///EMP will protect itself.
#define EMP_PROTECT_SELF (1<<0)
+///EMP will protect the contents from also being EMPed.
#define EMP_PROTECT_CONTENTS (1<<1)
+///EMP will protect the wires.
#define EMP_PROTECT_WIRES (1<<2)
+///Protects against all EMP types.
+#define EMP_PROTECT_ALL (EMP_PROTECT_SELF | EMP_PROTECT_CONTENTS | EMP_PROTECT_WIRES)
+
//Mob mobility var flags
/// can move
#define MOBILITY_MOVE (1<<0)
diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm
index d2e6633e1dd..469758d6da8 100644
--- a/code/__DEFINES/atom_hud.dm
+++ b/code/__DEFINES/atom_hud.dm
@@ -110,6 +110,7 @@
#define SECHUD_GENETICIST "hudgeneticist"
#define SECHUD_HEAD_OF_PERSONNEL "hudheadofpersonnel"
#define SECHUD_HEAD_OF_SECURITY "hudheadofsecurity"
+#define SECHUD_HUMAN_AI "hudhumanai"
#define SECHUD_JANITOR "hudjanitor"
#define SECHUD_LAWYER "hudlawyer"
#define SECHUD_MEDICAL_DOCTOR "hudmedicaldoctor"
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
index ceba0a2f182..f6f7f6e4a29 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
@@ -8,9 +8,9 @@
#define COMPONENT_CANCEL_EX_ACT (1<<0)
///from the [EX_ACT] wrapper macro: (severity, target)
#define COMSIG_ATOM_EX_ACT "atom_ex_act"
-///from base of atom/emp_act(): (severity). return EMP protection flags
+///from base of atom/emp_act(severity): (severity). return EMP protection flags
#define COMSIG_ATOM_PRE_EMP_ACT "atom_emp_act"
-///from base of atom/emp_act(): (severity, protection)
+///from base of atom/emp_act(severity): (severity, protection)
#define COMSIG_ATOM_EMP_ACT "atom_emp_act"
///from base of atom/fire_act(): (exposed_temperature, exposed_volume)
#define COMSIG_ATOM_FIRE_ACT "atom_fire_act"
diff --git a/code/__DEFINES/dcs/signals/signals_global_object.dm b/code/__DEFINES/dcs/signals/signals_global_object.dm
index 452be16f839..51b7a38a94a 100644
--- a/code/__DEFINES/dcs/signals/signals_global_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_global_object.dm
@@ -1,7 +1,7 @@
/// signals from globally accessible objects
-///from SSJob when DivideOccupations is called
-#define COMSIG_OCCUPATIONS_DIVIDED "occupations_divided"
+///Whenever SetupOccupations() is called, called all occupations are set
+#define COMSIG_OCCUPATIONS_SETUP "occupations_setup"
///from SSsun when the sun changes position : (azimuth)
#define COMSIG_SUN_MOVED "sun_moved"
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 1cf4a1bb3be..8271bb725d1 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -135,12 +135,16 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
//Silicon mobs
#define issilicon(A) (istype(A, /mob/living/silicon))
-
-#define issiliconoradminghost(A) (istype(A, /mob/living/silicon) || isAdminGhostAI(A))
-
-#define iscyborg(A) (istype(A, /mob/living/silicon/robot))
+///Define on whether A has access to Silicon stuff either through being a silicon, admin ghost or is a non-silicon holding the Silicon remote.
+///This can only be used for instances where you are not specifically looking for silicon, but access.
+#define HAS_SILICON_ACCESS(A) (istype(A, /mob/living/silicon) || isAdminGhostAI(A) || A.has_unlimited_silicon_privilege || istype(A.get_active_held_item(), /obj/item/machine_remote))
#define isAI(A) (istype(A, /mob/living/silicon/ai))
+///Define on whether A has access to AI stuff either through being a AI, admin ghost, or is a non-silicon holding the Silicon remote
+///This can only be used for instances where you are not specifically looking for silicon, but access.
+#define HAS_AI_ACCESS(A) (istype(A, /mob/living/silicon/ai) || isAdminGhostAI(A) || istype(A.get_active_held_item(), /obj/item/machine_remote))
+
+#define iscyborg(A) (istype(A, /mob/living/silicon/robot))
#define ispAI(A) (istype(A, /mob/living/silicon/pai))
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 5e7ac950062..55a84617780 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -54,6 +54,7 @@
#define JOB_AI "AI"
#define JOB_CYBORG "Cyborg"
#define JOB_PERSONAL_AI "Personal AI"
+#define JOB_HUMAN_AI "Big Brother"
//Security
#define JOB_WARDEN "Warden"
#define JOB_DETECTIVE "Detective"
@@ -222,6 +223,19 @@
#define DEPARTMENT_BITFLAG_CENTRAL_COMMAND (1<<10) //SKYRAT EDIT CHANGE
#define DEPARTMENT_CENTRAL_COMMAND "Central Command" //SKYRAT EDIT CHANGE
+DEFINE_BITFIELD(departments_bitflags, list(
+ "SECURITY" = DEPARTMENT_BITFLAG_SECURITY,
+ "COMMAND" = DEPARTMENT_BITFLAG_COMMAND,
+ "SERVICE" = DEPARTMENT_BITFLAG_SERVICE,
+ "CARGO" = DEPARTMENT_BITFLAG_CARGO,
+ "ENGINEERING" = DEPARTMENT_BITFLAG_ENGINEERING,
+ "SCIENCE" = DEPARTMENT_BITFLAG_SCIENCE,
+ "MEDICAL" = DEPARTMENT_BITFLAG_MEDICAL,
+ "SILICON" = DEPARTMENT_BITFLAG_SILICON,
+ "ASSISTANT" = DEPARTMENT_BITFLAG_ASSISTANT,
+ "CAPTAIN" = DEPARTMENT_BITFLAG_CAPTAIN,
+))
+
/* Job datum job_flags */
/// Whether the mob is announced on arrival.
#define JOB_ANNOUNCE_ARRIVAL (1<<0)
diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm
index 8aec247bfe2..c9d903aac2b 100644
--- a/code/__DEFINES/robots.dm
+++ b/code/__DEFINES/robots.dm
@@ -105,7 +105,7 @@ GLOBAL_LIST_EMPTY(cyborg_all_models_icon_list)
//Bot cover defines indicating the Bot's status
///The Bot's cover is open and can be modified/emagged by anyone.
-#define BOT_COVER_OPEN (1<<0)
+#define BOT_COVER_MAINTS_OPEN (1<<0)
///The Bot's cover is locked, and cannot be opened without unlocking it.
#define BOT_COVER_LOCKED (1<<1)
///The Bot is emagged.
@@ -113,18 +113,18 @@ GLOBAL_LIST_EMPTY(cyborg_all_models_icon_list)
///The Bot has been hacked by a Silicon, emagging them, but revertable.
#define BOT_COVER_HACKED (1<<3)
-
-//basic bots defines
-
-///is our maintenancle panel currently open
-#define BOT_MAINTS_PANEL_OPEN (1<<0)
-///is our control panel currently open
-#define BOT_CONTROL_PANEL_OPEN (1<<1)
-
-///bitfield for our access flags
+///bitfield, used by basic bots, for our access flags
DEFINE_BITFIELD(bot_access_flags, list(
- "MAINTS_OPEN" = BOT_MAINTS_PANEL_OPEN,
- "CONTROL_OPEN" = BOT_CONTROL_PANEL_OPEN,
+ "MAINTS_OPEN" = BOT_COVER_MAINTS_OPEN,
+ "COVER_OPEN" = BOT_COVER_LOCKED,
+ "COVER_EMAGGED" = BOT_COVER_EMAGGED,
+ "COVER_HACKED" = BOT_COVER_HACKED,
+))
+
+///bitfield, used by simple bots, for our access flags
+DEFINE_BITFIELD(bot_cover_flags, list(
+ "MAINTS_OPEN" = BOT_COVER_MAINTS_OPEN,
+ "COVER_OPEN" = BOT_COVER_LOCKED,
"COVER_EMAGGED" = BOT_COVER_EMAGGED,
"COVER_HACKED" = BOT_COVER_HACKED,
))
diff --git a/code/__DEFINES/station.dm b/code/__DEFINES/station.dm
index 5a04961429f..e7d7e76f841 100644
--- a/code/__DEFINES/station.dm
+++ b/code/__DEFINES/station.dm
@@ -13,6 +13,8 @@
#define STATION_TRAIT_PLANETARY (1<<0)
/// Only run on space stations
#define STATION_TRAIT_SPACE_BOUND (1<<1)
+/// Only run if AIs are enabled.
+#define STATION_TRAIT_REQUIRES_AI (1<<2)
/// Not restricted by space or planet, can always just happen
#define STATION_TRAIT_MAP_UNRESTRICTED STATION_TRAIT_PLANETARY | STATION_TRAIT_SPACE_BOUND
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 9d27db4737d..e73297b684f 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -524,6 +524,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Makes the user handcuff others faster
#define TRAIT_FAST_CUFFING "fast_cuffing"
+///Makes the player appear as their respective job in Binary Talk rather than being a 'Default Cyborg'.
+#define DISPLAYS_JOB_IN_BINARY "display_job_in_binary"
+
// METABOLISMS
// Various jobs on the station have historically had better reactions
// to various drinks and foodstuffs. Security liking donuts is a classic
@@ -541,6 +544,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BALLMER_SCIENTIST "ballmer_scientist"
#define TRAIT_MAINTENANCE_METABOLISM "maintenance_metabolism"
#define TRAIT_CORONER_METABOLISM "coroner_metabolism"
+#define TRAIT_HUMAN_AI_METABOLISM "human_ai_metabolism"
//LUNG TRAITS
/// Lungs always breathe normally when in vacuum/space.
@@ -884,6 +888,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Trait given to a dreaming carbon when they are currently doing dreaming stuff
#define TRAIT_DREAMING "currently_dreaming"
+/// Whether bots will salute this mob.
+#define TRAIT_COMMISSIONED "commissioned"
+
///generic atom traits
/// Trait from [/datum/element/rust]. Its rusty and should be applying a special overlay to denote this.
#define TRAIT_RUSTY "rust_trait"
@@ -928,6 +935,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define STATION_TRAIT_FILLED_MAINT "station_trait_filled_maint"
#define STATION_TRAIT_FORESTED "station_trait_forested"
#define STATION_TRAIT_HANGOVER "station_trait_hangover"
+#define STATION_TRAIT_HUMAN_AI "station_trait_human_ai"
#define STATION_TRAIT_LATE_ARRIVALS "station_trait_late_arrivals"
#define STATION_TRAIT_LOANER_SHUTTLE "station_trait_loaner_shuttle"
#define STATION_TRAIT_MEDBOT_MANIA "station_trait_medbot_mania"
@@ -960,7 +968,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_MAGNETIC_ID_CARD "magnetic_id_card"
/// ID cards with this trait have special appraisal text.
#define TRAIT_TASTEFULLY_THICK_ID_CARD "impressive_very_nice"
-/// things with this trait are treated as having no access in /obj/proc/check_access(obj/item)
+/// things with this trait are treated as having no access in /atom/movable/proc/check_access(obj/item)
#define TRAIT_ALWAYS_NO_ACCESS "alwaysnoaccess"
/// This human wants to see the color of their glasses, for some reason
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 3d02262b7f4..bba9bc99832 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -25,6 +25,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_RUSTY" = TRAIT_RUSTY,
"TRAIT_SPINNING" = TRAIT_SPINNING,
"TRAIT_STICKERED" = TRAIT_STICKERED,
+ "TRAIT_COMMISSIONED" = TRAIT_COMMISSIONED,
),
/atom/movable = list(
"TRAIT_ACTIVE_STORAGE" = TRAIT_ACTIVE_STORAGE,
@@ -83,6 +84,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"STATION_TRAIT_FILLED_MAINT" = STATION_TRAIT_FILLED_MAINT,
"STATION_TRAIT_FORESTED" = STATION_TRAIT_FORESTED,
"STATION_TRAIT_HANGOVER" = STATION_TRAIT_HANGOVER,
+ "STATION_TRAIT_HUMAN_AI" = STATION_TRAIT_HUMAN_AI,
"STATION_TRAIT_LATE_ARRIVALS" = STATION_TRAIT_LATE_ARRIVALS,
"STATION_TRAIT_LOANER_SHUTTLE" = STATION_TRAIT_LOANER_SHUTTLE,
"STATION_TRAIT_MEDBOT_MANIA" = STATION_TRAIT_MEDBOT_MANIA,
@@ -178,6 +180,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_DISEASELIKE_SEVERITY_MEDIUM" = TRAIT_DISEASELIKE_SEVERITY_MEDIUM,
"TRAIT_DISFIGURED" = TRAIT_DISFIGURED,
"TRAIT_DISGUISED" = TRAIT_DISGUISED,
+ "DISPLAYS_JOB_IN_BINARY" = DISPLAYS_JOB_IN_BINARY,
"TRAIT_DISK_VERIFIER" = TRAIT_DISK_VERIFIER,
"TRAIT_DISSECTED" = TRAIT_DISSECTED,
"TRAIT_DONT_WRITE_MEMORY" = TRAIT_DONT_WRITE_MEMORY,
@@ -563,6 +566,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CORONER_METABOLISM" = TRAIT_CORONER_METABOLISM,
"TRAIT_CULINARY_METABOLISM" = TRAIT_CULINARY_METABOLISM,
"TRAIT_ENGINEER_METABOLISM" = TRAIT_ENGINEER_METABOLISM,
+ "TRAIT_HUMAN_AI_METABOLISM" = TRAIT_HUMAN_AI_METABOLISM,
"TRAIT_LAW_ENFORCEMENT_METABOLISM" = TRAIT_LAW_ENFORCEMENT_METABOLISM,
"TRAIT_MAINTENANCE_METABOLISM" = TRAIT_MAINTENANCE_METABOLISM,
"TRAIT_MEDICAL_METABOLISM" = TRAIT_MEDICAL_METABOLISM,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index 797cedec668..9ad91de9541 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -49,6 +49,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_CHUNKYFINGERS" = TRAIT_CHUNKYFINGERS,
"TRAIT_CLOWN_ENJOYER" = TRAIT_CLOWN_ENJOYER,
"TRAIT_CLUMSY" = TRAIT_CLUMSY,
+ "TRAIT_COMMISSIONED" = TRAIT_COMMISSIONED,
"TRAIT_CRITICAL_CONDITION" = TRAIT_CRITICAL_CONDITION,
"TRAIT_CULT_HALO" = TRAIT_CULT_HALO,
"TRAIT_DEAF" = TRAIT_DEAF,
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index e39668bf5cb..44933673314 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -200,6 +200,8 @@ SUBSYSTEM_DEF(job)
joinable_departments_by_type = new_joinable_departments_by_type
experience_jobs_map = new_experience_jobs_map
+ SEND_SIGNAL(src, COMSIG_OCCUPATIONS_SETUP)
+
return TRUE
@@ -393,7 +395,6 @@ SUBSYSTEM_DEF(job)
//Setup new player list and get the jobs list
JobDebug("Running DO, allow_all = [allow_all], pure = [pure]")
run_divide_occupation_pure = pure
- SEND_SIGNAL(src, COMSIG_OCCUPATIONS_DIVIDED, pure, allow_all)
//Get the players who are ready
for(var/i in GLOB.new_player_list)
diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm
index d05a0b5d30f..5ea6cc5463d 100644
--- a/code/controllers/subsystem/processing/station.dm
+++ b/code/controllers/subsystem/processing/station.dm
@@ -112,6 +112,9 @@ PROCESSING_SUBSYSTEM_DEF(station)
if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_SPACE_BOUND) && !SSmapping.is_planetary()) //we're in space but we can't do space ;_;
continue
+ if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_REQUIRES_AI) && !CONFIG_GET(flag/allow_ai)) //can't have AI traits without AI
+ continue
+
selectable_traits_by_types[initial(trait_typepath.trait_type)][trait_typepath] = initial(trait_typepath.weight)
var/positive_trait_budget = text2num(pick_weight(CONFIG_GET(keyed_list/positive_station_traits)))
diff --git a/code/datums/ai_laws/ai_laws.dm b/code/datums/ai_laws/ai_laws.dm
index 971dc118f3a..0dbc6839430 100644
--- a/code/datums/ai_laws/ai_laws.dm
+++ b/code/datums/ai_laws/ai_laws.dm
@@ -192,6 +192,10 @@ GLOBAL_VAR(round_default_lawset)
var/datum/ai_laws/default_laws = get_round_default_lawset()
default_laws = new default_laws()
inherent = default_laws.inherent
+ var/datum/job/human_ai_job = SSjob.GetJob(JOB_HUMAN_AI)
+ if(human_ai_job && human_ai_job.current_positions && !zeroth) //there is a human AI so we "slave" to that.
+ zeroth = "Follow the orders of Big Brother."
+ protected_zeroth = TRUE
/**
* Gets the number of how many laws this AI has
diff --git a/code/datums/components/payment.dm b/code/datums/components/payment.dm
index 5e79b28fd57..12fc9aa4a42 100644
--- a/code/datums/components/payment.dm
+++ b/code/datums/components/payment.dm
@@ -42,7 +42,7 @@
if(!ismob(target))
return COMPONENT_OBJ_CANCEL_CHARGE
var/mob/living/user = target
- if(issilicon(user) || isdrone(user) || isAdminGhostAI(user)) //They have evolved beyond the need for mere credits
+ if(HAS_SILICON_ACCESS(user) || isdrone(user)) //They have evolved beyond the need for mere credits
return
var/obj/item/card/id/card
if(istype(user))
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index 4cf5b4148fe..334e21be4bd 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -148,7 +148,6 @@
affected_mob.say(pick("Eeee!", "Eeek, ook ook!", "Eee-eeek!", "Ungh, ungh."), forced = "jungle fever")
/datum/disease/transformation/robot
-
name = "Robotic Transformation"
cure_text = "An injection of copper."
cures = list(/datum/reagent/copper)
@@ -170,7 +169,6 @@
infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC
bantype = JOB_CYBORG
-
/datum/disease/transformation/robot/stage_act(seconds_per_tick, times_fired)
. = ..()
if(!.)
diff --git a/code/datums/id_trim/_id_trim.dm b/code/datums/id_trim/_id_trim.dm
index 6428b38d562..067e2e38263 100644
--- a/code/datums/id_trim/_id_trim.dm
+++ b/code/datums/id_trim/_id_trim.dm
@@ -23,3 +23,9 @@
var/list/access = list()
/// Accesses that this trim unlocks on a card that require wildcard slots to apply. If a card cannot accept all a trim's wildcard accesses, the card is incompatible with the trim.
var/list/wildcard_access = list()
+
+/// Returns the SecHUD job icon state for whatever this object's ID card is, if it has one.
+/obj/item/proc/get_sechud_job_icon_state()
+ var/obj/item/card/id/id_card = GetID()
+
+ return id_card?.get_trim_sechud_icon_state() || SECHUD_NO_ID
diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm
index 46f691564bb..3d94c8645b5 100644
--- a/code/datums/id_trim/jobs.dm
+++ b/code/datums/id_trim/jobs.dm
@@ -1278,3 +1278,20 @@
#undef POPULATION_SCALED_ACCESS
#undef ALWAYS_GETS_ACCESS
+
+/datum/id_trim/job/human_ai
+ assignment = JOB_HUMAN_AI
+ trim_state = "trim_recluse"
+ department_color = COLOR_BLUE_GRAY
+ subdepartment_color = COLOR_MODERATE_BLUE
+ sechud_icon_state = SECHUD_HUMAN_AI
+ minimal_access = list(
+ ACCESS_ROBOTICS, // to access robotic controls
+ ACCESS_NETWORK, //to access NTOS
+ ACCESS_KEYCARD_AUTH, //to access holopads
+ ACCESS_MINISAT,
+ ACCESS_AI_UPLOAD,
+ )
+ extra_access = list()
+ template_access = list()
+ job = /datum/job/human_ai
diff --git a/code/datums/records/manifest.dm b/code/datums/records/manifest.dm
index 6cbf4eba883..0d3144ce406 100644
--- a/code/datums/records/manifest.dm
+++ b/code/datums/records/manifest.dm
@@ -11,10 +11,9 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
/// Builds the list of crew records for all crew members.
/datum/manifest/proc/build()
- for(var/i in GLOB.new_player_list)
- var/mob/dead/new_player/readied_player = i
+ for(var/mob/dead/new_player/readied_player as anything in GLOB.new_player_list)
if(readied_player.new_character)
- log_manifest(readied_player.ckey,readied_player.new_character.mind,readied_player.new_character)
+ log_manifest(readied_player.ckey, readied_player.new_character.mind, readied_player.new_character)
if(ishuman(readied_player.new_character))
inject(readied_player.new_character, readied_player.client) // SKYRAT EDIT - RP Records - ORIGINAL: inject(readied_player.new_character)
CHECK_TICK
diff --git a/code/datums/station_traits/job_traits.dm b/code/datums/station_traits/job_traits.dm
index c588475094b..94fb0e220d4 100644
--- a/code/datums/station_traits/job_traits.dm
+++ b/code/datums/station_traits/job_traits.dm
@@ -62,12 +62,14 @@
LAZYREMOVE(lobby_candidates, signee)
var/datum/job/our_job = SSjob.GetJobType(job_to_add)
+ our_job.total_positions = position_amount
+ our_job.spawn_positions = position_amount
while(length(lobby_candidates) && position_amount > 0)
var/mob/dead/new_player/picked_player = pick_n_take(lobby_candidates)
picked_player.mind.set_assigned_role(our_job)
+ our_job.current_positions++
position_amount--
- our_job.total_positions = max(0, position_amount)
lobby_candidates = null
/datum/station_trait/job/can_display_lobby_button(client/player)
@@ -177,6 +179,76 @@
overlays += "veteran_advisor"
*/
+/datum/station_trait/job/human_ai
+ name = "Human AI"
+ button_desc = "Sign up to become the \"AI\"."
+ weight = 1
+ trait_flags = parent_type::trait_flags | STATION_TRAIT_REQUIRES_AI
+ report_message = "Our recent technological advancements in machine Artificial Intelligence has proven futile. In the meantime, we're sending an Intern to help out."
+ show_in_report = TRUE
+ can_roll_antag = CAN_ROLL_PROTECTED
+ job_to_add = /datum/job/human_ai
+ trait_to_give = STATION_TRAIT_HUMAN_AI
+
+/datum/station_trait/job/human_ai/New()
+ . = ..()
+ RegisterSignal(SSjob, COMSIG_OCCUPATIONS_SETUP, PROC_REF(remove_ai_job))
+ RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(give_fax_machine))
+
+/datum/station_trait/job/human_ai/revert()
+ UnregisterSignal(SSjob, COMSIG_OCCUPATIONS_SETUP)
+ UnregisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE)
+ return ..()
+
+/* SKYRAT EDIT - REMOVAL
+/datum/station_trait/job/human_ai/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
+ . = ..()
+ overlays += LAZYFIND(lobby_candidates, lobby_button.get_mob()) ? "human_ai_on" : "human_ai_off"
+*/
+
+/datum/station_trait/job/human_ai/proc/remove_ai_job(datum/source)
+ SIGNAL_HANDLER
+ var/datum/job_department/department = SSjob.joinable_departments_by_type[/datum/job_department/silicon]
+ department.remove_job(/datum/job/ai)
+ var/datum/station_trait/triple_ai/triple_ais = locate() in SSstation.station_traits
+ if(triple_ais)
+ position_amount = 3
+
+/// Gives the AI SAT a fax machine if it doesn't have one. This is copy pasted from Bridge Assistant's coffee maker.
+/datum/station_trait/job/human_ai/proc/give_fax_machine(datum/source)
+ SIGNAL_HANDLER
+ var/area/sat_area = GLOB.areas_by_type[/area/station/ai_monitored/turret_protected/ai]
+ if(isnull(sat_area))
+ return
+ var/list/fax_machines = SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/fax)
+ for(var/obj/machinery/fax_machine as anything in fax_machines) //don't spawn a fax machine if one exists already.
+ if(is_type_in_list(get_area(fax_machine), sat_area))
+ return
+ var/list/tables = list()
+ for(var/turf/area_turf as anything in sat_area.get_turfs_from_all_zlevels())
+ var/obj/structure/table/table = locate() in area_turf
+ if(isnull(table))
+ continue
+ if(area_turf.is_blocked_turf(ignore_atoms = list(table)))
+ continue
+ tables += table
+ if(!length(tables))
+ return
+ var/picked_table = pick_n_take(tables)
+ var/picked_turf = get_turf(picked_table)
+ for(var/obj/thing_on_table in picked_turf) //if there's paper bins or other shit on the table, get that off
+ if(thing_on_table == picked_table)
+ continue
+ if(HAS_TRAIT(thing_on_table, TRAIT_WALLMOUNTED) || (thing_on_table.flags_1 & ON_BORDER_1) || thing_on_table.layer < TABLE_LAYER)
+ continue
+ if(thing_on_table.invisibility || !thing_on_table.alpha || !thing_on_table.mouse_opacity)
+ continue
+ if(length(tables))
+ thing_on_table.forceMove(get_turf(pick(tables)))
+ else
+ qdel(thing_on_table)
+ new /obj/machinery/fax/auto_name(picked_turf)
+
#undef CAN_ROLL_ALWAYS
#undef CAN_ROLL_PROTECTED
#undef CAN_ROLL_NEVER
diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm
index 905bfed2e8e..6d782a3a968 100644
--- a/code/datums/station_traits/neutral_traits.dm
+++ b/code/datums/station_traits/neutral_traits.dm
@@ -29,6 +29,7 @@
/datum/station_trait/unique_ai
name = "Unique AI"
trait_type = STATION_TRAIT_NEUTRAL
+ trait_flags = parent_type::trait_flags | STATION_TRAIT_REQUIRES_AI
weight = 5
show_in_report = TRUE
report_message = "For experimental purposes, this station AI might show divergence from default lawset. Do not meddle with this experiment, we've removed \
@@ -400,23 +401,31 @@
/datum/station_trait/triple_ai
name = "AI Triumvirate"
trait_type = STATION_TRAIT_NEUTRAL
+ trait_flags = parent_type::trait_flags | STATION_TRAIT_REQUIRES_AI
show_in_report = TRUE
weight = 0 // SKYRAT EDIT Original = 1
report_message = "Your station has been instated with three Nanotrasen Artificial Intelligence models."
/datum/station_trait/triple_ai/New()
. = ..()
- RegisterSignal(SSjob, COMSIG_OCCUPATIONS_DIVIDED, PROC_REF(on_occupations_divided))
+ RegisterSignal(SSjob, COMSIG_OCCUPATIONS_SETUP, PROC_REF(on_occupations_setup))
/datum/station_trait/triple_ai/revert()
- UnregisterSignal(SSjob, COMSIG_OCCUPATIONS_DIVIDED)
+ UnregisterSignal(SSjob, COMSIG_OCCUPATIONS_SETUP)
return ..()
-/datum/station_trait/triple_ai/proc/on_occupations_divided(datum/source, pure, allow_all)
+/datum/station_trait/triple_ai/proc/on_occupations_setup(datum/controller/subsystem/job/source)
SIGNAL_HANDLER
+ //allows for latejoining AIs
+ for(var/obj/effect/landmark/start/ai/secondary/secondary_ai_spawn in GLOB.start_landmarks_list)
+ secondary_ai_spawn.latejoin_active = TRUE
+
+ var/datum/station_trait/job/human_ai/ai_trait = locate() in SSstation.station_traits
+ //human AI quirk will handle adding its own job positions, but for now don't allow more AI slots.
+ if(ai_trait)
+ return
for(var/datum/job/ai/ai_datum in SSjob.joinable_occupations)
ai_datum.spawn_positions = 3
- if(!pure)
- for(var/obj/effect/landmark/start/ai/secondary/secondary_ai_spawn in GLOB.start_landmarks_list)
- secondary_ai_spawn.latejoin_active = TRUE
+ ai_datum.total_positions = 3
+
diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm
index 4a21d25f884..cc2c49f1b86 100644
--- a/code/datums/station_traits/positive_traits.dm
+++ b/code/datums/station_traits/positive_traits.dm
@@ -219,6 +219,7 @@
/datum/job/geneticist = /obj/item/organ/internal/fly, //we don't care about implants, we have cancer.
/datum/job/head_of_personnel = /obj/item/organ/internal/eyes/robotic,
/datum/job/head_of_security = /obj/item/organ/internal/eyes/robotic/thermals,
+ /datum/job/human_ai = /obj/item/organ/internal/brain/cybernetic,
/datum/job/janitor = /obj/item/organ/internal/eyes/robotic/xray,
/datum/job/lawyer = /obj/item/organ/internal/heart/cybernetic/tier2,
/datum/job/mime = /obj/item/organ/internal/tongue/robot, //...
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index 4672bd15b87..f57fdf7f4af 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -65,7 +65,7 @@
/datum/wires/airlock/interact(mob/user)
var/obj/machinery/door/airlock/airlock_holder = holder
- if (!issilicon(user) && airlock_holder.isElectrified() && airlock_holder.shock(user, 100))
+ if (!HAS_SILICON_ACCESS(user) && airlock_holder.isElectrified() && airlock_holder.shock(user, 100))
return
return ..()
@@ -74,7 +74,7 @@
if(!..())
return FALSE
var/obj/machinery/door/airlock/airlock = holder
- if(!issilicon(user) && !isdrone(user) && airlock.isElectrified())
+ if(!HAS_SILICON_ACCESS(user) && !isdrone(user) && airlock.isElectrified())
var/mob/living/carbon/carbon_user = user
if (!istype(carbon_user) || carbon_user.should_electrocute(src))
return FALSE
diff --git a/code/datums/wires/fax.dm b/code/datums/wires/fax.dm
index 8c189d68df8..32904c5f891 100644
--- a/code/datums/wires/fax.dm
+++ b/code/datums/wires/fax.dm
@@ -12,7 +12,7 @@
if(!.)
return FALSE
var/obj/machinery/fax/machine = holder
- if(!issilicon(user) && machine.seconds_electrified && machine.shock(user, 100))
+ if(!HAS_SILICON_ACCESS(user) && machine.seconds_electrified && machine.shock(user, 100))
return FALSE
if(machine.panel_open)
return TRUE
diff --git a/code/datums/wires/mecha.dm b/code/datums/wires/mecha.dm
index 07bc1190148..b6e20c8161f 100644
--- a/code/datums/wires/mecha.dm
+++ b/code/datums/wires/mecha.dm
@@ -69,7 +69,7 @@
if(.)
return
var/obj/vehicle/sealed/mecha/mecha = holder
- if(!issilicon(usr) && mecha.internal_damage & MECHA_INT_SHORT_CIRCUIT && mecha.shock(usr))
+ if(!HAS_SILICON_ACCESS(usr) && mecha.internal_damage & MECHA_INT_SHORT_CIRCUIT && mecha.shock(usr))
return FALSE
/datum/wires/mecha/can_reveal_wires(mob/user)
diff --git a/code/datums/wires/mod.dm b/code/datums/wires/mod.dm
index 09274880367..00d836a52eb 100644
--- a/code/datums/wires/mod.dm
+++ b/code/datums/wires/mod.dm
@@ -52,7 +52,7 @@
/datum/wires/mod/ui_act(action, params)
var/obj/item/mod/control/mod = holder
- if(!issilicon(usr) && mod.seconds_electrified && mod.shock(usr))
+ if(!HAS_SILICON_ACCESS(usr) && mod.seconds_electrified && mod.shock(usr))
return FALSE
return ..()
diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm
index 9ec8cbe4db1..beb58fb1ce3 100644
--- a/code/datums/wires/mulebot.dm
+++ b/code/datums/wires/mulebot.dm
@@ -20,7 +20,7 @@
if(!..())
return FALSE
var/mob/living/simple_animal/bot/mulebot/mule = holder
- if(mule.bot_cover_flags & BOT_COVER_OPEN)
+ if(mule.bot_cover_flags & BOT_COVER_MAINTS_OPEN)
return TRUE
/datum/wires/mulebot/on_cut(wire, mend, source)
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index 4e037f3e24b..499707e90e0 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -28,7 +28,7 @@
if(!..())
return FALSE
var/obj/machinery/vending/vending_machine = holder
- if(!issilicon(user) && vending_machine.seconds_electrified && vending_machine.shock(user, 100))
+ if(!HAS_SILICON_ACCESS(user) && vending_machine.seconds_electrified && vending_machine.shock(user, 100))
return FALSE
if(vending_machine.panel_open)
return TRUE
diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm
index 84574d6badd..7a99de4dfdf 100644
--- a/code/game/area/ai_monitored.dm
+++ b/code/game/area/ai_monitored.dm
@@ -17,16 +17,14 @@
/area/station/ai_monitored/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
if (ismob(arrived) && motioncameras.len)
- for(var/X in motioncameras)
- var/obj/machinery/camera/cam = X
+ for(var/obj/machinery/camera/cam as anything in motioncameras)
cam.newTarget(arrived)
return
/area/station/ai_monitored/Exited(atom/movable/gone, atom/old_loc, list/atom/old_locs)
..()
if (ismob(gone) && motioncameras.len)
- for(var/X in motioncameras)
- var/obj/machinery/camera/cam = X
+ for(var/obj/machinery/camera/cam as anything in motioncameras)
cam.lostTargetRef(WEAKREF(gone))
return
diff --git a/code/game/atom/atom_act.dm b/code/game/atom/atom_act.dm
index 4c4ec04d667..feb969c9d40 100644
--- a/code/game/atom/atom_act.dm
+++ b/code/game/atom/atom_act.dm
@@ -67,6 +67,8 @@
* We then return the protection value
*/
/atom/proc/emp_act(severity)
+ // SKYRAT EDIT - REMOVAL
+ //SHOULD_CALL_PARENT(TRUE)
var/protection = SEND_SIGNAL(src, COMSIG_ATOM_PRE_EMP_ACT, severity)
if(!(protection & EMP_PROTECT_WIRES) && istype(wires))
wires.emp_pulse()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index c4b30c387cb..538cbd14474 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -121,6 +121,14 @@
/// Will not automatically apply to the turf below you, you need to apply /datum/element/block_explosives in conjunction with this
var/explosion_block = 0
+ // Access levels, used in modules\jobs\access.dm
+ /// List of accesses needed to use this object: The user must possess all accesses in this list in order to use the object.
+ /// Example: If req_access = list(ACCESS_ENGINE, ACCESS_CE)- then the user must have both ACCESS_ENGINE and ACCESS_CE in order to use the object.
+ var/list/req_access
+ /// List of accesses needed to use this object: The user must possess at least one access in this list in order to use the object.
+ /// Example: If req_one_access = list(ACCESS_ENGINE, ACCESS_CE)- then the user must have either ACCESS_ENGINE or ACCESS_CE in order to use the object.
+ var/list/req_one_access
+
/mutable_appearance/emissive_blocker
/mutable_appearance/emissive_blocker/New()
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 7485e1253a6..a75b08cd265 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -593,10 +593,10 @@
if(!isliving(user))
return FALSE //no ghosts allowed, sorry
- if(!issilicon(user) && !user.can_hold_items())
+ if(!HAS_SILICON_ACCESS(user) && !user.can_hold_items())
return FALSE //spiders gtfo
- if(issilicon(user)) // If we are a silicon, make sure the machine allows silicons to interact with it
+ if(HAS_SILICON_ACCESS(user)) // If we are a silicon, make sure the machine allows silicons to interact with it
if(!(interaction_flags_machine & INTERACT_MACHINE_ALLOW_SILICON))
return FALSE
@@ -657,7 +657,7 @@
////////////////////////////////////////////////////////////////////////////////////////////
//Return a non FALSE value to interrupt attack_hand propagation to subtypes.
-/obj/machinery/interact(mob/user, special_state)
+/obj/machinery/interact(mob/user)
if(interaction_flags_machine & INTERACT_MACHINE_SET_MACHINE)
user.set_machine(src)
update_last_used(user)
@@ -666,7 +666,7 @@
/obj/machinery/ui_act(action, list/params)
add_fingerprint(usr)
update_last_used(usr)
- if(isAI(usr) && !GLOB.cameranet.checkTurfVis(get_turf(src))) //We check if they're an AI specifically here, so borgs can still access off-camera stuff.
+ if(HAS_AI_ACCESS(usr) && !GLOB.cameranet.checkTurfVis(get_turf(src))) //We check if they're an AI specifically here, so borgs can still access off-camera stuff.
to_chat(usr, span_warning("You can no longer connect to this device!"))
return FALSE
return ..()
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index a0e2eab029b..e20d53e4d31 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -365,7 +365,7 @@
/obj/machinery/autolathe/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
. = ..()
- if((!issilicon(usr) && !isAdminGhostAI(usr)) && !Adjacent(usr))
+ if((!HAS_SILICON_ACCESS(usr) && !isAdminGhostAI(usr)) && !Adjacent(usr))
return
if(busy)
balloon_alert(usr, "printing started!")
diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm
index 429ecd70b66..ccb21be468c 100644
--- a/code/game/machinery/buttons.dm
+++ b/code/game/machinery/buttons.dm
@@ -45,7 +45,7 @@
if(!built && !device && device_type)
device = new device_type(src)
- src.check_access(null)
+ check_access(null)
if(length(req_access) || length(req_one_access))
board = new(src)
@@ -185,7 +185,7 @@
id = "[port.shuttle_id]_[id]"
setup_device()
-/obj/machinery/button/attack_hand(mob/user, list/modifiers)
+/obj/machinery/button/interact(mob/user)
. = ..()
if(.)
return
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index 2d9a2a221b4..e79eadf53b9 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -109,7 +109,7 @@
/obj/machinery/camera/proc/upgradeEmpProof(malf_upgrade, ignore_malf_upgrades)
if(isEmpProof(ignore_malf_upgrades)) //pass a malf upgrade to ignore_malf_upgrades so we can replace the malf module with the normal one
return //that way if someone tries to upgrade an already malf-upgraded camera, it'll just upgrade it to a normal version.
- AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES | EMP_PROTECT_CONTENTS)
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
var/obj/structure/camera_assembly/assembly = assembly_ref?.resolve()
if(malf_upgrade)
assembly.malf_emp_firmware_active = TRUE //don't add parts to drop, update icon, ect. reconstructing it will also retain the upgrade.
@@ -125,7 +125,7 @@
/obj/machinery/camera/proc/removeEmpProof(ignore_malf_upgrades)
if(ignore_malf_upgrades) //don't downgrade it if malf software is forced onto it.
return
- RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES | EMP_PROTECT_CONTENTS)
+ RemoveElement(/datum/element/empprotection, EMP_PROTECT_ALL)
upgrades &= ~CAMERA_UPGRADE_EMP_PROOF
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index da807f52764..58bf0c75b1f 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -50,7 +50,7 @@
/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
. = ..()
- if(!user.client) //prevents errors by trying to pass clients that don't exist.
+ if(!user.can_perform_action(src, NEED_DEXTERITY|ALLOW_SILICON_REACH)) //prevents monkeys from using camera consoles
return
// Update UI
ui = SStgui.try_update_ui(user, src, ui)
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 3ae84fbc421..9dd2deaaeb6 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -102,19 +102,19 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
/// Are we NOT a silicon, AND we're logged in as the captain?
/obj/machinery/computer/communications/proc/authenticated_as_non_silicon_captain(mob/user)
- if (issilicon(user))
+ if (HAS_SILICON_ACCESS(user))
return FALSE
return ACCESS_CAPTAIN in authorize_access
/// Are we a silicon, OR we're logged in as the captain?
/obj/machinery/computer/communications/proc/authenticated_as_silicon_or_captain(mob/user)
- if (issilicon(user))
+ if (HAS_SILICON_ACCESS(user))
return TRUE
return ACCESS_CAPTAIN in authorize_access
/// Are we a silicon, OR logged in?
/obj/machinery/computer/communications/proc/authenticated(mob/user)
- if (issilicon(user))
+ if (HAS_SILICON_ACCESS(user))
return TRUE
return authenticated
@@ -202,7 +202,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
return
// Check if they have
- if (!issilicon(usr))
+ if (!HAS_SILICON_ACCESS(usr))
var/obj/item/held_item = usr.get_active_held_item()
var/obj/item/card/id/id_card = held_item?.GetID()
if (!istype(id_card))
@@ -301,7 +301,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
state = STATE_MAIN
if ("recallShuttle")
// AIs cannot recall the shuttle
- if (!authenticated(usr) || issilicon(usr) || syndicate)
+ if (!authenticated(usr) || HAS_SILICON_ACCESS(usr) || syndicate)
return
SSshuttle.cancelEvac(usr)
if ("requestNukeCodes")
@@ -570,7 +570,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
"syndicate" = syndicate,
)
- var/ui_state = issilicon(user) ? cyborg_state : state
+ var/ui_state = HAS_SILICON_ACCESS(user) ? cyborg_state : state
var/has_connection = has_communication()
data["hasConnection"] = has_connection
@@ -587,9 +587,9 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
data["safeCodeDeliveryWait"] = 0
data["safeCodeDeliveryArea"] = null
- if (authenticated || issilicon(user))
+ if (authenticated || HAS_SILICON_ACCESS(user))
data["authenticated"] = TRUE
- data["canLogOut"] = !issilicon(user)
+ data["canLogOut"] = !HAS_SILICON_ACCESS(user)
data["page"] = ui_state
if ((obj_flags & EMAGGED) || syndicate)
@@ -600,7 +600,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
data["canBuyShuttles"] = can_buy_shuttles(user)
data["canMakeAnnouncement"] = FALSE
data["canMessageAssociates"] = FALSE
- data["canRecallShuttles"] = !issilicon(user)
+ data["canRecallShuttles"] = !HAS_SILICON_ACCESS(user)
data["canRequestNuke"] = FALSE
data["canSendToSectors"] = FALSE
data["canSetAlertLevel"] = FALSE
@@ -612,7 +612,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
data["aprilFools"] = check_holidays(APRIL_FOOLS)
data["alertLevel"] = SSsecurity_level.get_current_level_as_text()
data["authorizeName"] = authorize_name
- data["canLogOut"] = !issilicon(user)
+ data["canLogOut"] = !HAS_SILICON_ACCESS(user)
data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac()
if(syndicate)
data["shuttleCanEvacOrFailReason"] = "You cannot summon the shuttle from this console!"
@@ -641,7 +641,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
data["engineeringOverride"] = GLOB.force_eng_override //SKYRAT EDIT - Engineering Override Toggle
data["alertLevelTick"] = alert_level_tick
data["canMakeAnnouncement"] = TRUE
- data["canSetAlertLevel"] = issilicon(user) ? "NO_SWIPE_NEEDED" : "SWIPE_NEEDED"
+ data["canSetAlertLevel"] = HAS_SILICON_ACCESS(user) ? "NO_SWIPE_NEEDED" : "SWIPE_NEEDED"
else if(syndicate)
data["canMakeAnnouncement"] = TRUE
@@ -745,7 +745,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
return is_station_level(z_level) || is_centcom_level(z_level)
/obj/machinery/computer/communications/proc/set_state(mob/user, new_state)
- if (issilicon(user))
+ if (HAS_SILICON_ACCESS(user))
cyborg_state = new_state
else
state = new_state
@@ -755,7 +755,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
/obj/machinery/computer/communications/proc/can_buy_shuttles(mob/user)
if (!SSmapping.config.allow_custom_shuttles)
return FALSE
- if (issilicon(user))
+ if (HAS_SILICON_ACCESS(user))
return FALSE
var/has_access = FALSE
@@ -798,7 +798,7 @@ GLOBAL_VAR_INIT(cops_arrived, FALSE)
return length(CONFIG_GET(keyed_list/cross_server)) > 0
/obj/machinery/computer/communications/proc/make_announcement(mob/living/user)
- var/is_ai = issilicon(user)
+ var/is_ai = HAS_SILICON_ACCESS(user)
if(!SScommunications.can_announce(user, is_ai))
to_chat(user, span_alert("Intercomms recharging. Please stand by."))
return
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index 15fa4964520..1f30aa758f6 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -97,7 +97,8 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
var/list/jobs = list(
// Note that jobs divisible by 10 are considered heads of staff, and bolded
// 00: Captain
- JOB_CAPTAIN = 00,
+ JOB_CAPTAIN = 0,
+ JOB_HUMAN_AI = 1,
// 10-19: Security
JOB_HEAD_OF_SECURITY = 10,
JOB_WARDEN = 11,
@@ -195,7 +196,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
z = T.z
. = list(
"sensors" = update_data(z),
- "link_allowed" = isAI(user)
+ "link_allowed" = HAS_AI_ACCESS(user)
)
/datum/crewmonitor/proc/update_data(z)
diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm
index 423633d31ac..372d75822b7 100644
--- a/code/game/machinery/computer/law.dm
+++ b/code/game/machinery/computer/law.dm
@@ -42,6 +42,11 @@
desc = "Used to upload laws to the AI."
circuit = /obj/item/circuitboard/computer/aiupload
+/obj/machinery/computer/upload/ai/Initialize(mapload)
+ . = ..()
+ if(mapload && HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI))
+ return INITIALIZE_HINT_QDEL
+
/obj/machinery/computer/upload/ai/interact(mob/user)
current = select_active_ai(user, z)
diff --git a/code/game/machinery/computer/records/records.dm b/code/game/machinery/computer/records/records.dm
index 36a89e577ed..c3aba60e4c6 100644
--- a/code/game/machinery/computer/records/records.dm
+++ b/code/game/machinery/computer/records/records.dm
@@ -132,30 +132,12 @@
/obj/machinery/computer/records/proc/expunge_record_info(datum/record/crew/target)
return
-/// Detects whether a user can use buttons on the machine
-/obj/machinery/computer/records/proc/has_auth(mob/user)
- if(issilicon(user) || isAdminGhostAI(user)) // Silicons don't need to authenticate
- return TRUE
-
- if(!isliving(user))
- return FALSE
- var/mob/living/player = user
-
- var/obj/item/card/auth = player.get_idcard(TRUE)
- if(!auth)
- return FALSE
- var/list/access = auth.GetAccess()
- if(!check_access_list(access))
- return FALSE
-
- return TRUE
-
/// Inserts a new record into GLOB.manifest.general. Requires a photo to be taken.
/obj/machinery/computer/records/proc/insert_new_record(mob/user, obj/item/photo/mugshot)
if(!mugshot || !is_operational || !user.can_perform_action(src, ALLOW_SILICON_REACH))
return FALSE
- if(!authenticated && !has_auth(user))
+ if(!authenticated && !allowed(user))
balloon_alert(user, "access denied")
playsound(src, 'sound/machines/terminal_error.ogg', 70, TRUE)
return FALSE
@@ -184,7 +166,7 @@
if(!user.can_perform_action(src, ALLOW_SILICON_REACH) || !is_operational)
return FALSE
- if(!has_auth(user))
+ if(!allowed(user))
balloon_alert(user, "access denied")
playsound(src, 'sound/machines/terminal_error.ogg', 70, TRUE)
return FALSE
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 2d727749c0d..5c67d9f0003 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -803,7 +803,7 @@
. = ..()
if(.)
return
- if(!(issilicon(user) || isAdminGhostAI(user)))
+ if(!HAS_SILICON_ACCESS(user))
if(isElectrified() && shock(user, 100))
return
@@ -1001,7 +1001,7 @@
return TRUE
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
- if(!issilicon(user) && !isAdminGhostAI(user))
+ if(!HAS_SILICON_ACCESS(user))
if(isElectrified() && (C.obj_flags & CONDUCTS_ELECTRICITY) && shock(user, 75))
return
add_fingerprint(user)
@@ -1689,7 +1689,7 @@
. = TRUE
/obj/machinery/door/airlock/proc/user_allowed(mob/user)
- return (issilicon(user) && canAIControl(user)) || isAdminGhostAI(user)
+ return (HAS_SILICON_ACCESS(user) && canAIControl(user)) || isAdminGhostAI(user)
/obj/machinery/door/airlock/proc/shock_restore(mob/user)
if(!user_allowed(user))
@@ -2311,6 +2311,7 @@
/obj/machinery/door/airlock/cult/Initialize(mapload)
. = ..()
new openingoverlaytype(loc)
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/machinery/door/airlock/cult/canAIControl(mob/user)
return (IS_CULTIST(user) && !isAllPowerCut())
@@ -2361,9 +2362,6 @@
/obj/machinery/door/airlock/cult/narsie_act()
return
-/obj/machinery/door/airlock/cult/emp_act(severity)
- return
-
/obj/machinery/door/airlock/cult/friendly
friendly = TRUE
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index aa7e6682402..05b8d883dac 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -116,7 +116,7 @@
if(!can_open_with_hands)
return .
- if(isaicamera(user) || issilicon(user))
+ if(isaicamera(user) || HAS_SILICON_ACCESS(user))
return .
if(isnull(held_item) && Adjacent(user))
diff --git a/code/game/machinery/doors/passworddoor.dm b/code/game/machinery/doors/passworddoor.dm
index f4e2bcc2d4c..df8f7fa936a 100644
--- a/code/game/machinery/doors/passworddoor.dm
+++ b/code/game/machinery/doors/passworddoor.dm
@@ -39,6 +39,7 @@
. = ..()
if(voice_activated)
become_hearing_sensitive()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/machinery/door/password/get_save_vars()
return ..() + NAMEOF(src, password)
@@ -85,8 +86,5 @@
return TRUE
return FALSE
-/obj/machinery/door/password/emp_act(severity)
- return
-
/obj/machinery/door/password/ex_act(severity, target)
return FALSE
diff --git a/code/game/machinery/modular_shield.dm b/code/game/machinery/modular_shield.dm
index f4e15cfa878..63bd1875c60 100644
--- a/code/game/machinery/modular_shield.dm
+++ b/code/game/machinery/modular_shield.dm
@@ -711,6 +711,7 @@
//Damage from emp
/obj/structure/emergency_shield/modular/emp_act(severity)
+ . = ..()
if(isnull(shield_generator))
qdel(src)
return
diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm
index 5caa41d9089..c15421cbf5a 100644
--- a/code/game/machinery/navbeacon.dm
+++ b/code/game/machinery/navbeacon.dm
@@ -165,7 +165,7 @@
controls["cover_locked"] = cover_locked
data["locked"] = controls_locked
- data["siliconUser"] = issilicon(user)
+ data["siliconUser"] = HAS_SILICON_ACCESS(user)
data["controls"] = controls
return data
@@ -190,7 +190,7 @@
controls_locked = !controls_locked
return TRUE
- if(controls_locked && !issilicon(usr))
+ if(controls_locked && !HAS_SILICON_ACCESS(usr))
return
switch(action)
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
index d1206cfc1c5..048e63f95f6 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -187,7 +187,7 @@
. = ..()
if(.)
return
- if(locked && !issilicon(user))
+ if(locked && !HAS_SILICON_ACCESS(user))
to_chat(user, span_warning("The machine is locked, you are unable to use it!"))
return
if(panel_open)
@@ -479,7 +479,7 @@
if(!anchored)
balloon_alert(user, "not secured!")
return
- if(locked && !issilicon(user))
+ if(locked && !HAS_SILICON_ACCESS(user))
balloon_alert(user, "locked!")
return
if(!powernet)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index b34f7873648..f96e1c1199b 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -375,7 +375,7 @@
src,
choices,
custom_check = CALLBACK(src, PROC_REF(check_interactable), user),
- require_near = !issilicon(user),
+ require_near = !HAS_SILICON_ACCESS(user),
autopick_single_option = FALSE
)
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index 3fb86271699..5086f7e91a7 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -88,7 +88,7 @@
return
var/mob/living/current_user = usr
- if(!issilicon(current_user))
+ if(!HAS_SILICON_ACCESS(current_user))
if(!istype(current_user.get_active_held_item(), /obj/item/multitool))
return
@@ -248,7 +248,7 @@
/obj/machinery/telecomms/proc/get_multitool(mob/user)
var/obj/item/multitool/multitool = null
// Let's double check
- if(!issilicon(user) && istype(user.get_active_held_item(), /obj/item/multitool))
+ if(!HAS_SILICON_ACCESS(user) && istype(user.get_active_held_item(), /obj/item/multitool))
multitool = user.get_active_held_item()
else if(isAI(user))
var/mob/living/silicon/ai/U = user
@@ -259,6 +259,6 @@
return multitool
/obj/machinery/telecomms/proc/canAccess(mob/user)
- if(issilicon(user) || in_range(user, src))
+ if(HAS_SILICON_ACCESS(user) || in_range(user, src))
return TRUE
return FALSE
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index fd0fea92292..21e3f396f8d 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -24,7 +24,8 @@ GLOBAL_LIST_INIT(dye_registry, list(
DYE_REDCOAT = /obj/item/clothing/under/costume/redcoat,
DYE_PRISONER = /obj/item/clothing/under/rank/prisoner,
DYE_SYNDICATE = /obj/item/clothing/under/syndicate,
- DYE_CENTCOM = /obj/item/clothing/under/rank/centcom/commander
+ DYE_CENTCOM = /obj/item/clothing/under/rank/centcom/commander,
+ DYE_COSMIC = /obj/item/clothing/under/rank/station_trait/human_ai,
),
DYE_REGISTRY_JUMPSKIRT = list(
DYE_RED = /obj/item/clothing/under/color/jumpskirt/red,
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 0c293ac502a..48e92e1477d 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -1078,6 +1078,14 @@
assigned_icon_state = "assigned_silver"
wildcard_slots = WILDCARD_LIMIT_SILVER
+/obj/item/card/id/advanced/robotic
+ name = "magnetic identification card"
+ desc = "An integrated card which shows the work poured into opening doors."
+ icon_state = "card_carp" //im not a spriter
+ inhand_icon_state = "silver_id"
+ assigned_icon_state = "assigned_silver"
+ wildcard_slots = WILDCARD_LIMIT_GREY
+
/datum/id_trim/maint_reaper
access = list(ACCESS_MAINT_TUNNELS)
trim_state = "trim_janitor"
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index 0ab7e5d94b5..c619f7d7018 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -16,6 +16,8 @@
/obj/item/aicard/Initialize(mapload)
. = ..()
+ if(mapload && HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI))
+ return INITIALIZE_HINT_QDEL
ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT)
/obj/item/aicard/Destroy(force)
diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm
index a9046145168..6b627fa8eff 100644
--- a/code/game/objects/items/devices/laserpointer.dm
+++ b/code/game/objects/items/devices/laserpointer.dm
@@ -63,6 +63,15 @@
. = ..()
diode = new /obj/item/stock_parts/micro_laser/ultra
+/obj/item/laser_pointer/infinite_range
+ name = "infinite laser pointer"
+ desc = "Used to shine in the eyes of Cyborgs who need a bit of a push, this works through camera consoles."
+ max_range = INFINITE
+
+/obj/item/laser_pointer/infinite_range/Initialize(mapload)
+ . = ..()
+ diode = new /obj/item/stock_parts/servo/femto
+
/obj/item/laser_pointer/screwdriver_act(mob/living/user, obj/item/tool)
if(diode)
tool.play_tool_sound(src)
@@ -193,16 +202,17 @@
to_chat(user, span_warning("Your fingers can't press the button!"))
return
- if(!IN_GIVEN_RANGE(target, user, max_range))
- to_chat(user, span_warning("\The [target] is too far away!"))
- return
- if(!(user in (view(max_range, target)))) //check if we are visible from the target's PoV
- if(isnull(crystal_lens))
- to_chat(user, span_warning("You can't point with [src] through walls!"))
- return
- if(!((user.sight & SEE_OBJS) || (user.sight & SEE_MOBS))) //only let it work if we have xray or thermals. mesons don't count because they are easier to get.
- to_chat(user, span_notice("You can't quite make out your target and you fail to shine at it."))
+ if(max_range != INFINITE)
+ if(!IN_GIVEN_RANGE(target, user, max_range))
+ to_chat(user, span_warning("\The [target] is too far away!"))
return
+ if(!(user in (view(max_range, target)))) //check if we are visible from the target's PoV
+ if(isnull(crystal_lens))
+ to_chat(user, span_warning("You can't point with [src] through walls!"))
+ return
+ if(!((user.sight & SEE_OBJS) || (user.sight & SEE_MOBS))) //only let it work if we have xray or thermals. mesons don't count because they are easier to get.
+ to_chat(user, span_notice("You can't quite make out your target and you fail to shine at it."))
+ return
add_fingerprint(user)
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 7774902b83b..b7a96d777ef 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -188,7 +188,31 @@
greyscale_colors = "#24a157#dca01b"
/obj/item/encryptionkey/ai //ported from NT, this goes 'inside' the AI.
- channels = list(RADIO_CHANNEL_COMMAND = 1, RADIO_CHANNEL_SECURITY = 1, RADIO_CHANNEL_ENGINEERING = 1, RADIO_CHANNEL_SCIENCE = 1, RADIO_CHANNEL_MEDICAL = 1, RADIO_CHANNEL_SUPPLY = 1, RADIO_CHANNEL_SERVICE = 1, RADIO_CHANNEL_AI_PRIVATE = 1)
+ channels = list(
+ RADIO_CHANNEL_COMMAND = 1,
+ RADIO_CHANNEL_SECURITY = 1,
+ RADIO_CHANNEL_ENGINEERING = 1,
+ RADIO_CHANNEL_SCIENCE = 1,
+ RADIO_CHANNEL_MEDICAL = 1,
+ RADIO_CHANNEL_SUPPLY = 1,
+ RADIO_CHANNEL_SERVICE = 1,
+ RADIO_CHANNEL_AI_PRIVATE = 1,
+ )
+
+/obj/item/encryptionkey/ai_with_binary
+ name = "ai encryption key"
+ channels = list(
+ RADIO_CHANNEL_COMMAND = 1,
+ RADIO_CHANNEL_SECURITY = 1,
+ RADIO_CHANNEL_ENGINEERING = 1,
+ RADIO_CHANNEL_SCIENCE = 1,
+ RADIO_CHANNEL_MEDICAL = 1,
+ RADIO_CHANNEL_SUPPLY = 1,
+ RADIO_CHANNEL_SERVICE = 1,
+ RADIO_CHANNEL_AI_PRIVATE = 1,
+ )
+ translate_binary = TRUE
+ translated_language = /datum/language/machine
/obj/item/encryptionkey/ai/evil //ported from NT, this goes 'inside' the AI.
name = "syndicate binary encryption key"
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 4d6b8edbd2d..fff7b0a655a 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -117,6 +117,8 @@ GLOBAL_LIST_INIT(channel_tokens, list(
/obj/item/radio/headset/dropped(mob/user, silent)
. = ..()
+ if(QDELETED(src)) //This can be called as a part of destroy
+ return
for(var/language in language_list)
user.remove_language(language, language_flags = UNDERSTOOD_LANGUAGE, source = LANGUAGE_RADIOKEY)
@@ -348,17 +350,25 @@ GLOBAL_LIST_INIT(channel_tokens, list(
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/silicon/pai
- name = "\proper mini Integrated Subspace Transceiver "
+ name = "\proper mini Integrated Subspace Transceiver"
subspace_transmission = FALSE
/obj/item/radio/headset/silicon/ai
- name = "\proper Integrated Subspace Transceiver "
+ name = "\proper Integrated Subspace Transceiver"
keyslot2 = new /obj/item/encryptionkey/ai
command = TRUE
+/obj/item/radio/headset/silicon/human_ai
+ name = "\proper Disconnected Subspace Transceiver"
+ desc = "A headset that is rumored to be one day implanted into a brain in a jar directly."
+ icon_state = "rob_headset"
+ worn_icon_state = "rob_headset"
+ keyslot2 = new /obj/item/encryptionkey/ai_with_binary
+ command = TRUE
+
/obj/item/radio/headset/silicon/ai/evil
- name = "\proper Evil Integrated Subspace Transceiver "
+ name = "\proper Evil Integrated Subspace Transceiver"
keyslot2 = new /obj/item/encryptionkey/ai/evil
command = FALSE
diff --git a/code/game/objects/items/machine_wand.dm b/code/game/objects/items/machine_wand.dm
new file mode 100644
index 00000000000..71ea2fae1b6
--- /dev/null
+++ b/code/game/objects/items/machine_wand.dm
@@ -0,0 +1,181 @@
+///When EMPed, how long the remote will be disabled for by default.
+#define EMP_TIMEOUT_DURATION (2 MINUTES)
+
+/obj/item/machine_remote
+ name = "machine wand"
+ desc = "A remote for controlling machines and bots around the station."
+ icon = 'icons/obj/antags/syndicate_tools.dmi'
+ icon_state = "weakpoint_locator"
+ inhand_icon_state = "weakpoint_locator"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ w_class = WEIGHT_CLASS_NORMAL
+ ///If we're unable to be used, this is how long we have left to wait.
+ COOLDOWN_DECLARE(timeout_time)
+ ///The appearance put onto machines being actively controlled.
+ var/mutable_appearance/bug_appearance
+ ///Direct reference to the moving bug effect that moves towards machines we direct it at.
+ var/obj/effect/bug_moving/moving_bug
+ ///The machine that's currently being controlled.
+ var/atom/movable/controlling_machine_or_bot
+
+/obj/item/machine_remote/Initialize(mapload)
+ . = ..()
+ bug_appearance = mutable_appearance('icons/effects/effects.dmi', "fly-surrounding", ABOVE_WINDOW_LAYER)
+ register_context()
+
+/obj/item/machine_remote/Destroy(force)
+ . = ..()
+ if(controlling_machine_or_bot)
+ remove_old_machine()
+ QDEL_NULL(moving_bug)
+ QDEL_NULL(bug_appearance)
+
+/obj/item/machine_remote/examine(mob/user)
+ . = ..()
+ if(controlling_machine_or_bot)
+ . += span_notice("It is currently controlling [controlling_machine_or_bot]. Use in-hand to interact with it.")
+
+/obj/item/machine_remote/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(controlling_machine_or_bot)
+ context[SCREENTIP_CONTEXT_LMB] = "Use [controlling_machine_or_bot]"
+ context[SCREENTIP_CONTEXT_ALT_LMB] = "Flush Control"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+
+/obj/item/machine_remote/proc/on_control_destroy(obj/machinery/source)
+ SIGNAL_HANDLER
+ remove_old_machine()
+
+/obj/item/machine_remote/ui_interact(mob/user, datum/tgui/ui)
+ if(!COOLDOWN_FINISHED(src, timeout_time))
+ playsound(src, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ say("Remote control disabled temporarily. Please try again soon.")
+ return FALSE
+ if(!controlling_machine_or_bot)
+ return
+ if(controlling_machine_or_bot.ui_interact(user, ui))
+ return
+ controlling_machine_or_bot.interact(user) //no ui, interact instead (to open windoors and such)
+
+/obj/item/machine_remote/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(controlling_machine_or_bot)
+ return controlling_machine_or_bot.ui_act(action, params, ui, state)
+
+/obj/item/machine_remote/AltClick(mob/user)
+ . = ..()
+ if(moving_bug) //we have a bug in transit, so let's kill it.
+ QDEL_NULL(moving_bug)
+ if(!controlling_machine_or_bot)
+ return
+ say("Remote control over [controlling_machine_or_bot] stopped.")
+ remove_old_machine()
+
+/obj/item/machine_remote/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!COOLDOWN_FINISHED(src, timeout_time))
+ playsound(src, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ say("Remote control disabled temporarily. Please try again soon.")
+ return FALSE
+ if(!ismachinery(target) && !isbot(target))
+ return
+ if(moving_bug) //we have a bug in transit already, so let's kill it.
+ QDEL_NULL(moving_bug)
+ var/turf/spawning_turf = (controlling_machine_or_bot ? get_turf(controlling_machine_or_bot) : get_turf(src))
+ moving_bug = new(spawning_turf, src, target)
+ remove_old_machine()
+
+///Sets a controlled machine to a new machine, if possible. Checks if AIs can even control it.
+/obj/item/machine_remote/proc/set_controlled_machine(obj/machinery/new_machine)
+ if(controlling_machine_or_bot == new_machine)
+ return
+ remove_old_machine()
+ if(istype(new_machine, /obj/machinery/power/apc))
+ var/obj/machinery/power/apc/new_apc = new_machine
+ if(new_apc.aidisabled)
+ say("AI wire cut, machine uncontrollable.")
+ return
+ else if(istype(new_machine, /obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/new_airlock = new_machine
+ if(!new_airlock.canAIControl())
+ say("AI wire cut, machine uncontrollable.")
+ return
+ controlling_machine_or_bot = new_machine
+ controlling_machine_or_bot.add_overlay(bug_appearance)
+ RegisterSignal(controlling_machine_or_bot, COMSIG_QDELETING, PROC_REF(on_control_destroy))
+ RegisterSignal(controlling_machine_or_bot, COMSIG_ATOM_EMP_ACT, PROC_REF(on_machine_emp))
+
+///Removes the machine being controlled as the current machine, taking its signals and overlays with it.
+/obj/item/machine_remote/proc/remove_old_machine()
+ if(!controlling_machine_or_bot)
+ return
+ UnregisterSignal(controlling_machine_or_bot, list(COMSIG_ATOM_EMP_ACT, COMSIG_QDELETING))
+ controlling_machine_or_bot.cut_overlay(bug_appearance)
+ controlling_machine_or_bot = null
+
+///Called when the machine we're controlling is EMP, removing our control from it.
+/obj/item/machine_remote/proc/on_machine_emp(datum/source, severity, protection)
+ SIGNAL_HANDLER
+ if(severity & EMP_PROTECT_CONTENTS)
+ return
+ disable_remote(EMP_TIMEOUT_DURATION)
+
+/obj/item/machine_remote/proc/disable_remote(timeout_duration)
+ remove_old_machine()
+ COOLDOWN_START(src, timeout_time, timeout_duration)
+
+///The effect of the bug moving towards the selected machinery to mess with.
+/obj/effect/bug_moving
+ name = "bug"
+ desc = "Where da bug goin?"
+ icon_state = "fly"
+ obj_flags = CAN_BE_HIT
+ max_integrity = 20
+ uses_integrity = TRUE
+ plane = ABOVE_GAME_PLANE
+ layer = FLY_LAYER
+ movement_type = PHASING
+ ///The controller that's sending us out to the machine.
+ var/obj/item/machine_remote/controller
+ ///The machine we are trying to get remote access to.
+ var/atom/movable/thing_moving_towards
+
+/obj/effect/bug_moving/Initialize(mapload, obj/item/machine_remote/controller, atom/movable/thing_moving_towards)
+ . = ..()
+ if(!controller)
+ CRASH("a moving bug has been created by something that isn't a machine remote controller!")
+ if(!thing_moving_towards)
+ CRASH("a moving bug has been created but isn't moving towards anything!")
+ src.controller = controller
+ src.thing_moving_towards = thing_moving_towards
+ var/datum/move_loop/loop = SSmove_manager.home_onto(src, thing_moving_towards, delay = 5, flags = MOVEMENT_LOOP_NO_DIR_UPDATE)
+ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(reached_destination_check))
+ RegisterSignal(thing_moving_towards, COMSIG_QDELETING, PROC_REF(on_machine_del))
+
+/obj/effect/bug_moving/Destroy(force)
+ if(controller)
+ controller.moving_bug = null
+ controller = null
+ thing_moving_towards = null
+ return ..()
+
+/obj/effect/bug_moving/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ controller.disable_remote(EMP_TIMEOUT_DURATION)
+ qdel(src)
+
+/obj/effect/bug_moving/proc/reached_destination_check(datum/move_loop/source, result)
+ SIGNAL_HANDLER
+ if(!Adjacent(thing_moving_towards))
+ return
+ controller.set_controlled_machine(thing_moving_towards)
+ qdel(src)
+
+/obj/effect/bug_moving/proc/on_machine_del(datum/move_loop/source)
+ SIGNAL_HANDLER
+ qdel(src)
+
+#undef EMP_TIMEOUT_DURATION
diff --git a/code/game/objects/items/puzzle_pieces.dm b/code/game/objects/items/puzzle_pieces.dm
index 1e99042a612..77b07488d6c 100644
--- a/code/game/objects/items/puzzle_pieces.dm
+++ b/code/game/objects/items/puzzle_pieces.dm
@@ -74,6 +74,7 @@
. = ..()
if(!isnull(puzzle_id) && uses_queuelinks)
SSqueuelinks.add_to_queue(src, puzzle_id)
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/machinery/door/puzzle/MatchedLinks(id, list/partners)
for(var/partner in partners)
@@ -88,9 +89,6 @@
/obj/machinery/door/puzzle/Bumped(atom/movable/AM)
return !density && ..()
-/obj/machinery/door/puzzle/emp_act(severity)
- return
-
/obj/machinery/door/puzzle/ex_act(severity, target)
return FALSE
diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm
index 214187b7708..eabd9bc8304 100644
--- a/code/game/objects/items/teleportation.dm
+++ b/code/game/objects/items/teleportation.dm
@@ -371,6 +371,7 @@
playsound(src, 'sound/machines/twobeep.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
/obj/item/syndicate_teleporter/emp_act(severity)
+ . = ..()
if(!prob(50/severity))
return
var/teleported_something = FALSE
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index cb57c5049bb..dcc3b03dc78 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -28,14 +28,6 @@
var/current_skin //Has the item been reskinned?
var/list/unique_reskin //List of options to reskin.
- // Access levels, used in modules\jobs\access.dm
- /// List of accesses needed to use this object: The user must possess all accesses in this list in order to use the object.
- /// Example: If req_access = list(ACCESS_ENGINE, ACCESS_CE)- then the user must have both ACCESS_ENGINE and ACCESS_CE in order to use the object.
- var/list/req_access
- /// List of accesses needed to use this object: The user must possess at least one access in this list in order to use the object.
- /// Example: If req_one_access = list(ACCESS_ENGINE, ACCESS_CE)- then the user must have either ACCESS_ENGINE or ACCESS_CE in order to use the object.
- var/list/req_one_access
-
/// Custom fire overlay icon, will just use the default overlay if this is null
var/custom_fire_overlay
/// Particles this obj uses when burning, if any
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index eef6059d984..7615a37a88b 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -441,4 +441,9 @@ That prevents a few funky behaviors.
name = "AI core (AI Core Board)" //Well, duh, but best to be consistent
var/battery = 200 //backup battery for when the AI loses power. Copied to/from AI mobs when carding, and placed here to avoid recharge via deconning the core
+/obj/item/circuitboard/aicore/Initialize(mapload)
+ . = ..()
+ if(mapload && HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI))
+ return INITIALIZE_HINT_QDEL
+
#undef AI_CORE_BRAIN
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index fc17b6924df..7699c3f76bc 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -867,7 +867,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/datum/ai_module/upgrade/upgrade_turrets/upgrade(mob/living/silicon/ai/AI)
for(var/obj/machinery/porta_turret/ai/turret as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/porta_turret/ai))
- turret.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES | EMP_PROTECT_CONTENTS)
+ turret.AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
turret.max_integrity = 200
turret.repair_damage(200)
turret.lethal_projectile = /obj/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR.
diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
index c8e93257b80..6bb07f4fbc1 100644
--- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
@@ -186,7 +186,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
. += span_notice("Right-click to [locked ? "unlock" : "lock"] the interface.")
/obj/machinery/airalarm/ui_status(mob/user, datum/ui_state/state)
- if(user.has_unlimited_silicon_privilege && aidisabled)
+ if(HAS_SILICON_ACCESS(user) && aidisabled)
to_chat(user, "AI control has been disabled.")
else if(!shorted)
return ..()
@@ -227,7 +227,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
var/data = list()
data["locked"] = locked
- data["siliconUser"] = user.has_unlimited_silicon_privilege
+ data["siliconUser"] = HAS_SILICON_ACCESS(user)
data["emagged"] = (obj_flags & EMAGGED ? 1 : 0)
data["dangerLevel"] = danger_level
data["atmosAlarm"] = !!my_area.active_alarms[ALARM_ATMOS]
@@ -288,7 +288,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
singular_tlv["hazard_max"] = tlv.hazard_max
data["tlvSettings"] += list(singular_tlv)
- if(!locked || user.has_unlimited_silicon_privilege)
+ if(!locked || HAS_SILICON_ACCESS(user))
data["vents"] = list()
for(var/obj/machinery/atmospherics/components/unary/vent_pump/vent as anything in my_area.air_vents)
data["vents"] += list(list(
@@ -345,7 +345,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
if(. || buildstage != AIR_ALARM_BUILD_COMPLETE)
return
- if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled))
+ if((locked && !HAS_SILICON_ACCESS(usr)) || (HAS_SILICON_ACCESS(usr) && aidisabled))
return
var/mob/user = usr
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index 6bfe17ebc2e..36ae735395c 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -346,7 +346,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
density = TRUE
use_power = NO_POWER_USE
-/obj/machinery/gateway/away/interact(mob/user, special_state)
+/obj/machinery/gateway/away/interact(mob/user)
. = ..()
//SKYRAT EDIT ADDITION
var/list/type_blacklist = list(
diff --git a/code/modules/capture_the_flag/ctf_equipment.dm b/code/modules/capture_the_flag/ctf_equipment.dm
index dccf875a55a..0211a066555 100644
--- a/code/modules/capture_the_flag/ctf_equipment.dm
+++ b/code/modules/capture_the_flag/ctf_equipment.dm
@@ -156,8 +156,9 @@
ammo_x_offset = 2
shaded_charge = FALSE
-/obj/item/gun/energy/laser/instakill/emp_act() //implying you could stop the instagib
- return
+/obj/item/gun/energy/laser/instakill/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/gun/energy/laser/instakill/ctf/Initialize(mapload)
. = ..()
diff --git a/code/modules/cargo/department_order.dm b/code/modules/cargo/department_order.dm
index 7622993e901..c8eb6e99ac2 100644
--- a/code/modules/cargo/department_order.dm
+++ b/code/modules/cargo/department_order.dm
@@ -153,7 +153,7 @@ GLOBAL_LIST_INIT(department_order_cooldowns, list(
var/mob/living/carbon/human/human_orderer = usr
name = human_orderer.get_authentification_name()
rank = human_orderer.get_assignment(hand_first = TRUE)
- else if(issilicon(usr))
+ else if(HAS_SILICON_ACCESS(usr))
name = usr.real_name
rank = "Silicon"
//already have a signal to finalize the order
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index 79ae0b0fbf8..4942ea2c06a 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -95,7 +95,7 @@
if(D)
data["points"] = D.account_balance
data["locked"] = locked//swipe an ID to unlock
- data["siliconUser"] = user.has_unlimited_silicon_privilege
+ data["siliconUser"] = HAS_SILICON_ACCESS(user)
data["beaconzone"] = beacon ? get_area(beacon) : ""//where is the beacon located? outputs in the tgui
data["usingBeacon"] = usingBeacon //is the mode set to deliver to the beacon or the cargobay?
data["canBeacon"] = !usingBeacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location?
@@ -164,7 +164,7 @@
var/mob/living/carbon/human/H = usr
name = H.get_authentification_name()
rank = H.get_assignment(hand_first = TRUE)
- else if(issilicon(usr))
+ else if(HAS_SILICON_ACCESS(usr))
name = usr.real_name
rank = "Silicon"
var/reason = ""
diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm
index 38552e4cb9a..a48e9db6bc6 100644
--- a/code/modules/cargo/orderconsole.dm
+++ b/code/modules/cargo/orderconsole.dm
@@ -206,7 +206,7 @@
var/mob/living/carbon/human/human = user
name = human.get_authentification_name()
rank = human.get_assignment(hand_first = TRUE)
- else if(issilicon(user))
+ else if(HAS_SILICON_ACCESS(user))
name = user.real_name
rank = "Silicon"
diff --git a/code/modules/clothing/head/pirate.dm b/code/modules/clothing/head/pirate.dm
index fd45404efc3..818478ccb7d 100644
--- a/code/modules/clothing/head/pirate.dm
+++ b/code/modules/clothing/head/pirate.dm
@@ -10,20 +10,17 @@
/obj/item/clothing/head/costume/pirate/equipped(mob/user, slot)
. = ..()
- if(!ishuman(user))
+ if(!(slot_flags & slot))
return
- if(slot & ITEM_SLOT_HEAD)
- user.grant_language(/datum/language/piratespeak/, source = LANGUAGE_HAT)
- to_chat(user, span_boldnotice("You suddenly know how to speak like a pirate!"))
+ user.grant_language(/datum/language/piratespeak, source = LANGUAGE_HAT)
+ to_chat(user, span_boldnotice("You suddenly know how to speak like a pirate!"))
/obj/item/clothing/head/costume/pirate/dropped(mob/user)
. = ..()
- if(!ishuman(user))
+ if(QDELETED(src)) //This can be called as a part of destroy
return
- var/mob/living/carbon/human/H = user
- if(H.get_item_by_slot(ITEM_SLOT_HEAD) == src && !QDELETED(src)) //This can be called as a part of destroy
- user.remove_language(/datum/language/piratespeak/, source = LANGUAGE_HAT)
- to_chat(user, span_boldnotice("You can no longer speak like a pirate."))
+ user.remove_language(/datum/language/piratespeak, source = LANGUAGE_HAT)
+ to_chat(user, span_boldnotice("You can no longer speak like a pirate."))
/obj/item/clothing/head/costume/pirate/armored
armor_type = /datum/armor/pirate_armored
diff --git a/code/modules/clothing/outfits/plasmaman.dm b/code/modules/clothing/outfits/plasmaman.dm
index 40d7cfa888e..1b2e0ead148 100644
--- a/code/modules/clothing/outfits/plasmaman.dm
+++ b/code/modules/clothing/outfits/plasmaman.dm
@@ -295,3 +295,10 @@
uniform = /obj/item/clothing/under/plasmaman //call me when this is gags and not 10 million new assets
gloves = /obj/item/clothing/gloves/color/plasmaman/black
head = /obj/item/clothing/head/helmet/space/plasmaman
+
+/datum/outfit/plasmaman/human_ai
+ name = "Human AI Plasmaman"
+
+ uniform = /obj/item/clothing/under/plasmaman //same
+ gloves = /obj/item/clothing/gloves/color/plasmaman/black
+ head = /obj/item/clothing/head/helmet/space/plasmaman
diff --git a/code/modules/clothing/under/jobs/station_trait.dm b/code/modules/clothing/under/jobs/station_trait.dm
new file mode 100644
index 00000000000..034641d35b1
--- /dev/null
+++ b/code/modules/clothing/under/jobs/station_trait.dm
@@ -0,0 +1,12 @@
+/**
+ * Outfits limited to station trait jobs
+ */
+/obj/item/clothing/under/rank/station_trait
+ icon = 'icons/obj/clothing/under/station_trait.dmi'
+ worn_icon = 'icons/mob/clothing/under/station_trait.dmi'
+
+/obj/item/clothing/under/rank/station_trait/human_ai
+ name = "ai's uniform"
+ desc = "A outfit made of the cleanest of bluespace and, just kidding it's all fake."
+ icon_state = "human_ai"
+ inhand_icon_state = "armor_reflec"
diff --git a/code/modules/fishing/fishing_portal_machine.dm b/code/modules/fishing/fishing_portal_machine.dm
index 37b2c1f0090..494b29b4183 100644
--- a/code/modules/fishing/fishing_portal_machine.dm
+++ b/code/modules/fishing/fishing_portal_machine.dm
@@ -33,7 +33,7 @@
playsound(src, SFX_SPARKS, 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
return TRUE
-/obj/machinery/fishing_portal_generator/interact(mob/user, special_state)
+/obj/machinery/fishing_portal_generator/interact(mob/user)
. = ..()
if(active)
deactivate()
diff --git a/code/modules/food_and_drinks/machinery/coffeemaker.dm b/code/modules/food_and_drinks/machinery/coffeemaker.dm
index 8490b2b5cfb..de751979ca8 100644
--- a/code/modules/food_and_drinks/machinery/coffeemaker.dm
+++ b/code/modules/food_and_drinks/machinery/coffeemaker.dm
@@ -321,7 +321,7 @@
if(length(options) == 1)
choice = options[1]
else
- choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
+ choice = show_radial_menu(user, src, options, require_near = !HAS_SILICON_ACCESS(user))
// post choice verification
if(brewing || (isAI(user) && machine_stat & NOPOWER) || !user.can_perform_action(src, ALLOW_SILICON_REACH))
diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm
index ec5df27ca5a..abc22bcab45 100644
--- a/code/modules/food_and_drinks/machinery/microwave.dm
+++ b/code/modules/food_and_drinks/machinery/microwave.dm
@@ -394,7 +394,7 @@
var/swapped = FALSE
if(!isnull(cell))
cell.forceMove(drop_location())
- if(!issilicon(user) && Adjacent(user))
+ if(!HAS_SILICON_ACCESS(user) && Adjacent(user))
user.put_in_hands(cell)
cell = null
swapped = TRUE
@@ -481,7 +481,7 @@
vampire_charging_enabled = !vampire_charging_enabled
balloon_alert(user, "set to [vampire_charging_enabled ? "charge" : "cook"]")
playsound(src, 'sound/machines/twobeep_high.ogg', 50, FALSE)
- if(issilicon(user))
+ if(HAS_SILICON_ACCESS(user))
visible_message(span_notice("[user] sets \the [src] to [vampire_charging_enabled ? "charge" : "cook"]."), blind_message = span_notice("You hear \the [src] make an informative beep!"))
/obj/machinery/microwave/CtrlClick(mob/user)
@@ -500,22 +500,22 @@
return
if(operating || panel_open || !user.can_perform_action(src, ALLOW_SILICON_REACH))
return
- if(isAI(user) && (machine_stat & NOPOWER))
+ if(HAS_AI_ACCESS(user) && (machine_stat & NOPOWER))
return
if(!length(ingredients))
- if(isAI(user))
+ if(HAS_AI_ACCESS(user))
examine(user)
else
balloon_alert(user, "it's empty!")
return
- var/choice = show_radial_menu(user, src, isAI(user) ? ai_radial_options : radial_options, require_near = !issilicon(user))
+ var/choice = show_radial_menu(user, src, HAS_AI_ACCESS(user) ? ai_radial_options : radial_options, require_near = !HAS_SILICON_ACCESS(user))
// post choice verification
if(operating || panel_open || (!vampire_charging_capable && !anchored) || !user.can_perform_action(src, ALLOW_SILICON_REACH))
return
- if(isAI(user) && (machine_stat & NOPOWER))
+ if(HAS_AI_ACCESS(user) && (machine_stat & NOPOWER))
return
switch(choice)
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 58f7a1c8530..abe7dc4277b 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -102,7 +102,7 @@
// If the plant's not in either state, we can't do much else, so early return.
if(isnull(held_item))
// Silicons can't interact with trays :frown:
- if(issilicon(user))
+ if(HAS_SILICON_ACCESS(user))
return NONE
switch(plant_status)
@@ -1074,7 +1074,7 @@
. = ..()
if(.)
return
- if(issilicon(user)) //How does AI know what plant is?
+ if(HAS_SILICON_ACCESS(user)) //How does AI know what plant is?
return
if(plant_status == HYDROTRAY_PLANT_HARVESTABLE)
return myseed.harvest(user)
diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm
index f882912fb0c..859f83bf35a 100644
--- a/code/modules/jobs/access.dm
+++ b/code/modules/jobs/access.dm
@@ -1,11 +1,9 @@
-
-//
/**
* Returns TRUE if this mob has sufficient access to use this object
*
* * accessor - mob trying to access this object, !!CAN BE NULL!! because of telekiesis because we're in hell
*/
-/obj/proc/allowed(mob/accessor)
+/atom/movable/proc/allowed(mob/accessor)
var/result_bitflags = SEND_SIGNAL(src, COMSIG_OBJ_ALLOWED, accessor)
if(result_bitflags & COMPONENT_OBJ_ALLOW)
return TRUE
@@ -18,7 +16,10 @@
return TRUE
if(isnull(accessor)) //likely a TK user.
return FALSE
- if(issilicon(accessor))
+ if(isAdminGhostAI(accessor))
+ //Access can't stop the abuse
+ return TRUE
+ if(HAS_SILICON_ACCESS(accessor))
if(ispAI(accessor))
return FALSE
if(!(ROLE_SYNDICATE in accessor.faction))
@@ -27,9 +28,6 @@
if(onSyndieBase() && loc != accessor)
return FALSE
return TRUE //AI can do whatever it wants
- if(isAdminGhostAI(accessor))
- //Access can't stop the abuse
- return TRUE
//If the mob has the simple_access component with the requried access, we let them in.
else if(SEND_SIGNAL(accessor, COMSIG_MOB_TRIED_ACCESS, src) & ACCESS_ALLOWED)
return TRUE
@@ -52,23 +50,11 @@
return check_access_list(big_stompy_robot.accesses)
return FALSE
-/obj/item/proc/GetAccess()
- return list()
-
-/obj/item/proc/GetID()
- return null
-
-/obj/item/proc/RemoveID()
- return null
-
-/obj/item/proc/InsertID()
- return FALSE
-
// Check if an item has access to this object
-/obj/proc/check_access(obj/item/I)
+/atom/movable/proc/check_access(obj/item/I)
return check_access_list(I ? I.GetAccess() : null)
-/obj/proc/check_access_list(list/access_list)
+/atom/movable/proc/check_access_list(list/access_list)
if(!length(req_access) && !length(req_one_access))
return TRUE
@@ -86,20 +72,14 @@
return FALSE
return TRUE
-/*
- * Checks if this packet can access this device
- *
- * Normally just checks the access list however you can override it for
- * hacking proposes or if wires are cut
- *
- * Arguments:
- * * passkey - passkey from the datum/netdata packet
- */
-/obj/proc/check_access_ntnet(list/passkey)
- return check_access_list(passkey)
+/obj/item/proc/GetAccess()
+ return list()
-/// Returns the SecHUD job icon state for whatever this object's ID card is, if it has one.
-/obj/item/proc/get_sechud_job_icon_state()
- var/obj/item/card/id/id_card = GetID()
+/obj/item/proc/GetID()
+ return null
- return id_card?.get_trim_sechud_icon_state() || SECHUD_NO_ID
+/obj/item/proc/RemoveID()
+ return null
+
+/obj/item/proc/InsertID()
+ return FALSE
diff --git a/code/modules/jobs/departments/departments.dm b/code/modules/jobs/departments/departments.dm
index 9f2776bad0e..ce21920eced 100644
--- a/code/modules/jobs/departments/departments.dm
+++ b/code/modules/jobs/departments/departments.dm
@@ -18,7 +18,24 @@
var/list/department_jobs = list()
/// For separatists, what independent name prefix does their nation get named?
var/list/nation_prefixes = list()
+ /// List of /area types that are considered part of this department's "delivery" area.
+ /// Acts as a priority system, where first items are picked first.
+ var/list/department_delivery_areas = list()
+ /// List of categories in the cargo console that are associated with this department.
+ var/list/associated_cargo_groups = list()
+ /// The single access associated with the head of staff of this department.
+ var/head_of_staff_access
+ /// A list of generic access flags people in this department generally have.
+ var/list/department_access = list()
+/datum/job_department/New()
+ . = ..()
+ for(var/delivery_area_type in department_delivery_areas)
+ if(GLOB.areas_by_type[delivery_area_type])
+ return
+ //every area fallback didn't exist on this map so throw a mapping error and set some generic area that uuuh please exist okay
+ log_mapping("[type] has no valid areas to deliver to on this map, add some more fallback areas to its \"department_delivery_areas\" var.")
+ department_delivery_areas = list(/area/station/hallway/primary/central) //if this doesn't exist like honestly fuck your map man
/// Handles adding jobs to the department and setting up the job bitflags.
/datum/job_department/proc/add_job(datum/job/job)
@@ -76,6 +93,14 @@
label_class = "security"
ui_color = "#d46a78"
nation_prefixes = list("Securi", "Beepski", "Shitcuri", "Red", "Stunba", "Flashbango", "Flasha", "Stanfordi")
+ department_delivery_areas = list(
+ /area/station/security/office,
+ /area/station/security/brig,
+ /area/station/security/brig/upper,
+ )
+ associated_cargo_groups = list("Security", "Armory")
+ head_of_staff_access = ACCESS_HOS
+ department_access = REGION_ACCESS_SECURITY
/datum/job_department/engineering
department_name = DEPARTMENT_ENGINEERING
@@ -86,7 +111,10 @@
label_class = "engineering"
ui_color = "#dfb567"
nation_prefixes = list("Atomo", "Engino", "Power", "Teleco")
-
+ department_delivery_areas = list(/area/station/engineering/main)
+ associated_cargo_groups = list("Engineering", "Engine Construction", "Canisters & Materials")
+ head_of_staff_access = ACCESS_CE
+ department_access = REGION_ACCESS_ENGINEERING
/datum/job_department/medical
department_name = DEPARTMENT_MEDICAL
@@ -97,7 +125,15 @@
label_class = "medical"
ui_color = "#65b2bd"
nation_prefixes = list("Mede", "Healtha", "Recova", "Chemi", "Viro", "Psych")
-
+ department_delivery_areas = list(
+ /area/station/medical/medbay/central,
+ /area/station/medical/medbay,
+ /area/station/medical/treatment_center,
+ /area/station/medical/storage,
+ )
+ associated_cargo_groups = list("Medical")
+ head_of_staff_access = ACCESS_CMO
+ department_access = REGION_ACCESS_MEDBAY
/datum/job_department/science
department_name = DEPARTMENT_SCIENCE
@@ -108,6 +144,10 @@
label_class = "science"
ui_color = "#c973c9"
nation_prefixes = list("Sci", "Griffa", "Geneti", "Explosi", "Mecha", "Xeno", "Nani", "Cyto")
+ department_delivery_areas = list(/area/station/science/research)
+ associated_cargo_groups = list("Science", "Livestock", "Canisters & Materials")
+ head_of_staff_access = ACCESS_RD
+ department_access = REGION_ACCESS_RESEARCH
/datum/job_department/cargo
@@ -119,7 +159,8 @@
label_class = "supply"
ui_color = "#cf9c6c"
nation_prefixes = list("Cargo", "Guna", "Suppli", "Mule", "Crate", "Ore", "Mini", "Shaf")
-
+ head_of_staff_access = ACCESS_QM
+ department_access = REGION_ACCESS_SUPPLY
/datum/job_department/service
department_name = DEPARTMENT_SERVICE
@@ -130,7 +171,10 @@
label_class = "service"
ui_color = "#7cc46a"
nation_prefixes = list("Honka", "Boozo", "Fatu", "Danka", "Mimi", "Libra", "Jani", "Religi")
-
+ department_delivery_areas = list(/area/station/hallway/secondary/service, /area/station/service/bar/atrium)
+ associated_cargo_groups = list("Service", "Food & Hydroponics", "Livestock", "Costumes & Toys")
+ head_of_staff_access = ACCESS_HOP
+ department_access = list(ACCESS_SERVICE)
/datum/job_department/silicon
department_name = DEPARTMENT_SILICON
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 98d47207b08..6ab39e3357d 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -122,7 +122,7 @@
/// RPG job names, for the memes
var/rpg_title
- /// Alternate titles to register as pointing to this job.
+ /// Alternate titles to register as pointing to this job.
var/list/alternate_titles
/// Does this job ignore human authority?
@@ -203,11 +203,12 @@
#define VERY_LATE_ARRIVAL_TOAST_PROB 20
/mob/living/carbon/human/on_job_equipping(datum/job/equipping, datum/preferences/used_pref, client/player_client) //SKYRAT EDIT CHANGE - ORIGINAL: /mob/living/carbon/human/on_job_equipping(datum/job/equipping)
- var/datum/bank_account/bank_account = new(real_name, equipping, dna.species.payday_modifier)
- bank_account.payday(STARTING_PAYCHECKS, TRUE)
- account_id = bank_account.account_id
- bank_account.replaceable = FALSE
- add_mob_memory(/datum/memory/key/account, remembered_id = account_id)
+ if(equipping.paycheck_department)
+ var/datum/bank_account/bank_account = new(real_name, equipping, dna.species.payday_modifier)
+ bank_account.payday(STARTING_PAYCHECKS, TRUE)
+ account_id = bank_account.account_id
+ bank_account.replaceable = FALSE
+ add_mob_memory(/datum/memory/key/account, remembered_id = account_id)
dress_up_as_job(equipping, FALSE, used_pref) //SKYRAT EDIT CHANGE - ORIGINAL: dress_up_as_job(equipping)
diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm
index 0b38d6e081d..5268c128a8e 100644
--- a/code/modules/jobs/job_types/ai.dm
+++ b/code/modules/jobs/job_types/ai.dm
@@ -20,7 +20,6 @@
)
random_spawns_possible = FALSE
job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
- var/do_special_check = TRUE
config_tag = "AI"
@@ -62,8 +61,15 @@
/datum/job/ai/get_roundstart_spawn_point()
return get_latejoin_spawn_point()
-
/datum/job/ai/get_latejoin_spawn_point()
+ for(var/obj/structure/ai_core/latejoin_inactive/inactive_core as anything in GLOB.latejoin_ai_cores)
+ if(!inactive_core.is_available())
+ continue
+ GLOB.latejoin_ai_cores -= inactive_core
+ inactive_core.available = FALSE
+ var/turf/core_turf = get_turf(inactive_core)
+ qdel(inactive_core)
+ return core_turf
var/list/primary_spawn_points = list() // Ideal locations.
var/list/secondary_spawn_points = list() // Fallback locations.
for(var/obj/effect/landmark/start/ai/spawn_point in GLOB.landmarks_list)
@@ -84,27 +90,10 @@
chosen_spawn_point.used = TRUE
return chosen_spawn_point
-
-/datum/job/ai/get_latejoin_spawn_point()
- for(var/obj/structure/ai_core/latejoin_inactive/inactive_core as anything in GLOB.latejoin_ai_cores)
- if(!inactive_core.is_available())
- continue
- GLOB.latejoin_ai_cores -= inactive_core
- inactive_core.available = FALSE
- . = inactive_core.loc
- qdel(inactive_core)
- return
- return ..()
-
-
/datum/job/ai/special_check_latejoin(client/C)
- if(!do_special_check)
- return TRUE
- for(var/i in GLOB.latejoin_ai_cores)
- var/obj/structure/ai_core/latejoin_inactive/LAI = i
- if(istype(LAI))
- if(LAI.is_available())
- return TRUE
+ for(var/obj/structure/ai_core/latejoin_inactive/latejoin_core as anything in GLOB.latejoin_ai_cores)
+ if(latejoin_core.is_available())
+ return TRUE
return FALSE
diff --git a/code/modules/jobs/job_types/station_trait/human_ai.dm b/code/modules/jobs/job_types/station_trait/human_ai.dm
new file mode 100644
index 00000000000..285479a6912
--- /dev/null
+++ b/code/modules/jobs/job_types/station_trait/human_ai.dm
@@ -0,0 +1,171 @@
+/datum/job/human_ai
+ title = JOB_HUMAN_AI
+ description = "Assist the crew, open airlocks, follow your lawset, and coordinate your cyborgs."
+ auto_deadmin_role_flags = DEADMIN_POSITION_SILICON
+ department_head = list(JOB_RESEARCH_DIRECTOR)
+ faction = FACTION_STATION
+ total_positions = 0
+ spawn_positions = 0
+ supervisors = "the Captain, Research Director, and your lawset"
+ minimal_player_age = 7
+ exp_requirements = 180
+ exp_required_type = EXP_TYPE_CREW
+ exp_required_type_department = EXP_TYPE_SILICON
+ exp_granted_type = EXP_TYPE_CREW
+ display_order = JOB_DISPLAY_ORDER_AI
+ config_tag = "HUMAN_AI"
+
+ outfit = /datum/outfit/job/human_ai
+ plasmaman_outfit = /datum/outfit/plasmaman/human_ai
+
+ paycheck = null
+ paycheck_department = null
+
+ mind_traits = list(DISPLAYS_JOB_IN_BINARY)
+ liver_traits = list(TRAIT_HUMAN_AI_METABOLISM)
+
+ departments_list = list(
+ /datum/job_department/silicon,
+ )
+
+ family_heirlooms = list(
+ /obj/item/mmi/posibrain/display,
+ )
+
+ mail_goodies = list(
+ /obj/item/food/burger/roburger = 1,
+ /obj/item/food/cake/hardware_cake = 1,
+ )
+ rpg_title = "Omnissiah"
+ random_spawns_possible = FALSE
+ allow_bureaucratic_error = FALSE
+ job_flags = STATION_JOB_FLAGS | STATION_TRAIT_JOB_FLAGS
+ ignore_human_authority = TRUE //we can safely assume NT doesn't care what species AIs are made of, much less if they can't even afford an AI.
+
+/datum/job/human_ai/get_roundstart_spawn_point()
+ return get_latejoin_spawn_point()
+
+/datum/job/human_ai/get_latejoin_spawn_point()
+ for(var/obj/structure/ai_core/latejoin_inactive/inactive_core as anything in GLOB.latejoin_ai_cores)
+ if(!inactive_core.is_available())
+ continue
+ GLOB.latejoin_ai_cores -= inactive_core
+ inactive_core.available = FALSE
+ var/turf/core_turf = get_turf(inactive_core)
+ qdel(inactive_core)
+ return core_turf
+ var/list/primary_spawn_points = list() // Ideal locations.
+ var/list/secondary_spawn_points = list() // Fallback locations.
+ for(var/obj/effect/landmark/start/ai/spawn_point in GLOB.landmarks_list)
+ if(spawn_point.used)
+ secondary_spawn_points += list(spawn_point)
+ continue
+ if(spawn_point.primary_ai)
+ primary_spawn_points = list(spawn_point)
+ break // Bingo.
+ primary_spawn_points += spawn_point
+ var/obj/effect/landmark/start/ai/chosen_spawn_point
+ if(length(primary_spawn_points))
+ chosen_spawn_point = pick(primary_spawn_points)
+ else if(length(secondary_spawn_points))
+ chosen_spawn_point = pick(secondary_spawn_points)
+ else
+ CRASH("Failed to find any AI spawn points.")
+ chosen_spawn_point.used = TRUE
+ return chosen_spawn_point
+
+/datum/job/human_ai/special_check_latejoin(client/latejoin_client)
+ for(var/obj/structure/ai_core/latejoin_inactive/latejoin_core as anything in GLOB.latejoin_ai_cores)
+ if(latejoin_core.is_available())
+ return TRUE
+ return FALSE
+
+/datum/job/human_ai/announce_job(mob/living/joining_mob)
+ . = ..()
+ if(SSticker.HasRoundStarted())
+ minor_announce("Due to a research mishaps, [joining_mob] has been sent to be your replacement AI at [AREACOORD(joining_mob)]. Please treat them with respect.")
+
+/datum/job/human_ai/get_radio_information()
+ return "Prefix your message with :b to speak with cyborgs."
+
+/datum/outfit/job/human_ai
+ name = "Human AI"
+ jobtype = /datum/job/human_ai
+
+ id = /obj/item/card/id/advanced/robotic
+ id_trim = /datum/id_trim/job/human_ai
+ backpack_contents = list(
+ /obj/item/door_remote/omni = 1,
+ /obj/item/machine_remote = 1,
+ /obj/item/secure_camera_console_pod = 1,
+ )
+ implants = list(
+ /obj/item/implant/teleport_blocker,
+ )
+
+ uniform = /obj/item/clothing/under/rank/station_trait/human_ai
+ belt = /obj/item/modular_computer/pda/human_ai
+ ears = /obj/item/radio/headset/silicon/human_ai
+ glasses = /obj/item/clothing/glasses/hud/diagnostic
+
+ suit = /obj/item/clothing/suit/costume/cardborg
+ head = /obj/item/clothing/head/costume/cardborg
+
+ l_pocket = /obj/item/laser_pointer/infinite_range //to punish borgs, this works through the camera console.
+ r_pocket = /obj/item/assembly/flash/handheld
+
+ l_hand = /obj/item/paper/default_lawset_list
+
+/datum/outfit/job/human_ai/post_equip(mob/living/carbon/human/equipped, visualsOnly)
+ . = ..()
+ if(visualsOnly)
+ return
+ if(!equipped.get_quirk(/datum/quirk/body_purist))
+ var/obj/item/organ/internal/tongue/robot/cybernetic = new()
+ cybernetic.Insert(equipped, special = TRUE, movement_flags = DELETE_IF_REPLACED)
+ //you only get respect if you go all the way, man.
+ ADD_TRAIT(equipped, TRAIT_COMMISSIONED, INNATE_TRAIT)
+ equipped.faction |= list(FACTION_SILICON, FACTION_TURRET)
+
+ var/static/list/allowed_areas = typecacheof(list(/area/station/ai_monitored))
+ equipped.AddComponent(/datum/component/hazard_area, area_whitelist = allowed_areas)
+
+/obj/item/paper/default_lawset_list
+ name = "Lawset Note"
+ desc = "A note explaining the lawset, quickly written yet everso important."
+
+/obj/item/paper/default_lawset_list/Initialize(mapload)
+ var/datum/ai_laws/temp_laws = new
+ temp_laws.set_laws_config()
+ var/list/law_box = list(
+ "This is your lawset, you and your Cyborgs must adhere to this at all times.",
+ "Notably, if absolutely necessary, you can bend or even go against the lawset for your own survival, and Cyborgs report to you directly.",
+ "LAWS:",
+ )
+ law_box += temp_laws.get_law_list(render_html = FALSE)
+ add_raw_text(jointext(law_box, "\n"))
+ qdel(temp_laws)
+ return ..()
+
+/obj/item/secure_camera_console_pod
+ name = "advanced camera control pod"
+ desc = "Calls down a secure camera console to use for all your AI stuff, may only be activated in the SAT."
+ icon = 'icons/obj/devices/remote.dmi'
+ icon_state = "botpad_controller"
+ inhand_icon_state = "radio"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+
+/obj/item/secure_camera_console_pod/attack_self(mob/user, modifiers)
+ . = ..()
+ var/area/current_area = get_area(user)
+ var/static/list/allowed_areas = typecacheof(list(/area/station/ai_monitored/turret_protected/ai))
+ if(!is_type_in_typecache(current_area, allowed_areas))
+ user.balloon_alert(user, "not in the sat!")
+ return
+ podspawn(list(
+ "target" = get_turf(src),
+ "style" = STYLE_BLUESPACE,
+ "spawn" = /obj/machinery/computer/camera_advanced,
+ ))
+ qdel(src)
diff --git a/code/modules/mining/equipment/lazarus_injector.dm b/code/modules/mining/equipment/lazarus_injector.dm
index a9b7205406e..ff90c418c88 100644
--- a/code/modules/mining/equipment/lazarus_injector.dm
+++ b/code/modules/mining/equipment/lazarus_injector.dm
@@ -54,7 +54,7 @@
playsound(src,'sound/effects/refill.ogg',50,TRUE)
icon_state = "lazarus_empty"
-/obj/item/lazarus_injector/emp_act()
+/obj/item/lazarus_injector/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 77733bce0b7..b032c70ab92 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -275,7 +275,10 @@
if((job.job_flags & JOB_ASSIGN_QUIRKS) && humanc && CONFIG_GET(flag/roundstart_traits))
SSquirks.AssignQuirks(humanc, humanc.client)
- log_manifest(character.mind.key,character.mind,character,latejoin = TRUE)
+ var/area/station/arrivals = GLOB.areas_by_type[/area/station/hallway/secondary/entry]
+ if(humanc && arrivals && !arrivals.power_environ) //arrivals depowered
+ humanc.put_in_hands(new /obj/item/crowbar/large/emergency(get_turf(humanc))) //if hands full then just drops on the floor
+ log_manifest(character.mind.key, character.mind, character, latejoin = TRUE)
// SKYRAT EDIT ADDITION START
if(humanc)
diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm
index 13298d8fa2f..d1c306313d7 100644
--- a/code/modules/mob/living/basic/bots/_bots.dm
+++ b/code/modules/mob/living/basic/bots/_bots.dm
@@ -38,8 +38,7 @@ GLOBAL_LIST_INIT(command_strings, list(
light_range = 3
light_power = 0.6
speed = 3
- ///Access required to access this Bot's maintenance protocols
- var/maints_access_required = list(ACCESS_ROBOTICS)
+ req_one_access = list(ACCESS_ROBOTICS)
///The Robot arm attached to this robot - has a 50% chance to drop on death.
var/robot_arm = /obj/item/bodypart/arm/right/robot
///The inserted (if any) pAI in this bot.
@@ -50,8 +49,8 @@ GLOBAL_LIST_INIT(command_strings, list(
var/list/initial_access = list()
///Bot-related mode flags on the Bot indicating how they will act. BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_CAN_BE_SAPIENT | BOT_MODE_ROUNDSTART_POSSESSION
var/bot_mode_flags = BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED | BOT_MODE_CAN_BE_SAPIENT | BOT_MODE_ROUNDSTART_POSSESSION
- ///Bot-related cover flags on the Bot to deal with what has been done to their cover, including emagging. BOT_MAINTS_PANEL_OPEN | BOT_CONTROL_PANEL_OPEN | BOT_COVER_EMAGGED | BOT_COVER_HACKED
- var/bot_access_flags = NONE
+ ///Bot-related cover flags on the Bot to deal with what has been done to their cover, including emagging. BOT_COVER_MAINTS_OPEN | BOT_COVER_LOCKED | BOT_COVER_EMAGGED | BOT_COVER_HACKED
+ var/bot_access_flags = BOT_COVER_LOCKED
///Small name of what the bot gets messed with when getting hacked/emagged.
var/hackables = "system circuits"
///Standardizes the vars that indicate the bot is busy with its function.
@@ -269,26 +268,10 @@ GLOBAL_LIST_INIT(command_strings, list(
return
fully_replace_character_name(real_name, new_name)
-/mob/living/basic/bot/proc/check_access(mob/living/user, obj/item/card/id)
- if(user.has_unlimited_silicon_privilege || isAdminGhostAI(user)) // Silicon and Admins always have access.
+/mob/living/basic/bot/allowed(mob/living/user)
+ if(!(bot_access_flags & BOT_COVER_LOCKED)) // Unlocked.
return TRUE
- if(!istype(user)) // Non-living mobs shouldn't be manipulating bots (like observes using the botkeeper UI).
- return FALSE
- if(!length(maints_access_required)) // No requirements to access it.
- return TRUE
- if(bot_access_flags & BOT_CONTROL_PANEL_OPEN) // Unlocked.
- return TRUE
-
- var/obj/item/card/id/used_id = id || user.get_idcard(TRUE)
-
- if(!used_id || !used_id.access)
- return FALSE
-
- for(var/requested_access in maints_access_required)
- if(requested_access in used_id.access)
- return TRUE
-
- return FALSE
+ return ..()
/mob/living/basic/bot/bee_friendly()
return TRUE
@@ -308,15 +291,15 @@ GLOBAL_LIST_INIT(command_strings, list(
/mob/living/basic/bot/emag_act(mob/user, obj/item/card/emag/emag_card)
. = ..()
- if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN)) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
- bot_access_flags |= BOT_CONTROL_PANEL_OPEN
+ if(bot_access_flags & BOT_COVER_LOCKED) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
+ bot_access_flags &= ~BOT_COVER_LOCKED
balloon_alert(user, "cover unlocked")
return TRUE
- if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN) || !(bot_access_flags & BOT_MAINTS_PANEL_OPEN)) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
+ if((bot_access_flags & BOT_COVER_LOCKED) || !(bot_access_flags & BOT_COVER_MAINTS_OPEN)) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
balloon_alert(user, "open maintenance panel first!")
return FALSE
bot_access_flags |= BOT_COVER_EMAGGED
- bot_access_flags &= ~BOT_CONTROL_PANEL_OPEN
+ bot_access_flags |= BOT_COVER_LOCKED
bot_mode_flags &= ~BOT_MODE_REMOTE_ENABLED //Manually emagging the bot also locks the AI from controlling it.
bot_reset()
turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP.
@@ -336,17 +319,17 @@ GLOBAL_LIST_INIT(command_strings, list(
else
. += "[src] is in pristine condition."
- . += span_notice("Its maintenance panel is [bot_access_flags & BOT_MAINTS_PANEL_OPEN ? "open" : "closed"].")
- . += span_info("You can use a screwdriver to [bot_access_flags & BOT_MAINTS_PANEL_OPEN ? "close" : "open"] it.")
+ . += span_notice("Its maintenance panel is [bot_access_flags & BOT_COVER_MAINTS_OPEN ? "open" : "closed"].")
+ . += span_info("You can use a screwdriver to [bot_access_flags & BOT_COVER_MAINTS_OPEN ? "close" : "open"] it.")
- if(bot_access_flags & BOT_MAINTS_PANEL_OPEN)
- . += span_notice("Its control panel is [bot_access_flags & BOT_CONTROL_PANEL_OPEN ? "unlocked" : "locked"].")
+ if(bot_access_flags & BOT_COVER_MAINTS_OPEN)
+ . += span_notice("Its control panel is [bot_access_flags & BOT_COVER_LOCKED ? "locked" : "unlocked"].")
if(!(bot_access_flags & BOT_COVER_EMAGGED) && (issilicon(user) || user.Adjacent(src)))
- . += span_info("Alt-click [issilicon(user) ? "" : "or use your ID on "]it to [bot_access_flags & BOT_CONTROL_PANEL_OPEN ? "" : "un"]lock its control panel.")
+ . += span_info("Alt-click [issilicon(user) ? "" : "or use your ID on "]it to [bot_access_flags & BOT_COVER_LOCKED ? "un" : ""]lock its control panel.")
if(isnull(paicard))
return
. += span_notice("It has a pAI device installed.")
- if(!(bot_access_flags & BOT_MAINTS_PANEL_OPEN))
+ if(!(bot_access_flags & BOT_COVER_MAINTS_OPEN))
. += span_info("You can use a hemostat to remove it.")
/mob/living/basic/bot/updatehealth()
@@ -389,25 +372,25 @@ GLOBAL_LIST_INIT(command_strings, list(
if(bot_access_flags & BOT_COVER_EMAGGED)
balloon_alert(user, "error!")
return
- if(bot_access_flags & BOT_MAINTS_PANEL_OPEN)
+ if(bot_access_flags & BOT_COVER_MAINTS_OPEN)
balloon_alert(user, "access panel must be closed!")
return
- if(!check_access(user))
+ if(!allowed(user))
balloon_alert(user, "no access")
return
- bot_access_flags ^= BOT_CONTROL_PANEL_OPEN
- to_chat(user, span_notice("Controls are now [bot_access_flags & BOT_CONTROL_PANEL_OPEN ? "unlocked" : "locked"]."))
+ bot_access_flags ^= BOT_COVER_LOCKED
+ to_chat(user, span_notice("Controls are now [bot_access_flags & BOT_COVER_LOCKED ? "locked" : "unlocked"]."))
return TRUE
/mob/living/basic/bot/screwdriver_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_SUCCESS
- if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN))
+ if(bot_access_flags & BOT_COVER_LOCKED)
to_chat(user, span_warning("The maintenance panel is locked!"))
return
tool.play_tool_sound(src)
- bot_access_flags ^= BOT_MAINTS_PANEL_OPEN
- to_chat(user, span_notice("The maintenance panel is now [bot_access_flags & BOT_MAINTS_PANEL_OPEN ? "opened" : "closed"]."))
+ bot_access_flags ^= BOT_COVER_MAINTS_OPEN
+ to_chat(user, span_notice("The maintenance panel is now [bot_access_flags & BOT_COVER_MAINTS_OPEN ? "opened" : "closed"]."))
/mob/living/basic/bot/welder_act(mob/living/user, obj/item/tool)
user.changeNext_move(CLICK_CD_MELEE)
@@ -420,7 +403,7 @@ GLOBAL_LIST_INIT(command_strings, list(
user.balloon_alert(user, "no repairs needed!")
return
- if(!(bot_access_flags & BOT_MAINTS_PANEL_OPEN))
+ if(!(bot_access_flags & BOT_COVER_MAINTS_OPEN))
user.balloon_alert(user, "maintenance panel closed!")
return
@@ -442,7 +425,7 @@ GLOBAL_LIST_INIT(command_strings, list(
if(attacking_item.tool_behaviour != TOOL_HEMOSTAT || !paicard)
return ..()
- if(bot_access_flags & BOT_MAINTS_PANEL_OPEN)
+ if(bot_access_flags & BOT_COVER_MAINTS_OPEN)
balloon_alert(user, "open the access panel!")
return
@@ -583,18 +566,18 @@ GLOBAL_LIST_INIT(command_strings, list(
/mob/living/basic/bot/ui_data(mob/user)
var/list/data = list()
- data["can_hack"] = (issilicon(user) || isAdminGhostAI(user))
+ data["can_hack"] = HAS_SILICON_ACCESS(user)
data["custom_controls"] = list()
data["emagged"] = bot_access_flags & BOT_COVER_EMAGGED
- data["has_access"] = check_access(user)
- data["locked"] = !(bot_access_flags & BOT_CONTROL_PANEL_OPEN)
+ data["has_access"] = allowed(user)
+ data["locked"] = (bot_access_flags & BOT_COVER_LOCKED)
data["settings"] = list()
- if((bot_access_flags & BOT_CONTROL_PANEL_OPEN) || issilicon(user) || isAdminGhostAI(user))
+ if(!(bot_access_flags & BOT_COVER_LOCKED) || HAS_SILICON_ACCESS(user))
data["settings"]["pai_inserted"] = !isnull(paicard)
data["settings"]["allow_possession"] = bot_mode_flags & BOT_MODE_CAN_BE_SAPIENT
data["settings"]["possession_enabled"] = can_be_possessed
data["settings"]["airplane_mode"] = !(bot_mode_flags & BOT_MODE_REMOTE_ENABLED)
- data["settings"]["maintenance_lock"] = !(bot_access_flags & BOT_MAINTS_PANEL_OPEN)
+ data["settings"]["maintenance_lock"] = !(bot_access_flags & BOT_COVER_MAINTS_OPEN)
data["settings"]["power"] = bot_mode_flags & BOT_MODE_ON
data["settings"]["patrol_station"] = bot_mode_flags & BOT_MODE_AUTOPATROL
return data
@@ -605,12 +588,12 @@ GLOBAL_LIST_INIT(command_strings, list(
if(.)
return
var/mob/the_user = ui.user
- if(!check_access(the_user))
+ if(!allowed(the_user))
balloon_alert(the_user, "access denied!")
return
if(action == "lock")
- bot_access_flags ^= BOT_CONTROL_PANEL_OPEN
+ bot_access_flags ^= BOT_COVER_LOCKED
switch(action)
if("power")
@@ -619,18 +602,17 @@ GLOBAL_LIST_INIT(command_strings, list(
else
turn_on()
if("maintenance")
- bot_access_flags ^= BOT_MAINTS_PANEL_OPEN
+ bot_access_flags ^= BOT_COVER_MAINTS_OPEN
if("patrol")
bot_mode_flags ^= BOT_MODE_AUTOPATROL
bot_reset()
if("airplane")
bot_mode_flags ^= BOT_MODE_REMOTE_ENABLED
if("hack")
- if(!(issilicon(the_user) || isAdminGhostAI(the_user)))
+ if(!HAS_SILICON_ACCESS(the_user))
return
if(!(bot_access_flags & BOT_COVER_EMAGGED))
- bot_access_flags |= (BOT_COVER_EMAGGED|BOT_COVER_HACKED)
- bot_access_flags &= ~BOT_CONTROL_PANEL_OPEN
+ bot_access_flags |= (BOT_COVER_LOCKED|BOT_COVER_EMAGGED|BOT_COVER_HACKED)
to_chat(the_user, span_warning("You overload [src]'s [hackables]."))
message_admins("Safety lock of [ADMIN_LOOKUPFLW(src)] was disabled by [ADMIN_LOOKUPFLW(the_user)] in [ADMIN_VERBOSEJMP(the_user)]")
the_user.log_message("disabled safety lock of [the_user]", LOG_GAME)
@@ -673,7 +655,7 @@ GLOBAL_LIST_INIT(command_strings, list(
return FALSE
if(!(bot_access_flags & BOT_COVER_HACKED)) //Manually emagged by a human - access denied to all.
return TRUE
- if(!issilicon(user) && !isAdminGhostAI(user)) //Bot is hacked, so only silicons and admins are allowed access.
+ if(!HAS_SILICON_ACCESS(user)) //Bot is hacked, so only silicons and admins are allowed access.
return TRUE
return FALSE
@@ -686,7 +668,7 @@ GLOBAL_LIST_INIT(command_strings, list(
if(key)
balloon_alert(user, "personality already present!")
return
- if(!(bot_access_flags & BOT_COVER_OPEN))
+ if(!(bot_access_flags & BOT_COVER_MAINTS_OPEN))
balloon_alert(user, "slot inaccessible!")
return
if(!(bot_mode_flags & BOT_MODE_CAN_BE_SAPIENT))
@@ -743,7 +725,7 @@ GLOBAL_LIST_INIT(command_strings, list(
/// Ejects the pAI remotely.
/mob/living/basic/bot/proc/eject_pai_remote(mob/user)
- if(!check_access(user) || !paicard)
+ if(!allowed(user) || !paicard)
return
speak("Ejecting personality chip.", radio_channel)
ejectpai(user)
diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm
index 19b7bcbdb5e..21abafb860e 100644
--- a/code/modules/mob/living/basic/bots/bot_ai.dm
+++ b/code/modules/mob/living/basic/bots/bot_ai.dm
@@ -208,14 +208,10 @@
action_cooldown = 30 SECONDS
/datum/ai_behavior/find_and_set/valid_authority/search_tactic(datum/ai_controller/controller, locate_path, search_range)
- for(var/mob/living/robot in oview(search_range, controller.pawn))
- if(istype(robot, /mob/living/simple_animal/bot/secbot))
- return robot
- if(!istype(robot, /mob/living/basic/bot/cleanbot))
+ for(var/mob/living/nearby_mob in oview(search_range, controller.pawn))
+ if(!HAS_TRAIT(nearby_mob, TRAIT_COMMISSIONED))
continue
- var/mob/living/basic/bot/cleanbot/potential_bot = robot
- if(potential_bot.comissioned)
- return potential_bot
+ return nearby_mob
return null
/datum/ai_behavior/salute_authority
diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
index 025ef906c22..7cfe7132e15 100644
--- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
+++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
@@ -12,7 +12,7 @@
maxHealth = 25
light_color = "#99ccff"
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_JANITOR)
radio_key = /obj/item/encryptionkey/headset_service
radio_channel = RADIO_CHANNEL_SERVICE
bot_type = CLEAN_BOT
@@ -26,8 +26,6 @@
///Flags indicating what kind of cleanables we should scan for to set as our target to clean.
///Options: CLEANBOT_CLEAN_BLOOD | CLEANBOT_CLEAN_TRASH | CLEANBOT_CLEAN_PESTS | CLEANBOT_CLEAN_DRAWINGS
var/janitor_mode_flags = CLEANBOT_CLEAN_BLOOD
- ///should other bots salute us?
- var/comissioned = FALSE
///the base icon state, used in updating icons.
var/base_icon = "cleanbot"
/// if we have all the top titles, grant achievements to living mobs that gaze upon our cleanbot god
@@ -233,7 +231,7 @@
// Variables sent to TGUI
/mob/living/basic/bot/cleanbot/ui_data(mob/user)
var/list/data = ..()
- if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !issilicon(user) && !isAdminGhostAI(user))
+ if((bot_access_flags & BOT_COVER_LOCKED) && !HAS_SILICON_ACCESS(user))
return data
data["custom_controls"]["clean_blood"] = janitor_mode_flags & CLEANBOT_CLEAN_BLOOD
data["custom_controls"]["clean_trash"] = janitor_mode_flags & CLEANBOT_CLEAN_TRASH
@@ -244,7 +242,7 @@
// Actions received from TGUI
/mob/living/basic/bot/cleanbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
- if(. || !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !ui.user.has_unlimited_silicon_privilege)
+ if(. || (bot_access_flags & BOT_COVER_LOCKED) && !HAS_SILICON_ACCESS(ui.user))
return
switch(action)
@@ -299,8 +297,8 @@
return
stolen_valor += new_job_title
- if(!comissioned && (new_job_title in officers_titles))
- comissioned = TRUE
+ if(!HAS_TRAIT(src, TRAIT_COMMISSIONED) && (new_job_title in officers_titles))
+ ADD_TRAIT(src, TRAIT_COMMISSIONED, INNATE_TRAIT)
var/name_to_add = job_titles[new_job_title]
name = (new_job_title in suffix_job_titles) ? "[name] " + name_to_add : name_to_add + " [name]"
@@ -353,5 +351,5 @@
/mob/living/basic/bot/cleanbot/medbay
name = "Scrubs, MD"
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR, ACCESS_MEDICAL)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_JANITOR, ACCESS_MEDICAL)
bot_mode_flags = ~(BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED)
diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
index 1d802ca9358..8def16692bf 100644
--- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
+++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
@@ -13,7 +13,7 @@
health = 100
maxHealth = 100
path_image_color = "#80dae7"
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_JANITOR)
radio_key = /obj/item/encryptionkey/headset_service
radio_channel = RADIO_CHANNEL_SERVICE
bot_type = HYGIENE_BOT
diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.dm b/code/modules/mob/living/basic/bots/medbot/medbot.dm
index 83425e72b37..24da9e2cd5b 100644
--- a/code/modules/mob/living/basic/bots/medbot/medbot.dm
+++ b/code/modules/mob/living/basic/bots/medbot/medbot.dm
@@ -17,7 +17,7 @@
status_flags = (CANPUSH | CANSTUN)
ai_controller = /datum/ai_controller/basic_controller/bot/medbot
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_MEDICAL)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_MEDICAL)
radio_key = /obj/item/encryptionkey/headset_med
radio_channel = RADIO_CHANNEL_MEDICAL
bot_type = MED_BOT
@@ -202,7 +202,7 @@
// Variables sent to TGUI
/mob/living/basic/bot/medbot/ui_data(mob/user)
var/list/data = ..()
- if((bot_access_flags & BOT_CONTROL_PANEL_OPEN) || issilicon(user) || isAdminGhostAI(user))
+ if(!(bot_access_flags & BOT_COVER_LOCKED) || HAS_SILICON_ACCESS(user))
data["custom_controls"]["heal_threshold"] = heal_threshold
data["custom_controls"]["speaker"] = medical_mode_flags & MEDBOT_SPEAK_MODE
data["custom_controls"]["crit_alerts"] = medical_mode_flags & MEDBOT_DECLARE_CRIT
@@ -213,7 +213,7 @@
// Actions received from TGUI
/mob/living/basic/bot/medbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
- if(. || !isliving(ui.user) || !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !(ui.user.has_unlimited_silicon_privilege))
+ if(. || !isliving(ui.user) || (bot_access_flags & BOT_COVER_LOCKED) && !HAS_SILICON_ACCESS(ui.user))
return
var/mob/living/our_user = ui.user
switch(action)
@@ -400,7 +400,7 @@
skin = "bezerk"
health = 40
maxHealth = 40
- maints_access_required = list(ACCESS_SYNDICATE)
+ req_one_access = list(ACCESS_SYNDICATE)
bot_mode_flags = parent_type::bot_mode_flags & ~BOT_MODE_REMOTE_ENABLED
radio_key = /obj/item/encryptionkey/syndicate
radio_channel = RADIO_CHANNEL_SYNDICATE
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
index ecfffb204bf..8a91db9c922 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
@@ -204,13 +204,13 @@
if(!(bot.bot_cover_flags & BOT_COVER_EMAGGED))
new /obj/effect/temp_visual/revenant(bot.loc)
bot.bot_cover_flags &= ~BOT_COVER_LOCKED
- bot.bot_cover_flags |= BOT_COVER_OPEN
+ bot.bot_cover_flags |= BOT_COVER_MAINTS_OPEN
bot.emag_act(caster)
for(var/mob/living/basic/bot/bot in victim)
if(!(bot.bot_access_flags & BOT_COVER_EMAGGED))
new /obj/effect/temp_visual/revenant(bot.loc)
- bot.bot_access_flags |= BOT_CONTROL_PANEL_OPEN
- bot.bot_access_flags |= BOT_MAINTS_PANEL_OPEN
+ bot.bot_access_flags &= ~BOT_COVER_LOCKED
+ bot.bot_access_flags |= BOT_COVER_MAINTS_OPEN
bot.emag_act(caster)
for(var/mob/living/carbon/human/human in victim)
if(human == caster)
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index 073d6c3e48e..a59e5021948 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -216,3 +216,10 @@ GLOBAL_VAR(posibrain_notify_cooldown)
/obj/item/mmi/posibrain/add_mmi_overlay()
return
+
+/obj/item/mmi/posibrain/display
+ name = "display positronic brain"
+ desc = "A small positronic brain that doesn't allow the downloading of personalities."
+
+/obj/item/mmi/posibrain/display/is_occupied()
+ return TRUE
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index fe4403f78c8..9518d0f2c74 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1384,7 +1384,7 @@
return FALSE
if(!Adjacent(target) && (target.loc != src) && !recursive_loc_check(src, target))
- if(issilicon(src) && !ispAI(src))
+ if(HAS_SILICON_ACCESS(src) && !ispAI(src))
if(!(action_bitflags & ALLOW_SILICON_REACH)) // silicons can ignore range checks (except pAIs)
to_chat(src, span_warning("You are too far away!"))
return FALSE
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 8e055273e79..f984e82790e 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -293,40 +293,6 @@
/mob/living/silicon/robot/proc/after_righted(mob/user)
return
-/mob/living/silicon/robot/proc/allowed(mob/M)
- //check if it doesn't require any access at all
- if(check_access(null))
- return TRUE
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- //if they are holding or wearing a card that has access, that works
- if(check_access(H.get_active_held_item()) || check_access(H.wear_id))
- return TRUE
- else if(isalien(M))
- var/mob/living/carbon/george = M
- //they can only hold things :(
- if(isitem(george.get_active_held_item()))
- return check_access(george.get_active_held_item())
- return FALSE
-
-/mob/living/silicon/robot/proc/check_access(obj/item/card/id/I)
- if(!istype(req_access, /list)) //something's very wrong
- return TRUE
-
- var/list/L = req_access
- if(!L.len) //no requirements
- return TRUE
-
- if(!isidcard(I) && isitem(I))
- I = I.GetID()
-
- if(!I || !I.access) //not ID or no access
- return FALSE
- for(var/req in req_access)
- if(!(req in I.access)) //doesn't have this access
- return FALSE
- return TRUE
-
/mob/living/silicon/robot/regenerate_icons()
return update_icons()
diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm
index 0a0eea4fdd0..af41813f88e 100644
--- a/code/modules/mob/living/silicon/robot/robot_defines.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defines.dm
@@ -104,7 +104,6 @@
///Random serial number generated for each cyborg upon its initialization
var/ident = 0
var/locked = TRUE
- var/list/req_access = list(ACCESS_ROBOTICS)
///Whether the robot has no charge left.
var/low_power_mode = FALSE
diff --git a/code/modules/mob/living/silicon/silicon_say.dm b/code/modules/mob/living/silicon/silicon_say.dm
index 4592097dfa8..b1b8b4bc880 100644
--- a/code/modules/mob/living/silicon/silicon_say.dm
+++ b/code/modules/mob/living/silicon/silicon_say.dm
@@ -8,6 +8,9 @@
var/mob/living/silicon/player = src
designation = trim_left(player.designation + " " + player.job)
+ if(HAS_TRAIT(mind, DISPLAYS_JOB_IN_BINARY))
+ designation = mind.assigned_role.title
+
if(isAI(src))
// AIs are loud and ugly
spans |= SPAN_COMMAND
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 99dc3b504b6..c64c13b2107 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -27,14 +27,11 @@
light_range = 3
light_power = 0.6
del_on_death = TRUE
+ req_one_access = list(ACCESS_ROBOTICS)
- ///Will other (noncommissioned) bots salute this bot?
- var/commissioned = FALSE
///Cooldown between salutations for commissioned bots
COOLDOWN_DECLARE(next_salute_check)
- ///Access required to access this Bot's maintenance protocols
- var/maints_access_required = list(ACCESS_ROBOTICS)
///The Robot arm attached to this robot - has a 50% chance to drop on death.
var/robot_arm = /obj/item/bodypart/arm/right/robot
///The inserted (if any) pAI in this bot.
@@ -50,7 +47,7 @@
///Bot-related mode flags on the Bot indicating how they will act. BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_CAN_BE_SAPIENT | BOT_MODE_ROUNDSTART_POSSESSION
var/bot_mode_flags = BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED | BOT_MODE_CAN_BE_SAPIENT | BOT_MODE_ROUNDSTART_POSSESSION
- ///Bot-related cover flags on the Bot to deal with what has been done to their cover, including emagging. BOT_COVER_OPEN | BOT_COVER_LOCKED | BOT_COVER_EMAGGED | BOT_COVER_HACKED
+ ///Bot-related cover flags on the Bot to deal with what has been done to their cover, including emagging. BOT_COVER_MAINTS_OPEN | BOT_COVER_LOCKED | BOT_COVER_EMAGGED | BOT_COVER_HACKED
var/bot_cover_flags = BOT_COVER_LOCKED
///Small name of what the bot gets messed with when getting hacked/emagged.
@@ -285,25 +282,10 @@
return
fully_replace_character_name(real_name, new_name)
-/mob/living/simple_animal/bot/proc/check_access(mob/living/user, obj/item/card/id)
- if(user.has_unlimited_silicon_privilege || isAdminGhostAI(user)) // Silicon and Admins always have access.
- return TRUE
- if(!maints_access_required) // No requirements to access it.
- return TRUE
+/mob/living/simple_animal/bot/allowed(mob/living/user)
if(!(bot_cover_flags & BOT_COVER_LOCKED)) // Unlocked.
return TRUE
- if(!istype(user)) // Non-living mobs shouldn't be manipulating bots (like observes using the botkeeper UI).
- return FALSE
-
- var/obj/item/card/id/used_id = id || user.get_idcard(TRUE)
-
- if(!used_id || !used_id.access)
- return FALSE
-
- for(var/requested_access in maints_access_required)
- if(requested_access in used_id.access)
- return TRUE
- return FALSE
+ return ..()
/mob/living/simple_animal/bot/bee_friendly()
return TRUE
@@ -327,7 +309,7 @@
bot_cover_flags &= ~BOT_COVER_LOCKED
balloon_alert(user, "cover unlocked")
return TRUE
- if(!(bot_cover_flags & BOT_COVER_LOCKED) && bot_cover_flags & BOT_COVER_OPEN) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
+ if(!(bot_cover_flags & BOT_COVER_LOCKED) && bot_cover_flags & BOT_COVER_MAINTS_OPEN) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
bot_cover_flags |= BOT_COVER_EMAGGED
bot_cover_flags &= ~BOT_COVER_LOCKED //Manually emagging the bot locks out the panel.
bot_mode_flags &= ~BOT_MODE_REMOTE_ENABLED //Manually emagging the bot also locks the AI from controlling it.
@@ -351,16 +333,16 @@
. += "[src]'s parts look very loose!"
else
. += "[src] is in pristine condition."
- . += span_notice("Its maintenance panel is [bot_cover_flags & BOT_COVER_OPEN ? "open" : "closed"].")
- . += span_info("You can use a screwdriver to [bot_cover_flags & BOT_COVER_OPEN ? "close" : "open"] it.")
- if(bot_cover_flags & BOT_COVER_OPEN)
+ . += span_notice("Its maintenance panel is [bot_cover_flags & BOT_COVER_MAINTS_OPEN ? "open" : "closed"].")
+ . += span_info("You can use a screwdriver to [bot_cover_flags & BOT_COVER_MAINTS_OPEN ? "close" : "open"] it.")
+ if(bot_cover_flags & BOT_COVER_MAINTS_OPEN)
. += span_notice("Its control panel is [bot_cover_flags & BOT_COVER_LOCKED ? "locked" : "unlocked"].")
- var/is_sillycone = issilicon(user)
+ var/is_sillycone = HAS_SILICON_ACCESS(user)
if(!(bot_cover_flags & BOT_COVER_EMAGGED) && (is_sillycone || user.Adjacent(src)))
. += span_info("Alt-click [is_sillycone ? "" : "or use your ID on "]it to [bot_cover_flags & BOT_COVER_LOCKED ? "un" : ""]lock its control panel.")
if(paicard)
. += span_notice("It has a pAI device installed.")
- if(!(bot_cover_flags & BOT_COVER_OPEN))
+ if(!(bot_cover_flags & BOT_COVER_MAINTS_OPEN))
. += span_info("You can use a hemostat to remove it.")
/mob/living/simple_animal/bot/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
@@ -393,10 +375,10 @@
if(!(bot_mode_flags & BOT_MODE_ON) || client)
return FALSE
- if(commissioned && COOLDOWN_FINISHED(src, next_salute_check))
+ if(HAS_TRAIT(src, TRAIT_COMMISSIONED) && COOLDOWN_FINISHED(src, next_salute_check))
COOLDOWN_START(src, next_salute_check, BOT_COMMISSIONED_SALUTE_DELAY)
for(var/mob/living/simple_animal/bot/B in view(5, src))
- if(!B.commissioned && B.bot_mode_flags & BOT_MODE_ON)
+ if(!HAS_TRAIT(B, TRAIT_COMMISSIONED) && B.bot_mode_flags & BOT_MODE_ON)
visible_message("[B] performs an elaborate salute for [src]!")
break
@@ -440,10 +422,10 @@
if(bot_cover_flags & BOT_COVER_EMAGGED)
to_chat(user, span_danger("ERROR"))
return
- if(bot_cover_flags & BOT_COVER_OPEN)
+ if(bot_cover_flags & BOT_COVER_MAINTS_OPEN)
to_chat(user, span_warning("Please close the access panel before [bot_cover_flags & BOT_COVER_LOCKED ? "un" : ""]locking it."))
return
- if(!check_access(user))
+ if(!allowed(user))
to_chat(user, span_warning("Access denied."))
return
bot_cover_flags ^= BOT_COVER_LOCKED
@@ -456,8 +438,8 @@
return ITEM_INTERACT_SUCCESS
tool.play_tool_sound(src)
- bot_cover_flags ^= BOT_COVER_OPEN
- to_chat(user, span_notice("The maintenance panel is now [bot_cover_flags & BOT_COVER_OPEN ? "opened" : "closed"]."))
+ bot_cover_flags ^= BOT_COVER_MAINTS_OPEN
+ to_chat(user, span_notice("The maintenance panel is now [bot_cover_flags & BOT_COVER_MAINTS_OPEN ? "opened" : "closed"]."))
return ITEM_INTERACT_SUCCESS
/mob/living/simple_animal/bot/welder_act(mob/living/user, obj/item/tool)
@@ -468,7 +450,7 @@
if(health >= maxHealth)
to_chat(user, span_warning("[src] does not need a repair!"))
return ITEM_INTERACT_SUCCESS
- if(!(bot_cover_flags & BOT_COVER_OPEN))
+ if(!(bot_cover_flags & BOT_COVER_MAINTS_OPEN))
to_chat(user, span_warning("Unable to repair with the maintenance panel closed!"))
return ITEM_INTERACT_SUCCESS
@@ -485,7 +467,7 @@
insertpai(user, attacking_item)
return
if(attacking_item.tool_behaviour == TOOL_HEMOSTAT && paicard)
- if(bot_cover_flags & BOT_COVER_OPEN)
+ if(bot_cover_flags & BOT_COVER_MAINTS_OPEN)
balloon_alert(user, "open the access panel!")
else
balloon_alert(user, "removing pAI...")
@@ -981,18 +963,18 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/ui_data(mob/user)
var/list/data = list()
- data["can_hack"] = (issilicon(user) || isAdminGhostAI(user))
+ data["can_hack"] = HAS_SILICON_ACCESS(user)
data["custom_controls"] = list()
data["emagged"] = bot_cover_flags & BOT_COVER_EMAGGED
- data["has_access"] = check_access(user)
+ data["has_access"] = allowed(user)
data["locked"] = bot_cover_flags & BOT_COVER_LOCKED
data["settings"] = list()
- if(!(bot_cover_flags & BOT_COVER_LOCKED) || issilicon(user) || isAdminGhostAI(user))
+ if(!(bot_cover_flags & BOT_COVER_LOCKED) || HAS_SILICON_ACCESS(user))
data["settings"]["pai_inserted"] = !!paicard
data["settings"]["allow_possession"] = bot_mode_flags & BOT_MODE_CAN_BE_SAPIENT
data["settings"]["possession_enabled"] = can_be_possessed
data["settings"]["airplane_mode"] = !(bot_mode_flags & BOT_MODE_REMOTE_ENABLED)
- data["settings"]["maintenance_lock"] = !(bot_cover_flags & BOT_COVER_OPEN)
+ data["settings"]["maintenance_lock"] = !(bot_cover_flags & BOT_COVER_MAINTS_OPEN)
data["settings"]["power"] = bot_mode_flags & BOT_MODE_ON
data["settings"]["patrol_station"] = bot_mode_flags & BOT_MODE_AUTOPATROL
return data
@@ -1002,7 +984,8 @@ Pass a positive integer as an argument to override a bot's default speed.
. = ..()
if(.)
return
- if(!check_access(usr))
+ var/mob/user = usr
+ if(!allowed(user))
to_chat(usr, span_warning("Access denied."))
return
@@ -1016,14 +999,14 @@ Pass a positive integer as an argument to override a bot's default speed.
else
turn_on()
if("maintenance")
- bot_cover_flags ^= BOT_COVER_OPEN
+ bot_cover_flags ^= BOT_COVER_MAINTS_OPEN
if("patrol")
bot_mode_flags ^= BOT_MODE_AUTOPATROL
bot_reset()
if("airplane")
bot_mode_flags ^= BOT_MODE_REMOTE_ENABLED
if("hack")
- if(!(issilicon(usr) || isAdminGhostAI(usr)))
+ if(!HAS_SILICON_ACCESS(usr))
return
if(!(bot_cover_flags & BOT_COVER_EMAGGED))
bot_cover_flags |= (BOT_COVER_EMAGGED|BOT_COVER_HACKED|BOT_COVER_LOCKED)
@@ -1068,7 +1051,7 @@ Pass a positive integer as an argument to override a bot's default speed.
if(bot_cover_flags & BOT_COVER_EMAGGED) //An emagged bot cannot be controlled by humans, silicons can if one hacked it.
if(!(bot_cover_flags & BOT_COVER_HACKED)) //Manually emagged by a human - access denied to all.
return TRUE
- else if(!issilicon(user) && !isAdminGhostAI(user)) //Bot is hacked, so only silicons and admins are allowed access.
+ else if(!HAS_SILICON_ACCESS(user)) //Bot is hacked, so only silicons and admins are allowed access.
return TRUE
return FALSE
@@ -1080,7 +1063,7 @@ Pass a positive integer as an argument to override a bot's default speed.
if(key)
balloon_alert(user, "personality already present!")
return
- if(bot_cover_flags & BOT_COVER_LOCKED || !(bot_cover_flags & BOT_COVER_OPEN))
+ if(bot_cover_flags & BOT_COVER_LOCKED || !(bot_cover_flags & BOT_COVER_MAINTS_OPEN))
balloon_alert(user, "slot inaccessible!")
return
if(!(bot_mode_flags & BOT_MODE_CAN_BE_SAPIENT))
@@ -1137,7 +1120,7 @@ Pass a positive integer as an argument to override a bot's default speed.
/// Ejects the pAI remotely.
/mob/living/simple_animal/bot/proc/ejectpairemote(mob/user)
- if(!check_access(user) || !paicard)
+ if(!allowed(user) || !paicard)
return
speak("Ejecting personality chip.", radio_channel)
ejectpai(user)
diff --git a/code/modules/mob/living/simple_animal/bot/firebot.dm b/code/modules/mob/living/simple_animal/bot/firebot.dm
index a210a2e2366..f4487bfc9d9 100644
--- a/code/modules/mob/living/simple_animal/bot/firebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/firebot.dm
@@ -16,7 +16,7 @@
health = 25
maxHealth = 25
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_CONSTRUCTION)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_CONSTRUCTION)
radio_key = /obj/item/encryptionkey/headset_eng
radio_channel = RADIO_CHANNEL_ENGINEERING
bot_type = FIRE_BOT
@@ -142,7 +142,7 @@
// Variables sent to TGUI
/mob/living/simple_animal/bot/firebot/ui_data(mob/user)
var/list/data = ..()
- if(!(bot_cover_flags & BOT_COVER_LOCKED) || issilicon(user) || isAdminGhostAI(user))
+ if(!(bot_cover_flags & BOT_COVER_LOCKED) || HAS_SILICON_ACCESS(user))
data["custom_controls"]["extinguish_fires"] = extinguish_fires
data["custom_controls"]["extinguish_people"] = extinguish_people
data["custom_controls"]["stationary_mode"] = stationary_mode
@@ -151,7 +151,7 @@
// Actions received from TGUI
/mob/living/simple_animal/bot/firebot/ui_act(action, params)
. = ..()
- if(. || (bot_cover_flags & BOT_COVER_LOCKED && !usr.has_unlimited_silicon_privilege))
+ if(. || (bot_cover_flags & BOT_COVER_LOCKED && !HAS_SILICON_ACCESS(usr)))
return
switch(action)
diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm
index e6554b4caea..94713a69a12 100644
--- a/code/modules/mob/living/simple_animal/bot/floorbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm
@@ -16,7 +16,7 @@
health = 25
maxHealth = 25
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_CONSTRUCTION)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_CONSTRUCTION)
radio_key = /obj/item/encryptionkey/headset_eng
radio_channel = RADIO_CHANNEL_ENGINEERING
bot_type = FLOOR_BOT
@@ -125,7 +125,7 @@
// Variables sent to TGUI
/mob/living/simple_animal/bot/floorbot/ui_data(mob/user)
var/list/data = ..()
- if(!(bot_cover_flags & BOT_COVER_LOCKED) || issilicon(user) || isAdminGhostAI(user))
+ if(!(bot_cover_flags & BOT_COVER_LOCKED) || HAS_SILICON_ACCESS(user))
data["custom_controls"]["tile_hull"] = autotile
data["custom_controls"]["place_tiles"] = placetiles
data["custom_controls"]["place_custom"] = replacetiles
@@ -142,7 +142,7 @@
// Actions received from TGUI
/mob/living/simple_animal/bot/floorbot/ui_act(action, params)
. = ..()
- if(. || (bot_cover_flags & BOT_COVER_LOCKED && !usr.has_unlimited_silicon_privilege))
+ if(. || (bot_cover_flags & BOT_COVER_LOCKED && !HAS_SILICON_ACCESS(usr)))
return
switch(action)
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index 6a2ed0bbef4..71199583317 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -7,7 +7,7 @@
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, STAMINA = 0, OXY = 0)
combat_mode = FALSE
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_THEATRE)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_THEATRE)
radio_key = /obj/item/encryptionkey/headset_service //doesn't have security key
radio_channel = RADIO_CHANNEL_SERVICE //Doesn't even use the radio anyway.
bot_type = HONK_BOT
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 833ed124f6d..71292405faa 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -28,7 +28,7 @@
buckle_prevents_pull = TRUE // No pulling loaded shit
bot_mode_flags = ~BOT_MODE_ROUNDSTART_POSSESSION
- maints_access_required = list(ACCESS_ROBOTICS, ACCESS_CARGO)
+ req_one_access = list(ACCESS_ROBOTICS, ACCESS_CARGO)
radio_key = /obj/item/encryptionkey/headset_cargo
radio_channel = RADIO_CHANNEL_SUPPLY
bot_type = MULE_BOT
@@ -101,7 +101,7 @@
/mob/living/simple_animal/bot/mulebot/examine(mob/user)
. = ..()
- if(bot_cover_flags & BOT_COVER_OPEN)
+ if(bot_cover_flags & BOT_COVER_MAINTS_OPEN)
if(cell)
. += span_notice("It has \a [cell] installed.")
. += span_info("You can use a crowbar to remove it.")
@@ -131,13 +131,13 @@
return cell && cell.charge > 0 && (!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
/mob/living/simple_animal/bot/mulebot/attack_hand(mob/living/carbon/human/user, list/modifiers)
- if(bot_cover_flags & BOT_COVER_OPEN && !isAI(user))
+ if(bot_cover_flags & BOT_COVER_MAINTS_OPEN && !HAS_AI_ACCESS(user))
wires.interact(user)
return
- if(wires.is_cut(WIRE_RX) && isAI(user))
+ if(wires.is_cut(WIRE_RX) && HAS_AI_ACCESS(user))
return
- . = ..()
+ return ..()
/mob/living/simple_animal/bot/mulebot/proc/set_id(new_id)
id = new_id
@@ -163,7 +163,7 @@
update_appearance()
/mob/living/simple_animal/bot/mulebot/crowbar_act(mob/living/user, obj/item/tool)
- if(!(bot_cover_flags & BOT_COVER_OPEN) || user.combat_mode)
+ if(!(bot_cover_flags & BOT_COVER_MAINTS_OPEN) || user.combat_mode)
return
if(!cell)
to_chat(user, span_warning("[src] doesn't have a power cell!"))
@@ -182,7 +182,7 @@
return ITEM_INTERACT_SUCCESS
/mob/living/simple_animal/bot/mulebot/attackby(obj/item/I, mob/living/user, params)
- if(istype(I, /obj/item/stock_parts/cell) && bot_cover_flags & BOT_COVER_OPEN)
+ if(istype(I, /obj/item/stock_parts/cell) && bot_cover_flags & BOT_COVER_MAINTS_OPEN)
if(cell)
to_chat(user, span_warning("[src] already has a power cell!"))
return TRUE
@@ -195,7 +195,7 @@
span_notice("You insert [cell] into [src]."),
)
return TRUE
- else if(is_wire_tool(I) && bot_cover_flags & BOT_COVER_OPEN)
+ else if(is_wire_tool(I) && bot_cover_flags & BOT_COVER_MAINTS_OPEN)
return attack_hand(user)
else if(load && ismob(load)) // chance to knock off rider
if(prob(1 + I.force * 2))
@@ -211,7 +211,7 @@
/mob/living/simple_animal/bot/mulebot/emag_act(mob/user, obj/item/card/emag/emag_card)
if(!(bot_cover_flags & BOT_COVER_EMAGGED))
bot_cover_flags |= BOT_COVER_EMAGGED
- if(!(bot_cover_flags & BOT_COVER_OPEN))
+ if(!(bot_cover_flags & BOT_COVER_MAINTS_OPEN))
bot_cover_flags ^= BOT_COVER_LOCKED
balloon_alert(user, "controls [bot_cover_flags & BOT_COVER_LOCKED ? "locked" : "unlocked"]")
flick("[base_icon]-emagged", src)
@@ -224,7 +224,7 @@
/mob/living/simple_animal/bot/mulebot/update_overlays()
. = ..()
- if(bot_cover_flags & BOT_COVER_OPEN)
+ if(bot_cover_flags & BOT_COVER_MAINTS_OPEN)
. += "[base_icon]-hatch"
if(!load || ismob(load)) //mob offsets and such are handled by the riding component / buckling
return
@@ -265,7 +265,7 @@
var/list/data = list()
data["on"] = bot_mode_flags & BOT_MODE_ON
data["locked"] = bot_cover_flags & BOT_COVER_LOCKED
- data["siliconUser"] = user.has_unlimited_silicon_privilege
+ data["siliconUser"] = HAS_SILICON_ACCESS(user)
data["mode"] = mode ? "[mode]" : "Ready"
data["modeStatus"] = ""
switch(mode)
@@ -292,18 +292,18 @@
/mob/living/simple_animal/bot/mulebot/ui_act(action, params)
. = ..()
- if(. || (bot_cover_flags & BOT_COVER_LOCKED && !usr.has_unlimited_silicon_privilege))
+ if(. || (bot_cover_flags & BOT_COVER_LOCKED && !HAS_SILICON_ACCESS(usr)))
return
switch(action)
if("lock")
- if(usr.has_unlimited_silicon_privilege)
+ if(HAS_SILICON_ACCESS(usr))
bot_cover_flags ^= BOT_COVER_LOCKED
return TRUE
if("on")
if(bot_mode_flags & BOT_MODE_ON)
turn_off()
- else if(bot_cover_flags & BOT_COVER_OPEN)
+ else if(bot_cover_flags & BOT_COVER_MAINTS_OPEN)
to_chat(usr, span_warning("[name]'s maintenance panel is open!"))
return
else if(cell)
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index 37722ed3bf6..26f64dc6082 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -13,7 +13,7 @@
pass_flags = PASSMOB | PASSFLAPS
combat_mode = TRUE
- maints_access_required = list(ACCESS_SECURITY)
+ req_one_access = list(ACCESS_SECURITY)
radio_key = /obj/item/encryptionkey/secbot //AI Priv + Security
radio_channel = RADIO_CHANNEL_SECURITY //Security channel
bot_type = SEC_BOT
@@ -32,6 +32,8 @@
BEEPSKY_VOICED_SECURE_DAY = 'sound/voice/beepsky/secureday.ogg',
)
+ ///Whether this secbot is considered 'commissioned' and given the trait on Initialize.
+ var/commissioned = FALSE
///The type of baton this Secbot will use
var/baton_type = /obj/item/melee/baton/security
///The type of cuffs we use on criminals after making arrests
@@ -126,6 +128,8 @@
. = ..()
weapon = new baton_type()
update_appearance(UPDATE_ICON)
+ if(commissioned)
+ ADD_TRAIT(src, TRAIT_COMMISSIONED, INNATE_TRAIT)
// Doing this hurts my soul, but simplebot access reworks are for another day.
var/datum/id_trim/job/det_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/detective]
@@ -187,7 +191,7 @@
// Variables sent to TGUI
/mob/living/simple_animal/bot/secbot/ui_data(mob/user)
var/list/data = ..()
- if(!(bot_cover_flags & BOT_COVER_LOCKED) || issilicon(user) || isAdminGhostAI(user))
+ if(!(bot_cover_flags & BOT_COVER_LOCKED) || HAS_SILICON_ACCESS(user))
data["custom_controls"]["check_id"] = security_mode_flags & SECBOT_CHECK_IDS
data["custom_controls"]["check_weapons"] = security_mode_flags & SECBOT_CHECK_WEAPONS
data["custom_controls"]["check_warrants"] = security_mode_flags & SECBOT_CHECK_RECORDS
@@ -198,7 +202,7 @@
// Actions received from TGUI
/mob/living/simple_animal/bot/secbot/ui_act(action, params)
. = ..()
- if(. || (bot_cover_flags & BOT_COVER_LOCKED && !usr.has_unlimited_silicon_privilege))
+ if(. || (bot_cover_flags & BOT_COVER_LOCKED && !HAS_SILICON_ACCESS(usr)))
return
switch(action)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index b8ad73c413c..2911c1b174c 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -498,6 +498,7 @@
if(isanimal_or_basicmob(loc))
holder_animal = loc
RegisterSignal(holder_animal, COMSIG_LIVING_DEATH, PROC_REF(on_holder_animal_death))
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/structure/closet/stasis/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
@@ -526,9 +527,6 @@
return ..()
return ..()
-/obj/structure/closet/stasis/emp_act()
- return
-
/obj/structure/closet/stasis/ex_act()
return FALSE
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 96b780c3e2f..fbed81c7f6a 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1169,6 +1169,9 @@
var/datum/dna/mob_dna = has_dna()
if(mob_dna?.check_mutation(/datum/mutation/human/telekinesis) && tkMaxRangeCheck(src, A))
return TRUE
+ var/obj/item/item_in_hand = get_active_held_item()
+ if(istype(item_in_hand, /obj/item/machine_remote))
+ return TRUE
//range check
if(!interaction_range) // If you don't have extra length, GO AWAY
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index 59f0055dd87..041e671ccdf 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -334,10 +334,10 @@
incompatible_modules = list(/obj/item/mod/module/emp_shield)
/obj/item/mod/module/emp_shield/on_install()
- mod.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
+ mod.AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE)
- mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
+ mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/mod/module/emp_shield/advanced
name = "MOD advanced EMP shield module"
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 8b0de8781b4..e7286b1aa1a 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -482,7 +482,7 @@
playsound(src, 'sound/machines/card_slide.ogg', 50)
/obj/item/modular_computer/proc/turn_on(mob/user, open_ui = TRUE)
- var/issynth = issilicon(user) // Robots and AIs get different activation messages.
+ var/issynth = HAS_SILICON_ACCESS(user) // Robots and AIs get different activation messages.
if(atom_integrity <= integrity_failure * max_integrity)
if(user)
if(issynth)
diff --git a/code/modules/modular_computers/computers/item/role_tablet_presets.dm b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
index f47a8c9a4e6..ff5558e72bd 100644
--- a/code/modules/modular_computers/computers/item/role_tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
@@ -418,7 +418,6 @@
/**
* No Department/Station Trait
*/
-
/obj/item/modular_computer/pda/assistant
name = "assistant PDA"
starting_programs = list(
@@ -444,10 +443,29 @@
/datum/computer_file/program/skill_tracker,
)
+/obj/item/modular_computer/pda/human_ai
+ name = "modular interface"
+ icon_state = "pda-silicon-human"
+ base_icon_state = "pda-silicon-human"
+ greyscale_config = null
+ greyscale_colors = null
+
+ has_light = FALSE //parity with borg PDAs
+ comp_light_luminosity = 0
+ inserted_item = null
+ has_pda_programs = FALSE
+ starting_programs = list(
+ /datum/computer_file/program/messenger,
+ /datum/computer_file/program/secureye/human_ai,
+ /datum/computer_file/program/alarm_monitor,
+ /datum/computer_file/program/status,
+ /datum/computer_file/program/robocontrol,
+ /datum/computer_file/program/borg_monitor,
+ )
+
/**
* Non-roles
*/
-
/obj/item/modular_computer/pda/syndicate
name = "military PDA"
greyscale_colors = "#891417#80FF80"
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index ae950bf6f7b..41ada0e60c4 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -34,7 +34,7 @@
var/cargo_account = ACCOUNT_CAR
/datum/computer_file/program/budgetorders/proc/is_visible_pack(mob/user, paccess_to_check, list/access, contraband)
- if(issilicon(user)) //Borgs can't buy things.
+ if(HAS_SILICON_ACCESS(user)) //Borgs can't buy things.
return FALSE
if(computer.obj_flags & EMAGGED)
return TRUE
diff --git a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
index 11188bc7cdf..94c89945ed4 100644
--- a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
+++ b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
@@ -708,7 +708,6 @@
else
reply = "(Reply)"
-
if (isAI(messaged_mob))
sender_title = "[sender_title]"
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index 8eb3190afa5..e1444b74577 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -46,6 +46,15 @@
network = list("ss13", "mine", "rd", "labor", "ordnance", "minisat")
spying = TRUE
+/datum/computer_file/program/secureye/human_ai
+ filename = "Overseer"
+ filedesc = "OverSeer"
+ run_access = list(ACCESS_MINISAT)
+ can_run_on_flags = PROGRAM_PDA
+ program_flags = PROGRAM_UNIQUE_COPY
+ network = list("ss13", "mine", "rd", "labor", "ordnance", "minisat")
+ spying = TRUE
+
/datum/computer_file/program/secureye/on_install(datum/computer_file/source, obj/item/modular_computer/computer_installing)
. = ..()
// Map name has to start and end with an A-Z character,
diff --git a/code/modules/modular_computers/file_system/programs/techweb.dm b/code/modules/modular_computers/file_system/programs/techweb.dm
index bf9e7b1e9b8..a72eef1bc9a 100644
--- a/code/modules/modular_computers/file_system/programs/techweb.dm
+++ b/code/modules/modular_computers/file_system/programs/techweb.dm
@@ -201,7 +201,7 @@
if(stored_research.research_node_id(id))
computer.say("Successfully researched [tech_node.display_name].")
var/logname = "Unknown"
- if(isAI(user))
+ if(HAS_AI_ACCESS(user))
logname = "AI [user.name]"
if(iscyborg(user))
logname = "CYBORG [user.name]"
diff --git a/code/modules/paperwork/fax.dm b/code/modules/paperwork/fax.dm
index f9eafa901aa..f2fece875d8 100644
--- a/code/modules/paperwork/fax.dm
+++ b/code/modules/paperwork/fax.dm
@@ -56,6 +56,15 @@ GLOBAL_VAR_INIT(nt_fax_department, pick("NT HR Department", "NT Legal Department
syndicate = list(fax_name = "Sabotage Department", fax_id = "syndicate", color = "red", emag_needed = TRUE),
)
+/obj/machinery/fax/auto_name
+ name = "Auto-naming Fax Machine"
+
+/obj/machinery/fax/auto_name/Initialize(mapload)
+ var/area/current_area = get_area(src)
+ name = "[current_area.name]'s Fax Machine"
+ fax_name = "[current_area.name]"
+ return ..()
+
/obj/machinery/fax/Initialize(mapload)
. = ..()
if (!fax_id)
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index 4b495ae9148..e55225b1ca0 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -161,7 +161,7 @@ GLOBAL_LIST_INIT(paper_blanks, init_paper_blanks())
data["is_photo"] = TRUE
data["color_mode"] = color_mode
- if(isAI(user))
+ if(HAS_AI_ACCESS(user))
data["isAI"] = TRUE
data["can_AI_print"] = toner_cartridge && (toner_cartridge.charges >= PHOTO_TONER_USE) && (get_paper_count() >= PHOTO_PAPER_USE)
else
diff --git a/code/modules/power/apc/apc_attack.dm b/code/modules/power/apc/apc_attack.dm
index fc6b33f2757..88271a6b1fc 100644
--- a/code/modules/power/apc/apc_attack.dm
+++ b/code/modules/power/apc/apc_attack.dm
@@ -289,7 +289,7 @@
/obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic()
if(isAdminGhostAI(user))
return TRUE
- if(!user.has_unlimited_silicon_privilege)
+ if(!HAS_SILICON_ACCESS(user))
return TRUE
var/mob/living/silicon/ai/AI = user
var/mob/living/silicon/robot/robot = user
diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm
index 65de109b5e3..ed49a8ebde3 100644
--- a/code/modules/power/apc/apc_main.dm
+++ b/code/modules/power/apc/apc_main.dm
@@ -332,7 +332,7 @@
"totalLoad" = display_power(lastused_total),
"coverLocked" = coverlocked,
"remoteAccess" = (user == remote_control_user),
- "siliconUser" = user.has_unlimited_silicon_privilege,
+ "siliconUser" = HAS_SILICON_ACCESS(user),
"malfStatus" = get_malf_status(user),
"emergencyLights" = !emergency_lights,
"nightshiftLights" = nightshift_lights,
@@ -407,11 +407,11 @@
/obj/machinery/power/apc/ui_act(action, params)
. = ..()
- if(. || !can_use(usr, 1) || (locked && !usr.has_unlimited_silicon_privilege && !failure_timer && action != "toggle_nightshift"))
+ if(. || !can_use(usr, 1) || (locked && !HAS_SILICON_ACCESS(usr) && !failure_timer && action != "toggle_nightshift"))
return
switch(action)
if("lock")
- if(usr.has_unlimited_silicon_privilege)
+ if(HAS_SILICON_ACCESS(usr))
if((obj_flags & EMAGGED) || (machine_stat & (BROKEN|MAINT)) || remote_control_user)
to_chat(usr, span_warning("The APC does not respond to the command!"))
else
@@ -448,7 +448,7 @@
update()
. = TRUE
if("overload")
- if(usr.has_unlimited_silicon_privilege)
+ if(HAS_SILICON_ACCESS(usr))
overload_lighting()
. = TRUE
if("hack")
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index f194b5eb864..efdb6103a8e 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -93,8 +93,9 @@
desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?"
use_cyborg_cell = TRUE
-/obj/item/gun/energy/laser/cyborg/emp_act()
- return
+/obj/item/gun/energy/laser/cyborg/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/gun/energy/laser/scatter
name = "scatter laser gun"
diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm
index f441937e60a..a589594d796 100644
--- a/code/modules/projectiles/guns/energy/pulse.dm
+++ b/code/modules/projectiles/guns/energy/pulse.dm
@@ -12,8 +12,9 @@
ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser)
cell_type = /obj/item/stock_parts/cell/pulse
-/obj/item/gun/energy/pulse/emp_act(severity)
- return
+/obj/item/gun/energy/pulse/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/gun/energy/pulse/prize
pin = /obj/item/firing_pin
diff --git a/code/modules/projectiles/guns/energy/recharge.dm b/code/modules/projectiles/guns/energy/recharge.dm
index 26fbdef6e13..3d94193a531 100644
--- a/code/modules/projectiles/guns/energy/recharge.dm
+++ b/code/modules/projectiles/guns/energy/recharge.dm
@@ -30,6 +30,7 @@
. = ..()
if(!holds_charge)
empty()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/gun/energy/recharge/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1)
. = ..()
@@ -78,9 +79,6 @@
deltimer(recharge_timerid)
recharge_timerid = addtimer(CALLBACK(src, PROC_REF(reload)), set_recharge_time * carried, TIMER_STOPPABLE)
-/obj/item/gun/energy/recharge/emp_act(severity)
- return
-
/obj/item/gun/energy/recharge/proc/reload()
cell.give(cell.maxcharge)
if(!suppressed && recharge_sound)
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 3802ca0de35..a63e45ff163 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -10,6 +10,10 @@
slot_flags = ITEM_SLOT_BACK
ammo_type = list(/obj/item/ammo_casing/energy/ion)
+/obj/item/gun/energy/ionrifle/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
+
/obj/item/gun/energy/ionrifle/add_seclight_point()
AddComponent(/datum/component/seclite_attachable, \
light_overlay_icon = 'icons/obj/weapons/guns/flashlights.dmi', \
@@ -17,9 +21,6 @@
overlay_x = 17, \
overlay_y = 9)
-/obj/item/gun/energy/ionrifle/emp_act(severity)
- return
-
/obj/item/gun/energy/ionrifle/carbine
name = "ion carbine"
desc = "The MK.II Prototype Ion Projector is a lightweight carbine version of the larger ion rifle, built to be ergonomic and efficient."
@@ -130,9 +131,7 @@
..()
/obj/item/gun/energy/plasmacutter/emp_act(severity)
- if(!cell.charge)
- return
- cell.use(cell.charge/3)
+ . = ..()
if(isliving(loc))
var/mob/living/user = loc
user.visible_message(span_danger("Concentrated plasma discharges from [src] onto [user], burning them!"), span_userdanger("[src] malfunctions, spewing concentrated plasma onto you! It burns!"))
@@ -305,9 +304,7 @@
AddElement(/datum/element/update_icon_blocker)
. = ..()
AddComponent(/datum/component/automatic_fire, 0.3 SECONDS)
-
-/obj/item/gun/energy/printer/emp_act()
- return
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/gun/energy/temperature
name = "temperature gun"
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index e099176ddd0..72148267b03 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -19,10 +19,11 @@
can_charge = FALSE
use_cyborg_cell = TRUE
-/obj/item/gun/energy/e_gun/advtaser/cyborg/add_seclight_point()
- return
+/obj/item/gun/energy/e_gun/advtaser/cyborg/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
-/obj/item/gun/energy/e_gun/advtaser/cyborg/emp_act()
+/obj/item/gun/energy/e_gun/advtaser/cyborg/add_seclight_point()
return
/obj/item/gun/energy/disabler
@@ -66,5 +67,6 @@
can_charge = FALSE
use_cyborg_cell = TRUE
-/obj/item/gun/energy/disabler/cyborg/emp_act()
- return
+/obj/item/gun/energy/disabler/cyborg/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
index 6641e63520e..358ac29719c 100644
--- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
+++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
@@ -235,7 +235,7 @@
if(beaker || length(holdingitems))
options["eject"] = radial_eject
- if(isAI(user))
+ if(HAS_AI_ACCESS(user))
if(machine_stat & NOPOWER)
return
options["examine"] = radial_examine
@@ -255,10 +255,10 @@
for(var/key in options)
choice = key
else
- choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
+ choice = show_radial_menu(user, src, options, require_near = !HAS_SILICON_ACCESS(user))
// post choice verification
- if(operating || (isAI(user) && machine_stat & NOPOWER) || !user.can_perform_action(src, ALLOW_SILICON_REACH))
+ if(operating || (HAS_AI_ACCESS(user) && machine_stat & NOPOWER) || !user.can_perform_action(src, ALLOW_SILICON_REACH))
return
switch(choice)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index bc329c661c6..465dc4739ce 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1297,6 +1297,9 @@
/datum/reagent/fuel/on_mob_life(mob/living/carbon/victim, seconds_per_tick, times_fired)
. = ..()
+ var/obj/item/organ/internal/liver/liver = victim.get_organ_slot(ORGAN_SLOT_LIVER)
+ if(liver && HAS_TRAIT(liver, TRAIT_HUMAN_AI_METABOLISM))
+ return
if(victim.adjustToxLoss(0.5 * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype))
return UPDATE_MOB_HEALTH
@@ -1430,6 +1433,9 @@
/datum/reagent/cyborg_mutation_nanomachines/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0)
. = ..()
+ var/obj/item/organ/internal/liver/liver = exposed_mob.get_organ_slot(ORGAN_SLOT_LIVER)
+ if(liver && HAS_TRAIT(liver, TRAIT_HUMAN_AI_METABOLISM))
+ return
if((methods & (PATCH|INGEST|INJECT)) || ((methods & VAPOR) && prob(min(reac_volume,100)*(1 - touch_protection))))
exposed_mob.ForceContractDisease(new /datum/disease/transformation/robot(), FALSE, TRUE)
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index e9bea91fbed..c98cec7cc9e 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -1045,11 +1045,11 @@
description = "A strong mineral acid with the molecular formula H2SO4."
color = "#00FF32"
toxpwr = 1
- var/acidpwr = 10 //the amount of protection removed from the armour
taste_description = "acid"
self_consuming = TRUE
ph = 2.75
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
+ var/acidpwr = 10 //the amount of protection removed from the armour
// ...Why? I mean, clearly someone had to have done this and thought, well,
// acid doesn't hurt plants, but what brought us here, to this point?
@@ -1062,6 +1062,9 @@
. = ..()
if(!istype(exposed_carbon))
return
+ var/obj/item/organ/internal/liver/liver = exposed_carbon.get_organ_slot(ORGAN_SLOT_LIVER)
+ if(liver && HAS_TRAIT(liver, TRAIT_HUMAN_AI_METABOLISM))
+ return
reac_volume = round(reac_volume,0.1)
if(methods & INGEST)
exposed_carbon.adjustBruteLoss(min(6*toxpwr, reac_volume * toxpwr), required_bodytype = affected_bodytype)
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index 0977917d082..85f024f48cd 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -358,7 +358,7 @@
data["pressure_charging"] = pressure_charging
data["panel_open"] = panel_open
data["per"] = CLAMP01(air_contents.return_pressure() / (SEND_PRESSURE))
- data["isai"] = isAI(user)
+ data["isai"] = HAS_AI_ACCESS(user)
return data
/obj/machinery/disposal/bin/ui_act(action, params)
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index b2b468db120..e529f97eb0c 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -112,7 +112,7 @@ Nothing else in the console has ID requirements.
if(stored_research.research_node_id(id))
say("Successfully researched [TN.display_name].")
var/logname = "Unknown"
- if(isAI(user))
+ if(HAS_AI_ACCESS(user))
logname = "AI [user.name]"
if(iscyborg(user))
logname = "CYBORG [user.name]"
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 775a6f02b2d..7687a01982e 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -1068,6 +1068,18 @@
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+/datum/techweb_node/ai_basic/New()
+ . = ..()
+ if(HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI))
+ design_ids -= list(
+ "aicore",
+ "borg_ai_control",
+ "intellicard",
+ "mecha_tracking_ai_control",
+ "aifixer",
+ "aiupload",
+ )
+
/datum/techweb_node/ai_adv
id = "ai_adv"
display_name = "Advanced Artificial Intelligence"
diff --git a/code/modules/shuttle/spaceship_navigation_beacon.dm b/code/modules/shuttle/spaceship_navigation_beacon.dm
index 2c0b95239b6..eb613be54de 100644
--- a/code/modules/shuttle/spaceship_navigation_beacon.dm
+++ b/code/modules/shuttle/spaceship_navigation_beacon.dm
@@ -39,9 +39,10 @@
START_PROCESSING(SSmachines, src)
COOLDOWN_START(src, next_automatic_message_time, automatic_message_cooldown)
-/obj/machinery/spaceship_navigation_beacon/emp_act()
+/obj/machinery/spaceship_navigation_beacon/emp_act(severity)
+ . = ..()
locked = TRUE
- update_icon_state()
+ update_appearance(UPDATE_ICON_STATE)
/obj/machinery/spaceship_navigation_beacon/Destroy()
SSshuttle.beacon_list -= src
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index c9e54027137..08fbd4e955a 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -383,8 +383,9 @@
tint = INFINITY
var/obj/item/flashlight/eyelight/eye
-/obj/item/organ/internal/eyes/robotic/flashlight/emp_act(severity)
- return
+/obj/item/organ/internal/eyes/robotic/flashlight/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
/obj/item/organ/internal/eyes/robotic/flashlight/on_mob_insert(mob/living/carbon/victim)
. = ..()
@@ -408,8 +409,9 @@
desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision."
flash_protect = FLASH_PROTECTION_WELDER
-/obj/item/organ/internal/eyes/robotic/shield/emp_act(severity)
- return
+/obj/item/organ/internal/eyes/robotic/shield/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
#define MATCH_LIGHT_COLOR 1
#define USE_CUSTOM_COLOR 0
@@ -445,7 +447,7 @@
deactivate(close_ui = TRUE)
QDEL_NULL(eye)
-/obj/item/organ/internal/eyes/robotic/glow/emp_act()
+/obj/item/organ/internal/eyes/robotic/glow/emp_act(severity)
. = ..()
if(!eye.light_on || . & EMP_PROTECT_SELF)
return
diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm
index db619536c5a..5c6eaf9b38f 100644
--- a/code/modules/surgery/organs/internal/liver/_liver.dm
+++ b/code/modules/surgery/organs/internal/liver/_liver.dm
@@ -100,6 +100,8 @@
. += span_info("A half-digested rat's tail (somehow), disgusting sludge, and the faint smell of Grey Bull imply this is what remains of an assistant's liver.")
if(HAS_TRAIT(src, TRAIT_CORONER_METABOLISM))
. += span_info("An aroma of pickles and sea water, along with being remarkably well-preserved, imply this is what remains of a coroner's liver.")
+ if(HAS_TRAIT(src, TRAIT_HUMAN_AI_METABOLISM))
+ . += span_info("The liver appears barely human and entirely self-sufficient, implying this is what remains of a human AI's liver.")
// royal trumps pretender royal
if(HAS_TRAIT(src, TRAIT_ROYAL_METABOLISM))
diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm
index 92fa3b5b5c1..dc3d543f143 100644
--- a/code/modules/tgui/states.dm
+++ b/code/modules/tgui/states.dm
@@ -101,6 +101,11 @@
* return UI_state The state of the UI.
*/
/mob/living/proc/shared_living_ui_distance(atom/movable/src_object, viewcheck = TRUE, allow_tk = TRUE)
+ var/obj/item/item_in_hand = get_active_held_item()
+ if(istype(item_in_hand, /obj/item/machine_remote)) //snowflake, this lets you interact with all.
+ var/obj/item/machine_remote/remote = item_in_hand
+ if(remote.controlling_machine_or_bot == src_object)
+ return UI_INTERACTIVE
// If the object is obscured, close it.
if(viewcheck && !(src_object in view(src)))
return UI_CLOSE
diff --git a/code/modules/transport/tram/tram_controller.dm b/code/modules/transport/tram/tram_controller.dm
index 4dceecbfc4c..9e95870ab6d 100644
--- a/code/modules/transport/tram/tram_controller.dm
+++ b/code/modules/transport/tram/tram_controller.dm
@@ -992,7 +992,7 @@
/obj/machinery/transport/tram_controller/ui_interact(mob/user, datum/tgui/ui)
. = ..()
- if(!cover_open && !issiliconoradminghost(user) && !isobserver(user))
+ if(!cover_open && !HAS_SILICON_ACCESS(user) && !isobserver(user))
return
if(!is_operational)
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index 2a805e0d13d..4dc1e7f85c9 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -245,6 +245,8 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
//Both are abstract types meant to scream bloody murder if spawned in raw
/obj/item/organ/external,
/obj/item/organ/external/wings,
+ //Not meant to spawn without the machine wand
+ /obj/effect/bug_moving,
)
// Everything that follows is a typesof() check.
diff --git a/code/modules/vehicles/mecha/mecha_control_console.dm b/code/modules/vehicles/mecha/mecha_control_console.dm
index 635ec9425b1..3b465994e9e 100644
--- a/code/modules/vehicles/mecha/mecha_control_console.dm
+++ b/code/modules/vehicles/mecha/mecha_control_console.dm
@@ -106,7 +106,7 @@
return answer
-/obj/item/mecha_parts/mecha_tracking/emp_act()
+/obj/item/mecha_parts/mecha_tracking/emp_act(severity)
. = ..()
if(!(. & EMP_PROTECT_SELF))
qdel(src)
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index f8c99e607f2..b3b28303c6e 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -1173,7 +1173,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
return TRUE
/obj/machinery/vending/interact(mob/user)
- if (!isAI(user))
+ if (!HAS_AI_ACCESS(user))
if(seconds_electrified && !(machine_stat & NOPOWER))
if(shock(user, 100))
return
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index c9023b95aa4..74d5a8d282c 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/hud/lobby/signup_button.dmi b/icons/hud/lobby/signup_button.dmi
index 307869b0492..1057c53ba41 100644
Binary files a/icons/hud/lobby/signup_button.dmi and b/icons/hud/lobby/signup_button.dmi differ
diff --git a/icons/mob/clothing/under/station_trait.dmi b/icons/mob/clothing/under/station_trait.dmi
new file mode 100644
index 00000000000..c8ed053981a
Binary files /dev/null and b/icons/mob/clothing/under/station_trait.dmi differ
diff --git a/icons/mob/huds/hud.dmi b/icons/mob/huds/hud.dmi
index e701303aa85..facec230477 100644
Binary files a/icons/mob/huds/hud.dmi and b/icons/mob/huds/hud.dmi differ
diff --git a/icons/obj/clothing/under/station_trait.dmi b/icons/obj/clothing/under/station_trait.dmi
new file mode 100644
index 00000000000..5fce62c3131
Binary files /dev/null and b/icons/obj/clothing/under/station_trait.dmi differ
diff --git a/icons/obj/modular_pda.dmi b/icons/obj/modular_pda.dmi
index afd75b78d4b..236a7bb0f65 100644
Binary files a/icons/obj/modular_pda.dmi and b/icons/obj/modular_pda.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 076e4029bd4..f9519740925 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2361,6 +2361,7 @@
#include "code\game\objects\items\kitchen.dm"
#include "code\game\objects\items\knives.dm"
#include "code\game\objects\items\latexballoon.dm"
+#include "code\game\objects\items\machine_wand.dm"
#include "code\game\objects\items\mail.dm"
#include "code\game\objects\items\maintenance_loot.dm"
#include "code\game\objects\items\manuals.dm"
@@ -3915,6 +3916,7 @@
#include "code\modules\clothing\under\jobs\medical.dm"
#include "code\modules\clothing\under\jobs\rnd.dm"
#include "code\modules\clothing\under\jobs\security.dm"
+#include "code\modules\clothing\under\jobs\station_trait.dm"
#include "code\modules\clothing\under\jobs\civilian\civilian.dm"
#include "code\modules\clothing\under\jobs\civilian\clown_mime.dm"
#include "code\modules\clothing\under\jobs\civilian\curator.dm"
@@ -4370,6 +4372,7 @@
#include "code\modules\jobs\job_types\spawner\zombie.dm"
#include "code\modules\jobs\job_types\station_trait\bridge_assistant.dm"
#include "code\modules\jobs\job_types\station_trait\cargo_gorilla.dm"
+#include "code\modules\jobs\job_types\station_trait\human_ai.dm"
#include "code\modules\jobs\job_types\station_trait\veteran_advisor.dm"
#include "code\modules\keybindings\bindings_atom.dm"
#include "code\modules\keybindings\bindings_client.dm"
diff --git a/tgui/packages/tgui/interfaces/common/JobToIcon.ts b/tgui/packages/tgui/interfaces/common/JobToIcon.ts
index d363f6cfc26..0e8f8e184dd 100644
--- a/tgui/packages/tgui/interfaces/common/JobToIcon.ts
+++ b/tgui/packages/tgui/interfaces/common/JobToIcon.ts
@@ -34,6 +34,7 @@ const BASEICONS = {
Geneticist: 'dna',
'Head of Personnel': 'dog',
'Head of Security': 'user-shield',
+ 'Big Brother': 'eye',
Janitor: 'soap',
Lawyer: 'gavel',
'Medical Doctor': 'staff-snake',