diff --git a/code/modules/antagonists/changeling/powers/_biodegrade_bioacid.dm b/code/modules/antagonists/changeling/powers/_biodegrade_bioacid.dm new file mode 100644 index 00000000000..7278a16c89a --- /dev/null +++ b/code/modules/antagonists/changeling/powers/_biodegrade_bioacid.dm @@ -0,0 +1,25 @@ +/datum/reagent/toxin/acid/bio_acid + name = "adaptive bio-acid" + description = "An immensely strong, acidic substance of seemingly biological origin. It is teeming with microscopic\ + organisms that seem to alter its composition to most adaptively dissolve whatever it comes into contact with." + color = "#9455ff" + creation_purity = 100 + toxpwr = 0 + acidpwr = 0 + ph = 0.0 + penetrates_skin = TOUCH + +/datum/reagent/toxin/acid/bio_acid/expose_mob(mob/living/exposed_mob, methods = TOUCH, reac_volume, show_message = TRUE, touch_protection) + if(IS_CHANGELING(exposed_mob)) + to_chat(exposed_mob, span_changeling("We excrete a bio-agent to neutralize the bio-acid. It is routine and reflexive to do so.")) + volume = min(0.1, volume) + holder.update_total() + return + . = ..() + exposed_mob.adjust_fire_loss(round(reac_volume * min(1 - touch_protection), 0.1) * 3, required_bodytype = BODYTYPE_ORGANIC) // full bio protection = 100% damage reduction + exposed_mob.acid_act(10, 50) + +/datum/reagent/toxin/acid/bio_acid/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + if(SPT_PROB(50, seconds_per_tick)) + affected_mob.emote(pick("screech", "cry")) diff --git a/code/modules/antagonists/changeling/powers/biodegrade.dm b/code/modules/antagonists/changeling/powers/biodegrade.dm index 8a5fae3bd8a..4a49035d183 100644 --- a/code/modules/antagonists/changeling/powers/biodegrade.dm +++ b/code/modules/antagonists/changeling/powers/biodegrade.dm @@ -3,114 +3,106 @@ desc = "Dissolves restraints or other objects preventing free movement. Costs 30 chemicals." helptext = "This is obvious to nearby people, and can destroy standard restraints and closets." button_icon_state = "biodegrade" - chemical_cost = 30 //High cost to prevent spam + chemical_cost = 30 dna_cost = 2 req_human = TRUE disabled_by_fire = FALSE + var/static/bio_acid_path = /datum/reagent/toxin/acid/bio_acid + var/static/bio_acid_amount_per_spray = 6 + var/static/bio_acid_color = "#9455ff" /datum/action/changeling/biodegrade/sting_action(mob/living/carbon/human/user) - if(user.handcuffed) - var/obj/O = user.get_item_by_slot(ITEM_SLOT_HANDCUFFED) - if(!istype(O)) - return FALSE - user.visible_message(span_warning("[user] vomits a glob of acid on [user.p_their()] [O]!"), \ - span_warning("We vomit acidic ooze onto our restraints!")) + . = FALSE + var/list/obj/restraints = list() + var/obj/handcuffs = user.get_item_by_slot(ITEM_SLOT_HANDCUFFED) + var/obj/legcuffs = user.get_item_by_slot(ITEM_SLOT_LEGCUFFED) + var/obj/item/clothing/suit/straitjacket = user.get_item_by_slot(ITEM_SLOT_OCLOTHING) + var/obj/item/clothing/shoes/sneakers/orange/prisoner_shoes = user.get_item_by_slot(ITEM_SLOT_FEET) + var/obj/item/clothing/shoes/knotted_shoes = user.get_item_by_slot(ITEM_SLOT_FEET) + var/obj/some_manner_of_cage = astype(user.loc, /obj) + var/mob/living/space_invader = user.pulledby - addtimer(CALLBACK(src, PROC_REF(dissolve_handcuffs), user, O), 3 SECONDS) - log_combat(user, user.handcuffed, "melted handcuffs", addition = "(biodegrade)") - ..() - return TRUE + if(!istype(prisoner_shoes, /obj/item/clothing/shoes/sneakers/orange) || !prisoner_shoes?.attached_cuffs) + prisoner_shoes = null + if(!istype(knotted_shoes) || istype(knotted_shoes) && !knotted_shoes.tied == SHOES_KNOTTED) + knotted_shoes = null + if(!straitjacket?.breakouttime) + straitjacket = null - if(user.legcuffed) - var/obj/O = user.get_item_by_slot(ITEM_SLOT_LEGCUFFED) - if(!istype(O)) - return FALSE - user.visible_message(span_warning("[user] vomits a glob of acid on [user.p_their()] [O]!"), \ - span_warning("We vomit acidic ooze onto our restraints!")) + if(!handcuffs && !legcuffs && !straitjacket && !prisoner_shoes && !knotted_shoes && !some_manner_of_cage && !space_invader) + user.balloon_alert(user, "already free!") + return . + ..() - addtimer(CALLBACK(src, PROC_REF(dissolve_legcuffs), user, O), 3 SECONDS) - log_combat(user, user.legcuffed, "melted legcuffs", addition = "(biodegrade)") - ..() - return TRUE + if(handcuffs) + restraints.Add(handcuffs) + if(legcuffs) + restraints.Add(legcuffs) + if(straitjacket) + restraints.Add(straitjacket) + if(prisoner_shoes) + restraints.Add(prisoner_shoes) + if(knotted_shoes) + restraints.Add(knotted_shoes) + if(some_manner_of_cage) + restraints.Add(some_manner_of_cage) - if(user.wear_suit?.breakouttime) - var/obj/item/clothing/suit/S = user.get_item_by_slot(ITEM_SLOT_OCLOTHING) - if(!istype(S)) - return FALSE - user.visible_message(span_warning("[user] vomits a glob of acid across the front of [user.p_their()] [S]!"), \ - span_warning("We vomit acidic ooze onto our [user.wear_suit.name]!")) - addtimer(CALLBACK(src, PROC_REF(dissolve_straightjacket), user, S), 3 SECONDS) - log_combat(user, user.wear_suit, "melted [user.wear_suit]", addition = "(biodegrade)") - ..() - return TRUE + for(var/obj/restraint as anything in restraints) + if(restraint.obj_flags & (INDESTRUCTIBLE | ACID_PROOF | UNACIDABLE)) + to_chat(user, span_changeling("We cannot use bio-acid to destroy [restraint]!")) + continue - if(istype(user.loc, /obj/structure/closet)) - var/obj/structure/closet/C = user.loc - if(!istype(C)) - return FALSE - C.visible_message(span_warning("[C]'s hinges suddenly begin to melt and run!")) - to_chat(user, span_warning("We vomit acidic goop onto the interior of [C]!")) - addtimer(CALLBACK(src, PROC_REF(open_closet), user, C), 7 SECONDS) - log_combat(user, user.loc, "melted locker", addition = "(biodegrade)") - ..() - return TRUE - - if(istype(user.loc, /obj/structure/spider/cocoon)) - var/obj/structure/spider/cocoon/C = user.loc - if(!istype(C)) - return FALSE - C.visible_message(span_warning("[src] shifts and starts to fall apart!")) - to_chat(user, span_warning("We secrete acidic enzymes from our skin and begin melting our cocoon...")) - addtimer(CALLBACK(src, PROC_REF(dissolve_cocoon), user, C), 25) //Very short because it's just webs - log_combat(user, user.loc, "melted cocoon", addition = "(biodegrade)") - ..() - return TRUE - - var/obj/item/clothing/shoes/shoes = user.shoes - if(istype(shoes) && shoes.tied == SHOES_KNOTTED && !(shoes.resistance_flags & (INDESTRUCTIBLE|UNACIDABLE|ACID_PROOF))) - new /obj/effect/decal/cleanable/greenglow(shoes.drop_location()) + if(restraint == user.loc) + restraint.visible_message(span_warning("Bubbling acid start spewing out of [restraint]...")) + addtimer(CALLBACK(restraint, TYPE_PROC_REF(/atom, atom_destruction), ACID), 4 SECONDS) + for(var/beat in 1 to 3) + addtimer(CALLBACK(src, PROC_REF(make_puddle), restraint), beat SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), restraint, 'sound/items/tools/welder.ogg', 50, TRUE), beat SECONDS) + log_combat(user = user, target = restraint, what_done = "melted restraining container", addition = "(biodegrade)") + return + addtimer(CALLBACK(restraint, TYPE_PROC_REF(/atom, atom_destruction), ACID), 1.5 SECONDS) + log_combat(user = user, target = restraint, what_done = "melted restraining item", addition = "(biodegrade)") user.visible_message( - span_warning("[user] vomits a glob of acid on [user.p_their()] tied up [shoes.name], melting [shoes.p_them()] into a pool of goo!"), - span_warning("We vomit acidic ooze onto our tied up [shoes.name], melting [shoes.p_them()] into a pool of goo!"), - ) - log_combat(user, shoes, "melted own shoes", addition = "(biodegrade)") - qdel(shoes) - ..() - return TRUE + span_warning("[user] spews torrents of acid onto [restraint], melting them with horrifying ease."), + user.balloon_alert(user, "melting restraints..."), + span_danger("You hear retching, then the sizzling of powerful acid, closer to the sound of hissing steam.")) + playsound(user, 'sound/items/tools/welder.ogg', 50, TRUE) + . = TRUE - user.balloon_alert(user, "already free!") - return FALSE + if(space_invader) + punish_with_acid(user, space_invader) + . = TRUE + return . -/datum/action/changeling/biodegrade/proc/dissolve_handcuffs(mob/living/carbon/human/user, obj/O) - if(O && user.handcuffed == O) - user.visible_message(span_warning("[O] dissolve[O.gender == PLURAL?"":"s"] into a puddle of sizzling goop.")) - new /obj/effect/decal/cleanable/greenglow(O.drop_location()) - qdel(O) +/// Spawn green acid puddle underneath obj, used for callback +/datum/action/changeling/biodegrade/proc/make_puddle(obj/melted_restraint) + if (melted_restraint) // just incase obj gets qdel'd + return new /obj/effect/decal/cleanable/greenglow(get_turf(melted_restraint)) -/datum/action/changeling/biodegrade/proc/dissolve_legcuffs(mob/living/carbon/human/user, obj/O) - if(O && user.legcuffed == O) - user.visible_message(span_warning("[O] dissolve[O.gender == PLURAL?"":"s"] into a puddle of sizzling goop.")) - new /obj/effect/decal/cleanable/greenglow(O.drop_location()) - qdel(O) +/datum/action/changeling/biodegrade/proc/acid_blast(atom/movable/user, atom/movable/target) + var/datum/reagents/ephemeral_acid = new + ephemeral_acid.add_reagent(bio_acid_path, bio_acid_amount_per_spray) + var/mutable_appearance/splash_animation = mutable_appearance('icons/effects/effects.dmi', "splash") + splash_animation.color = bio_acid_color + target.flick_overlay_view(splash_animation, 3 SECONDS) + ephemeral_acid.expose(target, TOUCH) -/datum/action/changeling/biodegrade/proc/dissolve_straightjacket(mob/living/carbon/human/user, obj/S) - if(S && user.wear_suit == S) - user.visible_message(span_warning("[S] dissolves into a puddle of sizzling goop.")) - new /obj/effect/decal/cleanable/greenglow(S.drop_location()) - qdel(S) - -/datum/action/changeling/biodegrade/proc/open_closet(mob/living/carbon/human/user, obj/structure/closet/C) - if(C && user.loc == C) - C.visible_message(span_warning("[C]'s door breaks and opens!")) - new /obj/effect/decal/cleanable/greenglow(C.drop_location()) - C.welded = FALSE - C.locked = FALSE - C.broken = TRUE - C.open() - to_chat(user, span_warning("We open the container restraining us!")) - -/datum/action/changeling/biodegrade/proc/dissolve_cocoon(mob/living/carbon/human/user, obj/structure/spider/cocoon/C) - if(C && user.loc == C) - new /obj/effect/decal/cleanable/greenglow(C.drop_location()) - qdel(C) //The cocoon's destroy will move the changeling outside of it without interference - to_chat(user, span_warning("We dissolve the cocoon!")) +/datum/action/changeling/biodegrade/proc/punish_with_acid(mob/living/carbon/human/user, mob/living/hapless_manhandler) + acid_blast(user, hapless_manhandler) + playsound(user, 'sound/mobs/non-humanoids/bileworm/bileworm_spit.ogg', 50, TRUE) + if(IS_CHANGELING(hapless_manhandler)) + user.visible_message( + span_danger("[user] spews a mist of sizzling acid onto [hapless_manhandler]... but nothing happens!"), + span_changeling("We prepare our escape, spraying bio-acid on our captor... [span_danger("But nothing happened?!")]"), + span_danger("You hear retching, then a sizzling that terminates quite abruptly.") + ) + to_chat(hapless_manhandler, span_changeling("Our prey attempts to dissuade us with one of our biology's simplest adaptions. Quaint.")) + return + user.visible_message( + span_danger("[user] spews a mist of sizzling acid onto [hapless_manhandler], using the opportunity to wrestle away."), + user.balloon_alert(user, "dissuading captor..."), + span_danger("You hear retching, then sizzling, quickly muffled by a loud keening of pain.")) + hapless_manhandler.Stun(2 SECONDS) + hapless_manhandler.emote("scream") + hapless_manhandler.stop_pulling() + log_combat(user = user, target = hapless_manhandler, what_done = "acid-spewed to escape a grab", addition = "(biodegrade)") diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 20790b40ea3..ed28dcb015c 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -439,7 +439,7 @@ ///Get the item on the mob in the storage slot identified by the id passed in -/mob/proc/get_item_by_slot(slot_id) +/mob/proc/get_item_by_slot(slot_id) as /obj/item return null /// Gets what slot the item on the mob is held in. diff --git a/tgstation.dme b/tgstation.dme index acbea823808..36b93c48fe8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3301,6 +3301,7 @@ #include "code\modules\antagonists\changeling\changeling_power.dm" #include "code\modules\antagonists\changeling\fallen_changeling.dm" #include "code\modules\antagonists\changeling\headslug_eggs.dm" +#include "code\modules\antagonists\changeling\powers\_biodegrade_bioacid.dm" #include "code\modules\antagonists\changeling\powers\absorb.dm" #include "code\modules\antagonists\changeling\powers\adrenaline.dm" #include "code\modules\antagonists\changeling\powers\augmented_eyesight.dm"