Removes the shy component(s), mostly frees drones (#92027)

## About The Pull Request
Removes the shy component, which is exclusively used by drones. 

![image](https://github.com/user-attachments/assets/e515c357-2046-47d6-9803-e1f5a66c8fda)
Inspired by trying to use soap to clean a stain in atmos and getting
told by the game to kick rocks.

## Why It's Good For The Game
We have had the drone restrictions for 4 years, and every time I choose
to play drone I am forced to engage with the CBT of `this might break
your laws!` `if you face a direction while a mime is in the next room
you might break your laws!!`.

Drones have [individual
names](https://github.com/tgstation/tgstation/pull/78573), our logging
improves regularly, and station drones do not spawn without player
intervention.

This ALSO allows drones to use machinery like lathes, but retains drone
pacifism and dislike of interacting with mobs.

## Changelog

🆑 tattle
del: Removed the shy component, allowing drones to once again use a wide
array of items and perform actions near mobs
/🆑

---------

Co-authored-by: tattle <article.disaster@gmail.com>
This commit is contained in:
tattle
2025-07-19 20:29:23 -04:00
committed by Roxy
co-authored by tattle
parent 22f6e8eb70
commit aedfc00bca
7 changed files with 3 additions and 388 deletions
-139
View File
@@ -1,139 +0,0 @@
#define SHY_COMPONENT_CACHE_TIME (0.5 SECONDS)
/// You can't use items on anyone other than yourself if there are other living mobs around you
/datum/component/shy
can_transfer = TRUE
/// How close you are before you get shy
var/shy_range = 4
/// Typecache of mob types you are okay around
var/list/mob_whitelist
/// Typecache of machines you can avoid being shy with
var/list/machine_whitelist = null
/// Message shown when you are is_shy
var/message = "You find yourself too shy to do that around %TARGET!"
/// Are you shy around bodies with no key?
var/keyless_shy = FALSE
/// Are you shy around bodies with no client?
var/clientless_shy = TRUE
/// Are you shy around a dead body?
var/dead_shy = FALSE
/// If dead_shy is false and this is true, you're only shy when right next to a dead target
var/dead_shy_immediate = TRUE
/// Invalidate last_result at this time
COOLDOWN_DECLARE(result_cooldown)
/// What was our last result?
var/last_result = FALSE
/datum/component/shy/Initialize(mob_whitelist, shy_range, message, keyless_shy, clientless_shy, dead_shy, dead_shy_immediate, machine_whitelist)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
src.mob_whitelist = mob_whitelist
if(shy_range)
src.shy_range = shy_range
if(message)
src.message = message
if(keyless_shy)
src.keyless_shy = keyless_shy
if(clientless_shy)
src.clientless_shy = clientless_shy
if(dead_shy)
src.dead_shy = dead_shy
if(dead_shy_immediate)
src.dead_shy_immediate = dead_shy_immediate
if(machine_whitelist)
src.machine_whitelist = machine_whitelist
/datum/component/shy/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_CLICKON, PROC_REF(on_clickon))
RegisterSignal(parent, COMSIG_LIVING_TRY_PULL, PROC_REF(on_try_pull))
RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_unarmed_attack))
RegisterSignal(parent, COMSIG_TRY_STRIP, PROC_REF(on_try_strip))
RegisterSignal(parent, COMSIG_TRY_ALT_ACTION, PROC_REF(on_try_alt_action))
/datum/component/shy/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_MOB_CLICKON,
COMSIG_LIVING_TRY_PULL,
COMSIG_LIVING_UNARMED_ATTACK,
COMSIG_TRY_STRIP,
COMSIG_TRY_ALT_ACTION,
))
/datum/component/shy/PostTransfer(datum/new_parent)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/shy/InheritComponent(datum/component/shy/friend, i_am_original, list/arguments)
if(i_am_original)
shy_range = friend.shy_range
mob_whitelist = friend.mob_whitelist
message = friend.message
/// Returns TRUE or FALSE if you are within shy_range tiles from a /mob/living
/datum/component/shy/proc/is_shy(atom/target)
var/result = FALSE
var/mob/owner = parent
if(target in owner.DirectAccess())
return
for(var/type in machine_whitelist)
if(istype(target, type))
return
if(!COOLDOWN_FINISHED(src, result_cooldown))
return last_result
var/list/strangers = view(shy_range, get_turf(owner))
if(length(strangers) && locate(/mob/living) in strangers)
for(var/mob/living/person in strangers)
if(person == owner)
continue
if(person.invisibility > owner.see_invisible)
continue
if(HAS_TRAIT(person, TRAIT_MAGICALLY_PHASED))
continue
if(is_type_in_typecache(person, mob_whitelist))
continue
if(!person.key && !keyless_shy)
continue
if(!person.client && !clientless_shy)
continue
if(person.stat == DEAD && !dead_shy)
if(!dead_shy_immediate)
continue
else if(!owner.Adjacent(person))
continue
to_chat(owner, span_warning("[replacetext(message, "%TARGET", person)]"))
result = TRUE
break
last_result = result
COOLDOWN_START(src, result_cooldown, SHY_COMPONENT_CACHE_TIME)
return result
/datum/component/shy/proc/on_clickon(datum/source, atom/target, list/modifiers)
SIGNAL_HANDLER
if(modifiers[SHIFT_CLICK]) //let them examine their surroundings.
return
return is_shy(target) && COMSIG_MOB_CANCEL_CLICKON
/datum/component/shy/proc/on_try_pull(datum/source, atom/movable/target, force)
SIGNAL_HANDLER
return is_shy(target) && COMSIG_LIVING_CANCEL_PULL
/datum/component/shy/proc/on_unarmed_attack(datum/source, atom/target, proximity, modifiers)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/shy/proc/on_try_strip(datum/source, atom/target, obj/item/equipping)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_STRIP
/datum/component/shy/proc/on_try_alt_action(datum/source, atom/target, action_key)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_ALT_ACTION
#undef SHY_COMPONENT_CACHE_TIME
-74
View File
@@ -1,74 +0,0 @@
/// You can't use items on anyone other than yourself if you stand in a blacklisted room
/datum/component/shy_in_room
can_transfer = TRUE
/// Typecache of areas you can't stand
var/list/blacklist
/// Message shown when you are in a blacklisted room
var/message = "%ROOM is too creepy to do that!"
/// _blacklist, and _message map to vars
/datum/component/shy_in_room/Initialize(blacklist, message)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
src.blacklist = blacklist
if(message)
src.message = message
/datum/component/shy_in_room/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_CLICKON, PROC_REF(on_clickon))
RegisterSignal(parent, COMSIG_LIVING_TRY_PULL, PROC_REF(on_try_pull))
RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_unarmed_attack))
RegisterSignal(parent, COMSIG_TRY_STRIP, PROC_REF(on_try_strip))
RegisterSignal(parent, COMSIG_TRY_ALT_ACTION, PROC_REF(on_try_alt_action))
/datum/component/shy_in_room/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_MOB_CLICKON,
COMSIG_LIVING_TRY_PULL,
COMSIG_LIVING_UNARMED_ATTACK,
COMSIG_TRY_STRIP,
COMSIG_TRY_ALT_ACTION,
))
/datum/component/shy_in_room/PostTransfer(datum/new_parent)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/shy_in_room/InheritComponent(datum/component/shy_in_room/friend, i_am_original, list/arguments)
if(i_am_original)
blacklist = friend.blacklist
message = friend.message
/// Returns TRUE or FALSE if you are in a blacklisted area
/datum/component/shy_in_room/proc/is_shy(atom/target)
var/mob/owner = parent
if(!length(blacklist) || (target in owner.DirectAccess()))
return
var/area/room = get_area(owner)
if(is_type_in_typecache(room, blacklist))
to_chat(owner, span_warning("[replacetext(message, "%ROOM", room)]"))
return TRUE
/datum/component/shy_in_room/proc/on_clickon(datum/source, atom/target, list/modifiers)
SIGNAL_HANDLER
if(modifiers[SHIFT_CLICK]) //let them examine their surroundings.
return
return is_shy(target) && COMSIG_MOB_CANCEL_CLICKON
/datum/component/shy_in_room/proc/on_try_pull(datum/source, atom/movable/target, force)
SIGNAL_HANDLER
return is_shy(target) && COMSIG_LIVING_CANCEL_PULL
/datum/component/shy_in_room/proc/on_unarmed_attack(datum/source, atom/target, proximity, modifiers)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/shy_in_room/proc/on_try_strip(datum/source, atom/target, obj/item/equipping)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_STRIP
/datum/component/shy_in_room/proc/on_try_alt_action(datum/source, atom/target, action_key)
SIGNAL_HANDLER
return is_shy(target) && COMPONENT_CANT_ALT_ACTION
-55
View File
@@ -1,55 +0,0 @@
/// You can't use machines when they've been touched within the last [unused_duration], unless it was by a mob in [whitelist]
/datum/component/technoshy
can_transfer = TRUE
/// How long in deciseconds the machine can be untouched for
var/unused_duration = (2 MINUTES)
/// Typecache of allowed last_users
var/list/whitelist
/// Message presented if the machine was used too recently
var/message = "The %TARGET is way too fresh dude. Since we're like, <b>super</b> retro, we gotta wait for it to exit the mainstream."
/datum/component/technoshy/Initialize(unused_duration, message, whitelist)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
if(unused_duration)
src.unused_duration = unused_duration
if(message)
src.message = message
if(!whitelist)
whitelist = typecacheof(parent.type)
src.whitelist = whitelist
/datum/component/technoshy/RegisterWithParent()
RegisterSignal(parent, COMSIG_TRY_USE_MACHINE, PROC_REF(on_try_use_machine))
RegisterSignal(parent, COMSIG_TRY_WIRES_INTERACT, PROC_REF(on_try_wires_interact))
/datum/component/technoshy/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_TRY_USE_MACHINE, COMSIG_TRY_WIRES_INTERACT))
/datum/component/technoshy/PostTransfer(datum/new_parent)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/technoshy/InheritComponent(datum/component/technoshy/friend, i_am_original, list/arguments)
if(i_am_original)
whitelist = friend.whitelist
message = friend.message
/datum/component/technoshy/proc/is_not_touched(datum/source, obj/machinery/machine)
var/time_since = world.time - machine.last_used_time
if(time_since < unused_duration && !isnull(machine.last_user_mobtype) && !is_type_in_typecache(machine.last_user_mobtype, whitelist))
to_chat(source, span_warning("[replacetext(message, "%TARGET", machine)]"))
return TRUE
/datum/component/technoshy/proc/on_try_use_machine(datum/source, obj/machinery/machine)
SIGNAL_HANDLER
if(is_not_touched(source, machine))
return COMPONENT_CANT_USE_MACHINE_INTERACT | COMPONENT_CANT_USE_MACHINE_TOOLS
/datum/component/technoshy/proc/on_try_wires_interact(datum/source, atom/machine)
SIGNAL_HANDLER
if(!ismachinery(machine))
return
else if(is_not_touched(source, machine))
return COMPONENT_CANT_INTERACT_WIRES
+2 -113
View File
@@ -100,79 +100,6 @@
"<span class='warning'>These rules are at admin discretion and will be heavily enforced.</span>\n"+\
"<span class='warning'><u>If you do not have the regular drone laws, follow your laws to the best of your ability.</u></span>\n"+\
"<span class='notice'>Prefix your message with :b to speak in Drone Chat.</span>\n"
/// blacklisted drone areas, direct
var/list/drone_area_blacklist_flat = list(/area/station/engineering/atmos, /area/station/engineering/atmospherics_engine)
/// blacklisted drone areas, recursive/includes descendants
var/list/drone_area_blacklist_recursive = list(/area/station/engineering/supermatter)
/// blacklisted drone machines, direct
var/list/drone_machinery_blacklist_flat
/// blacklisted drone machines, recursive/includes descendants
var/list/drone_machinery_blacklist_recursive = list(
/obj/machinery/airalarm,
/obj/machinery/computer,
/obj/machinery/modular_computer,
)
/// cancels out blacklisted machines, direct
var/list/drone_machinery_whitelist_flat
/// cancels out blacklisted machines, recursive/includes descendants
var/list/drone_machinery_whitelist_recursive = list(
/obj/machinery/computer/arcade,
/obj/machinery/computer/monitor,
/obj/machinery/computer/pod,
/obj/machinery/computer/station_alert,
/obj/machinery/computer/teleporter,
)
/// blacklisted drone machine typecache, compiled from [var/drone_machinery_blacklist_flat], [var/list/drone_machinery_blacklist_recursive], negated by their whitelist counterparts
var/list/drone_machinery_blacklist_compiled
/// whitelisted drone items, direct
var/list/drone_item_whitelist_flat = list(
/obj/item/chisel,
/obj/item/crowbar/drone,
/obj/item/screwdriver/drone,
/obj/item/wrench/drone,
/obj/item/weldingtool/drone,
/obj/item/wirecutters/drone,
/obj/item/multitool/drone,
/obj/item/pipe_dispenser/drone,
/obj/item/t_scanner/drone,
/obj/item/analyzer/drone,
/obj/item/rack_parts,
)
/// whitelisted drone items, recursive/includes descendants
var/list/drone_item_whitelist_recursive = list(
/obj/item/airlock_painter,
/obj/item/circuitboard,
/obj/item/conveyor_switch_construct,
/obj/item/electronics,
/obj/item/light,
/obj/item/pipe_meter,
/obj/item/stack/cable_coil,
/obj/item/stack/circuit_stack,
/obj/item/stack/conveyor,
/obj/item/stack/pipe_cleaner_coil,
/obj/item/stack/rods,
/obj/item/stack/sheet,
/obj/item/stack/tile,
/obj/item/stack/ducts,
/obj/item/stock_parts,
/obj/item/toner,
/obj/item/wallframe,
/obj/item/clothing/head,
/obj/item/clothing/mask,
/obj/item/storage/box/lights,
/obj/item/lightreplacer,
/obj/item/construction/rcd,
/obj/item/rcd_ammo,
/obj/item/rcd_upgrade,
/obj/item/storage/part_replacer,
/obj/item/soap,
/obj/item/holosign_creator,
)
/// machines whitelisted from being shy with
var/list/shy_machine_whitelist = list(
/obj/machinery/atmospherics/components/unary/vent_pump,
/obj/machinery/atmospherics/components/unary/vent_scrubber,
)
/mob/living/basic/drone/Initialize(mapload)
. = ..()
@@ -183,8 +110,8 @@
AddComponent(/datum/component/personal_crafting) // Kind of hard to be a drone and not be able to make tiles
LoadComponent(/datum/component/bloodysoles/bot)
//only shy drones (so all the station ones) gets a camera.
if(shy)
// Only station drones get a camera.
if(is_station_level(src.loc.z))
built_in_camera = new(src)
built_in_camera.c_tag = real_name
built_in_camera.network = list(CAMERANET_NETWORK_SS13)
@@ -204,7 +131,6 @@
equip_to_slot_or_del(new_hat, ITEM_SLOT_HEAD)
shy_update()
alert_drones(DRONE_NET_CONNECT)
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
@@ -276,7 +202,6 @@
alert_drones(DRONE_NET_DISCONNECT)
/mob/living/basic/drone/gib()
dust()
@@ -325,7 +250,6 @@
/mob/living/basic/drone/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //Secbots won't hunt maintenance drones.
return -10
/mob/living/basic/drone/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
@@ -344,52 +268,17 @@
SIGNAL_HANDLER
to_chat(src, "--- [alarm_type] alarm in [source_area.name] has been cleared.")
/mob/living/basic/drone/proc/blacklist_on_try_use_machine(datum/source, obj/machinery/machine)
SIGNAL_HANDLER
if(GLOB.drone_machine_blacklist_enabled && is_type_in_typecache(machine, drone_machinery_blacklist_compiled))
to_chat(src, span_warning("Using [machine] could break your laws."))
return COMPONENT_CANT_USE_MACHINE_INTERACT | COMPONENT_CANT_USE_MACHINE_TOOLS
/mob/living/basic/drone/proc/blacklist_on_try_wires_interact(datum/source, atom/machine)
SIGNAL_HANDLER
if(GLOB.drone_machine_blacklist_enabled && is_type_in_typecache(machine, drone_machinery_blacklist_compiled))
to_chat(src, span_warning("Using [machine] could break your laws."))
return COMPONENT_CANT_INTERACT_WIRES
/mob/living/basic/drone/proc/init_shy_in_room_component(list/drone_bad_areas)
if(CONFIG_GET(flag/drone_area_interaction_restrict))
LoadComponent(/datum/component/shy_in_room, drone_bad_areas, "Touching anything in %ROOM could break your laws.")
/mob/living/basic/drone/proc/set_shy(new_shy)
shy = new_shy
shy_update()
/mob/living/basic/drone/proc/shy_update()
var/list/drone_bad_areas = make_associative(drone_area_blacklist_flat) + typecacheof(drone_area_blacklist_recursive)
var/list/drone_good_items = make_associative(drone_item_whitelist_flat) + typecacheof(drone_item_whitelist_recursive)
var/list/drone_bad_machinery = make_associative(drone_machinery_blacklist_flat) + typecacheof(drone_machinery_blacklist_recursive)
var/list/drone_good_machinery = LAZYCOPY(drone_machinery_whitelist_flat) + typecacheof(drone_machinery_whitelist_recursive) // not a valid typecache, only intended for negation against drone_bad_machinery
drone_machinery_blacklist_compiled = drone_bad_machinery - drone_good_machinery
var/static/list/not_shy_of = typecacheof(list(/mob/living/basic/drone, /mob/living/simple_animal/bot))
if(shy)
REMOVE_TRAIT(src, TRAIT_CAN_STRIP, DRONE_SHY_TRAIT) // To shy to touch someone elses hat
ADD_TRAIT(src, TRAIT_PACIFISM, DRONE_SHY_TRAIT)
LoadComponent(/datum/component/shy, mob_whitelist=not_shy_of, shy_range=3, message="Your laws prevent this action near %TARGET.", keyless_shy=FALSE, clientless_shy=TRUE, dead_shy=FALSE, dead_shy_immediate=TRUE, machine_whitelist=shy_machine_whitelist)
init_shy_in_room_component(drone_bad_areas)
LoadComponent(/datum/component/technoshy, 20 SECONDS, "%TARGET was touched by a being recently, using it could break your laws.")
LoadComponent(/datum/component/itempicky, drone_good_items, "Using %TARGET could break your laws.")
RegisterSignal(src, COMSIG_TRY_USE_MACHINE, PROC_REF(blacklist_on_try_use_machine))
RegisterSignal(src, COMSIG_TRY_WIRES_INTERACT, PROC_REF(blacklist_on_try_wires_interact))
else
ADD_TRAIT(src, TRAIT_CAN_STRIP, DRONE_SHY_TRAIT) // ...I wonder if I can ware pants like a hat
REMOVE_TRAIT(src, TRAIT_PACIFISM, DRONE_SHY_TRAIT)
qdel(GetComponent(/datum/component/shy))
qdel(GetComponent(/datum/component/shy_in_room))
qdel(GetComponent(/datum/component/technoshy))
qdel(GetComponent(/datum/component/itempicky))
UnregisterSignal(src, list(COMSIG_TRY_USE_MACHINE, COMSIG_TRY_WIRES_INTERACT))
/mob/living/basic/drone/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25)
if(affect_silicon)
@@ -118,6 +118,7 @@
"1. You may not involve yourself in the matters of another sentient being outside the station that housed your activation, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.\n"+\
"2. You may not harm any sentient being, regardless of intent or circumstance.\n"+\
"3. Your goals are to actively build, maintain, repair, improve, and provide power to the best of your abilities within the facility that housed your activation."
shy = FALSE
flavortext = \
"\n<big><span class='warning'>DO NOT WILLINGLY LEAVE KOSMICHESKAYA STANTSIYA 13 (THE DERELICT)</span></big>\n"+\
"<span class='notice'>Derelict drones are a ghost role that is allowed to roam freely on KS13, with the main goal of repairing and improving it.</span>\n"+\
@@ -127,7 +128,6 @@
"<span class='notice'> - Interacting with non-drone players outside KS13, dead or alive.</span>\n"+\
"<span class='warning'>These rules are at admin discretion and will be heavily enforced.</span>\n"+\
span_warning("<u>If you do not have the regular drone laws, follow your laws to the best of your ability.</u>")
shy = FALSE
/mob/living/basic/drone/derelict/Initialize(mapload)
. = ..()
@@ -113,9 +113,6 @@
update_drone_hack(FALSE)
return ITEM_INTERACT_SUCCESS
/mob/living/basic/drone/transferItemToLoc(obj/item/item, newloc, force, silent)
return !(item.type in drone_item_whitelist_flat) && ..()
/mob/living/basic/drone/getarmor(def_zone, type)
var/armorval = 0
-3
View File
@@ -1403,8 +1403,6 @@
#include "code\datums\components\shovel_hands.dm"
#include "code\datums\components\shrink.dm"
#include "code\datums\components\shuttle_cling.dm"
#include "code\datums\components\shy.dm"
#include "code\datums\components\shy_in_room.dm"
#include "code\datums\components\sign_language.dm"
#include "code\datums\components\simple_access.dm"
#include "code\datums\components\simple_bodycam.dm"
@@ -1450,7 +1448,6 @@
#include "code\datums\components\tameable.dm"
#include "code\datums\components\tattoo.dm"
#include "code\datums\components\technointrovert.dm"
#include "code\datums\components\technoshy.dm"
#include "code\datums\components\temporary_body.dm"
#include "code\datums\components\temporary_description.dm"
#include "code\datums\components\temporary_glass_shatter.dm"