Adds Roach Infusion to the DNA infuser (#76393)

## About The Pull Request

- Adds Roach Infusion to the DNA infuser. 
   - Bonuses include:
- All infused organs are 2x as healthy, notably your heart: Meaning
getting revived after being dead a while is easier
- When being attacked from behind or while lying down, take 50% less
damage from brute attacks
      - Lose disgust 32x faster, making it a non-issue
      - Higher toxin purge threshold (5 units, up from 3)
      - Virus resistance (same as spaceacillin)
      - 100 innate bomb armor, preventing explosions from gibbing you
      - 90 innate bio armor
- Immunity to appendicitis, radiation, and to being gibbed by nuclear
bombs
   - Downsides include:
      - Knockdowns are 3x as long
      - get 3x as hungry
      - Ingest reagents to your stomach 1.5x slower
      - Take 2x as much damage from toxins
- Toxins over the purge threshold deal 4x more liver damage (effectively
2x, as the liver has 2x health)
      - Becoming a bug
      - Roaches are gross

- Adds a way to kill roaches without having them splat. If they are
sprayed with bug spray, they will simply fall over, and can be scooped
up.


https://github.com/tgstation/tgstation/assets/51863163/5078c493-9e28-42cb-ae51-45fa25b67a34

## Why It's Good For The Game

More content for the DNA infuser, which benefits greatly from variety. 

While initially it may seem like a lot of bonuses, a lot of them are
very niche, with the exception being the brute resilience which is the
big "actually useful" bonus you gain.

The infusion is intended to be given to Engineers, offering innate
Radiation immunity to allow them to work on the Supermatter without
needing a rad suit. Likewise, if the work goes south and the Supermatter
goes boom, their body will more than likely survive the blast.

## Changelog

🆑 Melbert
add: Adds the Roach infusion to the DNA infuser. Do you want to survive
a nuclear apocalypse? Visit genetics today.
add: Adds a way to kill Roaches without splatting them. Visit botany for
a spray bottle of pestkiller.
qol: Infuser book is more book-like
fix: DNA infuser correctly gives on-success feedback messages
/🆑

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
MrMelbert
2023-07-02 03:44:03 +01:00
committed by GitHub
co-authored by Jacquerel
parent c3052b9bc3
commit 7f3d763285
32 changed files with 485 additions and 80 deletions
+1
View File
@@ -267,6 +267,7 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
GLOBAL_LIST_INIT(book_types, typecacheof(list(
/obj/item/book,
/obj/item/spellbook,
/obj/item/infuser_book,
)))
// Jobs
+7 -3
View File
@@ -567,10 +567,14 @@
///Squash flags. For squashable element
///Whether or not the squashing requires the squashed mob to be lying down
/// Squashing will not occur if the mob is not lying down (bodyposition is LYING_DOWN)
#define SQUASHED_SHOULD_BE_DOWN (1<<0)
///Whether or not to gib when the squashed mob is moved over
#define SQUASHED_SHOULD_BE_GIBBED (1<<0)
/// If present, outright gibs the squashed mob instead of just dealing damage
#define SQUASHED_SHOULD_BE_GIBBED (1<<1)
/// If squashing always passes if the mob is dead
#define SQUASHED_ALWAYS_IF_DEAD (1<<2)
/// Don't squash our mob if its not located in a turf
#define SQUASHED_DONT_SQUASH_IN_CONTENTS (1<<3)
/*
* Defines for "AI emotions", allowing the AI to expression emotions
+7 -1
View File
@@ -214,9 +214,15 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_RESISTLOWPRESSURE "resist_low_pressure"
/// This human is immune to the effects of being exploded. (ex_act)
#define TRAIT_BOMBIMMUNE "bomb_immunity"
/// Immune to being irradiated
#define TRAIT_RADIMMUNE "rad_immunity"
#define TRAIT_GENELESS "geneless"
/// This mob won't get gibbed by nukes going off
#define TRAIT_NUKEIMMUNE "nuke_immunity"
/// Can't be given viruses
#define TRAIT_VIRUSIMMUNE "virus_immunity"
/// Reduces the chance viruses will spread to this mob, and if the mob has a virus, slows its advancement
#define TRAIT_VIRUS_RESISTANCE "virus_resistance"
#define TRAIT_GENELESS "geneless"
#define TRAIT_PIERCEIMMUNE "pierce_immunity"
#define TRAIT_NODISMEMBER "dismember_immunity"
#define TRAIT_NOFIRE "nonflammable"
+14
View File
@@ -68,6 +68,20 @@
//doesn't have an object argument because this is "Stacking" with the animate call above
//3 billion% intentional
/// Similar to shake but more spasm-y and jerk-y
/atom/proc/spasm_animation(loops = -1)
var/list/transforms = list(
matrix(transform).Translate(-1, 0),
matrix(transform).Translate(0, 1),
matrix(transform).Translate(1, 0),
matrix(transform).Translate(0, -1),
)
animate(src, transform = transforms[1], time = 0.2, loop = loops)
animate(transform = transforms[2], time = 0.1)
animate(transform = transforms[3], time = 0.2)
animate(transform = transforms[4], time = 0.3)
/**
* Shear the transform on either or both axes.
* * x - X axis shearing
+5 -3
View File
@@ -37,11 +37,13 @@
return
var/mob/living/parent_as_living = parent
if(squash_flags & SQUASHED_SHOULD_BE_DOWN && parent_as_living.body_position != LYING_DOWN)
if((squash_flags & SQUASHED_DONT_SQUASH_IN_CONTENTS) && !isturf(parent_as_living.loc))
return
var/should_squash = prob(squash_chance)
if((squash_flags & SQUASHED_SHOULD_BE_DOWN) && parent_as_living.body_position != LYING_DOWN)
return
var/should_squash = ((squash_flags & SQUASHED_ALWAYS_IF_DEAD) && parent_as_living.stat == DEAD) || prob(squash_chance)
if(should_squash && on_squash_callback)
if(on_squash_callback.Invoke(parent_as_living, crossing_movable))
+3 -5
View File
@@ -64,7 +64,7 @@
if(ishuman(src))
var/mob/living/carbon/human/infecting_human = src
if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) && prob(75))
if(HAS_TRAIT(infecting_human, TRAIT_VIRUS_RESISTANCE) && prob(75))
return
switch(target_zone)
@@ -95,10 +95,8 @@
disease.try_infect(src)
/mob/living/proc/AirborneContractDisease(datum/disease/disease, force_spread)
if(ishuman(src))
var/mob/living/carbon/human/infecting_human = src
if(infecting_human.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) && prob(75))
return
if(HAS_TRAIT(src, TRAIT_VIRUS_RESISTANCE) && prob(75))
return
if(((disease.spread_flags & DISEASE_SPREAD_AIRBORNE) || force_spread) && prob((50*disease.spreading_modifier) - 1))
ForceContractDisease(disease)
+2 -2
View File
@@ -64,7 +64,7 @@
///Proc to process the disease and decide on whether to advance, cure or make the sympthoms appear. Returns a boolean on whether to continue acting on the symptoms or not.
/datum/disease/proc/stage_act(seconds_per_tick, times_fired)
var/slowdown = affected_mob.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) ? 0.5 : 1 // spaceacillin slows stage speed by 50%
var/slowdown = HAS_TRAIT(affected_mob, TRAIT_VIRUS_RESISTANCE) ? 0.5 : 1 // spaceacillin slows stage speed by 50%
if(has_cure())
if(disease_flags & CHRONIC && SPT_PROB(cure_chance, seconds_per_tick))
@@ -104,7 +104,7 @@
if(!(spread_flags & DISEASE_SPREAD_AIRBORNE) && !force_spread)
return
if(affected_mob.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) || (affected_mob.satiety > 0 && prob(affected_mob.satiety/10)))
if(HAS_TRAIT(affected_mob, TRAIT_VIRUS_RESISTANCE) || (affected_mob.satiety > 0 && prob(affected_mob.satiety/10)))
return
var/spread_range = 2
+88
View File
@@ -0,0 +1,88 @@
/// Simple element to be applied to reagents
/// When those reagents are exposed to mobs with the bug biotype, causes toxins damage
/// If this delivers the killing blow on a non-humanoid mob, it applies a special status effect that does a funny animation
/datum/element/bugkiller_reagent
/datum/element/bugkiller_reagent/Attach(datum/target)
. = ..()
if(!istype(target, /datum/reagent))
return
RegisterSignal(target, COMSIG_REAGENT_EXPOSE_MOB, PROC_REF(on_expose))
/datum/element/bugkiller_reagent/Detach(datum/source, ...)
. = ..()
UnregisterSignal(source, COMSIG_REAGENT_EXPOSE_MOB)
/datum/element/bugkiller_reagent/proc/on_expose(
datum/reagent/source,
mob/living/exposed_mob,
methods = TOUCH,
reac_volume,
show_message = TRUE,
touch_protection = 0,
)
SIGNAL_HANDLER
if(exposed_mob.stat == DEAD)
return
if(!(exposed_mob.mob_biotypes & MOB_BUG))
return
// capping damage so splashing a beaker on a moth is not an instant crit
var/damage = min(round(0.4 * reac_volume * (1 - touch_protection), 0.1), 12)
if(damage < 1)
return
if(!(exposed_mob.mob_biotypes & MOB_HUMANOID) && exposed_mob.health <= damage)
// no-ops if they are already in the process of dying
exposed_mob.apply_status_effect(/datum/status_effect/bugkiller_death)
return
if(exposed_mob.apply_damage(damage, TOX) && damage >= 6)
// yes i know it's not burn damage. the burning is on the inside.
to_chat(exposed_mob, span_danger("You feel a burning sensation."))
/// If bugkiller delivers a lethal dosage, applies this effect which does a funny animation THEN kills 'em
/// Also makes it so simplemobs / basicmobs no longer delete when they die (if they do)
/datum/status_effect/bugkiller_death
id = "bugkiller_death"
alert_type = /atom/movable/screen/alert/status_effect/bugkiller_death
/// How many times the spasm loops
var/spasm_loops = 0
/datum/status_effect/bugkiller_death/on_creation(mob/living/new_other, duration = 4 SECONDS)
src.duration = duration
src.spasm_loops = ROUND_UP(duration / 0.8) // one spasm ~= 0.8 deciseconds (yes deciseconds)
return ..()
/datum/status_effect/bugkiller_death/on_apply()
if(owner.stat == DEAD)
return FALSE
playsound(owner, 'sound/voice/human/malescream_1.ogg', 25, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 5)
to_chat(owner, span_userdanger("The world begins to go dark..."))
owner.spasm_animation(spasm_loops)
owner.adjust_eye_blur(duration)
return TRUE
/datum/status_effect/bugkiller_death/on_remove()
if(owner.stat == DEAD || QDELETED(owner))
return
if(isbasicmob(owner))
var/mob/living/basic/basic_owner = owner
basic_owner.basic_mob_flags &= ~DEL_ON_DEATH
basic_owner.basic_mob_flags |= FLIP_ON_DEATH
if(isanimal(owner))
var/mob/living/simple_animal/simple_owner = owner
simple_owner.del_on_death = FALSE
simple_owner.flip_on_death = TRUE
owner.investigate_log("died to being sprayed with bugkiller.", INVESTIGATE_DEATHS)
owner.death()
/atom/movable/screen/alert/status_effect/bugkiller_death
name = "Overwhelming Toxicity"
desc = "Don't go into the light!"
icon_state = "paralysis"
@@ -1,4 +1,18 @@
{
"appendix": [
{
"type": "icon_state",
"icon_state": "appendix",
"blend_mode": "overlay",
"color_ids": [ 1 ]
},
{
"type": "icon_state",
"icon_state": "appendix_insides",
"blend_mode": "overlay",
"color_ids": [ 2 ]
}
],
"brain": [
{
"type": "icon_state",
+1 -1
View File
@@ -41,7 +41,7 @@
return
if(victim.reagents)
if(victim.reagents.has_reagent(/datum/reagent/medicine/spaceacillin))
if(HAS_TRAIT(victim, TRAIT_VIRUS_RESISTANCE))
sanitization += 0.9
if(victim.reagents.has_reagent(/datum/reagent/space_cleaner/sterilizine/))
sanitization += 0.9
@@ -136,6 +136,7 @@
new_organ = new new_organ()
new_organ.replace_into(target)
check_tier_progression(target)
return TRUE
/// Picks a random mutated organ from the infuser entry which is also compatible with the target mob.
/// Tries to return a typepath of a valid mutant organ if all of the following criteria are true:
@@ -8,12 +8,22 @@
throw_speed = 2
throw_range = 5
w_class = WEIGHT_CLASS_TINY
drop_sound = 'sound/items/handling/book_drop.ogg'
pickup_sound = 'sound/items/handling/book_pickup.ogg'
/obj/item/infuser_book/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "InfuserBook")
ui.open()
playsound(src, SFX_PAGE_TURN, 30, TRUE)
/obj/item/infuser_book/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(action == "play_flip_sound")
playsound(src, SFX_PAGE_TURN, 30, TRUE)
/obj/item/infuser_book/ui_static_data(mob/user)
var/list/data = list()
@@ -75,3 +75,33 @@
infusion_desc = "skittish"
tier = DNA_MUTANT_TIER_ONE
status_effect_type = /datum/status_effect/organ_set_bonus/rat
/datum/infuser_entry/roach
name = "Roach"
infuse_mob_name = "cockroach"
desc = "It seems as if you're a fan of ancient literature by your interest in this. Assuredly, merging cockroach DNA into your genome \
will not cause you to become incapable of leaving your bed. These creatures are incredibly resilient against many things \
humans are weak to, and we can use that! Who wouldn't like to survive a nuclear blast? \
NOTE: Squished roaches will not work for the infuser, if that wasn't obvious. Try spraying them with some pestkiller from botany!"
threshold_desc = "you will no longer be gibbed by explosions, and gain incredible resistance to viruses and radiation."
qualities = list(
"resilience to attacks from behind",
"healthier organs",
"get over disgust very quickly",
"the ability to survive a nuclear apocalypse",
"harder to pick yourself up from falling over",
"avoid toxins at all costs",
"always down to find a snack",
)
input_obj_or_mob = list(
/mob/living/basic/cockroach,
)
output_organs = list(
/obj/item/organ/internal/heart/roach,
/obj/item/organ/internal/stomach/roach,
/obj/item/organ/internal/liver/roach,
/obj/item/organ/internal/appendix/roach,
)
infusion_desc = "kafkaesque" // Gregor Samsa !!
tier = DNA_MUTANT_TIER_ONE
status_effect_type = /datum/status_effect/organ_set_bonus/roach
@@ -0,0 +1,226 @@
#define ROACH_ORGAN_COLOR "#7c4200"
// Yeah i'm lazy and we don't use any of the other color slots
#define ROACH_COLORS ROACH_ORGAN_COLOR + ROACH_ORGAN_COLOR + ROACH_ORGAN_COLOR
/datum/armor/roach_internal_armor
bomb = 100
bio = 90
/datum/status_effect/organ_set_bonus/roach
id = "organ_set_bonus_roach"
organs_needed = 4
bonus_activate_text = span_notice("Roach DNA is deeply infused with you! \
You feel increasingly resistant to explosives, radiation, and viral agents.")
bonus_deactivate_text = span_notice("You are no longer majority roach, \
and you feel much more vulnerable to nuclear apocalypses.")
// - Immunity to nuke gibs
// - Nukes come with radiation (not actually but yknow)
bonus_traits = list(TRAIT_NUKEIMMUNE, TRAIT_RADIMMUNE, TRAIT_VIRUS_RESISTANCE)
/// Armor type attached to the owner's physiology
var/datum/armor/given_armor = /datum/armor/roach_internal_armor
/// Storing biotypes pre-organ bonus applied so we don't remove bug from mobs which should have it.
var/old_biotypes = NONE
/datum/status_effect/organ_set_bonus/roach/enable_bonus()
. = ..()
if(!ishuman(owner))
return
var/mob/living/carbon/human/human_owner = owner
human_owner.physiology.armor = human_owner.physiology.armor.add_other_armor(given_armor)
old_biotypes = human_owner.mob_biotypes
human_owner.mob_biotypes |= MOB_BUG
/datum/status_effect/organ_set_bonus/roach/disable_bonus()
. = ..()
if(!ishuman(owner) || QDELETED(owner))
return
var/mob/living/carbon/human/human_owner = owner
human_owner.physiology.armor = human_owner.physiology.armor.subtract_other_armor(given_armor)
if(!(old_biotypes & MOB_BUG)) // only remove bug if it wasn't there before
human_owner.mob_biotypes &= ~MOB_BUG
/// Roach heart:
/// Reduces damage taken from brute attacks from behind,
/// but increases duration of knockdowns
/obj/item/organ/internal/heart/roach
name = "mutated roach-heart"
desc = "Roach DNA infused into what was once a normal heart."
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "heart"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = ROACH_COLORS
/// Timer ID for resetting the damage resistance applied from attacks from behind
var/defense_timerid
/// Bodypart overlay applied to the chest the heart is in
var/datum/bodypart_overlay/simple/roach_shell/roach_shell
/obj/item/organ/internal/heart/roach/Initialize(mapload)
. = ..()
AddElement(/datum/element/noticable_organ, "has hardened, somewhat translucent skin.")
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach)
roach_shell = new()
/obj/item/organ/internal/heart/roach/Destroy()
QDEL_NULL(roach_shell)
return ..()
/obj/item/organ/internal/heart/roach/on_insert(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner))
return
var/mob/living/carbon/human/human_owner = organ_owner
RegisterSignal(human_owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(modify_damage))
human_owner.physiology.knockdown_mod *= 3
var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST)
chest.add_bodypart_overlay(roach_shell)
human_owner.update_body_parts()
/obj/item/organ/internal/heart/roach/on_remove(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner) || QDELETED(organ_owner))
return
var/mob/living/carbon/human/human_owner = organ_owner
UnregisterSignal(human_owner, COMSIG_MOB_APPLY_DAMAGE)
human_owner.physiology.knockdown_mod /= 3
if(defense_timerid)
reset_damage(human_owner)
var/obj/item/bodypart/chest/chest = human_owner.get_bodypart(BODY_ZONE_CHEST)
chest.remove_bodypart_overlay(roach_shell)
human_owner.update_body_parts()
/**
* Signal proc for [COMSIG_MOB_APPLY_DAMAGE]
*
* Being hit with brute damage in the back will impart a large damage resistance bonus for a very short period.
*/
/obj/item/organ/internal/heart/roach/proc/modify_damage(datum/source, damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction, obj/item/attacking_item)
SIGNAL_HANDLER
if(!ishuman(owner) || !attack_direction || damagetype != BRUTE || owner.stat >= UNCONSCIOUS)
return
var/mob/living/carbon/human/human_owner = owner
// No tactical spinning
if(human_owner.flags_1 & IS_SPINNING_1)
return
// If we're lying down, or were attacked from the back, we get armor.
var/should_armor_up = (human_owner.body_position == LYING_DOWN) || (human_owner.dir & attack_direction)
if(!should_armor_up)
return
// Take 50% less damage from attack behind us
if(!defense_timerid)
human_owner.physiology.brute_mod /= 2
human_owner.visible_message(span_warning("[human_owner]'s back hardens against the blow!"))
playsound(human_owner, 'sound/effects/constructform.ogg', 25, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
defense_timerid = addtimer(CALLBACK(src, PROC_REF(reset_damage), owner), 5 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
/obj/item/organ/internal/heart/roach/proc/reset_damage(mob/living/carbon/human/human_owner)
defense_timerid = null
if(!QDELETED(human_owner))
human_owner.physiology.brute_mod *= 2
human_owner.visible_message(span_warning("[human_owner]'s back softens again."))
// Simple overlay so we can add a roach shell to guys with roach hearts
/datum/bodypart_overlay/simple/roach_shell
icon_state = "roach_shell"
layers = EXTERNAL_FRONT|EXTERNAL_BEHIND
/datum/bodypart_overlay/simple/roach_shell/get_image(image_layer, obj/item/bodypart/limb)
return image(
icon = icon,
icon_state = "[icon_state]_[mutant_bodyparts_layertext(image_layer)]",
layer = image_layer,
)
/// Roach stomach:
/// Makes disgust a non-issue, very slightly worse at passing off reagents
/// Also makes you more hungry
/obj/item/organ/internal/stomach/roach
name = "mutated roach-stomach"
desc = "Roach DNA infused into what was once a normal stomach."
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
disgust_metabolism = 32 // Demolishes any disgust we have
metabolism_efficiency = 0.033 // Slightly worse at transferring reagents
hunger_modifier = 3
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "stomach"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = ROACH_COLORS
/obj/item/organ/internal/stomach/roach/Initialize(mapload)
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach)
/// Roach liver:
/// Purges toxins at a higher threshold, but takes more damage from them if not purged
/obj/item/organ/internal/liver/roach
name = "mutated roach-liver"
desc = "Roach DNA infused into what was once a normal liver."
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
toxTolerance = 5 // More tolerance for toxins
liver_resistance = 0.25 // But if they manage to get in you're screwed
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "liver"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = ROACH_COLORS
/obj/item/organ/internal/liver/roach/Initialize(mapload)
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach)
/obj/item/organ/internal/liver/roach/on_insert(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner))
return
var/mob/living/carbon/human/human_owner = owner
human_owner.physiology.tox_mod *= 2
/obj/item/organ/internal/liver/roach/on_remove(mob/living/carbon/organ_owner, special)
. = ..()
if(!ishuman(organ_owner) || QDELETED(organ_owner))
return
var/mob/living/carbon/human/human_owner = organ_owner
human_owner.physiology.tox_mod /= 2
/// Roach appendix:
/// No appendicitus! weee!
/obj/item/organ/internal/appendix/roach
name = "mutated roach-appendix"
desc = "Roach DNA infused into what was once a normal appendix. It could get <i>worse</i>?"
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "appendix"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = ROACH_COLORS
/obj/item/organ/internal/appendix/roach/Initialize(mapload)
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/roach)
/obj/item/organ/internal/appendix/roach/become_inflamed()
return
#undef ROACH_ORGAN_COLOR
#undef ROACH_COLORS
+1 -15
View File
@@ -159,22 +159,8 @@
if(!awakened)
return
var/static/list/transforms
if(!transforms)
var/matrix/M1 = matrix()
var/matrix/M2 = matrix()
var/matrix/M3 = matrix()
var/matrix/M4 = matrix()
M1.Translate(-1, 0)
M2.Translate(0, 1)
M3.Translate(1, 0)
M4.Translate(0, -1)
transforms = list(M1, M2, M3, M4)
animate(src, transform=transforms[1], time=0.2, loop=-1)
animate(transform=transforms[2], time=0.1)
animate(transform=transforms[3], time=0.2)
animate(transform=transforms[4], time=0.3)
spasm_animation()
/obj/item/his_grace/proc/drowse() //Good night, Mr. Grace.
if(!awakened || ascended)
@@ -627,6 +627,9 @@ GLOBAL_VAR(station_nuke_source)
* Helper proc that handles gibbing someone who has been nuked.
*/
/proc/nuke_gib(mob/living/gibbed, atom/source)
if(HAS_TRAIT(gibbed, TRAIT_NUKEIMMUNE))
return FALSE
if(istype(gibbed.loc, /obj/structure/closet/secure_closet/freezer))
var/obj/structure/closet/secure_closet/freezer/freezer = gibbed.loc
if(!freezer.jones)
@@ -2,13 +2,15 @@
name = "cockroach"
desc = "This station is just crawling with bugs."
icon_state = "cockroach"
icon_dead = "cockroach" //Make this work
icon_dead = "cockroach_no_animation"
density = FALSE
mob_biotypes = MOB_ORGANIC|MOB_BUG
mob_size = MOB_SIZE_TINY
held_w_class = WEIGHT_CLASS_TINY
health = 1
maxHealth = 1
speed = 1.25
can_be_held = TRUE
gold_core_spawnable = FRIENDLY_SPAWN
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
@@ -38,17 +40,23 @@
var/static/list/roach_drops = list(/obj/effect/decal/cleanable/insectguts)
AddElement(/datum/element/death_drops, roach_drops)
AddElement(/datum/element/swabable, cockroach_cell_line, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 7)
AddComponent(/datum/component/squashable, squash_chance = 50, squash_damage = 1)
AddElement(/datum/element/basic_body_temp_sensitive, 270, INFINITY)
AddComponent( \
/datum/component/squashable, \
squash_chance = 50, \
squash_damage = 1, \
squash_flags = SQUASHED_SHOULD_BE_GIBBED|SQUASHED_ALWAYS_IF_DEAD|SQUASHED_DONT_SQUASH_IN_CONTENTS, \
)
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
/mob/living/basic/cockroach/death(gibbed)
if(GLOB.station_was_nuked) //If the nuke is going off, then cockroaches are invincible. Keeps the nuke from killing them, cause cockroaches are immune to nukes.
return
return ..()
ADD_TRAIT(src, TRAIT_NUKEIMMUNE, INNATE_TRAIT)
ADD_TRAIT(src, TRAIT_RADIMMUNE, INNATE_TRAIT)
/mob/living/basic/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach.
return FALSE
// Roach goop is the gibs to drop
/mob/living/basic/cockroach/spawn_gibs()
return
/datum/ai_controller/basic_controller/cockroach
blackboard = list(
@@ -125,7 +133,13 @@
/mob/living/basic/cockroach/hauberoach/Initialize(mapload)
. = ..()
AddComponent(/datum/component/caltrop, min_damage = 10, max_damage = 15, flags = (CALTROP_BYPASS_SHOES | CALTROP_SILENT))
AddComponent(/datum/component/squashable, squash_chance = 100, squash_damage = 1, squash_callback = TYPE_PROC_REF(/mob/living/basic/cockroach/hauberoach, on_squish))
AddComponent( \
/datum/component/squashable, \
squash_chance = 100, \
squash_damage = 1, \
squash_flags = SQUASHED_SHOULD_BE_GIBBED|SQUASHED_ALWAYS_IF_DEAD|SQUASHED_DONT_SQUASH_IN_CONTENTS, \
squash_callback = TYPE_PROC_REF(/mob/living/basic/cockroach/hauberoach, on_squish), \
)
///Proc used to override the squashing behavior of the normal cockroach.
/mob/living/basic/cockroach/hauberoach/proc/on_squish(mob/living/cockroach, mob/living/living_target)
@@ -136,6 +150,7 @@
return TRUE
living_target.visible_message(span_notice("[living_target] squashes [cockroach], not even noticing its spike."), span_notice("You squashed [cockroach], not even noticing its spike."))
return FALSE
/datum/ai_controller/basic_controller/cockroach/hauberoach
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
@@ -65,9 +65,9 @@
if(stage < 6)
INVOKE_ASYNC(src, PROC_REF(RefreshInfectionImage))
var/slowdown = 1
if(ishuman(owner))
var/mob/living/carbon/human/baby_momma = owner
slowdown = baby_momma.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) ? 2 : 1 // spaceacillin doubles the time it takes to grow
if(!isnull(owner)) // it gestates out of bodies.
if(HAS_TRAIT(owner, TRAIT_VIRUS_RESISTANCE))
slowdown *= 2 // spaceacillin doubles the time it takes to grow
if(owner.has_status_effect(/datum/status_effect/nest_sustenance))
slowdown *= 0.80 //egg gestates 20% faster if you're trapped in a nest
@@ -16,7 +16,11 @@
var/siemens_coeff = 1 // resistance to shocks
var/stun_mod = 1 // % stun modifier
/// Multiplier applied to all incapacitating stuns (knockdown, stun, paralyze, immobilize)
var/stun_mod = 1
/// Multiplied aplpied to just knockdowns, stacks with above multiplicatively
var/knockdown_mod = 1
var/bleed_mod = 1 // % bleeding modifier
var/datum/armor/armor // internal armor datum
@@ -32,13 +32,6 @@
BODY_ZONE_CHEST = /obj/item/bodypart/chest/fly,
)
/datum/species/fly/handle_chemical(datum/reagent/chem, mob/living/carbon/human/affected, seconds_per_tick, times_fired)
. = ..()
if(. & COMSIG_MOB_STOP_REAGENT_CHECK)
return
if(chem.type == /datum/reagent/toxin/pestkiller)
affected.adjustToxLoss(3 * REM * seconds_per_tick)
/datum/species/fly/check_species_weakness(obj/item/weapon, mob/living/attacker)
if(istype(weapon, /obj/item/melee/flyswatter))
return 30 //Flyswatters deal 30x damage to flypeople.
@@ -46,19 +46,11 @@
return randname
/datum/species/moth/handle_chemical(datum/reagent/chem, mob/living/carbon/human/affected, seconds_per_tick, times_fired)
. = ..()
if(. & COMSIG_MOB_STOP_REAGENT_CHECK)
return
if(chem.type == /datum/reagent/toxin/pestkiller)
affected.adjustToxLoss(3 * REM * seconds_per_tick)
/datum/species/moth/check_species_weakness(obj/item/weapon, mob/living/attacker)
if(istype(weapon, /obj/item/melee/flyswatter))
return 10 //flyswatters deal 10x damage to moths
return 1
/datum/species/moth/randomize_features(mob/living/carbon/human/human_mob)
human_mob.dna.features["moth_markings"] = pick(GLOB.moth_markings_list)
randomize_external_organs(human_mob)
@@ -1,10 +1,10 @@
/mob/living/carbon/human/Stun(amount, ignore_canstun = FALSE)
amount = dna.species.spec_stun(src,amount)
amount = dna.species.spec_stun(src, amount)
return ..()
/mob/living/carbon/human/Knockdown(amount, ignore_canstun = FALSE)
amount = dna.species.spec_stun(src,amount)
amount = dna.species.spec_stun(src, amount) * physiology.knockdown_mod
return ..()
/mob/living/carbon/human/Paralyze(amount, ignore_canstun = FALSE)
@@ -16,7 +16,7 @@
return ..()
/mob/living/carbon/human/Unconscious(amount, ignore_canstun = FALSE)
amount = dna.species.spec_stun(src,amount)
amount = dna.species.spec_stun(src, amount)
if(HAS_TRAIT(src, TRAIT_HEAVY_SLEEPER))
amount *= (rand(125, 130) * 0.01)
return ..()
@@ -2002,17 +2002,18 @@
taste_description = "the pain of ten thousand slain mosquitos"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/ethanol/bug_spray/on_new(data)
. = ..()
AddElement(/datum/element/bugkiller_reagent)
/datum/reagent/consumable/ethanol/bug_spray/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired)
//Bugs should not drink Bug spray.
if(ismoth(drinker) || isflyperson(drinker))
drinker.adjustToxLoss(1 * REM * seconds_per_tick, FALSE, required_biotype = affected_biotype)
return ..()
/datum/reagent/consumable/ethanol/bug_spray/on_mob_metabolize(mob/living/carbon/drinker)
if(ismoth(drinker) || isflyperson(drinker))
// Does some damage to bug biotypes
var/did_damage = drinker.adjustToxLoss(1 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = MOB_BUG)
// Random chance of causing a screm if we did some damage
if(did_damage && SPT_PROB(2, seconds_per_tick))
drinker.emote("scream")
return ..()
return ..() || did_damage
/datum/reagent/consumable/ethanol/applejack
name = "Applejack"
@@ -261,6 +261,14 @@
ph = 8.1
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/medicine/spaceacillin/on_mob_metabolize(mob/living/L)
. = ..()
ADD_TRAIT(L, TRAIT_VIRUS_RESISTANCE, type)
/datum/reagent/medicine/spaceacillin/on_mob_end_metabolize(mob/living/L)
. = ..()
REMOVE_TRAIT(L, TRAIT_VIRUS_RESISTANCE, type)
//Goon Chems. Ported mainly from Goonstation. Easily mixable (or not so easily) and provide a variety of effects.
/datum/reagent/medicine/oxandrolone
@@ -401,17 +401,19 @@
ph = 3.2
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/toxin/pestkiller/on_new(data)
. = ..()
AddElement(/datum/element/bugkiller_reagent)
/datum/reagent/toxin/pestkiller/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = affected_mob.adjustToxLoss(2 * toxpwr * REM * seconds_per_tick, updating_health = FALSE, required_biotype = MOB_BUG)
return ..() || .
//Pest Spray
/datum/reagent/toxin/pestkiller/on_hydroponics_apply(obj/machinery/hydroponics/mytray, mob/user)
mytray.adjust_toxic(round(volume))
mytray.adjust_pestlevel(-rand(1,2))
/datum/reagent/toxin/pestkiller/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume)
. = ..()
if(exposed_mob.mob_biotypes & MOB_BUG)
var/damage = min(round(0.4*reac_volume, 0.1),10)
exposed_mob.adjustToxLoss(damage, required_biotype = affected_biotype)
/datum/reagent/toxin/pestkiller/organic
name = "Natural Pest Killer"
description = "An organic mixture used to kill pests, with less of the side effects. Do not ingest!"
View File
+1 -1
View File
@@ -30,7 +30,7 @@
return
// spaceacillin has a 75% chance to block infection
if(istype(target) && target.reagents.has_reagent(/datum/reagent/medicine/spaceacillin) && prob(75))
if(HAS_TRAIT(target, TRAIT_VIRUS_RESISTANCE) && prob(75))
return
var/obj/item/organ/internal/zombie_infection/infection
Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

