diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index e83f7c4c2c..6604811d34 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -12,6 +12,7 @@ #define MUTATE /datum/mutation/human/bad_dna #define COUGH /datum/mutation/human/cough #define DWARFISM /datum/mutation/human/dwarfism +#define GIGANTISM /datum/mutation/human/gigantism #define CLOWNMUT /datum/mutation/human/clumsy #define TOURETTES /datum/mutation/human/tourettes #define DEAFMUT /datum/mutation/human/deaf @@ -30,17 +31,28 @@ #define ELVIS /datum/mutation/human/elvis #define RADIOACTIVE /datum/mutation/human/radioactive #define GLOWY /datum/mutation/human/glow +#define ANTIGLOWY /datum/mutation/human/glow/anti #define TELEPATHY /datum/mutation/human/telepathy #define FIREBREATH /datum/mutation/human/firebreath #define VOID /datum/mutation/human/void +#define TONGUESPIKE /datum/mutation/human/tongue_spike +#define TONGUESPIKECHEM /datum/mutation/human/tongue_spike/chem #define STRONG /datum/mutation/human/strong +#define STIMMED /datum/mutation/human/stimmed #define FIRESWEAT /datum/mutation/human/fire #define THERMAL /datum/mutation/human/thermal -#define INSULATED /datum/mutation/human/insulated -#define SHOCKTOUCH /datum/mutation/human/shock #define ANTENNA /datum/mutation/human/antenna #define PARANOIA /datum/mutation/human/paranoia #define MINDREAD /datum/mutation/human/mindreader +#define INSULATED /datum/mutation/human/insulated +#define SHOCKTOUCH /datum/mutation/human/shock +#define OLFACTION /datum/mutation/human/olfaction +#define ACIDFLESH /datum/mutation/human/acidflesh +#define BADBLINK /datum/mutation/human/badblink +#define SPASTIC /datum/mutation/human/spastic +#define GELADIKINESIS /datum/mutation/human/geladikinesis +#define CRYOKINESIS /datum/mutation/human/cryokinesis +#define SPIDER_WEB /datum/mutation/human/webbing #define UI_CHANGED "ui changed" #define UE_CHANGED "ue changed" diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index 7083bb1102..65dc19ad5d 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -34,7 +34,7 @@ /datum/mutation/human/firebreath/modify() if(power) var/obj/effect/proc_holder/spell/aimed/firebreath/S = power - S.strength = GET_MUTATION_POWER(src) + S.strength = 4 + GET_MUTATION_POWER(src) /obj/effect/proc_holder/spell/aimed/firebreath name = "Fire Breath" @@ -43,13 +43,12 @@ charge_max = 1200 clothes_req = FALSE range = 20 - projectile_type = /obj/item/projectile/magic/aoe/fireball/firebreath base_icon_state = "fireball" action_icon_state = "fireball0" sound = 'sound/magic/demon_dies.ogg' //horrifying lizard noises active_msg = "You built up heat in your mouth." deactive_msg = "You swallow the flame." - var/strength = 1 + var/strength = 4 /obj/effect/proc_holder/spell/aimed/firebreath/before_cast(list/targets) . = ..() @@ -61,19 +60,46 @@ to_chat(C,"Something in front of your mouth caught fire!") return FALSE -/obj/effect/proc_holder/spell/aimed/firebreath/ready_projectile(obj/item/projectile/P, atom/target, mob/user, iteration) - if(!istype(P, /obj/item/projectile/magic/aoe/fireball)) - return - var/obj/item/projectile/magic/aoe/fireball/F = P - F.exp_fire += strength +/obj/effect/proc_holder/spell/aimed/firebreath/cast(list/targets, mob/living/user) + var/turf/T = user.loc + if(!isturf(T)) + return FALSE + firecone(T,user.dir,strength) + remove_ranged_ability() + charge_counter = 0 + start_recharge() + on_deactivation(user) - -/obj/item/projectile/magic/aoe/fireball/firebreath - name = "fire breath" - exp_heavy = 0 - exp_light = 0 - exp_flash = 0 - exp_fire= 4 +/proc/firecone(loc,dir,length) + var/addsides = FALSE + var/list/turf/recentturf = list(loc) + for (var/i = 0;i < length;i++) + var/list/turf/h = list() + for (var/turf/g in recentturf) + var/frontturf = get_step(g,dir) + if (addsides) + var/rightturf = get_step(frontturf,turn(dir,90)) + var/leftturf = get_step(frontturf,turn(dir,270)) + if (!(rightturf in h)) + h += rightturf + if (!(leftturf in h)) + h += leftturf + if (!(frontturf in h)) + h += frontturf + for (var/turf/j in h) + if (j.blocks_air) + h -= j + continue + for (var/obj/o in j) + if (o.CanAtmosPass == ATMOS_PASS_PROC ? !o.CanAtmosPass(loc) : !o.CanAtmosPass) + h -= j + continue + for (var/turf/l in h) + new /obj/effect/hotspot(l) + l.hotspot_expose(700,50,1) + sleep(1) + recentturf = h + addsides = !addsides /datum/mutation/human/void name = "Void Magnet" @@ -161,4 +187,318 @@ return ..() else to_chat(user,"The electricity doesn't seem to affect [target]...") - return ..() \ No newline at end of file + return ..() + + +/datum/mutation/human/olfaction + name = "Transcendent Olfaction" + desc = "Your sense of smell is comparable to that of a canine." + quality = POSITIVE + difficulty = 12 + text_gain_indication = "Smells begin to make more sense..." + text_lose_indication = "Your sense of smell goes back to normal." + power = /obj/effect/proc_holder/spell/targeted/olfaction + instability = 30 + synchronizer_coeff = 1 + var/reek = 200 + +/datum/mutation/human/olfaction/modify() + if(power) + var/obj/effect/proc_holder/spell/targeted/olfaction/S = power + S.sensitivity = GET_MUTATION_SYNCHRONIZER(src) + +/obj/effect/proc_holder/spell/targeted/olfaction + name = "Remember the Scent" + desc = "Get a scent off of the item you're currently holding to track it. With an empty hand, you'll track the scent you've remembered." + charge_max = 100 + clothes_req = FALSE + range = -1 + include_user = TRUE + action_icon_state = "nose" + var/mob/living/carbon/tracking_target + var/list/mob/living/carbon/possible = list() + var/sensitivity = 1 + +/obj/effect/proc_holder/spell/targeted/olfaction/cast(list/targets, mob/living/user = usr) + //can we sniff? is there miasma in the air? + var/datum/gas_mixture/air = user.loc.return_air() + var/list/cached_gases = air.gases + + if(cached_gases[/datum/gas/miasma]) + user.adjust_disgust(sensitivity * 45) + to_chat(user, "With your overly sensitive nose, you get a whiff of stench and feel sick! Try moving to a cleaner area!") + return + + var/atom/sniffed = user.get_active_held_item() + if(sniffed) + var/old_target = tracking_target + possible = list() + var/list/prints = sniffed.fingerprints + for(var/mob/living/carbon/C in GLOB.carbon_list) + if(prints[md5(C.dna.uni_identity)]) + possible |= C + if(!length(possible)) + to_chat(user,"Despite your best efforts, there are no scents to be found on [sniffed]...") + return + tracking_target = input(user, "Choose a scent to remember.", "Scent Tracking") as null|anything in sortNames(possible) + if(!tracking_target) + if(!old_target) + to_chat(user,"You decide against remembering any scents. Instead, you notice your own nose in your peripheral vision. This goes on to remind you of that one time you started breathing manually and couldn't stop. What an awful day that was.") + return + tracking_target = old_target + on_the_trail(user) + return + to_chat(user,"You pick up the scent of [tracking_target]. The hunt begins.") + on_the_trail(user) + return + + if(!tracking_target) + to_chat(user,"You're not holding anything to smell, and you haven't smelled anything you can track. You smell your skin instead; it's kinda salty.") + return + + on_the_trail(user) + +/obj/effect/proc_holder/spell/targeted/olfaction/proc/on_the_trail(mob/living/user) + if(!tracking_target) + to_chat(user,"You're not tracking a scent, but the game thought you were. Something's gone wrong! Report this as a bug.") + return + if(tracking_target == user) + to_chat(user,"You smell out the trail to yourself. Yep, it's you.") + return + if(usr.z < tracking_target.z) + to_chat(user,"The trail leads... way up above you? Huh. They must be really, really far away.") + return + else if(usr.z > tracking_target.z) + to_chat(user,"The trail leads... way down below you? Huh. They must be really, really far away.") + return + var/direction_text = "[dir2text(get_dir(usr, tracking_target))]" + if(direction_text) + to_chat(user,"You consider [tracking_target]'s scent. The trail leads [direction_text].") + + +/datum/mutation/human/self_amputation + name = "Autotomy" + desc = "Allows a creature to voluntary discard a random appendage." + quality = POSITIVE + text_gain_indication = "Your joints feel loose." + instability = 30 + power = /obj/effect/proc_holder/spell/self/self_amputation + + energy_coeff = 1 + synchronizer_coeff = 1 + +/obj/effect/proc_holder/spell/self/self_amputation + name = "Drop a limb" + desc = "Concentrate to make a random limb pop right off your body." + clothes_req = FALSE + human_req = FALSE + charge_max = 100 + action_icon_state = "autotomy" + +/obj/effect/proc_holder/spell/self/self_amputation/cast(list/targets, mob/user = usr) + if(!iscarbon(user)) + return + + var/mob/living/carbon/C = user + if(HAS_TRAIT(C, TRAIT_NODISMEMBER)) + return + + var/list/parts = list() + for(var/X in C.bodyparts) + var/obj/item/bodypart/BP = X + if(BP.body_part != HEAD && BP.body_part != CHEST) + if(BP.dismemberable) + parts += BP + if(!parts.len) + to_chat(usr, "You can't shed any more limbs!") + return + + var/obj/item/bodypart/BP = pick(parts) + BP.dismember() + +//spider webs +/datum/mutation/human/webbing + name = "Webbing Production" + desc = "Allows the user to lay webbing, and travel through it." + quality = POSITIVE + text_gain_indication = "Your skin feels webby." + instability = 15 + power = /obj/effect/proc_holder/spell/self/lay_genetic_web + +/obj/effect/proc_holder/spell/self/lay_genetic_web + name = "Lay Web" + desc = "Drops a web. Only you will be able to traverse your web easily, making it pretty good for keeping you safe." + clothes_req = FALSE + human_req = FALSE + charge_max = 4 SECONDS //the same time to lay a web + action_icon = 'icons/mob/actions/actions_genetic.dmi' + action_icon_state = "lay_web" + +/obj/effect/proc_holder/spell/self/lay_genetic_web/cast_check(skipcharge = 0,mob/user = usr) + . = ..() + if(!isturf(user.loc)) + to_chat(user, "You can't lay webs here!") + return FALSE + var/turf/T = get_turf(user) + var/obj/structure/spider/stickyweb/genetic/W = locate() in T + if(W) + to_chat(user, "There's already a web here!") + return FALSE + +/obj/effect/proc_holder/spell/self/lay_genetic_web/cast(list/targets, mob/user = usr) + var/turf/T = get_turf(user) + + user.visible_message("[user] begins to secrete a sticky substance.","You begin to lay a web.") + if(!do_after(user, 4 SECONDS, target = T)) + to_chat(user, "Your web spinning was interrupted!") + return + else + new /obj/structure/spider/stickyweb/genetic(T, user) + + +/datum/mutation/human/tongue_spike + name = "Tongue Spike" + desc = "Allows a creature to voluntary shoot their tongue out as a deadly weapon." + quality = POSITIVE + text_gain_indication = "Your feel like you can throw your voice." + instability = 15 + power = /obj/effect/proc_holder/spell/self/tongue_spike + + energy_coeff = 1 + synchronizer_coeff = 1 + +/obj/effect/proc_holder/spell/self/tongue_spike + name = "Launch spike" + desc = "Shoot your tongue out in the direction you're facing, embedding it and dealing damage until they remove it." + clothes_req = FALSE + human_req = TRUE + charge_max = 100 + action_icon = 'icons/mob/actions/actions_genetic.dmi' + action_icon_state = "spike" + var/spike_path = /obj/item/hardened_spike + +/obj/effect/proc_holder/spell/self/tongue_spike/cast(list/targets, mob/user = usr) + if(!iscarbon(user)) + return + + var/mob/living/carbon/C = user + if(HAS_TRAIT(C, TRAIT_NODISMEMBER)) + return + var/obj/item/organ/tongue/tongue + for(var/org in C.internal_organs) + if(istype(org, /obj/item/organ/tongue)) + tongue = org + break + + if(!tongue) + to_chat(C, "You don't have a tongue to shoot!") + return + + tongue.Remove(C, special = TRUE) + var/obj/item/hardened_spike/spike = new spike_path(get_turf(C), C) + tongue.forceMove(spike) + spike.throw_at(get_edge_target_turf(C,C.dir), 14, 4, C) + +/obj/item/hardened_spike + name = "biomass spike" + desc = "Hardened biomass, shaped into a spike. Very pointy!" + icon_state = "tonguespike" + force = 2 + throwforce = 15 //15 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = i didnt do the math + throw_speed = 4 + embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0, "embedded_ignore_throwspeed_threshold" = TRUE) + w_class = WEIGHT_CLASS_SMALL + sharpness = IS_SHARP + var/mob/living/carbon/human/fired_by + +/obj/item/hardened_spike/Initialize(mapload, firedby) + . = ..() + fired_by = firedby + addtimer(CALLBACK(src, .proc/checkembedded), 5 SECONDS) + +/obj/item/hardened_spike/proc/checkembedded() + if(ishuman(loc)) + var/mob/living/carbon/human/embedtest = loc + for(var/l in embedtest.bodyparts) + var/obj/item/bodypart/limb = l + if(src in limb.embedded_objects) + return limb + unembedded() + +/obj/item/hardened_spike/unembedded() + var/turf/T = get_turf(src) + visible_message("[src] cracks and twists, changing shape!") + for(var/i in contents) + var/obj/o = i + o.forceMove(T) + qdel(src) + +/datum/mutation/human/tongue_spike/chem + name = "Chem Spike" + desc = "Allows a creature to voluntary shoot their tongue out as biomass, allowing a long range transfer of chemicals." + quality = POSITIVE + text_gain_indication = "Your feel like you can really connect with people by throwing your voice." + instability = 15 + locked = TRUE + power = /obj/effect/proc_holder/spell/self/tongue_spike/chem + energy_coeff = 1 + synchronizer_coeff = 1 + +/obj/effect/proc_holder/spell/self/tongue_spike/chem + name = "Launch chem spike" + desc = "Shoot your tongue out in the direction you're facing, embedding it for a very small amount of damage. While the other person has the spike embedded, you can transfer your chemicals to them." + action_icon_state = "spikechem" + spike_path = /obj/item/hardened_spike/chem + +/obj/item/hardened_spike/chem + name = "chem spike" + desc = "Hardened biomass, shaped into... something." + icon_state = "tonguespikechem" + throwforce = 2 //2 + 2 (WEIGHT_CLASS_SMALL) * 0 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = i didnt do the math again but very low or smthin + embedding = list("embedded_pain_multiplier" = 0, "embed_chance" = 100, "embedded_fall_chance" = 0, "embedded_pain_chance" = 0, "embedded_ignore_throwspeed_threshold" = TRUE) //never hurts once it's in you + var/been_places = FALSE + var/datum/action/innate/send_chems/chems + +/obj/item/hardened_spike/chem/embedded(mob/living/carbon/human/embedded_mob) + if(been_places) + return + been_places = TRUE + chems = new + chems.transfered = embedded_mob + chems.spikey = src + to_chat(fired_by, "Link established! Use the \"Transfer Chemicals\" ability to send your chemicals to the linked target!") + chems.Grant(fired_by) + +/obj/item/hardened_spike/chem/unembedded() + to_chat(fired_by, "Link lost!") + QDEL_NULL(chems) + ..() + +/datum/action/innate/send_chems + icon_icon = 'icons/mob/actions/actions_genetic.dmi' + background_icon_state = "bg_spell" + check_flags = AB_CHECK_CONSCIOUS + button_icon_state = "spikechemswap" + name = "Transfer Chemicals" + desc = "Send all of your reagents into whomever the chem spike is embedded in. One use." + var/obj/item/hardened_spike/chem/spikey + var/mob/living/carbon/human/transfered + +/datum/action/innate/send_chems/Activate() + if(!ishuman(transfered) || !ishuman(owner)) + return + var/mob/living/carbon/human/transferer = owner + + to_chat(transfered, "You feel a tiny prick!") + transferer.reagents.trans_to(transfered, transferer.reagents.total_volume, 1, 1, 0, transfered_by = transferer) + + var/obj/item/bodypart/L = spikey.checkembedded() + + L.embedded_objects -= spikey + //this is where it would deal damage, if it transfers chems it removes itself so no damage + spikey.forceMove(get_turf(L)) + transfered.visible_message("[spikey] falls out of [transfered]!") + if(!transfered.has_embedded_objects()) + transfered.clear_alert("embeddedobject") + SEND_SIGNAL(transfered, COMSIG_CLEAR_MOOD_EVENT, "embedded") + spikey.unembedded() \ No newline at end of file diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index 399b822dce..144936ab95 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -246,6 +246,21 @@ return REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, "genetics") + +/datum/mutation/human/glow/anti + name = "Anti-Glow" + desc = "Your skin seems to attract and absorb nearby light creating 'darkness' around you." + text_gain_indication = "Your light around you seems to disappear." + glow = -3.5 //Slightly stronger, since negating light tends to be harder than making it. + locked = TRUE + +/datum/mutation/human/stimmed + name = "Stimmed" + desc = "The user's chemical balance is more robust." + quality = POSITIVE + text_gain_indication = "You feel stimmed." + difficulty = 16 + /datum/mutation/human/paranoia name = "Paranoia" desc = "Subject is easily terrified, and may suffer from hallucinations." @@ -259,4 +274,92 @@ owner.jitteriness = min(max(0, owner.jitteriness + 5), 30) if(prob(25)) to_chat(owner,"You feel someone creeping in on you...") - owner.hallucination += 20 \ No newline at end of file + owner.hallucination += 20 + + +/datum/mutation/human/badblink + name = "Spatial Instability" + desc = "The victim of the mutation has a very weak link to spatial reality, and may be displaced. Often causes extreme nausea." + quality = NEGATIVE + text_gain_indication = "The space around you twists sickeningly." + text_lose_indication = "The space around you settles back to normal." + difficulty = 18//high so it's hard to unlock and abuse + instability = 10 + synchronizer_coeff = 1 + energy_coeff = 1 + power_coeff = 1 + var/warpchance = 0 + +/datum/mutation/human/badblink/on_life() + if(prob(warpchance)) + var/warpmessage = pick( + "With a sickening 720-degree twist of [owner.p_their()] back, [owner] vanishes into thin air.", + "[owner] does some sort of strange backflip into another dimension. It looks pretty painful.", + "[owner] does a jump to the left, a step to the right, and warps out of reality.", + "[owner]'s torso starts folding inside out until it vanishes from reality, taking [owner] with it.", + "One moment, you see [owner]. The next, [owner] is gone.") + owner.visible_message(warpmessage, "You feel a wave of nausea as you fall through reality!") + var/warpdistance = rand(10,15) * GET_MUTATION_POWER(src) + do_teleport(owner, get_turf(owner), warpdistance, channel = TELEPORT_CHANNEL_FREE) + owner.adjust_disgust(GET_MUTATION_SYNCHRONIZER(src) * (warpchance * warpdistance)) + warpchance = 0 + owner.visible_message("[owner] appears out of nowhere!") + else + warpchance += 0.25 * GET_MUTATION_ENERGY(src) + +/datum/mutation/human/acidflesh + name = "Acidic Flesh" + desc = "Subject has acidic chemicals building up underneath the skin. This is often lethal." + quality = NEGATIVE + text_gain_indication = "A horrible burning sensation envelops you as your flesh turns to acid!" + text_lose_indication = "A feeling of relief fills you as your flesh goes back to normal." + difficulty = 18//high so it's hard to unlock and use on others + var/msgcooldown = 0 + +/datum/mutation/human/acidflesh/on_life() + if(prob(25)) + if(world.time > msgcooldown) + to_chat(owner, "Your acid flesh bubbles...") + msgcooldown = world.time + 200 + if(prob(15)) + owner.acid_act(rand(30,50), 10) + owner.visible_message("[owner]'s skin bubbles and pops.", "Your bubbling flesh pops! It burns!") + playsound(owner,'sound/weapons/sear.ogg', 50, TRUE) + +/datum/mutation/human/gigantism + name = "Gigantism"//negative version of dwarfism + desc = "The cells within the subject spread out to cover more area, making the subject appear larger." + quality = MINOR_NEGATIVE + difficulty = 12 + +/datum/mutation/human/gigantism/on_acquiring(mob/living/carbon/human/owner) + if(..()) + return + owner.resize = 1.25 + owner.update_transform() + owner.visible_message("[owner] suddenly grows!", "Everything around you seems to shrink..") + +/datum/mutation/human/gigantism/on_losing(mob/living/carbon/human/owner) + if(..()) + return + owner.resize = 0.8 + owner.update_transform() + owner.visible_message("[owner] suddenly shrinks!", "Everything around you seems to grow..") + +/datum/mutation/human/spastic + name = "Spastic" + desc = "Subject suffers from muscle spasms." + quality = NEGATIVE + text_gain_indication = "You flinch." + text_lose_indication = "Your flinching subsides." + difficulty = 16 + +/datum/mutation/human/spastic/on_acquiring() + if(..()) + return + owner.apply_status_effect(STATUS_EFFECT_SPASMS) + +/datum/mutation/human/spastic/on_losing() + if(..()) + return + owner.remove_status_effect(STATUS_EFFECT_SPASMS) \ No newline at end of file diff --git a/code/datums/mutations/cold.dm b/code/datums/mutations/cold.dm new file mode 100644 index 0000000000..717d1e8183 --- /dev/null +++ b/code/datums/mutations/cold.dm @@ -0,0 +1,42 @@ +/datum/mutation/human/geladikinesis + name = "Geladikinesis" + desc = "Allows the user to concentrate moisture and sub-zero forces into snow." + quality = POSITIVE + text_gain_indication = "Your hand feels cold." + instability = 10 + difficulty = 10 + synchronizer_coeff = 1 + power = /obj/effect/proc_holder/spell/targeted/conjure_item/snow + +/obj/effect/proc_holder/spell/targeted/conjure_item/snow + name = "Create Snow" + desc = "Concentrates cryokinetic forces to create snow, useful for snow-like construction." + item_type = /obj/item/stack/sheet/mineral/snow + charge_max = 50 + action_icon_state = "snow" + + +/datum/mutation/human/cryokinesis + name = "Cryokinesis" + desc = "Draws negative energy from the sub-zero void to freeze surrounding temperatures at subject's will." + quality = POSITIVE //upsides and downsides + text_gain_indication = "Your hand feels cold." + instability = 20 + difficulty = 12 + synchronizer_coeff = 1 + power = /obj/effect/proc_holder/spell/aimed/cryo + +/obj/effect/proc_holder/spell/aimed/cryo + name = "Cryobeam" + desc = "This power fires a frozen bolt at a target." + charge_max = 150 + cooldown_min = 150 + clothes_req = FALSE + range = 3 + projectile_type = /obj/item/projectile/temp/cryo + base_icon_state = "icebeam" + action_icon_state = "icebeam" + active_msg = "You focus your cryokinesis!" + deactive_msg = "You relax." + active = FALSE + diff --git a/code/datums/mutations/combined.dm b/code/datums/mutations/combined.dm index c166b01393..5fc9df7550 100644 --- a/code/datums/mutations/combined.dm +++ b/code/datums/mutations/combined.dm @@ -21,4 +21,12 @@ /datum/generecipe/mindread required = "/datum/mutation/human/antenna; /datum/mutation/human/paranoia" - result = MINDREAD \ No newline at end of file + result = MINDREAD + +/datum/generecipe/antiglow + required = "/datum/mutation/human/glow; /datum/mutation/human/void" + result = ANTIGLOWY + +/datum/generecipe/tonguechem + required = "/datum/mutation/human/tongue_spike; /datum/mutation/human/stimmed" + result = TONGUESPIKECHEM \ No newline at end of file diff --git a/code/datums/mutations/cold_resistance.dm b/code/datums/mutations/space_adaptation.dm similarity index 99% rename from code/datums/mutations/cold_resistance.dm rename to code/datums/mutations/space_adaptation.dm index 4b6beb7b00..90535fbf6e 100644 --- a/code/datums/mutations/cold_resistance.dm +++ b/code/datums/mutations/space_adaptation.dm @@ -27,3 +27,4 @@ return REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance") REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance") + diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 20c85f37a1..48fba2687d 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -28,6 +28,7 @@ take_damage(5, BURN, 0, 0) /obj/structure/spider/stickyweb + var/genetic = FALSE icon_state = "stickyweb1" /obj/structure/spider/stickyweb/Initialize() @@ -36,6 +37,8 @@ . = ..() /obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target) + if (genetic) + return if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider)) return TRUE else if(isliving(mover)) @@ -48,6 +51,27 @@ return prob(30) return TRUE +/obj/structure/spider/stickyweb/genetic //for the spider genes in genetics + genetic = TRUE + var/mob/living/allowed_mob + +/obj/structure/spider/stickyweb/genetic/Initialize(mapload, allowedmob) + allowed_mob = allowedmob + . = ..() + +/obj/structure/spider/stickyweb/genetic/CanPass(atom/movable/mover, turf/target) + . = ..() //this is the normal spider web return aka a spider would make this TRUE + if(mover == allowed_mob) + return TRUE + else if(isliving(mover)) //we change the spider to not be able to go through here + if(mover.pulledby == allowed_mob) + return TRUE + if(prob(50)) + to_chat(mover, "You get stuck in \the [src] for a moment.") + return FALSE + else if(istype(mover, /obj/item/projectile)) + return prob(30) + /obj/structure/spider/eggcluster name = "egg cluster" desc = "They seem to pulse slightly with an inner life." diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index cdd0cf3966..a6bdb2be1e 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -852,3 +852,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) if (HAS_TRAIT(src, TRAIT_NODROP)) return return ..() + +/obj/item/proc/embedded(mob/living/carbon/human/embedded_mob) + return + +/obj/item/proc/unembedded() + return \ No newline at end of file diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 81c5083ef7..440e86ba2f 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -619,7 +619,7 @@ is_capped = TRUE self_contained = FALSE // Don't disappear when they're empty can_change_colour = TRUE - + reagent_contents = list(/datum/reagent/fuel = 1, /datum/reagent/consumable/ethanol = 1) pre_noise = TRUE diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 0a3fd98511..b683ce0285 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -786,7 +786,7 @@ SLIME SCANNER item_state = "healthanalyzer" lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' - desc = "A hand-held scanner able to swiftly scan someone for potential mutations. Hold near a DNA console to update from their database." + desc = "A hand-held scanner for analyzing someones gene sequence on the fly. Hold near a DNA console to update the internal database." flags_1 = CONDUCT_1 item_flags = NOBLUDGEON slot_flags = ITEM_SLOT_BELT @@ -796,13 +796,25 @@ SLIME SCANNER throw_range = 7 materials = list(MAT_METAL=200) var/list/discovered = list() //hit a dna console to update the scanners database + var/list/buffer + var/ready = TRUE + var/cooldown = 200 /obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user) - user.visible_message("[user] has analyzed [M]'s genetic sequence.") - add_fingerprint(user) + if (!HAS_TRAIT(M, TRAIT_RADIMMUNE)) //no scanning if its a husk or DNA-less Species + user.visible_message("[user] analyzes [M]'s genetic sequence.", \ + "You analyze [M]'s genetic sequence.") + gene_scan(M, user) - gene_scan(M, user, src) + else + user.visible_message("[user] failed to analyse [M]'s genetic sequence.", "[M] has no readable genetic sequence!") + +/obj/item/sequence_scanner/attack_self(mob/user) + display_sequence(user) + +/obj/item/sequence_scanner/attack_self_tk(mob/user) + return /obj/item/sequence_scanner/afterattack(obj/O, mob/user, proximity) . = ..() @@ -812,28 +824,58 @@ SLIME SCANNER if(istype(O, /obj/machinery/computer/scan_consolenew)) var/obj/machinery/computer/scan_consolenew/C = O if(C.stored_research) - to_chat(user, "[name] database updated.") + to_chat(user, "[name] linked to central research database.") discovered = C.stored_research.discovered_mutations else to_chat(user,"No database to update from.") -/proc/gene_scan(mob/living/carbon/C, mob/living/user, obj/item/sequence_scanner/G) +/obj/item/sequence_scanner/proc/gene_scan(mob/living/carbon/C, mob/living/user) if(!iscarbon(C) || !C.has_dna()) return - to_chat(user, "[C.name]'s potential mutations.") - for(var/A in C.dna.mutation_index) - var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(A) - var/mut_name - if(G && (A in G.discovered)) - mut_name = "[HM.name] ([HM.alias])" - else - mut_name = HM.alias - var/temp = GET_GENE_STRING(HM.type, C.dna) - var/display - for(var/i in 0 to length(temp) / DNA_MUTATION_BLOCKS-1) - if(i) - display += "-" - display += copytext(temp, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1) + buffer = C.dna.mutation_index + to_chat(user, "Subject [C.name]'s DNA sequence has been saved to buffer.") + if(LAZYLEN(buffer)) + for(var/A in buffer) + to_chat(user, "[get_display_name(A)]") - to_chat(user, "- [mut_name] > [display]") \ No newline at end of file +/obj/item/sequence_scanner/proc/display_sequence(mob/living/user) + if(!LAZYLEN(buffer) || !ready) + return + var/list/options = list() + for(var/A in buffer) + options += get_display_name(A) + + var/answer = input(user, "Analyze Potential", "Sequence Analyzer") as null|anything in sortList(options) + if(answer && ready && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + var/sequence + for(var/A in buffer) //this physically hurts but i dont know what anything else short of an assoc list + if(get_display_name(A) == answer) + sequence = buffer[A] + break + + if(sequence) + var/display + for(var/i in 0 to length_char(sequence) / DNA_MUTATION_BLOCKS-1) + if(i) + display += "-" + display += copytext_char(sequence, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1) + + to_chat(user, "[display]
") + + ready = FALSE + icon_state = "[icon_state]_recharging" + addtimer(CALLBACK(src, .proc/recharge), cooldown, TIMER_UNIQUE) + +/obj/item/sequence_scanner/proc/recharge() + icon_state = initial(icon_state) + ready = TRUE + +/obj/item/sequence_scanner/proc/get_display_name(mutation) + var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(mutation) + if(!HM) + return "ERROR" + if(mutation in discovered) + return "[HM.name] ([HM.alias])" + else + return HM.alias \ No newline at end of file diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index 8849ac5173..60b1e81c94 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -296,54 +296,14 @@ name = "\improper DNA injector (Anti-Laser Eyes)" remove_mutations = list(LASEREYES) -/obj/item/dnainjector/thermalmut - name = "\improper DNA injector (Thermal Vision)" - add_mutations = list(THERMAL) - -/obj/item/dnainjector/antithermal - name = "\improper DNA injector (Anti-Thermal Vision)" - remove_mutations = list(THERMAL) - -/obj/item/dnainjector/telepathymut - name = "\improper DNA injector (Telepathy)" - add_mutations = list(TELEPATHY) - -/obj/item/dnainjector/antitelepathy - name = "\improper DNA injector (Anti-Telepathy)" - remove_mutations = list(TELEPATHY) - -/obj/item/dnainjector/voidmut - name = "\improper DNA injector (Void Magnet)" +/obj/item/dnainjector/void + name = "\improper DNA injector (Void)" add_mutations = list(VOID) /obj/item/dnainjector/antivoid - name = "\improper DNA injector (Anti-Void Magnet)" + name = "\improper DNA injector (Anti-Void)" remove_mutations = list(VOID) -/obj/item/dnainjector/firebreathmut - name = "\improper DNA injector (Firebreath)" - add_mutations = list(FIREBREATH) - -/obj/item/dnainjector/antifirebreath - name = "\improper DNA injector (Anti-Firebreath)" - remove_mutations = list(FIREBREATH) - -/obj/item/dnainjector/insulatedmut - name = "\improper DNA injector (Insulated)" - add_mutations = list(INSULATED) - -/obj/item/dnainjector/antiinsulated - name = "\improper DNA injector (Anti-Insulated)" - remove_mutations = list(INSULATED) - -/obj/item/dnainjector/shocktouchmut - name = "\improper DNA injector (Shock Touch)" - add_mutations = list(SHOCKTOUCH) - -/obj/item/dnainjector/antishocktouch - name = "\improper DNA injector (Anti-Shock Touch)" - remove_mutations = list(SHOCKTOUCH) - /obj/item/dnainjector/antenna name = "\improper DNA injector (Antenna)" add_mutations = list(ANTENNA) @@ -368,6 +328,133 @@ name = "\improper DNA injector (Anti-Mindread)" remove_mutations = list(MINDREAD) +/obj/item/dnainjector/radioactive + name = "\improper DNA injector (Radioactive)" + add_mutations = list(RADIOACTIVE) + +/obj/item/dnainjector/antiradioactive + name = "\improper DNA injector (Anti-Radioactive)" + remove_mutations = list(RADIOACTIVE) +/obj/item/dnainjector/olfaction + name = "\improper DNA injector (Olfaction)" + add_mutations = list(OLFACTION) + +/obj/item/dnainjector/antiolfaction + name = "\improper DNA injector (Anti-Olfaction)" + remove_mutations = list(OLFACTION) + +/obj/item/dnainjector/insulated + name = "\improper DNA injector (Insulated)" + add_mutations = list(INSULATED) + +/obj/item/dnainjector/antiinsulated + name = "\improper DNA injector (Anti-Insulated)" + remove_mutations = list(INSULATED) + +/obj/item/dnainjector/shock + name = "\improper DNA injector (Shock Touch)" + add_mutations = list(SHOCKTOUCH) + +/obj/item/dnainjector/antishock + name = "\improper DNA injector (Anti-Shock Touch)" + remove_mutations = list(SHOCKTOUCH) + +/obj/item/dnainjector/spacialinstability + name = "\improper DNA injector (Spacial Instability)" + add_mutations = list(BADBLINK) + +/obj/item/dnainjector/antispacialinstability + name = "\improper DNA injector (Anti-Spacial Instability)" + remove_mutations = list(BADBLINK) + +/obj/item/dnainjector/acidflesh + name = "\improper DNA injector (Acid Flesh)" + add_mutations = list(ACIDFLESH) + +/obj/item/dnainjector/antiacidflesh + name = "\improper DNA injector (Acid Flesh)" + remove_mutations = list(ACIDFLESH) + +/obj/item/dnainjector/gigantism + name = "\improper DNA injector (Gigantism)" + add_mutations = list(GIGANTISM) + +/obj/item/dnainjector/antigigantism + name = "\improper DNA injector (Anti-Gigantism)" + remove_mutations = list(GIGANTISM) + +/obj/item/dnainjector/spastic + name = "\improper DNA injector (Spastic)" + add_mutations = list(SPASTIC) + +/obj/item/dnainjector/antispastic + name = "\improper DNA injector (Anti-Spastic)" + remove_mutations = list(SPASTIC) + +/obj/item/dnainjector/geladikinesis + name = "\improper DNA injector (Geladikinesis)" + add_mutations = list(GELADIKINESIS) + +/obj/item/dnainjector/antigeladikinesis + name = "\improper DNA injector (Anti-Geladikinesis)" + remove_mutations = list(GELADIKINESIS) + +/obj/item/dnainjector/cryokinesis + name = "\improper DNA injector (Cryokinesis)" + add_mutations = list(CRYOKINESIS) + +/obj/item/dnainjector/anticryokinesis + name = "\improper DNA injector (Anti-Cryokinesis)" + remove_mutations = list(CRYOKINESIS) + +/obj/item/dnainjector/thermal + name = "\improper DNA injector (Thermal Vision)" + add_mutations = list(THERMAL) + +/obj/item/dnainjector/antithermal + name = "\improper DNA injector (Anti-Thermal Vision)" + remove_mutations = list(THERMAL) + +/obj/item/dnainjector/glow + name = "\improper DNA injector (Glowy)" + add_mutations = list(GLOWY) + +/obj/item/dnainjector/removeglow + name = "\improper DNA injector (Anti-Glowy)" + remove_mutations = list(GLOWY) + +/obj/item/dnainjector/antiglow + name = "\improper DNA injector (Antiglowy)" + add_mutations = list(ANTIGLOWY) + +/obj/item/dnainjector/removeantiglow + name = "\improper DNA injector (Anti-Antiglowy)" + remove_mutations = list(ANTIGLOWY) + +/obj/item/dnainjector/firebreath + name = "\improper DNA injector (Firebreath)" + add_mutations = list(FIREBREATH) + +/obj/item/dnainjector/antifirebreath + name = "\improper DNA injector (Anti-Firebreath)" + remove_mutations = list(FIREBREATH) + +/obj/item/dnainjector/tonguespike + name = "\improper DNA injector (Tongue Spike)" + add_mutations = list(TONGUESPIKE) + +/obj/item/dnainjector/antitonguespike + name = "\improper DNA injector (Anti-Tongue Spike)" + remove_mutations = list(TONGUESPIKE) + +/obj/item/dnainjector/spiderweb + name = "\improper DNA injector (Spider Web)" + add_mutations = list(SPIDER_WEB) + +/obj/item/dnainjector/antispiderweb + name = "\improper DNA injector (Anti-Spider Web)" + remove_mutations = list(SPIDER_WEB) + /obj/item/dnainjector/timed var/duration = 600 diff --git a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm index b7c90523b6..63e8ee6caf 100644 --- a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm +++ b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm @@ -96,6 +96,7 @@ user.dropItemToGround(src, TRUE) //user.drop_item() // "drop item" doesn't seem to exist anymore. New proc is user.dropItemToGround() but it doesn't seem like it's needed now? var/obj/item/bodypart/B = C.get_bodypart("chest") // This was all taken from hitby() in human_defense.dm B.embedded_objects |= src + embedded() add_mob_blood(target)//Place blood on the stake loc = C // Put INSIDE the character B.receive_damage(w_class * embedding.embedded_impact_pain_multiplier) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 637178ffe6..faf443e987 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -66,6 +66,7 @@ L.embedded_objects |= I I.add_mob_blood(src)//it embedded itself in you, of course it's bloody! I.forceMove(src) + I.embedded() L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier) visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 7507067597..9316a36e6b 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -316,6 +316,7 @@ BP.receive_damage(I.w_class*I.embedding.embedded_fall_pain_multiplier) BP.embedded_objects -= I I.forceMove(drop_location()) + I.unembedded() visible_message("[I] falls out of [name]'s [BP.name]!","[I] falls out of your [BP.name]!") if(!has_embedded_objects()) clear_alert("embeddedobject") diff --git a/code/modules/projectiles/projectile/special/temperature.dm b/code/modules/projectiles/projectile/special/temperature.dm index dae7d5f8b3..c17fd0a447 100644 --- a/code/modules/projectiles/projectile/special/temperature.dm +++ b/code/modules/projectiles/projectile/special/temperature.dm @@ -16,3 +16,8 @@ /obj/item/projectile/temp/hot name = "heat beam" temperature = 400 + +/obj/item/projectile/temp/cryo + name = "cryo beam" + range = 3 + temperature = -240 // Single slow shot reduces temp greatly \ No newline at end of file diff --git a/code/modules/spells/spell_types/mime.dm b/code/modules/spells/spell_types/mime.dm index d44c6a3e3d..621efdc821 100644 --- a/code/modules/spells/spell_types/mime.dm +++ b/code/modules/spells/spell_types/mime.dm @@ -128,6 +128,49 @@ invocation_type ="none" ..() +/obj/effect/proc_holder/spell/targeted/touch/mimerope + name = "Invisible Rope" + desc = "Form an invisible rope to tie people or trip people with." + school = "mime" + panel = "Mime" + invocation_type = "emote" + invocation_emote_self = "You start fabricate an invisible rope." + charge_max = 700 + sound = null + clothes_req = 0 + range = -1 + include_user = 1 + action_icon_state = "mime" + action_background_icon_state = "bg_mime" + hand_path = /obj/item/melee/touch_attack/mimerope + +/obj/effect/proc_holder/spell/targeted/touch/mimerope/Click() + if(usr && usr.mind) + if(!usr.mind.miming) + to_chat(usr, "You must dedicate yourself to silence first.") + return + if (usr.get_active_held_item()) + to_chat(usr, "Your hands must be free to create the invisible rope.") + return + invocation = "[usr.real_name] is twirling an invisible rope in [usr.p_their()] hands." + else + invocation_type ="none" + +/obj/effect/proc_holder/spell/targeted/touch/mimerope/cast(list/targets,mob/user = usr) + + usr.put_in_hands() + +/obj/item/melee/touch_attack/mimerope + item_state = "" + icon_state = "" + name = "mime rope" + desc = "An invisible rope." + +/obj/item/restraints/handcuffs/cable/mime + name = "mime restraints" + desc = "An invisible rope." + item_state = "" + icon_state = "" /obj/item/book/granter/spell/mimery_blockade spell = /obj/effect/proc_holder/spell/targeted/forcewall/mime diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 615933a3b5..a94e962da1 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -102,6 +102,7 @@ for(var/obj/item/I in embedded_objects) embedded_objects -= I I.forceMove(src) + I.unembedded() if(!C.has_embedded_objects()) C.clear_alert("embeddedobject") SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "embedded") diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index eed8f62e10..aaca33685b 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -161,6 +161,7 @@ for(var/obj/item/I in L.embedded_objects) L.embedded_objects -= I I.forceMove(T) + I.unembedded() clear_alert("embeddedobject") SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "embedded") diff --git a/code/modules/surgery/remove_embedded_object.dm b/code/modules/surgery/remove_embedded_object.dm index 0c3a3b55a5..7d7cd2df63 100644 --- a/code/modules/surgery/remove_embedded_object.dm +++ b/code/modules/surgery/remove_embedded_object.dm @@ -25,6 +25,7 @@ objects++ I.forceMove(get_turf(H)) L.embedded_objects -= I + I.unembedded() if(!H.has_embedded_objects()) H.clear_alert("embeddedobject") SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "embedded") diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 87fe12cad2..c58fb5d940 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/modular_citadel/code/modules/mob/living/carbon/human/human.dm b/modular_citadel/code/modules/mob/living/carbon/human/human.dm index e5d386b56b..2793e888f4 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/human.dm +++ b/modular_citadel/code/modules/mob/living/carbon/human/human.dm @@ -19,6 +19,7 @@ L.embedded_objects -= I L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class*painmul)//It hurts to rip it out, get surgery you dingus. And if you're ripping it out quickly via resist, it's gonna hurt even more I.forceMove(get_turf(src)) + I.unembedded() user.put_in_hands(I) user.emote("scream") user.visible_message("[user] rips [I] out of [user.p_their()] [L.name]!","You remove [I] from your [L.name].") diff --git a/tgstation.dme b/tgstation.dme index 203d5a22de..eb42ee7e1e 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -522,11 +522,12 @@ #include "code\datums\mutations\antenna.dm" #include "code\datums\mutations\body.dm" #include "code\datums\mutations\chameleon.dm" -#include "code\datums\mutations\cold_resistance.dm" +#include "code\datums\mutations\cold.dm" #include "code\datums\mutations\combined.dm" #include "code\datums\mutations\hulk.dm" #include "code\datums\mutations\radioactive.dm" #include "code\datums\mutations\sight.dm" +#include "code\datums\mutations\space_adaptation.dm" #include "code\datums\mutations\speech.dm" #include "code\datums\mutations\telekinesis.dm" #include "code\datums\ruins\lavaland.dm"