Files
Bubberstation/code/datums/components/bloodysoles.dm
T
Seth SchererandGitHub a3add37618 Refactors the forensics component into a datum (#66809)
About The Pull Request

This was doing things component really shouldn't be doing, and now all
of its behaviour is contained onto a datum, as it should've been the
whole time
Why It's Good For The CODEBASE

some things just really shouldn't be components, this was made back when DCS was first implemented and just thrown in because it was the new hot thing i guess, but datumized forensics makes far more sense, AND doesn't use GetComponent

TODO:

    More thorough testing to make sure nothing broke

Changelog

🆑
refactor: Turned the forensics component into a datum.
/🆑
2022-05-16 16:33:59 +12:00

295 lines
9.4 KiB
Plaintext

/**
* Component for clothing items that can pick up blood from decals and spread it around everywhere when walking, such as shoes or suits with integrated shoes.
*/
/datum/component/bloodysoles
/// The type of the last grub pool we stepped in, used to decide the type of footprints to make
var/last_blood_state = BLOOD_STATE_NOT_BLOODY
/// How much of each grubby type we have on our feet
var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
/// The ITEM_SLOT_* slot the item is equipped on, if it is.
var/equipped_slot
/// The parent item but casted into atom type for easier use.
var/atom/parent_atom
/// Either the mob carrying the item, or the mob itself for the /feet component subtype
var/mob/living/carbon/wielder
/// The world.time when we last picked up blood
var/last_pickup
/datum/component/bloodysoles/Initialize()
if(!isclothing(parent))
return COMPONENT_INCOMPATIBLE
parent_atom = parent
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/on_clean)
/**
* Unregisters from the wielder if necessary
*/
/datum/component/bloodysoles/proc/unregister()
if(!QDELETED(wielder))
UnregisterSignal(wielder, COMSIG_MOVABLE_MOVED)
UnregisterSignal(wielder, COMSIG_STEP_ON_BLOOD)
wielder = null
equipped_slot = null
/**
* Returns true if the parent item is obscured by something else that the wielder is wearing
*/
/datum/component/bloodysoles/proc/is_obscured()
return wielder.check_obscured_slots(TRUE) & equipped_slot
/**
* Run to update the icon of the parent
*/
/datum/component/bloodysoles/proc/update_icon()
var/obj/item/parent_item = parent
parent_item.update_slot_icon()
/**
* Run to equally share the blood between us and a decal
*/
/datum/component/bloodysoles/proc/share_blood(obj/effect/decal/cleanable/pool)
last_blood_state = pool.blood_state
// Share the blood between our boots and the blood pool
var/total_bloodiness = pool.bloodiness + bloody_shoes[last_blood_state]
// We can however be limited by how much blood we can hold
var/new_our_bloodiness = min(BLOOD_ITEM_MAX, total_bloodiness / 2)
bloody_shoes[last_blood_state] = new_our_bloodiness
pool.bloodiness = total_bloodiness - new_our_bloodiness // Give the pool the remaining blood incase we were limited
if(HAS_TRAIT(parent_atom, TRAIT_LIGHT_STEP)) //the character is agile enough to don't mess their clothing and hands just from one blood splatter at floor
return TRUE
parent_atom.add_blood_DNA(GET_ATOM_BLOOD_DNA(pool))
update_icon()
/**
* Find a blood decal on a turf that matches our last_blood_state
*/
/datum/component/bloodysoles/proc/find_pool_by_blood_state(turf/turfLoc, typeFilter = null)
for(var/obj/effect/decal/cleanable/blood/pool in turfLoc)
if(pool.blood_state == last_blood_state && (!typeFilter || istype(pool, typeFilter)))
return pool
/**
* Adds the parent type to the footprint's shoe_types var
*/
/datum/component/bloodysoles/proc/add_parent_to_footprint(obj/effect/decal/cleanable/blood/footprints/FP)
FP.shoe_types |= parent.type
/**
* Called when the parent item is equipped by someone
*
* Used to register our wielder
*/
/datum/component/bloodysoles/proc/on_equip(datum/source, mob/equipper, slot)
SIGNAL_HANDLER
if(!iscarbon(equipper))
return
var/obj/item/parent_item = parent
if(!(parent_item.slot_flags & slot))
unregister()
return
equipped_slot = slot
wielder = equipper
RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, .proc/on_moved)
RegisterSignal(wielder, COMSIG_STEP_ON_BLOOD, .proc/on_step_blood)
/**
* Called when the parent item has been dropped
*
* Used to deregister our wielder
*/
/datum/component/bloodysoles/proc/on_drop(datum/source, mob/dropper)
SIGNAL_HANDLER
unregister()
/**
* Called when the wielder has moved
*
* Used to make bloody footprints on the ground
*/
/datum/component/bloodysoles/proc/on_moved(datum/source, OldLoc, Dir, Forced)
SIGNAL_HANDLER
if(bloody_shoes[last_blood_state] == 0)
return
if(QDELETED(wielder) || is_obscured())
return
if(wielder.body_position == LYING_DOWN || !wielder.has_gravity(wielder.loc))
return
var/half_our_blood = bloody_shoes[last_blood_state] / 2
// Add footprints in old loc if we have enough cream
if(half_our_blood >= BLOOD_FOOTPRINTS_MIN)
var/turf/oldLocTurf = get_turf(OldLoc)
var/obj/effect/decal/cleanable/blood/footprints/oldLocFP = find_pool_by_blood_state(oldLocTurf, /obj/effect/decal/cleanable/blood/footprints)
if(oldLocFP)
// Footprints found in the tile we left, add us to it
add_parent_to_footprint(oldLocFP)
if (!(oldLocFP.exited_dirs & wielder.dir))
oldLocFP.exited_dirs |= wielder.dir
oldLocFP.update_appearance()
else if(find_pool_by_blood_state(oldLocTurf))
// No footprints in the tile we left, but there was some other blood pool there. Add exit footprints on it
bloody_shoes[last_blood_state] -= half_our_blood
update_icon()
oldLocFP = new(oldLocTurf)
if(!QDELETED(oldLocFP)) ///prints merged
oldLocFP.blood_state = last_blood_state
oldLocFP.exited_dirs |= wielder.dir
add_parent_to_footprint(oldLocFP)
oldLocFP.bloodiness = half_our_blood
oldLocFP.add_blood_DNA(GET_ATOM_BLOOD_DNA(parent_atom))
oldLocFP.update_appearance()
half_our_blood = bloody_shoes[last_blood_state] / 2
// If we picked up the blood on this tick in on_step_blood, don't make footprints at the same place
if(last_pickup && last_pickup == world.time)
return
// Create new footprints
if(half_our_blood >= BLOOD_FOOTPRINTS_MIN)
bloody_shoes[last_blood_state] -= half_our_blood
update_icon()
var/obj/effect/decal/cleanable/blood/footprints/FP = new(get_turf(parent_atom))
if(!QDELETED(FP)) ///prints merged
FP.blood_state = last_blood_state
FP.entered_dirs |= wielder.dir
add_parent_to_footprint(FP)
FP.bloodiness = half_our_blood
FP.add_blood_DNA(GET_ATOM_BLOOD_DNA(parent_atom))
FP.update_appearance()
/**
* Called when the wielder steps in a pool of blood
*
* Used to make the parent item bloody
*/
/datum/component/bloodysoles/proc/on_step_blood(datum/source, obj/effect/decal/cleanable/pool)
SIGNAL_HANDLER
if(QDELETED(wielder) || is_obscured())
return
if(istype(pool, /obj/effect/decal/cleanable/blood/footprints) && pool.blood_state == last_blood_state)
// The pool we stepped in was actually footprints with the same type
var/obj/effect/decal/cleanable/blood/footprints/pool_FP = pool
add_parent_to_footprint(pool_FP)
if((bloody_shoes[last_blood_state] / 2) >= BLOOD_FOOTPRINTS_MIN && !(pool_FP.entered_dirs & wielder.dir))
// If our feet are bloody enough, add an entered dir
pool_FP.entered_dirs |= wielder.dir
pool_FP.update_appearance()
share_blood(pool)
last_pickup = world.time
/**
* Called when the parent item is being washed
*/
/datum/component/bloodysoles/proc/on_clean(datum/source, clean_types)
SIGNAL_HANDLER
if(!(clean_types & CLEAN_TYPE_BLOOD) || last_blood_state == BLOOD_STATE_NOT_BLOODY)
return NONE
bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
last_blood_state = BLOOD_STATE_NOT_BLOODY
update_icon()
return COMPONENT_CLEANED
/**
* Like its parent but can be applied to carbon mobs instead of clothing items
*/
/datum/component/bloodysoles/feet
var/static/mutable_appearance/bloody_feet
/datum/component/bloodysoles/feet/Initialize()
if(!iscarbon(parent))
return COMPONENT_INCOMPATIBLE
parent_atom = parent
wielder = parent
if(!bloody_feet)
bloody_feet = mutable_appearance('icons/effects/blood.dmi', "shoeblood", SHOES_LAYER)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/on_clean)
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_moved)
RegisterSignal(parent, COMSIG_STEP_ON_BLOOD, .proc/on_step_blood)
RegisterSignal(parent, COMSIG_CARBON_UNEQUIP_SHOECOVER, .proc/unequip_shoecover)
RegisterSignal(parent, COMSIG_CARBON_EQUIP_SHOECOVER, .proc/equip_shoecover)
/datum/component/bloodysoles/feet/update_icon()
if(ishuman(wielder))
var/mob/living/carbon/human/human = wielder
if(NOBLOODOVERLAY in human.dna.species.species_traits)
return
if(bloody_shoes[BLOOD_STATE_HUMAN] > 0 && !is_obscured())
human.remove_overlay(SHOES_LAYER)
human.overlays_standing[SHOES_LAYER] = bloody_feet
human.apply_overlay(SHOES_LAYER)
else
human.update_inv_shoes()
/datum/component/bloodysoles/feet/add_parent_to_footprint(obj/effect/decal/cleanable/blood/footprints/FP)
if(!ishuman(wielder))
FP.species_types |= "unknown"
return
// Find any leg of our human and add that to the footprint, instead of the default which is to just add the human type
for(var/X in wielder.bodyparts)
var/obj/item/bodypart/affecting = X
if(affecting.body_part == LEG_RIGHT || affecting.body_part == LEG_LEFT)
if(!affecting.bodypart_disabled)
FP.species_types |= affecting.limb_id
break
/datum/component/bloodysoles/feet/is_obscured()
if(wielder.shoes)
return TRUE
return wielder.check_obscured_slots(TRUE) & ITEM_SLOT_FEET
/datum/component/bloodysoles/feet/on_moved(datum/source, OldLoc, Dir, Forced)
if(wielder.num_legs < 2)
return
..()
/datum/component/bloodysoles/feet/on_step_blood(datum/source, obj/effect/decal/cleanable/pool)
if(wielder.num_legs < 2)
return
..()
/datum/component/bloodysoles/feet/proc/unequip_shoecover(datum/source)
SIGNAL_HANDLER
update_icon()
/datum/component/bloodysoles/feet/proc/equip_shoecover(datum/source)
SIGNAL_HANDLER
update_icon()