diff --git a/baystation12.dme b/baystation12.dme
index 4511f4c8b6f..f9b001040fa 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1453,10 +1453,20 @@
#include "code\modules\atmospherics\components\unary\vent_scrubber.dm"
#include "code\modules\augment\active.dm"
#include "code\modules\augment\augment.dm"
-#include "code\modules\augment\simple.dm"
+#include "code\modules\augment\implanter.dm"
+#include "code\modules\augment\item.dm"
+#include "code\modules\augment\active\adaptive_binoculars.dm"
#include "code\modules\augment\active\armblades.dm"
#include "code\modules\augment\active\circuit.dm"
+#include "code\modules\augment\active\corrective_lenses.dm"
+#include "code\modules\augment\active\glare_dampeners.dm"
+#include "code\modules\augment\active\hudimplants.dm"
+#include "code\modules\augment\active\iatric_monitor.dm"
+#include "code\modules\augment\active\leukocyte_breeder.dm"
+#include "code\modules\augment\active\nerve_dampeners.dm"
#include "code\modules\augment\active\polytool.dm"
+#include "code\modules\augment\active\popout_guns.dm"
+#include "code\modules\augment\active\powerfists.dm"
#include "code\modules\augment\active\tool\engineering.dm"
#include "code\modules\augment\active\tool\surgical.dm"
#include "code\modules\augment\passive\armor.dm"
diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm
index 9edaec362ba..59e2d62378c 100644
--- a/code/__defines/damage_organs.dm
+++ b/code/__defines/damage_organs.dm
@@ -50,6 +50,7 @@
#define ORGAN_ROBOTIC (1<<11) // The organ is robotic. Changes numerous behaviors, search BP_IS_ROBOTIC for checks.
#define ORGAN_BRITTLE (1<<12) // The organ takes additional blunt damage. If robotic, cannot be repaired through normal means.
#define ORGAN_CRYSTAL (1<<13) // The organ does not suffer laser damage, but shatters on droplimb.
+#define ORGAN_CONFIGURE (1<<14) // The organ has an extra configuration step for surgery that it handles itself.
// Organ flag defines.
#define ORGAN_FLAG_CAN_AMPUTATE (1<<0) // The organ can be amputated.
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 4d26bdaf0ee..75ca3c16a69 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -229,9 +229,30 @@
#define BP_AUGMENT_CHEST_ACTIVE "active chest augment"
#define BP_AUGMENT_HEAD "head augment"
+/**
+* Augment Slots
+* Flags used by /obj/item/organ/internal/augment/var/augment_slots
+* Augment slots is used to control which body parts an augment may be installed into
+*/
+
+//Augment slots
+#define AUGMENT_ARM 0x1 ///The augment can be installed in arms
+#define AUGMENT_HAND 0x2 ///The augment can be installed in hands
+#define AUGMENT_LEG 0x4 ///The augment can be installed in legs
+#define AUGMENT_FOOT 0x8 ///The augment can be installed in legs
+#define AUGMENT_CHEST 0x10 ///The augment can be installed in the chest
+#define AUGMENT_GROIN 0x20 ///The augment can be installed in the lower body
+#define AUGMENT_HEAD 0x40 ///The augment can be installed in the head
+#define AUGMENT_ARMOR 0x80 ///The augment can be installed externally
+#define AUGMENT_FLUFF 0x100 ///The augment can be installed in a secondary head slot
+
+
//Augment flags
-#define AUGMENTATION_MECHANIC 1
-#define AUGMENTATION_ORGANIC 2
+#define AUGMENT_MECHANICAL 0x1 ///The augment can be installed in mechanical organs
+#define AUGMENT_BIOLOGICAL 0x2 ///The augment can be installed in biological organs
+#define AUGMENT_CRYSTALINE 0x4 ///The augment can be installed in crystaline organs
+#define AUGMENT_SCANNABLE 0x10 ///The augment is visible on body scanner results
+#define AUGMENT_INSPECTABLE 0x20 ///The augment is visible via grab inspection
// Limbs.
#define BP_L_FOOT "l_foot"
diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm
index b0a4ba461af..8fcad84a751 100644
--- a/code/_onclick/hud/ability_screen_objects.dm
+++ b/code/_onclick/hud/ability_screen_objects.dm
@@ -163,8 +163,8 @@
ability_master.toggle_open(1)
/mob/Initialize()
- ability_master = new /obj/screen/movable/ability_master(null,src)
. = ..()
+ ability_master = new /obj/screen/movable/ability_master(null,src)
///////////ACTUAL ABILITIES////////////
//This is what you click to do things//
diff --git a/code/modules/augment/active.dm b/code/modules/augment/active.dm
index c8be5bf6110..847999137f9 100644
--- a/code/modules/augment/active.dm
+++ b/code/modules/augment/active.dm
@@ -3,9 +3,9 @@
action_button_name = "Activate"
var/obj/item/organ/external/limb
+
/obj/item/organ/internal/augment/active/proc/activate()
-//Give verbs on install
/obj/item/organ/internal/augment/active/onInstall()
limb = owner.get_organ(parent_organ)
@@ -13,26 +13,23 @@
limb = null
/obj/item/organ/internal/augment/active/proc/can_activate()
- if(!owner || owner.incapacitated() || !is_usable())
+ if (!owner || owner.incapacitated() || !is_usable())
to_chat(owner, SPAN_WARNING("You can't do that now!"))
return FALSE
-
return TRUE
-
/obj/item/organ/internal/augment/active/attack_self()
. = ..()
- if(.)
+ if (.)
activate()
-//Need to change icon?
/obj/item/organ/internal/augment/active/refresh_action_button()
. = ..()
- if(.)
+ if (.)
action.button_icon_state = icon_state
- if(action.button) action.button.UpdateIcon()
-
+ if (action.button)
+ action.button.UpdateIcon()
/obj/item/organ/internal/augment/active/Destroy()
limb = null
- . = ..()
\ No newline at end of file
+ . = ..()
diff --git a/code/modules/augment/active/adaptive_binoculars.dm b/code/modules/augment/active/adaptive_binoculars.dm
new file mode 100644
index 00000000000..fc93f00bc70
--- /dev/null
+++ b/code/modules/augment/active/adaptive_binoculars.dm
@@ -0,0 +1,21 @@
+/obj/item/organ/internal/augment/active/item/adaptive_binoculars
+ name = "adaptive binoculars"
+ augment_slots = AUGMENT_HEAD
+ icon_state = "adaptive_binoculars"
+ desc = "Digital glass 'screens' can be deployed over the eyes. At the user's control, their image can be greatly enhanced, providing a view of distant areas."
+ action_button_name = "Deploy lenses"
+ origin_tech = list(TECH_DATA = 3, TECH_BIO = 2)
+ item = /obj/item/clothing/glasses/augment_binoculars
+
+
+/obj/item/organ/internal/augment/active/item/adaptive_binoculars/hidden
+ augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL
+
+
+/obj/item/organ/internal/augment/active/item/adaptive_binoculars/emp_act(severity)
+ . = ..()
+ if (item?.zoom)
+ to_chat(owner, SPAN_WARNING("Your eyes fill with static as \the [item.name] malfunction\s!"))
+ owner.eye_blind += 10
+ owner.eye_blurry += 20
+ item.unzoom()
diff --git a/code/modules/augment/active/armblades.dm b/code/modules/augment/active/armblades.dm
index 97361ea8099..92d70fb396a 100644
--- a/code/modules/augment/active/armblades.dm
+++ b/code/modules/augment/active/armblades.dm
@@ -1,41 +1,67 @@
-/obj/item/weapon/material/armblade
+/obj/item/weapon/material/armblade //Tested in game, force = 30 with steel which is what it's usually made of. Variations can exist.
icon_state = "armblade"
item_state = null
name = "armblade"
icon = 'icons/obj/augment.dmi'
- applies_material_colour = 0
desc = "A handy utility blade for the discerning augmentee. Warranty void if used for cutting."
- base_parry_chance = 30
- unbreakable = 1
- force_divisor = 0.2
- sharp = 1
- edge = 1
+ base_parry_chance = 30 //Difference between armblades is how well it parries and the blade size, which controls what it can dismember
+ unacidable = TRUE
+ sharp = TRUE
+ edge = TRUE
attack_verb = list("stabbed", "sliced", "cut")
- applies_material_colour = 0
-/obj/item/organ/internal/augment/active/simple/armblade
+
+/obj/item/organ/internal/augment/active/item/armblade
name = "embedded blade"
desc = "A sturdy housing for a steel utility blade."
action_button_name = "Deploy blade"
icon_state = "armblade"
- allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM)
- holding_type = /obj/item/weapon/material/armblade
- //Limited to robolimbs
- augment_flags = AUGMENTATION_MECHANIC
+ augment_slots = AUGMENT_ARM
+ item = /obj/item/weapon/material/armblade
+ augment_flags = AUGMENT_MECHANICAL | AUGMENT_SCANNABLE
+
/obj/item/weapon/material/armblade/claws
icon_state = "wolverine"
name = "combat claws"
desc = "These do not grow back."
- force_divisor = 0.3
+ base_parry_chance = 40
+
-//Alternate look
-/obj/item/organ/internal/augment/active/simple/wolverine
+/obj/item/organ/internal/augment/active/item/wolverine
name = "cyberclaws"
desc = "An unusual type of cybernetic weaponry, these sharp blades are bound to turn heads."
action_button_name = "Deploy claws"
icon_state = "wolverine"
- allowed_organs = list(BP_AUGMENT_R_HAND, BP_AUGMENT_L_HAND)
- holding_type = /obj/item/weapon/material/armblade/claws
- //Limited to robolimbs
- augment_flags = AUGMENTATION_MECHANIC
+ augment_slots = AUGMENT_HAND
+ item = /obj/item/weapon/material/armblade/claws
+ augment_flags = AUGMENT_MECHANICAL | AUGMENT_SCANNABLE
+
+
+/// Traitor version - no parry chance but good damage, and compatible with organic limbs
+/obj/item/organ/internal/augment/active/item/wrist_blade
+ name = "concealed wrist blade"
+ desc = "A concealed sheath made from bio-compatible cloth, shaped for a thin blade."
+ action_button_name = "Deploy blade"
+ icon_state = "armblade"
+ augment_slots = AUGMENT_ARM
+ item = /obj/item/weapon/material/armblade/wrist
+ origin_tech = list(TECH_COMBAT = 3, TECH_ESOTERIC = 4)
+ deploy_sound = 'sound/effects/holster/sheathout.ogg'
+ retract_sound = 'sound/effects/holster/sheathin.ogg'
+ augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL
+
+
+/obj/item/weapon/material/armblade/wrist
+ name = "wrist blade"
+ desc = "A thin and very sharp folding blade specially made for combat, made from a specialized alloy that prevents all that nasty blood and viscera from sticking to it. Its light weight allows for rapid slashing attacks."
+ icon_state = "wristblade"
+ item_state = "wristblade"
+ base_parry_chance = 0
+
+ /// SMALL prevents dismembering limbs - only hands & feet
+ w_class = ITEM_SIZE_SMALL
+
+
+/obj/item/weapon/material/armblade/wrist/add_blood(mob/living/carbon/human/M)
+ return FALSE
diff --git a/code/modules/augment/active/circuit.dm b/code/modules/augment/active/circuit.dm
index 70e6130dc53..f85160b94ed 100644
--- a/code/modules/augment/active/circuit.dm
+++ b/code/modules/augment/active/circuit.dm
@@ -1,38 +1,29 @@
-/obj/item/organ/internal/augment/active/simple/circuit
+/obj/item/organ/internal/augment/active/item/circuit
name = "integrated circuit frame"
action_button_name = "Activate Circuit"
icon_state = "circuit"
- allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM)
- holding_type = null //We must get the holding item externally
- //Limited to robolimbs
- augment_flags = AUGMENTATION_MECHANIC
- desc = "A DIY modular assembly, courtesy of Xion Industrial. Circuitry not included"
+ augment_slots = AUGMENT_ARM
+ augment_flags = AUGMENT_MECHANICAL | AUGMENT_SCANNABLE
+ desc = "A DIY modular assembly, courtesy of Xion Industrial. Circuitry not included."
-/obj/item/organ/internal/augment/active/simple/circuit/left
- allowed_organs = list(BP_AUGMENT_L_ARM)
-
-/obj/item/organ/internal/augment/active/simple/circuit/right
- allowed_organs = list(BP_AUGMENT_R_ARM)
-
-/obj/item/organ/internal/augment/active/simple/circuit/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if(isCrowbar(W))
- //Remove internal circuit
- if(holding)
- holding.canremove = 1
- holding.dropInto(loc)
- to_chat(user, SPAN_NOTICE("You take out \the [holding]."))
- holding = null
+/obj/item/organ/internal/augment/active/item/circuit/attackby(obj/item/I, mob/user)
+ if (isCrowbar(I))
+ if (item)
+ item.canremove = TRUE
+ item.dropInto(loc)
+ to_chat(user, SPAN_NOTICE("You take out \the [item]."))
+ item = null
playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
- else to_chat(user, SPAN_WARNING("The augment is empty!"))
+ else
+ to_chat(user, SPAN_WARNING("The augment is empty!"))
return
- if(istype(W, /obj/item/device/electronic_assembly/augment))
- if(holding)
+ if (istype(I, /obj/item/device/electronic_assembly/augment))
+ if (item)
to_chat(user, SPAN_WARNING("There's already an assembly in there."))
- else if(user.unEquip(W, src))
- holding = W
- holding.canremove = 0
+ else if (user.unEquip(I, src))
+ item = I
+ item.canremove = FALSE
playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
return
-
..()
\ No newline at end of file
diff --git a/code/modules/augment/active/corrective_lenses.dm b/code/modules/augment/active/corrective_lenses.dm
new file mode 100644
index 00000000000..674e284a54d
--- /dev/null
+++ b/code/modules/augment/active/corrective_lenses.dm
@@ -0,0 +1,21 @@
+/obj/item/organ/internal/augment/active/item/corrective_lenses
+ name = "corrective lenses"
+ augment_slots = AUGMENT_HEAD
+ icon_state = "corrective_lenses"
+ desc = "A pair of retractable, ultrathin corrective lenses are installed into the eye sockets. They can be deployed or retracted at will and serve as prescription glasses."
+ action_button_name = "Deploy lenses"
+ augment_flags = AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE | AUGMENT_INSPECTABLE
+ origin_tech = list(TECH_DATA = 2, TECH_BIO = 2)
+ item = /obj/item/clothing/glasses/augment
+
+
+/obj/item/organ/internal/augment/active/item/corrective_lenses/onRoundstart()
+ deploy()
+
+
+/obj/item/clothing/glasses/augment
+ name = "corrective lenses"
+ desc = "The most expensive prescription on this side of Sol."
+ body_parts_covered = null
+ prescription = 7
+ unacidable = TRUE
diff --git a/code/modules/augment/active/glare_dampeners.dm b/code/modules/augment/active/glare_dampeners.dm
new file mode 100644
index 00000000000..b05646e1350
--- /dev/null
+++ b/code/modules/augment/active/glare_dampeners.dm
@@ -0,0 +1,9 @@
+/obj/item/organ/internal/augment/active/item/glare_dampeners
+ name = "glare dampeners"
+ augment_slots = AUGMENT_HEAD
+ icon_state = "glare_dampeners"
+ desc = "Thick, tinted lenses installed in your head can deploy over your eyes, reducing visibility but providing protection from welding glare and bright lights."
+ action_button_name = "Deploy dampeners"
+ augment_flags = AUGMENT_BIOLOGICAL
+ origin_tech = list(TECH_DATA = 2, TECH_BIO = 2)
+ item = /obj/item/clothing/glasses/glare_dampeners
diff --git a/code/modules/augment/active/hudimplants.dm b/code/modules/augment/active/hudimplants.dm
index eb967fecb0b..aa203863fba 100644
--- a/code/modules/augment/active/hudimplants.dm
+++ b/code/modules/augment/active/hudimplants.dm
@@ -3,24 +3,29 @@
desc = "A small implantable heads-up display."
icon_state = "eye"
action_button_name = "Toggle HUD"
- allowed_organs = list(BP_AUGMENT_HEAD)
+ augment_slots = AUGMENT_HEAD
var/list/hud_type = list(HUD_MEDICAL, HUD_SECURITY)
var/active = FALSE
+
/obj/item/organ/internal/augment/active/hud/Process()
..()
if (!owner)
return
if (active)
- if (hud_type == HUD_MEDICAL)
- req_access = list(access_medical)
- if (allowed(owner))
- process_med_hud(owner, 1)
- else if (hud_type == HUD_SECURITY)
- req_access = list(access_security)
- if (allowed(owner))
- process_sec_hud(owner, 1)
+ switch(hud_type)
+ if (HUD_MEDICAL)
+ req_access = list(access_medical)
+ if (allowed(owner))
+ process_med_hud(owner, 1)
+ if (HUD_SECURITY)
+ req_access = list(access_security)
+ if (allowed(owner))
+ process_sec_hud(owner, 1)
+ if (HUD_JANITOR)
+ process_jani_hud(owner)
+
/obj/item/organ/internal/augment/active/hud/emp_act(severity)
if (istype(src.loc, /mob/living/carbon/human))
@@ -32,18 +37,36 @@
if (active)
active = FALSE
+
/obj/item/organ/internal/augment/active/hud/activate()
if (!can_activate())
return
active = !active
+ to_chat(owner, SPAN_NOTICE("You [active ? "enable" : "disable"] \the [src]."))
+
/obj/item/organ/internal/augment/active/hud/health
name = "integrated health HUD"
- desc = "The Vey-Med H-27 is an implantable HUD, designed to interface directly with the user's optic nerve and display information about patient vitals."
+ desc = "The Vey-Med H-27 is an implantable HUD, designed to interface with the user's optic nerve and display information about patient vitals."
icon_state = "eye_medical"
hud_type = HUD_MEDICAL
+
/obj/item/organ/internal/augment/active/hud/security
name = "integrated security HUD"
- desc = "The Hephaestus Industries C-VSR is an implantable HUD, designed to interface directly with the user's optic nerve and local databases to display security information."
- hud_type = HUD_SECURITY
\ No newline at end of file
+ desc = "The Hephaestus Industries C-VSR is an implantable HUD, designed to interface with the user's optic nerve and local databases to display security information."
+ hud_type = HUD_SECURITY
+
+
+/obj/item/organ/internal/augment/active/hud/janitor
+ name = "integrated filth HUD"
+ desc = "An implantable HUD based on the wearable janitorial version, designed to interface with the user's optic nerve and display information about nearby messes."
+ icon_state = "eye_janitor"
+ hud_type = HUD_JANITOR
+
+
+/obj/item/organ/internal/augment/active/hud/science
+ name = "integrated sciHUD"
+ desc = "An implantable HUD fitted with a portable analyzer capable of determining the research potential of a visible item or the components of a machine."
+ icon_state = "eye_science"
+ hud_type = HUD_SCIENCE
\ No newline at end of file
diff --git a/code/modules/augment/active/iatric_monitor.dm b/code/modules/augment/active/iatric_monitor.dm
new file mode 100644
index 00000000000..3f8cb2b0a96
--- /dev/null
+++ b/code/modules/augment/active/iatric_monitor.dm
@@ -0,0 +1,26 @@
+/obj/item/organ/internal/augment/active/iatric_monitor
+ name = "iatric monitor"
+ augment_slots = AUGMENT_HEAD
+ icon_state = "iatric_monitor"
+ desc = "A small computer system constantly tracks your physiological state and vital signs. A muscle gesture can be used to receive a simple diagnostic report, not unlike that from a handheld scanner."
+ augment_flags = AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE
+ origin_tech = list(TECH_DATA = 3, TECH_BIO = 2)
+
+
+/obj/item/organ/internal/augment/active/iatric_monitor/emp_act(severity)
+ . = ..()
+ if (severity)
+ var/scan_results = medical_scan_results(owner, TRUE, SKILL_NONE)
+ owner.playsound_simple(null, 'sound/effects/fastbeep.ogg', 20)
+ to_chat(owner, "
[scan_results]
")
+ to_chat(owner, SPAN_WARNING("Your [name] cheerily outputs a bogus report as it malfunctions."))
+
+
+/obj/item/organ/internal/augment/active/iatric_monitor/activate()
+ var/scan_results = medical_scan_results(owner, TRUE, SKILL_PROF)
+ owner.playsound_simple(null, 'sound/effects/fastbeep.ogg', 20)
+ to_chat(owner, "
[scan_results]
")
+
+
+/obj/item/organ/internal/augment/active/iatric_monitor/hidden
+ augment_flags = AUGMENT_BIOLOGICAL
diff --git a/code/modules/augment/active/leukocyte_breeder.dm b/code/modules/augment/active/leukocyte_breeder.dm
new file mode 100644
index 00000000000..aa29e738cb2
--- /dev/null
+++ b/code/modules/augment/active/leukocyte_breeder.dm
@@ -0,0 +1,69 @@
+/obj/item/organ/internal/augment/active/leukocyte_breeder
+ name = "leukocyte breeder"
+ augment_slots = AUGMENT_CHEST
+ icon_state = "leukosuite"
+ desc = "These stimulators augment the immune system and promote the growth of hunter-killer cells in the presence of a foreign invader, effectively boosting the body's immunity to parasites and disease."
+ action_button_name = "Toggle leukocyte breeder"
+ augment_flags = AUGMENT_BIOLOGICAL
+ origin_tech = list(TECH_DATA = 2, TECH_BIO = 4)
+ var/active = FALSE
+
+ /// How many processing ticks the augment has been enabled for
+ var/ticks_active = 0
+
+ /// After this many ticks, the owner has "broken in" the augment, and will benefit more but suffer drawbacks if it's disabled
+ var/ticks_to_acclimate = 120
+
+
+/obj/item/organ/internal/augment/active/leukocyte_breeder/emp_act(severity)
+ . = ..()
+ if (owner && active)
+ if (prob(100 - (20 * severity))) // 40% chance for 3, 60% chance for 2, and 80% chance for 1 severity, respectively
+ to_chat(owner, SPAN_WARNING("You feel a wave of nausea as your [name] deactivates."))
+ active = FALSE
+
+
+/obj/item/organ/internal/augment/active/leukocyte_breeder/onRoundstart()
+ active = TRUE // We can safely assume that someone starting off with the breeder will have it active
+ ticks_active = ticks_to_acclimate
+ to_chat(owner, SPAN_NOTICE("Your [name] has started the shift active, granting you its full benefits without needing to break it in."))
+
+
+/obj/item/organ/internal/augment/active/leukocyte_breeder/onInstall()
+ if (prob(10))
+ ticks_active = ticks_to_acclimate // Some folks are just lucky and don't get any side effects
+
+
+/obj/item/organ/internal/augment/active/leukocyte_breeder/activate()
+ if (!can_activate())
+ return
+ if (active && ticks_active >= ticks_to_acclimate)
+ // Give an alert if trying to deactivate while acclimated, so roundstart takers don't accidentally turn it off by learning the buttons
+ if (alert(owner, "Deactivate \the [src]?", name, "Yes", "No") != "Yes" || !can_activate())
+ return
+ active = !active
+ owner.playsound_simple(null, 'sound/effects/fastbeep.ogg', 20)
+ if (active)
+ to_chat(owner, SPAN_NOTICE("Leukocyte breeder engaged and improving immune response."))
+ else
+ to_chat(owner, SPAN_WARNING("Leukocyte breeder disengaged. Short-term health may suffer."))
+ if (owner.immunity >= (owner.immunity_norm * 0.9)) // Reduce short-term immunity, but only if it's at around normal levels
+ owner.immunity -= 10
+ ticks_active = 0
+
+
+/obj/item/organ/internal/augment/active/leukocyte_breeder/Process()
+ if (!owner)
+ return
+ if (active)
+ ticks_active++
+ if (owner.immunity < owner.immunity_norm * 1.1)
+ owner.immunity += 0.05
+ ticks_active++
+ if (ticks_active < ticks_to_acclimate)
+ if (ticks_active < ticks_to_acclimate)
+ if (prob(5))
+ owner.emote(pick("cough", "sneeze"))
+ if (prob(3))
+ to_chat(owner, SPAN_WARNING(pick("You feel uncomfortably hot.", "Your head aches.", "You feel lightheaded.")))
+ owner.dizziness += rand(3, 5)
diff --git a/code/modules/augment/active/nerve_dampeners.dm b/code/modules/augment/active/nerve_dampeners.dm
new file mode 100644
index 00000000000..9ee2fb1613a
--- /dev/null
+++ b/code/modules/augment/active/nerve_dampeners.dm
@@ -0,0 +1,45 @@
+/obj/item/organ/internal/augment/active/nerve_dampeners
+ name = "nerve dampeners"
+ augment_slots = AUGMENT_CHEST
+ icon_state = "muscule"
+ desc = "Each activation of this augment provides a strong painkilling effect for around thirty seconds, but will be followed by a powerful comedown. Excessive short-term use may cause brain damage."
+ augment_flags = AUGMENT_BIOLOGICAL
+ origin_tech = list(TECH_DATA = 4, TECH_BIO = 4)
+ var/ticks_remaining = 0
+
+
+/obj/item/organ/internal/augment/active/nerve_dampeners/can_activate()
+ if (ticks_remaining)
+ to_chat(owner, SPAN_WARNING("Your [name] is already active."))
+ return
+ . = ..()
+
+
+/obj/item/organ/internal/augment/active/nerve_dampeners/activate()
+ if (!can_activate())
+ return
+ to_chat(owner, SPAN_NOTICE("You activate your [name], and feel a wave of numbness wash over you!"))
+ ticks_remaining = 15
+ if (owner.drowsyness)
+ to_chat(owner, SPAN_DANGER("Your body slackens as you lose sensation."))
+ if (prob(owner.getBrainLoss()))
+ to_chat(owner, SPAN_DANGER("You slump to the ground and black out."))
+ owner.Paralyse(10)
+ owner.adjustBrainLoss(owner.drowsyness)
+
+
+/obj/item/organ/internal/augment/active/nerve_dampeners/Process()
+ if (!owner)
+ return
+ if (ticks_remaining)
+ ticks_remaining--
+ owner.add_chemical_effect(CE_PAINKILLER, 160) // About twice as strong as tramadol at full strength
+ if (!ticks_remaining) // ...but comes at a price. Brief short term benefit for a long-term comedown
+ to_chat(owner, SPAN_WARNING("You abruptly feel intensely exhausted as sensation returns."))
+ owner.drowsyness = max(owner.drowsyness, 15)
+ owner.confused = max(owner.confused, 15)
+ owner.slurring = max(owner.slurring, 30)
+ owner.chem_effects[CE_PAINKILLER] = 0
+ owner.stamina = 0
+ if(MOVING_QUICKLY(owner))
+ owner.set_moving_slowly()
diff --git a/code/modules/augment/active/polytool.dm b/code/modules/augment/active/polytool.dm
index 79409b5a69f..ddd8d81876e 100644
--- a/code/modules/augment/active/polytool.dm
+++ b/code/modules/augment/active/polytool.dm
@@ -2,57 +2,61 @@
name = "Polytool embedded module"
action_button_name = "Deploy Tool"
icon_state = "multitool"
- allowed_organs = list(BP_AUGMENT_R_HAND, BP_AUGMENT_L_HAND)
+ augment_slots = AUGMENT_HAND
var/list/items = list()
var/list/paths = list() //We may lose them
- augment_flags = AUGMENTATION_MECHANIC
+ augment_flags = AUGMENT_MECHANICAL
+
/obj/item/organ/internal/augment/active/polytool/Initialize()
. = ..()
- for(var/path in paths)
+ for (var/path in paths)
var/obj/item/I = new path (src)
I.canremove = FALSE
items += I
-/obj/item/organ/internal/augment/active/polytool/proc/holding_dropped(var/obj/item/I)
- //Stop caring
- GLOB.item_unequipped_event.unregister(I, src)
-
- if(I.loc != src) //something went wrong and is no longer attached/ it broke
- I.canremove = TRUE
/obj/item/organ/internal/augment/active/polytool/Destroy()
QDEL_NULL_LIST(items)
. = ..()
+
+/obj/item/organ/internal/augment/active/polytool/proc/holding_dropped(obj/item/I)
+ GLOB.item_unequipped_event.unregister(I, src)
+ if (I.loc != src)
+ I.canremove = TRUE
+
+
/obj/item/organ/internal/augment/active/polytool/activate()
- if(!can_activate())
+ if (!can_activate())
return
var/slot = null
-
- if(limb.organ_tag in list(BP_L_ARM, BP_L_HAND))
+ if (limb.organ_tag in list(BP_L_ARM, BP_L_HAND))
slot = slot_l_hand
- else if(limb.organ_tag in list(BP_R_ARM, BP_R_HAND))
+ else if (limb.organ_tag in list(BP_R_ARM, BP_R_HAND))
slot = slot_r_hand
-
var/obj/I = slot == slot_l_hand ? owner.l_hand : owner.r_hand
-
- var/list/options = list()
- for(var/obj/item/IT in items - I)
- options[IT] = IT.appearance
- var/obj/item = RADIAL_INPUT(owner, options)
- if(I)
- if(!(I in items))
- to_chat(owner, SPAN_WARNING("You can't extend your [item], [I] is in the way!"))
- return
- if(!owner.drop_from_inventory(I, src))
- to_chat(owner, SPAN_WARNING("You are unable to retract [I] into your [limb.name]!"))
+ if (I)
+ if (is_type_in_list(I,paths) && !(I.type in items)) //We don't want several of same but you can replace parts whenever
+ if (!owner.drop_from_inventory(I, src))
+ to_chat(owner, "\the [I] fails to retract.")
+ return
+ items += I
+ owner.visible_message(
+ SPAN_WARNING("[owner] retracts \his [I] into [limb]."),
+ SPAN_NOTICE("You retract your [I] into [limb].")
+ )
+ else
+ to_chat(owner, SPAN_WARNING("You must drop [I] before tool can be extend."))
+ else
+ var/obj/item = input(owner, "Select item for deploy") as null|anything in src
+ if (!item || !(src in owner.internal_organs))
return
- owner.visible_message(SPAN_WARNING("[owner] retracts \his [I] into \his [limb.name]."), SPAN_NOTICE("You retract your [I] into your [limb.name]."))
-
- if(owner.equip_to_slot_if_possible(item, slot))
- GLOB.item_unequipped_event.register(item, src, /obj/item/organ/internal/augment/active/polytool/proc/holding_dropped)
- owner.visible_message(
- SPAN_WARNING("[owner] extends \his [item.name] from \his [limb.name]."),
- SPAN_NOTICE("You extend your [item.name] from your [limb.name].")
- )
\ No newline at end of file
+ if (owner.equip_to_slot_if_possible(item, slot))
+ items -= item
+ //Keep track of it, make sure it returns
+ GLOB.item_unequipped_event.register(item, src, /obj/item/organ/internal/augment/active/polytool/proc/holding_dropped)
+ owner.visible_message(
+ SPAN_WARNING("[owner] extends \his [item.name] from [limb]."),
+ SPAN_NOTICE("You extend your [item.name] from [limb].")
+ )
diff --git a/code/modules/augment/active/popout_guns.dm b/code/modules/augment/active/popout_guns.dm
new file mode 100644
index 00000000000..758a1d093e6
--- /dev/null
+++ b/code/modules/augment/active/popout_guns.dm
@@ -0,0 +1,30 @@
+/obj/item/organ/internal/augment/active/item/popout_shotgun
+ name = "pop-out shotgun"
+ desc = "A galvanized steel mechanism that replaces most of the flesh below the elbow. Using the arm's natural range of motion as a hinge, it can be flicked open to reveal a 12-gauge shotgun with room for a single shell."
+ action_button_name = "Deploy shotgun"
+ icon_state = "popout_shotgun"
+ augment_slots = AUGMENT_ARM
+ item = /obj/item/weapon/gun/projectile/shotgun/popout
+ origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 3, TECH_ESOTERIC = 4)
+ deploy_sound = 'sound/weapons/guns/interaction/rifle_boltback.ogg'
+ retract_sound = 'sound/weapons/guns/interaction/rifle_boltforward.ogg'
+ augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE | AUGMENT_INSPECTABLE
+
+
+/obj/item/weapon/gun/projectile/shotgun/popout
+ name = "pop-out shotgun"
+ desc = "A specialized 12-gauge shotgun concealed in the forearm. If you plan to shoot, you better not miss."
+ icon = 'icons/obj/augment.dmi'
+ icon_state = "popout_shotgun"
+ item_state = "coilgun"
+ max_shells = 1
+ w_class = ITEM_SIZE_HUGE
+ force = 5
+ obj_flags = OBJ_FLAG_CONDUCTIBLE
+ caliber = CALIBER_SHOTGUN
+ load_method = SINGLE_CASING|SINGLE_LOAD
+ ammo_type = /obj/item/ammo_casing/shotgun/pellet
+ handle_casings = EJECT_CASINGS
+ load_sound = 'sound/weapons/guns/interaction/shotgun_instert.ogg'
+ has_safety = FALSE // No brakes on this train baby
+ unacidable = TRUE
diff --git a/code/modules/augment/active/powerfists.dm b/code/modules/augment/active/powerfists.dm
new file mode 100644
index 00000000000..493f05c55ef
--- /dev/null
+++ b/code/modules/augment/active/powerfists.dm
@@ -0,0 +1,208 @@
+/obj/item/weapon/powerfist
+ icon_state = "powerfist"
+ item_state = "powerfist"
+ name = "pneumatic powerfist"
+ icon = 'icons/obj/augment.dmi'
+ desc = "A strong, pneumatic powerfist. Packs quite the punch with other utility uses."
+ base_parry_chance = 12
+ force = 5
+ attack_cooldown = 1.5 * DEFAULT_WEAPON_COOLDOWN
+ hitsound = 'sound/effects/bamf.ogg'
+ attack_verb = list("smashed", "bludgeoned", "hammered", "battered")
+
+ var/obj/item/weapon/tank/tank
+ var/pressure_setting
+ var/list/possible_pressure_amounts = list(10, 20, 30, 50)
+
+
+/obj/item/weapon/powerfist/Initialize()
+ . = ..()
+ if (ispath(tank))
+ tank = new tank (src)
+ if (!pressure_setting)
+ pressure_setting = possible_pressure_amounts[1]
+ update_force()
+
+
+/obj/item/organ/internal/augment/active/item/powerfist
+ name = "pneumatic power gauntlet"
+ desc = "An armoured powered gauntlet for the arm. Your very own pneumatic doom machine."
+ action_button_name = "Deploy powerfist"
+ icon_state = "powerfist"
+ deploy_sound = 'sound/machines/suitstorage_cycledoor.ogg'
+ retract_sound = 'sound/machines/suitstorage_cycledoor.ogg'
+ augment_slots = AUGMENT_ARM
+ item = /obj/item/weapon/powerfist
+ augment_flags = AUGMENT_MECHANICAL | AUGMENT_SCANNABLE
+
+
+/obj/item/weapon/powerfist/attackby(obj/item/item, mob/user)
+ if (!istype(item, /obj/item/weapon/tank/emergency) || istype(item, /obj/item/weapon/tank/emergency/oxygen/double)) //Make sure the tank isn't too big or it'll gib people
+ to_chat(user, SPAN_WARNING("\The [src] only slots small tanks!"))
+ return
+ if (tank)
+ to_chat(user, SPAN_WARNING("\The [src] already has \a [tank] installed."))
+ return
+ user.visible_message(
+ SPAN_ITALIC("\The [user] starts connecting \a [item] to \his [src]."),
+ SPAN_ITALIC("You start connecting \the [item] to \the [src]."),
+ range = 5
+ )
+ if (!do_after(user, 3 SECONDS, item, progress = 0))
+ return
+ if (!user.unEquip(item, src))
+ return
+ user.visible_message(
+ SPAN_ITALIC("\The [user] finishes connecting \a [item] to \his [src]."),
+ SPAN_NOTICE("You finish connecting \the [item] to \the [src]."),
+ range = 5
+ )
+ playsound(user, 'sound/effects/refill.ogg', 50, 1, -6)
+ tank = item
+ update_force()
+ update_icon()
+
+
+/obj/item/weapon/powerfist/proc/update_force()
+ var/pressure = tank?.air_contents?.return_pressure()
+ if (pressure > 210)
+ force = (pressure * pressure_setting * 0.01) * (tank.volume / 425)
+ else
+ force = 5
+
+
+/obj/item/weapon/powerfist/verb/set_pressure_verb()
+ set name = "Set Powerfist Pressure"
+ set desc = "Set the powerfist's tank output pressure."
+ set category = "Object"
+ set src in range(0)
+ set_pressure()
+
+
+/obj/item/weapon/powerfist/proc/set_pressure()
+ var/N = input("Percentage of tank used per hit:", "[src]") as null | anything in possible_pressure_amounts
+ if (isnull(N))
+ return
+ pressure_setting = N
+ to_chat(usr, SPAN_NOTICE("You dial \the [src]'s pressure valve to [pressure_setting]%."))
+ update_force()
+
+
+/obj/item/weapon/powerfist/attack_self(mob/living/carbon/human/user)
+ set_pressure()
+
+
+/obj/item/weapon/powerfist/attack_hand(mob/living/user)
+ if (!tank)
+ to_chat(user, SPAN_WARNING("There's no tank in \the [src]."))
+ return
+ user.visible_message(
+ SPAN_ITALIC("\The [user] starts disconnecting \a [tank] from \his [src]."),
+ SPAN_ITALIC("You start disconnecting \the [tank] from \the [src]."),
+ range = 5
+ )
+ if (!do_after(user, 3 SECONDS, src, progress = 0))
+ return
+ user.visible_message(
+ SPAN_ITALIC("\The [user] finishes disconnecting \a [tank] from \his [src]."),
+ SPAN_NOTICE("You finish disconnecting \the [tank] from \the [src]."),
+ range = 5
+ )
+ user.put_in_hands(tank)
+ playsound(loc, 'sound/effects/spray3.ogg', 50)
+ tank = null
+ update_icon()
+ update_force()
+
+
+/obj/item/weapon/powerfist/on_update_icon()
+ ..()
+ if (tank)
+ overlays += image(icon, "powerfist_tank")
+ else
+ overlays -= image(icon,"powerfist_tank")
+
+
+/obj/item/weapon/powerfist/examine(mob/living/user, distance)
+ . = ..()
+ if (distance > 2)
+ return
+ to_chat(user, "The valve is dialed to [pressure_setting]%.")
+ if (tank)
+ to_chat(user, "\A [tank] is fitted in \the [src]'s tank valve.")
+ to_chat(user, "The tank dial reads [tank.air_contents.return_pressure()] kPa.")
+ else
+ to_chat(user, "Nothing is attached to the tank valve!")
+
+
+/obj/item/weapon/powerfist/proc/gas_loss()
+ if (tank?.air_contents)
+ var/lost_gas = tank.air_contents.total_moles * pressure_setting * 0.01
+ tank.remove_air(lost_gas)
+
+
+/obj/item/weapon/powerfist/proc/no_pressure()
+ if (tank && tank.air_contents?.return_pressure() < 210)
+ playsound(usr, 'sound/machines/rigerror.ogg', 50)
+ to_chat(usr, SPAN_WARNING("\The pressure dial on \the [src] flashes a warning: it's out of gas!"))
+ update_force()
+
+
+/obj/item/weapon/powerfist/afterattack(atom/target, mob/living/user, inrange, params)
+ if (!inrange || user.a_intent == I_HELP)
+ return
+ if (tank && tank.air_contents.return_pressure() > 210 && pressure_setting > 20 && inrange)
+ playsound(user, 'sound/effects/bamf.ogg', pressure_setting*2, 1) //louder the more pressure is used
+ gas_loss()
+ no_pressure()
+ if (istype(target, /obj/machinery/door/airlock) && pressure_setting > 30) //tearing open airlocks
+ var/obj/machinery/door/airlock/A = target
+ if (!A.operating && !A.locked)
+ if (A.welded)
+ A.visible_message(SPAN_DANGER("\The [user] forces the fingers of \the [src] in through the welded metal, beginning to pry \the [A] open!"))
+ if (do_after(user, 13 SECONDS, A, progress = 0) && !A.locked)
+ A.welded = FALSE
+ A.update_icon()
+ playsound(A, 'sound/effects/meteorimpact.ogg', 100, 1)
+ playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1)
+ A.visible_message(SPAN_DANGER("\The [user] tears \the [A] open with \a [src]!"))
+ addtimer(CALLBACK(A, /obj/machinery/door/airlock/.proc/open, TRUE), 0)
+ A.set_broken(TRUE)
+ return
+ else
+ A.visible_message(SPAN_DANGER("\The [user] pries the fingers of \a [src] in, beginning to force \the [A]!"))
+ if ((A.is_broken(NOPOWER) || do_after(user, 10 SECONDS, A, progress = 0)) && !(A.operating || A.welded || A.locked))
+ playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1)
+ if (A.density)
+ addtimer(CALLBACK(A, /obj/machinery/door/airlock/.proc/open, TRUE), 0)
+ if(!A.is_broken(NOPOWER))
+ A.set_broken(TRUE)
+ A.visible_message(SPAN_DANGER("\The [user] forces \the [A] open with \a [src]!"))
+ else
+ addtimer(CALLBACK(A, /obj/machinery/door/airlock/.proc/close, TRUE), 0)
+ if (!A.is_broken(NOPOWER))
+ A.set_broken(TRUE)
+ A.visible_message(SPAN_DANGER("\The [user] forces \the [A] closed with \a [src]!"))
+ if (A.locked)
+ to_chat(user, SPAN_WARNING("The airlock's bolts prevent it from being forced."))
+
+
+/obj/item/weapon/powerfist/apply_hit_effect(atom/target, mob/living/user)
+ if (tank)
+ gas_loss()
+ no_pressure()
+ if (istype(target, /mob/living))
+ if (pressure_setting == 50 && tank.air_contents.return_pressure() > 210)
+ var/mob/living/A = target
+ A.throw_at(get_edge_target_turf(user, user.dir), pressure_setting/10, pressure_setting/10) //penultimate/ultimate settings yeets people
+ user.visible_message(
+ SPAN_DANGER("\The [user] batters \the [A] with \a [src], sending them flying!"),
+ SPAN_WARNING("You batter \the [A] with \the [src], sending them flying!")
+ )
+ return ..()
+
+/obj/item/weapon/powerfist/prepared
+ tank = /obj/item/weapon/tank/emergency/oxygen/engi
+
+/obj/item/organ/internal/augment/active/item/powerfist/prepared
+ item = /obj/item/weapon/powerfist/prepared
diff --git a/code/modules/augment/active/tool/engineering.dm b/code/modules/augment/active/tool/engineering.dm
index 209c68d5cdc..818b9cbae71 100644
--- a/code/modules/augment/active/tool/engineering.dm
+++ b/code/modules/augment/active/tool/engineering.dm
@@ -1,5 +1,3 @@
-
-
/obj/item/organ/internal/augment/active/polytool/engineer
name = "\improper Engineering toolset"
action_button_name = "Deploy Engineering Tool"
@@ -12,12 +10,6 @@
/obj/item/weapon/wirecutters/finger,
/obj/item/device/multitool/finger
)
-//This is a hack, but i'm too fucking lazy to deal with it.
-/obj/item/organ/internal/augment/active/polytool/engineer/left
- allowed_organs = list(BP_AUGMENT_L_ARM)
-
-/obj/item/organ/internal/augment/active/polytool/engineer/right
- allowed_organs = list(BP_AUGMENT_R_ARM)
/obj/item/weapon/weldingtool/finger
name = "digital welder"
diff --git a/code/modules/augment/active/tool/surgical.dm b/code/modules/augment/active/tool/surgical.dm
index e3628a1d221..dc6c81c2749 100644
--- a/code/modules/augment/active/tool/surgical.dm
+++ b/code/modules/augment/active/tool/surgical.dm
@@ -11,10 +11,4 @@
/obj/item/weapon/retractor,
/obj/item/weapon/scalpel,
/obj/item/weapon/surgicaldrill
- )
-
-/obj/item/organ/internal/augment/active/polytool/surgical/left
- allowed_organs = list(BP_AUGMENT_L_ARM)
-
-/obj/item/organ/internal/augment/active/polytool/surgical/right
- allowed_organs = list(BP_AUGMENT_R_ARM)
\ No newline at end of file
+ )
\ No newline at end of file
diff --git a/code/modules/augment/augment.dm b/code/modules/augment/augment.dm
index 54cc3811a6d..ac720c1abcd 100644
--- a/code/modules/augment/augment.dm
+++ b/code/modules/augment/augment.dm
@@ -1,83 +1,223 @@
+/**
+* Augments
+* Extra organs that can be embedded to provide behaviors and flavor
+*/
/obj/item/organ/internal/augment
name = "embedded augment"
- desc = "Embedded augment."
icon = 'icons/obj/augment.dmi'
- //By default these fit on both flesh and robotic organs and are robotic
- status = ORGAN_ROBOTIC
- var/augment_flags = AUGMENTATION_MECHANIC | AUGMENTATION_ORGANIC
- var/list/allowed_organs = list(BP_AUGMENT_R_ARM, BP_AUGMENT_L_ARM)
+ status = ORGAN_ROBOTIC | ORGAN_CONFIGURE
default_action_type = /datum/action/item_action/organ/augment
- var/descriptor = ""
- var/known = TRUE
+ var/augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE
+ var/augment_slots = 0
-/obj/item/organ/internal/augment/Initialize()
- . = ..()
- organ_tag = pick(allowed_organs)
- update_parent_organ()
-//General expectation is onInstall and onRemoved are overwritten to add effects to augmentee
-/obj/item/organ/internal/augment/replaced(var/mob/living/carbon/human/target)
- if(..() && istype(owner))
+#define ORGAN_STYLE ( \
+ (organ.status & ORGAN_ROBOTIC) ? 1 \
+: (organ.status & ORGAN_CRYSTAL) ? 2 \
+: 0 \
+)
+
+#define ORGAN_STYLE_OK ( \
+ style == 0 && (augment_flags & AUGMENT_BIOLOGICAL) \
+|| style == 1 && (augment_flags & AUGMENT_MECHANICAL) \
+|| style == 2 && (augment_flags & AUGMENT_CRYSTALINE) \
+)
+
+/obj/item/organ/internal/augment/proc/get_valid_parent_organ(mob/living/carbon/subject)
+ if (!istype(subject))
+ return
+ var/style
+ var/obj/item/organ/external/organ
+ var/list/organs = subject.organs_by_name
+ if ((augment_slots & AUGMENT_CHEST) && !organs["[BP_CHEST]_aug"] && (organ = organs[BP_CHEST]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if ((augment_slots & AUGMENT_ARMOR) && !organs["[BP_CHEST]_aug_armor"] && (organ = organs[BP_CHEST]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if ((augment_slots & AUGMENT_GROIN) && !organs["[BP_GROIN]_aug"] && (organ = organs[BP_GROIN]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if ((augment_slots & AUGMENT_HEAD) && !organs["[BP_HEAD]_aug"] && (organ = organs[BP_HEAD]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if ((augment_slots & AUGMENT_FLUFF) && !organs["[BP_HEAD]_aug_fluff"] && (organ = organs[BP_HEAD]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (augment_slots & AUGMENT_ARM)
+ if (!organs["[BP_L_ARM]_aug"] && (organ = organs[BP_L_ARM]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (!organs["[BP_R_ARM]_aug"] && (organ = organs[BP_R_ARM]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (augment_slots & AUGMENT_HAND)
+ if (!organs["[BP_L_HAND]_aug"] && (organ = organs[BP_L_HAND]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (!organs["[BP_R_HAND]_aug"] && (organ = organs[BP_R_HAND]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (augment_slots & AUGMENT_LEG)
+ if (!organs["[BP_L_LEG]_aug"] && (organ = organs[BP_L_LEG]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (!organs["[BP_R_LEG]_aug"] && (organ = organs[BP_R_LEG]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (augment_slots & AUGMENT_FOOT)
+ if (!organs["[BP_L_FOOT]_aug"] && (organ = organs[BP_L_FOOT]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+ if (!organs["[BP_R_FOOT]_aug"] && (organ = organs[BP_R_FOOT]))
+ style = ORGAN_STYLE
+ if (ORGAN_STYLE_OK)
+ return organ
+
+#undef ORGAN_STYLE_OK
+#undef ORGAN_STYLE
+
+
+/obj/item/organ/internal/augment/surgery_configure(mob/living/user, mob/living/carbon/human/target, obj/item/organ/parent, obj/item/tool, decl/surgery_step/action)
+ var/found
+ switch (parent?.organ_tag)
+ if (null)
+ found = FALSE
+ if (BP_L_ARM, BP_R_ARM)
+ found = augment_slots & AUGMENT_ARM
+ if (BP_L_HAND, BP_R_HAND)
+ found = augment_slots & AUGMENT_HAND
+ if (BP_L_LEG, BP_R_LEG)
+ found = augment_slots & AUGMENT_LEG
+ if (BP_L_FOOT, BP_R_FOOT)
+ found = augment_slots & AUGMENT_FOOT
+ if (BP_CHEST)
+ found = augment_slots & (AUGMENT_CHEST | AUGMENT_ARMOR)
+ if (BP_GROIN)
+ found = augment_slots & AUGMENT_GROIN
+ if (BP_HEAD)
+ found = augment_slots & (AUGMENT_HEAD | AUGMENT_FLUFF)
+ if (!found)
+ to_chat(user, SPAN_WARNING("\The [src] can't be installed in \the [parent]."))
+ parent_organ = null
+ organ_tag = null
+ return 1
+ parent_organ = parent.organ_tag
+ if (found == AUGMENT_ARMOR)
+ organ_tag = "[parent_organ]_aug_armor"
+ if (found == AUGMENT_FLUFF)
+ organ_tag = "[parent_organ]_aug_fluff"
+ else
+ organ_tag = "[parent_organ]_aug"
+
+
+/obj/item/organ/internal/augment/replaced(mob/living/carbon/human/target)
+ if (..() && istype(owner))
onInstall()
-/obj/item/organ/internal/augment/proc/onInstall()
- return
-/obj/item/organ/internal/augment/removed(var/mob/living/user, var/drop_organ=1)
+/obj/item/organ/internal/augment/removed(mob/living/user, drop_organ = TRUE)
onRemove()
..()
+
+/// Virtual for removing augment effects from owner
/obj/item/organ/internal/augment/proc/onRemove()
return
-/obj/item/organ/internal/augment/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if(isScrewdriver(W) && allowed_organs.len > 1)
- //Here we can adjust location for implants that allow multiple slots
- organ_tag = show_radial_menu(user, src, allowed_organs)
- update_parent_organ()
- playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
- return
- ..()
+/// Virtual for adding augment effects to owner when surgically added
+/obj/item/organ/internal/augment/proc/onInstall()
+ return
-/obj/item/organ/internal/augment/proc/update_parent_organ()
- //This tries to match a parent organ to an augment slot
- //This is intended to match the possible positions to a parent organ
-
- if(organ_tag == BP_AUGMENT_L_LEG)
- parent_organ = BP_L_LEG
- descriptor = "left leg."
- else if(organ_tag == BP_AUGMENT_R_LEG)
- parent_organ = BP_R_LEG
- descriptor = "right leg."
- else if(organ_tag == BP_AUGMENT_L_HAND)
- parent_organ = BP_L_HAND
- descriptor = "left hand."
- else if(organ_tag == BP_AUGMENT_R_HAND)
- parent_organ = BP_R_HAND
- descriptor = "right hand."
- else if(organ_tag == BP_AUGMENT_L_ARM)
- parent_organ = BP_L_ARM
- descriptor = "left arm."
- else if(organ_tag == BP_AUGMENT_R_ARM)
- parent_organ = BP_R_ARM
- descriptor = "right arm."
- else if(organ_tag == BP_AUGMENT_HEAD)
- parent_organ = BP_HEAD
- descriptor = "head."
- else if(organ_tag == BP_AUGMENT_CHEST_ACTIVE || organ_tag == BP_AUGMENT_CHEST_ARMOUR)
- parent_organ = BP_CHEST
- descriptor = "chest."
+
+/// Virtual for adding additional behavior when augment is already present at owner instantiation
+/obj/item/organ/internal/augment/proc/onRoundstart()
+ return
/obj/item/organ/internal/augment/examine(mob/user, distance)
. = ..()
- if(distance <= 1)
- to_chat(user, "It is configured to be attached to the [descriptor].")
- if(augment_flags & AUGMENTATION_MECHANIC && augment_flags & AUGMENTATION_ORGANIC)
- to_chat(user, "It can interface with both prosthetic and fleshy organs.")
+ var/level
+ if (isobserver(user))
+ level = 2
+ else if (distance > 1)
+ return
+ else if (user.mind?.special_role)
+ level = 2
+ else if (user.skill_check(SKILL_DEVICES, SKILL_PROF))
+ level = 2
+ else if (user.skill_check(SKILL_DEVICES, SKILL_ADEPT))
+ level = 1
+ if (!level)
+ return
+ var/list/attach_types = list()
+ if (augment_flags & AUGMENT_MECHANICAL)
+ attach_types += "mechanical"
+ if (augment_flags & AUGMENT_BIOLOGICAL)
+ attach_types += "biological"
+ if (augment_flags & AUGMENT_CRYSTALINE)
+ attach_types += "crystaline"
+ var/list/attach_parts = list()
+ if (augment_slots & (AUGMENT_CHEST|AUGMENT_ARMOR))
+ attach_parts += "chests"
+ if (augment_slots & AUGMENT_GROIN)
+ attach_parts += "lower bodies"
+ if (augment_slots & (AUGMENT_HEAD|AUGMENT_FLUFF))
+ attach_parts += "heads"
+ if (augment_slots & AUGMENT_ARM)
+ attach_parts += "arms"
+ if (augment_slots & AUGMENT_HAND)
+ attach_parts += "hands"
+ if (augment_slots & AUGMENT_LEG)
+ attach_parts += "legs"
+ if (augment_slots & AUGMENT_FOOT)
+ attach_parts += "feet"
+ var/message = "It can be installed in [english_list(attach_parts)] that are [english_list(attach_types)]."
+ if (level > 1)
+ var/list/discovery = list()
+ if (augment_flags & AUGMENT_SCANNABLE)
+ discovery += "scanners"
+ if (augment_flags & AUGMENT_INSPECTABLE)
+ discovery += "manual inspection"
+ if (discovery.len)
+ message += " It can be discovered by [english_list(discovery)]."
else
- if(augment_flags & AUGMENTATION_MECHANIC)
- to_chat(user, "It can interface with prosthetic organs.")
- else if(augment_flags & AUGMENTATION_ORGANIC)
- to_chat(user, "It can interface with fleshy organs.")
+ message += " It is undetectable."
+ to_chat(user, message)
+
+
+/datum/codex_entry/augment
+ display_name = "Implantable Augmentation"
+ associated_paths = list(/obj/item/organ/internal/augment)
+ lore_text = {"\
+
Augmentations are a broad category of devices that are added to the bodies of biological and \ + mechanical individuals in order to provide some function or benefit to the user. The most common \ + augmentations in humans are medical or otherwise corrective, but everything from weapons to reward \ + stimulators can be wired into the body one way or another, making many modern humans classic cyborgs.
\ +In non-biological entities "augmentations" are often simply normal body components that are not \ + already installed - but many of the same non-medical tools, utilities, and entertainment devices \ + are available.
\ + "} + mechanics_text = {"\ +Augmentations provide various (or no) functionality and are either passive or active. \ + A passive augmentation, so long as it is not too damaged, Just Works. An active augmentation can be \ + toggled on or off via its associated UI button, which appears on its owners screen once it has been \ + implanted.
\ +Some active augmentations, like tools and weapons, will try to place an item into (or take it from) \ + a hand or other inventory slot. You will need to keep those slots free in order to turn those \ + active augmentations on.
\ ++ "} diff --git a/code/modules/augment/implanter.dm b/code/modules/augment/implanter.dm new file mode 100644 index 00000000000..68c4d28b940 --- /dev/null +++ b/code/modules/augment/implanter.dm @@ -0,0 +1,173 @@ +/obj/item/device/augment_implanter + name = "augment autodoc" + desc = "An oblong box with an irregular shape and a seam running down the center." + icon = 'icons/obj/surgery.dmi' + icon_state = "compact_bionic_module" + w_class = ITEM_SIZE_NORMAL + origin_tech = list(TECH_DATA = 3, TECH_ESOTERIC = 3) + var/obj/item/organ/internal/augment/augment + + /// If not falsy, skip the messy installation and just install the augment + var/instant + + /// Transient value to block multiple activations at the same time + var/working + + +/obj/item/device/augment_implanter/Initialize() + . = ..() + if (ispath(augment)) + augment = new augment (src) + + +/obj/item/device/augment_implanter/examine(mob/user) + . = ..() + if (isobserver(user) || (user.mind && user.mind.special_role != null) || user.skill_check(SKILL_DEVICES, SKILL_PROF)) + to_chat(user, "A single-use augment installer with no medical knowledge necessary! " + SPAN_DANGER("Painkillers not included!")) + if (isnull(augment)) + to_chat(user, "It seems to be empty.") + return + to_chat(user, SPAN_BOLD("It contains:")) + augment.examine(user) + + +/obj/item/device/augment_implanter/attackby(obj/item/I, mob/living/user) + if (isCrowbar(I) && augment) + user.visible_message( + SPAN_ITALIC("\The [user] starts to remove \the [augment] from \the [src]."), + SPAN_WARNING("You start to remove \the [augment] from \the [src]."), + SPAN_ITALIC("You hear metal creaking.") + ) + playsound(user, 'sound/items/Crowbar.ogg', 50, TRUE) + if (!do_after(user, 10 SECONDS, src) || !augment) + return + user.visible_message( + SPAN_ITALIC("\The [user] removes \the [augment] from \the [src]."), + SPAN_WARNING("You remove \the [augment] from \the [src]."), + SPAN_ITALIC("You hear a clunk.") + ) + playsound(user, 'sound/items/Deconstruct.ogg', 50, TRUE) + user.put_in_hands(augment) + augment = null + return + ..() + + +/obj/item/device/augment_implanter/attack_self(mob/living/carbon/human/user) + if (working) + return + if (!istype(user)) + return + if (!augment) + to_chat(user, SPAN_WARNING("\The [src] is empty.")) + return + if (!ishuman(user)) + to_chat(user, SPAN_WARNING("\The [src] is incompatible with you.")) + return + var/target_zone = user.zone_sel.selecting + if (!target_zone) + return + var/obj/item/organ/external/parent = user.get_organ(target_zone) + if (!parent) + to_chat(user, SPAN_WARNING("You don't have \a [target_zone]!")) + return + var/flavor = (parent.status & ORGAN_ROBOTIC) ? 1 : (parent.status & ORGAN_CRYSTAL) ? 2 : 0 + if (flavor == 0 && !(augment.augment_flags & AUGMENT_BIOLOGICAL)) + to_chat(user, SPAN_WARNING("\The [augment] cannot be installed in biological organs.")) + return + if (flavor == 1 && !(augment.augment_flags & AUGMENT_MECHANICAL)) + to_chat(user, SPAN_WARNING("\The [augment] cannot be installed in mechanical organs.")) + return + if (flavor == 2 && !(augment.augment_flags & AUGMENT_CRYSTALINE)) + to_chat(user, SPAN_WARNING("\The [augment] cannot be installed in crystaline organs.")) + return + var/surgery_step = decls_repository.get_decl(/decl/surgery_step/internal/replace_organ) + if (augment.surgery_configure(user, user, parent, src, surgery_step)) + return + var/occupied = user.internal_organs_by_name[augment.organ_tag] + if (occupied) + to_chat(user, SPAN_WARNING("You already have \an [occupied] installed there.")) + return + if (flavor != -1) + var/old_loc = loc + var/proceed = alert(user, "This is going to hurt. Are you prepared?", "Woah there!", "I am!", "Wait...") + if (proceed != "I am!") + return + if (loc != old_loc) + return + var/success = instant + if (!instant) + working = TRUE + to_chat(user, SPAN_WARNING("\icon[src] Commencing procedure. " + SPAN_DANGER("Please remain calm."))) + user.visible_message(SPAN_WARNING("\The [user] places \his [parent.name] against \the [src].")) + if (!do_after(user, 2 SECONDS, src)) + goto FailedAugmentImplant + user.visible_message(SPAN_DANGER("\The [src] purrs maliciously and unfurls its armatures with frightening speed!")) + if (flavor != 1) + user.custom_pain("Your [parent.name] feels like it's being shredded apart!", 160) + else + to_chat(user, SPAN_ITALIC("The access panel on your [parent.name] is torn open.")) + playsound(user, 'sound/items/electronic_assembly_emptying.ogg', 50, TRUE) + parent.createwound(CUT, parent.min_broken_damage / 2, 1) + parent.clamp_organ() + parent.open_incision() + parent.fracture() + if (!do_after(user, 8 SECONDS, src)) + goto FailedAugmentImplant + user.visible_message(SPAN_DANGER("\The [src] begins to insert its payload into \the [user]'s [parent.name]!")) + if (flavor != 1) + user.custom_pain("You feel something rooting around violently inside your [parent.name]!", 160) + else + to_chat(user, SPAN_ITALIC("Your [parent.name] shifts and twitches as \the [src] works.")) + if (!flavor) + playsound(user, 'sound/effects/squelch1.ogg', 25, TRUE) + else + playsound(user, 'sound/items/jaws_pry.ogg', 50, TRUE) + if (!do_after(user, 8 SECONDS, src)) + goto FailedAugmentImplant + user.visible_message(SPAN_WARNING("\The [src] withdraws from \the [user]'s [parent.name] and seals the [flavor != 1 ? "wound" : "hatch"].")) + if (!do_after(user, 2 SECONDS, src)) + goto FailedAugmentImplant + parent.status &= ~ORGAN_BROKEN + parent.stage = 0 + parent.update_wounds() + var/datum/wound/wound = parent.get_incision() + if (istype(wound)) + wound.close() + if (parent.clamped()) + parent.remove_clamps() + parent.update_wounds() + success = TRUE + FailedAugmentImplant: + working = FALSE + if (!success) + user.visible_message(SPAN_DANGER("\The [src] falls away from \the [user], leaving \his [parent.name] a mangled mess!")) + parent.take_general_damage(15) + return + to_chat(user, SPAN_WARNING("\icon[src] Procedure complete. ") + SPAN_NOTICE("Have a nice day.")) + playsound(user, 'sound/machines/ping.ogg', 50, FALSE) + augment.forceMove(user) + augment.replaced(user, parent) + augment = null + + +/obj/item/device/augment_implanter/iatric_monitor + augment = /obj/item/organ/internal/augment/active/iatric_monitor/hidden + +/obj/item/device/augment_implanter/wrist_blade + augment = /obj/item/organ/internal/augment/active/item/wrist_blade + +/obj/item/device/augment_implanter/popout_shotgun + augment = /obj/item/organ/internal/augment/active/item/popout_shotgun + +/obj/item/device/augment_implanter/nerve_dampeners + augment = /obj/item/organ/internal/augment/active/nerve_dampeners + +/obj/item/device/augment_implanter/adaptive_binoculars + augment = /obj/item/organ/internal/augment/active/item/adaptive_binoculars/hidden + +/obj/item/device/augment_implanter/engineering_toolset + augment = /obj/item/organ/internal/augment/active/polytool/engineer + +/obj/item/device/augment_implanter/powerfist + augment = /obj/item/organ/internal/augment/active/item/powerfist/prepared diff --git a/code/modules/augment/item.dm b/code/modules/augment/item.dm new file mode 100644 index 00000000000..23ac0ff1d03 --- /dev/null +++ b/code/modules/augment/item.dm @@ -0,0 +1,117 @@ +/** +* Toggleable physical equipment augment +* Specify a path on item to create on Initialize +* Specify a slot or leave null to discover on install +*/ +/obj/item/organ/internal/augment/active/item + var/deploy_sound = 'sound/items/helmet_close.ogg' + var/retract_sound = 'sound/items/helmet_open.ogg' + var/obj/item/item + var/slot + + +/obj/item/organ/internal/augment/active/item/Initialize() + . = ..() + if (ispath(item)) + item = new item (src) + item.canremove = FALSE + + +/obj/item/organ/internal/augment/active/item/onInstall() + . = ..() + if (!slot) + switch (parent_organ) + if (BP_L_ARM, BP_L_HAND) + slot = slot_l_hand + if (BP_R_ARM, BP_R_HAND) + slot = slot_r_hand + if (BP_HEAD) + slot = slot_glasses + if (BP_CHEST) + slot = slot_wear_suit + if (BP_GROIN) + slot = slot_belt + + +/obj/item/organ/internal/augment/active/item/onRemove() + retract(FALSE) + slot = initial(slot) + ..() + + +/obj/item/organ/internal/augment/active/item/Destroy() + if (item) + GLOB.item_unequipped_event.unregister(item, src) + if (item.loc == src) + qdel(item) + else + item.canremove = TRUE + item = null + return ..() + + +/obj/item/organ/internal/augment/active/item/proc/item_dropped() + GLOB.item_unequipped_event.unregister(item, src) + if (item.loc != src) // It fell off! + item.canremove = TRUE + +/obj/item/organ/internal/augment/active/item/proc/deploy(as_owner = TRUE) + if (!slot) + return + if (!item) + return + if (!owner.equip_to_slot_if_possible(item, slot)) + return + GLOB.item_unequipped_event.register(item, src, /obj/item/organ/internal/augment/active/item/proc/item_dropped) + if (deploy_sound) + playsound(owner, deploy_sound, 30) + if (as_owner) + owner.visible_message( + SPAN_WARNING("\The [owner] extends \his [item.name] from \his [limb.name]."), + SPAN_NOTICE("You extend your [item.name] from your [limb.name].") + ) + else + visible_message(SPAN_WARNING("\The [item.name] extend\s.")) + return TRUE + + +/obj/item/organ/internal/augment/active/item/proc/retract(as_owner = TRUE) + if (!item) + return + if (item.loc == src) + return + if (item.loc != owner) + return + var/mob/M = item.loc + if (!M.drop_from_inventory(item, src)) + to_chat(owner, "\The [item.name] fails to retract.") + return + if (retract_sound) + playsound(owner, retract_sound, 30) + if (as_owner) + M.visible_message( + SPAN_WARNING("\The [M] retracts \his [item.name] into \his [limb.name]."), + SPAN_NOTICE("You retract your [item.name] into your [limb.name].") + ) + else + visible_message(SPAN_WARNING("\The [item.name] retract\s.")) + return TRUE + + +/obj/item/organ/internal/augment/active/item/activate() + if (!can_activate()) + return + if (item.loc == src) + deploy() + else + retract() + owner.update_action_buttons() + + +/obj/item/organ/internal/augment/active/item/can_activate() + if (!..()) + return FALSE + if (!item) + to_chat(owner, SPAN_WARNING("The device is damaged and fails to deploy.")) + return FALSE + return TRUE diff --git a/code/modules/augment/passive/armor.dm b/code/modules/augment/passive/armor.dm index dad640529b8..d8695936dd7 100644 --- a/code/modules/augment/passive/armor.dm +++ b/code/modules/augment/passive/armor.dm @@ -1,7 +1,8 @@ /obj/item/organ/internal/augment/armor name = "subdermal armor" - allowed_organs = list(BP_AUGMENT_CHEST_ARMOUR) + augment_slots = AUGMENT_ARMOR icon_state = "armor-chest" desc = "A flexible composite mesh designed to prevent tearing and puncturing of underlying tissue." + augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE | AUGMENT_INSPECTABLE var/brute_mult = 0.8 - var/burn_mult = 1 \ No newline at end of file + var/burn_mult = 1 diff --git a/code/modules/augment/passive/boost.dm b/code/modules/augment/passive/boost.dm index f971f2a8077..57351c14bd0 100644 --- a/code/modules/augment/passive/boost.dm +++ b/code/modules/augment/passive/boost.dm @@ -1,15 +1,25 @@ -/datum/skill_buff/augment - var/id - /obj/item/organ/internal/augment/boost - var/list/buffs = list()//Which abilities does this impact? - var/list/injury_debuffs = list()//If organ is damaged, should we reduce anything? - var/buffpath = /datum/skill_buff/augment //if you use something else it should be a subtype or it will runtime - var/active = 0 //mostly to control if we should remove buffs when we go - var/debuffing = 0 //if we applied a debuff - var/id //Unique Id assigned on new icon_state = "booster" - allowed_organs = list(BP_AUGMENT_HEAD) + augment_slots = AUGMENT_HEAD + + /// Unique ID for collecting the right effect in skill handling + var/id + + /// Which abilities does this impact? + var/list/buffs = list() + + /// If organ is damaged, should we reduce anything? + var/list/injury_debuffs = list() + + /// Only subtypes of /datum/skill_buff/augment + var/buffpath = /datum/skill_buff/augment + + /// Mostly to control if we should remove buffs when we go + var/active = FALSE + + /// If we applied a debuff + var/debuffing = FALSE + /obj/item/organ/internal/augment/boost/Initialize() . = ..() @@ -17,51 +27,59 @@ /obj/item/organ/internal/augment/boost/onInstall() - if(buffs.len) + if (buffs.len) var/datum/skill_buff/augment/A A = owner.buff_skill(buffs, 0, buffpath) - if(A && istype(A)) - active = 1 + if (A && istype(A)) + active = TRUE A.id = id + /obj/item/organ/internal/augment/boost/onRemove() - debuffing = 0 - if(!active) + debuffing = FALSE + if (!active) return - var/list/B = owner.fetch_buffs_of_type(buffpath, 0) - for(var/datum/skill_buff/augment/D in B) - if(D.id == id) - D.remove() - return + for(var/datum/skill_buff/augment/D as anything in owner.fetch_buffs_of_type(buffpath, 0)) + if (D.id != id) + continue + D.remove() + return + -//Procs to set the negative skills and positive ones (This is once the initial setup has been done) /obj/item/organ/internal/augment/boost/proc/debuff() - if(!injury_debuffs ||!injury_debuffs.len) - return 0 - var/list/B = owner.fetch_buffs_of_type(buffpath, 0) - for(var/datum/skill_buff/augment/D in B) - if(D.id == id) - D.recalculate(injury_debuffs) - debuffing = 1 - return 1 + if (!length(injury_debuffs)) + return FALSE + for(var/datum/skill_buff/augment/D as anything in owner.fetch_buffs_of_type(buffpath, 0)) + if (D.id != id) + continue + D.recalculate(injury_debuffs) + debuffing = TRUE + return TRUE + return FALSE + /obj/item/organ/internal/augment/boost/proc/buff() - if(!buffs || !buffs.len) - return 0 - var/list/B = owner.fetch_buffs_of_type(buffpath, 0) - for(var/datum/skill_buff/augment/D in B) - if(D.id == id) - D.recalculate(buffs) - debuffing = 0 - return 1 + if (!length(buffs)) + return FALSE + for(var/datum/skill_buff/augment/D as anything in owner.fetch_buffs_of_type(buffpath, 0)) + if (D.id != id) + continue + D.recalculate(buffs) + debuffing = FALSE + return TRUE + return FALSE + /obj/item/organ/internal/augment/boost/Process() ..() - if(!owner) + if (!owner) return - if(is_broken() && !debuffing) - debuff() - else if(!is_broken() && debuffing) + if (!debuffing) + if (is_broken()) + debuff() + else if (!is_broken()) buff() +/datum/skill_buff/augment + var/id diff --git a/code/modules/augment/passive/boost/muscle.dm b/code/modules/augment/passive/boost/muscle.dm index d7f2a03e69c..34cf4b5646b 100644 --- a/code/modules/augment/passive/boost/muscle.dm +++ b/code/modules/augment/passive/boost/muscle.dm @@ -1,46 +1,57 @@ -//This one must do special handling because you need 2, so other than vars it doesn't share tht much -/datum/skill_buff/augment/muscle - /obj/item/organ/internal/augment/boost/muscle buffs = list(SKILL_HAULING = 1) buffpath = /datum/skill_buff/augment/muscle name = "mechanical muscles" - allowed_organs = list(BP_AUGMENT_R_LEG, BP_AUGMENT_L_LEG) + augment_slots = AUGMENT_LEG icon_state = "muscule" - desc = "Nanofiber tendons powered by an array of actuators to help the wearer mantain speed even while encumbered. You may want to install these in pairs to see a result." + desc = "Nanofiber tendons powered by an array of actuators increase the speed and agility of the user. You may want to install these in pairs to see a result." + augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE | AUGMENT_INSPECTABLE var/obj/item/organ/internal/augment/boost/muscle/other //we need two for these -/obj/item/organ/internal/augment/boost/muscle/onInstall() +/obj/item/organ/internal/augment/boost/muscle/proc/get_acrobatics_modifier() + if (!other?.is_broken() && !is_broken()) + return 1 - //1.st Determine where we are and who we should be asking for guidance - //we must be second to activate buff - if(organ_tag == BP_AUGMENT_L_LEG) - other = owner.internal_organs_by_name[BP_AUGMENT_R_LEG] - else if(organ_tag == BP_AUGMENT_R_LEG) - other = owner.internal_organs_by_name[BP_AUGMENT_L_LEG] - if(other && istype(other)) - var/datum/skill_buff/augment/muscle/A - A = owner.buff_skill(buffs, 0, buffpath) - if(A && istype(A)) - active = 1 - other.active = 1 + +/obj/item/organ/internal/augment/boost/muscle/onInstall() + if (parent_organ == BP_L_LEG) + other = owner.internal_organs_by_name["[BP_R_LEG]_aug"] + else if (parent_organ == BP_R_LEG) + other = owner.internal_organs_by_name["[BP_L_LEG]_aug"] + if (other && istype(other)) //we must be second to activate buff + var/succesful = TRUE + if (owner.get_skill_value(SKILL_HAULING) < SKILL_PROF) + succesful = FALSE + var/datum/skill_buff/augment/muscle/A + A = owner.buff_skill(buffs, 0, buffpath) + if (A && istype(A)) + succesful = TRUE + A.id = id + if (succesful) other.other = src - A.id = id + other.active = TRUE + active = TRUE + /obj/item/organ/internal/augment/boost/muscle/onRemove() - if(!active) + if (!active) return - var/list/B = owner.fetch_buffs_of_type(buffpath, 0) - for(var/datum/skill_buff/augment/muscle/D in B) - if(D.id == id) - D.remove() - if(other) - other.active = 0 - other.other = null - other = null - return + for(var/datum/skill_buff/augment/D as anything in owner.fetch_buffs_of_type(buffpath, 0)) + if (D.id != id) + continue + D.remove() + break + if (other) + other.active = FALSE + other.other = null + other = null + active = FALSE + /obj/item/organ/internal/augment/boost/muscle/Destroy() . = ..() - other = null //If somehow onRemove didn't handle it + other = null + + +/datum/skill_buff/augment/muscle diff --git a/code/modules/augment/passive/nanoaura.dm b/code/modules/augment/passive/nanoaura.dm index b93ff73dd42..49cdf38a6a6 100644 --- a/code/modules/augment/passive/nanoaura.dm +++ b/code/modules/augment/passive/nanoaura.dm @@ -1,94 +1,73 @@ -//This handy augment protects you to a degree, keeping it online after critical damage however is bad - -/obj/aura/nanoaura - name = "Nanoaura" - var/obj/item/organ/internal/augment/active/nanounit/unit = null - var/active = 0 - - -//The organ itself - /obj/item/organ/internal/augment/active/nanounit name = "Nanite MCU" - allowed_organs = list(BP_AUGMENT_CHEST_ACTIVE) + augment_slots = AUGMENT_CHEST icon_state = "armor-chest" desc = "Nanomachines, son." action_button_name = "Toggle Nanomachines" - var/obj/aura/nanoaura/aura = null + var/obj/aura/nanoaura/aura var/charges = 4 - var/max_charges = 4 - var/list/memesounds = list('sound/effects/nanomachines/nanomachinesson.ogg', 'sound/effects/nanomachines/physicaltrauma.ogg') - var/next_regen_time - var/regen_delay = 5 MINUTES - var/regen_amount = 1 - -/obj/item/organ/internal/augment/active/nanounit/Initialize() - . = ..() - next_regen_time = world.time + regen_delay - START_PROCESSING(SSobj, src) - -/obj/item/organ/internal/augment/active/nanounit/Destroy() - STOP_PROCESSING(SSobj, src) -/obj/item/organ/internal/augment/active/nanounit/Process() - if(next_regen_time < world.time && (charges != max_charges)) - charges += regen_amount - to_chat(owner, SPAN_NOTICE("Nanite MCU: Nanomaterial reprocessing complete - charge added. Current charges: [charges].")) - next_regen_time = world.time + regen_delay - ..() /obj/item/organ/internal/augment/active/nanounit/onInstall() - aura = new /obj/aura/nanoaura(owner, src) + aura = new /obj/aura/nanoaura (owner, src) + /obj/item/organ/internal/augment/active/nanounit/onRemove() QDEL_NULL(aura) ..() + /obj/item/organ/internal/augment/active/nanounit/proc/catastrophic_failure() playsound(owner,'sound/mecha/internaldmgalarm.ogg',25,1) owner.visible_message(SPAN_WARNING("The nanites attempt to harden. But they seem... brittle.")) for(var/obj/item/organ/external/E in owner.organs) - if(prob(25)) - E.status |= ORGAN_BRITTLE //Some nanites are not responding and you're out of luck - to_chat(owner,SPAN_DANGER("Your [E.name] feels cold and rigid")) + if (prob(25)) + E.status |= ORGAN_BRITTLE + to_chat(owner, SPAN_DANGER("Your [E.name] feels cold and rigid")) QDEL_NULL(aura) + /obj/item/organ/internal/augment/active/nanounit/activate() if(!aura || !can_activate()) return if(aura.active) - aura.active = 0 - to_chat(owner,SPAN_NOTICE("Nanites entering sleep mode.")) + aura.active = FALSE + to_chat(owner, SPAN_NOTICE("Nanites entering sleep mode.")) else - aura.active = 1 - to_chat(owner,SPAN_NOTICE("Activation sequence in progress.")) - playsound(owner,'sound/weapons/flash.ogg',35,1) + aura.active = TRUE + to_chat(owner, SPAN_NOTICE("Activation sequence in progress.")) + playsound(owner, 'sound/weapons/flash.ogg', 35, 1) /obj/item/organ/internal/augment/active/nanounit/Destroy() . = ..() QDEL_NULL(aura) -/obj/aura/nanoaura/Initialize(var/maploading, var/obj/item/organ/internal/augment/active/nanounit/holder) + +/obj/aura/nanoaura + name = "Nanoaura" + var/obj/item/organ/internal/augment/active/nanounit/unit + var/active + + +/obj/aura/nanoaura/Initialize(maploading, obj/item/organ/internal/augment/active/nanounit/holder) . = ..() unit = holder - playsound(loc,'sound/weapons/flash.ogg',35,1) - to_chat(loc,SPAN_NOTICE("Your skin tingles as the nanites spread over your body.")) + playsound(loc, 'sound/weapons/flash.ogg',35,1) + to_chat(loc, SPAN_NOTICE("Your skin tingles as the nanites spread over your body.")) + -/obj/aura/nanoaura/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(!active) +/obj/aura/nanoaura/bullet_act(obj/item/projectile/P, def_zone) + if (!active) return - if(unit.charges > 0) + if (unit.charges > 0) user.visible_message(SPAN_WARNING("The nanomachines harden as a response to physical trauma!")) - playsound(user,'sound/effects/basscannon.ogg',35,1) + playsound(user, 'sound/effects/basscannon.ogg',35,1) unit.charges -= 1 - if(prob(1)) - var/memesound = pick(unit.memesounds) - playsound(user, memesound, 35, 1) - if(unit.charges <= 0) + if (unit.charges <= 0) to_chat(user, SPAN_DANGER("Warning: Critical damage treshold passed. Shut down unit to avoid further damage")) - return AURA_FALSE|AURA_CANCEL - else unit.catastrophic_failure() + return AURA_FALSE | AURA_CANCEL + unit.catastrophic_failure() /obj/aura/nanoaura/Destroy() diff --git a/code/modules/augment/simple.dm b/code/modules/augment/simple.dm deleted file mode 100644 index af9577bacce..00000000000 --- a/code/modules/augment/simple.dm +++ /dev/null @@ -1,73 +0,0 @@ -//Simple toggleabse module. Just put holding in hands or get it back -/obj/item/organ/internal/augment/active/simple - var/obj/item/holding = null - var/holding_type = null - -/obj/item/organ/internal/augment/active/simple/Initialize() - . = ..() - if(holding_type) - holding = new holding_type(src) - holding.canremove = 0 - -/obj/item/organ/internal/augment/active/simple/Destroy() - if(holding) - GLOB.item_unequipped_event.unregister(holding, src) - if(holding.loc == src) - QDEL_NULL(holding) - - -/obj/item/organ/internal/augment/active/simple/proc/holding_dropped() - - //Stop caring - GLOB.item_unequipped_event.unregister(holding, src) - - if(holding.loc != src) //something went wrong and is no longer attached/ it broke - holding.canremove = 1 - holding = null //We no longer hold this, you will have to get a replacement module or fix it somehow - -/obj/item/organ/internal/augment/active/simple/proc/deploy() - - var/slot = null - if(limb.organ_tag in list(BP_L_ARM, BP_L_HAND)) - slot = slot_l_hand - else if(limb.organ_tag in list(BP_R_ARM, BP_R_HAND)) - slot = slot_r_hand - if(owner.equip_to_slot_if_possible(holding, slot)) - GLOB.item_unequipped_event.register(holding, src, /obj/item/organ/internal/augment/active/simple/proc/holding_dropped ) - owner.visible_message( - SPAN_WARNING("[owner] extends \his [holding.name] from [limb]."), - SPAN_NOTICE("You extend your [holding.name] from [limb].") - ) - -/obj/item/organ/internal/augment/active/simple/proc/retract() - if(holding.loc == src) - return - - if(ismob(holding.loc) && holding.loc == owner) - var/mob/M = holding.loc - if(!M.drop_from_inventory(holding, src)) - to_chat(owner, "\the [holding.name] fails to retract.") - return - M.visible_message( - SPAN_WARNING("[M] retracts \his [holding.name] into [limb]."), - SPAN_NOTICE("You retract your [holding.name] into [limb].") - ) - - - -/obj/item/organ/internal/augment/active/simple/activate() - if(!can_activate()) - return - - if(holding.loc == src) //item not in hands - deploy() - else //retract item - retract() - -/obj/item/organ/internal/augment/active/simple/can_activate() - if(..()) - if(!holding) - to_chat(owner, SPAN_WARNING("The device is damaged and fails to deploy")) - return FALSE - return TRUE - return FALSE \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index c74ef47f8fc..1454fe34473 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -404,9 +404,9 @@ var/list/gear_datums = list() var/obj/item/item = spawn_item(H, metadata) item.add_fingerprint(H) - if(implanted) - implant_into_mob(H, item) - return //avoids weird stuff. +// if(implanted) +// implant_into_mob(H, item) +// return //avoids weird stuff. var/atom/placed_in = H.equip_to_storage(item) if(placed_in) to_chat(H, "Placing \the [item] in your [placed_in.name]!") @@ -417,7 +417,7 @@ var/list/gear_datums = list() else to_chat(H, "Dropping \the [item] on the ground!") -/datum/gear/proc/implant_into_mob(var/mob/living/carbon/human/H, obj/item/I) +/*/datum/gear/proc/implant_into_mob(var/mob/living/carbon/human/H, obj/item/I) var/obj/item/organ/external/organ_to_implant_into = H.get_organ(BP_CHEST) if(istype(I, /obj/item/organ/internal/augment)) //We are an augment, figure out the parent organ we go into. @@ -438,3 +438,4 @@ var/list/gear_datums = list() IM.forceMove(organ_to_implant_into) IM.implanted(H) //just in case to_chat(H, SPAN_NOTICE("Implanting you with [IM] in your [organ_to_implant_into.name]!")) +*/ \ No newline at end of file diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 30dbc5fbc3e..fac607940c3 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -231,3 +231,29 @@ icon_state = "rwelding-g" item_state = "rwelding-g" tint = TINT_MODERATE + +/obj/item/clothing/glasses/glare_dampeners + name = "glare dampeners" + desc = "Synthetic lenses over the eyes, protecting from bright lights." + icon_state = "welding-g" + item_state = "welding-g" + use_alt_layer = TRUE + flash_protection = FLASH_PROTECTION_MODERATE + darkness_view = -1 + +/obj/item/clothing/glasses/augment_binoculars + name = "adaptive binoculars" + desc = "Digital lenses covering the eyes, capable of zooming in on distant targets." + gender = PLURAL + icon_state = "thermal" + item_state = "glasses" + action_button_name = "Toggle zoom" + zoomdevicename = "lenses" + electric = TRUE + unacidable = TRUE + +/obj/item/clothing/glasses/augment_binoculars/attack_self(mob/user) + if(zoom) + unzoom(user) + else + zoom(user) diff --git a/code/modules/organs/external/_external_damage.dm b/code/modules/organs/external/_external_damage.dm index 354ce034f6c..fa133333b96 100644 --- a/code/modules/organs/external/_external_damage.dm +++ b/code/modules/organs/external/_external_damage.dm @@ -323,7 +323,7 @@ obj/item/organ/external/take_general_damage(var/amount, var/silent = FALSE) return FALSE /obj/item/organ/external/proc/get_brute_mod(var/damage_flags) - var/obj/item/organ/internal/augment/armor/A = owner && owner.internal_organs_by_name[BP_AUGMENT_CHEST_ARMOUR] + var/obj/item/organ/internal/augment/armor/A = owner && owner.internal_organs_by_name["[BP_CHEST]_aug_armor"] var/B = 1 if(A && istype(A)) B = A.brute_mult @@ -337,7 +337,7 @@ obj/item/organ/external/take_general_damage(var/amount, var/silent = FALSE) return B + (0.2 * burn_dam/max_damage) //burns make you take more brute damage /obj/item/organ/external/proc/get_burn_mod(var/damage_flags) - var/obj/item/organ/internal/augment/armor/A = owner && owner.internal_organs_by_name[BP_AUGMENT_CHEST_ARMOUR] + var/obj/item/organ/internal/augment/armor/A = owner && owner.internal_organs_by_name["[BP_CHEST]_aug_armor"] var/B = 1 if(A && istype(A)) B = A.burn_mult diff --git a/code/modules/organs/external/diagnostics.dm b/code/modules/organs/external/diagnostics.dm index 66ee87fabaf..8be8a8b198c 100644 --- a/code/modules/organs/external/diagnostics.dm +++ b/code/modules/organs/external/diagnostics.dm @@ -105,8 +105,8 @@ unknown_body++ if(unknown_body) . += "Unknown body present" - for(var/obj/item/organ/internal/augment/aug in internal_organs) - if(istype(aug) && aug.known) + for (var/obj/item/organ/internal/augment/aug in internal_organs) + if (aug.augment_flags & AUGMENT_SCANNABLE) . += "[capitalize(aug.name)] implanted" /obj/item/organ/external/proc/inspect(mob/user) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 5fa5de001f3..86282eb0be8 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -384,3 +384,10 @@ var/list/organ_cache = list() /obj/item/organ/proc/get_mechanical_assisted_descriptor() return "mechanically-assisted [name]" + +/** +* Pre-surgery modification of the organ if it has status|ORGAN_CONFIGURE +* Halts surgery if the return value is truthy +*/ +/obj/item/organ/proc/surgery_configure(mob/living/user, mob/living/carbon/human/target, obj/item/organ/parent, obj/item/tool, decl/surgery_step/action) + return diff --git a/code/modules/research/designs/designs_mechfab.dm b/code/modules/research/designs/designs_mechfab.dm index 1330c3c1def..2bfacc7d667 100644 --- a/code/modules/research/designs/designs_mechfab.dm +++ b/code/modules/research/designs/designs_mechfab.dm @@ -466,14 +466,14 @@ /datum/design/item/mechfab/augment/armblade name = "Armblade" - build_path = /obj/item/organ/internal/augment/active/simple/armblade + build_path = /obj/item/organ/internal/augment/active/item/armblade materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 750) req_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 2, TECH_MATERIAL = 4, TECH_BIO = 3) id = "augment_blade" /datum/design/item/mechfab/augment/armblade/wolverine name = "Cyberclaws" - build_path = /obj/item/organ/internal/augment/active/simple/wolverine + build_path = /obj/item/organ/internal/augment/active/item/wolverine materials = list(DEFAULT_WALL_MATERIAL = 6000, "diamond" = 250) req_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4, TECH_MATERIAL = 4, TECH_BIO = 3) id = "augment_wolverine" @@ -522,7 +522,7 @@ /datum/design/item/mechfab/augment/circuit name = "Integrated circuit frame" - build_path = /obj/item/organ/internal/augment/active/simple/circuit + build_path = /obj/item/organ/internal/augment/active/item/circuit materials = list(DEFAULT_WALL_MATERIAL = 3000) id = "augment_circuitry" diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index 1204c1d7476..ce83f94e378 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -33,8 +33,17 @@ . = FALSE var/obj/item/organ/external/E = tool var/obj/item/organ/external/P = target.organs_by_name[E.parent_organ] + var/obj/item/organ/external/T = target.organs_by_name[E.organ_tag] + + if ((E.status & ORGAN_CONFIGURE) && E.surgery_configure(user, target, P, tool, src)) + return + if(!P || P.is_stump()) to_chat(user, SPAN_WARNING("The [E.amputation_point] is missing!")) + else if(T && T.is_stump()) + to_chat(user, SPAN_WARNING("You cannot attach \a [E] when there is a stump!")) + else if(T) + to_chat(user, SPAN_WARNING("There is already \a [E]!")) else if(BP_IS_ROBOTIC(P) && !BP_IS_ROBOTIC(E)) to_chat(user, SPAN_WARNING("You cannot attach a flesh part to a robotic body.")) else if(BP_IS_CRYSTAL(P) && !BP_IS_CRYSTAL(E)) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 7f13694f99d..03ffcbb25ab 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -231,6 +231,10 @@ . = FALSE var/obj/item/organ/internal/O = tool var/obj/item/organ/external/affected = target.get_organ(target_zone) + + if ((O.status & ORGAN_CONFIGURE) && O.surgery_configure(user, target, affected, tool, src)) + return + if(istype(O) && istype(affected)) if(BP_IS_CRYSTAL(O) && !BP_IS_CRYSTAL(affected)) to_chat(user, SPAN_WARNING("You cannot install a crystalline organ into a non-crystalline bodypart.")) @@ -331,7 +335,7 @@ if(istype(organ_to_replace, /obj/item/organ/internal/augment)) var/obj/item/organ/internal/augment/A = organ_to_replace - if(!(A.augment_flags & AUGMENTATION_ORGANIC)) + if(!(A.augment_flags & AUGMENT_BIOLOGICAL)) to_chat(user, SPAN_WARNING("\The [A] cannot function within a non-robotic limb.")) return FALSE diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 163821768b8..fdf4821c59d 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -471,7 +471,7 @@ decl/surgery_step/robotics/get_skill_reqs(mob/living/user, mob/living/carbon/hum var/obj/item/organ/organ_to_replace = show_radial_menu(user, target, attachable_organs) if(istype(organ_to_replace, /obj/item/organ/internal/augment)) var/obj/item/organ/internal/augment/A = organ_to_replace - if(!(A.augment_flags & AUGMENTATION_MECHANIC)) + if(!(A.augment_flags & AUGMENT_MECHANICAL)) to_chat(user, SPAN_WARNING("\The [A] cannot function within a robotic limb!")) return FALSE return organ_to_replace diff --git a/maps/torch/loadout/loadout_augments.dm b/maps/torch/loadout/loadout_augments.dm index c52c7df9193..39f22a0ef44 100644 --- a/maps/torch/loadout/loadout_augments.dm +++ b/maps/torch/loadout/loadout_augments.dm @@ -1,5 +1,6 @@ //combat //utility +/* /datum/gear/augmentation/implanted_surgical display_name = "surgical polytool - left arm (ROBOTIC)" path = /obj/item/organ/internal/augment/active/polytool/surgical/left @@ -26,4 +27,6 @@ /datum/gear/augmentation/implanted_circuitkit/right display_name = "circuit augment - right arm (ROBOTIC)" - path = /obj/item/organ/internal/augment/active/simple/circuit/right \ No newline at end of file + path = /obj/item/organ/internal/augment/active/simple/circuit/right + +*/ \ No newline at end of file diff --git a/test/check-paths.sh b/test/check-paths.sh index b259c17d1a7..cb43c5d0b23 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -31,7 +31,7 @@ exactly 10 "/obj text paths" '"/obj' exactly 8 "/turf text paths" '"/turf' exactly 0 "world<< uses" 'world<<|world[[:space:]]<<' exactly 43 "world.log<< uses" 'world.log<<|world.log[[:space:]]<<' -exactly 480 "<< uses" '(?