From bb5a51262cd470d2021c67e3d2d0f20ac8c690b9 Mon Sep 17 00:00:00 2001 From: LordVollkorn <66637090+LordVollkorn@users.noreply.github.com> Date: Tue, 25 Aug 2020 17:38:25 +0200 Subject: [PATCH 01/30] [READY] Portable Chemical Mixer: Removal of the anomaly core requirement (as per poll) (#52985) * Removed the anomaly core requirement. * Small codefix --- .../items/devices/portable_chem_mixer.dm | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/code/game/objects/items/devices/portable_chem_mixer.dm b/code/game/objects/items/devices/portable_chem_mixer.dm index 5f600721216..3fdf73ee3b5 100644 --- a/code/game/objects/items/devices/portable_chem_mixer.dm +++ b/code/game/objects/items/devices/portable_chem_mixer.dm @@ -1,6 +1,6 @@ /obj/item/storage/portable_chem_mixer name = "Portable Chemical Mixer" - desc = "A portable device that dispenses and mixes chemicals. Requires a vortex anomaly core. All necessary reagents need to be supplied with beakers. A label indicates that a screwdriver is required to open it for refills. This device can be worn on a belt. The letters 'S&T' are imprinted on the side." + desc = "A portable device that dispenses and mixes chemicals. All necessary reagents need to be supplied with beakers. A label indicates that a screwdriver is required to open it for refills. This device can be worn on a belt. The letters 'S&T' are imprinted on the side." icon = 'icons/obj/chemical.dmi' icon_state = "portablechemicalmixer_open" worn_icon_state = "portable_chem_mixer" @@ -13,8 +13,6 @@ var/obj/item/reagent_containers/beaker = null ///Creating an empty slot for a beaker that can be added to dispense into var/amount = 30 ///The amount of reagent that is to be dispensed currently - var/anomaly_core_present = FALSE ///TRUE if an anomaly core has been added - var/list/dispensable_reagents = list() ///List in which all currently dispensable reagents go /obj/item/storage/portable_chem_mixer/ComponentInitialize() @@ -36,14 +34,6 @@ ..() /obj/item/storage/portable_chem_mixer/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/assembly/signaler/anomaly/vortex) && !anomaly_core_present) - anomaly_core_present = TRUE - QDEL_NULL(I) - to_chat(user, "You insert the vortex anomaly core. The device is now functional. A screwdriver is needed to open and close the device for refills.") - return - if(!anomaly_core_present) - to_chat(user, "A vortex anomaly core has to be inserted to activate this device.") - return var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED) if (I.tool_behaviour == TOOL_SCREWDRIVER) SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked) @@ -122,19 +112,13 @@ return TRUE /obj/item/storage/portable_chem_mixer/attack_hand(mob/user) - if(!anomaly_core_present) - to_chat(user, "A vortex anomaly core has to be inserted to activate this device.") - else if(loc == user) - var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED) - if (locked) - ui_interact(user) - return - return ..() + if (loc != user) + return ..() + if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)) + ui_interact(user) + return /obj/item/storage/portable_chem_mixer/attack_self(mob/user) - if(!anomaly_core_present) - to_chat(user, "A vortex anomaly core has to be inserted to activate this device.") - return if(loc == user) var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED) if (locked) From f65ceb24ba9a39570162ee61f20b20d78b3f069d Mon Sep 17 00:00:00 2001 From: tgstation-server Date: Tue, 25 Aug 2020 08:38:29 -0700 Subject: [PATCH 02/30] Automatic changelog generation for PR #52985 [ci skip] --- html/changelogs/AutoChangeLog-pr-52985.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-52985.yml diff --git a/html/changelogs/AutoChangeLog-pr-52985.yml b/html/changelogs/AutoChangeLog-pr-52985.yml new file mode 100644 index 00000000000..d50191b69bf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-52985.yml @@ -0,0 +1,4 @@ +author: "Jay Smith & Michael Trauf" +delete-after: True +changes: + - balance: "The Portable Chemical Mixer no longer requires an anomaly core." From 4925565a2b8b93d8d9aba605446819818c34277a Mon Sep 17 00:00:00 2001 From: Qustinnus Date: Tue, 25 Aug 2020 18:49:41 +0200 Subject: [PATCH 03/30] Adds an action speed modifier system (#53023) --- code/__DEFINES/actionspeed_modification.dm | 2 + code/__HELPERS/mobs.dm | 7 +- code/datums/components/mood.dm | 6 + code/datums/status_effects/status_effect.dm | 4 - code/datums/status_effects/wound_effects.dm | 23 ++- .../actionspeed/_actionspeed_modifier.dm | 175 ++++++++++++++++++ code/modules/actionspeed/modifiers/base.dm | 2 + code/modules/actionspeed/modifiers/mood.dm | 7 + .../actionspeed/modifiers/status_effects.dm | 5 + code/modules/mob/living/carbon/carbon.dm | 14 -- code/modules/mob/living/carbon/human/human.dm | 4 - .../mob/living/carbon/human/physiology.dm | 2 - code/modules/mob/mob.dm | 1 + code/modules/mob/mob_defines.dm | 6 + .../crossbreeding/_status_effects.dm | 9 +- tgstation.dme | 5 + 16 files changed, 232 insertions(+), 40 deletions(-) create mode 100644 code/__DEFINES/actionspeed_modification.dm create mode 100644 code/modules/actionspeed/_actionspeed_modifier.dm create mode 100644 code/modules/actionspeed/modifiers/base.dm create mode 100644 code/modules/actionspeed/modifiers/mood.dm create mode 100644 code/modules/actionspeed/modifiers/status_effects.dm diff --git a/code/__DEFINES/actionspeed_modification.dm b/code/__DEFINES/actionspeed_modification.dm new file mode 100644 index 00000000000..40fbd3fe26d --- /dev/null +++ b/code/__DEFINES/actionspeed_modification.dm @@ -0,0 +1,2 @@ +//ids +#define ACTIONSPEED_ID_SANITY "sanity_component" diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 55346f4f153..9762b0e7c92 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -264,7 +264,7 @@ GLOBAL_LIST_EMPTY(species_list) if(holding) holdingnull = FALSE //Users hand started holding something, check to see if it's still holding that - delay *= user.do_after_coefficent() + delay *= user.cached_multiplicative_actions_slowdown var/datum/progressbar/progbar if(progress) @@ -318,9 +318,6 @@ GLOBAL_LIST_EMPTY(species_list) LAZYREMOVE(user.do_afters, target) LAZYREMOVE(target.targeted_by, user) -/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 - . = 1 - return ///Timed action involving at least one mob user and a list of targets. /proc/do_after_mob(mob/user, list/targets, time = 3 SECONDS, uninterruptible = FALSE, progress = TRUE, datum/callback/extra_checks, required_mobility_flags = MOBILITY_STAND) @@ -332,6 +329,8 @@ GLOBAL_LIST_EMPTY(species_list) return FALSE var/user_loc = user.loc + time *= user.cached_multiplicative_actions_slowdown + var/drifting = FALSE if(!user.Process_Spacemove(0) && user.inertia_dir) drifting = TRUE diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm index b39d4b417a1..65da13931e7 100644 --- a/code/datums/components/mood.dm +++ b/code/datums/components/mood.dm @@ -208,26 +208,32 @@ if(SANITY_INSANE to SANITY_CRAZY) setInsanityEffect(MAJOR_INSANITY_PEN) master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/insane) + master.add_actionspeed_modifier(/datum/actionspeed_modifier/low_sanity) sanity_level = 6 if(SANITY_CRAZY to SANITY_UNSTABLE) setInsanityEffect(MINOR_INSANITY_PEN) master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/crazy) + master.add_actionspeed_modifier(/datum/actionspeed_modifier/low_sanity) sanity_level = 5 if(SANITY_UNSTABLE to SANITY_DISTURBED) setInsanityEffect(0) master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/disturbed) + master.add_actionspeed_modifier(/datum/actionspeed_modifier/low_sanity) sanity_level = 4 if(SANITY_DISTURBED to SANITY_NEUTRAL) setInsanityEffect(0) master.remove_movespeed_modifier(MOVESPEED_ID_SANITY) + master.remove_actionspeed_modifier(ACTIONSPEED_ID_SANITY) sanity_level = 3 if(SANITY_NEUTRAL+1 to SANITY_GREAT+1) //shitty hack but +1 to prevent it from responding to super small differences setInsanityEffect(0) master.remove_movespeed_modifier(MOVESPEED_ID_SANITY) + master.add_actionspeed_modifier(/datum/actionspeed_modifier/high_sanity) sanity_level = 2 if(SANITY_GREAT+1 to INFINITY) setInsanityEffect(0) master.remove_movespeed_modifier(MOVESPEED_ID_SANITY) + master.add_actionspeed_modifier(/datum/actionspeed_modifier/high_sanity) sanity_level = 1 update_mood_icon() diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index c0ffc35e788..3130dd9a749 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -73,10 +73,6 @@ return duration = world.time + original_duration -//do_after modifier! -/datum/status_effect/proc/interact_speed_modifier() - return 1 - //clickdelay/nextmove modifiers! /datum/status_effect/proc/nextmove_modifier() return 1 diff --git a/code/datums/status_effects/wound_effects.dm b/code/datums/status_effects/wound_effects.dm index 90da3e61833..51db0a3038b 100644 --- a/code/datums/status_effects/wound_effects.dm +++ b/code/datums/status_effects/wound_effects.dm @@ -145,14 +145,25 @@ // bones /datum/status_effect/wound/blunt -/datum/status_effect/wound/blunt/interact_speed_modifier() - var/mob/living/carbon/C = owner +/datum/status_effect/wound/blunt/on_apply() + . = ..() + RegisterSignal(owner, COMSIG_MOB_SWAP_HANDS, .proc/on_swap_hands) + on_swap_hands() - if(C.get_active_hand() == linked_limb) - to_chat(C, "The [lowertext(linked_wound)] in your [linked_limb.name] slows your progress!") - return linked_wound.interaction_efficiency_penalty +/datum/status_effect/wound/blunt/on_remove() + . = ..() + UnregisterSignal(owner, COMSIG_MOB_SWAP_HANDS) + var/mob/living/carbon/wound_owner = owner + wound_owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/blunt_wound) - return 1 +/datum/status_effect/wound/blunt/proc/on_swap_hands() + SIGNAL_HANDLER + + var/mob/living/carbon/wound_owner = owner + if(wound_owner.get_active_hand() == linked_limb) + wound_owner.add_actionspeed_modifier(/datum/actionspeed_modifier/blunt_wound, (linked_wound.interaction_efficiency_penalty - 1)) + else + wound_owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/blunt_wound) /datum/status_effect/wound/blunt/nextmove_modifier() var/mob/living/carbon/C = owner diff --git a/code/modules/actionspeed/_actionspeed_modifier.dm b/code/modules/actionspeed/_actionspeed_modifier.dm new file mode 100644 index 00000000000..fe244e66532 --- /dev/null +++ b/code/modules/actionspeed/_actionspeed_modifier.dm @@ -0,0 +1,175 @@ +/*! Actionspeed modification datums. + + How action speed for mobs works + +Action speed is now calculated by using modifier datums which are added to mobs. Some of them (nonvariable ones) are globally cached, the variable ones are instanced and changed based on need. + +This gives us the ability to have multiple sources of actionspeed, reliabily keep them applied and remove them when they should be + +THey can have unique sources and a bunch of extra fancy flags that control behaviour + +Previously trying to update action speed was a shot in the dark that usually meant mobs got stuck going faster or slower + +Actionspeed modification list is a simple key = datum system. Key will be the datum's ID if it is overridden to not be null, or type if it is not. + +DO NOT override datum IDs unless you are going to have multiple types that must overwrite each other. It's more efficient to use types, ID functionality is only kept for cases where dynamic creation of modifiers need to be done. + +When update actionspeed is called, the list of items is iterated, according to flags priority and a bunch of conditions +this spits out a final calculated value which is used as a modifer to last_move + modifier for calculating when a mob +can next move + +*/ + +/datum/actionspeed_modifier + /// Whether or not this is a variable modifier. Variable modifiers can NOT be ever auto-cached. ONLY CHECKED VIA INITIAL(), EFFECTIVELY READ ONLY (and for very good reason) + var/variable = FALSE + + /// Unique ID. You can never have different modifications with the same ID. By default, this SHOULD NOT be set. Only set it for cases where you're dynamically making modifiers/need to have two types overwrite each other. If unset, uses path (converted to text) as ID. + var/id + + /// Higher ones override lower priorities. This is NOT used for ID, ID must be unique, if it isn't unique the newer one overwrites automatically if overriding. + var/priority = 0 + var/flags = NONE + + /// Multiplicative slowdown + var/multiplicative_slowdown = 0 + + /// Other modification datums this conflicts with. + var/conflicts_with + +/datum/actionspeed_modifier/New() + . = ..() + if(!id) + id = "[type]" //We turn the path into a string. + +GLOBAL_LIST_EMPTY(actionspeed_modification_cache) + +/// Grabs a STATIC MODIFIER datum from cache. YOU MUST NEVER EDIT THESE DATUMS, OR IT WILL AFFECT ANYTHING ELSE USING IT TOO! +/proc/get_cached_actionspeed_modifier(modtype) + if(!ispath(modtype, /datum/actionspeed_modifier)) + CRASH("[modtype] is not a actionspeed modification typepath.") + var/datum/actionspeed_modifier/actionspeed_mod = modtype + if(initial(actionspeed_mod.variable)) + CRASH("[modtype] is a variable modifier, and can never be cached.") + actionspeed_mod = GLOB.actionspeed_modification_cache[modtype] + if(!actionspeed_mod) + actionspeed_mod = GLOB.actionspeed_modification_cache[modtype] = new modtype + return actionspeed_mod + +///Add a action speed modifier to a mob. If a variable subtype is passed in as the first argument, it will make a new datum. If ID conflicts, it will overwrite the old ID. +/mob/proc/add_actionspeed_modifier(datum/actionspeed_modifier/type_or_datum, update = TRUE) + if(ispath(type_or_datum)) + if(!initial(type_or_datum.variable)) + type_or_datum = get_cached_actionspeed_modifier(type_or_datum) + else + type_or_datum = new type_or_datum + var/datum/actionspeed_modifier/existing = LAZYACCESS(actionspeed_modification, type_or_datum.id) + if(existing) + if(existing == type_or_datum) //same thing don't need to touch + return TRUE + remove_actionspeed_modifier(existing, FALSE) + if(length(actionspeed_modification)) + BINARY_INSERT(type_or_datum.id, actionspeed_modification, datum/actionspeed_modifier, type_or_datum, priority, COMPARE_VALUE) + LAZYSET(actionspeed_modification, type_or_datum.id, type_or_datum) + if(update) + update_actionspeed() + return TRUE + +/// Remove a action speed modifier from a mob, whether static or variable. +/mob/proc/remove_actionspeed_modifier(datum/actionspeed_modifier/type_id_datum, update = TRUE) + var/key + if(ispath(type_id_datum)) + key = initial(type_id_datum.id) || "[type_id_datum]" //id if set, path set to string if not. + else if(!istext(type_id_datum)) //if it isn't text it has to be a datum, as it isn't a type. + key = type_id_datum.id + else //assume it's an id + key = type_id_datum + if(!LAZYACCESS(actionspeed_modification, key)) + return FALSE + LAZYREMOVE(actionspeed_modification, key) + if(update) + update_actionspeed(FALSE) + return TRUE + +/*! Used for variable slowdowns like hunger/health loss/etc, works somewhat like the old list-based modification adds. Returns the modifier datum if successful + How this SHOULD work is: + 1. Ensures type_id_datum one way or another refers to a /variable datum. This makes sure it can't be cached. This includes if it's already in the modification list. + 2. Instantiate a new datum if type_id_datum isn't already instantiated + in the list, using the type. Obviously, wouldn't work for ID only. + 3. Add the datum if necessary using the regular add proc + 4. If any of the rest of the args are not null (see: multiplicative slowdown), modify the datum + 5. Update if necessary +*/ +/mob/proc/add_or_update_variable_actionspeed_modifier(datum/actionspeed_modifier/type_id_datum, update = TRUE, multiplicative_slowdown) + var/modified = FALSE + var/inject = FALSE + var/datum/actionspeed_modifier/final + if(istext(type_id_datum)) + final = LAZYACCESS(actionspeed_modification, type_id_datum) + if(!final) + CRASH("Couldn't find existing modification when provided a text ID.") + else if(ispath(type_id_datum)) + if(!initial(type_id_datum.variable)) + CRASH("Not a variable modifier") + final = LAZYACCESS(actionspeed_modification, initial(type_id_datum.id) || "[type_id_datum]") + if(!final) + final = new type_id_datum + inject = TRUE + modified = TRUE + else + if(!initial(type_id_datum.variable)) + CRASH("Not a variable modifier") + final = type_id_datum + if(!LAZYACCESS(actionspeed_modification, final.id)) + inject = TRUE + modified = TRUE + if(!isnull(multiplicative_slowdown)) + final.multiplicative_slowdown = multiplicative_slowdown + modified = TRUE + if(inject) + add_actionspeed_modifier(final, FALSE) + if(update && modified) + update_actionspeed(TRUE) + return final + +///Is there a actionspeed modifier for this mob +/mob/proc/has_actionspeed_modifier(datum/actionspeed_modifier/datum_type_id) + var/key + if(ispath(datum_type_id)) + key = initial(datum_type_id.id) || "[datum_type_id]" + else if(istext(datum_type_id)) + key = datum_type_id + else + key = datum_type_id.id + return LAZYACCESS(actionspeed_modification, key) + +/// Go through the list of actionspeed modifiers and calculate a final actionspeed. ANY ADD/REMOVE DONE IN UPDATE_actionspeed MUST HAVE THE UPDATE ARGUMENT SET AS FALSE! +/mob/proc/update_actionspeed() + . = 0 + var/list/conflict_tracker = list() + for(var/key in get_actionspeed_modifiers()) + var/datum/actionspeed_modifier/M = actionspeed_modification[key] + var/conflict = M.conflicts_with + var/amt = M.multiplicative_slowdown + if(conflict) + // Conflicting modifiers prioritize the larger slowdown or the larger speedup + // We purposefuly don't handle mixing speedups and slowdowns on the same id + if(abs(conflict_tracker[conflict]) < abs(amt)) + conflict_tracker[conflict] = amt + else + continue + . += amt + cached_multiplicative_actions_slowdown = . + +///Adds a default action speed +/mob/proc/initialize_actionspeed() + add_or_update_variable_actionspeed_modifier(/datum/actionspeed_modifier/base, multiplicative_slowdown = 1) + +/// Get the action speed modifiers list of the mob +/mob/proc/get_actionspeed_modifiers() + . = LAZYCOPY(actionspeed_modification) + for(var/id in actionspeed_mod_immunities) + . -= id + +/// Checks if a action speed modifier is valid and not missing any data +/proc/actionspeed_data_null_check(datum/actionspeed_modifier/M) //Determines if a data list is not meaningful and should be discarded. + . = !(M.multiplicative_slowdown) diff --git a/code/modules/actionspeed/modifiers/base.dm b/code/modules/actionspeed/modifiers/base.dm new file mode 100644 index 00000000000..97c5124c3bc --- /dev/null +++ b/code/modules/actionspeed/modifiers/base.dm @@ -0,0 +1,2 @@ +/datum/actionspeed_modifier/base + variable = TRUE diff --git a/code/modules/actionspeed/modifiers/mood.dm b/code/modules/actionspeed/modifiers/mood.dm new file mode 100644 index 00000000000..d8ed005d42b --- /dev/null +++ b/code/modules/actionspeed/modifiers/mood.dm @@ -0,0 +1,7 @@ +/datum/actionspeed_modifier/low_sanity + multiplicative_slowdown = 0.25 + id = ACTIONSPEED_ID_SANITY + +/datum/actionspeed_modifier/high_sanity + multiplicative_slowdown = -0.1 + id = ACTIONSPEED_ID_SANITY diff --git a/code/modules/actionspeed/modifiers/status_effects.dm b/code/modules/actionspeed/modifiers/status_effects.dm new file mode 100644 index 00000000000..615346b7902 --- /dev/null +++ b/code/modules/actionspeed/modifiers/status_effects.dm @@ -0,0 +1,5 @@ +/datum/actionspeed_modifier/timecookie + multiplicative_slowdown = -0.05 + +/datum/actionspeed_modifier/blunt_wound + variable = TRUE diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index b0f0e6363ac..7543af69243 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -984,20 +984,6 @@ set_usable_hands(usable_hands - 1) -/mob/living/carbon/do_after_coefficent() - . = ..() - var/datum/component/mood/mood = src.GetComponent(/datum/component/mood) //Currently, only carbons or higher use mood, move this once that changes. - if(mood) - switch(mood.sanity) //Alters do_after delay based on how sane you are - if(-INFINITY to SANITY_DISTURBED) - . *= 1.25 - if(SANITY_NEUTRAL to INFINITY) - . *= 0.90 - - for(var/i in status_effects) - var/datum/status_effect/S = i - . *= S.interact_speed_modifier() - /mob/living/carbon/proc/create_internal_organs() for(var/X in internal_organs) var/obj/item/organ/I = X diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3373c2a10df..8cb38602ab3 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1158,10 +1158,6 @@ riding_datum.handle_vehicle_layer() . = ..(target, force, check_loc) -/mob/living/carbon/human/do_after_coefficent() - . = ..() - . *= physiology.do_after_speed - /mob/living/carbon/human/updatehealth() . = ..() dna?.species.spec_updatehealth(src) diff --git a/code/modules/mob/living/carbon/human/physiology.dm b/code/modules/mob/living/carbon/human/physiology.dm index 4d411deb53d..2a197b64ee2 100644 --- a/code/modules/mob/living/carbon/human/physiology.dm +++ b/code/modules/mob/living/carbon/human/physiology.dm @@ -23,7 +23,5 @@ var/hunger_mod = 1 //% of hunger rate taken per tick. - var/do_after_speed = 1 //Speed mod for do_after. Lower is better. If temporarily adjusting, please only modify using *= and /=, so you don't interrupt other calculations. - /datum/physiology/New() armor = new diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index b252d180ff5..f47477104b7 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -75,6 +75,7 @@ set_nutrition(rand(NUTRITION_LEVEL_START_MIN, NUTRITION_LEVEL_START_MAX)) . = ..() update_config_movespeed() + initialize_actionspeed() update_movespeed(TRUE) /** diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index f1afd6339c1..63dca4857dc 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -28,6 +28,12 @@ var/list/movespeed_mod_immunities //Lazy list, see mob_movespeed.dm /// The calculated mob speed slowdown based on the modifiers list var/cached_multiplicative_slowdown + /// List of action speed modifiers applying to this mob + var/list/actionspeed_modification //Lazy list, see mob_movespeed.dm + /// List of action speed modifiers ignored by this mob. List -> List (id) -> List (sources) + var/list/actionspeed_mod_immunities //Lazy list, see mob_movespeed.dm + /// The calculated mob action speed slowdown based on the modifiers list + var/cached_multiplicative_actions_slowdown /// List of action hud items the user has var/list/datum/action/actions /// A special action? No idea why this lives here diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index f49b4c7ded1..3f072210a85 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -317,15 +317,12 @@ duration = 600 /datum/status_effect/timecookie/on_apply() - if(ishuman(owner)) - var/mob/living/carbon/human/H - H.physiology.do_after_speed *= 0.95 + owner.add_actionspeed_modifier(/datum/actionspeed_modifier/timecookie) return ..() /datum/status_effect/timecookie/on_remove() - if(ishuman(owner)) - var/mob/living/carbon/human/H - H.physiology.do_after_speed /= 0.95 + owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/timecookie) + return ..() /datum/status_effect/lovecookie id = "lovecookie" diff --git a/tgstation.dme b/tgstation.dme index cc0dd610f01..2106a4b41ff 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -23,6 +23,7 @@ #include "code\__DEFINES\_tick.dm" #include "code\__DEFINES\access.dm" #include "code\__DEFINES\achievements.dm" +#include "code\__DEFINES\actionspeed_modification.dm" #include "code\__DEFINES\admin.dm" #include "code\__DEFINES\antagonists.dm" #include "code\__DEFINES\atmospherics.dm" @@ -1326,6 +1327,10 @@ #include "code\game\turfs\open\floor\plating\misc_plating.dm" #include "code\game\turfs\open\space\space.dm" #include "code\game\turfs\open\space\transit.dm" +#include "code\modules\actionspeed\_actionspeed_modifier.dm" +#include "code\modules\actionspeed\modifiers\base.dm" +#include "code\modules\actionspeed\modifiers\mood.dm" +#include "code\modules\actionspeed\modifiers\status_effects.dm" #include "code\modules\admin\admin.dm" #include "code\modules\admin\admin_investigate.dm" #include "code\modules\admin\admin_ranks.dm" From d27e20065a68c85987e1eb5a9a69b308c3e22763 Mon Sep 17 00:00:00 2001 From: tgstation-server Date: Tue, 25 Aug 2020 09:49:45 -0700 Subject: [PATCH 04/30] Automatic changelog generation for PR #53023 [ci skip] --- html/changelogs/AutoChangeLog-pr-53023.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-53023.yml diff --git a/html/changelogs/AutoChangeLog-pr-53023.yml b/html/changelogs/AutoChangeLog-pr-53023.yml new file mode 100644 index 00000000000..088b23dbfcc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-53023.yml @@ -0,0 +1,4 @@ +author: "floyd" +delete-after: True +changes: + - code_imp: "action speed modifiers" From 239a356e46380f0f6e8f45133590a38e9a85ed25 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Aug 2020 14:30:27 -0400 Subject: [PATCH 05/30] Fix admin shuttle adjust anoucement times (#53172) --- code/modules/admin/topic.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 0135d1f9bd5..a3a995bb28a 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -238,7 +238,7 @@ return SSshuttle.emergency.setTimer(timer*10) log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.") - minor_announce("The emergency shuttle will reach its destination in [DisplayTimeText(timer/10)].") + minor_announce("The emergency shuttle will reach its destination in [DisplayTimeText(timer)].") message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.") else if(href_list["trigger_centcom_recall"]) if(!check_rights(R_ADMIN)) From 3343863325c2e25fadd40e2e0b2862d6d2c639f4 Mon Sep 17 00:00:00 2001 From: Arkatos1 <43862960+Arkatos1@users.noreply.github.com> Date: Tue, 25 Aug 2020 21:33:11 +0200 Subject: [PATCH 06/30] tgui: Shuttle Console (#53168) --- code/modules/mining/mine_items.dm | 2 +- .../security_levels/security_levels.dm | 4 +- code/modules/shuttle/computer.dm | 139 +++++++++++------ code/modules/shuttle/emergency.dm | 2 +- code/modules/shuttle/ferry.dm | 12 -- code/modules/shuttle/shuttle.dm | 21 ++- .../tgui/interfaces/ShuttleConsole.js | 145 ++++++++++++++++++ tgui/public/tgui-panel.bundle.js | 2 +- tgui/public/tgui.bundle.js | 2 +- 9 files changed, 260 insertions(+), 69 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/ShuttleConsole.js diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index a9e8b3242e7..1d7d00efff0 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -71,7 +71,7 @@ circuit = /obj/item/circuitboard/computer/mining_shuttle shuttleId = "mining" possible_destinations = "mining_home;mining_away;landing_zone_dock;mining_public" - no_destination_swap = 1 + no_destination_swap = TRUE var/static/list/dumb_rev_heads = list() //ATTACK HAND IGNORING PARENT RETURN VALUE diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm index 0a8e4073d82..882d93e891f 100644 --- a/code/modules/security_levels/security_levels.dm +++ b/code/modules/security_levels/security_levels.dm @@ -60,7 +60,7 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN) if(is_station_level(FA.z)) FA.update_icon() for(var/obj/machinery/computer/shuttle/pod/pod in GLOB.machines) - pod.admin_controlled = 0 + pod.admin_controlled = FALSE if(SEC_LEVEL_DELTA) minor_announce(CONFIG_GET(string/alert_delta), "Attention! Delta security level reached!",1) if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL) @@ -73,7 +73,7 @@ GLOBAL_VAR_INIT(security_level, SEC_LEVEL_GREEN) if(is_station_level(FA.z)) FA.update_icon() for(var/obj/machinery/computer/shuttle/pod/pod in GLOB.machines) - pod.admin_controlled = 0 + pod.admin_controlled = FALSE if(level >= SEC_LEVEL_RED) for(var/obj/machinery/door/D in GLOB.machines) if(D.red_alert_access) diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/computer.dm index 34e93c30ce6..4aebab7ac15 100644 --- a/code/modules/shuttle/computer.dm +++ b/code/modules/shuttle/computer.dm @@ -5,69 +5,108 @@ icon_keyboard = "tech_key" light_color = LIGHT_COLOR_CYAN req_access = list( ) + /// ID of the attached shuttle var/shuttleId + /// Possible destinations of the attached shuttle var/possible_destinations = "" - var/admin_controlled - var/no_destination_swap = 0 + /// Variable dictating if the attached shuttle requires authorization from the admin staff to move + var/admin_controlled = FALSE + /// Variable dictating if the attached shuttle can change destinations mid-flight + var/no_destination_swap = FALSE + /// ID of the currently selected destination of the attached shuttle + var/destination + /// Authorization request cooldown to prevent request spam to admin staff + COOLDOWN_DECLARE(request_cooldown) -/obj/machinery/computer/shuttle/ui_interact(mob/user) - . = ..() +/obj/machinery/computer/shuttle/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ShuttleConsole", name) + ui.open() + +/obj/machinery/computer/shuttle/ui_data(mob/user) + var/list/data = list() var/list/options = params2list(possible_destinations) var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) - var/dat = "Donk Co. Transportation Division
Status: [M ? M.getStatusText() : "*Missing*"]

" - if(M) - var/destination_found - for(var/obj/docking_port/stationary/S in SSshuttle.stationary) - if(!options.Find(S.id)) - continue - if(!M.check_dock(S, silent=TRUE)) - continue - destination_found = 1 - dat += "Send to [S.name]
" - if(!destination_found) - dat += "Shuttle Locked
" - if(admin_controlled) - dat += "Authorized personnel only
" - dat += "Request Authorization
" - dat += "Close" + data["docked_location"] = M ? M.get_status_text_tgui() : "Unknown" + data["status"] = M.mode == SHUTTLE_IGNITING ? "Igniting" : M.mode != SHUTTLE_IDLE ? "In Transit" : "Idle" + data["locations"] = list() + data["locked"] = FALSE + data["authorization_required"] = admin_controlled + data["timer_str"] = M ? M.getTimerStr() : "00:00" + data["destination"] = destination + if(admin_controlled) + data["status"] = "Unauthorized Access" + if(!M) + data["status"] = "Missing" + return data + for(var/obj/docking_port/stationary/S in SSshuttle.stationary) + if(!options.Find(S.id)) + continue + if(!M.check_dock(S, silent = TRUE)) + continue + var/list/location_data = list( + id = S.id, + name = S.name + ) + data["locations"] += list(location_data) + if(length(data["locations"]) == 1) + for(var/location in data["locations"]) + destination = location["id"] + data["destination"] = destination + if(!length(data["locations"])) + data["locked"] = TRUE + data["status"] = "Locked" + return data - var/datum/browser/popup = new(user, "computer", M ? M.name : "shuttle", 300, 200) - popup.set_content("
[dat]
") - popup.open() - -/obj/machinery/computer/shuttle/Topic(href, href_list) - if(..()) +/obj/machinery/computer/shuttle/ui_act(action, params) + . = ..() + if(.) return - usr.set_machine(src) - src.add_fingerprint(usr) if(!allowed(usr)) to_chat(usr, "Access denied.") return - if(href_list["move"]) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) - if(M.launch_status == ENDGAME_LAUNCHED) - to_chat(usr, "You've already escaped. Never going back to that place again!") - return - if(no_destination_swap) - if(M.mode == SHUTTLE_RECHARGING) - to_chat(usr, "Shuttle engines are not ready for use.") + switch(action) + if("move") + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) + if(M.launch_status == ENDGAME_LAUNCHED) + to_chat(usr, "You've already escaped. Never going back to that place again!") return - if(M.mode != SHUTTLE_IDLE) - to_chat(usr, "Shuttle already in transit.") + if(no_destination_swap) + if(M.mode == SHUTTLE_RECHARGING) + to_chat(usr, "Shuttle engines are not ready for use.") + return + if(M.mode != SHUTTLE_IDLE) + to_chat(usr, "Shuttle already in transit.") + return + var/list/options = params2list(possible_destinations) + if(!(params["shuttle_id"] in options)) + log_admin("[usr] attempted to href dock exploit on [src] with target location \"[params["shuttle_id"]]\"") + message_admins("[usr] just attempted to href dock exploit on [src] with target location \"[params["shuttle_id"]]\"") return - if(!(href_list["move"] in params2list(possible_destinations))) - log_admin("[usr] attempted to forge a target location through a href exploit on [src] with target location \"[href_list["move"]]\"") - message_admins("[ADMIN_FULLMONTY(usr)] attempted to forge a target location through a href exploit on [src]") - return - switch(SSshuttle.moveShuttle(shuttleId, href_list["move"], 1)) - if(0) - say("Shuttle departing. Please stand away from the doors.") - log_shuttle("[key_name(usr)] has sent shuttle \"[M]\" towards \"[href_list["move"]]\", using [src].") - if(1) - to_chat(usr, "Invalid shuttle requested.") - else - to_chat(usr, "Unable to comply.") + switch(SSshuttle.moveShuttle(shuttleId, params["shuttle_id"], 1)) + if(0) + say("Shuttle departing. Please stand away from the doors.") + log_shuttle("[key_name(usr)] has sent shuttle \"[M]\" towards \"[params["shuttle_id"]]\", using [src].") + return TRUE + if(1) + to_chat(usr, "Invalid shuttle requested.") + else + to_chat(usr, "Unable to comply.") + if("set_destination") + var/target_destination = params["destination"] + if(target_destination) + destination = target_destination + return TRUE + if("request") + if(!COOLDOWN_FINISHED(src, request_cooldown)) + to_chat(usr, "CentCom is still processing last authorization request!") + return + COOLDOWN_START(src, request_cooldown, 1 MINUTES) + to_chat(usr, "Your request has been received by CentCom.") + to_chat(GLOB.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to CentCom.") + return TRUE /obj/machinery/computer/shuttle/emag_act(mob/user) if(obj_flags & EMAGGED) diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index a5c203a2f96..0f532aec134 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -465,7 +465,7 @@ /obj/machinery/computer/shuttle/pod name = "pod control computer" - admin_controlled = 1 + admin_controlled = TRUE possible_destinations = "pod_asteroid" icon = 'icons/obj/terminals.dmi' icon_state = "dorm_available" diff --git a/code/modules/shuttle/ferry.dm b/code/modules/shuttle/ferry.dm index f7e3bda44d1..1a26a564d79 100644 --- a/code/modules/shuttle/ferry.dm +++ b/code/modules/shuttle/ferry.dm @@ -5,7 +5,6 @@ shuttleId = "ferry" possible_destinations = "ferry_home;ferry_away" req_access = list(ACCESS_CENT_GENERAL) - var/allow_silicons = FALSE var/allow_emag = FALSE @@ -24,17 +23,6 @@ /obj/machinery/computer/shuttle/ferry/request name = "ferry console" circuit = /obj/item/circuitboard/computer/ferry/request - var/last_request //prevents spamming admins - var/cooldown = 600 possible_destinations = "ferry_home;ferry_away" req_access = list(ACCESS_CENT_GENERAL) resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/machinery/computer/shuttle/ferry/request/Topic(href, href_list) - ..() - if(href_list["request"]) - if(last_request && (last_request + cooldown > world.time)) - return - last_request = world.time - to_chat(usr, "Your request has been received by CentCom.") - to_chat(GLOB.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to CentCom.") diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 15f2875e7ed..2a1523a412d 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -708,6 +708,26 @@ else return "00:00" +/** + * Gets shuttle location status in a form of string for tgui interfaces + */ +/obj/docking_port/mobile/proc/get_status_text_tgui() + var/obj/docking_port/stationary/dockedAt = get_docked() + var/docked_at = dockedAt?.name || "Unknown" + if(istype(dockedAt, /obj/docking_port/stationary/transit)) + if(timeLeft() > 1 HOURS) + return "Hyperspace" + else + var/obj/docking_port/stationary/dst + if(mode == SHUTTLE_RECALL) + dst = previous + else + dst = destination + return "In transit towards [dst?.name || "unknown location"]" + else if(mode == SHUTTLE_RECHARGING) + return "[docked_at], recharging [getTimerStr()]" + else + return docked_at /obj/docking_port/mobile/proc/getStatusText() var/obj/docking_port/stationary/dockedAt = get_docked() @@ -727,7 +747,6 @@ else return docked_at - /obj/docking_port/mobile/proc/getDbgStatusText() var/obj/docking_port/stationary/dockedAt = get_docked() . = (dockedAt && dockedAt.name) ? dockedAt.name : "unknown" diff --git a/tgui/packages/tgui/interfaces/ShuttleConsole.js b/tgui/packages/tgui/interfaces/ShuttleConsole.js new file mode 100644 index 00000000000..64048e44c54 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ShuttleConsole.js @@ -0,0 +1,145 @@ +import { useBackend } from '../backend'; +import { Box, Button, Dropdown, Flex, Icon, LabeledList, Modal, Section } from '../components'; +import { Window } from '../layouts'; + +export const ShuttleConsole = (props, context) => { + const { act, data } = useBackend(context); + const { + authorization_required, + } = data; + return ( + + {!!authorization_required && ( + + + + + + + {'SHUTTLE LOCKED'} + + + +