+2
View File
@@ -1175,6 +1175,7 @@
#include "code\datums\elements\befriend_petting.dm"
#include "code\datums\elements\blocks_explosives.dm"
#include "code\datums\elements\bsa_blocker.dm"
#include "code\datums\elements\bugkiller_reagent.dm"
#include "code\datums\elements\bump_click.dm"
#include "code\datums\elements\can_barricade.dm"
#include "code\datums\elements\caseless.dm"
@@ -1691,6 +1692,7 @@
#include "code\game\machinery\dna_infuser\organ_sets\goliath_organs.dm"
#include "code\game\machinery\dna_infuser\organ_sets\gondola_organs.dm"
#include "code\game\machinery\dna_infuser\organ_sets\rat_organs.dm"
#include "code\game\machinery\dna_infuser\organ_sets\roach_organs.dm"
#include "code\game\machinery\doors\airlock.dm"
#include "code\game\machinery\doors\airlock_electronics.dm"
#include "code\game\machinery\doors\airlock_types.dm"
@@ -28,7 +28,7 @@ type TierData = {
name: string;
};
const PAGE_HEIGHT = '235px';
const PAGE_HEIGHT = 30;
const TIER2TIERDATA: TierData[] = [
{
@@ -69,7 +69,7 @@ const TIER2TIERDATA: TierData[] = [
];
export const InfuserBook = (props, context) => {
const { data } = useBackend<DnaInfuserData>(context);
const { data, act } = useBackend<DnaInfuserData>(context);
const { entries } = data;
const [bookPosition, setBookPosition] = useLocalState<BookPosition>(
@@ -87,7 +87,11 @@ export const InfuserBook = (props, context) => {
let currentEntry = paginatedEntries[chapter][pageInChapter];
const switchChapter = (newChapter) => {
if (chapter === newChapter) {
return;
}
setBookPosition({ chapter: newChapter, pageInChapter: 0 });
act('play_flip_sound'); // just so we can play a sound fx on page turn
};
const setPage = (newPage) => {
@@ -112,6 +116,7 @@ export const InfuserBook = (props, context) => {
newBookPosition.pageInChapter = newPage;
}
setBookPosition(newBookPosition);
act('play_flip_sound'); // just so we can play a sound fx on page turn
};
const tabs = [
@@ -127,7 +132,7 @@ export const InfuserBook = (props, context) => {
const restrictedNext = chapter === 3 && pageInChapter === 0;
return (
<Window title="DNA Infusion Manual" width={620} height={375}>
<Window title="DNA Infusion Manual" width={620} height={500}>
<Window.Content>
<Stack vertical>
<Stack.Item mb={-1}>
@@ -215,7 +220,7 @@ export const InfuserInstructions = (props, context) => {
<br />
3. Have someone activate the machine externally.
<br />
<Box inline color="white">
<Box mt="10px" inline color="white">
And you&apos;re done! Note that the infusion source will be
obliterated in the process.
</Box>