Files
Paradise/code/datums/elements/decal_element.dm
Chap f0ac13b80b [FEATURE] Decal painter (#27036)
* Add_Decal_Painter

* Seperate TGUI file

* Decal painter DmIcon

* Fix some merge errors

* I don't know why this merge is so incredibly fucked

* Update TGUI bundle

* I swear to god

* Fixes DmIcon

* TGUI bundle build

* Remove implicit var

* Final cleanup

* Add decal_painter sprite

* Update code/game/objects/items/devices/painter/decal_painter.dm

Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Signed-off-by: Chap <erwin@lombok.demon.nl>

* Add helper proc for deleting all components of a type from a datum

* Added comments to cycle_style

* Elementized decal fixes

* Use the new decal system

* TGUI bundle

---------

Signed-off-by: Chap <erwin@lombok.demon.nl>
Co-authored-by: Adrer <adrermail@gmail.com>
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
2024-11-29 14:30:48 +00:00

178 lines
6.9 KiB
Plaintext

// NOTE:
// This is an incredibly piecemeal port of /tg/'s decal element.
// It does not include several pieces of functionality that exist in /tg/.
//
// Namely:
// - It does not support smoothing decals
// - It does not send a signal when a decal is detached (used for trapdoors on /tg/)
// - It does not support custom plane configuration as this behavior seems primarily concerned with multi-z
/datum/element/decal
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH_ON_HOST_DESTROY
argument_hash_start_idx = 2
/// Whether this decal can be cleaned.
var/cleanable
/// A description this decal appends to the target's examine message.
var/description
/// If true this was initialized with no set direction - will follow the parent dir.
var/directional
/// The base icon state that this decal was initialized with.
var/base_icon_state
/// The overlay applied by this decal to the target.
var/mutable_appearance/pic
/datum/element/decal/Attach(atom/target, _icon, _icon_state, _dir, _layer=TURF_LAYER, _alpha=255, _color, _cleanable=FALSE, _description, mutable_appearance/_pic)
. = ..()
if(!isatom(target))
return ELEMENT_INCOMPATIBLE
if(_pic)
pic = _pic
else if(!generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha, target))
return ELEMENT_INCOMPATIBLE
description = _description
cleanable = _cleanable
directional = _dir
base_icon_state = _icon_state
RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(apply_overlay), TRUE)
if(target.initialized)
target.update_appearance(UPDATE_OVERLAYS) //could use some queuing here now maybe.
else
RegisterSignal(target, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE, PROC_REF(late_update_icon), TRUE)
if(isitem(target))
INVOKE_ASYNC(target, TYPE_PROC_REF(/obj/item/, update_slot_icon), TRUE)
if(_dir)
RegisterSignal(target, list(COMSIG_ATOM_DECALS_ROTATING, COMSIG_ATOM_GET_DECALS), PROC_REF(get_decals), TRUE)
SSdcs.RegisterSignal(target, COMSIG_ATOM_DIR_CHANGE, TYPE_PROC_REF(/datum/controller/subsystem/processing/dcs, rotate_decals), override=TRUE)
if(_cleanable)
RegisterSignal(target, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_react), TRUE)
if(_description)
RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(examine), TRUE)
RegisterSignal(target, COMSIG_TURF_ON_SHUTTLE_MOVE, PROC_REF(shuttle_move_react), TRUE)
/// Remove old decals and apply new decals after rotation as necessary
/datum/controller/subsystem/processing/dcs/proc/rotate_decals(datum/source, old_dir, new_dir)
SIGNAL_HANDLER // COMSIG_ATOM_DIR_CHANGE
if(old_dir == new_dir)
return
var/list/datum/element/decal/old_decals = list() //instances
SEND_SIGNAL(source, COMSIG_ATOM_DECALS_ROTATING, old_decals)
if(!length(old_decals))
UnregisterSignal(source, COMSIG_ATOM_DIR_CHANGE)
return
var/list/resulting_decals_params = list() // param lists
for(var/datum/element/decal/rotating as anything in old_decals)
resulting_decals_params += list(rotating.get_rotated_parameters(old_dir,new_dir))
//Instead we could generate ids and only remove duplicates to save on churn on four-corners symmetry ?
for(var/datum/element/decal/decal in old_decals)
decal.Detach(source)
for(var/result in resulting_decals_params)
source.AddElement(/datum/element/decal, result["icon"], result["icon_state"], result["dir"], result["layer"], result["alpha"], result["color"], result["cleanable"], result["desc"])
/datum/element/decal/proc/get_rotated_parameters(old_dir,new_dir)
var/rotation = 0
if(directional) //Even when the dirs are the same rotation is coming out as not 0 for some reason
rotation = SIMPLIFY_DEGREES(dir2angle(new_dir)-dir2angle(old_dir))
new_dir = turn(pic.dir,-rotation)
return list(
"icon" = pic.icon,
"icon_state" = base_icon_state,
"dir" = new_dir,
"plane" = pic.plane,
"layer" = pic.layer,
"alpha" = pic.alpha,
"color" = pic.color,
"cleanable" = cleanable,
"desc" = description
)
/datum/element/decal/proc/late_update_icon(atom/source)
SIGNAL_HANDLER // COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE
if(istype(source) && !(source.flags_2 & DECAL_INIT_UPDATE_EXPERIENCED_2))
source.flags_2 |= DECAL_INIT_UPDATE_EXPERIENCED_2
source.update_appearance(UPDATE_OVERLAYS)
UnregisterSignal(source, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE)
/**
* If the decal was not given an appearance, it will generate one based on the other given arguments.
* element won't be compatible if it cannot do either
* all args are fed into creating an image, they are byond vars for images you'll recognize in the byond docs
* (except source, source is the object whose appearance we're copying.)
*/
/datum/element/decal/proc/generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha, source)
if(!_icon || !_icon_state)
return FALSE
var/temp_image = image(_icon, null, _icon_state, _layer, _dir)
pic = new(temp_image)
pic.color = _color
pic.alpha = _alpha
return TRUE
/datum/element/decal/Detach(atom/source)
UnregisterSignal(source, list(
COMSIG_ATOM_DIR_CHANGE,
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_PARENT_EXAMINE,
COMSIG_ATOM_UPDATE_OVERLAYS,
COMSIG_TURF_ON_SHUTTLE_MOVE,
COMSIG_ATOM_DECALS_ROTATING
))
SSdcs.UnregisterSignal(source, COMSIG_ATOM_DIR_CHANGE)
source.update_appearance(UPDATE_OVERLAYS)
if(isitem(source))
INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item/, update_slot_icon))
return ..()
/datum/element/decal/proc/apply_overlay(atom/source)
SIGNAL_HANDLER // COMSIG_ATOM_UPDATE_OVERLAYS
source.add_overlay(pic)
// TODO: Fix this disgusting hack
//
// `COMSIG_ATOM_UPDATE_OVERLAYS` is sent at the end of
// /atom/proc/update_icon's stanza for updating overlays, instead
// somewhere useful, like, during it. /tg/ handles this by sending
// a list of overlays with the signal, allowing receivers to add to
// the list, instead of returning their own.
//
// This is much saner and more flexible, but would require refactoring
// many many uses of update_overlay() across the code base, which is left
// as an exercise for the next poor sap to touch this code (probably me).
if(source.managed_overlays && !islist(source.managed_overlays))
source.managed_overlays = list(source.managed_overlays, pic)
else
LAZYDISTINCTADD(source.managed_overlays, pic)
/datum/element/decal/proc/clean_react(datum/source, clean_types)
SIGNAL_HANDLER // COMSIG_COMPONENT_CLEAN_ACT
if(clean_types & cleanable)
Detach(source)
return COMPONENT_CLEANED
return NONE
/datum/element/decal/proc/examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER // COMSIG_PARENT_EXAMINE
examine_list += description
/datum/element/decal/proc/shuttle_move_react(datum/source, turf/new_turf)
SIGNAL_HANDLER // COMSIG_TURF_ON_SHUTTLE_MOVE
if(new_turf == source)
return
Detach(source)
new_turf.AddElement(type, pic.icon, base_icon_state, directional, pic.layer, pic.alpha, pic.color, cleanable, description)
/datum/element/decal/proc/get_decals(datum/source, list/datum/element/decal/rotating)
SIGNAL_HANDLER // COMSIG_ATOM_DECALS_ROTATING
rotating += src