mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 14:08:31 +01:00
[MANUAL FIXED MIRROR] 22129 and 22154 (#22379)
* Makes hoods into a component (#75977) ## About The Pull Request Refactors the behaviour of "one clothing item deploying another clothing item" from `/obj/item/clothing/suit/hooded` and makes it into a component. This allows you to make hooded items which are not part of that typepath. It also means you could make (for instance) a hat which can deploy a pair of sunglasses into the eye slot or a jumpsuit with deployable clown shoes or something. I need to pass in an assload of callbacks because we have a bunch of special hoodies that want to do things when you raise and lower the hood, but for a normal item you would not need these. ## Why It's Good For The Game Frees people from the tyrrany of typepaths, mostly. Plausibly you could use it to do something fun we don't currently do. ## Changelog Not player facing, hopefully. As long as I did this all right. * Makes hoods into a component * [no gbp] Fixes item action buttons * Update items.dm * Fix mirror 22129 * Some last minute updates -- comment and small optimization --------- Co-authored-by: Jacquerel <hnevard@gmail.com> Co-authored-by: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Co-authored-by: lessthanthree <83487515+lessthnthree@users.noreply.github.com>
This commit is contained in:
co-authored by
Jacquerel
SkyratBot
lessthanthree
parent
765bcfb355
commit
df074b9966
@@ -126,12 +126,19 @@
|
||||
#define COMSIG_ITEM_DROPPED "item_drop"
|
||||
///from base of obj/item/pickup(): (/mob/taker)
|
||||
#define COMSIG_ITEM_PICKUP "item_pickup"
|
||||
///from base of obj/item/on_outfit_equip(): (mob/equipper, visuals_only, slot)
|
||||
#define COMSIG_ITEM_EQUIPPED_AS_OUTFIT "item_equip_as_outfit"
|
||||
|
||||
/// Sebt from obj/item/ui_action_click(): (mob/user, datum/action)
|
||||
#define COMSIG_ITEM_UI_ACTION_CLICK "item_action_click"
|
||||
/// Return to prevent the default behavior (attack_selfing) from ocurring.
|
||||
#define COMPONENT_ACTION_HANDLED (1<<0)
|
||||
|
||||
/// Sent from obj/item/item_action_slot_check(): (mob/user, datum/action, slot)
|
||||
#define COMSIG_ITEM_UI_ACTION_SLOT_CHECKED "item_action_slot_checked"
|
||||
/// Return to prevent the default behavior (attack_selfing) from ocurring.
|
||||
#define COMPONENT_ITEM_ACTION_SLOT_INVALID (1<<0)
|
||||
|
||||
///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone)
|
||||
#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone"
|
||||
///from base of obj/item/hit_reaction(): (owner, hitby, attack_text, final_block_chance, damage, attack_type, damage_type)
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Component which allows clothing to deploy a different kind of clothing onto you.
|
||||
* The simplest example is hooded suits deploying hoods onto your head.
|
||||
*/
|
||||
/datum/component/toggle_attached_clothing
|
||||
/// Instance of the item we're creating
|
||||
var/obj/item/deployable
|
||||
/// Action used to toggle deployment
|
||||
var/datum/action/item_action/toggle_action
|
||||
/// Typepath of what we're creating
|
||||
var/deployable_type
|
||||
/// Which slot this item equips into
|
||||
var/equipped_slot
|
||||
/// Name of toggle action
|
||||
var/action_name = ""
|
||||
/// If true, we delete our deployable on toggle rather than putting it in nullspace
|
||||
var/destroy_on_removal
|
||||
/// Current state of our deployable equipment
|
||||
var/currently_deployed = FALSE
|
||||
/// What should be added to the end of the parent icon state when equipment is deployed? Set to "" for no change
|
||||
var/parent_icon_state_suffix = ""
|
||||
/// Icon state for overlay to display over the parent item while deployable item is not deployed
|
||||
var/down_overlay_state_suffix = ""
|
||||
/// Overlay to display over the parent item while deployable item is not deployed
|
||||
var/mutable_appearance/undeployed_overlay
|
||||
/// Optional callback triggered before deploying, return TRUE to continue or FALSE to cancel
|
||||
var/datum/callback/pre_creation_check
|
||||
/// Optional callback triggered when we create our deployable equipment
|
||||
var/datum/callback/on_created
|
||||
/// Optional callback triggered when we have deployed our equipment
|
||||
var/datum/callback/on_deployed
|
||||
/// Optional callback triggered before we hide our equipment, before as we may delete it afterwards
|
||||
var/datum/callback/on_removed
|
||||
|
||||
/datum/component/toggle_attached_clothing/Initialize(
|
||||
deployable_type,
|
||||
equipped_slot,
|
||||
action_name = "Toggle",
|
||||
destroy_on_removal = FALSE,
|
||||
parent_icon_state_suffix = "",
|
||||
down_overlay_state_suffix = "",
|
||||
datum/callback/pre_creation_check,
|
||||
datum/callback/on_created,
|
||||
datum/callback/on_deployed,
|
||||
datum/callback/on_removed,
|
||||
)
|
||||
. = ..()
|
||||
if (!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if (!deployable_type || !equipped_slot)
|
||||
return COMPONENT_INCOMPATIBLE // Not strictly true but INITIALIZE_HINT_QDEL doesn't work from components
|
||||
src.deployable_type = deployable_type
|
||||
src.equipped_slot = equipped_slot
|
||||
src.destroy_on_removal = destroy_on_removal
|
||||
src.parent_icon_state_suffix = parent_icon_state_suffix
|
||||
src.down_overlay_state_suffix = down_overlay_state_suffix
|
||||
src.pre_creation_check = pre_creation_check
|
||||
src.on_created = on_created
|
||||
src.on_deployed = on_deployed
|
||||
src.on_removed = on_removed
|
||||
|
||||
var/obj/item/clothing_parent = parent
|
||||
toggle_action = new(parent)
|
||||
toggle_action.name = action_name
|
||||
clothing_parent.add_item_action(toggle_action)
|
||||
|
||||
RegisterSignal(parent, COMSIG_ITEM_UI_ACTION_CLICK, PROC_REF(on_toggle_pressed))
|
||||
RegisterSignal(parent, COMSIG_ITEM_UI_ACTION_SLOT_CHECKED, PROC_REF(on_action_slot_checked))
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_parent_equipped))
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED_AS_OUTFIT, PROC_REF(on_parent_equipped_outfit))
|
||||
if (down_overlay_state_suffix)
|
||||
var/overlay_state = "[initial(clothing_parent.icon_state)][down_overlay_state_suffix]"
|
||||
undeployed_overlay = mutable_appearance(initial(clothing_parent.worn_icon), overlay_state, -SUIT_LAYER)
|
||||
RegisterSignal(parent, COMSIG_ITEM_GET_WORN_OVERLAYS, PROC_REF(on_checked_overlays))
|
||||
clothing_parent.update_slot_icon()
|
||||
|
||||
if (!destroy_on_removal)
|
||||
create_deployable()
|
||||
|
||||
/datum/component/toggle_attached_clothing/Destroy(force, silent)
|
||||
unequip_deployable()
|
||||
QDEL_NULL(deployable)
|
||||
QDEL_NULL(toggle_action)
|
||||
QDEL_NULL(on_created)
|
||||
QDEL_NULL(on_deployed)
|
||||
QDEL_NULL(on_removed)
|
||||
return ..()
|
||||
|
||||
/// Toggle deployable when the UI button is clicked
|
||||
/datum/component/toggle_attached_clothing/proc/on_toggle_pressed(obj/item/source, mob/user, datum/action)
|
||||
SIGNAL_HANDLER
|
||||
if (action != toggle_action)
|
||||
return
|
||||
toggle_deployable()
|
||||
return COMPONENT_ACTION_HANDLED
|
||||
|
||||
/// Called when action attempts to check what slot the item is worn in
|
||||
/datum/component/toggle_attached_clothing/proc/on_action_slot_checked(obj/item/clothing/source, mob/user, datum/action, slot)
|
||||
SIGNAL_HANDLER
|
||||
if (action != toggle_action)
|
||||
return
|
||||
if (!(source.slot_flags & slot))
|
||||
return COMPONENT_ITEM_ACTION_SLOT_INVALID
|
||||
|
||||
/// Apply an overlay while the item is not deployed
|
||||
/datum/component/toggle_attached_clothing/proc/on_checked_overlays(obj/item/source, list/overlays, mutable_appearance/standing, isinhands, icon_file)
|
||||
SIGNAL_HANDLER
|
||||
if (isinhands || currently_deployed)
|
||||
return
|
||||
overlays += undeployed_overlay
|
||||
|
||||
/// Deploys gear if it is hidden, hides it if it is deployed
|
||||
/datum/component/toggle_attached_clothing/proc/toggle_deployable()
|
||||
if (currently_deployed)
|
||||
remove_deployable()
|
||||
return
|
||||
|
||||
var/obj/item/parent_gear = parent
|
||||
if (!ishuman(parent_gear.loc))
|
||||
return
|
||||
var/mob/living/carbon/human/wearer = parent_gear.loc
|
||||
if (wearer.is_holding(parent_gear))
|
||||
parent_gear.balloon_alert(wearer, "wear it first!")
|
||||
return
|
||||
if (wearer.get_item_by_slot(equipped_slot))
|
||||
parent_gear.balloon_alert(wearer, "slot occupied!")
|
||||
return
|
||||
if (!deployable && !create_deployable())
|
||||
return
|
||||
if (!wearer.equip_to_slot_if_possible(deployable, slot = equipped_slot))
|
||||
if(destroy_on_removal)
|
||||
remove_deployable()
|
||||
return
|
||||
currently_deployed = TRUE
|
||||
on_deployed?.Invoke(deployable)
|
||||
if (parent_icon_state_suffix)
|
||||
parent_gear.icon_state = "[initial(parent_gear.icon_state)][parent_icon_state_suffix]"
|
||||
parent_gear.worn_icon_state = parent_gear.icon_state
|
||||
parent_gear.update_slot_icon()
|
||||
wearer.update_mob_action_buttons()
|
||||
|
||||
/// Undeploy gear if it moves slots somehow
|
||||
/datum/component/toggle_attached_clothing/proc/on_parent_equipped(obj/item/clothing/source, mob/equipper, slot)
|
||||
SIGNAL_HANDLER
|
||||
if (slot & equipped_slot)
|
||||
return
|
||||
remove_deployable()
|
||||
|
||||
/// Display deployed if worn in an outfit
|
||||
/datum/component/toggle_attached_clothing/proc/on_parent_equipped_outfit(obj/item/clothing/source, mob/equipper, visuals_only, slot)
|
||||
SIGNAL_HANDLER
|
||||
create_deployable()
|
||||
toggle_deployable()
|
||||
|
||||
/// Create our gear, returns true if we actually made anything
|
||||
/datum/component/toggle_attached_clothing/proc/create_deployable()
|
||||
if (deployable)
|
||||
return FALSE
|
||||
if (pre_creation_check && !pre_creation_check.Invoke())
|
||||
return FALSE
|
||||
deployable = new deployable_type(parent)
|
||||
if (!istype(deployable))
|
||||
stack_trace("Tried to create non-clothing item from toggled clothing.")
|
||||
RegisterSignal(deployable, COMSIG_ITEM_DROPPED, PROC_REF(on_deployed_dropped))
|
||||
RegisterSignal(deployable, COMSIG_ITEM_EQUIPPED, PROC_REF(on_deployed_equipped))
|
||||
RegisterSignal(deployable, COMSIG_QDELETING, PROC_REF(on_deployed_destroyed))
|
||||
on_created?.Invoke(deployable)
|
||||
return TRUE
|
||||
|
||||
/// Undeploy gear if you drop it
|
||||
/datum/component/toggle_attached_clothing/proc/on_deployed_dropped()
|
||||
SIGNAL_HANDLER
|
||||
remove_deployable()
|
||||
|
||||
/// Undeploy gear if it moves slots somehow
|
||||
/datum/component/toggle_attached_clothing/proc/on_deployed_equipped(obj/item/clothing/source, mob/equipper, slot)
|
||||
SIGNAL_HANDLER
|
||||
if (source.slot_flags & slot)
|
||||
return
|
||||
remove_deployable()
|
||||
|
||||
/// Undeploy gear if it is deleted
|
||||
/datum/component/toggle_attached_clothing/proc/on_deployed_destroyed()
|
||||
SIGNAL_HANDLER
|
||||
remove_deployable()
|
||||
deployable = null
|
||||
|
||||
/// Removes our deployed equipment from the wearer
|
||||
/datum/component/toggle_attached_clothing/proc/remove_deployable()
|
||||
unequip_deployable()
|
||||
if (!currently_deployed)
|
||||
return
|
||||
currently_deployed = FALSE
|
||||
on_removed?.Invoke(deployable)
|
||||
|
||||
var/obj/item/parent_gear = parent
|
||||
if (destroy_on_removal)
|
||||
QDEL_NULL(deployable)
|
||||
else if (parent_icon_state_suffix)
|
||||
parent_gear.icon_state = "[initial(parent_gear.icon_state)]"
|
||||
parent_gear.worn_icon_state = parent_gear.icon_state
|
||||
parent_gear.update_slot_icon()
|
||||
parent_gear.update_item_action_buttons()
|
||||
|
||||
/// Removes an equipped deployable atom upon its retraction or destruction
|
||||
/datum/component/toggle_attached_clothing/proc/unequip_deployable()
|
||||
if (!deployable)
|
||||
return
|
||||
if (!ishuman(deployable.loc))
|
||||
deployable.forceMove(parent)
|
||||
return
|
||||
var/mob/living/carbon/human/wearer = deployable.loc
|
||||
wearer.transferItemToLoc(deployable, parent, force = TRUE, silent = TRUE)
|
||||
@@ -703,7 +703,7 @@
|
||||
/// Gives one of our item actions to a mob, when equipped to a certain slot
|
||||
/obj/item/proc/give_item_action(datum/action/action, mob/to_who, slot)
|
||||
// Some items only give their actions buttons when in a specific slot.
|
||||
if(!item_action_slot_check(slot, to_who))
|
||||
if(!item_action_slot_check(slot, to_who, action) || SEND_SIGNAL(src, COMSIG_ITEM_UI_ACTION_SLOT_CHECKED, to_who, action, slot) & COMPONENT_ITEM_ACTION_SLOT_INVALID)
|
||||
// There is a chance we still have our item action currently,
|
||||
// and are moving it from a "valid slot" to an "invalid slot".
|
||||
// So call Remove() here regardless, even if excessive.
|
||||
@@ -713,7 +713,7 @@
|
||||
action.Grant(to_who)
|
||||
|
||||
/// Sometimes we only want to grant the item's action if it's equipped in a specific slot.
|
||||
/obj/item/proc/item_action_slot_check(slot, mob/user)
|
||||
/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/action)
|
||||
if(slot & (ITEM_SLOT_BACKPACK|ITEM_SLOT_LEGCUFFED)) //these aren't true slots, so avoid granting actions there
|
||||
return FALSE
|
||||
return TRUE
|
||||
@@ -1253,9 +1253,9 @@
|
||||
source_item?.reagents?.add_reagent(/datum/reagent/blood, 2)
|
||||
|
||||
else if(custom_materials?.len) //if we've got materials, lets see whats in it
|
||||
/// How many mats have we found? You can only be affected by two material datums by default
|
||||
// How many mats have we found? You can only be affected by two material datums by default
|
||||
var/found_mats = 0
|
||||
/// How much of each material is in it? Used to determine if the glass should break
|
||||
// How much of each material is in it? Used to determine if the glass should break
|
||||
var/total_material_amount = 0
|
||||
|
||||
for(var/mats in custom_materials)
|
||||
@@ -1270,7 +1270,7 @@
|
||||
//if there's glass in it and the glass is more than 60% of the item, then we can shatter it
|
||||
if(custom_materials[GET_MATERIAL_REF(/datum/material/glass)] >= total_material_amount * 0.60)
|
||||
if(prob(66)) //66% chance to break it
|
||||
/// The glass shard that is spawned into the source item
|
||||
// The glass shard that is spawned into the source item
|
||||
var/obj/item/shard/broken_glass = new /obj/item/shard(loc)
|
||||
broken_glass.name = "broken [name]"
|
||||
broken_glass.desc = "This used to be \a [name], but it sure isn't anymore."
|
||||
@@ -1287,7 +1287,7 @@
|
||||
span_warning("Eugh! Did I just bite into something?"))
|
||||
|
||||
else if(w_class == WEIGHT_CLASS_TINY) //small items like soap or toys that don't have mat datums
|
||||
/// victim's chest (for cavity implanting the item)
|
||||
// victim's chest (for cavity implanting the item)
|
||||
var/obj/item/bodypart/chest/victim_cavity = victim.get_bodypart(BODY_ZONE_CHEST)
|
||||
if(victim_cavity.cavity_item)
|
||||
victim.vomit(5, FALSE, FALSE, distance = 0)
|
||||
@@ -1422,7 +1422,8 @@
|
||||
|
||||
/// Special stuff you want to do when an outfit equips this item.
|
||||
/obj/item/proc/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
|
||||
return
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED_AS_OUTFIT, outfit_wearer, visuals_only, item_slot)
|
||||
|
||||
/// Whether or not this item can be put into a storage item through attackby
|
||||
/obj/item/proc/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user)
|
||||
|
||||
@@ -584,6 +584,7 @@
|
||||
|
||||
uniform = /obj/item/clothing/under/color/black
|
||||
suit = /obj/item/clothing/suit/hooded/cultrobes/alt
|
||||
head = /obj/item/clothing/head/hooded/cult_hoodie/alt
|
||||
shoes = /obj/item/clothing/shoes/cult/alt
|
||||
r_hand = /obj/item/melee/blood_magic/stun
|
||||
|
||||
|
||||
@@ -213,7 +213,16 @@ Striking a noncultist, however, will tear their flesh."}
|
||||
heat_protection = CHEST|GROIN|LEGS|ARMS
|
||||
max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
|
||||
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie
|
||||
/// Whether the hood is flipped up
|
||||
var/hood_up = FALSE
|
||||
|
||||
/// Called when the hood is worn
|
||||
/obj/item/clothing/suit/hooded/cultrobes/on_hood_up(obj/item/clothing/head/hooded/hood)
|
||||
hood_up = TRUE
|
||||
|
||||
/// Called when the hood is hidden
|
||||
/obj/item/clothing/suit/hooded/cultrobes/on_hood_down(obj/item/clothing/head/hooded/hood)
|
||||
hood_up = FALSE
|
||||
|
||||
/datum/armor/hooded_cultrobes
|
||||
melee = 40
|
||||
|
||||
@@ -767,4 +767,5 @@
|
||||
name = "Heretic (Preview only)"
|
||||
|
||||
suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
|
||||
head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
|
||||
r_hand = /obj/item/melee/touch_attack/mansus_fist
|
||||
|
||||
@@ -109,26 +109,23 @@
|
||||
// Let examiners know this works as a focus only if the hood is down
|
||||
. += span_notice("Allows you to cast heretic spells while the hood is down.")
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void/RemoveHood()
|
||||
// This is before the hood actually goes down
|
||||
// We only make it visible if the hood is being moved from up to down
|
||||
if(hood_up)
|
||||
make_visible()
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void/on_hood_down(obj/item/clothing/head/hooded/hood)
|
||||
make_visible()
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void/MakeHood()
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void/can_create_hood()
|
||||
if(!isliving(loc))
|
||||
CRASH("[src] attempted to make a hood on a non-living thing: [loc]")
|
||||
|
||||
var/mob/living/wearer = loc
|
||||
if(!IS_HERETIC_OR_MONSTER(wearer))
|
||||
loc.balloon_alert(loc, "you can't get the hood up!")
|
||||
return
|
||||
if(IS_HERETIC_OR_MONSTER(wearer))
|
||||
return TRUE
|
||||
|
||||
// When we make the hood, that means we're going invisible
|
||||
loc.balloon_alert(loc, "can't get the hood up!")
|
||||
return FALSE
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void/on_hood_created(obj/item/clothing/head/hooded/hood)
|
||||
. = ..()
|
||||
make_invisible()
|
||||
return ..()
|
||||
|
||||
/// Makes our cloak "invisible". Not the wearer, the cloak itself.
|
||||
/obj/item/clothing/suit/hooded/cultrobes/void/proc/make_invisible()
|
||||
|
||||
@@ -354,6 +354,7 @@
|
||||
|
||||
uniform = /obj/item/clothing/under/color/grey
|
||||
suit = /obj/item/clothing/suit/hooded/ablative
|
||||
head = /obj/item/clothing/head/hooded/ablative
|
||||
gloves = /obj/item/clothing/gloves/color/yellow
|
||||
mask = /obj/item/clothing/mask/gas
|
||||
l_hand = /obj/item/melee/energy/sword
|
||||
|
||||
@@ -46,22 +46,18 @@
|
||||
if (prob(hit_reflect_chance))
|
||||
return TRUE
|
||||
|
||||
/obj/item/clothing/suit/hooded/ablative/ToggleHood()
|
||||
/obj/item/clothing/suit/hooded/ablative/on_hood_up(obj/item/clothing/head/hooded/hood)
|
||||
. = ..()
|
||||
if (!hood_up)
|
||||
return
|
||||
var/mob/living/carbon/user = loc
|
||||
var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
|
||||
ADD_TRAIT(user, TRAIT_SECURITY_HUD, HELMET_TRAIT)
|
||||
hud.show_to(user)
|
||||
balloon_alert(user, "you put on the hood, and enable the hud")
|
||||
balloon_alert(user, "hud enabled")
|
||||
|
||||
/obj/item/clothing/suit/hooded/ablative/RemoveHood()
|
||||
if (!hood_up)
|
||||
return ..()
|
||||
/obj/item/clothing/suit/hooded/ablative/on_hood_down(obj/item/clothing/head/hooded/hood)
|
||||
var/mob/living/carbon/user = loc
|
||||
var/datum/atom_hud/sec_hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
|
||||
REMOVE_TRAIT(user, TRAIT_SECURITY_HUD, HELMET_TRAIT)
|
||||
sec_hud.hide_from(user)
|
||||
balloon_alert(user, "you take off the hood, and disable the hud")
|
||||
balloon_alert(user, "hud disabled")
|
||||
return ..()
|
||||
|
||||
@@ -1,124 +1,61 @@
|
||||
//Hoods for winter coats and chaplain hoodie etc
|
||||
|
||||
/// Subtype with support for hoods
|
||||
/// You no longer actually need to extend this and can just add the component yourself without a lot of this boilerplate code
|
||||
/obj/item/clothing/suit/hooded
|
||||
actions_types = list(/datum/action/item_action/toggle_hood)
|
||||
var/obj/item/clothing/head/hooded/hood
|
||||
var/hoodtype = /obj/item/clothing/head/hooded/winterhood //so the chaplain hoodie or other hoodies can override this
|
||||
///Alternative mode for hiding the hood, instead of storing the hood in the suit it qdels it, useful for when you deal with hooded suit with storage.
|
||||
/// Alternative mode for hiding the hood, instead of storing the hood in the suit it qdels it, useful for when you deal with hooded suit with storage.
|
||||
var/alternative_mode = FALSE
|
||||
///Whether the hood is flipped up
|
||||
var/hood_up = FALSE
|
||||
/// What should be added to the end of the icon state when the hood is up? Set to "" for the suit sprite to not change at all
|
||||
var/hood_up_affix = "_t"
|
||||
/// Are we zipped? Mostly relevant for wintercoats, leaving this here to simplify logic and so someone else can extend it if they ever wish to.
|
||||
var/zipped = FALSE
|
||||
/// Icon state added as a worn overlay while the hood is down, leave as "" for no overlay
|
||||
var/hood_down_overlay_suffix = ""
|
||||
/// Reference to hood object, if it exists
|
||||
var/obj/item/clothing/head/hooded/hood
|
||||
|
||||
/obj/item/clothing/suit/hooded/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!alternative_mode)
|
||||
MakeHood()
|
||||
|
||||
if (!hoodtype)
|
||||
return
|
||||
AddComponent(\
|
||||
/datum/component/toggle_attached_clothing,\
|
||||
deployable_type = hoodtype,\
|
||||
equipped_slot = ITEM_SLOT_HEAD,\
|
||||
action_name = "Toggle Hood",\
|
||||
destroy_on_removal = alternative_mode,\
|
||||
parent_icon_state_suffix = hood_up_affix,\
|
||||
down_overlay_state_suffix = hood_down_overlay_suffix, \
|
||||
pre_creation_check = CALLBACK(src, PROC_REF(can_create_hood)),\
|
||||
on_created = CALLBACK(src, PROC_REF(on_hood_created)),\
|
||||
on_deployed = CALLBACK(src, PROC_REF(on_hood_up)),\
|
||||
on_removed = CALLBACK(src, PROC_REF(on_hood_down)),\
|
||||
)
|
||||
|
||||
/obj/item/clothing/suit/hooded/Destroy()
|
||||
. = ..()
|
||||
QDEL_NULL(hood)
|
||||
|
||||
/obj/item/clothing/suit/hooded/proc/MakeHood()
|
||||
if(!hood)
|
||||
var/obj/item/clothing/head/hooded/W = new hoodtype(src)
|
||||
W.suit = src
|
||||
hood = W
|
||||
|
||||
/obj/item/clothing/suit/hooded/ui_action_click()
|
||||
ToggleHood()
|
||||
|
||||
/obj/item/clothing/suit/hooded/item_action_slot_check(slot, mob/user)
|
||||
if(slot & (ITEM_SLOT_OCLOTHING|ITEM_SLOT_NECK))
|
||||
return TRUE
|
||||
|
||||
/obj/item/clothing/suit/hooded/equipped(mob/user, slot)
|
||||
if(!(slot & (ITEM_SLOT_OCLOTHING|ITEM_SLOT_NECK)))
|
||||
RemoveHood()
|
||||
hood = null
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/hooded/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
|
||||
if(visuals_only)
|
||||
MakeHood()
|
||||
ToggleHood()
|
||||
/// Override to only create the hood conditionally
|
||||
/obj/item/clothing/suit/hooded/proc/can_create_hood()
|
||||
return TRUE
|
||||
|
||||
/obj/item/clothing/suit/hooded/proc/RemoveHood()
|
||||
icon_state = "[initial(icon_state)]"
|
||||
worn_icon_state = icon_state
|
||||
zipped = FALSE
|
||||
hood_up = FALSE
|
||||
/// Called when the hood is instantiated
|
||||
/obj/item/clothing/suit/hooded/proc/on_hood_created(obj/item/clothing/head/hooded/hood)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
src.hood = hood
|
||||
RegisterSignal(hood, COMSIG_QDELETING, PROC_REF(on_hood_deleted))
|
||||
|
||||
if(hood)
|
||||
if(ishuman(hood.loc))
|
||||
var/mob/living/carbon/human/H = hood.loc
|
||||
H.transferItemToLoc(hood, src, TRUE)
|
||||
H.update_worn_oversuit()
|
||||
else
|
||||
hood.forceMove(src)
|
||||
/// Called when hood is deleted
|
||||
/obj/item/clothing/suit/hooded/proc/on_hood_deleted()
|
||||
SIGNAL_HANDLER
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
hood = null
|
||||
|
||||
if(alternative_mode)
|
||||
QDEL_NULL(hood)
|
||||
/// Called when the hood is worn
|
||||
/obj/item/clothing/suit/hooded/proc/on_hood_up(obj/item/clothing/head/hooded/hood)
|
||||
return
|
||||
|
||||
update_item_action_buttons()
|
||||
|
||||
/obj/item/clothing/suit/hooded/dropped()
|
||||
..()
|
||||
RemoveHood()
|
||||
|
||||
/obj/item/clothing/suit/hooded/proc/ToggleHood()
|
||||
if(!hood_up)
|
||||
if(!ishuman(loc))
|
||||
return
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(H.is_holding(src))
|
||||
to_chat(H, span_warning("You must be wearing [src] to put up the hood!"))
|
||||
return
|
||||
if(H.head)
|
||||
to_chat(H, span_warning("You're already wearing something on your head!"))
|
||||
return
|
||||
else
|
||||
if(alternative_mode)
|
||||
MakeHood()
|
||||
if(!H.equip_to_slot_if_possible(hood,ITEM_SLOT_HEAD,0,0,1))
|
||||
if(alternative_mode)
|
||||
RemoveHood()
|
||||
return
|
||||
hood_up = TRUE
|
||||
icon_state = "[initial(icon_state)][hood_up_affix]"
|
||||
worn_icon_state = icon_state
|
||||
zipped = TRUE // Just to maintain the same behavior, and so we avoid any bugs that otherwise relied on this behavior of zipping the jacket when bringing up the hood
|
||||
H.update_worn_oversuit()
|
||||
H.update_mob_action_buttons()
|
||||
else
|
||||
RemoveHood()
|
||||
|
||||
/obj/item/clothing/head/hooded
|
||||
var/obj/item/clothing/suit/hooded/suit
|
||||
|
||||
|
||||
/obj/item/clothing/head/hooded/Destroy()
|
||||
suit = null
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/head/hooded/dropped()
|
||||
..()
|
||||
if(suit)
|
||||
suit.RemoveHood()
|
||||
|
||||
/obj/item/clothing/head/hooded/equipped(mob/user, slot)
|
||||
..()
|
||||
if(!(slot & ITEM_SLOT_HEAD))
|
||||
if(suit)
|
||||
suit.RemoveHood()
|
||||
else
|
||||
qdel(src)
|
||||
|
||||
// Toggle exosuits for different aesthetic styles (hoodies, suit jacket buttons, etc)
|
||||
// Pretty much just a holder for `/datum/component/toggle_icon`.
|
||||
/// Called when the hood is hidden
|
||||
/obj/item/clothing/suit/hooded/proc/on_hood_down(obj/item/clothing/head/hooded/hood)
|
||||
return
|
||||
|
||||
/obj/item/clothing/suit/toggle
|
||||
/// The noun that is displayed to the user on toggle. EX: "Toggles the suit's [buttons]".
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
allowed = list()
|
||||
armor_type = /datum/armor/hooded_wintercoat
|
||||
/// The mutable_appearance of the associated hood, when it's down. Can be null when the hood is up.
|
||||
var/mutable_appearance/hood_overlay
|
||||
hood_down_overlay_suffix = "_hood"
|
||||
/// How snug are we?
|
||||
var/zipped = FALSE
|
||||
|
||||
/datum/armor/hooded_wintercoat
|
||||
bio = 10
|
||||
@@ -31,8 +32,14 @@
|
||||
/obj/item/toy,
|
||||
)
|
||||
|
||||
generate_hood_overlay()
|
||||
/obj/item/clothing/suit/hooded/wintercoat/on_hood_up(obj/item/clothing/head/hooded/hood)
|
||||
. = ..()
|
||||
zipped = TRUE
|
||||
|
||||
/// Called when the hood is hidden
|
||||
/obj/item/clothing/suit/hooded/wintercoat/on_hood_down(obj/item/clothing/head/hooded/hood)
|
||||
. = ..()
|
||||
zipped = FALSE
|
||||
|
||||
/obj/item/clothing/suit/hooded/wintercoat/examine(mob/user)
|
||||
. = ..()
|
||||
@@ -54,19 +61,6 @@
|
||||
var/mob/living/carbon/human/wearer = loc
|
||||
wearer.update_worn_oversuit()
|
||||
|
||||
|
||||
/// Helper proc to generate the `hood_overlay` associated to the wintercoat.
|
||||
/obj/item/clothing/suit/hooded/wintercoat/proc/generate_hood_overlay()
|
||||
hood_overlay = mutable_appearance(initial(worn_icon), "[initial(icon_state)]_hood", -SUIT_LAYER)
|
||||
|
||||
|
||||
/obj/item/clothing/suit/hooded/wintercoat/worn_overlays(mutable_appearance/standing, isinhands)
|
||||
. = ..()
|
||||
|
||||
if(!isinhands && !hood_up)
|
||||
. += hood_overlay
|
||||
|
||||
|
||||
/obj/item/clothing/head/hooded/winterhood
|
||||
name = "winter hood"
|
||||
desc = "A cozy winter hood attached to a heavy winter jacket."
|
||||
@@ -649,6 +643,7 @@
|
||||
/obj/item/clothing/suit/hooded/wintercoat/custom
|
||||
name = "tailored winter coat"
|
||||
desc = "A heavy jacket made from 'synthetic' animal furs, with custom colors."
|
||||
hood_down_overlay_suffix = ""
|
||||
greyscale_colors = "#ffffff#ffffff#808080#808080#808080#808080"
|
||||
greyscale_config = /datum/greyscale_config/winter_coats
|
||||
greyscale_config_worn = /datum/greyscale_config/winter_coats/worn
|
||||
@@ -658,13 +653,15 @@
|
||||
//In case colors are changed after initialization
|
||||
/obj/item/clothing/suit/hooded/wintercoat/custom/set_greyscale(list/colors, new_config, new_worn_config, new_inhand_left, new_inhand_right)
|
||||
. = ..()
|
||||
if(hood)
|
||||
var/list/coat_colors = SSgreyscale.ParseColorString(greyscale_colors)
|
||||
var/list/new_coat_colors = coat_colors.Copy(1,4)
|
||||
hood.set_greyscale(new_coat_colors) //Adopt the suit's grayscale coloring for visual clarity.
|
||||
if(!hood)
|
||||
return
|
||||
var/list/coat_colors = SSgreyscale.ParseColorString(greyscale_colors)
|
||||
var/list/new_coat_colors = coat_colors.Copy(1,4)
|
||||
hood.set_greyscale(new_coat_colors) //Adopt the suit's grayscale coloring for visual clarity.
|
||||
hood.update_slot_icon()
|
||||
|
||||
//But also keep old method in case the hood is (re-)created later
|
||||
/obj/item/clothing/suit/hooded/wintercoat/custom/MakeHood()
|
||||
/obj/item/clothing/suit/hooded/wintercoat/custom/on_hood_created(obj/item/clothing/head/hooded/hood)
|
||||
. = ..()
|
||||
var/list/coat_colors = (SSgreyscale.ParseColorString(greyscale_colors))
|
||||
var/list/new_coat_colors = coat_colors.Copy(1,4)
|
||||
|
||||
@@ -445,6 +445,7 @@
|
||||
set_wearer(user)
|
||||
|
||||
/obj/item/mod/control/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
|
||||
. = ..()
|
||||
quick_activation()
|
||||
|
||||
/obj/item/mod/control/doStrip(mob/stripper, mob/owner)
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
/datum/unit_test/ablative_hood_hud/Run()
|
||||
var/mob/living/carbon/human/person = allocate(/mob/living/carbon/human/consistent)
|
||||
var/obj/item/clothing/suit/hooded/ablative/coat = allocate(/obj/item/clothing/suit/hooded/ablative)
|
||||
var/datum/component/toggle_attached_clothing/hood = coat.GetComponent(/datum/component/toggle_attached_clothing)
|
||||
person.equip_to_slot(coat, ITEM_SLOT_OCLOTHING)
|
||||
TEST_ASSERT(!HAS_TRAIT(person, TRAIT_SECURITY_HUD), "Person already had a sechud before trying to equip the ablative hood.")
|
||||
coat.ToggleHood()
|
||||
hood.toggle_deployable()
|
||||
TEST_ASSERT(HAS_TRAIT(person, TRAIT_SECURITY_HUD), "Person toggled the ablative hood but didn't gain a sechud.")
|
||||
coat.ToggleHood()
|
||||
hood.toggle_deployable()
|
||||
TEST_ASSERT(!HAS_TRAIT(person, TRAIT_SECURITY_HUD), "Person lowered their ablative hood but still has a sechud.")
|
||||
|
||||
// Check that player doesn't gain sec hud if the hood is toggled when already wearing a helmet
|
||||
@@ -17,9 +18,10 @@
|
||||
/datum/unit_test/ablative_hood_hud_with_helmet/Run()
|
||||
var/mob/living/carbon/human/person = allocate(/mob/living/carbon/human/consistent)
|
||||
var/obj/item/clothing/suit/hooded/ablative/coat = allocate(/obj/item/clothing/suit/hooded/ablative)
|
||||
var/datum/component/toggle_attached_clothing/hood = coat.GetComponent(/datum/component/toggle_attached_clothing)
|
||||
var/obj/item/clothing/head/helmet/hat = allocate(/obj/item/clothing/head/helmet)
|
||||
person.equip_to_slot(coat, ITEM_SLOT_OCLOTHING)
|
||||
person.equip_to_slot(hat, ITEM_SLOT_HEAD)
|
||||
TEST_ASSERT(!HAS_TRAIT(person, TRAIT_SECURITY_HUD), "Person already had a sechud before trying to equip the ablative hood.")
|
||||
coat.ToggleHood()
|
||||
hood.toggle_deployable()
|
||||
TEST_ASSERT(!HAS_TRAIT(person, TRAIT_SECURITY_HUD), "Person has gained a sechud from toggling the ablative hood despite already wearing a helmet.")
|
||||
|
||||
@@ -110,8 +110,9 @@
|
||||
equipped.eye_color_right = BLOODCULT_EYE
|
||||
equipped.update_body()
|
||||
|
||||
var/obj/item/clothing/suit/hooded/hooded = locate() in equipped
|
||||
hooded.ToggleHood()
|
||||
var/obj/item/clothing/suit/hooded/hooded = equipped.wear_suit
|
||||
var/datum/component/toggle_attached_clothing/hood = hooded.GetComponent(/datum/component/toggle_attached_clothing)
|
||||
hood.toggle_deployable() // start unhooded
|
||||
|
||||
|
||||
/obj/effect/mob_spawn/corpse/human/clock_cultist
|
||||
|
||||
+16
-21
@@ -10,36 +10,31 @@
|
||||
greyscale_config_worn = /datum/greyscale_config/winter_coat_worn
|
||||
greyscale_colors = "#666666#CCBBAA#0000FF"
|
||||
flags_1 = IS_PLAYER_COLORABLE_1
|
||||
hood_overlay = null // for this particular coat, the hood already is drawn onto the base sprite so we won't use this
|
||||
//hood_down_overlay_suffix = "" future maintainers -- uncomment this when my toil gets undone by PR 22129.
|
||||
// This should stop the hood overlay and negate the need for the two proc overrides below, as well as the 'hood_overlay = null'
|
||||
hood_down_overlay_suffix = ""
|
||||
/// Whether the hood is flipped up
|
||||
var/hood_up = FALSE
|
||||
|
||||
// NO HOOD OVERLAYS EVER
|
||||
/obj/item/clothing/suit/hooded/wintercoat/colourable/generate_hood_overlay()
|
||||
return
|
||||
|
||||
// We are going to temporarily act as if the hood is up when it's down
|
||||
// so that we don't ever add the hood_overlay in the parent proc.
|
||||
// I cannot stress how much this coat hates hood overlays. We must avoid them at all costs.
|
||||
/obj/item/clothing/suit/hooded/wintercoat/colourable/worn_overlays(mutable_appearance/standing, isinhands)
|
||||
if(!hood_up)
|
||||
hood_up = TRUE
|
||||
. = ..()
|
||||
else
|
||||
return ..()
|
||||
/// Called when the hood is worn
|
||||
/obj/item/clothing/suit/hooded/wintercoat/colourable/on_hood_up(obj/item/clothing/head/hooded/hood)
|
||||
hood_up = TRUE
|
||||
|
||||
/// Called when the hood is hidden
|
||||
/obj/item/clothing/suit/hooded/wintercoat/colourable/on_hood_down(obj/item/clothing/head/hooded/hood)
|
||||
hood_up = FALSE
|
||||
|
||||
//In case colors are changed after initialization
|
||||
/obj/item/clothing/suit/hooded/wintercoat/colourable/set_greyscale(list/colors, new_config, new_worn_config, new_inhand_left, new_inhand_right)
|
||||
. = ..()
|
||||
if(hood)
|
||||
var/list/coat_colors = SSgreyscale.ParseColorString(greyscale_colors)
|
||||
var/list/new_coat_colors = coat_colors.Copy(1,3)
|
||||
hood.set_greyscale(new_coat_colors) //Adopt the suit's grayscale coloring for visual clarity.
|
||||
|
||||
if(!hood)
|
||||
return
|
||||
|
||||
var/list/coat_colors = SSgreyscale.ParseColorString(greyscale_colors)
|
||||
var/list/new_coat_colors = coat_colors.Copy(1,3)
|
||||
hood.set_greyscale(new_coat_colors) //Adopt the suit's grayscale coloring for visual clarity.
|
||||
|
||||
//But also keep old method in case the hood is (re-)created later
|
||||
/obj/item/clothing/suit/hooded/wintercoat/colourable/MakeHood()
|
||||
/obj/item/clothing/suit/hooded/wintercoat/colourable/on_hood_created(obj/item/clothing/head/hooded/hood)
|
||||
. = ..()
|
||||
var/list/coat_colors = (SSgreyscale.ParseColorString(greyscale_colors))
|
||||
var/list/new_coat_colors = coat_colors.Copy(1,3)
|
||||
|
||||
@@ -1116,6 +1116,7 @@
|
||||
#include "code\datums\components\tether.dm"
|
||||
#include "code\datums\components\thermite.dm"
|
||||
#include "code\datums\components\tippable.dm"
|
||||
#include "code\datums\components\toggle_attached_clothing.dm"
|
||||
#include "code\datums\components\toggle_suit.dm"
|
||||
#include "code\datums\components\transforming.dm"
|
||||
#include "code\datums\components\trapdoor.dm"
|
||||
|
||||
Reference in New Issue
Block a user