diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 78f6aa827f034..ff56ca0501d8e 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -140,10 +140,13 @@ #define STATUS_EFFECT_C_FOAMED /datum/status_effect/c_foamed +#define STATUS_EFFECT_RUST_CORRUPTION /datum/status_effect/rust_corruption + #define STATUS_EFFECT_TEMPORAL_SLASH /datum/status_effect/temporal_slash #define STATUS_EFFECT_TEMPORAL_SLASH_FINISHER /datum/status_effect/temporal_slash_finisher + //#define STATUS_EFFECT_NECROPOLIS_CURSE /datum/status_effect/necropolis_curse //#define CURSE_BLINDING 1 //makes the edges of the target's screen obscured //#define CURSE_SPAWNING 2 //spawns creatures that attack the target only diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index 2b3496855f570..6fa0ea9d42d9c 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -328,6 +328,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// A trait for determining if a atom/movable is currently crossing into another z-level by using of /turf/space z-level "destination-xyz" transfers #define TRAIT_CURRENTLY_Z_MOVING "currently_z_moving" // please dont adminbus this +//****** TURF TRAITS *****// +#define TRAIT_RUSTY "rust_trait" + // // common trait sources #define TRAIT_GENERIC "generic" diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 7ce28f4c915f6..925dd2d981ca1 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -144,7 +144,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /turf = list( - "bluespace_speed_trait" = TRAIT_BLUESPACE_SPEED + "bluespace_speed_trait" = TRAIT_BLUESPACE_SPEED, + "TRAIT_RUSTY" = TRAIT_RUSTY ), /obj/effect = list( diff --git a/code/datums/elements/rust_element.dm b/code/datums/elements/rust_element.dm new file mode 100644 index 0000000000000..d2464e016df77 --- /dev/null +++ b/code/datums/elements/rust_element.dm @@ -0,0 +1,123 @@ +/** + * Adding this element to an atom will have it automatically render an overlay. + */ +/datum/element/rust + element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY // Detach for turfs + argument_hash_start_idx = 2 + /// The rust image itself, since the icon and icon state are only used as an argument + var/image/rust_overlay + +/datum/element/rust/Attach(atom/target, rust_icon = 'icons/effects/rust_overlay.dmi', rust_icon_state = "rust_default") + . = ..() + if(!isatom(target)) + return ELEMENT_INCOMPATIBLE + + rust_overlay = image(rust_icon, "rust[rand(1, 6)]") + ADD_TRAIT(target, TRAIT_RUSTY, "rusted_turf") + RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(apply_rust_overlay)) + RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(handle_examine)) + RegisterSignal(target, COMSIG_INTERACT_TARGET, PROC_REF(on_interaction)) + RegisterSignal(target, COMSIG_TOOL_ATTACK, PROC_REF(welder_tool_act)) + // Unfortunately registering with parent sometimes doesn't cause an overlay update + target.update_appearance() + +/datum/element/rust/Detach(atom/source) + . = ..() + UnregisterSignal(source, COMSIG_ATOM_UPDATE_OVERLAYS) + UnregisterSignal(source, COMSIG_PARENT_EXAMINE) + UnregisterSignal(source, COMSIG_TOOL_ATTACK) + UnregisterSignal(source, COMSIG_INTERACT_TARGET) + REMOVE_TRAIT(source, TRAIT_RUSTY, "rusted_turf") + source.cut_overlays() + source.update_appearance() + +/datum/element/rust/proc/handle_examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER //COMSIG_PARENT_EXAMINE + + examine_list += "[source] is very rusty, you could probably burn it off." + +/datum/element/rust/proc/apply_rust_overlay(atom/parent_atom, list/overlays) + SIGNAL_HANDLER //COMSIG_ATOM_UPDATE_OVERLAYS + + if(rust_overlay) + parent_atom.add_overlay(rust_overlay) + +/// Because do_after sleeps we register the signal here and defer via an async call +/datum/element/rust/proc/welder_tool_act(atom/source, obj/item/item, mob/user) + SIGNAL_HANDLER // COMSIG_TOOL_ATTACK + + INVOKE_ASYNC(src, PROC_REF(handle_tool_use), source, item, user) + return COMPONENT_CANCEL_TOOLACT + +/// We call this from secondary_tool_act because we sleep with do_after +/datum/element/rust/proc/handle_tool_use(atom/source, obj/item/item, mob/user) + switch(item.tool_behaviour) + if(TOOL_WELDER) + if(!item.tool_start_check(source, user, amount=1)) + return + to_chat(user, "You start burning off the rust...") + + if(!item.use_tool(source, user, 5 SECONDS, volume = item.tool_volume)) + return + to_chat(user, "You burn off the rust!") + Detach(source) + return + +/// Prevents placing floor tiles on rusted turf +/datum/element/rust/proc/on_interaction(datum/source, mob/living/user, obj/item/tool, list/modifiers) + SIGNAL_HANDLER // COMSIG_INTERACT_TARGET + if(istype(tool, /obj/item/stack/tile) || istype(tool, /obj/item/stack/rods) || istype(tool, /obj/item/rcd)) + to_chat(user, "[source] is too rusted to build on!") + return ITEM_INTERACT_COMPLETE + +/// For rust applied by heretics (if that ever happens) / revenants +/datum/element/rust/heretic + +/datum/element/rust/heretic/Attach(atom/target, rust_icon, rust_icon_state) + . = ..() + if(. == ELEMENT_INCOMPATIBLE) + return . + RegisterSignal(target, COMSIG_ATOM_ENTERED, PROC_REF(on_entered)) + RegisterSignal(target, COMSIG_ATOM_EXITED, PROC_REF(on_exited)) + +/datum/element/rust/heretic/Detach(atom/source) + . = ..() + UnregisterSignal(source, COMSIG_ATOM_ENTERED) + UnregisterSignal(source, COMSIG_ATOM_EXITED) + for(var/obj/effect/glowing_rune/rune_to_remove in source) + qdel(rune_to_remove) + for(var/mob/living/victim in source) + victim.remove_status_effect(STATUS_EFFECT_RUST_CORRUPTION) + +/datum/element/rust/heretic/proc/on_entered(turf/source, atom/movable/entered, ...) + SIGNAL_HANDLER + + if(!isliving(entered)) + return + var/mob/living/victim = entered + if(istype(victim, /mob/living/simple_animal/revenant)) + return + victim.apply_status_effect(STATUS_EFFECT_RUST_CORRUPTION) + +/datum/element/rust/heretic/proc/on_exited(turf/source, atom/movable/gone) + SIGNAL_HANDLER + if(!isliving(gone)) + return + var/mob/living/leaver = gone + leaver.remove_status_effect(STATUS_EFFECT_RUST_CORRUPTION) + +// Small visual effect imparted onto rusted things by revenants. +/obj/effect/glowing_rune + icon = 'icons/effects/eldritch.dmi' + icon_state = "small_rune_1" + anchored = TRUE + plane = FLOOR_PLANE + layer = SIGIL_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/effect/glowing_rune/Initialize(mapload) + . = ..() + pixel_y = rand(-6, 6) + pixel_x = rand(-6, 6) + icon_state = "small_rune_[rand(1, 12)]" + update_appearance() diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 5e3b3357b5ee1..2c5acaf69cc07 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -1431,6 +1431,23 @@ alert_type = null status_type = STATUS_EFFECT_REPLACE +/datum/status_effect/rust_corruption + alert_type = null + id = "rust_turf_effects" + tick_interval = 2 SECONDS + +/datum/status_effect/rust_corruption/tick() + . = ..() + if(issilicon(owner)) + owner.adjustBruteLoss(10) + return + //We don't have disgust, so... + if(ishuman(owner)) + owner.adjustBrainLoss(2.5) + owner.reagents?.remove_all(0.75) + else + owner.adjustBruteLoss(3) //Weaker than borgs but still constant. + /// This is the threshold where the attack will stun on the last hit. Why? Because it is cool, that's why. #define FINISHER_THRESHOLD 7 diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm index ad6da8561d80c..4fae962fa5136 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm @@ -495,18 +495,18 @@ /turf/simulated/wall/defile() ..() - if(prob(15) && !rusted) + if(prob(15)) new/obj/effect/temp_visual/revenant(loc) - rust() + magic_rust_turf() /turf/simulated/wall/indestructible/defile() return /turf/simulated/wall/r_wall/defile() ..() - if(prob(15) && !rusted) + if(prob(15)) new/obj/effect/temp_visual/revenant(loc) - rust() + magic_rust_turf() /mob/living/carbon/human/defile() to_chat(src, "You suddenly feel [pick("sick and tired", "tired and confused", "nauseated", "dizzy")].") @@ -531,8 +531,10 @@ broken = FALSE burnt = FALSE make_plating(1) + magic_rust_turf() /turf/simulated/floor/plating/defile() + magic_rust_turf() if(flags & BLESSED_TILE) flags &= ~BLESSED_TILE new /obj/effect/temp_visual/revenant(loc) diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 1fdab8f14835a..9e5a4241d59ed 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -307,3 +307,21 @@ if(prob(50)) break_tile_to_plating() hotspot_expose(1000,CELL_VOLUME) + +/turf/open/floor/plating/rust + //SDMM supports colors, this is simply for easier mapping + //and should be removed on initialize + color = COLOR_BROWN + +/turf/simulated/floor/plating/rust/Initialize(mapload) + . = ..() + AddElement(/datum/element/rust) + color = null + +/turf/open/floor/plating/heretic_rust + color = COLOR_GREEN_GRAY + +/turf/simulated/floor/plating/heretic_rust/Initialize(mapload) + . = ..() + AddElement(/datum/element/rust/heretic) + color = null diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index 3ddc3948d4660..1f75cad5ef91e 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -129,7 +129,7 @@ /turf/simulated/floor/plating/welder_act(mob/user, obj/item/I) if(!broken && !burnt && !unfastened) return - . = TRUE + . = ..() if(!I.tool_use_check(user, 0)) return if(user.a_intent == INTENT_HARM) // no repairing on harm intent, so you can use the welder in a fight near damaged paneling without welding your eyes out diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index f7050f5af9340..422cf388c2dff 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -40,10 +40,6 @@ var/sheet_type = /obj/item/stack/sheet/metal var/sheet_amount = 2 var/girder_type = /obj/structure/girder - /// Are we a rusty wall or not? - var/rusted = FALSE - /// Have we got a rusty overlay? - var/rusted_overlay /// Are we a explodable turf? var/explodable = FALSE /// Do we have a explodable overlay? @@ -89,26 +85,12 @@ if(can_dismantle_with_welder) . += "Using a lit welding tool on this item will allow you to slice through it, eventually removing the outer layer." -/// Apply rust effects to the wall -/turf/simulated/wall/proc/rust() - if(rusted) - return - rusted = TRUE - update_appearance(UPDATE_NAME|UPDATE_OVERLAYS) - -/turf/simulated/wall/update_name() - . = ..() - name = "[rusted ? "rusted " : ""][name]" - /turf/simulated/wall/update_overlays() . = ..() if(!damage_overlays[1]) //list hasn't been populated generate_overlays() QUEUE_SMOOTH(src) - if(rusted && !rusted_overlay) - rusted_overlay = icon('icons/turf/overlays.dmi', pick("rust", "rust2"), pick(NORTH, SOUTH, EAST, WEST)) - . += rusted_overlay if(explodable && !explodable_overlay) explodable_overlay = icon('icons/turf/overlays.dmi', pick("explodable"), pick(NORTH, SOUTH, EAST, WEST)) @@ -368,7 +350,7 @@ return CONTINUE_ATTACK /turf/simulated/wall/welder_act(mob/user, obj/item/I) - . = TRUE + . = ..() if(reagents?.get_reagent_amount("thermite") && I.use_tool(src, user, volume = I.tool_volume)) thermitemelt(user) return diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index c8d6fe4feb96f..09341eaea3543 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -487,7 +487,6 @@ /turf/attack_by(obj/item/attacking, mob/user, params) if(..()) return TRUE - if(can_lay_cable()) if(istype(attacking, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = attacking @@ -606,6 +605,19 @@ C.take_organ_damage(damage) C.KnockDown(3 SECONDS) +/turf/proc/rust_turf() + if(HAS_TRAIT(src, TRAIT_RUSTY)) + return + + AddElement(/datum/element/rust) + +/turf/proc/magic_rust_turf() + if(HAS_TRAIT(src, TRAIT_RUSTY)) + return + + AddElement(/datum/element/rust/heretic) + new /obj/effect/glowing_rune(src) + /// Returns a list of all attached /datum/element/decal/ for this turf /turf/proc/get_decals() var/list/datum/element/decals = list() diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 7f1e35bb744f4..ac92796cc50fa 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -222,7 +222,8 @@ T.burn_tile() /obj/effect/mapping_helpers/turfs/rust - icon_state = "rustwall" + icon = 'icons/effects/rust_overlay.dmi' + icon_state = "rust1" var/spawn_probability = 100 /obj/effect/mapping_helpers/turfs/rust/payload(turf/simulated/wall/T) @@ -230,7 +231,12 @@ return if(prob(spawn_probability)) - T.rust() + rustify(T) + +/obj/effect/mapping_helpers/turfs/proc/rustify(turf/T) + var/turf/simulated/wall/W = T + if(istype(W) && !HAS_TRAIT(W, TRAIT_RUSTY)) + W.rust_turf() /obj/effect/mapping_helpers/turfs/rust/probably spawn_probability = 75 diff --git a/icons/effects/eldritch.dmi b/icons/effects/eldritch.dmi new file mode 100644 index 0000000000000..6481aa7591bd8 Binary files /dev/null and b/icons/effects/eldritch.dmi differ diff --git a/icons/effects/rust_overlay.dmi b/icons/effects/rust_overlay.dmi new file mode 100644 index 0000000000000..f4d6cc33c4179 Binary files /dev/null and b/icons/effects/rust_overlay.dmi differ diff --git a/icons/turf/overlays.dmi b/icons/turf/overlays.dmi index 4b9aa80e14eb2..79fddb79b5120 100644 Binary files a/icons/turf/overlays.dmi and b/icons/turf/overlays.dmi differ diff --git a/paradise.dme b/paradise.dme index 2959029f31e4a..90e5468b42641 100644 --- a/paradise.dme +++ b/paradise.dme @@ -543,6 +543,7 @@ #include "code\datums\elements\high_value_item.dm" #include "code\datums\elements\rad_insulation.dm" #include "code\datums\elements\ridable.dm" +#include "code\datums\elements\rust_element.dm" #include "code\datums\elements\shatters_when_thrown.dm" #include "code\datums\elements\strippable.dm" #include "code\datums\elements\waddling.dm"