diff --git a/code/__DEFINES/crusher_defines.dm b/code/__DEFINES/crusher_defines.dm index 9f403acd65c..0f9697b462b 100644 --- a/code/__DEFINES/crusher_defines.dm +++ b/code/__DEFINES/crusher_defines.dm @@ -14,3 +14,4 @@ #define TROPHY_BROOD_TONGUE "trophy brood tongue" #define TROPHY_DEMON_CLAWS "trophy demon claws" #define TROPHY_LEGIONNAIRE_SPINE "trophy legionnaire spine" +#define TROPHY_RAPTOR_FEATHER "trophy raptor feather" 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 ad8b0094a43..93243739868 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -337,6 +337,12 @@ /// Index in modifiers containing the modifer to surgery speed #define SPEED_MOD_INDEX 2 +/// From /datum/status_effect/proc/on_creation() : (datum/status_effect/effect) +#define COMSIG_LIVING_STATUS_APPLIED "living_status_applied" + +/// From /datum/status_effect/proc/Destroy() : (datum/status_effect/effect) +#define COMSIG_LIVING_STATUS_REMOVED "living_status_removed" + /// From /datum/spawners_menu/ui_static_data(mob/user) : (list/string_info) #define COMSIG_LIVING_GHOSTROLE_INFO "living_ghostrole_info" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index d874ababd0b..802bf04212d 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -1546,4 +1546,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Trait that allows mobs to perform surgery on themselves #define TRAIT_SELF_SURGERY "self_surgery" +/// Trait that makes mobs with it immune to mining gear AOE attacks +#define TRAIT_MINING_AOE_IMMUNE "mining_aoe_immune" + // END TRAIT DEFINES diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 4d8210213b0..9309e7e4cf9 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -618,6 +618,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NOGRAV_ALWAYS_DRIFT" = TRAIT_NOGRAV_ALWAYS_DRIFT, "TRAIT_SPEECH_BOOSTER" = TRAIT_SPEECH_BOOSTER, "TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING, + "TRAIT_MINING_AOE_IMMUNE" = TRAIT_MINING_AOE_IMMUNE, "TRAIT_IGNORE_FIRE_PROTECTION" = TRAIT_IGNORE_FIRE_PROTECTION, "TRAIT_ILLUSORY_EFFECT" = TRAIT_ILLUSORY_EFFECT, "TRAIT_INVISIBLE_TO_CAMERA" = TRAIT_INVISIBLE_TO_CAMERA, diff --git a/code/datums/components/ranged_attacks.dm b/code/datums/components/ranged_attacks.dm index 60fcabfac94..b998362cfc6 100644 --- a/code/datums/components/ranged_attacks.dm +++ b/code/datums/components/ranged_attacks.dm @@ -45,13 +45,15 @@ /datum/component/ranged_attacks/RegisterWithParent() . = ..() - RegisterSignal(parent, COMSIG_MOB_ATTACK_RANGED, PROC_REF(fire_ranged_attack)) ADD_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type) + RegisterSignal(parent, COMSIG_MOB_ATTACK_RANGED, PROC_REF(fire_ranged_attack)) RegisterSignal(parent, COMSIG_MOB_TROPHY_ACTIVATED(TROPHY_WATCHER), PROC_REF(disable_attack)) + RegisterSignal(parent, COMSIG_LIVING_STATUS_APPLIED, PROC_REF(on_status_applied)) + RegisterSignal(parent, COMSIG_LIVING_STATUS_REMOVED, PROC_REF(on_status_removed)) /datum/component/ranged_attacks/UnregisterFromParent() . = ..() - UnregisterSignal(parent, COMSIG_MOB_ATTACK_RANGED) + UnregisterSignal(parent, list(COMSIG_MOB_ATTACK_RANGED, COMSIG_MOB_TROPHY_ACTIVATED(TROPHY_WATCHER), COMSIG_LIVING_STATUS_APPLIED, COMSIG_LIVING_STATUS_REMOVED)) REMOVE_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type) /datum/component/ranged_attacks/proc/fire_ranged_attack(mob/living/basic/firer, atom/target, modifiers) @@ -89,7 +91,26 @@ SEND_SIGNAL(parent, COMSIG_BASICMOB_POST_ATTACK_RANGED, target, modifiers) return +/// Delay the attack when hit by a watcher trophy detonation /datum/component/ranged_attacks/proc/disable_attack(mob/source, obj/item/crusher_trophy/used_trophy, mob/living/user) SIGNAL_HANDLER var/stun_duration = (used_trophy.bonus_value * 0.1) SECONDS COOLDOWN_INCREMENT(src, fire_cooldown, stun_duration) + +/// Increase CD when rebuked +/datum/component/ranged_attacks/proc/on_status_applied(mob/living/source, datum/status_effect/effect) + SIGNAL_HANDLER + if (!istype(effect, /datum/status_effect/rebuked)) + return + var/datum/status_effect/rebuked/rebuked = effect + if (!COOLDOWN_FINISHED(src, fire_cooldown)) + COOLDOWN_INCREMENT(src, fire_cooldown, cooldown_time * (rebuked.cd_increase - 1)) + cooldown_time *= rebuked.cd_increase + +/// Reduce CD back when the rebuked status runs out +/datum/component/ranged_attacks/proc/on_status_removed(mob/living/source, datum/status_effect/effect) + SIGNAL_HANDLER + if (!istype(effect, /datum/status_effect/rebuked)) + return + var/datum/status_effect/rebuked/rebuked = effect + cooldown_time /= rebuked.cd_increase diff --git a/code/datums/status_effects/_status_effect.dm b/code/datums/status_effects/_status_effect.dm index 58abf9b315c..17eb45e168f 100644 --- a/code/datums/status_effects/_status_effect.dm +++ b/code/datums/status_effects/_status_effect.dm @@ -79,7 +79,7 @@ START_PROCESSING(SSpriority_effects, src) update_particles() - + SEND_SIGNAL(owner, COMSIG_LIVING_STATUS_APPLIED, src) return TRUE /datum/status_effect/Destroy() @@ -96,6 +96,7 @@ LAZYREMOVE(owner.status_effects, src) on_remove() UnregisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL) + SEND_SIGNAL(owner, COMSIG_LIVING_STATUS_REMOVED, src) owner = null if(particle_effect) QDEL_NULL(particle_effect) diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 97345da429c..9c28809a9d1 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -938,12 +938,14 @@ duration = 30 SECONDS tick_interval = 1 SECONDS alert_type = null + /// By how much we should increase the attack cooldown + var/cd_increase = 2.5 /datum/status_effect/rebuked/on_apply() owner.next_move_modifier *= 2 if(ishostile(owner)) var/mob/living/simple_animal/hostile/simple_owner = owner - simple_owner.ranged_cooldown_time *= 2.5 + simple_owner.ranged_cooldown_time *= cd_increase return TRUE /datum/status_effect/rebuked/on_remove() @@ -953,7 +955,7 @@ owner.next_move_modifier *= 0.5 if(ishostile(owner)) var/mob/living/simple_animal/hostile/simple_owner = owner - simple_owner.ranged_cooldown_time /= 2.5 + simple_owner.ranged_cooldown_time /= cd_increase /datum/status_effect/freezing_blast id = "freezing_blast" diff --git a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm index 4e67861a7ed..4a0069660f2 100644 --- a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm @@ -188,15 +188,15 @@ // 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) var/target_health = target.health + var/combined_damage = detonation_damage for(var/obj/item/crusher_trophy/crusher_trophy as anything in trophies) - crusher_trophy.on_mark_detonation(target, user) + combined_damage += crusher_trophy.on_mark_detonation(target, user) if(QDELETED(target)) return if(!QDELETED(crusher_damage_effect)) crusher_damage_effect.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did new /obj/effect/temp_visual/kinetic_blast(get_turf(target)) var/backstabbed = FALSE - var/combined_damage = detonation_damage var/def_check = target.getarmor(type = BOMB) // Backstab bonus if(check_behind(user, target) || boosted_mark) @@ -297,6 +297,8 @@ log_override = TRUE /// Has this projectile been boosted var/boosted = FALSE + /// Should this projectile go through allied mobs? + var/ignore_allies = FALSE /obj/projectile/destabilizer/Initialize(mapload) . = ..() @@ -313,6 +315,14 @@ damage = 10 range = 9 +/obj/projectile/destabilizer/prehit_pierce(atom/target) + if(!isliving(target) || !firer || !ignore_allies) + return ..() + var/mob/living/victim = target + if(firer.faction_check_atom(victim)) + return PROJECTILE_PIERCE_PHASE + return ..() + /obj/projectile/destabilizer/on_hit(atom/target, blocked = 0, pierce_hit) var/obj/item/kinetic_crusher/used_crusher if(istype(fired_from, /obj/item/kinetic_crusher)) diff --git a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher_trophies.dm b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher_trophies.dm index 7d01752982d..0a3e49843de 100644 --- a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher_trophies.dm +++ b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher_trophies.dm @@ -63,6 +63,7 @@ return /// Does an effect when you hit a mob that is marked via the projectile +/// Returns additional damage for detonation /obj/item/crusher_trophy/proc/on_mark_detonation(mob/living/target, mob/living/user) //the target and the user SHOULD_CALL_PARENT(TRUE) //if we dont have a set id, use the typepath as identifier diff --git a/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm b/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm index fdc1564004c..2f5e4b8302b 100644 --- a/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm +++ b/code/modules/mining/equipment/kinetic_crusher/trophies_fauna.dm @@ -103,7 +103,7 @@ missing_health *= missing_health_ratio //bonus is active at all times, even if you're above 90 health missing_health *= bonus_value //multiply the remaining amount by bonus_value if(missing_health > 0) - target.adjustBruteLoss(missing_health) //and do that much damage + return missing_health //and do that much damage // Lobstrosity - Rebukes targets, increasing their click cooldown. /obj/item/crusher_trophy/lobster_claw @@ -116,7 +116,7 @@ wildhunter_drop = /obj/item/organ/monster_core/rush_gland /obj/item/crusher_trophy/lobster_claw/effect_desc() - return "mark detonation to briefly rebuke the target for [bonus_value] seconds" + return "mark detonation to briefly rebuke the target for [bonus_value] second[bonus_value > 1 ? "s" : ""]" /obj/item/crusher_trophy/lobster_claw/on_mark_detonation(mob/living/target, mob/living/user) . = ..() @@ -186,13 +186,29 @@ check_flags = NONE owner_has_control = FALSE cooldown_time = 10 SECONDS - projectile_type = /obj/projectile/bileworm_acid + projectile_type = /obj/projectile/bileworm_acid/crusher projectile_sound = 'sound/mobs/non-humanoids/bileworm/bileworm_spit.ogg' /datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/spewlet/New(Target) firing_directions = GLOB.cardinals.Copy() return ..() +/obj/projectile/bileworm_acid/crusher + damage_type = BRUTE // Otherwise the mobs take heavily reduced damage + +/obj/projectile/bileworm_acid/crusher/prehit_pierce(atom/target) + if (!isliving(target)) + return ..() + var/mob/living/as_living = target + // Only hit hostile things, or mining mobs if we have no firer (somehow) + if (firer) + if (firer.faction_check_atom(as_living)) + return PROJECTILE_DELETE_WITHOUT_HITTING + return ..() + if (as_living.mob_biotypes & MOB_MINING) + return ..() + return PROJECTILE_DELETE_WITHOUT_HITTING + // demonic watcher /obj/item/crusher_trophy/ice_demon_cube name = "demonic cube" @@ -245,7 +261,7 @@ . = ..() user.apply_status_effect(/datum/status_effect/speed_boost, 1 SECONDS) -// Polar bear +// Polar bear - If you're hurt, you attack twice when you detonate a mark /obj/item/crusher_trophy/bear_paw name = "polar bear paw" desc = "It's a polar bear paw." @@ -260,7 +276,21 @@ . = ..() if(user.health / user.maxHealth > 0.5) return - var/obj/item/I = user.get_active_held_item() - if(!I) - return - I.melee_attack_chain(user, target, null) + var/obj/item/weapon = user.get_active_held_item() + if(weapon) + addtimer(CALLBACK(weapon, TYPE_PROC_REF(/obj/item, melee_attack_chain), user, target), 0.1 SECONDS) + +// Raptor - Your shots now go through your allied mobs. You monster. +/obj/item/crusher_trophy/raptor_feather + name = "raptor feather" + desc = "A feather of an innocent raptor. You'd go to hell for this one, if you weren't already mining in it." + icon_state = "raptor_feather" + denied_type = /obj/item/crusher_trophy/raptor_feather + trophy_id = TROPHY_RAPTOR_FEATHER + wildhunter_drop = /obj/item/food/meat/slab/chicken + +/obj/item/crusher_trophy/raptor_feather/effect_desc() + return "your shots to go through your allies" + +/obj/item/crusher_trophy/raptor_feather/on_projectile_fire(obj/projectile/destabilizer/marker, mob/living/user) + marker.ignore_allies = TRUE diff --git a/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm b/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm index 9b1897c77d7..56444d71dd9 100644 --- a/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm +++ b/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm @@ -222,7 +222,7 @@ return var/mob/living/basic/legion_brood/minion = new (user.loc) minion.assign_creator(user) - minion.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] = target + minion.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) /obj/item/crusher_trophy/legionnaire_spine/attack_self(mob/user) if(!isliving(user)) @@ -241,7 +241,7 @@ //The Thing /obj/item/crusher_trophy/flesh_glob name = "glob of shifting flesh" - desc = "A glob of shifting flesh. Sealed shut permanently. Suitable as a trophy for a kinetic crusher." + desc = "A glob of shifting flesh, sealed shut permanently. Suitable as a trophy for a kinetic crusher." icon_state = "glob" denied_type = /obj/item/crusher_trophy/flesh_glob bonus_value = 20 diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm index fa229751d24..282ae4d6f55 100644 --- a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm @@ -55,6 +55,7 @@ icon_state = "ice_demon" icon_living = "ice_demon" icon_gib = "syndicate_gib" + density = FALSE mouse_opacity = MOUSE_OPACITY_ICON basic_mob_flags = DEL_ON_DEATH speed = 5 @@ -70,6 +71,14 @@ ///how long do we exist for var/existence_period = 15 SECONDS +// Passable, but mining mobs avoid pathing through them +/mob/living/basic/mining/demon_afterimage/CanAStarPass(to_dir, datum/can_pass_info/pass_info) + var/mob/living/requester = pass_info.requester_ref?.resolve() + // Only block mining mobs so that drones etc can still path through + if (istype(requester) && (requester.mob_biotypes & MOB_MINING) && requester.loc != loc) + return FALSE + return ..() + /mob/living/basic/mining/demon_afterimage/Initialize(mapload) . = ..() AddElement(/datum/element/simple_flying) diff --git a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm index cf0fa68bb32..0f1f0962559 100644 --- a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm +++ b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm @@ -48,6 +48,10 @@ /// Set when the drone is begining to leave lavaland after the vent is secured. var/escaping = FALSE +/mob/living/basic/node_drone/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_MINING_AOE_IMMUNE, INNATE_TRAIT) + /mob/living/basic/node_drone/death(gibbed) . = ..() explosion(origin = src, light_impact_range = 1, smoke = 1) @@ -57,7 +61,6 @@ attached_vent = null return ..() - /mob/living/basic/node_drone/examine(mob/user) . = ..() var/sameside = user.faction_check_atom(src, exact_match = FALSE) diff --git a/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm b/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm index 2bbda673ed7..ad8dd30de5d 100644 --- a/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm +++ b/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm @@ -38,6 +38,10 @@ GLOBAL_LIST_EMPTY(raptor_population) attack_sound = 'sound/items/weapons/punch1.ogg' faction = list(FACTION_RAPTOR, FACTION_NEUTRAL) speak_emote = list("screeches") + butcher_results = list( + /obj/item/food/meat/slab/chicken = 4, + /obj/item/stack/sheet/bone = 2, + ) ai_controller = /datum/ai_controller/basic_controller/raptor ///can this mob breed var/can_breed = TRUE @@ -73,6 +77,12 @@ GLOBAL_LIST_EMPTY(raptor_population) AddElement(/datum/element/wears_collar) add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE), INNATE_TRAIT) + AddElement(\ + /datum/element/crusher_loot,\ + trophy_type = /obj/item/crusher_trophy/raptor_feather,\ + drop_mod = 100,\ + drop_immediately = FALSE,\ + ) if(!mapload) GLOB.raptor_population += REF(src) diff --git a/code/modules/mob/living/basic/minebots/minebot.dm b/code/modules/mob/living/basic/minebots/minebot.dm index 6c6ad8a208f..eb63db2cd00 100644 --- a/code/modules/mob/living/basic/minebots/minebot.dm +++ b/code/modules/mob/living/basic/minebots/minebot.dm @@ -22,6 +22,7 @@ sentience_type = SENTIENCE_MINEBOT speak_emote = list("states") mob_biotypes = MOB_ROBOTIC + faction = list(FACTION_STATION, FACTION_NEUTRAL) death_message = "blows apart!" light_system = OVERLAY_LIGHT light_range = 6 @@ -67,7 +68,7 @@ AddComponent(/datum/component/obeys_commands, pet_commands) var/static/list/death_drops = list(/obj/effect/decal/cleanable/blood/gibs/robot_debris/old) AddElement(/datum/element/death_drops, death_drops) - add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), INNATE_TRAIT) + add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE, TRAIT_MINING_AOE_IMMUNE), INNATE_TRAIT) AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE) var/static/list/innate_actions = list( diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index 3eee7969e9c..3a244f7ea74 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -620,12 +620,17 @@ playsound(src, 'sound/effects/magic/magic_missile.ogg', 200, vary = TRUE) for(var/turf/closed/mineral/rock in circle_range_turfs(src, 2)) rock.gets_drilled() - for(var/mob/living/mob in range(1, src)) - mob.apply_damage(damage * (ismining(mob) ? fauna_boost : 1), BRUTE, spread_damage = TRUE) - if(!ishostile(mob) || !firer) + for(var/mob/living/victim in range(1, src)) + if(HAS_TRAIT(victim, TRAIT_MINING_AOE_IMMUNE)) continue - var/mob/living/simple_animal/hostile/hostile_mob = mob - hostile_mob.GiveTarget(firer) + victim.apply_damage(damage * (ismining(victim) ? fauna_boost : 1), BRUTE, spread_damage = TRUE) + if(!firer) + continue + if(ishostile(victim)) + var/mob/living/simple_animal/hostile/hostile_mob = victim + hostile_mob.GiveTarget(firer) + else if(isbasicmob(victim)) + victim.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, firer) for(var/obj/object in range(1, src)) object.take_damage(damage, BRUTE, BOMB) qdel(src) diff --git a/icons/obj/mining_zones/artefacts.dmi b/icons/obj/mining_zones/artefacts.dmi index 808ca54cd5e..86cfdcd7624 100644 Binary files a/icons/obj/mining_zones/artefacts.dmi and b/icons/obj/mining_zones/artefacts.dmi differ