Files
MrMelbert b69267ab8c Spacer's drift speed bonus doesn't stack with jetpack speed (#96169)
## About The Pull Request

Splits `inertia_move_multiplier` in two,
`inertia_move_multiplier_passive` and `inertia_move_multiplier_active`
When calculating drift speed, only the better of the two is used.* 
Spacer now buffs the former, jetpacks now buff the latter

In short, the bonus to drift speed applied from being a spacer no longer
applies multiplicatively with the bonus to drift speed from jetpacks.

**(If we want further nuance we'd probably have to look at something
like movespeed modifiers. But in the meanwhile this aims to
differentiate "this atom innately drifts faster" and "something is
making this atom drift faster".)*

## Why It's Good For The Game

The 0.75x modifier from Spacer combined with the 0.66x modifier jetpacks
apply ultimately comes out to a bonus of like 0.5x, or in other words, a
2x speed bonus which is a liiiiiiiiiiittle ridiculous. Jetpack jousting
is chaotic enough as-is.

With the changes Spacers will still get mileage out of especially poor
jetpacks (i.e. improvised jetpacks), but with a normal or advanced
jetpack, they'll go as fast as anyone else does.

## Changelog

🆑 Melbert
balance: Spacer: Bonus to drift speed no longer stacks with jetpack
speed - only the greater of the two effects is applied.
/🆑
2026-05-24 17:27:31 -06:00

193 lines
6.6 KiB
Plaintext

// Welcome to the jetpack component
// Apply this to something when you want it to be "like a jetpack"
// So propulsion through space on move, that sort of thing
/datum/component/jetpack
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// Checks to ensure if we can move
var/datum/callback/check_on_move
/// Checks to ensure we can activate
var/datum/callback/check_on_activation
/// If we should stabilize ourselves when not drifting
var/stabilize = FALSE
/// The signal we listen for as an activation
var/activation_signal
/// The signal we listen for as a de-activation
var/deactivation_signal
/// The return flag our parent expects for a failed activation
var/return_flag
/// The effect system for the jet pack trail
var/datum/effect_system/trail_follow/trail
/// The typepath to instansiate our trail as, when we need it
var/effect_type
/// Drift force applied each movement tick
var/drift_force
VAR_PRIVATE/active = FALSE
/**
* Arguments:
* * stabilize - If we should drift when we finish moving, or sit stable in space]
* * drift_force - How much force is applied whenever the user tries to move, applied as a multiplier to the user's inertia_move_multiplier.
* * activation_signal - Signal we activate on
* * deactivation_signal - Signal we deactivate on
* * return_flag - Flag to return if activation fails
* * check_on_move - Callback we call each time we attempt a move, we expect it to retun true if the move is ok, false otherwise. It expects an arg, TRUE if fuel should be consumed, FALSE othewise
* * effect_type - Type of trail_follow to spawn
*/
/datum/component/jetpack/Initialize(
stabilize = FALSE,
drift_force = 1 NEWTONS,
activation_signal,
deactivation_signal,
return_flag,
datum/callback/check_on_move,
datum/callback/check_on_activation,
datum/effect_system/trail_follow/effect_type,
)
. = ..()
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
if(!activation_signal) // Can't activate? go away
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, activation_signal, PROC_REF(activate))
if(deactivation_signal)
RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate))
src.stabilize = stabilize
src.check_on_move = check_on_move
src.check_on_activation = check_on_activation
src.activation_signal = activation_signal
src.deactivation_signal = deactivation_signal
src.return_flag = return_flag
src.effect_type = effect_type
src.drift_force = drift_force
/datum/component/jetpack/InheritComponent(datum/component/component, original, stabilize, drift_force = 1 NEWTONS, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/callback/check_on_activation, datum/effect_system/trail_follow/effect_type)
UnregisterSignal(parent, src.activation_signal)
if(src.deactivation_signal)
UnregisterSignal(parent, src.deactivation_signal)
RegisterSignal(parent, activation_signal, PROC_REF(activate))
if(deactivation_signal)
RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate))
src.stabilize = stabilize
src.check_on_move = check_on_move
src.check_on_activation = check_on_activation
src.activation_signal = activation_signal
src.deactivation_signal = deactivation_signal
src.return_flag = return_flag
src.effect_type = effect_type
src.drift_force = drift_force
if(trail && trail.effect_type != effect_type)
setup_trail(trail.holder)
/datum/component/jetpack/Destroy(force)
QDEL_NULL(trail)
check_on_move = null
check_on_activation = null
return ..()
/datum/component/jetpack/proc/setup_trail(mob/user)
QDEL_NULL(trail)
trail = new effect_type(user)
trail.auto_process = FALSE
trail.start()
/datum/component/jetpack/proc/activate(datum/source, mob/new_user)
SIGNAL_HANDLER
if(!isnull(check_on_activation) && !check_on_activation.Invoke())
return return_flag
active = TRUE
RegisterSignal(new_user, COMSIG_MOVABLE_MOVED, PROC_REF(move_react))
RegisterSignal(new_user, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(pre_move_react))
RegisterSignal(new_user, COMSIG_MOB_ATTEMPT_HALT_SPACEMOVE, PROC_REF(on_pushoff))
RegisterSignal(new_user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT, PROC_REF(on_input_block))
RegisterSignal(new_user, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(stabilize))
if (effect_type)
setup_trail(new_user)
new_user.inertia_move_multiplier_active /= drift_force // lower multiplier = faster drifting
/datum/component/jetpack/proc/deactivate(datum/source, mob/old_user)
SIGNAL_HANDLER
if(!active)
return
active = FALSE
UnregisterSignal(old_user, list(
COMSIG_MOVABLE_PRE_MOVE,
COMSIG_MOVABLE_MOVED,
COMSIG_MOB_ATTEMPT_HALT_SPACEMOVE,
COMSIG_MOVABLE_DRIFT_BLOCK_INPUT,
COMSIG_MOVABLE_SPACEMOVE,
))
QDEL_NULL(trail)
old_user.inertia_move_multiplier_active *= drift_force
/datum/component/jetpack/proc/move_react(mob/source)
SIGNAL_HANDLER
if (!should_trigger(source))
return
if(source.client.intended_direction && check_on_move.Invoke(TRUE)) //You use jet when press keys. yes.
trail?.generate_effect()
/datum/component/jetpack/proc/stabilize(mob/source, movement_dir, continuous_move, backup)
SIGNAL_HANDLER
if(!continuous_move && movement_dir)
return COMSIG_MOVABLE_STOP_SPACEMOVE
// Check if we have the fuel to stop this. Do NOT consume any fuel, just check
// This is done because things other then us can use our fuel
if(stabilize && check_on_move.Invoke(FALSE))
return COMSIG_MOVABLE_STOP_SPACEMOVE
return NONE
/datum/component/jetpack/proc/should_trigger(mob/source)
if(!source || !source.client)//Don't allow jet self using
return FALSE
if(!isturf(source.loc))//You can't use jet in nowhere or from mecha/closet
return FALSE
if(!(source.movement_type & FLOATING) || source.buckled)//You don't want use jet in gravity or while buckled.
return FALSE
if(source.pulledby)//You don't must use jet if someone pull you
return FALSE
if(source.throwing)//You don't must use jet if you thrown
return FALSE
return TRUE
/datum/component/jetpack/proc/pre_move_react(mob/source)
SIGNAL_HANDLER
trail?.oldposition = get_turf(source)
/datum/component/jetpack/proc/on_input_block(mob/source)
SIGNAL_HANDLER
if (!should_trigger(source))
return NONE
if (!check_on_move.Invoke(TRUE))
return NONE
return DRIFT_ALLOW_INPUT
/datum/component/jetpack/proc/on_pushoff(mob/source, movement_dir, continuous_move, atom/backup)
SIGNAL_HANDLER
if (get_dir(source, backup) == movement_dir || source.loc == backup.loc)
return NONE
if (!source.client?.intended_direction || source.client.intended_direction == get_dir(source, backup))
return NONE
if (isnull(source.drift_handler))
return NONE
if (!should_trigger(source) || !check_on_move.Invoke(FALSE))
return NONE
return COMPONENT_PREVENT_SPACEMOVE_HALT