mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 12:41:09 +01:00
## About The Pull Request Adds female gender shaping to digi species (such as lizards), for the top half only. <details><summary>Shown here</summary> <img width="337" height="464" alt="dreamseeker_HhKjfrmJ1f" src="https://github.com/user-attachments/assets/a5a57580-6798-40e4-925c-b3a63e0e540e" /> </details> Also just adds a bunch of bodyshape args now that we are passing that to the clothing rendering. This will help with anything where you want unique digi handling for any specific items. Also fixes some missing args and even improper args in some of these proc overrides. ## Why It's Good For The Game Makes things more convenient for coders. Adds some more customization options for feminine lizardfolk. ## Changelog 🆑 code: bodyshape is now accessible in build_worn_overlays() and similar procs. image: lizards now have support female gender shaping for their suits /🆑 --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
260 lines
9.1 KiB
Plaintext
260 lines
9.1 KiB
Plaintext
/// Allows players to place hats on the atom this is attached to
|
|
/datum/component/hat_stabilizer
|
|
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
|
/// Currently "stored" hat. No armor or function will be inherited, only the icon and cover flags.
|
|
var/obj/item/attached_hat
|
|
/// If TRUE, the hat will fall to the ground when the owner does so. It can also be shot off.
|
|
var/loose_hat = FALSE
|
|
/// Original cover flags for the helmet, before a hat is placed
|
|
var/former_flags
|
|
var/former_visor_flags
|
|
/// If true, add_overlay will use worn overlay instead of item appearance
|
|
var/use_worn_icon = TRUE
|
|
/// Pixel z offset for the hat
|
|
var/pixel_z_offset
|
|
/// Which way a loose hat is offset
|
|
var/head_angle = 1
|
|
|
|
/datum/component/hat_stabilizer/Initialize(use_worn_icon = FALSE, pixel_z_offset = 0, loose_hat = FALSE)
|
|
if(!ismovable(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
|
|
var/atom/movable/source = parent
|
|
source.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
|
|
|
|
src.use_worn_icon = use_worn_icon
|
|
src.pixel_z_offset = pixel_z_offset
|
|
src.loose_hat = loose_hat
|
|
// Examine signals
|
|
RegisterSignal(source, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
|
RegisterSignal(source, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
|
|
|
|
// Equip signals, used to drop loose hats
|
|
RegisterSignal(source, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
|
|
RegisterSignal(source, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
|
|
|
|
// Wear & Remove
|
|
RegisterSignal(source, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
|
|
RegisterSignal(source, COMSIG_ATOM_ATTACK_HAND_SECONDARY, PROC_REF(on_secondary_attack_hand))
|
|
|
|
// Overlays
|
|
RegisterSignal(source, COMSIG_MODULE_GENERATE_WORN_OVERLAY, PROC_REF(get_worn_overlays))
|
|
RegisterSignal(source, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(get_separate_worn_overlays))
|
|
|
|
RegisterSignal(source, COMSIG_QDELETING, PROC_REF(on_qdel))
|
|
|
|
// Inherit the new values passed to the component
|
|
/datum/component/hat_stabilizer/InheritComponent(datum/component/hat_stabilizer/new_comp, original, use_worn_icon, pixel_z_offset, loose_hat)
|
|
if(!original)
|
|
return
|
|
|
|
if(!isnull(use_worn_icon))
|
|
src.use_worn_icon = use_worn_icon
|
|
if(!isnull(use_worn_icon))
|
|
src.use_worn_icon = use_worn_icon
|
|
if(!isnull(pixel_z_offset))
|
|
src.pixel_z_offset = pixel_z_offset
|
|
if(!isnull(loose_hat))
|
|
src.loose_hat = loose_hat
|
|
|
|
/datum/component/hat_stabilizer/UnregisterFromParent()
|
|
if (attached_hat)
|
|
remove_hat()
|
|
UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_ATOM_ATTACKBY,
|
|
COMSIG_ATOM_ATTACK_HAND_SECONDARY, COMSIG_MODULE_GENERATE_WORN_OVERLAY,
|
|
COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_QDELETING,
|
|
COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
|
|
|
|
/datum/component/hat_stabilizer/proc/on_equip(datum/source, mob/equipper, slot)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!loose_hat)
|
|
return
|
|
|
|
var/obj/item/our_item = parent
|
|
if(!(slot & our_item.slot_flags))
|
|
return
|
|
RegisterSignals(equipper, list(COMSIG_MOB_SLIPPED, COMSIG_LIVING_SLAPPED, COMSIG_MOVABLE_POST_THROW), PROC_REF(throw_hat))
|
|
RegisterSignal(equipper, COMSIG_LIVING_THUD, PROC_REF(drop_hat))
|
|
|
|
/datum/component/hat_stabilizer/proc/on_drop(datum/source, mob/dropper)
|
|
SIGNAL_HANDLER
|
|
UnregisterSignal(dropper, list(COMSIG_MOB_SLIPPED, COMSIG_LIVING_SLAPPED, COMSIG_MOVABLE_POST_THROW, COMSIG_LIVING_THUD))
|
|
|
|
/datum/component/hat_stabilizer/proc/throw_hat(mob/hatless)
|
|
SIGNAL_HANDLER
|
|
if(!loose_hat)
|
|
return
|
|
var/obj/item/hat = remove_hat()
|
|
if(!hat)
|
|
return
|
|
hat.visible_message(span_danger("[hat] goes flying off [hatless]'s head!"))
|
|
hat.throw_at(get_edge_target_turf(get_turf(hat), pick(GLOB.alldirs)), 2, 1, spin = TRUE)
|
|
|
|
/datum/component/hat_stabilizer/proc/drop_hat(mob/hatless)
|
|
SIGNAL_HANDLER
|
|
if(!loose_hat)
|
|
return
|
|
remove_hat()
|
|
|
|
/datum/component/hat_stabilizer/proc/on_examine(datum/source, mob/user, list/base_examine)
|
|
SIGNAL_HANDLER
|
|
if(attached_hat)
|
|
base_examine += span_notice("There's \a [attached_hat] [loose_hat ? "loosely" : ""] placed on [parent].")
|
|
else
|
|
base_examine += span_notice("There's nothing placed on [parent]. Yet.")
|
|
|
|
/datum/component/hat_stabilizer/proc/get_worn_overlays(atom/movable/source, list/overlays, mutable_appearance/standing, isinhands, icon_file, bodyshape = NONE)
|
|
SIGNAL_HANDLER
|
|
if(isinhands)
|
|
return
|
|
if(!attached_hat)
|
|
return
|
|
var/mutable_appearance/worn_overlay = attached_hat.build_worn_icon(default_layer = ABOVE_BODY_FRONT_HEAD_LAYER - 0.1, default_icon_file = 'icons/mob/clothing/head/default.dmi', bodyshape = bodyshape)
|
|
worn_overlay.appearance_flags |= RESET_COLOR
|
|
// loose hats are slightly angled
|
|
if(loose_hat)
|
|
var/matrix/tilt_trix = matrix(worn_overlay.transform)
|
|
var/angle = 5
|
|
tilt_trix.Turn(angle * head_angle)
|
|
worn_overlay.transform = tilt_trix
|
|
worn_overlay.pixel_z = pixel_z_offset + attached_hat.worn_y_offset
|
|
overlays += worn_overlay
|
|
|
|
/datum/component/hat_stabilizer/proc/get_separate_worn_overlays(atom/movable/source, list/overlays, mutable_appearance/standing, mutable_appearance/draw_target, isinhands, icon_file, bodyshape = NONE)
|
|
SIGNAL_HANDLER
|
|
if (isinhands)
|
|
return
|
|
if(!attached_hat)
|
|
return
|
|
var/mutable_appearance/worn_overlay = attached_hat.build_worn_icon(default_layer = ABOVE_BODY_FRONT_HEAD_LAYER - 0.1, default_icon_file = 'icons/mob/clothing/head/default.dmi', bodyshape = bodyshape)
|
|
for (var/mutable_appearance/overlay in worn_overlay.overlays)
|
|
overlay.layer = -ABOVE_BODY_FRONT_HEAD_LAYER + 0.1
|
|
// loose hats are slightly angled
|
|
if(loose_hat)
|
|
var/matrix/tilt_trix = matrix(worn_overlay.transform)
|
|
var/angle = 5
|
|
tilt_trix.Turn(angle * head_angle)
|
|
worn_overlay.transform = tilt_trix
|
|
worn_overlay.pixel_z = pixel_z_offset + attached_hat.worn_y_offset
|
|
overlays += worn_overlay
|
|
|
|
/datum/component/hat_stabilizer/proc/on_qdel(atom/movable/source)
|
|
SIGNAL_HANDLER
|
|
|
|
if (attached_hat)
|
|
QDEL_NULL(attached_hat)
|
|
|
|
/datum/component/hat_stabilizer/proc/on_attackby(datum/source, obj/item/hitting_item, mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
var/atom/movable/movable_parent = parent
|
|
if(!(hitting_item.slot_flags & ITEM_SLOT_HEAD))
|
|
return
|
|
|
|
if(attached_hat)
|
|
movable_parent.balloon_alert(user, "hat already attached!")
|
|
return
|
|
if(isclothing(hitting_item))
|
|
var/obj/item/clothing/hat = hitting_item
|
|
if(hat.clothing_flags & STACKABLE_HELMET_EXEMPT)
|
|
movable_parent.balloon_alert(user, "invalid hat!")
|
|
return
|
|
|
|
if(!user.transferItemToLoc(hitting_item, parent, force = FALSE, silent = TRUE))
|
|
return
|
|
|
|
attach_hat(hitting_item, user)
|
|
|
|
/datum/component/hat_stabilizer/proc/attach_hat(obj/item/hat, mob/user)
|
|
var/atom/movable/movable_parent = parent
|
|
attached_hat = hat
|
|
RegisterSignal(hat, COMSIG_MOVABLE_MOVED, PROC_REF(on_hat_movement))
|
|
head_angle = pick(1, -1)
|
|
|
|
if (!isnull(user))
|
|
movable_parent.balloon_alert(user, "hat attached")
|
|
|
|
if (!isclothing(parent))
|
|
movable_parent.update_appearance()
|
|
return
|
|
|
|
var/obj/item/clothing/apparel = parent
|
|
if(isclothing(attached_hat))
|
|
var/obj/item/clothing/realhat = attached_hat
|
|
apparel.attach_clothing_traits(realhat.clothing_traits)
|
|
apparel.visor_flags_cover |= realhat.visor_flags_cover
|
|
former_flags = apparel.flags_cover
|
|
former_visor_flags = apparel.visor_flags_cover
|
|
apparel.flags_cover |= attached_hat.flags_cover
|
|
apparel.update_appearance()
|
|
|
|
if (ismob(apparel.loc))
|
|
var/mob/wearer = apparel.loc
|
|
wearer.update_clothing(wearer.get_slot_by_item(apparel))
|
|
|
|
/datum/component/hat_stabilizer/proc/on_hat_movement(obj/hat, mob/user)
|
|
SIGNAL_HANDLER
|
|
remove_hat(ismob(user) ? user : null)
|
|
|
|
/datum/component/hat_stabilizer/proc/on_secondary_attack_hand(datum/source, mob/user)
|
|
SIGNAL_HANDLER
|
|
. = COMPONENT_CANCEL_ATTACK_CHAIN
|
|
if(!attached_hat)
|
|
return
|
|
var/atom/movable/movable_parent = parent
|
|
if (remove_hat(user))
|
|
movable_parent.balloon_alert(user, "hat removed")
|
|
else
|
|
movable_parent.balloon_alert_to_viewers("the hat falls to the floor!")
|
|
|
|
/datum/component/hat_stabilizer/proc/on_retraction()
|
|
|
|
/datum/component/hat_stabilizer/proc/remove_hat(mob/user)
|
|
if(QDELETED(attached_hat))
|
|
return
|
|
|
|
var/atom/movable/movable_parent = parent
|
|
UnregisterSignal(attached_hat, COMSIG_MOVABLE_MOVED)
|
|
|
|
if (attached_hat.loc == parent)
|
|
attached_hat.forceMove(movable_parent.drop_location())
|
|
|
|
if(!isnull(user))
|
|
. = user.put_in_active_hand(attached_hat)
|
|
else
|
|
movable_parent.balloon_alert_to_viewers("the hat falls to the floor!")
|
|
|
|
if (!isclothing(parent))
|
|
attached_hat = null
|
|
movable_parent.update_appearance()
|
|
return
|
|
|
|
var/former_hat = attached_hat
|
|
var/obj/item/clothing/apparel = parent
|
|
if(isclothing(attached_hat))
|
|
var/obj/item/clothing/truehat = attached_hat
|
|
apparel.detach_clothing_traits(truehat.clothing_traits)
|
|
apparel.flags_cover = former_flags
|
|
apparel.visor_flags_cover = former_visor_flags
|
|
apparel.update_appearance()
|
|
attached_hat = null
|
|
if (ismob(apparel.loc))
|
|
var/mob/wearer = apparel.loc
|
|
wearer.update_clothing(wearer.get_slot_by_item(apparel))
|
|
if(isnull(user))
|
|
return former_hat
|
|
|
|
/datum/component/hat_stabilizer/proc/on_requesting_context_from_item(atom/source, list/context, obj/item/held_item, mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
if(attached_hat && !held_item)
|
|
context[SCREENTIP_CONTEXT_RMB] = "Remove hat"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
if(istype(held_item, /obj/item/clothing/head))
|
|
context[SCREENTIP_CONTEXT_LMB] = "Attach hat"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
return NONE
|