diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index be662ecf7cd..de4a5cb6b9f 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -6,6 +6,8 @@ faction += "\ref[src]" determine_move_and_pull_forces() GLOB.mob_living_list += src + if(advanced_bullet_dodge_chance) + RegisterSignal(src, COMSIG_ATOM_PREHIT, PROC_REF(advanced_bullet_dodge)) // Used to determine the forces dependend on the mob size // Will only change the force if the force was not set in the mob type itself @@ -51,6 +53,7 @@ QDEL_NULL(middleClickOverride) if(mind?.current == src) mind.current = null + UnregisterSignal(src, COMSIG_ATOM_PREHIT) return ..() /mob/living/ghostize(can_reenter_corpse = 1) @@ -1071,6 +1074,9 @@ update_transform() if("lighting_alpha") sync_lighting_plane_alpha() + if("advanced_bullet_dodge_chance") + UnregisterSignal(src, COMSIG_ATOM_PREHIT) + RegisterSignal(src, COMSIG_ATOM_PREHIT, PROC_REF(advanced_bullet_dodge)) /mob/living/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable, block_movement) stop_pulling() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index aef08f931e7..1715f148a85 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -51,6 +51,33 @@ check_projectile_dismemberment(P, def_zone) return P.on_hit(src, armor, def_zone) +/// Tries to dodge incoming bullets if we aren't disabled for any reasons. Advised to overide with advanced effects, this is as basic example admins can apply. +/mob/living/proc/advanced_bullet_dodge(mob/living/source, obj/item/projectile/hitting_projectile) + SIGNAL_HANDLER + + if(HAS_TRAIT(source, TRAIT_IMMOBILIZED)) + return NONE + if(source.stat != CONSCIOUS) + return NONE + if(!prob(advanced_bullet_dodge_chance)) + return NONE + + source.visible_message( + "[source] [pick("dodges","jumps out of the way of","evades","dives out of the way of")] [hitting_projectile]!", + "You evade [hitting_projectile]!", + ) + playsound(source, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE) + var/dir_to_avoid = angle2dir_cardinal(hitting_projectile.Angle) + var/list/potential_first_directions = list(NORTH, SOUTH, EAST, WEST) + potential_first_directions -= dir_to_avoid + step(source, pick(potential_first_directions)) + // Chance to dodge multiple shotgun spreads, but not likely. Mainly: Infinite loop prevention from admins setting it to 100 and doing something stupid. + // If you want to set your dodge chance to 100 on a subtype, no issue: Just make sure the subtype does not step in a direction, otherwise you'll have the mob move a large distance to dodge rubbershot. + if(prob(50)) + addtimer(VARSET_CALLBACK(src, advanced_bullet_dodge_chance, advanced_bullet_dodge_chance), 0.25 SECONDS) // Returns fast enough for multiple laser shots. + advanced_bullet_dodge_chance = 0 + return ATOM_PREHIT_FAILURE + /mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) return 0 diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 8928ad4c6c0..09c0f5375bd 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -100,6 +100,9 @@ /// Famous last words -- if succumbing, what the user's last words were var/last_words + ///This variable is the chance for a mob to automatically dodge a bullet. Useful for admins, and applied to some mobs by default, such as the malfunctioning drone mobs. + var/advanced_bullet_dodge_chance = 0 + /* Taste Vars */ diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm index 38e012c6fc8..f66f763b252 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm @@ -30,6 +30,7 @@ faction = list("malf_drone") deathmessage = "suddenly breaks apart." del_on_death = TRUE + advanced_bullet_dodge_chance = 25 // This will be adjusted when active, vs deactivated. Randomises on hit if it is zero. var/passive_mode = TRUE // if true, don't target anything. /mob/living/simple_animal/hostile/malf_drone/Initialize(mapload) @@ -69,6 +70,8 @@ do_sparks(3, 1, src) passive_mode = FALSE update_icons() + if(!advanced_bullet_dodge_chance) + advanced_bullet_dodge_chance = 25 . = ..() // this will handle finding a target if there is a valid one nearby /mob/living/simple_animal/hostile/malf_drone/Life(seconds, times_fired) @@ -83,12 +86,38 @@ passive_mode = !passive_mode if(passive_mode) visible_message("[src] retracts several targetting vanes.") + advanced_bullet_dodge_chance = 0 if(target) LoseTarget() else visible_message("[src] suddenly lights up, and additional targetting vanes slide into place.") + advanced_bullet_dodge_chance = 25 update_icons() +/// We overide the basic effect, as malfunctioning drones are in space, and use jets to dodge. Also lets us do cool effects. +/mob/living/simple_animal/hostile/malf_drone/advanced_bullet_dodge(mob/living/source, obj/item/projectile/hitting_projectile) + if(HAS_TRAIT(source, TRAIT_IMMOBILIZED)) + return NONE + if(source.stat != CONSCIOUS) + return NONE + if(!prob(advanced_bullet_dodge_chance)) + return NONE + + source.visible_message( + "[source]'s jets [pick("boosts", "propels", "pulses", "flares up and moves", "shudders and pushes")] it out of '[hitting_projectile]'s way!", + "You evade [hitting_projectile]!", + ) + playsound(source, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/effects/refill.ogg'), 75, TRUE) + var/dir_to_avoid = angle2dir_cardinal(hitting_projectile.Angle) + var/list/potential_first_directions = list(NORTH, SOUTH, EAST, WEST) + potential_first_directions -= dir_to_avoid + new /obj/effect/temp_visual/decoy/fading(source.loc, source) + step(source, pick(potential_first_directions)) + if(prob(50)) + addtimer(VARSET_CALLBACK(source, advanced_bullet_dodge_chance, advanced_bullet_dodge_chance), 0.25 SECONDS) + advanced_bullet_dodge_chance = 0 + return ATOM_PREHIT_FAILURE + /mob/living/simple_animal/hostile/malf_drone/emp_act(severity) adjustHealth(100 / severity) // takes the same damage as a mining drone from emp