Files
Bubberstation/code/datums/components/toggle_attached_clothing.dm
EnterTheJake 6faa37853b Void Heretic Rework: You Can (Not) Heat Up. (#85728)
## About The Pull Request

Reworks most of the Void Heretic kit.

All Void spells now apply a stacking debuff that makes you gradually
colder.

Void Blast has been replaced with a new spell "Void Conduit" and a new
Side knowledge spell, "Void Prison" has been introduced.

 Waltz At The End Of Time has been completely overhauled.

Lastly Void Blade has been resprited along with the overlay of Void
Chill, check any of the linked videos to see the new look.

All new sprites have been kindly made by OrcaCora.

## Why It's Good For The Game


Void path is *Supposedly* themed around the cold of space but doesn't
have any tools to live up to the fantasy of being an harbringer of the
Void whose main goal is to freeze the station to an icicle.

Then there's also the issue of Void being utterly underwhelming compared
to his neighbors, Cold is way too easy to treat as a status effect, so
much so, a simple cup of coffee can utterly shutdown the void storm,
which is just unacceptable.

# **Changes**

- **Void Chill**: Is now a stacking debuff, each stack slows movement
speed by a percentage while lowering body temperature, upon reaching the
cap, which is 5 stacks, the victim cannot heat up anymore, all void
Spells apply 1 stack of the debuff, with the exception of the grasp
applying 5 (2 for the mark and 3 for the detonation) and the blade
upgrade applying 2.


**Reasoning**: Void chill currently suffers from being completely
shutdown by sipping tea, coffee or having the COLD_RESIST trait,with
this change you can still negate the damage and the slowdown you get
from being cold but not the slowdown from the debuff itself.

- **Aristocrat's Way**: Now grants no slip on ice and water tiles on
cold or depressurized turfs

**Reasoning:** Since the rework revolves around making Void heretic more
involved in freezing the station, they should be able to move in their
domain without getting punished for it.

- **Void Cloak:** Can now be toggled on and off to make the cloak
visible or invisible; when the cloak is visible it grants low pressure
immunity trait.

**Reasoning:** Not having pressure resistance as a Void Heretic just
sucks, as you are clearly intended to somewhat go into space at some
point; giving it to the cloak is a good compromise since you sacrifice
better protection (***the armor values of the cloak are pitiful***) for
utility.


- **Replaces "Void Blast" with a new spell, "Void Conduit"**.

**Void Conduit:** Opens a gate to the Void; it releases an intermittent
pulse that damages windows and airlocks and applies a stack of void
chill to non heretics, Heretics are granted the low pressure resistance
trait.
**Reasoning**: I like the idea behind Void Blast; unfortunately, the
spell has limited uses if an area isn't already spaced, which is hard to
do considering the current kit of Void Heretic doesn't provide anything
to help in that regard.

I wanted to give Void Heretic a tool to turn any area of their choosing
into their ideal habitat. Not amazing for quick assassinations, good if
you are anticipating a fight or want to simply expand your domain, the
added Trait might seem redundant, but let's not forget that the void
cloak is still a side knowledge, I don't want newbie heretics to space
an area and accidentally killing themselves because of it.
 
Video Demonstration: https://www.youtube.com/watch?v=nhPdj1hIgSI

- **New side knowledge: "Void Prison."**

**Void Prison:** It makes the target invulnerable and unable to do
anything for 10 seconds , when the spell ends, it applies a few stacks
of void chill, cannot be self-cast.

It occupies the same slot of Blood Siphon (inbetween Raw Ritual and Void
Phase.)


Video Demonstration: https://www.youtube.com/watch?v=nKZd8aEcZFw

**Reasoning**: Void is technically meant to be an assassination path and
not really apt at tackling multiple opponents, this spell might come
into clutch if you are outnumbered, or simply want some breathing room
from your pursuers.

- **Void Jaunt and Void Pull**

Cooldown respectively reduced to 25 and 30 seconds, down from 30 and 40.

**Reasoning:** I felt that the cooldown on these was a bit too high
overall; as it stands, Void Phase cooldown is twice as long as Ashen
passage, one of the best and arguably most sidepathed spells in the
game.

Granted phase is faster and has more range but I don't think it
justifies its cooldown being this long.

Void pull could also use slightly less cooldown, being the very last
spell you unlock and offering a simple melee knockdown.

- **Seeking Blade:** Now applies 2 stacks of void chill per hit.

**Reasoning:** Seeking blade is a bit underwhelming for being the final
blade upgrade, teleporting to a target you just stunned at melee range
is incredibly niche, now that Void chill is a stacking debuff we can
probably just slap it on the blade itself.


- **Waltz At The End Of Time:** The Heretic becomes weightless and able
to levitate around, (carp movement essentialy).

The heavy storm is no longer bound to the room the heretic is in but is
now an aura around them, this does a few things.

1) Releases a pulse that depressurizes areas, shatters windows, airlocks
and firelocks, and applies a stack of void chill to non heretics, the
passive burn and oxy damage effect has been removed.

2) Grants to the Heretic projectile deflection.


**Reasoning:** Void Has the reputation of having the weakest ascension
in the game, which it's hard to disagree on considering you can
completely neutralize the effects of the storm by simply sipping tea and
how generally little it does overall.

It's also kind of strange that you have this massive Eldritch storm
ravaging the station and for it to have no effects on its atmosphere,
infrastructure, or projectiles whatsoever.

Video Demonstration: https://www.youtube.com/watch?v=1_blr20-hgA


## Changelog

🆑
add: New Heretic Side Knowledge, Void Prison.
add: New Void Spell Void Conduit has now replaced Void Blast.
balance: Void Chill is now a stacking debuff, upon reaching the cap,
makes the target unable to heat up.
balance: Aristocrat's way now grants immunity to ice and water slips on
cold turfs.
balance:  Void Cloak now grants low pressure resistance when visible.
balance: Void Phase and Void pull have received a minor CD reduction.
balance: Seeking Blade now applies a couple of stacks of void chill.
balance: Void Heretic Ascension has been overhauled, it's now protects
the heretic from projectiles, destroys windows and airlocks and applies
void chills to non heretics.
image: Void Blade and Void Chill have received some new sprites.
/🆑

---------

Co-authored-by: Xander3359 <66163761+Xander3359@users.noreply.github.com>
Co-authored-by: necromanceranne <40847847+necromanceranne@users.noreply.github.com>
2024-09-10 22:21:04 +00:00

218 lines
8.0 KiB
Plaintext

/**
* 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_POST_UNEQUIP, PROC_REF(remove_deployable))
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)
unequip_deployable()
QDEL_NULL(deployable)
QDEL_NULL(toggle_action)
undeployed_overlay = null
pre_creation_check = null
on_created = null
on_deployed = null
on_removed = null
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()
SIGNAL_HANDLER
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)
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)