mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
## About The Pull Request Adds slips to the list of existing shove stun methods originally set in https://github.com/tgstation/tgstation/pull/84640 (wall shoves, telebaton, mansus grasp), and also reifies this concept as the "dazed" status effect. This makes it so that being knocked down from a slip from any source (e.g. wet floor, clown stuff, lube, foam, oil, butterdog) gives the dazed visual effect and makes you eligible for being shove stunned. The status always lasts for 3 seconds even if e.g. slipping on lube knocks you down for 15, but this can be customized per slip. ## Why It's Good For The Game Further rewards environmental play and provides another feasible means of fighting back against better equipped opponents, both in line with the original PR. Also the visual cue fits well as an immediate signal that you're dazed and can't get up. ## Changelog 🆑 balance: slips now make you eligible for being shove stunned /🆑 --------- Co-authored-by: Roryl-c <5150427+Roryl-c@users.noreply.github.com>
250 lines
9.6 KiB
Plaintext
250 lines
9.6 KiB
Plaintext
/**
|
|
* # Slip behaviour component
|
|
*
|
|
* Add this component to an object to make it a slippery object, slippery objects make mobs that cross them fall over.
|
|
* Items with this component that get picked up may give their parent mob the slip behaviour.
|
|
*
|
|
* Here is a simple example of adding the component behaviour to an object.area
|
|
*
|
|
* AddComponent(/datum/component/slippery, 80, (NO_SLIP_WHEN_WALKING | SLIDE))
|
|
*
|
|
* This adds slippery behaviour to the parent atom, with a 80 decisecond (~8 seconds) knockdown
|
|
* The lube flags control how the slip behaves, in this case, the mob wont slip if it's in walking mode (NO_SLIP_WHEN_WALKING)
|
|
* and if they do slip, they will slide a few tiles (SLIDE)
|
|
*
|
|
*
|
|
* This component has configurable behaviours, see the [Initialize proc for the argument listing][/datum/component/slippery/proc/Initialize].
|
|
*/
|
|
/datum/component/slippery
|
|
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
|
/// If the slip forces the crossing mob to drop held items.
|
|
var/force_drop_items = FALSE
|
|
/// How long the slip keeps the crossing mob knocked over (they can still crawl and use weapons) for.
|
|
var/knockdown_time = 0
|
|
/// How long the slip paralyzes (prevents the crossing mob doing anything) for.
|
|
var/paralyze_time = 0
|
|
/// How long the slip dazes (makes the crossing mob vulnerable to shove stuns) for.
|
|
var/daze_time = 3 SECONDS
|
|
/// Flags for how slippery the parent is. See [__DEFINES/mobs.dm]
|
|
var/lube_flags
|
|
/// Optional callback allowing you to define custom conditions for slipping
|
|
var/datum/callback/can_slip_callback
|
|
/// Optional call back that is called when a mob slips on this component
|
|
var/datum/callback/on_slip_callback
|
|
/// If parent is an item, this is the person currently holding/wearing the parent (or the parent if no one is holding it)
|
|
var/mob/living/holder
|
|
/// Whitelist of item slots the parent can be equipped in that make the holder slippery. If null or empty, it will always make the holder slippery.
|
|
var/list/slot_whitelist = list(ITEM_SLOT_OCLOTHING, ITEM_SLOT_ICLOTHING, ITEM_SLOT_GLOVES, ITEM_SLOT_FEET, ITEM_SLOT_HEAD, ITEM_SLOT_MASK, ITEM_SLOT_BELT, ITEM_SLOT_NECK)
|
|
///what we give to connect_loc by default, makes slippable mobs moving over us slip
|
|
var/static/list/default_connections = list(
|
|
COMSIG_ATOM_ENTERED = PROC_REF(Slip),
|
|
)
|
|
|
|
///what we give to connect_loc if we're an item and get equipped by a mob, or if we're a mob. makes slippable mobs moving over the mob slip
|
|
var/static/list/mob_connections = list(
|
|
COMSIG_ATOM_ENTERED = PROC_REF(slip_on_mob),
|
|
)
|
|
|
|
/**
|
|
* Initialize the slippery component behaviour
|
|
*
|
|
* When applied to any atom in the game this will apply slipping behaviours to that atom
|
|
*
|
|
* Arguments:
|
|
* * knockdown - Length of time the knockdown applies (Deciseconds)
|
|
* * lube_flags - Controls the slip behaviour, they are listed starting [here][SLIDE]
|
|
* * datum/callback/on_slip_callback - Callback to define further custom controls on when slipping is applied
|
|
* * paralyze - length of time to paralyze the crossing mob for (Deciseconds)
|
|
* * daze - length of time to daze the crossing mob for (Deciseconds), default 3 seconds
|
|
* * force_drop - should the crossing mob drop items in its hands or not
|
|
* * slot_whitelist - flags controlling where on a mob this item can be equipped to make the parent mob slippery full list [here][ITEM_SLOT_OCLOTHING]
|
|
* * datum/callback/on_slip_callback - Callback to add custom behaviours as the crossing mob is slipped
|
|
*/
|
|
/datum/component/slippery/Initialize(
|
|
knockdown,
|
|
lube_flags = NONE,
|
|
datum/callback/on_slip_callback,
|
|
paralyze,
|
|
daze = 3 SECONDS,
|
|
force_drop = FALSE,
|
|
slot_whitelist,
|
|
datum/callback/can_slip_callback,
|
|
)
|
|
src.knockdown_time = max(knockdown, 0)
|
|
src.paralyze_time = max(paralyze, 0)
|
|
src.daze_time = max(daze, 0)
|
|
src.force_drop_items = force_drop
|
|
src.lube_flags = lube_flags
|
|
src.can_slip_callback = can_slip_callback
|
|
src.on_slip_callback = on_slip_callback
|
|
if(slot_whitelist)
|
|
src.slot_whitelist = slot_whitelist
|
|
|
|
add_connect_loc_behalf_to_parent()
|
|
if(!ismovable(parent))
|
|
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(Slip))
|
|
else if(isitem(parent))
|
|
src.lube_flags |= SLIPPERY_WHEN_LYING_DOWN
|
|
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
|
|
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
|
|
RegisterSignal(parent, COMSIG_ITEM_APPLY_FANTASY_BONUSES, PROC_REF(apply_fantasy_bonuses))
|
|
RegisterSignal(parent, COMSIG_ITEM_REMOVE_FANTASY_BONUSES, PROC_REF(remove_fantasy_bonuses))
|
|
|
|
/datum/component/slippery/Destroy(force)
|
|
can_slip_callback = null
|
|
on_slip_callback = null
|
|
holder = null
|
|
return ..()
|
|
|
|
/datum/component/slippery/proc/apply_fantasy_bonuses(obj/item/source, bonus)
|
|
SIGNAL_HANDLER
|
|
knockdown_time = source.modify_fantasy_variable("knockdown_time", knockdown_time, bonus)
|
|
if(bonus >= 5)
|
|
paralyze_time = source.modify_fantasy_variable("paralyze_time", paralyze_time, bonus)
|
|
LAZYSET(source.fantasy_modifications, "lube_flags", lube_flags)
|
|
lube_flags |= SLIDE
|
|
if(bonus >= 10)
|
|
lube_flags |= GALOSHES_DONT_HELP|SLIP_WHEN_CRAWLING
|
|
|
|
/datum/component/slippery/proc/remove_fantasy_bonuses(obj/item/source, bonus)
|
|
SIGNAL_HANDLER
|
|
knockdown_time = source.reset_fantasy_variable("knockdown_time", knockdown_time)
|
|
paralyze_time = source.reset_fantasy_variable("paralyze_time", paralyze_time)
|
|
var/previous_lube_flags = LAZYACCESS(source.fantasy_modifications, "lube_flags")
|
|
LAZYREMOVE(source.fantasy_modifications, "lube_flags")
|
|
if(!isnull(previous_lube_flags))
|
|
lube_flags = previous_lube_flags
|
|
|
|
/datum/component/slippery/proc/add_connect_loc_behalf_to_parent()
|
|
var/list/connections_to_use
|
|
if(isliving(parent))
|
|
connections_to_use = mob_connections
|
|
else if(ismovable(parent))
|
|
connections_to_use = default_connections
|
|
if(connections_to_use)
|
|
AddComponent(/datum/component/connect_loc_behalf, parent, connections_to_use)
|
|
|
|
/datum/component/slippery/InheritComponent(
|
|
datum/component/slippery/component,
|
|
i_am_original,
|
|
knockdown,
|
|
lube_flags = NONE,
|
|
datum/callback/on_slip_callback,
|
|
paralyze,
|
|
daze,
|
|
force_drop = FALSE,
|
|
slot_whitelist,
|
|
datum/callback/can_slip_callback,
|
|
)
|
|
if(component)
|
|
knockdown = component.knockdown_time
|
|
lube_flags = component.lube_flags
|
|
on_slip_callback = component.on_slip_callback
|
|
can_slip_callback = component.on_slip_callback
|
|
paralyze = component.paralyze_time
|
|
daze = component.daze_time
|
|
force_drop = component.force_drop_items
|
|
slot_whitelist = component.slot_whitelist
|
|
|
|
src.knockdown_time = max(knockdown, 0)
|
|
src.paralyze_time = max(paralyze, 0)
|
|
src.daze_time = max(daze, 0)
|
|
src.force_drop_items = force_drop
|
|
src.lube_flags = lube_flags
|
|
src.on_slip_callback = on_slip_callback
|
|
src.can_slip_callback = can_slip_callback
|
|
if(slot_whitelist)
|
|
src.slot_whitelist = slot_whitelist
|
|
/**
|
|
* The proc that does the sliping. Invokes the slip callback we have set.
|
|
*
|
|
* Arguments
|
|
* * source - the source of the signal
|
|
* * arrived - the atom/movable that is being slipped.
|
|
*/
|
|
/datum/component/slippery/proc/Slip(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
SIGNAL_HANDLER
|
|
if(!isliving(arrived))
|
|
return
|
|
if(lube_flags & SLIPPERY_TURF)
|
|
var/turf/turf = get_turf(source)
|
|
if(HAS_TRAIT(turf, TRAIT_TURF_IGNORE_SLIPPERY))
|
|
return
|
|
var/mob/living/victim = arrived
|
|
if(victim.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
|
|
return
|
|
if(can_slip_callback && !can_slip_callback.Invoke(holder, victim))
|
|
return
|
|
if(victim.slip(knockdown_time, parent, lube_flags, paralyze_time, daze_time, force_drop_items))
|
|
on_slip_callback?.Invoke(victim)
|
|
|
|
/**
|
|
* Gets called when COMSIG_ITEM_EQUIPPED is sent to parent.
|
|
* This proc register slip signals to the equipper.
|
|
* If we have a slot whitelist, we only register the signals if the slot is valid (ex: clown PDA only slips in ID or belt slot).
|
|
*
|
|
* Arguments
|
|
* * source - the source of the signal
|
|
* * equipper - the mob we're equipping the slippery thing to
|
|
* * slot - the slot we're equipping the slippery thing to on the equipper.
|
|
*/
|
|
/datum/component/slippery/proc/on_equip(datum/source, mob/equipper, slot)
|
|
SIGNAL_HANDLER
|
|
|
|
if((!LAZYLEN(slot_whitelist) || (slot in slot_whitelist)) && isliving(equipper))
|
|
holder = equipper
|
|
qdel(GetComponent(/datum/component/connect_loc_behalf))
|
|
AddComponent(/datum/component/connect_loc_behalf, holder, mob_connections)
|
|
RegisterSignal(holder, COMSIG_QDELETING, PROC_REF(holder_deleted))
|
|
|
|
/**
|
|
* Detects if the holder mob is deleted.
|
|
* If our holder mob is the holder set in this component, we null it.
|
|
*
|
|
* Arguments:
|
|
* * source - the source of the signal
|
|
* * possible_holder - the mob being deleted.
|
|
*/
|
|
/datum/component/slippery/proc/holder_deleted(datum/source, datum/possible_holder)
|
|
SIGNAL_HANDLER
|
|
|
|
if(possible_holder == holder)
|
|
holder = null
|
|
|
|
/**
|
|
* Gets called when COMSIG_ITEM_DROPPED is sent to parent.
|
|
* Makes our holder mob un-slippery.
|
|
*
|
|
* Arguments:
|
|
* * source - the source of the signal
|
|
* * user - the mob that was formerly wearing our slippery item.
|
|
*/
|
|
/datum/component/slippery/proc/on_drop(datum/source, mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
UnregisterSignal(user, COMSIG_QDELETING)
|
|
|
|
qdel(GetComponent(/datum/component/connect_loc_behalf))
|
|
add_connect_loc_behalf_to_parent()
|
|
|
|
holder = null
|
|
|
|
/**
|
|
* The slip proc, but for equipped items.
|
|
* Slips the person who crossed us if we're lying down and unbuckled.
|
|
*
|
|
* Arguments:
|
|
* * source - the source of the signal
|
|
* * arrived - the atom/movable that slipped on us.
|
|
*/
|
|
/datum/component/slippery/proc/slip_on_mob(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
SIGNAL_HANDLER
|
|
|
|
var/mob/living/living = holder || parent
|
|
if(!(lube_flags & SLIPPERY_WHEN_LYING_DOWN) || (living.body_position == LYING_DOWN && !living.buckled))
|
|
Slip(source, arrived)
|
|
|
|
/datum/component/slippery/UnregisterFromParent()
|
|
. = ..()
|
|
qdel(GetComponent(/datum/component/connect_loc_behalf))
|