From 4fde078f23474cdd2b927d88e219748a9ca34231 Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Sat, 23 Nov 2024 15:50:58 +0200 Subject: [PATCH] Refactors projectile dampeners and adds some new visuals (#87913) Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/__DEFINES/traits/declarations.dm | 3 + code/__HELPERS/filters.dm | 10 +- code/_globalvars/traits/_traits.dm | 1 + .../fields/projectile_dampener.dm | 117 --------- .../projectile_dampener.dm | 238 ++++++++++++++++++ .../projectile_dampener_effects.dm | 38 +++ code/game/objects/items/robot/items/tools.dm | 37 +-- .../mob/living/basic/bots/medbot/medbot.dm | 1 - .../modules/mob/living/silicon/robot/robot.dm | 19 ++ code/modules/mod/modules/modules_security.dm | 22 -- tgstation.dme | 3 +- 11 files changed, 318 insertions(+), 171 deletions(-) delete mode 100644 code/datums/proximity_monitor/fields/projectile_dampener.dm create mode 100644 code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener.dm create mode 100644 code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener_effects.dm diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 94064352b84..eb623ed708e 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -1391,4 +1391,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Gives a little examine to their body that they can be revived with a soul #define TRAIT_GHOSTROLE_ON_REVIVE "ghostrole_on_revive" +///Trait given to atoms currently affected by projectile dampeners +#define TRAIT_GOT_DAMPENED "got_dampened" + // END TRAIT DEFINES diff --git a/code/__HELPERS/filters.dm b/code/__HELPERS/filters.dm index 14233a28076..930c889b09c 100644 --- a/code/__HELPERS/filters.dm +++ b/code/__HELPERS/filters.dm @@ -312,9 +312,13 @@ GLOBAL_LIST_INIT(master_filter_info, list( animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) animate(offset = random_roll - 1, time = rand() * 20 + 10) -/proc/remove_wibbly_filters(atom/in_atom) +/proc/remove_wibbly_filters(atom/in_atom, remove_duration = 0) var/filter for(var/i in 1 to 7) filter = in_atom.get_filter("wibbly-[i]") - animate(filter) - in_atom.remove_filter("wibbly-[i]") + if(remove_duration == 0) + animate(filter) + in_atom.remove_filter("wibbly-[i]") + continue + animate(filter, x = 0, y = 0, size = 0, offset = 0, time = remove_duration) + addtimer(CALLBACK(in_atom, TYPE_PROC_REF(/datum, remove_filter), "wibbly-[i]"), remove_duration) diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index cd68ebac4ac..c54d78421a3 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -22,6 +22,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_FISHING_SPOT" = TRAIT_FISHING_SPOT, "TRAIT_FOOD_CHEF_MADE" = TRAIT_FOOD_CHEF_MADE, "TRAIT_FOOD_FRIED" = TRAIT_FOOD_FRIED, + "TRAIT_GOT_DAMPENED" = TRAIT_GOT_DAMPENED, "TRAIT_QUALITY_FOOD_INGREDIENT" = TRAIT_QUALITY_FOOD_INGREDIENT, "TRAIT_FOOD_SILVER" = TRAIT_FOOD_SILVER, "TRAIT_KEEP_TOGETHER" = TRAIT_KEEP_TOGETHER, diff --git a/code/datums/proximity_monitor/fields/projectile_dampener.dm b/code/datums/proximity_monitor/fields/projectile_dampener.dm deleted file mode 100644 index fe23fe0be33..00000000000 --- a/code/datums/proximity_monitor/fields/projectile_dampener.dm +++ /dev/null @@ -1,117 +0,0 @@ - -//Projectile dampening field that slows projectiles and lowers their damage for an energy cost deducted every 1/5 second. -//Only use square radius for this! -/datum/proximity_monitor/advanced/projectile_dampener - var/static/image/edgeturf_south = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_south") - var/static/image/edgeturf_north = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_north") - var/static/image/edgeturf_west = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_west") - var/static/image/edgeturf_east = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_east") - var/static/image/northwest_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northwest") - var/static/image/southwest_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southwest") - var/static/image/northeast_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northeast") - var/static/image/southeast_corner = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southeast") - var/static/image/generic_edge = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_generic") - var/list/obj/projectile/tracked = list() - var/list/obj/projectile/staging = list() - // lazylist that keeps track of the overlays added to the edge of the field - var/list/edgeturf_effects - -/datum/proximity_monitor/advanced/projectile_dampener/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, atom/projector) - ..() - RegisterSignal(projector, COMSIG_QDELETING, PROC_REF(on_projector_del)) - recalculate_field(full_recalc = TRUE) - START_PROCESSING(SSfastprocess, src) - -/datum/proximity_monitor/advanced/projectile_dampener/Destroy() - STOP_PROCESSING(SSfastprocess, src) - for(var/obj/projectile/projectile in tracked) - release_projectile(projectile) - return ..() - -/datum/proximity_monitor/advanced/projectile_dampener/recalculate_field(full_recalc) - full_recalc = TRUE // We always perform a full recalc because we need to update ALL the sprites - return ..() - -/datum/proximity_monitor/advanced/projectile_dampener/process() - var/list/ranged = list() - for(var/obj/projectile/projectile in range(current_range, get_turf(host))) - ranged += projectile - for(var/obj/projectile/projectile in tracked) - if(!(projectile in ranged) || !projectile.loc) - release_projectile(projectile) - -/datum/proximity_monitor/advanced/projectile_dampener/setup_edge_turf(turf/target) - . = ..() - var/image/overlay = get_edgeturf_overlay(get_edgeturf_direction(target)) - var/obj/effect/abstract/effect = new(target) // Makes the field visible to players. - effect.icon = overlay.icon - effect.icon_state = overlay.icon_state - effect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - effect.layer = ABOVE_ALL_MOB_LAYER - SET_PLANE(effect, ABOVE_GAME_PLANE, target) - LAZYSET(edgeturf_effects, target, effect) - -/datum/proximity_monitor/advanced/projectile_dampener/on_z_change(datum/source) - recalculate_field(full_recalc = TRUE) - -/datum/proximity_monitor/advanced/projectile_dampener/cleanup_edge_turf(turf/target) - . = ..() - var/obj/effect/abstract/effect = LAZYACCESS(edgeturf_effects, target) - LAZYREMOVE(edgeturf_effects, target) - if(effect) - qdel(effect) - -/datum/proximity_monitor/advanced/projectile_dampener/proc/get_edgeturf_overlay(direction) - switch(direction) - if(NORTH) - return edgeturf_north - if(SOUTH) - return edgeturf_south - if(EAST) - return edgeturf_east - if(WEST) - return edgeturf_west - if(NORTHEAST) - return northeast_corner - if(NORTHWEST) - return northwest_corner - if(SOUTHEAST) - return southeast_corner - if(SOUTHWEST) - return southwest_corner - else - return generic_edge - -/datum/proximity_monitor/advanced/projectile_dampener/proc/capture_projectile(obj/projectile/projectile) - if(projectile in tracked) - return - SEND_SIGNAL(src, COMSIG_DAMPENER_CAPTURE, projectile) - tracked += projectile - -/datum/proximity_monitor/advanced/projectile_dampener/proc/release_projectile(obj/projectile/projectile) - SEND_SIGNAL(src, COMSIG_DAMPENER_RELEASE, projectile) - tracked -= projectile - -/datum/proximity_monitor/advanced/projectile_dampener/proc/on_projector_del(datum/source) - SIGNAL_HANDLER - qdel(src) - -/datum/proximity_monitor/advanced/projectile_dampener/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) - if(isprojectile(movable) && get_dist(movable, host) > current_range) - if(movable in tracked) - release_projectile(movable) - -/datum/proximity_monitor/advanced/projectile_dampener/field_edge_crossed(atom/movable/movable, turf/location, turf/old_location) - if(isprojectile(movable)) - capture_projectile(movable) - -/datum/proximity_monitor/advanced/projectile_dampener/peaceborg/process(seconds_per_tick) - for(var/mob/living/silicon/robot/borg in range(current_range, get_turf(host))) - if(!borg.has_buckled_mobs()) - continue - for(var/mob/living/buckled_mob in borg.buckled_mobs) - buckled_mob.visible_message(span_warning("[buckled_mob] is knocked off of [borg] by the charge in [borg]'s chassis induced by the hyperkinetic dampener field!")) //I know it's bad. - buckled_mob.Paralyze(1 SECONDS) - borg.unbuckle_mob(buckled_mob) - do_sparks(5, 0, buckled_mob) - ..() diff --git a/code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener.dm b/code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener.dm new file mode 100644 index 00000000000..3185872efad --- /dev/null +++ b/code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener.dm @@ -0,0 +1,238 @@ +#define CHANGING_OFFSET "changing_offset" +#define OVERLAY_DATA "overlay_data" +#define STARTING_POSITION "starting_position" +#define ANIMATE_DAMPENER_TIME 1.5 SECONDS + +//Projectile dampening field that slows projectiles and lowers their damage for an energy cost deducted every 1/5 second. +//Only use square radius for this! +/datum/proximity_monitor/advanced/projectile_dampener + edge_is_a_field = TRUE + var/static/list/effect_direction_images = list( + "[SOUTH]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_south"), + "[NORTH]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_north"), + "[WEST]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_west"), + "[EAST]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_east"), + "[NORTHWEST]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northwest"), + "[SOUTHWEST]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southwest"), + "[NORTHEAST]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_northeast"), + "[SOUTHEAST]" = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_southeast"), + ) + var/static/image/generic_edge = image('icons/effects/fields.dmi', icon_state = "projectile_dampen_generic") + ///overlay we apply to caught bullets + var/static/image/new_bullet_overlay= image('icons/effects/fields.dmi', "projectile_dampen_effect") + ///list of all the visual effects we keep track of + var/list/edgeturf_effects = list() + ///atom that contains all the fields in its vis_contents + var/atom/movable/field_effect_holder/my_movable + /// datum that holds the effects we apply on caught bullets + var/datum/dampener_projectile_effects/bullet_effects + +/datum/proximity_monitor/advanced/projectile_dampener/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, atom/projector, datum/dampener_projectile_effects/effects_typepath) + ..() + RegisterSignal(projector, COMSIG_QDELETING, PROC_REF(on_projector_del)) + var/atom/movable/movable_host = _host + my_movable = new(get_turf(_host)) + my_movable.transform = my_movable.transform.Scale(current_range, current_range) + my_movable.set_glide_size(movable_host.glide_size) + bullet_effects = effects_typepath ? new effects_typepath() : new + draw_effect() + +/datum/proximity_monitor/advanced/projectile_dampener/on_moved(atom/movable/source, atom/old_loc) + . = ..() + my_movable.Move(source.loc, get_dir(my_movable.loc, source.loc), source.glide_size) + +/datum/proximity_monitor/advanced/projectile_dampener/on_z_change(datum/source) + recalculate_field(full_recalc = TRUE) + +/datum/proximity_monitor/advanced/projectile_dampener/field_edge_crossed(atom/movable/movable, turf/location, turf/old_location) + . = ..() + if(!isprojectile(movable)) + return + determine_wobble(location) + +/datum/proximity_monitor/advanced/projectile_dampener/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) + . = ..() + if(!isprojectile(movable)) + return + determine_wobble(old_location) + +/datum/proximity_monitor/advanced/projectile_dampener/field_turf_crossed(atom/movable/movable, turf/old_location, turf/new_location) + if(!isprojectile(movable) || HAS_TRAIT_FROM(movable, TRAIT_GOT_DAMPENED, REF(src))) + return + catch_bullet_effect(movable) + +/datum/proximity_monitor/advanced/projectile_dampener/field_turf_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) + if(!isprojectile(movable) || get_dist(new_location, host) <= current_range) + return + release_bullet_effect(movable) + +/datum/proximity_monitor/advanced/projectile_dampener/setup_field_turf(turf/target) + for(var/atom/possible_projectile in target) + if(isprojectile(possible_projectile)) + catch_bullet_effect(possible_projectile) + +/datum/proximity_monitor/advanced/projectile_dampener/cleanup_field_turf(turf/target) + for(var/atom/possible_projectile in target) + if(isprojectile(possible_projectile) && HAS_TRAIT_FROM(possible_projectile, TRAIT_GOT_DAMPENED, REF(src))) + release_bullet_effect(possible_projectile) + +///proc that applies the wobbly effect on point of bullet entry +/datum/proximity_monitor/advanced/projectile_dampener/proc/determine_wobble(turf/location) + var/coord_x = location.x - host.x + var/coord_y = location.y - host.y + var/obj/effect/overlay/vis/field/my_field = edgeturf_effects["[coord_x],[coord_y]"] + my_field?.set_wobbly(0.15 SECONDS) + +/datum/proximity_monitor/advanced/projectile_dampener/proc/projectile_overlay_updated(atom/source, list/overlays) + SIGNAL_HANDLER + + if(!isnull(new_bullet_overlay) && HAS_TRAIT_FROM(source, TRAIT_GOT_DAMPENED, REF(src))) + overlays += new_bullet_overlay + +///a bullet has entered our field, apply the dampening effects to it +/datum/proximity_monitor/advanced/projectile_dampener/proc/catch_bullet_effect(obj/projectile/bullet) + ADD_TRAIT(bullet,TRAIT_GOT_DAMPENED, REF(src)) + RegisterSignal(bullet, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(projectile_overlay_updated)) + SEND_SIGNAL(src, COMSIG_DAMPENER_CAPTURE, bullet) + bullet_effects.apply_effects(bullet) + bullet.update_appearance() + +///removing the effects after it has exited our field +/datum/proximity_monitor/advanced/projectile_dampener/proc/release_bullet_effect(obj/projectile/bullet) + REMOVE_TRAIT(bullet, TRAIT_GOT_DAMPENED, REF(src)) + SEND_SIGNAL(src, COMSIG_DAMPENER_RELEASE, bullet) + bullet_effects.remove_effects(bullet) + bullet.update_appearance() + UnregisterSignal(bullet, COMSIG_ATOM_UPDATE_OVERLAYS) + +///rendering all the field visuals. first we render the corners, then we connect them +/datum/proximity_monitor/advanced/projectile_dampener/proc/draw_effect() + var/max_pixel_offset = current_range * ICON_SIZE_ALL + var/top_right_corner = list(effect_direction_images["[NORTHEAST]"], max_pixel_offset, max_pixel_offset) + var/top_left_corner = list(effect_direction_images["[NORTHWEST]"], -max_pixel_offset, max_pixel_offset) + var/bottom_left_corner = list(effect_direction_images["[SOUTHWEST]"], -max_pixel_offset, -max_pixel_offset) + var/bottom_right_corner = list(effect_direction_images["[SOUTHEAST]"], max_pixel_offset, -max_pixel_offset) + + var/list/corners = list(top_right_corner, top_left_corner, bottom_left_corner, bottom_right_corner) + for(var/corner in corners) + draw_corner(corner) + + var/list/corners_to_connect = list( + list(OVERLAY_DATA = effect_direction_images["[NORTH]"], CHANGING_OFFSET = "x_offset", STARTING_POSITION = max_pixel_offset), + list(OVERLAY_DATA = effect_direction_images["[SOUTH]"], CHANGING_OFFSET = "x_offset", STARTING_POSITION = -max_pixel_offset), + list(OVERLAY_DATA = effect_direction_images["[WEST]"], CHANGING_OFFSET = "y_offset", STARTING_POSITION = -max_pixel_offset), + list(OVERLAY_DATA = effect_direction_images["[EAST]"], CHANGING_OFFSET = "y_offset", STARTING_POSITION = max_pixel_offset), + ) + for(var/direction in corners_to_connect) + draw_edge(direction, max_pixel_offset) + +///rendering the corners +/datum/proximity_monitor/advanced/projectile_dampener/proc/draw_corner(list/corner_data) + var/obj/effect/overlay/vis/field/corner_effect = new() + var/image/image_overlay = corner_data[1] + corner_effect.icon = image_overlay.icon + corner_effect.icon_state = image_overlay.icon_state + corner_effect.alpha = 0 + corner_effect.pixel_x = corner_data[2] + corner_effect.pixel_y = corner_data[3] + add_effect_to_host(corner_effect) + +///connecting the corners to one another +/datum/proximity_monitor/advanced/projectile_dampener/proc/draw_edge(list/edge_data, target_offset) + var/starting_offset = edge_data[STARTING_POSITION] + var/current_offset = (-1 * target_offset) + ICON_SIZE_ALL + var/image/overlay = edge_data[OVERLAY_DATA] + while(current_offset != target_offset) + var/obj/effect/overlay/vis/field/edge_effect = new() + edge_effect.alpha = 0 + edge_effect.icon = overlay.icon + edge_effect.icon_state = overlay.icon_state + + if(edge_data[CHANGING_OFFSET] == "x_offset") + edge_effect.pixel_y = starting_offset + edge_effect.pixel_x = current_offset + else + edge_effect.pixel_x = starting_offset + edge_effect.pixel_y = current_offset + add_effect_to_host(edge_effect) + current_offset += ICON_SIZE_ALL + +///handles adding the visual effect's data +/datum/proximity_monitor/advanced/projectile_dampener/proc/add_effect_to_host(obj/effect/overlay/vis/field/effect_to_add) + my_movable.vis_contents += effect_to_add + var/coordinate_x = effect_to_add.pixel_x / ICON_SIZE_ALL + var/coordinate_y = effect_to_add.pixel_y / ICON_SIZE_ALL + effect_to_add.transform = effect_to_add.transform.Scale(1 / current_range, 1 / current_range) + edgeturf_effects["[coordinate_x],[coordinate_y]"] = effect_to_add + effect_to_add.set_wobbly(wobble_duration = ANIMATE_DAMPENER_TIME) + animate(effect_to_add, alpha = 255, time = ANIMATE_DAMPENER_TIME, flags = ANIMATION_PARALLEL) + +/datum/proximity_monitor/advanced/projectile_dampener/proc/on_projector_del(datum/source) + SIGNAL_HANDLER + qdel(src) + +/datum/proximity_monitor/advanced/projectile_dampener/Destroy() + for(var/coordinates in edgeturf_effects) + var/obj/effect/overlay/vis/field/effect_to_remove = edgeturf_effects[coordinates] + edgeturf_effects -= coordinates + effect_to_remove.set_wobbly(wobble_duration = ANIMATE_DAMPENER_TIME) + animate(effect_to_remove, alpha = 0, time = ANIMATE_DAMPENER_TIME, flags = ANIMATION_PARALLEL) + QDEL_IN(my_movable, ANIMATE_DAMPENER_TIME) + my_movable = null + bullet_effects = null + return ..() + +/datum/proximity_monitor/advanced/projectile_dampener/peaceborg + +/datum/proximity_monitor/advanced/projectile_dampener/peaceborg/field_turf_crossed(atom/movable/movable, turf/old_location, turf/new_location) + . = ..() + if(!iscyborg(movable) || !HAS_TRAIT_FROM(movable, TRAIT_GOT_DAMPENED, REF(src))) + ADD_TRAIT(movable, TRAIT_GOT_DAMPENED, REF(src)) + +/datum/proximity_monitor/advanced/projectile_dampener/peaceborg/field_turf_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) + if(!iscyborg(movable) || get_dist(new_location, host) <= current_range) + return + REMOVE_TRAIT(movable, TRAIT_GOT_DAMPENED, REF(src)) + +/datum/proximity_monitor/advanced/projectile_dampener/peaceborg/setup_field_turf(turf/target) + for(var/atom/interesting_atom as anything in target) + if(iscyborg(interesting_atom)) + ADD_TRAIT(interesting_atom, TRAIT_GOT_DAMPENED, REF(src)) + if(isprojectile(interesting_atom)) + catch_bullet_effect(interesting_atom) + +/datum/proximity_monitor/advanced/projectile_dampener/peaceborg/cleanup_field_turf(turf/target) + for(var/atom/interesting_atom as anything in target) + if(iscyborg(interesting_atom)) + REMOVE_TRAIT(interesting_atom, TRAIT_GOT_DAMPENED, REF(src)) + if(isprojectile(interesting_atom)) + release_bullet_effect(interesting_atom) + +/obj/effect/overlay/vis/field + appearance_flags = PIXEL_SCALE|LONG_GLIDE + vis_flags = parent_type::vis_flags | VIS_INHERIT_PLANE + ///are we currently WOBBLING + var/wobbling_effect = FALSE + +/obj/effect/overlay/vis/field/proc/set_wobbly(wobble_duration) + if(wobbling_effect) + return + wobbling_effect = TRUE + apply_wibbly_filters(src) + addtimer(CALLBACK(src, PROC_REF(remove_wobbly)), wobble_duration) + +/obj/effect/overlay/vis/field/proc/remove_wobbly() + if(QDELETED(src)) + return + remove_wibbly_filters(src, remove_duration = 0.25 SECONDS) + addtimer(VARSET_CALLBACK(src, wobbling_effect, FALSE), 0.25 SECONDS) + +/atom/movable/field_effect_holder + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + appearance_flags = PIXEL_SCALE|LONG_GLIDE + plane = ABOVE_GAME_PLANE + +#undef CHANGING_OFFSET +#undef OVERLAY_DATA +#undef STARTING_POSITION +#undef ANIMATE_DAMPENER_TIME diff --git a/code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener_effects.dm b/code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener_effects.dm new file mode 100644 index 00000000000..bd07d7836fc --- /dev/null +++ b/code/datums/proximity_monitor/fields/projectile_dampener/projectile_dampener_effects.dm @@ -0,0 +1,38 @@ +///datum that holds the effects we have on bullet catching +/datum/dampener_projectile_effects + ///new projectiles speeds + var/projectile_speed_multiplier = 0.4 + /// new projectiles damage + var/projectile_damage_multiplier = 0.75 + /// new projectiles knockdown + var/projectile_knockdown_multiplier = 0.66 + /// new projectiles stun + var/projectile_stun_multiplier = 0.66 + /// new projectiles stamina damage + var/projectile_stamina_multiplier = 0.66 + +/datum/dampener_projectile_effects/proc/apply_effects(obj/projectile/bullet) + if(projectile_speed_multiplier) + bullet.speed *= projectile_speed_multiplier + if(projectile_damage_multiplier) + bullet.damage *= projectile_damage_multiplier + if(projectile_knockdown_multiplier) + bullet.knockdown *= projectile_knockdown_multiplier + if(projectile_stamina_multiplier) + bullet.stamina *= projectile_stamina_multiplier + if(projectile_stun_multiplier) + bullet.stun *= projectile_stun_multiplier + +/datum/dampener_projectile_effects/proc/remove_effects(obj/projectile/bullet) + bullet.speed /= projectile_speed_multiplier + bullet.damage /= projectile_damage_multiplier + bullet.knockdown /= projectile_knockdown_multiplier + bullet.stamina /= projectile_stamina_multiplier + bullet.stun /= projectile_stun_multiplier + +/datum/dampener_projectile_effects/peacekeeper + projectile_speed_multiplier = 0.66 + projectile_damage_multiplier = 0.5 + projectile_knockdown_multiplier = 1 + projectile_stun_multiplier = 1 + projectile_stamina_multiplier = 1 diff --git a/code/game/objects/items/robot/items/tools.dm b/code/game/objects/items/robot/items/tools.dm index 6e16388b4b1..dfea34fba9b 100644 --- a/code/game/objects/items/robot/items/tools.dm +++ b/code/game/objects/items/robot/items/tools.dm @@ -31,19 +31,13 @@ var/mob/living/silicon/robot/host = null /// The field var/datum/proximity_monitor/advanced/projectile_dampener/peaceborg/dampening_field - var/projectile_damage_coefficient = 0.5 /// Energy cost per tracked projectile damage amount per second var/projectile_damage_tick_ecost_coefficient = 10 - /** - * Speed coefficient - * Higher the coefficient faster the projectile. - */ - var/projectile_speed_coefficient = 0.66 /// Energy cost per tracked projectile per second var/projectile_tick_speed_ecost = 75 - /// Projectile sent out by the dampener - var/list/obj/projectile/tracked - var/image/projectile_effect + /// Projectiles dampened by our dampener + var/list/tracked_bullet_cost = list() + /// the radius of our field var/field_radius = 3 var/active = FALSE /// activation cooldown @@ -55,8 +49,6 @@ energy_recharge = 5000 /obj/item/borg/projectile_dampen/Initialize(mapload) - projectile_effect = image('icons/effects/fields.dmi', "projectile_dampen_effect") - tracked = list() START_PROCESSING(SSfastprocess, src) host = loc RegisterSignal(host, COMSIG_LIVING_DEATH, PROC_REF(on_death)) @@ -94,7 +86,7 @@ if(istype(dampening_field)) QDEL_NULL(dampening_field) var/mob/living/silicon/robot/owner = get_host() - dampening_field = new(owner, field_radius, TRUE, src) + dampening_field = new(owner, field_radius, TRUE, src, /datum/dampener_projectile_effects/peacekeeper) RegisterSignal(dampening_field, COMSIG_DAMPENER_CAPTURE, PROC_REF(dampen_projectile)) RegisterSignal(dampening_field, COMSIG_DAMPENER_RELEASE, PROC_REF(restore_projectile)) owner?.model.allow_riding = FALSE @@ -103,8 +95,7 @@ /obj/item/borg/projectile_dampen/proc/deactivate_field() QDEL_NULL(dampening_field) visible_message(span_warning("\The [src] shuts off!")) - for(var/projectile in tracked) - restore_projectile(projectile = projectile) + tracked_bullet_cost.Cut() active = FALSE var/mob/living/silicon/robot/owner = get_host() @@ -137,11 +128,9 @@ /obj/item/borg/projectile_dampen/proc/process_usage(seconds_per_tick) var/usage = 0 - for(var/obj/projectile/inner_projectile as anything in tracked) - if(!inner_projectile.is_hostile_projectile()) - continue + for(var/projectile as anything in tracked_bullet_cost) usage += projectile_tick_speed_ecost * seconds_per_tick - usage += tracked[inner_projectile] * projectile_damage_tick_ecost_coefficient * seconds_per_tick + usage += tracked_bullet_cost[projectile] * projectile_damage_tick_ecost_coefficient * seconds_per_tick energy = clamp(energy - usage, 0, maxenergy) if(energy <= 0) deactivate_field() @@ -161,18 +150,12 @@ /obj/item/borg/projectile_dampen/proc/dampen_projectile(datum/source, obj/projectile/projectile) SIGNAL_HANDLER - tracked[projectile] = projectile.damage - projectile.damage *= projectile_damage_coefficient - projectile.speed *= projectile_speed_coefficient - projectile.add_overlay(projectile_effect) + if(projectile.is_hostile_projectile()) + tracked_bullet_cost[REF(projectile)] = projectile.damage /obj/item/borg/projectile_dampen/proc/restore_projectile(datum/source, obj/projectile/projectile) SIGNAL_HANDLER - - tracked -= projectile - projectile.damage /= projectile_damage_coefficient - projectile.speed /= projectile_speed_coefficient - projectile.cut_overlay(projectile_effect) + tracked_bullet_cost -= REF(projectile) //bare minimum omni-toolset for modularity /obj/item/borg/cyborg_omnitool diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.dm b/code/modules/mob/living/basic/bots/medbot/medbot.dm index 2466ab931cd..aeb03354db0 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot.dm @@ -145,7 +145,6 @@ remove_hat_signals = remove_hat,\ traits_prevent_checks = prevent_checks,\ ) - RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack)) if(!HAS_TRAIT(SSstation, STATION_TRAIT_MEDBOT_MANIA) || !mapload || !is_station_level(z)) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 67a61c4b832..a592a605e5a 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -19,6 +19,7 @@ AddElement(/datum/element/ridable, /datum/component/riding/creature/cyborg) RegisterSignal(src, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(charge)) RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater)) + RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_GOT_DAMPENED), PROC_REF(on_dampen)) robot_modules_background = new() robot_modules_background.icon_state = "block" @@ -963,6 +964,10 @@ buckle_mob_flags= RIDER_NEEDS_ARM // just in case return ..() +/mob/living/silicon/robot/post_buckle_mob(mob/living/victim_to_boot) + if(HAS_TRAIT(src, TRAIT_GOT_DAMPENED)) + eject_riders() + /mob/living/silicon/robot/can_resist() if(lockcharge) balloon_alert(src, "locked down!") @@ -1059,3 +1064,17 @@ . = ..() set_stat(CONSCIOUS) //This is a horrible hack, but silicon code forced my hand update_stat() + +/mob/living/silicon/robot/proc/on_dampen() + SIGNAL_HANDLER + eject_riders() + +/mob/living/silicon/robot/proc/eject_riders() + if(!length(buckled_mobs)) + return + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.visible_message(span_warning("[buckled_mob] is knocked off of [src] by the charge in [src]'s chassis induced by the hyperkinetic dampener field!")) + buckled_mob.Paralyze(1 SECONDS) + unbuckle_mob(buckled_mob) + do_sparks(5, 0, src) + diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm index dff5a000b92..b705de0f4be 100644 --- a/code/modules/mod/modules/modules_security.dm +++ b/code/modules/mod/modules/modules_security.dm @@ -326,32 +326,10 @@ if(istype(dampening_field)) QDEL_NULL(dampening_field) dampening_field = new(mod.wearer, field_radius, TRUE, src) - RegisterSignal(dampening_field, COMSIG_DAMPENER_CAPTURE, PROC_REF(dampen_projectile)) - RegisterSignal(dampening_field, COMSIG_DAMPENER_RELEASE, PROC_REF(release_projectile)) /obj/item/mod/module/projectile_dampener/on_deactivation(display_message, deleting = FALSE) QDEL_NULL(dampening_field) -/obj/item/mod/module/projectile_dampener/proc/dampen_projectile(datum/source, obj/projectile/projectile) - SIGNAL_HANDLER - - projectile.damage *= damage_multiplier - projectile.stamina *= damage_multiplier - projectile.stun *= debuff_multiplier - projectile.knockdown *= debuff_multiplier - projectile.speed *= speed_multiplier - projectile.add_overlay(projectile_effect) - -/obj/item/mod/module/projectile_dampener/proc/release_projectile(datum/source, obj/projectile/projectile) - SIGNAL_HANDLER - - projectile.damage /= damage_multiplier - projectile.speed /= speed_multiplier - projectile.stamina /= damage_multiplier - projectile.stun /= debuff_multiplier - projectile.knockdown /= debuff_multiplier - projectile.cut_overlay(projectile_effect) - ///Active Sonar - Displays a hud circle on the turf of any living creatures in the given radius /obj/item/mod/module/active_sonar name = "MOD active sonar" diff --git a/tgstation.dme b/tgstation.dme index c83d7d87c42..16d883964c4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1738,9 +1738,10 @@ #include "code\datums\proximity_monitor\proximity_monitor.dm" #include "code\datums\proximity_monitor\fields\ai_target_tracking.dm" #include "code\datums\proximity_monitor\fields\gravity.dm" -#include "code\datums\proximity_monitor\fields\projectile_dampener.dm" #include "code\datums\proximity_monitor\fields\timestop.dm" #include "code\datums\proximity_monitor\fields\void_storm.dm" +#include "code\datums\proximity_monitor\fields\projectile_dampener\projectile_dampener.dm" +#include "code\datums\proximity_monitor\fields\projectile_dampener\projectile_dampener_effects.dm" #include "code\datums\quirks\_quirk.dm" #include "code\datums\quirks\_quirk_constant_data.dm" #include "code\datums\quirks\negative_quirks\addict.dm"