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" '(?