diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index b565b32aa49..2383727cd76 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -223,11 +223,6 @@ /// From /obj/effect/temp_visual/resonance/burst() : (mob/creator, mob/living/hit_living) #define COMSIG_LIVING_RESONATOR_BURST "living_resonator_burst" -/// From /obj/projectile/attempt_parry() : (obj/projectile/parried_projectile) -#define COMSIG_LIVING_PROJECTILE_PARRYING "living_projectile_parrying" - /// Return to allow the parry to happen - #define ALLOW_PARRY (1<<0) - /// From /obj/projectile/on_parry() : (obj/projectile/parried_projectile) #define COMSIG_LIVING_PROJECTILE_PARRIED "living_projectile_parried" /// Return to prevent the projectile from executing any code in on_parry() diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index d34de1e3567..d7e4af3a738 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -389,10 +389,16 @@ ///sent to targets during the process_hit proc of projectiles #define COMSIG_PROJECTILE_PREHIT "com_proj_prehit" #define PROJECTILE_INTERRUPT_HIT (1<<0) +///from /obj/projectile/pixel_move(): () +#define COMSIG_PROJECTILE_PIXEL_STEP "projectile_pixel_step" +///sent to self during the process_hit proc of projectiles +#define COMSIG_PROJECTILE_SELF_PREHIT "com_proj_prehit" ///from the base of /obj/projectile/Range(): () #define COMSIG_PROJECTILE_RANGE "projectile_range" ///from the base of /obj/projectile/on_range(): () #define COMSIG_PROJECTILE_RANGE_OUT "projectile_range_out" +///from the base of /obj/projectile/process(): () +#define COMSIG_PROJECTILE_BEFORE_MOVE "projectile_before_move" ///from [/obj/item/proc/tryEmbed] sent when trying to force an embed (mainly for projectiles and eating glass) #define COMSIG_EMBED_TRY_FORCE "item_try_embed" #define COMPONENT_EMBED_SUCCESS (1<<1) diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 90e3fc38265..3d80117cfb4 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -1208,5 +1208,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Trait given to a turf that should not be allowed to be terraformed, such as turfs holding ore vents. #define TRAIT_NO_TERRAFORM "no_terraform" +///Trait which allows mobs to parry mining mob projectiles +#define TRAIT_MINING_PARRYING "mining_parrying" // END TRAIT DEFINES diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 47887ede45e..f11cc50dbbd 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -292,3 +292,6 @@ /// Trait when a drink was renamed by a shaker #define SHAKER_LABEL_TRAIT "shaker_trait" + +/// Trait added by style component +#define STYLE_TRAIT "style" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index a71e425d827..96b3373d7b2 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -525,6 +525,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_XENO_IMMUNE" = TRAIT_XENO_IMMUNE, "TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING, "TRAIT_XRAY_VISION" = TRAIT_XRAY_VISION, + "TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING, ), /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 55a9d37b701..8a0bb416011 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -250,6 +250,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_XENO_IMMUNE" = TRAIT_XENO_IMMUNE, "TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING, "TRAIT_XRAY_VISION" = TRAIT_XRAY_VISION, + "TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING, ), /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, diff --git a/code/datums/components/parry.dm b/code/datums/components/parry.dm index fedfcb77d15..a2cfe789cba 100644 --- a/code/datums/components/parry.dm +++ b/code/datums/components/parry.dm @@ -1,38 +1,121 @@ -/// Add to a living mob to allow them to "parry" projectiles by clicking on their tile, sending them back at the firer. -/datum/component/projectile_parry - /// typecache of valid projectiles to be able to parry - var/list/parryable_projectiles +/// Add to a projectile to allow it to be parried by mobs with a certain trait (TRAIT_MINING_PARRYING by default) +/datum/component/parriable_projectile + /// List of all turfs the projectile passed on its last loop and we assigned comsigs to + var/list/turf/parry_turfs = list() + /// List of all mobs who have clicked on a parry turf in last moveloop + var/list/mob/parriers = list() + /// When the projectile was created + var/fire_time = 0 + /// If this projectile has been parried + var/parried = FALSE + /// How much this projectile is sped up when parried + var/parry_speed_mult + /// How much this projectile's damage is increased when parried + var/parry_damage_mult + /// How much this projectile is sped up when boosted (parried by owner) + var/boost_speed_mult + /// How much this projectile's damage is increased when boosted (parried by owner) + var/boost_damage_mult + /// Trait required to be able to parry this projectile + var/parry_trait + /// For how long do valid tiles persist? Acts as clientside lag compensation + var/grace_period + /// Callback for special effects upon parrying + var/datum/callback/parry_callback - -/datum/component/projectile_parry/Initialize(list/projectiles_to_parry) - if(!isliving(parent)) +/datum/component/parriable_projectile/Initialize(parry_speed_mult = 0.8, parry_damage_mult = 1.15, boost_speed_mult = 0.6, boost_damage_mult = 1.5, parry_trait = TRAIT_MINING_PARRYING, grace_period = 0.25 SECONDS, datum/callback/parry_callback = null) + if(!isprojectile(parent)) return COMPONENT_INCOMPATIBLE + src.parry_speed_mult = parry_speed_mult + src.parry_damage_mult = parry_damage_mult + src.boost_speed_mult = boost_speed_mult + src.boost_damage_mult = boost_damage_mult + src.parry_trait = parry_trait + src.grace_period = grace_period + src.parry_callback = parry_callback + fire_time = world.time - parryable_projectiles = typecacheof(projectiles_to_parry) +/datum/component/parriable_projectile/Destroy(force) + for (var/turf/parry_turf as anything in parry_turfs) + UnregisterSignal(parry_turf, COMSIG_CLICK) + . = ..() +/datum/component/parriable_projectile/RegisterWithParent() + RegisterSignal(parent, COMSIG_PROJECTILE_PIXEL_STEP, PROC_REF(on_moved)) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(before_move)) + RegisterSignal(parent, COMSIG_PROJECTILE_BEFORE_MOVE, PROC_REF(before_move)) + RegisterSignal(parent, COMSIG_PROJECTILE_SELF_PREHIT, PROC_REF(before_hit)) -/datum/component/projectile_parry/RegisterWithParent() - RegisterSignal(parent, COMSIG_LIVING_PROJECTILE_PARRYING, PROC_REF(parrying_projectile)) - RegisterSignal(parent, COMSIG_LIVING_PROJECTILE_PARRIED, PROC_REF(parried_projectile)) +/datum/component/parriable_projectile/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_PROJECTILE_PIXEL_STEP, COMSIG_MOVABLE_MOVED, COMSIG_PROJECTILE_BEFORE_MOVE, COMSIG_PROJECTILE_SELF_PREHIT)) - -/datum/component/projectile_parry/UnregisterFromParent() - UnregisterSignal(parent, list(COMSIG_LIVING_PROJECTILE_PARRYING, COMSIG_LIVING_PROJECTILE_PARRIED)) - - -/datum/component/projectile_parry/proc/parrying_projectile(datum/source, obj/projectile/parried_projectile) +/datum/component/parriable_projectile/proc/before_move(obj/projectile/source) SIGNAL_HANDLER - if(is_type_in_typecache(parried_projectile, parryable_projectiles)) - return ALLOW_PARRY + var/list/turfs_to_remove = list() + for (var/turf/parry_turf as anything in parry_turfs) + if (parry_turfs[parry_turf] < world.time) + turfs_to_remove += parry_turf + for (var/turf/parry_turf as anything in turfs_to_remove) + parry_turfs -= parry_turf + UnregisterSignal(parry_turf, COMSIG_CLICK) -/datum/component/projectile_parry/proc/parried_projectile(datum/source, obj/projectile/parried_projectile) + var/list/parriers_to_remove = list() + for (var/mob/parrier as anything in parriers) + if (parriers[parrier] < world.time) + parriers_to_remove += parrier + + for (var/mob/parrier as anything in parriers_to_remove) + parriers_to_remove -= parrier + +/datum/component/parriable_projectile/proc/on_moved(obj/projectile/source) + SIGNAL_HANDLER + if (!isturf(source.loc)) + return + parry_turfs[source.loc] = world.time + grace_period + RegisterSignal(source.loc, COMSIG_CLICK, PROC_REF(on_turf_click)) + +/datum/component/parriable_projectile/proc/on_turf_click(turf/source, atom/location, control, list/params, mob/user) + SIGNAL_HANDLER + if (!HAS_TRAIT(user, parry_trait)) + return + var/obj/projectile/proj_parent = parent + if (proj_parent.firer == user && (fire_time + grace_period > world.time) && !parried) + attempt_parry(proj_parent, user) + return + parriers[user] = world.time + grace_period + +/datum/component/parriable_projectile/proc/before_hit(obj/projectile/source, list/bullet_args) SIGNAL_HANDLER - var/mob/living/living_parent = parent + var/mob/user = bullet_args[2] + if (!istype(user) || !parriers[user] || parried) + return + parriers -= user + attempt_parry(source, user) - living_parent.playsound_local(get_turf(parried_projectile), 'sound/effects/parry.ogg', 50, TRUE) - living_parent.overlay_fullscreen("projectile_parry", /atom/movable/screen/fullscreen/crit/projectile_parry, 2) - addtimer(CALLBACK(living_parent, TYPE_PROC_REF(/mob, clear_fullscreen), "projectile_parry"), 0.25 SECONDS) - living_parent.visible_message(span_warning("[living_parent] expertly parries [parried_projectile] with [living_parent.p_their()] bare hand!"), span_warning("You parry [parried_projectile] with your hand!")) +/datum/component/parriable_projectile/proc/attempt_parry(obj/projectile/source, mob/user) + if (SEND_SIGNAL(user, COMSIG_LIVING_PROJECTILE_PARRIED, source) & INTERCEPT_PARRY_EFFECTS) + return + + parried = TRUE + if (source.firer != user) + if (abs(source.Angle - dir2angle(user)) < 15) + source.set_angle((source.Angle + 180) % 360 + rand(-3, 3)) + else + source.set_angle(dir2angle(user) + rand(-3, 3)) + user.visible_message(span_warning("[user] expertly parries [source] with [user.p_their()] bare hand!"), span_warning("You parry [source] with your hand!")) + else + user.visible_message(span_warning("[user] boosts [source] with [user.p_their()] bare hand!"), span_warning("You boost [source] with your hand!")) + source.firer = user + source.speed *= (source.firer == user) ? boost_speed_mult : parry_speed_mult + source.damage *= (source.firer == user) ? boost_damage_mult : parry_damage_mult + source.add_atom_colour(COLOR_RED_LIGHT, TEMPORARY_COLOUR_PRIORITY) + if (!isnull(parry_callback)) + parry_callback.Invoke(user) + + user.playsound_local(source.loc, 'sound/effects/parry.ogg', 50, TRUE) + user.overlay_fullscreen("projectile_parry", /atom/movable/screen/fullscreen/crit/projectile_parry, 2) + addtimer(CALLBACK(user, TYPE_PROC_REF(/mob, clear_fullscreen), "projectile_parry"), 0.25 SECONDS) + return PROJECTILE_INTERRUPT_HIT diff --git a/code/datums/components/style/style.dm b/code/datums/components/style/style.dm index fcf62dead1a..9bc420cc175 100644 --- a/code/datums/components/style/style.dm +++ b/code/datums/components/style/style.dm @@ -37,8 +37,6 @@ var/timerid /// Highest score attained by this component, to avoid as much overhead when considering to award a high score to the client var/high_score = 0 - /// Weakref to the added projectile parry component - var/datum/weakref/projectile_parry /// What rank, minimum, the user needs to be to hotswap items var/hotswap_rank = STYLE_BRUTAL /// If this is multitooled, making it make funny noises on the user's rank going up @@ -110,20 +108,7 @@ RegisterSignal(parent, COMSIG_LIVING_DEFUSED_GIBTONITE, PROC_REF(on_gibtonite_defuse)) RegisterSignal(parent, COMSIG_LIVING_CRUSHER_DETONATE, PROC_REF(on_crusher_detonate)) RegisterSignal(parent, COMSIG_LIVING_DISCOVERED_GEYSER, PROC_REF(on_geyser_discover)) - - projectile_parry = WEAKREF(parent.AddComponent(\ - /datum/component/projectile_parry,\ - list(\ - /obj/projectile/colossus,\ - /obj/projectile/temp/watcher,\ - /obj/projectile/kinetic,\ - /obj/projectile/bileworm_acid,\ - /obj/projectile/herald,\ - /obj/projectile/kiss,\ - )\ - ) - ) - + ADD_TRAIT(parent, TRAIT_MINING_PARRYING, STYLE_TRAIT) /datum/component/style/UnregisterFromParent() UnregisterSignal(parent, COMSIG_USER_ITEM_INTERACTION) @@ -137,10 +122,7 @@ UnregisterSignal(parent, COMSIG_LIVING_DEFUSED_GIBTONITE) UnregisterSignal(parent, COMSIG_LIVING_CRUSHER_DETONATE) UnregisterSignal(parent, COMSIG_LIVING_DISCOVERED_GEYSER) - - if(projectile_parry) - qdel(projectile_parry.resolve()) - + REMOVE_TRAIT(parent, TRAIT_MINING_PARRYING, STYLE_TRAIT) /datum/component/style/Destroy(force) STOP_PROCESSING(SSdcs, src) @@ -150,14 +132,11 @@ mob_parent.hud_used.show_hud(mob_parent.hud_used.hud_version) return ..() - /datum/component/style/process(seconds_per_tick) point_multiplier = round(max(point_multiplier - 0.2 * seconds_per_tick, 1), 0.1) change_points(-5 * seconds_per_tick * ROUND_UP((style_points + 1) / 200), use_multiplier = FALSE) update_screen() - - /datum/component/style/proc/add_action(action, amount) if(length(actions) > 9) actions.Cut(1, 2) diff --git a/code/datums/components/style/style_meter.dm b/code/datums/components/style/style_meter.dm index cc02baec1d5..eb9ca3653fc 100644 --- a/code/datums/components/style/style_meter.dm +++ b/code/datums/components/style/style_meter.dm @@ -98,6 +98,7 @@ clean_up(loc) forceMove(get_turf(src)) + INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, put_in_hands), src) return CLICK_ACTION_SUCCESS /obj/item/style_meter/multitool_act(mob/living/user, obj/item/tool) @@ -115,9 +116,7 @@ /// Unregister signals and just generally clean up ourselves after being removed from glasses /obj/item/style_meter/proc/clean_up(atom/movable/old_location) old_location.cut_overlay(meter_appearance) - UnregisterSignal(old_location, COMSIG_ITEM_EQUIPPED) - UnregisterSignal(old_location, COMSIG_ITEM_DROPPED) - UnregisterSignal(old_location, COMSIG_ATOM_EXAMINE) + UnregisterSignal(old_location, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ATOM_EXAMINE, COMSIG_CLICK_ALT)) UnregisterSignal(old_location, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL)) if(!style_meter) return diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 83d99461fce..df930896336 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -345,20 +345,18 @@ /datum/status_effect/crusher_mark id = "crusher_mark" duration = 300 //if you leave for 30 seconds you lose the mark, deal with it - status_type = STATUS_EFFECT_MULTIPLE + status_type = STATUS_EFFECT_REFRESH alert_type = null var/mutable_appearance/marked_underlay - var/obj/item/kinetic_crusher/hammer_synced + var/boosted = FALSE - -/datum/status_effect/crusher_mark/on_creation(mob/living/new_owner, obj/item/kinetic_crusher/new_hammer_synced) +/datum/status_effect/crusher_mark/on_creation(mob/living/new_owner, was_boosted) . = ..() - if(.) - hammer_synced = new_hammer_synced + boosted = was_boosted /datum/status_effect/crusher_mark/on_apply() if(owner.mob_size >= MOB_SIZE_LARGE) - marked_underlay = mutable_appearance('icons/effects/effects.dmi', "shield2") + marked_underlay = mutable_appearance('icons/effects/effects.dmi', boosted ? "shield" : "shield2") marked_underlay.pixel_x = -owner.pixel_x marked_underlay.pixel_y = -owner.pixel_y owner.underlays += marked_underlay @@ -366,16 +364,11 @@ return FALSE /datum/status_effect/crusher_mark/Destroy() - hammer_synced = null if(owner) owner.underlays -= marked_underlay QDEL_NULL(marked_underlay) return ..() -//we will only clear ourselves if the crusher is the one that owns us. -/datum/status_effect/crusher_mark/before_remove(obj/item/kinetic_crusher/attacking_hammer) - return (attacking_hammer == hammer_synced) - /datum/status_effect/stacking/saw_bleed id = "saw_bleed" tick_interval = 0.6 SECONDS diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm index 23fa5f2ad40..8030eed9883 100644 --- a/code/game/objects/items/hand_items.dm +++ b/code/game/objects/items/hand_items.dm @@ -544,6 +544,10 @@ damage = 0 // love can't actually hurt you armour_penetration = 100 // but if it could, it would cut through even the thickest plate +/obj/projectile/kiss/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parriable_projectile) + /obj/projectile/kiss/fire(angle, atom/direct_target) if(firer) name = "[name] blown by [firer]" diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 0a4a6ce6572..6a276e83072 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -105,15 +105,9 @@ crusher_trophy.on_melee_hit(target, user) if(QDELETED(target)) return - // Clear existing marks - var/valid_crusher_attack = FALSE - for(var/datum/status_effect/crusher_mark/crusher_mark_effect as anything in target.get_all_status_effect_of_id(/datum/status_effect/crusher_mark)) - //this will erase ALL crusher marks, not only ones by you. - if(crusher_mark_effect.hammer_synced != src || !target.remove_status_effect(/datum/status_effect/crusher_mark, src)) - continue - valid_crusher_attack = TRUE - break - if(!valid_crusher_attack) + var/datum/status_effect/crusher_mark/mark = target.has_status_effect(/datum/status_effect/crusher_mark) + var/boosted_mark = mark?.boosted + if(!target.remove_status_effect(mark)) return // Detonation effect var/datum/status_effect/crusher_damage/crusher_damage_effect = target.has_status_effect(/datum/status_effect/crusher_damage) || target.apply_status_effect(/datum/status_effect/crusher_damage) @@ -130,7 +124,7 @@ var/backstab_dir = get_dir(user, target) var/def_check = target.getarmor(type = BOMB) // Backstab bonus - if((user.dir & backstab_dir) && (target.dir & backstab_dir)) + if((user.dir & backstab_dir) && (target.dir & backstab_dir) || boosted_mark) backstabbed = TRUE combined_damage += backstab_bonus playsound(user, 'sound/weapons/kinetic_accel.ogg', 100, TRUE) //Seriously who spelled it wrong @@ -164,7 +158,6 @@ attached_trophy.on_projectile_fire(destabilizer, user) destabilizer.preparePixelProjectile(target, user, modifiers) destabilizer.firer = user - destabilizer.hammer_synced = src playsound(user, 'sound/weapons/plasma_cutter.ogg', 100, TRUE) destabilizer.fire() charged = FALSE @@ -211,24 +204,24 @@ armor_flag = BOMB range = 6 log_override = TRUE - ///The crusher that's firing this projectile. - var/obj/item/kinetic_crusher/hammer_synced + /// Has this projectile been boosted + var/boosted = FALSE -/obj/projectile/destabilizer/Destroy() - hammer_synced = null - return ..() +/obj/projectile/destabilizer/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parriable_projectile, parry_callback = CALLBACK(src, PROC_REF(on_parry))) + +/obj/projectile/destabilizer/proc/on_parry(mob/user) + SIGNAL_HANDLER + boosted = TRUE + // Get a bit of a damage/range boost after being parried + damage = 10 + range = 9 /obj/projectile/destabilizer/on_hit(atom/target, blocked = 0, pierce_hit) if(isliving(target)) var/mob/living/living_target = target - var/has_mark_from_this_crusher = FALSE - for(var/datum/status_effect/crusher_mark/crusher_mark_effect as anything in living_target.get_all_status_effect_of_id(/datum/status_effect/crusher_mark)) - if(crusher_mark_effect.hammer_synced != hammer_synced) - continue - has_mark_from_this_crusher = TRUE - break - if(!has_mark_from_this_crusher) - living_target.apply_status_effect(/datum/status_effect/crusher_mark, hammer_synced) + living_target.apply_status_effect(/datum/status_effect/crusher_mark, boosted) var/target_turf = get_turf(target) if(ismineralturf(target_turf)) var/turf/closed/mineral/hit_mineral = target_turf diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm index 7993fe74a3f..f023215d7dd 100644 --- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm +++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm @@ -79,6 +79,10 @@ damage_type = BURN pass_flags = PASSTABLE +/obj/projectile/bileworm_acid/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parriable_projectile) + /datum/action/cooldown/mob_cooldown/devour name = "Devour" desc = "Burrow underground, and then move to your target to consume them. Short cooldown, but your target must be unconscious." diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher_projectiles.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher_projectiles.dm index 19e718d9fee..cf7fa3a7e69 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher_projectiles.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher_projectiles.dm @@ -7,6 +7,10 @@ armor_flag = ENERGY temperature = -50 +/obj/projectile/temp/watcher/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parriable_projectile) + /obj/projectile/temp/watcher/on_hit(mob/living/target, blocked = 0, pierce_hit) . = ..() if (!isliving(target)) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index a71b9f76af3..d51a5862246 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -188,6 +188,10 @@ plane = GAME_PLANE var/explode_hit_objects = TRUE +/obj/projectile/colossus/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parriable_projectile) + /obj/projectile/colossus/can_hit_target(atom/target, direct_target = FALSE, ignore_loc = FALSE, cross_failed = FALSE) if(isliving(target)) direct_target = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm index 9f162e0cfdc..55e1938f900 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm @@ -232,6 +232,10 @@ damage_type = BRUTE pass_flags = PASSTABLE +/obj/projectile/herald/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parriable_projectile) + /obj/projectile/herald/on_hit(atom/target, blocked = 0, pierce_hit) if(ismob(target) && ismob(firer)) var/mob/living/mob_target = target diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index b6a960e05ad..53cbe825085 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -195,6 +195,9 @@ var/pressure_decrease = 0.25 var/obj/item/gun/energy/recharge/kinetic_accelerator/kinetic_gun +/obj/projectile/kinetic/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parriable_projectile, parry_callback = CALLBACK(src, PROC_REF(on_parry))) /obj/projectile/kinetic/Destroy() kinetic_gun = null @@ -213,6 +216,13 @@ damage = damage * pressure_decrease pressure_decrease_active = TRUE +/obj/projectile/kinetic/proc/on_parry(mob/user) + SIGNAL_HANDLER + + // Ensure that if the user doesn't have tracer mod we're still visible + icon_state = "ka_tracer" + update_appearance() + /obj/projectile/kinetic/on_range() strike_thing() ..() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 000ce04e5b0..5833a95c567 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -208,14 +208,9 @@ var/wound_falloff_tile ///How much we want to drop the embed_chance value, if we can embed, per tile, for falloff purposes var/embed_falloff_tile - var/static/list/projectile_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered), - COMSIG_ATOM_ATTACK_HAND = PROC_REF(attempt_parry), - ) + var/static/list/projectile_connections = list(COMSIG_ATOM_ENTERED = PROC_REF(on_entered)) /// If true directly targeted turfs can be hit var/can_hit_turfs = FALSE - /// If this projectile has been parried before - var/parried = FALSE /obj/projectile/Initialize(mapload) . = ..() @@ -420,33 +415,6 @@ return Impact(A) -/// Signal proc for when a mob attempts to attack this projectile or the turf it's on with an empty hand. -/obj/projectile/proc/attempt_parry(datum/source, mob/user, list/modifiers) - SIGNAL_HANDLER - - if(parried) - return FALSE - - if(SEND_SIGNAL(user, COMSIG_LIVING_PROJECTILE_PARRYING, src) & ALLOW_PARRY) - on_parry(user, modifiers) - return TRUE - - return FALSE - - -/// Called when a mob with PARRY_TRAIT clicks on this projectile or the tile its on, reflecting the projectile within 7 degrees and increasing the bullet's stats. -/obj/projectile/proc/on_parry(mob/user, list/modifiers) - if(SEND_SIGNAL(user, COMSIG_LIVING_PROJECTILE_PARRIED, src) & INTERCEPT_PARRY_EFFECTS) - return - - parried = TRUE - set_angle(dir2angle(user.dir) + rand(-3, 3)) - firer = user - speed *= 0.8 // Go 20% faster when parried - damage *= 1.15 // And do 15% more damage - add_atom_colour(COLOR_RED_LIGHT, TEMPORARY_COLOUR_PRIORITY) - - /** * Called when the projectile hits something * This can either be from it bumping something, @@ -528,7 +496,7 @@ return process_hit(T, select_target(T, target, bumped), bumped, hit_something) // try to hit something else // at this point we are going to hit the thing // in which case send signal to it - if (SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) & PROJECTILE_INTERRUPT_HIT) + if ((SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) & PROJECTILE_INTERRUPT_HIT) || (SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_PREHIT, args) & PROJECTILE_INTERRUPT_HIT)) qdel(src) return BULLET_ACT_BLOCK if(mode == PROJECTILE_PIERCE_HIT) @@ -776,7 +744,7 @@ required_moves = SSprojectiles.global_max_tick_moves time_offset += overrun * speed time_offset += MODULUS(elapsed_time_deciseconds, speed) - + SEND_SIGNAL(src, COMSIG_PROJECTILE_BEFORE_MOVE) for(var/i in 1 to required_moves) pixel_move(pixel_speed_multiplier, FALSE) @@ -816,7 +784,6 @@ fired = TRUE play_fov_effect(starting, 6, "gunfire", dir = NORTH, angle = Angle) SEND_SIGNAL(src, COMSIG_PROJECTILE_FIRE) - RegisterSignal(src, COMSIG_ATOM_ATTACK_HAND, PROC_REF(attempt_parry)) if(hitscan) process_hitscan() if(QDELETED(src)) @@ -936,21 +903,25 @@ trajectory.increment(-trajectory_multiplier) qdel(src) return - if(T.z != loc.z) - var/old = loc - before_z_change(loc, T) - trajectory_ignore_forcemove = TRUE - forceMove(T) - trajectory_ignore_forcemove = FALSE - after_z_change(old, loc) - if(!hitscanning) - pixel_x = trajectory.return_px() - pixel_y = trajectory.return_py() - forcemoved = TRUE - hitscan_last = loc - else if(T != loc) + if (T == loc) + continue + if (T.z == loc.z) step_towards(src, T) hitscan_last = loc + SEND_SIGNAL(src, COMSIG_PROJECTILE_PIXEL_STEP) + continue + var/old = loc + before_z_change(loc, T) + trajectory_ignore_forcemove = TRUE + forceMove(T) + trajectory_ignore_forcemove = FALSE + after_z_change(old, loc) + if(!hitscanning) + pixel_x = trajectory.return_px() + pixel_y = trajectory.return_py() + forcemoved = TRUE + hitscan_last = loc + SEND_SIGNAL(src, COMSIG_PROJECTILE_PIXEL_STEP) if(QDELETED(src)) //deleted on last move return if(!hitscanning && !forcemoved)