Files
SmArtKarandGitHub c37d65b3d4 Makes decals unhittable in combat mode, reduces vent debris spawns (#96859)
## About The Pull Request
Cleanable decals, such as rubble, trash, glass shards, etc, can no
longer be hit (not interacted with, but just offensively hit) in combat
mode.
Ore vents now more consistently clear rocks around them, and spawn
noticeably less debris.

## Why It's Good For The Game

Suggested under my necropolis tile PR, some larger decals may steal your
clicks in combat which I do not think offers any positive gameplay
value.
Ore vents just spawn too much debris, and as it only has one sprite, it
just looks really bad.

## Changelog
🆑
qol: Ore vents spawn less rubble and are more consistent at excavating
the surrounding area
balance: Cleanable decals can no longer be hit (not cleaned, just hit)
if the user has combat mode on.
/🆑
2026-07-09 17:43:15 +02:00

154 lines
5.7 KiB
Plaintext

/obj/effect/decal/cleanable
gender = PLURAL
layer = CLEANABLE_FLOOR_OBJECT_LAYER
abstract_type = /obj/effect/decal/cleanable
flags_1 = UNPAINTABLE_1
var/list/random_icon_states = null
/// When two of these are on a same tile or do we need to merge them into just one?
var/mergeable_decal = TRUE
var/beauty = 0
/// The type of cleaning required to clean the decal. See __DEFINES/cleaning.dm for the options
var/clean_type = CLEAN_TYPE_LIGHT_DECAL
///The reagent this decal holds. Leave blank for none.
var/datum/reagent/decal_reagent
///The amount of reagent this decal holds, if decal_reagent is defined
var/reagent_amount = 0
/// If TRUE, gains TRAIT_MOPABLE on init - thus this cleanable will cleaned if its turf is cleaned
/// Set to FALSE for things that hang high on the walls or things which generally shouldn't be mopped up
var/is_mopped = TRUE
/obj/effect/decal/cleanable/Initialize(mapload, list/datum/disease/diseases)
. = ..()
if (LAZYLEN(random_icon_states))
icon_state = pick(random_icon_states)
base_icon_state = icon_state
if (isturf(loc))
for (var/obj/effect/decal/cleanable/other in loc)
if (other != src && other.type == type && !QDELETED(other) && replace_decal(other))
handle_merge_decal(other)
return INITIALIZE_HINT_QDEL
if (is_mopped)
ADD_TRAIT(src, TRAIT_MOPABLE, INNATE_TRAIT)
if (LAZYLEN(diseases))
add_diseases(diseases)
AddElement(/datum/element/beauty, beauty)
var/turf/our_turf = get_turf(src)
if (our_turf && is_station_level(our_turf.z))
SSblackbox.record_feedback("tally", "station_mess_created", 1, name)
/obj/effect/decal/cleanable/Destroy()
var/turf/our_turf = get_turf(src)
if (our_turf && is_station_level(our_turf.z))
SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name)
return ..()
/obj/effect/decal/cleanable/proc/add_diseases(list/datum/disease/diseases)
var/list/datum/disease/diseases_to_add = list()
for (var/datum/disease/disease as anything in diseases)
if (disease.spread_flags & DISEASE_SPREAD_CONTACT_FLUIDS)
diseases_to_add += disease
if (LAZYLEN(diseases_to_add))
AddComponent(/datum/component/infective, diseases_to_add)
/// Check if we should give up in favor of the pre-existing decal
/obj/effect/decal/cleanable/proc/replace_decal(obj/effect/decal/cleanable/other)
if (mergeable_decal)
return TRUE
/obj/effect/decal/cleanable/wash(clean_types)
. = ..()
if (. || clean_types & clean_type)
qdel(src)
. |= COMPONENT_CLEANED|COMPONENT_CLEANED_GAIN_XP
/obj/effect/decal/cleanable/proc/handle_merge_decal(obj/effect/decal/cleanable/merger)
if (!reagents && !decal_reagent)
return
if (!reagents && !merger.reagents && (!merger.decal_reagent || merger.decal_reagent == decal_reagent))
merger.decal_reagent = decal_reagent
merger.reagent_amount += reagent_amount
else if (lazy_init_reagents() && merger.lazy_init_reagents())
merger.reagents.maximum_volume += reagents.total_volume
reagents.trans_to(merger, reagents.total_volume)
/// Returns reagents datum if it exists, or lazyloads one if it doesn't
/obj/effect/decal/cleanable/proc/lazy_init_reagents()
RETURN_TYPE(/datum/reagents)
if (reagents)
return reagents
return init_reagents(decal_reagent, reagent_amount)
/obj/effect/decal/cleanable/proc/init_reagents(reagent = null, amount = null)
RETURN_TYPE(/datum/reagents)
if (!reagent)
return
create_reagents(amount)
reagents.add_reagent(reagent, amount)
return reagents
/obj/effect/decal/cleanable/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
// Why are rags cups???
if (!istype(tool, /obj/item/reagent_containers/cup) || istype(tool, /obj/item/rag))
return NONE
if (!lazy_init_reagents()?.total_volume)
to_chat(user, span_notice("[src] isn't thick enough to scoop up!"))
return ITEM_INTERACT_BLOCKING
if (!reagents.trans_to(tool, reagents.total_volume, transferred_by = user))
to_chat(user, span_warning("[tool] is full!"))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You scoop up [reagents.total_volume > 0 ? "some of " : ""]\the [src] into \the [tool]!"))
if (!reagents.total_volume) //scooped up all of it
qdel(src)
return ITEM_INTERACT_SUCCESS
/// Checks if this decal can be bloodcrawled in
/obj/effect/decal/cleanable/proc/can_bloodcrawl_in()
if (decal_reagent == /datum/reagent/blood || reagents?.has_reagent(/datum/reagent/blood))
return TRUE
var/list/blood_DNA = GET_ATOM_BLOOD_DECALS(src)
for (var/blood_key in blood_DNA)
var/datum/blood_type/blood_type = blood_DNA[blood_key]
if (blood_type.reagent_type == /datum/reagent/blood)
return TRUE
/obj/effect/decal/cleanable/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
if (!isliving(user))
return ..()
var/mob/living/as_living = user
if (as_living.combat_mode)
return TRUE
return ..()
/// Creates a cleanable decal on a turf
/// Use this if your decal is one of one, and thus we should not spawn it if it's there already
/// Returns either the existing cleanable, the one we created, or null if we can't spawn on that turf
/turf/proc/spawn_unique_cleanable(obj/effect/decal/cleanable/cleanable_type)
var/turf/checkturf = src
while (isgroundlessturf(checkturf) && checkturf.zPassOut(DOWN))
var/turf/below = GET_TURF_BELOW(checkturf)
if (!below || !below.zPassIn(DOWN))
break
checkturf = below
// There is no need to spam unique cleanables, they don't stack and it just chews cpu
var/obj/effect/decal/cleanable/existing = locate(cleanable_type) in checkturf
if (existing)
return existing
return new cleanable_type(checkturf)
/turf/proc/spawn_glitter(glitter_colors)
var/obj/effect/decal/cleanable/glitter/new_glitter = spawn_unique_cleanable(/obj/effect/decal/cleanable/glitter)
new_glitter.color = pick_weight(glitter_colors)