Files
Bubberstation/code/datums/components/cleaner.dm
SkyratBot 59dc5c36b7 [MIRROR] Cleans up the fallout from plane cube [MDB IGNORE] (#17003)
* Cleans up the fallout from plane cube (#70235)

* Cleans up the fallout from plane cube

Alright.
Makes cleaning bubbles respect planes
Adds support for updating overlays on move, fixing an issue with pointing at items
Adds better error messages for failing to provide args for mutable_appearance()
Fixes a bug where string overlays were not respecting insertion order

* Adds documentation for offset spokesman and offset_const

* Better stack trace

* Removes some redundant uses of cached MAs

At this scale, attempting to cache MAs like this has 0 impact on anything
And just makes things more messy then they need to be

* ensures fullscreen objects START offset, so things are always proper

* ensures chatmessages always have the right offset

* fixes compile

* whoops, the above lighting plane should actually be ABOVE the lighting plane

* fixes compile, also cleans up the fire overlay a tad

* Adds a unit test for plane masters that are shrunk by multiz being double shrunk

This is slightly hacky because of how I'm handing the plane master
group, but it's not THAT bad, and gives me some real good coverage

* Properly targets the seethrough plane at the game world plate. This fixes unit tests, and also just makes more sense

* whoops

* oh

* adds datum support for allocate(), cleans up a harddel from testing

* Makes camera chunks index at 1, and also makes them support non powers of two sizes, since that was unneeded

* fixes runtime in allocate

* Cleans up the fallout from plane cube

* liquid tweaks

* oop

Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: tastyfish <crazychris32@gmail.com>
2022-10-19 19:43:05 -04:00

146 lines
7.1 KiB
Plaintext

/**
* Component that can be used to clean things.
* Takes care of duration, cleaning skill and special cleaning interactions.
* A callback can be set by the datum holding the cleaner to add custom functionality.
* Soap uses a callback to decrease the amount of uses it has left after cleaning for example.
*/
/datum/component/cleaner
/// The time it takes to clean something, without reductions from the cleaning skill modifier.
var/base_cleaning_duration
/// Offsets the cleaning duration modifier that you get from your cleaning skill, the duration won't be modified to be more than the base duration.
var/skill_duration_modifier_offset
/// Determines what this cleaner can wash off, [the available options are found here](code/__DEFINES/cleaning.html).
var/cleaning_strength
/// Gets called before you start cleaning, returns TRUE/FALSE whether the clean should actually wash tiles, or DO_NOT_CLEAN to not clean at all.
var/datum/callback/pre_clean_callback
/// Gets called when something is successfully cleaned.
var/datum/callback/on_cleaned_callback
/datum/component/cleaner/Initialize(
base_cleaning_duration = 3 SECONDS,
skill_duration_modifier_offset = 0,
cleaning_strength = CLEAN_SCRUB,
datum/callback/pre_clean_callback = null,
datum/callback/on_cleaned_callback = null,
)
src.base_cleaning_duration = base_cleaning_duration
src.skill_duration_modifier_offset = skill_duration_modifier_offset
src.cleaning_strength = cleaning_strength
src.pre_clean_callback = pre_clean_callback
src.on_cleaned_callback = on_cleaned_callback
/datum/component/cleaner/Destroy(force, silent)
if(pre_clean_callback)
QDEL_NULL(pre_clean_callback)
if(on_cleaned_callback)
QDEL_NULL(on_cleaned_callback)
return ..()
/datum/component/cleaner/RegisterWithParent()
if(isbot(parent))
RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, .proc/on_unarmed_attack)
return
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/on_afterattack)
/datum/component/cleaner/UnregisterFromParent()
if(isbot(parent))
UnregisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK)
return
UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK)
/**
* Handles the COMSIG_LIVING_UNARMED_ATTACK signal used for cleanbots
* Redirects to afterattack, while setting parent (the bot) as user.
*/
/datum/component/cleaner/proc/on_unarmed_attack(datum/source, atom/target, proximity_flags, modifiers)
SIGNAL_HANDLER
return on_afterattack(source, target, parent, proximity_flags, modifiers)
/**
* Handles the COMSIG_ITEM_AFTERATTACK signal by calling the clean proc.
*
* Arguments
* * source the datum that sent the signal to start cleaning
* * target the thing being cleaned
* * user the person doing the cleaning
* * clean_target set this to false if the target should not be washed and if experience should not be awarded to the user
*/
/datum/component/cleaner/proc/on_afterattack(datum/source, atom/target, mob/user, proximity_flag, click_parameters)
SIGNAL_HANDLER
if(!proximity_flag)
return
var/clean_target
if(pre_clean_callback)
clean_target = pre_clean_callback?.Invoke(source, target, user)
if(clean_target == DO_NOT_CLEAN)
return
INVOKE_ASYNC(src, .proc/clean, source, target, user, clean_target) //signal handlers can't have do_afters inside of them
/**
* Cleans something using this cleaner.
* The cleaning duration is modified by the cleaning skill of the user.
* Successfully cleaning gives cleaning experience to the user and invokes the on_cleaned_callback.
*
* Arguments
* * source the datum that sent the signal to start cleaning
* * target the thing being cleaned
* * user the person doing the cleaning
* * clean_target set this to false if the target should not be washed and if experience should not be awarded to the user
*/
/datum/component/cleaner/proc/clean(datum/source, atom/target, mob/living/user, clean_target = TRUE)
if(!HAS_TRAIT(target, CURRENTLY_CLEANING)) //add the trait and overlay
ADD_TRAIT(target, CURRENTLY_CLEANING, src)
// We need to update our planes on overlay changes
RegisterSignal(target, COMSIG_MOVABLE_Z_CHANGED, .proc/cleaning_target_moved)
var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, GAME_PLANE)
var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, ABOVE_GAME_PLANE)
if(target.plane > low_bubble.plane) //check if the higher overlay is necessary
target.add_overlay(high_bubble)
else if(target.plane == low_bubble.plane)
if(target.layer > low_bubble.layer)
target.add_overlay(high_bubble)
else
target.add_overlay(low_bubble)
else //(target.plane < low_bubble.plane)
target.add_overlay(low_bubble)
//set the cleaning duration
var/cleaning_duration = base_cleaning_duration
if(user.mind) //higher cleaning skill can make the duration shorter
//offsets the multiplier you get from cleaning skill, but doesn't allow the duration to be longer than the base duration
cleaning_duration = (cleaning_duration * min(user.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER)+skill_duration_modifier_offset, 1))
//do the cleaning
user.visible_message(span_notice("[user] starts to clean [target]!"), span_notice("You start to clean [target]..."))
if(do_after(user, cleaning_duration, target = target))
user.visible_message(span_notice("[user] finishes cleaning [target]!"), span_notice("You finish cleaning [target]."))
if(clean_target)
for(var/obj/effect/decal/cleanable/cleanable_decal in target) //it's important to do this before you wash all of the cleanables off
user.mind?.adjust_experience(/datum/skill/cleaning, round(cleanable_decal.beauty / CLEAN_SKILL_BEAUTY_ADJUSTMENT))
if(target.wash(cleaning_strength))
user.mind?.adjust_experience(/datum/skill/cleaning, round(CLEAN_SKILL_GENERIC_WASH_XP))
on_cleaned_callback?.Invoke(source, target, user)
//remove the cleaning overlay
var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, GAME_PLANE)
var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, ABOVE_GAME_PLANE)
target.cut_overlay(low_bubble)
target.cut_overlay(high_bubble)
REMOVE_TRAIT(target, CURRENTLY_CLEANING, src)
/datum/component/cleaner/proc/cleaning_target_moved(atom/movable/source, turf/old_turf, turf/new_turf, same_z_layer)
if(same_z_layer)
return
// First, get rid of the old overlay
var/mutable_appearance/old_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, old_turf, GAME_PLANE)
var/mutable_appearance/old_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, old_turf, ABOVE_GAME_PLANE)
source.cut_overlay(old_low_bubble)
source.cut_overlay(old_high_bubble)
// Now, add the new one
var/mutable_appearance/new_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, new_turf, GAME_PLANE)
var/mutable_appearance/new_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, new_turf, ABOVE_GAME_PLANE)
source.add_overlay(new_low_bubble)
source.add_overlay(new_high_bubble)