From 5c0fc3f4034be5126782af5eedf31ac3fcf11420 Mon Sep 17 00:00:00 2001 From: Helg2 <93882977+Helg2@users.noreply.github.com> Date: Sun, 14 Jul 2024 18:00:15 +0300 Subject: [PATCH] Makes hunter mirages more convincing, motion detectors now ignore xenos in stealth, adds some ap to hunter's stealth attacks. (#13) * motion sensors * Update xeno_illusion.dm * Update mobs.dm * Update sentries.dm * Update ai.dm * Update mobs.dm * Update abilities_hunter.dm --- code/__HELPERS/ai.dm | 13 ++++++- code/_globalvars/lists/mobs.dm | 8 ++-- code/game/objects/items/motion_detector.dm | 18 +++------ .../ai/ai_behaviors/xeno/xeno_illusion.dm | 23 +++++++++--- .../modular_armor/attachments/modules.dm | 13 ++++--- .../castes/hunter/abilities_hunter.dm | 37 ++++++++++--------- code/modules/projectiles/sentries.dm | 15 ++------ 7 files changed, 70 insertions(+), 57 deletions(-) diff --git a/code/__HELPERS/ai.dm b/code/__HELPERS/ai.dm index 018da4c5e1a..3b371ce8a67 100644 --- a/code/__HELPERS/ai.dm +++ b/code/__HELPERS/ai.dm @@ -54,6 +54,16 @@ continue . += nearby_mech +///Returns a list of mobs/illusion via get_dist and same z level method, very cheap compared to range() +/proc/cheap_get_illusions_near(atom/movable/source, distance) + . = list() + for(var/mob/illusion/nearby_illusion AS in GLOB.mob_illusions_list) + if(source.z != nearby_illusion.z) + continue + if(get_dist(source, nearby_illusion) > distance) + continue + . += nearby_illusion + ///Returns the nearest target that has the right target flag /proc/get_nearest_target(atom/source, distance, target_flags, attacker_faction, attacker_hive) if(!source) @@ -62,8 +72,7 @@ var/shorter_distance = distance + 1 if(target_flags & TARGET_HUMAN) for(var/mob/living/nearby_human AS in cheap_get_humans_near(source, distance)) - //if(nearby_human.stat == DEAD || nearby_human.faction == attacker_faction || nearby_human.alpha <= SCOUT_CLOAK_RUN_ALPHA) //ORIGINAL - if(nearby_human.stat == DEAD || nearby_human.faction == attacker_faction || nearby_human.alpha <= SCOUT_CLOAK_RUN_ALPHA || isnestedhost(nearby_human)) //RUTGMC EDIT, no nest breaking minions + if(nearby_human.stat == DEAD || nearby_human.faction == attacker_faction || nearby_human.alpha <= SCOUT_CLOAK_RUN_ALPHA || isnestedhost(nearby_human)) continue if(get_dist(source, nearby_human) < shorter_distance) nearest_target = nearby_human diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 5e71c169167..89b9f2963d3 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -114,11 +114,13 @@ GLOBAL_LIST_INIT(forbid_excepts, list( /mob/living/carbon/xenomorph/shrike, /mob/living/carbon/xenomorph/larva, /mob/living/carbon/xenomorph/drone, - )) +)) GLOBAL_LIST_EMPTY_TYPED(hellhound_list, /mob/living/carbon/xenomorph/hellhound) GLOBAL_LIST_EMPTY_TYPED(yautja_mob_list, /mob/living/carbon/human/species/yautja) +GLOBAL_LIST_EMPTY_TYPED(mob_illusions_list, /mob/illusion) + GLOBAL_LIST_INIT(xeno_types_tier_one, list(/mob/living/carbon/xenomorph/runner, /mob/living/carbon/xenomorph/drone, /mob/living/carbon/xenomorph/sentinel, /mob/living/carbon/xenomorph/defender)) GLOBAL_LIST_INIT(xeno_types_tier_two, list(/mob/living/carbon/xenomorph/hunter, /mob/living/carbon/xenomorph/panther, /mob/living/carbon/xenomorph/warrior, /mob/living/carbon/xenomorph/spitter, /mob/living/carbon/xenomorph/hivelord, /mob/living/carbon/xenomorph/carrier, /mob/living/carbon/xenomorph/bull)) GLOBAL_LIST_INIT(xeno_types_tier_three, list(/mob/living/carbon/xenomorph/gorger, /mob/living/carbon/xenomorph/ravager, /mob/living/carbon/xenomorph/praetorian, /mob/living/carbon/xenomorph/boiler, /mob/living/carbon/xenomorph/defiler, /mob/living/carbon/xenomorph/crusher, /mob/living/carbon/xenomorph/shrike, /mob/living/carbon/xenomorph/behemoth, /mob/living/carbon/xenomorph/chimera)) @@ -149,7 +151,7 @@ GLOBAL_LIST_INIT(hive_ui_static_data, init_hive_status_lists()) // init by make_ GLOB.hive_ui_caste_index[type_path] = length(.) //Starts from 0. - var/icon/xeno_minimap = icon('icons/UI_icons/map_blips.dmi', initial(caste.minimap_icon)) ///RUTGMC edit, icon redirect to module + var/icon/xeno_minimap = icon('icons/UI_icons/map_blips.dmi', initial(caste.minimap_icon)) var/tier = initial(caste.tier) if(tier == XENO_TIER_MINION) continue @@ -167,8 +169,6 @@ GLOBAL_LIST_INIT(hive_ui_static_data, init_hive_status_lists()) // init by make_ "evolution_max" = initial(caste.evolution_threshold) )) - - /proc/update_config_movespeed_type_lookup(update_mobs = TRUE) var/list/mob_types = list() var/list/entry_value = CONFIG_GET(keyed_list/multiplicative_movespeed) diff --git a/code/game/objects/items/motion_detector.dm b/code/game/objects/items/motion_detector.dm index 6afe6311027..f53c32863f4 100644 --- a/code/game/objects/items/motion_detector.dm +++ b/code/game/objects/items/motion_detector.dm @@ -130,24 +130,18 @@ clean_operator() return hostile_detected = FALSE - for (var/mob/living/carbon/human/nearby_human AS in cheap_get_humans_near(operator, range)) + for(var/mob/living/carbon/human/nearby_human AS in cheap_get_humans_near(operator, range)) if(nearby_human == operator) continue - /* RU TGMC EDIT - if(nearby_human.last_move_time + move_sensitivity < world.time) - continue - RU TGMC EDIT */ -//RU TGMC EDIT if(HAS_TRAIT(nearby_human, TRAIT_LIGHT_STEP)) continue -//RUTGMC EDIT ADDITION END prepare_blip(nearby_human, nearby_human.wear_id?.iff_signal & operator.wear_id.iff_signal ? MOTION_DETECTOR_FRIENDLY : MOTION_DETECTOR_HOSTILE) - for (var/mob/living/carbon/xenomorph/nearby_xeno AS in cheap_get_xenos_near(operator, range)) - /* RU TGMC EDIT - if(nearby_xeno.last_move_time + move_sensitivity < world.time ) + for(var/mob/living/carbon/xenomorph/nearby_xeno AS in cheap_get_xenos_near(operator, range)) + if(HAS_TRAIT(nearby_xeno, TRAIT_TURRET_HIDDEN)) continue - RU TGMC EDIT */ prepare_blip(nearby_xeno, MOTION_DETECTOR_HOSTILE) + for(var/mob/illusion/nearby_illusion AS in cheap_get_illusions_near(operator, range)) + prepare_blip(nearby_illusion, MOTION_DETECTOR_HOSTILE) if(hostile_detected) playsound(loc, 'sound/items/tick.ogg', 100, 0, 7, 2) addtimer(CALLBACK(src, PROC_REF(clean_blips)), 1 SECONDS) @@ -164,7 +158,7 @@ /obj/item/attachable/motiondetector/proc/prepare_blip(mob/target, status) if(!operator.client) return - if(!target) // RUTGMC ADDITION + if(!target) return if(status == MOTION_DETECTOR_HOSTILE) hostile_detected = TRUE diff --git a/code/modules/ai/ai_behaviors/xeno/xeno_illusion.dm b/code/modules/ai/ai_behaviors/xeno/xeno_illusion.dm index 535540062aa..15fd8b627f5 100644 --- a/code/modules/ai/ai_behaviors/xeno/xeno_illusion.dm +++ b/code/modules/ai/ai_behaviors/xeno/xeno_illusion.dm @@ -22,16 +22,22 @@ return /datum/ai_behavior/xeno/illusion/attack_target(datum/soure, atom/attacked) + if(world.time < mob_parent.next_move) + return if(!attacked) attacked = atom_to_walk_to + if(get_dist(attacked, mob_parent) > 1) + return var/mob/illusion/illusion_parent = mob_parent var/mob/living/carbon/xenomorph/original_xeno = illusion_parent.original_mob - mob_parent.changeNext_move(original_xeno.xeno_caste.attack_delay) + illusion_parent.changeNext_move(original_xeno.xeno_caste.attack_delay + rand(0, 5)) + illusion_parent.face_atom(attacked) if(ismob(attacked)) - mob_parent.do_attack_animation(attacked, ATTACK_EFFECT_REDSLASH) - playsound(mob_parent.loc, "alien_claw_flesh", 25, 1) + illusion_parent.do_attack_animation(attacked, ATTACK_EFFECT_REDSLASH) + playsound(illusion_parent.loc, "alien_claw_flesh", 25, 1) return - mob_parent.do_attack_animation(attacked, ATTACK_EFFECT_CLAW) + illusion_parent.do_attack_animation(attacked, ATTACK_EFFECT_CLAW) + playsound(illusion_parent.loc, "alien_claw_metal", 25, 1) /mob/illusion density = FALSE @@ -48,14 +54,21 @@ return INITIALIZE_HINT_QDEL src.original_mob = original_mob appearance = original_mob.appearance + setDir(original_mob.dir) desc = original_mob.desc name = original_mob.name RegisterSignals(original_mob, list(COMSIG_QDELETING, COMSIG_MOB_DEATH), PROC_REF(destroy_illusion)) + GLOB.mob_illusions_list += src QDEL_IN(src, life_time) +/mob/illusion/Destroy() + . = ..() + GLOB.mob_illusions_list -= src + ///Delete this illusion when the original xeno is ded /mob/illusion/proc/destroy_illusion() SIGNAL_HANDLER + GLOB.mob_illusions_list -= src qdel(src) /// Remove the filter effect added when it was hit @@ -73,5 +86,5 @@ . = ..() if(.) return INITIALIZE_HINT_QDEL - add_movespeed_modifier(MOVESPEED_ID_XENO_CASTE_SPEED, TRUE, 0, NONE, TRUE, original_mob.xeno_caste.speed * 1.3) + add_movespeed_modifier(MOVESPEED_ID_XENO_CASTE_SPEED, TRUE, 0, NONE, FALSE, original_mob.xeno_caste.speed * pick(0.9, 1, 1.1, 1.2, 1.3)) // rand doesn't work here because it's decimals AddComponent(/datum/component/ai_controller, /datum/ai_behavior/xeno/illusion, escorted_atom) diff --git a/code/modules/clothing/modular_armor/attachments/modules.dm b/code/modules/clothing/modular_armor/attachments/modules.dm index a0e1216d903..c2c9c0baf06 100644 --- a/code/modules/clothing/modular_armor/attachments/modules.dm +++ b/code/modules/clothing/modular_armor/attachments/modules.dm @@ -491,15 +491,15 @@ /obj/item/armor_module/module/welding/on_attach(obj/item/attaching_to, mob/user) . = ..() parent.AddComponent(/datum/component/clothing_tint, TINT_5, active) - if(active) // RUTGMC ADDITION START - parent.eye_protection += eye_protection_mod // reset to the users base eye // RUTGMC ADDITION END + if(active) + parent.eye_protection += eye_protection_mod // reset to the users base eye /obj/item/armor_module/module/welding/on_detach(obj/item/detaching_from, mob/user) parent.GetComponent(/datum/component/clothing_tint) var/datum/component/clothing_tint/tints = parent?.GetComponent(/datum/component/clothing_tint) tints.RemoveComponent() - if(active) // RUTGMC ADDITION START - parent.eye_protection -= eye_protection_mod // reset to the users base eye // RUTGMC ADDITION END + if(active) + parent.eye_protection -= eye_protection_mod // reset to the users base eye return ..() /obj/item/armor_module/module/welding/activate(mob/living/user) @@ -763,7 +763,6 @@ flags_attach_features = ATTACH_REMOVABLE|ATTACH_ACTIVATION|ATTACH_APPLY_ON_MOB slot = ATTACHMENT_SLOT_HEAD_MODULE prefered_slot = SLOT_HEAD - /// Who's using this item var/mob/living/carbon/human/operator ///The range of this motion detector @@ -824,9 +823,13 @@ hostile_detected = TRUE prepare_blip(nearby_human, nearby_human.wear_id?.iff_signal & operator.wear_id?.iff_signal ? MOTION_DETECTOR_FRIENDLY : MOTION_DETECTOR_HOSTILE) for(var/mob/living/carbon/xenomorph/nearby_xeno AS in cheap_get_xenos_near(operator, range)) + if(HAS_TRAIT(nearby_xeno, TRAIT_TURRET_HIDDEN)) + continue if(!hostile_detected) hostile_detected = TRUE prepare_blip(nearby_xeno, MOTION_DETECTOR_HOSTILE) + for(var/mob/illusion/nearby_illusion AS in cheap_get_illusions_near(operator, range)) + prepare_blip(nearby_illusion, MOTION_DETECTOR_HOSTILE) if(hostile_detected) playsound(loc, 'sound/items/tick.ogg', 100, 0, 1) addtimer(CALLBACK(src, PROC_REF(clean_blips)), scan_time / 2) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/hunter/abilities_hunter.dm b/code/modules/mob/living/carbon/xenomorph/castes/hunter/abilities_hunter.dm index 0f937641bbe..49190f5f764 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/hunter/abilities_hunter.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/hunter/abilities_hunter.dm @@ -17,7 +17,7 @@ var/can_sneak_attack = FALSE var/stealth_alpha_multiplier = 1 -/datum/action/ability/xeno_action/stealth/remove_action(mob/living/L) +/datum/action/ability/xeno_action/stealth/remove_action() if(stealth) cancel_stealth() return ..() @@ -51,16 +51,15 @@ RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(handle_stealth)) RegisterSignal(owner, COMSIG_XENOMORPH_POUNCE_END, PROC_REF(sneak_attack_pounce)) - RegisterSignal(owner, COMSIG_XENO_LIVING_THROW_HIT, PROC_REF(mob_hit)) - RegisterSignal(owner, COMSIG_XENOMORPH_ATTACK_LIVING, PROC_REF(sneak_attack_slash)) - RegisterSignal(owner, COMSIG_XENOMORPH_DISARM_HUMAN, PROC_REF(sneak_attack_slash)) + RegisterSignal(owner, COMSIG_XENOMORPH_LEAP_BUMP, PROC_REF(mob_hit)) + RegisterSignals(owner, list(COMSIG_XENOMORPH_ATTACK_LIVING, COMSIG_XENOMORPH_DISARM_HUMAN), PROC_REF(sneak_attack_slash)) RegisterSignal(owner, COMSIG_XENOMORPH_ZONE_SELECT, PROC_REF(sneak_attack_zone)) RegisterSignal(owner, COMSIG_XENOMORPH_PLASMA_REGEN, PROC_REF(plasma_regen)) // TODO: attack_alien() overrides are a mess and need a lot of work to make them require parentcalling RegisterSignals(owner, list( COMSIG_XENOMORPH_GRAB, - COMSIG_XENOMORPH_THROW_HIT, + COMSIG_XENOMORPH_LEAP_BUMP, COMSIG_LIVING_IGNITED, COMSIG_LIVING_ADD_VENTCRAWL), PROC_REF(cancel_stealth)) @@ -163,14 +162,18 @@ cancel_stealth() /// Callback for when a mob gets hit as part of a pounce -/datum/action/ability/xeno_action/stealth/proc/mob_hit(datum/source, mob/living/M) +/datum/action/ability/xeno_action/stealth/proc/mob_hit(datum/source, mob/living/living_target) SIGNAL_HANDLER - if(M.stat || isxeno(M)) + if(living_target.stat || isxeno(living_target)) + return + if(!can_sneak_attack) return - if(can_sneak_attack) - M.adjust_stagger(3 SECONDS) - M.add_slowdown(1) - to_chat(owner, span_xenodanger("Pouncing from the shadows, we stagger our victim.")) + living_target.adjust_stagger(3 SECONDS) + living_target.add_slowdown(1) + + var/mob/living/carbon/xenomorph/xeno = owner + living_target.attack_alien_harm(xeno, xeno.xeno_caste.melee_damage * xeno.xeno_melee_damage_modifier) + to_chat(owner, span_xenodanger("Pouncing from the shadows, we stagger our victim.")) /datum/action/ability/xeno_action/stealth/proc/sneak_attack_slash(datum/source, mob/living/target, damage, list/damage_mod, list/armor_mod) SIGNAL_HANDLER @@ -185,7 +188,7 @@ target.adjust_stagger(2 SECONDS) target.add_slowdown(1) target.ParalyzeNoChain(1 SECONDS) - target.apply_damage(damage, BRUTE, xeno.zone_selected, MELEE) // additional damage + target.apply_damage(damage, BRUTE, xeno.zone_selected, MELEE, , penetration = 15) // additional damage cancel_stealth() @@ -270,11 +273,12 @@ return var/mob/living/carbon/xenomorph/xeno = owner + damage = xeno.xeno_caste.melee_damage * xeno.xeno_melee_damage_modifier owner.visible_message(span_danger("\The [owner] strikes [target] with deadly precision!"), \ span_danger("We strike [target] with deadly precision!")) target.ParalyzeNoChain(1 SECONDS) - target.apply_damage(20, BRUTE, xeno.zone_selected) // additional damage + target.apply_damage(damage, BRUTE, xeno.zone_selected, MELEE, penetration = 25) // additional damage cancel_stealth() @@ -316,8 +320,7 @@ if(owner.layer != MOB_LAYER) owner.layer = MOB_LAYER var/datum/action/ability/xeno_action/xenohide/hide_action = owner.actions_by_path[/datum/action/ability/xeno_action/xenohide] - //hide_action?.button?.cut_overlay(mutable_appearance('icons/Xeno/actions.dmi', "selected_purple_frame", ACTION_LAYER_ACTION_ICON_STATE, FLOAT_PLANE)) // Removes Hide action icon border // ORIGINAL - hide_action?.button?.cut_overlay(mutable_appearance('icons/Xeno/actions.dmi', "selected_purple_frame", ACTION_LAYER_ACTION_ICON_STATE, FLOAT_PLANE)) // RUTGMC EDIT + hide_action?.button?.cut_overlay(mutable_appearance('icons/Xeno/actions.dmi', "selected_purple_frame", ACTION_LAYER_ACTION_ICON_STATE, FLOAT_PLANE)) // Removes Hide action icon border if(owner.buckled) owner.buckled.unbuckle_mob(owner) RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(movement_fx)) @@ -354,7 +357,7 @@ if(!human_target.check_shields(COMBAT_TOUCH_ATTACK, 30, "melee")) xeno_owner.Paralyze(XENO_POUNCE_SHIELD_STUN_DURATION) xeno_owner.set_throwing(FALSE) - playsound(xeno_owner, 'sound/machines/bonk.ogg', 50, FALSE) // RUTGMC ADDITION + playsound(xeno_owner, 'sound/machines/bonk.ogg', 50, FALSE) return trigger_pounce_effect(living_target) pounce_complete() @@ -373,7 +376,7 @@ UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_XENO_OBJ_THROW_HIT, COMSIG_XENOMORPH_LEAP_BUMP, COMSIG_MOVABLE_POST_THROW)) SEND_SIGNAL(owner, COMSIG_XENOMORPH_POUNCE_END) var/mob/living/carbon/xenomorph/xeno_owner = owner - xeno_owner.set_throwing(FALSE) // RUTGMC ADDITION, for whatever modular fuckery, without this pounce doesn't stop + xeno_owner.set_throwing(FALSE) xeno_owner.xeno_flags &= ~XENO_LEAPING /datum/action/ability/activable/xeno/pounce/proc/reset_pass_flags() diff --git a/code/modules/projectiles/sentries.dm b/code/modules/projectiles/sentries.dm index b9be053f51d..4ed6405f75c 100644 --- a/code/modules/projectiles/sentries.dm +++ b/code/modules/projectiles/sentries.dm @@ -1,10 +1,8 @@ /obj/machinery/deployable/mounted/sentry - resistance_flags = UNACIDABLE|XENO_DAMAGEABLE use_power = 0 req_one_access = list(ACCESS_MARINE_ENGINEERING, ACCESS_MARINE_ENGPREP, ACCESS_MARINE_LEADER) hud_possible = list(MACHINE_HEALTH_HUD, MACHINE_AMMO_HUD) - ///Spark system for making sparks var/datum/effect_system/spark_spread/spark_system ///Camera for viewing with cam consoles @@ -13,18 +11,14 @@ var/range = 7 ///Damage required to knock the sentry over and disable it var/knockdown_threshold = 150 - ///List of targets that can be shot at var/list/atom/potential_targets = list() - ///Time of last alert var/last_alert = 0 ///Time of last damage alert var/last_damage_alert = 0 - ///Radio so that the sentry can scream for help var/obj/item/radio/radio - ///Iff signal of the sentry. If the /gun has a set IFF then this will be the same as that. If not the sentry will get its IFF signal from the deployer var/iff_signal = NONE ///List of terrains/structures/machines that the sentry ignores for targetting. (If a window is inside the list, the sentry will shot at targets even if the window breaks los) For accuracy, this is on a specific typepath base and not istype(). @@ -311,10 +305,7 @@ return if(prob(10)) spark_system.start() - //RUTGMC EDIT CHANGE BEGIN - //if(damage_amount >= knockdown_threshold) // ORIGINAL if(damage_amount >= knockdown_threshold && damage_type != STAMINA) //Knockdown is certain if we deal this much in one hit; no more RNG nonsense, the fucking thing is bolted. - //RUTGMC EDIT END knock_down() . = ..() @@ -331,7 +322,7 @@ if(!internal_item) return var/obj/item/weapon/gun/gun = get_internal_item() - if(!gun) // RUTGMC ADDITION + if(!gun) return if(!alert_code || !CHECK_BITFIELD(gun.turret_flags, TURRET_ALERTS) || !CHECK_BITFIELD(gun.turret_flags, TURRET_ON)) return @@ -361,7 +352,6 @@ playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, FALSE) radio.talk_into(src, "[notice]", FREQ_COMMON) - /obj/machinery/deployable/mounted/sentry/process() update_icon() if(!scan()) @@ -388,6 +378,8 @@ if(nearby_xeno.stat == DEAD || HAS_TRAIT(nearby_xeno, TRAIT_TURRET_HIDDEN) || CHECK_BITFIELD(nearby_xeno.status_flags, INCORPOREAL) || CHECK_BITFIELD(nearby_xeno.xeno_iff_check(), iff_signal)) //So wraiths wont be shot at when in phase shift continue potential_targets += nearby_xeno + for(var/mob/illusion/nearby_illusion AS in cheap_get_illusions_near(src, range)) + potential_targets += nearby_illusion for(var/obj/vehicle/sealed/mecha/nearby_mech AS in cheap_get_mechs_near(src, range)) if(!length(nearby_mech.occupants)) continue @@ -536,7 +528,6 @@ if(internal_gun) . += image('icons/Marine/sentry.dmi', src, internal_gun.placed_overlay_iconstate, dir = dir) - //Throwable turret /obj/machinery/deployable/mounted/sentry/cope density = FALSE