Files
Bubberstation/code/datums/components/takes_reagent_appearance.dm
SkyratBot 067188d366 [MIRROR] Micro-optimize qdel by only permitting one parameter [MDB IGNORE] (#25889)
* Micro-optimize qdel by only permitting one parameter (#80628)

Productionizes #80615.

The core optimization is this:

```patch
-	var/hint = to_delete.Destroy(arglist(args.Copy(2))) // Let our friend know they're about to get fucked up.
+	var/hint = to_delete.Destroy(force) // Let our friend know they're about to get fucked up.
```

We avoid a heap allocation in the form of copying the args over to a new
list. A/B testing shows this results in 33% better overtime, and in a
real round shaving off a full second of self time and 0.4 seconds of
overtime--both of these would be doubled in the event this is merged as
the new proc was only being run 50% of the time.

* Micro-optimize qdel by only permitting one parameter

---------

Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
2023-12-29 14:41:12 +00:00

168 lines
5.4 KiB
Plaintext

/**
* ## "Takes reagent appearance" Component
*
* Bit of a mouthful, but when applied to an item that can hold reagents (primarily reagent containers),
* said item will take on an appearance based on the majority share reagent inside
*
* This is more than just "changing the color a bit" or "applies an overlay", this is
* an entire icon / icon state / name change, making it look like a different item entirely
*
* This is done by cross-referencing the glass style datums. See [/datum/glass_style] for more information about that.
*
* An example usage is bartender mixed drinks - each reagent gets its own fancy drink sprite
*/
/datum/component/takes_reagent_appearance
/// The type to compare against the glass_style's required_container_type. The parent's type by default.
var/base_container_type
/// Icon file when attached to the item
var/icon_pre_change
/// Icon state when attached to the item
var/icon_state_pre_change
/// Optional callback invoked when when the item's appearance is changed
var/datum/callback/on_icon_changed
/// Optional callback invoked when our item has its appearance reverted to default
var/datum/callback/on_icon_reset
/datum/component/takes_reagent_appearance/Initialize(
datum/callback/on_icon_changed,
datum/callback/on_icon_reset,
base_container_type,
)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
var/obj/item/item_parent = parent
if(isnull(item_parent.reagents))
return COMPONENT_INCOMPATIBLE
icon_pre_change = item_parent.icon
icon_state_pre_change = item_parent.base_icon_state || item_parent.icon_state
src.on_icon_changed = on_icon_changed
src.on_icon_reset = on_icon_reset
src.base_container_type = base_container_type || parent.type
/datum/component/takes_reagent_appearance/Destroy(force)
on_icon_changed = null
on_icon_reset = null
return ..()
/datum/component/takes_reagent_appearance/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_UPDATE_APPEARANCE, PROC_REF(on_update_appearance))
/datum/component/takes_reagent_appearance/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_ATOM_UPDATE_APPEARANCE)
if(QDELING(parent))
return
var/obj/item/item_parent = parent
item_parent.name = initial(item_parent.name)
item_parent.desc = initial(item_parent.desc)
item_parent.icon = icon_pre_change
item_parent.icon_state = icon_state_pre_change
on_icon_reset?.Invoke()
item_parent.update_appearance()
/// Signal proc for [COMSIG_ATOM_UPDATE_APPEARANCE]
/// We hook into the update appearance proc to perform our own update based on our glass style
/// Preventing any further updates down the line on successes
/datum/component/takes_reagent_appearance/proc/on_update_appearance(datum/source, updates)
SIGNAL_HANDLER
. = NONE
var/datum/glass_style/main_style = get_main_reagent_style()
if(updates & UPDATE_NAME)
. |= update_name(main_style)
if(updates & UPDATE_DESC)
. |= update_desc(main_style)
if(updates & UPDATE_ICON)
. |= update_icon(main_style)
return .
/**
* Performs the name update.
*
* * Returns [COMSIG_ATOM_NO_UPDATE_NAME] if one was complete
* * Returns [NONE] if nothing happened
* * Returns [NONE] if the name was reset to initial state
*/
/datum/component/takes_reagent_appearance/proc/update_name(datum/glass_style/style)
if(HAS_TRAIT(parent, TRAIT_WAS_RENAMED))
return NONE
var/obj/item/item_parent = parent
if(isnull(style))
// no style (reset)
item_parent.name = initial(item_parent.name)
else if(style.name)
// style
style.set_name(item_parent)
return COMSIG_ATOM_NO_UPDATE_NAME
return NONE
/**
* Performs the description update.
*
* * Returns [COMSIG_ATOM_NO_UPDATE_DESC] if one was complete
* * Returns [NONE] if nothing happened
* * Returns [NONE] if the description was reset to initial state
*/
/datum/component/takes_reagent_appearance/proc/update_desc(datum/glass_style/style)
if(HAS_TRAIT(parent, TRAIT_WAS_RENAMED))
return NONE
var/obj/item/item_parent = parent
if(isnull(style))
// no style (reset)
item_parent.desc = initial(item_parent.desc)
else if(style.desc)
// style
style.set_desc(item_parent)
return COMSIG_ATOM_NO_UPDATE_DESC
return NONE
/**
* Performs the icon update.
*
* * Returns [COMSIG_ATOM_NO_UPDATE_ICON] if an icon or icon state ocurred
* * Returns [NONE] if the icon or icon state was reset to base state
*/
/datum/component/takes_reagent_appearance/proc/update_icon(datum/glass_style/style)
var/obj/item/item_parent = parent
if(isnull(style))
// no style (reset)
item_parent.icon = icon_pre_change
item_parent.icon_state = icon_state_pre_change
else if(style.icon && style.icon_state)
// style
style.set_appearance(item_parent)
on_icon_changed?.InvokeAsync(style)
return COMSIG_ATOM_NO_UPDATE_ICON_STATE
// Reset gets invoked regardless, as further updates may "reset" the icon yet
on_icon_reset?.InvokeAsync()
return NONE
/**
* Gets the correspinding style based on the parent's state and reagents within
*
* * Returns null if its reagents are empty
* * Returns null if no majority reagent was found
* * Otherwise returns a glass style datum
*/
/datum/component/takes_reagent_appearance/proc/get_main_reagent_style()
RETURN_TYPE(/datum/glass_style)
var/obj/item/item_parent = parent
if(item_parent.reagents.total_volume <= 0)
return null
var/datum/reagent/main_reagent = item_parent.reagents.get_master_reagent()
if(isnull(main_reagent))
return null
return GLOB.glass_style_singletons[base_container_type][main_reagent.type]