DCS Update + Footstep element (#17076)

* DCS Update + Footstep element

* Steppy

* Revert "Steppy"

This reverts commit fab0590fb7.

* Reapply "Steppy"

This reverts commit 1f7ad8f8f7.

* Ough

* Oops! Wrong map.

* lil stuff

* Update

* Only loud

* Missed one

* NO MORE SILENT

* ...parenthesis.
This commit is contained in:
Guti
2025-02-08 18:19:59 -05:00
committed by GitHub
parent d1b65c3635
commit a081d48590
79 changed files with 1695 additions and 579 deletions
+25
View File
@@ -0,0 +1,25 @@
# Template file for your new element
See _element.dm for detailed explanations
```dm
/datum/element/myelement
element_flags = ELEMENT_BESPOKE | ELEMENT_COMPLEX_DETACH | ELEMENT_DETACH_ON_HOST_DESTROY | ELEMENT_NOTAREALFLAG // code/__DEFINES/dcs/flags.dm
//argument_hash_start_idx = 2 // Use with ELEMENT_BESPOKE
var/list/myvar = list()
/datum/element/myelement/Attach(datum/target)
if(!ismovable(target))
return COMPONENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_MOVED, myproc)
to_chat(target, "Hey, you're in your element.")
/datum/element/myelement/Detach(datum/source)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
to_chat(source, "You feel way out of your element.")
/datum/element/myelement/proc/myproc(datum/source)
SIGNAL_HANDLER
playsound(source, 'sound/effects/gong.ogg', 50, TRUE)
```
+43 -7
View File
@@ -10,11 +10,14 @@
/**
* The index of the first attach argument to consider for duplicate elements
*
* All arguments from this index onwards (1 based) are hashed into the key to determine
* if this is a new unique element or one already exists
*
* Is only used when flags contains [ELEMENT_BESPOKE]
*
* This is infinity so you must explicitly set this
*/
var/id_arg_index = INFINITY
var/argument_hash_start_idx = INFINITY
/// Activates the functionality defined by the element on the given target datum
/datum/element/proc/Attach(datum/target)
@@ -22,19 +25,19 @@
if(type == /datum/element)
return ELEMENT_INCOMPATIBLE
SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src)
if(element_flags & ELEMENT_DETACH)
if(element_flags & ELEMENT_DETACH_ON_HOST_DESTROY)
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(OnTargetDelete), override = TRUE)
/datum/element/proc/OnTargetDelete(datum/source, force)
/datum/element/proc/OnTargetDelete(datum/source)
SIGNAL_HANDLER
Detach(source)
/// Deactivates the functionality defines by the element on the given datum
/datum/element/proc/Detach(datum/source, ...)
SIGNAL_HANDLER
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
SHOULD_CALL_PARENT(TRUE)
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
/datum/element/Destroy(force)
@@ -48,20 +51,53 @@
/// Finds the singleton for the element type given and attaches it to src
/datum/proc/_AddElement(list/arguments)
if(QDELING(src))
CRASH("We just tried to add an element to a qdeleted datum, something is fucked")
var/datum/element/element_type = arguments[1]
stack_trace("We just tried to add the element [element_type] to a qdeleted datum, something is fucked")
return
var/datum/element/ele = SSdcs.GetElement(arguments)
if(!ele) // We couldn't fetch the element, likely because it was not an element.
return // the crash message has already been sent
arguments[1] = src
if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE)
CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]")
CRASH("Incompatible element [ele.type] was assigned to a [type]! args: [json_encode(args)]")
/**
* Finds the singleton for the element type given and detaches it from src
* You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE]
*/
/datum/proc/_RemoveElement(list/arguments)
var/datum/element/ele = SSdcs.GetElement(arguments)
var/datum/element/ele = SSdcs.GetElement(arguments, FALSE)
if(!ele) // We couldn't fetch the element, likely because it didn't exist.
return
if(ele.element_flags & ELEMENT_COMPLEX_DETACH)
arguments[1] = src
ele.Detach(arglist(arguments))
else
ele.Detach(src)
/**
* Used to manage (typically non_bespoke) elements with multiple sources through traits
* so we don't have to make them a components again.
* The element will be later removed once all trait sources are gone, there's no need of a
* "RemoveElementTrait" counterpart.
*/
/datum/proc/AddElementTrait(trait, source, datum/element/eletype, ...)
if(!ispath(eletype, /datum/element))
CRASH("AddElementTrait called, but [eletype] is not of a /datum/element path")
ADD_TRAIT(src, trait, source)
if(HAS_TRAIT_NOT_FROM(src, trait, source))
return
var/list/arguments = list(eletype)
/// 3 is the length of fixed args of this proc, any further one is passed down to AddElement.
if(length(args) > 3)
arguments += args.Copy(4)
/// We actually pass down a copy of the arguments since it's manipulated by the end of the proc.
_AddElement(arguments.Copy())
var/datum/ele = SSdcs.GetElement(arguments)
ele.RegisterSignal(src, SIGNAL_REMOVETRAIT(trait), TYPE_PROC_REF(/datum/element, _detach_on_trait_removed))
/datum/element/proc/_detach_on_trait_removed(datum/source, trait)
SIGNAL_HANDLER
Detach(source)
UnregisterSignal(source, SIGNAL_REMOVETRAIT(trait))
+2 -2
View File
@@ -2,8 +2,8 @@
* Simple conflict checking for getting number of conflicting things on someone with the same ID.
*/
/datum/element/conflict_checking
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH
id_arg_index = 1
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY
argument_hash_start_idx = 1
/// we don't need to KNOW who has us, only our ID.
var/id
+187
View File
@@ -0,0 +1,187 @@
#define SHOULD_DISABLE_FOOTSTEPS(source)
///Footstep element. Plays footsteps at parents location when it is appropriate.
/datum/element/footstep
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE
argument_hash_start_idx = 2
///A list containing living mobs and the number of steps they have taken since the last time their footsteps were played.
var/list/steps_for_living = list()
///volume determines the extra volume of the footstep. This is multiplied by the base volume, should there be one.
var/volume
///e_range stands for extra range - aka how far the sound can be heard. This is added to the base value and ignored if there isn't a base value.
var/e_range
///footstep_type is a define which determines what kind of sounds should get chosen.
var/footstep_type
///This can be a list OR a soundfile OR null. Determines whatever sound gets played.
var/footstep_sounds
///Whether or not to add variation to the sounds played
var/sound_vary = FALSE
/datum/element/footstep/Attach(datum/target, footstep_type = FOOTSTEP_MOB_BAREFOOT, volume = 0.1, e_range = -8, sound_vary = FALSE)
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
src.volume = volume
src.e_range = e_range
src.footstep_type = footstep_type
src.sound_vary = sound_vary
if(ishuman(target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(play_humanstep))
steps_for_living[target] = 0
return
footstep_sounds = check_footstep_type(footstep_type)
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(play_simplestep))
steps_for_living[target] = 0
/datum/element/footstep/proc/check_footstep_type(footstep_type)
var/footstep_ret
switch(footstep_type)
if(FOOTSTEP_MOB_TESHARI)
footstep_ret = GLOB.lightclawfootstep
if(FOOTSTEP_MOB_CLAW)
footstep_ret = GLOB.clawfootstep
if(FOOTSTEP_MOB_HEAVY)
footstep_ret = GLOB.heavyfootstep
if(FOOTSTEP_MOB_SHOE)
footstep_ret = GLOB.footstep
if(FOOTSTEP_MOB_SLIME)
footstep_ret = 'sound/effects/footstep/slime1.ogg'
if(FOOTSTEP_MOB_SLITHER)
footstep_ret = 'sound/effects/footstep/crawl1.ogg'
else
footstep_ret = GLOB.barefootstep
return footstep_ret
/datum/element/footstep/Detach(atom/movable/source)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
steps_for_living -= source
return ..()
///Prepares a footstep for living mobs. Determines if it should get played. Returns the turf it should get played on. Note that it is always a /turf/simulated
/datum/element/footstep/proc/prepare_step(mob/living/source)
var/turf/simulated/turf = get_turf(source)
if(!istype(turf))
return
if(source.is_incorporeal())
return
if(source.buckled || source.throwing || source.movement_type & (source.is_ventcrawling | source.flying))
return
if(source.lying) //play crawling sound if we're lying
if(turf.footstep)
playsound(turf, 'sound/effects/footstep/crawl1.ogg', 15 * volume, falloff = 1, vary = sound_vary)
return
if(iscarbon(source))
var/mob/living/carbon/carbon_source = source
if(!carbon_source.get_organ(BP_L_LEG) && !carbon_source.get_organ(BP_R_LEG))
return
if(carbon_source.m_intent == I_WALK)
return// stealth
steps_for_living[source] += 1
var/steps = steps_for_living[source]
if(steps >= 6)
steps_for_living[source] = 0
steps = 0
if(steps % 2)
return
if(steps != 0 && !get_gravity(source)) // don't need to step as often when you hop around
return
. = list(
FOOTSTEP_MOB_SHOE = turf.footstep,
FOOTSTEP_MOB_BAREFOOT = turf.barefootstep,
FOOTSTEP_MOB_HEAVY = turf.heavyfootstep,
FOOTSTEP_MOB_CLAW = turf.clawfootstep,
STEP_SOUND_PRIORITY = STEP_SOUND_NO_PRIORITY
)
var/overriden = SEND_SIGNAL(turf, COMSIG_TURF_PREPARE_STEP_SOUND, .) & FOOTSTEP_OVERRIDEN
//The turf has no footstep sound (e.g. open space) and none of the objects on that turf (e.g. catwalks) overrides it
if(!overriden && isnull(turf.footstep))
return null
return .
/datum/element/footstep/proc/play_simplestep(mob/living/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
var/volume_multiplier = 0.3
if(!isturf(source.loc))
return
var/list/prepared_steps = prepare_step(source)
if(isnull(prepared_steps))
return
if(isfile(footstep_sounds) || istext(footstep_sounds))
playsound(source.loc, footstep_sounds, volume * volume_multiplier, falloff = 1, vary = sound_vary)
return
var/turf_footstep = prepared_steps[footstep_type]
if(isnull(turf_footstep) || !footstep_sounds[turf_footstep])
return
playsound(source.loc, pick(footstep_sounds[turf_footstep][1]), footstep_sounds[turf_footstep][2] * volume, TRUE, footstep_sounds[turf_footstep][3] + e_range, falloff = 1, vary = sound_vary)
/datum/element/footstep/proc/play_humanstep(mob/living/carbon/human/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
var/volume_multiplier = 0.3
var/range_adjustment = 0
var/list/prepared_steps = prepare_step(source)
if(isnull(prepared_steps))
return
//cache for sanic speed (lists are references anyways)
var/footstep_sounds = GLOB.footstep
if( source.shoes || ( source.wear_suit && (source.wear_suit.body_parts_covered & FEET) ) )
// we are wearing shoes
var/shoestep_type = prepared_steps[FOOTSTEP_MOB_SHOE]
if(!isnull(shoestep_type) && footstep_sounds[shoestep_type]) // shoestep type can be null
playsound(source.loc, pick(footstep_sounds[shoestep_type][1]),
footstep_sounds[shoestep_type][2] * volume * volume_multiplier,
TRUE,
footstep_sounds[shoestep_type][3] + e_range + range_adjustment, falloff = 1, vary = sound_vary)
else
// we are barefoot
if(source.species.special_step_sounds)
playsound(source.loc, pick(source.species.special_step_sounds), volume, TRUE, falloff = 1, vary = sound_vary)
else if (istype(source.species, /datum/species/shapeshifter/promethean))
playsound(source.loc, 'sound/effects/footstep/slime1.ogg', volume, TRUE, falloff = 1)
else if (source.custom_footstep == FOOTSTEP_MOB_SLITHER)
playsound(source.loc, 'sound/effects/footstep/crawl1.ogg', 15 * volume, falloff = 1, vary = sound_vary)
else
var/barefoot_type = prepared_steps[FOOTSTEP_MOB_BAREFOOT]
var/bare_footstep_sounds
if(source.custom_footstep != FOOTSTEP_MOB_HUMAN)
bare_footstep_sounds = check_footstep_type(source.custom_footstep)
else
bare_footstep_sounds = GLOB.barefootstep
if(!isnull(barefoot_type) && bare_footstep_sounds[barefoot_type]) // barefoot_type can be null
playsound(source.loc, pick(bare_footstep_sounds[barefoot_type][1]),
bare_footstep_sounds[barefoot_type][2] * volume * volume_multiplier,
TRUE,
bare_footstep_sounds[barefoot_type][3] + e_range + range_adjustment, falloff = 1, vary = sound_vary)
///Prepares a footstep for machine walking
/datum/element/footstep/proc/play_simplestep_machine(atom/movable/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
var/turf/simulated/source_loc = get_turf(source)
if(!istype(source_loc))
return
playsound(source_loc, footstep_sounds, 50, falloff = 1, vary = sound_vary)
#undef SHOULD_DISABLE_FOOTSTEPS
+81
View File
@@ -0,0 +1,81 @@
///When attached, the footstep sound played by the footstep element will be replaced by this one's
/datum/element/footstep_override
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH_ON_HOST_DESTROY
argument_hash_start_idx = 2
///The sound played for movables with claw step sound type.
var/clawfootstep
///The sound played for movables with barefoot step sound type.
var/barefootstep
///The sound played for movables with heavy step sound type.
var/heavyfootstep
///The sound played for movables with shoed step sound type.
var/footstep
///The priority this element has in relation to other elements of the same type attached to other movables on the same turf.
var/priority
/**
* A list of turfs occupied by the movables this element is attached to.
* Needed so it stops listening the turf's signals ONLY when it has no movable with the element.
*/
var/list/occupied_turfs = list()
/datum/element/footstep_override/Attach(atom/movable/target, clawfootstep = FOOTSTEP_HARD_CLAW, barefootstep = FOOTSTEP_HARD_BAREFOOT, heavyfootstep = FOOTSTEP_GENERIC_HEAVY, footstep = FOOTSTEP_FLOOR, priority = STEP_SOUND_NO_PRIORITY)
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
src.clawfootstep = clawfootstep
src.barefootstep = barefootstep
src.heavyfootstep = heavyfootstep
src.footstep = footstep
src.priority = priority
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
if(isturf(target.loc))
occupy_turf(target, target.loc)
/datum/element/footstep_override/Detach(atom/movable/source)
if(isturf(source.loc))
vacate_turf(source, source.loc)
return ..()
/datum/element/footstep_override/proc/on_moved(atom/movable/source, atom/oldloc)
SIGNAL_HANDLER
if(isturf(oldloc))
vacate_turf(source, oldloc)
if(isturf(source.loc))
occupy_turf(source, source.loc)
/**
* Adds the movable to the list of movables with the element occupying the turf.
* If the turf was not on the list of occupied turfs before, a signal will be registered
* to it.
*/
/datum/element/footstep_override/proc/occupy_turf(atom/movable/movable, turf/location)
if(occupied_turfs[location])
occupied_turfs[location] |= movable
return
occupied_turfs[location] = list(movable)
RegisterSignal(location, COMSIG_TURF_PREPARE_STEP_SOUND, PROC_REF(prepare_steps))
/**
* Removes the movable from the list of movables with the element occupying the turf.
* If the turf is no longer occupied, it'll be removed from the list, and the signal
* unregistered from it
*/
/datum/element/footstep_override/proc/vacate_turf(atom/movable/movable, turf/location)
LAZYREMOVE(occupied_turfs[location], movable)
if(!occupied_turfs[location])
occupied_turfs -= location
UnregisterSignal(location, COMSIG_TURF_PREPARE_STEP_SOUND)
///Changes the sound types to be played if the element priority is higher than the one in the steps list.
/datum/element/footstep_override/proc/prepare_steps(turf/source, list/steps)
SIGNAL_HANDLER
if(steps[STEP_SOUND_PRIORITY] > priority)
return
steps[FOOTSTEP_MOB_SHOE] = footstep
steps[FOOTSTEP_MOB_BAREFOOT] = barefootstep
steps[FOOTSTEP_MOB_HEAVY] = heavyfootstep
steps[FOOTSTEP_MOB_CLAW] = clawfootstep
steps[STEP_SOUND_PRIORITY] = priority
return FOOTSTEP_OVERRIDEN
+1 -1
View File
@@ -2,7 +2,7 @@
* Attached to movable atoms with opacity. Listens to them move and updates their old and new turf loc's opacity accordingly.
*/
/datum/element/light_blocking
element_flags = ELEMENT_DETACH
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY
/datum/element/light_blocking/Attach(datum/target)
+94
View File
@@ -0,0 +1,94 @@
/datum/element/slosh
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE
var/step_count
var/vore_organs_reagents
var/vore_footstep_volume
var/vore_footstep_chance
/datum/element/slosh/Attach(datum/target)
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(handle_sloshstep), override = TRUE)
step_count = 0
vore_organs_reagents = list()
vore_footstep_volume = 0
vore_footstep_chance = 0
return
/datum/element/slosh/Detach(datum/source)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
step_count -= source
return ..()
/datum/element/slosh/proc/handle_sloshstep(mob/living/source)
SIGNAL_HANDLER
if(ishuman(source))
var/mob/living/carbon/human/source_human = source
if(source_human.m_intent == "walk" && step_count++ % 20 == 0)
return
if(source_human.m_intent == "run" && step_count++ % 2 != 0)
return
choose_vorefootstep(source)
if(issilicon(source))
if(step_count++ % 2)
choose_vorefootstep(source)
/datum/element/slosh/proc/choose_vorefootstep(mob/living/source)
if(step_count++ >= 5)
vore_organs_reagents = list()
var/highest_vol = 0
for(var/obj/belly/B in source.vore_organs)
var/total_volume = B.reagents.total_volume
vore_organs_reagents += total_volume
if(B.show_liquids && B.vorefootsteps_sounds && highest_vol < total_volume)
highest_vol = total_volume
if(highest_vol < 20)
vore_footstep_volume = 0
vore_footstep_chance = 0
else
vore_footstep_volume = 20 + highest_vol * 4/5
vore_footstep_chance = highest_vol/4
step_count = 0
if(!vore_footstep_volume || !vore_footstep_chance)
return
if(prob(vore_footstep_chance))
handle_vorefootstep(source)
/datum/element/slosh/proc/handle_vorefootstep(mob/living/source)
if(!CONFIG_GET(number/vorefootstep_volume) || !vore_footstep_volume)
return
var/S = pick(GLOB.slosh)
if(!S) return
var/volume = CONFIG_GET(number/vorefootstep_volume) * (vore_footstep_volume/100)
if(ishuman(source))
var/mob/living/carbon/human/human_source = source
if(!human_source.shoes || human_source.m_intent == "walk")
volume = CONFIG_GET(number/vorefootstep_volume) * (vore_footstep_volume/100) * 0.75
else if(human_source.shoes)
var/obj/item/clothing/shoes/feet = human_source.shoes
if(istype(feet))
volume = feet.step_volume_mod * CONFIG_GET(number/vorefootstep_volume) * (vore_footstep_volume/100) * 0.75
if(!human_source.has_organ(BP_L_FOOT) && !human_source.has_organ(BP_R_FOOT))
return
if(source.buckled || source.lying || source.throwing)
return
if(!has_gravity(source) && prob(75))
return
playsound(source.loc, S, volume, FALSE, preference = /datum/client_preference/digestion_noises)
return