diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index e8904f0bb1f86..6f6a9d209a10e 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -84,6 +84,8 @@
#define BODYTYPE_ALIEN (1<<3)
///The limb is from a golem
#define BODYTYPE_GOLEM (1<<4)
+//The limb is a peg limb
+#define BODYTYPE_PEG (1<<5)
// Bodyshape defines for how things can be worn, i.e., what "shape" the mob sprite is
///The limb fits the human mold. This is not meant to be literal, if the sprite "fits" on a human, it is "humanoid", regardless of origin.
@@ -95,7 +97,7 @@
///The limb is snouted.
#define BODYSHAPE_SNOUTED (1<<3)
-#define BODYTYPE_BIOSCRAMBLE_INCOMPATIBLE (BODYTYPE_ROBOTIC | BODYTYPE_LARVA_PLACEHOLDER | BODYTYPE_GOLEM)
+#define BODYTYPE_BIOSCRAMBLE_INCOMPATIBLE (BODYTYPE_ROBOTIC | BODYTYPE_LARVA_PLACEHOLDER | BODYTYPE_GOLEM | BODYTYPE_PEG)
#define BODYTYPE_CAN_BE_BIOSCRAMBLED(bodytype) (!(bodytype & BODYTYPE_BIOSCRAMBLE_INCOMPATIBLE))
// Defines for Species IDs. Used to refer to the name of a species, for things like bodypart names or species preferences.
@@ -135,6 +137,8 @@
#define BODYPART_ID_LARVA "larva"
#define BODYPART_ID_PSYKER "psyker"
#define BODYPART_ID_MEAT "meat"
+#define BODYPART_ID_PEG "peg"
+
//See: datum/species/var/digitigrade_customization
///The species does not have digitigrade legs in generation.
@@ -631,6 +635,7 @@
// Otherwise they are completely arbitrary
#define MONKEY_HEIGHT_DWARF 2
#define MONKEY_HEIGHT_MEDIUM 4
+#define MONKEY_HEIGHT_TALL HUMAN_HEIGHT_DWARF
#define HUMAN_HEIGHT_DWARF 6
#define HUMAN_HEIGHT_SHORTEST 8
#define HUMAN_HEIGHT_SHORT 10
diff --git a/code/__DEFINES/research.dm b/code/__DEFINES/research.dm
index d3f99314f1d8f..cf35d553ec4dc 100644
--- a/code/__DEFINES/research.dm
+++ b/code/__DEFINES/research.dm
@@ -4,8 +4,15 @@
//! Techweb names for new point types. Can be used to define specific point values for specific types of research (science, security, engineering, etc.)
#define TECHWEB_POINT_TYPE_GENERIC "General Research"
+//! Amount of points required to unlock nodes of corresponding tiers
+#define TECHWEB_TIER_1_POINTS 40
+#define TECHWEB_TIER_2_POINTS 80
+#define TECHWEB_TIER_3_POINTS 120
+#define TECHWEB_TIER_4_POINTS 160
+#define TECHWEB_TIER_5_POINTS 200
+
//! Amount of points gained per second by a single R&D server, see: [research][code/controllers/subsystem/research.dm]
-#define TECHWEB_SINGLE_SERVER_INCOME 52.3
+#define TECHWEB_SINGLE_SERVER_INCOME 1
//! Swab cell line types
#define CELL_LINE_TABLE_SLUDGE "cell_line_sludge_table"
diff --git a/code/__DEFINES/research/slimes.dm b/code/__DEFINES/research/slimes.dm
index f54248a21a3ac..e03c6af8f581d 100644
--- a/code/__DEFINES/research/slimes.dm
+++ b/code/__DEFINES/research/slimes.dm
@@ -61,3 +61,6 @@
#define SLIME_TYPE_SEPIA "sepia"
#define SLIME_TYPE_SILVER "silver"
#define SLIME_TYPE_YELLOW "yellow"
+
+// The alpha value of transperent slime types
+#define SLIME_TRANSPARENCY_ALPHA 180
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index 42e00045761e5..feddc24c6f858 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -33,6 +33,8 @@
#define IS_ORGANIC_LIMB(limb) (limb.bodytype & BODYTYPE_ORGANIC)
/// Helper to figure out if a limb is robotic
#define IS_ROBOTIC_LIMB(limb) (limb.bodytype & BODYTYPE_ROBOTIC)
+/// Helper to figure out if a limb is a peg limb
+#define IS_PEG_LIMB(limb) (limb.bodytype & BODYTYPE_PEG)
// Flags for the bodypart_flags var on /obj/item/bodypart
/// Bodypart cannot be dismembered or amputated
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 17d9008a543b3..8f1c58bc96684 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -138,8 +138,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_AGENDER "agender"
/// Species with this trait have a blood clan mechanic
#define TRAIT_BLOOD_CLANS "blood_clans"
-/// Species with this trait have markings (this SUCKS, remove this later in favor of bodypart overlays)
-#define TRAIT_HAS_MARKINGS "has_markings"
/// Species with this trait use skin tones for coloration
#define TRAIT_USES_SKINTONES "uses_skintones"
/// Species with this trait use mutant colors for coloration
@@ -366,6 +364,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_GAMERGOD "gamer-god"
#define TRAIT_GIANT "giant"
#define TRAIT_DWARF "dwarf"
+/// Makes you way too tall. Like just too much, dude, it's kind of creepy. Humanoid only.
+#define TRAIT_TOO_TALL "too_tall"
/// makes your footsteps completely silent
#define TRAIT_SILENT_FOOTSTEPS "silent_footsteps"
/// hnnnnnnnggggg..... you're pretty good....
@@ -1153,5 +1153,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// This item cannot be selected for or used by a theft objective (Spies, Traitors, etc.)
#define TRAIT_ITEM_OBJECTIVE_BLOCKED "item_objective_blocked"
+/// This trait lets you attach limbs to any player without surgery.
+#define TRAIT_EASY_ATTACH "easy_attach"
// END TRAIT DEFINES
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 67f33d0460e47..ead7b324cea13 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -390,6 +390,7 @@ DEFINE_BITFIELD(bodytype, list(
"BODYTYPE_LARVA_PLACEHOLDER" = BODYTYPE_LARVA_PLACEHOLDER,
"BODYTYPE_ALIEN" = BODYTYPE_ALIEN,
"BODYTYPE_GOLEM" = BODYTYPE_GOLEM,
+ "BODYTYPE_PEG" = BODYTYPE_PEG,
))
DEFINE_BITFIELD(acceptable_bodytype, list(
@@ -398,6 +399,7 @@ DEFINE_BITFIELD(acceptable_bodytype, list(
"BODYTYPE_LARVA_PLACEHOLDER" = BODYTYPE_LARVA_PLACEHOLDER,
"BODYTYPE_ALIEN" = BODYTYPE_ALIEN,
"BODYTYPE_GOLEM" = BODYTYPE_GOLEM,
+ "BODYTYPE_PEG" = BODYTYPE_PEG,
))
DEFINE_BITFIELD(bodyshape, list(
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 1fe465da39f0d..9175b86dc3589 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -248,7 +248,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_HARDLY_WOUNDED" = TRAIT_HARDLY_WOUNDED,
"TRAIT_HAS_BEEN_KIDNAPPED" = TRAIT_HAS_BEEN_KIDNAPPED,
"TRAIT_HAS_CRANIAL_FISSURE" = TRAIT_HAS_CRANIAL_FISSURE,
- "TRAIT_HAS_MARKINGS" = TRAIT_HAS_MARKINGS,
"TRAIT_HATED_BY_DOGS" = TRAIT_HATED_BY_DOGS,
"TRAIT_HEAD_INJURY_BLOCKED" = TRAIT_HEAD_INJURY_BLOCKED,
"TRAIT_HEALS_FROM_CARP_RIFTS" = TRAIT_HEALS_FROM_CARP_RIFTS,
@@ -465,6 +464,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_THINKING_IN_CHARACTER" = TRAIT_THINKING_IN_CHARACTER,
"TRAIT_THROWINGARM" = TRAIT_THROWINGARM,
"TRAIT_TIME_STOP_IMMUNE" = TRAIT_TIME_STOP_IMMUNE,
+ "TRAIT_TOO_TALL" = TRAIT_TOO_TALL,
"TRAIT_TOWER_OF_BABEL" = TRAIT_TOWER_OF_BABEL,
"TRAIT_TOXIMMUNE" = TRAIT_TOXIMMUNE,
"TRAIT_TOXINLOVER" = TRAIT_TOXINLOVER,
@@ -548,6 +548,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
/obj/item/bodypart = list(
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS,
),
+ /obj/item/bodypart = list(
+ "TRAIT_EASY_ATTACH" = TRAIT_EASY_ATTACH,
+ ),
/obj/item/card/id = list(
"TRAIT_JOB_FIRST_ID_CARD" = TRAIT_JOB_FIRST_ID_CARD,
"TRAIT_MAGNETIC_ID_CARD" = TRAIT_MAGNETIC_ID_CARD,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index d9f6350a07480..150a10c108494 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -94,7 +94,6 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_GUNFLIP" = TRAIT_GUNFLIP,
"TRAIT_HANDS_BLOCKED" = TRAIT_HANDS_BLOCKED,
"TRAIT_HARDLY_WOUNDED" = TRAIT_HARDLY_WOUNDED,
- "TRAIT_HAS_MARKINGS" = TRAIT_HAS_MARKINGS,
"TRAIT_HEAVY_SLEEPER" = TRAIT_HEAVY_SLEEPER,
"TRAIT_HIDE_EXTERNAL_ORGANS" = TRAIT_HIDE_EXTERNAL_ORGANS,
"TRAIT_HOLY" = TRAIT_HOLY,
diff --git a/code/controllers/subsystem/sprite_accessories.dm b/code/controllers/subsystem/sprite_accessories.dm
index ec5934ac8e8d4..f381df80df6da 100644
--- a/code/controllers/subsystem/sprite_accessories.dm
+++ b/code/controllers/subsystem/sprite_accessories.dm
@@ -35,7 +35,7 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
var/list/socks_list //! stores /datum/sprite_accessory/socks indexed by name
//Lizard Bits (all datum lists indexed by name)
- var/list/body_markings_list
+ var/list/lizard_markings_list
var/list/snouts_list
var/list/horns_list
var/list/frills_list
@@ -87,7 +87,7 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
socks_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/socks)[DEFAULT_SPRITE_LIST]
- body_markings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings)[DEFAULT_SPRITE_LIST]
+ lizard_markings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/lizard_markings)[DEFAULT_SPRITE_LIST]
tails_list_human = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tails_list_lizard = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
tails_list_monkey = init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/monkey, add_blank = TRUE)[DEFAULT_SPRITE_LIST]
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 468882f0e86ec..efca8dd19b5cc 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -697,6 +697,13 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, span_boldannounce("Rebooting World in [DisplayTimeText(delay)]. [reason]"))
+ var/statspage = CONFIG_GET(string/roundstatsurl)
+ var/gamelogloc = CONFIG_GET(string/gamelogurl)
+ if(statspage)
+ to_chat(world, span_info("Round statistics and logs can be viewed at this website!"))
+ else if(gamelogloc)
+ to_chat(world, span_info("Round logs can be located at this website!"))
+
var/start_wait = world.time
UNTIL(round_end_sound_sent || (world.time - start_wait) > (delay * 2)) //don't wait forever
sleep(delay - (world.time - start_wait))
@@ -707,13 +714,6 @@ SUBSYSTEM_DEF(ticker)
if(end_string)
end_state = end_string
- var/statspage = CONFIG_GET(string/roundstatsurl)
- var/gamelogloc = CONFIG_GET(string/gamelogurl)
- if(statspage)
- to_chat(world, span_info("Round statistics and logs can be viewed at this website!"))
- else if(gamelogloc)
- to_chat(world, span_info("Round logs can be located at this website!"))
-
log_game(span_boldannounce("Rebooting World. [reason]"))
world.Reboot()
diff --git a/code/datums/bodypart_overlays/markings_bodypart_overlay.dm b/code/datums/bodypart_overlays/markings_bodypart_overlay.dm
new file mode 100644
index 0000000000000..c2c6f54d861d3
--- /dev/null
+++ b/code/datums/bodypart_overlays/markings_bodypart_overlay.dm
@@ -0,0 +1,31 @@
+/// For body markings applied on the species, which need some extra code
+/datum/bodypart_overlay/simple/body_marking
+ layers = EXTERNAL_ADJACENT
+ /// Listen to the gendercode, if the limb is bimorphic
+ var/use_gender = FALSE
+ /// Which dna feature key to draw from
+ var/dna_feature_key
+ /// Which bodyparts do we apply ourselves to?
+ var/list/applies_to = list(/obj/item/bodypart/head, /obj/item/bodypart/chest, /obj/item/bodypart/arm/left, /obj/item/bodypart/arm/right, \
+ /obj/item/bodypart/leg/left, /obj/item/bodypart/leg/right)
+
+/// Get the accessory list from SSaccessories. Used in species.dm to get the right sprite
+/datum/bodypart_overlay/simple/body_marking/proc/get_accessory(name)
+ CRASH("get_accessories() not overriden on [type] !")
+
+/datum/bodypart_overlay/simple/body_marking/get_image(layer, obj/item/bodypart/limb)
+ var/gender_string = (use_gender && limb.is_dimorphic) ? (limb.gender == MALE ? MALE : FEMALE + "_") : "" //we only got male and female sprites
+ return image(icon, gender_string + icon_state + "_" + limb.body_zone, layer = layer)
+
+/datum/bodypart_overlay/simple/body_marking/moth
+ dna_feature_key = "moth_markings"
+
+/datum/bodypart_overlay/simple/body_marking/moth/get_accessory(name)
+ return SSaccessories.moth_markings_list[name]
+
+/datum/bodypart_overlay/simple/body_marking/lizard
+ dna_feature_key = "lizard_markings"
+ applies_to = list(/obj/item/bodypart/chest)
+
+/datum/bodypart_overlay/simple/body_marking/lizard/get_accessory(name)
+ return SSaccessories.lizard_markings_list[name]
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 75fb406e3371e..d5677dad7ca61 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -207,8 +207,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
L[DNA_MUTANT_COLOR_BLOCK] = sanitize_hexcolor(features["mcolor"], include_crunch = FALSE)
if(features["ethcolor"])
L[DNA_ETHEREAL_COLOR_BLOCK] = sanitize_hexcolor(features["ethcolor"], include_crunch = FALSE)
- if(features["body_markings"])
- L[DNA_LIZARD_MARKINGS_BLOCK] = construct_block(SSaccessories.body_markings_list.Find(features["body_markings"]), length(SSaccessories.body_markings_list))
+ if(features["lizard_markings"])
+ L[DNA_LIZARD_MARKINGS_BLOCK] = construct_block(SSaccessories.lizard_markings_list.Find(features["lizard_markings"]), length(SSaccessories.lizard_markings_list))
if(features["tail_cat"])
L[DNA_TAIL_BLOCK] = construct_block(SSaccessories.tails_list_human.Find(features["tail_cat"]), length(SSaccessories.tails_list_human))
if(features["tail_lizard"])
@@ -354,7 +354,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(DNA_ETHEREAL_COLOR_BLOCK)
set_uni_feature_block(blocknumber, sanitize_hexcolor(features["ethcolor"], include_crunch = FALSE))
if(DNA_LIZARD_MARKINGS_BLOCK)
- set_uni_feature_block(blocknumber, construct_block(SSaccessories.body_markings_list.Find(features["body_markings"]), length(SSaccessories.body_markings_list)))
+ set_uni_feature_block(blocknumber, construct_block(SSaccessories.lizard_markings_list.Find(features["lizard_markings"]), length(SSaccessories.lizard_markings_list)))
if(DNA_TAIL_BLOCK)
set_uni_feature_block(blocknumber, construct_block(SSaccessories.tails_list_human.Find(features["tail_cat"]), length(SSaccessories.tails_list_human)))
if(DNA_LIZARD_TAIL_BLOCK)
@@ -423,7 +423,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
/datum/dna/proc/update_instability(alert=TRUE)
stability = 100
for(var/datum/mutation/human/M in mutations)
- if(M.class == MUT_EXTRA)
+ if(M.class == MUT_EXTRA || M.instability < 0)
stability -= M.instability * GET_MUTATION_STABILIZER(M)
if(holder)
var/message
@@ -649,8 +649,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
dna.features["mcolor"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_MUTANT_COLOR_BLOCK))
if(dna.features["ethcolor"])
dna.features["ethcolor"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_ETHEREAL_COLOR_BLOCK))
- if(dna.features["body_markings"])
- dna.features["body_markings"] = SSaccessories.body_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_MARKINGS_BLOCK), length(SSaccessories.body_markings_list))]
+ if(dna.features["lizard_markings"])
+ dna.features["lizard_markings"] = SSaccessories.lizard_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_MARKINGS_BLOCK), length(SSaccessories.lizard_markings_list))]
if(dna.features["snout"])
dna.features["snout"] = SSaccessories.snouts_list[deconstruct_block(get_uni_feature_block(features, DNA_SNOUT_BLOCK), length(SSaccessories.snouts_list))]
if(dna.features["horns"])
@@ -686,14 +686,10 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
external_organ.mutate_feature(features, src)
if(icon_update)
- if(mutcolor_update)
- update_body(is_creating = TRUE)
- else
- update_body()
+ update_body(is_creating = mutcolor_update)
if(mutations_overlay_update)
update_mutations_overlay()
-
/mob/proc/domutcheck()
return
diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm
index 4913a057e011a..95f59ade38c39 100644
--- a/code/datums/mutations/_mutations.dm
+++ b/code/datums/mutations/_mutations.dm
@@ -1,3 +1,23 @@
+
+/// Negatives that are virtually harmless and mostly just funny (language)
+// Set to 0 because munchkinning via miscommunication = bad
+#define NEGATIVE_STABILITY_MINI 0
+/// Negatives that are slightly annoying (unused)
+#define NEGATIVE_STABILITY_MINOR -20
+/// Negatives that present an uncommon or weak, consistent hindrance to gameplay (cough, paranoia)
+#define NEGATIVE_STABILITY_MODERATE -30
+/// Negatives that present a major consistent hindrance to gameplay (deaf, mute, acid flesh)
+#define NEGATIVE_STABILITY_MAJOR -40
+
+/// Positives that provide basically no benefit (glowy)
+#define POSITIVE_INSTABILITY_MINI 5
+/// Positives that are niche in application or useful in rare circumstances (parlor tricks, geladikinesis, autotomy)
+#define POSITIVE_INSTABILITY_MINOR 10
+/// Positives that provide a new ability that's roughly par with station equipment (insulated, cryokinesis)
+#define POSITIVE_INSTABILITY_MODERATE 25
+/// Positives that are unique, very powerful, and noticeably change combat/gameplay (hulk, tk)
+#define POSITIVE_INSTABILITY_MAJOR 35
+
/datum/mutation
var/name
diff --git a/code/datums/mutations/adaptation.dm b/code/datums/mutations/adaptation.dm
index 1c183f9367d3a..2cac87dd4b4c4 100644
--- a/code/datums/mutations/adaptation.dm
+++ b/code/datums/mutations/adaptation.dm
@@ -4,7 +4,7 @@
quality = POSITIVE
difficulty = 16
text_gain_indication = "Your body feels warm!"
- instability = 25
+ instability = POSITIVE_INSTABILITY_MAJOR
conflicts = list(/datum/mutation/human/pressure_adaptation)
/datum/mutation/human/temperature_adaptation/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
@@ -31,7 +31,7 @@
quality = POSITIVE
difficulty = 16
text_gain_indication = "Your body feels numb!"
- instability = 25
+ instability = POSITIVE_INSTABILITY_MAJOR
conflicts = list(/datum/mutation/human/temperature_adaptation)
/datum/mutation/human/pressure_adaptation/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm
index 80edceea1e5ed..fc1a94276f702 100644
--- a/code/datums/mutations/antenna.dm
+++ b/code/datums/mutations/antenna.dm
@@ -4,7 +4,7 @@
quality = POSITIVE
text_gain_indication = "You feel an antenna sprout from your forehead."
text_lose_indication = "Your antenna shrinks back down."
- instability = 5
+ instability = POSITIVE_INSTABILITY_MINOR
difficulty = 8
var/datum/weakref/radio_weakref
@@ -47,7 +47,7 @@
text_gain_indication = "You hear distant voices at the corners of your mind."
text_lose_indication = "The distant voices fade."
power_path = /datum/action/cooldown/spell/pointed/mindread
- instability = 40
+ instability = POSITIVE_INSTABILITY_MINOR
difficulty = 8
locked = TRUE
diff --git a/code/datums/mutations/autotomy.dm b/code/datums/mutations/autotomy.dm
index 5a70455db5fb8..bb78ceb08dcf7 100644
--- a/code/datums/mutations/autotomy.dm
+++ b/code/datums/mutations/autotomy.dm
@@ -3,7 +3,7 @@
desc = "Allows a creature to voluntary discard a random appendage."
quality = POSITIVE
text_gain_indication = span_notice("Your joints feel loose.")
- instability = 30
+ instability = POSITIVE_INSTABILITY_MINOR
power_path = /datum/action/cooldown/spell/self_amputation
energy_coeff = 1
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index 896820746d61d..eaa1ba1f5cafa 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -4,6 +4,7 @@
/datum/mutation/human/epilepsy
name = "Epilepsy"
desc = "A genetic defect that sporadically causes seizures."
+ instability = NEGATIVE_STABILITY_MODERATE
quality = NEGATIVE
text_gain_indication = "You get a headache."
synchronizer_coeff = 1
@@ -50,6 +51,7 @@
/datum/mutation/human/bad_dna
name = "Unstable DNA"
desc = "Strange mutation that causes the holder to randomly mutate."
+ instability = NEGATIVE_STABILITY_MAJOR
quality = NEGATIVE
text_gain_indication = "You feel strange."
locked = TRUE
@@ -79,6 +81,7 @@
/datum/mutation/human/cough
name = "Cough"
desc = "A chronic cough."
+ instability = NEGATIVE_STABILITY_MODERATE
quality = MINOR_NEGATIVE
text_gain_indication = "You start coughing."
synchronizer_coeff = 1
@@ -96,6 +99,7 @@
/datum/mutation/human/paranoia
name = "Paranoia"
desc = "Subject is easily terrified, and may suffer from hallucinations."
+ instability = NEGATIVE_STABILITY_MODERATE
quality = NEGATIVE
text_gain_indication = "You feel screams echo through your mind..."
text_lose_indication = "The screaming in your mind fades."
@@ -112,8 +116,8 @@
desc = "A mutation believed to be the cause of dwarfism."
quality = POSITIVE
difficulty = 16
- instability = 5
- conflicts = list(/datum/mutation/human/gigantism)
+ instability = POSITIVE_INSTABILITY_MINOR
+ conflicts = list(/datum/mutation/human/gigantism, /datum/mutation/human/acromegaly)
locked = TRUE // Default intert species for now, so locked from regular pool.
/datum/mutation/human/dwarfism/on_acquiring(mob/living/carbon/human/owner)
@@ -128,10 +132,71 @@
REMOVE_TRAIT(owner, TRAIT_DWARF, GENETIC_MUTATION)
owner.visible_message(span_danger("[owner] suddenly grows!"), span_notice("Everything around you seems to shrink.."))
+/datum/mutation/human/acromegaly
+ name = "Acromegaly"
+ desc = "A mutation believed to be the cause of acromegaly, or 'being unusually tall'."
+ quality = MINOR_NEGATIVE
+ difficulty = 16
+ instability = NEGATIVE_STABILITY_MODERATE
+ synchronizer_coeff = 1
+ conflicts = list(/datum/mutation/human/dwarfism)
+
+/datum/mutation/human/acromegaly/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ ADD_TRAIT(owner, TRAIT_TOO_TALL, GENETIC_MUTATION)
+ owner.visible_message(span_danger("[owner] suddenly grows tall!"), span_notice("You feel a small strange urge to fight small men with slingshots. Or maybe play some basketball."))
+ RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(head_bonk))
+ owner.regenerate_icons()
+
+/datum/mutation/human/acromegaly/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ REMOVE_TRAIT(owner, TRAIT_TOO_TALL, GENETIC_MUTATION)
+ owner.visible_message(span_danger("[owner] suddenly shrinks!"), span_notice("You return to your usual height."))
+ UnregisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(head_bonk))
+ owner.regenerate_icons()
+
+// This is specifically happening because they're not used to their new height and are stumbling around into machinery made for normal humans
+/datum/mutation/human/acromegaly/proc/head_bonk(mob/living/parent)
+ SIGNAL_HANDLER
+ var/turf/airlock_turf = get_turf(parent)
+ var/atom/movable/whacked_by = locate(/obj/machinery/door/airlock) in airlock_turf || locate(/obj/machinery/door/firedoor) in airlock_turf || locate(/obj/structure/mineral_door) in airlock_turf
+ if(!whacked_by || prob(100 - (8 * GET_MUTATION_SYNCHRONIZER(src))))
+ return
+ to_chat(parent, span_danger("You hit your head on \the [whacked_by]'s header!"))
+ var/dmg = HAS_TRAIT(parent, TRAIT_HEAD_INJURY_BLOCKED) ? rand(1,4) : rand(2,9)
+ parent.apply_damage(dmg, BRUTE, BODY_ZONE_HEAD)
+ parent.do_attack_animation(whacked_by, ATTACK_EFFECT_PUNCH)
+ playsound(whacked_by, 'sound/effects/bang.ogg', 10, TRUE)
+ parent.adjust_staggered_up_to(STAGGERED_SLOWDOWN_LENGTH, 10 SECONDS)
+
+/datum/mutation/human/gigantism
+ name = "Gigantism" //negative version of dwarfism
+ desc = "The cells within the subject spread out to cover more area, making the subject appear larger."
+ quality = MINOR_NEGATIVE
+ difficulty = 12
+ conflicts = list(/datum/mutation/human/dwarfism)
+
+/datum/mutation/human/gigantism/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ ADD_TRAIT(owner, TRAIT_GIANT, GENETIC_MUTATION)
+ owner.update_transform(1.25)
+ owner.visible_message(span_danger("[owner] suddenly grows!"), span_notice("Everything around you seems to shrink.."))
+
+/datum/mutation/human/gigantism/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ REMOVE_TRAIT(owner, TRAIT_GIANT, GENETIC_MUTATION)
+ owner.update_transform(0.8)
+ owner.visible_message(span_danger("[owner] suddenly shrinks!"), span_notice("Everything around you seems to grow.."))
+
//Clumsiness has a very large amount of small drawbacks depending on item.
/datum/mutation/human/clumsy
name = "Clumsiness"
desc = "A genome that inhibits certain brain functions, causing the holder to appear clumsy. Honk!"
+ instability = NEGATIVE_STABILITY_MAJOR
quality = MINOR_NEGATIVE
text_gain_indication = "You feel lightheaded."
@@ -151,6 +216,7 @@
name = "Tourette's Syndrome"
desc = "A chronic twitch that forces the user to scream bad words." //definitely needs rewriting
quality = NEGATIVE
+ instability = 0
text_gain_indication = "You twitch."
synchronizer_coeff = 1
@@ -173,6 +239,7 @@
/datum/mutation/human/deaf
name = "Deafness"
desc = "The holder of this genome is completely deaf."
+ instability = NEGATIVE_STABILITY_MAJOR
quality = NEGATIVE
text_gain_indication = "You can't seem to hear anything."
@@ -194,6 +261,7 @@
text_gain_indication = "You feel unusually monkey-like."
text_lose_indication = "You feel like your old self."
quality = NEGATIVE
+ instability = NEGATIVE_STABILITY_MAJOR // mmmonky
remove_on_aheal = FALSE
locked = TRUE //Species specific, keep out of actual gene pool
mutadone_proof = TRUE
@@ -219,7 +287,7 @@
desc = "You permanently emit a light with a random color and intensity."
quality = POSITIVE
text_gain_indication = "Your skin begins to glow softly."
- instability = 5
+ instability = POSITIVE_INSTABILITY_MINI
power_coeff = 1
conflicts = list(/datum/mutation/human/glow/anti)
var/glow_power = 2
@@ -257,6 +325,7 @@
desc = "Your skin seems to attract and absorb nearby light creating 'darkness' around you."
text_gain_indication = "The light around you seems to disappear."
conflicts = list(/datum/mutation/human/glow)
+ instability = POSITIVE_INSTABILITY_MINOR
locked = TRUE
glow_power = -1.5
@@ -265,10 +334,10 @@
/datum/mutation/human/strong
name = "Strength"
- desc = "The user's muscles slightly expand."
+ desc = "The user's muscles slightly expand. Commonly seen in top-ranking boxers."
quality = POSITIVE
text_gain_indication = "You feel strong."
- instability = 5
+ instability = POSITIVE_INSTABILITY_MINI
difficulty = 16
/datum/mutation/human/strong/on_acquiring(mob/living/carbon/human/owner)
@@ -286,10 +355,11 @@
/datum/mutation/human/stimmed
name = "Stimmed"
- desc = "The user's chemical balance is more robust."
+ desc = "The user's chemical balance is more robust. This mutation is known to slightly improve workout efficiency."
quality = POSITIVE
+ instability = POSITIVE_INSTABILITY_MINI
text_gain_indication = "You feel stimmed."
- instability = 5
+ instability = 15
difficulty = 16
/datum/mutation/human/stimmed/on_acquiring(mob/living/carbon/human/owner)
@@ -311,7 +381,7 @@
text_gain_indication = "Your fingertips go numb."
text_lose_indication = "Your fingertips regain feeling."
difficulty = 16
- instability = 25
+ instability = POSITIVE_INSTABILITY_MODERATE
/datum/mutation/human/insulated/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -355,7 +425,7 @@
text_gain_indication = "The space around you twists sickeningly."
text_lose_indication = "The space around you settles back to normal."
difficulty = 18//high so it's hard to unlock and abuse
- instability = 10
+ instability = NEGATIVE_STABILITY_MODERATE
synchronizer_coeff = 1
energy_coeff = 1
power_coeff = 1
@@ -381,6 +451,7 @@
/datum/mutation/human/acidflesh
name = "Acidic Flesh"
desc = "Subject has acidic chemicals building up underneath the skin. This is often lethal."
+ instability = NEGATIVE_STABILITY_MAJOR
quality = NEGATIVE
text_gain_indication = "A horrible burning sensation envelops you as your flesh turns to acid!"
text_lose_indication = "A feeling of relief fills you as your flesh goes back to normal."
@@ -398,30 +469,10 @@
owner.visible_message(span_warning("[owner]'s skin bubbles and pops."), span_userdanger("Your bubbling flesh pops! It burns!"))
playsound(owner,'sound/weapons/sear.ogg', 50, TRUE)
-/datum/mutation/human/gigantism
- name = "Gigantism"//negative version of dwarfism
- desc = "The cells within the subject spread out to cover more area, making the subject appear larger."
- quality = MINOR_NEGATIVE
- difficulty = 12
- conflicts = list(/datum/mutation/human/dwarfism)
-
-/datum/mutation/human/gigantism/on_acquiring(mob/living/carbon/human/owner)
- if(..())
- return
- ADD_TRAIT(owner, TRAIT_GIANT, GENETIC_MUTATION)
- owner.update_transform(1.25)
- owner.visible_message(span_danger("[owner] suddenly grows!"), span_notice("Everything around you seems to shrink.."))
-
-/datum/mutation/human/gigantism/on_losing(mob/living/carbon/human/owner)
- if(..())
- return
- REMOVE_TRAIT(owner, TRAIT_GIANT, GENETIC_MUTATION)
- owner.update_transform(0.8)
- owner.visible_message(span_danger("[owner] suddenly shrinks!"), span_notice("Everything around you seems to grow.."))
-
/datum/mutation/human/spastic
name = "Spastic"
desc = "Subject suffers from muscle spasms."
+ instability = NEGATIVE_STABILITY_MODERATE
quality = NEGATIVE
text_gain_indication = "You flinch."
text_lose_indication = "Your flinching subsides."
@@ -440,6 +491,7 @@
/datum/mutation/human/extrastun
name = "Two Left Feet"
desc = "A mutation that replaces the right foot with another left foot. Symptoms include kissing the floor when taking a step."
+ instability = NEGATIVE_STABILITY_MODERATE
quality = NEGATIVE
text_gain_indication = "Your right foot feels... left."
text_lose_indication = "Your right foot feels alright."
@@ -471,6 +523,7 @@
/datum/mutation/human/martyrdom
name = "Internal Martyrdom"
desc = "A mutation that makes the body destruct when near death. Not damaging, but very, VERY disorienting."
+ instability = NEGATIVE_STABILITY_MAJOR // free stability >:)
locked = TRUE
quality = POSITIVE //not that cloning will be an option a lot but generally lets keep this around i guess?
text_gain_indication = "You get an intense feeling of heartburn."
@@ -518,6 +571,7 @@
/datum/mutation/human/headless
name = "H.A.R.S."
desc = "A mutation that makes the body reject the head, the brain receding into the chest. Stands for Head Allergic Rejection Syndrome. Warning: Removing this mutation is very dangerous, though it will regenerate non-vital head organs."
+ instability = NEGATIVE_STABILITY_MAJOR
difficulty = 12 //pretty good for traitors
quality = NEGATIVE //holy shit no eyes or tongue or ears
text_gain_indication = "Something feels off."
diff --git a/code/datums/mutations/chameleon.dm b/code/datums/mutations/chameleon.dm
index d5cbc36d20a1f..e3ce8826a9438 100644
--- a/code/datums/mutations/chameleon.dm
+++ b/code/datums/mutations/chameleon.dm
@@ -6,7 +6,7 @@
difficulty = 16
text_gain_indication = "You feel one with your surroundings."
text_lose_indication = "You feel oddly exposed."
- instability = 25
+ instability = POSITIVE_INSTABILITY_MAJOR
power_coeff = 1
/datum/mutation/human/chameleon/on_acquiring(mob/living/carbon/human/owner)
diff --git a/code/datums/mutations/cold.dm b/code/datums/mutations/cold.dm
index 57c4f854fc7a2..32e162bf7d3a0 100644
--- a/code/datums/mutations/cold.dm
+++ b/code/datums/mutations/cold.dm
@@ -3,7 +3,7 @@
desc = "Allows the user to concentrate moisture and sub-zero forces into snow."
quality = POSITIVE
text_gain_indication = "Your hand feels cold."
- instability = 10
+ instability = POSITIVE_INSTABILITY_MINOR
difficulty = 10
synchronizer_coeff = 1
power_path = /datum/action/cooldown/spell/conjure_item/snow
@@ -25,7 +25,7 @@
desc = "Draws negative energy from the sub-zero void to freeze surrounding temperatures at subject's will."
quality = POSITIVE //upsides and downsides
text_gain_indication = "Your hand feels cold."
- instability = 30
+ instability = POSITIVE_INSTABILITY_MODERATE
difficulty = 12
synchronizer_coeff = 1
energy_coeff = 1
diff --git a/code/datums/mutations/fire_breath.dm b/code/datums/mutations/fire_breath.dm
index f8631761ba202..5836997729456 100644
--- a/code/datums/mutations/fire_breath.dm
+++ b/code/datums/mutations/fire_breath.dm
@@ -7,7 +7,7 @@
text_gain_indication = "Your throat is burning!"
text_lose_indication = "Your throat is cooling down."
power_path = /datum/action/cooldown/spell/cone/staggered/fire_breath
- instability = 30
+ instability = POSITIVE_INSTABILITY_MODERATE
energy_coeff = 1
power_coeff = 1
diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm
index 63e0abc22a33f..3fdc5de52ce2b 100644
--- a/code/datums/mutations/hulk.dm
+++ b/code/datums/mutations/hulk.dm
@@ -8,7 +8,7 @@
text_gain_indication = "Your muscles hurt!"
species_allowed = list(SPECIES_HUMAN) //no skeleton/lizard hulk
health_req = 25
- instability = 40
+ instability = POSITIVE_INSTABILITY_MAJOR
var/scream_delay = 50
var/last_scream = 0
/// List of traits to add/remove when someone gets this mutation.
diff --git a/code/datums/mutations/olfaction.dm b/code/datums/mutations/olfaction.dm
index 305f6d16e8389..f487702a3c6c8 100644
--- a/code/datums/mutations/olfaction.dm
+++ b/code/datums/mutations/olfaction.dm
@@ -6,7 +6,7 @@
text_gain_indication = "Smells begin to make more sense..."
text_lose_indication = "Your sense of smell goes back to normal."
power_path = /datum/action/cooldown/spell/olfaction
- instability = 30
+ instability = POSITIVE_INSTABILITY_MODERATE
synchronizer_coeff = 1
/datum/mutation/human/olfaction/modify()
@@ -119,6 +119,9 @@
/// Actually go through and give the user a hint of the direction our target is.
/datum/action/cooldown/spell/olfaction/proc/on_the_trail(mob/living/caster)
var/mob/living/carbon/current_target = tracking_ref?.resolve()
+ //Using get_turf to deal with those pesky closets that put your x y z to 0
+ var/turf/current_target_turf = get_turf(current_target)
+ var/turf/caster_turf = get_turf(caster)
if(!current_target)
to_chat(caster, span_warning("You're not tracking a scent, but the game thought you were. \
Something's gone wrong! Report this as a bug."))
@@ -130,14 +133,14 @@
to_chat(caster, span_warning("You smell out the trail to yourself. Yep, it's you."))
return
- if(caster.z < current_target.z)
+ if(caster_turf.z < current_target_turf.z)
to_chat(caster, span_warning("The trail leads... way up above you? Huh. They must be really, really far away."))
return
- else if(caster.z > current_target.z)
+ else if(caster_turf.z > current_target_turf.z)
to_chat(caster, span_warning("The trail leads... way down below you? Huh. They must be really, really far away."))
return
- var/direction_text = span_bold("[dir2text(get_dir(caster, current_target))]")
+ var/direction_text = span_bold("[dir2text(get_dir(caster_turf, current_target_turf))]")
if(direction_text)
to_chat(caster, span_notice("You consider [current_target]'s scent. The trail leads [direction_text]."))
diff --git a/code/datums/mutations/passive.dm b/code/datums/mutations/passive.dm
index 9d694aaf1ebbb..14135fe426ea4 100644
--- a/code/datums/mutations/passive.dm
+++ b/code/datums/mutations/passive.dm
@@ -2,7 +2,7 @@
name = "Biotech Compatibility"
desc = "Subject is more compatibile with biotechnology such as skillchips."
quality = POSITIVE
- instability = 5
+ instability = POSITIVE_INSTABILITY_MINI
/datum/mutation/human/biotechcompat/on_acquiring(mob/living/carbon/human/owner)
. = ..()
@@ -16,7 +16,7 @@
name = "Clever"
desc = "Causes the subject to feel just a little bit smarter. Most effective in specimens with low levels of intelligence."
quality = POSITIVE
- instability = 20
+ instability = POSITIVE_INSTABILITY_MODERATE // literally makes you on par with station equipment
text_gain_indication = "You feel a little bit smarter."
text_lose_indication = "Your mind feels a little bit foggy."
diff --git a/code/datums/mutations/radioactive.dm b/code/datums/mutations/radioactive.dm
index 8f710bfa497a4..8700e405662a6 100644
--- a/code/datums/mutations/radioactive.dm
+++ b/code/datums/mutations/radioactive.dm
@@ -3,7 +3,7 @@
desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings."
quality = NEGATIVE
text_gain_indication = "You can feel it in your bones!"
- instability = 5
+ instability = NEGATIVE_STABILITY_MAJOR
difficulty = 8
power_coeff = 1
/// Weakref to our radiation emitter component
diff --git a/code/datums/mutations/sight.dm b/code/datums/mutations/sight.dm
index 8b26f6ca268f0..66e307c247846 100644
--- a/code/datums/mutations/sight.dm
+++ b/code/datums/mutations/sight.dm
@@ -2,6 +2,7 @@
/datum/mutation/human/nearsight
name = "Near Sightness"
desc = "The holder of this mutation has poor eyesight."
+ instability = NEGATIVE_STABILITY_MODERATE
quality = MINOR_NEGATIVE
text_gain_indication = "You can't see very well."
@@ -19,6 +20,7 @@
/datum/mutation/human/blind
name = "Blindness"
desc = "Renders the subject completely blind."
+ instability = NEGATIVE_STABILITY_MAJOR
quality = NEGATIVE
text_gain_indication = "You can't seem to see anything."
@@ -40,7 +42,7 @@
difficulty = 18
text_gain_indication = "You can see the heat rising off of your skin..."
text_lose_indication = "You can no longer see the heat rising off of your skin..."
- instability = 25
+ instability = POSITIVE_INSTABILITY_MAJOR // thermals aren't station equipment
synchronizer_coeff = 1
power_coeff = 1
energy_coeff = 1
@@ -110,7 +112,7 @@
name = "X Ray Vision"
desc = "A strange genome that allows the user to see between the spaces of walls." //actual x-ray would mean you'd constantly be blasting rads, wich might be fun for later //hmb
text_gain_indication = "The walls suddenly disappear!"
- instability = 35
+ instability = POSITIVE_INSTABILITY_MAJOR
locked = TRUE
/datum/mutation/human/xray/on_acquiring(mob/living/carbon/human/owner)
@@ -182,6 +184,7 @@
/datum/mutation/human/illiterate
name = "Illiterate"
desc = "Causes a severe case of Aphasia that prevents reading or writing."
+ instability = NEGATIVE_STABILITY_MAJOR
quality = NEGATIVE
text_gain_indication = "You feel unable to read or write."
text_lose_indication = "You feel able to read and write again."
diff --git a/code/datums/mutations/speech.dm b/code/datums/mutations/speech.dm
index 4c78c19610439..1400503dfc45d 100644
--- a/code/datums/mutations/speech.dm
+++ b/code/datums/mutations/speech.dm
@@ -4,6 +4,7 @@
/datum/mutation/human/nervousness
name = "Nervousness"
desc = "Causes the holder to stutter."
+ instability = NEGATIVE_STABILITY_MINI
quality = MINOR_NEGATIVE
text_gain_indication = "You feel nervous."
@@ -14,6 +15,7 @@
/datum/mutation/human/wacky
name = "Wacky"
desc = "You are not a clown. You are the entire circus."
+ instability = NEGATIVE_STABILITY_MINI
quality = MINOR_NEGATIVE
text_gain_indication = "You feel an off sensation in your voicebox."
text_lose_indication = "The off sensation passes."
@@ -36,6 +38,7 @@
/datum/mutation/human/mute
name = "Mute"
desc = "Completely inhibits the vocal section of the brain."
+ instability = NEGATIVE_STABILITY_MAJOR
quality = NEGATIVE
text_gain_indication = "You feel unable to express yourself at all."
text_lose_indication = "You feel able to speak freely again."
@@ -53,6 +56,7 @@
/datum/mutation/human/unintelligible
name = "Unintelligible"
desc = "Partially inhibits the vocal center of the brain, severely distorting speech."
+ instability = NEGATIVE_STABILITY_MODERATE
quality = NEGATIVE
text_gain_indication = "You can't seem to form any coherent thoughts!"
text_lose_indication = "Your mind feels more clear."
@@ -70,6 +74,7 @@
/datum/mutation/human/swedish
name = "Swedish"
desc = "A horrible mutation originating from the distant past. Thought to be eradicated after the incident in 2037."
+ instability = NEGATIVE_STABILITY_MINI
quality = MINOR_NEGATIVE
text_gain_indication = "You feel Swedish, however that works."
text_lose_indication = "The feeling of Swedishness passes."
@@ -101,6 +106,7 @@
/datum/mutation/human/chav
name = "Chav"
desc = "Unknown"
+ instability = NEGATIVE_STABILITY_MINI
quality = MINOR_NEGATIVE
text_gain_indication = "Ye feel like a reet prat like, innit?"
text_lose_indication = "You no longer feel like being rude and sassy."
@@ -138,6 +144,7 @@
/datum/mutation/human/elvis
name = "Elvis"
desc = "A terrifying mutation named after its 'patient-zero'."
+ instability = NEGATIVE_STABILITY_MINI
quality = MINOR_NEGATIVE
locked = TRUE
text_gain_indication = "You feel pretty good, honeydoll."
@@ -203,6 +210,7 @@
/datum/mutation/human/medieval
name = "Medieval"
desc = "A horrible mutation originating from the distant past, thought to have once been a common gene in all of old world Europe."
+ instability = NEGATIVE_STABILITY_MINI
quality = MINOR_NEGATIVE
text_gain_indication = "You feel like seeking the holy grail!"
text_lose_indication = "You no longer feel like seeking anything."
@@ -243,6 +251,7 @@
/datum/mutation/human/piglatin
name = "Pig Latin"
desc = "Historians say back in the 2020's humanity spoke entirely in this mystical language."
+ instability = NEGATIVE_STABILITY_MINI
quality = MINOR_NEGATIVE
text_gain_indication = span_notice("Omethingsay eelsfay offyay.")
text_lose_indication = span_notice("The off sensation passes.")
diff --git a/code/datums/mutations/telekinesis.dm b/code/datums/mutations/telekinesis.dm
index 53d8beb56ff50..bd6ba13070df3 100644
--- a/code/datums/mutations/telekinesis.dm
+++ b/code/datums/mutations/telekinesis.dm
@@ -6,7 +6,7 @@
difficulty = 18
text_gain_indication = "You feel smarter!"
limb_req = BODY_ZONE_HEAD
- instability = 30
+ instability = POSITIVE_INSTABILITY_MAJOR
///Typecache of atoms that TK shouldn't interact with
var/static/list/blacklisted_atoms = typecacheof(list(/atom/movable/screen))
diff --git a/code/datums/mutations/telepathy.dm b/code/datums/mutations/telepathy.dm
index 8619c2bddc476..0e3dffb48b4a4 100644
--- a/code/datums/mutations/telepathy.dm
+++ b/code/datums/mutations/telepathy.dm
@@ -6,5 +6,5 @@
text_lose_indication = "You don't hear your mind echo anymore."
difficulty = 12
power_path = /datum/action/cooldown/spell/list_target/telepathy
- instability = 10
+ instability = POSITIVE_INSTABILITY_MINOR // basically a mediocre PDA messager
energy_coeff = 1
diff --git a/code/datums/mutations/tongue_spike.dm b/code/datums/mutations/tongue_spike.dm
index e6249041250b8..9e25ad6f4f296 100644
--- a/code/datums/mutations/tongue_spike.dm
+++ b/code/datums/mutations/tongue_spike.dm
@@ -3,7 +3,7 @@
desc = "Allows a creature to voluntary shoot their tongue out as a deadly weapon."
quality = POSITIVE
text_gain_indication = span_notice("Your feel like you can throw your voice.")
- instability = 15
+ instability = POSITIVE_INSTABILITY_MINI // worthless. also serves as a bit of a hint that it's not good
power_path = /datum/action/cooldown/spell/tongue_spike
energy_coeff = 1
@@ -89,7 +89,7 @@
desc = "Allows a creature to voluntary shoot their tongue out as biomass, allowing a long range transfer of chemicals."
quality = POSITIVE
text_gain_indication = span_notice("Your feel like you can really connect with people by throwing your voice.")
- instability = 15
+ instability = POSITIVE_INSTABILITY_MINOR // slightly less worthless. slightly.
locked = TRUE
power_path = /datum/action/cooldown/spell/tongue_spike/chem
energy_coeff = 1
diff --git a/code/datums/mutations/touch.dm b/code/datums/mutations/touch.dm
index ca94f109ac664..eaa4909703525 100644
--- a/code/datums/mutations/touch.dm
+++ b/code/datums/mutations/touch.dm
@@ -7,7 +7,7 @@
text_gain_indication = "You feel power flow through your hands."
text_lose_indication = "The energy in your hands subsides."
power_path = /datum/action/cooldown/spell/touch/shock
- instability = 35
+ instability = POSITIVE_INSTABILITY_MODERATE // bad stun baton
energy_coeff = 1
power_coeff = 1
diff --git a/code/datums/mutations/void_magnet.dm b/code/datums/mutations/void_magnet.dm
index 48f04eda636a7..b5c893e32c547 100644
--- a/code/datums/mutations/void_magnet.dm
+++ b/code/datums/mutations/void_magnet.dm
@@ -3,7 +3,7 @@
desc = "A rare genome that attracts odd forces not usually observed."
quality = MINOR_NEGATIVE //upsides and downsides
text_gain_indication = span_notice("You feel a heavy, dull force just beyond the walls watching you.")
- instability = 30
+ instability = POSITIVE_INSTABILITY_MODERATE // useful, but has large drawbacks
power_path = /datum/action/cooldown/spell/void/cursed
energy_coeff = 1
synchronizer_coeff = 1
diff --git a/code/datums/mutations/webbing.dm b/code/datums/mutations/webbing.dm
index 0fda118d0ed60..002687d55be0f 100644
--- a/code/datums/mutations/webbing.dm
+++ b/code/datums/mutations/webbing.dm
@@ -4,7 +4,7 @@
desc = "Allows the user to lay webbing, and travel through it."
quality = POSITIVE
text_gain_indication = "Your skin feels webby."
- instability = 15
+ instability = POSITIVE_INSTABILITY_MODERATE // useful until you're lynched
power_path = /datum/action/cooldown/mob_cooldown/lay_web/genetic
energy_coeff = 1
diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm
index e6e8b956e6568..e5cf49475e22e 100644
--- a/code/datums/sprite_accessories.dm
+++ b/code/datums/sprite_accessories.dm
@@ -1700,24 +1700,24 @@
// MutantParts Definitions //
/////////////////////////////
-/datum/sprite_accessory/body_markings
- icon = 'icons/mob/human/species/lizard/lizard_misc.dmi'
+/datum/sprite_accessory/lizard_markings
+ icon = 'icons/mob/human/species/lizard/lizard_markings.dmi'
-/datum/sprite_accessory/body_markings/none
+/datum/sprite_accessory/lizard_markings/none
name = "None"
icon_state = "none"
-/datum/sprite_accessory/body_markings/dtiger
+/datum/sprite_accessory/lizard_markings/dtiger
name = "Dark Tiger Body"
icon_state = "dtiger"
gender_specific = TRUE
-/datum/sprite_accessory/body_markings/ltiger
+/datum/sprite_accessory/lizard_markings/ltiger
name = "Light Tiger Body"
icon_state = "ltiger"
gender_specific = TRUE
-/datum/sprite_accessory/body_markings/lbelly
+/datum/sprite_accessory/lizard_markings/lbelly
name = "Light Belly"
icon_state = "lbelly"
gender_specific = TRUE
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index c9e94e0dd97f5..3d4bd7e93655c 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -31,7 +31,7 @@
SIGNAL_HANDLER
if(istype(attacking_item, /obj/item/kinetic_crusher))
- total_damage += damage_dealt
+ total_damage += (-1 * damage_dealt)
/datum/status_effect/syphon_mark
id = "syphon_mark"
diff --git a/code/game/machinery/civilian_bounties.dm b/code/game/machinery/civilian_bounties.dm
index e5cd81cc965a1..fa0d28c999c88 100644
--- a/code/game/machinery/civilian_bounties.dm
+++ b/code/game/machinery/civilian_bounties.dm
@@ -168,7 +168,7 @@
return
inserted_scan_id.registered_account.civilian_bounty = inserted_scan_id.registered_account.bounties[choice]
inserted_scan_id.registered_account.bounties = null
- SSblackbox.record_feedback("tally", "bounties_assigned", 1, choice.type)
+ SSblackbox.record_feedback("tally", "bounties_assigned", 1, inserted_scan_id.registered_account.civilian_bounty.type)
return inserted_scan_id.registered_account.civilian_bounty
/obj/machinery/computer/piratepad_control/civilian/click_alt(mob/user)
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index f0d7b2e30eb52..9028d6d367d91 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -1,5 +1,20 @@
-/// Base timeout for creating mutation activators and other injectors
-#define INJECTOR_TIMEOUT 100
+/// Base timeout for creating mutation activators
+#define MIN_ACTIVATOR_TIMEOUT 5 SECONDS
+/// Base cooldown multiplier for activator upgrades
+#define ACTIVATOR_COOLDOWN_MULTIPLIER 0.25
+/// Base timeout for creating mutation injectors
+#define MIN_INJECTOR_TIMEOUT 10 SECONDS
+/// Base cooldown multiplier for injecotr upgrades
+#define INJECTOR_COOLDOWN_MULTIPLIER 0.15
+
+/// Base timeout for creating advanced injectors
+#define MIN_ADVANCED_TIMEOUT 15 SECONDS
+/// Base cooldown multiplier for advanced injector upgrades
+#define ADVANCED_COOLDOWN_MULTIPLIER 0.1
+
+/// Used for other things like UI/UE/Initial CD
+#define MISC_INJECTOR_TIMEOUT 60 SECONDS
+
/// Maximum number of genetic makeup storage slots in DNA Console
#define NUMBER_OF_BUFFERS 3
/// Timeout for DNA Scramble in DNA Consoles
@@ -221,7 +236,7 @@
connect_to_scanner()
// Set appropriate ready timers and limits for machines functions
- injector_ready = world.time + INJECTOR_TIMEOUT
+ injector_ready = world.time + MISC_INJECTOR_TIMEOUT
scramble_ready = world.time + SCRAMBLE_TIMEOUT
joker_ready = world.time + JOKER_TIMEOUT
COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN)
@@ -816,21 +831,33 @@
I.research = TRUE
// If there's an operational connected scanner, we can use its upgrades
// to improve our injector's genetic damage generation
+ var/cd_reduction_mult = 1 + ACTIVATOR_COOLDOWN_MULTIPLIER
+ var/base_cd_time = max(MIN_ACTIVATOR_TIMEOUT, abs(HM.instability) SECONDS)
+
if(scanner_operational())
I.damage_coeff = connected_scanner.damage_coeff*4
- injector_ready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected_scanner.precision_coeff)
- else
- injector_ready = world.time + INJECTOR_TIMEOUT
+ // T1: 1.25 - 0.25: 1: 100%
+ // T4: 1.25 - 1: 0.25 = 25%
+ // 25% reduction per tier
+ cd_reduction_mult -= ACTIVATOR_COOLDOWN_MULTIPLIER * (connected_scanner.precision_coeff)
+
+ injector_ready = world.time + (base_cd_time * cd_reduction_mult)
else
I.name = "[HM.name] mutator"
- I.doitanyway = TRUE
+ I.force_mutate = TRUE
// If there's an operational connected scanner, we can use its upgrades
// to improve our injector's genetic damage generation
+ var/cd_reduction_mult = 1 + INJECTOR_COOLDOWN_MULTIPLIER
+ var/base_cd_time = max(MIN_INJECTOR_TIMEOUT, abs(HM.instability) * 1 SECONDS)
+
if(scanner_operational())
- I.damage_coeff = connected_scanner.damage_coeff
- injector_ready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected_scanner.precision_coeff)
- else
- injector_ready = world.time + INJECTOR_TIMEOUT * 5
+ I.damage_coeff = connected_scanner.damage_coeff*4
+ // T1: 1.15 - 0.15: 1: 100%
+ // T4: 1.15 - 0.60: 0.55: 55%
+ // 15% reduction per tier
+ cd_reduction_mult -= (INJECTOR_COOLDOWN_MULTIPLIER * connected_scanner.precision_coeff)
+
+ injector_ready = world.time + (base_cd_time * cd_reduction_mult)
if(connected_scanner)
connected_scanner.use_energy(connected_scanner.active_power_usage)
else
@@ -1349,7 +1376,7 @@
// If we successfully created an injector, don't forget to set the new
// ready timer.
if(I)
- injector_ready = world.time + INJECTOR_TIMEOUT
+ injector_ready = world.time + MISC_INJECTOR_TIMEOUT
if(connected_scanner)
connected_scanner.use_energy(connected_scanner.active_power_usage)
else
@@ -1538,22 +1565,29 @@
// Run through each mutation in our Advanced Injector and add them to a
// new injector
+ var/total_stability
for(var/A in injector)
var/datum/mutation/human/HM = A
I.add_mutations += new HM.type(copymut=HM)
+ total_stability += HM.instability
// Force apply any mutations, this is functionality similar to mutators
- I.doitanyway = TRUE
+ I.force_mutate = TRUE
I.name = "Advanced [inj_name] injector"
// If there's an operational connected scanner, we can use its upgrades
// to improve our injector's genetic damage generation
+ var/cd_reduction_mult = 1 + ADVANCED_COOLDOWN_MULTIPLIER
+ var/base_cd_time = max(MIN_ADVANCED_TIMEOUT, abs(total_stability) SECONDS)
+
if(scanner_operational())
- I.damage_coeff = connected_scanner.damage_coeff
- injector_ready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected_scanner.precision_coeff)
- else
- injector_ready = world.time + INJECTOR_TIMEOUT * 8
+ I.damage_coeff = connected_scanner.damage_coeff*4
+ // T1: 1.1 - 0.1: 1: 100%
+ // T4: 1.1 - 0.4: 0.7 = 70%
+ // 10% reduction per tier
+ cd_reduction_mult -= ADVANCED_COOLDOWN_MULTIPLIER * (connected_scanner.precision_coeff)
+ injector_ready = world.time + (base_cd_time * cd_reduction_mult)
return
// Adds a mutation to an advanced injector
@@ -2299,11 +2333,20 @@
SIGNAL_HANDLER
set_connected_scanner(null)
+#undef MIN_ACTIVATOR_TIMEOUT
+#undef ACTIVATOR_COOLDOWN_MULTIPLIER
+#undef MIN_INJECTOR_TIMEOUT
+#undef INJECTOR_COOLDOWN_MULTIPLIER
+
+#undef MIN_ADVANCED_TIMEOUT
+#undef ADVANCED_COOLDOWN_MULTIPLIER
+
+#undef MISC_INJECTOR_TIMEOUT
+
#undef GENETIC_DAMAGE_PULSE_UNIQUE_IDENTITY
#undef GENETIC_DAMAGE_PULSE_UNIQUE_FEATURES
#undef ENZYME_COPY_BASE_COOLDOWN
-#undef INJECTOR_TIMEOUT
#undef NUMBER_OF_BUFFERS
#undef SCRAMBLE_TIMEOUT
#undef JOKER_TIMEOUT
diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm
index 4775642881fcc..cb0ab9900d5cc 100644
--- a/code/game/machinery/dna_scanner.dm
+++ b/code/game/machinery/dna_scanner.dm
@@ -11,9 +11,9 @@
circuit = /obj/item/circuitboard/machine/dnascanner
var/locked = FALSE
- var/damage_coeff
+ var/damage_coeff = 1
var/scan_level
- var/precision_coeff
+ var/precision_coeff = 1
var/message_cooldown
var/breakout_time = 1200
var/obj/machinery/computer/scan_consolenew/linked_console = null
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index ae37691f1c1a1..37310e2aceb49 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -601,7 +601,11 @@
if(!machine_stat)
update_icon(ALL, AIRLOCK_DENY)
playsound(src,doorDeni,50,FALSE,3)
- addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon), ALL, AIRLOCK_CLOSED), AIRLOCK_DENY_ANIMATION_TIME)
+ addtimer(CALLBACK(src, PROC_REF(handle_deny_end)), AIRLOCK_DENY_ANIMATION_TIME)
+
+/obj/machinery/door/airlock/proc/handle_deny_end()
+ if(airlock_state == AIRLOCK_DENY)
+ update_icon(ALL, AIRLOCK_CLOSED)
/obj/machinery/door/airlock/examine(mob/user)
. = ..()
diff --git a/code/game/machinery/flatpacker.dm b/code/game/machinery/flatpacker.dm
index 1eb8f6e2cc5cb..9468dacc24c56 100644
--- a/code/game/machinery/flatpacker.dm
+++ b/code/game/machinery/flatpacker.dm
@@ -231,6 +231,10 @@
materials.retrieve_sheets(amount, ejecting, drop_location())
return TRUE
+/obj/machinery/flatpacker/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ITEM_INTERACT_BLOCKING
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/flatpacker/Destroy()
QDEL_NULL(inserted_board)
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index d977605b2436f..b99c8000c58a4 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -444,6 +444,7 @@
beauty = -50
clean_type = CLEAN_TYPE_BLOOD
mouse_opacity = MOUSE_OPACITY_OPAQUE
+ resistance_flags = UNACIDABLE | ACID_PROOF | FIRE_PROOF | FLAMMABLE //gross way of doing this but would need to disassemble fire_act call stack otherwise
/// Maximum amount of hotspots this pool can create before deleting itself
var/burn_amount = 3
/// Is this fuel pool currently burning?
@@ -453,6 +454,10 @@
/obj/effect/decal/cleanable/fuel_pool/Initialize(mapload, burn_stacks)
. = ..()
+ var/static/list/ignition_trigger_connections = list(
+ COMSIG_TURF_MOVABLE_THROW_LANDED = PROC_REF(ignition_trigger),
+ )
+ AddElement(/datum/element/connect_loc, ignition_trigger_connections)
for(var/obj/effect/decal/cleanable/fuel_pool/pool in get_turf(src)) //Can't use locate because we also belong to that turf
if(pool == src)
continue
@@ -509,6 +514,26 @@
ignite()
return ..()
+/obj/effect/decal/cleanable/fuel_pool/on_entered(datum/source, atom/movable/entered_atom)
+ . = ..()
+ if(entered_atom.throwing) // don't light from things being thrown over us, we handle that somewhere else
+ return
+ ignition_trigger(source = src, enflammable_atom = entered_atom)
+
+/obj/effect/decal/cleanable/fuel_pool/proc/ignition_trigger(datum/source, atom/movable/enflammable_atom)
+ SIGNAL_HANDLER
+
+ if(isitem(enflammable_atom))
+ var/obj/item/enflamed_item = enflammable_atom
+ if(enflamed_item.get_temperature() > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
+ ignite()
+ return
+ else if(isliving(enflammable_atom))
+ var/mob/living/enflamed_liver = enflammable_atom
+ if(enflamed_liver.on_fire)
+ ignite()
+
+
/obj/effect/decal/cleanable/fuel_pool/hivis
icon_state = "fuel_pool_hivis"
diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm
index 874c53fa83c7d..f9bb819fd42a1 100644
--- a/code/game/objects/effects/effect_system/effects_sparks.dm
+++ b/code/game/objects/effects/effect_system/effects_sparks.dm
@@ -26,24 +26,65 @@
return INITIALIZE_HINT_LATELOAD
/obj/effect/particle_effect/sparks/LateInitialize()
+ RegisterSignals(src, list(COMSIG_MOVABLE_CROSS, COMSIG_MOVABLE_CROSS_OVER), PROC_REF(sparks_touched))
flick(icon_state, src)
playsound(src, SFX_SPARKS, 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- var/turf/T = loc
- if(isturf(T))
- T.hotspot_expose(1000,100)
+ var/turf/location = loc
+ if(isturf(location))
+ affect_location(location, just_initialized = TRUE)
QDEL_IN(src, 20)
/obj/effect/particle_effect/sparks/Destroy()
- var/turf/T = loc
- if(isturf(T))
- T.hotspot_expose(1000,100)
+ var/turf/location = loc
+ if(isturf(location))
+ affect_location(location)
return ..()
/obj/effect/particle_effect/sparks/Move()
..()
- var/turf/T = loc
- if(isturf(T))
- T.hotspot_expose(1000,100)
+ var/turf/location = loc
+ if(isturf(location))
+ affect_location(location)
+
+/*
+* Apply the effects of this spark to its location.
+*
+* When the spark is first created, Cross() and Crossed() don't get called,
+* so for the first initialization, we make sure to specifically invoke the
+* behavior of the spark on all the mobs and objects in the location.
+* turf/location - The place the spark is affectiong
+* just_initialized - If the spark is just being created, and we need to manually affect everything in the location
+*/
+/obj/effect/particle_effect/sparks/proc/affect_location(turf/location, just_initialized = FALSE)
+ location.hotspot_expose(1000,100)
+ if(just_initialized)
+ for(var/atom/movable/singed in location)
+ sparks_touched(src, singed)
+
+/*
+* This is called when anything passes through the same tiles as a spark, or when a spark passes through something's tile.
+*
+* This is invoked by the signals sent by every atom when they're crossed or crossing something. It
+* signifies that something has been touched by sparks, and should be affected by possible pyrotechnic affects..
+* datum/source - Can either be the spark itself or an object that just walked into it
+* mob/living/singed_mob - The mob that was touched by the spark
+*/
+/obj/effect/particle_effect/sparks/proc/sparks_touched(datum/source, atom/movable/singed)
+ SIGNAL_HANDLER
+
+ if(isobj(singed))
+ var/obj/singed_obj = singed
+ if(singed_obj.resistance_flags & FLAMMABLE && !(singed_obj.resistance_flags & ON_FIRE)) //only fire_act flammable objects instead of burning EVERYTHING
+ singed_obj.fire_act(1,100)
+ if(singed_obj.reagents)
+ var/datum/reagents/reagents = singed_obj.reagents
+ reagents?.expose_temperature(1000)
+ return
+ if(isliving(singed))
+ var/mob/living/singed_living = singed
+ if(singed_living.fire_stacks)
+ singed_living.ignite_mob(FALSE) //ignite the mob, silent = FALSE (You're set on fire!)
+ return
/datum/effect_system/spark_spread
effect_type = /obj/effect/particle_effect/sparks
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index 174a8fde5f7c2..6ca951d262a67 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -282,7 +282,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM
var/obj/item/reagent_containers/cup/glass = interacting_with
if(!istype(glass)) //you can dip cigarettes into beakers
return NONE
-
+ if(istype(glass, /obj/item/reagent_containers/cup/mortar))
+ return NONE
if(glass.reagents.trans_to(src, chem_volume, transferred_by = user)) //if reagents were transferred, show the message
to_chat(user, span_notice("You dip \the [src] into \the [glass]."))
//if not, either the beaker was empty, or the cigarette was full
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index c3203a0ace7a9..598c16c9041a8 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -211,6 +211,7 @@
it explodes properly when it gets a signal (and it does).
*/
/obj/item/transfer_valve/proc/toggle_valve(obj/item/tank/target, change_volume = TRUE)
+ playsound(src, 'sound/effects/valve_opening.ogg', 50)
if(!valve_open && tank_one && tank_two)
var/turf/bombturf = get_turf(src)
diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm
index 0dc20c6dbb4d9..8fee497a4f9ee 100644
--- a/code/game/objects/items/dna_injector.dm
+++ b/code/game/objects/items/dna_injector.dm
@@ -163,7 +163,7 @@
/obj/item/dnainjector/activator
name = "\improper DNA activator"
desc = "Activates the current mutation on injection, if the subject has it."
- var/doitanyway = FALSE
+ var/force_mutate = FALSE
var/research = FALSE //Set to true to get expended and filled injectors for chromosomes
var/filled = FALSE
var/crispr_charge = FALSE // Look for viruses, look at symptoms, if research and Dormant DNA Activator or Viral Evolutionary Acceleration, set to true
@@ -176,7 +176,7 @@
if(istype(added_mutation, /datum/mutation/human))
mutation = added_mutation.type
if(!target.dna.activate_mutation(added_mutation))
- if(doitanyway)
+ if(force_mutate)
target.dna.add_mutation(added_mutation, MUT_EXTRA)
else if(research && target.client)
filled = TRUE
@@ -184,7 +184,7 @@
for(var/datum/symptom/symp in disease.symptoms)
if((symp.type == /datum/symptom/genetic_mutation) || (symp.type == /datum/symptom/viralevolution))
crispr_charge = TRUE
- log_combat(user, target, "[!doitanyway ? "failed to inject" : "injected"]", "[src] ([mutation])[crispr_charge ? " with CRISPR charge" : ""]")
+ log_combat(user, target, "[!force_mutate ? "failed to inject" : "injected"]", "[src] ([mutation])[crispr_charge ? " with CRISPR charge" : ""]")
return TRUE
/// DNA INJECTORS
diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm
index be47d7009053b..ac309e275d77c 100644
--- a/code/game/objects/items/extinguisher.dm
+++ b/code/game/objects/items/extinguisher.dm
@@ -32,7 +32,11 @@
/// Can we refill this at a water tank?
var/refilling = FALSE
/// What tank we need to refill this.
- var/tanktype = /obj/structure/reagent_dispensers/watertank
+ var/tanktypes = list(
+ /obj/structure/reagent_dispensers/watertank,
+ /obj/structure/reagent_dispensers/plumbed,
+ /obj/structure/reagent_dispensers/water_cooler,
+ )
/// something that should be replaced with base_icon_state
var/sprite_name = "fire_extinguisher"
/// Maximum distance launched water will travel.
@@ -131,7 +135,10 @@
tank_holder_icon_state = "holder_foam_extinguisher"
dog_fashion = null
chem = /datum/reagent/firefighting_foam
- tanktype = /obj/structure/reagent_dispensers/foamtank
+ tanktypes = list(
+ /obj/structure/reagent_dispensers/foamtank,
+ /obj/structure/reagent_dispensers/plumbed,
+ )
sprite_name = "foam_extinguisher"
precision = TRUE
max_water = 100
@@ -178,10 +185,14 @@
. += span_notice("Alt-click to empty it.")
/obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user)
- if(istype(target, tanktype) && target.Adjacent(user))
+ if(is_type_in_list(target, tanktypes) && target.Adjacent(user))
if(reagents.total_volume == reagents.maximum_volume)
balloon_alert(user, "already full!")
return TRUE
+ // Make sure we're refilling with the proper chem.
+ if(!(target.reagents.has_reagent(chem)))
+ balloon_alert(user, "can't refill with this liquid!")
+ return TRUE
var/obj/structure/reagent_dispensers/W = target //will it work?
var/transferred = W.reagents.trans_to(src, max_water, transferred_by = user)
if(transferred > 0)
@@ -305,5 +316,8 @@
name = "fire extender"
desc = "A traditional red fire extinguisher. Made in Britain... wait, what?"
chem = /datum/reagent/fuel
- tanktype = /obj/structure/reagent_dispensers/fueltank
+ tanktypes = list(
+ /obj/structure/reagent_dispensers/fueltank,
+ /obj/structure/reagent_dispensers/plumbed
+ )
cooling_power = 0
diff --git a/code/game/objects/items/grenades/_grenade.dm b/code/game/objects/items/grenades/_grenade.dm
index 5deb833b1f234..ec16b4c22fe95 100644
--- a/code/game/objects/items/grenades/_grenade.dm
+++ b/code/game/objects/items/grenades/_grenade.dm
@@ -16,7 +16,6 @@
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 // We detonate upon being exploded.
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT
- resistance_flags = FLAMMABLE
max_integrity = 40
/// Bitfields which prevent the grenade from detonating if set. Includes ([GRENADE_DUD]|[GRENADE_USED])
var/dud_flags = NONE
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 2d886163a50ba..1a695442d8a76 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -425,6 +425,7 @@
. = ..()
if(!.)
return .
+ ADD_TRAIT(cyborg, TRAIT_FASTMED, REF(src))
for(var/obj/item/borg/cyborg_omnitool/medical/omnitool_upgrade in cyborg.model.modules)
if(omnitool_upgrade.upgraded)
to_chat(user, span_warning("This unit is already equipped with an omnitool upgrade!"))
@@ -436,6 +437,7 @@
. = ..()
if(!.)
return .
+ REMOVE_TRAIT(cyborg, TRAIT_FASTMED, REF(src))
for(var/obj/item/borg/cyborg_omnitool/omnitool in cyborg.model.modules)
omnitool.downgrade_omnitool()
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 7b6d4b6207340..662f94c61d5e1 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -347,6 +347,12 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new /datum/stack_recipe("pew (left)", /obj/structure/chair/pew/left, 3, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE),
new /datum/stack_recipe("pew (right)", /obj/structure/chair/pew/right, 3, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE)
)),
+ new/datum/stack_recipe_list("peg limbs", list(
+ new /datum/stack_recipe("peg arm (left)", /obj/item/bodypart/arm/left/ghetto, 2, crafting_flags = NONE, category = CAT_MISC),
+ new /datum/stack_recipe("peg arm (right)", /obj/item/bodypart/arm/right/ghetto, 2, crafting_flags = NONE, category = CAT_MISC),
+ new /datum/stack_recipe("peg leg (left)", /obj/item/bodypart/leg/left/ghetto, 2, crafting_flags = NONE, category = CAT_MISC),
+ new /datum/stack_recipe("peg leg (right)", /obj/item/bodypart/leg/right/ghetto, 2, crafting_flags = NONE, category = CAT_MISC)
+ )),
null, \
))
@@ -377,6 +383,21 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
/obj/item/stack/sheet/mineral/wood/fifty
amount = 50
+/obj/item/stack/sheet/mineral/wood/interact_with_atom(mob/living/carbon/human/target, mob/user)
+ if(!istype(target))
+ return NONE
+
+ var/obj/item/bodypart/affecting = target.get_bodypart(check_zone(user.zone_selected))
+ if(affecting && IS_PEG_LIMB(affecting))
+ if(user == target)
+ user.visible_message(span_notice("[user] starts to fix their [affecting.name]."), span_notice("You start fixing [target == user ? "your" : "[target]'s"] [affecting.name]."))
+ if(!do_after(user, 5 SECONDS, target))
+ return ITEM_INTERACT_FAILURE
+ if(target.item_heal(user, brute_heal = 15, burn_heal = 15, heal_message_brute = "splintering", heal_message_burn = "charring", required_bodytype = BODYTYPE_PEG))
+ use(1)
+ return ITEM_INTERACT_SUCCESS
+ else
+ return NONE
/*
* Bamboo
*/
diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm
index 0e9b9743a4593..b2b0109c04c88 100644
--- a/code/game/objects/items/tools/weldingtool.dm
+++ b/code/game/objects/items/tools/weldingtool.dm
@@ -160,7 +160,7 @@
if(!use_tool(attacked_humanoid, user, use_delay, volume=50, amount=1))
return ITEM_INTERACT_BLOCKING
- item_heal_robotic(attacked_humanoid, user, 15, 0)
+ attacked_humanoid.item_heal(user, brute_heal = 15, burn_heal = 0, heal_message_brute = "dents", heal_message_burn = "burnt wires", required_bodytype = BODYTYPE_ROBOTIC)
return ITEM_INTERACT_SUCCESS
/obj/item/weldingtool/afterattack(atom/target, mob/user, click_parameters)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
index 32f3eb7419ced..aeb14c9b5dd28 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
@@ -25,6 +25,7 @@ It's like a regular ol' straight pipe, but you can turn it on and off.
normalize_cardinal_directions()
if(animation)
flick("[valve_type]valve_[on][!on]-[set_overlay_offset(piping_layer)]", src)
+ playsound(src, 'sound/effects/valve_opening.ogg', 50)
icon_state = "[valve_type]valve_[on ? "on" : "off"]-[set_overlay_offset(piping_layer)]"
/**
@@ -37,6 +38,7 @@ It's like a regular ol' straight pipe, but you can turn it on and off.
. = on
on = to_open
if(on)
+ playsound(src, 'sound/effects/gas_hissing.ogg', 50)
update_icon_nopipes()
update_parents()
var/datum/pipeline/parent1 = parents[1]
diff --git a/code/modules/bitrunning/designs.dm b/code/modules/bitrunning/designs.dm
index 4e7bca1c1a8dd..96ae65d41e99b 100644
--- a/code/modules/bitrunning/designs.dm
+++ b/code/modules/bitrunning/designs.dm
@@ -72,16 +72,3 @@
RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_CARGO
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
-
-
-/datum/techweb_node/bitrunning
- id = "bitrunning"
- display_name = "Bitrunning Technology"
- description = "Bluespace technology has led to the development of quantum-scale computing, which unlocks the means to materialize atomic structures while executing advanced programs."
- prereq_ids = list("practical_bluespace")
- design_ids = list(
- "byteforge",
- "quantum_console",
- "netpod",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
diff --git a/code/modules/buildmode/buildmode.dm b/code/modules/buildmode/buildmode.dm
index 36a3db07f597e..80f5ae1ad27b0 100644
--- a/code/modules/buildmode/buildmode.dm
+++ b/code/modules/buildmode/buildmode.dm
@@ -50,6 +50,8 @@
holder.player_details.post_login_callbacks -= li_cb
li_cb = null
holder = null
+ modebutton = null
+ dirbutton = null
QDEL_NULL(mode)
QDEL_LIST(buttons)
QDEL_LIST(modeswitch_buttons)
diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm
index 6c62ed1d054c2..485276b4ade2a 100644
--- a/code/modules/client/preferences/_preference.dm
+++ b/code/modules/client/preferences/_preference.dm
@@ -112,6 +112,10 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
/// will show the feature as selectable.
var/relevant_mutant_bodypart = null
+ /// If the selected species has this in its /datum/species/body_markings,
+ /// will show the feature as selectable.
+ var/relevant_body_markings = null
+
/// If the selected species has this in its /datum/species/inherent_traits,
/// will show the feature as selectable.
var/relevant_inherent_trait = null
@@ -333,6 +337,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
|| !isnull(relevant_inherent_trait) \
|| !isnull(relevant_external_organ) \
|| !isnull(relevant_head_flag) \
+ || !isnull(relevant_body_markings) \
)
var/species_type = preferences.read_preference(/datum/preference/choiced/species)
diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm
index bee57300ec4a4..38c83690b3b5e 100644
--- a/code/modules/client/preferences/species_features/lizard.dm
+++ b/code/modules/client/preferences/species_features/lizard.dm
@@ -29,20 +29,20 @@
category = PREFERENCE_CATEGORY_FEATURES
main_feature_name = "Body markings"
should_generate_icons = TRUE
- relevant_mutant_bodypart = "body_markings"
+ relevant_body_markings = /datum/bodypart_overlay/simple/body_marking/lizard
/datum/preference/choiced/lizard_body_markings/init_possible_values()
- return assoc_to_keys_features(SSaccessories.body_markings_list)
+ return assoc_to_keys_features(SSaccessories.lizard_markings_list)
/datum/preference/choiced/lizard_body_markings/icon_for(value)
- var/datum/sprite_accessory/sprite_accessory = SSaccessories.body_markings_list[value]
+ var/datum/sprite_accessory/sprite_accessory = SSaccessories.lizard_markings_list[value]
var/icon/final_icon = icon('icons/mob/human/species/lizard/bodyparts.dmi', "lizard_chest_m")
if (sprite_accessory.icon_state != "none")
var/icon/body_markings_icon = icon(
'icons/mob/human/species/lizard/lizard_misc.dmi',
- "m_body_markings_[sprite_accessory.icon_state]_ADJ",
+ "male_[sprite_accessory.icon_state]_chest",
)
final_icon.Blend(body_markings_icon, ICON_OVERLAY)
@@ -55,7 +55,7 @@
return final_icon
/datum/preference/choiced/lizard_body_markings/apply_to_human(mob/living/carbon/human/target, value)
- target.dna.features["body_markings"] = value
+ target.dna.features["lizard_markings"] = value
/datum/preference/choiced/lizard_frills
savefile_key = "feature_lizard_frills"
diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm
index 745e6fb917b8f..f697d857d4fc4 100644
--- a/code/modules/client/preferences/species_features/moth.dm
+++ b/code/modules/client/preferences/species_features/moth.dm
@@ -34,7 +34,7 @@
category = PREFERENCE_CATEGORY_FEATURES
main_feature_name = "Body markings"
should_generate_icons = TRUE
- relevant_mutant_bodypart = "moth_markings"
+ relevant_body_markings = /datum/bodypart_overlay/simple/body_marking/moth
/datum/preference/choiced/moth_markings/init_possible_values()
return assoc_to_keys_features(SSaccessories.moth_markings_list)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index cd32002d0434b..98184b3fce25e 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -360,7 +360,7 @@
. = ..()
if(href_list["list_armor"])
- var/list/readout = list("PROTECTION CLASSES")
+ var/list/readout = list()
var/datum/armor/armor = get_armor()
var/added_damage_header = FALSE
@@ -369,9 +369,9 @@
if(!rating)
continue
if(!added_damage_header)
- readout += "\nARMOR (I-X)"
+ readout += "ARMOR (I-X)"
added_damage_header = TRUE
- readout += "\n[armor_to_protection_name(damage_key)] [armor_to_protection_class(rating)]"
+ readout += "[armor_to_protection_name(damage_key)] [armor_to_protection_class(rating)]"
var/added_durability_header = FALSE
for(var/durability_key in ARMOR_LIST_DURABILITY())
@@ -379,9 +379,9 @@
if(!rating)
continue
if(!added_durability_header)
- readout += "\nDURABILITY (I-X)"
+ readout += "DURABILITY (I-X)"
added_damage_header = TRUE
- readout += "\n[armor_to_protection_name(durability_key)] [armor_to_protection_class(rating)]"
+ readout += "[armor_to_protection_name(durability_key)] [armor_to_protection_class(rating)]"
if(flags_cover & HEADCOVERSMOUTH || flags_cover & PEPPERPROOF)
var/list/things_blocked = list()
@@ -390,12 +390,15 @@
if(flags_cover & PEPPERPROOF)
things_blocked += "pepperspray"
if(length(things_blocked))
- readout += "\nCOVERAGE"
- readout += "\nIt will block [english_list(things_blocked)]."
+ readout += "COVERAGE"
+ readout += "It will block [english_list(things_blocked)]."
- readout += ""
- to_chat(usr, "[readout.Join()]")
+ if(!length(readout))
+ readout += "No armor or durability information available."
+
+ var/formatted_readout = span_notice("PROTECTION CLASSES
[jointext(readout, "\n")]")
+ to_chat(usr, examine_block(formatted_readout))
/**
* Rounds armor_value down to the nearest 10, divides it by 10 and then converts it to Roman numerals.
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index 155e4dbe42fb6..c1889cc77383d 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -5,8 +5,8 @@
icon = 'icons/obj/clothing/suits/armor.dmi'
w_class = WEIGHT_CLASS_BULKY
-/obj/item/reactive_armor_shell/attackby(obj/item/weapon, mob/user, params)
- ..()
+/obj/item/reactive_armor_shell/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
var/static/list/anomaly_armour_types = list(
/obj/effect/anomaly/grav = /obj/item/clothing/suit/armor/reactive/repulse,
/obj/effect/anomaly/flux = /obj/item/clothing/suit/armor/reactive/tesla,
@@ -17,15 +17,16 @@
/obj/effect/anomaly/ectoplasm = /obj/item/clothing/suit/armor/reactive/ectoplasm,
)
- if(istype(weapon, /obj/item/assembly/signaler/anomaly))
- var/obj/item/assembly/signaler/anomaly/anomaly = weapon
- var/armour_path = anomaly_armour_types[anomaly.anomaly_type]
+ if(istype(tool, /obj/item/assembly/signaler/anomaly))
+ var/obj/item/assembly/signaler/anomaly/anomaly = tool
+ var/armour_path = is_path_in_list(anomaly.anomaly_type, anomaly_armour_types, TRUE)
if(!armour_path)
armour_path = /obj/item/clothing/suit/armor/reactive/stealth //Lets not cheat the player if an anomaly type doesnt have its own armour coded
to_chat(user, span_notice("You insert [anomaly] into the chest plate, and the armour gently hums to life."))
new armour_path(get_turf(src))
qdel(src)
qdel(anomaly)
+ return ITEM_INTERACT_SUCCESS
//Reactive armor
/obj/item/clothing/suit/armor/reactive
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index 3434fb6fdb1ed..f0915bdbf77cd 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -118,6 +118,14 @@
experiment_proper = TRUE
required_gas = /datum/gas/nitrous_oxide
+/datum/experiment/ordnance/gaseous/plasma
+ name = "Plasma Gas Shells"
+ description = "The delivery of Plasma gas into an area of operation might prove useful. Pack the specified gas into a tank and burst it using a Tank Compressor. Publish the data in a paper."
+ gain = list(10,40)
+ target_amount = list(200,600)
+ experiment_proper = TRUE
+ required_gas = /datum/gas/plasma
+
/datum/experiment/ordnance/gaseous/bz
name = "BZ Gas Shells"
description = "The delivery of BZ gas into an area of operation might prove useful. Pack the specified gas into a tank and burst it using a Tank Compressor. Publish the data in a paper."
@@ -315,7 +323,7 @@
required_stock_part = /obj/item/stock_parts/micro_laser/ultra
/datum/experiment/scanning/random/mecha_damage_scan
- name = "Exosuit Materials 1: Stress Failure Test"
+ name = "Exosuit Materials: Stress Failure Test"
description = "Your exosuit fabricators allow for rapid production on a small scale, but the structural integrity of created parts is inferior to more traditional means."
exp_tag = "Scan"
possible_types = list(/obj/vehicle/sealed/mecha)
@@ -324,7 +332,7 @@
var/damage_percent
/datum/experiment/scanning/random/mecha_equipped_scan
- name = "Exosuit Materials 2: Load Strain Test"
+ name = "Exosuit Materials: Load Strain Test"
description = "Exosuit equipment places unique strain upon the structure of the vehicle. Scan exosuits you have assembled from your exosuit fabricator and fully equipped to accelerate our structural stress simulations."
possible_types = list(/obj/vehicle/sealed/mecha)
total_requirement = 2
@@ -368,3 +376,80 @@
continue
return TRUE
return FALSE
+
+/// Scan for cybernetic organs
+/datum/experiment/scanning/people/augmented_organs
+ name = "Human Field Research: Augmented Organs"
+ description = "We need to gather data on how cybernetic vital organs integrate with human biology. Conduct a scan on a human with these implants to help us understand their compatibility"
+ performance_hint = "Perform an organ manipulation surgery to replace one of the vital organs with a cybernetic variant."
+ required_traits_desc = "augmented vital organs"
+
+/datum/experiment/scanning/people/augmented_organs/is_valid_scan_target(mob/living/carbon/human/check)
+ . = ..()
+ if (!.)
+ return
+ var/static/list/vital_organ_slots = list(
+ ORGAN_SLOT_HEART,
+ ORGAN_SLOT_LUNGS,
+ ORGAN_SLOT_EYES,
+ ORGAN_SLOT_EARS,
+ ORGAN_SLOT_LIVER,
+ ORGAN_SLOT_STOMACH,
+ )
+
+ for (var/obj/item/organ/organ as anything in check.organs)
+ if (IS_ORGANIC_ORGAN(organ))
+ continue
+ if (!(organ.slot in vital_organ_slots))
+ continue
+ return TRUE
+ return FALSE
+
+/// Scan for skillchips
+/datum/experiment/scanning/people/skillchip
+ name = "Human Field Research: Skill Chip Implants"
+ description = "Before sticking programmed circuits into human brain, we need to know how it handles simple ones. Scan a live person with a skill chip implant in their brain."
+ performance_hint = "Perform a skill chip implantation with a skill station."
+ required_traits_desc = "skill chip implant"
+
+/datum/experiment/scanning/people/skillchip/is_valid_scan_target(mob/living/carbon/human/check, datum/component/experiment_handler/experiment_handler)
+ . = ..()
+ if (!.)
+ return
+ var/obj/item/organ/internal/brain/scanned_brain = check.get_organ_slot(ORGAN_SLOT_BRAIN)
+ if (isnull(scanned_brain))
+ experiment_handler.announce_message("Subject is brainless!")
+ return FALSE
+ if (scanned_brain.get_used_skillchip_slots() == 0)
+ experiment_handler.announce_message("No skill chips found!")
+ return FALSE
+ return TRUE
+
+/datum/experiment/scanning/reagent/cryostylane
+ name = "Pure Cryostylane Scan"
+ description = "It appears that the Cryostylane reagent can potentially halt all physiological processes in the human body. Produce Cryostylane with at least 99% purity and scan the beaker."
+ required_reagent = /datum/reagent/cryostylane
+ min_purity = 0.99
+
+/datum/experiment/scanning/bluespace_crystal
+ name = "Bluespace Crystal Sampling"
+ description = "Investigate the properties of bluespace crystals by scanning either an artificial or naturally occurring variant. This will help us deepen our understanding of bluespace phenomena."
+ required_atoms = list(/obj/item/stack/ore/bluespace_crystal = 1)
+
+/datum/experiment/scanning/points/machinery_tiered_scan/tier2_any
+ name = "Upgraded Stock Parts Benchmark"
+ description = "Our newly-designed machinery components require practical application tests for hints at possible further advancements, as well as a general confirmation that we didn't actually design worse parts somehow. Scan any machinery with Upgraded Parts and report the results."
+ required_points = 8
+ required_atoms = list(
+ /obj/machinery = 1
+ )
+ required_tier = 2
+
+/datum/experiment/scanning/points/machinery_tiered_scan/tier3_any
+ name = "Advanced Stock Parts Benchmark"
+ description = "Our newly-designed machinery components require practical application tests for hints at possible further advancements, as well as a general confirmation that we didn't actually design worse parts somehow. Scan any machinery with Advanced Parts and report the results."
+ required_points = 8
+ required_atoms = list(
+ /obj/machinery = 1
+ )
+ required_tier = 3
diff --git a/code/modules/experisci/experiment/types/scanning_people.dm b/code/modules/experisci/experiment/types/scanning_people.dm
index 379d7e9089524..c3879e2a8d32c 100644
--- a/code/modules/experisci/experiment/types/scanning_people.dm
+++ b/code/modules/experisci/experiment/types/scanning_people.dm
@@ -18,15 +18,15 @@
return FALSE
if(!ishuman(target))
return FALSE
- return is_valid_scan_target(target)
+ return is_valid_scan_target(target, experiment_handler)
/// Checks that the passed mob is valid human to scan
-/datum/experiment/scanning/people/proc/is_valid_scan_target(mob/living/carbon/human/check)
+/datum/experiment/scanning/people/proc/is_valid_scan_target(mob/living/carbon/human/check, datum/component/experiment_handler/experiment_handler)
SHOULD_CALL_PARENT(TRUE)
if(!mind_required || !isnull(check.mind))
return TRUE
if(isliving(usr))
- check.balloon_alert(usr, "subject is mindless!")
+ experiment_handler.announce_message("Subject is mindless!")
return FALSE
/datum/experiment/scanning/people/serialize_progress_stage(atom/target, list/seen_instances)
diff --git a/code/modules/experisci/experiment/types/scanning_reagent.dm b/code/modules/experisci/experiment/types/scanning_reagent.dm
new file mode 100644
index 0000000000000..1a39e2941efeb
--- /dev/null
+++ b/code/modules/experisci/experiment/types/scanning_reagent.dm
@@ -0,0 +1,35 @@
+/// An experiment where you scan a container with a specified reagent of certain purity
+/datum/experiment/scanning/reagent
+ exp_tag = "Reagent Scan"
+ allowed_experimentors = list(/obj/item/experi_scanner, /obj/item/scanner_wand)
+ required_atoms = list(/obj/item/reagent_containers = 1)
+ /// The reagent required to present in the scanned container
+ var/datum/reagent/required_reagent = /datum/reagent/water
+ /// The minimum required purity of required_reagent
+ var/min_purity = 0
+
+/datum/experiment/scanning/reagent/final_contributing_index_checks(datum/component/experiment_handler/experiment_handler, atom/target, typepath)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(!is_reagent_container(target))
+ return FALSE
+ return is_valid_scan_target(experiment_handler, target)
+
+/datum/experiment/scanning/reagent/proc/is_valid_scan_target(datum/component/experiment_handler/experiment_handler, obj/item/reagent_containers/container)
+ SHOULD_CALL_PARENT(TRUE)
+ if (container.reagents.total_volume == 0)
+ experiment_handler.announce_message("Container empty!")
+ return FALSE
+ var/datum/reagent/master_reagent = container.reagents.get_master_reagent()
+ if (master_reagent.type != required_reagent)
+ experiment_handler.announce_message("Reagent not found!")
+ return FALSE
+ if (master_reagent.purity < min_purity)
+ experiment_handler.announce_message("Purity too low!")
+ return FALSE
+ return TRUE
+
+/datum/experiment/scanning/reagent/serialize_progress_stage(atom/target, list/seen_instances)
+ return EXPERIMENT_PROG_INT("Scan a reagent container with [required_reagent::name] of at least [PERCENT(min_purity)] purity.", \
+ seen_instances.len, required_atoms[target])
diff --git a/code/modules/experisci/experiment/types/scanning_vatgrown.dm b/code/modules/experisci/experiment/types/scanning_vatgrown.dm
index f4578fdf41238..0a7ade381a4ea 100644
--- a/code/modules/experisci/experiment/types/scanning_vatgrown.dm
+++ b/code/modules/experisci/experiment/types/scanning_vatgrown.dm
@@ -3,7 +3,7 @@
description = "Base experiment for scanning atoms that were vatgrown"
exp_tag = "Cytology Scan"
total_requirement = 1
- possible_types = list(/mob/living/basic/cockroach)
+ possible_types = list(/mob/living/basic/slime)
traits = EXPERIMENT_TRAIT_DESTRUCTIVE
/datum/experiment/scanning/random/cytology/final_contributing_index_checks(datum/component/experiment_handler/experiment_handler, atom/target, typepath)
diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm
index f4ae3548fa659..0bbf15352318d 100644
--- a/code/modules/mining/lavaland/megafauna_loot.dm
+++ b/code/modules/mining/lavaland/megafauna_loot.dm
@@ -755,7 +755,7 @@
"wings" = "None",
"frills" = "None",
"spines" = "Long",
- "body_markings" = "Dark Tiger Body",
+ "lizard_markings" = "Dark Tiger Body",
"legs" = DIGITIGRADE_LEGS,
)
consumer.eye_color_left = "#FEE5A3"
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 556a959e4f8eb..3c02de4707317 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -984,8 +984,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
game.ui_interact(usr)
/mob/dead/observer/CtrlShiftClickOn(atom/target)
- if(check_rights(R_SPAWN))
- change_mob_type(/mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
+ if(isobserver(target) && check_rights(R_SPAWN))
+ var/mob/dead/observer/target_ghost = target
+
+ target_ghost.change_mob_type(/mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
/mob/dead/observer/examine(mob/user)
. = ..()
diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
index b2aff7371a426..ce363af236465 100644
--- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
+++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
@@ -55,7 +55,7 @@
/mob/living/basic/gorilla/Initialize(mapload)
. = ..()
- add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP), ROUNDSTART_TRAIT)
+ add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_CHUNKYFINGERS), ROUNDSTART_TRAIT)
AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
AddElement(/datum/element/dextrous)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_BAREFOOT)
diff --git a/code/modules/mob/living/basic/slime/slime.dm b/code/modules/mob/living/basic/slime/slime.dm
index 4864cb82016a7..fa40ac28d816d 100644
--- a/code/modules/mob/living/basic/slime/slime.dm
+++ b/code/modules/mob/living/basic/slime/slime.dm
@@ -187,6 +187,8 @@
/mob/living/basic/slime/regenerate_icons()
cut_overlays()
+ if(slime_type.transparent)
+ alpha = SLIME_TRANSPARENCY_ALPHA
var/icon_text = "[slime_type.colour]-[life_stage]"
icon_dead = "[icon_text]-dead"
if(stat != DEAD)
diff --git a/code/modules/mob/living/basic/slime/slime_type.dm b/code/modules/mob/living/basic/slime/slime_type.dm
index 11f3798804017..048f861eeb08a 100644
--- a/code/modules/mob/living/basic/slime/slime_type.dm
+++ b/code/modules/mob/living/basic/slime/slime_type.dm
@@ -1,6 +1,8 @@
/datum/slime_type
///Our slime's colour as text. Used by both description, and icon
var/colour
+ ///Whether the slime icons should be semi-transparent
+ var/transparent = FALSE
///The type our slime spawns
var/core_type
///The possible mutations of our slime
@@ -12,6 +14,7 @@
/datum/slime_type/grey
colour = SLIME_TYPE_GREY
+ transparent = TRUE
core_type = /obj/item/slime_extract/grey
mutations = list(
/datum/slime_type/blue = 1,
@@ -21,11 +24,11 @@
)
rgb_code = COLOR_SLIME_GREY
-
//TIER 1
/datum/slime_type/blue
colour = SLIME_TYPE_BLUE
+ transparent = TRUE
core_type = /obj/item/slime_extract/blue
mutations = list(
/datum/slime_type/darkblue = 1,
@@ -46,6 +49,7 @@
/datum/slime_type/purple
colour = SLIME_TYPE_PURPLE
+ transparent = TRUE
core_type = /obj/item/slime_extract/purple
mutations = list(
/datum/slime_type/darkblue = 1,
@@ -56,6 +60,7 @@
/datum/slime_type/orange
colour = SLIME_TYPE_ORANGE
+ transparent = TRUE
core_type = /obj/item/slime_extract/orange
mutations = list(
/datum/slime_type/darkpurple = 1,
@@ -68,6 +73,7 @@
/datum/slime_type/darkblue
colour = SLIME_TYPE_DARK_BLUE
+ transparent = TRUE
core_type = /obj/item/slime_extract/darkblue
mutations = list(
/datum/slime_type/blue = 1,
@@ -98,6 +104,7 @@
/datum/slime_type/yellow
colour = SLIME_TYPE_YELLOW
+ transparent = TRUE
core_type = /obj/item/slime_extract/yellow
mutations = list(
/datum/slime_type/bluespace = 2,
@@ -119,6 +126,7 @@
/datum/slime_type/cerulean
colour = SLIME_TYPE_CERULEAN
+ transparent = TRUE
core_type = /obj/item/slime_extract/cerulean
mutations = list(
/datum/slime_type/cerulean = 1,
@@ -135,6 +143,7 @@
/datum/slime_type/sepia
colour = SLIME_TYPE_SEPIA
+ transparent = TRUE
core_type = /obj/item/slime_extract/sepia
mutations = list(
/datum/slime_type/sepia = 1,
@@ -154,6 +163,7 @@
/datum/slime_type/green
colour = SLIME_TYPE_GREEN
+ transparent = TRUE
core_type = /obj/item/slime_extract/green
mutations = list(
/datum/slime_type/black = 1,
@@ -163,6 +173,7 @@
/datum/slime_type/pink
colour = SLIME_TYPE_PINK
+ transparent = TRUE
core_type = /obj/item/slime_extract/pink
mutations = list(
/datum/slime_type/lightpink = 1,
@@ -172,6 +183,7 @@
/datum/slime_type/red
colour = SLIME_TYPE_RED
+ transparent = TRUE
core_type = /obj/item/slime_extract/red
mutations = list(
/datum/slime_type/oil = 1,
@@ -191,6 +203,7 @@
/datum/slime_type/black
colour = SLIME_TYPE_BLACK
+ transparent = TRUE
core_type = /obj/item/slime_extract/black
mutations = list(
/datum/slime_type/black = 1,
@@ -199,6 +212,7 @@
/datum/slime_type/lightpink
colour = SLIME_TYPE_LIGHT_PINK
+ transparent = TRUE
core_type = /obj/item/slime_extract/lightpink
mutations = list(
/datum/slime_type/lightpink = 1,
@@ -217,6 +231,7 @@
/datum/slime_type/rainbow
colour = SLIME_TYPE_RAINBOW
+ transparent = TRUE
core_type = /obj/item/slime_extract/rainbow
mutations = list(
/datum/slime_type/rainbow = 1,
diff --git a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
index be31e121249a6..2286f65b79758 100644
--- a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
+++ b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
@@ -77,6 +77,11 @@
buffet = new(src)
buffet.Grant(src)
+/mob/living/basic/space_dragon/Destroy()
+ fire_breath = null
+ buffet = null
+ return ..()
+
/mob/living/basic/space_dragon/Login()
. = ..()
if(!isnull(chosen_colour))
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index b9dca314b5189..d5d048a33f844 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -102,6 +102,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
///Replaces default appendix with a different organ.
var/obj/item/organ/internal/appendix/mutantappendix = /obj/item/organ/internal/appendix
+ /// Store body marking defines. See mobs.dm for bitflags
+ var/list/body_markings = list()
+
/// Flat modifier on all damage taken via [apply_damage][/mob/living/proc/apply_damage] (so being punched, shot, etc.)
/// IE: 10 = 10% less damage taken.
var/damage_modifier = 0
@@ -466,7 +469,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/obj/item/organ/external/new_organ = SSwardrobe.provide_type(organ_path)
new_organ.Insert(human, special=TRUE, movement_flags = DELETE_IF_REPLACED)
-
+ add_body_markings(human_who_gained_species)
if(length(inherent_traits))
human_who_gained_species.add_traits(inherent_traits, SPECIES_TRAIT)
@@ -526,6 +529,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
clear_tail_moodlets(C)
+ remove_body_markings(C)
+
// Removes all languages previously associated with [LANGUAGE_SPECIES], gaining our new species will add new ones back
var/datum/language_holder/losing_holder = GLOB.prototype_language_holders[species_language_holder]
for(var/language in losing_holder.understood_languages)
@@ -560,42 +565,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
eye_organ.refresh(call_update = FALSE)
standing += eye_organ.generate_body_overlay(species_human)
- // organic body markings (oh my god this is terrible please rework this to be done on the limbs themselves i beg you)
- if(HAS_TRAIT(species_human, TRAIT_HAS_MARKINGS))
- var/obj/item/bodypart/chest/chest = species_human.get_bodypart(BODY_ZONE_CHEST)
- var/obj/item/bodypart/arm/right/right_arm = species_human.get_bodypart(BODY_ZONE_R_ARM)
- var/obj/item/bodypart/arm/left/left_arm = species_human.get_bodypart(BODY_ZONE_L_ARM)
- var/obj/item/bodypart/leg/right/right_leg = species_human.get_bodypart(BODY_ZONE_R_LEG)
- var/obj/item/bodypart/leg/left/left_leg = species_human.get_bodypart(BODY_ZONE_L_LEG)
- var/datum/sprite_accessory/markings = SSaccessories.moth_markings_list[species_human.dna.features["moth_markings"]]
- var/mutable_appearance/marking = mutable_appearance(layer = -BODY_LAYER, appearance_flags = KEEP_TOGETHER)
-
- if(noggin && (IS_ORGANIC_LIMB(noggin)))
- var/mutable_appearance/markings_head_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_head")
- marking.overlays += markings_head_overlay
-
- if(chest && (IS_ORGANIC_LIMB(chest)))
- var/mutable_appearance/markings_chest_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_chest")
- marking.overlays += markings_chest_overlay
-
- if(right_arm && (IS_ORGANIC_LIMB(right_arm)))
- var/mutable_appearance/markings_r_arm_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_r_arm")
- marking.overlays += markings_r_arm_overlay
-
- if(left_arm && (IS_ORGANIC_LIMB(left_arm)))
- var/mutable_appearance/markings_l_arm_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_l_arm")
- marking.overlays += markings_l_arm_overlay
-
- if(right_leg && (IS_ORGANIC_LIMB(right_leg)))
- var/mutable_appearance/markings_r_leg_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_r_leg")
- marking.overlays += markings_r_leg_overlay
-
- if(left_leg && (IS_ORGANIC_LIMB(left_leg)))
- var/mutable_appearance/markings_l_leg_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_l_leg")
- marking.overlays += markings_l_leg_overlay
-
- standing += marking
-
//Underwear, Undershirts & Socks
if(!HAS_TRAIT(species_human, TRAIT_NO_UNDERWEAR))
if(species_human.underwear)
@@ -672,8 +641,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
switch(bodypart)
if("ears")
accessory = SSaccessories.ears_list[source.dna.features["ears"]]
- if("body_markings")
- accessory = SSaccessories.body_markings_list[source.dna.features["body_markings"]]
if("legs")
accessory = SSaccessories.legs_list[source.dna.features["legs"]]
@@ -727,6 +694,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
source.apply_overlay(BODY_ADJ_LAYER)
source.apply_overlay(BODY_FRONT_LAYER)
+ update_body_markings(source)
+
//This exists so sprite accessories can still be per-layer without having to include that layer's
//number in their sprite name, which causes issues when those numbers change.
/datum/species/proc/mutant_bodyparts_layertext(layer)
@@ -1519,6 +1488,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
|| (preference.relevant_inherent_trait in inherent_traits) \
|| (preference.relevant_external_organ in external_organs) \
|| (preference.relevant_head_flag && check_head_flags(preference.relevant_head_flag)) \
+ || (preference.relevant_body_markings in body_markings) \
)
features += preference.savefile_key
@@ -2110,3 +2080,48 @@ GLOBAL_LIST_EMPTY(features_by_species)
return fixed_mut_color
return null
+
+/// Add species appropriate body markings
+/datum/species/proc/add_body_markings(mob/living/carbon/human/hooman)
+ for(var/markings_type in body_markings) //loop through possible species markings
+ var/datum/bodypart_overlay/simple/body_marking/markings = new markings_type() // made to die... mostly because we cant use initial on lists but its convenient and organized
+ var/accessory_name = hooman.dna.features[markings.dna_feature_key] //get the accessory name from dna
+ var/datum/sprite_accessory/moth_markings/accessory = markings.get_accessory(accessory_name) //get the actual datum
+
+ if(isnull(accessory))
+ CRASH("Value: [accessory_name] did not have a corresponding sprite accessory!")
+
+ for(var/obj/item/bodypart/part as anything in markings.applies_to) //check through our limbs
+ var/obj/item/bodypart/people_part = hooman.get_bodypart(initial(part.body_zone)) // and see if we have a compatible marking for that limb
+
+ if(!people_part)
+ continue
+
+ var/datum/bodypart_overlay/simple/body_marking/overlay = new markings_type ()
+
+ // Tell the overlay what it should look like
+ overlay.icon = accessory.icon
+ overlay.icon_state = accessory.icon_state
+ overlay.use_gender = accessory.gender_specific
+ overlay.draw_color = accessory.color_src ? hooman.dna.features["mcolor"] : null
+
+ people_part.add_bodypart_overlay(overlay)
+
+/// Remove body markings
+/datum/species/proc/remove_body_markings(mob/living/carbon/human/hooman)
+ for(var/obj/item/bodypart/part as anything in hooman.bodyparts)
+ for(var/datum/bodypart_overlay/simple/body_marking/marking in part.bodypart_overlays)
+ part.remove_bodypart_overlay(marking)
+
+/// Update the overlays if necessary
+/datum/species/proc/update_body_markings(mob/living/carbon/human/hooman)
+ var/needs_update = FALSE
+ for(var/datum/bodypart_overlay/simple/body_marking/marking as anything in body_markings)
+ if(initial(marking.dna_feature_key) == body_markings[marking]) // dna is same as our species (sort of mini-cache), so no update needed
+ continue
+ needs_update = TRUE
+ break
+
+ if(needs_update)
+ remove_body_markings(hooman)
+ add_body_markings(hooman)
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index 627745cba929e..8d62ed5907948 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -103,7 +103,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
/proc/create_consistent_human_dna(mob/living/carbon/human/target)
target.dna.features["mcolor"] = COLOR_VIBRANT_LIME
target.dna.features["ethcolor"] = COLOR_WHITE
- target.dna.features["body_markings"] = get_consistent_feature_entry(SSaccessories.body_markings_list)
+ target.dna.features["lizard_markings"] = get_consistent_feature_entry(SSaccessories.lizard_markings_list)
target.dna.features["ears"] = get_consistent_feature_entry(SSaccessories.ears_list)
target.dna.features["frills"] = get_consistent_feature_entry(SSaccessories.frills_list)
target.dna.features["horns"] = get_consistent_feature_entry(SSaccessories.horns_list)
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index da2c057d740bb..52ffe51bfe68b 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -293,6 +293,11 @@
return MONKEY_HEIGHT_DWARF
else
return HUMAN_HEIGHT_DWARF
+ if(HAS_TRAIT(src, TRAIT_TOO_TALL))
+ if(ismonkey(src))
+ return MONKEY_HEIGHT_TALL
+ else
+ return HUMAN_HEIGHT_TALLEST
else if(ismonkey(src))
return MONKEY_HEIGHT_MEDIUM
@@ -360,3 +365,30 @@
var/damage = ((min_damage / 4) + (max_damage / 4)) / 2 // We expect you to have 4 functional limbs- if you have fewer you're probably not going to be so good at lifting
return ceil(damage * (ceil(athletics_level / 2)) * fitness_modifier * maxHealth)
+
+/mob/living/carbon/human/proc/item_heal(mob/user, brute_heal, burn_heal, heal_message_brute, heal_message_burn, required_bodytype)
+ var/obj/item/bodypart/affecting = src.get_bodypart(check_zone(user.zone_selected))
+ if (!affecting || !(affecting.bodytype == required_bodytype))
+ to_chat(user, span_warning("[affecting] is already in good condition!"))
+ return FALSE
+
+ var/brute_damaged = affecting.brute_dam > 0
+ var/burn_damaged = affecting.burn_dam > 0
+
+ var/nothing_to_heal = ((brute_heal <= 0 || !brute_damaged) && (burn_heal <= 0 || !burn_damaged))
+ if (nothing_to_heal)
+ to_chat(user, span_notice("[affecting] is already in good condition!"))
+ return FALSE
+
+ src.update_damage_overlays()
+ var/message
+ if ((brute_damaged && brute_heal > 0) && (burn_damaged && burn_heal > 0))
+ message = "[heal_message_brute] and [heal_message_burn] on"
+ else if (brute_damaged && brute_heal > 0)
+ message = "[heal_message_brute] on"
+ else
+ message = "[heal_message_burn] on"
+ affecting.heal_damage(brute_heal, burn_heal, required_bodytype)
+ user.visible_message(span_notice("[user] fixes some of the [message] [src]'s [affecting.name]."), \
+ span_notice("You fix some of the [message] [src == user ? "your" : "[src]'s"] [affecting.name]."))
+ return TRUE
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index bd320079b65d5..fe5817eab2780 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -864,20 +864,34 @@ generate/load female uniform sprites matching all previously decided variables
"params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 4),
),
))
+ if(HUMAN_HEIGHT_DWARF) // tall monkeys and dwarves use the same value
+ if(ismonkey(src))
+ appearance.add_filters(list(
+ list(
+ "name" = "Monkey_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 1),
+ ),
+ list(
+ "name" = "Monkey_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 1),
+ ),
+ ))
+ else
+ appearance.add_filters(list(
+ list(
+ "name" = "Gnome_Cut_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 2),
+ ),
+ list(
+ "name" = "Gnome_Cut_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 3),
+ ),
+ ))
// Don't set this one directly, use TRAIT_DWARF
- if(HUMAN_HEIGHT_DWARF)
- appearance.add_filters(list(
- list(
- "name" = "Gnome_Cut_Torso",
- "priority" = 1,
- "params" = displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 2),
- ),
- list(
- "name" = "Gnome_Cut_Legs",
- "priority" = 1,
- "params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 3),
- ),
- ))
if(HUMAN_HEIGHT_SHORTEST)
appearance.add_filters(list(
list(
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index b2a036a9d6d71..ba813901b3e87 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -8,7 +8,8 @@
TRAIT_TACKLING_TAILED_DEFENDER,
)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_REPTILE
- mutant_bodyparts = list("body_markings" = "None", "legs" = "Normal Legs")
+ mutant_bodyparts = list("legs" = "Normal Legs")
+ body_markings = list(/datum/bodypart_overlay/simple/body_marking/lizard = "None")
external_organs = list(
/obj/item/organ/external/horns = "None",
/obj/item/organ/external/frills = "None",
@@ -49,7 +50,7 @@
/datum/species/lizard/randomize_features()
var/list/features = ..()
- features["body_markings"] = pick(SSaccessories.body_markings_list)
+ features["lizard_markings"] = pick(SSaccessories.lizard_markings_list)
return features
/datum/species/lizard/get_scream_sound(mob/living/carbon/human/lizard)
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index e2458563b038c..26efe358221fc 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -3,12 +3,11 @@
plural_form = "Mothmen"
id = SPECIES_MOTH
inherent_traits = list(
- TRAIT_HAS_MARKINGS,
TRAIT_TACKLING_WINGED_ATTACKER,
TRAIT_ANTENNAE,
)
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG
- mutant_bodyparts = list("moth_markings" = "None")
+ body_markings = list(/datum/bodypart_overlay/simple/body_marking/moth = "None")
external_organs = list(/obj/item/organ/external/wings/moth = "Plain", /obj/item/organ/external/antennae = "Plain")
meat = /obj/item/food/meat/slab/human/mutant/moth
mutanttongue = /obj/item/organ/internal/tongue/moth
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 51b2f3954973d..7a205da5c089f 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -486,7 +486,8 @@
. = ..()
if(has_gravity())
for(var/mob/living/carbon/human/future_pancake in loc)
- run_over(future_pancake)
+ if(future_pancake.body_position == LYING_DOWN)
+ run_over(future_pancake)
diag_hud_set_mulebotcell()
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 9092efac428ca..a6a1c58c7eddf 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -331,19 +331,6 @@
toast.name = header
toast.target_ref = WEAKREF(source)
-/// Heals a robotic limb on a mob
-/proc/item_heal_robotic(mob/living/carbon/human/human, mob/user, brute_heal, burn_heal)
- var/obj/item/bodypart/affecting = human.get_bodypart(check_zone(user.zone_selected))
- if(!affecting || IS_ORGANIC_LIMB(affecting))
- to_chat(user, span_warning("[affecting] is already in good condition!"))
- return FALSE
- var/brute_damage = brute_heal > burn_heal //changes repair text based on how much brute/burn was supplied
- if((brute_heal > 0 && affecting.brute_dam > 0) || (burn_heal > 0 && affecting.burn_dam > 0))
- if(affecting.heal_damage(brute_heal, burn_heal, required_bodytype = BODYTYPE_ROBOTIC))
- human.update_damage_overlays()
- user.visible_message(span_notice("[user] fixes some of the [brute_damage ? "dents on" : "burnt wires in"] [human]'s [affecting.name]."), \
- span_notice("You fix some of the [brute_damage ? "dents on" : "burnt wires in"] [human == user ? "your" : "[human]'s"] [affecting.name]."))
- return TRUE //successful heal
///Is the passed in mob a ghost with admin powers, doesn't check for AI interact like isAdminGhost() used to
diff --git a/code/modules/power/apc/apc_malf.dm b/code/modules/power/apc/apc_malf.dm
index f4c27e15a4067..1419e12c46be3 100644
--- a/code/modules/power/apc/apc_malf.dm
+++ b/code/modules/power/apc/apc_malf.dm
@@ -36,6 +36,12 @@
return
if(!is_station_level(z))
return
+ INVOKE_ASYNC(src, PROC_REF(malfshunt), malf)
+
+/obj/machinery/power/apc/proc/malfshunt(mob/living/silicon/ai/malf)
+ var/confirm = tgui_alert(malf, "Are you sure that you want to shunt? This will take you out of your core!", "Shunt to [name]?", list("Yes", "No"))
+ if(confirm != "Yes")
+ return
malf.ShutOffDoomsdayDevice()
occupier = malf
if (isturf(malf.loc)) // create a deactivated AI core if the AI isn't coming from an emergency mech shunt
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index e55442f31fd1f..a8e20cde8c7a0 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -575,7 +575,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
user.visible_message(span_notice("[user] starts to fix some of the wires in [H]'s [affecting.name]."), span_notice("You start fixing some of the wires in [H == user ? "your" : "[H]'s"] [affecting.name]."))
if(!do_after(user, 5 SECONDS, H))
return
- if(item_heal_robotic(H, user, 0, 15))
+ if(H.item_heal(user, 0, 15, "dents", "burnt wires", BODYTYPE_ROBOTIC))
use(1)
return
else
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index c7fefc745f7e0..7237ee0e32747 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -254,6 +254,8 @@
require_model = TRUE
model_type = list(/obj/item/robot_model/miner)
model_flags = BORG_MODEL_MINER
+ //Most modkits are supposed to allow duplicates. The ones that don't should be blocked by PKA code anyways.
+ allow_duplicates = TRUE
var/denied_type = null
var/maximum_of_type = 1
var/cost = 30
diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm
index f7fc1b04ac8de..40305c9a8bc47 100644
--- a/code/modules/reagents/chemistry/recipes.dm
+++ b/code/modules/reagents/chemistry/recipes.dm
@@ -281,7 +281,7 @@
* * modifier - a flat additive numeric to the size of the explosion - set this if you want a minimum range
* * strengthdiv - the divisional factor of the explosion, a larger number means a smaller range - This is the part that modifies an explosion's range with volume (i.e. it divides it by this number)
*/
-/datum/chemical_reaction/proc/default_explode(datum/reagents/holder, created_volume, modifier = 0, strengthdiv = 10)
+/datum/chemical_reaction/proc/default_explode(datum/reagents/holder, created_volume, modifier = 0, strengthdiv = 10, clear_mob_reagents)
var/power = modifier + round(created_volume/strengthdiv, 1)
if(power > 0)
var/turf/T = get_turf(holder.my_atom)
@@ -300,8 +300,29 @@
var/datum/effect_system/reagents_explosion/e = new()
e.set_up(power , T, 0, 0)
e.start(holder.my_atom)
- holder.clear_reagents()
-
+ if (ismob(holder.my_atom))
+ if(!clear_mob_reagents)
+ return
+ // Only clear reagents if they use a special explosive reaction to do it; it shouldn't apply
+ // to any explosion inside a person
+ holder.clear_reagents()
+ if(iscarbon(holder.my_atom))
+ var/mob/living/carbon/victim = holder.my_atom
+ var/vomit_flags = MOB_VOMIT_MESSAGE | MOB_VOMIT_FORCE
+ // The vomiting here is for effect, not meant to help with purging
+ victim.vomit(vomit_flags, distance = 5)
+ // Not quite the same if the reaction is in their stomach; they'll throw up
+ // from any explosion, but it'll only make them puke up everything in their
+ // stomach
+ else if (istype(holder.my_atom, /obj/item/organ/internal/stomach))
+ var/obj/item/organ/internal/stomach/indigestion = holder.my_atom
+ if(power < 1)
+ return
+ indigestion.owner?.vomit(MOB_VOMIT_MESSAGE | MOB_VOMIT_FORCE, lost_nutrition = 150, distance = 5, purge_ratio = 1)
+ holder.clear_reagents()
+ return
+ else
+ holder.clear_reagents()
/*
*Creates a flash effect only - less expensive than explode()
*
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index d190a44aea063..f02aaa3ab2473 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -1,13 +1,48 @@
+#define PURGING_REAGENTS list( \
+ /datum/reagent/medicine/c2/multiver, \
+ /datum/reagent/medicine/pen_acid, \
+ /datum/reagent/medicine/calomel, \
+ /datum/reagent/medicine/ammoniated_mercury, \
+ /datum/reagent/medicine/c2/syriniver, \
+ /datum/reagent/medicine/c2/musiver \
+)
+
/datum/chemical_reaction/reagent_explosion
var/strengthdiv = 10
var/modifier = 0
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EXPLOSIVE | REACTION_TAG_MODERATE | REACTION_TAG_DANGEROUS
required_temp = 0 //Prevent impromptu RPGs
-
-/datum/chemical_reaction/reagent_explosion/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
- default_explode(holder, created_volume, modifier, strengthdiv)
-
+ // Only clear mob reagents in special cases
+ var/clear_mob_reagents = FALSE
+
+/datum/chemical_reaction/reagent_explosion/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume, clear_mob_reagents)
+ // If an explosive reaction clears mob reagents, it should always be a minimum power
+ if(ismob(holder.my_atom) && clear_mob_reagents)
+ if(round((created_volume / strengthdiv) + modifier, 1) < 1)
+ modifier += 1 - ((created_volume / strengthdiv) + modifier)
+ // If this particular explosion doesn't automatically clear mob reagents as an inherent quality,
+ // then we can still clear mob reagents with some mad science malpractice that shouldn't work but
+ // does because omnizine is magic and also it's the future or whatever
+ if(ismob(holder.my_atom) && !clear_mob_reagents)
+ // The explosion needs to be a minimum power to clear reagents: see above
+ var/purge_power = round((created_volume / strengthdiv) + modifier, 1)
+ if(purge_power >= 1)
+ var/has_purging_chemical = FALSE
+ // They need one of the purge reagents in them
+ for(var/purging_chem as anything in PURGING_REAGENTS)
+ if(holder.has_reagent(purging_chem))
+ // We have a purging chemical
+ has_purging_chemical = TRUE
+ break
+ // Then we need omnizine! MAGIC!
+ var/has_omnizine = holder.has_reagent(/datum/reagent/medicine/omnizine)
+ if(has_purging_chemical && has_omnizine)
+ // With all this medical "science" combined, we can clear mob reagents
+ clear_mob_reagents = TRUE
+ default_explode(holder, created_volume, modifier, strengthdiv, clear_mob_reagents)
+
+#undef PURGING_REAGENTS
/datum/chemical_reaction/reagent_explosion/nitroglycerin
results = list(/datum/reagent/nitroglycerin = 2)
required_reagents = list(/datum/reagent/glycerol = 1, /datum/reagent/toxin/acid/nitracid = 1, /datum/reagent/toxin/acid = 1)
@@ -104,11 +139,18 @@
/datum/chemical_reaction/reagent_explosion/penthrite_explosion_epinephrine
required_reagents = list(/datum/reagent/medicine/c2/penthrite = 1, /datum/reagent/medicine/epinephrine = 1)
strengthdiv = 5
+ // Penthrite is rare as hell, so this clears your reagents
+ // Will most likely be from miners accidentally penstacking
+ clear_mob_reagents = TRUE
+
/datum/chemical_reaction/reagent_explosion/penthrite_explosion_atropine
required_reagents = list(/datum/reagent/medicine/c2/penthrite = 1, /datum/reagent/medicine/atropine = 1)
strengthdiv = 5
modifier = 5
+ // Rare reagents clear your reagents
+ // Probably not good for you because you'll need healing chems to survive this most likely
+ clear_mob_reagents = TRUE
/datum/chemical_reaction/reagent_explosion/potassium_explosion
required_reagents = list(/datum/reagent/water = 1, /datum/reagent/potassium = 1)
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
deleted file mode 100644
index 1c6e6f2b9f7ee..0000000000000
--- a/code/modules/research/techweb/all_nodes.dm
+++ /dev/null
@@ -1,2498 +0,0 @@
-
-//Current rate: 135000 research points in 90 minutes
-
-//Base Nodes
-/datum/techweb_node/base
- id = "base"
- starting_node = TRUE
- display_name = "Basic Research Technology"
- description = "NT default research technologies."
- // Default research tech, prevents bricking
- design_ids = list(
- "basic_capacitor",
- "basic_cell",
- "basic_matter_bin",
- "basic_micro_laser",
- "basic_scanning",
- "blast",
- "bounced_radio",
- "bowl",
- "bucket",
- "c-reader",
- "c38_rubber",
- "camera_assembly",
- "camera_film",
- "camera",
- "capbox",
- "chisel",
- "circuit_imprinter_offstation",
- "circuit_imprinter",
- "circuit",
- "circuitgreen",
- "circuitred",
- "coffee_cartridge",
- "coffeemaker",
- "coffeepot",
- "condenser",
- "conveyor_belt",
- "conveyor_switch",
- "custom_vendor_refill",
- "destructive_analyzer",
- "destructive_scanner",
- "desttagger",
- "doppler_array",
- "drinking_glass",
- "earmuffs",
- "electropack",
- "experi_scanner",
- "experimentor",
- "extinguisher",
- "fax",
- "fish_case",
- "fishing_rod",
- "fishing_portal_generator",
- "flashlight",
- "fluid_ducts",
- "foam_dart",
- "fork",
- "gas_filter",
- "handcuffs_s",
- "handlabel",
- "health_sensor",
- "holodisk",
- "igniter",
- "infrared_emitter",
- "intercom_frame",
- "kitchen_knife",
- "laptop",
- "light_bulb",
- "light_replacer",
- "light_tube",
- "mechfab",
- "micro_servo",
- "miniature_power_cell",
- "newscaster_frame",
- "oven_tray",
- "packagewrap",
- "pet_carrier",
- "plasmaglass",
- "plasmaman_gas_filter",
- "plasmareinforcedglass",
- "plasteel",
- "plastic_fork",
- "plastic_knife",
- "plastic_spoon",
- "plastitanium",
- "plastitaniumglass",
- "plate",
- "prox_sensor",
- "radio_headset",
- "rdconsole",
- "rdserver",
- "rdservercontrol",
- "recorder",
- "rglass",
- "roll",
- "sec_38",
- "sec_beanbag_slug",
- "sec_dart",
- "sec_Islug",
- "sec_rshot",
- "sec_pen",
- "servingtray",
- "shaker",
- "shot_glass",
- "signaler",
- "slime_scanner",
- "solar_panel",
- "solar_tracker",
- "souppot",
- "space_heater",
- "spoon",
- "status_display_frame",
- "sticky_tape",
- "syrup_bottle",
- "tape",
- "tech_disk",
- "timer",
- "titaniumglass",
- "toner_large",
- "toner",
- "tongs",
- "toy_armblade",
- "toy_balloon",
- "toygun",
- "tram_floor_dark",
- "tram_floor_light",
- "trapdoor_electronics",
- "turbine_part_compressor",
- "turbine_part_rotor",
- "turbine_part_stator",
- "turret_control",
- "universal_scanner",
- "voice_analyzer",
- "watering_can",
- )
- experiments_to_unlock = list(
- /datum/experiment/autopsy/nonhuman,
- /datum/experiment/scanning/random/material/medium/one,
- /datum/experiment/scanning/random/material/medium/three,
- /datum/experiment/scanning/random/material/hard/one,
- /datum/experiment/scanning/random/material/hard/two,
- /datum/experiment/scanning/people/novel_organs,
- )
-
-/datum/techweb_node/mmi
- id = "mmi"
- starting_node = TRUE
- display_name = "Man Machine Interface"
- description = "A slightly Frankensteinian device that allows human brains to interface natively with software APIs."
- design_ids = list(
- "mmi",
- )
-
-/datum/techweb_node/cyborg
- id = "cyborg"
- starting_node = TRUE
- display_name = "Cyborg Construction"
- description = "Sapient robots with preloaded tool modules and programmable laws."
- design_ids = list(
- "borg_chest",
- "borg_head",
- "borg_l_arm",
- "borg_l_leg",
- "borg_r_arm",
- "borg_r_leg",
- "borg_suit",
- "borg_upgrade_rename",
- "borg_upgrade_restart",
- "borgupload",
- "cyborgrecharger",
- "robocontrol",
- "sflash",
- )
-
-/datum/techweb_node/mech
- id = "mecha"
- starting_node = TRUE
- display_name = "Mechanical Exosuits"
- description = "Mechanized exosuits that are several magnitudes stronger and more powerful than the average human."
- design_ids = list(
- "mech_recharger",
- "mecha_tracking",
- "mechacontrol",
- "mechapower",
- "ripley_chassis",
- "ripley_left_arm",
- "ripley_left_leg",
- "ripley_main",
- "ripley_peri",
- "ripley_right_arm",
- "ripley_right_leg",
- "ripley_torso",
- "ripleyupgrade",
- "mech_hydraulic_clamp",
- "mech_radio",
- "mech_air_tank",
- "mech_thrusters",
- )
-
-/datum/techweb_node/mod_basic
- id = "mod"
- starting_node = TRUE
- display_name = "Basic Modular Suits"
- description = "Specialized back mounted power suits with various different modules."
- design_ids = list(
- "mod_boots",
- "mod_chestplate",
- "mod_gauntlets",
- "mod_helmet",
- "mod_paint_kit",
- "mod_shell",
- "mod_plating_standard",
- "mod_storage",
- "mod_welding",
- "mod_safety",
- "mod_mouthhole",
- "mod_flashlight",
- "mod_longfall",
- "mod_thermal_regulator",
- "mod_plasma",
- "mod_sign_radio",
- )
-
-/datum/techweb_node/mech_tools
- id = "mech_tools"
- starting_node = TRUE
- display_name = "Basic Exosuit Equipment"
- description = "Various tools fit for basic mech units"
- design_ids = list(
- "mech_drill",
- "mech_extinguisher",
- "mech_mscanner",
- )
-
-/datum/techweb_node/basic_tools
- id = "basic_tools"
- starting_node = TRUE
- display_name = "Basic Tools"
- description = "Basic mechanical, electronic, surgical and botanical tools."
- design_ids = list(
- "airlock_painter",
- "analyzer",
- "boxcutter",
- "cable_coil",
- "cable_coil",
- "crowbar",
- "cultivator",
- "decal_painter",
- "hatchet",
- "mop",
- "multitool",
- "normtrash",
- "pipe_painter",
- "plant_analyzer",
- "plunger",
- "pushbroom",
- "rwd",
- "razor",
- "screwdriver",
- "secateurs",
- "shovel",
- "spade",
- "spraycan",
- "tile_sprayer",
- "tscanner",
- "welding_helmet",
- "welding_tool",
- "wirebrush",
- "wirecutters",
- "wrench",
- "pickaxe",
- )
-
-/datum/techweb_node/basic_medical
- id = "basic_medical"
- starting_node = TRUE
- display_name = "Basic Medical Equipment"
- description = "Basic medical tools and equipment."
- design_ids = list(
- "beaker",
- "biopsy_tool",
- "blood_filter",
- "bonesetter",
- "cautery",
- "circular_saw",
- "cybernetic_ears",
- "cybernetic_eyes",
- "cybernetic_eyes_moth",
- "cybernetic_heart",
- "cybernetic_liver",
- "cybernetic_lungs",
- "cybernetic_stomach",
- "defibmountdefault",
- "dropper",
- "hemostat",
- "large_beaker",
- "medicalbed",
- "mmi_m",
- "operating",
- "petri_dish",
- "pillbottle",
- "plumbing_rcd",
- "plumbing_rcd_service",
- "plumbing_rcd_sci",
- "portable_chem_mixer",
- "penlight",
- "retractor",
- "scalpel",
- "stethoscope",
- "surgical_drapes",
- "surgical_tape",
- "surgicaldrill",
- "swab",
- "syringe",
- "xlarge_beaker",
- )
-
-/datum/techweb_node/basic_circuitry
- id = "basic_circuitry"
- starting_node = TRUE
- display_name = "Basic Integrated Circuits"
- description = "Research on how to fully exploit the power of integrated circuits"
- design_ids = list(
- "circuit_multitool",
- "comp_access_checker",
- "comp_arctan2",
- "comp_arithmetic",
- "comp_assoc_list_pick",
- "comp_assoc_list_remove",
- "comp_assoc_list_set",
- "comp_binary_convert",
- "comp_clock",
- "comp_comparison",
- "comp_concat",
- "comp_concat_list",
- "comp_decimal_convert",
- "comp_delay",
- "comp_direction",
- "comp_element_find",
- "comp_filter_list",
- "comp_foreach",
- "comp_format",
- "comp_format_assoc",
- "comp_get_column",
- "comp_gps",
- "comp_health",
- "comp_health_state",
- "comp_hear",
- "comp_id_access_reader",
- "comp_id_getter",
- "comp_id_info_reader",
- "comp_index",
- "comp_index_assoc",
- "comp_index_table",
- "comp_laserpointer",
- "comp_length",
- "comp_light",
- "comp_list_add",
- "comp_list_assoc_literal",
- "comp_list_clear",
- "comp_list_literal",
- "comp_list_pick",
- "comp_list_remove",
- "comp_logic",
- "comp_matscanner",
- "comp_mmi",
- "comp_module",
- "comp_multiplexer",
- "comp_not",
- "comp_ntnet_receive",
- "comp_ntnet_send",
- "comp_ntnet_send_list_literal",
- "comp_pinpointer",
- "comp_pressuresensor",
- "comp_radio",
- "comp_random",
- "comp_reagents",
- "comp_router",
- "comp_select_query",
- "comp_self",
- "comp_set_variable_trigger",
- "comp_soundemitter",
- "comp_species",
- "comp_speech",
- "comp_speech",
- "comp_split",
- "comp_string_contains",
- "comp_tempsensor",
- "comp_textcase",
- "comp_timepiece",
- "comp_toggle",
- "comp_tonumber",
- "comp_tostring",
- "comp_trigonometry",
- "comp_typecast",
- "comp_typecheck",
- "comp_view_sensor",
- "compact_remote_shell",
- "component_printer",
- "integrated_circuit",
- "module_duplicator",
- "usb_cable"
- )
-
-/////////////////////////Biotech/////////////////////////
-
-/datum/techweb_node/biotech
- id = "biotech"
- display_name = "Biological Technology"
- description = "What makes us tick." //the MC, silly!
- prereq_ids = list("base")
- design_ids = list(
- "beer_dispenser",
- "blood_pack",
- "chem_dispenser",
- "chem_heater",
- "chem_mass_spec",
- "chem_master",
- "chem_pack",
- "defibmount",
- "defibrillator",
- "genescanner",
- "healthanalyzer",
- "med_spray_bottle",
- "medical_kiosk",
- "medigel",
- "medipen_refiller",
- "pandemic",
- "penlight_paramedic",
- "soda_dispenser",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- required_experiments = list(/datum/experiment/autopsy/human)
-
-/datum/techweb_node/adv_biotech
- id = "adv_biotech"
- display_name = "Advanced Biotechnology"
- description = "Advanced Biotechnology"
- prereq_ids = list("biotech")
- design_ids = list(
- "autopsyscanner",
- "crewpinpointer",
- "defibrillator_compact",
- "harvester",
- "healthanalyzer_advanced",
- "holobarrier_med",
- "limbgrower",
- "meta_beaker",
- "ph_meter",
- "piercesyringe",
- "plasmarefiller",
- "smoke_machine",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- required_experiments = list(/datum/experiment/autopsy/nonhuman)
- discount_experiments = list(/datum/experiment/scanning/random/material/meat = 4000)
-
-/datum/techweb_node/xenoorgan_biotech
- id = "xenoorgan_bio"
- display_name = "Xeno-organ Biology"
- description = "Plasmaman, Ethereals, Lizardpeople... What makes our non-human crewmembers tick?"
- prereq_ids = list("adv_biotech")
- design_ids = list(
- "limbdesign_ethereal",
- "limbdesign_felinid",
- "limbdesign_lizard",
- "limbdesign_plasmaman",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 6500)
- discount_experiments = list(
- /datum/experiment/scanning/random/cytology/easy = 1000,
- /datum/experiment/scanning/points/slime/hard = 5000,
- /datum/experiment/autopsy/xenomorph = 5000,
- )
-
-/datum/techweb_node/morphological_theory
- id = "morphological_theory"
- display_name = "Anomalous Morphology"
- description = "Use poorly understood energies to change your body."
- prereq_ids = list("adv_biotech", "anomaly_research")
- design_ids = list("polymorph_belt")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(
- /datum/experiment/scanning/people/novel_organs = 5000,
- )
-
-/datum/techweb_node/bio_process
- id = "bio_process"
- display_name = "Biological Processing"
- description = "From slimes to kitchens."
- prereq_ids = list("biotech")
- design_ids = list(
- "deepfryer",
- "dish_drive",
- "fat_sucker",
- "gibber",
- "griddle",
- "microwave",
- "microwave_engineering",
- "monkey_recycler",
- "oven",
- "processor",
- "range", // should be in a further node, probably
- "reagentgrinder",
- "smartfridge",
- "stove",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
- discount_experiments = list(/datum/experiment/scanning/random/cytology = 3000) //Big discount to reinforce doing it.
-
-/datum/techweb_node/marine_util
- id = "marine_util"
- display_name = "Marine Utility"
- description = "Fish are nice to look at and all, but they can be put to use."
- prereq_ids = list("bio_process")
- design_ids = list(
- "bioelec_gen",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
- // only available if you've done the first fishing experiment (thus unlocking fishing tech), but not a strict requirement to get the tech
- discount_experiments = list(/datum/experiment/scanning/fish/second = 3000)
-
-/////////////////////////Advanced Surgery/////////////////////////
-
-/datum/techweb_node/imp_wt_surgery
- id = "imp_wt_surgery"
- display_name = "Improved Wound-Tending Surgery"
- description = "Who would have known being more gentle with a hemostat decreases patient pain?"
- prereq_ids = list("biotech")
- design_ids = list(
- "surgery_heal_brute_upgrade",
- "surgery_heal_burn_upgrade",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-/datum/techweb_node/oldstation_surgery
- id = "oldstation_surgery"
- display_name = "Experimental Dissection"
- description = "Grants access to experimental dissections, which allows generation of research points."
- design_ids = list(
- "surgery_oldstation_dissection",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
- hidden = TRUE
- show_on_wiki = FALSE
-
-/datum/techweb_node/adv_surgery
- id = "adv_surgery"
- display_name = "Advanced Surgery"
- description = "When simple medicine doesn't cut it."
- prereq_ids = list("imp_wt_surgery")
- design_ids = list(
- "surgery_heal_brute_upgrade_femto",
- "surgery_heal_burn_upgrade_femto",
- "surgery_heal_combo",
- "surgery_lobotomy",
- "surgery_wing_reconstruction",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
-
-/datum/techweb_node/exp_surgery
- id = "exp_surgery"
- display_name = "Experimental Surgery"
- description = "When evolution isn't fast enough."
- prereq_ids = list("adv_surgery")
- design_ids = list(
- "surgery_cortex_folding",
- "surgery_cortex_imprint",
- "surgery_heal_combo_upgrade",
- "surgery_ligament_hook",
- "surgery_ligament_reinforcement",
- "surgery_muscled_veins",
- "surgery_nerve_ground",
- "surgery_nerve_splice",
- "surgery_pacify",
- "surgery_vein_thread",
- "surgery_viral_bond",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
- discount_experiments = list(/datum/experiment/scanning/random/plants/traits = 4500)
-
-/datum/techweb_node/alien_surgery
- id = "alien_surgery"
- display_name = "Alien Surgery"
- description = "Abductors did nothing wrong."
- prereq_ids = list("exp_surgery", "alientech")
- design_ids = list(
- "surgery_brainwashing",
- "surgery_heal_combo_upgrade_femto",
- "surgery_zombie",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
-
-/////////////////////////data theory tech/////////////////////////
-
-/datum/techweb_node/datatheory //Computer science
- id = "datatheory"
- display_name = "Data Theory"
- description = "Big Data, in space!"
- prereq_ids = list("base")
- design_ids = list(
- "bounty_pad",
- "bounty_pad_control",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-
-/////////////////////////engineering tech/////////////////////////
-
-/datum/techweb_node/engineering
- id = "engineering"
- display_name = "Industrial Engineering"
- description = "A refresher course on modern engineering technology."
- prereq_ids = list("base")
- design_ids = list(
- "adv_capacitor",
- "adv_matter_bin",
- "adv_scanning",
- "airalarm_electronics",
- "airlock_board",
- "anomaly_refinery",
- "apc_control",
- "atmos_control",
- "atmos_thermal",
- "atmosalerts",
- "autolathe",
- "cell_charger",
- "crystallizer",
- "electrolyzer",
- "emergency_oxygen_engi",
- "emergency_oxygen",
- "emitter",
- "mass_driver",
- "firealarm_electronics",
- "firelock_board",
- "generic_tank",
- "grounding_rod",
- "high_cell",
- "high_micro_laser",
- "mesons",
- "nano_servo",
- "oxygen_tank",
- "pacman",
- "plasma_tank",
- "plasmaman_tank_belt",
- "pneumatic_seal",
- "power_control",
- "powermonitor",
- "recharger",
- "recycler",
- "rped",
- "scanner_gate",
- "solarcontrol",
- "stack_console",
- "stack_machine",
- "suit_storage_unit",
- "tank_compressor",
- "tesla_coil",
- "thermomachine",
- "w-recycler",
- "welding_goggles",
- "flatpacker",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 12500)
- discount_experiments = list(/datum/experiment/scanning/random/material/easy = 7500)
- experiments_to_unlock = list(/datum/experiment/scanning/points/machinery_pinpoint_scan/tier2_microlaser)
-
-/datum/techweb_node/adv_engi
- id = "adv_engi"
- display_name = "Advanced Engineering"
- description = "Pushing the boundaries of physics, one chainsaw-fist at a time."
- prereq_ids = list("engineering", "emp_basic")
- design_ids = list(
- "HFR_core",
- "HFR_corner",
- "HFR_fuel_input",
- "HFR_interface",
- "HFR_moderator_input",
- "HFR_waste_output",
- "engine_goggles",
- "forcefield_projector",
- "magboots",
- "rcd_loaded",
- "rcd_ammo",
- "rpd_loaded",
- "rtd_loaded",
- "sheetifier",
- "weldingmask",
- "bolter_wrench",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 15000)
- discount_experiments = list(
- /datum/experiment/scanning/random/material/medium/one = 4000,
- /datum/experiment/ordnance/gaseous/bz = 10000,
- )
-
-/datum/techweb_node/anomaly
- id = "anomaly_research"
- display_name = "Anomaly Research"
- description = "Unlock the potential of the mysterious anomalies that appear on station."
- prereq_ids = list("adv_engi", "practical_bluespace")
- design_ids = list(
- "anomaly_neutralizer",
- "reactive_armour",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
-
-/datum/techweb_node/high_efficiency
- id = "high_efficiency"
- display_name = "High Efficiency Parts"
- description = "Finely-tooled manufacturing techniques allowing for picometer-perfect precision levels."
- prereq_ids = list("engineering", "datatheory")
- design_ids = list(
- "pico_servo",
- "super_matter_bin",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier2_lathes = 5000)
-
-/datum/techweb_node/adv_power
- id = "adv_power"
- display_name = "Advanced Power Manipulation"
- description = "How to get more zap."
- prereq_ids = list("engineering")
- design_ids = list(
- "hyper_cell",
- "power_turbine_console",
- "smes",
- "super_capacitor",
- "super_cell",
- "turbine_compressor",
- "turbine_rotor",
- "turbine_stator",
- "modular_shield_generator",
- "modular_shield_node",
- "modular_shield_relay",
- "modular_shield_charger",
- "modular_shield_well",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_pinpoint_scan/tier2_capacitors = 2500)
-
-/////////////////////////Bluespace tech/////////////////////////
-/datum/techweb_node/bluespace_basic //Bluespace-memery
- id = "bluespace_basic"
- display_name = "Basic Bluespace Theory"
- description = "Basic studies into the mysterious alternate dimension known as bluespace."
- prereq_ids = list("base")
- design_ids = list(
- "beacon",
- "bluespace_crystal",
- "telesci_gps",
- "xenobioconsole",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/bluespace_travel
- id = "bluespace_travel"
- display_name = "Bluespace Travel"
- description = "Application of Bluespace for static teleportation technology."
- prereq_ids = list("practical_bluespace")
- design_ids = list(
- "bluespace_pod",
- "launchpad",
- "launchpad_console",
- "quantumpad",
- "tele_hub",
- "tele_station",
- "teleconsole",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_bluespacemachines = 4000)
-
-/datum/techweb_node/micro_bluespace
- id = "micro_bluespace"
- display_name = "Miniaturized Bluespace Research"
- description = "Extreme reduction in space required for bluespace engines, leading to portable bluespace technology."
- prereq_ids = list("bluespace_travel", "practical_bluespace", "high_efficiency")
- design_ids = list(
- "bluespace_matter_bin",
- "bluespacebodybag",
- "medicalbed_emergency",
- "femto_servo",
- "quantum_keycard",
- "swapper",
- "triphasic_scanning",
- "wormholeprojector",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_variety = 5000)
- /* /datum/experiment/exploration_scan/random/condition) this should have a point cost but im not even sure the experiment works properly lmao*/
-
-/datum/techweb_node/advanced_bluespace
- id = "bluespace_storage"
- display_name = "Advanced Bluespace Storage"
- description = "With the use of bluespace we can create even more advanced storage devices than we could have ever done"
- prereq_ids = list("micro_bluespace", "janitor")
- design_ids = list(
- "bag_holding",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
-
-/datum/techweb_node/practical_bluespace
- id = "practical_bluespace"
- display_name = "Applied Bluespace Research"
- description = "Using bluespace to make things faster and better."
- prereq_ids = list("bluespace_basic", "engineering")
- design_ids = list(
- "bluespacebeaker",
- "bluespacesyringe",
- "bluespace_coffeepot",
- "bs_rped",
- "minerbag_holding",
- "ore_silo",
- "phasic_scanning",
- "plumbing_receiver",
- "roastingstick",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_pinpoint_scan/tier2_scanmodules = 3500)
-
-/datum/techweb_node/bluespace_power
- id = "bluespace_power"
- display_name = "Bluespace Power Technology"
- description = "Even more powerful.. power!"
- prereq_ids = list("adv_power", "practical_bluespace")
- design_ids = list(
- "bluespace_cell",
- "quadratic_capacitor",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_pinpoint_scan/tier3_cells = 3000)
-
-/datum/techweb_node/unregulated_bluespace
- id = "unregulated_bluespace"
- display_name = "Unregulated Bluespace Research"
- description = "Bluespace technology using unstable or unbalanced procedures, prone to damaging the fabric of bluespace. Outlawed by galactic conventions."
- prereq_ids = list("bluespace_travel", "syndicate_basic")
- design_ids = list(
- "desynchronizer",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-
-/////////////////////////plasma tech/////////////////////////
-/datum/techweb_node/basic_plasma
- id = "basic_plasma"
- display_name = "Basic Plasma Research"
- description = "Research into the mysterious and dangerous substance, plasma."
- prereq_ids = list("engineering")
- design_ids = list(
- "mech_generator",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/adv_plasma
- id = "adv_plasma"
- display_name = "Advanced Plasma Research"
- description = "Research on how to fully exploit the power of plasma."
- prereq_ids = list("basic_plasma")
- design_ids = list(
- "mech_plasma_cutter",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/////////////////////////integrated circuits tech/////////////////////////
-
-/datum/techweb_node/adv_shells
- id = "adv_shells"
- display_name = "Advanced Shell Research"
- description = "Grants access to more complicated shell designs."
- prereq_ids = list("basic_circuitry", "engineering")
- design_ids = list(
- "assembly_shell",
- "bot_shell",
- "comp_equip_action",
- "controller_shell",
- "dispenser_shell",
- "door_shell",
- "gun_shell",
- "keyboard_shell",
- "module_shell",
- "money_bot_shell",
- "scanner_gate_shell",
- "scanner_shell",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/bci_shells
- id = "bci_shells"
- display_name = "Brain-Computer Interfaces"
- description = "Grants access to biocompatable shell designs and components."
- prereq_ids = list("adv_shells")
- design_ids = list(
- "bci_implanter",
- "bci_shell",
- "comp_bar_overlay",
- "comp_camera_bci",
- "comp_counter_overlay",
- "comp_install_detector",
- "comp_object_overlay",
- "comp_reagent_injector",
- "comp_target_intercept",
- "comp_thought_listener",
- "comp_vox",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
-
-/datum/techweb_node/movable_shells_tech
- id = "movable_shells"
- display_name = "Movable Shell Research"
- description = "Grants access to movable shells."
- prereq_ids = list("adv_shells", "robotics")
- design_ids = list(
- "comp_pathfind",
- "comp_pull",
- "drone_shell",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000)
-
-/datum/techweb_node/server_shell_tech
- id = "server_shell"
- display_name = "Server Technology Research"
- description = "Grants access to a server shell that has a very high capacity for components."
- prereq_ids = list("adv_shells", "computer_data_disks")
- design_ids = list(
- "server_shell",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000)
-
-/////////////////////////robotics tech/////////////////////////
-/datum/techweb_node/robotics
- id = "robotics"
- display_name = "Basic Robotics Research"
- description = "Programmable machines that make our lives lazier."
- prereq_ids = list("base")
- design_ids = list(
- "paicard",
- "mecha_camera",
- "botnavbeacon",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/adv_robotics
- id = "adv_robotics"
- display_name = "Advanced Robotics Research"
- description = "Advanced synthetic neural networks and synaptic pathways allows for extraordinary leaps in cybernetic intelligence and interfacing."
- prereq_ids = list("robotics")
- design_ids = list(
- "advanced_l_arm",
- "advanced_r_arm",
- "advanced_l_leg",
- "advanced_r_leg",
- "mmi_posi",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/adv_bots
- id = "adv_bots"
- display_name = "Advanced Bots Research"
- description = "Grants access to a special launchpad designed for bots."
- prereq_ids = list("robotics")
- design_ids = list(
- "botpad",
- "botpad_remote",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/exodrone_tech
- id = "exodrone"
- display_name = "Exploration Drone Research"
- description = "Technology for exploring far away locations."
- prereq_ids = list("robotics")
- design_ids = list(
- "exodrone_console",
- "exodrone_launcher",
- "exoscanner",
- "exoscanner_console",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/neural_programming
- id = "neural_programming"
- display_name = "Neural Programming"
- description = "Study into networks of processing units that mimic our brains."
- prereq_ids = list("biotech", "datatheory")
- design_ids = list(
- "skill_station",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/cyborg_upg_util
- id = "cyborg_upg_util"
- display_name = "Cyborg Upgrades: Utility"
- description = "Utility upgrades for cyborgs."
- prereq_ids = list("adv_robotics")
- design_ids = list(
- "borg_upgrade_advancedmop",
- "borg_upgrade_broomer",
- "borg_upgrade_expand",
- "borg_upgrade_prt",
- "borg_upgrade_selfrepair",
- "borg_upgrade_thrusters",
- "borg_upgrade_trashofholding",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
-
-/datum/techweb_node/cyborg_upg_util/New()
- . = ..()
- if(!CONFIG_GET(flag/disable_secborg))
- design_ids += "borg_upgrade_disablercooler"
-
-/datum/techweb_node/cyborg_upg_serv
- id = "cyborg_upg_serv"
- display_name = "Cyborg Upgrades: Service"
- description = "Service upgrades for cyborgs."
- prereq_ids = list("adv_robotics")
- design_ids = list(
- "borg_upgrade_rolling_table",
- "borg_upgrade_condiment_synthesizer",
- "borg_upgrade_silicon_knife",
- "borg_upgrade_service_apparatus",
- "borg_upgrade_drink_apparatus",
- "borg_upgrade_service_cookbook",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
-
-/datum/techweb_node/cyborg_upg_engiminer
- id = "cyborg_upg_engiminer"
- display_name = "Cyborg Upgrades: Engineering & Mining"
- description = "Engineering and Mining upgrades for cyborgs."
- prereq_ids = list("adv_engi", "basic_mining")
- design_ids = list(
- "borg_upgrade_circuitapp",
- "borg_upgrade_diamonddrill",
- "borg_upgrade_holding",
- "borg_upgrade_lavaproof",
- "borg_upgrade_rped",
- "borg_upgrade_hypermod",
- "borg_upgrade_inducer",
- "borg_upgrade_engineeringomnitool",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
-
-/datum/techweb_node/cyborg_upg_med
- id = "cyborg_upg_med"
- display_name = "Cyborg Upgrades: Medical"
- description = "Medical upgrades for cyborgs."
- prereq_ids = list("adv_biotech")
- design_ids = list(
- "borg_upgrade_beakerapp",
- "borg_upgrade_defibrillator",
- "borg_upgrade_expandedsynthesiser",
- "borg_upgrade_piercinghypospray",
- "borg_upgrade_pinpointer",
- "borg_upgrade_surgicalprocessor",
- "borg_upgrade_surgicalomnitool",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
-
-/datum/techweb_node/ai_basic
- id = "ai_basic"
- display_name = "Artificial Intelligence"
- description = "AI unit research."
- prereq_ids = list("adv_robotics")
- design_ids = list(
- "aicore",
- "borg_ai_control",
- "intellicard",
- "mecha_tracking_ai_control",
- "aifixer",
- "aiupload",
- "reset_module",
- "asimov_module",
- "default_module",
- "nutimov_module",
- "paladin_module",
- "robocop_module",
- "corporate_module",
- "drone_module",
- "oxygen_module",
- "safeguard_module",
- "protectstation_module",
- "quarantine_module",
- "freeform_module",
- "remove_module",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/ai_basic/New()
- . = ..()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI))
- design_ids -= list(
- "aicore",
- "borg_ai_control",
- "intellicard",
- "mecha_tracking_ai_control",
- "aifixer",
- "aiupload",
- )
-
-/datum/techweb_node/ai_adv
- id = "ai_adv"
- display_name = "Advanced Artificial Intelligence"
- description = "State of the art lawsets to be used for AI research."
- prereq_ids = list("ai_basic")
- design_ids = list(
- "asimovpp_module",
- "paladin_devotion_module",
- "dungeon_master_module",
- "painter_module",
- "ten_commandments_module",
- "hippocratic_module",
- "maintain_module",
- "liveandletlive_module",
- "reporter_module",
- "yesman_module",
- "hulkamania_module",
- "peacekeeper_module",
- "overlord_module",
- "tyrant_module",
- "antimov_module",
- "balance_module",
- "thermurderdynamic_module",
- "damaged_module",
- "freeformcore_module",
- "onehuman_module",
- "purge_module",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000)
-
-//Any kind of point adjustment needs to happen before SSresearch sets up the whole node tree, it gets cached
-/datum/techweb_node/ai/New()
- . = ..()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_UNIQUE_AI))
- research_costs[TECHWEB_POINT_TYPE_GENERIC] *= 3
-
-/////////////////////////EMP tech/////////////////////////
-/datum/techweb_node/emp_basic //EMP tech for some reason
- id = "emp_basic"
- display_name = "Electromagnetic Theory"
- description = "Study into usage of frequencies in the electromagnetic spectrum."
- prereq_ids = list("base")
- design_ids = list(
- "holosign",
- "holosignsec",
- "holosignengi",
- "holosignatmos",
- "holosignrestaurant",
- "holosignbar",
- "inducer",
- "inducerengi",
- "tray_goggles",
- "holopad",
- "vendatray",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/emp_adv
- id = "emp_adv"
- display_name = "Advanced Electromagnetic Theory"
- description = "Determining whether reversing the polarity will actually help in a given situation."
- prereq_ids = list("emp_basic")
- design_ids = list(
- "ultra_micro_laser",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_pinpoint_scan/tier2_microlaser = 1500)
-
-/datum/techweb_node/emp_super
- id = "emp_super"
- display_name = "Quantum Electromagnetic Technology" //bs
- description = "Even better electromagnetic technology."
- prereq_ids = list("emp_adv")
- design_ids = list(
- "quadultra_micro_laser",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 15000)
- discount_experiments = list(
- /datum/experiment/scanning/points/machinery_pinpoint_scan/tier3_microlaser = 4000,
- /datum/experiment/ordnance/gaseous/noblium = 10000,
- )
-
-/////////////////////////Clown tech/////////////////////////
-/datum/techweb_node/clown
- id = "clown"
- display_name = "Clown Technology"
- description = "Honk?!"
- prereq_ids = list("base")
- design_ids = list(
- "air_horn",
- "borg_transform_clown",
- "honk_chassis",
- "honk_head",
- "honk_left_arm",
- "honk_left_leg",
- "honk_right_arm",
- "honk_right_leg",
- "honk_torso",
- "honker_main",
- "honker_peri",
- "honker_targ",
- "implant_trombone",
- "mech_banana_mortar",
- "mech_honker",
- "mech_mousetrap_mortar",
- "mech_punching_face",
- "clown_firing_pin",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-////////////////////////Computer tech////////////////////////
-/datum/techweb_node/comptech
- id = "comptech"
- display_name = "Computer Consoles"
- description = "Computers and how they work."
- prereq_ids = list("datatheory")
- design_ids = list(
- "bankmachine",
- "barcode_scanner",
- "cargo",
- "cargorequest",
- "comconsole",
- "crewconsole",
- "idcard",
- "libraryconsole",
- "mining",
- "photobooth",
- "rdcamera",
- "seccamera",
- "security_photobooth",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
-
-/datum/techweb_node/data_disks
- id = "computer_data_disks"
- display_name = "Computer Data Disks"
- description = "Data disks used for storing modular computer stuff."
- prereq_ids = list("comptech")
- design_ids = list(
- "portadrive_advanced",
- "portadrive_basic",
- "portadrive_super",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-/datum/techweb_node/computer_board_gaming
- id = "computer_board_gaming"
- display_name = "Arcade Games"
- description = "For the slackers on the station."
- prereq_ids = list("comptech")
- design_ids = list(
- "arcade_battle",
- "arcade_orion",
- "slotmachine",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3250)
- discount_experiments = list(/datum/experiment/physical/arcade_winner = 3000)
-
-/datum/techweb_node/comp_recordkeeping
- id = "comp_recordkeeping"
- display_name = "Computerized Recordkeeping"
- description = "Organized record databases and how they're used."
- prereq_ids = list("comptech")
- design_ids = list(
- "account_console",
- "automated_announcement",
- "med_data",
- "prisonmanage",
- "secdata",
- "vendor",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-/datum/techweb_node/telecomms
- id = "telecomms"
- display_name = "Telecommunications Technology"
- description = "Subspace transmission technology for near-instant communications devices."
- prereq_ids = list("comptech", "bluespace_basic")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- design_ids = list(
- "comm_monitor",
- "comm_server",
- "gigabeacon",
- "message_monitor",
- "ntnet_relay",
- "s_amplifier",
- "s_analyzer",
- "s_ansible",
- "s_broadcaster",
- "s_bus",
- "s_crystal",
- "s_filter",
- "s_hub",
- "s_messaging",
- "s_processor",
- "s_receiver",
- "s_relay",
- "s_server",
- "s_transmitter",
- "s_treatment",
- )
-
-/datum/techweb_node/tram
- id = "tram"
- display_name = "Tram Technology"
- description = "Technology for linear induction transportation systems."
- prereq_ids = list("telecomms")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
- design_ids = list(
- "tram_controller",
- "tram_display",
- "crossing_signal",
- "guideway_sensor",
- )
-
-/datum/techweb_node/integrated_hud
- id = "integrated_HUDs"
- display_name = "Integrated HUDs"
- description = "The usefulness of computerized records, projected straight onto your eyepiece!"
- prereq_ids = list("comp_recordkeeping", "emp_basic")
- design_ids = list(
- "diagnostic_hud",
- "health_hud",
- "scigoggles",
- "security_hud",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
-
-/datum/techweb_node/nvg_tech
- id = "NVGtech"
- display_name = "Night Vision Technology"
- description = "Allows seeing in the dark without actual light!"
- prereq_ids = list("integrated_HUDs", "adv_engi", "emp_adv")
- design_ids = list(
- "diagnostic_hud_night",
- "health_hud_night",
- "night_visision_goggles",
- "nvgmesons",
- "nv_scigoggles",
- "security_hud_night",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
-
-////////////////////////Medical////////////////////////
-/datum/techweb_node/genetics
- id = "genetics"
- display_name = "Genetic Engineering"
- description = "We have the technology to change him."
- prereq_ids = list("biotech")
- design_ids = list(
- "dna_disk",
- "dnainfuser",
- "dnascanner",
- "scan_console",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/cryotech
- id = "cryotech"
- display_name = "Cryostasis Technology"
- description = "Smart freezing of objects to preserve them!"
- prereq_ids = list("adv_engi", "biotech")
- design_ids = list(
- "cryo_grenade",
- "cryotube",
- "splitbeaker",
- "stasis",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
-
-/datum/techweb_node/subdermal_implants
- id = "subdermal_implants"
- display_name = "Subdermal Implants"
- description = "Electronic implants buried beneath the skin."
- prereq_ids = list("biotech")
- design_ids = list(
- "c38_trac",
- "implant_chem",
- "implant_tracking",
- "implant_exile",
- "implantcase",
- "implanter",
- "locator",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/advanced_implants
- id = "adv_subdermal_implants"
- display_name = "Advanced Subdermal Implants"
- description = "Subdermal implants that leverage bluespace research to control their bluespace signature."
- prereq_ids = list("subdermal_implants", "micro_bluespace")
- design_ids = list(
- "implant_beacon",
- "implant_bluespace",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/cyber_organs
- id = "cyber_organs"
- display_name = "Cybernetic Organs"
- description = "We have the technology to rebuild him."
- prereq_ids = list("biotech")
- design_ids = list(
- "cybernetic_ears_u",
- "cybernetic_eyes_improved",
- "cybernetic_eyes_improved_moth",
- "cybernetic_heart_tier2",
- "cybernetic_liver_tier2",
- "cybernetic_lungs_tier2",
- "cybernetic_stomach_tier2",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-/datum/techweb_node/cyber_organs/New()
- ..()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION))
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
-
-/datum/techweb_node/cyber_organs_upgraded
- id = "cyber_organs_upgraded"
- display_name = "Upgraded Cybernetic Organs"
- description = "We have the technology to upgrade him."
- prereq_ids = list("adv_biotech", "cyber_organs")
- design_ids = list(
- "cybernetic_ears_whisper",
- "cybernetic_ears_xray",
- "ci-gloweyes",
- "ci-welding",
- "ci-gloweyes-moth",
- "ci-welding-moth",
- "cybernetic_heart_tier3",
- "cybernetic_liver_tier3",
- "cybernetic_lungs_tier3",
- "cybernetic_stomach_tier3",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
-
-/datum/techweb_node/cyber_organs_upgraded/New()
- ..()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION))
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-/datum/techweb_node/cyber_implants
- id = "cyber_implants"
- display_name = "Cybernetic Implants"
- description = "Electronic implants that improve humans."
- prereq_ids = list("adv_biotech", "datatheory")
- design_ids = list(
- "ci-breather",
- "ci-diaghud",
- "ci-medhud",
- "ci-nutriment",
- "ci-sechud",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/cyber_implants/New()
- ..()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION))
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-/datum/techweb_node/adv_cyber_implants
- id = "adv_cyber_implants"
- display_name = "Advanced Cybernetic Implants"
- description = "Upgraded and more powerful cybernetic implants."
- prereq_ids = list("neural_programming", "cyber_implants","integrated_HUDs")
- design_ids = list(
- "ci-nutrimentplus",
- "ci-reviver",
- "ci-surgery",
- "ci-toolset",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/adv_cyber_implants/New()
- ..()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION))
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
-
-/datum/techweb_node/combat_cyber_implants
- id = "combat_cyber_implants"
- display_name = "Combat Cybernetic Implants"
- description = "Military grade combat implants to improve performance."
- prereq_ids = list("adv_cyber_implants","weaponry","NVGtech","high_efficiency")
- design_ids = list(
- "ci-antidrop",
- "ci-antistun",
- "ci-thermals",
- "ci-thrusters",
- "ci-xray",
- "ci-thermals-moth",
- "ci-xray-moth",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/combat_cyber_implants/New()
- ..()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION))
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
-
-////////////////////////Tools////////////////////////
-
-/datum/techweb_node/basic_mining
- id = "basic_mining"
- display_name = "Mining Technology"
- description = "Better than Efficiency V."
- prereq_ids = list("engineering", "basic_plasma")
- design_ids = list(
- "borg_upgrade_cooldownmod",
- "borg_upgrade_damagemod",
- "borg_upgrade_rangemod",
- "cargoexpress",
- "cooldownmod",
- "damagemod",
- "drill",
- "mecha_kineticgun",
- "mining_equipment_vendor",
- "ore_redemption",
- "plasmacutter",
- "rangemod",
- "superresonator",
- "triggermod",
- "mining_scanner",
- "brm",
- "b_smelter",
- "b_refinery",
- )//e a r l y g a m e)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/adv_mining
- id = "adv_mining"
- display_name = "Advanced Mining Technology"
- description = "Efficiency Level 127" //dumb mc references
- prereq_ids = list("basic_mining", "adv_power", "adv_plasma")
- design_ids = list(
- "drill_diamond",
- "hypermod",
- "jackhammer",
- "plasmacutter_adv",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
- discount_experiments = list(/datum/experiment/scanning/random/material/hard/one = 5000)
-
-/datum/techweb_node/janitor
- id = "janitor"
- display_name = "Advanced Sanitation Technology"
- description = "Clean things better, faster, stronger, and harder!"
- prereq_ids = list("adv_engi")
- design_ids = list(
- "advmop",
- "beartrap",
- "blutrash",
- "buffer",
- "vacuum",
- "holobarrier_jani",
- "light_replacer_blue",
- "paint_remover",
- "spraybottle",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
- discount_experiments = list(/datum/experiment/scanning/random/janitor_trash = 3000) //75% discount for scanning some trash, seems fair right?
-
-/datum/techweb_node/botany
- id = "botany"
- display_name = "Botanical Engineering"
- description = "Botanical tools"
- prereq_ids = list("biotech")
- design_ids = list(
- "biogenerator",
- "flora_gun",
- "gene_shears",
- "hydro_tray",
- "portaseeder",
- "seed_extractor",
- "adv_watering_can",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
- required_experiments = list(/datum/experiment/scanning/random/plants/wild)
- discount_experiments = list(/datum/experiment/scanning/random/plants/traits = 3000)
-
-/datum/techweb_node/fishing
- id = "fishing"
- display_name = "Fishing Technology"
- description = "Cutting edge fishing advancements."
- prereq_ids = list("base")
- design_ids = list(
- "fishing_rod_tech",
- "stabilized_hook",
- "auto_reel",
- "fish_analyzer",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
- required_experiments = list(/datum/experiment/scanning/fish)
-
-/datum/techweb_node/exp_tools
- id = "exp_tools"
- display_name = "Experimental Tools"
- description = "Highly advanced tools."
- prereq_ids = list("adv_engi")
- design_ids = list(
- "exwelder",
- "handdrill",
- "jawsoflife",
- "laserscalpel",
- "mechanicalpinches",
- "rangedanalyzer",
- "searingtool",
- "adv_fire_extinguisher",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
- discount_experiments = list(/datum/experiment/scanning/random/material/hard/one = 5000)
-
-/datum/techweb_node/sec_basic
- id = "sec_basic"
- display_name = "Basic Security Equipment"
- description = "Standard equipment used by security."
- prereq_ids = list("base")
- design_ids = list(
- "bola_energy",
- "evidencebag",
- "pepperspray",
- "seclite",
- "zipties",
- "inspector",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-/datum/techweb_node/rcd_upgrade
- id = "rcd_upgrade"
- display_name = "Rapid Device Upgrade Designs"
- description = "Unlocks new designs that improve rapid devices."
- prereq_ids = list("adv_engi")
- design_ids = list(
- "rcd_upgrade_anti_interrupt",
- "rcd_upgrade_cooling",
- "rcd_upgrade_frames",
- "rcd_upgrade_furnishing",
- "rcd_upgrade_simple_circuits",
- "rpd_upgrade_unwrench",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/adv_rcd_upgrade
- id = "adv_rcd_upgrade"
- display_name = "Advanced RCD Designs Upgrade"
- description = "Unlocks new RCD designs."
- design_ids = list(
- "rcd_upgrade_silo_link",
- )
- prereq_ids = list(
- "bluespace_travel",
- "rcd_upgrade",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
- discount_experiments = list(/datum/experiment/scanning/random/material/hard/two = 5000)
-
-/////////////////////////weaponry tech/////////////////////////
-/datum/techweb_node/weaponry
- id = "weaponry"
- display_name = "Weapon Development Technology"
- description = "Our researchers have found new ways to weaponize just about everything now."
- prereq_ids = list("engineering")
- design_ids = list(
- "ballistic_shield",
- "pin_testing",
- "tele_shield",
- "lasershell",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 20000)
- discount_experiments = list(/datum/experiment/ordnance/explosive/pressurebomb = 10000)
-
-/datum/techweb_node/adv_weaponry
- id = "adv_weaponry"
- display_name = "Advanced Weapon Development Technology"
- description = "Our weapons are breaking the rules of reality by now."
- prereq_ids = list("adv_engi", "weaponry")
- design_ids = list(
- "pin_loyalty",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
-
-/datum/techweb_node/electric_weapons
- id = "electronic_weapons"
- display_name = "Electric Weapons"
- description = "Weapons using electric technology"
- prereq_ids = list("weaponry", "adv_power" , "emp_basic")
- design_ids = list(
- "ioncarbine",
- "stunrevolver",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/radioactive_weapons
- id = "radioactive_weapons"
- display_name = "Radioactive Weaponry"
- description = "Weapons using radioactive technology."
- prereq_ids = list("adv_engi", "adv_weaponry")
- design_ids = list(
- "nuclear_gun",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/beam_weapons
- id = "beam_weapons"
- display_name = "Beam Weaponry"
- description = "Various basic beam weapons"
- prereq_ids = list("adv_weaponry")
- design_ids = list(
- "temp_gun",
- "xray_laser",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/adv_beam_weapons
- id = "adv_beam_weapons"
- display_name = "Advanced Beam Weaponry"
- description = "Various advanced beam weapons"
- prereq_ids = list("beam_weapons")
- design_ids = list(
- "beamrifle",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/explosive_weapons
- id = "explosive_weapons"
- display_name = "Explosive & Pyrotechnical Weaponry"
- description = "If the light stuff just won't do it."
- prereq_ids = list("adv_weaponry")
- design_ids = list(
- "adv_grenade",
- "large_grenade",
- "pyro_grenade",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/exotic_ammo
- id = "exotic_ammo"
- display_name = "Exotic Ammunition"
- description = "They won't know what hit em."
- prereq_ids = list("weaponry")
- design_ids = list(
- "c38_hotshot",
- "c38_iceblox",
- "techshotshell",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/gravity_gun
- id = "gravity_gun"
- display_name = "One-point Bluespace-gravitational Manipulator"
- description = "Fancy wording for gravity gun."
- prereq_ids = list("adv_weaponry", "bluespace_travel")
- design_ids = list(
- "gravitygun",
- "mech_gravcatapult",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-//MODsuit tech
-
-/datum/techweb_node/mod_advanced
- id = "mod_advanced"
- display_name = "Advanced Modular Suits"
- description = "More advanced modules, to improve modular suits."
- prereq_ids = list("robotics")
- design_ids = list(
- "mod_visor_diaghud",
- "mod_gps",
- "mod_reagent_scanner",
- "mod_clamp",
- "mod_drill",
- "mod_orebag",
- "modlink_scryer",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mod_engineering
- id = "mod_engineering"
- display_name = "Engineering Modular Suits"
- description = "Engineering suits, for powered engineers."
- prereq_ids = list("mod_advanced", "engineering")
- design_ids = list(
- "mod_plating_engineering",
- "mod_visor_meson",
- "mod_t_ray",
- "mod_magboot",
- "mod_tether",
- "mod_constructor",
- "mod_mister_atmos",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mod_advanced_engineering
- id = "mod_advanced_engineering"
- display_name = "Advanced Engineering Modular Suits"
- description = "Advanced Engineering suits, for advanced powered engineers."
- prereq_ids = list("mod_engineering", "adv_engi")
- design_ids = list(
- "mod_plating_atmospheric",
- "mod_jetpack",
- "mod_rad_protection",
- "mod_emp_shield",
- "mod_storage_expanded",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
-
-/datum/techweb_node/mod_advanced_engineering/New()
- if(HAS_TRAIT(SSstation, STATION_TRAIT_RADIOACTIVE_NEBULA)) //we'll really need the rad protection modsuit module
- starting_node = TRUE
-
- return ..()
-
-/datum/techweb_node/mod_medical
- id = "mod_medical"
- display_name = "Medical Modular Suits"
- description = "Medical suits for quick rescue purposes."
- prereq_ids = list("mod_advanced", "biotech")
- design_ids = list(
- "mod_plating_medical",
- "mod_visor_medhud",
- "mod_health_analyzer",
- "mod_quick_carry",
- "mod_injector",
- "mod_organ_thrower",
- "mod_dna_lock",
- "mod_patienttransport",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mod_advanced_medical
- id = "mod_advanced_medical"
- display_name = "Advanced Medical Modular Suits"
- description = "Advanced medical suits for quicker rescue purposes."
- prereq_ids = list("mod_medical", "adv_biotech")
- design_ids = list(
- "mod_defib",
- "mod_threadripper",
- "mod_surgicalprocessor",
- "mod_statusreadout",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
-
-/datum/techweb_node/mod_security
- id = "mod_security"
- display_name = "Security Modular Suits"
- description = "Security suits for space crime handling."
- prereq_ids = list("mod_advanced", "sec_basic")
- design_ids = list(
- "mod_plating_security",
- "mod_visor_sechud",
- "mod_stealth",
- "mod_mag_harness",
- "mod_pathfinder",
- "mod_holster",
- "mod_sonar",
- "mod_projectile_dampener",
- "mod_criminalcapture",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mod_entertainment
- id = "mod_entertainment"
- display_name = "Entertainment Modular Suits"
- description = "Powered suits for protection against low-humor environments."
- prereq_ids = list("mod_advanced", "clown")
- design_ids = list(
- "mod_plating_cosmohonk",
- "mod_bikehorn",
- "mod_microwave_beam",
- "mod_waddle",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mod_anomaly
- id = "mod_anomaly"
- display_name = "Anomalock Modular Suits"
- description = "Modules for modular suits that require anomaly cores to function."
- prereq_ids = list("mod_advanced", "anomaly_research")
- design_ids = list(
- "mod_antigrav",
- "mod_teleporter",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mod_anomaly_engi
- id = "mod_anomaly_engi"
- display_name = "Engineering Anomalock Modular Suits"
- description = "Advanced modules for modular suits, using anomaly cores to become even better engineers."
- prereq_ids = list("mod_advanced_engineering", "mod_anomaly")
- design_ids = list(
- "mod_kinesis",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
-
-////////////////////////mech technology////////////////////////
-/datum/techweb_node/adv_mecha
- id = "adv_mecha"
- display_name = "Advanced Exosuits"
- description = "For when you just aren't Gundam enough."
- prereq_ids = list("adv_robotics")
- design_ids = list(
- "mech_repair_droid",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
- discount_experiments = list(/datum/experiment/scanning/random/material/medium/three = 5000)
-
-/datum/techweb_node/odysseus
- id = "mecha_odysseus"
- display_name = "EXOSUIT: Odysseus"
- description = "Odysseus exosuit designs"
- prereq_ids = list("base")
- design_ids = list(
- "odysseus_chassis",
- "odysseus_head",
- "odysseus_left_arm",
- "odysseus_left_leg",
- "odysseus_main",
- "odysseus_peri",
- "odysseus_right_arm",
- "odysseus_right_leg",
- "odysseus_torso",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/clarke
- id = "mecha_clarke"
- display_name = "EXOSUIT: Clarke"
- description = "Clarke exosuit designs"
- prereq_ids = list("engineering")
- design_ids = list(
- "clarke_chassis",
- "clarke_head",
- "clarke_left_arm",
- "clarke_main",
- "clarke_peri",
- "clarke_right_arm",
- "clarke_torso",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/paddy
- id = "mech_paddy"
- display_name = "EXOSUIT: APLU \"Paddy\""
- description = "Paddy exosuit designs"
- prereq_ids = list("adv_mecha", "adv_mecha_armor")
- design_ids = list(
- "paddyupgrade",
- "mech_hydraulic_claw"
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_mechbay = 5000)
-
-/datum/techweb_node/gygax
- id = "mech_gygax"
- display_name = "EXOSUIT: Gygax"
- description = "Gygax exosuit designs"
- prereq_ids = list("adv_mecha", "adv_mecha_armor")
- design_ids = list(
- "gygax_armor",
- "gygax_chassis",
- "gygax_head",
- "gygax_left_arm",
- "gygax_left_leg",
- "gygax_main",
- "gygax_peri",
- "gygax_right_arm",
- "gygax_right_leg",
- "gygax_targ",
- "gygax_torso",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_mechbay = 5000)
-
-/datum/techweb_node/durand
- id = "mech_durand"
- display_name = "EXOSUIT: Durand"
- description = "Durand exosuit designs"
- prereq_ids = list("adv_mecha", "adv_mecha_armor")
- design_ids = list(
- "durand_armor",
- "durand_chassis",
- "durand_head",
- "durand_left_arm",
- "durand_left_leg",
- "durand_main",
- "durand_peri",
- "durand_right_arm",
- "durand_right_leg",
- "durand_targ",
- "durand_torso",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_mechbay = 3500)
-
-/datum/techweb_node/phazon
- id = "mecha_phazon"
- display_name = "EXOSUIT: Phazon"
- description = "Phazon exosuit designs"
- prereq_ids = list("adv_mecha", "adv_mecha_armor" , "micro_bluespace")
- design_ids = list(
- "phazon_armor",
- "phazon_chassis",
- "phazon_head",
- "phazon_left_arm",
- "phazon_left_leg",
- "phazon_main",
- "phazon_peri",
- "phazon_right_arm",
- "phazon_right_leg",
- "phazon_targ",
- "phazon_torso",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_mechbay = 2500)
-
-/datum/techweb_node/savannah_ivanov
- id = "mecha_savannah_ivanov"
- display_name = "EXOSUIT: Savannah-Ivanov"
- description = "Savannah-Ivanov exosuit designs"
- prereq_ids = list("adv_mecha", "weaponry", "exp_tools")
- design_ids = list(
- "savannah_ivanov_armor",
- "savannah_ivanov_chassis",
- "savannah_ivanov_head",
- "savannah_ivanov_left_arm",
- "savannah_ivanov_left_leg",
- "savannah_ivanov_main",
- "savannah_ivanov_peri",
- "savannah_ivanov_right_arm",
- "savannah_ivanov_right_leg",
- "savannah_ivanov_targ",
- "savannah_ivanov_torso",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_mechbay = 3000)
-
-/datum/techweb_node/adv_mecha_tools
- id = "adv_mecha_tools"
- display_name = "Advanced Exosuit Equipment"
- description = "Tools for high level mech suits"
- prereq_ids = list("adv_mecha")
- design_ids = list(
- "mech_rcd",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/med_mech_tools
- id = "med_mech_tools"
- display_name = "Medical Exosuit Equipment"
- description = "Tools for high level mech suits"
- prereq_ids = list("adv_biotech")
- design_ids = list(
- "mech_medi_beam",
- "mech_sleeper",
- "mech_syringe_gun",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_armor
- id = "adv_mecha_armor"
- display_name = "Exosuit Heavy Armor Research"
- description = "Recreating heavy armor with new rapid fabrication techniques."
- prereq_ids = list("adv_mecha", "bluespace_power")
- design_ids = list(
- "mech_ccw_armor",
- "mech_proj_armor",
- )
- required_experiments = list(/datum/experiment/scanning/random/mecha_damage_scan)
- discount_experiments = list(/datum/experiment/scanning/random/mecha_equipped_scan = 5000)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
-
-/datum/techweb_node/mech_scattershot
- id = "mecha_tools"
- display_name = "Exosuit Weapon (LBX AC 10 \"Scattershot\")"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("adv_mecha")
- design_ids = list(
- "mech_scattershot",
- "mech_scattershot_ammo",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_carbine
- id = "mech_carbine"
- display_name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("exotic_ammo")
- design_ids = list(
- "mech_carbine",
- "mech_carbine_ammo",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_ion
- id = "mmech_ion"
- display_name = "Exosuit Weapon (MKIV Ion Heavy Cannon)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("electronic_weapons", "emp_adv")
- design_ids = list(
- "mech_ion",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_tesla
- id = "mech_tesla"
- display_name = "Exosuit Weapon (MKI Tesla Cannon)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("electronic_weapons", "adv_power")
- design_ids = list(
- "mech_tesla",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_laser
- id = "mech_laser"
- display_name = "Exosuit Weapon (CH-PS \"Immolator\" Laser)"
- description = "A basic piece of mech weaponry"
- prereq_ids = list("beam_weapons")
- design_ids = list(
- "mech_laser",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_laser_heavy
- id = "mech_laser_heavy"
- display_name = "Exosuit Weapon (CH-LC \"Solaris\" Laser Cannon)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("adv_beam_weapons")
- design_ids = list(
- "mech_laser_heavy",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_disabler
- id = "mech_disabler"
- display_name = "Exosuit Weapon (CH-DS \"Peacemaker\" Mounted Disabler)"
- description = "A basic piece of mech weaponry"
- prereq_ids = list("adv_mecha")
- design_ids = list(
- "mech_disabler",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_grenade_launcher
- id = "mech_grenade_launcher"
- display_name = "Exosuit Weapon (SGL-6 Grenade Launcher)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("explosive_weapons")
- design_ids = list(
- "mech_grenade_launcher",
- "mech_grenade_launcher_ammo",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_missile_rack
- id = "mech_missile_rack"
- display_name = "Exosuit Weapon (BRM-6 Missile Rack)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("explosive_weapons")
- design_ids = list(
- "mech_missile_rack",
- "mech_missile_rack_ammo",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/clusterbang_launcher
- id = "clusterbang_launcher"
- display_name = "Exosuit Module (SOB-3 Clusterbang Launcher)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("explosive_weapons")
- design_ids = list(
- "clusterbang_launcher",
- "clusterbang_launcher_ammo",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_teleporter
- id = "mech_teleporter"
- display_name = "Exosuit Module (Teleporter Module)"
- description = "An advanced piece of mech Equipment"
- prereq_ids = list("micro_bluespace")
- design_ids = list(
- "mech_teleporter",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_wormhole_gen
- id = "mech_wormhole_gen"
- display_name = "Exosuit Module (Localized Wormhole Generator)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("bluespace_travel")
- design_ids = list(
- "mech_wormhole_gen",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_lmg
- id = "mech_lmg"
- display_name = "Exosuit Weapon (\"Ultra AC 2\" LMG)"
- description = "An advanced piece of mech weaponry"
- prereq_ids = list("adv_mecha")
- design_ids = list(
- "mech_lmg",
- "mech_lmg_ammo",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-/datum/techweb_node/mech_diamond_drill
- id = "mech_diamond_drill"
- display_name = "Exosuit Diamond Drill"
- description = "A diamond drill fit for a large exosuit"
- prereq_ids = list("adv_mining")
- design_ids = list(
- "mech_diamond_drill",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
-
-////////////////////////Alien technology////////////////////////
-/datum/techweb_node/alientech //AYYYYYYYYLMAOO tech
- id = "alientech"
- display_name = "Alien Technology"
- description = "Things used by the greys."
- prereq_ids = list("biotech","engineering")
- required_items_to_unlock = list(
- /obj/item/stack/sheet/mineral/abductor,
- /obj/item/abductor,
- /obj/item/cautery/alien,
- /obj/item/circuitboard/machine/abductor,
- /obj/item/circular_saw/alien,
- /obj/item/crowbar/abductor,
- /obj/item/gun/energy/alien,
- /obj/item/gun/energy/shrink_ray,
- /obj/item/hemostat/alien,
- /obj/item/melee/baton/abductor,
- /obj/item/multitool/abductor,
- /obj/item/retractor/alien,
- /obj/item/scalpel/alien,
- /obj/item/screwdriver/abductor,
- /obj/item/surgicaldrill/alien,
- /obj/item/weldingtool/abductor,
- /obj/item/wirecutters/abductor,
- /obj/item/wrench/abductor,
- )
- design_ids = list(
- "alienalloy",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- hidden = TRUE
-
-/datum/techweb_node/alientech/on_station_research()
- SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_ALIENTECH] = TRUE
-
-/datum/techweb_node/alien_bio
- id = "alien_bio"
- display_name = "Alien Biological Tools"
- description = "Advanced biological tools."
- prereq_ids = list("alientech", "adv_biotech")
- design_ids = list(
- "alien_cautery",
- "alien_drill",
- "alien_hemostat",
- "alien_retractor",
- "alien_saw",
- "alien_scalpel",
- )
-
- required_items_to_unlock = list(
- /obj/item/abductor,
- /obj/item/cautery/alien,
- /obj/item/circuitboard/machine/abductor,
- /obj/item/circular_saw/alien,
- /obj/item/crowbar/abductor,
- /obj/item/gun/energy/alien,
- /obj/item/gun/energy/shrink_ray,
- /obj/item/hemostat/alien,
- /obj/item/melee/baton/abductor,
- /obj/item/multitool/abductor,
- /obj/item/retractor/alien,
- /obj/item/scalpel/alien,
- /obj/item/screwdriver/abductor,
- /obj/item/surgicaldrill/alien,
- /obj/item/weldingtool/abductor,
- /obj/item/wirecutters/abductor,
- /obj/item/wrench/abductor,
- )
-
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 12500)
- discount_experiments = list(/datum/experiment/scanning/points/slime/hard = 10000)
- hidden = TRUE
-
-/datum/techweb_node/alien_engi
- id = "alien_engi"
- display_name = "Alien Engineering"
- description = "Alien engineering tools"
- prereq_ids = list("alientech", "adv_engi")
-
- design_ids = list(
- "alien_crowbar",
- "alien_multitool",
- "alien_screwdriver",
- "alien_welder",
- "alien_wirecutters",
- "alien_wrench",
- )
-
- required_items_to_unlock = list(
- /obj/item/abductor,
- /obj/item/circuitboard/machine/abductor,
- /obj/item/crowbar/abductor,
- /obj/item/gun/energy/shrink_ray,
- /obj/item/melee/baton/abductor,
- /obj/item/multitool/abductor,
- /obj/item/screwdriver/abductor,
- /obj/item/weldingtool/abductor,
- /obj/item/wirecutters/abductor,
- /obj/item/wrench/abductor,
- )
-
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
-
-/datum/techweb_node/syndicate_basic
- id = "syndicate_basic"
- display_name = "Illegal Technology"
- description = "Dangerous research used to create dangerous objects."
- prereq_ids = list("adv_engi", "adv_weaponry", "explosive_weapons")
- design_ids = list(
- "advanced_camera",
- "ai_cam_upgrade",
- "borg_syndicate_module",
- "donksoft_refill",
- "donksofttoyvendor",
- "largecrossbow",
- "mag_autorifle",
- "mag_autorifle_ap",
- "mag_autorifle_ic",
- "rapidsyringe",
- "suppressor",
- "super_pointy_tape",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
- hidden = TRUE
-
-/datum/techweb_node/syndicate_basic/New() //Crappy way of making syndicate gear decon supported until there's another way.
- . = ..()
- if(!SSearly_assets.initialized)
- RegisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(register_uplink_items))
- else
- register_uplink_items()
-
-/**
- * This needs some clarification: The uplink_items_by_type list is populated on datum/asset/json/uplink/generate.
- * SStraitor doesn't actually initialize. I'm bamboozled.
- */
-/datum/techweb_node/syndicate_basic/proc/register_uplink_items()
- SIGNAL_HANDLER
- UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
- required_items_to_unlock = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
- if(!item.item || !item.illegal_tech)
- continue
- required_items_to_unlock |= item.item //allows deconning to unlock.
-
-
-////////////////////////B.E.P.I.S. Locked Techs////////////////////////
-/datum/techweb_node/light_apps
- id = "light_apps"
- display_name = "Illumination Applications"
- description = "Applications of lighting and vision technology not originally thought to be commercially viable."
- prereq_ids = list("base")
- design_ids = list(
- "bright_helmet",
- "rld_mini",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/extreme_office
- id = "extreme_office"
- display_name = "Advanced Office Applications"
- description = "Some of our smartest lab guys got together on a Friday and improved our office efficiency by 350%. Here's how."
- prereq_ids = list("base")
- design_ids = list(
- "mauna_mug",
- "rolling_table",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/spec_eng
- id = "spec_eng"
- display_name = "Specialized Engineering"
- description = "Conventional wisdom has deemed these engineering products 'technically' safe, but far too dangerous to traditionally condone."
- prereq_ids = list("base")
- design_ids = list(
- "eng_gloves",
- "lava_rods",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/aus_security
- id = "aus_security"
- display_name = "Australicus Security Protocols"
- description = "It is said that security in the Australicus sector is tight, so we took some pointers from their equipment. Thankfully, our sector lacks any signs of these, 'dropbears'."
- prereq_ids = list("base")
- design_ids = list(
- "pin_explorer",
- "stun_boomerang",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/interrogation
- id = "interrogation"
- display_name = "Enhanced Interrogation Technology"
- description = "By cross-referencing several declassified documents from past dictatorial regimes, we were able to develop an incredibly effective interrogation device. \
- Ethical concerns about loss of free will do not apply to criminals, according to galactic law."
- prereq_ids = list("base")
- design_ids = list(
- "hypnochair",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/sticky_advanced
- id = "sticky_advanced"
- display_name = "Advanced Sticky Technology"
- description = "Taking a good joke too far? Nonsense!"
- design_ids = list(
- "pointy_tape",
- "super_sticky_tape",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/tackle_advanced
- id = "tackle_advanced"
- display_name = "Advanced Grapple Technology"
- description = "Nanotrasen would like to remind its researching staff that it is never acceptable to \"glomp\" your coworkers, and further \"scientific trials\" on the subject \
- will no longer be accepted in its academic journals."
- design_ids = list(
- "tackle_dolphin",
- "tackle_rocket",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/mod_experimental
- id = "mod_experimental"
- display_name = "Experimental Modular Suits"
- description = "Applications of experimentality when creating MODsuits have created these..."
- prereq_ids = list("base")
- design_ids = list(
- "mod_disposal",
- "mod_joint_torsion",
- "mod_recycler",
- "mod_shooting",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/mod_experimental
- id = "mod_experimental"
- display_name = "Experimental Modular Suits"
- description = "Applications of experimentality when creating MODsuits have created these..."
- prereq_ids = list("base")
- design_ids = list(
- "mod_disposal",
- "mod_joint_torsion",
- "mod_recycler",
- "mod_shooting",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
-
-/datum/techweb_node/posisphere
- id = "positronic_sphere"
- display_name = "Experimental Spherical Positronic Brain"
- description = "Recent developments on cost-cutting measures have allowed us to cut positronic brain cubes into twice-as-cheap spheres. Unfortunately, it also allows them to move around the lab via rolling maneuvers."
- prereq_ids = list("base")
- design_ids = list(
- "posisphere",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- hidden = TRUE
- experimental = TRUE
diff --git a/code/modules/research/techweb/nodes/alien_nodes.dm b/code/modules/research/techweb/nodes/alien_nodes.dm
new file mode 100644
index 0000000000000..bf19c0c54a47d
--- /dev/null
+++ b/code/modules/research/techweb/nodes/alien_nodes.dm
@@ -0,0 +1,100 @@
+/datum/techweb_node/alientech //AYYYYYYYYLMAOO tech
+ id = "alientech"
+ display_name = "Alien Technology"
+ description = "Things used by the greys."
+ prereq_ids = list("bluespace_travel")
+ required_items_to_unlock = list(
+ /obj/item/stack/sheet/mineral/abductor,
+ /obj/item/abductor,
+ /obj/item/cautery/alien,
+ /obj/item/circuitboard/machine/abductor,
+ /obj/item/circular_saw/alien,
+ /obj/item/crowbar/abductor,
+ /obj/item/gun/energy/alien,
+ /obj/item/gun/energy/shrink_ray,
+ /obj/item/hemostat/alien,
+ /obj/item/melee/baton/abductor,
+ /obj/item/multitool/abductor,
+ /obj/item/retractor/alien,
+ /obj/item/scalpel/alien,
+ /obj/item/screwdriver/abductor,
+ /obj/item/surgicaldrill/alien,
+ /obj/item/weldingtool/abductor,
+ /obj/item/wirecutters/abductor,
+ /obj/item/wrench/abductor,
+ )
+ design_ids = list(
+ "alienalloy",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ hidden = TRUE
+
+/datum/techweb_node/alientech/on_station_research()
+ SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_ALIENTECH] = TRUE
+
+/datum/techweb_node/alien_engi
+ id = "alien_engi"
+ display_name = "Alien Engineering"
+ description = "Alien engineering tools"
+ prereq_ids = list("alientech", "exp_tools")
+ design_ids = list(
+ "alien_crowbar",
+ "alien_multitool",
+ "alien_screwdriver",
+ "alien_welder",
+ "alien_wirecutters",
+ "alien_wrench",
+ )
+ required_items_to_unlock = list(
+ /obj/item/abductor,
+ /obj/item/circuitboard/machine/abductor,
+ /obj/item/crowbar/abductor,
+ /obj/item/gun/energy/shrink_ray,
+ /obj/item/melee/baton/abductor,
+ /obj/item/multitool/abductor,
+ /obj/item/screwdriver/abductor,
+ /obj/item/weldingtool/abductor,
+ /obj/item/wirecutters/abductor,
+ /obj/item/wrench/abductor,
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ hidden = TRUE
+
+/datum/techweb_node/alien_surgery
+ id = "alien_surgery"
+ display_name = "Alien Surgery"
+ description = "Abductors did nothing wrong."
+ prereq_ids = list("alientech", "surgery_tools")
+ design_ids = list(
+ "alien_cautery",
+ "alien_drill",
+ "alien_hemostat",
+ "alien_retractor",
+ "alien_saw",
+ "alien_scalpel",
+ "surgery_brainwashing",
+ "surgery_heal_combo_upgrade_femto",
+ "surgery_zombie",
+ )
+ required_items_to_unlock = list(
+ /obj/item/abductor,
+ /obj/item/cautery/alien,
+ /obj/item/circuitboard/machine/abductor,
+ /obj/item/circular_saw/alien,
+ /obj/item/crowbar/abductor,
+ /obj/item/gun/energy/alien,
+ /obj/item/gun/energy/shrink_ray,
+ /obj/item/hemostat/alien,
+ /obj/item/melee/baton/abductor,
+ /obj/item/multitool/abductor,
+ /obj/item/retractor/alien,
+ /obj/item/scalpel/alien,
+ /obj/item/screwdriver/abductor,
+ /obj/item/surgicaldrill/alien,
+ /obj/item/weldingtool/abductor,
+ /obj/item/wirecutters/abductor,
+ /obj/item/wrench/abductor,
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/points/slime/hard = TECHWEB_TIER_5_POINTS)
+ hidden = TRUE
diff --git a/code/modules/research/techweb/nodes/atmos_nodes.dm b/code/modules/research/techweb/nodes/atmos_nodes.dm
new file mode 100644
index 0000000000000..b97538070beba
--- /dev/null
+++ b/code/modules/research/techweb/nodes/atmos_nodes.dm
@@ -0,0 +1,114 @@
+/datum/techweb_node/atmos
+ id = "atmos"
+ starting_node = TRUE
+ display_name = "Atmospherics"
+ description = "Maintaining station air and related life support systems."
+ design_ids = list(
+ "atmos_control",
+ "atmosalerts",
+ "thermomachine",
+ "space_heater",
+ "generic_tank",
+ "oxygen_tank",
+ "plasma_tank",
+ "plasmaman_tank_belt",
+ "plasmarefiller",
+ "extinguisher",
+ "gas_filter",
+ "plasmaman_gas_filter",
+ "analyzer",
+ "pipe_painter",
+ )
+
+/datum/techweb_node/gas_compression
+ id = "gas_compression"
+ display_name = "Gas Compression"
+ description = "Highly pressurized gases hold potential for unlocking immense energy capabilities."
+ prereq_ids = list("atmos")
+ design_ids = list(
+ "tank_compressor",
+ "emergency_oxygen",
+ "emergency_oxygen_engi",
+ "power_turbine_console",
+ "turbine_part_compressor",
+ "turbine_part_rotor",
+ "turbine_part_stator",
+ "turbine_compressor",
+ "turbine_rotor",
+ "turbine_stator",
+ "atmos_thermal",
+ "pneumatic_seal",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/plasma_control
+ id = "plasma_control"
+ display_name = "Controlled Plasma"
+ description = "Experiments with high-pressure gases and electricity resulting in crystallization and controlled plasma reactions."
+ prereq_ids = list("gas_compression", "energy_manipulation")
+ design_ids = list(
+ "crystallizer",
+ "electrolyzer",
+ "pacman",
+ "mech_generator",
+ "plasmacutter",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ required_experiments = list(/datum/experiment/ordnance/gaseous/plasma)
+
+/datum/techweb_node/fusion
+ id = "fusion"
+ display_name = "Fusion"
+ description = "Investigating fusion reactor technology to achieve sustainable and efficient energy production through controlled plasma reactions involving noble gases."
+ prereq_ids = list("plasma_control")
+ design_ids = list(
+ "HFR_core",
+ "HFR_corner",
+ "HFR_fuel_input",
+ "HFR_interface",
+ "HFR_moderator_input",
+ "HFR_waste_output",
+ "bolter_wrench",
+ "rpd_loaded",
+ "engine_goggles",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/ordnance/gaseous/bz)
+ discount_experiments = list(/datum/experiment/ordnance/gaseous/nitrous_oxide = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/exp_tools
+ id = "exp_tools"
+ display_name = "Experimental Tools"
+ description = "Enhances the functionality and versatility of station tools."
+ prereq_ids = list("fusion")
+ design_ids = list(
+ "flatpacker",
+ "handdrill",
+ "exwelder",
+ "jawsoflife",
+ "rangedanalyzer",
+ "rtd_loaded",
+ "rcd_loaded",
+ "rcd_ammo",
+ "weldingmask",
+ "magboots",
+ "adv_fire_extinguisher",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(/datum/experiment/ordnance/gaseous/noblium = TECHWEB_TIER_4_POINTS)
+
+/datum/techweb_node/rcd_upgrade
+ id = "rcd_upgrade"
+ display_name = "Rapid Device Upgrade Designs"
+ description = "New designs and enhancements for RCD and RPD."
+ prereq_ids = list("exp_tools", "parts_bluespace")
+ design_ids = list(
+ "rcd_upgrade_silo_link",
+ "rcd_upgrade_anti_interrupt",
+ "rcd_upgrade_cooling",
+ "rcd_upgrade_frames",
+ "rcd_upgrade_furnishing",
+ "rcd_upgrade_simple_circuits",
+ "rpd_upgrade_unwrench",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
diff --git a/code/modules/research/techweb/nodes/bepis_nodes.dm b/code/modules/research/techweb/nodes/bepis_nodes.dm
new file mode 100644
index 0000000000000..c051aab526f81
--- /dev/null
+++ b/code/modules/research/techweb/nodes/bepis_nodes.dm
@@ -0,0 +1,123 @@
+/datum/techweb_node/light_apps
+ id = "light_apps"
+ display_name = "Illumination Applications"
+ description = "Applications of lighting and vision technology not originally thought to be commercially viable."
+ design_ids = list(
+ "bright_helmet",
+ "rld_mini",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/extreme_office
+ id = "extreme_office"
+ display_name = "Advanced Office Applications"
+ description = "Some of our smartest lab guys got together on a Friday and improved our office efficiency by 350%. Here's how."
+ design_ids = list(
+ "mauna_mug",
+ "rolling_table",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/spec_eng
+ id = "spec_eng"
+ display_name = "Specialized Engineering"
+ description = "Conventional wisdom has deemed these engineering products 'technically' safe, but far too dangerous to traditionally condone."
+ design_ids = list(
+ "eng_gloves",
+ "lava_rods",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/aus_security
+ id = "aus_security"
+ display_name = "Australicus Security Protocols"
+ description = "It is said that security in the Australicus sector is tight, so we took some pointers from their equipment. Thankfully, our sector lacks any signs of these, 'dropbears'."
+ design_ids = list(
+ "pin_explorer",
+ "stun_boomerang",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/interrogation
+ id = "interrogation"
+ display_name = "Enhanced Interrogation Technology"
+ description = "By cross-referencing several declassified documents from past dictatorial regimes, we were able to develop an incredibly effective interrogation device. \
+ Ethical concerns about loss of free will do not apply to criminals, according to galactic law."
+ design_ids = list(
+ "hypnochair",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/sticky_advanced
+ id = "sticky_advanced"
+ display_name = "Advanced Sticky Technology"
+ description = "Taking a good joke too far? Nonsense!"
+ design_ids = list(
+ "pointy_tape",
+ "super_sticky_tape",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/tackle_advanced
+ id = "tackle_advanced"
+ display_name = "Advanced Grapple Technology"
+ description = "Nanotrasen would like to remind its researching staff that it is never acceptable to \"glomp\" your coworkers, and further \"scientific trials\" on the subject \
+ will no longer be accepted in its academic journals."
+ design_ids = list(
+ "tackle_dolphin",
+ "tackle_rocket",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/mod_experimental
+ id = "mod_experimental"
+ display_name = "Experimental Modular Suits"
+ description = "Applications of experimentality when creating MODsuits have created these..."
+ design_ids = list(
+ "mod_disposal",
+ "mod_joint_torsion",
+ "mod_recycler",
+ "mod_shooting",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/mod_experimental
+ id = "mod_experimental"
+ display_name = "Experimental Modular Suits"
+ description = "Applications of experimentality when creating MODsuits have created these..."
+ design_ids = list(
+ "mod_disposal",
+ "mod_joint_torsion",
+ "mod_recycler",
+ "mod_shooting",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
+
+/datum/techweb_node/posisphere
+ id = "positronic_sphere"
+ display_name = "Experimental Spherical Positronic Brain"
+ description = "Recent developments on cost-cutting measures have allowed us to cut positronic brain cubes into twice-as-cheap spheres. Unfortunately, it also allows them to move around the lab via rolling maneuvers."
+ design_ids = list(
+ "posisphere",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ experimental = TRUE
diff --git a/code/modules/research/techweb/nodes/biology_nodes.dm b/code/modules/research/techweb/nodes/biology_nodes.dm
new file mode 100644
index 0000000000000..6a14d7d517c8f
--- /dev/null
+++ b/code/modules/research/techweb/nodes/biology_nodes.dm
@@ -0,0 +1,64 @@
+/datum/techweb_node/bio_scan
+ id = "bio_scan"
+ display_name = "Biological Scan"
+ description = "Advanced technology for analyzing patient health and reagent compositions, ensuring precise diagnostics and treatment in the medical bay."
+ prereq_ids = list("medbay_equip")
+ design_ids = list(
+ "healthanalyzer",
+ "autopsyscanner",
+ "medical_kiosk",
+ "chem_master",
+ "ph_meter",
+ "scigoggles",
+ "mod_reagent_scanner",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/cytology
+ id = "cytology"
+ display_name = "Cytology"
+ description = "Cellular biology research focused on cultivation of limbs and diverse organisms from cells."
+ prereq_ids = list("bio_scan")
+ design_ids = list(
+ "limbgrower",
+ "pandemic",
+ "petri_dish",
+ "swab",
+ "biopsy_tool",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/xenobiology
+ id = "xenobiology"
+ display_name = "Xenobiology"
+ description = "Exploration of non-human biology, unlocking the secrets of extraterrestrial lifeforms and their unique biological processes."
+ prereq_ids = list("cytology")
+ design_ids = list(
+ "xenobioconsole",
+ "slime_scanner",
+ "limbdesign_ethereal",
+ "limbdesign_felinid",
+ "limbdesign_lizard",
+ "limbdesign_plasmaman",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/scanning/random/cytology)
+
+/datum/techweb_node/gene_engineering
+ id = "gene_engineering"
+ display_name = "Gene Engineering"
+ description = "Research into sophisticated DNA manipulation techniques, enabling the modification of human genetic traits to unlock specific abilities and enhancements."
+ prereq_ids = list("selection", "xenobiology")
+ design_ids = list(
+ "dnascanner",
+ "scan_console",
+ "dna_disk",
+ "dnainfuser",
+ "genescanner",
+ "mod_dna_lock",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(
+ /datum/experiment/scanning/random/plants/traits = TECHWEB_TIER_2_POINTS,
+ /datum/experiment/scanning/points/slime/hard = TECHWEB_TIER_2_POINTS,
+ )
diff --git a/code/modules/research/techweb/nodes/circuit_nodes.dm b/code/modules/research/techweb/nodes/circuit_nodes.dm
new file mode 100644
index 0000000000000..81f6dad275f0d
--- /dev/null
+++ b/code/modules/research/techweb/nodes/circuit_nodes.dm
@@ -0,0 +1,151 @@
+/datum/techweb_node/programming
+ id = "programming"
+ starting_node = TRUE
+ display_name = "Programming"
+ description = "Dedicate an entire shift to program a fridge to greet you when opened."
+ prereq_ids = list("robotics")
+ design_ids = list(
+ "component_printer",
+ "module_duplicator",
+ "circuit_multitool",
+ "compact_remote_shell",
+ "usb_cable",
+ "integrated_circuit",
+ "comp_access_checker",
+ "comp_arctan2",
+ "comp_arithmetic",
+ "comp_assoc_list_pick",
+ "comp_assoc_list_remove",
+ "comp_assoc_list_set",
+ "comp_binary_convert",
+ "comp_clock",
+ "comp_comparison",
+ "comp_concat",
+ "comp_concat_list",
+ "comp_decimal_convert",
+ "comp_delay",
+ "comp_direction",
+ "comp_element_find",
+ "comp_filter_list",
+ "comp_foreach",
+ "comp_format",
+ "comp_format_assoc",
+ "comp_get_column",
+ "comp_gps",
+ "comp_health",
+ "comp_health_state",
+ "comp_hear",
+ "comp_id_access_reader",
+ "comp_id_getter",
+ "comp_id_info_reader",
+ "comp_index",
+ "comp_index_assoc",
+ "comp_index_table",
+ "comp_laserpointer",
+ "comp_length",
+ "comp_light",
+ "comp_list_add",
+ "comp_list_assoc_literal",
+ "comp_list_clear",
+ "comp_list_literal",
+ "comp_list_pick",
+ "comp_list_remove",
+ "comp_logic",
+ "comp_matscanner",
+ "comp_mmi",
+ "comp_module",
+ "comp_multiplexer",
+ "comp_not",
+ "comp_ntnet_receive",
+ "comp_ntnet_send",
+ "comp_ntnet_send_list_literal",
+ "comp_pinpointer",
+ "comp_pressuresensor",
+ "comp_radio",
+ "comp_random",
+ "comp_reagents",
+ "comp_router",
+ "comp_select_query",
+ "comp_self",
+ "comp_set_variable_trigger",
+ "comp_soundemitter",
+ "comp_species",
+ "comp_speech",
+ "comp_speech",
+ "comp_split",
+ "comp_string_contains",
+ "comp_tempsensor",
+ "comp_textcase",
+ "comp_timepiece",
+ "comp_toggle",
+ "comp_tonumber",
+ "comp_tostring",
+ "comp_trigonometry",
+ "comp_typecast",
+ "comp_typecheck",
+ "comp_view_sensor",
+ )
+
+/datum/techweb_node/circuit_shells
+ id = "circuit_shells"
+ display_name = "Advanced Circuit Shells"
+ description = "Adding brains to more things."
+ prereq_ids = list("programming")
+ design_ids = list(
+ "assembly_shell",
+ "bot_shell",
+ "controller_shell",
+ "dispenser_shell",
+ "door_shell",
+ "gun_shell",
+ "keyboard_shell",
+ "module_shell",
+ "money_bot_shell",
+ "scanner_gate_shell",
+ "scanner_shell",
+ "comp_equip_action",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/bci
+ id = "bci"
+ display_name = "Brain-Computer Interface"
+ description = "Embedded brain circuits. May occasionally stream Nanotrasen ads in dreams."
+ prereq_ids = list("circuit_shells", "passive_implants")
+ design_ids = list(
+ "bci_implanter",
+ "bci_shell",
+ "comp_bar_overlay",
+ "comp_camera_bci",
+ "comp_counter_overlay",
+ "comp_install_detector",
+ "comp_object_overlay",
+ "comp_reagent_injector",
+ "comp_target_intercept",
+ "comp_thought_listener",
+ "comp_vox",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/people/skillchip = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/programmed_robot
+ id = "programmed_robot"
+ display_name = "Programmed Robot"
+ description = "Grants access to movable shells, allowing for remote operations and pranks."
+ prereq_ids = list("circuit_shells")
+ design_ids = list(
+ "drone_shell",
+ "comp_pathfind",
+ "comp_pull",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/programmed_server
+ id = "programmed_server"
+ display_name = "Programmed Server"
+ description = "Grants access to a server shell that has a very high capacity for components."
+ prereq_ids = list("bci")
+ design_ids = list(
+ "server_shell",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
diff --git a/code/modules/research/techweb/nodes/cyborg_nodes.dm b/code/modules/research/techweb/nodes/cyborg_nodes.dm
new file mode 100644
index 0000000000000..a4e0debd6ebb8
--- /dev/null
+++ b/code/modules/research/techweb/nodes/cyborg_nodes.dm
@@ -0,0 +1,231 @@
+/datum/techweb_node/augmentation
+ id = "augmentation"
+ starting_node = TRUE
+ display_name = "Augmentation"
+ description = "For those who prefer shiny metal over squishy flesh."
+ prereq_ids = list("robotics")
+ design_ids = list(
+ "borg_chest",
+ "borg_head",
+ "borg_l_arm",
+ "borg_l_leg",
+ "borg_r_arm",
+ "borg_r_leg",
+ "cybernetic_eyes",
+ "cybernetic_eyes_moth",
+ "cybernetic_ears",
+ "cybernetic_lungs",
+ "cybernetic_stomach",
+ "cybernetic_liver",
+ "cybernetic_heart",
+ )
+
+/datum/techweb_node/cybernetics
+ id = "cybernetics"
+ display_name = "Cybernetics"
+ description = "Sapient robots with preloaded tool modules and programmable laws."
+ prereq_ids = list("augmentation")
+ design_ids = list(
+ "robocontrol",
+ "borgupload",
+ "cyborgrecharger",
+ "borg_suit",
+ "mmi_posi",
+ "mmi",
+ "mmi_m",
+ "advanced_l_arm",
+ "advanced_r_arm",
+ "advanced_l_leg",
+ "advanced_r_leg",
+ "borg_upgrade_rename",
+ "borg_upgrade_restart",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/borg_service
+ id = "borg_service"
+ display_name = "Service Cyborg Upgrades"
+ description = "Let them do the cookin' by the book."
+ prereq_ids = list("cybernetics")
+ design_ids = list(
+ "borg_upgrade_rolling_table",
+ "borg_upgrade_condiment_synthesizer",
+ "borg_upgrade_silicon_knife",
+ "borg_upgrade_service_apparatus",
+ "borg_upgrade_drink_apparatus",
+ "borg_upgrade_service_cookbook",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/borg_mining
+ id = "borg_mining"
+ display_name = "Mining Cyborg Upgrades"
+ description = "To mine places too dangerous for humans."
+ prereq_ids = list("cybernetics")
+ design_ids = list(
+ "borg_upgrade_lavaproof",
+ "borg_upgrade_holding",
+ "borg_upgrade_diamonddrill",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/borg_medical
+ id = "borg_medical"
+ display_name = "Medical Cyborg Upgrades"
+ description = "Let them follow Asimov's First Law."
+ prereq_ids = list("borg_service", "surgery_adv")
+ design_ids = list(
+ "borg_upgrade_pinpointer",
+ "borg_upgrade_beakerapp",
+ "borg_upgrade_defibrillator",
+ "borg_upgrade_expandedsynthesiser",
+ "borg_upgrade_piercinghypospray",
+ "borg_upgrade_surgicalprocessor",
+ "borg_upgrade_surgicalomnitool",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/borg_utility
+ id = "borg_utility"
+ display_name = "Untility Cyborg Upgrades"
+ description = "Let them wipe our floors for us."
+ prereq_ids = list("borg_service", "sanitation")
+ design_ids = list(
+ "borg_upgrade_advancedmop",
+ "borg_upgrade_broomer",
+ "borg_upgrade_expand",
+ "borg_upgrade_prt",
+ "borg_upgrade_selfrepair",
+ "borg_upgrade_thrusters",
+ "borg_upgrade_trashofholding",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/borg_utility/New()
+ . = ..()
+ if(!CONFIG_GET(flag/disable_secborg))
+ design_ids += "borg_upgrade_disablercooler"
+
+/datum/techweb_node/borg_engi
+ id = "borg_engi"
+ display_name = "Engineering Cyborg Upgrades"
+ description = "To slack even more."
+ prereq_ids = list("borg_mining", "parts_upg")
+ design_ids = list(
+ "borg_upgrade_rped",
+ "borg_upgrade_engineeringomnitool",
+ "borg_upgrade_circuitapp",
+ "borg_upgrade_inducer",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+// Implants root node
+/datum/techweb_node/passive_implants
+ id = "passive_implants"
+ display_name = "Passive Implants"
+ description = "Implants designed to operate seamlessly without active user input, enhancing various physiological functions or providing continuous benefits."
+ prereq_ids = list("augmentation")
+ design_ids = list(
+ "skill_station",
+ "implant_trombone",
+ "implant_chem",
+ "implant_tracking",
+ "implant_exile",
+ "implant_beacon",
+ "implant_bluespace",
+ "implantcase",
+ "implanter",
+ "locator",
+ "c38_trac",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/cyber/cyber_implants
+ id = "cyber_implants"
+ display_name = "Cybernetic Implants"
+ description = "Advanced technological enhancements integrated into the body, offering improved physical capabilities."
+ prereq_ids = list("passive_implants", "cybernetics")
+ design_ids = list(
+ "ci-breather",
+ "ci-nutriment",
+ "ci-thrusters",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/cyber/New()
+ ..()
+ if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION))
+ research_costs[TECHWEB_POINT_TYPE_GENERIC] /= 2
+
+/datum/techweb_node/cyber/combat_implants
+ id = "combat_implants"
+ display_name = "Combat Implants"
+ description = "To make sure that you can wake the f*** up, samurai."
+ prereq_ids = list("cyber_implants")
+ design_ids = list(
+ "ci-reviver",
+ "ci-antidrop",
+ "ci-antistun",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+
+/datum/techweb_node/cyber/integrated_toolsets
+ id = "integrated_toolsets"
+ display_name = "Integrated Toolsets"
+ description = "Decades of contraband smuggling by assistants have led to the development of a full toolbox that fits seamlessly into your arm."
+ prereq_ids = list("combat_implants", "exp_tools")
+ design_ids = list(
+ "ci-nutrimentplus",
+ "ci-toolset",
+ "ci-surgery",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+
+/datum/techweb_node/cyber/cyber_organs
+ id = "cyber_organs"
+ display_name = "Cybernetic Organs"
+ description = "We have the technology to rebuild him."
+ prereq_ids = list("cybernetics")
+ design_ids = list(
+ "cybernetic_eyes_improved",
+ "cybernetic_eyes_improved_moth",
+ "cybernetic_ears_u",
+ "cybernetic_lungs_tier2",
+ "cybernetic_stomach_tier2",
+ "cybernetic_liver_tier2",
+ "cybernetic_heart_tier2",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/cyber/cyber_organs_upgraded
+ id = "cyber_organs_upgraded"
+ display_name = "Upgraded Cybernetic Organs"
+ description = "We have the technology to upgrade him."
+ prereq_ids = list("cyber_organs")
+ design_ids = list(
+ "ci-gloweyes",
+ "ci-welding",
+ "ci-gloweyes-moth",
+ "ci-welding-moth",
+ "cybernetic_ears_whisper",
+ "cybernetic_lungs_tier3",
+ "cybernetic_stomach_tier3",
+ "cybernetic_liver_tier3",
+ "cybernetic_heart_tier3",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ required_experiments = list(/datum/experiment/scanning/people/augmented_organs)
+
+/datum/techweb_node/cyber/cyber_organs_adv
+ id = "cyber_organs_adv"
+ display_name = "Advanced Cybernetic Organs"
+ description = "Cutting-edge cybernetic organs offering enhanced sensory capabilities, making it easier than ever to detect ERP."
+ prereq_ids = list("cyber_organs_upgraded", "night_vision")
+ design_ids = list(
+ "cybernetic_ears_xray",
+ "ci-thermals",
+ "ci-xray",
+ "ci-thermals-moth",
+ "ci-xray-moth",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
diff --git a/code/modules/research/techweb/nodes/engi_nodes.dm b/code/modules/research/techweb/nodes/engi_nodes.dm
new file mode 100644
index 0000000000000..182f689c372cd
--- /dev/null
+++ b/code/modules/research/techweb/nodes/engi_nodes.dm
@@ -0,0 +1,232 @@
+// Parts root node
+/datum/techweb_node/parts
+ id = "parts"
+ starting_node = TRUE
+ display_name = "Essential Stock Parts"
+ description = "Foundational components that form the backbone of station operations, encompassing a range of essential equipment necessary for day-to-day functionality."
+ design_ids = list(
+ "micro_servo",
+ "basic_capacitor",
+ "basic_matter_bin",
+ "basic_micro_laser",
+ "basic_scanning",
+ "high_cell",
+ "basic_cell",
+ "miniature_power_cell",
+ "condenser",
+ "igniter",
+ "infrared_emitter",
+ "prox_sensor",
+ "signaler",
+ "timer",
+ "voice_analyzer",
+ "health_sensor",
+ "sflash",
+ )
+
+/datum/techweb_node/parts_upg
+ id = "parts_upg"
+ display_name = "Upgraded Parts"
+ description = "Offering enhanced capabilities beyond their basic counterparts."
+ prereq_ids = list("parts", "energy_manipulation")
+ design_ids = list(
+ "rped",
+ "high_micro_laser",
+ "adv_capacitor",
+ "nano_servo",
+ "adv_matter_bin",
+ "adv_scanning",
+ "super_cell",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/parts_adv
+ id = "parts_adv"
+ display_name = "Advanced Parts"
+ description = "The most finely tuned and accurate stock parts."
+ prereq_ids = list("parts_upg")
+ design_ids = list(
+ "ultra_micro_laser",
+ "super_capacitor",
+ "pico_servo",
+ "super_matter_bin",
+ "phasic_scanning",
+ "hyper_cell",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier2_any)
+
+
+/datum/techweb_node/parts_bluespace
+ id = "parts_bluespace"
+ display_name = "Bluespace Parts"
+ description = "Integrating the latest in bluespace technology, these advanced components not only enhance functionality but also open up new possibilities for the station's technological capabilities."
+ prereq_ids = list("parts_adv", "bluespace_travel")
+ design_ids = list(
+ "bs_rped",
+ "quadultra_micro_laser",
+ "quadratic_capacitor",
+ "femto_servo",
+ "bluespace_matter_bin",
+ "triphasic_scanning",
+ "bluespace_cell",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/points/machinery_tiered_scan/tier3_any = TECHWEB_TIER_4_POINTS)
+
+/datum/techweb_node/telecomms
+ id = "telecomms"
+ display_name = "Telecommunications Technology"
+ description = "A comprehensive suite of machinery for station-wide communication setups, ensuring seamless connectivity and operational coordination."
+ prereq_ids = list("parts_bluespace")
+ design_ids = list(
+ "comm_monitor",
+ "comm_server",
+ "message_monitor",
+ "s_hub",
+ "s_messaging",
+ "s_server",
+ "s_processor",
+ "s_relay",
+ "s_bus",
+ "s_broadcaster",
+ "s_receiver",
+ "s_amplifier",
+ "s_analyzer",
+ "s_ansible",
+ "s_crystal",
+ "s_filter",
+ "s_transmitter",
+ "s_treatment",
+ "gigabeacon",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+
+// Engineering root node
+/datum/techweb_node/construction
+ id = "construction"
+ starting_node = TRUE
+ display_name = "Construction"
+ description = "Tools and essential machinery used for station maintenance and expansion."
+ design_ids = list(
+ "circuit_imprinter_offstation",
+ "circuit_imprinter",
+ "solarcontrol",
+ "solar_panel",
+ "solar_tracker",
+ "power_control",
+ "airalarm_electronics",
+ "airlock_board",
+ "firealarm_electronics",
+ "firelock_board",
+ "trapdoor_electronics",
+ "blast",
+ "tile_sprayer",
+ "airlock_painter",
+ "decal_painter",
+ "rwd",
+ "cable_coil",
+ "welding_helmet",
+ "welding_tool",
+ "tscanner",
+ "analyzer",
+ "multitool",
+ "wrench",
+ "crowbar",
+ "screwdriver",
+ "wirecutters",
+ "light_bulb",
+ "light_tube",
+ "intercom_frame",
+ "newscaster_frame",
+ "status_display_frame",
+ "circuit",
+ "circuitgreen",
+ "circuitred",
+ "tram_floor_dark",
+ "tram_floor_light",
+ "tram_controller",
+ "tram_display",
+ "crossing_signal",
+ "guideway_sensor",
+ )
+
+/datum/techweb_node/energy_manipulation
+ id = "energy_manipulation"
+ display_name = "Energy Manipulation"
+ description = "Harnessing the raw power of lightning arcs through sophisticated energy control methods."
+ prereq_ids = list("construction")
+ design_ids = list(
+ "apc_control",
+ "powermonitor",
+ "smes",
+ "emitter",
+ "grounding_rod",
+ "tesla_coil",
+ "cell_charger",
+ "recharger",
+ "inducer",
+ "inducerengi",
+ "welding_goggles",
+ "tray_goggles",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/holographics
+ id = "holographics"
+ display_name = "Holographics"
+ description = "Use of holographic technology for signage and barriers."
+ prereq_ids = list("energy_manipulation")
+ design_ids = list(
+ "forcefield_projector",
+ "holosign",
+ "holosignsec",
+ "holosignengi",
+ "holosignatmos",
+ "holosignrestaurant",
+ "holosignbar",
+ "holobarrier_jani",
+ "holobarrier_med",
+ "holopad",
+ "vendatray",
+ "holodisk",
+ "modular_shield_generator",
+ "modular_shield_node",
+ "modular_shield_relay",
+ "modular_shield_charger",
+ "modular_shield_well",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/hud
+ id = "hud"
+ display_name = "Integrated HUDs"
+ description = "Initially developed for assistants to learn the nuances of different professions through augmented reality."
+ prereq_ids = list("holographics", "cyber_implants")
+ design_ids = list(
+ "health_hud",
+ "diagnostic_hud",
+ "security_hud",
+ "mod_visor_medhud",
+ "mod_visor_diaghud",
+ "mod_visor_sechud",
+ "ci-medhud",
+ "ci-diaghud",
+ "ci-sechud",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/night_vision
+ id = "night_vision"
+ display_name = "Night Vision Technology"
+ description = "There are whispers that Nanotrasen pushed for this technology to extend shift durations, ensuring productivity around the clock."
+ prereq_ids = list("hud")
+ design_ids = list(
+ "diagnostic_hud_night",
+ "health_hud_night",
+ "night_visision_goggles",
+ "nvgmesons",
+ "nv_scigoggles",
+ "security_hud_night",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/mech_nodes.dm b/code/modules/research/techweb/nodes/mech_nodes.dm
new file mode 100644
index 0000000000000..4f58ed25c8955
--- /dev/null
+++ b/code/modules/research/techweb/nodes/mech_nodes.dm
@@ -0,0 +1,251 @@
+/datum/techweb_node/mech_assembly
+ id = "mech_assembly"
+ starting_node = TRUE
+ display_name = "Mech Assembly"
+ description = "Development of mech designed to contend with artificial gravity while transporting cargo."
+ prereq_ids = list("robotics")
+ design_ids = list(
+ "mechapower",
+ "mech_recharger",
+ "ripley_chassis",
+ "ripley_torso",
+ "ripley_left_arm",
+ "ripley_right_arm",
+ "ripley_left_leg",
+ "ripley_right_leg",
+ "ripley_main",
+ "ripley_peri",
+ "mech_hydraulic_clamp",
+ )
+
+/datum/techweb_node/mech_equipment
+ id = "mech_equipment"
+ display_name = "Expedition Equipment"
+ description = "Specialized mech gear tailored for navigating space and celestial bodies, ensuring durability and functionality in the harshest conditions."
+ prereq_ids = list("mech_assembly")
+ design_ids = list(
+ "mechacontrol",
+ "botpad",
+ "botpad_remote",
+ "ripleyupgrade",
+ "mech_air_tank",
+ "mech_thrusters",
+ "mech_extinguisher",
+ "mecha_camera",
+ "mecha_tracking",
+ "mech_radio",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/mech_clown
+ id = "mech_clown"
+ display_name = "Funny Robots"
+ description = "Fueled by laughter."
+ prereq_ids = list("mech_assembly")
+ design_ids = list(
+ "honk_chassis",
+ "honk_torso",
+ "honk_head",
+ "honk_left_arm",
+ "honk_right_arm",
+ "honk_left_leg",
+ "honk_right_leg",
+ "honker_main",
+ "honker_peri",
+ "honker_targ",
+ "mech_banana_mortar",
+ "mech_honker",
+ "mech_mousetrap_mortar",
+ "mech_punching_face",
+ "borg_transform_clown",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/mech_medical
+ id = "mech_medical"
+ display_name = "Medical Mech"
+ description = "Advanced robotic unit equipped with syringe guns and healing beams, revolutionizing medical assistance in hazardous environments."
+ prereq_ids = list("mech_assembly", "chem_synthesis")
+ design_ids = list(
+ "odysseus_chassis",
+ "odysseus_torso",
+ "odysseus_head",
+ "odysseus_left_arm",
+ "odysseus_right_arm",
+ "odysseus_left_leg",
+ "odysseus_right_leg",
+ "odysseus_main",
+ "odysseus_peri",
+ "mech_medi_beam",
+ "mech_syringe_gun",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/mech_mining
+ id = "mech_mining"
+ display_name = "Mining Mech"
+ description = "Robust mech engineered to withstand lava and storms for continuous off-station mining operations."
+ prereq_ids = list("mech_equipment", "mining")
+ design_ids = list(
+ "clarke_chassis",
+ "clarke_torso",
+ "clarke_head",
+ "clarke_left_arm",
+ "clarke_right_arm",
+ "clarke_main",
+ "clarke_peri",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/mech_combat
+ id = "mech_combat"
+ display_name = "Combat Mechs"
+ description = "Modular armor upgrades and specialized equipment for security mechs."
+ prereq_ids = list("mech_equipment")
+ design_ids = list(
+ "mech_ccw_armor",
+ "mech_proj_armor",
+ "paddyupgrade",
+ "mech_hydraulic_claw",
+ "mech_disabler",
+ "mech_repair_droid",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ required_experiments = list(/datum/experiment/scanning/random/mecha_equipped_scan)
+ discount_experiments = list(/datum/experiment/scanning/random/mecha_damage_scan = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/mech_assault
+ id = "mech_assault"
+ display_name = "Assault Mech"
+ description = "Heavy battle mech boasting robust armor but sacrificing speed for enhanced durability."
+ prereq_ids = list("mech_combat")
+ design_ids = list(
+ "durand_armor",
+ "durand_chassis",
+ "durand_torso",
+ "durand_head",
+ "durand_left_arm",
+ "durand_right_arm",
+ "durand_left_leg",
+ "durand_right_leg",
+ "durand_main",
+ "durand_peri",
+ "durand_targ",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/mech_light
+ id = "mech_light"
+ display_name = "Light Combat Mech"
+ description = "Agile combat mech equipped with overclocking capabilities for temporary speed boosts, prioritizing speed over durability on the battlefield."
+ prereq_ids = list("mech_combat")
+ design_ids = list(
+ "gygax_armor",
+ "gygax_chassis",
+ "gygax_torso",
+ "gygax_head",
+ "gygax_left_arm",
+ "gygax_right_arm",
+ "gygax_left_leg",
+ "gygax_right_leg",
+ "gygax_main",
+ "gygax_peri",
+ "gygax_targ",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/mech_heavy
+ id = "mech_heavy"
+ display_name = "Heavy Mech"
+ description = "Advanced heavy mechanized unit with dual pilot capability, designed for robust battlefield performance and increased tactical versatility."
+ prereq_ids = list("mech_assault")
+ design_ids = list(
+ "savannah_ivanov_armor",
+ "savannah_ivanov_chassis",
+ "savannah_ivanov_torso",
+ "savannah_ivanov_head",
+ "savannah_ivanov_left_arm",
+ "savannah_ivanov_right_arm",
+ "savannah_ivanov_left_leg",
+ "savannah_ivanov_right_leg",
+ "savannah_ivanov_main",
+ "savannah_ivanov_peri",
+ "savannah_ivanov_targ",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+
+/datum/techweb_node/mech_infiltrator
+ id = "mech_infiltrator"
+ display_name = "Infiltration Mech"
+ description = "Advanced mech with phasing capabilities, allowing it to move through walls and obstacles, ideal for covert and special operations."
+ prereq_ids = list("mech_light", "anomaly_research")
+ design_ids = list(
+ "phazon_armor",
+ "phazon_chassis",
+ "phazon_torso",
+ "phazon_head",
+ "phazon_left_arm",
+ "phazon_right_arm",
+ "phazon_left_leg",
+ "phazon_right_leg",
+ "phazon_main",
+ "phazon_peri",
+ "phazon_targ",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+
+/datum/techweb_node/mech_energy_guns
+ id = "mech_energy_guns"
+ display_name = "Mech Energy Guns"
+ description = "Scaled-up versions of electric weapons optimized for mech deployment."
+ prereq_ids = list("mech_combat", "electric_weapons")
+ design_ids = list(
+ "mech_laser",
+ "mech_laser_heavy",
+ "mech_ion",
+ "mech_tesla",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+
+/datum/techweb_node/mech_firearms
+ id = "mech_firearms"
+ display_name = "Mech Firearms"
+ description = "Mounted ballistic weaponry, enhancing combat capabilities for mechanized units."
+ prereq_ids = list("mech_energy_guns", "exotic_ammo")
+ design_ids = list(
+ "mech_lmg",
+ "mech_lmg_ammo",
+ "mech_scattershot",
+ "mech_scattershot_ammo",
+ "mech_carbine",
+ "mech_carbine_ammo",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+
+/datum/techweb_node/mech_heavy_arms
+ id = "mech_heavy_arms"
+ display_name = "Heavy Mech Firearms"
+ description = "High-impact weaponry integrated into mechs, optimized for maximum firepower."
+ prereq_ids = list("mech_heavy", "exotic_ammo")
+ design_ids = list(
+ "clusterbang_launcher",
+ "clusterbang_launcher_ammo",
+ "mech_grenade_launcher",
+ "mech_grenade_launcher_ammo",
+ "mech_missile_rack",
+ "mech_missile_rack_ammo",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+
+/datum/techweb_node/mech_equip_bluespace
+ id = "mech_equip_bluespace"
+ display_name = "Bluespace Mech Equipment"
+ description = "An array of equipment empowered by bluespace, providing unmatched mobility and utility."
+ prereq_ids = list("mech_infiltrator", "bluespace_travel")
+ design_ids = list(
+ "mech_gravcatapult",
+ "mech_teleporter",
+ "mech_wormhole_gen",
+ "mech_rcd",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
diff --git a/code/modules/research/techweb/nodes/medbay_nodes.dm b/code/modules/research/techweb/nodes/medbay_nodes.dm
new file mode 100644
index 0000000000000..ce4ef103eda2d
--- /dev/null
+++ b/code/modules/research/techweb/nodes/medbay_nodes.dm
@@ -0,0 +1,97 @@
+/datum/techweb_node/medbay_equip
+ id = "medbay_equip"
+ starting_node = TRUE
+ display_name = "Medbay Equipment"
+ description = "Essential medical tools to patch you up while medbay is still intact."
+ design_ids = list(
+ "operating",
+ "medicalbed",
+ "defibmountdefault",
+ "defibrillator",
+ "surgical_drapes",
+ "scalpel",
+ "retractor",
+ "hemostat",
+ "cautery",
+ "circular_saw",
+ "surgicaldrill",
+ "bonesetter",
+ "blood_filter",
+ "surgical_tape",
+ "penlight",
+ "penlight_paramedic",
+ "stethoscope",
+ "beaker",
+ "large_beaker",
+ "syringe",
+ "dropper",
+ "pillbottle",
+ )
+
+/datum/techweb_node/chem_synthesis
+ id = "chem_synthesis"
+ display_name = "Chemical Synthesis"
+ description = "Synthesizing complex chemicals from electricity and thin air... Don't ask how..."
+ prereq_ids = list("medbay_equip")
+ design_ids = list(
+ "xlarge_beaker",
+ "blood_pack",
+ "chem_pack",
+ "med_spray_bottle",
+ "medigel",
+ "medipen_refiller",
+ "soda_dispenser",
+ "beer_dispenser",
+ "chem_dispenser",
+ "portable_chem_mixer",
+ "chem_heater",
+ "w-recycler",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/plumbing
+ id = "plumbing"
+ display_name = "Plumbing"
+ description = "Essential infrastructure for building chemical factories. To scale up the production of happy pills to an industrial level."
+ prereq_ids = list("chem_synthesis")
+ design_ids = list(
+ "plumbing_rcd",
+ "plumbing_rcd_service",
+ "plumbing_rcd_sci",
+ "plunger",
+ "fluid_ducts",
+ "meta_beaker",
+ "piercesyringe",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/cryostasis
+ id = "cryostasis"
+ display_name = "Cryostasis"
+ description = "The result of clown accidentally drinking a chemical, now repurposed for safely preserving crew members in suspended animation."
+ prereq_ids = list("plumbing", "plasma_control")
+ design_ids = list(
+ "cryotube",
+ "mech_sleeper",
+ "stasis",
+ "cryo_grenade",
+ "splitbeaker",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/scanning/reagent/cryostylane)
+
+/datum/techweb_node/medbay_equip_adv
+ id = "medbay_equip_adv"
+ display_name = "Advanced Medbay Equipment"
+ description = "State-of-the-art medical gear for keeping the crew in one piece — mostly."
+ prereq_ids = list("cryostasis")
+ design_ids = list(
+ "chem_mass_spec",
+ "healthanalyzer_advanced",
+ "mod_health_analyzer",
+ "crewpinpointer",
+ "defibrillator_compact",
+ "defibmount",
+ "medicalbed_emergency",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/mining_nodes.dm b/code/modules/research/techweb/nodes/mining_nodes.dm
new file mode 100644
index 0000000000000..e1caaf57902e2
--- /dev/null
+++ b/code/modules/research/techweb/nodes/mining_nodes.dm
@@ -0,0 +1,104 @@
+/datum/techweb_node/material_processing
+ id = "material_proc"
+ starting_node = TRUE
+ display_name = "Material Processing"
+ description = "Refinement and processing of alloys and ores to enhance their utility and value."
+ design_ids = list(
+ "pickaxe",
+ "shovel",
+ "conveyor_switch",
+ "conveyor_belt",
+ "mass_driver",
+ "recycler",
+ "stack_machine",
+ "stack_console",
+ "autolathe",
+ "rglass",
+ "plasmaglass",
+ "plasmareinforcedglass",
+ "plasteel",
+ "titaniumglass",
+ "plastitanium",
+ "plastitaniumglass",
+ )
+
+/datum/techweb_node/mining
+ id = "mining"
+ display_name = "Mining Technology"
+ description = "Development of tools meant to optimize mining operations and resource extraction."
+ prereq_ids = list("material_proc")
+ design_ids = list(
+ "cargoexpress",
+ "brm",
+ "b_smelter",
+ "b_refinery",
+ "ore_redemption",
+ "mining_equipment_vendor",
+ "mining_scanner",
+ "mech_mscanner",
+ "superresonator",
+ "mech_drill",
+ "mod_drill",
+ "drill",
+ "mod_orebag",
+ "beacon",
+ "telesci_gps",
+ "mod_gps",
+ "mod_visor_meson",
+ "mesons",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/low_pressure_excavation
+ id = "low_pressure_excavation"
+ display_name = "Low-Pressure Excavation"
+ description = "Research of Proto-Kinetic Accelerators (PKAs), pneumatic guns renowned for their exceptional performance in low-pressure environments."
+ prereq_ids = list("mining", "gas_compression")
+ design_ids = list(
+ "mecha_kineticgun",
+ "damagemod",
+ "rangemod",
+ "cooldownmod",
+ "triggermod",
+ "hypermod",
+ "borg_upgrade_damagemod",
+ "borg_upgrade_rangemod",
+ "borg_upgrade_cooldownmod",
+ "borg_upgrade_hypermod",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/plasma_mining
+ id = "plasma_mining"
+ display_name = "Plasma Beam Mining"
+ description = "Engineers' plasma welders have proven highly effective in mining operations. This led to the development of a mech-mounted variant and an enhanced handheld cutter for miners."
+ prereq_ids = list("low_pressure_excavation", "plasma_control")
+ design_ids = list(
+ "mech_plasma_cutter",
+ "plasmacutter_adv",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/bitrunning
+ id = "bitrunning"
+ display_name = "Bitrunning Technology"
+ description = "Bluespace technology has led to the development of quantum-scale computing, which unlocks the means to materialize atomic structures while executing advanced programs."
+ prereq_ids = list("gaming", "applied_bluespace")
+ design_ids = list(
+ "byteforge",
+ "quantum_console",
+ "netpod",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/mining_adv
+ id = "mining_adv"
+ display_name = "Advanced Mining Technology"
+ description = "High-level mining equipment, pushing the boundaries of efficiency and effectiveness in resource extraction."
+ prereq_ids = list("plasma_mining")
+ design_ids = list(
+ "jackhammer",
+ "drill_diamond",
+ "mech_diamond_drill",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/modsuit_nodes.dm b/code/modules/research/techweb/nodes/modsuit_nodes.dm
new file mode 100644
index 0000000000000..a5005a66ee794
--- /dev/null
+++ b/code/modules/research/techweb/nodes/modsuit_nodes.dm
@@ -0,0 +1,139 @@
+/datum/techweb_node/mod_suit
+ id = "mod_suit"
+ starting_node = TRUE
+ display_name = "Modular Exosuit"
+ description = "Specialized back mounted power suits with various different modules."
+ prereq_ids = list("robotics")
+ design_ids = list(
+ "suit_storage_unit",
+ "mod_shell",
+ "mod_chestplate",
+ "mod_helmet",
+ "mod_gauntlets",
+ "mod_boots",
+ "mod_plating_standard",
+ "mod_paint_kit",
+ "mod_storage",
+ "mod_plasma",
+ "mod_flashlight",
+ )
+
+/datum/techweb_node/mod_equip
+ id = "mod_equip"
+ display_name = "Modular Suit Equipment"
+ description = "More advanced modules, to improve modular suits."
+ prereq_ids = list("mod_suit")
+ design_ids = list(
+ "modlink_scryer",
+ "mod_clamp",
+ "mod_tether",
+ "mod_welding",
+ "mod_safety",
+ "mod_mouthhole",
+ "mod_longfall",
+ "mod_thermal_regulator",
+ "mod_sign_radio",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/mod_entertainment
+ id = "mod_entertainment"
+ display_name = "Entertainment Modular Suit"
+ description = "Powered suits for protection against low-humor environments."
+ prereq_ids = list("mod_suit")
+ design_ids = list(
+ "mod_plating_cosmohonk",
+ "mod_bikehorn",
+ "mod_microwave_beam",
+ "mod_waddle",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/mod_medical
+ id = "mod_medical"
+ display_name = "Medical Modular Suit"
+ description = "Medical exosuits for quick rescue purposes."
+ prereq_ids = list("mod_suit", "chem_synthesis")
+ design_ids = list(
+ "mod_plating_medical",
+ "mod_quick_carry",
+ "mod_injector",
+ "mod_organ_thrower",
+ "mod_patienttransport",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/mod_engi
+ id = "mod_engi"
+ display_name = "Engineering Modular Suits"
+ description = "Engineering suits, for powered engineers."
+ prereq_ids = list("mod_equip")
+ design_ids = list(
+ "mod_plating_engineering",
+ "mod_t_ray",
+ "mod_magboot",
+ "mod_constructor",
+ "mod_mister_atmos",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/mod_security
+ id = "mod_security"
+ display_name = "Security Modular Suits"
+ description = "Security suits for space crime handling."
+ prereq_ids = list("mod_equip")
+ design_ids = list(
+ "mod_plating_security",
+ "mod_stealth",
+ "mod_mag_harness",
+ "mod_pathfinder",
+ "mod_holster",
+ "mod_sonar",
+ "mod_projectile_dampener",
+ "mod_criminalcapture",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/mod_medical_adv
+ id = "mod_medical_adv"
+ display_name = "Field Surgery Modules"
+ description = "Medical exosuit equipment designed for conducting surgical operations in field conditions."
+ prereq_ids = list("mod_medical", "surgery_adv")
+ design_ids = list(
+ "mod_defib",
+ "mod_threadripper",
+ "mod_surgicalprocessor",
+ "mod_statusreadout",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/mod_engi_adv
+ id = "mod_engi_adv"
+ display_name = "Advanced Engineering Modular Suit"
+ description = "Advanced Engineering suits, for advanced powered engineers."
+ prereq_ids = list("mod_engi")
+ design_ids = list(
+ "mod_plating_atmospheric",
+ "mod_jetpack",
+ "mod_rad_protection",
+ "mod_emp_shield",
+ "mod_storage_expanded",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/mod_engi_adv/New()
+ if(HAS_TRAIT(SSstation, STATION_TRAIT_RADIOACTIVE_NEBULA)) //we'll really need the rad protection modsuit module
+ starting_node = TRUE
+ return ..()
+
+/datum/techweb_node/mod_anomaly
+ id = "mod_anomaly"
+ display_name = "Anomalock Modular Suit"
+ description = "Modules for exosuits that require anomaly cores to function."
+ prereq_ids = list("mod_engi_adv", "anomaly_research")
+ design_ids = list(
+ "mod_antigrav",
+ "mod_teleporter",
+ "mod_kinesis",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/research_nodes.dm b/code/modules/research/techweb/nodes/research_nodes.dm
new file mode 100644
index 0000000000000..b3af62c72e994
--- /dev/null
+++ b/code/modules/research/techweb/nodes/research_nodes.dm
@@ -0,0 +1,94 @@
+/datum/techweb_node/fundamental_sci
+ id = "fundamental_sci"
+ starting_node = TRUE
+ display_name = "Fundamental Science"
+ description = "Establishing the bedrock of scientific understanding, paving the way for deeper exploration and theoretical inquiry."
+ design_ids = list(
+ "rdserver",
+ "rdservercontrol",
+ "rdconsole",
+ "tech_disk",
+ "doppler_array",
+ "experimentor",
+ "destructive_analyzer",
+ "destructive_scanner",
+ "experi_scanner",
+ "ntnet_relay",
+ "laptop",
+ "portadrive_basic",
+ "portadrive_advanced",
+ "portadrive_super",
+ )
+
+/datum/techweb_node/bluespace_theory
+ id = "bluespace_theory"
+ display_name = "Bluespace Theory"
+ description = "Basic studies into the mysterious alternate dimension known as bluespace."
+ prereq_ids = list("fundamental_sci")
+ design_ids = list(
+ "bluespace_crystal",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/applied_bluespace
+ id = "applied_bluespace"
+ display_name = "Applied Bluespace Research"
+ description = "With a heightened grasp of bluespace dynamics, sophisticated applications and technologies can be devised using data from bluespace crystal analyses."
+ prereq_ids = list("bluespace_theory")
+ design_ids = list(
+ "ore_silo",
+ "minerbag_holding",
+ "plumbing_receiver",
+ "bluespacebeaker",
+ "adv_watering_can",
+ "bluespace_coffeepot",
+ "bluespacesyringe",
+ "blutrash",
+ "light_replacer_blue",
+ "bluespacebodybag",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ required_experiments = list(/datum/experiment/scanning/bluespace_crystal)
+
+/datum/techweb_node/bluespace_travel
+ id = "bluespace_travel"
+ display_name = "Bluespace Travel"
+ description = "Facilitate teleportation methods based on bluespace principles to revolutionize logistical efficiency."
+ prereq_ids = list("applied_bluespace")
+ design_ids = list(
+ "teleconsole",
+ "tele_station",
+ "tele_hub",
+ "launchpad_console",
+ "quantumpad",
+ "launchpad",
+ "bluespace_pod",
+ "quantum_keycard",
+ "swapper",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/anomaly_research
+ id = "anomaly_research"
+ display_name = "Anomaly Research"
+ description = "Delving into the study of mysterious anomalies to investigate methods to refine and harness their unpredictable energies."
+ prereq_ids = list("applied_bluespace")
+ design_ids = list(
+ "anomaly_refinery",
+ "anomaly_neutralizer",
+ "reactive_armour",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/anomaly_shells
+ id = "anomaly_shells"
+ display_name = "Advanced Anomaly Shells"
+ description = "New shells designed to utilize anomaly cores, maximizing their potential in innovative ways."
+ prereq_ids = list("anomaly_research")
+ design_ids = list(
+ "bag_holding",
+ "wormholeprojector",
+ "gravitygun",
+ "polymorph_belt"
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/robo_nodes.dm b/code/modules/research/techweb/nodes/robo_nodes.dm
new file mode 100644
index 0000000000000..556fa560daf6b
--- /dev/null
+++ b/code/modules/research/techweb/nodes/robo_nodes.dm
@@ -0,0 +1,97 @@
+/datum/techweb_node/robotics
+ id = "robotics"
+ starting_node = TRUE
+ display_name = "Robotics"
+ description = "Programmable machines that make our lives lazier."
+ design_ids = list(
+ "mechfab",
+ "botnavbeacon",
+ "paicard",
+ )
+
+/datum/techweb_node/exodrone
+ id = "exodrone"
+ display_name = "Exploration Drones"
+ description = "Adapted arcade machines to covertly harness gamers' skills in controlling real drones for practical purposes."
+ prereq_ids = list("robotics")
+ design_ids = list(
+ "exoscanner_console",
+ "exoscanner",
+ "exodrone_console",
+ "exodrone_launcher",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+// AI root node
+/datum/techweb_node/ai
+ id = "ai"
+ display_name = "Artificial Intelligence"
+ description = "Exploration of AI systems, more intelligent than the entire crew put together."
+ prereq_ids = list("robotics")
+ design_ids = list(
+ "aiupload",
+ "aifixer",
+ "intellicard",
+ "mecha_tracking_ai_control",
+ "borg_ai_control",
+ "aicore",
+ "reset_module",
+ "asimov_module",
+ "default_module",
+ "nutimov_module",
+ "paladin_module",
+ "robocop_module",
+ "corporate_module",
+ "drone_module",
+ "oxygen_module",
+ "safeguard_module",
+ "protectstation_module",
+ "quarantine_module",
+ "freeform_module",
+ "remove_module",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/ai/New()
+ . = ..()
+ if(HAS_TRAIT(SSstation, STATION_TRAIT_HUMAN_AI))
+ design_ids -= list(
+ "aicore",
+ "borg_ai_control",
+ "intellicard",
+ "mecha_tracking_ai_control",
+ "aifixer",
+ "aiupload",
+ )
+ else if(HAS_TRAIT(SSstation, STATION_TRAIT_UNIQUE_AI))
+ research_costs[TECHWEB_POINT_TYPE_GENERIC] *= 3
+
+/datum/techweb_node/ai_laws
+ id = "ai_laws"
+ display_name = "Advanced AI Laws"
+ description = "Delving into sophisticated AI directives, with hopes that they won't lead to humanity's extinction."
+ prereq_ids = list("ai")
+ design_ids = list(
+ "asimovpp_module",
+ "paladin_devotion_module",
+ "dungeon_master_module",
+ "painter_module",
+ "ten_commandments_module",
+ "hippocratic_module",
+ "maintain_module",
+ "liveandletlive_module",
+ "reporter_module",
+ "yesman_module",
+ "hulkamania_module",
+ "peacekeeper_module",
+ "overlord_module",
+ "tyrant_module",
+ "antimov_module",
+ "balance_module",
+ "thermurderdynamic_module",
+ "damaged_module",
+ "freeformcore_module",
+ "onehuman_module",
+ "purge_module",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
diff --git a/code/modules/research/techweb/nodes/security_nodes.dm b/code/modules/research/techweb/nodes/security_nodes.dm
new file mode 100644
index 0000000000000..97092f2682a63
--- /dev/null
+++ b/code/modules/research/techweb/nodes/security_nodes.dm
@@ -0,0 +1,107 @@
+/datum/techweb_node/basic_arms
+ id = "basic_arms"
+ starting_node = TRUE
+ display_name = "Basic Arms"
+ description = "Ballistics can be unpredictable in space."
+ design_ids = list(
+ "toygun",
+ "c38_rubber",
+ "sec_38",
+ "capbox",
+ "foam_dart",
+ "sec_beanbag_slug",
+ "sec_dart",
+ "sec_Islug",
+ "sec_rshot",
+ )
+
+/datum/techweb_node/sec_equip
+ id = "sec_equip"
+ display_name = "Security Equipment"
+ description = "All the essentials to subdue a mime."
+ prereq_ids = list("basic_arms")
+ design_ids = list(
+ "camera_assembly",
+ "secdata",
+ "mining",
+ "prisonmanage",
+ "rdcamera",
+ "seccamera",
+ "security_photobooth",
+ "photobooth",
+ "scanner_gate",
+ "turret_control",
+ "pepperspray",
+ "inspector",
+ "evidencebag",
+ "handcuffs_s",
+ "zipties",
+ "seclite",
+ "electropack",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/riot_supression
+ id = "riot_supression"
+ display_name = "Riot Supression"
+ description = "When you are on the opposing side of a revolutionary movement."
+ prereq_ids = list("sec_equip")
+ design_ids = list(
+ "pin_testing",
+ "pin_loyalty",
+ "tele_shield",
+ "ballistic_shield",
+ "bola_energy",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/explosives
+ id = "explosives"
+ display_name = "Explosives"
+ description = "For once, intentional explosions."
+ prereq_ids = list("riot_supression")
+ design_ids = list(
+ "large_grenade",
+ "adv_grenade",
+ "pyro_grenade",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/ordnance/explosive/lowyieldbomb)
+ discount_experiments = list(/datum/experiment/ordnance/explosive/highyieldbomb = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/exotic_ammo
+ id = "exotic_ammo"
+ display_name = "Exotic Ammunition"
+ description = "Specialized bullets designed to ignite, freeze, and inflict various other effects on targets, expanding combat capabilities."
+ prereq_ids = list("explosives")
+ design_ids = list(
+ "c38_hotshot",
+ "c38_iceblox",
+ "techshotshell",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+
+/datum/techweb_node/electric_weapons
+ id = "electric_weapons"
+ display_name = "Electric Weaponry"
+ description = "Energy-based weaponry designed for both lethal and non-lethal applications."
+ prereq_ids = list("riot_supression")
+ design_ids = list(
+ "stunrevolver",
+ "ioncarbine",
+ "temp_gun",
+ "lasershell",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+
+/datum/techweb_node/beam_weapons
+ id = "beam_weapons"
+ display_name = "Advanced Beam Weaponry"
+ description = "So advanced, even engineers are baffled by its operational principles."
+ prereq_ids = list("electric_weapons")
+ design_ids = list(
+ "beamrifle",
+ "xray_laser",
+ "nuclear_gun",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/service_nodes.dm b/code/modules/research/techweb/nodes/service_nodes.dm
new file mode 100644
index 0000000000000..ff56afc115eb5
--- /dev/null
+++ b/code/modules/research/techweb/nodes/service_nodes.dm
@@ -0,0 +1,236 @@
+/datum/techweb_node/office_equip
+ id = "office_equip"
+ starting_node = TRUE
+ display_name = "Office Equipment"
+ description = "Nanotrasen's finest in ergonomic office tech, ensuring station admin stays productive and compliant with corporate policies — because even in space, paperwork never stops."
+ design_ids = list(
+ "fax",
+ "sec_pen",
+ "handlabel",
+ "roll",
+ "universal_scanner",
+ "desttagger",
+ "packagewrap",
+ "sticky_tape",
+ "toner_large",
+ "toner",
+ "boxcutter",
+ "bounced_radio",
+ "radio_headset",
+ "earmuffs",
+ "recorder",
+ "tape",
+ "toy_balloon",
+ "pet_carrier",
+ "chisel",
+ "spraycan",
+ "camera_film",
+ "camera",
+ "razor",
+ "bucket",
+ "mop",
+ "pushbroom",
+ "normtrash",
+ "wirebrush",
+ "flashlight",
+ )
+
+/datum/techweb_node/sanitation
+ id = "sanitation"
+ display_name = "Advanced Sanitation Technology"
+ description = "Nanotrasen's latest in janitorial tech, making sure the station stays spotless and bear-free."
+ prereq_ids = list("office_equip")
+ design_ids = list(
+ "advmop",
+ "light_replacer",
+ "spraybottle",
+ "paint_remover",
+ "beartrap",
+ "buffer",
+ "vacuum",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/random/janitor_trash = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/toys
+ id = "toys"
+ display_name = "New Toys"
+ description = "For new pranks."
+ prereq_ids = list("office_equip")
+ design_ids = list(
+ "smoke_machine",
+ "toy_armblade",
+ "air_horn",
+ "clown_firing_pin",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/consoles
+ id = "consoles"
+ display_name = "Civilian Consoles"
+ description = "User-friendly consoles for non-technical crew members, enhancing communication and access to essential station information."
+ prereq_ids = list("office_equip")
+ design_ids = list(
+ "comconsole",
+ "automated_announcement",
+ "cargo",
+ "cargorequest",
+ "med_data",
+ "crewconsole",
+ "bankmachine",
+ "account_console",
+ "idcard",
+ "c-reader",
+ "libraryconsole",
+ "barcode_scanner",
+ "vendor",
+ "custom_vendor_refill",
+ "bounty_pad_control",
+ "bounty_pad",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/gaming
+ id = "gaming"
+ display_name = "Gaming"
+ description = "For the slackers on the station."
+ prereq_ids = list("toys", "consoles")
+ design_ids = list(
+ "arcade_battle",
+ "arcade_orion",
+ "slotmachine",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ discount_experiments = list(/datum/experiment/physical/arcade_winner = TECHWEB_TIER_2_POINTS)
+
+// Kitchen root node
+/datum/techweb_node/cafeteria_equip
+ id = "cafeteria_equip"
+ starting_node = TRUE
+ display_name = "Cafeteria Equipment"
+ description = "When standard-issue tubed food no longer satisfies the station crew's appetite..."
+ design_ids = list(
+ "griddle",
+ "microwave",
+ "bowl",
+ "plate",
+ "oven_tray",
+ "servingtray",
+ "tongs",
+ "spoon",
+ "fork",
+ "kitchen_knife",
+ "plastic_spoon",
+ "plastic_fork",
+ "plastic_knife",
+ "shaker",
+ "drinking_glass",
+ "shot_glass",
+ "coffee_cartridge",
+ "coffeemaker",
+ "coffeepot",
+ "syrup_bottle",
+ )
+
+/datum/techweb_node/food_proc
+ id = "food_proc"
+ display_name = "Food Processing"
+ description = "Top-tier kitchen appliances from Nanotrasen, designed to keep the crew well-fed and happy."
+ prereq_ids = list("cafeteria_equip")
+ design_ids = list(
+ "deepfryer",
+ "oven",
+ "stove",
+ "range",
+ "souppot",
+ "processor",
+ "gibber",
+ "monkey_recycler",
+ "reagentgrinder",
+ "microwave_engineering",
+ "smartfridge",
+ "sheetifier",
+ "fat_sucker",
+ "dish_drive",
+ "roastingstick",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+// Fishing root node
+/datum/techweb_node/fishing_equip
+ id = "fishing_equip"
+ starting_node = TRUE
+ display_name = "Fishing Equipment"
+ description = "Basic fishing gear tailored for space station environments, perfect for extraterrestrial aquatic pursuits."
+ design_ids = list(
+ "fishing_portal_generator",
+ "fishing_rod",
+ "fish_case",
+ )
+
+/datum/techweb_node/fishing_equip_adv
+ id = "fishing_equip_adv"
+ display_name = "Advanced Fishing Tools"
+ description = "Continuing advancements in fishing technology, incorporating cutting-edge features in space fishing operations. Just don't try this on space carps..."
+ prereq_ids = list("fishing_equip")
+ design_ids = list(
+ "fishing_rod_tech",
+ "stabilized_hook",
+ "auto_reel",
+ "fish_analyzer",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ required_experiments = list(/datum/experiment/scanning/fish)
+
+/datum/techweb_node/marine_util
+ id = "marine_util"
+ display_name = "Marine Utility"
+ description = "Fish are nice to look at and all, but they can be put to use."
+ prereq_ids = list("fishing_equip_adv")
+ design_ids = list(
+ "bioelec_gen",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ // only available if you've done the first fishing experiment (thus unlocking fishing tech), but not a strict requirement to get the tech
+ discount_experiments = list(/datum/experiment/scanning/fish/second = TECHWEB_TIER_3_POINTS)
+
+// Botany root node
+/datum/techweb_node/botany_equip
+ id = "botany_equip"
+ starting_node = TRUE
+ display_name = "Botany Equipment"
+ description = "Essential tools for maintaining onboard gardens, supporting plant growth in the unique environment of the space station."
+ design_ids = list(
+ "seed_extractor",
+ "plant_analyzer",
+ "watering_can",
+ "spade",
+ "cultivator",
+ "secateurs",
+ "hatchet",
+ )
+
+/datum/techweb_node/hydroponics
+ id = "hydroponics"
+ display_name = "Hydroponics"
+ description = "Research into advanced hydroponic systems for efficient and sustainable plant cultivation."
+ prereq_ids = list("botany_equip", "chem_synthesis")
+ design_ids = list(
+ "biogenerator",
+ "hydro_tray",
+ "portaseeder",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/selection
+ id = "selection"
+ display_name = "Artificial Selection"
+ description = "Advancement in plant cultivation techniques through artificial selection, enabling precise manipulation of plant DNA."
+ prereq_ids = list("hydroponics")
+ design_ids = list(
+ "flora_gun",
+ "gene_shears",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/scanning/random/plants/wild)
+ discount_experiments = list(/datum/experiment/scanning/random/plants/traits = TECHWEB_TIER_3_POINTS)
diff --git a/code/modules/research/techweb/nodes/surgery_nodes.dm b/code/modules/research/techweb/nodes/surgery_nodes.dm
new file mode 100644
index 0000000000000..3cd3d373c0599
--- /dev/null
+++ b/code/modules/research/techweb/nodes/surgery_nodes.dm
@@ -0,0 +1,72 @@
+/datum/techweb_node/oldstation_surgery
+ id = "oldstation_surgery"
+ display_name = "Experimental Dissection"
+ description = "Grants access to experimental dissections, which allows generation of research points."
+ prereq_ids = list("medbay_equip")
+ design_ids = list(
+ "surgery_oldstation_dissection",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+ hidden = TRUE
+ show_on_wiki = FALSE
+
+/datum/techweb_node/surgery
+ id = "surgery"
+ display_name = "Improved Wound-Tending"
+ description = "Who would have known being more gentle with a hemostat decreases patient pain?"
+ prereq_ids = list("medbay_equip")
+ design_ids = list(
+ "surgery_heal_brute_upgrade",
+ "surgery_heal_burn_upgrade",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
+
+/datum/techweb_node/surgery_adv
+ id = "surgery_adv"
+ display_name = "Advanced Surgery"
+ description = "When simple medicine doesn't cut it."
+ prereq_ids = list("surgery")
+ design_ids = list(
+ "harvester",
+ "surgery_heal_brute_upgrade_femto",
+ "surgery_heal_burn_upgrade_femto",
+ "surgery_heal_combo",
+ "surgery_lobotomy",
+ "surgery_wing_reconstruction",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ required_experiments = list(/datum/experiment/autopsy/human)
+
+/datum/techweb_node/surgery_exp
+ id = "surgery_exp"
+ display_name = "Experimental Surgery"
+ description = "When evolution isn't fast enough."
+ prereq_ids = list("surgery_adv")
+ design_ids = list(
+ "surgery_cortex_folding",
+ "surgery_cortex_imprint",
+ "surgery_heal_combo_upgrade",
+ "surgery_ligament_hook",
+ "surgery_ligament_reinforcement",
+ "surgery_muscled_veins",
+ "surgery_nerve_ground",
+ "surgery_nerve_splice",
+ "surgery_pacify",
+ "surgery_vein_thread",
+ "surgery_viral_bond",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/autopsy/nonhuman)
+
+/datum/techweb_node/surgery_tools
+ id = "surgery_tools"
+ display_name = "Advanced Surgery Tools"
+ description = "Surgical instruments of dual purpose for quick operations."
+ prereq_ids = list("surgery_exp")
+ design_ids = list(
+ "laserscalpel",
+ "searingtool",
+ "mechanicalpinches",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(/datum/experiment/autopsy/xenomorph = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/syndicate_nodes.dm b/code/modules/research/techweb/nodes/syndicate_nodes.dm
new file mode 100644
index 0000000000000..c571551877099
--- /dev/null
+++ b/code/modules/research/techweb/nodes/syndicate_nodes.dm
@@ -0,0 +1,48 @@
+/datum/techweb_node/syndicate_basic
+ id = "syndicate_basic"
+ display_name = "Illegal Technology"
+ description = "Dangerous research used to create dangerous objects."
+ prereq_ids = list("exp_tools", "exotic_ammo")
+ design_ids = list(
+ "advanced_camera",
+ "ai_cam_upgrade",
+ "borg_syndicate_module",
+ "donksoft_refill",
+ "donksofttoyvendor",
+ "largecrossbow",
+ "mag_autorifle",
+ "mag_autorifle_ap",
+ "mag_autorifle_ic",
+ "rapidsyringe",
+ "suppressor",
+ "super_pointy_tape",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+ hidden = TRUE
+
+/datum/techweb_node/syndicate_basic/New() //Crappy way of making syndicate gear decon supported until there's another way.
+ . = ..()
+ if(!SSearly_assets.initialized)
+ RegisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(register_uplink_items))
+ else
+ register_uplink_items()
+
+/datum/techweb_node/syndicate_basic/proc/register_uplink_items()
+ SIGNAL_HANDLER
+ UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
+ required_items_to_unlock = list()
+ for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
+ var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
+ if(!item.item || !item.illegal_tech)
+ continue
+ required_items_to_unlock |= item.item //allows deconning to unlock.
+
+/datum/techweb_node/unregulated_bluespace
+ id = "unregulated_bluespace"
+ display_name = "Unregulated Bluespace Research"
+ description = "Bluespace technology using unstable or unbalanced procedures, prone to damaging the fabric of bluespace. Outlawed by galactic conventions."
+ prereq_ids = list("parts_bluespace", "syndicate_basic")
+ design_ids = list(
+ "desynchronizer",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 318926465ae08..c03a930395ab9 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -362,7 +362,7 @@
if(ishuman(victim))
var/mob/living/carbon/human/human_victim = victim
- if(HAS_TRAIT(victim, TRAIT_LIMBATTACHMENT))
+ if(HAS_TRAIT(victim, TRAIT_LIMBATTACHMENT) || HAS_TRAIT(src, TRAIT_EASY_ATTACH))
if(!human_victim.get_bodypart(body_zone))
user.temporarilyRemoveItemFromInventory(src, TRUE)
if(!try_attach_limb(victim))
diff --git a/code/modules/surgery/bodyparts/ghetto_parts.dm b/code/modules/surgery/bodyparts/ghetto_parts.dm
new file mode 100644
index 0000000000000..36c0310dead76
--- /dev/null
+++ b/code/modules/surgery/bodyparts/ghetto_parts.dm
@@ -0,0 +1,79 @@
+/obj/item/bodypart/arm/left/ghetto
+ name = "left peg arm"
+ desc = "A roughly hewn wooden peg replaces where a forearm should be. It's simple and sturdy, clearly made in a hurry with whatever materials were at hand. Despite its crude appearance, it gets the job done."
+ icon = 'icons/mob/human/species/ghetto.dmi'
+ icon_static = 'icons/mob/human/species/ghetto.dmi'
+ limb_id = BODYPART_ID_PEG
+ icon_state = "peg_l_arm"
+ bodytype = BODYTYPE_PEG
+ should_draw_greyscale = FALSE
+ attack_verb_simple = list("bashed", "slashed")
+ unarmed_damage_low = 3
+ unarmed_damage_high = 9
+ unarmed_effectiveness = 5
+ brute_modifier = 1.2
+ burn_modifier = 1.5
+ bodypart_traits = list(TRAIT_CHUNKYFINGERS)
+
+/obj/item/bodypart/arm/left/ghetto/Initialize(mapload, ...)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_EASY_ATTACH, INNATE_TRAIT)
+
+/obj/item/bodypart/arm/right/ghetto
+ name = "right peg arm"
+ desc = "A roughly hewn wooden peg replaces where a forearm should be. It's simple and sturdy, clearly made in a hurry with whatever materials were at hand. Despite its crude appearance, it gets the job done."
+ icon = 'icons/mob/human/species/ghetto.dmi'
+ icon_static = 'icons/mob/human/species/ghetto.dmi'
+ limb_id = BODYPART_ID_PEG
+ icon_state = "peg_r_arm"
+ bodytype = BODYTYPE_PEG
+ should_draw_greyscale = FALSE
+ attack_verb_simple = list("bashed", "slashed")
+ unarmed_damage_low = 3
+ unarmed_damage_high = 9
+ unarmed_effectiveness = 5
+ brute_modifier = 1.2
+ burn_modifier = 1.5
+ bodypart_traits = list(TRAIT_CHUNKYFINGERS)
+
+/obj/item/bodypart/arm/right/ghetto/Initialize(mapload, ...)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_EASY_ATTACH, INNATE_TRAIT)
+
+/obj/item/bodypart/leg/left/ghetto
+ name = "left peg leg"
+ desc = "Fashioned from what looks suspiciously like a table leg, this peg leg brings a whole new meaning to 'dining on the go.' It's a bit wobbly and creaks ominously with every step, but at least you can claim to have the most well-balanced diet on the seven seas."
+ icon = 'icons/mob/human/species/ghetto.dmi'
+ icon_static = 'icons/mob/human/species/ghetto.dmi'
+ limb_id = BODYPART_ID_PEG
+ icon_state = "peg_l_leg"
+ bodytype = BODYTYPE_PEG
+ should_draw_greyscale = FALSE
+ unarmed_damage_low = 2
+ unarmed_damage_high = 5
+ unarmed_effectiveness = 10
+ brute_modifier = 1.2
+ burn_modifier = 1.5
+
+/obj/item/bodypart/leg/left/ghetto/Initialize(mapload, ...)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_EASY_ATTACH, INNATE_TRAIT)
+
+/obj/item/bodypart/leg/right/ghetto
+ name = "right peg leg"
+ desc = "Fashioned from what looks suspiciously like a table leg, this peg leg brings a whole new meaning to 'dining on the go.' It's a bit wobbly and creaks ominously with every step, but at least you can claim to have the most well-balanced diet on the seven seas."
+ icon = 'icons/mob/human/species/ghetto.dmi'
+ icon_static = 'icons/mob/human/species/ghetto.dmi'
+ limb_id = BODYPART_ID_PEG
+ icon_state = "peg_r_leg"
+ bodytype = BODYTYPE_PEG
+ should_draw_greyscale = FALSE
+ unarmed_damage_low = 2
+ unarmed_damage_high = 5
+ unarmed_effectiveness = 10
+ brute_modifier = 1.2
+ burn_modifier = 1.5
+
+/obj/item/bodypart/leg/right/ghetto/Initialize(mapload, ...)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_EASY_ATTACH, INNATE_TRAIT)
diff --git a/code/modules/unit_tests/changeling.dm b/code/modules/unit_tests/changeling.dm
index 9749d760ea91c..7f86510fd62d1 100644
--- a/code/modules/unit_tests/changeling.dm
+++ b/code/modules/unit_tests/changeling.dm
@@ -80,7 +80,7 @@
ling.dna.features["horns"] = "Curled"
ling.dna.features["frills"] = "Short"
ling.dna.features["spines"] = "Long + Membrane"
- ling.dna.features["body_markings"] = "Light Belly"
+ ling.dna.features["lizard_markings"] = "Light Belly"
ling.dna.features["legs"] = DIGITIGRADE_LEGS
ling.eye_color_left = COLOR_WHITE
ling.eye_color_right = COLOR_WHITE
diff --git a/html/changelogs/AutoChangeLog-pr-83439.yml b/html/changelogs/AutoChangeLog-pr-83439.yml
new file mode 100644
index 0000000000000..9e721ae4ba0a4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83439.yml
@@ -0,0 +1,8 @@
+author: "carlarctg"
+delete-after: True
+changes:
+ - balance: "Negative mutations now allow you to have more positive mutations, via reducing your instability!"
+ - code_imp: "All mutations have been overall standardized via defines on their instability values. Many mediocre positive mutations have had their cost reduced significantly!"
+ - rscadd: "Added a new height mutation: Acromegaly! It's the opposite of Dwarfism and makes you uncannily tall. It also makes you hit your head 8% or 4% (with synch) of the time you pass through airlocks. Wear a helmet!"
+ - rscadd: "Gigantism is now a recipe mutation, mix Acromegaly with Strength to get it."
+ - qol: "Injectors and activators' duration is now dependent on the in/stability (absolute value) of the mutations to be injected! With a minimum of 5-10-15 seconds for each type of injector. Also changed up a bit how part upgrade cooldowns work, by making each tier reduce cooldowns by 25-15-10% for each injector type."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83673.yml b/html/changelogs/AutoChangeLog-pr-83673.yml
new file mode 100644
index 0000000000000..0211d4508cf97
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83673.yml
@@ -0,0 +1,5 @@
+author: "Bisar"
+delete-after: True
+changes:
+ - rscadd: "Sparks now ignite flammable things. Including you. Keep a fire extinguisher handy or stop dousing yourself in welding fuel!"
+ - bugfix: "Fixed a few oversights with welding fuel pools not igniting when you throw lit/hot things into them or when you walk into them while on fire."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83744.yml b/html/changelogs/AutoChangeLog-pr-83744.yml
new file mode 100644
index 0000000000000..8425dfbfccd62
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83744.yml
@@ -0,0 +1,4 @@
+author: "Time-Green"
+delete-after: True
+changes:
+ - refactor: "Lizard and moth markings now use the bodypart overlay system"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83792.yml b/html/changelogs/AutoChangeLog-pr-83792.yml
new file mode 100644
index 0000000000000..c856d7586a8ce
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83792.yml
@@ -0,0 +1,4 @@
+author: "TheRyeGuyWhoWillNowDie"
+delete-after: True
+changes:
+ - rscadd: "the advanced omnitool upgrade now hastens the mediborg's syringe too"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83840.yml b/html/changelogs/AutoChangeLog-pr-83840.yml
deleted file mode 100644
index 5d879aece5df2..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83840.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "TheBoondock"
-delete-after: True
-changes:
- - sound: "added compressed air sound for when air tanks are inserted into machinery"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83895.yml b/html/changelogs/AutoChangeLog-pr-83895.yml
deleted file mode 100644
index 6b66da81feef3..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83895.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Hacks-The-Doors"
-delete-after: True
-changes:
- - balance: "dental pills can now be used in crit."
- - balance: "dental pills now give off a message when you start using them"
- - balance: "dental pills have a 2.5 second delay when in soft crit"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83949.yml b/html/changelogs/AutoChangeLog-pr-83949.yml
deleted file mode 100644
index 62584dcc7c0fc..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83949.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "grungussuss"
-delete-after: True
-changes:
- - rscadd: "Added Misha the bear to the HoS office on icebox."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83952.yml b/html/changelogs/AutoChangeLog-pr-83952.yml
new file mode 100644
index 0000000000000..431aaa759582e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83952.yml
@@ -0,0 +1,4 @@
+author: "GPeckman"
+delete-after: True
+changes:
+ - bugfix: "Airlocks should no longer appear closed sometimes when fireman carrying someone into them."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83953.yml b/html/changelogs/AutoChangeLog-pr-83953.yml
deleted file mode 100644
index fe5f444909fe3..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83953.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "JackEnoff"
-delete-after: True
-changes:
- - balance: "Certain changeling abilities won't work while on fire."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83999.yml b/html/changelogs/AutoChangeLog-pr-83999.yml
new file mode 100644
index 0000000000000..a04af9580bdf3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-83999.yml
@@ -0,0 +1,4 @@
+author: "Jacquerel"
+delete-after: True
+changes:
+ - balance: "Gorillas have big fingers, which mostly just prevents them from using laser pointers and stun batons"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84053.yml b/html/changelogs/AutoChangeLog-pr-84053.yml
new file mode 100644
index 0000000000000..6aebdbc471f5f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84053.yml
@@ -0,0 +1,4 @@
+author: "jlsnow301"
+delete-after: True
+changes:
+ - bugfix: "TGUI say will no longer spill your /me contents when you get attacked"
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-06.yml b/html/changelogs/archive/2024-06.yml
index c0da4690e635b..9f757c45df58b 100644
--- a/html/changelogs/archive/2024-06.yml
+++ b/html/changelogs/archive/2024-06.yml
@@ -742,3 +742,61 @@
carlarctg:
- bugfix: Cosmos spells will no longer star mark your steed
- qol: Baby plushies are now smaller than their parents
+2024-06-17:
+ Hacks-The-Doors:
+ - balance: dental pills can now be used in crit.
+ - balance: dental pills now give off a message when you start using them
+ - balance: dental pills have a 2.5 second delay when in soft crit
+ JackEnoff:
+ - balance: Certain changeling abilities won't work while on fire.
+ TheBoondock:
+ - sound: added compressed air sound for when air tanks are inserted into machinery
+ grungussuss:
+ - rscadd: Added Misha the bear to the HoS office on icebox.
+2024-06-18:
+ Bisar:
+ - balance: Replaced the free reagent purging with actually purging by exploding
+ someone in the blood while they have omnizine and a purgative in their bloodstream.
+ Let's go practice medicine!
+ - bugfix: Fixed the free reagent purging mechanic from causing an explosion so weak
+ that it doesn't cause any damage.
+ GPeckman:
+ - bugfix: Mining borgs can get multiple modkits of the same type installed again.
+ Goat:
+ - qol: fire extinguishers can now be filled via stationary tanks. (and water coolers)
+ GoblinBackwards:
+ - bugfix: Fixed mulebots being able to run over people who are leaning against a
+ wall.
+ - bugfix: Fixed anomaly cores from the high-intensity grav anomaly event creating
+ the wrong type of reactive armour.
+ Kaostico:
+ - bugfix: Transcendent Olfaction mutation now works properly
+ LucyGrind:
+ - bugfix: allows cigarette grinding in mortar
+ MTandi:
+ - bugfix: Non-metallic slime types are semi-transparent
+ - balance: Reshuffled tech tree, making nodes more specialized
+ - qol: Research points devided by the amount generated per second, so now research
+ points correspond to seconds
+ - rscadd: Introduced reagent purity scan experiments (required for Cryostasis node)
+ - rscadd: Introduced synthetic organ scan experiment (required for top tier cyber
+ organs)
+ - rscadd: Added a variant of machinery scan experiment that accepts any machines
+ with upgraded parts (required for tier 3 parts)
+ - rscdel: Removed material scanning experiments from the tech tree
+ Melbert:
+ - qol: Block'd out armor readout, should be more readable now
+ - bugfix: Crusher Fix For Real
+ SyncIt21:
+ - bugfix: ctrl+shift clicking on a ghost will only quick spawn that clicked target
+ and not you
+ TheBoondock:
+ - sound: added squeaky turn and gas hissing sound to gas valve
+ TheRyeGuyWhoWillNowDie:
+ - qol: adds a confirmation to malf AI shunting into APCs
+ mc-oofert:
+ - bugfix: build mode and space dragons dont harddel on destroy
+ - bugfix: you may now open the panel of a flatpacker with a screwdriver
+ san7890:
+ - qol: A message with a link to publicly accessible logs (if enabled by your server
+ operators) should now be visible far earlier when a world is about to reboot.
diff --git a/icons/mob/human/bodyparts.dmi b/icons/mob/human/bodyparts.dmi
index d6e4472973a32..78be880423c1e 100644
Binary files a/icons/mob/human/bodyparts.dmi and b/icons/mob/human/bodyparts.dmi differ
diff --git a/icons/mob/human/species/ghetto.dmi b/icons/mob/human/species/ghetto.dmi
new file mode 100644
index 0000000000000..e11701428ebcb
Binary files /dev/null and b/icons/mob/human/species/ghetto.dmi differ
diff --git a/icons/mob/human/species/lizard/lizard_markings.dmi b/icons/mob/human/species/lizard/lizard_markings.dmi
new file mode 100644
index 0000000000000..7cc8f2fa1b8a5
Binary files /dev/null and b/icons/mob/human/species/lizard/lizard_markings.dmi differ
diff --git a/icons/mob/human/species/lizard/lizard_misc.dmi b/icons/mob/human/species/lizard/lizard_misc.dmi
index ab228f29076f0..346581978e4b7 100644
Binary files a/icons/mob/human/species/lizard/lizard_misc.dmi and b/icons/mob/human/species/lizard/lizard_misc.dmi differ
diff --git a/sound/attributions.txt b/sound/attributions.txt
index bcd6f41edf278..29c86945dd1a1 100644
--- a/sound/attributions.txt
+++ b/sound/attributions.txt
@@ -188,3 +188,7 @@ https://freesound.org/people/BinaryMonkFlint/sounds/333296/
tank_remove_thunk.ogg was made by mixing two sound tracks from Freesound:
https://freesound.org/people/lowdjinn/sounds/533885/ and;
https://freesound.org/people/BMacZero/sounds/96137/
+
+valve_opening.ogg was made by mixing water flowing samples from:
+https://freesound.org/people/scriotxstudios/sounds/349111/?attribution=1 and squeaky scrape sound from:
+https://freesound.org/people/Department64/sounds/669028/ which was modified with lower pitch
diff --git a/sound/effects/gas_hissing.ogg b/sound/effects/gas_hissing.ogg
new file mode 100644
index 0000000000000..58df62ef0842f
Binary files /dev/null and b/sound/effects/gas_hissing.ogg differ
diff --git a/sound/effects/valve_opening.ogg b/sound/effects/valve_opening.ogg
new file mode 100644
index 0000000000000..9e71912041467
Binary files /dev/null and b/sound/effects/valve_opening.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index dd55b451101ef..5c2702885460a 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -989,6 +989,7 @@
#include "code\datums\atmosphere\planetary.dm"
#include "code\datums\bodypart_overlays\bodypart_overlay.dm"
#include "code\datums\bodypart_overlays\emote_bodypart_overlay.dm"
+#include "code\datums\bodypart_overlays\markings_bodypart_overlay.dm"
#include "code\datums\bodypart_overlays\mutant_bodypart_overlay.dm"
#include "code\datums\bodypart_overlays\simple_bodypart_overlay.dm"
#include "code\datums\brain_damage\brain_trauma.dm"
@@ -4048,6 +4049,7 @@
#include "code\modules\experisci\experiment\types\scanning_people.dm"
#include "code\modules\experisci\experiment\types\scanning_plants.dm"
#include "code\modules\experisci\experiment\types\scanning_points.dm"
+#include "code\modules\experisci\experiment\types\scanning_reagent.dm"
#include "code\modules\experisci\experiment\types\scanning_vatgrown.dm"
#include "code\modules\explorer_drone\adventure.dm"
#include "code\modules\explorer_drone\control_console.dm"
@@ -5678,8 +5680,24 @@
#include "code\modules\research\techweb\__techweb_helpers.dm"
#include "code\modules\research\techweb\_techweb.dm"
#include "code\modules\research\techweb\_techweb_node.dm"
-#include "code\modules\research\techweb\all_nodes.dm"
#include "code\modules\research\techweb\techweb_types.dm"
+#include "code\modules\research\techweb\nodes\alien_nodes.dm"
+#include "code\modules\research\techweb\nodes\atmos_nodes.dm"
+#include "code\modules\research\techweb\nodes\bepis_nodes.dm"
+#include "code\modules\research\techweb\nodes\biology_nodes.dm"
+#include "code\modules\research\techweb\nodes\circuit_nodes.dm"
+#include "code\modules\research\techweb\nodes\cyborg_nodes.dm"
+#include "code\modules\research\techweb\nodes\engi_nodes.dm"
+#include "code\modules\research\techweb\nodes\mech_nodes.dm"
+#include "code\modules\research\techweb\nodes\medbay_nodes.dm"
+#include "code\modules\research\techweb\nodes\mining_nodes.dm"
+#include "code\modules\research\techweb\nodes\modsuit_nodes.dm"
+#include "code\modules\research\techweb\nodes\research_nodes.dm"
+#include "code\modules\research\techweb\nodes\robo_nodes.dm"
+#include "code\modules\research\techweb\nodes\security_nodes.dm"
+#include "code\modules\research\techweb\nodes\service_nodes.dm"
+#include "code\modules\research\techweb\nodes\surgery_nodes.dm"
+#include "code\modules\research\techweb\nodes\syndicate_nodes.dm"
#include "code\modules\research\xenobiology\xenobio_camera.dm"
#include "code\modules\research\xenobiology\xenobiology.dm"
#include "code\modules\research\xenobiology\crossbreeding\__corecross.dm"
@@ -5879,6 +5897,7 @@
#include "code\modules\surgery\advanced\bioware\vein_threading.dm"
#include "code\modules\surgery\bodyparts\_bodyparts.dm"
#include "code\modules\surgery\bodyparts\dismemberment.dm"
+#include "code\modules\surgery\bodyparts\ghetto_parts.dm"
#include "code\modules\surgery\bodyparts\head.dm"
#include "code\modules\surgery\bodyparts\head_hair_and_lips.dm"
#include "code\modules\surgery\bodyparts\helpers.dm"
diff --git a/tgui/packages/tgui-say/TguiSay.tsx b/tgui/packages/tgui-say/TguiSay.tsx
index 7bc459c7f2e84..fbee44f00f9e2 100644
--- a/tgui/packages/tgui-say/TguiSay.tsx
+++ b/tgui/packages/tgui-say/TguiSay.tsx
@@ -162,7 +162,7 @@ export class TguiSay extends Component<{}, State> {
? prefix + currentValue
: currentValue;
- this.messages.forceSayMsg(grunt);
+ this.messages.forceSayMsg(grunt, this.channelIterator.current());
this.reset();
}
@@ -274,6 +274,7 @@ export class TguiSay extends Component<{}, State> {
};
reset() {
+ this.currentPrefix = null;
this.setValue('');
this.setSize();
this.setState({
diff --git a/tgui/packages/tgui-say/timers.ts b/tgui/packages/tgui-say/timers.ts
index 85c58f7424ae9..d1388487c07c6 100644
--- a/tgui/packages/tgui-say/timers.ts
+++ b/tgui/packages/tgui-say/timers.ts
@@ -1,5 +1,7 @@
import { debounce, throttle } from 'common/timer';
+import { Channel } from './ChannelIterator';
+
const SECONDS = 1000;
/** Timers: Prevents overloading the server, throttles messages */
@@ -10,7 +12,8 @@ export const byondMessages = {
0.4 * SECONDS,
),
forceSayMsg: debounce(
- (entry: string) => Byond.sendMessage('force', { entry, channel: 'Say' }),
+ (entry: string, channel: Channel) =>
+ Byond.sendMessage('force', { entry, channel }),
1 * SECONDS,
true,
),