diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm index 57dc338d3ebc3..44e1a19c9c187 100644 --- a/code/__DEFINES/atom_hud.dm +++ b/code/__DEFINES/atom_hud.dm @@ -47,6 +47,10 @@ #define DIAG_CAMERA_HUD "22" /// Steady Hacked APC effect, visible only to Malf AIs #define MALF_APC_HUD "23" +// DOPPLER ADDITION BEGIN +/// If they have the implanted quirk +#define SEC_IMPLANT_HUD "25" +// DOPPLER ADDITION END //by default everything in the hud_list of an atom is an image //a value in hud_list with one of these will change that behavior diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm index ffba025270e42..2ea30bbeaffbb 100644 --- a/code/__DEFINES/inventory.dm +++ b/code/__DEFINES/inventory.dm @@ -112,6 +112,10 @@ DEFINE_BITFIELD(no_equip_flags, list( #define HIDEBELT (1<<14) ///hides antennae #define HIDEANTENNAE (1<<15) +//DOPPLER ADDITION START +#define HIDETAIL (1<<16) +#define HIDEHORNS (1<<17) +//DOPPLER ADDITION END //bitflags for clothing coverage - also used for limbs #define HEAD (1<<0) diff --git a/code/__DEFINES/text.dm b/code/__DEFINES/text.dm index 3b5cb5d795011..1511bba2c0f2c 100644 --- a/code/__DEFINES/text.dm +++ b/code/__DEFINES/text.dm @@ -94,6 +94,10 @@ #define FLESH_SCAR_FILE "wounds/flesh_scar_desc.json" /// File location for bone wound descriptions #define BONE_SCAR_FILE "wounds/bone_scar_desc.json" +// DOPPLER ADDITION BEGIN - SYNTH WOUNDS +/// File location for metalic wound descriptions +#define METAL_SCAR_FILE "wounds/metal_scar_desc.json" +// DOPPLER ADDITION END /// File location for scar wound descriptions #define SCAR_LOC_FILE "wounds/scar_loc.json" /// File location for exodrone descriptions diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index 79dfb13edfcfc..8c63a5584dcb1 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -128,11 +128,15 @@ GLOBAL_LIST_INIT(bio_state_anatomy, list( /// Cranial fissure wound. #define WOUND_SERIES_CRANIAL_FISSURE "wound_series_cranial_fissure" -// NOVA EDIT ADDITION BEGIN - MUSCLE AND SYNTH WOUNDS +// DOPPLER ADDITION BEGIN - synth wounds // Have to put it here so I can use it in the global list of wound series /// See muscle.dm and robotic_blunt.dm #define WOUND_SERIES_MUSCLE_DAMAGE "nova_wound_series_muscle_damage" - +#define WOUND_SERIES_METAL_BLUNT_BASIC "wound_series_metal_blunt_basic" +#define WOUND_SERIES_METAL_BURN_OVERHEAT "wound_series_metal_burn_basic" +#define WOUND_SERIES_WIRE_SLASH_ELECTRICAL_DAMAGE "wound_series_metal_slash_electrical_damage_basic" +#define WOUND_SERIES_WIRE_PIERCE_ELECTRICAL_DAMAGE "wound_series_metal_pierce_electrical_damage_basic" +// DOPPLER ADDITION END /// A assoc list of (wound typepath -> wound_pregen_data instance). Every wound should have a pregen data. GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate_wound_static_data()) @@ -284,7 +288,8 @@ GLOBAL_LIST_INIT(wounding_types_to_series, list( /// Assoc list of biotype -> ideal scar file to be used and grab stuff from. GLOBAL_LIST_INIT(biotypes_to_scar_file, list( "[BIO_FLESH]" = FLESH_SCAR_FILE, - "[BIO_BONE]" = BONE_SCAR_FILE + "[BIO_BONE]" = BONE_SCAR_FILE, + "[BIO_METAL]" = METAL_SCAR_FILE // DOPPLER EDIT ADDITION - METAL SCARS (see robotic_blunt.dm) )) // ~burn wound infection defines diff --git a/code/__DEFINES/~doppler_defines/declarations.dm b/code/__DEFINES/~doppler_defines/declarations.dm deleted file mode 100644 index 88fbb381b8a2c..0000000000000 --- a/code/__DEFINES/~doppler_defines/declarations.dm +++ /dev/null @@ -1,10 +0,0 @@ -/// Trait that changes the ending effects of twitch leaving your system -#define TRAIT_TWITCH_ADAPTED "twitch_adapted" -/// Given to the detective, if they have this, they can see syndicate special descriptions. -#define TRAIT_DETECTIVE "detective_ability" -/// Trait for the excitable quirk, woof! -#define TRAIT_EXCITABLE "wagwag" -/// Trait for hemophages particularly! -#define TRAIT_OXYIMMUNE "oxyimmune" // Immune to oxygen damage, ideally give this to all non-breathing species or bad stuff will happen -// Trait for extra language point. -#define TRAIT_LINGUIST "Linguist" diff --git a/code/__DEFINES/~doppler_defines/is_helpers.dm b/code/__DEFINES/~doppler_defines/is_helpers.dm index b5acc5258dd11..606790c68fee3 100644 --- a/code/__DEFINES/~doppler_defines/is_helpers.dm +++ b/code/__DEFINES/~doppler_defines/is_helpers.dm @@ -5,5 +5,6 @@ #define issnail(A) (is_species(A, /datum/species/snail)) #define ishemophage(A) (is_species(A, /datum/species/genemod/hemophage)) #define isramatan(A) (is_species(A, /datum/species/ramatan)) -//Species with green blood -#define hasgreenblood(A) (isinsectoid(A) || issnail(A) || HAS_TRAIT(A, TRAIT_GREEN_BLOOD)) +//Species blood colors +#define hasgreenblood(A) (isinsectoid(A) || issnail(A) || isflyperson(A) || isalien(A) || HAS_TRAIT(A, TRAIT_GREEN_BLOOD)) +#define hasblueblood(A) (isandroid(A) || HAS_TRAIT(A, TRAIT_BLUE_BLOOD)) diff --git a/code/__DEFINES/~doppler_defines/mutant_blacklists.dm b/code/__DEFINES/~doppler_defines/mutant_blacklists.dm new file mode 100644 index 0000000000000..d0e8c707ab1aa --- /dev/null +++ b/code/__DEFINES/~doppler_defines/mutant_blacklists.dm @@ -0,0 +1,8 @@ +GLOBAL_LIST_INIT(species_blacklist_no_mutant, list( + /datum/species/human, + )) + +GLOBAL_LIST_INIT(species_blacklist_no_humanoid, list( + /datum/species/golem, + /datum/species/genemod/primitive, + )) diff --git a/code/__DEFINES/~doppler_defines/mutant_variations.dm b/code/__DEFINES/~doppler_defines/mutant_variations.dm index df917caf41a24..4cc24ddc70254 100644 --- a/code/__DEFINES/~doppler_defines/mutant_variations.dm +++ b/code/__DEFINES/~doppler_defines/mutant_variations.dm @@ -12,7 +12,7 @@ #define MONKEY "monkey" #define DEER "deer" #define BUG "bug" -#define SYNTHETIC "synthetic" +#define CYBERNETIC "cybernetic" #define HUMANOID "humanoid" #define ALIEN "alien" @@ -30,7 +30,7 @@ GLOBAL_LIST_INIT(mutant_variations, list( LIZARD, MONKEY, MOUSE, - SYNTHETIC, + CYBERNETIC, ALIEN, )) diff --git a/code/__DEFINES/~doppler_defines/quirks.dm b/code/__DEFINES/~doppler_defines/quirks.dm new file mode 100644 index 0000000000000..31575956e5c6d --- /dev/null +++ b/code/__DEFINES/~doppler_defines/quirks.dm @@ -0,0 +1,6 @@ +/// the minimum percentage of RGB darkness reduction that Nova's NV will always give. for base reference, the old NV quirk was equal to 4.5. note: some wide variance in min/max is needed to ensure hue has some chance to linear convert across +#define DOPPLER_NIGHT_VISION_POWER_MIN 4.5 +/// the maximum percentage. for reference, low-light adapted eyes (icecats and ashwalkers) have 30. +#define DOPPLER_NIGHT_VISION_POWER_MAX 9 +/// percentage of the NIGHT_VISION_POWER_MAX increase that is applied for eyes with low innate flash protection (photophobia quirk/moth eyes). At 0.75, this raises NV to 22.5 at hypersensitive flash_protect. +#define DOPPLER_NIGHT_VISION_SENSITIVITY_MULT 0.75 diff --git a/code/__DEFINES/~doppler_defines/signals.dm b/code/__DEFINES/~doppler_defines/signals.dm index 7c267ce6e0ea1..b35a9f28a4c4b 100644 --- a/code/__DEFINES/~doppler_defines/signals.dm +++ b/code/__DEFINES/~doppler_defines/signals.dm @@ -15,3 +15,5 @@ #define COMSIG_PULSATING_TUMOR_REMOVED "pulsating_tumor_removed" /// From /obj/item/organ/internal/stomach/after_eat(atom/edible) #define COMSIG_STOMACH_AFTER_EAT "stomach_after_eat" +/// Whenever a baton successfully executes its nonlethal attack. WARNING wonderful FUCKING CODE by niko THIS IS peak AAAAAAAAAAAAH +#define COMSIG_PRE_BATON_FINALIZE_ATTACK "pre_baton_finalize_attack" diff --git a/code/__DEFINES/~doppler_defines/traits.dm b/code/__DEFINES/~doppler_defines/traits.dm index 5abb9806d6d8f..718af41a3e7db 100644 --- a/code/__DEFINES/~doppler_defines/traits.dm +++ b/code/__DEFINES/~doppler_defines/traits.dm @@ -1,20 +1,58 @@ +//// +// Skills +//// /// trait that lets you do xenoarch magnification #define TRAIT_XENOARCH_QUALIFIED "trait_xenoarch_qualified" - /// Traits granted by glassblowing #define TRAIT_GLASSBLOWING "glassblowing" - /// Trait that is applied whenever someone or something is glassblowing #define TRAIT_CURRENTLY_GLASSBLOWING "currently_glassblowing" +//// +// Species +//// +/// Trait for hemophages particularly! +#define TRAIT_OXYIMMUNE "oxyimmune" // Immune to oxygen damage, ideally give this to all non-breathing species or bad stuff will happen +/// Trait that defines if an android species type is charging their cell +#define TRAIT_CHARGING "charging" /// Trait which lets species pick from a list of animal traits, used by genemod + subtypes and anthromorphs #define TRAIT_ANIMALISTIC "animalistic" - // Green blood traits #define TRAIT_GREEN_BLOOD "green_blood" - +// Blue blood traits +#define TRAIT_BLUE_BLOOD "blue_blood" // Trait that lets golems put stone limbs back on #define TRAIT_GOLEM_LIMBATTACHMENT "golem_limbattachment" +//// +// Quirks +//// +/// Trait for extra language point. +#define TRAIT_LINGUIST "linguist" +/// Trait for the excitable quirk, woof! +#define TRAIT_EXCITABLE "wagwag" +/// Trait for if you are left handed +#define TRAIT_LEFT_HANDED "left_handed" +/// Trait for people with the cybernetic quirk +#define TRAIT_PERMITTED_CYBERNETIC "permitted_cybernetic" + +//// +// Jobs +//// +/// Given to the detective, if they have this, they can see syndicate special descriptions. +#define TRAIT_DETECTIVE "detective_ability" + +//// +// Reagents +//// /// Trait that was granted by a reagent. #define REAGENT_TRAIT "reagent" +/// Trait that changes the ending effects of twitch leaving your system +#define TRAIT_TWITCH_ADAPTED "twitch_adapted" + +//// +// Wounds +//// +/// When someone is fixing electrical damage, this trait is set and prevents the wound from worsening. +// We use a trait to avoid erronous setting of a variable to false if two people are repairing and one stops. +#define TRAIT_ELECTRICAL_DAMAGE_REPAIRING "electrical_damage_repairing" diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 45afc0e0c85a6..d1c8ae6a36544 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -206,6 +206,10 @@ DEFINE_BITFIELD(flags_inv, list( "HIDESHOES" = HIDESHOES, "HIDESNOUT" = HIDESNOUT, "HIDESUITSTORAGE" = HIDESUITSTORAGE, + // DOPPLER ADDITION START + "HIDETAIL" = HIDETAIL, + "HIDEHORNS" = HIDEHORNS, + // DOPPLER ADDITION END )) DEFINE_BITFIELD(machine_stat, list( diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index dafb51d8d6ef1..3a6768f06c34e 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -116,6 +116,11 @@ GLOBAL_LIST_INIT(traits_by_type, list( /datum/wound = list( "TRAIT_WOUND_SCANNED" = TRAIT_WOUND_SCANNED, ), + // DOPPLER ADDITON BEGIN + /datum/wound/electrical_damage = list( + "TRAIT_ELECTRICAL_DAMAGE_REPAIRING" = TRAIT_ELECTRICAL_DAMAGE_REPAIRING, + ), + // DOPPLER ADDITON END /obj = list( "TRAIT_WALLMOUNTED" = TRAIT_WALLMOUNTED, "TRAIT_CONTRABAND" = TRAIT_CONTRABAND, @@ -700,6 +705,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_TWITCH_ADAPTED" = TRAIT_TWITCH_ADAPTED, "TRAIT_GOLEM_LIMBATTACHMENT" = TRAIT_GOLEM_LIMBATTACHMENT, "TRAIT_LINGUIST" = TRAIT_LINGUIST, + "TRAIT_PERMITTED_CYBERNETIC" = TRAIT_PERMITTED_CYBERNETIC, + "TRAIT_CHARGING" = TRAIT_CHARGING, + "TRAIT_LEFT_HANDED" = TRAIT_LEFT_HANDED, ), // DOPPLER EDIT ADDITION END )) diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 8447ca019c1b1..ebeac9c899904 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -382,7 +382,10 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_DETECTIVE" = TRAIT_DETECTIVE, "TRAIT_EXCITABLE" = TRAIT_EXCITABLE, "TRAIT_TWITCH_ADAPTED" = TRAIT_TWITCH_ADAPTED, - "TRAIT_LINGUIST" = TRAIT_LINGUIST + "TRAIT_LINGUIST" = TRAIT_LINGUIST, + "TRAIT_PERMITTED_CYBERNETIC" = TRAIT_PERMITTED_CYBERNETIC, + "TRAIT_CHARGING" = TRAIT_CHARGING, + "TRAIT_LEFT_HANDED" = TRAIT_LEFT_HANDED, ), // DOPPLER EDIT ADDITION END )) diff --git a/code/datums/components/jousting.dm b/code/datums/components/jousting.dm index 6d11e20b3a3dd..fae4499e5e38b 100644 --- a/code/datums/components/jousting.dm +++ b/code/datums/components/jousting.dm @@ -115,6 +115,8 @@ target.Paralyze(knockdown_time) user.visible_message(span_danger("[msg]!")) + return usable_charge // DOPPLER EDIT ADDITION - Baton jousting + /** * Called when a mob moves. * Handles checking their direction, changing it if they turned, diff --git a/code/datums/quirks/negative_quirks/photophobia.dm b/code/datums/quirks/negative_quirks/photophobia.dm index b543aeda07631..4388c3da361c4 100644 --- a/code/datums/quirks/negative_quirks/photophobia.dm +++ b/code/datums/quirks/negative_quirks/photophobia.dm @@ -40,13 +40,15 @@ /datum/quirk/photophobia/proc/update_eyes(obj/item/organ/internal/eyes/target_eyes) if(!istype(target_eyes)) return - target_eyes.flash_protect = max(target_eyes.flash_protect - 1, FLASH_PROTECTION_HYPER_SENSITIVE) + target_eyes.flash_protect = max(target_eyes.flash_protect - severity, FLASH_PROTECTION_HYPER_SENSITIVE) // DOPPLER EDIT CHANGE - ORIGINAL: target_eyes.flash_protect = max(target_eyes.flash_protect - 1, FLASH_PROTECTION_HYPER_SENSITIVE) + target_eyes.refresh() // DOPPLER EDIT ADDITION /datum/quirk/photophobia/proc/restore_eyes(obj/item/organ/internal/eyes/normal_eyes) SIGNAL_HANDLER if(!istype(normal_eyes)) return normal_eyes.flash_protect = initial(normal_eyes.flash_protect) + normal_eyes.refresh() // DOPPLER EDIT ADDITION /datum/quirk/photophobia/proc/on_holder_moved(mob/living/source, atom/old_loc, dir, forced) SIGNAL_HANDLER diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm index 3e8c49c2d5944..8c646c9536fa2 100644 --- a/code/datums/wounds/bones.dm +++ b/code/datums/wounds/bones.dm @@ -162,6 +162,8 @@ // DOPPLER EDIT ADDITION BEGIN - Green blood color if(hasgreenblood(victim)) new /obj/effect/temp_visual/dir_setting/bloodsplatter/green(victim.loc, victim.dir) + if(hasblueblood(victim)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/blue(victim.loc, victim.dir) else new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) // DOPPLER EDIT ADDITION END @@ -176,6 +178,8 @@ // DOPPLER EDIT ADDITION BEGIN - Green blood color if(hasgreenblood(victim)) new /obj/effect/temp_visual/dir_setting/bloodsplatter/green(victim.loc, victim.dir) + if(hasblueblood(victim)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/blue(victim.loc, victim.dir) else new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) // DOPPLER EDIT ADDITION END diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 58cc789430ae0..4aae21dd7f14b 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -49,7 +49,7 @@ hud_icons = list(ID_HUD) /datum/atom_hud/data/human/security/advanced - hud_icons = list(ID_HUD, IMPSEC_FIRST_HUD, IMPLOYAL_HUD, IMPSEC_SECOND_HUD, WANTED_HUD) + hud_icons = list(ID_HUD, IMPSEC_FIRST_HUD, IMPLOYAL_HUD, IMPSEC_SECOND_HUD, WANTED_HUD, SEC_IMPLANT_HUD) // DOPPLER EDIT, old code: hud_icons = list(ID_HUD, IMPSEC_FIRST_HUD, IMPLOYAL_HUD, IMPSEC_SECOND_HUD, WANTED_HUD) /datum/atom_hud/data/human/fan_hud hud_icons = list(FAN_HUD) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index adac393d7bedb..902c9c38141f5 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -44,6 +44,8 @@ . += create_table_notices(list( "name", "job", + "charge", //DOPPLER EDIT ADDITION + "is_robot", //DOPPLER EDIT ADDITION "life_status", "suffocation", "toxin", @@ -64,6 +66,8 @@ var/list/entry = list() entry["name"] = player_record["name"] entry["job"] = player_record["assignment"] + entry["charge"] = player_record["charge"] // DOPPLER EDIT ADDITION + entry["is_robot"] = player_record["is_robot"] // DOPPLER EDIT ADDITION entry["life_status"] = player_record["life_status"] entry["suffocation"] = player_record["oxydam"] entry["toxin"] = player_record["toxdam"] @@ -245,6 +249,13 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) if (jobs[trim_assignment] != null) entry["ijob"] = jobs[trim_assignment] + // DOPPLER EDIT ADDITION START + if (isandroid(tracked_human)) + var/datum/species/android/energy_holder = tracked_human.dna.species + entry["is_robot"] = TRUE + entry["charge"] = "[round((energy_holder.core_energy/1000000), 0.1)]MJ" + // DOPPLER EDIT ADDITION END + // Broken sensors show garbage data if (uniform.has_sensor == BROKEN_SENSORS) entry["life_status"] = rand(0,1) diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 753c94dced2c9..808357df1d8d3 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -442,6 +442,8 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache) var/obj/effect/decal/cleanable/final_splatter if(istype(src, /obj/effect/decal/cleanable/blood/hitsplatter/green)) final_splatter = new /obj/effect/decal/cleanable/blood/green/splatter/over_window(prev_loc) + if(istype(src, /obj/effect/decal/cleanable/blood/hitsplatter/blue)) + final_splatter = new /obj/effect/decal/cleanable/blood/blue/splatter/over_window(prev_loc) else final_splatter = new /obj/effect/decal/cleanable/blood/splatter/over_window(prev_loc) // DOPPLER EDIT CHANGE END @@ -459,6 +461,8 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache) var/obj/effect/decal/cleanable/final_splatter if(istype(src, /obj/effect/decal/cleanable/blood/hitsplatter/green)) final_splatter = new /obj/effect/decal/cleanable/blood/green/splatter/over_window(prev_loc) + if(istype(src, /obj/effect/decal/cleanable/blood/hitsplatter/blue)) + final_splatter = new /obj/effect/decal/cleanable/blood/blue/splatter/over_window(prev_loc) else final_splatter = new /obj/effect/decal/cleanable/blood/splatter/over_window(prev_loc) // DOPPLER EDIT CHANGE END diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 03c79049b3087..808b37c54722e 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -978,7 +978,7 @@ return ITEM_INTERACT_BLOCKING if(check_empty(user)) return ITEM_INTERACT_BLOCKING - + /* DOPPLER EDIT REMOVAL START if(isbodypart(interacting_with) && actually_paints) var/obj/item/bodypart/limb = interacting_with if(!IS_ORGANIC_LIMB(limb)) @@ -994,6 +994,7 @@ playsound(user.loc, 'sound/effects/spray.ogg', 5, TRUE, 5) limb.change_appearance(style_list_icons[choice], greyscale = FALSE) return ITEM_INTERACT_SUCCESS + DOPPLER EDIT REMOVAL END */ if(interacting_with.color) paint_color = interacting_with.color balloon_alert(user, "matched colour of target") diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm index 01e5983b7d7aa..d54601d8d7817 100644 --- a/code/game/objects/items/melee/baton.dm +++ b/code/game/objects/items/melee/baton.dm @@ -27,7 +27,7 @@ /// The length of the knockdown applied to the user on clumsy_check() var/clumsy_knockdown_time = 18 SECONDS /// How much stamina damage we deal on a successful hit against a living, non-cyborg mob. - var/stamina_damage = 55 + var/stamina_damage = 35 // DOPPLER EDIT - Less Stamina Damage (Original: 55) /// Chance of causing force_say() when stunning a human mob var/force_say_chance = 33 /// Can we stun cyborgs? @@ -185,6 +185,7 @@ if(!in_attack_chain && HAS_TRAIT_FROM(target, TRAIT_IWASBATONED, REF(user))) return BATON_ATTACK_DONE + SEND_SIGNAL(src, COMSIG_PRE_BATON_FINALIZE_ATTACK, target, user, modifiers, in_attack_chain) // DOPPLER EDIT ADDITION cooldown_check = world.time + cooldown if(on_stun_sound) playsound(get_turf(src), on_stun_sound, on_stun_volume, TRUE, -1) @@ -404,7 +405,7 @@ force = 5 cooldown = 2.5 SECONDS force_say_chance = 80 //very high force say chance because it's funny - stamina_damage = 85 + stamina_damage = 115 // DOPPLER EDIT: Original 85 clumsy_knockdown_time = 24 SECONDS affect_cyborg = TRUE on_stun_sound = 'sound/items/weapons/contractor_baton/contractorbatonhit.ogg' @@ -438,7 +439,7 @@ armor_type = /datum/armor/baton_security throwforce = 7 force_say_chance = 50 - stamina_damage = 60 + stamina_damage = 35 // DOPPLER EDIT - 4 baton crit now (Original: 60) knockdown_time = 5 SECONDS clumsy_knockdown_time = 15 SECONDS cooldown = 2.5 SECONDS @@ -655,7 +656,7 @@ */ /obj/item/melee/baton/security/additional_effects_non_cyborg(mob/living/target, mob/living/user) target.set_jitter_if_lower(40 SECONDS) - target.set_confusion_if_lower(10 SECONDS) + // target.set_confusion_if_lower(10 SECONDS) // DOPPLER EDIT REMOVAL target.set_stutter_if_lower(16 SECONDS) SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index a7415c951f2f9..f01fe56431d94 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -277,6 +277,10 @@ new /obj/item/reagent_containers/blood/o_plus(src) new /obj/item/reagent_containers/blood/lizard(src) new /obj/item/reagent_containers/blood/ethereal(src) + // DOPPLER ADDITION START + new /obj/item/reagent_containers/blood/robot(src) + new /obj/item/reagent_containers/blood/bug(src) + // DOPPLER ADDITION END for(var/i in 1 to 3) new /obj/item/reagent_containers/blood/random(src) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 5e848879d01f7..d8af96204be5f 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -307,6 +307,17 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Quirks all_quirks = save_data?["all_quirks"] + /// DOPPLER SHIFT ADDITION BEGIN + var/list/save_languages = SANITIZE_LIST(save_data["languages"]) + for(var/language in save_languages) + var/value = save_languages[language] + save_languages -= language + + if(istext(language)) + language = _text2path(language) + save_languages[language] = value + languages = save_languages + /// DOPPLER SHIFT ADDITION END //try to fix any outdated data if necessary //preference updating will handle saving the updated data for us. @@ -317,6 +328,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car randomise = SANITIZE_LIST(randomise) job_preferences = SANITIZE_LIST(job_preferences) all_quirks = SANITIZE_LIST(all_quirks) + languages = SANITIZE_LIST(languages) /// DOPPLER SHIFT ADDITION //Validate job prefs for(var/j in job_preferences) @@ -325,6 +337,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car all_quirks = SSquirks.filter_invalid_quirks(SANITIZE_LIST(all_quirks)) validate_quirks() + sanitize_languages() /// DOPPLER SHIFT ADDITION return TRUE @@ -365,6 +378,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Quirks save_data["all_quirks"] = all_quirks + save_data["languages"] = languages /// DOPPLER SHIFT ADDITION - we might want to migrate this return TRUE diff --git a/code/modules/events/ghost_role/space_ninja.dm b/code/modules/events/ghost_role/space_ninja.dm index eaccbe3de39cb..14bcd03976a6f 100644 --- a/code/modules/events/ghost_role/space_ninja.dm +++ b/code/modules/events/ghost_role/space_ninja.dm @@ -23,10 +23,20 @@ if(isnull(chosen_one)) return NOT_ENOUGH_PLAYERS //spawn the ninja and assign the candidate + // DOPPLER ADDITION START - Preference Ninjas + var/loadme = tgui_alert(chosen_one, "Do you wish to load your character slot?", "Load Character?", list("Yes!", "No, I want to be random!"), timeout = 60 SECONDS) + // DOPPLER ADDITION END var/mob/living/carbon/human/ninja = create_space_ninja(spawn_location) ninja.key = chosen_one.key ninja.mind.add_antag_datum(/datum/antagonist/ninja) spawned_mobs += ninja + // DOPPLER ADDITION START - Preference Ninjas + if(loadme == "Yes!") + ninja.client?.prefs?.safe_transfer_prefs_to(ninja) + ninja.dna.update_dna_identity() + + SSquirks.AssignQuirks(ninja, ninja.client) + // DOPPLER ADDITION END message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a space ninja by an event.") ninja.log_message("was spawned as a ninja by an event.", LOG_GAME) diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm index f0b2f089cb6e7..67273c49d7eb3 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm @@ -56,6 +56,10 @@ search_range = (mode_flags & MEDBOT_STATIONARY_MODE) ? 1 : initial(search_range) var/list/ignore_keys = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] for(var/mob/living/carbon/human/treatable_target in oview(search_range, controller.pawn)) + // DOPPLER EDIT ADDITION START + if(treatable_target.mob_biotypes & MOB_ROBOTIC) + continue + // DOPPLER EDIT ADDITION END if(LAZYACCESS(ignore_keys, treatable_target) || treatable_target.stat == DEAD) continue if((access_flags & BOT_COVER_EMAGGED) && treatable_target.stat == CONSCIOUS) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index dfe669a7092ed..ee76e7a503b13 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -361,7 +361,7 @@ //to add a splatter of blood or other mob liquid. /mob/living/proc/add_splatter_floor(turf/T, small_drip) - if(get_blood_id() != /datum/reagent/blood && !hasgreenblood(src)) // DOPPLER EDIT CHANGE START - ORIGINAL: if(get_blood_id() != /datum/reagent/blood) + if(get_blood_id() != /datum/reagent/blood && !hasgreenblood(src) && !hasblueblood(src)) // DOPPLER EDIT CHANGE START - ORIGINAL: if(get_blood_id() != /datum/reagent/blood) return if(!T) T = get_turf(src) @@ -385,7 +385,9 @@ // DOPPLER EDIT CHANGE START - ORIGINAL: drop = new(T, get_static_viruses()) if(hasgreenblood(src)) drop = new /obj/effect/decal/cleanable/blood/drip/green(T, get_static_viruses()) - else + if(hasblueblood(src)) + drop = new /obj/effect/decal/cleanable/blood/drip/blue(T, get_static_viruses()) + if(!hasblueblood(src) && !hasgreenblood(src)) drop = new(T, get_static_viruses()) // DOPPLER EDIT END drop.transfer_mob_blood_dna(src) @@ -397,7 +399,9 @@ // DOPPLER EDIT CHANGE START - ORIGINAL: B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) if(hasgreenblood(src)) B = new /obj/effect/decal/cleanable/blood/green/splatter(T, get_static_viruses()) - else + if(hasblueblood(src)) + B = new /obj/effect/decal/cleanable/blood/blue/splatter(T, get_static_viruses()) + if(!hasblueblood(src) && !hasgreenblood(src)) B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) // DOPPLER EDIT END if(QDELETED(B)) //Give it up diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 47ceecd8a009d..19f83e6625d58 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1444,6 +1444,8 @@ var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter if(hasgreenblood(src)) our_splatter = new /obj/effect/decal/cleanable/blood/hitsplatter/green(loc) + if(hasblueblood(src)) + our_splatter = new /obj/effect/decal/cleanable/blood/hitsplatter/blue(loc) else our_splatter = new /obj/effect/decal/cleanable/blood/hitsplatter(loc) // DOPPLER EDIT CHANGE END diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index f6ac6e8dc2ffe..1049a0bbf9aa4 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -1,6 +1,6 @@ /mob/living/carbon/update_obscured_slots(obscured_flags) ..() - if(obscured_flags & (HIDEEARS|HIDEEYES|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT|HIDEMUTWINGS)) + if(obscured_flags & (HIDEEARS|HIDEEYES|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT|HIDEMUTWINGS|HIDETAIL|HIDEHORNS)) // DOPPLER EDIT, old code: if(obscured_flags & (HIDEEARS|HIDEEYES|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT|HIDEMUTWINGS)) update_body() /// Updates features and clothing attached to a specific limb with limb-specific offsets diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index dd97c12bd844f..f2e0edd7e15f3 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -8,6 +8,14 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift) /mob/living/carbon/human/spawn_gibs(drop_bitflags=NONE) if(flags_1 & HOLOGRAM_1) return + // DOPPLER ADDITION START + if(isandroid(src)) + new /obj/effect/gibspawner/robot(drop_location(), src, get_static_viruses()) + return + if(hasgreenblood(src)) + new /obj/effect/gibspawner/xeno/bodypartless(drop_location(), src, get_static_viruses()) + return + // DOPPLER ADDITION END if(drop_bitflags & DROP_BODYPARTS) new /obj/effect/gibspawner/human(drop_location(), src, get_static_viruses()) else diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index c63c99b8da537..92faf0b1cdf38 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -5,7 +5,7 @@ icon = 'icons/mob/human/human.dmi' icon_state = "human_basic" appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE - hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPSEC_FIRST_HUD,IMPSEC_SECOND_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD) + hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPSEC_FIRST_HUD,IMPSEC_SECOND_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD,SEC_IMPLANT_HUD) // DOPPLER EDIT, old code: hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPSEC_FIRST_HUD,IMPSEC_SECOND_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD) hud_type = /datum/hud/human pressure_resistance = 25 can_buckle = TRUE diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index b46bb5d153151..5bb933cb23cde 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -330,13 +330,17 @@ var/splatter_dir = dir if(starting) splatter_dir = get_dir(starting, target_turf) - if(isalien(living_target) || hasgreenblood(living_target)) // DOPPLER EDIT CHANGE - Green blood color - Original line: if(isalien(living_target)) - new /obj/effect/temp_visual/dir_setting/bloodsplatter/green(target_turf, splatter_dir) // DOPPLER EDIT CHANGE - Green blood color - Original line: new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target_turf, splatter_dir) - else + // DOPPLER ADDITION START + if(hasblueblood(living_target)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/blue(target_turf, splatter_dir) + if(hasgreenblood(living_target)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/green(target_turf, splatter_dir) + if(!hasblueblood(living_target) && !hasgreenblood(living_target)) new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_turf, splatter_dir) + // DOPPLER ADDITION END if(prob(33)) living_target.add_splatter_floor(target_turf) - else if (hit_bodypart?.biological_state & (BIO_METAL|BIO_WIRED)) + if (hit_bodypart?.biological_state & (BIO_ROBOTIC)) // DOPPLER EDIT - old code: else if (hit_bodypart?.biological_state & (BIO_METAL|BIO_WIRED)) var/random_damage_mult = RANDOM_DECIMAL(0.85, 1.15) // SOMETIMES you can get more or less sparks var/damage_dealt = ((damage / (1 - (blocked / 100))) * random_damage_mult) diff --git a/code/modules/reagents/chemistry/holder/mob_life.dm b/code/modules/reagents/chemistry/holder/mob_life.dm index 611de150920e4..5d7da162743b4 100644 --- a/code/modules/reagents/chemistry/holder/mob_life.dm +++ b/code/modules/reagents/chemistry/holder/mob_life.dm @@ -93,6 +93,14 @@ if(!owner) owner = reagent.holder.my_atom + //DOPPLER ADDITION BEGIN + var/can_process = reagent_process_flags_valid(owner, reagent) + //If the mob can't process it, remove the reagent at it's normal rate without doing any addictions, overdoses, or on_mob_life() for the reagent + if(!can_process) + reagent.holder.remove_reagent(reagent.type, reagent.metabolization_rate) + return + //DOPPLER ADDITION END + if(owner && reagent && (!dead || (reagent.chemical_flags & REAGENT_DEAD_PROCESS))) if(owner.reagent_check(reagent, seconds_per_tick, times_fired)) return diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm index 8c0aa36189f99..7b6fb34efe1a4 100644 --- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm @@ -220,6 +220,7 @@ inverse_chem = /datum/reagent/inverse/hercuri inverse_chem_val = 0.3 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC // DOPPLER EDIT - Lets hercuri process in synths /datum/reagent/medicine/c2/hercuri/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 15c79e4c150b9..9a92828c89e07 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -22,12 +22,20 @@ blood_type = new_reagent.data["blood_type"] else if(holder.has_reagent(/datum/reagent/consumable/liquidelectricity)) blood_type = "LE" + /* DOPPLER REMOVAL START else if(holder.has_reagent(/datum/reagent/lube)) blood_type = "S" + DOPPLER REMOVAL END */ else if(holder.has_reagent(/datum/reagent/water)) blood_type = "H2O" else if(holder.has_reagent(/datum/reagent/toxin/slimejelly)) blood_type = "TOX" + // DOPPLER ADDITION START + else if(holder.has_reagent(/datum/reagent/synth_blood)) + blood_type = "R" + else if(holder.has_reagent(/datum/reagent/bug_blood)) + blood_type = "I" + // DOPPLER ADDITION END else blood_type = null return ..() @@ -43,7 +51,7 @@ /obj/item/reagent_containers/blood/random/Initialize(mapload, vol) icon_state = "bloodpack" - blood_type = pick("A+", "A-", "B+", "B-", "O+", "O-", "L") + blood_type = pick("A+", "A-", "B+", "B-", "O+", "O-", "L", "LE", "R", "I") // DOPPLER EDIT, old code: blood_type = pick("A+", "A-", "B+", "B-", "O+", "O-", "L") return ..() /obj/item/reagent_containers/blood/a_plus @@ -71,6 +79,7 @@ blood_type = "LE" unique_blood = /datum/reagent/consumable/liquidelectricity +/* DOPPLER REMOVAL START /obj/item/reagent_containers/blood/snail blood_type = "S" unique_blood = /datum/reagent/lube @@ -78,6 +87,7 @@ /obj/item/reagent_containers/blood/snail/examine() . = ..() . += span_notice("It's a bit slimy... The label indicates that this is meant for snails.") + DOPPLER REMOVAL END */ /obj/item/reagent_containers/blood/podperson blood_type = "H2O" diff --git a/code/modules/research/techweb/nodes/cyborg_nodes.dm b/code/modules/research/techweb/nodes/cyborg_nodes.dm index eeeed268be552..061f450694c43 100644 --- a/code/modules/research/techweb/nodes/cyborg_nodes.dm +++ b/code/modules/research/techweb/nodes/cyborg_nodes.dm @@ -19,6 +19,14 @@ "cybernetic_stomach", "cybernetic_liver", "cybernetic_heart", + // DOPPLER ADDITION START + "android_chest", + "android_head", + "android_l_arm", + "android_l_leg", + "android_r_arm", + "android_r_leg", + // DOPPLER ADDITION END ) experiments_to_unlock = list( /datum/experiment/scanning/people/android, @@ -193,6 +201,12 @@ "ci-nutrimentplus", "ci-toolset", "ci-surgery", + // DOPPLER ADDITION START + "ci-botany", + "ci-janitor", + "ci-razor", + "ci-drill", + // DOPPLER ADDITION END ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS) announce_channels = list(RADIO_CHANNEL_SCIENCE, RADIO_CHANNEL_MEDICAL) diff --git a/code/modules/surgery/organs/external/_visual_organs.dm b/code/modules/surgery/organs/external/_visual_organs.dm index 74399838f1f6b..7ac88de4d60bd 100644 --- a/code/modules/surgery/organs/external/_visual_organs.dm +++ b/code/modules/surgery/organs/external/_visual_organs.dm @@ -120,7 +120,7 @@ Unlike normal organs, we're actually inside a persons limbs at all times feature_key = "horns" /datum/bodypart_overlay/mutant/horns/can_draw_on_bodypart(mob/living/carbon/human/human) - if((human.head?.flags_inv & HIDEHAIR) || (human.wear_mask?.flags_inv & HIDEHAIR)) + if((human.head?.flags_inv & HIDEHORNS) || (human.wear_mask?.flags_inv & HIDEHORNS)) // DOPPLER EDIT, old code: if((human.head?.flags_inv & HIDEHAIR) || (human.wear_mask?.flags_inv & HIDEHAIR)) return FALSE return TRUE diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm index ba3e711f15974..656eac6c8418b 100644 --- a/code/modules/surgery/organs/external/tails.dm +++ b/code/modules/surgery/organs/external/tails.dm @@ -153,7 +153,7 @@ return "[wagging ? "wagging_" : ""][sprite_datum.icon_state]" //add the wagging tag if we be wagging /datum/bodypart_overlay/mutant/tail/can_draw_on_bodypart(mob/living/carbon/human/human) - if(human.wear_suit && (human.wear_suit.flags_inv & HIDEJUMPSUIT)) + if(human.wear_suit && (human.wear_suit.flags_inv & HIDETAIL)) // DOPPLER EDIT, old code: if(human.wear_suit && (human.wear_suit.flags_inv & HIDEJUMPSUIT)) return FALSE return TRUE diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index ddf59b71455d0..c8c462f57584a 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -83,8 +83,13 @@ affected_human.eye_color_right = eye_color_right else eye_color_right = affected_human.eye_color_right - if(HAS_TRAIT(affected_human, TRAIT_NIGHT_VISION) && !lighting_cutoff) - lighting_cutoff = LIGHTING_CUTOFF_REAL_LOW + if(HAS_TRAIT(affected_human, TRAIT_NIGHT_VISION)) // DOPPLER EDIT CHANGE - ORIGINAL: if(HAS_TRAIT(affected_human, TRAIT_NIGHT_VISION) && !lighting_cutoff) + //lighting_cutoff = LIGHTING_CUTOFF_REAL_LOW // DOPPLER EDIT REMOVAL + // DOPPLER EDIT ADDITION START - NIGHT VISION ADJUSTMENT - adjusts color cutoffs based on chosen quirk color, or left eye colour if not available + var/datum/quirk/night_vision/nv_quirk = affected_human.get_quirk(/datum/quirk/night_vision) + nv_quirk.nv_color_cutoffs = nv_quirk.calculate_color_cutoffs(nv_quirk.nv_color) + color_cutoffs = nv_quirk.nv_color_cutoffs + // DOPPLER EDIT ADDITION END if(CONFIG_GET(flag/native_fov) && native_fov) affected_human.add_fov_trait(type, native_fov) diff --git a/code/modules/unit_tests/quirks.dm b/code/modules/unit_tests/quirks.dm index cca2c312cfe27..d63e2beac049f 100644 --- a/code/modules/unit_tests/quirks.dm +++ b/code/modules/unit_tests/quirks.dm @@ -50,6 +50,10 @@ /datum/species/skeleton = null, // Anyone with noblood should not get a blood bag /datum/species/jelly = /obj/item/reagent_containers/blood/toxin, /datum/species/human = /obj/item/reagent_containers/blood/o_minus, + // DOPPLER ADDITION START + /datum/species/android = /obj/item/reagent_containers/blood/robot, + /datum/species/insectoid = /obj/item/reagent_containers/blood/bug, + // DOPPLER ADDITION END ) /datum/unit_test/blood_deficiency_mail/Run() diff --git a/code/~doppler_earliest_defines.dm b/code/~doppler_earliest_defines.dm index 677b8c5977843..2914dc5174a7f 100644 --- a/code/~doppler_earliest_defines.dm +++ b/code/~doppler_earliest_defines.dm @@ -90,7 +90,7 @@ name = DOPPLER_SPRITE_ACCESSORY_NOEARS icon_state = /datum/sprite_accessory/ears/none::icon_state -/datum/sprite_accessory/ears_more/synthetic/none +/datum/sprite_accessory/ears_more/cybernetic/none icon = 'modular_doppler/modular_customization/accessories/code/~overrides/icons/fallbacks.dmi' name = DOPPLER_SPRITE_ACCESSORY_NOEARS icon_state = /datum/sprite_accessory/ears/none::icon_state @@ -161,7 +161,7 @@ name = DOPPLER_SPRITE_ACCESSORY_NOTAIL icon_state = /datum/sprite_accessory/tails/none::icon_state -/datum/sprite_accessory/tails/synthetic/none +/datum/sprite_accessory/tails/cybernetic/none icon = /datum/sprite_accessory/tails/none::icon name = DOPPLER_SPRITE_ACCESSORY_NOTAIL icon_state = /datum/sprite_accessory/tails/none::icon_state diff --git a/config/game_options.txt b/config/game_options.txt index 6bb1f54c92783..3d3bf1fbf2461 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -366,7 +366,7 @@ ROUNDSTART_RACES plasmaman #ROUNDSTART_RACES shadow ROUNDSTART_RACES ramatan ROUNDSTART_RACES anthromorph -ROUNDSTART_RACES aquatic +#ROUNDSTART_RACES aquatic ROUNDSTART_RACES insectoid ROUNDSTART_RACES genemod ROUNDSTART_RACES primitive_genemod @@ -384,7 +384,7 @@ ROUNDSTART_RACES snail #ROUNDSTART_RACES pod ## Races that are radical departures to what a species is, so while they technically can be roundstart - it's probably not wise -#ROUNDSTART_RACES android +ROUNDSTART_RACES android ROUNDSTART_RACES golem ##------------------------------------------------------------------------------------------- diff --git a/modular_doppler/deforest_medical_items/code/cargo_packs.dm b/modular_doppler/deforest_medical_items/code/cargo_packs.dm index fe7e50aa19985..5dced06ad9d9e 100644 --- a/modular_doppler/deforest_medical_items/code/cargo_packs.dm +++ b/modular_doppler/deforest_medical_items/code/cargo_packs.dm @@ -64,7 +64,59 @@ crate_name = "\improper DeForest Med-Vend resupply crate" desc = "Contains a restocking canister for DeForest Med-Vendors." access = ACCESS_MEDICAL - cost = CARGO_CRATE_VALUE * 5 + cost = CARGO_CRATE_VALUE * 3 contains = list( /obj/item/vending_refill/medical_deforest, ) + +//// +// Synthetic medicine +//// +/datum/supply_pack/science/synthetic_burns + name = "Synthetic Burns Kit" + desc = "Contains bottles of pre-chilled hercuri and dinitrogen plasmide, perfect for treating synthetic burns!" + cost = CARGO_CRATE_VALUE * 2.5 + contains = list(/obj/item/reagent_containers/spray/hercuri/chilled = 3, /obj/item/reagent_containers/spray/dinitrogen_plasmide = 3) + crate_name = "chilled hercuri crate" + + access_view = FALSE + access = FALSE + access_any = FALSE + +/datum/supply_pack/science/synth_treatment_kits + name = "Synthetic Treatment Kits" + desc = "Contains a treatment kit for synthetic lifeforms, filled with everything you need to treat an inorganic wound!" + cost = CARGO_CRATE_VALUE * 3 + contains = list(/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit = 1) + crate_name = "synthetic treatment kits crate" + + access_view = FALSE + access = FALSE + access_any = FALSE + +/datum/supply_pack/science/synth_healing_chems + name = "Synthetic Medicine Pack" + desc = "Contains a variety of synthetic-exclusive medicine. 2 pill bottles of liquid solder, 2 of nanite slurry, 2 of system cleaner." + cost = CARGO_CRATE_VALUE * 5.5 + contains = list( + /obj/item/storage/pill_bottle/liquid_solder = 2, + /obj/item/storage/pill_bottle/nanite_slurry = 2, + /obj/item/storage/pill_bottle/system_cleaner = 2 + ) + crate_name = "synthetic medicine crate" + + access_view = FALSE + access = FALSE + access_any = FALSE + +/datum/supply_pack/science/synth_medkits + name = "Mechanical Repair Kits" + desc = "Contains a few low-grade portable synthetic medkits, useful for distributing to the crew." + cost = CARGO_CRATE_VALUE * 4.5 // same as treatment kits + contains = list(/obj/item/storage/medkit/robotic_repair/stocked = 4) + + crate_name = "synthetic repair kits crate" + + access_view = FALSE + access = FALSE + access_any = FALSE diff --git a/modular_doppler/deforest_medical_items/code/injectors.dm b/modular_doppler/deforest_medical_items/code/injectors.dm index 02334261f3e32..02558b52bb102 100644 --- a/modular_doppler/deforest_medical_items/code/injectors.dm +++ b/modular_doppler/deforest_medical_items/code/injectors.dm @@ -207,3 +207,25 @@ /datum/reagent/medicine/higadrite = 5, /datum/reagent/medicine/silibinin = 5, ) + +// Medpen for robots that fixes toxin damage and purges synth chems but slows them down for a bit +/obj/item/reagent_containers/hypospray/medipen/deforest/robot_system_cleaner + name = "synthetic cleaner autoinjector" + desc = "A Deforest branded autoinjector, loaded with system cleaner for purging synthetics of reagents." + base_icon_state = "robor" + icon_state = "robor" + list_reagents = list( + /datum/reagent/medicine/system_cleaner = 15, + /datum/reagent/dinitrogen_plasmide = 5, + ) + +// Medpen for robots that fixes brain damage but slows them down for a bit +/obj/item/reagent_containers/hypospray/medipen/deforest/robot_liquid_solder + name = "synthetic smart-solder autoinjector" + desc = "A Deforest branded autoinjector, loaded with liquid solder to repair synthetic processor core damage." + base_icon_state = "robor_brain" + icon_state = "robor_brain" + list_reagents = list( + /datum/reagent/medicine/liquid_solder = 15, + /datum/reagent/dinitrogen_plasmide = 5, + ) diff --git a/modular_doppler/deforest_medical_items/code/medstation_designs/blood.dm b/modular_doppler/deforest_medical_items/code/medstation_designs/blood.dm index f7624f60d1a4e..0d523d14f4fa7 100644 --- a/modular_doppler/deforest_medical_items/code/medstation_designs/blood.dm +++ b/modular_doppler/deforest_medical_items/code/medstation_designs/blood.dm @@ -107,3 +107,25 @@ RND_CATEGORY_INITIAL, RND_CATEGORY_DEFOREST_BLOOD, ) + +/datum/design/organic_bloodbag_robot + name = "R Blood Pack" + id = "organic_bloodbag_robot" + build_type = BIOGENERATOR + materials = list(/datum/material/biomass = 100) + build_path = /obj/item/reagent_containers/blood/robot + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_DEFOREST_BLOOD, + ) + +/datum/design/organic_bloodbag_bug + name = "I Blood Pack" + id = "organic_bloodbag_bug" + build_type = BIOGENERATOR + materials = list(/datum/material/biomass = 100) + build_path = /obj/item/reagent_containers/blood/bug + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_DEFOREST_BLOOD, + ) diff --git a/modular_doppler/deforest_medical_items/code/storage_items_robotics.dm b/modular_doppler/deforest_medical_items/code/storage_items_robotics.dm new file mode 100644 index 0000000000000..f57467bcf266b --- /dev/null +++ b/modular_doppler/deforest_medical_items/code/storage_items_robotics.dm @@ -0,0 +1,75 @@ +// Pre-packed medkit for healing synths and repairing their wounds rapidly in the field +/obj/item/storage/medkit/robotic_repair + name = "robotic repair equipment kit" + desc = "An industrial-strength plastic box filled with supplies for repairing synthetics from critical damage." + icon = 'modular_doppler/deforest_medical_items/icons/storage.dmi' + icon_state = "synth_medkit" + inhand_icon_state = "medkit" + worn_icon = 'modular_doppler/deforest_medical_items/icons/worn/worn.dmi' + worn_icon_state = "frontier" + drop_sound = 'sound/items/handling/ammobox_drop.ogg' + pickup_sound = 'sound/items/handling/ammobox_pickup.ogg' + +/obj/item/storage/medkit/robotic_repair/Initialize(mapload) + . = ..() + var/static/list/list_of_everything_mechanical_medkits_can_hold = list_of_everything_medkits_can_hold + list( + /obj/item/stack/cable_coil, + /obj/item/crowbar, + /obj/item/screwdriver, + /obj/item/wrench, + /obj/item/weldingtool, + /obj/item/wirecutters, + /obj/item/multitool, + /obj/item/plunger, + /obj/item/clothing/head/utility/welding, + /obj/item/clothing/glasses/welding, + ) + var/static/list/exception_cache = typecacheof( + /obj/item/clothing/head/utility/welding, + ) + + atom_storage.set_holdable(list_of_everything_mechanical_medkits_can_hold) + LAZYINITLIST(atom_storage.exception_hold) + atom_storage.exception_hold = atom_storage.exception_hold + exception_cache + atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL + +/obj/item/storage/medkit/robotic_repair/stocked + +/obj/item/storage/medkit/robotic_repair/stocked/PopulateContents() + var/static/items_inside = list( + /obj/item/stack/medical/gauze = 1, + /obj/item/reagent_containers/pill/robotic_patch/synth_repair = 2, + /obj/item/stack/medical/wound_recovery/robofoam = 1, + /obj/item/reagent_containers/hypospray/medipen/deforest/robot_system_cleaner = 1, + /obj/item/reagent_containers/hypospray/medipen/deforest/coagulants = 1, // Coagulants help electrical damage + /obj/item/healthanalyzer/simple = 1, + ) + generate_items_inside(items_inside,src) + +/obj/item/storage/medkit/robotic_repair/preemo + name = "premium robotic repair equipment kit" + desc = "An industrial-strength plastic box filled with supplies for repairing synthetics from critical damage. \ + This one has extra storage on the sides for even more equipment than the standard medkit model." + icon_state = "synth_medkit_super" + +/obj/item/storage/medkit/robotic_repair/preemo/Initialize(mapload) + . = ..() + atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL + atom_storage.max_slots = 12 + atom_storage.max_total_storage = 12 * WEIGHT_CLASS_NORMAL + +/obj/item/storage/medkit/robotic_repair/preemo/stocked + +/obj/item/storage/medkit/robotic_repair/preemo/stocked/PopulateContents() + var/static/items_inside = list( + /obj/item/stack/medical/gauze/twelve = 1, + /obj/item/stack/cable_coil/thirty = 1, + /obj/item/reagent_containers/pill/robotic_patch/synth_repair = 4, + /obj/item/stack/medical/wound_recovery/robofoam = 1, + /obj/item/reagent_containers/hypospray/medipen/deforest/robot_system_cleaner = 1, + /obj/item/reagent_containers/hypospray/medipen/deforest/robot_liquid_solder = 1, + /obj/item/reagent_containers/hypospray/medipen/deforest/coagulants = 1, + /obj/item/reagent_containers/spray/dinitrogen_plasmide = 1, + /obj/item/healthanalyzer/simple = 1, + ) + generate_items_inside(items_inside,src) diff --git a/modular_doppler/deforest_medical_items/code/synth_healing.dm b/modular_doppler/deforest_medical_items/code/synth_healing.dm new file mode 100644 index 0000000000000..b2aac928c979b --- /dev/null +++ b/modular_doppler/deforest_medical_items/code/synth_healing.dm @@ -0,0 +1,104 @@ +// Used to stop synth structural damage +/obj/item/stack/medical/wound_recovery/robofoam + name = "robotic repair spray" + singular_name = "robotic repair spray" + desc = "A needle-tip foam gun filled with an advanced synthetic foam that rapidly \ + fills and stabilizes structural damage in synthetics. The damaged area will be \ + vulnerable to further damage while the foam hardens" + icon = 'modular_doppler/deforest_medical_items/icons/stack_items.dmi' + icon_state = "robofoam" + inhand_icon_state = "implantcase" + applicable_wounds = list( + /datum/wound/blunt/robotic, + ) + max_amount = 2 + amount = 2 + merge_type = /obj/item/stack/medical/wound_recovery/robofoam + treatment_sound = 'sound/effects/spray.ogg' + causes_pain = FALSE + +/obj/item/stack/medical/wound_recovery/robofoam/examine(mob/user) + . = ..() + . += span_notice("This cheaper foam can only be used to fill structural wounds on synthetics.") + return . + +/obj/item/stack/medical/wound_recovery/robofoam/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/user) + . = ..() + healed_mob.reagents.add_reagent(/datum/reagent/medicine/nanite_slurry, 5) + healed_mob.reagents.add_reagent(/datum/reagent/medicine/coagulant/fabricated, 5) + +// Used to cure practically any synthetic wound +/obj/item/stack/medical/wound_recovery/robofoam_super + name = "premium robotic repair spray" + singular_name = "premium robotic repair spray" + desc = "A needle-tip foam gun filled with an advanced synthetic foam that rapidly \ + fills and stabilizes structural damage in synthetics. The damaged area will be \ + vulnerable to further damage while the foam hardens. \ + This special premium type can also be used to repair almost any possible type \ + of synthetic damage." + icon = 'modular_doppler/deforest_medical_items/icons/stack_items.dmi' + icon_state = "robofoam_super" + inhand_icon_state = "implantcase" + applicable_wounds = list( + /datum/wound/blunt/robotic, + /datum/wound/muscle/robotic, + /datum/wound/electrical_damage, + /datum/wound/burn/robotic, + ) + max_amount = 2 + amount = 2 + merge_type = /obj/item/stack/medical/wound_recovery/robofoam_super + treatment_sound = 'sound/effects/spray.ogg' + causes_pain = FALSE + +/obj/item/stack/medical/wound_recovery/robofoam_super/examine(mob/user) + . = ..() + . += span_notice("This more expensive foam can be used to fill any type of wound on synthetics.") + return . + +/obj/item/stack/medical/wound_recovery/robofoam_super/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/user) + . = ..() + healed_mob.reagents.add_reagent(/datum/reagent/medicine/coagulant/fabricated, 5) + healed_mob.reagents.add_reagent(/datum/reagent/medicine/nanite_slurry, 5) + healed_mob.reagents.add_reagent(/datum/reagent/dinitrogen_plasmide, 5) + +// Synth repair patch, gives the synth a small amount of healing chems +/obj/item/reagent_containers/pill/robotic_patch + name = "robotic patch" + desc = "A chemical patch for touch-based applications on synthetics." + icon = 'modular_doppler/deforest_medical_items/icons/stack_items.dmi' + icon_state = "synth_patch" + inhand_icon_state = null + possible_transfer_amounts = list() + volume = 40 + apply_type = PATCH + apply_method = "apply" + self_delay = 3 SECONDS + dissolvable = FALSE + +/obj/item/reagent_containers/pill/robotic_patch/attack(mob/living/L, mob/user) + if(ishuman(L)) + var/obj/item/bodypart/affecting = L.get_bodypart(check_zone(user.zone_selected)) + if(!affecting) + to_chat(user, span_warning("The limb is missing!")) + return + if(!IS_ROBOTIC_LIMB(affecting)) + to_chat(user, span_notice("Robotic patches won't work on an organic limb!")) + return + return ..() + +/obj/item/reagent_containers/pill/robotic_patch/canconsume(mob/eater, mob/user) + if(!iscarbon(eater)) + return FALSE + return TRUE + +// The actual patch +/obj/item/reagent_containers/pill/robotic_patch/synth_repair + name = "robotic repair patch" + desc = "A sealed patch with a small nanite swarm along with electrical coagulant reagents to repair small amounts of synthetic damage." + icon_state = "synth_patch" + list_reagents = list( + /datum/reagent/medicine/nanite_slurry = 10, + /datum/reagent/dinitrogen_plasmide = 5, + /datum/reagent/medicine/coagulant/fabricated = 10, + ) diff --git a/modular_doppler/loadout_categories/categories/belts.dm b/modular_doppler/loadout_categories/categories/belts.dm index 8dc7bc401ca53..2faf0304cacf4 100644 --- a/modular_doppler/loadout_categories/categories/belts.dm +++ b/modular_doppler/loadout_categories/categories/belts.dm @@ -121,9 +121,13 @@ name = "Satchel Med-kit" item_path = /obj/item/storage/backpack/duffelbag/deforest_medkit +/datum/loadout_item/belts/synth_repair + name = "Android Repair Kit" + item_path = /obj/item/storage/medkit/robotic_repair/stocked + /datum/loadout_item/belts/frontier_med name = "Frontier Med-kit" - item_path = /obj/item/storage/medkit/frontier + item_path = /obj/item/storage/medkit/frontier/stocked /datum/loadout_item/belts/deforest_paramed name = "Satchel Tech-kit" diff --git a/modular_doppler/loadout_categories/categories/clothing.dm b/modular_doppler/loadout_categories/categories/clothing.dm index 2ccc76b57f268..6cc4a6ccdc19d 100644 --- a/modular_doppler/loadout_categories/categories/clothing.dm +++ b/modular_doppler/loadout_categories/categories/clothing.dm @@ -311,6 +311,10 @@ name = "Messy Ian Shirt" item_path = /obj/item/clothing/suit/costume/wellworn_shirt/messy/graphic/ian +/datum/loadout_item/suit/dagger_mantle + name = "'Dagger' Designer Mantle" + item_path = /obj/item/clothing/suit/dagger_mantle + /* * HAWAIIAN */ @@ -319,6 +323,14 @@ name = "Hawaiian Shirt" item_path = /obj/item/clothing/suit/costume/hawaiian +/* +* MAID OUT WITH THE FIT +*/ + +/datum/loadout_item/suit/maid_costume + name = "Maid Costume" + item_path = /obj/item/clothing/under/costume/maid + /* * MISC */ diff --git a/modular_doppler/loadout_categories/categories/gloves.dm b/modular_doppler/loadout_categories/categories/gloves.dm index 9415099a7bdcc..0d07980a3a890 100644 --- a/modular_doppler/loadout_categories/categories/gloves.dm +++ b/modular_doppler/loadout_categories/categories/gloves.dm @@ -67,3 +67,11 @@ /datum/loadout_item/gloves/latex name = "Latex Gloves" item_path = /obj/item/clothing/gloves/latex + +/datum/loadout_item/gloves/translationgloves + name = "Translation Gloves" + item_path = /obj/item/clothing/gloves/radio + +/datum/loadout_item/gloves/maid_arm_covers + name = "Maid Costume" + item_path = /obj/item/clothing/gloves/maid diff --git a/modular_doppler/loadout_categories/categories/heads.dm b/modular_doppler/loadout_categories/categories/heads.dm index 4d900d5cd620c..f18bc53bd7651 100644 --- a/modular_doppler/loadout_categories/categories/heads.dm +++ b/modular_doppler/loadout_categories/categories/heads.dm @@ -97,3 +97,7 @@ /datum/loadout_item/head/bow_sweet name = "Bow (Sweet)" item_path = /obj/item/clothing/head/bow/sweet + +/datum/loadout_item/head/maid_headband + name = "Maid Headband" + item_path = /obj/item/clothing/head/costume/maidheadband diff --git a/modular_doppler/loadout_categories/categories/inhands.dm b/modular_doppler/loadout_categories/categories/inhands.dm index 1ab5375ec4960..e6bb65a69c4f1 100644 --- a/modular_doppler/loadout_categories/categories/inhands.dm +++ b/modular_doppler/loadout_categories/categories/inhands.dm @@ -30,7 +30,3 @@ /datum/loadout_item/inhand/slimpack name = "Custom Flatpack" item_path = /obj/item/storage/backpack/satchel/flat/empty/custom - -/datum/loadout_item/inhand/translationgloves - name = "Translation Gloves" - item_path = /obj/item/clothing/gloves/radio diff --git a/modular_doppler/loadout_categories/categories/neck.dm b/modular_doppler/loadout_categories/categories/neck.dm index f15a9ce48bf47..298bffcd11a34 100644 --- a/modular_doppler/loadout_categories/categories/neck.dm +++ b/modular_doppler/loadout_categories/categories/neck.dm @@ -1,3 +1,27 @@ /datum/loadout_item/neck/colonial_cloak name = "Colonial Cloak" item_path = /obj/item/clothing/neck/cloak/colonial + +/datum/loadout_item/neck/robe_cape + name = "Robe Cape" + item_path = /obj/item/clothing/neck/robe_cape + +/datum/loadout_item/neck/long_cape + name = "Long Cape" + item_path = /obj/item/clothing/neck/long_cape + +/datum/loadout_item/neck/wide_cape + name = "Wide Cape" + item_path = /obj/item/clothing/neck/wide_cape + +/datum/loadout_item/neck/ranger_poncho + name = "Ranger Poncho" + item_path = /obj/item/clothing/neck/ranger_poncho + +/datum/loadout_item/neck/face_scarf + name = "Face Scarf" + item_path = /obj/item/clothing/neck/face_scarf + +/datum/loadout_item/neck/maid_neck_cover + name = "Maid Neck Cover" + item_path = /obj/item/clothing/neck/maid_neck_cover diff --git a/modular_doppler/loadout_categories/categories/pockets.dm b/modular_doppler/loadout_categories/categories/pockets.dm index fe6e64ec5820e..6f5e2f0211c85 100644 --- a/modular_doppler/loadout_categories/categories/pockets.dm +++ b/modular_doppler/loadout_categories/categories/pockets.dm @@ -238,4 +238,4 @@ /datum/loadout_item/pocket_items/civil_defense name = "Civil Defense Med-kit" - item_path = /obj/item/storage/medkit/civil_defense + item_path = /obj/item/storage/medkit/civil_defense/stocked diff --git a/modular_doppler/loadout_categories/categories/undersuit.dm b/modular_doppler/loadout_categories/categories/undersuit.dm index 9b97bd86f0735..b811519abc237 100644 --- a/modular_doppler/loadout_categories/categories/undersuit.dm +++ b/modular_doppler/loadout_categories/categories/undersuit.dm @@ -18,7 +18,13 @@ /datum/loadout_item/undersuit/insert_path_into_outfit(datum/outfit/outfit, mob/living/carbon/human/equipper, visuals_only = FALSE, override_items = LOADOUT_OVERRIDE_BACKPACK) if(override_items == LOADOUT_OVERRIDE_BACKPACK && !visuals_only) if(outfit.uniform) - LAZYADD(outfit.backpack_contents, outfit.uniform) + if(equipper.jumpsuit_style == PREF_SKIRT) + outfit.uniform = "[outfit.uniform]/skirt" + if(!text2path(outfit.uniform)) + outfit.uniform = initial(outfit.uniform) + LAZYADD(outfit.backpack_contents, outfit.uniform) + else + LAZYADD(outfit.backpack_contents, outfit.uniform) outfit.uniform = item_path else outfit.uniform = item_path diff --git a/modular_doppler/modular_cosmetics/code/suits/basesuits.dm b/modular_doppler/modular_cosmetics/code/suits/basesuits.dm index d51882beb311c..19688ac500047 100644 --- a/modular_doppler/modular_cosmetics/code/suits/basesuits.dm +++ b/modular_doppler/modular_cosmetics/code/suits/basesuits.dm @@ -43,12 +43,33 @@ +/// SPACESUITS +/obj/item/clothing/head/helmet/space + supported_bodyshapes = list(BODYSHAPE_HUMANOID, BODYSHAPE_SNOUTED) + bodyshape_icon_files = list(BODYSHAPE_HUMANOID_T = 'icons/mob/clothing/head/spacehelm.dmi', + BODYSHAPE_SNOUTED_T = 'modular_doppler/modular_cosmetics/icons/mob/head/spacehelm_muzzled.dmi') + /obj/item/clothing/suit/space supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION supported_bodyshapes = list(BODYSHAPE_HUMANOID, BODYSHAPE_DIGITIGRADE) bodyshape_icon_files = list(BODYSHAPE_HUMANOID_T = 'icons/mob/clothing/suits/spacesuit.dmi', BODYSHAPE_DIGITIGRADE_T = 'modular_doppler/modular_cosmetics/icons/mob/suit/spacesuit_digi.dmi') +/// RADSUITS +/obj/item/clothing/head/utility/radiation + supported_bodyshapes = list(BODYSHAPE_HUMANOID, BODYSHAPE_SNOUTED) + bodyshape_icon_files = list(BODYSHAPE_HUMANOID_T = 'icons/mob/clothing/head/utility.dmi', + BODYSHAPE_SNOUTED_T = 'modular_doppler/modular_cosmetics/icons/mob/head/basehead_muzzled.dmi') + +/obj/item/clothing/suit/utility/radiation + supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION + supported_bodyshapes = list(BODYSHAPE_HUMANOID, BODYSHAPE_DIGITIGRADE) + bodyshape_icon_files = list(BODYSHAPE_HUMANOID_T = 'icons/mob/clothing/suits/utility.dmi', + BODYSHAPE_DIGITIGRADE_T = 'modular_doppler/modular_cosmetics/icons/mob/suit/suit_digi.dmi') + + + +/// FLAKY SUITS /obj/item/clothing/suit/chaplainsuit supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION supported_bodyshapes = list(BODYSHAPE_HUMANOID, BODYSHAPE_DIGITIGRADE) diff --git a/modular_doppler/modular_cosmetics/icons/mob/head/spacehelm_muzzled.dmi b/modular_doppler/modular_cosmetics/icons/mob/head/spacehelm_muzzled.dmi new file mode 100644 index 0000000000000..d4ede3246cdce Binary files /dev/null and b/modular_doppler/modular_cosmetics/icons/mob/head/spacehelm_muzzled.dmi differ diff --git a/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_body_markings.dm b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_body_markings.dm new file mode 100644 index 0000000000000..9a39c2995fcae --- /dev/null +++ b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_body_markings.dm @@ -0,0 +1,5 @@ +/datum/sprite_accessory/lizard_markings/synth_lizard + icon = 'modular_doppler/modular_customization/accessories/icons/cybernetic/synth_markings.dmi' + name = "Synthetic Lizard" + icon_state = "synth_lizard" + gender_specific = TRUE diff --git a/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_ears.dm b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_ears.dm new file mode 100644 index 0000000000000..8f48910d3622c --- /dev/null +++ b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_ears.dm @@ -0,0 +1,22 @@ +/datum/sprite_accessory/ears_more/cybernetic + icon = 'modular_doppler/modular_customization/accessories/icons/cybernetic/synth_ears.dmi' + +/datum/sprite_accessory/ears_more/cybernetic/antennae + name = "Angled Antennae" + icon_state = "antennae" + +/datum/sprite_accessory/ears_more/cybernetic/tvantennae + name = "TV Antennae" + icon_state = "tvantennae" + +/datum/sprite_accessory/ears_more/cybernetic/cyberhead + name = "Cyberhead" + icon_state = "cyberhead" + +/datum/sprite_accessory/ears_more/cybernetic/antlers + name = "Antlers" + icon_state = "antlers" + +/datum/sprite_accessory/ears_more/cybernetic/crowned + name = "Crowned" + icon_state = "crowned" diff --git a/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_horns.dm b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_horns.dm new file mode 100644 index 0000000000000..782f81eb36459 --- /dev/null +++ b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_horns.dm @@ -0,0 +1,16 @@ +/datum/sprite_accessory/horns/synth + icon = 'modular_doppler/modular_customization/accessories/icons/cybernetic/synth_horns.dmi' + name = "Short (Synth)" + icon_state = "short" + +/datum/sprite_accessory/horns/synth/long + name = "Long (Synth)" + icon_state = "long" + +/datum/sprite_accessory/horns/synth/doubled + name = "Doubled (Synth)" + icon_state = "doubled" + +/datum/sprite_accessory/horns/synth/thick + name = "Thick (Synth)" + icon_state = "thick" diff --git a/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_tail.dm b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_tail.dm new file mode 100644 index 0000000000000..f5c34dac23b19 --- /dev/null +++ b/modular_doppler/modular_customization/accessories/code/cybernetic_accessories/synth_tail.dm @@ -0,0 +1,10 @@ +/datum/sprite_accessory/tails/cybernetic + icon = 'modular_doppler/modular_customization/accessories/icons/cybernetic/synth_tail.dmi' + +/datum/sprite_accessory/tails/cybernetic/default + name = "Plug" + icon_state = "plugtail" + +/datum/sprite_accessory/tails/cybernetic/cable + name = "Cable" + icon_state = "cable" diff --git a/modular_doppler/sprite_accessories/code/hair.dm b/modular_doppler/modular_customization/accessories/code/hair.dm similarity index 98% rename from modular_doppler/sprite_accessories/code/hair.dm rename to modular_doppler/modular_customization/accessories/code/hair.dm index c57828ce11ade..54048feeda384 100644 --- a/modular_doppler/sprite_accessories/code/hair.dm +++ b/modular_doppler/modular_customization/accessories/code/hair.dm @@ -1,7 +1,7 @@ // Hairstyles /datum/sprite_accessory/hair/modular - icon = 'modular_doppler/sprite_accessories/icons/hair.dmi' + icon = 'modular_doppler/modular_customization/accessories/icons/hair.dmi' /datum/sprite_accessory/hair/modular/pigtails4 name = "Pigtails 4" @@ -651,10 +651,14 @@ name = "Long Dreadlocks" icon_state = "hair_dreadlocks_long" +/datum/sprite_accessory/hair/modular/inari + name = "Inari" + icon_state = "inari" + // Facial hair /datum/sprite_accessory/facial_hair/modular - icon = 'modular_doppler/sprite_accessories/icons/facialhair.dmi' + icon = 'modular_doppler/modular_customization/accessories/icons/facialhair.dmi' /datum/sprite_accessory/facial_hair/modular/sideburns name = "Sideburns" diff --git a/modular_doppler/modular_customization/accessories/code/synthetic_accessories/synth_ears.dm b/modular_doppler/modular_customization/accessories/code/synthetic_accessories/synth_ears.dm deleted file mode 100644 index 31a2348f185fe..0000000000000 --- a/modular_doppler/modular_customization/accessories/code/synthetic_accessories/synth_ears.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/sprite_accessory/ears_more/synthetic - icon = 'modular_doppler/modular_customization/accessories/icons/synthetic/synth_ears.dmi' - -// Nothing here yet diff --git a/modular_doppler/modular_customization/accessories/code/synthetic_accessories/synth_tail.dm b/modular_doppler/modular_customization/accessories/code/synthetic_accessories/synth_tail.dm deleted file mode 100644 index c3a27a449f137..0000000000000 --- a/modular_doppler/modular_customization/accessories/code/synthetic_accessories/synth_tail.dm +++ /dev/null @@ -1,10 +0,0 @@ -/datum/sprite_accessory/tails/synthetic - icon = 'modular_doppler/modular_customization/accessories/icons/synthetic/synth_tail.dmi' - -/datum/sprite_accessory/tails/synthetic/default - name = "Plug" - icon_state = "plugtail" - -/datum/sprite_accessory/tails/synthetic/cable - name = "Cable" - icon_state = "cable" diff --git a/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_ears.dmi b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_ears.dmi new file mode 100644 index 0000000000000..bc1dd357615ee Binary files /dev/null and b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_ears.dmi differ diff --git a/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_horns.dmi b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_horns.dmi new file mode 100644 index 0000000000000..343bcbb617a7b Binary files /dev/null and b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_horns.dmi differ diff --git a/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_markings.dmi b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_markings.dmi new file mode 100644 index 0000000000000..9b7ecf7befacd Binary files /dev/null and b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_markings.dmi differ diff --git a/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_screens.dmi b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_screens.dmi new file mode 100644 index 0000000000000..f8764a36140a4 Binary files /dev/null and b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_screens.dmi differ diff --git a/modular_doppler/modular_customization/accessories/icons/synthetic/synth_tail.dmi b/modular_doppler/modular_customization/accessories/icons/cybernetic/synth_tail.dmi similarity index 100% rename from modular_doppler/modular_customization/accessories/icons/synthetic/synth_tail.dmi rename to modular_doppler/modular_customization/accessories/icons/cybernetic/synth_tail.dmi diff --git a/modular_doppler/sprite_accessories/icons/facialhair.dmi b/modular_doppler/modular_customization/accessories/icons/facialhair.dmi similarity index 100% rename from modular_doppler/sprite_accessories/icons/facialhair.dmi rename to modular_doppler/modular_customization/accessories/icons/facialhair.dmi diff --git a/modular_doppler/sprite_accessories/icons/hair.dmi b/modular_doppler/modular_customization/accessories/icons/hair.dmi similarity index 66% rename from modular_doppler/sprite_accessories/icons/hair.dmi rename to modular_doppler/modular_customization/accessories/icons/hair.dmi index 3b886a38fa3ce..f2510019853a9 100644 Binary files a/modular_doppler/sprite_accessories/icons/hair.dmi and b/modular_doppler/modular_customization/accessories/icons/hair.dmi differ diff --git a/modular_doppler/modular_customization/accessories/icons/synthetic/synth_ears.dmi b/modular_doppler/modular_customization/accessories/icons/synthetic/synth_ears.dmi deleted file mode 100644 index 4081ec6be0018..0000000000000 Binary files a/modular_doppler/modular_customization/accessories/icons/synthetic/synth_ears.dmi and /dev/null differ diff --git a/modular_doppler/modular_customization/organs/external/tail.dm b/modular_doppler/modular_customization/organs/external/tail.dm index afc8ea7c30201..d518e6939f53c 100644 --- a/modular_doppler/modular_customization/organs/external/tail.dm +++ b/modular_doppler/modular_customization/organs/external/tail.dm @@ -173,18 +173,18 @@ /datum/bodypart_overlay/mutant/tail/fish/get_global_feature_list() return SSaccessories.tails_list_fish -/// Synth tail +/// Cybernetic tail // -/obj/item/organ/external/tail/synthetic +/obj/item/organ/external/tail/cybernetic preference = "feature_synth_tail" dna_block = null organ_flags = ORGAN_ROBOTIC - bodypart_overlay = /datum/bodypart_overlay/mutant/tail/synthetic + bodypart_overlay = /datum/bodypart_overlay/mutant/tail/cybernetic -/datum/bodypart_overlay/mutant/tail/synthetic +/datum/bodypart_overlay/mutant/tail/cybernetic feature_key = "tail_other" -/datum/bodypart_overlay/mutant/tail/synthetic/get_global_feature_list() +/datum/bodypart_overlay/mutant/tail/cybernetic/get_global_feature_list() return SSaccessories.tails_list_synth @@ -193,6 +193,7 @@ /obj/item/organ/external/tail/humanoid preference = "feature_humanoid_tail" dna_block = null + wag_flags = WAG_ABLE bodypart_overlay = /datum/bodypart_overlay/mutant/tail/humanoid /datum/bodypart_overlay/mutant/tail/humanoid @@ -207,6 +208,7 @@ /obj/item/organ/external/tail/alien preference = "feature_alien_tail" dna_block = null + wag_flags = WAG_ABLE bodypart_overlay = /datum/bodypart_overlay/mutant/tail/alien /datum/bodypart_overlay/mutant/tail/alien diff --git a/modular_doppler/modular_customization/organs/internal/ears.dm b/modular_doppler/modular_customization/organs/internal/ears.dm index 75478d96e6090..500df70c5c226 100644 --- a/modular_doppler/modular_customization/organs/internal/ears.dm +++ b/modular_doppler/modular_customization/organs/internal/ears.dm @@ -104,14 +104,13 @@ /datum/bodypart_overlay/mutant/ears/humanoid_ears/get_global_feature_list() return SSaccessories.ears_list_humanoid -/// Synthetic ears +/// Cybernetic ears // -/obj/item/organ/internal/ears/synthetic +/obj/item/organ/internal/ears/cybernetic preference = "feature_synth_ears" - organ_flags = ORGAN_ROBOTIC - bodypart_overlay = /datum/bodypart_overlay/mutant/ears/synthetic_ears + bodypart_overlay = /datum/bodypart_overlay/mutant/ears/cybernetic -/datum/bodypart_overlay/mutant/ears/synthetic_ears/get_global_feature_list() +/datum/bodypart_overlay/mutant/ears/cybernetic/get_global_feature_list() return SSaccessories.ears_list_synthetic /// Alien ears diff --git a/modular_doppler/modular_customization/preferences/antennae.dm b/modular_doppler/modular_customization/preferences/antennae.dm index 801dcff44a552..29d596e3f6996 100644 --- a/modular_doppler/modular_customization/preferences/antennae.dm +++ b/modular_doppler/modular_customization/preferences/antennae.dm @@ -1,6 +1,6 @@ /datum/species/regenerate_organs(mob/living/carbon/target, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) . = ..() - if(target.dna.features["moth_antennae"]) + if(target.dna.features["moth_antennae"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.features["moth_antennae"] != /datum/sprite_accessory/moth_antennae/none::name && target.dna.features["moth_antennae"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/antennae) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -24,12 +24,22 @@ /datum/preference/toggle/antennae/create_default_value() return FALSE +/datum/preference/toggle/antennae/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE + //sprite selection /datum/preference/choiced/moth_antennae category = PREFERENCE_CATEGORY_CLOTHING /datum/preference/choiced/moth_antennae/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/has_antennae = preferences.read_preference(/datum/preference/toggle/antennae) if(has_antennae == TRUE) return TRUE diff --git a/modular_doppler/modular_customization/preferences/cyber_limbs.dm b/modular_doppler/modular_customization/preferences/cyber_limbs.dm new file mode 100644 index 0000000000000..909aaaa0349c4 --- /dev/null +++ b/modular_doppler/modular_customization/preferences/cyber_limbs.dm @@ -0,0 +1,266 @@ +// What will be supplied to proc/init_possible_values and proc/apply_to_human +GLOBAL_LIST_INIT(frame_types, list( + "none", + "bare", + "synth_lizard", + "bs_one", + "bs_two", + "classic", + "e_three_n", + "hi_one", + "hi_two", + "mariinsky", + "mc", + "sgm", + "wtm", + "xmg_one", + "xmg_two", + "zhp", + )) + +// What will be showed in the drop-down +GLOBAL_LIST_INIT(frame_type_names, list( + "none" = "Species Default", + "bare" = "Bare", + "synth_lizard" = "Synthetic Lizard", + "bs_one" = "Bishop Cyberkinetics", + "bs_two" = "Bishop Cyberkinetics 2.0", + "classic" = "Android", + "e_three_n" = "E3N", + "hi_one" = "Hephaestus Industries", + "hi_two" = "Hephaestus Industries 2.0", + "mariinsky" = "Mariinsky Ballet Company", + "mc" = "Morpheus Cyberkinetics", + "sgm" = "Shellguard Munitions S-Series", + "wtm" = "Ward Takahashi Manufacturing", + "xmg_one" = "Xion Manufacturing Group", + "xmg_two" = "Xion Manufacturing Group 2.0", + "zhp" = "Zeng-Hu Pharmaceuticals", + )) + +/datum/species/regenerate_organs(mob/living/carbon/target, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) + . = ..() + if(target.dna.features["frame_list"] && !(type in GLOB.species_blacklist_no_humanoid)) + //head + if(target.dna.features["frame_list"][BODY_ZONE_HEAD] && type == /datum/species/android) + var/obj/item/bodypart/head/old_limb = target.get_bodypart(BODY_ZONE_HEAD) + old_limb.drop_limb(TRUE, FALSE, FALSE) + old_limb.moveToNullspace() + var/obj/item/bodypart/head/replacement = SSwardrobe.provide_type(target.dna.features["frame_list"][BODY_ZONE_HEAD]) + replacement.try_attach_limb(target, TRUE) + //chest + if(target.dna.features["frame_list"][BODY_ZONE_CHEST]) + var/obj/item/bodypart/chest/old_limb = target.get_bodypart(BODY_ZONE_CHEST) + old_limb.drop_limb(TRUE, FALSE, FALSE) + old_limb.moveToNullspace() + var/obj/item/bodypart/chest/replacement = SSwardrobe.provide_type(target.dna.features["frame_list"][BODY_ZONE_CHEST]) + replacement.try_attach_limb(target, TRUE) + //right arm + if(target.dna.features["frame_list"][BODY_ZONE_R_ARM]) + var/obj/item/bodypart/arm/right/old_limb = target.get_bodypart(BODY_ZONE_R_ARM) + old_limb.drop_limb(TRUE, FALSE, FALSE) + old_limb.moveToNullspace() + var/obj/item/bodypart/arm/right/replacement = SSwardrobe.provide_type(target.dna.features["frame_list"][BODY_ZONE_R_ARM]) + replacement.try_attach_limb(target, TRUE) + //left arm + if(target.dna.features["frame_list"][BODY_ZONE_L_ARM]) + var/obj/item/bodypart/arm/left/old_limb = target.get_bodypart(BODY_ZONE_L_ARM) + old_limb.drop_limb(TRUE, FALSE, FALSE) + old_limb.moveToNullspace() + var/obj/item/bodypart/arm/left/replacement = SSwardrobe.provide_type(target.dna.features["frame_list"][BODY_ZONE_L_ARM]) + replacement.try_attach_limb(target, TRUE) + //right leg + if(target.dna.features["frame_list"][BODY_ZONE_R_LEG]) + var/obj/item/bodypart/leg/right/old_limb = target.get_bodypart(BODY_ZONE_R_LEG) + old_limb.drop_limb(TRUE, FALSE, FALSE) + old_limb.moveToNullspace() + var/obj/item/bodypart/leg/right/replacement = SSwardrobe.provide_type(target.dna.features["frame_list"][BODY_ZONE_R_LEG]) + replacement.try_attach_limb(target, TRUE) + //left leg + if(target.dna.features["frame_list"][BODY_ZONE_L_LEG]) + var/obj/item/bodypart/leg/left/old_limb = target.get_bodypart(BODY_ZONE_L_LEG) + old_limb.drop_limb(TRUE, FALSE, FALSE) + old_limb.moveToNullspace() + var/obj/item/bodypart/leg/left/replacement = SSwardrobe.provide_type(target.dna.features["frame_list"][BODY_ZONE_L_LEG]) + replacement.try_attach_limb(target, TRUE) + return . + +// Head +/datum/preference/choiced/head_type + main_feature_name = "Add Limb: Head" + savefile_key = "head_type" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + +/datum/preference/choiced/head_type/compile_constant_data() + var/list/data = ..() + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.frame_type_names + return data + +/datum/preference/choiced/head_type/init_possible_values() + return GLOB.frame_types + +/datum/preference/choiced/head_type/apply_to_human(mob/living/carbon/human/target, value) + if(value == "none") + return + LAZYADDASSOC(target.dna.features["frame_list"], BODY_ZONE_HEAD, text2path("/obj/item/bodypart/head/robot/android/[value]")) + +/datum/preference/choiced/head_type/create_default_value() + return "none" + +/datum/preference/choiced/head_type/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species == /datum/species/android) // lifting this restriction would require code for the head's internal organs to become cybernetic too + return TRUE + return FALSE + +// Chest +/datum/preference/choiced/chest_type + main_feature_name = "Add Limb: Chest" + savefile_key = "chest_type" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + +/datum/preference/choiced/chest_type/compile_constant_data() + var/list/data = ..() + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.frame_type_names + return data + +/datum/preference/choiced/chest_type/init_possible_values() + return GLOB.frame_types + +/datum/preference/choiced/chest_type/apply_to_human(mob/living/carbon/human/target, value) + if(value == "none") + return + LAZYADDASSOC(target.dna.features["frame_list"], BODY_ZONE_CHEST, text2path("/obj/item/bodypart/chest/robot/android/[value]")) + +/datum/preference/choiced/chest_type/create_default_value() + return "none" + +/datum/preference/choiced/chest_type/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_humanoid) + return FALSE + return TRUE + +// Right arm +/datum/preference/choiced/arm_r_type + main_feature_name = "Add Limb: R-Arm" + savefile_key = "arm_r_type" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + +/datum/preference/choiced/arm_r_type/compile_constant_data() + var/list/data = ..() + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.frame_type_names + return data + +/datum/preference/choiced/arm_r_type/init_possible_values() + return GLOB.frame_types + +/datum/preference/choiced/arm_r_type/apply_to_human(mob/living/carbon/human/target, value) + if(value == "none") + return + LAZYADDASSOC(target.dna.features["frame_list"], BODY_ZONE_R_ARM, text2path("/obj/item/bodypart/arm/right/robot/android/[value]")) + +/datum/preference/choiced/arm_r_type/create_default_value() + return "none" + +/datum/preference/choiced/arm_r_type/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_humanoid) + return FALSE + return TRUE + +// Left arm +/datum/preference/choiced/arm_l_type + main_feature_name = "Add Limb: L-Arm" + savefile_key = "arm_l_type" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + +/datum/preference/choiced/arm_l_type/compile_constant_data() + var/list/data = ..() + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.frame_type_names + return data + +/datum/preference/choiced/arm_l_type/init_possible_values() + return GLOB.frame_types + +/datum/preference/choiced/arm_l_type/apply_to_human(mob/living/carbon/human/target, value) + if(value == "none") + return + LAZYADDASSOC(target.dna.features["frame_list"], BODY_ZONE_L_ARM, text2path("/obj/item/bodypart/arm/left/robot/android/[value]")) + +/datum/preference/choiced/arm_l_type/create_default_value() + return "none" + +/datum/preference/choiced/arm_l_type/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_humanoid) + return FALSE + return TRUE + +// Right leg +/datum/preference/choiced/leg_r_type + main_feature_name = "Add Limb: R-Leg" + savefile_key = "leg_r_type" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + +/datum/preference/choiced/leg_r_type/compile_constant_data() + var/list/data = ..() + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.frame_type_names + return data + +/datum/preference/choiced/leg_r_type/init_possible_values() + return GLOB.frame_types + +/datum/preference/choiced/leg_r_type/apply_to_human(mob/living/carbon/human/target, value) + if(value == "none") + return + LAZYADDASSOC(target.dna.features["frame_list"], BODY_ZONE_R_LEG, text2path("/obj/item/bodypart/leg/right/robot/android/[value]")) + +/datum/preference/choiced/leg_r_type/create_default_value() + return "none" + +/datum/preference/choiced/leg_r_type/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_humanoid) + return FALSE + return TRUE + +// Left leg +/datum/preference/choiced/leg_l_type + main_feature_name = "Add Limb: L-Leg" + savefile_key = "leg_l_type" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + +/datum/preference/choiced/leg_l_type/compile_constant_data() + var/list/data = ..() + data[CHOICED_PREFERENCE_DISPLAY_NAMES] = GLOB.frame_type_names + return data + +/datum/preference/choiced/leg_l_type/init_possible_values() + return GLOB.frame_types + +/datum/preference/choiced/leg_l_type/apply_to_human(mob/living/carbon/human/target, value) + if(value == "none") + return + LAZYADDASSOC(target.dna.features["frame_list"], BODY_ZONE_L_LEG, text2path("/obj/item/bodypart/leg/left/robot/android/[value]")) + +/datum/preference/choiced/leg_l_type/create_default_value() + return "none" + +/datum/preference/choiced/leg_l_type/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_humanoid) + return FALSE + return TRUE diff --git a/modular_doppler/modular_customization/preferences/ears.dm b/modular_doppler/modular_customization/preferences/ears.dm index 459099545c80b..827cd72ba3a03 100644 --- a/modular_doppler/modular_customization/preferences/ears.dm +++ b/modular_doppler/modular_customization/preferences/ears.dm @@ -27,7 +27,7 @@ ears_list_fish = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears_more/fish)["default_sprites"] ears_list_bug = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears_more/bug)["default_sprites"] ears_list_humanoid = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears_more/humanoid)["default_sprites"] - ears_list_synthetic = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears_more/synthetic)["default_sprites"] + ears_list_synthetic = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears_more/cybernetic)["default_sprites"] ears_list_alien = init_sprite_accessory_subtypes(/datum/sprite_accessory/ears_more/alien)["default_sprites"] /datum/dna @@ -36,7 +36,7 @@ /datum/species/regenerate_organs(mob/living/carbon/target, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) . = ..() - if(target.dna.features["ears"]) + if(target.dna.features["ears"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.ear_type == NO_VARIATION) return . else if(target.dna.features["ears"] != /datum/sprite_accessory/ears/none::name && target.dna.features["ears"] != /datum/sprite_accessory/blank::name) @@ -44,11 +44,6 @@ var/obj/item/organ/replacement = SSwardrobe.provide_type(organ_path) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) return . - var/obj/item/organ/internal/ears/old_part = target.get_organ_slot(ORGAN_SLOT_EARS) - if(istype(old_part)) - old_part.Remove(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) - old_part.moveToNullspace() - /// Dropdown to select which ears you'll be rocking /datum/preference/choiced/ear_variation @@ -68,6 +63,12 @@ if(chosen_variation == NO_VARIATION) target.dna.features["ears"] = /datum/sprite_accessory/ears/none::name +/datum/preference/choiced/ear_variation/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE /// All current ear types to choose from // Cat @@ -79,6 +80,9 @@ /datum/preference/choiced/ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == CAT) return TRUE @@ -110,6 +114,9 @@ /datum/preference/choiced/lizard_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == LIZARD) return TRUE @@ -140,6 +147,9 @@ /datum/preference/choiced/fox_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == FOX) return TRUE @@ -170,6 +180,9 @@ /datum/preference/choiced/dog_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == DOG) return TRUE @@ -200,6 +213,9 @@ /datum/preference/choiced/bunny_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == BUNNY) return TRUE @@ -230,6 +246,9 @@ /datum/preference/choiced/bird_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == BIRD) return TRUE @@ -260,6 +279,9 @@ /datum/preference/choiced/mouse_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == MOUSE) return TRUE @@ -290,6 +312,9 @@ /datum/preference/choiced/monkey_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == MONKEY) return TRUE @@ -320,6 +345,9 @@ /datum/preference/choiced/deer_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == DEER) return TRUE @@ -350,6 +378,9 @@ /datum/preference/choiced/fish_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == FISH) return TRUE @@ -380,6 +411,9 @@ /datum/preference/choiced/bug_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == BUG) return TRUE @@ -410,6 +444,9 @@ /datum/preference/choiced/humanoid_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == HUMANOID) return TRUE @@ -426,7 +463,7 @@ var/datum/sprite_accessory/chosen_ears = SSaccessories.ears_list_humanoid[value] return generate_ears_icon(chosen_ears) -// Synthetic +// Cybernetic /datum/preference/choiced/synthetic_ears savefile_key = "feature_synth_ears" savefile_identifier = PREFERENCE_CHARACTER @@ -440,16 +477,19 @@ /datum/preference/choiced/synthetic_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) - if(chosen_variation == SYNTHETIC) + if(chosen_variation == CYBERNETIC) return TRUE return FALSE /datum/preference/choiced/synthetic_ears/create_default_value() - return /datum/sprite_accessory/ears_more/synthetic/none::name + return /datum/sprite_accessory/ears_more/cybernetic/none::name /datum/preference/choiced/synthetic_ears/apply_to_human(mob/living/carbon/human/target, value) - if(target.dna.ear_type == SYNTHETIC) + if(target.dna.ear_type == CYBERNETIC) target.dna.features["ears"] = value /datum/preference/choiced/synthetic_ears/icon_for(value) @@ -470,6 +510,9 @@ /datum/preference/choiced/alien_ears/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/ear_variation) if(chosen_variation == ALIEN) return TRUE diff --git a/modular_doppler/modular_customization/preferences/fluff.dm b/modular_doppler/modular_customization/preferences/fluff.dm index 1e2bb08024b63..709ef1b4fea28 100644 --- a/modular_doppler/modular_customization/preferences/fluff.dm +++ b/modular_doppler/modular_customization/preferences/fluff.dm @@ -7,7 +7,7 @@ /datum/species/regenerate_organs(mob/living/carbon/target, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) . = ..() - if(target.dna.features["fluff"]) + if(target.dna.features["fluff"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.features["fluff"] != /datum/sprite_accessory/fluff/none::name && target.dna.features["fluff"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/fluff) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -31,6 +31,13 @@ /datum/preference/toggle/fluff/create_default_value() return FALSE +/datum/preference/toggle/fluff/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE + /datum/preference/choiced/fluff savefile_key = "fluff" savefile_identifier = PREFERENCE_CHARACTER @@ -44,6 +51,9 @@ /datum/preference/choiced/fluff/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/has_fluff = preferences.read_preference(/datum/preference/toggle/fluff) if(has_fluff) return TRUE diff --git a/modular_doppler/modular_customization/preferences/frills.dm b/modular_doppler/modular_customization/preferences/frills.dm index 1955ad840eb65..788d84ea09ab6 100644 --- a/modular_doppler/modular_customization/preferences/frills.dm +++ b/modular_doppler/modular_customization/preferences/frills.dm @@ -35,9 +35,16 @@ /datum/preference/toggle/frills/create_default_value() return FALSE +/datum/preference/toggle/frills/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE + /datum/species/regenerate_organs(mob/living/carbon/target, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) . = ..() - if(target.dna.features["frills"]) + if(target.dna.features["frills"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.features["frills"] != /datum/sprite_accessory/frills/none::name && target.dna.features["frills"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/frills) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -53,6 +60,9 @@ /datum/preference/choiced/lizard_frills/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/has_frills = preferences.read_preference(/datum/preference/toggle/frills) if(has_frills == TRUE) return TRUE diff --git a/modular_doppler/modular_customization/preferences/horns.dm b/modular_doppler/modular_customization/preferences/horns.dm index 9aa9251c09885..a47a60cd1fedc 100644 --- a/modular_doppler/modular_customization/preferences/horns.dm +++ b/modular_doppler/modular_customization/preferences/horns.dm @@ -53,9 +53,16 @@ /datum/preference/toggle/horns/create_default_value() return FALSE +/datum/preference/toggle/horns/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE + /datum/species/regenerate_organs(mob/living/carbon/target, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) . = ..() - if(target.dna.features["horns"]) + if(target.dna.features["horns"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.features["horns"] != /datum/sprite_accessory/horns/none::name && target.dna.features["horns"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/horns) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -71,6 +78,9 @@ /datum/preference/choiced/lizard_horns/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/has_horns = preferences.read_preference(/datum/preference/toggle/horns) if(has_horns == TRUE) return TRUE diff --git a/modular_doppler/modular_customization/preferences/snout.dm b/modular_doppler/modular_customization/preferences/snout.dm index b5c1809acc639..f40ca4532386c 100644 --- a/modular_doppler/modular_customization/preferences/snout.dm +++ b/modular_doppler/modular_customization/preferences/snout.dm @@ -1,6 +1,6 @@ /datum/species/regenerate_organs(mob/living/carbon/target, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) . = ..() - if(target.dna.features["snout"]) + if(target.dna.features["snout"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.features["snout"] != /datum/sprite_accessory/snouts/none::name && target.dna.features["snout"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/snout) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -24,11 +24,21 @@ /datum/preference/toggle/snout/create_default_value() return FALSE +/datum/preference/toggle/snout/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE + /datum/preference/choiced/lizard_snout category = PREFERENCE_CATEGORY_CLOTHING /datum/preference/choiced/lizard_snout/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/has_snout = preferences.read_preference(/datum/preference/toggle/snout) if(has_snout == TRUE) return TRUE diff --git a/modular_doppler/modular_customization/preferences/tail.dm b/modular_doppler/modular_customization/preferences/tail.dm index a582045636623..5a564a1c4f6f9 100644 --- a/modular_doppler/modular_customization/preferences/tail.dm +++ b/modular_doppler/modular_customization/preferences/tail.dm @@ -22,7 +22,7 @@ tails_list_deer = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/deer)["default_sprites"] tails_list_fish = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/fish)["default_sprites"] tails_list_bug = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/bug)["default_sprites"] - tails_list_synth = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/synthetic)["default_sprites"] + tails_list_synth = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/cybernetic)["default_sprites"] tails_list_humanoid = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/humanoid)["default_sprites"] tails_list_alien = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/alien)["default_sprites"] @@ -37,19 +37,19 @@ if(!ishuman(target)) return - if(target.dna.features["tail_lizard"] != /datum/sprite_accessory/tails/lizard/none::name && target.dna.features["tail_lizard"] != /datum/sprite_accessory/blank::name) + if(target.dna.features["tail_lizard"] != /datum/sprite_accessory/tails/lizard/none::name && !(type in GLOB.species_blacklist_no_mutant) && target.dna.features["tail_lizard"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/tail/lizard) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) return . - else if(target.dna.features["tail_cat"] != /datum/sprite_accessory/tails/human/none::name && target.dna.features["tail_cat"] != /datum/sprite_accessory/blank::name) + else if(target.dna.features["tail_cat"] != /datum/sprite_accessory/tails/human/none::name && !(type in GLOB.species_blacklist_no_mutant) && target.dna.features["tail_cat"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/tail/cat) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) return . - else if(target.dna.features["tail_monkey"] != /datum/sprite_accessory/tails/monkey/none::name && target.dna.features["tail_monkey"] != /datum/sprite_accessory/blank::name) + else if(target.dna.features["tail_monkey"] != /datum/sprite_accessory/tails/monkey/none::name && !(type in GLOB.species_blacklist_no_mutant) && target.dna.features["tail_monkey"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/tail/monkey) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) return . - else if((target.dna.features["tail_other"] != /datum/sprite_accessory/tails/lizard/none::name && target.dna.features["tail_other"] != /datum/sprite_accessory/blank::name) && (target.dna.tail_type != NO_VARIATION)) + else if((target.dna.features["tail_other"] != /datum/sprite_accessory/tails/lizard/none::name && !(type in GLOB.species_blacklist_no_mutant) && target.dna.features["tail_other"] != /datum/sprite_accessory/blank::name) && (target.dna.tail_type != NO_VARIATION)) var/obj/item/organ/organ_path = text2path("/obj/item/organ/external/tail/[target.dna.tail_type]") var/obj/item/organ/replacement = SSwardrobe.provide_type(organ_path) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -75,6 +75,13 @@ /datum/preference/choiced/tail_variation/init_possible_values() return list(NO_VARIATION) + (GLOB.mutant_variations) +/datum/preference/choiced/tail_variation/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE + /datum/preference/choiced/tail_variation/apply_to_human(mob/living/carbon/human/target, chosen_variation) // Read by the regenerate_organs() proc to know what organ subtype to grant target.dna.tail_type = chosen_variation @@ -113,6 +120,9 @@ /datum/preference/choiced/lizard_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == LIZARD) return TRUE @@ -137,6 +147,9 @@ /datum/preference/choiced/tail_human/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == CAT) return TRUE @@ -166,6 +179,9 @@ /datum/preference/choiced/dog_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == DOG) return TRUE @@ -196,6 +212,9 @@ /datum/preference/choiced/fox_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == FOX) return TRUE @@ -226,6 +245,9 @@ /datum/preference/choiced/bunny_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == BUNNY) return TRUE @@ -256,6 +278,9 @@ /datum/preference/choiced/mouse_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == MOUSE) return TRUE @@ -286,6 +311,9 @@ /datum/preference/choiced/bird_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == BIRD) return TRUE @@ -311,6 +339,9 @@ /datum/preference/choiced/monkey_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == MONKEY) return TRUE @@ -340,6 +371,9 @@ /datum/preference/choiced/deer_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == DEER) return TRUE @@ -370,6 +404,9 @@ /datum/preference/choiced/fish_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == FISH) return TRUE @@ -400,6 +437,9 @@ /datum/preference/choiced/bug_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == BUG) return TRUE @@ -430,16 +470,19 @@ /datum/preference/choiced/synth_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) - if(chosen_variation == SYNTHETIC) + if(chosen_variation == CYBERNETIC) return TRUE return FALSE /datum/preference/choiced/synth_tail/create_default_value() - return /datum/sprite_accessory/tails/synthetic/none::name + return /datum/sprite_accessory/tails/cybernetic/none::name /datum/preference/choiced/synth_tail/apply_to_human(mob/living/carbon/human/target, value) - if(target.dna.tail_type == SYNTHETIC) + if(target.dna.tail_type == CYBERNETIC) target.dna.features["tail_other"] = value /datum/preference/choiced/synth_tail/icon_for(value) @@ -460,6 +503,9 @@ /datum/preference/choiced/humanoid_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == HUMANOID) return TRUE @@ -490,6 +536,9 @@ /datum/preference/choiced/alien_tail/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/tail_variation) if(chosen_variation == ALIEN) return TRUE diff --git a/modular_doppler/modular_customization/preferences/wings.dm b/modular_doppler/modular_customization/preferences/wings.dm index 1b38e70719a88..a8462d0b6ef06 100644 --- a/modular_doppler/modular_customization/preferences/wings.dm +++ b/modular_doppler/modular_customization/preferences/wings.dm @@ -17,14 +17,14 @@ if(!ishuman(target)) return - if(target.dna.features["moth_wings"]) + if(target.dna.features["moth_wings"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.wing_type == NO_VARIATION) return . if((target.dna.features["moth_wings"] != /datum/sprite_accessory/moth_wings/none::name && target.dna.features["moth_wings"] != /datum/sprite_accessory/blank::name)) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/wings/moth) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) return . - if(target.dna.features["wings"]) + if(target.dna.features["wings"] && !(type in GLOB.species_blacklist_no_mutant)) if(target.dna.features["wings"] != /datum/sprite_accessory/wings_more/none::name && target.dna.features["wings"] != /datum/sprite_accessory/blank::name) var/obj/item/organ/replacement = SSwardrobe.provide_type(/obj/item/organ/external/wings/more) replacement.Insert(target, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -60,6 +60,13 @@ if("Moth Wings") target.dna.features["wings"] = /datum/sprite_accessory/wings_more/none::name +/datum/preference/choiced/wing_variation/is_accessible(datum/preferences/preferences) + . = ..() + var/species = preferences.read_preference(/datum/preference/choiced/species) + if(species in GLOB.species_blacklist_no_mutant) + return FALSE + return TRUE + // Wings /datum/preference/choiced/wings savefile_key = "feature_wings" @@ -71,6 +78,9 @@ /datum/preference/choiced/wings/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/wing_variation) if(chosen_variation == "Wings") return TRUE @@ -95,6 +105,9 @@ /datum/preference/choiced/moth_wings/is_accessible(datum/preferences/preferences) . = ..() + var/datum/species/species = preferences.read_preference(/datum/preference/choiced/species) + if(species.type in GLOB.species_blacklist_no_mutant) + return FALSE var/chosen_variation = preferences.read_preference(/datum/preference/choiced/wing_variation) if(chosen_variation == "Moth Wings") return TRUE diff --git a/modular_doppler/modular_food_drinks_and_chems/chemistry_reagents.dm b/modular_doppler/modular_food_drinks_and_chems/chemistry_reagents.dm index 744cf7e509cd0..dad2f93dd66ae 100644 --- a/modular_doppler/modular_food_drinks_and_chems/chemistry_reagents.dm +++ b/modular_doppler/modular_food_drinks_and_chems/chemistry_reagents.dm @@ -70,9 +70,14 @@ to_chat(M, span_notice("[pick("I feel oddly calm.", "I feel relaxed.", "Mew?")]")) ..() -/* + #define DERMAGEN_SCAR_FIX_AMOUNT 10 +/datum/chemical_reaction/medicine/dermagen + results = list(/datum/reagent/medicine/dermagen = 5) + required_reagents = list(/datum/reagent/consumable/ethanol = 4, /datum/reagent/medicine/c2/synthflesh = 3, /datum/reagent/medicine/mine_salve = 3) + mix_message = "The slurry congeals into a thick cream." + /datum/reagent/medicine/dermagen name = "Dermagen" description = "Heals scars formed by past physical trauma when applied. Minimum 10u needed, only works when applied topically." @@ -97,7 +102,7 @@ qdel(i) #undef DERMAGEN_SCAR_FIX_AMOUNT -*/ + /** * Check if this holder contains a reagent with a `chemical_flags_doppler` containing this flag. diff --git a/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/alcohol_reagents.dm b/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/alcohol_reagents.dm index b529340534249..45977cf15c031 100644 --- a/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/alcohol_reagents.dm +++ b/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/alcohol_reagents.dm @@ -1,12 +1,7 @@ -/*STUFF WE CAN'T USE YET BECAUSE WE HAVEN'T PORTED THEIR PRECURSORS - -// Modular Booze REAGENTS, see the following file for the mixes: modular_nova\modules\customization\modules\food_and_drinks\recipes\drinks_recipes.dm - /datum/reagent/consumable/ethanol/whiskey process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC //let's not force the detective to change his alcohol brand -*/ -/*SYNTHETIC DRINKS +//SYNTHETIC DRINKS /datum/reagent/consumable/ethanol/synthanol name = "Synthanol" description = "A runny liquid with conductive capacities. Its effects on synthetics are similar to those of alcohol on organics." @@ -120,9 +115,9 @@ icon_state = "synthignonglass" name = "glass of synthignon" desc = "Someone mixed good wine and robot booze. Romantic, but atrocious." -*/ -// Other Booze + +// Other Booze /datum/reagent/consumable/ethanol/bloody_mary chemical_flags_doppler = REAGENT_BLOOD_REGENERATING @@ -374,7 +369,7 @@ else quality = DRINK_GOOD return ..() -/* + /datum/reagent/consumable/ethanol/oil_drum name = "Oil Drum" color = "#000000" //(0, 0, 0) @@ -395,7 +390,7 @@ else quality = DRINK_GOOD return ..() -*/ + /datum/reagent/consumable/ethanol/nord_king name = "Nord King" color = "#EB1010" //(235, 16, 16) @@ -445,7 +440,7 @@ . = ..() if(drinker.blood_volume < BLOOD_VOLUME_NORMAL) drinker.blood_volume = min(drinker.blood_volume + (1 * REM * seconds_per_tick), BLOOD_VOLUME_NORMAL) //Same as Bloody Mary, as it is roughly the same difficulty to make. Gives hemophages a bit more choices to supplant their blood levels. -/* + /datum/reagent/consumable/ethanol/abduction_fruit name = "Abduction Fruit" color = "#DEFACD" //(222, 250, 205) @@ -461,12 +456,12 @@ desc = "Mixed fruits that were never meant to be mixed..." /datum/reagent/consumable/ethanol/abduction_fruit/expose_mob(mob/living/exposed_mob, methods, reac_volume) - if(isabductor(exposed_mob) || isxenohybrid(exposed_mob)) + if(isabductor(exposed_mob)) // isxenohybrid(exposed_mob) quality = RACE_DRINK else quality = DRINK_GOOD return ..() -*/ + /datum/reagent/consumable/ethanol/bug_zapper name = "Bug Zapper" color = "#F5882A" //(222, 250, 205) @@ -529,7 +524,7 @@ else quality = DRINK_GOOD return ..() -/* + /datum/reagent/consumable/ethanol/jell_wyrm name = "Jell Wyrm" color = "#FF6200" //(255, 98, 0) @@ -553,14 +548,14 @@ #define JELLWYRM_DISGUST 25 /datum/reagent/consumable/ethanol/jell_wyrm/expose_mob(mob/living/exposed_mob, methods, reac_volume) - if(isjellyperson(exposed_mob) || isslimeperson(exposed_mob) || isluminescent(exposed_mob)) + if(isjellyperson(exposed_mob)) quality = RACE_DRINK else //if youre not a slime, jell wyrm should be GROSS exposed_mob.adjust_disgust(JELLWYRM_DISGUST) return ..() #undef JELLWYRM_DISGUST -*/ + /datum/reagent/consumable/ethanol/laval_spit //Yes Laval name = "Laval Spit" color = "#DE3009" //(222, 48, 9) diff --git a/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/drinks.dm b/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/drinks.dm index f4562c5599628..6723717241029 100644 --- a/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/drinks.dm +++ b/modular_doppler/modular_food_drinks_and_chems/food_and_drinks/drinks.dm @@ -76,12 +76,12 @@ list_reagents = list(/datum/reagent/fuel = 25, /datum/reagent/carbondioxide = 5) custom_price = PAYCHECK_LOWER * 1.2 -/*/obj/item/reagent_containers/cup/soda_cans/doppler/synthanolcan +/obj/item/reagent_containers/cup/soda_cans/doppler/synthanolcan name = "Silly Cone's Synthanol" desc = "A recompiling can of synthanol." icon_state = "synthanolcan" list_reagents = list(/datum/reagent/consumable/ethanol/synthanol = 30) - custom_price = PAYCHECK_CREW*/ + custom_price = PAYCHECK_CREW #undef SODA_FIZZINESS_THROWN #undef SODA_FIZZINESS_SHAKE diff --git a/modular_doppler/modular_medical/code/carbon_update_icons.dm b/modular_doppler/modular_medical/code/carbon_update_icons.dm index 5f5b69c3041a2..7448813256fa5 100644 --- a/modular_doppler/modular_medical/code/carbon_update_icons.dm +++ b/modular_doppler/modular_medical/code/carbon_update_icons.dm @@ -1,7 +1,7 @@ /mob/living/carbon/proc/update_bandage_overlays() remove_overlay(BANDAGE_LAYER) - var/mutable_appearance/overlays = mutable_appearance('modular_doppler/modular_medical/icons/on_limb_overlays.dmi', "", -BANDAGE_LAYER) + var/mutable_appearance/overlays = mutable_appearance('modular_doppler/modular_medical/icons/wounds_onmob.dmi', "", -BANDAGE_LAYER) overlays_standing[BANDAGE_LAYER] = overlays for(var/b in bodyparts) diff --git a/modular_doppler/modular_medical/code/medical_lefthand.dmi b/modular_doppler/modular_medical/code/medical_lefthand.dmi new file mode 100644 index 0000000000000..5c5e34a5e68d6 Binary files /dev/null and b/modular_doppler/modular_medical/code/medical_lefthand.dmi differ diff --git a/modular_doppler/modular_medical/code/medical_righthand.dmi b/modular_doppler/modular_medical/code/medical_righthand.dmi new file mode 100644 index 0000000000000..569d3665f5c11 Binary files /dev/null and b/modular_doppler/modular_medical/code/medical_righthand.dmi differ diff --git a/modular_doppler/modular_medical/code/medkit.dm b/modular_doppler/modular_medical/code/medkit.dm new file mode 100644 index 0000000000000..3972a90700ecb --- /dev/null +++ b/modular_doppler/modular_medical/code/medkit.dm @@ -0,0 +1,208 @@ +/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit + name = "synthetic treatment kit" + desc = "A \"surgical\" duffel bag containing everything you need to treat the worst and best of inorganic wounds." + +/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit/PopulateContents() // yes, this is all within the storage capacity + // Slash/Pierce wound tools - can reduce intensity of electrical damage (wires can fix generic burn damage) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/wirecutters(src) + // Blunt/Brute tools + new /obj/item/weldingtool/largetank(src) // Used for repairing blunt damage or heating metal at T3 blunt + new /obj/item/screwdriver(src) // Used for fixing T1 blunt or securing internals of T2/3 blunt + new /obj/item/bonesetter(src) + // Clothing items + new /obj/item/clothing/head/utility/welding(src) + new /obj/item/clothing/gloves/color/black(src) // Protects from T3 mold metal step + new /obj/item/clothing/glasses/hud/diagnostic(src) // When worn, generally improves wound treatment quality + // Reagent containers + new /obj/item/reagent_containers/spray/hercuri/chilled(src) // Highly effective (specifically coded to be) against burn wounds + new /obj/item/reagent_containers/spray/dinitrogen_plasmide(src) // same + // Generic medical items + new /obj/item/stack/medical/gauze/twelve(src) + new /obj/item/healthanalyzer(src) + new /obj/item/healthanalyzer/simple(src) // Buffs wound treatment and gives details of wounds it scans + // "Ghetto" tools, things you shouldnt ideally use but you might have to + new /obj/item/stack/medical/bone_gel(src) // Ghetto T2/3 option for securing internals + new /obj/item/plunger(src) // Can be used to mold heated metal at T3 + +// a treatment kit with extra space and more tools/upgraded tools, like a crowbar, insuls, a reinforced plunger, a crowbar and wrench +/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit/trauma + name = "synthetic trauma kit" + desc = "A \"surgical\" duffel bag containing everything you need to treat the worst and best of inorganic wounds. This one has extra tools and space \ + for treatment of the WORST of the worst! However, it's highly specialized interior means it can ONLY hold synthetic repair tools." + storage_type = /datum/storage/duffel/synth_trauma_kit + +/datum/storage/duffel/synth_trauma_kit + exception_max = 6 + max_slots = 28 + max_total_storage = 36 + +/datum/storage/duffel/synth_trauma_kit/New(atom/parent, max_slots, max_specific_storage, max_total_storage, numerical_stacking, allow_quick_gather, allow_quick_empty, collection_mode, attack_hand_interact) + . = ..() + + var/static/list/exception_cache = typecacheof(list( + // Mainly just stacks, with the exception of pill bottles and sprays + /obj/item/stack/cable_coil, + /obj/item/stack/medical/gauze, + /obj/item/reagent_containers/spray, + /obj/item/stack/medical/bone_gel, + /obj/item/rcd_ammo, + /obj/item/storage/pill_bottle, + )) + + var/static/list/can_hold_list = list( + // Stacks + /obj/item/stack/cable_coil, + /obj/item/stack/medical/gauze, + /obj/item/stack/medical/bone_gel, + // Reagent containers, for synth medicine + /obj/item/reagent_containers/spray, + /obj/item/storage/pill_bottle, + /obj/item/reagent_containers/pill, + /obj/item/reagent_containers/cup, + /obj/item/reagent_containers/syringe, + // Tools, including tools you might not want to use but might have to (hemostat/retractor/etc) + /obj/item/screwdriver, + /obj/item/wrench, + /obj/item/crowbar, + /obj/item/weldingtool, + /obj/item/bonesetter, + /obj/item/wirecutters, + /obj/item/hemostat, + /obj/item/retractor, + /obj/item/cautery, + /obj/item/plunger, + // RCD stuff - RCDs can easily treat the 1st step of T3 blunt + /obj/item/construction/rcd, + /obj/item/rcd_ammo, + // Clothing items + /obj/item/clothing/gloves, + /obj/item/clothing/glasses/hud/health, + /obj/item/clothing/glasses/hud/diagnostic, + /obj/item/clothing/glasses/welding, + /obj/item/clothing/glasses/sunglasses, // still provides some welding protection + /obj/item/clothing/head/utility/welding, + /obj/item/clothing/mask/gas/welding, + // Generic health items + /obj/item/healthanalyzer, + ) + exception_hold = exception_cache + + // We keep the type list and the typecache list separate... + var/static/list/can_hold_cache = typecacheof(can_hold_list) + can_hold = can_hold_cache + + //...So we can run this without it generating a line for every subtype. + can_hold_description = generate_hold_desc(can_hold_list) + +/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit/trauma/PopulateContents() // yes, this is all within the storage capacity + // Slash/Pierce wound tools - can reduce intensity of electrical damage (wires can fix generic burn damage) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/wirecutters(src) + // Blunt/Brute tools + new /obj/item/weldingtool/hugetank(src) // Used for repairing blunt damage or heating metal at T3 blunt + new /obj/item/screwdriver(src) // Used for fixing T1 blunt or securing internals of T2/3 blunt + new /obj/item/wrench(src) // Same as screwdriver for T2/3 + new /obj/item/crowbar(src) // Ghetto fixing option for T2/3 blunt + new /obj/item/bonesetter(src) + // Clothing items + new /obj/item/clothing/head/utility/welding(src) + new /obj/item/clothing/gloves/color/black(src) // Protects from T3 mold metal step + new /obj/item/clothing/gloves/color/yellow(src) // Protects from electrical damage and crowbarring a blunt wound + new /obj/item/clothing/glasses/hud/diagnostic(src) // When worn, generally improves wound treatment quality + // Reagent containers + new /obj/item/reagent_containers/spray/hercuri/chilled(src) // Highly effective (specifically coded to be) against burn wounds + new /obj/item/reagent_containers/spray/dinitrogen_plasmide(src) // same + // Generic medical items + new /obj/item/stack/medical/gauze/twelve(src) + new /obj/item/healthanalyzer(src) + new /obj/item/healthanalyzer/simple(src) // Buffs wound treatment and gives details of wounds it scans + // "Ghetto" tools, things you shouldnt ideally use but you might have to + new /obj/item/stack/medical/bone_gel(src) // Ghetto T2/3 option for securing internals + new /obj/item/plunger/reinforced(src) // Can be used to mold heated metal at T3 + +// advanced tools, an RCD, chems, etc etc. dont give this one to the crew early in the round +/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit/trauma/advanced + name = "advanced synth trauma kit" + desc = "An \"advanced\" \"surgical\" duffel bag containing absolutely everything you need to treat the worst and best of inorganic wounds. \ + This one has extra tools and space for treatment of the ones even worse than the WORST of the worst! However, its highly specialized interior \ + means it can ONLY hold synthetic repair tools." + + storage_type = /datum/storage/duffel/synth_trauma_kit/advanced + +/datum/storage/duffel/synth_trauma_kit/advanced + exception_max = 10 + max_slots = 33 + max_total_storage = 50 + +/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit/trauma/advanced/PopulateContents() // yes, this is all within the storage capacity + // Slash/Pierce wound tools - can reduce intensity of electrical damage (wires can fix generic burn damage) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/crowbar/power(src) // jaws of life - wirecutters and crowbar + // Blunt/Brute tools + new /obj/item/weldingtool/experimental(src) // Used for repairing blunt damage or heating metal at T3 blunt + new /obj/item/screwdriver/power(src) // drill - screwdriver and wrench + new /obj/item/construction/rcd/loaded(src) // lets you instantly heal T3 blunt step 1 + new /obj/item/bonesetter(src) + // Clothing items + new /obj/item/clothing/head/utility/welding(src) + new /obj/item/clothing/gloves/combat(src) // insulated AND heat-resistant + new /obj/item/clothing/glasses/hud/diagnostic(src) // When worn, generally improves wound treatment quality + // Reagent containers + new /obj/item/reagent_containers/spray/hercuri/chilled(src) // Highly effective (specifically coded to be) against burn wounds + new /obj/item/reagent_containers/spray/hercuri/chilled(src) // 2 of them + new /obj/item/reagent_containers/spray/dinitrogen_plasmide(src) // same + new /obj/item/reagent_containers/spray/dinitrogen_plasmide(src) + new /obj/item/storage/pill_bottle/nanite_slurry(src) // Heals blunt/burn + new /obj/item/storage/pill_bottle/liquid_solder(src) // Heals brain damage + new /obj/item/storage/pill_bottle/system_cleaner(src) // Heals toxin damage and purges chems + // Generic medical items + new /obj/item/stack/medical/gauze/twelve(src) + new /obj/item/healthanalyzer/advanced(src) // advanced, not a normal analyzer + new /obj/item/healthanalyzer/simple(src) // Buffs wound treatment and gives details of wounds it scans + // "Ghetto" tools, things you shouldn't ideally use but you might have to + new /obj/item/stack/medical/bone_gel(src) // Ghetto T2/3 option for securing internals + new /obj/item/plunger/reinforced(src) // Can be used to mold heated metal at T3 blunt + +/obj/item/storage/backpack/duffelbag/science/synth_treatment_kit/trauma/advanced/unzipped + zipped_up = FALSE + +// basetype, do not use +/obj/item/storage/medkit/mechanical + name = "mechanical medkit" + desc = "For those mechanical booboos." + + icon = 'modular_doppler/modular_medical/icons/medkit.dmi' + icon_state = "medkit_mechanical" + inhand_icon_state = "medkit_mechanical" + lefthand_file = 'modular_doppler/modular_medical/code/medical_lefthand.dmi' + righthand_file = 'modular_doppler/modular_medical/code/medical_righthand.dmi' + +/obj/item/storage/medkit/mechanical/Initialize(mapload) + . = ..() + + var/static/list/list_of_everything_mechanical_medkits_can_hold = list_of_everything_medkits_can_hold + list( + /obj/item/stack/cable_coil, + /obj/item/crowbar, + /obj/item/screwdriver, + /obj/item/wrench, + /obj/item/weldingtool, + /obj/item/wirecutters, + /obj/item/multitool, + /obj/item/plunger, + /obj/item/clothing/head/utility/welding, + /obj/item/clothing/glasses/welding, + ) + var/static/list/exception_cache = typecacheof( + /obj/item/clothing/head/utility/welding + ) + + atom_storage.set_holdable(list_of_everything_mechanical_medkits_can_hold) + LAZYINITLIST(atom_storage.exception_hold) + atom_storage.exception_hold = atom_storage.exception_hold + exception_cache diff --git a/modular_doppler/modular_medical/icons/implants.dmi b/modular_doppler/modular_medical/icons/implants.dmi new file mode 100644 index 0000000000000..51ad45a299497 Binary files /dev/null and b/modular_doppler/modular_medical/icons/implants.dmi differ diff --git a/modular_doppler/modular_medical/icons/implants_lefthand.dmi b/modular_doppler/modular_medical/icons/implants_lefthand.dmi new file mode 100644 index 0000000000000..74bcb1c18d996 Binary files /dev/null and b/modular_doppler/modular_medical/icons/implants_lefthand.dmi differ diff --git a/modular_doppler/modular_medical/icons/implants_onmob.dmi b/modular_doppler/modular_medical/icons/implants_onmob.dmi new file mode 100644 index 0000000000000..9674f1b198f7e Binary files /dev/null and b/modular_doppler/modular_medical/icons/implants_onmob.dmi differ diff --git a/modular_doppler/modular_medical/icons/implants_righthand.dmi b/modular_doppler/modular_medical/icons/implants_righthand.dmi new file mode 100644 index 0000000000000..b9ecc781a3ffb Binary files /dev/null and b/modular_doppler/modular_medical/icons/implants_righthand.dmi differ diff --git a/modular_doppler/modular_medical/icons/medkit.dmi b/modular_doppler/modular_medical/icons/medkit.dmi new file mode 100644 index 0000000000000..451d8587d1469 Binary files /dev/null and b/modular_doppler/modular_medical/icons/medkit.dmi differ diff --git a/modular_doppler/modular_medical/icons/organ_actions.dmi b/modular_doppler/modular_medical/icons/organ_actions.dmi new file mode 100644 index 0000000000000..07fa9519b34a3 Binary files /dev/null and b/modular_doppler/modular_medical/icons/organ_actions.dmi differ diff --git a/modular_doppler/modular_medical/icons/on_limb_overlays.dmi b/modular_doppler/modular_medical/icons/wounds_onmob.dmi similarity index 100% rename from modular_doppler/modular_medical/icons/on_limb_overlays.dmi rename to modular_doppler/modular_medical/icons/wounds_onmob.dmi diff --git a/modular_doppler/modular_medical/medical_designs/medical_designs.dm b/modular_doppler/modular_medical/medical_designs/medical_designs.dm index 7ae0549fe3869..d5516d3a80462 100644 --- a/modular_doppler/modular_medical/medical_designs/medical_designs.dm +++ b/modular_doppler/modular_medical/medical_designs/medical_designs.dm @@ -10,3 +10,95 @@ RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL, ) departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/cyberimp_botany + name = "Hydroponics Toolset Implant" + desc = "Everything a botanist needs in an arm implant, designed to be installed on a subject's arm." + id = "ci-botany" + build_type = MECHFAB | PROTOLATHE + materials = list ( + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/plastic = SHEET_MATERIAL_AMOUNT, + ) + construction_time = 20 SECONDS + build_path = /obj/item/organ/internal/cyberimp/arm/botany + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_TOOLS, + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/cyberimp_janitor + name = "Sanitation Toolset Implant" + desc = "A set of janitor tools fitted into an arm implant, designed to be installed on subject's arm." + id = "ci-janitor" + build_type = PROTOLATHE | MECHFAB + materials = list ( + /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT, + ) + construction_time = 20 SECONDS + build_path = /obj/item/organ/internal/cyberimp/arm/janitor + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_TOOLS, + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE + +/datum/design/cyberimp_drill + name = "Dalba Masterworks 'Burrower' Integrated Drill" + desc = "Extending from a stabilization bracer built into the upper forearm, this implant allows for a steel mining drill to extend over the user's hand. Little by little, we advance a bit further with each turn. That's how a drill works!" + id = "ci-drill" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + materials = list ( + /datum/material/iron = SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT, + ) + construction_time = 20 SECONDS + build_path = /obj/item/organ/internal/cyberimp/arm/mining_drill + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_TOOLS, + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_CARGO + +/datum/design/cyberimp_diamond_drill + name = "Dalba Masterworks 'Tunneler' Diamond Drill" + desc = "Extending from a stabilization bracer built into the upper forearm, this implant allows for a masterwork diamond mining drill to extend over the user's hand. This drill will open a hole in the universe, and that hole will be a path for those behind us!" + id = "ci-drill-diamond" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + materials = list( + /datum/material/iron = SHEET_MATERIAL_AMOUNT*3, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/diamond = SHEET_MATERIAL_AMOUNT, + ) + construction_time = 30 SECONDS + build_path = /obj/item/organ/internal/cyberimp/arm/mining_drill/diamond + category = list( + RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_MINING + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_CARGO + +/datum/techweb_node/mining_adv/New() //Here for the integrated drill augments. + design_ids = list( + "ci-drill-diamond" + ) + return ..() + +/datum/design/cyberimp_claws + name = "Razor Claws Implant" + desc = "Long, sharp, double-edged razors installed within the fingers, functional for cutting. All kinds of cutting." + id = "ci-razor" + build_type = PROTOLATHE | AWAY_LATHE | MECHFAB + materials = list ( + /datum/material/iron = SHEET_MATERIAL_AMOUNT, + /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT, + /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT, + ) + construction_time = 20 SECONDS + build_path = /obj/item/organ/internal/cyberimp/arm/razor_claws + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_TOOLS, + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SECURITY diff --git a/modular_doppler/modular_medical/reagents/blood_pack.dm b/modular_doppler/modular_medical/reagents/blood_pack.dm new file mode 100644 index 0000000000000..68aedd7cb44aa --- /dev/null +++ b/modular_doppler/modular_medical/reagents/blood_pack.dm @@ -0,0 +1,7 @@ +/obj/item/reagent_containers/blood/robot + blood_type = "R" + unique_blood = /datum/reagent/synth_blood + +/obj/item/reagent_containers/blood/bug + blood_type = "I" + unique_blood = /datum/reagent/bug_blood diff --git a/modular_doppler/modular_medical/reagents/crates.dm b/modular_doppler/modular_medical/reagents/crates.dm new file mode 100644 index 0000000000000..b81fce1043221 --- /dev/null +++ b/modular_doppler/modular_medical/reagents/crates.dm @@ -0,0 +1,14 @@ +/obj/structure/closet/crate/freezer/surplus_limbs + name = "prosthetic limbs" + desc = "A crate containing an assortment of robust prosthetic limbs." + +/obj/structure/closet/crate/freezer/surplus_limbs/PopulateContents() + . = ..() + new /obj/item/bodypart/arm/left/robot/android(src) + new /obj/item/bodypart/arm/left/robot/android(src) + new /obj/item/bodypart/arm/right/robot/android(src) + new /obj/item/bodypart/arm/right/robot/android(src) + new /obj/item/bodypart/leg/left/robot/android(src) + new /obj/item/bodypart/leg/left/robot/android(src) + new /obj/item/bodypart/leg/right/robot/android(src) + new /obj/item/bodypart/leg/right/robot/android(src) diff --git a/modular_doppler/modular_medical/reagents/medicine.dm b/modular_doppler/modular_medical/reagents/medicine.dm index 0c91f395fce81..fd857a3b5eb26 100644 --- a/modular_doppler/modular_medical/reagents/medicine.dm +++ b/modular_doppler/modular_medical/reagents/medicine.dm @@ -1,3 +1,15 @@ +/datum/reagent/medicine/syndicate_nanites //Used exclusively by Syndicate medical cyborgs + process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC //Let's not cripple synth ops + +/datum/reagent/medicine/stimulants + process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC //Syndicate developed 'accelerants' for synths? + +/datum/reagent/medicine/leporazine + process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC + +/datum/reagent/flightpotion + process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC + /datum/reagent/medicine/lidocaine name = "Lidocaine" description = "A numbing agent used often for surgeries, metabolizes slowly." @@ -15,8 +27,6 @@ . = ..() affected_mob.adjustOrganLoss(ORGAN_SLOT_HEART,3 * REM * seconds_per_tick, 80) -//Inverse Medicines// - /datum/reagent/inverse/lidocaine name = "Lidopaine" description = "A paining agent used often for... being a jerk, metabolizes faster than lidocaine." @@ -41,3 +51,117 @@ color = "#ff7373" //255, 155. 155 clot_rate = 0.15 //Half as strong as standard coagulant passive_bleed_modifier = 0.5 // around 2/3 the bleeding reduction + + +// REAGENTS FOR SYNTHS +/datum/chemical_reaction/system_cleaner + results = list(/datum/reagent/medicine/system_cleaner = 4) + required_reagents = list(/datum/reagent/consumable/ethanol = 1, /datum/reagent/chlorine = 1, /datum/reagent/phenol = 2, /datum/reagent/potassium = 1) + +/datum/reagent/medicine/system_cleaner + name = "System Cleaner" + description = "Neutralizes harmful chemical compounds inside synthetic systems and refreshes system software." + reagent_state = LIQUID + color = "#F1C40F" + taste_description = "ethanol" + metabolization_rate = 2 * REAGENTS_METABOLISM + process_flags = REAGENT_SYNTHETIC + +/datum/reagent/medicine/system_cleaner/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + affected_mob.adjustToxLoss(-2 * REM * seconds_per_tick, 0) + affected_mob.adjust_disgust(-5 * REM * seconds_per_tick) + var/remove_amount = 1 * REM * seconds_per_tick; + for(var/thing in affected_mob.reagents.reagent_list) + var/datum/reagent/reagent = thing + if(reagent != src) + affected_mob.reagents.remove_reagent(reagent.type, remove_amount) + ..() + return TRUE + +/datum/chemical_reaction/liquid_solder + results = list(/datum/reagent/medicine/liquid_solder = 3) + required_reagents = list(/datum/reagent/consumable/ethanol = 1, /datum/reagent/copper = 1, /datum/reagent/silver = 1) + required_temp = 370 + mix_message = "The mixture becomes a metallic slurry." + +/datum/reagent/medicine/liquid_solder + name = "Liquid Solder" + description = "Repairs brain damage in synthetics." + reagent_state = LIQUID + color = "#727272" + taste_description = "metal" + process_flags = REAGENT_SYNTHETIC + +/datum/reagent/medicine/liquid_solder/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick) + affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -3 * REM * seconds_per_tick) + if(prob(10)) + affected_mob.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC) + return ..() + +#define NANITE_SLURRY_ORGANIC_PURGE_RATE 4 +#define NANITE_SLURRY_ORGANIC_VOMIT_CHANCE 25 + +/datum/chemical_reaction/nanite_slurry + results = list(/datum/reagent/medicine/nanite_slurry = 3) + required_reagents = list(/datum/reagent/foaming_agent = 1, /datum/reagent/gold = 1, /datum/reagent/iron = 1) + mix_message = "The mixture becomes a metallic slurry." + +/datum/reagent/medicine/nanite_slurry + name = "Nanite Slurry" + description = "A localized swarm of nanomachines specialized in repairing mechanical parts. Due to the nanites needing to interface with the host's systems to repair them, a surplus of them will cause them to overheat, or for the swarm to forcefully eject out of the mouth of organics for safety." + reagent_state = LIQUID + color = "#cccccc" + overdose_threshold = 20 + metabolization_rate = 1.25 * REAGENTS_METABOLISM + process_flags = REAGENT_SYNTHETIC | REAGENT_ORGANIC + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + /// How much brute and burn individually is healed per tick + var/healing = 3 + /// How much body temperature is increased by per overdose cycle on robotic bodyparts. + var/temperature_change = 50 + +/datum/reagent/medicine/nanite_slurry/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick) + var/heal_amount = healing * REM * seconds_per_tick + affected_mob.heal_bodypart_damage(heal_amount, heal_amount, required_bodytype = BODYTYPE_ROBOTIC) + return ..() + +/datum/reagent/medicine/nanite_slurry/overdose_process(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + if(affected_mob.mob_biotypes & MOB_ROBOTIC) + affected_mob.adjust_bodytemperature(temperature_change * REM * seconds_per_tick) + return ..() + affected_mob.reagents.remove_reagent(type, NANITE_SLURRY_ORGANIC_PURGE_RATE) //gets removed from organics very fast + if(prob(NANITE_SLURRY_ORGANIC_VOMIT_CHANCE)) + affected_mob.vomit(vomit_flags = (MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM), vomit_type = /obj/effect/decal/cleanable/vomit/nanites) + return TRUE + +#undef NANITE_SLURRY_ORGANIC_PURGE_RATE +#undef NANITE_SLURRY_ORGANIC_VOMIT_CHANCE + +/datum/chemical_reaction/medicine/taste_suppressor + results = list(/datum/reagent/medicine/taste_suppressor = 3) + required_reagents = list(/datum/reagent/sodium = 1, /datum/reagent/sulfur = 1, /datum/reagent/water = 1) + mix_message = "The mixture becomes clear like water." + +/datum/chemical_reaction/medicine/taste_suppressor/maint + results = list(/datum/reagent/medicine/taste_suppressor = 3, /datum/reagent/chlorine = 1) // The chlorine dissociated from the sodium to allow for the synthesis of the taste suppressor + required_reagents = list(/datum/reagent/consumable/salt = 2, /datum/reagent/sulfur = 1, /datum/reagent/water = 1) + required_temp = 300 + +/datum/reagent/medicine/taste_suppressor + name = "Taste Suppressor" + description = "A colorless medicine aimed to dull the sense of taste of those that consumed it, as long as it's in their system." + color = "#AAAAAA77" + reagent_state = LIQUID + metabolization_rate = 0.5 * REAGENTS_METABOLISM + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + chemical_flags_doppler = REAGENT_BLOOD_REGENERATING // It has REAGENT_BLOOD_REGENERATING only because it makes it so Hemophages can safely drink it, which makes complete sense considering this is meant to suppress their tumor's reactiveness to anything that doesn't regenerate blood. + +/datum/reagent/medicine/taste_suppressor/on_mob_metabolize(mob/living/affected_mob) + . = ..() + + ADD_TRAIT(affected_mob, TRAIT_AGEUSIA, REF(src)) + +/datum/reagent/medicine/taste_suppressor/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + + REMOVE_TRAIT(affected_mob, TRAIT_AGEUSIA, REF(src)) diff --git a/modular_doppler/modular_medical/reagents/pill.dm b/modular_doppler/modular_medical/reagents/pill.dm new file mode 100644 index 0000000000000..acf54dbd69ec6 --- /dev/null +++ b/modular_doppler/modular_medical/reagents/pill.dm @@ -0,0 +1,26 @@ +/obj/item/reagent_containers/pill/liquid_solder + name = "liquid solder pill" + desc = "Used to treat synthetic brain damage." + icon_state = "pill21" + list_reagents = list(/datum/reagent/medicine/liquid_solder = 10) + rename_with_volume = TRUE + +// Lower quantity solder pill. +// 50u pills heal 375 brain damage, 10u pills heal 75. +/obj/item/reagent_containers/pill/liquid_solder/braintumor + desc = "Used to treat symptoms of synthetic brain damage." + list_reagents = list(/datum/reagent/medicine/liquid_solder = 10) + +/obj/item/reagent_containers/pill/nanite_slurry + name = "nanite slurry pill" + desc = "Used to repair robotic bodyparts." + icon_state = "pill18" + list_reagents = list(/datum/reagent/medicine/nanite_slurry = 15) // 20 is OD + rename_with_volume = TRUE + +/obj/item/reagent_containers/pill/system_cleaner + name = "system cleaner pill" + desc = "Used to detoxify synthetic bodies." + icon_state = "pill7" + list_reagents = list(/datum/reagent/medicine/system_cleaner = 10) + rename_with_volume = TRUE diff --git a/modular_doppler/modular_medical/reagents/pill_bottles.dm b/modular_doppler/modular_medical/reagents/pill_bottles.dm new file mode 100644 index 0000000000000..562e13af903f2 --- /dev/null +++ b/modular_doppler/modular_medical/reagents/pill_bottles.dm @@ -0,0 +1,33 @@ +// Pill bottles for synthetic healing medications +/obj/item/storage/pill_bottle/liquid_solder + name = "bottle of liquid solder pills" + desc = "Contains pills used to treat synthetic brain damage." + +/obj/item/storage/pill_bottle/liquid_solder/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/reagent_containers/pill/liquid_solder(src) + +// Contains 4 liquid_solder pills instead of 7, and 10u pills instead of 50u. +// 50u pills heal 375 brain damage, 10u pills heal 75. +/obj/item/storage/pill_bottle/liquid_solder/braintumor + desc = "Contains diluted pills used to treat synthetic brain damage symptoms. Take one when feeling lightheaded." + +/obj/item/storage/pill_bottle/liquid_solder/braintumor/PopulateContents() + for(var/i in 1 to 4) + new /obj/item/reagent_containers/pill/liquid_solder/braintumor(src) + +/obj/item/storage/pill_bottle/nanite_slurry + name = "bottle of nanite slurry pills" + desc = "Contains pills used to treat robotic bodyparts." + +/obj/item/storage/pill_bottle/nanite_slurry/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/reagent_containers/pill/nanite_slurry(src) + +/obj/item/storage/pill_bottle/system_cleaner + name = "bottle of system cleaner pills" + desc = "Contains pills used to detoxify synthetic bodies." + +/obj/item/storage/pill_bottle/system_cleaner/PopulateContents() + for(var/i in 1 to 7) + new /obj/item/reagent_containers/pill/system_cleaner(src) diff --git a/modular_doppler/modular_medical/reagents/reagents.dm b/modular_doppler/modular_medical/reagents/reagents.dm index 3f825dfe4bf6c..ffea7aca83b42 100644 --- a/modular_doppler/modular_medical/reagents/reagents.dm +++ b/modular_doppler/modular_medical/reagents/reagents.dm @@ -1,3 +1,20 @@ /datum/reagent ///What can process this? REAGENT_ORGANIC, REAGENT_SYNTHETIC, or REAGENT_ORGANIC | REAGENT_SYNTHETIC?. We'll assume by default that it affects organics. var/process_flags = REAGENT_ORGANIC + +/proc/reagent_process_flags_valid(mob/processor, datum/reagent/reagent) + if(ishuman(processor)) + var/mob/living/carbon/human/human_processor = processor + //Check if this mob's species is set and can process this type of reagent + //If we somehow avoided getting a species or reagent_flags set, we'll assume we aren't meant to process ANY reagents + if(human_processor.dna && human_processor.dna.species.reagent_flags) + var/processor_flags = human_processor.dna.species.reagent_flags + if((reagent.process_flags & REAGENT_SYNTHETIC) && (processor_flags & PROCESS_SYNTHETIC)) //SYNTHETIC-oriented reagents require PROCESS_SYNTHETIC + return TRUE + if((reagent.process_flags & REAGENT_ORGANIC) && (processor_flags & PROCESS_ORGANIC)) //ORGANIC-oriented reagents require PROCESS_ORGANIC + return TRUE + return FALSE + else if(reagent.process_flags == REAGENT_SYNTHETIC) + //We'll assume that non-human mobs lack the ability to process synthetic-oriented reagents (adjust this if we need to change that assumption) + return FALSE + return TRUE diff --git a/modular_doppler/modular_medical/reagents/sprays.dm b/modular_doppler/modular_medical/reagents/sprays.dm new file mode 100644 index 0000000000000..8e2371e3f2854 --- /dev/null +++ b/modular_doppler/modular_medical/reagents/sprays.dm @@ -0,0 +1,12 @@ +/obj/item/reagent_containers/spray/hercuri/chilled + name = "chilled hercuri spray" // effective at cooling low-temperature burns but also is more efficienct at cooling high-temperature + desc = "A medical spray bottle. This one contains hercuri, a medicine used to negate the effects of dangerous high-temperature environments. \ + This one comes pre-chilled, making it especially good at cooling synthetic burns! \n\ + It has a bold warning label near the nozzle: ONLY USE IN EMERGENCIES! WILL CAUSE FREEZING! SECONDARY EFFECT ONLY USEFUL ON LIVING SYNTHS! INEFFECTIVE ON DECEASED! \n\ + There's a smaller warning label on the body of the spray: IN EVENT OF RUNAWAY ENDOTHERMY, APPLY SYSTEM CLEANER!" + var/starting_temperature = 100 + +/obj/item/reagent_containers/spray/hercuri/chilled/add_initial_reagents() + . = ..() + + reagents.chem_temp = starting_temperature diff --git a/modular_doppler/modular_medical/surgery/organs/internal/cyberimp.dm b/modular_doppler/modular_medical/surgery/organs/internal/cyberimp.dm new file mode 100644 index 0000000000000..eedf7f6b64a16 --- /dev/null +++ b/modular_doppler/modular_medical/surgery/organs/internal/cyberimp.dm @@ -0,0 +1,265 @@ +/obj/item/organ/internal/cyberimp/arm/surgery/l + zone = BODY_ZONE_L_ARM + +/obj/item/organ/internal/cyberimp/arm/botany + name = "hydroponics toolset implant" + desc = "A rather simple arm implant containing tools used in gardening and botanical research." + icon_state = "toolkit_generic" + actions_types = list(/datum/action/item_action/organ_action/toggle/toolkit) + items_to_create = list(/obj/item/cultivator, + /obj/item/shovel/spade, + /obj/item/hatchet, + /obj/item/cultivator, + /obj/item/plant_analyzer, + /obj/item/secateurs, + ) + +/obj/item/organ/internal/cyberimp/arm/botany/l + zone = BODY_ZONE_L_ARM + +/obj/item/implant_mounted_chainsaw + name = "integrated chainsaw" + desc = "A chainsaw that conceals inside your arm." + icon = 'icons/obj/weapons/chainsaw.dmi' + icon_state = "chainsaw_on" + inhand_icon_state = "mounted_chainsaw" + lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' + force = 24 + throwforce = 0 + throw_range = 0 + throw_speed = 0 + sharpness = SHARP_EDGED + attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices") + attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice") + hitsound = 'sound/items/weapons/chainsawhit.ogg' + tool_behaviour = TOOL_SAW + toolspeed = 1 + +/obj/item/organ/internal/cyberimp/arm/botany/emag_act() + if(obj_flags & EMAGGED) + return FALSE + for(var/datum/weakref/created_item in items_list) + to_chat(usr, span_notice("You unlock [src]'s deluxe landscaping equipment!")) + items_list += WEAKREF(new /obj/item/implant_mounted_chainsaw(src)) //time to landscape the station + obj_flags |= EMAGGED + return TRUE + +/obj/item/organ/internal/cyberimp/arm/janitor + name = "sanitation toolset implant" + desc = "A set of janitorial tools on the user's arm." + actions_types = list(/datum/action/item_action/organ_action/toggle/toolkit) + items_to_create = list(/obj/item/lightreplacer, + /obj/item/holosign_creator, + /obj/item/mop, + /obj/item/reagent_containers/spray/cleaner, + /obj/item/lightreplacer, + /obj/item/wirebrush, + ) + +/obj/item/organ/internal/cyberimp/arm/janitor/l + zone = BODY_ZONE_L_ARM + +/obj/item/organ/internal/cyberimp/arm/janitor/emag_act() + if(obj_flags & EMAGGED) + return FALSE + for(var/datum/weakref/created_item in items_list) + to_chat(usr, span_notice("You unlock [src]'s integrated deluxe cleaning supplies!")) + items_list += WEAKREF(new /obj/item/soap/syndie(src)) //We add not replace. + items_list += WEAKREF(new /obj/item/reagent_containers/spray/cyborg_lube(src)) + obj_flags |= EMAGGED + return TRUE + +/obj/item/organ/internal/cyberimp/arm/razor_claws + name = "razor claws implant" + desc = "A set of hidden, retractable blades built into the fingertips; cyborg mercenary approved." + items_to_create = list(/obj/item/knife/razor_claws) + actions_types = list(/datum/action/item_action/organ_action/toggle/razor_claws) + icon = 'modular_doppler/modular_medical/icons/implants.dmi' + icon_state = "wolverine" + extend_sound = 'sound/items/unsheath.ogg' + retract_sound = 'sound/items/sheath.ogg' + +/obj/item/organ/internal/cyberimp/arm/razor_claws/l + zone = BODY_ZONE_L_ARM + +/datum/action/item_action/organ_action/toggle/razor_claws + name = "Extend Claws" + desc = "You can also activate the claws in your hand to change their mode." + button_icon = 'modular_doppler/modular_medical/icons/organ_actions.dmi' + button_icon_state = "wolverine" + +/obj/item/knife/razor_claws + name = "implanted razor claws" + desc = "A set of sharp, retractable claws built into the fingertips, five double-edged blades sure to turn people into mincemeat. Capable of shifting into 'Precision' mode to act similar to wirecutters." + icon = 'modular_doppler/modular_medical/icons/implants.dmi' + righthand_file = 'modular_doppler/modular_medical/icons/implants_righthand.dmi' + lefthand_file = 'modular_doppler/modular_medical/icons/implants_lefthand.dmi' + icon_state = "wolverine" + inhand_icon_state = "wolverine" + var/knife_force = 10 + w_class = WEIGHT_CLASS_BULKY + var/knife_wound_bonus = 5 + var/cutter_force = 6 + var/cutter_wound_bonus = 0 + var/cutter_bare_wound_bonus = 15 + tool_behaviour = TOOL_KNIFE + toolspeed = 1 + +/obj/item/knife/razor_claws/attack_self(mob/user) + playsound(get_turf(user), 'sound/items/tools/change_drill.ogg', 50, TRUE) + if(tool_behaviour != TOOL_WIRECUTTER) + tool_behaviour = TOOL_WIRECUTTER + to_chat(user, span_notice("You shift [src] into Precision mode, for wirecutting.")) + icon_state = "precision_wolverine" + inhand_icon_state = "precision_wolverine" + force = cutter_force + wound_bonus = cutter_wound_bonus + bare_wound_bonus = cutter_bare_wound_bonus + sharpness = NONE + hitsound = 'sound/items/tools/wirecutter.ogg' + usesound = 'sound/items/tools/wirecutter.ogg' + attack_verb_continuous = list("bashes", "batters", "bludgeons", "thrashes", "whacks") + attack_verb_simple = list("bash", "batter", "bludgeon", "thrash", "whack") + else + tool_behaviour = TOOL_KNIFE + to_chat(user, span_notice("You shift [src] into Killing mode, for slicing.")) + icon_state = "wolverine" + inhand_icon_state = "wolverine" + force = knife_force + sharpness = SHARP_EDGED + wound_bonus = knife_wound_bonus + bare_wound_bonus = 15 + hitsound = 'sound/items/weapons/bladeslice.ogg' + usesound = 'sound/items/weapons/bladeslice.ogg' + attack_verb_continuous = list("slashes", "tears", "slices", "tears", "lacerates", "rips", "dices", "cuts", "rends") + attack_verb_simple = list("slash", "tear", "slice", "tear", "lacerate", "rip", "dice", "cut", "rend") + +/obj/item/knife/razor_claws/attackby(obj/item/stone, mob/user, param) + if(!istype(stone, /obj/item/scratching_stone)) + return ..() + + knife_force = 15 + knife_wound_bonus = 15 + armour_penetration = 10 //Let's give them some AP for the trouble. + item_flags |= NEEDS_PERMIT + + if(tool_behaviour == TOOL_KNIFE) + force = knife_force + wound_bonus = knife_wound_bonus + + name = "enhanced razor claws" + desc += span_warning("\n\nThese have undergone a special honing process; they'll kill people even faster than they used to.") + user.visible_message(span_warning("[user] sharpens [src], [stone] disintegrating!"), span_warning("You sharpen [src], making it much more deadly than before, but [stone] disintegrates under the stress.")) + playsound(src, 'sound/items/unsheath.ogg', 25, TRUE) + qdel(stone) + return ..() + +/obj/item/scratching_stone + name = "scratching stone" + icon = 'icons/obj/service/kitchen.dmi' + icon_state = "sharpener" + desc = "A specialized kind of whetstone, made of unknown alloys to hone a cyborg mercenary's claws to the best they can be. This one looks like a shitty second-hand sold by razorkids. It's got like, what, maybe one use left?" + force = 5 + throwforce = 10 //Hey, you can at least use it as a brick. + +/datum/supply_pack/goody/scratching_stone + name = "Scratching Stone" + desc = "A high-grade sharpening stone made of specialized alloys, meant to sharpen razor-claws. Unfortunately, this particular one has by far seen better days." + cost = CARGO_CRATE_VALUE * 4 //800 credits + contains = list(/obj/item/scratching_stone) + contraband = TRUE + +/obj/item/organ/internal/cyberimp/arm/mining_drill + name = "\improper Dalba Masterworks 'Burrower' Integrated Drill" + desc = "Extending from a stabilization bracer built into the upper forearm, this implant allows for a steel mining drill to extend over the user's hand. Little by little, we advance a bit further with each turn. That's how a drill works!" + icon = 'modular_doppler/modular_medical/icons/implants.dmi' + icon_state = "steel" + items_to_create = list(/obj/item/pickaxe/drill/implant) + implant_overlay = null + implant_color = null + /// The bodypart overlay datum we should apply to whatever mob we are put into's someone's arm + var/datum/bodypart_overlay/simple/steel_drill/drill_overlay + +/obj/item/organ/internal/cyberimp/arm/mining_drill/l + zone = BODY_ZONE_L_ARM + +/datum/bodypart_overlay/simple/steel_drill + icon = 'modular_doppler/modular_medical/icons/implants_onmob.dmi' + layers = EXTERNAL_FRONT + +/datum/bodypart_overlay/simple/steel_drill/left + icon_state = "steel_left" + +/datum/bodypart_overlay/simple/steel_drill/right + icon_state = "steel_right" + +/obj/item/organ/internal/cyberimp/arm/mining_drill/on_bodypart_insert(obj/item/bodypart/limb, movement_flags) + . = ..() + if(zone == BODY_ZONE_L_ARM) + drill_overlay = new /datum/bodypart_overlay/simple/steel_drill/left + else + drill_overlay = new /datum/bodypart_overlay/simple/steel_drill/right + limb.add_bodypart_overlay(drill_overlay) + owner?.update_body_parts() + +/obj/item/organ/internal/cyberimp/arm/mining_drill/on_mob_remove(mob/living/carbon/arm_owner) + . = ..() + bodypart_owner?.remove_bodypart_overlay(drill_overlay) + arm_owner.update_body_parts() + QDEL_NULL(drill_overlay) + +/obj/item/pickaxe/drill/implant + name = "integrated mining drill" + desc = "Extending from a stabilization bracer built into the upper forearm, this implant allows for a steel mining drill to extend over the user's hand. Little by little, we advance a bit further with each turn. That's how a drill works!" + slot_flags = NONE + icon = 'modular_doppler/modular_medical/icons/implants.dmi' + righthand_file = 'modular_doppler/modular_medical/icons/implants_righthand.dmi' + lefthand_file = 'modular_doppler/modular_medical/icons/implants_lefthand.dmi' + icon_state = "steel" + inhand_icon_state = "steel" + toolspeed = 0.6 //faster than a pickaxe + usesound = 'sound/items/weapons/drill.ogg' + hitsound = 'sound/items/weapons/drill.ogg' + /// How recent the spin emote was + var/recent_spin = 0 + /// The delay for how often you should be able to do it to prevent spam + var/spin_delay = 10 SECONDS + +/obj/item/pickaxe/drill/implant/click_alt(mob/user) + spin() + return CLICK_ACTION_SUCCESS + +/obj/item/pickaxe/drill/implant/verb/spin() + set name = "Spin Drillbit" + set category = "Object" + set desc = "Click to spin your drill's head. It won't do practically anything, but it's pretty cool anyway." + + var/mob/user = usr + + if(user.stat || !in_range(user, src)) + return + + if (recent_spin > world.time) + return + recent_spin = world.time + spin_delay + + playsound(user, 'modular_doppler/modular_sounds/sound/machines/whirr.ogg', 50, FALSE) + user.visible_message(span_warning("[user] spins [src]'s bit, accelerating for a moment to thousands of RPM."), span_notice("You spin [src]'s bit, accelerating for a moment to thousands of RPM.")) + +/obj/item/organ/internal/cyberimp/arm/mining_drill/diamond + name = "\improper Dalba Masterworks 'Tunneler' Diamond Integrated Drill" + desc = "Extending from a stabilization bracer built into the upper forearm, this implant allows for a masterwork diamond mining drill to extend over the user's hand. This drill will open a hole in the universe, and that hole will be a path for those behind us!" + icon_state = "diamond" + items_to_create = list(/obj/item/pickaxe/drill/implant/diamond) + +/obj/item/pickaxe/drill/implant/diamond + name = "integrated diamond mining drill" + desc = "Extending from a stabilization bracer built into the upper forearm, this implant allows for a masterwork diamond mining drill to extend over the user's hand. This drill will open a hole in the universe, and that hole will be a path for those behind us!" + icon_state = "diamond" + inhand_icon_state = "diamond" + toolspeed = 0.2 + force = 20 + demolition_mod = 1.25 + usesound = 'sound/items/weapons/drill.ogg' + hitsound = 'sound/items/weapons/drill.ogg' diff --git a/modular_doppler/modular_medical/surgery/revival.dm b/modular_doppler/modular_medical/surgery/revival.dm new file mode 100644 index 0000000000000..0f4f0c93aec4d --- /dev/null +++ b/modular_doppler/modular_medical/surgery/revival.dm @@ -0,0 +1,29 @@ +/datum/surgery/revival/carbon/mechanic + name = "Full System Reboot" + requires_bodypart_type = BODYTYPE_ROBOTIC + possible_locs = list(BODY_ZONE_HEAD) + target_mobtypes = list(/mob/living/carbon) + surgery_flags = parent_type::surgery_flags | SURGERY_REQUIRE_LIMB + steps = list( + /datum/surgery_step/mechanic_open, + /datum/surgery_step/open_hatch, + /datum/surgery_step/mechanic_unwrench, + /datum/surgery_step/prepare_electronics, + /datum/surgery_step/revive/carbon/mechanic, + /datum/surgery_step/mechanic_wrench, + /datum/surgery_step/mechanic_close, + ) + +/datum/surgery/revival/carbon/mechanic/is_valid_target(mob/living/carbon/patient) + if (!(patient.mob_biotypes & (MOB_ROBOTIC|MOB_HUMANOID))) + return FALSE + var/obj/item/organ/internal/brain/target_brain = patient.get_organ_slot(ORGAN_SLOT_BRAIN) + return !isnull(target_brain) + +/datum/surgery_step/revive/carbon/mechanic + +/datum/surgery_step/revive/carbon/mechanic/on_revived(mob/surgeon, mob/living/patient) + . = ..() + var/mob/living/carbon/robotic_patient = patient + var/datum/species/android/energy_holder = robotic_patient.dna.species + energy_holder.core_energy += 1 MEGA JOULES // from the defibb :) diff --git a/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt.dm b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt.dm new file mode 100644 index 0000000000000..e29e5e9dbbd8f --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt.dm @@ -0,0 +1,401 @@ +/// The multiplier put against our movement effects if our victim has the determined reagent +#define ROBOTIC_WOUND_DETERMINATION_MOVEMENT_EFFECT_MOD 0.7 +/// The multiplier of stagger intensity on hit if our victim has the determined reagent +#define ROBOTIC_WOUND_DETERMINATION_STAGGER_MOVEMENT_MULT 0.7 + +/// The multiplier put against our movement effects if our limb is grasped +#define ROBOTIC_BLUNT_GRASPED_MOVEMENT_MULT 0.7 + +/datum/wound/blunt/robotic + name = "Robotic Blunt (Screws and bolts) Wound" + wound_flags = (ACCEPTS_GAUZE|SPLINT_OVERLAY|CAN_BE_GRASPED) + + default_scar_file = METAL_SCAR_FILE + + /// If we suffer severe head booboos, we can get brain traumas tied to them + var/datum/brain_trauma/active_trauma + /// What brain trauma group, if any, we can draw from for head wounds + var/brain_trauma_group + /// If we deal brain traumas, when is the next one due? + var/next_trauma_cycle + /// How long do we wait +/- 20% for the next trauma? + var/trauma_cycle_cooldown + + /// The ratio stagger score will be multiplied against for determining the final chance of moving away from the attacker. + var/stagger_movement_chance_ratio = 1 + /// The ratio stagger score will be multiplied against for determining the amount of pixelshifting we will do when we are hit. + var/stagger_shake_shift_ratio = 0.05 + + /// The ratio of stagger score to shake duration during a stagger() call + var/stagger_score_to_shake_duration_ratio = 0.1 + + /// In the stagger aftershock, the stagger score will be multiplied against for determining the chance of dropping held items. + var/stagger_drop_chance_ratio = 1.25 + /// In the stagger aftershock, the stagger score will be multiplied against for determining the chance of falling over. + var/stagger_fall_chance_ratio = 1 + + /// In the stagger aftershock, the stagger score will be multiplied against for determining how long we are knocked down for. + var/stagger_aftershock_knockdown_ratio = 0.5 + /// In the stagger after shock, the stagger score will be multiplied against this (if caused by movement) for determining how long we are knocked down for. + var/stagger_aftershock_knockdown_movement_ratio = 0.1 + + /// If the victim stops moving before the aftershock, aftershock effects will be multiplied against this. + var/aftershock_stopped_moving_score_mult = 0.1 + + /// The ratio damage applied will be multiplied against for determining our stagger score. + var/chest_attacked_stagger_mult = 2.5 + /// The minimum score an attack must do to trigger a stagger. + var/chest_attacked_stagger_minimum_score = 5 + /// The ratio of damage to stagger chance on hit. + var/chest_attacked_stagger_chance_ratio = 2 + + /// The base score given to stagger() when we successfully stagger on a move. + var/base_movement_stagger_score = 30 + /// The base chance of moving to trigger stagger(). + var/chest_movement_stagger_chance = 1 + + /// The base duration of a stagger()'s sprite shaking. + var/base_stagger_shake_duration = 1.5 SECONDS + /// The base duration of a stagger()'s sprite shaking if caused by movement. + var/base_stagger_movement_shake_duration = 1.5 SECONDS + + /// The ratio of stagger score to camera shake chance. + var/stagger_camera_shake_chance_ratio = 0.75 + /// The base duration of a stagger's aftershock's camerashake. + var/base_aftershock_camera_shake_duration = 1.5 SECONDS + /// The base strength of a stagger's aftershock's camerashake. + var/base_aftershock_camera_shake_strength = 0.5 + + /// The amount of x and y pixels we will be shaken around by during a movement stagger. + var/movement_stagger_shift = 1 + + /// If we are currently oscillating. If true, we cannot stagger(). + var/oscillating = FALSE + + /// % chance for hitting our limb to fix something. + var/percussive_maintenance_repair_chance = 10 + /// Damage must be under this to proc percussive maintenance. + var/percussive_maintenance_damage_max = 7 + /// Damage must be over this to proc percussive maintenance. + var/percussive_maintenance_damage_min = 0 + + /// The time, in world time, that we will be allowed to do another movement shake. Useful because it lets us prioritize attacked shakes over movement shakes. + var/time_til_next_movement_shake_allowed = 0 + + /// The percent our limb must get to max possible damage by burn damage alone to count as malleable if it has no T2 burn wound. + var/limb_burn_percent_to_max_threshold_for_malleable = 0.8 // must be 75% to max damage by burn damage alone + + /// The last time our victim has moved. Used for determining if we should increase or decrease the chance of having stagger aftershock. + var/last_time_victim_moved = 0 + + processes = TRUE + /// Whenever an oscillation is triggered by movement, we wait 4 seconds before trying to do another. + COOLDOWN_DECLARE(movement_stagger_cooldown) + +/datum/wound_pregen_data/blunt_metal + abstract = TRUE + required_limb_biostate = BIO_METAL + wound_series = WOUND_SERIES_METAL_BLUNT_BASIC + required_wounding_types = list(WOUND_BLUNT) + +/datum/wound_pregen_data/blunt_metal/generate_scar_priorities() + return list("[BIO_METAL]") + +/datum/wound/blunt/robotic/set_victim(new_victim) + if(victim) + UnregisterSignal(victim, COMSIG_MOVABLE_MOVED) + UnregisterSignal(victim, COMSIG_MOB_AFTER_APPLY_DAMAGE) + if(new_victim) + RegisterSignal(new_victim, COMSIG_MOVABLE_MOVED, PROC_REF(victim_moved)) + RegisterSignal(new_victim, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(victim_attacked)) + + return ..() + +/datum/wound/blunt/robotic/get_limb_examine_description() + return span_warning("This limb looks loosely held together.") + +// this wound is unaffected by cryoxadone and pyroxadone +/datum/wound/blunt/robotic/on_xadone(power) + return + +/datum/wound/blunt/robotic/wound_injury(datum/wound/old_wound, attack_direction) + . = ..() + + // hook into gaining/losing gauze so crit bone wounds can re-enable/disable depending if they're slung or not + if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group) + processes = TRUE + active_trauma = victim.gain_trauma_type(brain_trauma_group, TRAUMA_RESILIENCE_WOUND) + next_trauma_cycle = world.time + (rand(100-WOUND_BONE_HEAD_TIME_VARIANCE, 100+WOUND_BONE_HEAD_TIME_VARIANCE) * 0.01 * trauma_cycle_cooldown) + + var/obj/item/held_item = victim.get_item_for_held_index(limb.held_index || 0) + if(held_item && (disabling || prob(30 * severity))) + if(istype(held_item, /obj/item/offhand)) + held_item = victim.get_inactive_held_item() + if(held_item && victim.dropItemToGround(held_item)) + victim.visible_message(span_danger("[victim] drops [held_item] in shock!"), span_warning("The force on your [limb.plaintext_zone] causes you to drop [held_item]!"), vision_distance=COMBAT_MESSAGE_RANGE) + +/datum/wound/blunt/robotic/remove_wound(ignore_limb, replaced) + . = ..() + + QDEL_NULL(active_trauma) + +/datum/wound/blunt/robotic/handle_process(seconds_per_tick, times_fired) + . = ..() + + if (!victim || HAS_TRAIT(victim, TRAIT_STASIS)) + return + + if (limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group && world.time > next_trauma_cycle) + if (active_trauma) + QDEL_NULL(active_trauma) + else + active_trauma = victim.gain_trauma_type(brain_trauma_group, TRAUMA_RESILIENCE_WOUND) + next_trauma_cycle = world.time + (rand(100-WOUND_BONE_HEAD_TIME_VARIANCE, 100+WOUND_BONE_HEAD_TIME_VARIANCE) * 0.01 * trauma_cycle_cooldown) + +/// If true, allows our superstructure to be modified if we are T3. RCDs can always fix our superstructure. +/datum/wound/blunt/robotic/proc/limb_malleable() + if (!isnull(get_overheat_wound())) + return TRUE + var/burn_damage_to_max = (limb.burn_dam / limb.max_damage) // only exists for the weird case where it cant get a overheat wound + if (burn_damage_to_max >= limb_burn_percent_to_max_threshold_for_malleable) + return TRUE + return FALSE + +/// If we have one, returns a robotic overheat wound of severe severity or higher. Null otherwise. +/datum/wound/blunt/robotic/proc/get_overheat_wound() + RETURN_TYPE(/datum/wound/burn/robotic/overheat) + for (var/datum/wound/found_wound as anything in limb.wounds) + var/datum/wound_pregen_data/pregen_data = found_wound.get_pregen_data() + if (pregen_data.wound_series == WOUND_SERIES_METAL_BURN_OVERHEAT && found_wound.severity >= WOUND_SEVERITY_MODERATE) // meh solution but whateva + return found_wound + return null + +/// If our victim is lying down and is attacked in the chest, effective oscillation damage is multiplied against this. +#define OSCILLATION_ATTACKED_LYING_DOWN_EFFECT_MULT 0.5 + +/// If the attacker is wearing a diag hud, chance of percussive maintenance succeeding is multiplied against this. +#define PERCUSSIVE_MAINTENANCE_DIAG_HUD_CHANCE_MULT 1.5 +/// If our wound has been scanned by a wound analyzer, chance of percussive maintenance succeeding is multiplied against this. +#define PERCUSSIVE_MAINTENANCE_WOUND_SCANNED_CHANCE_MULT 1.5 +/// If the attacker is NOT our victim, chance of percussive maintenance succeeding is multiplied against this. +#define PERCUSSIVE_MAINTENANCE_ATTACKER_NOT_VICTIM_CHANCE_MULT 2.5 + +/// Signal handler proc to when our victim has damage applied via apply_damage(), which is a external attack. +/datum/wound/blunt/robotic/proc/victim_attacked(datum/source, damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction, attacking_item) + SIGNAL_HANDLER + + if (def_zone != limb.body_zone) // use this proc since receive damage can also be called for like, chems and shit + return + if(!victim) + return + + var/effective_damage = (damage - blocked) + + var/obj/item/stack/gauze = limb.current_gauze + if(gauze) + effective_damage *= gauze.splint_factor + + switch (limb.body_zone) + + if(BODY_ZONE_CHEST) + var/oscillation_mult = 1 + if (victim.body_position == LYING_DOWN) + oscillation_mult *= OSCILLATION_ATTACKED_LYING_DOWN_EFFECT_MULT + var/oscillation_damage = effective_damage + var/stagger_damage = oscillation_damage * chest_attacked_stagger_mult + if (victim.has_status_effect(/datum/status_effect/determined)) + oscillation_damage *= ROBOTIC_WOUND_DETERMINATION_STAGGER_MOVEMENT_MULT + if ((stagger_damage >= chest_attacked_stagger_minimum_score) && prob(oscillation_damage * chest_attacked_stagger_chance_ratio)) + stagger(stagger_damage * oscillation_mult, attack_direction, attacking_item, shift = stagger_damage * stagger_shake_shift_ratio) + + if(!uses_percussive_maintenance() || damage < percussive_maintenance_damage_min || damage > percussive_maintenance_damage_max || damagetype != BRUTE || sharpness) + return + var/success_chance_mult = 1 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + success_chance_mult *= PERCUSSIVE_MAINTENANCE_WOUND_SCANNED_CHANCE_MULT + var/mob/living/user + if (isatom(attacking_item)) + var/atom/attacking_atom = attacking_item + user = attacking_atom.loc // nullable + + if (istype(user)) + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + success_chance_mult *= PERCUSSIVE_MAINTENANCE_DIAG_HUD_CHANCE_MULT + + if (user != victim) + success_chance_mult *= PERCUSSIVE_MAINTENANCE_ATTACKER_NOT_VICTIM_CHANCE_MULT // encourages people to get other people to beat the shit out of their limbs + if (prob(percussive_maintenance_repair_chance * success_chance_mult)) + handle_percussive_maintenance_success(attacking_item, user) + else + handle_percussive_maintenance_failure(attacking_item, user) + +#undef OSCILLATION_ATTACKED_LYING_DOWN_EFFECT_MULT +#undef PERCUSSIVE_MAINTENANCE_DIAG_HUD_CHANCE_MULT +#undef PERCUSSIVE_MAINTENANCE_WOUND_SCANNED_CHANCE_MULT +#undef PERCUSSIVE_MAINTENANCE_ATTACKER_NOT_VICTIM_CHANCE_MULT + +/// The percent, in decimal, of a stagger's shake() duration, that will be used in a addtimer() to queue aftershock(). +#define STAGGER_PERCENT_OF_SHAKE_DURATION_TO_AFTERSHOCK_DELAY 0.65 // 1 = happens at the end, .5 = happens halfway through + +/// Causes an oscillation, which 1. has a chance to move our victim away from the attacker, and 2. after a delay, calls aftershock(). +/datum/wound/blunt/robotic/proc/stagger(stagger_score, attack_direction, obj/item/attacking_item, from_movement, shake_duration = base_stagger_shake_duration, shift, knockdown_ratio = stagger_aftershock_knockdown_ratio) + if (oscillating) + return + + var/self_message = "Your [limb.plaintext_zone] oscillates" + var/message = "[victim]'s [limb.plaintext_zone] oscillates" + if (attacking_item) + message += " from the impact" + else if (from_movement) + message += " from the movement" + message += "!" + self_message += "! You might be able to avoid an aftershock by stopping and waiting..." + + if (isnull(attack_direction) && !isnull(attacking_item)) + attack_direction = get_dir(victim, attacking_item) + + if (!isnull(attack_direction) && prob(stagger_score * stagger_movement_chance_ratio)) + to_chat(victim, span_warning("The force of the blow sends you reeling!")) + var/turf/target_loc = get_step(victim, attack_direction) + victim.Move(target_loc) + + victim.visible_message(span_warning(message), ignored_mobs = victim) + to_chat(victim, span_warning(self_message)) + victim.balloon_alert(victim, "oscillation! stop moving") + + victim.Shake(pixelshiftx = shift, pixelshifty = shift, duration = shake_duration) + var/aftershock_delay = (shake_duration * STAGGER_PERCENT_OF_SHAKE_DURATION_TO_AFTERSHOCK_DELAY) + var/knockdown_time = stagger_score * knockdown_ratio + addtimer(CALLBACK(src, PROC_REF(aftershock), stagger_score, attack_direction, attacking_item, world.time, knockdown_time), aftershock_delay) + oscillating = TRUE + +#undef STAGGER_PERCENT_OF_SHAKE_DURATION_TO_AFTERSHOCK_DELAY + +#define AFTERSHOCK_GRACE_THRESHOLD_PERCENT 0.33 // lower mult = later grace period = more forgiving + +/** + * Timer proc from stagger(). + * + * Based on chance, causes items to be dropped, knockdown to be applied, and/or screenshake to occur. + * Chance is massively reduced if the victim isn't moving. + */ +/datum/wound/blunt/robotic/proc/aftershock(stagger_score, attack_direction, obj/item/attacking_item, stagger_starting_time, knockdown_time) + if (!still_exists()) + return FALSE + + var/message = "The oscillations from your [limb.plaintext_zone] spread, " + var/limb_message = "causing " + var/limb_affected + + var/stopped_moving_grace_threshold = (world.time - ((world.time - stagger_starting_time) * AFTERSHOCK_GRACE_THRESHOLD_PERCENT)) + var/victim_stopped_moving = (last_time_victim_moved <= stopped_moving_grace_threshold) + if (victim_stopped_moving) + stagger_score *= aftershock_stopped_moving_score_mult + + if (prob(stagger_score * stagger_drop_chance_ratio)) + limb_message += "your hands" + victim.drop_all_held_items() + limb_affected = TRUE + + if (prob(stagger_score * stagger_fall_chance_ratio)) + if (limb_affected) + limb_message += " and " + limb_message += "your legs" + victim.Knockdown(knockdown_time) + limb_affected = TRUE + + if (prob(stagger_score * stagger_camera_shake_chance_ratio)) + if (limb_affected) + limb_message += " and " + limb_message += "your head" + shake_camera(victim, base_aftershock_camera_shake_duration, base_aftershock_camera_shake_strength) + limb_affected = TRUE + + if (limb_affected) + message += "[limb_message] to shake uncontrollably!" + else + message += "but pass harmlessly" + if (victim_stopped_moving) + message += " thanks to your stillness" + message += "." + + to_chat(victim, span_danger(message)) + victim.balloon_alert(victim, "oscillation over") + + oscillating = FALSE + +#undef AFTERSHOCK_GRACE_THRESHOLD_PERCENT + +/// Called when percussive maintenance succeeds at its random roll. +/datum/wound/blunt/robotic/proc/handle_percussive_maintenance_success(attacking_item, mob/living/user) + victim.visible_message(span_green("[victim]'s [limb.plaintext_zone] rattles from the impact, but looks a lot more secure!"), \ + span_green("Your [limb.plaintext_zone] rattles into place!")) + remove_wound() + +/// Called when percussive maintenance fails at its random roll. +/datum/wound/blunt/robotic/proc/handle_percussive_maintenance_failure(attacking_item, mob/living/user) + to_chat(victim, span_warning("Your [limb.plaintext_zone] rattles around, but you don't sense any sign of improvement.")) + +/// If our victim has no gravity, the effects of movement are multiplied by this. +#define VICTIM_MOVED_NO_GRAVITY_EFFECT_MULT 0.5 +/// If our victim is resting, or is walking and isnt forced to move, the effects of movement are multiplied by this. +#define VICTIM_MOVED_CAREFULLY_EFFECT_MULT 0.25 + +/// Signal handler proc that applies movements affect to our victim if they were moved. +/datum/wound/blunt/robotic/proc/victim_moved(datum/source, atom/old_loc, dir, forced, list/old_locs) + SIGNAL_HANDLER + + var/overall_mult = 1 + + var/obj/item/stack/gauze = limb.current_gauze + if (gauze) + overall_mult *= gauze.splint_factor + if (!victim.has_gravity(get_turf(victim))) + overall_mult *= VICTIM_MOVED_NO_GRAVITY_EFFECT_MULT + else if (victim.body_position == LYING_DOWN || (!forced && victim.move_intent == MOVE_INTENT_WALK)) + overall_mult *= VICTIM_MOVED_CAREFULLY_EFFECT_MULT + if (victim.has_status_effect(/datum/status_effect/determined)) + overall_mult *= ROBOTIC_WOUND_DETERMINATION_MOVEMENT_EFFECT_MOD + if (limb.grasped_by) + overall_mult *= ROBOTIC_BLUNT_GRASPED_MOVEMENT_MULT + + overall_mult *= get_buckled_movement_consequence_mult(victim.buckled) + + if (limb.body_zone == BODY_ZONE_CHEST && COOLDOWN_FINISHED(src, movement_stagger_cooldown)) + var/stagger_chance = chest_movement_stagger_chance * overall_mult + if (prob(stagger_chance)) + COOLDOWN_START(src, movement_stagger_cooldown, 4 SECONDS) + stagger(base_movement_stagger_score, shake_duration = base_stagger_movement_shake_duration, from_movement = TRUE, shift = movement_stagger_shift, knockdown_ratio = stagger_aftershock_knockdown_movement_ratio) + + last_time_victim_moved = world.time + +#undef VICTIM_MOVED_NO_GRAVITY_EFFECT_MULT +#undef VICTIM_MOVED_CAREFULLY_EFFECT_MULT + +/// If our victim is buckled to a generic object, movement effects will be multiplied against this. +#define VICTIM_BUCKLED_BASE_MOVEMENT_EFFECT_MULT 0.5 +/// If our victim is buckled to a medical bed (e.g. rollerbed), movement effects will be multiplied against this. +#define VICTIM_BUCKLED_ROLLER_BED_MOVEMENT_EFFECT_MULT 0.05 + +/// Returns a multiplier to our movement effects based on what our victim is buckled to. +/datum/wound/blunt/robotic/proc/get_buckled_movement_consequence_mult(atom/movable/buckled_to) + if (!buckled_to) + return 1 + + if (istype(buckled_to, /obj/structure/bed/medical)) + return VICTIM_BUCKLED_ROLLER_BED_MOVEMENT_EFFECT_MULT + else + return VICTIM_BUCKLED_BASE_MOVEMENT_EFFECT_MULT + +#undef VICTIM_BUCKLED_BASE_MOVEMENT_EFFECT_MULT +#undef VICTIM_BUCKLED_ROLLER_BED_MOVEMENT_EFFECT_MULT + +/// If this wound can be treated in its current state by just hitting it with a low force object. Exists for conditional logic, e.g. "Should we respond +/// to percussive maintenance right now?". Critical blunt uses this to only react when the limb is malleable and superstructure is broken. +/datum/wound/blunt/robotic/proc/uses_percussive_maintenance() + return FALSE + +#undef ROBOTIC_WOUND_DETERMINATION_MOVEMENT_EFFECT_MOD +#undef ROBOTIC_WOUND_DETERMINATION_STAGGER_MOVEMENT_MULT + +#undef ROBOTIC_BLUNT_GRASPED_MOVEMENT_MULT diff --git a/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T1.dm b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T1.dm new file mode 100644 index 0000000000000..d4db7f6311b91 --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T1.dm @@ -0,0 +1,65 @@ +/datum/wound/blunt/robotic/moderate + name = "Loosened Screws" + desc = "Various semi-external fastening instruments have loosened, causing components to jostle, inhibiting limb control." + treat_text = "Recommend topical re-fastening of instruments with a screwdriver, though percussive maintenance via low-force bludgeoning may suffice - \ + albeit at risk of worsening the injury." + examine_desc = "appears to be loosely secured" + occur_text = "jostles awkwardly and seems to slightly unfasten" + severity = WOUND_SEVERITY_MODERATE + simple_treat_text = "Bandaging the wound will reduce the impact until its screws are secured - which is faster if done by \ + someone else, a roboticist, an engineer, or with a diagnostic HUD." + homemade_treat_text = "In a pinch, percussive maintenance can reset the screws - the chance of which is increased if done by someone else or \ + with a diagnostic HUD!" + status_effect_type = /datum/status_effect/wound/blunt/robotic/moderate + treatable_tools = list(TOOL_SCREWDRIVER) + interaction_efficiency_penalty = 1.2 + limp_slowdown = 2.5 + limp_chance = 30 + threshold_penalty = 20 + can_scar = FALSE + a_or_from = "from" + +/datum/wound_pregen_data/blunt_metal/loose_screws + abstract = FALSE + wound_path_to_generate = /datum/wound/blunt/robotic/moderate + viable_zones = list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + threshold_minimum = 30 + +/datum/wound/blunt/robotic/moderate/uses_percussive_maintenance() + return TRUE + +/datum/wound/blunt/robotic/moderate/treat(obj/item/potential_treater, mob/user) + if (potential_treater.tool_behaviour == TOOL_SCREWDRIVER) + fasten_screws(potential_treater, user) + return TRUE + + return ..() + +/// The main treatment for T1 blunt. Uses a screwdriver, guaranteed to always work, better with a diag hud. Removes the wound. +/datum/wound/blunt/robotic/moderate/proc/fasten_screws(obj/item/screwdriver_tool, mob/user) + if (!screwdriver_tool.tool_start_check()) + return + + var/delay_mult = 1 + + if (user == victim) + delay_mult *= 2 + + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + delay_mult *= 0.5 + + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= 0.5 + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + victim.visible_message(span_notice("[user] begins fastening the screws of [their_or_other] [limb.plaintext_zone]..."), \ + span_notice("You begin fastening the screws of [your_or_other] [limb.plaintext_zone]...")) + + if (!screwdriver_tool.use_tool(target = victim, user = user, delay = (6 SECONDS * delay_mult), volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return + + victim.visible_message(span_green("[user] finishes fastening [their_or_other] [limb.plaintext_zone]!"), \ + span_green("You finish fastening [your_or_other] [limb.plaintext_zone]!")) + + remove_wound() diff --git a/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T2.dm b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T2.dm new file mode 100644 index 0000000000000..1dd35f14beddc --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T2.dm @@ -0,0 +1,52 @@ +/datum/wound/blunt/robotic/secures_internals/severe + name = "Detached Fastenings" + desc = "Various fastening devices are extremely loose and solder has disconnected at multiple points, causing significant jostling of internal components and \ + noticable limb dysfunction." + treat_text = "Fastening of bolts and screws by a qualified technician (though bone gel may suffice in the absence of one) followed by re-soldering." + examine_desc = "jostles with every move, solder visibly broken" + occur_text = "visibly cracks open, solder flying everywhere" + severity = WOUND_SEVERITY_SEVERE + + simple_treat_text = "If on the chest, walk, grasp it, splint, rest or buckle yourself to something to reduce movement effects. \ + Afterwards, get someone else, ideally a robo/engi to screwdriver/wrench it, and then re-solder it!" + homemade_treat_text = "If unable to screw/wrench, bone gel can, over time, secure inner components at risk of corrossion. \ + Alternatively, crowbar the limb open to expose the internals - this will make it easier to re-secure them, but has a high risk of shocking you, \ + so use insulated gloves. This will cripple the limb, so use it only as a last resort!" + + wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|SPLINT_OVERLAY|CAN_BE_GRASPED) + treatable_by = list(/obj/item/stack/medical/bone_gel) + status_effect_type = /datum/status_effect/wound/blunt/robotic/severe + treatable_tools = list(TOOL_WELDER, TOOL_CROWBAR) + + interaction_efficiency_penalty = 2 + limp_slowdown = 6 + limp_chance = 60 + + brain_trauma_group = BRAIN_TRAUMA_MILD + trauma_cycle_cooldown = 1.5 MINUTES + + threshold_penalty = 40 + + base_movement_stagger_score = 40 + + chest_attacked_stagger_chance_ratio = 5 + chest_attacked_stagger_mult = 3 + + chest_movement_stagger_chance = 2 + + stagger_aftershock_knockdown_ratio = 0.3 + stagger_aftershock_knockdown_movement_ratio = 0.2 + + a_or_from = "from" + + ready_to_secure_internals = TRUE + ready_to_resolder = FALSE + + scar_keyword = "bluntsevere" + +/datum/wound_pregen_data/blunt_metal/fastenings + abstract = FALSE + + wound_path_to_generate = /datum/wound/blunt/robotic/secures_internals/severe + + threshold_minimum = 65 diff --git a/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T3.dm b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T3.dm new file mode 100644 index 0000000000000..0c06eff483f69 --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/blunt/robotic_blunt_T3.dm @@ -0,0 +1,398 @@ +/datum/wound/blunt/robotic/secures_internals/critical + name = "Collapsed Superstructure" + desc = "The superstructure has totally collapsed in one or more locations, causing extreme internal oscillation with every move and massive limb dysfunction" + treat_text = "Reforming of superstructure via either RCD or manual molding, followed by typical treatment of loosened internals. \ + To manually mold, the limb must be aggressively grabbed and welded held to it to make it malleable (though attacking it til thermal overload may be adequate) \ + followed by firmly grasping and molding the limb with heat-resistant gloves." + occur_text = "caves in on itself, damaged solder and shrapnel flying out in a miniature explosion" + examine_desc = "has caved in, with internal components visible through gaps in the metal" + severity = WOUND_SEVERITY_CRITICAL + + disabling = TRUE + + simple_treat_text = "If on the chest, walk, grasp it, splint, rest or buckle yourself to something to reduce movement effects. \ + Afterwards, get someone, ideally a robo/engi to firmly grasp the limb and hold a welder to it. Then, have them use their hands to mold the metal - \ + careful though, it's hot! An RCD can skip all this, but is hard to come by. Afterwards, have them screw/wrench and then re-solder the limb!" + + homemade_treat_text = "The metal can be made malleable by repeated application of a welder, to a severe burn. Afterwards, a plunger can reset the metal, \ + as can percussive maintenance. After the metal is reset, if unable to screw/wrench, bone gel can, over time, secure inner components at risk of corrossion. \ + Alternatively, crowbar the limb open to expose the internals - this will make it easier to re-secure them, but has a high risk of shocking you, \ + so use insulated gloves. This will cripple the limb, so use it only as a last resort!" + + interaction_efficiency_penalty = 2.8 + limp_slowdown = 8 + limp_chance = 80 + threshold_penalty = 60 + + brain_trauma_group = BRAIN_TRAUMA_SEVERE + trauma_cycle_cooldown = 2.5 MINUTES + + scar_keyword = "bluntcritical" + + status_effect_type = /datum/status_effect/wound/blunt/robotic/critical + + sound_effect = 'sound/effects/wounds/crack2.ogg' + + wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|SPLINT_OVERLAY|CAN_BE_GRASPED) + treatable_by = list(/obj/item/stack/medical/bone_gel) + status_effect_type = /datum/status_effect/wound/blunt/robotic/critical + treatable_tools = list(TOOL_WELDER, TOOL_CROWBAR) + + base_movement_stagger_score = 50 + + base_aftershock_camera_shake_duration = 1.75 SECONDS + base_aftershock_camera_shake_strength = 1 + + chest_attacked_stagger_chance_ratio = 6.5 + chest_attacked_stagger_mult = 4 + + chest_movement_stagger_chance = 8 + + aftershock_stopped_moving_score_mult = 0.3 + + stagger_aftershock_knockdown_ratio = 0.5 + stagger_aftershock_knockdown_movement_ratio = 0.3 + + percussive_maintenance_repair_chance = 3 + percussive_maintenance_damage_max = 6 + + regen_time_needed = 60 SECONDS + gel_damage = 20 + + ready_to_secure_internals = FALSE + ready_to_resolder = FALSE + + a_or_from = "a" + + /// Has the first stage of our treatment been completed? E.g. RCDed, manually molded... + var/superstructure_remedied = FALSE + +/datum/wound_pregen_data/blunt_metal/superstructure + abstract = FALSE + wound_path_to_generate = /datum/wound/blunt/robotic/secures_internals/critical + threshold_minimum = 125 + +/datum/wound/blunt/robotic/secures_internals/critical/item_can_treat(obj/item/potential_treater) + if(!superstructure_remedied) + if(istype(potential_treater, /obj/item/construction/rcd)) + return TRUE + if(limb_malleable() && istype(potential_treater, /obj/item/plunger)) + return TRUE + return ..() + +/datum/wound/blunt/robotic/secures_internals/critical/check_grab_treatments(obj/item/potential_treater, mob/user) + if(potential_treater.tool_behaviour == TOOL_WELDER && (!superstructure_remedied && !limb_malleable())) + return TRUE + return ..() + +/datum/wound/blunt/robotic/secures_internals/critical/treat(obj/item/item, mob/user) + if(!superstructure_remedied) + if(istype(item, /obj/item/construction/rcd)) + return rcd_superstructure(item, user) + if(uses_percussive_maintenance() && istype(item, /obj/item/plunger)) + return plunge(item, user) + if(item.tool_behaviour == TOOL_WELDER && !limb_malleable() && isliving(victim.pulledby)) + var/mob/living/living_puller = victim.pulledby + if (living_puller.grab_state >= GRAB_AGGRESSIVE) // only let other people do this + return heat_metal(item, user) + return ..() + +/datum/wound/blunt/robotic/secures_internals/critical/try_handling(mob/living/carbon/human/user) + if(user.pulling != victim || user.zone_selected != limb.body_zone) + return FALSE + + if(superstructure_remedied || !limb_malleable()) + return FALSE + + if(user.grab_state < GRAB_AGGRESSIVE) + to_chat(user, span_warning("You must have [victim] in an aggressive grab to manipulate [victim.p_their()] [LOWER_TEXT(name)]!")) + return TRUE + + user.visible_message(span_danger("[user] begins softly pressing against [victim]'s collapsed [limb.plaintext_zone]..."), \ + span_notice("You begin softly pressing against [victim]'s collapsed [limb.plaintext_zone]..."), \ + ignored_mobs = victim) + to_chat(victim, span_userdanger("[user] begins pressing against your collapsed [limb.plaintext_zone]!")) + + var/delay_mult = 1 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= 0.75 + + if(!do_after(user, 4 SECONDS, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return + mold_metal(user) + return TRUE + +/// If the user turns combat mode on after they start to mold metal, our limb takes this much brute damage. +#define MOLD_METAL_SABOTAGE_BRUTE_DAMAGE 30 // really punishing +/// Our limb takes this much brute damage on a failed mold metal attempt. +#define MOLD_METAL_FAILURE_BRUTE_DAMAGE 5 +/// If the user's hand is unprotected from heat when they mold metal, we do this much burn damage to it. +#define MOLD_METAL_HAND_BURNT_BURN_DAMAGE 5 +/// Gloves must be above or at this threshold to cause the user to not be burnt apon trying to mold metal. +#define MOLD_METAL_HEAT_RESISTANCE_THRESHOLD 1000 // less than the black gloves max resist +/** + * Standard treatment for 1st step of T3, after the limb has been made malleable. Done via aggrograb. + * High chance to work, very high with robo/engi wires and diag hud. + * Can be sabotaged by switching to combat mode. + * Deals brute to the limb on failure. + * Burns the hand of the user if it's not insulated. + */ +/datum/wound/blunt/robotic/secures_internals/critical/proc/mold_metal(mob/living/carbon/human/user) + var/chance = 60 + + var/knows_wires = FALSE + if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES)) + chance *= 2 + knows_wires = TRUE + else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES)) + chance *= 1.25 + knows_wires = TRUE + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + chance *= 2 + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + if (knows_wires) + chance *= 1.25 + else + chance *= 2 + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + + if ((user != victim && user.combat_mode)) + user.visible_message(span_bolddanger("[user] molds [their_or_other] [limb.plaintext_zone] into a really silly shape! What a goofball!"), \ + span_danger("You maliciously mold [victim]'s [limb.plaintext_zone] into a weird shape, damaging it in the process!"), ignored_mobs = victim) + to_chat(victim, span_userdanger("[user] molds your [limb.plaintext_zone] into a weird shape, damaging it in the process!")) + + limb.receive_damage(brute = MOLD_METAL_SABOTAGE_BRUTE_DAMAGE, wound_bonus = CANT_WOUND, damage_source = user) + else if (prob(chance)) + user.visible_message(span_green("[user] carefully molds [their_or_other] [limb.plaintext_zone] into the proper shape!"), \ + span_green("You carefully mold [victim]'s [limb.plaintext_zone] into the proper shape!"), ignored_mobs = victim) + to_chat(victim, span_green("[user] carefully molds your [limb.plaintext_zone] into the proper shape!")) + to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone] has been molded into the proper shape! Your next step is to use a screwdriver/wrench to secure your internals.")) + set_superstructure_status(TRUE) + else + user.visible_message(span_danger("[user] accidentally molds [their_or_other] [limb.plaintext_zone] into the wrong shape!"), \ + span_danger("You accidentally mold [your_or_other] [limb.plaintext_zone] into the wrong shape!"), ignored_mobs = victim) + to_chat(victim, span_userdanger("[user] accidentally molds your [limb.plaintext_zone] into the wrong shape!")) + + limb.receive_damage(brute = MOLD_METAL_FAILURE_BRUTE_DAMAGE, damage_source = user, wound_bonus = CANT_WOUND) + + var/sufficiently_insulated_gloves = FALSE + var/obj/item/clothing/gloves/worn_gloves = user.gloves + if ((worn_gloves?.heat_protection & HANDS) && worn_gloves?.max_heat_protection_temperature && worn_gloves.max_heat_protection_temperature >= MOLD_METAL_HEAT_RESISTANCE_THRESHOLD) + sufficiently_insulated_gloves = TRUE + + if (sufficiently_insulated_gloves || HAS_TRAIT(user, TRAIT_RESISTHEAT) || HAS_TRAIT(user, TRAIT_RESISTHEATHANDS)) + return + + to_chat(user, span_danger("You burn your hand on [victim]'s [limb.plaintext_zone]!")) + var/obj/item/bodypart/affecting = user.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm") + affecting?.receive_damage(burn = MOLD_METAL_HAND_BURNT_BURN_DAMAGE, damage_source = limb) + +#undef MOLD_METAL_SABOTAGE_BRUTE_DAMAGE +#undef MOLD_METAL_FAILURE_BRUTE_DAMAGE +#undef MOLD_METAL_HAND_BURNT_BURN_DAMAGE +#undef MOLD_METAL_HEAT_RESISTANCE_THRESHOLD + +/** + * A "safe" way to give our victim a T2 burn wound. Requires an aggrograb, and a welder. This is required to mold metal, the 1st step of treatment. + * Guaranteed to work. After a delay, causes a T2 burn wound with no damage. + * Can be sabotaged by enabling combat mode to cause a T3. + */ +/datum/wound/blunt/robotic/secures_internals/critical/proc/heat_metal(obj/item/welder, mob/living/user) + if (!welder.tool_use_check()) + return TRUE + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + + user?.visible_message(span_danger("[user] carefully holds [welder] to [their_or_other] [limb.plaintext_zone], slowly heating it..."), \ + span_warning("You carefully hold [welder] to [your_or_other] [limb.plaintext_zone], slowly heating it..."), ignored_mobs = victim) + + var/delay_mult = 1 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= 0.75 + + if (!welder.use_tool(target = victim, user = user, delay = 3 SECONDS * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return TRUE + + var/wound_path = /datum/wound/burn/robotic/overheat/moderate + if (user != victim && user.combat_mode) + wound_path = /datum/wound/burn/robotic/overheat/critical // it really isnt that bad, overheat wounds are a bit funky + user.visible_message(span_danger("[user] heats [victim]'s [limb.plaintext_zone] aggressively, overheating it far beyond the necessary point!"), \ + span_danger("You heat [victim]'s [limb.plaintext_zone] aggressively, overheating it far beyond the necessary point!"), ignored_mobs = victim) + to_chat(victim, span_userdanger("[user] heats your [limb.plaintext_zone] aggressively, overheating it far beyond the necessary point!")) + + var/datum/wound/burn/robotic/overheat/overheat_wound = new wound_path + overheat_wound.apply_wound(limb, wound_source = welder) + + to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone] is now heated, allowing it to be molded! Your next step is to have someone physically reset the superstructure with their hands.")) + return TRUE + +/// Cost of an RCD to quickly fix our broken in raw matter +#define ROBOTIC_T3_BLUNT_WOUND_RCD_COST 25 +/// Cost of an RCD to quickly fix our broken in silo material +#define ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST ROBOTIC_T3_BLUNT_WOUND_RCD_COST / 4 + +/// The "premium" treatment for 1st step of T3. Requires an RCD. Guaranteed to work, but can cause damage if delay is high. +/datum/wound/blunt/robotic/secures_internals/critical/proc/rcd_superstructure(obj/item/construction/rcd/treating_rcd, mob/user) + if (!treating_rcd.tool_use_check()) + return TRUE + + var/has_enough_matter = (treating_rcd.get_matter(user) > ROBOTIC_T3_BLUNT_WOUND_RCD_COST) + var/silo_has_enough_materials = (treating_rcd.get_silo_iron() > ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST) + + if (!silo_has_enough_materials && !has_enough_matter) // neither the silo, nor the rcd, has enough + user?.balloon_alert(user, "not enough matter!") + return TRUE + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + + var/base_time = 7 SECONDS + var/delay_mult = 1 + var/knows_wires = FALSE + if (victim == user) + delay_mult *= 2 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= 0.75 + if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES)) + delay_mult *= 0.5 + knows_wires = TRUE + else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES)) + delay_mult *= 0.5 // engis are accustomed to using RCDs + knows_wires = TRUE + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + if (knows_wires) + delay_mult *= 0.85 + else + delay_mult *= 0.5 + + var/final_time = (base_time * delay_mult) + var/misused = (final_time > base_time) // if we damage the limb when we're done + + if (user) + var/misused_text = (misused ? "unsteadily " : "") + + var/message = "[user]'s RCD whirs to life as it begins [misused_text]replacing the damaged superstructure of [their_or_other] [limb.plaintext_zone]..." + var/self_message = "Your RCD whirs to life as it begins [misused_text]replacing the damaged superstructure of [your_or_other] [limb.plaintext_zone]..." + + if (misused) // warning span if misused, notice span otherwise + message = span_danger(message) + self_message = span_danger(self_message) + else + message = span_notice(message) + self_message = span_notice(self_message) + + user.visible_message(message, self_message) + + if (!treating_rcd.use_tool(target = victim, user = user, delay = final_time, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return TRUE + playsound(get_turf(treating_rcd), 'sound/machines/ping.ogg', 75) // celebration! we did it + set_superstructure_status(TRUE) + + var/use_amount = (silo_has_enough_materials ? ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST : ROBOTIC_T3_BLUNT_WOUND_RCD_COST) + if (!treating_rcd.useResource(use_amount, user)) + return TRUE + + if (user) + var/misused_text = (misused ? ", though it replaced a bit more than it should've..." : "!") + var/message = "[user]'s RCD lets out a small ping as it finishes replacing the superstructure of [their_or_other] [limb.plaintext_zone][misused_text]" + var/self_message = "Your RCD lets out a small ping as it finishes replacing the superstructure of [your_or_other] [limb.plaintext_zone][misused_text]" + if (misused) + message = span_danger(message) + self_message = span_danger(self_message) + else + message = span_green(message) + self_message = span_green(self_message) + + user.visible_message(message, self_message) + if (misused) + limb.receive_damage(brute = 10, damage_source = treating_rcd, wound_bonus = CANT_WOUND) + // the double message is fine here, since the first message also tells you if you fucked up and did some damage + to_chat(user, span_green("The superstructure has been reformed! Your next step is to secure the internals via a screwdriver/wrench.")) + return TRUE + +#undef ROBOTIC_T3_BLUNT_WOUND_RCD_COST +#undef ROBOTIC_T3_BLUNT_WOUND_RCD_SILO_COST + +/** + * Goofy but practical, this is the superior ghetto self-tend of T3's first step compared to percussive maintenance. + * Still requires the limb to be malleable, but has a high chance of success and doesn't burn your hand, but gives worse bonuses for wires/HUD. + */ +/datum/wound/blunt/robotic/secures_internals/critical/proc/plunge(obj/item/plunger/treating_plunger, mob/user) + if (!treating_plunger.tool_use_check()) + return TRUE + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + user?.visible_message(span_notice("[user] begins plunging at the dents on [their_or_other] [limb.plaintext_zone] with [treating_plunger]..."), \ + span_green("You begin plunging at the dents on [your_or_other] [limb.plaintext_zone] with [treating_plunger]...")) + + var/delay_mult = 1 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= 0.75 + + delay_mult /= treating_plunger.plunge_mod + + if (!treating_plunger.use_tool(target = victim, user = user, delay = 6 SECONDS * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return TRUE + + var/success_chance = 80 + if (victim == user) + success_chance *= 0.6 + + if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES)) + success_chance *= 1.25 + else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES)) + success_chance *= 1.1 + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + success_chance *= 1.25 // it's kinda alien to do this, so even people with the wires get the full bonus of diag huds + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + success_chance *= 1.5 + + if (prob(success_chance)) + user?.visible_message(span_green("[victim]'s [limb.plaintext_zone] lets out a sharp POP as [treating_plunger] forces it into its normal position!"), \ + span_green("[victim]'s [limb.plaintext_zone] lets out a sharp POP as your [treating_plunger] forces it into its normal position!")) + to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone]'s structure has been reset to its proper position! Your next step is to secure it with a screwdriver/wrench, though bone gel would also work.")) + set_superstructure_status(TRUE) + else + user?.visible_message(span_danger("[victim]'s [limb.plaintext_zone] splinters from [treating_plunger]'s plunging!"), \ + span_danger("[capitalize(your_or_other)] [limb.plaintext_zone] splinters from your [treating_plunger]'s plunging!")) + limb.receive_damage(brute = 5, damage_source = treating_plunger) + + return TRUE + +/datum/wound/blunt/robotic/secures_internals/critical/handle_percussive_maintenance_success(attacking_item, mob/living/user) + var/your_or_other = (user == victim ? "your" : "[victim]'s") + victim.visible_message(span_green("[victim]'s [limb.plaintext_zone] gets smashed into a proper shape!"), \ + span_green("Your [limb.plaintext_zone] gets smashed into a proper shape!")) + + var/user_message = "[capitalize(your_or_other)] [limb.plaintext_zone]'s superstructure has been reset! Your next step is to screwdriver/wrench the internals, \ + though if you're desperate enough to use percussive maintenance, you might want to either use a crowbar or bone gel..." + to_chat(user, span_green(user_message)) + + set_superstructure_status(TRUE) + +/datum/wound/blunt/robotic/secures_internals/critical/handle_percussive_maintenance_failure(attacking_item, mob/living/user) + to_chat(victim, span_danger("Your [limb.plaintext_zone] only deforms more from the impact...")) + limb.receive_damage(brute = 1, damage_source = attacking_item, wound_bonus = CANT_WOUND) + +/datum/wound/blunt/robotic/secures_internals/critical/uses_percussive_maintenance() + return (!superstructure_remedied && limb_malleable()) + +/// Transitions our steps by setting both superstructure and secure internals readiness. +/datum/wound/blunt/robotic/secures_internals/critical/proc/set_superstructure_status(remedied) + superstructure_remedied = remedied + ready_to_secure_internals = remedied + +/datum/wound/blunt/robotic/secures_internals/critical/get_wound_step_info() + . = ..() + + if (!superstructure_remedied) + . = "The superstructure must be reformed." + if (!limb_malleable()) + . += " The limb must be heated to thermal overload, then manually molded with a firm grasp" + else + . += " The limb has been sufficiently heated, and can be manually molded with a firm grasp/repeated application of a low-force object" + . += " - OR an RCD may be used with little risk." diff --git a/modular_doppler/modular_medical/wounds/synth/blunt/secures_internals.dm b/modular_doppler/modular_medical/wounds/synth/blunt/secures_internals.dm new file mode 100644 index 0000000000000..335fa074a3e2f --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/blunt/secures_internals.dm @@ -0,0 +1,416 @@ +/// A subtype of blunt wounds that has a "secure internals" step +/datum/wound/blunt/robotic/secures_internals + /// Our current counter for gel + gauze regeneration + var/regen_time_elapsed = 0 SECONDS + /// Time needed for gel to secure internals. + var/regen_time_needed = 30 SECONDS + + /// If we have used bone gel to secure internals. + var/gelled = FALSE + /// Total brute damage taken over the span of [regen_time_needed] deciseconds when we gel our limb. + var/gel_damage = 10 // brute in total + + /// If we are ready to begin screwdrivering or gelling our limb. + var/ready_to_secure_internals = FALSE + /// If our external plating has been torn open and we can access our internals without a tool + var/crowbarred_open = FALSE + /// If internals are secured, and we are ready to weld our limb closed and end the wound + var/ready_to_resolder = TRUE + +/datum/wound/blunt/robotic/secures_internals/handle_process(seconds_per_tick, times_fired) + . = ..() + + if (!victim || HAS_TRAIT(victim, TRAIT_STASIS)) + return + + if (gelled) + regen_time_elapsed += ((seconds_per_tick SECONDS) / 2) + if(victim.body_position == LYING_DOWN) + if(SPT_PROB(30, seconds_per_tick)) + regen_time_elapsed += 1 SECONDS + if(victim.IsSleeping() && SPT_PROB(30, seconds_per_tick)) + regen_time_elapsed += 1 SECONDS + + var/effective_damage = ((gel_damage / (regen_time_needed / 10)) * seconds_per_tick) + var/obj/item/stack/gauze = limb.current_gauze + if (gauze) + effective_damage *= gauze.splint_factor + limb.receive_damage(effective_damage, wound_bonus = CANT_WOUND, damage_source = src) + if(effective_damage && prob(33)) + var/gauze_text = (gauze?.splint_factor ? ", although the [gauze] helps to prevent some of the leakage" : "") + to_chat(victim, span_danger("Your [limb.plaintext_zone] sizzles as some gel leaks and warps the exterior metal[gauze_text]...")) + + if(regen_time_elapsed > regen_time_needed) + if(!victim || !limb) + qdel(src) + return + to_chat(victim, span_green("The gel within your [limb.plaintext_zone] has fully hardened, allowing you to re-solder it!")) + gelled = FALSE + ready_to_resolder = TRUE + ready_to_secure_internals = FALSE + set_disabling(FALSE) + +/datum/wound/blunt/robotic/secures_internals/modify_desc_before_span(desc) + . = ..() + + var/use_exclamation = FALSE + + if (!limb.current_gauze) // gauze covers it up + if (crowbarred_open) + . += ", [span_notice("and is violently torn open, internals visible to the outside")]" + use_exclamation = TRUE + if (gelled) + . += ", [span_notice("with fizzling blue surgical gel leaking out of the cracks")]" + use_exclamation = TRUE + if (use_exclamation) + . += "!" + +/datum/wound/blunt/robotic/secures_internals/get_scanner_description(mob/user) + . = ..() + + var/to_add = get_wound_status() + if (!isnull(to_add)) + . += "\nWound status: [to_add]" + +/datum/wound/blunt/robotic/secures_internals/get_simple_scanner_description(mob/user) + . = ..() + + var/to_add = get_wound_status() + if (!isnull(to_add)) + . += "\nWound status: [to_add]" + +/// Returns info specific to the dynamic state of the wound. +/datum/wound/blunt/robotic/secures_internals/proc/get_wound_status(mob/user) + if (crowbarred_open) + . += "The limb has been torn open, allowing ease of access to internal components, but also disabling it. " + if (gelled) + . += "Bone gel has been applied, causing progressive corrosion of the metal, but eventually securing the internals. " + +/datum/wound/blunt/robotic/secures_internals/item_can_treat(obj/item/potential_treater, mob/user) + if (potential_treater.tool_behaviour == TOOL_WELDER || potential_treater.tool_behaviour == TOOL_CAUTERY) + if (ready_to_resolder) + return TRUE + + if (ready_to_secure_internals) + if (item_can_secure_internals(potential_treater)) + return TRUE + + return ..() + +/datum/wound/blunt/robotic/secures_internals/treat(obj/item/potential_treater, mob/user) + if (ready_to_secure_internals) + if (istype(potential_treater, /obj/item/stack/medical/bone_gel)) + return apply_gel(potential_treater, user) + else if (!crowbarred_open && potential_treater.tool_behaviour == TOOL_CROWBAR) + return crowbar_open(potential_treater, user) + else if (item_can_secure_internals(potential_treater)) + return secure_internals_normally(potential_treater, user) + else if (ready_to_resolder && (potential_treater.tool_behaviour == TOOL_WELDER) || (potential_treater.tool_behaviour == TOOL_CAUTERY)) + return resolder(potential_treater, user) + + return ..() + +/// Returns TRUE if the item can be used in our 1st step (2nd if T3) of repairs. +/datum/wound/blunt/robotic/secures_internals/proc/item_can_secure_internals(obj/item/potential_treater) + return (potential_treater.tool_behaviour == TOOL_SCREWDRIVER || potential_treater.tool_behaviour == TOOL_WRENCH || istype(potential_treater, /obj/item/stack/medical/bone_gel)) + +#define CROWBAR_OPEN_SELF_TEND_DELAY_MULT 2 +#define CROWBAR_OPEN_KNOWS_ROBO_WIRES_DELAY_MULT 0.5 +#define CROWBAR_OPEN_KNOWS_ENGI_WIRES_DELAY_MULT 0.5 +#define CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT 0.5 +#define CROWBAR_OPEN_WOUND_SCANNED_DELAY_MULT 0.5 +/// If our limb is essential, damage dealt to it by tearing it open will be multiplied against this. +#define CROWBAR_OPEN_ESSENTIAL_LIMB_DAMAGE_MULT 1.5 + +/// The "power" put into electrocute_act whenever someone gets shocked when they crowbar open our limb +#define CROWBAR_OPEN_SHOCK_POWER 20 +/// The brute damage done to this limb (doubled on essential limbs) when it is crowbarred open +#define CROWBAR_OPEN_BRUTE_DAMAGE 20 + +/** + * Available during the "secure internals" step of T2 and T3. Requires a crowbar. Low-quality ghetto option. + * + * Tears open the limb, exposing internals. This massively increases the chance of secure internals succeeding, and removes the self-tend malice. + * + * Deals significant damage to the limb, and shocks the user (causing failure) if victim is alive, this limb is wired, and user is not insulated. + */ +/datum/wound/blunt/robotic/secures_internals/proc/crowbar_open(obj/item/crowbarring_item, mob/living/user) + if (!crowbarring_item.tool_start_check()) + return TRUE + + var/delay_mult = 1 + if (user == victim) + delay_mult *= CROWBAR_OPEN_SELF_TEND_DELAY_MULT + + var/knows_wires = FALSE + if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES)) + delay_mult *= CROWBAR_OPEN_KNOWS_ROBO_WIRES_DELAY_MULT + knows_wires = TRUE + else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES)) + delay_mult *= CROWBAR_OPEN_KNOWS_ENGI_WIRES_DELAY_MULT + knows_wires = TRUE + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + if (knows_wires) + delay_mult *= (CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT * 1.5) + else + delay_mult *= CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= CROWBAR_OPEN_WOUND_SCANNED_DELAY_MULT + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + + var/limb_can_shock_pre_sleep = (victim.stat != DEAD && limb.biological_state & BIO_WIRED) + var/shock_or_not = (limb_can_shock_pre_sleep ? ", risking electrocution" : "") + var/self_message = span_warning("You start prying open [your_or_other] [limb.plaintext_zone] with [crowbarring_item][shock_or_not]...") + + user?.visible_message(span_bolddanger("[user] starts prying open [their_or_other] [limb.plaintext_zone] with [crowbarring_item]!"), self_message, ignored_mobs = list(victim)) + + var/victim_message + if (user != victim) // this exists so we can do a userdanger + victim_message = span_userdanger("[user] starts prying open your [limb.plaintext_zone] with [crowbarring_item]!") + else + victim_message = self_message + to_chat(victim, victim_message) + + playsound(get_turf(crowbarring_item), 'sound/machines/airlock/airlock_alien_prying.ogg', 30, TRUE) + if (!crowbarring_item.use_tool(target = victim, user = user, delay = (7 SECONDS * delay_mult), volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return TRUE + + var/limb_can_shock = (victim.stat != DEAD && limb.biological_state & BIO_WIRED) // re-define the previous shock variable because we slept + var/stunned = FALSE + + var/message + + if (user && limb_can_shock) + var/electrocute_flags = (SHOCK_KNOCKDOWN|SHOCK_NO_HUMAN_ANIM|SHOCK_SUPPRESS_MESSAGE) + var/stun_chance = 100 + + if (HAS_TRAIT(user, TRAIT_SHOCKIMMUNE)) + stun_chance = 0 + + else if (iscarbon(user)) // doesn't matter if we're shock immune, it's set to 0 anyway + var/mob/living/carbon/carbon_user = user + if (carbon_user.gloves) + stun_chance *= carbon_user.gloves.siemens_coefficient + + if (ishuman(user)) + var/mob/living/carbon/human/human_user = user + stun_chance *= human_user.physiology.siemens_coeff + stun_chance *= carbon_user.dna.species.siemens_coeff + + if (stun_chance && prob(stun_chance)) + electrocute_flags &= ~SHOCK_KNOCKDOWN + electrocute_flags &= ~SHOCK_NO_HUMAN_ANIM + stunned = TRUE + + message = span_boldwarning("[user] is shocked by [their_or_other] [limb.plaintext_zone], [user.p_their()] crowbar slipping as [user.p_they()] briefly convulse!") + self_message = span_userdanger("You are shocked by [your_or_other] [limb.plaintext_zone], causing your crowbar to slip out!") + if (user != victim) + victim_message = span_userdanger("[user] is shocked by your [limb.plaintext_zone] in [user.p_their()] efforts to tear it open!") + + var/shock_damage = CROWBAR_OPEN_SHOCK_POWER + if (limb.current_gauze) + shock_damage *= limb.current_gauze.splint_factor // always good to let gauze do something + user.electrocute_act(shock_damage, limb, flags = electrocute_flags) + + if (!stunned) + var/other_shock_text = "" + var/self_shock_text = "" + if (!limb_can_shock) + other_shock_text = ", and is striken by golden bolts of electricity" + self_shock_text = ", but are immediately shocked by the electricity contained within" + message = span_boldwarning("[user] tears open [their_or_other] [limb.plaintext_zone] with [user.p_their()] crowbar[other_shock_text]!") + self_message = span_warning("You tear open [your_or_other] [limb.plaintext_zone] with your crowbar[self_shock_text]!") + if (user != victim) + victim_message = span_userdanger("Your [limb.plaintext_zone] fragments and splinters as [user] tears it open with [user.p_their()] crowbar!") + + playsound(get_turf(crowbarring_item), 'sound/effects/bang.ogg', 35, TRUE) // we did it! + to_chat(user, span_green("You've torn [your_or_other] [limb.plaintext_zone] open, heavily damaging it but making it a lot easier to screwdriver the internals!")) + var/damage = CROWBAR_OPEN_BRUTE_DAMAGE + if (limb_essential()) // can't be disabled + damage *= CROWBAR_OPEN_ESSENTIAL_LIMB_DAMAGE_MULT + limb.receive_damage(brute = CROWBAR_OPEN_BRUTE_DAMAGE, wound_bonus = CANT_WOUND, damage_source = crowbarring_item) + set_torn_open(TRUE) + + if (user == victim) + victim_message = self_message + + user.visible_message(message, self_message, ignored_mobs = list(victim)) + to_chat(victim, victim_message) + return TRUE + +#undef CROWBAR_OPEN_SELF_TEND_DELAY_MULT +#undef CROWBAR_OPEN_KNOWS_ROBO_WIRES_DELAY_MULT +#undef CROWBAR_OPEN_KNOWS_ENGI_WIRES_DELAY_MULT +#undef CROWBAR_OPEN_HAS_DIAG_HUD_DELAY_MULT +#undef CROWBAR_OPEN_WOUND_SCANNED_DELAY_MULT +#undef CROWBAR_OPEN_ESSENTIAL_LIMB_DAMAGE_MULT + +#undef CROWBAR_OPEN_BRUTE_DAMAGE +#undef CROWBAR_OPEN_SHOCK_POWER + +/// Sets [crowbarred_open] to the new value. If we werent originally disabling, or if we arent currently and we're torn open, we set disabling to true. +/datum/wound/blunt/robotic/secures_internals/proc/set_torn_open(torn_open_state) + // if we aren't disabling but we were torn open, OR if we aren't disabling by default + var/should_update_disabling = ((!disabling && torn_open_state) || !initial(disabling)) + + crowbarred_open = torn_open_state + if(should_update_disabling) + set_disabling(torn_open_state) + +/// If, on a secure internals attempt, we have less than this chance to succeed, we warn the user. +#define SECURE_INTERNALS_CONFUSED_CHANCE_THRESHOLD 25 +#define SECURE_INTERNALS_FAILURE_BRUTE_DAMAGE 5 + +/** + * The primary way of performing the secure internals step for T2/T3. Uses a screwdriver/wrench. Very hard to do by yourself, or without a diag hud/wire knowledge. + * Roboticists/engineers have a very high chance of succeeding. + * Deals some brute damage on failure, but moves to the final step of treatment (re-soldering) on success. + * + * If [crowbarred_open], made far more likely and remove the self-tend malice. + */ +/datum/wound/blunt/robotic/secures_internals/proc/secure_internals_normally(obj/item/securing_item, mob/user) + if (!securing_item.tool_start_check()) + return TRUE + + var/chance = 10 + var/delay_mult = 1 + + if (user == victim) + if (!crowbarred_open) + chance *= 0.2 + delay_mult *= 2 + + var/knows_wires = FALSE + if (crowbarred_open) + chance *= 4 // even self-tends get a high chance of success if torn open! + if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES)) + chance *= 8 // almost guaranteed if it's not self surgery - guaranteed with diag hud + delay_mult *= 0.75 + knows_wires = TRUE + else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES)) + chance *= 5.5 + delay_mult *= 0.85 + knows_wires = TRUE + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + if (knows_wires) + chance *= 1.25 // ((10 * 8) * 1.25) = 100% + else + chance *= 4 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + chance *= 1.5 // youre not intended to fix this by yourself this way + delay_mult *= 0.8 + + var/confused = (chance < SECURE_INTERNALS_CONFUSED_CHANCE_THRESHOLD) // generate chance beforehand, so we can use this var + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + user?.visible_message(span_notice("[user] begins the delicate operation of securing the internals of [their_or_other] [limb.plaintext_zone]..."), \ + span_notice("You begin the delicate operation of securing the internals of [your_or_other] [limb.plaintext_zone]...")) + if (confused) + to_chat(user, span_warning("You are confused by the layout of [your_or_other] [limb.plaintext_zone]! A diagnostic hud would help, as would knowing robo/engi wires! You could also tear the limb open with a crowbar, or get someone else to help.")) + + if (!securing_item.use_tool(target = victim, user = user, delay = (10 SECONDS * delay_mult), volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return TRUE + + if (prob(chance)) + user?.visible_message(span_green("[user] finishes securing the internals of [their_or_other] [limb.plaintext_zone]!"), \ + span_green("You finish securing the internals of [your_or_other] [limb.plaintext_zone]!")) + to_chat(user, span_green("[capitalize(your_or_other)] [limb.plaintext_zone]'s internals are now secure! Your next step is to weld/cauterize it.")) + ready_to_secure_internals = FALSE + ready_to_resolder = TRUE + else + user?.visible_message(span_danger("[user] screws up and accidentally damages [their_or_other] [limb.plaintext_zone]!")) + limb.receive_damage(brute = SECURE_INTERNALS_FAILURE_BRUTE_DAMAGE, damage_source = securing_item, wound_bonus = CANT_WOUND) + + return TRUE + +#undef SECURE_INTERNALS_CONFUSED_CHANCE_THRESHOLD +#undef SECURE_INTERNALS_FAILURE_BRUTE_DAMAGE + +/** + * "Premium" ghetto option of the secure internals step for T2/T3. Requires bone gel. Guaranteed to work. + * Deals damage over time and disables the limb, but finishes the step afterwards. + */ +/datum/wound/blunt/robotic/secures_internals/proc/apply_gel(obj/item/stack/medical/bone_gel/gel, mob/user) + if (gelled) + to_chat(user, span_warning("[user == victim ? "Your" : "[victim]'s"] [limb.plaintext_zone] is already filled with bone gel!")) + return TRUE + + var/delay_mult = 1 + if (victim == user) + delay_mult *= 1.5 + + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= 0.75 + + user.visible_message(span_danger("[user] begins hastily applying [gel] to [victim]'s [limb.plaintext_zone]..."), span_warning("You begin hastily applying [gel] to [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone], disregarding the acidic effect it seems to have on the metal...")) + + if (!do_after(user, (6 SECONDS * delay_mult), target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return TRUE + + gel.use(1) + if(user != victim) + user.visible_message(span_notice("[user] finishes applying [gel] to [victim]'s [limb.plaintext_zone], emitting a fizzing noise!"), span_notice("You finish applying [gel] to [victim]'s [limb.plaintext_zone]!"), ignored_mobs=victim) + to_chat(victim, span_userdanger("[user] finishes applying [gel] to your [limb.plaintext_zone], and you can hear the sizzling of the metal...")) + else + victim.visible_message(span_notice("[victim] finishes applying [gel] to [victim.p_their()] [limb.plaintext_zone], emitting a funny fizzing sound!"), span_notice("You finish applying [gel] to your [limb.plaintext_zone], and you can hear the sizzling of the metal...")) + + gelled = TRUE + set_disabling(TRUE) + processes = TRUE + return TRUE + +/** + * The final step of T2/T3, requires a welder/cautery. Guaranteed to work. Cautery is slower. + * Once complete, removes the wound entirely. + */ +/datum/wound/blunt/robotic/secures_internals/proc/resolder(obj/item/welding_item, mob/user) + if (!welding_item.tool_start_check()) + return TRUE + + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + victim.visible_message(span_notice("[user] begins re-soldering [their_or_other] [limb.plaintext_zone]..."), \ + span_notice("You begin re-soldering [your_or_other] [limb.plaintext_zone]...")) + + var/delay_mult = 1 + if (welding_item.tool_behaviour == TOOL_CAUTERY) + delay_mult *= 3 // less efficient + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + delay_mult *= 0.75 + + if (!welding_item.use_tool(target = victim, user = user, delay = 7 SECONDS * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return TRUE + + victim.visible_message(span_green("[user] finishes re-soldering [their_or_other] [limb.plaintext_zone]!"), \ + span_notice("You finish re-soldering [your_or_other] [limb.plaintext_zone]!")) + remove_wound() + return TRUE + +/// Returns a string with our current treatment step for use in health analyzers. +/datum/wound/blunt/robotic/secures_internals/proc/get_wound_step_info() + var/string + + if (ready_to_resolder) + string = "Apply a welder/cautery to the limb to finalize repairs." + else if (ready_to_secure_internals) + string = "Use a screwdriver/wrench to secure the internals of the limb. This step is best performed by a qualified technician. \ + In absence of one, bone gel or a crowbar may be used." + + return string + +/datum/wound/blunt/robotic/secures_internals/get_scanner_description(mob/user) + . = ..() + + var/wound_step = get_wound_step_info() + if (wound_step) + . += "\n\nCurrent step: [span_notice(wound_step)]" + +/datum/wound/blunt/robotic/secures_internals/get_simple_scanner_description(mob/user) + . = ..() + + var/wound_step = get_wound_step_info() + if (wound_step) + . += "\n\nCurrent step: [span_notice(wound_step)]" diff --git a/modular_doppler/modular_medical/wounds/synth/medicine_reagents.dm b/modular_doppler/modular_medical/wounds/synth/medicine_reagents.dm new file mode 100644 index 0000000000000..a5399f26802aa --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/medicine_reagents.dm @@ -0,0 +1,64 @@ +// a potent coolant that treats synthetic burns at decent efficiency. compared to hercuri it's worse, but without +// the lethal side effects, opting for a movement speed decrease instead +/datum/reagent/dinitrogen_plasmide + name = "Dinitrogen Plasmide" + description = "A compound of nitrogen and stabilized plasma, this substance has the ability to flash-cool overheated metals \ + while avoiding excessive damage. Being a heavy compound, it has the effect of slowing anything that metabolizes it." + ph = 4.8 + specific_heat = SPECIFIC_HEAT_PLASMA * 1.2 + color = "#b779cc" + taste_description = "dull plasma" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + process_flags = REAGENT_ORGANIC | REAGENT_SYNTHETIC + overdose_threshold = 60 // it takes a lot, if youre really messed up you CAN hit this but it's unlikely + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + +/datum/reagent/dinitrogen_plasmide/on_mob_metabolize(mob/living/affected_mob) + . = ..() + + affected_mob.add_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide) + to_chat(affected_mob, span_warning("Your joints suddenly feel stiff.")) + +/datum/reagent/dinitrogen_plasmide/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + + affected_mob.remove_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide) + affected_mob.remove_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide_overdose) + to_chat(affected_mob, span_warning("Your joints no longer feel stiff!")) + +/datum/reagent/dinitrogen_plasmide/overdose_start(mob/living/affected_mob) + . = ..() + + to_chat(affected_mob, span_danger("You feel like your joints are filling with some viscous fluid!")) + affected_mob.add_movespeed_modifier(/datum/movespeed_modifier/dinitrogen_plasmide_overdose) + +/datum/reagent/dinitrogen_plasmide/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + + holder.remove_reagent(type, 1.2 * seconds_per_tick) // decays + holder.add_reagent(/datum/reagent/stable_plasma, 0.4 * seconds_per_tick) + holder.add_reagent(/datum/reagent/nitrogen, 0.8 * seconds_per_tick) + +/datum/movespeed_modifier/dinitrogen_plasmide + multiplicative_slowdown = 0.3 + +/datum/movespeed_modifier/dinitrogen_plasmide_overdose + multiplicative_slowdown = 1.3 + +/datum/chemical_reaction/dinitrogen_plasmide_formation + results = list(/datum/reagent/dinitrogen_plasmide = 3) + required_reagents = list(/datum/reagent/stable_plasma = 1, /datum/reagent/nitrogen = 2) + required_catalysts = list(/datum/reagent/acetone = 0.1) + required_temp = 400 + optimal_temp = 550 + overheat_temp = 590 + + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_HEALING + +/obj/item/reagent_containers/spray/dinitrogen_plasmide + name = "coolant spray" + desc = "A medical spray bottle. This one contains dinitrogen plasmide, a potent coolant commonly used to treat synthetic burns. \ + Has the side effect of causing movement slowdown." + icon = 'icons/obj/medical/chemical.dmi' + icon_state = "sprayer_med_yellow" + list_reagents = list(/datum/reagent/dinitrogen_plasmide = 100) diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_burns.dm b/modular_doppler/modular_medical/wounds/synth/robotic_burns.dm new file mode 100644 index 0000000000000..b03d615f4edcc --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/robotic_burns.dm @@ -0,0 +1,484 @@ +#define OVERHEAT_ON_STASIS_HEAT_MULT 0.25 + +/datum/wound_pregen_data/burnt_metal + abstract = TRUE + required_limb_biostate = BIO_METAL + required_wounding_types = list(WOUND_BURN) + wound_series = WOUND_SERIES_METAL_BURN_OVERHEAT + +/datum/wound_pregen_data/burnt_metal/generate_scar_priorities() + return list("[BIO_METAL]") + +/datum/wound/burn/robotic/overheat + treat_text = "Introduction of a cold environment or lowering of body temperature." + + simple_desc = "Metals are overheated, increasing damage taken significantly and raising body temperature!" + simple_treat_text = "Ideally cryogenics, but any source of low body temperature can work. Spraying with spray bottles/extinguishers/showers \ + will quickly cool the limb, but cause damage. Hercuri is especially effective in quick cooling. \ + Clothing reduces the water/hercuri that makes it to the metal, and gauze binds it and reduces the damage taken." + homemade_treat_text = "You can also splash any liquid on it for a rather inefficient and damaging coolant!" + + default_scar_file = METAL_SCAR_FILE + + wound_flags = (ACCEPTS_GAUZE|SPLINT_OVERLAY|CAN_BE_GRASPED) // gauze binds the metal and makes it resistant to thermal shock + + processes = TRUE + + /// The virtual temperature of the chassis. Crucial for many things, like our severity, the temp we transfer, our cooling damage, etc. + var/chassis_temperature + + /// The lower bound of the chassis_temperature we can start with. + var/starting_temperature_min = (BODYTEMP_NORMAL + 200) + /// The upper bound of the chassis_temperature we can start with. + var/starting_temperature_max = (BODYTEMP_NORMAL + 250) + + /// If [chassis_temperature] goes below this, we reduce in severity. + var/cooling_threshold = (BODYTEMP_NORMAL + 3) + /// If [chassis_temperature] goes above this, we increase in severity. + var/heating_threshold = (BODYTEMP_NORMAL + 300) + + /// The buffer in kelvin we will subtract from the chassis_temperature of a wound we demote to. + var/cooling_demote_buffer = 60 + /// The buffer in kelvin we will add to the chassis_temperature of a wound we promote to. + var/heating_promote_buffer = 60 + + /// The coefficient of heat transfer we will use when shifting our temp to the victim's. + var/bodytemp_coeff = 0.04 + /// For every degree below normal bodytemp, we will multiply our incoming temperature by 1 + degrees * this. Allows incentivization of freezing yourself instead of just waiting. + var/bodytemp_difference_expose_bonus_ratio = 0.035 + /// The coefficient of heat transfer we will use when shifting our victim's temp to ours. + var/outgoing_bodytemp_coeff = 0 + /// The mult applied to heat output when we are on a important limb, e.g. head/torso. + var/important_outgoing_mult = 1.2 + /// The coefficient of heat transfer we will use when shifting our temp to a turf. + var/turf_coeff = 0.02 + + /// The maximum temperature we can cause by heating our victim. + var/max_outgoing_temperature = BODYTEMP_HEAT_WOUND_LIMIT - 1 + + /// If we are hit with burn damage, the damage will be multiplied against this to determine the effective heat we get. + var/incoming_damage_heat_coeff = 3 + + /// The coefficient of heat transfer we will use when receiving heat from reagent contact. + var/base_reagent_temp_coefficient = 0.02 + + /// The ratio of temp shift -> brute damage. Careful with this value, it can make stuff really really nasty. + var/heat_shock_delta_to_damage_ratio = 0.12 + /// The minimum heat difference we must have on reagent contact to cause heat shock damage. + var/heat_shock_minimum_delta = 5 + + /// If we are sprayed with a extinguisher/shower with obscuring clothing on (think clothing that prevents surgery), the effect is multiplied against this. + var/sprayed_with_reagent_clothed_mult = 0.15 + + /// The wound we demote to when we go below cooling threshold. If null, removes us. + var/datum/wound/burn/robotic/demotes_to + /// The wound we promote to when we go above heating threshold. + var/datum/wound/burn/robotic/promotes_to + + /// The color of the light we will generate. + var/light_color + /// The power of the light we will generate. + var/light_power + /// The range of the light we will generate. + var/light_range + + /// The glow we have attached to our victim, to simulate our limb glowing. + var/obj/effect/dummy/lighting_obj/moblight/mob_glow + + /// A bad system I'm using to track the worst scar we earned (since we can demote, we want the biggest our wound has been, not what it was when it was cured (probably moderate)) + var/datum/scar/highest_scar + + /// A assoc list of (reagent typepath -> cooling), where cooling is how much its presence will reduce the effective temperature of a reagent spray for cooling us. + var/static/list/reagent_types_to_extra_cooling = list( + /datum/reagent/medicine/c2/hercuri = 60, + /datum/reagent/dinitrogen_plasmide = 50, + ) + + /// A assoc list of (reagent typepath -> damage mult), where the mult will be multiplied against the thermal shock damage. + var/static/list/reagent_types_to_thermal_shock_mult = list( + /datum/reagent/medicine/c2/hercuri = 0.3, + /datum/reagent/dinitrogen_plasmide = 0.6, + ) + + +/datum/wound/burn/robotic/overheat/New(temperature) + chassis_temperature = (isnull(temperature) ? get_random_starting_temperature() : temperature) + + return ..() + +/datum/wound/burn/robotic/overheat/wound_injury(datum/wound/old_wound, attack_direction) + . = ..() + + if (old_wound && old_wound.severity > severity && istype(old_wound, /datum/wound/burn/robotic/overheat)) + var/datum/wound/burn/robotic/overheat/overheat_wound = old_wound + if (overheat_wound.highest_scar) + set_highest_scar(overheat_wound.highest_scar) + overheat_wound.clear_highest_scar() + + if (!highest_scar && can_scar) + var/datum/scar/new_scar = new + set_highest_scar(new_scar) + new_scar.generate(limb, src, add_to_scars = FALSE) + +/datum/wound/burn/robotic/overheat/proc/set_highest_scar(datum/scar/new_scar) + if (highest_scar) + UnregisterSignal(highest_scar, COMSIG_QDELETING) + if (new_scar) + RegisterSignal(new_scar, COMSIG_QDELETING, PROC_REF(clear_highest_scar)) + highest_scar = new_scar + +/datum/wound/burn/robotic/overheat/proc/clear_highest_scar(datum/source) + SIGNAL_HANDLER + + set_highest_scar(null) + +/datum/wound/burn/robotic/overheat/remove_wound(ignore_limb, replaced) + if (!replaced && highest_scar) + already_scarred = TRUE + highest_scar.lazy_attach(limb) + return ..() + +/datum/wound/burn/robotic/overheat/Destroy() + QDEL_NULL(mob_glow) + + highest_scar = null + return ..() + +/datum/wound/burn/robotic/overheat/set_victim(mob/living/new_victim) + if (victim) + QDEL_NULL(mob_glow) + UnregisterSignal(victim, COMSIG_MOB_AFTER_APPLY_DAMAGE) + UnregisterSignal(victim, COMSIG_ATOM_AFTER_EXPOSE_REAGENTS) + if (new_victim) + mob_glow = new_victim.mob_light(light_range, light_power, light_color) + mob_glow.set_light_on(TRUE) + RegisterSignal(new_victim, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(victim_attacked)) + RegisterSignal(new_victim, COMSIG_ATOM_AFTER_EXPOSE_REAGENTS, PROC_REF(victim_exposed_to_reagents)) + + return ..() + +/datum/wound/burn/robotic/overheat/proc/get_random_starting_temperature() + return LERP(starting_temperature_min, starting_temperature_max, rand()) // LERP since we deal with decimals + +/datum/wound/burn/robotic/get_limb_examine_description() + return span_warning("The metal on this limb is glowing radiantly.") + +/datum/wound/burn/robotic/overheat/handle_process(seconds_per_tick, times_fired) + if (isnull(victim)) + var/turf/our_turf = get_turf(limb) + if (!isnull(our_turf)) + expose_temperature(our_turf.GetTemperature(), (turf_coeff * seconds_per_tick)) + return + if (outgoing_bodytemp_coeff <= 0) + return + var/statis_mult = 1 + if (HAS_TRAIT(victim, TRAIT_STASIS)) // stasis heavily reduces the ingoing and outgoing transfer of heat + statis_mult *= OVERHEAT_ON_STASIS_HEAT_MULT + + var/difference_from_average = max((BODYTEMP_NORMAL - victim.bodytemperature), 0) + var/difference_mult = 1 + (difference_from_average * bodytemp_difference_expose_bonus_ratio) + if (expose_temperature(victim.bodytemperature, (bodytemp_coeff * seconds_per_tick * statis_mult * difference_mult))) + return + var/mult = outgoing_bodytemp_coeff + if (limb_essential()) + mult *= important_outgoing_mult + var/adjustment_allowed = max((max_outgoing_temperature - victim.bodytemperature), 0) + var/amount_to_adjust = min((((chassis_temperature - victim.bodytemperature) * mult) * TEMPERATURE_DAMAGE_COEFFICIENT * seconds_per_tick * statis_mult), adjustment_allowed) + victim.adjust_bodytemperature(amount_to_adjust) + +/// Signal proc for when our victim is externally attacked. Increases chassis temp based on burn damage received. +/datum/wound/burn/robotic/overheat/proc/victim_attacked(datum/source, damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction, attacking_item) + SIGNAL_HANDLER + + if (def_zone != limb.body_zone) // use this proc since receive damage can also be called for like, chems and shit + return + + if (!victim) + return + + if (damagetype != BURN) + return + + if (wound_bonus == CANT_WOUND) + return + + var/effective_damage = (damage - blocked) + if (effective_damage <= 0) + return + + expose_temperature((chassis_temperature + effective_damage), incoming_damage_heat_coeff) + +/** + * Signal proc for when our victim is exposed to reagents, obviously. + * + * Equalizes temp to the reagent temp, but also causes thermal shock. Basically, does damage based on the temp differential. + * Clothes reduce the effects massively. Hercuri reduces the thermal shock and gets a special temp buff. + */ +/datum/wound/burn/robotic/overheat/proc/victim_exposed_to_reagents(datum/signal_source, list/reagents, datum/reagents/source, methods, volume_modifier, show_message) + SIGNAL_HANDLER + + var/reagent_coeff = base_reagent_temp_coefficient + if (!get_location_accessible(victim, limb.body_zone)) + if (ishuman(victim)) + // hi! it's niko! small rant + // this proc has no goddamn reason to be on human, it could so easily just have used a proc on carbon that would get the required bodyparts to check + // but no. it had to hardcode the list in the proc itself so it's impossible to modularly fix this + // so instead we just say fuck it and hope to god only human subtypes get this wound + // tldr; ryll why + var/mob/living/carbon/human/human_victim = victim + for (var/obj/item/clothing/iter_clothing as anything in human_victim.get_clothing_on_part(limb)) + if (iter_clothing.clothing_flags & THICKMATERIAL) + return + + reagent_coeff *= sprayed_with_reagent_clothed_mult + + if (istype(source.my_atom, /obj/effect/particle_effect/water/extinguisher)) // this used to be a lot, lot more modular, but sadly reagent temps/volumes and shit are horribly inconsistant + expose_temperature(source.chem_temp, (2.55 * reagent_coeff), TRUE) + return + + if (istype(source.my_atom, /obj/machinery/shower)) + expose_temperature(source.chem_temp, (15 * volume_modifier * reagent_coeff), TRUE) + return + + var/total_reagent_amount = 0 + var/chem_temp_increment = 0 + var/thermal_shock_mult = 1 + // imperfect, this means you can microdose hercuri/plasmide in a huge tank of water and have the entire effect. + // really not a big deal, though, they arent really limited by availability + for (var/datum/reagent/iterated_reagent as anything in reagents) + total_reagent_amount += reagents[iterated_reagent] + chem_temp_increment += reagent_types_to_extra_cooling[iterated_reagent.type] + thermal_shock_mult *= reagent_types_to_thermal_shock_mult[iterated_reagent.type] + + var/local_chem_temp = max(source.chem_temp - chem_temp_increment, 0) + + expose_temperature(local_chem_temp, (reagent_coeff * volume_modifier * total_reagent_amount), TRUE, heat_shock_damage_mult = thermal_shock_mult) + +/// Adjusts chassis_temperature by the delta between temperature and itself, multiplied by coeff. +/// If heat_shock is TRUE, limb will receive brute damage based on the delta. +/datum/wound/burn/robotic/overheat/proc/expose_temperature(temperature, coeff = 0.02, heat_shock = FALSE, heat_shock_damage_mult = 1) + var/temp_delta = (temperature - chassis_temperature) * coeff + + var/unclamped_new_temperature = (chassis_temperature + temp_delta) + var/clamped_new_temperature + var/heat_adjustment_used + + if (temp_delta > 0) + clamped_new_temperature = min(min(chassis_temperature + max(temp_delta, 1), temperature), heating_threshold) + heat_adjustment_used = (clamped_new_temperature / unclamped_new_temperature) + else + clamped_new_temperature = max(max(chassis_temperature + min(temp_delta, -1), temperature), cooling_threshold) + heat_adjustment_used = (unclamped_new_temperature / clamped_new_temperature) + + if (heat_shock && abs(temp_delta) > heat_shock_minimum_delta) + var/gauze_mult = 1 + var/obj/item/stack/gauze = limb.current_gauze + if (gauze) + gauze_mult *= (gauze.splint_factor) * 0.4 // very very effective + + if (limb.grasped_by) + gauze_mult *= 0.7 // hold it down yourself + + if (victim) + var/gauze_or_not = (!isnull(gauze) ? ", but [gauze] helps to keep it together" : "") + var/clothing_text = (!get_location_accessible(victim, limb.body_zone) ? ", [victim.p_their()] clothing absorbing some of the liquid" : "") + victim.visible_message(span_warning("[victim]'s [limb.plaintext_zone] strains from the thermal shock[clothing_text][gauze_or_not]!")) + playsound(victim, 'sound/items/tools/welder.ogg', 25) + + var/damage = (((abs(temp_delta) * heat_shock_delta_to_damage_ratio) * gauze_mult) * heat_shock_damage_mult) * heat_adjustment_used + limb.receive_damage(brute = damage, wound_bonus = CANT_WOUND) + + chassis_temperature = clamped_new_temperature // can only be decimal or 1, so it can only reduce the intensity of the adjustment + + return check_temperature() + +/// Removes, demotes, or promotes ourselves to a new wound type if our temperature is past a heating/cooling threshold. +/datum/wound/burn/robotic/overheat/proc/check_temperature() + if (chassis_temperature <= cooling_threshold) + if (demotes_to) + victim.visible_message(span_green("[victim]'s [limb.plaintext_zone] turns a more pleasant thermal color as it cools down a little..."), span_green("Your [limb.plaintext_zone] seems to cool down a little!")) + replace_wound(new demotes_to(cooling_threshold - cooling_demote_buffer)) + return TRUE + else + victim.visible_message(span_green("[victim]'s [limb.plaintext_zone] simmers gently as it returns to its usual colors!"), span_green("Your [limb.plaintext_zone] simmers gently as it returns to its usual colors!")) + remove_wound() + return TRUE + else if (promotes_to && chassis_temperature >= heating_threshold) + victim.visible_message(span_danger("[victim]'s [limb.plaintext_zone] brightens as it overheats further!"), span_userdanger("Your [limb.plaintext_zone] sizzles and brightens as it overheats further!")) + replace_wound(new promotes_to(heating_threshold + heating_promote_buffer)) + return TRUE + +/// Returns a string with our temperature and heating/cooling thresholds, for use in health analyzers. +/datum/wound/burn/robotic/overheat/proc/get_wound_status_info() + var/current_temp_celcius = round(chassis_temperature - T0C, 0.1) + var/current_temp_fahrenheit = round(chassis_temperature * 1.8-459.67, 0.1) + + var/cool_celcius = round(cooling_threshold - T0C, 0.1) + var/cool_fahrenheit = round(cooling_threshold * 1.8-459.67, 0.1) + + var/heat_celcius = round(heating_threshold - T0C, 0.1) + var/heat_fahrenheit = round(heating_threshold * 1.8-459.67, 0.1) + + return "Its current temperature is [span_blue("[current_temp_celcius ] °C ([current_temp_fahrenheit] °F)")], \ + and needs to cool to [span_nicegreen("[cool_celcius] °C ([cool_fahrenheit] °F)")], but \ + will worsen if heated to [span_purple("[heat_celcius] °C ([heat_fahrenheit] °F)")]." + +/datum/wound/burn/robotic/overheat/get_scanner_description(mob/user) + . = ..() + + . += "\nWound status: [get_wound_status_info()]" + +/datum/wound/burn/robotic/overheat/get_simple_scanner_description(mob/user) + . = ..() + + . += "\nWound status: [get_wound_status_info()]" + +// this wound is unaffected by cryoxadone and pyroxadone +/datum/wound/burn/robotic/overheat/on_xadone(power) + return + +/datum/wound/burn/robotic/overheat/moderate + name = "Transient Overheating" + desc = "External metals have exceeded lower-bound thermal limits and have lost some structural integrity, increasing damage taken as well as the chance to \ + sustain additional wounds." + occur_text = "lets out a slight groan as it turns a dull shade of thermal red" + examine_desc = "is glowing a dull thermal red and giving off heat" + treat_text = "Reduction of body temperature to expedite the passive heat dissipation - or, if thermal shock is to be risked, application of a fire extinguisher/shower." + severity = WOUND_SEVERITY_MODERATE + + damage_multiplier_penalty = 1.15 //1.15x damage taken + + starting_temperature_min = (BODYTEMP_NORMAL + 350) + starting_temperature_max = (BODYTEMP_NORMAL + 400) + + cooling_threshold = (BODYTEMP_NORMAL + 100) + heating_threshold = (BODYTEMP_NORMAL + 500) + + cooling_demote_buffer = 60 + heating_promote_buffer = 100 + + a_or_from = "from" + + // easy to get + threshold_penalty = 30 + + status_effect_type = /datum/status_effect/wound/burn/robotic/moderate + + sound_volume = 20 + + outgoing_bodytemp_coeff = 0.0056 + bodytemp_coeff = 0.006 + + base_reagent_temp_coefficient = 0.03 + heat_shock_delta_to_damage_ratio = 0.2 + + promotes_to = /datum/wound/burn/robotic/overheat/severe + + light_color = COLOR_RED + light_power = 0.1 + light_range = 0.5 + + can_scar = FALSE + +/datum/wound_pregen_data/burnt_metal/transient_overheat + abstract = FALSE + + wound_path_to_generate = /datum/wound/burn/robotic/overheat/moderate + + threshold_minimum = 30 + +/datum/wound/burn/robotic/overheat/severe + name = "Thermal Overload" + desc = "Exterior plating has surpassed critical thermal levels, causing significant failure in structural integrity and overheating of internal systems." + occur_text = "sizzles, the externals turning a dull shade of orange" + examine_desc = "appears discolored and polychromatic, parts of it glowing a dull orange" + treat_text = "Isolation from physical hazards, and accommodation of passive heat dissipation - active cooling may be used, but temperature differentials significantly \ + raise the risk of thermal shock." + severity = WOUND_SEVERITY_SEVERE + + a_or_from = "from" + + threshold_penalty = 65 + + status_effect_type = /datum/status_effect/wound/burn/robotic/severe + damage_multiplier_penalty = 1.25 // 1.25x damage taken + + starting_temperature_min = (BODYTEMP_NORMAL + 550) + starting_temperature_max = (BODYTEMP_NORMAL + 600) + + heating_promote_buffer = 150 + + cooling_threshold = (BODYTEMP_NORMAL + 375) + heating_threshold = (BODYTEMP_NORMAL + 800) + + outgoing_bodytemp_coeff = 0.0053 + bodytemp_coeff = 0.004 + + base_reagent_temp_coefficient = 0.03 + heat_shock_delta_to_damage_ratio = 0.2 + + demotes_to = /datum/wound/burn/robotic/overheat/moderate + promotes_to = /datum/wound/burn/robotic/overheat/critical + + light_color = COLOR_BRIGHT_ORANGE + light_power = 0.8 + light_range = 0.5 + + scar_keyword = "burnsevere" + +/datum/wound_pregen_data/burnt_metal/severe + abstract = FALSE + wound_path_to_generate = /datum/wound/burn/robotic/overheat/severe + threshold_minimum = 80 + +/datum/wound/burn/robotic/overheat/critical + name = "Runaway Exothermy" + desc = "Carapace is beyond melting point, causing catastrophic structural integrity failure as well as massively heating up the subject." + occur_text = "turns a bright shade of radiant white as it sizzles and melts" + examine_desc = "is a blinding shade of white, almost melting from the heat" + treat_text = "Immediate confinement to cryogenics, as rapid overheating and physical vulnerability may occur. Active cooling is not advised, \ + since the thermal shock may be lethal with such a temperature differential." + severity = WOUND_SEVERITY_CRITICAL + + a_or_from = "from" + + sound_effect = 'sound/effects/wounds/sizzle2.ogg' + + threshold_penalty = 100 + + status_effect_type = /datum/status_effect/wound/burn/robotic/critical + + damage_multiplier_penalty = 1.5 //1.5x damage taken + + starting_temperature_min = (BODYTEMP_NORMAL + 1050) + starting_temperature_max = (BODYTEMP_NORMAL + 1100) + + cooling_demote_buffer = 100 + + cooling_threshold = (BODYTEMP_NORMAL + 775) + heating_threshold = INFINITY + + outgoing_bodytemp_coeff = 0.0055 // burn... BURN... + bodytemp_coeff = 0.0025 + + base_reagent_temp_coefficient = 0.03 + heat_shock_delta_to_damage_ratio = 0.2 + + max_outgoing_temperature = BODYTEMP_HEAT_WOUND_LIMIT // critical CAN cause wounds, but only barely + + demotes_to = /datum/wound/burn/robotic/overheat/severe + + wound_flags = (MANGLES_EXTERIOR|ACCEPTS_GAUZE|SPLINT_OVERLAY|CAN_BE_GRASPED) + + light_color = COLOR_VERY_SOFT_YELLOW + light_power = 1.3 + light_range = 1.5 + + scar_keyword = "burncritical" + +/datum/wound_pregen_data/burnt_metal/critical + abstract = FALSE + wound_path_to_generate = /datum/wound/burn/robotic/overheat/critical + threshold_minimum = 140 + +#undef OVERHEAT_ON_STASIS_HEAT_MULT diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_muscle.dm b/modular_doppler/modular_medical/wounds/synth/robotic_muscle.dm new file mode 100644 index 0000000000000..4e91c58f7f675 --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/robotic_muscle.dm @@ -0,0 +1,47 @@ +/datum/wound/muscle/robotic + sound_effect = 'sound/effects/wounds/blood1.ogg' + +/datum/wound_pregen_data/muscle/robotic + required_limb_biostate = (BIO_METAL) + +/datum/wound/muscle/robotic/moderate + name = "Overworked Servo" + desc = "A servo has been overworked, and will operate with reduced efficiency until rested." + treat_text = "A tight splint on the affected limb, as well as plenty of rest and sleep." + examine_desc = "appears to be moving sluggishly" + occur_text = "jitters for a moment before moving sluggishly" + severity = WOUND_SEVERITY_MODERATE + interaction_efficiency_penalty = 1.5 + limp_slowdown = 2 + limp_chance = 30 + threshold_penalty = 15 + status_effect_type = /datum/status_effect/wound/muscle/robotic/moderate + regen_ticks_needed = 90 + +/datum/wound_pregen_data/muscle/robotic/servo + abstract = FALSE + wound_path_to_generate = /datum/wound/muscle/robotic/moderate + threshold_minimum = 35 + +/datum/wound/muscle/robotic/severe + name = "Exhausted Piston" + sound_effect = 'sound/effects/wounds/blood2.ogg' + desc = "An important hydraulic piston has been critically overused, resulting in total dysfunction until it recovers." + treat_text = "A tight splint on the affected limb, as well as plenty of rest and sleep." + examine_desc = "is stiffly limp, the extremities splayed out widely" + occur_text = "goes completely stiff, seeming to lock into position" + severity = WOUND_SEVERITY_SEVERE + interaction_efficiency_penalty = 2 + limp_slowdown = 5 + limp_chance = 40 + threshold_penalty = 35 + disabling = TRUE + status_effect_type = /datum/status_effect/wound/muscle/robotic/severe + regen_ticks_needed = 150 + +/datum/wound_pregen_data/muscle/robotic/hydraulic + abstract = FALSE + + wound_path_to_generate = /datum/wound/muscle/robotic/severe + threshold_minimum = 80 + diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_pierce.dm b/modular_doppler/modular_medical/wounds/synth/robotic_pierce.dm new file mode 100644 index 0000000000000..97ee48225eb9a --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/robotic_pierce.dm @@ -0,0 +1,152 @@ +// Pierce +// Slow to rise but high damage overall +// Hard-ish to fix +/datum/wound/electrical_damage/pierce + heat_differential_healing_mult = 0.01 + simple_desc = "Electrical conduits have been pierced open, resulting in a fault that slowly intensifies, but with extreme maximum voltage!" + +/datum/wound_pregen_data/electrical_damage/pierce + abstract = TRUE + wound_series = WOUND_SERIES_WIRE_PIERCE_ELECTRICAL_DAMAGE + required_wounding_types = list(WOUND_PIERCE) + +/datum/wound/burn/electrical_damage/pierce/get_limb_examine_description() + return span_warning("The metal on this limb is pierced open.") + +/datum/wound/electrical_damage/pierce/wound_injury(datum/wound/old_wound = null, attack_direction = null) + if(limb.can_bleed() && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY) + victim.spray_blood(attack_direction, severity) + + return ..() + +/datum/wound/electrical_damage/pierce/moderate + name = "Punctured Capacitor" + desc = "A major capacitor has been broken open, causing slow but noticable electrical damage." + occur_text = "shoots out a short stream of sparks" + examine_desc = "is shuddering gently, movements a little weak" + treat_text = "Replacing of damaged wiring, though repairs via wirecutting instruments or sutures may suffice, albeit at limited efficiency. In case of emergency, \ + subject may be subjected to high temperatures to allow solder to reset." + + sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg' + + severity = WOUND_SEVERITY_MODERATE + + sound_volume = 30 + + threshold_penalty = 30 + + intensity = 10 SECONDS + processing_full_shock_threshold = 7 MINUTES + + processing_shock_power_per_second_max = 1.2 + processing_shock_power_per_second_min = 1.1 + + processing_shock_stun_chance = 0.5 + processing_shock_spark_chance = 35 + + process_shock_spark_count_max = 1 + process_shock_spark_count_min = 1 + + wirecut_repair_percent = 0.078 + wire_repair_percent = 0.018 + + initial_sparks_amount = 1 + + status_effect_type = /datum/status_effect/wound/electrical_damage/pierce/moderate + + a_or_from = "a" + + scar_keyword = "piercemoderate" + +/datum/wound_pregen_data/electrical_damage/pierce/moderate + abstract = FALSE + wound_path_to_generate = /datum/wound/electrical_damage/pierce/moderate + threshold_minimum = 40 + +/datum/wound/electrical_damage/pierce/severe + name = "Penetrated Transformer" + desc = "A major transformer has been pierced, causing slow-to-progess but eventually intense electrical damage." + occur_text = "sputters and goes limp for a moment as it ejects a stream of sparks" + examine_desc = "is shuddering significantly, its servos briefly giving way in a rythmic pattern" + treat_text = "Containment of damaged wiring via gauze, then application of fresh wiring/sutures, or resetting of displaced wiring via wirecutter/retractor." + + sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg' + + severity = WOUND_SEVERITY_SEVERE + + sound_volume = 15 + + threshold_penalty = 40 + + intensity = 20 SECONDS + processing_full_shock_threshold = 6.5 MINUTES + + processing_shock_power_per_second_max = 1.6 + processing_shock_power_per_second_min = 1.5 + + processing_shock_stun_chance = 2.5 + processing_shock_spark_chance = 60 + + process_shock_spark_count_max = 2 + process_shock_spark_count_min = 1 + + wirecut_repair_percent = 0.046 + wire_repair_percent = 0.01 + + initial_sparks_amount = 3 + + status_effect_type = /datum/status_effect/wound/electrical_damage/pierce/moderate + + a_or_from = "a" + + scar_keyword = "piercemoderate" + +/datum/wound_pregen_data/electrical_damage/pierce/severe + abstract = FALSE + wound_path_to_generate = /datum/wound/electrical_damage/pierce/severe + threshold_minimum = 60 + +/datum/wound/electrical_damage/pierce/critical + name = "Ruptured PSU" + desc = "The local PSU of this limb has suffered a core rupture, causing a progressive power failure that will slowly intensify into massive electrical damage." + occur_text = "flashes with radiant blue, emitting a noise not unlike a Jacob's Ladder" + examine_desc = "'s PSU is visible, with a sizable hole in the center" + treat_text = "Immediate securing via gauze, followed by emergency cable replacement and securing via wirecutters or hemostat. \ + If the fault has become uncontrollable, extreme heat therapy is recommended." + + severity = WOUND_SEVERITY_CRITICAL + wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|CAN_BE_GRASPED|SPLINT_OVERLAY) + + sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg' + + sound_volume = 30 + + threshold_penalty = 60 + + intensity = 30 SECONDS + processing_full_shock_threshold = 5.5 MINUTES + + processing_shock_power_per_second_max = 2.2 + processing_shock_power_per_second_min = 2.1 + + processing_shock_stun_chance = 1 + processing_shock_spark_chance = 90 + + process_shock_spark_count_max = 3 + process_shock_spark_count_min = 2 + + wirecut_repair_percent = 0.032 + wire_repair_percent = 0.008 + + initial_sparks_amount = 8 + + status_effect_type = /datum/status_effect/wound/electrical_damage/pierce/moderate + + a_or_from = "a" + + scar_keyword = "piercecritical" + +/datum/wound_pregen_data/electrical_damage/pierce/critical + abstract = FALSE + wound_path_to_generate = /datum/wound/electrical_damage/pierce/critical + threshold_minimum = 110 diff --git a/modular_doppler/modular_medical/wounds/synth/robotic_slash.dm b/modular_doppler/modular_medical/wounds/synth/robotic_slash.dm new file mode 100644 index 0000000000000..82f4308229b4b --- /dev/null +++ b/modular_doppler/modular_medical/wounds/synth/robotic_slash.dm @@ -0,0 +1,654 @@ +/// How much damage and progress is reduced when on stasis. +#define ELECTRICAL_DAMAGE_ON_STASIS_MULT 0.15 +/// How much damage and progress is reduced when limb is grasped. +#define ELECTRICAL_DAMAGE_GRASPED_MULT 0.7 +/// How much damage and progress is reduced when our victim lies down. +#define ELECTRICAL_DAMAGE_LYING_DOWN_MULT 0.7 +/// How much progress is reduced when our victim is dead. +#define ELECTRICAL_DAMAGE_DEAD_PROGRESS_MULT 0.2 // they'll be resting to, so this is more like 0.1 + +/// Base time for a wirecutter being used. +#define ELECTRICAL_DAMAGE_WIRECUTTER_BASE_DELAY 8 SECONDS +/// Base time for a cable coil being used. +#define ELECTRICAL_DAMAGE_SUTURE_WIRE_BASE_DELAY 0.8 SECONDS +/// Global damage multiplier for the power a given electrical damage wound will add per tick. +#define ELECTRICAL_DAMAGE_POWER_PER_TICK_MULT 1 +/// Global damage multiplier for how much repairing wiring will reduce intensity. Higher is more. +#define ELECTRICAL_DAMAGE_SUTURE_WIRE_HEALING_AMOUNT_MULT 1 + +/// The minimum shock power we must have available to zap our victim. Must be at least one, since electrocute_act fails if it's lower. +#define ELECTRICAL_DAMAGE_MINIMUM_SHOCK_POWER_PER_ZAP 1 +/// The maximum burn damage our limb can have before we refuse to let people who havent aggrograbbed the limb repair it with wires. This is so people can opt to just fix the burn damage. +#define ELECTRICAL_DAMAGE_MAX_BURN_DAMAGE_TO_LET_WIRES_REPAIR 5 + +/// If progress is positive (not decreasing) after applying ELECTRICAL_DAMAGE_CLOTTING_HEALING_AMOUNT, we multiply it against this. +#define ELECTRICAL_DAMAGE_CLOTTING_PROGRESS_MULT 0.5 + +/datum/wound/electrical_damage + name = "Electrical (Wires) Wound" + + simple_treat_text = "Replacing of broken wiring, or repairing via a wirecutter. Bandaging binds the wiring and reduces intensity buildup, \ + as does firmly grasping the limb - both the victim and someone else can do this. Roboticists/Engineers get a bonus to treatment, as do diagnostic HUDs." + homemade_treat_text = "Sutures can repair the wiring at reduced efficiency, as can retractors. In a pinch, high temperatures can repair the wiring!" + + wound_flags = (ACCEPTS_GAUZE|CAN_BE_GRASPED|SPLINT_OVERLAY) + + treatable_tools = list(TOOL_WIRECUTTER, TOOL_RETRACTOR) + treatable_by = list(/obj/item/stack/medical/suture) + treatable_by_grabbed = list(/obj/item/stack/cable_coil) + + default_scar_file = METAL_SCAR_FILE + + processes = TRUE + + /// How many sparks do we spawn when we're gained? + var/initial_sparks_amount = 1 + + /// How much of our damage is reduced if the target is shock immune. Percent. + var/shock_immunity_self_damage_reduction = 75 + + /// Mult for our damage if we are unimportant. + var/limb_unimportant_damage_mult = 0.8 + /// Mult for our progress if we are unimportant. + var/limb_unimportant_progress_mult = 0.8 + + /// The overall "intensity" of this wound. Goes up to [processing_full_shock_threshold], and is used for determining our effect scaling. Measured in deciseconds. + var/intensity + /// The time, in deciseconds, it takes to reach 100% power. + var/processing_full_shock_threshold = 3 MINUTES + /// If [intensity] is at or below this, we remove ourselves. + var/minimum_intensity = 0 + + /// How much shock power we add to [processing_shock_power_this_tick] per tick. Lower bound + var/processing_shock_power_per_second_min = 0.1 + /// How much shock power we add to [processing_shock_power_this_tick] per tick. Upper bound + var/processing_shock_power_per_second_max = 0.2 + + /// In the case we get below 1 power, we add the power to this buffer and use it next tick. + var/processing_shock_power_this_tick = 0 + /// The chance for each processed shock to stun the user. + var/processing_shock_stun_chance = 0 + /// The chance for each processed shock to spark. + var/processing_shock_spark_chance = 30 + /// The chance for each processed shock to message the user. + var/process_shock_message_chance = 80 + + /// Simple mult for how much of real time is added to [intensity]. + var/seconds_per_intensity_mult = 1 + + /// How many sparks we spawn if a shock sparks. Lower bound + var/process_shock_spark_count_min = 1 + /// How many sparks we spawn if a shock sparks. Upper bound + var/process_shock_spark_count_max = 1 + + // Generally should be less fast than wire, but its effectiveness should increase with severity + /// The percent, in decimal, a successful wirecut use will reduce intensity by. + var/wirecut_repair_percent + // Generally should be lower than wirecut + /// The percent, in decimal, a successful wire use will reduce intensity by. + var/wire_repair_percent + + /// The basic multiplier to all our effects. Damage, progress, etc. + var/overall_effect_mult = 1 + + /// The bodyheat our victim must be at or above to start getting passive healing. + var/heat_thresh_to_heal = (BODYTEMP_HEAT_DAMAGE_LIMIT + 30) + /// The mult that heat differences between normal and bodytemp threshold is multiplied against. Controls passive heat healing. + var/heat_differential_healing_mult = 0.08 + + /// Percent chance for a heat repair to give the victim a message. + var/heat_heal_message_chance = 20 + + /// If [get_intensity_mult()] is at or above this, the limb gets disabled. If null, it will never occur. + var/disable_at_intensity_mult + +/datum/wound_pregen_data/electrical_damage + abstract = TRUE + required_limb_biostate = (BIO_WIRED) + required_wounding_types = list(WOUND_SLASH) + wound_series = WOUND_SERIES_WIRE_SLASH_ELECTRICAL_DAMAGE + +/datum/wound_pregen_data/electrical_damage/generate_scar_priorities() + return list("[BIO_METAL]") // wire scars dont exist so we can just use metal + +/datum/wound/burn/electrical_damage/slash/get_limb_examine_description() + return span_warning("The wiring on this limb is slashed open.") + +/datum/wound/electrical_damage/handle_process(seconds_per_tick, times_fired) + . = ..() + + var/base_mult = get_base_mult() + + var/seconds_per_tick_for_intensity = seconds_per_tick * get_progress_mult() + seconds_per_tick_for_intensity = modify_progress_after_progress_mult(seconds_per_tick_for_intensity, seconds_per_tick) + + adjust_intensity(seconds_per_tick_for_intensity SECONDS) + + if (!victim || victim.stat == DEAD) + return + + var/damage_mult = get_damage_mult(victim) + var/intensity_mult = get_intensity_mult() + + damage_mult *= seconds_per_tick + damage_mult *= intensity_mult + + var/picked_damage = LERP(processing_shock_power_per_second_min, processing_shock_power_per_second_max, rand()) + processing_shock_power_this_tick += (picked_damage * damage_mult) + if (processing_shock_power_this_tick <= ELECTRICAL_DAMAGE_MINIMUM_SHOCK_POWER_PER_ZAP) + return + + var/stun_chance = (processing_shock_stun_chance * intensity_mult) * base_mult + var/spark_chance = (processing_shock_spark_chance * intensity_mult) * base_mult + + var/should_stun = SPT_PROB(stun_chance, seconds_per_tick) + var/should_message = SPT_PROB(process_shock_message_chance, seconds_per_tick) + + zap(victim, + processing_shock_power_this_tick, + stun = should_stun, + spark = SPT_PROB(spark_chance, seconds_per_tick), + animation = should_stun, message = FALSE, + message = should_stun, + tell_victim_if_no_message = should_message, + ignore_immunity = TRUE, + jitter_time = seconds_per_tick, + stutter_time = 0, + delay_stun = TRUE, + knockdown = TRUE, + ignore_gloves = TRUE + ) + processing_shock_power_this_tick = 0 + +/// If someone is aggrograbbing us and targetting our limb, intensity progress is multiplied against this. +#define LIMB_AGGROGRABBED_PROGRESS_MULT 0.5 + +/// Returns the multiplier used by our intensity progress. Intensity increment is multiplied against this. +/datum/wound/electrical_damage/proc/get_progress_mult() + var/progress_mult = get_base_mult() * seconds_per_intensity_mult + + if (!limb_essential()) + progress_mult *= limb_unimportant_progress_mult + + if (isliving(victim.pulledby)) + var/mob/living/living_puller = victim.pulledby + if (living_puller.grab_state >= GRAB_AGGRESSIVE && living_puller.zone_selected == limb.body_zone) + progress_mult *= LIMB_AGGROGRABBED_PROGRESS_MULT // they're holding it down + + if (victim.stat == DEAD) + progress_mult *= ELECTRICAL_DAMAGE_DEAD_PROGRESS_MULT // doesnt totally stop it but slows it down a lot + + return progress_mult +#undef LIMB_AGGROGRABBED_PROGRESS_MULT + +/// Returns the multiplier used by the damage we deal. +/datum/wound/electrical_damage/proc/get_damage_mult(mob/living/target) + SHOULD_BE_PURE(TRUE) + + var/damage_mult = get_base_mult() + + if (!limb_essential()) + damage_mult *= limb_unimportant_damage_mult + + return damage_mult * ELECTRICAL_DAMAGE_POWER_PER_TICK_MULT + +/// Returns the global multiplier used by both progress and damage. +/datum/wound/electrical_damage/proc/get_base_mult() + var/base_mult = 1 + + if (victim) + if (HAS_TRAIT(victim, TRAIT_STASIS)) + base_mult *= ELECTRICAL_DAMAGE_ON_STASIS_MULT + if (victim.body_position == LYING_DOWN) + base_mult *= ELECTRICAL_DAMAGE_LYING_DOWN_MULT + if (limb.grasped_by) + base_mult *= ELECTRICAL_DAMAGE_GRASPED_MULT + + if (victim.has_status_effect(/datum/status_effect/determined)) + base_mult *= WOUND_DETERMINATION_BLEED_MOD + + if (HAS_TRAIT(victim, TRAIT_SHOCKIMMUNE)) // it'd be a bit cheesy to just become immune to this, so it only makes it a lot lot better + base_mult *= shock_immunity_self_damage_reduction + + var/splint_mult = (limb.current_gauze ? limb.current_gauze.splint_factor : 1) + base_mult *= splint_mult + + return overall_effect_mult * base_mult + +/// Is called after seconds_for_intensity is modified by get_progress_mult(). +/datum/wound/electrical_damage/proc/modify_progress_after_progress_mult(seconds_for_intensity, seconds_per_tick) + if (!victim) + return seconds_for_intensity + + seconds_for_intensity -= (get_heat_healing() * seconds_per_tick) + + if (seconds_for_intensity > 0 && HAS_TRAIT(victim, TRAIT_COAGULATING)) + seconds_for_intensity *= ELECTRICAL_DAMAGE_CLOTTING_PROGRESS_MULT + + if (HAS_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING)) + seconds_for_intensity = min(seconds_for_intensity, 0) // it cant get any worse + + return seconds_for_intensity + +/// Returns how many deciseconds progress should be reduced by, based on the current heat of our victim's body. +/datum/wound/electrical_damage/proc/get_heat_healing(do_message = prob(heat_heal_message_chance)) + var/healing_amount = max((victim.bodytemperature - heat_thresh_to_heal), 0) * heat_differential_healing_mult + if (do_message && healing_amount) + to_chat(victim, span_notice("You feel the solder within your [limb.plaintext_zone] reform and repair your [name]...")) + + return healing_amount + +/// Changes intensity by the given amount, and then updates our status, removing ourselves if fixed. +/datum/wound/electrical_damage/proc/adjust_intensity(to_adjust) + intensity = clamp((intensity + to_adjust), 0, processing_full_shock_threshold) + + if (disable_at_intensity_mult) + set_disabling(get_intensity_mult() >= disable_at_intensity_mult) + + remove_if_fixed() + +/datum/wound/electrical_damage/wound_injury(datum/wound/electrical_damage/old_wound, attack_direction) + . = ..() + + if (old_wound) + intensity = max(intensity, old_wound.intensity) + processing_shock_power_this_tick = old_wound.processing_shock_power_this_tick + + do_sparks(initial_sparks_amount, FALSE, victim) + if (limb.can_bleed() && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY) + victim.spray_blood(attack_direction, severity) + +/datum/wound/electrical_damage/modify_desc_before_span(desc, mob/user) + . = ..() + + if (limb.current_gauze) + return + + var/intensity_mult = get_intensity_mult() + if (intensity_mult < 0.2 || (victim.stat == DEAD)) + return + + . += ", and " + + var/extra + switch (intensity_mult) + if (0.2 to 0.4) + extra += "[span_deadsay("is letting out some sparks")]" + if (0.4 to 0.6) + extra += "[span_deadsay("is sparking quite a bit")]" + if (0.6 to 0.8) + extra += "[span_deadsay("is practically hemorrhaging sparks")]" + if (0.8 to 1) + extra += "[span_deadsay("has golden bolts of electricity constantly striking the surface")]" + + . += extra + +/datum/wound/electrical_damage/get_scanner_description(mob/user) + . = ..() + + . += "\nWound status: [get_wound_status_info()]" + +/datum/wound/electrical_damage/get_simple_scanner_description(mob/user) + . = ..() + + . += "\nWound status: [get_wound_status_info()]" + +/// Returns a string with our fault intensity and threshold to removal for use in health analyzers. +/datum/wound/electrical_damage/proc/get_wound_status_info() + return "Fault intensity is currently at [span_bold("[get_intensity_mult() * 100]")]%. It must be reduced to [span_blue("[minimum_intensity]")]% to remove the wound." + +// this wound is unaffected by cryoxadone and pyroxadone +/datum/wound/electrical_damage/on_xadone(power) + return + +/datum/wound/electrical_damage/item_can_treat(obj/item/potential_treater, mob/user) + if (istype(potential_treater, /obj/item/stack/cable_coil) && ((user.pulling == victim && user.grab_state >= GRAB_AGGRESSIVE) || (limb.burn_dam <= ELECTRICAL_DAMAGE_MAX_BURN_DAMAGE_TO_LET_WIRES_REPAIR))) + return TRUE // if we're aggrograbbed, or relatively undamaged, go ahead. else, we dont want to impede normal treatment + + return ..() + +/datum/wound/electrical_damage/treat(obj/item/treating_item, mob/user) + if (treating_item.tool_behaviour == TOOL_WIRECUTTER || treating_item.tool_behaviour == TOOL_RETRACTOR) + return wirecut(treating_item, user) + + if (istype(treating_item, /obj/item/stack/medical/suture) || istype(treating_item, /obj/item/stack/cable_coil)) + return suture_wires(treating_item, user) + + return ..() + +/** + * The "trauma" treatment, done with cables/sutures. Sutures get a debuff. + * Low self-tend penalty. + * Very fast, but low value. Eats up wires for breakfast. + * Has limited wire/HUD bonuses. If you're a robo, use a wirecutter instead. + */ +/datum/wound/electrical_damage/proc/suture_wires(obj/item/stack/suturing_item, mob/living/carbon/human/user) + if (!suturing_item.tool_start_check()) + return TRUE + + var/is_suture = (istype(suturing_item, /obj/item/stack/medical/suture)) + + var/change = (processing_full_shock_threshold * wire_repair_percent) * ELECTRICAL_DAMAGE_SUTURE_WIRE_HEALING_AMOUNT_MULT + var/delay_mult = 1 + if (user == victim) + delay_mult *= 1.4 + if (is_suture) + delay_mult *= 1.5 + var/obj/item/stack/medical/suture/suture_item = suturing_item + var/obj/item/stack/medical/suture/base_suture = /obj/item/stack/medical/suture + change = max(change - (suture_item.heal_brute - initial(base_suture.heal_brute)), 0.00001) + + // as this is the trauma treatment, there are less bonuses + // if youre doing this, youre probably doing this on-the-spot + if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES)) + delay_mult *= 0.8 + else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES)) + delay_mult *= 0.9 + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + delay_mult *= 0.8 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + change *= 1.5 + + ADD_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user)) + do_suture_repairs(suturing_item, user, change, delay_mult) + REMOVE_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user)) + return TRUE + +/// Does a while loop that repairs us with cables. A proc for containing runtimes and allowing trait removal at all times. +/datum/wound/electrical_damage/proc/do_suture_repairs(obj/item/stack/suturing_item, mob/living/carbon/human/user, change, delay_mult) + var/is_suture = (istype(suturing_item, /obj/item/stack/medical/suture)) + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + var/replacing_or_suturing = (is_suture ? "repairing some" : "replacing") + while (suturing_item.tool_start_check()) + user?.visible_message(span_danger("[user] begins [replacing_or_suturing] wiring within [their_or_other] [limb.plaintext_zone] with [suturing_item]..."), \ + span_notice("You begin [replacing_or_suturing] wiring within [your_or_other] [limb.plaintext_zone] with [suturing_item]...")) + if (!suturing_item.use_tool(target = victim, user = user, delay = ELECTRICAL_DAMAGE_SUTURE_WIRE_BASE_DELAY * delay_mult, amount = 1, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return + + if (user != victim && user.combat_mode) + user?.visible_message(span_danger("[user] mangles some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \ + span_danger("You mangle some of [your_or_other] [limb.plaintext_zone]'s wiring!"), ignored_mobs = victim) + to_chat(victim, span_userdanger("[capitalize(your_or_other)] mangles some of your [limb.plaintext_zone]'s wiring!")) + adjust_intensity(change * 2) + else + var/repairs_or_replaces = (is_suture ? "repairs" : "replaces") + var/repair_or_replace = (is_suture ? "repair" : "replace") + user?.visible_message(span_notice("[user] [repairs_or_replaces] some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \ + span_notice("You [repair_or_replace] some of [your_or_other] [limb.plaintext_zone]'s wiring!")) + adjust_intensity(-change) + victim?.balloon_alert(user, "intensity reduced to [get_intensity_mult() * 100]%") + + if (fixed()) + return + +/** + * The "proper" treatment, done with wirecutters/retractors. Retractors get a debuff. + * High self-tend penalty. + * Slow, but high value. + * Has high wire/HUD bonuses. The ideal treatment for a robo. + */ +/datum/wound/electrical_damage/proc/wirecut(obj/item/wirecutting_tool, mob/living/carbon/human/user) + if (!wirecutting_tool.tool_start_check()) + return TRUE + + var/is_retractor = (wirecutting_tool.tool_behaviour == TOOL_RETRACTOR) + + var/change = (processing_full_shock_threshold * wirecut_repair_percent) + var/delay_mult = 1 + if (user == victim) + delay_mult *= 2 + if (is_retractor) + delay_mult *= 2 + var/knows_wires = FALSE + if (HAS_TRAIT(user, TRAIT_KNOW_ROBO_WIRES)) + delay_mult *= 0.3 + knows_wires = TRUE + else if (HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES)) + delay_mult *= 0.6 + knows_wires = TRUE + if (HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) + if (knows_wires) + delay_mult *= 0.9 + else + delay_mult *= 0.75 + if (HAS_TRAIT(src, TRAIT_WOUND_SCANNED)) + change *= 1.5 + + ADD_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user)) + do_wirecutter_repairs(wirecutting_tool, user, change, delay_mult) + REMOVE_TRAIT(src, TRAIT_ELECTRICAL_DAMAGE_REPAIRING, REF(user)) + return TRUE + +/// Does a while loop that repairs us with a wirecutter. A proc for containing runtimes and allowing trait removal at all times. +/datum/wound/electrical_damage/proc/do_wirecutter_repairs(obj/item/wirecutting_tool, mob/living/carbon/human/user, change, delay_mult) + var/their_or_other = (user == victim ? "[user.p_their()]" : "[victim]'s") + var/your_or_other = (user == victim ? "your" : "[victim]'s") + while (wirecutting_tool.tool_start_check()) + user?.visible_message(span_danger("[user] begins resetting misplaced wiring within [their_or_other] [limb.plaintext_zone]..."), \ + span_notice("You begin resetting misplaced wiring within [your_or_other] [limb.plaintext_zone]...")) + if (!wirecutting_tool.use_tool(target = victim, user = user, delay = ELECTRICAL_DAMAGE_WIRECUTTER_BASE_DELAY * delay_mult, volume = 50, extra_checks = CALLBACK(src, PROC_REF(still_exists)))) + return + + if (user != victim && user.combat_mode) + user?.visible_message(span_danger("[user] mangles some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \ + span_danger("You mangle some of [your_or_other] [limb.plaintext_zone]'s wiring!"), ignored_mobs = victim) + to_chat(victim, span_userdanger("[capitalize(your_or_other)] mangles some of your [limb.plaintext_zone]'s wiring!")) + adjust_intensity(change * 2) + else + user?.visible_message(span_notice("[user] resets some of [their_or_other] [limb.plaintext_zone]'s wiring!"), \ + span_notice("You reset some of [your_or_other] [limb.plaintext_zone]'s wiring!")) + adjust_intensity(-change) + victim?.balloon_alert(user, "intensity reduced to [get_intensity_mult() * 100]%") + + if (fixed()) + return + +/// If fixed() is true, we remove ourselves and return TRUE. FALSE otherwise. +/datum/wound/electrical_damage/proc/remove_if_fixed() + if (fixed()) + to_chat(victim, span_green("Your [limb.plaintext_zone] has recovered from its [name]!")) + remove_wound() + return TRUE + return FALSE + +/// Should we remove ourselves? +/datum/wound/electrical_damage/proc/fixed() + return (intensity <= minimum_intensity || isnull(limb)) + +/// Returns the multiplier we apply to our outgoing damage based off our current intensity. Is always between 0-1. +/datum/wound/electrical_damage/proc/get_intensity_mult() + return (min((intensity / processing_full_shock_threshold), 1)) + +/// Wrapper for electrocute_act +/datum/wound/electrical_damage/proc/zap( + mob/living/target, + damage, + coeff = 1, + stun, + spark = TRUE, + animation = TRUE, + message = TRUE, + ignore_immunity = FALSE, + delay_stun = FALSE, + knockdown = FALSE, + ignore_gloves = FALSE, + tell_victim_if_no_message = TRUE, + jitter_time = 20 SECONDS, + stutter_time = 4 SECONDS, +) + + var/flags = NONE + if (!stun) + flags |= SHOCK_NOSTUN + if (!animation) + flags |= SHOCK_NO_HUMAN_ANIM + if (!message) + flags |= SHOCK_SUPPRESS_MESSAGE + if (tell_victim_if_no_message && target == victim) + to_chat(target, span_warning("Your [limb.plaintext_zone] short-circuits and zaps you!")) + if (ignore_immunity) + flags |= SHOCK_IGNORE_IMMUNITY + if (delay_stun) + flags |= SHOCK_DELAY_STUN + if (knockdown) + flags |= SHOCK_KNOCKDOWN + if (ignore_gloves) + flags |= SHOCK_NOGLOVES + + target.electrocute_act(damage, limb, coeff, flags, jitter_time, stutter_time) + if (spark) + do_sparks(rand(process_shock_spark_count_min, process_shock_spark_count_max), FALSE, victim) + +// Slash +// Fast to rise, but lower damage overall +// Also a bit easy to treat +/datum/wound/electrical_damage/slash + simple_desc = "Wiring has been slashed open, resulting in a fault that quickly intensifies!" + +/datum/wound/electrical_damage/slash/moderate + name = "Frayed Wiring" + desc = "Internal wiring has suffered a slight abrasion, causing a slow electrical fault that will intensify over time." + occur_text = "lets out a few sparks, as a few frayed wires stick out" + examine_desc = "has a few frayed wires sticking out" + treat_text = "Replacing of damaged wiring, though repairs via wirecutting instruments or sutures may suffice, albeit at limited efficiency. In case of emergency, \ + subject may be subjected to high temperatures to allow solder to reset." + + sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg' + + severity = WOUND_SEVERITY_MODERATE + + sound_volume = 30 + + threshold_penalty = 20 + + intensity = 10 SECONDS + processing_full_shock_threshold = 3 MINUTES + + processing_shock_power_per_second_max = 0.5 + processing_shock_power_per_second_min = 0.4 + + processing_shock_stun_chance = 0 + processing_shock_spark_chance = 30 + + process_shock_spark_count_max = 1 + process_shock_spark_count_min = 1 + + wirecut_repair_percent = 0.1 + wire_repair_percent = 0.023 + + initial_sparks_amount = 1 + + status_effect_type = /datum/status_effect/wound/electrical_damage/slash/moderate + + a_or_from = "from" + + scar_keyword = "slashmoderate" + +/datum/wound_pregen_data/electrical_damage/slash/moderate + abstract = FALSE + wound_path_to_generate = /datum/wound/electrical_damage/slash/moderate + threshold_minimum = 35 + +/datum/wound/electrical_damage/slash/severe + name = "Severed Conduits" + desc = "A number of wires have been completely cut, resulting in electrical faults that will intensify at a worrying rate." + occur_text = "sends some electrical fiber in the direction of the blow, beginning to profusely spark" + examine_desc = "has multiple severed wires visible to the outside" + treat_text = "Containment of damaged wiring via gauze, then application of fresh wiring/sutures, or resetting of displaced wiring via wirecutter/retractor." + + sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg' + + severity = WOUND_SEVERITY_SEVERE + + sound_volume = 15 + + threshold_penalty = 30 + + intensity = 10 SECONDS + processing_full_shock_threshold = 2 MINUTES + + processing_shock_power_per_second_max = 0.7 + processing_shock_power_per_second_min = 0.6 + + processing_shock_stun_chance = 0 + processing_shock_spark_chance = 60 + + process_shock_spark_count_max = 2 + process_shock_spark_count_min = 1 + + wirecut_repair_percent = 0.09 + wire_repair_percent = 0.015 + + initial_sparks_amount = 3 + + status_effect_type = /datum/status_effect/wound/electrical_damage/slash/severe + + a_or_from = "from" + + scar_keyword = "slashsevere" + +/datum/wound_pregen_data/electrical_damage/slash/severe + abstract = FALSE + wound_path_to_generate = /datum/wound/electrical_damage/slash/severe + threshold_minimum = 60 + +/datum/wound/electrical_damage/slash/critical + name = "Systemic Fault" + desc = "A significant portion of the power distribution network has been cut open, resulting in massive power loss and runaway electrocution." + occur_text = "lets out a violent \"zhwarp\" sound as angry electric arcs attack the surrounding air" + examine_desc = "has lots of mauled wires sticking out" + treat_text = "Immediate securing via gauze, followed by emergency cable replacement and securing via wirecutters or retractor. \ + If the fault has become uncontrollable, extreme heat therapy is recommended." + + severity = WOUND_SEVERITY_CRITICAL + wound_flags = (ACCEPTS_GAUZE|MANGLES_EXTERIOR|CAN_BE_GRASPED|SPLINT_OVERLAY) + + sound_effect = 'modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg' + + sound_volume = 30 + + threshold_penalty = 50 + + intensity = 10 SECONDS + processing_full_shock_threshold = 1.25 MINUTES + + processing_shock_power_per_second_max = 1.3 + processing_shock_power_per_second_min = 1.1 + + processing_shock_stun_chance = 5 + processing_shock_spark_chance = 90 + + process_shock_spark_count_max = 3 + process_shock_spark_count_min = 2 + + wirecut_repair_percent = 0.08 + wire_repair_percent = 0.01 + + initial_sparks_amount = 8 + + status_effect_type = /datum/status_effect/wound/electrical_damage/slash/critical + + a_or_from = "a" + + scar_keyword = "slashcritical" + +/datum/wound_pregen_data/electrical_damage/slash/critical + abstract = FALSE + wound_path_to_generate = /datum/wound/electrical_damage/slash/critical + threshold_minimum = 100 + +#undef ELECTRICAL_DAMAGE_ON_STASIS_MULT +#undef ELECTRICAL_DAMAGE_GRASPED_MULT +#undef ELECTRICAL_DAMAGE_LYING_DOWN_MULT +#undef ELECTRICAL_DAMAGE_DEAD_PROGRESS_MULT + +#undef ELECTRICAL_DAMAGE_WIRECUTTER_BASE_DELAY +#undef ELECTRICAL_DAMAGE_SUTURE_WIRE_BASE_DELAY + +#undef ELECTRICAL_DAMAGE_MINIMUM_SHOCK_POWER_PER_ZAP +#undef ELECTRICAL_DAMAGE_MAX_BURN_DAMAGE_TO_LET_WIRES_REPAIR +#undef ELECTRICAL_DAMAGE_POWER_PER_TICK_MULT +#undef ELECTRICAL_DAMAGE_SUTURE_WIRE_HEALING_AMOUNT_MULT + +#undef ELECTRICAL_DAMAGE_CLOTTING_PROGRESS_MULT diff --git a/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg new file mode 100644 index 0000000000000..cb7b3d0a79f49 Binary files /dev/null and b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T1.ogg differ diff --git a/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg new file mode 100644 index 0000000000000..4fad258357538 Binary files /dev/null and b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T2.ogg differ diff --git a/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg new file mode 100644 index 0000000000000..f0ea8e2217027 Binary files /dev/null and b/modular_doppler/modular_medical/wounds/synth/sound/robotic_slash_T3.ogg differ diff --git a/modular_doppler/modular_medical/wounds/wound_effects.dm b/modular_doppler/modular_medical/wounds/wound_effects.dm index f5b5c8cb9a138..8f9aef16210d3 100644 --- a/modular_doppler/modular_medical/wounds/wound_effects.dm +++ b/modular_doppler/modular_medical/wounds/wound_effects.dm @@ -1,3 +1,12 @@ +/datum/status_effect/wound/blunt/robotic/moderate + id = "unsecure_moderate" + +/datum/status_effect/wound/blunt/robotic/severe + id = "unsecure_severe" + +/datum/status_effect/wound/blunt/robotic/critical + id = "unsecure_critical" + /datum/status_effect/wound/electrical_damage/slash/moderate id = "electric_slash_moderate" diff --git a/modular_doppler/modular_quirks/code/good_quirks.dm b/modular_doppler/modular_quirks/code/good_quirks.dm deleted file mode 100644 index 2b0b26fefd478..0000000000000 --- a/modular_doppler/modular_quirks/code/good_quirks.dm +++ /dev/null @@ -1,9 +0,0 @@ -/datum/quirk/linguist - name = "Linguist" - desc = "You're a student of numerous languages and come with an additional language point." - value = 4 - mob_trait = TRAIT_LINGUIST - gain_text = span_notice("Your brain seems more equipped to handle different modes of conversation.") - lose_text = span_danger("Your grasp of the finer points of Draconic idioms fades away.") - medical_record_text = "Patient demonstrates a high brain plasticity in regards to language learning." - icon = FA_ICON_BOOK_ATLAS diff --git a/modular_doppler/modular_quirks/code/excitable/quirk.dm b/modular_doppler/modular_quirks/excitable/excitable.dm similarity index 100% rename from modular_doppler/modular_quirks/code/excitable/quirk.dm rename to modular_doppler/modular_quirks/excitable/excitable.dm diff --git a/modular_doppler/modular_quirks/left_handed/left_handed.dm b/modular_doppler/modular_quirks/left_handed/left_handed.dm new file mode 100644 index 0000000000000..3f25672e25a73 --- /dev/null +++ b/modular_doppler/modular_quirks/left_handed/left_handed.dm @@ -0,0 +1,7 @@ +/datum/quirk/left_handed + name = "Left Handed" + desc = "You just so happen to be a left-handed, neat!" + icon = FA_ICON_HAND_SPARKLES + value = 0 + mob_trait = TRAIT_LEFT_HANDED + medical_record_text = "Patient is documented to have a left dominant hand." diff --git a/modular_doppler/modular_quirks/linguist/linguist.dm b/modular_doppler/modular_quirks/linguist/linguist.dm new file mode 100644 index 0000000000000..1a92e80509461 --- /dev/null +++ b/modular_doppler/modular_quirks/linguist/linguist.dm @@ -0,0 +1,141 @@ +/datum/quirk/linguist + name = "Linguist" + desc = "You're a student of numerous languages and come with an additional language point." + value = 4 + mob_trait = TRAIT_LINGUIST + gain_text = span_notice("Your brain seems more equipped to handle different modes of conversation.") + lose_text = span_danger("Your grasp of the finer points of Draconic idioms fades away.") + medical_record_text = "Patient demonstrates a high brain plasticity in regards to language learning." + icon = FA_ICON_BOOK_ATLAS + +/datum/quirk/night_vision + name = "Night Vision" + desc = "You can see slightly more clearly in full darkness than most people." + icon = FA_ICON_MOON + value = 4 + mob_trait = TRAIT_NIGHT_VISION + gain_text = span_notice("The shadows seem a little less dark.") + lose_text = span_danger("Everything seems a little darker.") + medical_record_text = "Patient's eyes show above-average acclimation to darkness." + mail_goodies = list( + /obj/item/flashlight/flashdark, + /obj/item/food/grown/mushroom/glowshroom/shadowshroom, + /obj/item/skillchip/light_remover, + ) + +/datum/quirk/night_vision/add(client/client_source) + refresh_quirk_holder_eyes() + +/datum/quirk/night_vision/remove() + refresh_quirk_holder_eyes() + +/datum/quirk/night_vision/proc/refresh_quirk_holder_eyes() + var/mob/living/carbon/human/human_quirk_holder = quirk_holder + var/obj/item/organ/internal/eyes/eyes = human_quirk_holder.get_organ_by_type(/obj/item/organ/internal/eyes) + if(!eyes) + return + // We've either added or removed TRAIT_NIGHT_VISION before calling this proc. Just refresh the eyes. + eyes.refresh() + +// This NV quirk variant applies color_offsets night vision to a mob based on its chosen eye color. +// Some eye colors will produce very slightly stronger mechanical night vision effects just by virtue of their RGB values being scaled higher (typically lighter colours). + +/datum/quirk/night_vision + desc = "You can see a little better in darkness than most ordinary humanoids. If your eyes are naturally more sensitive to light through other means (such as being photophobic or a mothperson), this effect is significantly stronger." + medical_record_text = "Patient's visual sensory organs demonstrate non-standard performance in low-light conditions." + var/nv_color = null /// Holds the player's selected night vision colour + var/list/nv_color_cutoffs = null /// Contains the color_cutoffs applied to the user's eyes w/ our custom hue (once built) + +/datum/quirk/night_vision/add_unique(client/client_source) + . = ..() + nv_color = client_source?.prefs.read_preference(/datum/preference/color/nv_color) + if (isnull(nv_color)) + var/mob/living/carbon/human/human_holder = quirk_holder + nv_color = process_chat_color(human_holder.eye_color_left) + nv_color_cutoffs = calculate_color_cutoffs(nv_color) + refresh_quirk_holder_eyes() // make double triple dog sure we apply the overlay + +/// Calculate eye organ color_cutoffs used in tinted night vision with a supplied hexcode colour, clamping and scaling appropriately. +/datum/quirk/night_vision/proc/calculate_color_cutoffs(color) + var/mob/living/carbon/human/target = quirk_holder + + // if we have more sensitive eyes, increase the power + var/obj/item/organ/internal/eyes/target_eyes = target.get_organ_slot(ORGAN_SLOT_EYES) + if (!istype(target_eyes)) + return + var/infravision_multiplier = max(0, (-(target_eyes.flash_protect) * DOPPLER_NIGHT_VISION_SENSITIVITY_MULT)) + 1 + + var/list/new_rgb_cutoffs = new /list(3) + for(var/i in 1 to 3) + var/base_color = hex2num(copytext(color, (i*2), (i*2)+2)) //convert their supplied hex colour value to RGB + var/adjusted_color = max(((base_color / 255) * (DOPPLER_NIGHT_VISION_POWER_MAX * infravision_multiplier)), (DOPPLER_NIGHT_VISION_POWER_MIN * infravision_multiplier)) //linear convert their eye color into a color_cutoff range, ensuring it is clamped + new_rgb_cutoffs[i] = adjusted_color + + return new_rgb_cutoffs + +/datum/quirk_constant_data/night_vision + associated_typepath = /datum/quirk/night_vision + customization_options = list(/datum/preference/color/nv_color) + +// Client preference for night vision colour +/datum/preference/color/nv_color + savefile_key = "nv_color" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + +/datum/preference/color/nv_color/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return "Night Vision" in preferences.all_quirks + +/datum/preference/color/nv_color/apply_to_human(mob/living/carbon/human/target, value) + return + +// run the Blessed Runechat Proc since it does most of what we want regarding luminance clamping anyway. could it be better? probably. is it more work? yes, it's a LOT of work. +/datum/preference/color/nv_color/deserialize(input, datum/preferences/preferences) + return process_chat_color(sanitize_hexcolor(input)) + +/datum/preference/color/nv_color/serialize(input) + return process_chat_color(sanitize_hexcolor(input)) + +/datum/preference/color/nv_color/create_default_value() + return process_chat_color("#[random_color()]") + +/datum/quirk/photophobia + desc = "Bright lights are uncomfortable and upsetting to you for whatever reason. Your eyes are also more sensitive to light in general. This shares a unique interaction with Night Vision." + /// how much of a flash_protect deficit the quirk inflicts + var/severity = 1 + +/datum/quirk/photophobia/add_unique(client/client_source) + var/sensitivity = client_source?.prefs.read_preference(/datum/preference/choiced/photophobia_severity) + switch (sensitivity) + if ("Hypersensitive") + severity = 2 + if ("Sensitive") + severity = 1 + var/obj/item/organ/internal/eyes/holder_eyes = quirk_holder.get_organ_slot(ORGAN_SLOT_EYES) + restore_eyes(holder_eyes) // add_unique() happens after add() so we need to jank reset this to ensure sensitivity is properly applied at roundstart + check_eyes(holder_eyes) + +/datum/quirk_constant_data/photophobia + associated_typepath = /datum/quirk/photophobia + customization_options = list(/datum/preference/choiced/photophobia_severity) + +/datum/preference/choiced/photophobia_severity + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "photophobia_severity" + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/choiced/photophobia_severity/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return "Photophobia" in preferences.all_quirks + +/datum/preference/choiced/photophobia_severity/init_possible_values() + var/list/values = list("Sensitive", "Hypersensitive") + return values + +/datum/preference/choiced/photophobia_severity/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/modular_doppler/modular_quirks/code/overwrites/musician.dm b/modular_doppler/modular_quirks/overwrites/musician.dm similarity index 100% rename from modular_doppler/modular_quirks/code/overwrites/musician.dm rename to modular_doppler/modular_quirks/overwrites/musician.dm diff --git a/modular_doppler/modular_quirks/permitted_cybernetic/code/preferences.dm b/modular_doppler/modular_quirks/permitted_cybernetic/code/preferences.dm new file mode 100644 index 0000000000000..056b0caf0939c --- /dev/null +++ b/modular_doppler/modular_quirks/permitted_cybernetic/code/preferences.dm @@ -0,0 +1,20 @@ +/datum/preference/choiced/permitted_cybernetic + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "permitted_cybernetic" + savefile_identifier = PREFERENCE_CHARACTER + can_randomize = FALSE + +/datum/preference/choiced/permitted_cybernetic/init_possible_values() + return list("Random") + assoc_to_keys(GLOB.possible_quirk_implants) + +/datum/preference/choiced/permitted_cybernetic/create_default_value() + return "Random" + +/datum/preference/choiced/permitted_cybernetic/is_accessible(datum/preferences/preferences) + if (!..()) + return FALSE + + return "Permitted Cybernetic" in preferences.all_quirks + +/datum/preference/choiced/permitted_cybernetic/apply_to_human(mob/living/carbon/human/target, value) + return diff --git a/modular_doppler/modular_quirks/permitted_cybernetic/permitted_cybernetic.dm b/modular_doppler/modular_quirks/permitted_cybernetic/permitted_cybernetic.dm new file mode 100644 index 0000000000000..e9a15bf98d88c --- /dev/null +++ b/modular_doppler/modular_quirks/permitted_cybernetic/permitted_cybernetic.dm @@ -0,0 +1,73 @@ +GLOBAL_LIST_INIT(possible_quirk_implants, list( + "Engineering Toolset" = /obj/item/organ/internal/cyberimp/arm/toolset, + "Surgery Toolset" = /obj/item/organ/internal/cyberimp/arm/surgery, + "Hydroponics Toolset" = /obj/item/organ/internal/cyberimp/arm/botany, + "Sanitation Toolset" = /obj/item/organ/internal/cyberimp/arm/janitor, + "Razorclaw Implant" = /obj/item/organ/internal/cyberimp/arm/razor_claws, + "Excavator Implant" = /obj/item/organ/internal/cyberimp/arm/mining_drill, +)) + +/datum/quirk/permitted_cybernetic + name = "Permitted Cybernetic" + desc = "You're allowed a cybernetic implant aboard the station, though this is information is available for security." + value = 8 + mob_trait = TRAIT_PERMITTED_CYBERNETIC + icon = FA_ICON_WRENCH + +/datum/quirk_constant_data/implanted + associated_typepath = /datum/quirk/permitted_cybernetic + customization_options = list(/datum/preference/choiced/permitted_cybernetic) + +/datum/quirk/permitted_cybernetic/add_unique(client/client_source) + var/obj/item/organ/desired_implant = GLOB.possible_quirk_implants[client_source?.prefs?.read_preference(/datum/preference/choiced/permitted_cybernetic)] + if(isnull(desired_implant)) //Client gone or they chose a random implant + desired_implant = GLOB.possible_quirk_implants[pick(GLOB.possible_quirk_implants)] + + var/mob/living/carbon/human/human_holder = quirk_holder + if(desired_implant.zone in GLOB.arm_zones) + if(HAS_TRAIT(human_holder, TRAIT_LEFT_HANDED)) //Left handed person? Give them a leftie implant + desired_implant = text2path("[desired_implant]/l") + + if(human_holder.dna.species.type in GLOB.species_blacklist_no_humanoid) + to_chat(human_holder, span_warning("Due to your species type, the [name] quirk has been disabled.")) + return + if(human_holder.mind?.assigned_role.title == JOB_PRISONER) + to_chat(human_holder, span_warning("Due to your job, the [name] quirk has been disabled.")) + return + + var/obj/item/organ/internal/cybernetic = new desired_implant() + cybernetic.Insert(human_holder, special = TRUE, movement_flags = DELETE_IF_REPLACED) + medical_record_text = "Patient has a company approved [cybernetic.name] installed within their body." + +/datum/quirk/permitted_cybernetic/add(client/client_source) + . = ..() + quirk_holder.update_implanted_hud() + +/datum/quirk/permitted_cybernetic/remove() + var/mob/living/old_holder = quirk_holder + . = ..() + old_holder.update_implanted_hud() + +/mob/living/prepare_data_huds() + . = ..() + update_implanted_hud() + +/// Adds the HUD element if src has its trait. Removes it otherwise. +/mob/living/proc/update_implanted_hud() + var/image/quirk_holder = hud_list?[SEC_IMPLANT_HUD] + if(isnull(quirk_holder)) + return + + var/icon/temporary_icon = icon(icon, icon_state, dir) + quirk_holder.pixel_y = temporary_icon.Height() - world.icon_size + + if(ishuman(src)) + var/mob/living/carbon/human/target = src + if(target.dna.species.type in GLOB.species_blacklist_no_humanoid) + return + if(HAS_TRAIT(src, TRAIT_PERMITTED_CYBERNETIC)) + set_hud_image_active(SEC_IMPLANT_HUD) + quirk_holder.icon = 'modular_doppler/overwrites/huds/hud.dmi' + quirk_holder.icon_state = "hud_imp_quirk" + else + set_hud_image_inactive(SEC_IMPLANT_HUD) diff --git a/modular_doppler/modular_sounds/sound/machines/whirr.ogg b/modular_doppler/modular_sounds/sound/machines/whirr.ogg new file mode 100644 index 0000000000000..bc51d5aa04676 Binary files /dev/null and b/modular_doppler/modular_sounds/sound/machines/whirr.ogg differ diff --git a/modular_doppler/modular_sounds/sound/mobs/humanoids/android/attributions.txt b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/attributions.txt new file mode 100644 index 0000000000000..947512733701f --- /dev/null +++ b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/attributions.txt @@ -0,0 +1,3 @@ +{ +monitor_switch.ogg - https://freesound.org/people/bolkmar/sounds/511890/ , License: CC0 +} diff --git a/modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav new file mode 100644 index 0000000000000..eeaee3a92fc35 Binary files /dev/null and b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav differ diff --git a/modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg new file mode 100644 index 0000000000000..a2a12fc306539 Binary files /dev/null and b/modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg differ diff --git a/modular_doppler/modular_species/_species.dm b/modular_doppler/modular_species/_species.dm index 422499d5faacd..00048be82343c 100644 --- a/modular_doppler/modular_species/_species.dm +++ b/modular_doppler/modular_species/_species.dm @@ -1,3 +1,45 @@ +/** + * # species datum + * + * Datum that handles different species in the game. + * + * This datum handles species in the game, such as lizardpeople, mothmen, zombies, skeletons, etc. + * It is used in [carbon humans][mob/living/carbon/human] to determine various things about them, like their food preferences, if they have biological genders, their damage resistances, and more. + * + */ +/datum/species + /// Adding a language type to this in the form of /datum/language will allow the language to be displayed in preferences for that species, even if it is a secret language. + /// Currently used for Ættmál in hearthkin. + var/list/language_prefs_whitelist + ///If a species can always be picked in prefs for the purposes of customizing it for ghost roles or events + var/always_customizable = FALSE + ///How are we treated regarding processing reagents, by default we process them as if we're organic + var/reagent_flags = PROCESS_ORGANIC + ///This is the outfit which will be used by the species its preview portrait + var/datum/outfit/preview_outfit = /datum/outfit/job/assistant/consistent + + +/// Cybernetic limbs logic here! +// Used for most races +/datum/species/on_species_gain(mob/living/carbon/human/target, datum/species/old_species, pref_load) + var/list/frame_bodyparts = target.dna.features["frame_list"] + if(type in GLOB.species_blacklist_no_humanoid) + return ..() + if(type == /datum/species/android && frame_bodyparts && frame_bodyparts[BODY_ZONE_HEAD]) + bodypart_overrides[BODY_ZONE_HEAD] = frame_bodyparts[BODY_ZONE_HEAD] + if(frame_bodyparts && frame_bodyparts[BODY_ZONE_CHEST]) + bodypart_overrides[BODY_ZONE_CHEST] = frame_bodyparts[BODY_ZONE_CHEST] + if(frame_bodyparts && frame_bodyparts[BODY_ZONE_R_ARM]) + bodypart_overrides[BODY_ZONE_R_ARM] = frame_bodyparts[BODY_ZONE_R_ARM] + if(frame_bodyparts && frame_bodyparts[BODY_ZONE_L_ARM]) + bodypart_overrides[BODY_ZONE_L_ARM] = frame_bodyparts[BODY_ZONE_L_ARM] + if(frame_bodyparts && frame_bodyparts[BODY_ZONE_R_LEG]) + bodypart_overrides[BODY_ZONE_R_LEG] = frame_bodyparts[BODY_ZONE_R_LEG] + if(frame_bodyparts && frame_bodyparts[BODY_ZONE_L_LEG]) + bodypart_overrides[BODY_ZONE_L_LEG] = frame_bodyparts[BODY_ZONE_L_LEG] + return ..() + + /// Animal trait logic goes here! // Used for the genemod and anthro species @@ -28,3 +70,13 @@ ADD_TRAIT(target, TRAIT_CATLIKE_GRACE, SPECIES_TRAIT) ADD_TRAIT(target, TRAIT_HATED_BY_DOGS, SPECIES_TRAIT) + +/// spec_revival logic +/datum/species/proc/spec_revival(mob/living/carbon/human/target) + return + +/mob/living/carbon/human/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE) + . = ..() + if(.) + if(dna && dna.species) + dna.species.spec_revival(src) diff --git a/modular_doppler/modular_species/code/bloodsplatter.dm b/modular_doppler/modular_species/code/bloodsplatter.dm index 4243a8813a35a..f4b1f4611beda 100644 --- a/modular_doppler/modular_species/code/bloodsplatter.dm +++ b/modular_doppler/modular_species/code/bloodsplatter.dm @@ -1,15 +1,3 @@ -/// Green blood reagent -/datum/reagent/blood/green - data = list("viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null,"quirks"=null) - name = "Green Blood" - color = "#50c034" // rgb: 0, 200, 0 - metabolization_rate = 12.5 * REAGENTS_METABOLISM //fast rate so it disappears fast. - taste_description = "iron" - taste_mult = 1.3 - penetrates_skin = NONE - ph = 7.4 - default_container = /obj/item/reagent_containers/blood - /// In this .dm we make a green bloodsplatter subtype // Using the xenoblood icons /obj/effect/decal/cleanable/blood/hitsplatter/green @@ -18,20 +6,44 @@ random_icon_states = list("xhitsplatter1", "xhitsplatter2", "xhitsplatter3") blood_state = BLOOD_STATE_XENO +/obj/effect/decal/cleanable/blood/hitsplatter/blue + icon = 'modular_doppler/modular_species/icons/blood.dmi' + icon_state = "xhitsplatter1" + random_icon_states = list("bhitsplatter1", "bhitsplatter2", "bhitsplatter3") + blood_state = BLOOD_STATE_NOT_BLOODY + /obj/effect/temp_visual/dir_setting/bloodsplatter/green splatter_type = "xsplatter" +/obj/effect/temp_visual/dir_setting/bloodsplatter/blue + icon = 'modular_doppler/modular_species/icons/blood.dmi' + splatter_type = "bsplatter" + /obj/effect/decal/cleanable/blood/green - name = "green blood" + name = "insect blood" desc = "It's green... And it looks like... blood?" icon = 'icons/effects/blood.dmi' icon_state = "xfloor1" random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7") bloodiness = BLOOD_AMOUNT_PER_DECAL + should_dry = FALSE blood_state = BLOOD_STATE_XENO beauty = -250 clean_type = CLEAN_TYPE_BLOOD +/obj/effect/decal/cleanable/blood/blue + name = "robot blood" + desc = "It's blue... And it looks like... blood?" + icon = 'modular_doppler/modular_species/icons/blood.dmi' + icon_state = "bfloor1" + random_icon_states = list("bfloor1", "bfloor2", "bfloor3", "bfloor4", "bfloor5", "bfloor6", "bfloor7") + bloodiness = BLOOD_AMOUNT_PER_DECAL + should_dry = FALSE + blood_state = BLOOD_STATE_NOT_BLOODY + clean_type = CLEAN_TYPE_BLOOD + +//// splatter +// green /obj/effect/decal/cleanable/blood/green/splatter random_icon_states = list("xgibbl1", "xgibbl2", "xgibbl3", "xgibbl4", "xgibbl5") @@ -44,22 +56,49 @@ /obj/effect/decal/cleanable/blood/green/splatter/over_window/NeverShouldHaveComeHere(turf/here_turf) return isgroundlessturf(here_turf) +// blue +/obj/effect/decal/cleanable/blood/blue/splatter + random_icon_states = list("bgibbl1", "bgibbl2", "bgibbl3", "bgibbl4", "bgibbl5") + +/obj/effect/decal/cleanable/blood/blue/splatter/over_window + layer = ABOVE_WINDOW_LAYER + plane = GAME_PLANE + vis_flags = VIS_INHERIT_PLANE + alpha = 180 + +/obj/effect/decal/cleanable/blood/blue/splatter/over_window/NeverShouldHaveComeHere(turf/here_turf) + return isgroundlessturf(here_turf) + +//// drips +// green /obj/effect/decal/cleanable/blood/drip/green - name = "drips of blood" + name = "drips of insect blood" desc = "It's green." icon = 'modular_doppler/modular_species/icons/blood.dmi' icon_state = "xdrip5" random_icon_states = list("xdrip1","xdrip2","xdrip3","xdrip4","xdrip5") - should_dry = FALSE //human only thing + should_dry = FALSE blood_state = BLOOD_STATE_XENO beauty = -150 +// blue +/obj/effect/decal/cleanable/blood/drip/blue + name = "drips of robot blood" + desc = "It's blue." + icon = 'modular_doppler/modular_species/icons/blood.dmi' + icon_state = "bdrip5" + random_icon_states = list("bdrip1","bdrip2","bdrip3","bdrip4","bdrip5") + should_dry = FALSE + blood_state = BLOOD_STATE_NOT_BLOODY + beauty = -100 //getTrail overwrite /mob/living/carbon/human/getTrail() - if(!(hasgreenblood(src))) - return ..() - if(getBruteLoss() < 300) - return pick (list("xltrails_1", "xltrails2")) - else - return pick (list("xttrails_1", "xttrails2")) + if((hasgreenblood(src))) + if(getBruteLoss() < 300) + return pick (list("xltrails_1", "xltrails2")) + else + return pick (list("xttrails_1", "xttrails2")) + if((hasblueblood(src))) + return pick (list("btrails_1", "btrails2")) + return ..() diff --git a/modular_doppler/modular_species/icons/blood.dmi b/modular_doppler/modular_species/icons/blood.dmi index 530731c98d33a..574b8ab5e9c26 100644 Binary files a/modular_doppler/modular_species/icons/blood.dmi and b/modular_doppler/modular_species/icons/blood.dmi differ diff --git a/modular_doppler/modular_species/overwrites/code/species.dm b/modular_doppler/modular_species/overwrites/code/species.dm deleted file mode 100644 index dec467a8fae0e..0000000000000 --- a/modular_doppler/modular_species/overwrites/code/species.dm +++ /dev/null @@ -1,20 +0,0 @@ -// Code which allows us to set custom outfits for species portraits -/datum/species - ///This is the outfit which will be used by the species its preview portrait - var/datum/outfit/preview_outfit = /datum/outfit/job/assistant/consistent - -/** - * # species datum - * - * Datum that handles different species in the game. - * - * This datum handles species in the game, such as lizardpeople, mothmen, zombies, skeletons, etc. - * It is used in [carbon humans][mob/living/carbon/human] to determine various things about them, like their food preferences, if they have biological genders, their damage resistances, and more. - * - */ -/datum/species - /// Adding a language type to this in the form of /datum/language will allow the language to be displayed in preferences for that species, even if it is a secret language. - /// Currently used for Ættmál in hearthkin. - var/list/language_prefs_whitelist - ///If a species can always be picked in prefs for the purposes of customizing it for ghost roles or events - var/always_customizable = FALSE diff --git a/modular_doppler/modular_species/species_types/android/android.dm b/modular_doppler/modular_species/species_types/android/android.dm new file mode 100644 index 0000000000000..c1837933bf462 --- /dev/null +++ b/modular_doppler/modular_species/species_types/android/android.dm @@ -0,0 +1,134 @@ +/// The starter amount for the android's core +#define ENERGY_START_AMT 10 MEGA JOULES +/// The amount at which mob energy decreases +#define ENERGY_DRAIN_AMT 10 KILO JOULES + +/datum/species/android + name = "Android" + id = SPECIES_ANDROID + preview_outfit = /datum/outfit/android_preview + examine_limb_id = SPECIES_HUMAN + inherent_traits = list( + TRAIT_MUTANT_COLORS, + TRAIT_GENELESS, + TRAIT_LIMBATTACHMENT, + TRAIT_NOBREATH, + TRAIT_NOHUNGER, + TRAIT_NOCRITDAMAGE, + TRAIT_NO_DNA_COPY, + TRAIT_NO_PLASMA_TRANSFORM, + TRAIT_RADIMMUNE, + TRAIT_RESISTLOWPRESSURE, + /*TG traits we remove + TRAIT_LIVERLESS_METABOLISM, + TRAIT_PIERCEIMMUNE, + TRAIT_OVERDOSEIMMUNE, + TRAIT_TOXIMMUNE, + TRAIT_NOFIRE, + TRAIT_NOBLOOD, + TRAIT_NO_UNDERWEAR, + TRAIT_RESISTHEAT, + TRAIT_RESISTCOLD, + TRAIT_RESISTHIGHPRESSURE,*/ + TRAIT_STABLEHEART, + TRAIT_STABLELIVER, + ) + reagent_flags = PROCESS_SYNTHETIC + body_markings = list(/datum/bodypart_overlay/simple/body_marking/lizard = "None") + mutantheart = /obj/item/organ/internal/heart/cybernetic/tier2 + mutantstomach = /obj/item/organ/internal/stomach/cybernetic/tier2 + mutantliver = /obj/item/organ/internal/liver/cybernetic/tier2 + exotic_blood = /datum/reagent/synth_blood + exotic_bloodtype = "R*" + + bodytemp_heat_damage_limit = (BODYTEMP_NORMAL + 146) // 456 K / 183 C + bodytemp_cold_damage_limit = (BODYTEMP_NORMAL - 80) // 230 K / -43 C + /// Ability to recharge! + var/datum/action/innate/power_cord/power_cord + /// Hud element to display our energy level + var/atom/movable/screen/android/energy/energy_tracker + /// How much energy we start with + var/core_energy = ENERGY_START_AMT + +/datum/outfit/android_preview + name = "Android (Species Preview)" + // nude + +/datum/species/android/on_species_gain(mob/living/carbon/target, datum/species/old_species, pref_load) + . = ..() + if(ishuman(target)) + power_cord = new + power_cord.Grant(target) + +/datum/species/android/on_species_loss(mob/living/carbon/target, datum/species/new_species, pref_load) + . = ..() + if(power_cord) + power_cord.Remove(target) + if(target.hud_used) + var/datum/hud/hud_used = target.hud_used + hud_used.infodisplay -= energy_tracker + QDEL_NULL(energy_tracker) + +/datum/species/android/spec_revival(mob/living/carbon/human/target) + playsound(target.loc, 'sound/machines/chime.ogg', 50, TRUE) + target.visible_message(span_notice("[target]'s LEDs flicker to life!"), span_notice("All systems nominal. You're back online!")) + +/datum/species/android/spec_life(mob/living/carbon/human/target, seconds_per_tick, times_fired) + . = ..() + handle_hud(target) + + if(target.stat == SOFT_CRIT || target.stat == HARD_CRIT) + target.adjustFireLoss(1) //Still deal some damage in case a cold environment would be preventing us from the sweet release to robot heaven + target.adjust_bodytemperature(13) //We're overheating!! + if(prob(10)) + to_chat(target, span_warning("Alert: Critical damage taken! Cooling systems failing!")) + do_sparks(3, FALSE, target) + + if(HAS_TRAIT(target, TRAIT_CHARGING)) + return + if(core_energy > 0) + core_energy -= ENERGY_DRAIN_AMT + if(core_energy <= 0) + target.death() // You can do a lot in a day. + +/datum/species/android/proc/handle_hud(mob/living/carbon/human/target) + // update it + if(energy_tracker) + energy_tracker.update_energy_hud(core_energy) + // initialize it + else if(target.hud_used) + var/datum/hud/hud_used = target.hud_used + energy_tracker = new(null, hud_used) + hud_used.infodisplay += energy_tracker + + target.hud_used.show_hud(target.hud_used.hud_version) + +/datum/species/android/prepare_human_for_preview(mob/living/carbon/human/robot_for_preview) + robot_for_preview.dna.ear_type = CYBERNETIC + robot_for_preview.dna.features["ears"] = "TV Antennae" + robot_for_preview.dna.features["ears_color_1"] = "#333333" + robot_for_preview.dna.features["frame_list"] = list( + BODY_ZONE_HEAD = /obj/item/bodypart/head/robot/android/sgm, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/robot/android/sgm, + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/robot/android/sgm, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/robot/android/sgm, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/robot/android/sgm, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/robot/android/sgm) + regenerate_organs(robot_for_preview) + robot_for_preview.update_body(is_creating = TRUE) + +/datum/species/android/get_physical_attributes() + return "Androids are almost, but not quite, identical to fully augmented humans. \ + Unlike those, though, they're completely immune to toxin damage, don't have blood or organs (besides their head), don't get hungry, and can reattach their limbs! \ + That said, an EMP will devastate them and they cannot process any chemicals." + +/datum/species/android/get_species_description() + return "Androids are an entirely synthetic species." + +/datum/species/android/get_species_lore() + return list( + "Androids are a synthetic species created by Nanotrasen as an intermediary between humans and cyborgs." + ) + +#undef ENERGY_START_AMT +#undef ENERGY_DRAIN_AMT diff --git a/modular_doppler/modular_species/species_types/android/android_hud.dm b/modular_doppler/modular_species/species_types/android/android_hud.dm new file mode 100644 index 0000000000000..b5aca6d217f56 --- /dev/null +++ b/modular_doppler/modular_species/species_types/android/android_hud.dm @@ -0,0 +1,22 @@ +/// 1 tile down +#define UI_ENERGY_DISPLAY "WEST:6,CENTER-1:0" + +///Maptext define for Hemophage HUDs +#define FORMAT_ANDROID_HUD_TEXT(valuecolor, value) MAPTEXT("
[round((value/1000000), 0.01)]MJ
") + +/atom/movable/screen/android + icon = 'modular_doppler/modular_species/species_types/android/icons/android_hud.dmi' + +/atom/movable/screen/android/energy + name = "Energy Tracker" + icon_state = "energy_display" + screen_loc = UI_ENERGY_DISPLAY + +/atom/movable/screen/android/energy/proc/update_energy_hud(core_energy) + maptext = FORMAT_ANDROID_HUD_TEXT(hud_text_color(), core_energy) + +/atom/movable/screen/android/energy/proc/hud_text_color(core_energy) + return core_energy < 2.5 MEGA JOULES ? "#ffffff" : "#FFAAAA" + +#undef UI_ENERGY_DISPLAY +#undef FORMAT_ANDROID_HUD_TEXT diff --git a/modular_doppler/modular_species/species_types/android/android_parts.dm b/modular_doppler/modular_species/species_types/android/android_parts.dm new file mode 100644 index 0000000000000..c29a6d0f4daf2 --- /dev/null +++ b/modular_doppler/modular_species/species_types/android/android_parts.dm @@ -0,0 +1,908 @@ +// easy define for head_flags of android heads that dont feature eyes (aka monitor heads (aka IPCs)) +#define HEAD_MONITOR_FACE (HEAD_HAIR|HEAD_LIPS|HEAD_DEBRAIN) +// easy define for the android bodypart .dmi +#define ANDROID_BODYPARTS_DMI 'modular_doppler/modular_species/species_types/android/icons/android_parts.dmi' + +/obj/item/bodypart/proc/change_type(mob/living/user, obj/item/tool) + if(brute_dam || burn_dam) + user.balloon_alert(user, "limb damaged!") + return NONE + + var/list/possible_appearances = list() + for(var/types in GLOB.frame_types) + if(types == "none") + continue + LAZYADDASSOC(possible_appearances, types, image(icon = ANDROID_BODYPARTS_DMI, icon_state = "[types]_[body_zone]")) + //pick + var/new_type = show_radial_menu(user, src, possible_appearances, require_near = TRUE, tooltips = TRUE, radius = 48) + if(!new_type) + return NONE + //weld + if(tool.use_tool(src, user, delay = 2 SECONDS, volume = 20)) + var/type_to_spawn = text2path("[type]/[new_type]") + if(!type_to_spawn) + type_to_spawn = text2path("[parent_type]/[new_type]") + var/obj/item/bodypart/new_bodypart = new type_to_spawn(loc) + //inherit detail + for(var/obj/item/organ/to_transfer in contents) + to_transfer.bodypart_insert(new_bodypart) + new_bodypart.name = name + new_bodypart.desc = desc + qdel(src) + return ITEM_INTERACT_SUCCESS + +/// +// Overwrites +/// +// head +/obj/item/bodypart/head/robot/android + biological_state = (BIO_ROBOTIC|BIO_BLOODED) + +/obj/item/bodypart/head/robot/android/welder_act_secondary(mob/living/user, obj/item/tool) + . = ..() + return change_type(user, tool) + +/obj/item/bodypart/head/robot/android/examine(mob/user) + . = ..() + . += span_blue("Right-click with a welding-tool to alter the limb appearance.") + +// chest +/obj/item/bodypart/chest/robot/android + biological_state = (BIO_ROBOTIC|BIO_BLOODED) + +/obj/item/bodypart/chest/robot/android/welder_act_secondary(mob/living/user, obj/item/tool) + . = ..() + return change_type(user, tool) + +/obj/item/bodypart/chest/robot/android/examine(mob/user) + . = ..() + . += span_blue("Right-click with a welding-tool to alter the limb appearance.") + +/obj/item/bodypart/chest/robot/android/check_limbs() + return + +// right arm +/obj/item/bodypart/arm/right/robot/android + biological_state = (BIO_ROBOTIC|BIO_BLOODED) + +/obj/item/bodypart/arm/right/robot/android/welder_act_secondary(mob/living/user, obj/item/tool) + . = ..() + return change_type(user, tool) + +/obj/item/bodypart/arm/right/robot/android/examine(mob/user) + . = ..() + . += span_blue("Right-click with a welding-tool to alter the limb appearance.") + +// left arm +/obj/item/bodypart/arm/left/robot/android + biological_state = (BIO_ROBOTIC|BIO_BLOODED) + +/obj/item/bodypart/arm/left/robot/android/welder_act_secondary(mob/living/user, obj/item/tool) + . = ..() + return change_type(user, tool) + +/obj/item/bodypart/arm/left/robot/android/examine(mob/user) + . = ..() + . += span_blue("Right-click with a welding-tool to alter the limb appearance.") + +// right leg +/obj/item/bodypart/leg/right/robot/android + biological_state = (BIO_ROBOTIC|BIO_BLOODED) + +/obj/item/bodypart/leg/right/robot/android/welder_act_secondary(mob/living/user, obj/item/tool) + . = ..() + return change_type(user, tool) + +/obj/item/bodypart/leg/right/robot/android/examine(mob/user) + . = ..() + . += span_blue("Right-click with a welding-tool to alter the limb appearance.") + +// left leg +/obj/item/bodypart/leg/left/robot/android + biological_state = (BIO_ROBOTIC|BIO_BLOODED) + +/obj/item/bodypart/leg/left/robot/android/welder_act_secondary(mob/living/user, obj/item/tool) + . = ..() + return change_type(user, tool) + +/obj/item/bodypart/leg/left/robot/android/examine(mob/user) + . = ..() + . += span_blue("Right-click with a welding-tool to alter the limb appearance.") + +/// +// Classic +/// +/obj/item/bodypart/head/robot/android/classic + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "classic_head" + limb_id = "classic" + +/obj/item/bodypart/chest/robot/android/classic + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "classic_chest" + limb_id = "classic" + +/obj/item/bodypart/arm/right/robot/android/classic + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "classic_r_arm" + limb_id = "classic" + +/obj/item/bodypart/arm/left/robot/android/classic + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "classic_l_arm" + limb_id = "classic" + +/obj/item/bodypart/leg/right/robot/android/classic + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "classic_r_leg" + limb_id = "classic" + +/obj/item/bodypart/leg/left/robot/android/classic + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "classic_l_leg" + limb_id = "classic" + +/// +// Bare +/// +/obj/item/bodypart/head/robot/android/bare + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bare_head" + limb_id = "bare" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/bare/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/bare/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/bare + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bare_chest" + limb_id = "bare" + +/obj/item/bodypart/arm/right/robot/android/bare + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bare_r_arm" + limb_id = "bare" + +/obj/item/bodypart/arm/left/robot/android/bare + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bare_l_arm" + limb_id = "bare" + +/obj/item/bodypart/leg/right/robot/android/bare + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bare_r_leg" + limb_id = "bare" + +/obj/item/bodypart/leg/left/robot/android/bare + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bare_r_leg" + limb_id = "bare" + +/// +// Mariinsky +/// +/obj/item/bodypart/head/robot/android/mariinsky + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mariinsky_head" + limb_id = "mariinsky" + +/obj/item/bodypart/chest/robot/android/mariinsky + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mariinsky_chest" + limb_id = "mariinsky" + +/obj/item/bodypart/arm/right/robot/android/mariinsky + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mariinsky_r_arm" + limb_id = "mariinsky" + +/obj/item/bodypart/arm/left/robot/android/mariinsky + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mariinsky_l_arm" + limb_id = "mariinsky" + +/obj/item/bodypart/leg/right/robot/android/mariinsky + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mariinsky_r_leg" + limb_id = "mariinsky" + +/obj/item/bodypart/leg/left/robot/android/mariinsky + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mariinsky_r_leg" + limb_id = "mariinsky" + +/// +// E3N +/// +/obj/item/bodypart/head/robot/android/e_three_n + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "e_three_n_head" + limb_id = "e_three_n" + head_flags = (HEAD_HAIR|HEAD_DEBRAIN) + +/obj/item/bodypart/chest/robot/android/e_three_n + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "e_three_n_chest" + limb_id = "e_three_n" + +/obj/item/bodypart/arm/right/robot/android/e_three_n + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "e_three_n_r_arm" + limb_id = "e_three_n" + +/obj/item/bodypart/arm/left/robot/android/e_three_n + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "e_three_n_l_arm" + limb_id = "e_three_n" + +/obj/item/bodypart/leg/right/robot/android/e_three_n + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "e_three_n_r_leg" + limb_id = "e_three_n" + +/obj/item/bodypart/leg/left/robot/android/e_three_n + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "e_three_n_r_leg" + limb_id = "e_three_n" + +/// +// Morpheus +/// +/obj/item/bodypart/head/robot/android/mc //morb + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mc_head" + limb_id = "mc" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/mc/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/mc/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/mc + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mc_chest" + limb_id = "mc" + +/obj/item/bodypart/arm/right/robot/android/mc + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mc_r_arm" + limb_id = "mc" + +/obj/item/bodypart/arm/left/robot/android/mc + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mc_l_arm" + limb_id = "mc" + +/obj/item/bodypart/leg/right/robot/android/mc + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mc_r_leg" + limb_id = "mc" + +/obj/item/bodypart/leg/left/robot/android/mc + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "mc_r_leg" + limb_id = "mc" + +/// +// Bishop Cyberkinetics +/// +/obj/item/bodypart/head/robot/android/bs_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_one_head" + limb_id = "bs_one" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/bs_one/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/bs_one/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/bs_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_one_chest" + limb_id = "bs_one" + +/obj/item/bodypart/arm/right/robot/android/bs_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_one_r_arm" + limb_id = "bs_one" + +/obj/item/bodypart/arm/left/robot/android/bs_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_one_l_arm" + limb_id = "bs_one" + +/obj/item/bodypart/leg/right/robot/android/bs_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_one_r_leg" + limb_id = "bs_one" + +/obj/item/bodypart/leg/left/robot/android/bs_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_one_r_leg" + limb_id = "bs_one" + +/// +// Bishop Cyberkinetics 2.0 +/// +/obj/item/bodypart/head/robot/android/bs_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_two_head" + limb_id = "bs_two" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/bs_two/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/bs_two/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/bs_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_two_chest" + limb_id = "bs_two" + +/obj/item/bodypart/arm/right/robot/android/bs_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_two_r_arm" + limb_id = "bs_two" + +/obj/item/bodypart/arm/left/robot/android/bs_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_two_l_arm" + limb_id = "bs_two" + +/obj/item/bodypart/leg/right/robot/android/bs_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_two_r_leg" + limb_id = "bs_two" + +/obj/item/bodypart/leg/left/robot/android/bs_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "bs_two_r_leg" + limb_id = "bs_two" + +/// +// Hephaestus Industries +/// +/obj/item/bodypart/head/robot/android/hi_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_one_head" + limb_id = "hi_one" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/hi_one/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/hi_one/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/hi_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_one_chest" + limb_id = "hi_one" + +/obj/item/bodypart/arm/right/robot/android/hi_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_one_r_arm" + limb_id = "hi_one" + +/obj/item/bodypart/arm/left/robot/android/hi_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_one_l_arm" + limb_id = "hi_one" + +/obj/item/bodypart/leg/right/robot/android/hi_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_one_r_leg" + limb_id = "hi_one" + +/obj/item/bodypart/leg/left/robot/android/hi_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_one_r_leg" + limb_id = "hi_one" + +/// +// Hephaestus Industries 2.0 +/// +/obj/item/bodypart/head/robot/android/hi_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_two_head" + limb_id = "hi_two" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/hi_two/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/hi_two/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/hi_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_two_chest" + limb_id = "hi_two" + +/obj/item/bodypart/arm/right/robot/android/hi_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_two_r_arm" + limb_id = "hi_two" + +/obj/item/bodypart/arm/left/robot/android/hi_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_two_l_arm" + limb_id = "hi_two" + +/obj/item/bodypart/leg/right/robot/android/hi_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_two_r_leg" + limb_id = "hi_two" + +/obj/item/bodypart/leg/left/robot/android/hi_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "hi_two_r_leg" + limb_id = "hi_two" + +/// +// Shellguard Munitions Standard Series 😎 +/// +/obj/item/bodypart/head/robot/android/sgm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "sgm_head" + limb_id = "sgm" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/sgm/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/sgm/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/sgm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "sgm_chest" + limb_id = "sgm" + +/obj/item/bodypart/arm/right/robot/android/sgm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "sgm_r_arm" + limb_id = "sgm" + +/obj/item/bodypart/arm/left/robot/android/sgm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "sgm_l_arm" + limb_id = "sgm" + +/obj/item/bodypart/leg/right/robot/android/sgm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "sgm_r_leg" + limb_id = "sgm" + +/obj/item/bodypart/leg/left/robot/android/sgm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "sgm_r_leg" + limb_id = "sgm" + +/// +// Ward Takahashi Manufacturing +/// +/obj/item/bodypart/head/robot/android/wtm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "wtm_head" + limb_id = "wtm" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/wtm/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/wtm/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/wtm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "wtm_chest" + limb_id = "wtm" + +/obj/item/bodypart/arm/right/robot/android/wtm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "wtm_r_arm" + limb_id = "wtm" + +/obj/item/bodypart/arm/left/robot/android/wtm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "wtm_l_arm" + limb_id = "wtm" + +/obj/item/bodypart/leg/right/robot/android/wtm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "wtm_r_leg" + limb_id = "wtm" + +/obj/item/bodypart/leg/left/robot/android/wtm + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "wtm_r_leg" + limb_id = "wtm" + +/// +// Xion Manufacturing Group +/// +/obj/item/bodypart/head/robot/android/xmg_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_one_head" + limb_id = "xmg_one" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/xmg_one/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/xmg_one/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/xmg_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_one_chest" + limb_id = "xmg_one" + +/obj/item/bodypart/arm/right/robot/android/xmg_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_one_r_arm" + limb_id = "xmg_one" + +/obj/item/bodypart/arm/left/robot/android/xmg_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_one_l_arm" + limb_id = "xmg_one" + +/obj/item/bodypart/leg/right/robot/android/xmg_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_one_r_leg" + limb_id = "xmg_one" + +/obj/item/bodypart/leg/left/robot/android/xmg_one + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_one_r_leg" + limb_id = "xmg_one" + +/// +// Xion Manufacturing Group 2.0 +/// +/obj/item/bodypart/head/robot/android/xmg_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_two_head" + limb_id = "xmg_two" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/xmg_two/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/xmg_two/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/xmg_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_two_chest" + limb_id = "xmg_two" + +/obj/item/bodypart/arm/right/robot/android/xmg_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_two_r_arm" + limb_id = "xmg_two" + +/obj/item/bodypart/arm/left/robot/android/xmg_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_two_l_arm" + limb_id = "xmg_two" + +/obj/item/bodypart/leg/right/robot/android/xmg_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_two_r_leg" + limb_id = "xmg_two" + +/obj/item/bodypart/leg/left/robot/android/xmg_two + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "xmg_two_r_leg" + limb_id = "xmg_two" + + +/// +// Zeng-Hu Pharmaceuticals +/// +/obj/item/bodypart/head/robot/android/zhp + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "zhp_head" + limb_id = "zhp" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/zhp/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head) + +/obj/item/bodypart/head/robot/android/zhp/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head)) + +/obj/item/bodypart/chest/robot/android/zhp + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "zhp_chest" + limb_id = "zhp" + +/obj/item/bodypart/arm/right/robot/android/zhp + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "zhp_r_arm" + limb_id = "zhp" + +/obj/item/bodypart/arm/left/robot/android/zhp + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "zhp_l_arm" + limb_id = "zhp" + +/obj/item/bodypart/leg/right/robot/android/zhp + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "zhp_r_leg" + limb_id = "zhp" + +/obj/item/bodypart/leg/left/robot/android/zhp + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_state = "zhp_r_leg" + limb_id = "zhp" + +/// +// Synthetic Lizard +/// +/obj/item/bodypart/head/robot/android/synth_lizard + bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_SNOUTED + should_draw_greyscale = TRUE + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_greyscale = ANDROID_BODYPARTS_DMI + icon_state = "synth_lizard_head" + limb_id = "synth_lizard" + head_flags = HEAD_MONITOR_FACE + +/obj/item/bodypart/head/robot/android/synth_lizard/on_adding(mob/living/carbon/new_owner) + . = ..() + new_owner.AddComponent(/datum/component/monitor_head/lizard) + +/obj/item/bodypart/head/robot/android/synth_lizard/on_removal(mob/living/carbon/old_owner) + . = ..() + qdel(old_owner.GetComponent(/datum/component/monitor_head/lizard)) + +/obj/item/bodypart/chest/robot/android/synth_lizard + is_dimorphic = TRUE + should_draw_greyscale = TRUE + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_greyscale = ANDROID_BODYPARTS_DMI + icon_state = "synth_lizard_chest_f" + limb_id = "synth_lizard" + +/obj/item/bodypart/arm/right/robot/android/synth_lizard + should_draw_greyscale = TRUE + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_greyscale = ANDROID_BODYPARTS_DMI + icon_state = "synth_lizard_r_arm" + limb_id = "synth_lizard" + +/obj/item/bodypart/arm/left/robot/android/synth_lizard + should_draw_greyscale = TRUE + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_greyscale = ANDROID_BODYPARTS_DMI + icon_state = "synth_lizard_l_arm" + limb_id = "synth_lizard" + +/obj/item/bodypart/leg/right/robot/android/synth_lizard + bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE + should_draw_greyscale = TRUE + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_greyscale = ANDROID_BODYPARTS_DMI + icon_state = "synth_lizard_r_leg" + limb_id = "synth_lizard" + +/obj/item/bodypart/leg/left/robot/android/synth_lizard + bodyshape = BODYSHAPE_HUMANOID | BODYSHAPE_DIGITIGRADE + should_draw_greyscale = TRUE + icon_static = ANDROID_BODYPARTS_DMI + icon = ANDROID_BODYPARTS_DMI + icon_greyscale = ANDROID_BODYPARTS_DMI + icon_state = "synth_lizard_r_leg" + limb_id = "synth_lizard" + +#undef HEAD_MONITOR_FACE +#undef ANDROID_BODYPARTS_DMI + +/datum/design/android_head + name = "Android Head" + id = "android_head" + build_type = MECHFAB + build_path = /obj/item/bodypart/head/robot/android + materials = list( + /datum/material/iron=SHEET_MATERIAL_AMOUNT*10, + /datum/material/silver=SHEET_MATERIAL_AMOUNT*3, + ) + construction_time = 20 SECONDS + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS + ) + +/datum/design/android_chest + name = "Android Chest" + id = "android_chest" + build_type = MECHFAB + build_path = /obj/item/bodypart/chest/robot/android + materials = list( + /datum/material/iron=SHEET_MATERIAL_AMOUNT*10, + /datum/material/silver=SHEET_MATERIAL_AMOUNT*3, + ) + construction_time = 20 SECONDS + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS + ) + +/datum/design/android_l_arm + name = "Android Left Arm" + id = "android_l_arm" + build_type = MECHFAB + build_path = /obj/item/bodypart/arm/left/robot/android + materials = list( + /datum/material/iron=SHEET_MATERIAL_AMOUNT*10, + /datum/material/silver=SHEET_MATERIAL_AMOUNT*3, + ) + construction_time = 20 SECONDS + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS + ) + +/datum/design/android_r_arm + name = "Android Right Arm" + id = "android_r_arm" + build_type = MECHFAB + build_path = /obj/item/bodypart/arm/right/robot/android + materials = list( + /datum/material/iron=SHEET_MATERIAL_AMOUNT*10, + /datum/material/silver=SHEET_MATERIAL_AMOUNT*3, + ) + construction_time = 20 SECONDS + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS + ) + +/datum/design/android_l_leg + name = "Android Left Leg" + id = "android_l_leg" + build_type = MECHFAB + build_path = /obj/item/bodypart/leg/left/robot/android + materials = list( + /datum/material/iron=SHEET_MATERIAL_AMOUNT*10, + /datum/material/silver=SHEET_MATERIAL_AMOUNT*3, + ) + construction_time = 20 SECONDS + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS + ) + +/datum/design/android_r_leg + name = "Android Right Leg" + id = "android_r_leg" + build_type = MECHFAB + build_path = /obj/item/bodypart/leg/right/robot/android + materials = list( + /datum/material/iron=SHEET_MATERIAL_AMOUNT*10, + /datum/material/silver=SHEET_MATERIAL_AMOUNT*3, + ) + construction_time = 20 SECONDS + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS + ) diff --git a/modular_doppler/modular_species/species_types/android/icons/android_hud.dmi b/modular_doppler/modular_species/species_types/android/icons/android_hud.dmi new file mode 100644 index 0000000000000..b524cf6328041 Binary files /dev/null and b/modular_doppler/modular_species/species_types/android/icons/android_hud.dmi differ diff --git a/modular_doppler/modular_species/species_types/android/icons/android_parts.dmi b/modular_doppler/modular_species/species_types/android/icons/android_parts.dmi new file mode 100644 index 0000000000000..ec042e3409120 Binary files /dev/null and b/modular_doppler/modular_species/species_types/android/icons/android_parts.dmi differ diff --git a/modular_doppler/modular_species/species_types/android/monitor_component.dm b/modular_doppler/modular_species/species_types/android/monitor_component.dm new file mode 100644 index 0000000000000..2bdc43cd0cd32 --- /dev/null +++ b/modular_doppler/modular_species/species_types/android/monitor_component.dm @@ -0,0 +1,200 @@ +#define MONITOR_HEAD (1<<0) +#define MONITOR_HEAD_LIZARD (1<<1) + +GLOBAL_LIST_INIT(monitor_displays, list( + "Disabled" = "none", + "Blank" = "blank", + "Blank (White)" = "blankwhite", + "Blue" = "blue", + "Blue Screen of Death" = "bsod", + "Breakout" = "breakout", + "Cigarette" = "smoking", + "Console" = "console", + "Cubic Wave" = "squarewave", + "Cyclops" = "eye", + "Database" = "database", + "ECG Wave" = "ecgwave", + "Eight" = "eight", + "Eyes" = "eyes", + "Goggles" = "goggles", + "Gol Glider" = "golglider", + "Green" = "green", + "Heart" = "heart", + "Heartrate Monitor" = "heartrate", + "Luminous Eyes" = "lumi_eyes", + "Mono Eye" = "mono_eye", + "Music" = "music", + "Nature" = "nature", + "Pink" = "pink", + "Purple" = "purple", + "Rainbow" = "rainbow", + "Red Text" = "redtext", + "Red" = "red", + "RGB" = "rgb", + "Scroll" = "scroll", + "Shower" = "shower", + "Sine Wave" = "sinewave", + "Smiley" = "yellow", + "Stars" = "stars", + "Sunburst" = "sunburst", + "Test Screen" = "test", + "Text Drop" = "textdrop", + "TV Static" = "static", + "TV Static (Color)" = "static3", + "TV Static (Slow)" = "static2", + "Waiting..." = "waiting", + )) + +GLOBAL_LIST_INIT(monitor_lizard_displays, list( + "Disabled" = "none", + "Eyes" = "liz_eyes", + "Question" = "liz_question", + "Exclaim" = "liz_exclaim", + )) + +// the overlay +/datum/bodypart_overlay/simple/monitor_head + icon = 'modular_doppler/modular_customization/accessories/icons/cybernetic/synth_screens.dmi' + icon_state = "none" + layers = EXTERNAL_ADJACENT + +// the component +/datum/component/monitor_head + dupe_mode = COMPONENT_DUPE_UNIQUE + var/datum/action/innate/monitor_head/display_action + +/datum/component/monitor_head/lizard + +/datum/component/monitor_head/Initialize(...) + . = ..() + if(!ishuman(parent)) + return COMPONENT_INCOMPATIBLE + + if(istype(src, /datum/component/monitor_head/lizard)) + display_action = new /datum/action/innate/monitor_head/lizard + else + display_action = new + + display_action.Grant(parent) + +/datum/component/monitor_head/Destroy(force) + if(display_action) + display_action.Remove(parent) + return ..() + +// the action +/datum/action/innate/monitor_head + name = "Change Display" + check_flags = AB_CHECK_CONSCIOUS + button_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "drone_vision" + background_icon_state = "bg_default" + /// the var that should be changed when a different screen list should be used + var/head_type = MONITOR_HEAD + /// the overlay we use + var/datum/bodypart_overlay/simple/monitor_head/display_overlay + +/datum/action/innate/monitor_head/Grant(mob/grant_to) + . = ..() + RegisterSignal(grant_to, COMSIG_MOB_EMOTE, PROC_REF(check_emote)) + +/datum/action/innate/monitor_head/Remove(mob/remove_from) + . = ..() + UnregisterSignal(remove_from, COMSIG_MOB_EMOTE) + +/datum/action/innate/monitor_head/Activate() + var/mob/living/carbon/human/wearer = owner + var/new_display = tgui_input_list(usr, "Choose your character's screen:", "Monitor Display", head_type & MONITOR_HEAD ? GLOB.monitor_displays : GLOB.monitor_lizard_displays) + if(!new_display) + return + + if(!display_overlay) + create_screen(wearer) + + change_screen(wearer, "[head_type & MONITOR_HEAD ? GLOB.monitor_displays[new_display] : GLOB.monitor_lizard_displays[new_display]]") + +/datum/action/innate/monitor_head/proc/check_emote(mob/living/carbon/wearer, datum/emote/emote) + SIGNAL_HANDLER + /// a list of the 'key' variable of emotes that have a screen update effect + var/static/list/screen_emotes = list( + "tunesing", + "exclaim", + "question", + ) + // early return + if(!(emote.key in screen_emotes)) + return + + if(!display_overlay) + create_screen(wearer) + + var/old_screen = display_overlay.icon_state + + if(head_type & MONITOR_HEAD) + switch(emote.key) + if("tunesing") + change_screen(wearer, "music") + + if(head_type & MONITOR_HEAD_LIZARD) + switch(emote.key) + if("exclaim") + change_screen(wearer, "liz_exclaim") + if("question") + change_screen(wearer, "liz_question") + // this timer is 5 seconds just like the emote overlays, so they are synchronized + addtimer(CALLBACK(src, PROC_REF(change_screen), wearer, old_screen), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) + +/datum/action/innate/monitor_head/update_status_on_signal(mob/living/carbon/wearer, new_stat, old_stat) + . = ..() + + if(!display_overlay) + create_screen(wearer) + + if(head_type & MONITOR_HEAD) + switch(new_stat) + if(SOFT_CRIT) + change_screen(wearer, "bsod") + if(HARD_CRIT) + change_screen(wearer, "static3") + if(UNCONSCIOUS) + change_screen(wearer, "none") + if(DEAD) + change_screen(wearer, "none") + + if(head_type & MONITOR_HEAD_LIZARD) + switch(new_stat) + if(UNCONSCIOUS) + change_screen(wearer, "none") + if(DEAD) + change_screen(wearer, "none") + +/datum/action/innate/monitor_head/proc/create_screen(mob/living/carbon/wearer) + var/obj/item/bodypart/head/monitor_head = wearer.get_bodypart(BODY_ZONE_HEAD) + display_overlay = new /datum/bodypart_overlay/simple/monitor_head + + if(head_type & MONITOR_HEAD_LIZARD) + var/mob/living/carbon/human/human_wearer = wearer + display_overlay.draw_color = human_wearer.eye_color_left + + monitor_head.add_bodypart_overlay(display_overlay) + +/datum/action/innate/monitor_head/proc/change_screen(mob/living/carbon/wearer, screen) + playsound(wearer, 'modular_doppler/modular_sounds/sound/mobs/humanoids/android/monitor_switch.ogg', 100, TRUE) + display_overlay.icon_state = screen + + wearer.update_body_parts() + +/datum/action/innate/monitor_head/Remove(mob/remove_from) + if(remove_from && display_overlay) + var/mob/living/carbon/human/wearer = remove_from + var/obj/item/bodypart/head/monitor_head = wearer.get_bodypart(BODY_ZONE_HEAD) + monitor_head.remove_bodypart_overlay(display_overlay) + wearer.update_body_parts() + return ..() + +/datum/action/innate/monitor_head/lizard + head_type = MONITOR_HEAD_LIZARD + + +#undef MONITOR_HEAD +#undef MONITOR_HEAD_LIZARD diff --git a/modular_doppler/modular_species/species_types/android/power_cord.dm b/modular_doppler/modular_species/species_types/android/power_cord.dm new file mode 100644 index 0000000000000..a92579b68d533 --- /dev/null +++ b/modular_doppler/modular_species/species_types/android/power_cord.dm @@ -0,0 +1,139 @@ +// Charge level defines +#define POWER_CORD_CHARGE_MAX 10 MEGA JOULES +#define POWER_CORD_CHARGE_RATE (STANDARD_CELL_RATE * 1.5) +#define POWER_CORD_CHARGE_DELAY 0.55 SECONDS +#define POWER_CORD_APC_MINIMUM_PERCENT 5 + +/datum/action/innate/power_cord + name = "Power Cord" + check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS + button_icon_state = "toolkit_generic" + button_icon = 'icons/obj/medical/organs/organs.dmi' + background_icon_state = "bg_default" + // What will be given in-hand + var/obj/item/hand_item/power_cord/power_cord + +/datum/action/innate/power_cord/Activate() + for(var/obj/item/hand_item/item in owner.held_items) + if(item) + owner.balloon_alert(owner, "hand occupied!") + return + power_cord = new + owner.put_in_active_hand(power_cord) + playsound(owner, 'sound/vehicles/mecha/mechmove03.ogg', 20, TRUE) + +/obj/item/hand_item/power_cord + name = "power cord" + desc = "An internal power cord. Useful if you run on electricity. Not so much otherwise." + icon = 'icons/obj/stack_objects.dmi' + icon_state = "wire" + /// What can be drained + var/static/list/cord_whitelist = typecacheof(list( + /obj/item/stock_parts/power_store, + /obj/machinery/power/apc, + )) + +// Attempt to charge from an object by using them on the power cord. +/obj/item/hand_item/power_cord/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!can_power_draw(tool, user)) + return NONE + try_power_draw(tool, user) + return ITEM_INTERACT_SUCCESS + +// Attempt to charge from an object by using the power cord on them. +/obj/item/hand_item/power_cord/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!can_power_draw(interacting_with, user)) + return NONE + try_power_draw(interacting_with, user) + return ITEM_INTERACT_SUCCESS + +/// Returns TRUE or FALSE depending on if the target object can be used as a power source. +/obj/item/hand_item/power_cord/proc/can_power_draw(obj/target, mob/user) + return ishuman(user) && is_type_in_typecache(target, cord_whitelist) + +/// Attempts to start using an object as a power source. +/obj/item/hand_item/power_cord/proc/try_power_draw(obj/target, mob/living/carbon/human/user) + user.changeNext_move(CLICK_CD_MELEE) + + var/datum/species/android/energy_holder = user.dna.species + if(energy_holder.core_energy >= POWER_CORD_CHARGE_MAX) + user.balloon_alert(user, "fully charged!") + return + + user.visible_message(span_notice("[user] inserts a power connector into [target]."), span_notice("You begin to draw power from [target].")) + do_power_draw(target, user) + + if(QDELETED(target)) + return + + if(HAS_TRAIT(user, TRAIT_CHARGING)) + REMOVE_TRAIT(user, TRAIT_CHARGING, SPECIES_TRAIT) + user.visible_message(span_notice("[user] unplugs from [target]."), span_notice("You unplug from [target].")) + +/** + * Runs a loop to charge an android from a cell or APC. + * Displays chat messages to the user and nearby observers. + * + * Stops when: + * - The user's is full. + * - The cell has less than the minimum charge. + * - The user moves, or anything else that can happen to interrupt a do_after. + * + * Arguments: + * * target - The power cell or APC to drain. + * * user - The human mob draining the power cell. + */ +/obj/item/hand_item/power_cord/proc/do_power_draw(obj/target, mob/living/carbon/human/user) + // Draw power from an APC if one was given. + var/obj/machinery/power/apc/target_apc + if(istype(target, /obj/machinery/power/apc)) + target_apc = target + + var/obj/item/stock_parts/power_store/target_cell = target_apc ? target_apc.cell : target + var/minimum_cell_charge = target_apc ? POWER_CORD_APC_MINIMUM_PERCENT : 0 + + if(!target_cell || target_cell.percent() < minimum_cell_charge) + user.balloon_alert(user, "APC charge low!") + return + var/energy_needed + while(TRUE) + ADD_TRAIT(user, TRAIT_CHARGING, SPECIES_TRAIT) + // Check if the charge level of the cell is below the minimum. + // Prevents from overloading the cell. + if(target_cell.percent() < minimum_cell_charge) + user.balloon_alert(user, "APC charge low!") + break + + // Attempt to drain charge from the cell. + if(!do_after(user, POWER_CORD_CHARGE_DELAY, target)) // slurp slurp slurp slurp + break + + // Check if the user is nearly fully charged. + // Ensures minimum draw is always lower than this margin. + var/datum/species/android/energy_holder = user.dna.species + energy_needed = POWER_CORD_CHARGE_MAX - energy_holder.core_energy + + // Calculate how much to draw from the cell this cycle. + var/current_draw = min(energy_needed, POWER_CORD_CHARGE_RATE * POWER_CORD_CHARGE_DELAY) + + var/energy_delivered = target_cell.use(current_draw, force = TRUE) + target_cell.update_appearance() + if(!energy_delivered) + // The cell could be sabotaged, which causes it to explode and qdelete. + if(QDELETED(target_cell)) + return + user.balloon_alert(user, "[target_apc ? "APC" : "Cell"] empty!") + break + + energy_holder.core_energy += energy_delivered + + playsound(user, 'modular_doppler/modular_sounds/sound/mobs/humanoids/android/drain.wav', 25, FALSE) + if(prob(8)) + do_sparks(3, FALSE, target_cell.loc) + if(energy_holder.core_energy >= POWER_CORD_CHARGE_MAX) + user.balloon_alert(user, "fully charged") + break + +#undef POWER_CORD_CHARGE_MAX +#undef POWER_CORD_CHARGE_RATE +#undef POWER_CORD_APC_MINIMUM_PERCENT diff --git a/modular_doppler/modular_species/species_types/flypeople/flypeople.dm b/modular_doppler/modular_species/species_types/flypeople/flypeople.dm new file mode 100644 index 0000000000000..ec837892e94e8 --- /dev/null +++ b/modular_doppler/modular_species/species_types/flypeople/flypeople.dm @@ -0,0 +1,3 @@ +/datum/species/fly + exotic_blood = /datum/reagent/bug_blood + exotic_bloodtype = "I*" diff --git a/modular_doppler/modular_species/species_types/genemod/genemod.dm b/modular_doppler/modular_species/species_types/genemod/genemod.dm index 9cfb03bb94139..d8ddf2b6e9e0e 100644 --- a/modular_doppler/modular_species/species_types/genemod/genemod.dm +++ b/modular_doppler/modular_species/species_types/genemod/genemod.dm @@ -10,12 +10,13 @@ TRAIT_ANIMALISTIC, TRAIT_MUTANT_COLORS, ) + body_markings = list(/datum/bodypart_overlay/simple/body_marking/lizard = "None") changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT digitigrade_customization = DIGITIGRADE_OPTIONAL digi_leg_overrides = list( - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/digitigrade/anthromorph, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/digitigrade/anthromorph, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/digitigrade/genemod, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/digitigrade/genemod, ) /datum/outfit/genemod_preview diff --git a/modular_doppler/modular_species/species_types/genemod/genemod_bodyparts.dm b/modular_doppler/modular_species/species_types/genemod/genemod_bodyparts.dm new file mode 100644 index 0000000000000..8ec917271d0f7 --- /dev/null +++ b/modular_doppler/modular_species/species_types/genemod/genemod_bodyparts.dm @@ -0,0 +1,15 @@ +/obj/item/bodypart/leg/left/digitigrade/genemod + icon_greyscale = 'modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi' + +/obj/item/bodypart/leg/left/digitigrade/genemod/update_limb(dropping_limb = FALSE, is_creating = FALSE) + . = ..() + if(limb_id == SPECIES_LIZARD) + limb_id = SPECIES_HUMAN + +/obj/item/bodypart/leg/right/digitigrade/genemod + icon_greyscale = 'modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi' + +/obj/item/bodypart/leg/right/digitigrade/genemod/update_limb(dropping_limb = FALSE, is_creating = FALSE) + . = ..() + if(limb_id == SPECIES_LIZARD) + limb_id = SPECIES_HUMAN diff --git a/modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi b/modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi new file mode 100644 index 0000000000000..1fd24c1afcc3c Binary files /dev/null and b/modular_doppler/modular_species/species_types/genemod/icons/bodyparts.dmi differ diff --git a/modular_doppler/modular_species/species_types/insectoid/insectoid.dm b/modular_doppler/modular_species/species_types/insectoid/insectoid.dm index a7b36b3d2b590..03407ac158c0b 100644 --- a/modular_doppler/modular_species/species_types/insectoid/insectoid.dm +++ b/modular_doppler/modular_species/species_types/insectoid/insectoid.dm @@ -13,7 +13,7 @@ inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID payday_modifier = 1.0 changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT - exotic_blood = /datum/reagent/blood/green + exotic_blood = /datum/reagent/bug_blood exotic_bloodtype = "I*" digitigrade_customization = DIGITIGRADE_OPTIONAL diff --git a/modular_doppler/modular_species/species_types/ramatae/ramatan.dm b/modular_doppler/modular_species/species_types/ramatae/ramatan.dm index 0a909d4caaa3d..9f37aa915dd29 100644 --- a/modular_doppler/modular_species/species_types/ramatae/ramatan.dm +++ b/modular_doppler/modular_species/species_types/ramatae/ramatan.dm @@ -13,6 +13,7 @@ /mob/living/carbon/human/species/ramatan race = /datum/species/ramatan + /datum/species/ramatan // The Ramatae of Aadia III - The Origin name = "\improper Ramatan" diff --git a/modular_doppler/modular_species/species_types/snails/modular_snail.dm b/modular_doppler/modular_species/species_types/snails/modular_snail.dm index e18aa0f758227..31b7048f25a92 100644 --- a/modular_doppler/modular_species/species_types/snails/modular_snail.dm +++ b/modular_doppler/modular_species/species_types/snails/modular_snail.dm @@ -1,9 +1,10 @@ #define SHELL_TRANSPARENCY_ALPHA 90 /datum/species/snail + preview_outfit = /datum/outfit/snail_preview mutantliver = /obj/item/organ/internal/liver/snail //This is just a better liver to deal with toxins, it's a thematic thing. mutantheart = /obj/item/organ/internal/heart/snail //This gives them the shell buff where they take less damage from behind, and their heart's more durable. - exotic_blood = /datum/reagent/blood/green + exotic_blood = /datum/reagent/bug_blood exotic_bloodtype = "I*" digitigrade_customization = DIGITIGRADE_OPTIONAL @@ -12,6 +13,11 @@ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/digitigrade/insectoid, ) +/datum/outfit/snail_preview + name = "Snail (Species Preview)" + uniform = /obj/item/clothing/under/rank/medical/chemist/pharmacologist/skirt + mask = /obj/item/clothing/mask/surgical + /datum/species/snail/on_species_gain(mob/living/carbon/new_snailperson, datum/species/old_species, pref_load) . = ..() new_snailperson.update_icons() @@ -141,7 +147,12 @@ wearer.update_worn_back() /datum/species/snail/prepare_human_for_preview(mob/living/carbon/human/snail) - snail.dna.features["mcolor"] = "#adaba7" + snail.dna.features["mcolor"] = "#797289" + snail.hairstyle = "Phoenix Half-Shaven" + snail.hair_color = "#4C3C7E" + snail.eye_color_left = "#615188" + snail.eye_color_right = "#615188" + regenerate_organs(snail, src, visual_only = TRUE) snail.update_body(TRUE) /datum/species/snail/create_pref_unique_perks() diff --git a/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm b/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm index 265bf226425eb..271e53f5bb968 100644 --- a/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm +++ b/modular_doppler/modular_vending/code/doppler_vendors/de_forest/vendor.dm @@ -22,8 +22,11 @@ /obj/item/stack/medical/mesh = 4, /obj/item/stack/medical/mesh/bloody = 2, /obj/item/stack/medical/bandage = 4, + /obj/item/reagent_containers/pill/robotic_patch/synth_repair = 4, /obj/item/stack/medical/wound_recovery = 2, /obj/item/stack/medical/wound_recovery/rapid_coagulant = 2, + /obj/item/stack/medical/wound_recovery/robofoam = 2, + /obj/item/stack/medical/wound_recovery/robofoam_super = 1, /obj/item/storage/pill_bottle/painkiller = 4, /obj/item/storage/medkit/civil_defense/stocked = 2, ), @@ -42,6 +45,8 @@ /obj/item/reagent_containers/hypospray/medipen/deforest/lepoturi = 3, /obj/item/reagent_containers/hypospray/medipen/deforest/psifinil = 3, /obj/item/reagent_containers/hypospray/medipen/deforest/halobinin = 3, + /obj/item/reagent_containers/hypospray/medipen/deforest/robot_system_cleaner = 3, + /obj/item/reagent_containers/hypospray/medipen/deforest/robot_liquid_solder = 3, ), ), ) diff --git a/modular_doppler/modular_weapons/code/jousting.dm b/modular_doppler/modular_weapons/code/jousting.dm new file mode 100644 index 0000000000000..f4e6beffe190d --- /dev/null +++ b/modular_doppler/modular_weapons/code/jousting.dm @@ -0,0 +1,15 @@ +/datum/component/jousting/Initialize(damage_boost_per_tile, knockdown_chance_per_tile, knockdown_time, max_tile_charge, min_tile_charge, datum/callback/successful_joust_callback) + . = ..() + + RegisterSignal(parent, COMSIG_PRE_BATON_FINALIZE_ATTACK, PROC_REF(on_successful_baton_attack)) + +/datum/component/jousting/proc/on_successful_baton_attack(datum/source, mob/living/target, mob/user) + SIGNAL_HANDLER + + if (!istype(parent, /obj/item/melee/baton/security)) + return + + var/obj/item/melee/baton/security/baton = parent + var/usable_charge = on_successful_attack(source, target, user) + if(usable_charge) + baton.on_successful_joust(target, user, usable_charge) diff --git a/modular_doppler/modular_weapons/code/melee.dm b/modular_doppler/modular_weapons/code/melee.dm new file mode 100644 index 0000000000000..5557424c3a022 --- /dev/null +++ b/modular_doppler/modular_weapons/code/melee.dm @@ -0,0 +1,16 @@ +/obj/item/melee/baton + /// For use with jousting. For each usable jousting tile, increase the stamina damage of the jousting hit by this much. + var/stamina_damage_per_jousting_tile = 2 + +/obj/item/melee/baton/Initialize(mapload) + . = ..() + + add_jousting_component() + +/// Component adder proc for custom behavior, without needing to add more vars. +/obj/item/melee/baton/proc/add_jousting_component() + AddComponent(/datum/component/jousting, damage_boost_per_tile = 0, knockdown_chance_per_tile = 6) + +/// For jousting. Called when a joust is considered successfully done. +/obj/item/melee/baton/proc/on_successful_joust(mob/living/target, mob/user, usable_charge) + target.apply_damage(stamina_damage_per_jousting_tile * usable_charge, STAMINA) diff --git a/modular_doppler/overwrites/huds/hud.dmi b/modular_doppler/overwrites/huds/hud.dmi index 4084f10c8acc8..c0fa61ebe4112 100644 Binary files a/modular_doppler/overwrites/huds/hud.dmi and b/modular_doppler/overwrites/huds/hud.dmi differ diff --git a/strings/wounds/metal_scar_desc.json b/strings/wounds/metal_scar_desc.json new file mode 100644 index 0000000000000..4cbe5eb058da4 --- /dev/null +++ b/strings/wounds/metal_scar_desc.json @@ -0,0 +1,67 @@ +{ + "generic": ["general disfigurement"], + + "dislocate": [ + "some slight crookedness" + ], + + "bluntsevere": [ + "an area of slightly crumpled metal", + "some misaligned plates" + ], + + "bluntcritical": [ + "a malformed superstructure", + "some heavily beaten plating" + ], + + "slashmoderate": [ + "a very thin line of solder", + "a few welded cuts" + ], + + "slashsevere": [ + "a pair of soldered cracks", + "an line of freshly replaced metal", + "some deep welds on the metal" + ], + + "slashcritical": [ + "a matrix of desperately soldered cracks", + "some gruesome welding lines", + "a series of deep and wide welded gashes" + ], + + "piercemoderate": [ + "a dot of hardened solder", + "a dot of fresh metal" + ], + + "piercesevere": [ + "a large wad of hardened solder", + "a few cracks originating from a small hole" + ], + + "piercecritical": [ + "a large splot of hardened solder", + "a spiderweb of cracks crawling from a sealed hole" + ], + + "burnsevere": [ + "some vaguely discolored metal", + "some burnt ridges", + "a few heat cracks" + ], + + "burncritical": [ + "streaks of polychromatic metal", + "gruesomely heat-warped plating", + "huge melting marks" + ], + + "dismember": [ + "has fresh metal around various joints", + "has some solder marks securing various joints", + "has clear re-seating marks" + ] +} diff --git a/tgstation.dme b/tgstation.dme index db6f599134955..8e2b4184dede4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -406,7 +406,6 @@ #include "code\__DEFINES\~doppler_defines\colony_fabricator_misc.dm" #include "code\__DEFINES\~doppler_defines\colors.dm" #include "code\__DEFINES\~doppler_defines\construction.dm" -#include "code\__DEFINES\~doppler_defines\declarations.dm" #include "code\__DEFINES\~doppler_defines\DNA.dm" #include "code\__DEFINES\~doppler_defines\enterprise_resource_planning.dm" #include "code\__DEFINES\~doppler_defines\examinemore.dm" @@ -420,10 +419,12 @@ #include "code\__DEFINES\~doppler_defines\manufacturer_strings.dm" #include "code\__DEFINES\~doppler_defines\mobfactions.dm" #include "code\__DEFINES\~doppler_defines\mobs.dm" +#include "code\__DEFINES\~doppler_defines\mutant_blacklists.dm" #include "code\__DEFINES\~doppler_defines\mutant_variations.dm" #include "code\__DEFINES\~doppler_defines\obj_flags_doppler.dm" #include "code\__DEFINES\~doppler_defines\organ_slots.dm" #include "code\__DEFINES\~doppler_defines\preferences.dm" +#include "code\__DEFINES\~doppler_defines\quirks.dm" #include "code\__DEFINES\~doppler_defines\reagent_forging_tools.dm" #include "code\__DEFINES\~doppler_defines\reagents.dm" #include "code\__DEFINES\~doppler_defines\reskin_defines.dm" @@ -6532,6 +6533,8 @@ #include "modular_doppler\deforest_medical_items\code\injectors.dm" #include "modular_doppler\deforest_medical_items\code\medstation.dm" #include "modular_doppler\deforest_medical_items\code\storage_items.dm" +#include "modular_doppler\deforest_medical_items\code\storage_items_robotics.dm" +#include "modular_doppler\deforest_medical_items\code\synth_healing.dm" #include "modular_doppler\deforest_medical_items\code\treatment_zone_projector.dm" #include "modular_doppler\deforest_medical_items\code\vulnerable_status_effect.dm" #include "modular_doppler\deforest_medical_items\code\chemicals\demoneye.dm" @@ -6705,10 +6708,15 @@ #include "modular_doppler\modular_cosmetics\GAGS\greyscale_configs_under.dm" #include "modular_doppler\modular_crafting\code\crafting_extended.dm" #include "modular_doppler\modular_crafting\code\sheet_types.dm" +#include "modular_doppler\modular_customization\accessories\code\hair.dm" #include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_body_markings.dm" #include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_ears.dm" #include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_snout.dm" #include "modular_doppler\modular_customization\accessories\code\aquatic_accessories\aquatic_tail.dm" +#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_body_markings.dm" +#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_ears.dm" +#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_horns.dm" +#include "modular_doppler\modular_customization\accessories\code\cybernetic_accessories\synth_tail.dm" #include "modular_doppler\modular_customization\accessories\code\human_accessories\human_ears.dm" #include "modular_doppler\modular_customization\accessories\code\human_accessories\human_horns.dm" #include "modular_doppler\modular_customization\accessories\code\human_accessories\human_tail.dm" @@ -6753,8 +6761,6 @@ #include "modular_doppler\modular_customization\accessories\code\ramatan_accessories\ramatan_frills.dm" #include "modular_doppler\modular_customization\accessories\code\ramatan_accessories\ramatan_snout.dm" #include "modular_doppler\modular_customization\accessories\code\ramatan_accessories\ramatan_tail.dm" -#include "modular_doppler\modular_customization\accessories\code\synthetic_accessories\synth_ears.dm" -#include "modular_doppler\modular_customization\accessories\code\synthetic_accessories\synth_tail.dm" #include "modular_doppler\modular_customization\accessories\code\underwear_accessories\bras.dm" #include "modular_doppler\modular_customization\accessories\code\underwear_accessories\socks.dm" #include "modular_doppler\modular_customization\accessories\code\underwear_accessories\undershirts.dm" @@ -6772,6 +6778,7 @@ #include "modular_doppler\modular_customization\preferences\body_marking_lizard.dm" #include "modular_doppler\modular_customization\preferences\body_marking_moth.dm" #include "modular_doppler\modular_customization\preferences\chat_color.dm" +#include "modular_doppler\modular_customization\preferences\cyber_limbs.dm" #include "modular_doppler\modular_customization\preferences\ears.dm" #include "modular_doppler\modular_customization\preferences\fluff.dm" #include "modular_doppler\modular_customization\preferences\frills.dm" @@ -6822,39 +6829,66 @@ #include "modular_doppler\modular_mapping\code\areas\station.dm" #include "modular_doppler\modular_mapping\code\ruins\icemoon.dm" #include "modular_doppler\modular_medical\code\carbon_update_icons.dm" +#include "modular_doppler\modular_medical\code\medkit.dm" #include "modular_doppler\modular_medical\medical_designs\medical_designs.dm" +#include "modular_doppler\modular_medical\reagents\blood_pack.dm" #include "modular_doppler\modular_medical\reagents\bug_blood.dm" +#include "modular_doppler\modular_medical\reagents\crates.dm" #include "modular_doppler\modular_medical\reagents\medicine.dm" +#include "modular_doppler\modular_medical\reagents\pill.dm" +#include "modular_doppler\modular_medical\reagents\pill_bottles.dm" #include "modular_doppler\modular_medical\reagents\reagents.dm" +#include "modular_doppler\modular_medical\reagents\sprays.dm" #include "modular_doppler\modular_medical\reagents\synth_blood.dm" +#include "modular_doppler\modular_medical\surgery\revival.dm" +#include "modular_doppler\modular_medical\surgery\organs\internal\cyberimp.dm" #include "modular_doppler\modular_medical\wounds\_wounds.dm" #include "modular_doppler\modular_medical\wounds\bleed.dm" #include "modular_doppler\modular_medical\wounds\medical.dm" #include "modular_doppler\modular_medical\wounds\muscle.dm" #include "modular_doppler\modular_medical\wounds\wound_effects.dm" +#include "modular_doppler\modular_medical\wounds\synth\medicine_reagents.dm" +#include "modular_doppler\modular_medical\wounds\synth\robotic_burns.dm" +#include "modular_doppler\modular_medical\wounds\synth\robotic_muscle.dm" +#include "modular_doppler\modular_medical\wounds\synth\robotic_pierce.dm" +#include "modular_doppler\modular_medical\wounds\synth\robotic_slash.dm" +#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt.dm" +#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt_T1.dm" +#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt_T2.dm" +#include "modular_doppler\modular_medical\wounds\synth\blunt\robotic_blunt_T3.dm" +#include "modular_doppler\modular_medical\wounds\synth\blunt\secures_internals.dm" #include "modular_doppler\modular_mob_spawn\code\mob_spawn.dm" #include "modular_doppler\modular_mood\code\mood_events\brushed.dm" #include "modular_doppler\modular_mood\code\mood_events\dog_wag.dm" #include "modular_doppler\modular_mood\code\mood_events\hotspring.dm" #include "modular_doppler\modular_mood\code\mood_events\race_drink.dm" -#include "modular_doppler\modular_quirks\code\good_quirks.dm" -#include "modular_doppler\modular_quirks\code\excitable\quirk.dm" -#include "modular_doppler\modular_quirks\code\overwrites\musician.dm" +#include "modular_doppler\modular_quirks\excitable\excitable.dm" +#include "modular_doppler\modular_quirks\left_handed\left_handed.dm" +#include "modular_doppler\modular_quirks\linguist\linguist.dm" +#include "modular_doppler\modular_quirks\overwrites\musician.dm" #include "modular_doppler\modular_quirks\paycheck_rations\code\quirk.dm" #include "modular_doppler\modular_quirks\paycheck_rations\code\rationpacks.dm" #include "modular_doppler\modular_quirks\paycheck_rations\code\reagents.dm" #include "modular_doppler\modular_quirks\paycheck_rations\code\ticket_book.dm" #include "modular_doppler\modular_quirks\paycheck_rations\code\tickets.dm" +#include "modular_doppler\modular_quirks\permitted_cybernetic\permitted_cybernetic.dm" +#include "modular_doppler\modular_quirks\permitted_cybernetic\code\preferences.dm" #include "modular_doppler\modular_sounds\code\sounds.dm" #include "modular_doppler\modular_species\_species.dm" #include "modular_doppler\modular_species\code\bloodsplatter.dm" -#include "modular_doppler\modular_species\overwrites\code\species.dm" +#include "modular_doppler\modular_species\species_types\android\android.dm" +#include "modular_doppler\modular_species\species_types\android\android_hud.dm" +#include "modular_doppler\modular_species\species_types\android\android_parts.dm" +#include "modular_doppler\modular_species\species_types\android\monitor_component.dm" +#include "modular_doppler\modular_species\species_types\android\power_cord.dm" #include "modular_doppler\modular_species\species_types\anthromorph\anthromorph.dm" #include "modular_doppler\modular_species\species_types\anthromorph\anthromorph_bodyparts.dm" #include "modular_doppler\modular_species\species_types\aquatic\aquatic.dm" #include "modular_doppler\modular_species\species_types\aquatic\aquatic_bodyparts.dm" #include "modular_doppler\modular_species\species_types\ethereal\ethereal.dm" +#include "modular_doppler\modular_species\species_types\flypeople\flypeople.dm" #include "modular_doppler\modular_species\species_types\genemod\genemod.dm" +#include "modular_doppler\modular_species\species_types\genemod\genemod_bodyparts.dm" #include "modular_doppler\modular_species\species_types\golem\golem.dm" #include "modular_doppler\modular_species\species_types\hemophage\_hemophage_defines.dm" #include "modular_doppler\modular_species\species_types\hemophage\_organ_corruption.dm" @@ -6899,6 +6933,8 @@ #include "modular_doppler\modular_vending\code\tg_vendors\megaseed.dm" #include "modular_doppler\modular_vending\code\tg_vendors\wardrobes.dm" #include "modular_doppler\modular_weapons\code\gunsets.dm" +#include "modular_doppler\modular_weapons\code\jousting.dm" +#include "modular_doppler\modular_weapons\code\melee.dm" #include "modular_doppler\modular_weapons\company_and_or_faction_based\carwo_defense_systems\gunsets.dm" #include "modular_doppler\modular_weapons\manufacturer_examine\code\gun_company_additions.dm" #include "modular_doppler\modular_weapons\manufacturer_examine\code\manufacturer_element.dm" @@ -6933,7 +6969,6 @@ #include "modular_doppler\religion\code\mind.dm" #include "modular_doppler\religion\code\religious_sects.dm" #include "modular_doppler\research\designs\limbgrower_designs.dm" -#include "modular_doppler\sprite_accessories\code\hair.dm" #include "modular_doppler\stone\code\ore_veins.dm" #include "modular_doppler\stone\code\stone.dm" #include "modular_doppler\tableflip\tableflip.dm" diff --git a/tgui/packages/tgui/interfaces/CrewConsole.tsx b/tgui/packages/tgui/interfaces/CrewConsole.tsx index 9c15a551bb0ea..fc08d6d4493ab 100644 --- a/tgui/packages/tgui/interfaces/CrewConsole.tsx +++ b/tgui/packages/tgui/interfaces/CrewConsole.tsx @@ -126,6 +126,8 @@ type CrewSensor = { name: string; assignment: string | undefined; ijob: number; + charge: string | undefined; // DOPPLER ADDITION + is_robot: any; // DOPPLER ADDITION life_status: number; oxydam: number; toxdam: number; @@ -229,6 +231,8 @@ const CrewTableEntry = (props: CrewTableEntryProps) => { name, assignment, ijob, + charge, // DOPPLER ADDITION + is_robot, // DOPPLER ADDITION life_status, oxydam, toxdam, @@ -245,6 +249,12 @@ const CrewTableEntry = (props: CrewTableEntryProps) => { {assignment !== undefined ? ` (${assignment})` : ''} + {/* DOPPLER ADDITION START */} + {is_robot ? : ''} + {charge !== undefined ? ` ${charge}` : ''} + + + {/* DOPPLER ADDITION END */} {oxydam !== undefined ? ( = { + name: 'Night vision tint', + component: FeatureColorInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/doppler/photophobia.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/doppler/photophobia.tsx new file mode 100644 index 0000000000000..6cf0afd15714e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/doppler/photophobia.tsx @@ -0,0 +1,8 @@ +// THIS IS A NOVA SECTOR UI FILE +import { FeatureChoiced } from '../../base'; +import { FeatureDropdownInput } from '../../dropdowns'; + +export const photophobia_severity: FeatureChoiced = { + name: 'Severity', + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/implanted.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/implanted.tsx new file mode 100644 index 0000000000000..68788cbd83887 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/implanted.tsx @@ -0,0 +1,7 @@ +import { FeatureChoiced } from '../base'; +import { FeatureDropdownInput } from '../dropdowns'; + +export const permitted_cybernetic: FeatureChoiced = { + name: 'Permitted Cybernetic', + component: FeatureDropdownInput, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_limbs.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_limbs.tsx new file mode 100644 index 0000000000000..a99d032fe2de4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_limbs.tsx @@ -0,0 +1,78 @@ +import { + FeatureChoiced, + FeatureChoicedServerData, + FeatureValueProps, +} from '../base'; +import { FeatureDropdownInput } from '../dropdowns'; + +export const head_type: FeatureChoiced = { + name: 'Add Limb: Head', + description: ` + Add a cybernetic head to your character, this option is exclusive to this species. +`, + component: ( + props: FeatureValueProps, + ) => { + return ; + }, +}; + +export const chest_type: FeatureChoiced = { + name: 'Add Limb: Chest', + description: ` + Add a cybernetic chassis to your character. +`, + component: ( + props: FeatureValueProps, + ) => { + return ; + }, +}; + +export const arm_r_type: FeatureChoiced = { + name: 'Add Limb: R-Arm', + description: ` + Add a cybernetic arm to your character. +`, + component: ( + props: FeatureValueProps, + ) => { + return ; + }, +}; + +export const arm_l_type: FeatureChoiced = { + name: 'Add Limb: L-Arm', + description: ` + Add a cybernetic arm to your character. +`, + component: ( + props: FeatureValueProps, + ) => { + return ; + }, +}; + +export const leg_r_type: FeatureChoiced = { + name: 'Add Limb: R-Leg', + description: ` + Add a cybernetic leg to your character. +`, + component: ( + props: FeatureValueProps, + ) => { + return ; + }, +}; + +export const leg_l_type: FeatureChoiced = { + name: 'Add Limb: L-Leg', + description: ` + Add a cybernetic leg to your character. +`, + component: ( + props: FeatureValueProps, + ) => { + return ; + }, +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx index 1083dfcef89d1..76f0a9f931ead 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/dopplershift_preferences/mutant_tricolors.tsx @@ -115,10 +115,10 @@ export const caps_color: Feature = { }; export const has_markings: FeatureToggle = { - name: 'Add Part: Basic Markings', + name: 'Add Part: Markings', category: 'GAMEPLAY', description: ` - When toggled, adds basic bodymarkings to your character. + When toggled, adds bodymarkings to your character. `, component: CheckboxInput, }; @@ -166,10 +166,10 @@ export const has_moth_wings: FeatureToggle = { }; export const has_antennae: FeatureToggle = { - name: 'Add Part: Antennae', + name: 'Add Part: Moth Antennae', category: 'GAMEPLAY', description: ` - When toggled, adds antennae to your character. + When toggled, adds moff antennae to your character. `, component: CheckboxInput, };