From 1f66295b54dcbc9ce1581ef1b522902f7da15efe Mon Sep 17 00:00:00 2001 From: CHOMPStation2StaffMirrorBot <94713762+CHOMPStation2StaffMirrorBot@users.noreply.github.com> Date: Thu, 25 Jun 2026 11:36:59 -0700 Subject: [PATCH] [MIRROR] Aberrations (#12852) Co-authored-by: Cameron Lennox --- code/datums/components/crafting/recipes.dm | 2 + .../weapons/material/material_weapons.dm | 4 +- code/game/objects/items/weapons/traps.dm | 47 +++++++- code/game/objects/items/weapons/traps_vr.dm | 18 --- .../objects/traps/aberrations/aberration.dm | 55 +++++++++ code/game/objects/traps/aberrations/mapped.dm | 10 ++ .../traps/aberrations/spawners/_spawner.dm | 46 ++++++++ .../traps/aberrations/spawners/fire.dm | 34 ++++++ .../traps/aberrations/spawners/glass.dm | 10 ++ code/game/objects/traps/glass_wire.dm | 106 ++++++++++++++++++ .../materials/materials/alien_alloy.dm | 1 + code/modules/surgery/generic.dm | 1 + icons/obj/traps/abberations.dmi | Bin 0 -> 6588 bytes vorestation.dme | 7 +- 14 files changed, 315 insertions(+), 26 deletions(-) delete mode 100644 code/game/objects/items/weapons/traps_vr.dm create mode 100644 code/game/objects/traps/aberrations/aberration.dm create mode 100644 code/game/objects/traps/aberrations/mapped.dm create mode 100644 code/game/objects/traps/aberrations/spawners/_spawner.dm create mode 100644 code/game/objects/traps/aberrations/spawners/fire.dm create mode 100644 code/game/objects/traps/aberrations/spawners/glass.dm create mode 100644 code/game/objects/traps/glass_wire.dm create mode 100644 icons/obj/traps/abberations.dmi diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm index 110a19b2ed..98035259a2 100644 --- a/code/datums/components/crafting/recipes.dm +++ b/code/datums/components/crafting/recipes.dm @@ -71,6 +71,8 @@ var/obj/item/material/twohanded/spear/S = result S.set_material(M.material.name) + if(!(S.material.flags & MATERIAL_BRITTLE)) + S.fragile = FALSE qdel(M) /datum/crafting_recipe/surgerytable diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index cf000db8d7..9e6603be37 100644 --- a/code/game/objects/items/weapons/material/material_weapons.dm +++ b/code/game/objects/items/weapons/material/material_weapons.dm @@ -26,7 +26,7 @@ var/default_material = MAT_STEEL var/datum/material/material var/drops_debris = 1 - var/named_from_material = 1 //YW EDIT, Does it prepend the material's name to it's name? + var/named_from_material = TRUE /obj/item/material/Initialize(mapload, material_key) . = ..() @@ -65,7 +65,7 @@ if(!material) qdel(src) else - if(named_from_material) //YW EDIT + if(named_from_material) name = "[material.display_name] [initial(name)]" health = round(material.integrity/10) if(applies_material_colour) diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index 74b21ec156..438012bca5 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -21,6 +21,24 @@ var/deployed = 0 var/camo_net = FALSE var/stun_length = 0.25 SECONDS + slot_flags = SLOT_MASK + item_icons = list( + slot_wear_mask_str = 'icons/inventory/face/mob.dmi' + ) + +/obj/item/beartrap/equipped() + if(ishuman(src.loc)) + var/mob/living/carbon/human/H = src.loc + if(H.wear_mask == src) + add_verb(H, /mob/living/proc/shred_limb_temp) + else + remove_verb(H, /mob/living/proc/shred_limb_temp) + ..() + +/obj/item/beartrap/dropped(mob/user, equipping, slot) + remove_verb(user, /mob/living/proc/shred_limb_temp) + ..() + /obj/item/beartrap/start_active deployed = TRUE @@ -193,12 +211,13 @@ if(!QDELETED(src)) health = round(material.integrity / 3) - name = (material.get_edge_damage() * force_divisor > 15) ? "[material.display_name] razor wire" : "[material.display_name] [initial(name)]" + if(named_from_material) + name = (material.get_edge_damage() * force_divisor > 15) ? "[material.display_name] razor wire" : "[material.display_name] [initial(name)]" /obj/item/material/barbedwire/proc/can_use(mob/user) return (user.IsAdvancedToolUser() && !issilicon(user) && !user.stat && !user.restrained()) -/obj/item/material/barbedwire/attack_hand(mob/user as mob) +/obj/item/material/barbedwire/attack_hand(mob/user) if(anchored && can_use(user)) user.visible_message( span_danger("[user] starts to collect \the [src]."), @@ -242,7 +261,7 @@ anchored = TRUE update_icon() -/obj/item/material/barbedwire/attackby(obj/item/W as obj, mob/user as mob) +/obj/item/material/barbedwire/attackby(obj/item/W, mob/user) if(!istype(W)) return @@ -275,7 +294,7 @@ else icon_state = "[initial(icon_state)]" -/obj/item/material/barbedwire/Crossed(atom/movable/AM as mob|obj) +/obj/item/material/barbedwire/Crossed(atom/movable/AM) if(AM.is_incorporeal()) return if(anchored && isliving(AM)) @@ -290,7 +309,7 @@ update_icon() ..() -/obj/item/material/barbedwire/proc/shock(mob/user as mob, prb, target_zone = BP_TORSO) +/obj/item/material/barbedwire/proc/shock(mob/user, prb, target_zone = BP_TORSO) if(!anchored || health == 0) // anchored/destroyed grilles are never connected return 0 if(material.conductivity <= 0) @@ -399,3 +418,21 @@ /obj/item/material/barbedwire/plastic name = "snare wire" default_material = MAT_PLASTIC + +/obj/item/material/barbedwire/plastic/starts_active + anchored = TRUE + +/obj/item/material/barbedwire/plastic/starts_active/Initialize(mapload, material_key) + . = ..() + update_icon() + +/obj/item/material/barbedwire/durasteel + name = "durasteel razor wire" + default_material = MAT_DURASTEEL + +/obj/item/material/barbedwire/durasteel/starts_active + anchored = TRUE + +/obj/item/material/barbedwire/durasteel/starts_active/Initialize(mapload, material_key) + . = ..() + update_icon() diff --git a/code/game/objects/items/weapons/traps_vr.dm b/code/game/objects/items/weapons/traps_vr.dm deleted file mode 100644 index 96a0f31968..0000000000 --- a/code/game/objects/items/weapons/traps_vr.dm +++ /dev/null @@ -1,18 +0,0 @@ -/obj/item/beartrap - slot_flags = SLOT_MASK - item_icons = list( - slot_wear_mask_str = 'icons/inventory/face/mob.dmi' - ) - -/obj/item/beartrap/equipped() - if(ishuman(src.loc)) - var/mob/living/carbon/human/H = src.loc - if(H.wear_mask == src) - add_verb(H, /mob/living/proc/shred_limb_temp) - else - remove_verb(H, /mob/living/proc/shred_limb_temp) - ..() - -/obj/item/beartrap/dropped(mob/user, equipping, slot) - remove_verb(user, /mob/living/proc/shred_limb_temp) - ..() diff --git a/code/game/objects/traps/aberrations/aberration.dm b/code/game/objects/traps/aberrations/aberration.dm new file mode 100644 index 0000000000..fe9239c453 --- /dev/null +++ b/code/game/objects/traps/aberrations/aberration.dm @@ -0,0 +1,55 @@ + +/obj/effect/abstract/abberation + name = "Abberation" + desc = "Some sort of weird, pulsating entity." + icon = 'icons/effects/effects.dmi' + icon_state = "pre_confuse" + alpha = 0 + + ///If the abberation starts active or not. + var/start_active = TRUE + + ///If the abberation has a toggling effect (Turns on-off) + var/enabled = FALSE + + ///If it pulses or not. i.e. has a non-continuous effect + var/pulses = FALSE + + ///Min time for pulses to occur + var/pulse_time_min = 15 + + ///Max time for pulses to occur + var/pulse_time_max = 30 + + ///The cooldown for our pulse + COOLDOWN_DECLARE(pulse) + +/obj/effect/abstract/abberation/Initialize(mapload) + . = ..() + if(start_active) + START_PROCESSING(SSobj, src) + +/obj/effect/abstract/abberation/Destroy() + . = ..() + STOP_PROCESSING(SSobj, src) + +/obj/effect/abstract/abberation/process() + if(COOLDOWN_FINISHED(src, pulse)) + perform_pulse() + COOLDOWN_START(src, pulse, rand(pulse_time_min, pulse_time_max)) + perform_ambient_effects() + +/obj/effect/abstract/abberation/proc/perform_pulse() + return + +/obj/effect/abstract/abberation/proc/perform_ambient_effects() + return + + +/* +//Useful for later + var/static/list/connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(ignite_the_unworthy), + ) + AddComponent(/datum/component/connect_range, src, connections, range) +*/ diff --git a/code/game/objects/traps/aberrations/mapped.dm b/code/game/objects/traps/aberrations/mapped.dm new file mode 100644 index 0000000000..1cc03ecae7 --- /dev/null +++ b/code/game/objects/traps/aberrations/mapped.dm @@ -0,0 +1,10 @@ +/obj/random/aberration + name = "Random Abberation" + desc = "This is a random abberation." + icon = 'icons/obj/toy.dmi' + icon_state = "generic" + spawn_nothing_percentage = 25 + +/obj/random/aberration/item_to_spawn() + return pick(prob(15);/obj/effect/abstract/abberation/spawner/fire, + prob(15);/obj/effect/abstract/abberation/spawner/glass) diff --git a/code/game/objects/traps/aberrations/spawners/_spawner.dm b/code/game/objects/traps/aberrations/spawners/_spawner.dm new file mode 100644 index 0000000000..e5bb7f4bb7 --- /dev/null +++ b/code/game/objects/traps/aberrations/spawners/_spawner.dm @@ -0,0 +1,46 @@ +//Fire abberation +/obj/effect/abstract/abberation/spawner + name = "Spawner Abberation" + + ///What range we spawn our effects. + var/range = 4 + + ///If we use random range or not + var/random_range = TRUE + + ///Minimum range of effects + var/random_min = 3 + + ///Maximum range of effects + var/random_max = 5 + + ///How much the % chance drops the further it goes outwards. Avoids having a giant square. + var/percent_drop = 15 + + ///What effect we spawn + var/effect_to_spawn + + ///If we qdel after we place our stuff or not. + var/delete_after_placement = TRUE + + start_active = FALSE + + +/obj/effect/abstract/abberation/spawner/Initialize(mapload) + if(!effect_to_spawn) + return INITIALIZE_HINT_QDEL + . = ..() + if(random_range) + range = rand(random_min, random_max) + place_effects() + if(delete_after_placement) + return INITIALIZE_HINT_QDEL + +/obj/effect/abstract/abberation/spawner/proc/place_effects() + for(var/turf/simulated/turf in range(src, range)) + var/how_far = get_dist(src, turf) + if(prob(100 - ((how_far - 1) * percent_drop))) //100% at 1 range, 85 at 2 range, 70 at 3, 55 at 4, 40 at 5 + var/obj/thing_to_find = locate(effect_to_spawn) in turf + if(thing_to_find) + continue + new effect_to_spawn(turf) diff --git a/code/game/objects/traps/aberrations/spawners/fire.dm b/code/game/objects/traps/aberrations/spawners/fire.dm new file mode 100644 index 0000000000..6b238c0a7e --- /dev/null +++ b/code/game/objects/traps/aberrations/spawners/fire.dm @@ -0,0 +1,34 @@ +//Fire abberation +/obj/effect/abstract/abberation/spawner/fire + name = "Fire Abberation" + effect_to_spawn = /obj/effect/abberation_fire + +//The things that actually light you on fire. +/obj/effect/abberation_fire + icon = 'icons/obj/traps/abberations.dmi' + icon_state = "abb_fire" + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + COOLDOWN_DECLARE(fire_timer) + +/obj/effect/abberation_fire/Crossed(atom/movable/AM) + if(AM.is_incorporeal() || isobserver(AM) || istype(AM, /obj/effect/abstract)) + return + if(isobj(AM)) + var/obj/item/item_check = AM + if(item_check.item_flags & ABSTRACT) + return + if(COOLDOWN_FINISHED(src, fire_timer)) + ignite_entity(AM) + +/obj/effect/abberation_fire/proc/reset_trap() + for(var/mob/living/living_being in get_turf(src)) + if(living_being.is_incorporeal()) + continue + ignite_entity(living_being) //Begin anew + +/obj/effect/abberation_fire/proc/ignite_entity(atom/movable/AM) + AM.fire_act(null, 10000, 1000) + alpha = 125 + animate(src, alpha = 255, time = 2 SECONDS) + addtimer(CALLBACK(src, PROC_REF(reset_trap)), 2 SECONDS, TIMER_DELETE_ME|TIMER_UNIQUE) //Calling a proc with no arguments + COOLDOWN_START(src, fire_timer, 2 SECONDS) diff --git a/code/game/objects/traps/aberrations/spawners/glass.dm b/code/game/objects/traps/aberrations/spawners/glass.dm new file mode 100644 index 0000000000..1cfc688c4e --- /dev/null +++ b/code/game/objects/traps/aberrations/spawners/glass.dm @@ -0,0 +1,10 @@ +//Fire abberation +/obj/effect/abstract/abberation/spawner/glass + name = "Glass Abberation" + effect_to_spawn = /obj/item/material/barbedwire/glass/start_active + start_active = TRUE + delete_after_placement = FALSE + +/obj/effect/abstract/abberation/spawner/glass/perform_pulse() + place_effects() + return diff --git a/code/game/objects/traps/glass_wire.dm b/code/game/objects/traps/glass_wire.dm new file mode 100644 index 0000000000..2a9a786e32 --- /dev/null +++ b/code/game/objects/traps/glass_wire.dm @@ -0,0 +1,106 @@ +/obj/item/material/barbedwire/glass + name = "glass wire" + desc = "Thin webs of strewn glass, very easy to break." + icon = 'icons/obj/traps/abberations.dmi' + icon_state = "abb_glass" + default_material = MAT_GLASS + applies_material_colour = FALSE + named_from_material = FALSE + drops_debris = FALSE + +/obj/item/material/barbedwire/glass/start_active + anchored = TRUE + +/obj/item/material/barbedwire/glass/start_active/Initialize(mapload, material_key) + . = ..() + update_icon() + +/obj/item/material/barbedwire/glass/attack_hand(mob/user) + playsound(src, 'sound/effects/glass_step.ogg', 50, 1) + user.visible_message( + span_danger("[user] touches \the [src] and it shatters."), + span_notice("You touch \the [src] and it shatters!") + ) + //also cut hands if gloves are not thick, TODO. + health = 0 //Instantly shatters. + check_health() + return + +/obj/item/material/barbedwire/glass/attackby(obj/item/W, mob/user) + if(!istype(W)) + return + + user.setClickCooldown(user.get_attack_speed(W)) + user.do_attack_animation(src) + playsound(src, 'sound/effects/grillehit.ogg', 40, 1) + health = 0 //Instantly shatters. + check_health() + +/obj/item/material/barbedwire/glass/update_icon() + ..() + icon_state = "abb_glass" + +/obj/item/material/barbedwire/glass/Crossed(atom/movable/AM) + if(AM.is_incorporeal()) + return + if(anchored) + if(isliving(AM)) + var/mob/living/L = AM + L.visible_message( + span_danger("[L] steps in \the [src]."), + span_danger("You step in \the [src]!"), + span_infoplain(span_bold("You hear a sharp rustling!")) + ) + attack_mob(L) + update_icon() + else + health = 0 + check_health() + +/obj/item/material/barbedwire/glass/attack_mob(mob/living/L) + L.add_modifier(/datum/modifier/entangled, 3 SECONDS) + + var/target_zone = ran_zone() + var/obj/item/organ/external/affecting = L.get_organ(target_zone) + + //armour + var/blocked = L.run_armor_check(target_zone, "melee") + if(!affecting) //touched a limb that doesn't exist + break_wire() + to_chat(L, span_danger("You narrowly avoid being cut by \the [src]!")) + return + + //Check the list of clothing for thick materials. + var/list/our_clothing = affecting.get_covering_clothing() + for(var/obj/item/clothing/gear in our_clothing) + if(gear.item_flags & THICKMATERIAL) + break_wire() + to_chat(L, span_danger("Your [gear] snags on \the [src], shattering it and protecting you!")) + return + + if(blocked >= 100) + break_wire() + to_chat(L, span_danger("Your equipment protects you from \the [src]!")) + return + + if(!L.apply_damage(force * (issilicon(L) ? 0.25 : 1), BRUTE, target_zone, blocked, sharp, edge, src)) + break_wire() + to_chat(L, span_danger("You narrowly avoid being cut by \the [src]!")) + return + + to_chat(L, span_danger("You're cut by \the [src]!")) + + if(ishuman(L)) + var/mob/living/carbon/human/human_target = L + if(affecting && (affecting.robotic < ORGAN_ROBOT) && (affecting.take_damage(brute = force, sharp = TRUE, edge = TRUE, used_weapon = "Glass strands"))) + affecting.open = TRUE + human_target.UpdateDamageIcon() + human_target.updatehealth() + + break_wire() + return + +/obj/item/material/barbedwire/glass/proc/break_wire() + playsound(src, 'sound/effects/glass_step.ogg', 50, 1) + health = 0 + check_health() diff --git a/code/modules/materials/materials/alien_alloy.dm b/code/modules/materials/materials/alien_alloy.dm index f56ee5841c..77e04d444b 100644 --- a/code/modules/materials/materials/alien_alloy.dm +++ b/code/modules/materials/materials/alien_alloy.dm @@ -13,6 +13,7 @@ protectiveness = 80 // 80% wiki_flag = WIKI_SPOILER supply_conversion_value = 5 // Neat if you somehow manage to sell this + shard_type = null /datum/material/alienalloy/elevatorium name = MAT_ALIEN_ELEVAT diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 8bb396b61d..abb4c28963 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -349,6 +349,7 @@ span_notice("You cauterize the incision on [target]'s [affected.name] with \the [tool].")) user.balloon_alert_visible("cauterizes the incision on [target]'s [affected.name]", "incison cauterized on \the [affected.name]") affected.open = 0 + affected.organ_clamp() //Technically cauterizing them, but this does the same on the backend. affected.germ_level = 0 affected.status &= ~ORGAN_BLEEDING diff --git a/icons/obj/traps/abberations.dmi b/icons/obj/traps/abberations.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6316b9f597b6281d5981f493fab8314a7244b37c GIT binary patch literal 6588 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