Changes to crusher trophies and mining AOE, adds a new raptor-sourced trophy (#92241)

## About The Pull Request

- Rebuke effect (from god's eye and lobstrocity claw trophy) now works
on basicmobs, increasing the cooldown on their ranged attacks just like
it does for simplemobs.
- Bileworm spewlet trophy shots no longer hit your allied mobs, as
previously this would cause you to constantly hit your own
raptor/minebots/NODE drones, making it actively detrimental in some
situations. Its shots now deals brute damage instead of burn, as
otherwise its damage was reduced by 70% due to innate projectile
resistance of lavaland mobs, making it deal measely 6 damage every 10
seconds.
- MOD sphere module bombs now properly aggro lavaland mobs, as
previously they only worked on simplemobs (also fixed a direct
assignment to the blackboard in legionnaire spine code).
- They also no longer deal damage to minebots and NODE drones.
- Afterimages from the ice demon and their trophy can now be passed
through, although hostile AI would attempt to avoid doing so. This way
the trophy should no longer be an active detriment to players, and
demons themselves should be less jank to fight.

And if you're a heartless enough bastard, you can kill and butcher your
raptor to get a new raptor feather crusher trophy, which allows your
destabilizer shots to phase through your allied mobs similarly to
passthrough mods for PKA.

<img width="174" height="125" alt="Aseprite_3Olcd7oyVJ"
src="https://github.com/user-attachments/assets/99d7eebb-e36d-428b-aa48-f1261a173ca1"
/>

## Why It's Good For The Game

These changes should make vent defense more bearable, as right now its
very easy to accidentally damage and kill your own drone due to them
being hit by all AOEs in miner arsenal.
- Rebuke - should probably work on basicmobs as only remaining
simplemobs on lavaland are megafauna
- Bileworm spewlet - its a joke of a trophy at 6 damage as it has a 10
second cooldown, and it hitting your allies made vent defense much
harder than it should've been
- Sphere changes - should make bombs not kill your NODE/mining drones,
aggro helps prevent cheese.
- Afterimages - the trophy can end up bodyblocking you, this change
should make it less of a pain in the ass to use the trophy and to fight
the demons themselves.
- Raptor feather - useful for vent defense when you're using minebots or
have dismounted your raptor, right now its a pain for reasons mentioned
above

## Changelog
🆑
add: Added a raptor feather crusher trophy which makes your crusher
shots go through your allied mobs.
balance: Rebuke effect from lobster claw trophy and the eye of god now
applies to basicmob attacks
balance: Bileworm spewlet's damage is no longer reduced by 70% when
hitting lavaland fauna, and it no longer can hit allied mobs
balance: Sphere MODule bombs no longer hit NODE drones and minebots
balance: Ice demon/ice demon cube afterimages can now be walked through
by players
fix: Sphere MODule bombs now aggro basicmobs hit by their explosions
/🆑
This commit is contained in:
SmArtKar
2025-07-28 09:24:19 +02:00
committed by Roxy
parent 3cb5acd8ba
commit 077262c0f9
17 changed files with 128 additions and 24 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -1548,4 +1548,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

View File

@@ -632,6 +632,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,

View File

@@ -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

View File

@@ -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)

View File

@@ -942,12 +942,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()
@@ -957,7 +959,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"

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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(

View File

@@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 43 KiB