diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 0d2e88042d..0efd5a1f79 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -106,6 +106,9 @@
#define COMSIG_CLICK "atom_click" //from base of atom/Click(): (location, control, params, mob/user)
#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob)
+ #define COMPONENT_ALLOW_EXAMINATE 1
+ #define COMPONENT_DENY_EXAMINATE 2 //Higher priority compared to the above one
+
#define COMSIG_CLICK_CTRL "ctrl_click" //from base of atom/CtrlClickOn(): (/mob)
#define COMSIG_CLICK_ALT "alt_click" //from base of atom/AltClick(): (/mob)
#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click" //from base of atom/CtrlShiftClick(/mob)
@@ -159,7 +162,6 @@
// /mob signals
#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A)
- #define COMPONENT_ALLOW_EXAMINE 1
#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed)
#define COMPONENT_BLOCK_DEATH_BROADCAST 1 //stops the death from being broadcasted in deadchat.
#define COMSIG_MOB_GHOSTIZE "mob_ghostize" //from base of mob/Ghostize(): (can_reenter_corpse, special, penalize)
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 9bf59d5e76..e8a57bb257 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -319,9 +319,11 @@
/mob/proc/ShiftClickOn(atom/A)
A.ShiftClick(src)
return
+
/atom/proc/ShiftClick(mob/user)
- SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user)
- user.examinate(src)
+ var/flags = SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user)
+ if(!(flags & COMPONENT_DENY_EXAMINATE) && user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE))
+ user.examinate(src)
return
/*
diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm
new file mode 100644
index 0000000000..5b471a0dc7
--- /dev/null
+++ b/code/datums/elements/mob_holder.dm
@@ -0,0 +1,184 @@
+/datum/element/mob_holder
+ element_flags = ELEMENT_BESPOKE
+ id_arg_index = 2
+ var/worn_state
+ var/alt_worn
+ var/right_hand
+ var/left_hand
+ var/inv_slots
+ var/proctype //if present, will be invoked on headwear generation.
+
+/datum/element/mob_holder/Attach(datum/target, _worn_state, _alt_worn, _right_hand, _left_hand, _inv_slots = NONE, _proctype)
+ . = ..()
+
+ if(!isliving(target))
+ return ELEMENT_INCOMPATIBLE
+
+ worn_state = _worn_state
+ alt_worn = _alt_worn
+ right_hand = _right_hand
+ left_hand = _left_hand
+ inv_slots = _inv_slots
+ proctype = _proctype
+
+ RegisterSignal(target, COMSIG_CLICK_ALT, .proc/mob_try_pickup)
+ RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+
+/datum/element/mob_holder/Detach(datum/source, force)
+ . = ..()
+ UnregisterSignal(source, COMSIG_CLICK_ALT)
+ UnregisterSignal(source, COMSIG_PARENT_EXAMINE)
+
+/datum/element/mob_holder/proc/on_examine(mob/living/source, mob/user, list/examine_list)
+ if(ishuman(user) && !istype(source.loc, /obj/item/clothing/head/mob_holder))
+ examine_list += "Looks like [source.p_they(TRUE)] can be picked up with Alt+Click!"
+
+/datum/element/mob_holder/proc/mob_try_pickup(mob/living/source, mob/user)
+ if(!ishuman(user) || !user.Adjacent(source) || user.incapacitated())
+ return FALSE
+ if(user.get_active_held_item())
+ to_chat(user, "Your hands are full!")
+ return FALSE
+ if(source.buckled)
+ to_chat(user, "[src] is buckled to something!")
+ return FALSE
+ if(source == user)
+ to_chat(user, "You can't pick yourself up.")
+ return FALSE
+ source.visible_message("[user] starts picking up [source].", \
+ "[user] starts picking you up!")
+ if(!do_after(user, 20, target = src) || source.buckled)
+ return FALSE
+
+ source.visible_message("[user] picks up [source]!", \
+ "[user] picks you up!")
+ to_chat(user, "You pick [src] up.")
+ source.drop_all_held_items()
+ var/obj/item/clothing/head/mob_holder/holder = new(get_turf(source), source, worn_state, alt_worn, right_hand, left_hand, inv_slots)
+ if(proctype)
+ INVOKE_ASYNC(src, proctype, source, holder, user)
+ user.put_in_hands(holder)
+ return TRUE
+
+/datum/element/mob_holder/proc/drone_worn_icon(mob/living/simple_animal/drone/D, obj/item/clothing/head/mob_holder/holder, mob/user)
+ var/new_state = "[D.visualAppearence]_hat"
+ holder.item_state = new_state
+ holder.icon_state = new_state
+
+
+//The item itself,
+/obj/item/clothing/head/mob_holder
+ name = "bugged mob"
+ desc = "Yell at coderbrush."
+ icon = null
+ alternate_worn_icon = 'icons/mob/animals_held.dmi'
+ righthand_file = 'icons/mob/animals_held_rh.dmi'
+ lefthand_file = 'icons/mob/animals_held_lh.dmi'
+ icon_state = ""
+ w_class = WEIGHT_CLASS_BULKY
+ var/mob/living/held_mob
+
+/obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/target, worn_state, alt_worn, right_hand, left_hand, slots = NONE)
+ . = ..()
+
+ if(target)
+ assimilate(target)
+
+ if(alt_worn)
+ alternate_worn_icon = alt_worn
+ if(worn_state)
+ item_state = worn_state
+ icon_state = worn_state
+ if(left_hand)
+ lefthand_file = left_hand
+ if(right_hand)
+ righthand_file = right_hand
+ slot_flags = slots
+
+/obj/item/clothing/head/mob_holder/proc/assimilate(mob/living/target)
+ target.setDir(SOUTH)
+ held_mob = target
+ target.forceMove(src)
+ var/image/I = new //work around to retain the same appearance to the mob idependently from inhands/worn states.
+ I.appearance = target.appearance
+ I.override = TRUE
+ add_overlay(I)
+ name = target.name
+ desc = target.desc
+ switch(target.mob_size)
+ if(MOB_SIZE_TINY)
+ w_class = WEIGHT_CLASS_TINY
+ if(MOB_SIZE_SMALL)
+ w_class = WEIGHT_CLASS_NORMAL
+ if(MOB_SIZE_LARGE)
+ w_class = WEIGHT_CLASS_HUGE
+ RegisterSignal(src, COMSIG_CLICK_SHIFT, .proc/examine_held_mob)
+
+/obj/item/clothing/head/mob_holder/Destroy()
+ if(held_mob)
+ release()
+ return ..()
+
+/obj/item/clothing/head/mob_holder/proc/examine_held_mob(datum/source, mob/user)
+ held_mob.ShiftClick(user)
+ return COMPONENT_DENY_EXAMINATE
+
+/obj/item/clothing/head/mob_holder/Exited(atom/movable/AM, atom/newloc)
+ . = ..()
+ if(AM == held_mob)
+ held_mob.reset_perspective()
+ held_mob = null
+ qdel(src)
+
+/obj/item/clothing/head/mob_holder/Entered(atom/movable/AM, atom/newloc)
+ . = ..()
+ if(AM != held_mob)
+ var/destination = loc
+ if(isliving(loc)) //the mob is held or worn, drop things on the floor
+ destination = get_turf(loc)
+ AM.forceMove(destination)
+
+/obj/item/clothing/head/mob_holder/dropped()
+ . = ..()
+ if(held_mob && isturf(loc))//don't release on soft-drops
+ release()
+
+/obj/item/clothing/head/mob_holder/proc/release()
+ if(held_mob)
+ var/mob/living/L = held_mob
+ held_mob = null
+ L.forceMove(get_turf(L))
+ L.reset_perspective()
+ L.setDir(SOUTH)
+ qdel(src)
+
+/obj/item/clothing/head/mob_holder/relaymove(mob/user)
+ return
+
+/obj/item/clothing/head/mob_holder/container_resist()
+ if(isliving(loc))
+ var/mob/living/L = loc
+ L.visible_message("[src] escapes from [L]!", "[src] escapes your grip!")
+ release()
+
+/obj/item/clothing/head/mob_holder/assume_air(datum/gas_mixture/env)
+ var/atom/location = loc
+ if(!loc)
+ return //null
+ var/turf/T = get_turf(loc)
+ while(location != T)
+ location = location.loc
+ if(ismob(location))
+ return location.loc.assume_air(env)
+ return loc.assume_air(env)
+
+/obj/item/clothing/head/mob_holder/remove_air(amount)
+ var/atom/location = loc
+ if(!loc)
+ return //null
+ var/turf/T = get_turf(loc)
+ while(location != T)
+ location = location.loc
+ if(ismob(location))
+ return location.loc.remove_air(amount)
+ return loc.remove_air(amount)
diff --git a/code/datums/elements/wuv.dm b/code/datums/elements/wuv.dm
index bac7d1ff21..55bc9bafad 100644
--- a/code/datums/elements/wuv.dm
+++ b/code/datums/elements/wuv.dm
@@ -30,6 +30,10 @@
RegisterSignal(target, COMSIG_MOB_ATTACK_HAND, .proc/on_attack_hand)
+/datum/element/wuv/Detach(datum/source, force)
+ . = ..()
+ UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
+
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
var/mob/living/L = source
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 34c268578c..3f2a259df7 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -16,10 +16,6 @@
. += "[t_He] [t_is] wearing [wear_mask.get_examine_string(user)] on [t_his] face."
if (wear_neck)
. += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n"
- if(can_be_held)
- . += "[t_He] looks small enough to be picked up with Alt+Click!\n"
-
-
for(var/obj/item/I in held_items)
if(!(I.item_flags & ABSTRACT))
@@ -116,4 +112,5 @@
. += "[t_He] look[p_s()] very happy."
if(MOOD_LEVEL_HAPPY4 to INFINITY)
. += "[t_He] look[p_s()] ecstatic."
+ SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .)
. += "*---------*"
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index ef8a745b89..b560699a58 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -396,6 +396,7 @@
var/temp_flavor = print_flavor_text_2()
if(temp_flavor)
. += temp_flavor
+ SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .)
. += "*---------*"
/mob/living/proc/status_effect_examines(pronoun_replacement) //You can include this in any mob's examine() to show the examine texts of status effects!
diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
index 96d52dcb27..220e639e34 100644
--- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm
+++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
@@ -132,14 +132,14 @@
return INITIALIZE_HINT_QDEL
owner = new_owner
START_PROCESSING(SSobj, src)
- RegisterSignal(owner, COMSIG_MOB_EXAMINATE, .proc/examinate_check)
+ RegisterSignal(owner, COMSIG_CLICK_SHIFT, .proc/examinate_check)
RegisterSignal(src, COMSIG_ATOM_HEARER_IN_VIEW, .proc/include_owner)
RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head)
RegisterSignal(owner, COMSIG_LIVING_FULLY_HEAL, .proc/retrieve_head)
-/obj/item/dullahan_relay/proc/examinate_check(mob/source, atom/A)
- if(source.client.eye == src && ((A in view(source.client.view, src)) || (isturf(A) && source.sight & SEE_TURFS) || (ismob(A) && source.sight & SEE_MOBS) || (isobj(A) && source.sight & SEE_OBJS)))
- return COMPONENT_ALLOW_EXAMINE
+/obj/item/dullahan_relay/proc/examinate_check(atom/source, mob/user)
+ if(user.client.eye == src)
+ return COMPONENT_ALLOW_EXAMINATE
/obj/item/dullahan_relay/proc/include_owner(datum/source, list/processing_list, list/hearers)
if(!QDELETED(owner))
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index ea5203778b..cd51a4234b 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -15,7 +15,6 @@
bodyparts = list(/obj/item/bodypart/chest/monkey, /obj/item/bodypart/head/monkey, /obj/item/bodypart/l_arm/monkey,
/obj/item/bodypart/r_arm/monkey, /obj/item/bodypart/r_leg/monkey, /obj/item/bodypart/l_leg/monkey)
hud_type = /datum/hud/monkey
- can_be_held = "monkey"
/mob/living/carbon/monkey/Initialize(mapload, cubespawned=FALSE, mob/spawner)
verbs += /mob/living/proc/mob_sleep
@@ -42,14 +41,15 @@
create_dna(src)
dna.initialize_dna(random_blood_type())
+/mob/living/carbon/monkey/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/mob_holder, "monkey", null, null, null, SLOT_HEAD)
+
+
/mob/living/carbon/monkey/Destroy()
SSmobs.cubemonkeys -= src
return ..()
-/mob/living/carbon/monkey/generate_mob_holder()
- var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, "monkey", 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi', TRUE)
- return holder
-
/mob/living/carbon/monkey/create_internal_organs()
internal_organs += new /obj/item/organ/appendix
internal_organs += new /obj/item/organ/lungs
diff --git a/code/modules/mob/living/inhand_holder.dm b/code/modules/mob/living/inhand_holder.dm
deleted file mode 100644
index 695258f944..0000000000
--- a/code/modules/mob/living/inhand_holder.dm
+++ /dev/null
@@ -1,130 +0,0 @@
-//Generic system for picking up mobs.
-//Currently works for head and hands.
-/obj/item/clothing/head/mob_holder
- name = "bugged mob"
- desc = "Yell at coderbrush."
- icon = null
- icon_state = ""
- var/mob/living/held_mob
- var/can_head = FALSE
- w_class = WEIGHT_CLASS_BULKY
-
-/obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/M, _worn_state, alt_worn, lh_icon, rh_icon, _can_head_override = FALSE)
- . = ..()
-
- if(M)
- M.setDir(SOUTH)
- held_mob = M
- M.forceMove(src)
- appearance = M.appearance
- name = M.name
- desc = M.desc
-
- if(_can_head_override)
- can_head = _can_head_override
- if(alt_worn)
- alternate_worn_icon = alt_worn
- if(_worn_state)
- item_state = _worn_state
- icon_state = _worn_state
- if(lh_icon)
- lefthand_file = lh_icon
- if(rh_icon)
- righthand_file = rh_icon
- if(!can_head)
- slot_flags = NONE
-
-/obj/item/clothing/head/mob_holder/Destroy()
- if(held_mob)
- release()
- return ..()
-
-/obj/item/clothing/head/mob_holder/dropped()
- ..()
- if(isturf(loc))//don't release on soft-drops
- release()
-
-/obj/item/clothing/head/mob_holder/proc/release()
- if(isliving(loc))
- var/mob/living/L = loc
- L.dropItemToGround(src)
- if(held_mob)
- var/mob/living/m = held_mob
- m.forceMove(get_turf(m))
- m.reset_perspective()
- m.setDir(SOUTH)
- held_mob = null
- qdel(src)
-
-/obj/item/clothing/head/mob_holder/relaymove(mob/user)
- return
-
-/obj/item/clothing/head/mob_holder/container_resist()
- if(isliving(loc))
- var/mob/living/L = loc
- visible_message("[src] escapes [L]!")
- release()
-
-/mob/living/proc/mob_pickup(mob/living/L)
- var/obj/item/clothing/head/mob_holder/holder = generate_mob_holder()
- if(!holder)
- return
- drop_all_held_items()
- L.put_in_hands(holder)
- return
-
-/mob/living/proc/mob_try_pickup(mob/living/user)
- if(!ishuman(user) || !src.Adjacent(user) || user.incapacitated() || !can_be_held)
- return FALSE
- if(user.get_active_held_item())
- to_chat(user, "Your hands are full!")
- return FALSE
- if(buckled)
- to_chat(user, "[src] is buckled to something!")
- return FALSE
- if(src == user)
- to_chat(user, "You can't pick yourself up.")
- return FALSE
- visible_message("[user] starts picking up [src].", \
- "[user] starts picking you up!")
- if(!do_after(user, 20, target = src))
- return FALSE
-
- if(user.get_active_held_item()||buckled)
- return FALSE
-
- visible_message("[user] picks up [src]!", \
- "[user] picks you up!")
- to_chat(user, "You pick [src] up.")
- mob_pickup(user)
- return TRUE
-
-/mob/living/AltClick(mob/user)
- . = ..()
- if(mob_try_pickup(user))
- return TRUE
-
-
-// I didn't define these for mobs, because you shouldn't be able to breathe out of mobs and using their loc isn't always the logical thing to do.
-
-/obj/item/clothing/head/mob_holder/assume_air(datum/gas_mixture/env)
- var/atom/location = loc
- if(!loc)
- return //null
- var/turf/T = get_turf(loc)
- while(location != T)
- location = location.loc
- if(ismob(location))
- return location.loc.assume_air(env)
- return loc.assume_air(env)
-
-/obj/item/clothing/head/mob_holder/remove_air(amount)
- var/atom/location = loc
- if(!loc)
- return //null
- var/turf/T = get_turf(loc)
- while(location != T)
- location = location.loc
- if(ismob(location))
- return location.loc.remove_air(amount)
- return loc.remove_air(amount)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 90ada3d718..b3dc82a40f 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -40,11 +40,6 @@
QDEL_LIST(diseases)
return ..()
-
-/mob/living/proc/generate_mob_holder()
- var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, (istext(can_be_held) ? can_be_held : ""), 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi')
- return holder
-
/mob/living/onZImpact(turf/T, levels)
if(!isgroundlessturf(T))
ZImpactDamage(T, levels)
@@ -1172,8 +1167,6 @@
return
if(!over.Adjacent(src) || (user != src) || !canUseTopic(over))
return
- if(can_be_held)
- mob_try_pickup(over)
/mob/living/proc/get_static_viruses() //used when creating blood and other infective objects
if(!LAZYLEN(diseases))
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index e668828392..84f0004805 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -101,8 +101,6 @@
var/list/obj/effect/proc_holder/abilities = list()
- var/can_be_held = FALSE //whether this can be picked up and held.
-
var/radiation = 0 //If the mob is irradiated.
var/ventcrawl_layer = PIPING_LAYER_DEFAULT
var/losebreath = 0
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 9e2dea0442..409665f792 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -11,7 +11,7 @@
health = 500
maxHealth = 500
layer = BELOW_MOB_LAYER
- can_be_held = TRUE
+ var/datum/element/mob_holder/current_mob_holder //because only a few of their chassis can be actually held.
var/network = "ss13"
var/obj/machinery/camera/current = null
@@ -64,9 +64,6 @@
var/list/possible_chassis //initialized in initialize.
var/list/dynamic_chassis_icons //ditto.
var/list/chassis_pixel_offsets_x //stupid dogborgs
- var/static/item_head_icon = 'icons/mob/pai_item_head.dmi'
- var/static/item_lh_icon = 'icons/mob/pai_item_lh.dmi'
- var/static/item_rh_icon = 'icons/mob/pai_item_rh.dmi'
var/emitterhealth = 20
var/emittermaxhealth = 20
diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm
index bb02bf172d..8ae44c042f 100644
--- a/code/modules/mob/living/silicon/pai/pai_shell.dm
+++ b/code/modules/mob/living/silicon/pai/pai_shell.dm
@@ -96,6 +96,12 @@
dynamic_chassis = choice
resist_a_rest(FALSE, TRUE)
update_icon()
+ if(possible_chassis[chassis])
+ current_mob_holder = AddElement(/datum/element/mob_holder, chassis, 'icons/mob/pai_item_head.dmi', 'icons/mob/pai_item_rh.dmi', 'icons/mob/pai_item_lh.dmi', SLOT_HEAD)
+ else
+ current_mob_holder?.Detach(src)
+ current_mob_holder = null
+ return
to_chat(src, "You switch your holochassis projection composite to [chassis]")
/mob/living/silicon/pai/lay_down()
@@ -117,19 +123,6 @@
set_light(0)
to_chat(src, "You disable your integrated light.")
-/mob/living/silicon/pai/mob_pickup(mob/living/L)
- var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, chassis, item_head_icon, item_lh_icon, item_rh_icon)
- if(!L.put_in_hands(holder))
- qdel(holder)
- else
- L.visible_message("[L] scoops up [src]!")
-
-/mob/living/silicon/pai/mob_try_pickup(mob/living/user)
- if(!possible_chassis[chassis])
- to_chat(user, "[src]'s current form isn't able to be carried!")
- return FALSE
- return ..()
-
/mob/living/silicon/pai/verb/toggle_chassis_sit()
set name = "Toggle Chassis Sit"
set category = "IC"
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 016bee2d44..f12f3649cf 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -31,7 +31,7 @@
var/mob/living/simple_animal/mouse/movement_target
gold_core_spawnable = FRIENDLY_SPAWN
collar_type = "cat"
- can_be_held = "cat2"
+ var/held_icon = "cat2"
do_footstep = TRUE
/mob/living/simple_animal/pet/cat/Initialize()
@@ -41,6 +41,7 @@
/mob/living/simple_animal/pet/cat/ComponentInitialize()
. = ..()
AddElement(/datum/element/wuv, "purrs!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "hisses!", EMOTE_AUDIBLE)
+ AddElement(/datum/element/mob_holder, held_icon)
/mob/living/simple_animal/pet/cat/update_canmove()
..()
@@ -60,6 +61,7 @@
icon_state = "spacecat"
icon_living = "spacecat"
icon_dead = "spacecat_dead"
+ held_icon = "spacecat"
unsuitable_atmos_damage = 0
minbodytemp = TCMB
maxbodytemp = T0C + 40
@@ -71,6 +73,7 @@
icon_state = "original"
icon_living = "original"
icon_dead = "original_dead"
+ held_icon = "original"
collar_type = null
unique_pet = TRUE
@@ -84,7 +87,7 @@
pass_flags = PASSMOB
mob_size = MOB_SIZE_SMALL
collar_type = "kitten"
- can_be_held = "cat"
+ held_icon = "cat"
//RUNTIME IS ALIVE! SQUEEEEEEEE~
/mob/living/simple_animal/pet/cat/Runtime
@@ -249,7 +252,7 @@
attacked_sound = 'sound/items/eatfood.ogg'
deathmessage = "loses its false life and collapses!"
death_sound = "bodyfall"
- can_be_held = "cak"
+ held_icon = "cak"
/mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts)
..()
diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm
index 554d03991a..f02d21c2af 100644
--- a/code/modules/mob/living/simple_animal/friendly/dog.dm
+++ b/code/modules/mob/living/simple_animal/friendly/dog.dm
@@ -13,13 +13,14 @@
see_in_dark = 5
speak_chance = 1
turns_per_move = 10
+ var/held_icon = "corgi"
do_footstep = TRUE
- can_be_held = TRUE
/mob/living/simple_animal/pet/dog/ComponentInitialize()
. = ..()
AddElement(/datum/element/wuv, "yaps_happily!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "growls!", EMOTE_AUDIBLE)
+ AddElement(/datum/element/mob_holder, held_icon)
//Corgis and pugs are now under one dog subtype
@@ -34,13 +35,11 @@
childtype = list(/mob/living/simple_animal/pet/dog/corgi/puppy = 95, /mob/living/simple_animal/pet/dog/corgi/puppy/void = 5)
animal_species = /mob/living/simple_animal/pet/dog
gold_core_spawnable = FRIENDLY_SPAWN
- can_be_held = TRUE
collar_type = "corgi"
var/obj/item/inventory_head
var/obj/item/inventory_back
var/shaved = FALSE
var/nofur = FALSE //Corgis that have risen past the material plane of existence.
- can_be_held = "corgi"
/mob/living/simple_animal/pet/dog/corgi/Destroy()
QDEL_NULL(inventory_head)
@@ -69,7 +68,7 @@
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/pug = 3)
gold_core_spawnable = FRIENDLY_SPAWN
collar_type = "pug"
- can_be_held = "pug"
+ held_icon = "pug"
/mob/living/simple_animal/pet/dog/corgi/exoticcorgi
name = "Exotic Corgi"
@@ -156,13 +155,6 @@
..()
update_corgi_fluff()
-/mob/living/simple_animal/pet/dog/corgi/mob_pickup(mob/living/L)
- var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, "corgi", null, 'icons/mob/pets_held_lh.dmi', 'icons/mob/pets_held_rh.dmi', FALSE)
- if(!L.put_in_hands(holder))
- qdel(holder)
- else
- L.visible_message("[L] scoops up [src]!")
-
/mob/living/simple_animal/pet/dog/corgi/Topic(href, href_list)
if(!(iscarbon(usr) || iscyborg(usr)) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
usr << browse(null, "window=mob[REF(src)]")
@@ -371,7 +363,10 @@
icon_dead = "old_corgi_dead"
desc = "At a ripe old age of [record_age] Ian's not as spry as he used to be, but he'll always be the HoP's beloved corgi." //RIP
turns_per_move = 20
- can_be_held = "old_corgi"
+ var/datum/element/mob_holder/ele = SSdcs.GetElement(/datum/element/mob_holder, held_icon)
+ if(ele)
+ ele.Detach(src)
+ AddElement(/datum/element/mob_holder, "old_corgi")
/mob/living/simple_animal/pet/dog/corgi/Ian/Life()
if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved)
@@ -594,7 +589,7 @@
unsuitable_atmos_damage = 0
minbodytemp = TCMB
maxbodytemp = T0C + 40
- can_be_held = "void_puppy"
+ held_icon = "void_puppy"
/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = 0)
return 1 //Void puppies can navigate space.
@@ -616,7 +611,7 @@
response_harm = "kicks"
var/turns_since_scan = 0
var/puppies = 0
- can_be_held = "lisa"
+ held_icon = "lisa"
//Lisa already has a cute bow!
/mob/living/simple_animal/pet/dog/corgi/Lisa/Topic(href, href_list)
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
index 28fb1f5a42..158719414a 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
@@ -51,7 +51,7 @@
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
see_in_dark = 7
blood_volume = 0
- can_be_held = TRUE
+ var/can_be_held = TRUE //mob holder element.
held_items = list(null, null)
var/staticChoice = "static"
var/list/staticChoices = list("static", "blank", "letter", "animal")
@@ -101,6 +101,11 @@
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
+/mob/living/simple_animal/drone/ComponentInitialize()
+ . = ..()
+ if(can_be_held)
+ //icon/item state is defined in mob_holder/drone_worn_icon()
+ AddElement(/datum/element/mob_holder, null, 'icons/mob/head.dmi', 'icons/mob/inhands/clothing_righthand.dmi', 'icons/mob/inhands/clothing_lefthand.dmi', TRUE, /datum/element/mob_holder.proc/drone_worn_icon)
/mob/living/simple_animal/drone/med_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
@@ -283,7 +288,3 @@
var/obj/item/clothing/H = head
if(H.clothing_flags & SCAN_REAGENTS)
return TRUE
-
-/mob/living/simple_animal/drone/generate_mob_holder()
- var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, "[visualAppearence]_hat", null, null, null, TRUE)
- return holder
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
index c9207fcf89..0f97a4c496 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
@@ -29,12 +29,6 @@
if("Nothing")
return
-//picky up the drone c:
-/mob/living/simple_animal/drone/attack_hand(mob/user)
- if(user.a_intent != INTENT_HELP)
- return ..() // TODO: convert picking up mobs into an element or component.
- mob_try_pickup(user)
-
/mob/living/simple_animal/drone/proc/try_reactivate(mob/living/user)
var/mob/dead/observer/G = get_ghost()
if(!client && (!G || !G.client))
diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm
index 3f58ca593e..95b082b733 100644
--- a/code/modules/mob/living/simple_animal/friendly/fox.dm
+++ b/code/modules/mob/living/simple_animal/friendly/fox.dm
@@ -18,9 +18,12 @@
response_disarm = "gently pushes aside"
response_harm = "kicks"
gold_core_spawnable = FRIENDLY_SPAWN
- can_be_held = "fox"
do_footstep = TRUE
+/mob/living/simple_animal/pet/fox/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/mob_holder, "fox")
+
//Captain fox
/mob/living/simple_animal/pet/fox/Renault
name = "Renault"
diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm
index cafa0d9009..6275256f5a 100644
--- a/code/modules/mob/living/simple_animal/friendly/lizard.dm
+++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm
@@ -23,7 +23,10 @@
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
var/static/list/edibles = typecacheof(list(/mob/living/simple_animal/butterfly, /mob/living/simple_animal/cockroach)) //list of atoms, however turfs won't affect AI, but will affect consumption.
- can_be_held = "lizard" //you can hold lizards now.
+
+/mob/living/simple_animal/hostile/lizard/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/mob_holder, "lizard", null, null, null, SLOT_HEAD) //you can hold lizards now.
/mob/living/simple_animal/hostile/lizard/CanAttack(atom/the_target)//Can we actually attack a possible target?
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
@@ -40,7 +43,3 @@
return TRUE
else
return ..()
-
-/mob/living/simple_animal/hostile/lizard/generate_mob_holder()
- var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, "lizard", 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi', TRUE)
- return holder
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index 69d9a0eef3..d326573957 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -26,17 +26,16 @@
var/body_color //brown, gray and white, leave blank for random
gold_core_spawnable = FRIENDLY_SPAWN
var/chew_probability = 1
- can_be_held = TRUE
/mob/living/simple_animal/mouse/Initialize()
. = ..()
AddComponent(/datum/component/squeak, list('sound/effects/mousesqueek.ogg'=1), 100)
if(!body_color)
- body_color = pick( list("brown","gray","white") )
+ body_color = pick(list("brown","gray","white"))
+ AddElement(/datum/element/mob_holder, "mouse_[body_color]")
icon_state = "mouse_[body_color]"
icon_living = "mouse_[body_color]"
icon_dead = "mouse_[body_color]_dead"
- can_be_held = "mouse_[body_color]"
/mob/living/simple_animal/mouse/proc/splat()
src.health = 0
@@ -89,17 +88,14 @@
/mob/living/simple_animal/mouse/white
body_color = "white"
icon_state = "mouse_white"
- can_be_held = "mouse_white"
/mob/living/simple_animal/mouse/gray
body_color = "gray"
icon_state = "mouse_gray"
- can_be_held = "mouse_gray"
/mob/living/simple_animal/mouse/brown
body_color = "brown"
icon_state = "mouse_brown"
- can_be_held = "mouse_brown"
//TOM IS ALIVE! SQUEEEEEEEE~K :)
/mob/living/simple_animal/mouse/brown/Tom
@@ -124,7 +120,3 @@
/obj/item/reagent_containers/food/snacks/deadmouse/on_grind()
reagents.clear_reagents()
-/mob/living/simple_animal/mouse/generate_mob_holder()
- var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, (istext(can_be_held) ? can_be_held : ""), 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi')
- holder.w_class = WEIGHT_CLASS_TINY
- return holder
diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm
index 175cb8e838..21df73fa61 100644
--- a/code/modules/mob/living/simple_animal/friendly/sloth.dm
+++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm
@@ -22,9 +22,11 @@
maxHealth = 50
speed = 10
glide_size = 2
- can_be_held = "sloth" //finally oranges can be held
do_footstep = TRUE
+/mob/living/simple_animal/pet/fox/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/mob_holder, "sloth") //finally oranges can be held
//Cargo Sloth
/mob/living/simple_animal/sloth/paperwork
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 52e2673dd6..35c7510ad9 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -321,20 +321,18 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
set name = "Examine"
set category = "IC"
- if(!client)
- return
-
- if(!(SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A) & COMPONENT_ALLOW_EXAMINE) && ((client.eye != src && client.eye != loc) || (isturf(A) && !(sight & SEE_TURFS) && !(A in view(client ? client.view : world.view, src)))))
- //cameras & co don't allow users to examine far away things, also shift-click catcher may issue examinate() calls for out-of-sight turfs
+ if(isturf(A) && !(sight & SEE_TURFS) && !(A in view(client ? client.view : world.view, src)))
+ // shift-click catcher may issue examinate() calls for out-of-sight turfs
return
if(is_blind(src))
- to_chat(src, "Something is there but you can't see it.")
+ to_chat(src, "Something is there but you can't see it!")
return
face_atom(A)
var/list/result = A.examine(src)
to_chat(src, result.Join("\n"))
+ SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A)
//same as above
//note: ghosts can point, this is intended
diff --git a/tgstation.dme b/tgstation.dme
index 7407ee2e91..d67af98920 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -487,6 +487,7 @@
#include "code\datums\elements\dusts_on_leaving_area.dm"
#include "code\datums\elements\earhealing.dm"
#include "code\datums\elements\ghost_role_eligibility.dm"
+#include "code\datums\elements\mob_holder.dm"
#include "code\datums\elements\wuv.dm"
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"
@@ -2124,7 +2125,6 @@
#include "code\modules\mob\living\damage_procs.dm"
#include "code\modules\mob\living\death.dm"
#include "code\modules\mob\living\emote.dm"
-#include "code\modules\mob\living\inhand_holder.dm"
#include "code\modules\mob\living\life.dm"
#include "code\modules\mob\living\living.dm"
#include "code\modules\mob\living\living_defense.dm"