diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm index 4c8ec4d6ded..73031b3035f 100644 --- a/code/__DEFINES/dcs/signals/signals_mod.dm +++ b/code/__DEFINES/dcs/signals/signals_mod.dm @@ -5,3 +5,7 @@ #define COMSIG_MOD_ACTIVATE "mod_activate" /// Cancels the suit's activation #define MOD_CANCEL_ACTIVATE (1 << 0) +/// Called when a MOD is having modules removed from crowbar_act(mob/user, obj/crowbar) +#define COMSIG_MOD_MODULE_REMOVAL "mod_module_removal" + /// Cancels the removal of modules + #define MOD_CANCEL_REMOVAL (1 << 0) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index b5fac043bef..9a9c3db887f 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -365,3 +365,7 @@ ///from base of /obj/item/mmi/set_brainmob(): (mob/living/brain/new_brainmob) #define COMSIG_MMI_SET_BRAINMOB "mmi_set_brainmob" + +/// from base of /obj/item/slimepotion/speed/afterattack(): (obj/target, /obj/src, mob/user) +#define COMSIG_SPEED_POTION_APPLIED "speed_potion" + #define SPEED_POTION_SUCCESSFUL (1<<0) diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 9e55caa5fea..34a297bb506 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -545,10 +545,8 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) /datum/component/storage/proc/attackby(datum/source, obj/item/I, mob/M, params) SIGNAL_HANDLER - if(istype(I, /obj/item/hand_labeler)) - var/obj/item/hand_labeler/labeler = I - if(labeler.mode) - return FALSE + if(!I.attackby_storage_insert(src, parent, M)) + return FALSE . = TRUE //no afterattack if(iscyborg(M)) return diff --git a/code/datums/elements/ridable.dm b/code/datums/elements/ridable.dm index 3bc31b14494..5bafbd3a0b9 100644 --- a/code/datums/elements/ridable.dm +++ b/code/datums/elements/ridable.dm @@ -29,10 +29,10 @@ RegisterSignal(target, COMSIG_MOVABLE_PREBUCKLE, .proc/check_mounting) if(isvehicle(target)) - RegisterSignal(target, COMSIG_PARENT_ATTACKBY, .proc/check_potion) + RegisterSignal(target, COMSIG_SPEED_POTION_APPLIED, .proc/check_potion) /datum/element/ridable/Detach(datum/target) - UnregisterSignal(target, list(COMSIG_MOVABLE_PREBUCKLE, COMSIG_PARENT_ATTACKBY)) + UnregisterSignal(target, list(COMSIG_MOVABLE_PREBUCKLE, COMSIG_SPEED_POTION_APPLIED)) return ..() /// Someone is buckling to this movable, which is literally the only thing we care about (other than speed potions) @@ -108,33 +108,28 @@ return FALSE /// Checks to see if we've been hit with a red xenobio potion to make us faster. This is only registered if we're a vehicle -/datum/element/ridable/proc/check_potion(atom/movable/ridable_atom, obj/item/slimepotion/speed/speed_potion, mob/living/user, params) +/datum/element/ridable/proc/check_potion(atom/movable/ridable_atom, obj/item/slimepotion/speed/speed_potion, mob/living/user) SIGNAL_HANDLER - if(!istype(speed_potion)) - return if(potion_boosted) to_chat(user, span_warning("[ridable_atom] has already been coated with red, that's as fast as it'll go!")) return if(ridable_atom.has_buckled_mobs()) // effect won't take place til the next time someone mounts it, so just prevent that situation to_chat(user, span_warning("It's too dangerous to smear [speed_potion] on [ridable_atom] while it's being ridden!")) return - var/speed_limit = round(CONFIG_GET(number/movedelay/run_delay) * 0.85, 0.01) var/datum/component/riding/theoretical_riding_component = riding_component_type var/theoretical_speed = initial(theoretical_riding_component.vehicle_move_delay) - if(theoretical_speed <= speed_limit) // i say speed but this is actually move delay, so you have to be ABOVE the speed limit to pass to_chat(user, span_warning("[ridable_atom] can't be made any faster!")) return - Detach(ridable_atom) ridable_atom.AddElement(/datum/element/ridable, component_type = riding_component_type, potion_boost = TRUE) to_chat(user, span_notice("You slather the red gunk over [ridable_atom], making it faster.")) ridable_atom.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) ridable_atom.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY) qdel(speed_potion) - return COMPONENT_NO_AFTERATTACK + return SPEED_POTION_SUCCESSFUL /// Remove all of the relevant [riding offhand items][/obj/item/riding_offhand] from the target /datum/element/ridable/proc/unequip_buckle_inhands(mob/living/carbon/user, atom/movable/target_movable) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 5e66f5e707a..82309f66db4 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1315,3 +1315,7 @@ attack_basic_mob /// Special stuff you want to do when an outfit equips this item. /obj/item/proc/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot) return + +/// Whether or not this item can be put into a storage item through attackby +/obj/item/proc/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user) + return TRUE diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index f7869dc2278..447f1068550 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -51,7 +51,7 @@ ADD_TRAIT(piece, TRAIT_NODROP, MOD_TRAIT) if(!user) return TRUE - wearer.visible_message(span_notice("[wearer]'s [piece] deploy[piece.p_s()] with a mechanical hiss."), + wearer.visible_message(span_notice("[wearer]'s [piece.name] deploy[piece.p_s()] with a mechanical hiss."), span_notice("[piece] deploy[piece.p_s()] with a mechanical hiss."), span_hear("You hear a mechanical hiss.")) playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) @@ -59,7 +59,7 @@ else if(piece.loc != src) if(!user) return FALSE - balloon_alert(user, "[piece] already deployed!") + balloon_alert(user, "[piece.name] already deployed!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) else if(!user) @@ -79,7 +79,7 @@ boots.show_overslot() if(!user) return - wearer.visible_message(span_notice("[wearer]'s [piece] retract[piece.p_s()] back into [src] with a mechanical hiss."), + wearer.visible_message(span_notice("[wearer]'s [piece.name] retract[piece.p_s()] back into [src] with a mechanical hiss."), span_notice("[piece] retract[piece.p_s()] back into [src] with a mechanical hiss."), span_hear("You hear a mechanical hiss.")) playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) diff --git a/code/modules/mod/mod_ai.dm b/code/modules/mod/mod_ai.dm index 26bf7607b34..700fce07333 100644 --- a/code/modules/mod/mod_ai.dm +++ b/code/modules/mod/mod_ai.dm @@ -70,16 +70,18 @@ if((!active && wearer) || !cell || cell.charge < CELL_PER_STEP || user != ai || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE)) return FALSE var/timemodifier = MOVE_DELAY * (ISDIAGONALDIR(direction) ? SQRT_2 : 1) * (wearer ? WEARER_DELAY : LONE_DELAY) - COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown) - playsound(src, 'sound/mecha/mechmove01.ogg', 25, TRUE) - cell.charge = max(0, cell.charge - CELL_PER_STEP) - if(wearer) - ADD_TRAIT(wearer, TRAIT_FORCED_STANDING, MOD_TRAIT) - addtimer(CALLBACK(src, .proc/ai_fall), AI_FALL_TIME, TIMER_UNIQUE | TIMER_OVERRIDE) - if(ismovable(wearer?.loc)) - return wearer.loc.relaymove(wearer, direction) if(wearer && !wearer.Process_Spacemove(direction)) return FALSE + else if(!wearer && (!has_gravity() || !isturf(loc))) + return FALSE + COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown_active) + cell.charge = max(0, cell.charge - CELL_PER_STEP) + playsound(src, 'sound/mecha/mechmove01.ogg', 25, TRUE) + if(ismovable(wearer?.loc)) + return wearer.loc.relaymove(wearer, direction) + else if(wearer) + ADD_TRAIT(wearer, TRAIT_FORCED_STANDING, MOD_TRAIT) + addtimer(CALLBACK(src, .proc/ai_fall), AI_FALL_TIME, TIMER_UNIQUE | TIMER_OVERRIDE) var/atom/movable/mover = wearer || src return step(mover, direction) diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index ef3da163ae6..015fa5e44ae 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -14,7 +14,7 @@ w_class = WEIGHT_CLASS_BULKY slot_flags = ITEM_SLOT_BACK strip_delay = 10 SECONDS - slowdown = 2 + slowdown = 1.25 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10) actions_types = list( /datum/action/item_action/mod/deploy, @@ -59,9 +59,9 @@ /// Power usage of the MOD. var/cell_drain = DEFAULT_CELL_DRAIN /// Slowdown of the MOD when not active. - var/slowdown_inactive = 2 + var/slowdown_inactive = 1.25 /// Slowdown of the MOD when active. - var/slowdown_active = 1 + var/slowdown_active = 0.75 /// MOD cell. var/obj/item/stock_parts/cell/cell /// MOD helmet. @@ -137,6 +137,7 @@ module = new module(src) install(module) RegisterSignal(src, COMSIG_ATOM_EXITED, .proc/on_exit) + RegisterSignal(src, COMSIG_SPEED_POTION_APPLIED, .proc/on_potion) movedelay = CONFIG_GET(number/movedelay/run_delay) /obj/item/mod/control/Destroy() @@ -200,12 +201,13 @@ . += span_notice("You could use modules on it to install them.") . += span_notice("You could remove modules with a crowbar.") . += span_notice("You could update the access with an ID.") + . += span_notice("You could access the wire panel with a wire configuring tool.") if(cell) . += span_notice("You could remove the cell with an empty hand.") else . += span_notice("You could use a cell on it to install one.") if(ai) - . += span_notice("You could remove [ai] with an intellicard") + . += span_notice("You could remove [ai] with an intellicard.") else . += span_notice("You could install an AI with an intellicard.") @@ -314,6 +316,9 @@ balloon_alert(user, "insufficient access!") playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return + if(SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVAL, user) & MOD_CANCEL_REMOVAL) + playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + return FALSE if(length(modules)) var/list/removable_modules = list() for(var/obj/item/mod/module/module as anything in modules) @@ -406,6 +411,8 @@ if(active && !toggle_activate(stripper, force_deactivate = TRUE)) return for(var/obj/item/part in mod_parts) + if(part.loc == src) + continue conceal(null, part) return ..() @@ -617,3 +624,23 @@ return cell.give(amount) update_cell_alert() + +/obj/item/mod/control/proc/on_potion(atom/movable/source, obj/item/slimepotion/speed/speed_potion, mob/living/user) + SIGNAL_HANDLER + + if(slowdown_inactive <= 0) + to_chat(user, span_warning("[src] has already been coated with red, that's as fast as it'll go!")) + return + if(wearer) + to_chat(user, span_warning("It's too dangerous to smear [speed_potion] on [src] while it's on someone!")) + return + to_chat(user, span_notice("You slather the red gunk over [src], making it faster.")) + var/list/all_parts = mod_parts.Copy() + src + for(var/obj/item/part as anything in all_parts) + part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + part.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY) + slowdown_inactive = 0 + slowdown_active = 0 + slowdown = 0 + qdel(speed_potion) + return SPEED_POTION_SUCCESSFUL diff --git a/code/modules/mod/modules/modules.dm b/code/modules/mod/modules/modules.dm deleted file mode 100644 index 0ff9b5f48c0..00000000000 --- a/code/modules/mod/modules/modules.dm +++ /dev/null @@ -1,1780 +0,0 @@ -/obj/item/mod/module/storage - name = "MOD storage module" - desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \ - the surface of the suit, useful for storing various bits, and or bobs." - icon_state = "storage" - complexity = 3 - incompatible_modules = list(/obj/item/mod/module/storage) - var/datum/component/storage/concrete/storage - var/max_w_class = WEIGHT_CLASS_NORMAL - var/max_combined_w_class = 15 - var/max_items = 7 - -/obj/item/mod/module/storage/Initialize(mapload) - . = ..() - storage = AddComponent(/datum/component/storage/concrete) - storage.max_w_class = max_w_class - storage.max_combined_w_class = max_combined_w_class - storage.max_items = max_items - storage.allow_big_nesting = TRUE - SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE) - -/obj/item/mod/module/storage/on_install() - var/datum/component/storage/modstorage = mod.AddComponent(/datum/component/storage, storage) - modstorage.max_w_class = max_w_class - modstorage.max_combined_w_class = max_combined_w_class - modstorage.max_items = max_items - SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE) - -/obj/item/mod/module/storage/on_uninstall() - var/datum/component/storage/modstorage = mod.GetComponent(/datum/component/storage) - storage.slaves -= modstorage - qdel(modstorage) - SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE) - -/obj/item/mod/module/storage/large_capacity - name = "MOD expanded storage module" - desc = "Reverse engineered by Nakamura Engineering from Donk Corporation designs, this system of hidden compartments \ - is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \ - whether smuggling, or simply hauling." - icon_state = "storage_large" - max_combined_w_class = 21 - max_items = 14 - -/obj/item/mod/module/storage/syndicate - name = "MOD syndicate storage module" - desc = "A storage system using nanotechnology developed by Cybersun Industries, these compartments use \ - esoteric technology to compress the physical matter of items put inside of them, \ - essentially shrinking items for much easier and more portable storage." - icon_state = "storage_syndi" - max_combined_w_class = 30 - max_items = 21 - -/obj/item/mod/module/storage/bluespace - name = "MOD bluespace storage module" - desc = "A storage system developed by Nanotrasen, these compartments employ \ - miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside." - icon_state = "storage_large" - max_w_class = WEIGHT_CLASS_GIGANTIC - max_combined_w_class = 60 - max_items = 21 - -/obj/item/mod/module/visor - name = "MOD visor module" - desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you." - module_type = MODULE_TOGGLE - complexity = 2 - active_power_cost = DEFAULT_CELL_DRAIN*0.3 - incompatible_modules = list(/obj/item/mod/module/visor) - cooldown_time = 0.5 SECONDS - var/hud_type - var/list/visor_traits = list() - -/obj/item/mod/module/visor/on_activation() - . = ..() - if(!.) - return - if(hud_type) - var/datum/atom_hud/hud = GLOB.huds[hud_type] - hud.add_hud_to(mod.wearer) - for(var/trait in visor_traits) - ADD_TRAIT(mod.wearer, trait, MOD_TRAIT) - mod.wearer.update_sight() - -/obj/item/mod/module/visor/on_deactivation() - . = ..() - if(!.) - return - if(hud_type) - var/datum/atom_hud/hud = GLOB.huds[hud_type] - hud.remove_hud_from(mod.wearer) - for(var/trait in visor_traits) - REMOVE_TRAIT(mod.wearer, trait, MOD_TRAIT) - mod.wearer.update_sight() - -/obj/item/mod/module/visor/medhud - name = "MOD medical visor module" - desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \ - biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \ - access data such as patient files in a convenient readout. They say these also let you see behind you." - icon_state = "medhud_visor" - hud_type = DATA_HUD_MEDICAL_ADVANCED - visor_traits = list(TRAIT_MEDICAL_HUD) - -/obj/item/mod/module/visor/diaghud - name = "MOD diagnostic visor module" - desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \ - from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \ - and integrity of such. They say these also let you see behind you." - icon_state = "diaghud_visor" - hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED - visor_traits = list(TRAIT_DIAGNOSTIC_HUD) - -/obj/item/mod/module/visor/sechud - name = "MOD security visor module" - desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \ - plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \ - and generally know who to shoot. They say these also let you see behind you." - icon_state = "sechud_visor" - hud_type = DATA_HUD_SECURITY_ADVANCED - visor_traits = list(TRAIT_SECURITY_HUD) - -/obj/item/mod/module/visor/meson - name = "MOD meson visor module" - desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \ - technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \ - through walls, regardless of lighting conditions. They say these also let you see behind you." - icon_state = "meson_visor" - visor_traits = list(TRAIT_MESON_VISION, TRAIT_SUPERMATTER_MADNESS_IMMUNE) - -/obj/item/mod/module/visor/thermal - name = "MOD thermal visor module" - desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \ - the thermal radiation output of objects near the user. While it can detect the heat output of even something as \ - small as a rodent, it still produces irritating red overlay. They say these also let you see behind you." - icon_state = "thermal_visor" - visor_traits = list(TRAIT_THERMAL_VISION) - -/obj/item/mod/module/visor/night - name = "MOD night visor module" - desc = "A heads-up display installed into the visor of the suit. Typical for both civilian and military applications, \ - this allows the user to perceive their surroundings while in complete darkness, enhancing the view by tenfold; \ - yet brightening everything into a spooky green glow. They say these also let you see behind you." - icon_state = "night_visor" - visor_traits = list(TRAIT_TRUE_NIGHT_VISION) - -/obj/item/mod/module/welding - name = "MOD welding protection module" - desc = "A module installed into the visor of the suit, this projects a \ - polarized, holographic overlay in front of the user's eyes. It's rated high enough for \ - immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights." - icon_state = "welding" - complexity = 1 - incompatible_modules = list(/obj/item/mod/module/welding) - overlay_state_inactive = "module_welding" - -/obj/item/mod/module/welding/on_suit_activation() - mod.helmet.flash_protect = FLASH_PROTECTION_WELDER - -/obj/item/mod/module/welding/on_suit_deactivation() - mod.helmet.flash_protect = initial(mod.helmet.flash_protect) - -/obj/item/mod/module/t_ray - name = "MOD t-ray scan module" - desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \ - to essentially echolocate things beneath the floor, mostly cables and pipes. \ - A staple of atmospherics work, and counter-smuggling work." - icon_state = "tray" - module_type = MODULE_TOGGLE - complexity = 2 - active_power_cost = DEFAULT_CELL_DRAIN * 0.2 - incompatible_modules = list(/obj/item/mod/module/t_ray) - cooldown_time = 0.5 SECONDS - var/range = 2 - -/obj/item/mod/module/t_ray/on_active_process(delta_time) - t_ray_scan(mod.wearer, 8, range) - -#define HEALTH_SCAN "Health" -#define WOUND_SCAN "Wound" -#define CHEM_SCAN "Chemical" - -/obj/item/mod/module/health_analyzer - name = "MOD health analyzer module" - desc = "A module installed into the glove of the suit. This is a high-tech biological scanning suite, \ - allowing the user indepth information on the vitals and injuries of others even at a distance, \ - all with the flick of the wrist. Data is displayed in a convenient package on HUD in the helmet, \ - but it's up to you to do something with it." - icon_state = "health" - module_type = MODULE_ACTIVE - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN - incompatible_modules = list(/obj/item/mod/module/health_analyzer) - cooldown_time = 0.5 SECONDS - var/mode = HEALTH_SCAN - var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN) - -/obj/item/mod/module/health_analyzer/on_select_use(atom/target) - . = ..() - if(!.) - return - if(!isliving(target)) - return - switch(mode) - if(HEALTH_SCAN) - healthscan(mod.wearer, target) - if(WOUND_SCAN) - woundscan(mod.wearer, target) - if(CHEM_SCAN) - chemscan(mod.wearer, target) - drain_power(use_power_cost) - -/obj/item/mod/module/health_analyzer/get_configuration() - . = ..() - .["mode"] = add_ui_configuration("Scan Mode", "list", mode, modes) - -/obj/item/mod/module/health_analyzer/configure_edit(key, value) - switch(key) - if("mode") - mode = value - -#undef HEALTH_SCAN -#undef WOUND_SCAN -#undef CHEM_SCAN - -/obj/item/mod/module/stealth - name = "MOD prototype cloaking module" - desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \ - to bend light around the user, as well as mimetic materials to make the surface of the suit match the \ - surroundings based off sensor data. For some reason, this tech is rarely seen." - icon_state = "cloak" - module_type = MODULE_TOGGLE - complexity = 4 - active_power_cost = DEFAULT_CELL_DRAIN * 2 - use_power_cost = DEFAULT_CELL_DRAIN * 10 - incompatible_modules = list(/obj/item/mod/module/stealth) - cooldown_time = 5 SECONDS - var/bumpoff = TRUE - var/stealth_alpha = 50 - -/obj/item/mod/module/stealth/on_activation() - . = ..() - if(!.) - return - if(bumpoff) - RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, .proc/unstealth) - RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_unarmed_attack) - RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, .proc/on_bullet_act) - RegisterSignal(mod.wearer, list(COMSIG_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED), .proc/unstealth) - animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS) - drain_power(use_power_cost) - -/obj/item/mod/module/stealth/on_deactivation() - . = ..() - if(!.) - return - if(bumpoff) - UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP) - UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED)) - animate(mod.wearer, alpha = 255, time = 1.5 SECONDS) - -/obj/item/mod/module/stealth/proc/unstealth(datum/source) - SIGNAL_HANDLER - - to_chat(mod.wearer, span_warning("[src] gets discharged from contact!")) - do_sparks(2, TRUE, src) - drain_power(use_power_cost) - on_deactivation() - -/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target) - SIGNAL_HANDLER - - if(!isliving(target)) - return - unstealth(source) - -/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/projectile/projectile) - SIGNAL_HANDLER - - if(projectile.nodamage) - return - unstealth(source) - -/obj/item/mod/module/stealth/ninja - name = "MOD advanced cloaking module" - desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \ - The field has been tuned to be even more responsive and fast-acting, with enough stability to \ - continue operation of the field even if the user bumps into others. \ - The draw on the power cell has been reduced drastically, \ - making this perfect for activities like standing near sentry turrets for extended periods of time." - icon_state = "cloak_ninja" - bumpoff = FALSE - stealth_alpha = 20 - active_power_cost = DEFAULT_CELL_DRAIN - use_power_cost = DEFAULT_CELL_DRAIN * 5 - cooldown_time = 3 SECONDS - -/obj/item/mod/module/jetpack - name = "MOD ion jetpack module" - desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \ - Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \ - charge from the suit's cell. Some say this isn't Nakamura Engineering's first foray into jet-enabled suits." - icon_state = "jetpack" - module_type = MODULE_TOGGLE - complexity = 3 - active_power_cost = DEFAULT_CELL_DRAIN * 0.5 - use_power_cost = DEFAULT_CELL_DRAIN - incompatible_modules = list(/obj/item/mod/module/jetpack) - cooldown_time = 0.5 SECONDS - overlay_state_inactive = "module_jetpack" - overlay_state_active = "module_jetpack_on" - var/stabilizers = FALSE - var/full_speed = FALSE - var/datum/effect_system/trail_follow/ion/ion_trail - -/obj/item/mod/module/jetpack/Initialize(mapload) - . = ..() - ion_trail = new - ion_trail.auto_process = FALSE - ion_trail.set_up(src) - -/obj/item/mod/module/jetpack/Destroy() - QDEL_NULL(ion_trail) - return ..() - -/obj/item/mod/module/jetpack/on_activation() - . = ..() - if(!.) - return - ion_trail.start() - RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/move_react) - RegisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE, .proc/pre_move_react) - RegisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE, .proc/spacemove_react) - if(full_speed) - mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) - -/obj/item/mod/module/jetpack/on_deactivation(mob/user) - . = ..() - if(!.) - return - ion_trail.stop() - UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) - UnregisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE) - UnregisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE) - if(full_speed) - mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) - -/obj/item/mod/module/jetpack/get_configuration() - . = ..() - .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers) - -/obj/item/mod/module/jetpack/configure_edit(key, value) - switch(key) - if("stabilizers") - stabilizers = text2num(value) - -/obj/item/mod/module/jetpack/proc/move_react(mob/user) - SIGNAL_HANDLER - - if(!active)//If jet dont work, it dont work - return - if(!isturf(mod.wearer.loc))//You can't use jet in nowhere or from mecha/closet - return - if(!(mod.wearer.movement_type & FLOATING) || mod.wearer.buckled)//You don't want use jet in gravity or while buckled. - return - if(mod.wearer.pulledby)//You don't must use jet if someone pull you - return - if(mod.wearer.throwing)//You don't must use jet if you thrown - return - if(user.client && length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes. - allow_thrust() - -/obj/item/mod/module/jetpack/proc/pre_move_react(mob/user) - SIGNAL_HANDLER - - ion_trail.oldposition = get_turf(src) - -/obj/item/mod/module/jetpack/proc/spacemove_react(mob/user, movement_dir) - SIGNAL_HANDLER - - if(active && (stabilizers || movement_dir)) - return COMSIG_MOVABLE_STOP_SPACEMOVE - -/obj/item/mod/module/jetpack/proc/allow_thrust() - if(!drain_power(use_power_cost)) - return - ion_trail.generate_effect() - return TRUE - -/obj/item/mod/module/magboot - name = "MOD magnetic stability module" - desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \ - excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \ - However, these basic models do not feature computerized systems to automatically toggle them on and off, \ - so numerous users report a certain stickiness to their steps." - icon_state = "magnet" - module_type = MODULE_TOGGLE - complexity = 2 - active_power_cost = DEFAULT_CELL_DRAIN * 0.5 - incompatible_modules = list(/obj/item/mod/module/magboot) - cooldown_time = 0.5 SECONDS - var/slowdown_active = 0.5 - -/obj/item/mod/module/magboot/on_activation() - . = ..() - if(!.) - return - ADD_TRAIT(mod.wearer, TRAIT_NEGATES_GRAVITY, MOD_TRAIT) - ADD_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) - mod.slowdown += slowdown_active - mod.wearer.update_gravity(mod.wearer.has_gravity()) - mod.wearer.update_equipment_speed_mods() - -/obj/item/mod/module/magboot/on_deactivation() - . = ..() - if(!.) - return - REMOVE_TRAIT(mod.wearer, TRAIT_NEGATES_GRAVITY, MOD_TRAIT) - REMOVE_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) - mod.slowdown -= slowdown_active - mod.wearer.update_gravity(mod.wearer.has_gravity()) - mod.wearer.update_equipment_speed_mods() - -/obj/item/mod/module/magboot/advanced - name = "MOD advanced magnetic stability module" - removable = FALSE - complexity = 0 - slowdown_active = 0 - -/obj/item/mod/module/holster - name = "MOD holster module" - desc = "Based off typical storage compartments, this system allows the suit to holster a \ - standard firearm across its surface and allow for extremely quick retrieval. \ - While some users prefer the chest, others the forearm for quick deployment, \ - some law enforcement prefer the holster to extend from the thigh." - icon_state = "holster" - module_type = MODULE_USABLE - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN * 0.5 - incompatible_modules = list(/obj/item/mod/module/holster) - cooldown_time = 0.5 SECONDS - var/obj/item/gun/holstered - -/obj/item/mod/module/holster/on_use() - . = ..() - if(!.) - return - if(!holstered) - var/obj/item/gun/holding = mod.wearer.get_active_held_item() - if(!holding) - balloon_alert(mod.wearer, "nothing to holster!") - return - if(!istype(holding) || holding.w_class > WEIGHT_CLASS_BULKY) - balloon_alert(mod.wearer, "it doesn't fit!") - return - if(mod.wearer.transferItemToLoc(holding, src, FALSE, FALSE)) - holstered = holding - balloon_alert(mod.wearer, "weapon holstered") - playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) - drain_power(use_power_cost) - else if(mod.wearer.put_in_active_hand(holstered, FALSE, TRUE)) - balloon_alert(mod.wearer, "weapon drawn") - holstered = null - playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) - drain_power(use_power_cost) - else - balloon_alert(mod.wearer, "holster full!") - -/obj/item/mod/module/holster/on_uninstall() - if(holstered) - holstered.forceMove(drop_location()) - holstered = null - -/obj/item/mod/module/holster/Destroy() - QDEL_NULL(holstered) - return ..() - -/obj/item/mod/module/tether - name = "MOD emergency tether module" - desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \ - While some older models of cargo-oriented grapples have capacities of a few tons, \ - these are only capable of working in zero-gravity environments, a blessing to some Engineers." - icon_state = "tether" - module_type = MODULE_ACTIVE - complexity = 3 - use_power_cost = DEFAULT_CELL_DRAIN - incompatible_modules = list(/obj/item/mod/module/tether) - cooldown_time = 1.5 SECONDS - -/obj/item/mod/module/tether/on_use() - if(mod.wearer.has_gravity(get_turf(src))) - balloon_alert(mod.wearer, "too much gravity!") - playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE) - return FALSE - return ..() - -/obj/item/mod/module/tether/on_select_use(atom/target) - . = ..() - if(!.) - return - var/obj/projectile/tether = new /obj/projectile/tether(mod.wearer.loc) - tether.preparePixelProjectile(target, mod.wearer) - tether.firer = mod.wearer - INVOKE_ASYNC(tether, /obj/projectile.proc/fire) - drain_power(use_power_cost) - -/obj/projectile/tether - name = "tether" - icon_state = "tether_projectile" - icon = 'icons/obj/mod.dmi' - pass_flags = PASSTABLE - damage = 0 - nodamage = TRUE - range = 10 - hitsound = 'sound/weapons/batonextend.ogg' - hitsound_wall = 'sound/weapons/batonextend.ogg' - suppressed = SUPPRESSED_VERY - hit_threshhold = LATTICE_LAYER - var/line - -/obj/projectile/tether/fire(setAngle) - if(firer) - line = firer.Beam(src, "line", 'icons/obj/mod.dmi') - ..() - -/obj/projectile/tether/on_hit(atom/target) - . = ..() - if(firer) - firer.throw_at(target, 10, 1, firer, FALSE, FALSE, null, MOVE_FORCE_NORMAL, TRUE) - -/obj/projectile/tether/Destroy() - QDEL_NULL(line) - return ..() - -/obj/item/mod/module/mouthhole - name = "MOD eating apparatus module" - desc = "A favorite by Miners, this modification to the helmet utilizes a nanotechnology barrier infront of the mouth \ - to allow eating and drinking while retaining protection and atmosphere. \ - However, it will do nothing to improve the taste of a goliath steak." - icon_state = "apparatus" - complexity = 1 - incompatible_modules = list(/obj/item/mod/module/mouthhole) - overlay_state_inactive = "module_apparatus" - var/former_flags = NONE - var/former_visor_flags = NONE - -/obj/item/mod/module/mouthhole/on_install() - former_flags = mod.helmet.flags_cover - former_visor_flags = mod.helmet.visor_flags_cover - mod.helmet.flags_cover &= ~HEADCOVERSMOUTH - mod.helmet.visor_flags_cover &= ~HEADCOVERSMOUTH - -/obj/item/mod/module/mouthhole/on_uninstall() - if(!(former_flags & HEADCOVERSMOUTH)) - mod.helmet.flags_cover |= HEADCOVERSMOUTH - if(!(former_visor_flags & HEADCOVERSMOUTH)) - mod.helmet.visor_flags_cover |= HEADCOVERSMOUTH - -/obj/item/mod/module/rad_protection - name = "MOD radiation protection module" - desc = "A module utilizing polymers and reflective shielding to protect the user against ionizing radiation; \ - a common danger in space. This comes with software to notify the wearer that they're even in a radioactive area, \ - giving a voice to an otherwise silent killer." - icon_state = "radshield" - complexity = 2 - idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 - incompatible_modules = list(/obj/item/mod/module/rad_protection) - tgui_id = "rad_counter" - var/perceived_threat_level - -/obj/item/mod/module/rad_protection/on_suit_activation() - AddComponent(/datum/component/geiger_sound) - ADD_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) - RegisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION, .proc/on_pre_potential_irradiation) - for(var/obj/item/part in mod.mod_parts) - ADD_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) - -/obj/item/mod/module/rad_protection/on_suit_deactivation() - qdel(GetComponent(/datum/component/geiger_sound)) - REMOVE_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) - UnregisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION) - for(var/obj/item/part in mod.mod_parts) - REMOVE_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) - -/obj/item/mod/module/rad_protection/add_ui_data() - . = ..() - .["userradiated"] = mod.wearer ? HAS_TRAIT(mod.wearer, TRAIT_IRRADIATED) : 0 - .["usertoxins"] = mod.wearer ? mod.wearer.getToxLoss() : 0 - .["threatlevel"] = perceived_threat_level - -/obj/item/mod/module/rad_protection/proc/on_pre_potential_irradiation(datum/source, datum/radiation_pulse_information/pulse_information, insulation_to_target) - SIGNAL_HANDLER - - perceived_threat_level = get_perceived_radiation_danger(pulse_information, insulation_to_target) - addtimer(VARSET_CALLBACK(src, perceived_threat_level, null), TIME_WITHOUT_RADIATION_BEFORE_RESET, TIMER_UNIQUE | TIMER_OVERRIDE) - -/obj/item/mod/module/emp_shield - name = "MOD EMP shield module" - desc = "A field inhibitor installed into the suit, protecting it against feedback such as \ - electromagnetic pulses that would otherwise damage the electronic systems of the suit or devices on the wearer. \ - However, it will take from the suit's power to do so. Luckily, your PDA already has one of these." - icon_state = "empshield" - complexity = 1 - idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 - incompatible_modules = list(/obj/item/mod/module/emp_shield) - -/obj/item/mod/module/emp_shield/on_install() - mod.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) - -/obj/item/mod/module/emp_shield/on_uninstall() - mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) - -/obj/item/mod/module/flashlight - name = "MOD flashlight module" - desc = "A simple pair of flashlights installed on the left and right sides of the helmet, \ - useful for providing light in a variety of ranges and colors. \ - Some survivalists prefer the color green for their illumination, for reasons unknown." - icon_state = "flashlight" - module_type = MODULE_TOGGLE - complexity = 1 - active_power_cost = DEFAULT_CELL_DRAIN * 0.3 - incompatible_modules = list(/obj/item/mod/module/flashlight) - cooldown_time = 0.5 SECONDS - overlay_state_inactive = "module_light" - light_system = MOVABLE_LIGHT_DIRECTIONAL - light_color = COLOR_WHITE - light_range = 3 - light_power = 1 - light_on = FALSE - var/base_power = DEFAULT_CELL_DRAIN * 0.1 - var/min_range = 2 - var/max_range = 5 - -/obj/item/mod/module/flashlight/on_activation() - . = ..() - if(!.) - return - set_light_flags(light_flags | LIGHT_ATTACHED) - set_light_on(active) - active_power_cost = base_power * light_range - -/obj/item/mod/module/flashlight/on_deactivation() - . = ..() - if(!.) - return - set_light_flags(light_flags & ~LIGHT_ATTACHED) - set_light_on(active) - -/obj/item/mod/module/flashlight/on_process(delta_time) - . = ..() - if(!.) - return - active_power_cost = base_power * light_range - -/obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing) - . = ..() - if(!active) - return - var/mutable_appearance/light_icon = mutable_appearance('icons/mob/mod.dmi', "module_light_on", layer = standing.layer + 0.2) - light_icon.appearance_flags = RESET_COLOR - light_icon.color = light_color - . += light_icon - -/obj/item/mod/module/flashlight/get_configuration() - . = ..() - .["light_color"] = add_ui_configuration("Light Color", "color", light_color) - .["light_range"] = add_ui_configuration("Light Range", "number", light_range) - -/obj/item/mod/module/flashlight/configure_edit(key, value) - switch(key) - if("light_color") - value = input(usr, "Pick new light color", "Flashlight Color") as color|null - if(!value) - return - if(is_color_dark(value, 50)) - balloon_alert(mod.wearer, "too dark!") - return - set_light_color(value) - mod.wearer.update_inv_back() - if("light_range") - set_light_range(clamp(value, min_range, max_range)) - -/obj/item/mod/module/reagent_scanner - name = "MOD reagent scanner module" - desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \ - containers and projecting the information in an easy-to-read format on the wearer's display. \ - It cannot detect flavors, so that's up to you." - icon_state = "scanner" - module_type = MODULE_TOGGLE - complexity = 1 - active_power_cost = DEFAULT_CELL_DRAIN * 0.2 - incompatible_modules = list(/obj/item/mod/module/reagent_scanner) - cooldown_time = 0.5 SECONDS - -/obj/item/mod/module/reagent_scanner/on_activation() - . = ..() - if(!.) - return - ADD_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) - -/obj/item/mod/module/reagent_scanner/on_deactivation() - . = ..() - if(!.) - return - REMOVE_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) - -/obj/item/mod/module/reagent_scanner/advanced - name = "MOD advanced reagent scanner module" - complexity = 0 - removable = FALSE - var/explosion_detection_dist = 21 - -/obj/item/mod/module/reagent_scanner/advanced/on_activation() - . = ..() - if(!.) - return - mod.wearer.research_scanner++ - RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, .proc/sense_explosion) - -/obj/item/mod/module/reagent_scanner/advanced/on_deactivation() - . = ..() - if(!.) - return - mod.wearer.research_scanner-- - RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION) - -/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(datum/source, turf/epicenter, - devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) - SIGNAL_HANDLER - var/turf/wearer_turf = get_turf(mod.wearer) - if(wearer_turf.z != epicenter.z) - return - if(get_dist(epicenter, wearer_turf) > explosion_detection_dist) - return - to_chat(mod.wearer, span_notice("Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]")) - - -/obj/item/mod/module/dispenser - name = "MOD burger dispenser module" - desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \ - This can draw incredible amounts of power from the suit's cell to create edible organic matter in the \ - palm of the wearer's glove; however, research seemed to have entirely stopped at burgers. \ - Notably, all attempts to get it to dispense Earl Grey tea have failed." - icon_state = "dispenser" - module_type = MODULE_USABLE - complexity = 3 - use_power_cost = DEFAULT_CELL_DRAIN * 2 - incompatible_modules = list(/obj/item/mod/module/dispenser) - cooldown_time = 5 SECONDS - var/dispense_type = /obj/item/food/burger/plain - var/dispense_time = 0 SECONDS - -/obj/item/mod/module/dispenser/on_use() - . = ..() - if(!.) - return - if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod)) - balloon_alert(mod.wearer, "interrupted!") - return - var/obj/item/dispensed = new dispense_type(mod.wearer.loc) - mod.wearer.put_in_hands(dispensed) - balloon_alert(mod.wearer, "[dispensed] dispensed") - playsound(src, 'sound/machines/click.ogg', 100, TRUE) - drain_power(use_power_cost) - -/obj/item/mod/module/gps - name = "MOD internal GPS module" - desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \ - down to the exact coordinates. This information is fed to a central database viewable from the device itself, \ - though using it to help people is up to you." - icon_state = "gps" - module_type = MODULE_ACTIVE - complexity = 1 - active_power_cost = DEFAULT_CELL_DRAIN * 0.3 - device = /obj/item/gps/mod - incompatible_modules = list(/obj/item/mod/module/gps) - cooldown_time = 0.5 SECONDS - -/obj/item/gps/mod - name = "MOD internal GPS" - desc = "Common Nanotrasen technology that calcaulates the user's position from anywhere in space, down to their coordinates." - icon_state = "gps-b" - gpstag = "MOD0" - -/obj/item/mod/module/constructor - name = "MOD constructor module" - desc = "This module entirely occupies the wearer's forearm, notably causing conflict with \ - advanced arm servos meant to carry crewmembers. However, it functions as an \ - extremely advanced construction hologram scanner, as well as containing the \ - latest engineering schematics combined with inbuilt memory to help the user build walls." - icon_state = "constructor" - module_type = MODULE_USABLE - complexity = 2 - idle_power_cost = DEFAULT_CELL_DRAIN * 0.2 - use_power_cost = DEFAULT_CELL_DRAIN * 2 - incompatible_modules = list(/obj/item/mod/module/constructor, /obj/item/mod/module/quick_carry) - cooldown_time = 11 SECONDS - -/obj/item/mod/module/constructor/on_suit_activation() - ADD_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) - -/obj/item/mod/module/constructor/on_suit_deactivation() - REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) - -/obj/item/mod/module/constructor/on_use() - . = ..() - if(!.) - return - rcd_scan(src, fade_time = 10 SECONDS) - drain_power(use_power_cost) - -/obj/item/mod/module/quick_carry - name = "MOD quick carry module" - desc = "A suite of advanced servos, redirecting power from the suit's arms to help carry the wounded; \ - or simply for fun. However, Nanotrasen has locked the module's ability to assist in hand-to-hand combat." - icon_state = "carry" - complexity = 1 - idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 - incompatible_modules = list(/obj/item/mod/module/quick_carry, /obj/item/mod/module/constructor) - -/obj/item/mod/module/quick_carry/on_suit_activation() - ADD_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT) - -/obj/item/mod/module/quick_carry/on_suit_deactivation() - REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT) - -/obj/item/mod/module/quick_carry/advanced - name = "MOD advanced quick carry module" - removable = FALSE - complexity = 0 - -/obj/item/mod/module/quick_carry/on_suit_activation() - ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) - ADD_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) - -/obj/item/mod/module/quick_carry/on_suit_deactivation() - REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) - REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) - -/obj/item/mod/module/longfall - name = "MOD longfall module" - desc = "Useful for protecting both the suit and the wearer, \ - utilizing commonplace systems to convert the possible damage from a fall into kinetic charge, \ - as well as internal gyroscopes to ensure the user's safe falling. \ - Useful for mining, monorail tracks, or even skydiving!" - icon_state = "longfall" - complexity = 1 - use_power_cost = DEFAULT_CELL_DRAIN * 5 - incompatible_modules = list(/obj/item/mod/module/longfall) - -/obj/item/mod/module/longfall/on_suit_activation() - RegisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT, .proc/z_impact_react) - -/obj/item/mod/module/longfall/on_suit_deactivation() - UnregisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT) - -/obj/item/mod/module/longfall/proc/z_impact_react(datum/source, levels, turf/fell_on) - if(!drain_power(use_power_cost*levels)) - return - new /obj/effect/temp_visual/mook_dust(fell_on) - mod.wearer.Stun(levels * 1 SECONDS) - to_chat(mod.wearer, span_notice("[src] protects you from the damage!")) - return NO_Z_IMPACT_DAMAGE - -/obj/item/mod/module/thermal_regulator - name = "MOD thermal regulator module" - desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \ - flexible cooling lines. This circulates coolant at various user-controlled temperatures, \ - ensuring they're comfortable; even if they're some that like it hot." - icon_state = "regulator" - module_type = MODULE_TOGGLE - complexity = 2 - active_power_cost = DEFAULT_CELL_DRAIN * 0.3 - incompatible_modules = list(/obj/item/mod/module/thermal_regulator) - cooldown_time = 0.5 SECONDS - var/temperature_setting = BODYTEMP_NORMAL - var/min_temp = 293.15 - var/max_temp = 318.15 - -/obj/item/mod/module/thermal_regulator/get_configuration() - . = ..() - .["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C) - -/obj/item/mod/module/thermal_regulator/configure_edit(key, value) - switch(key) - if("temperature_setting") - temperature_setting = clamp(value + T0C, min_temp, max_temp) - -/obj/item/mod/module/thermal_regulator/on_active_process(delta_time) - mod.wearer.adjust_bodytemperature(get_temp_change_amount((temperature_setting - mod.wearer.bodytemperature), 0.08 * delta_time)) - -/obj/item/mod/module/injector - name = "MOD injector module" - desc = "A module installed into the wrist of the suit, this functions as a high-capacity syringe, \ - with a tip fine enough to locate the emergency injection ports on any suit of armor, \ - penetrating it with ease. Even yours." - icon_state = "injector" - module_type = MODULE_ACTIVE - complexity = 1 - active_power_cost = DEFAULT_CELL_DRAIN * 0.3 - device = /obj/item/reagent_containers/syringe/mod - incompatible_modules = list(/obj/item/mod/module/injector) - cooldown_time = 0.5 SECONDS - -/obj/item/reagent_containers/syringe/mod - name = "MOD injector syringe" - desc = "A high-capacity syringe, with a tip fine enough to locate \ - the emergency injection ports on any suit of armor, penetrating it with ease. Even yours." - icon_state = "mod_0" - base_icon_state = "mod" - amount_per_transfer_from_this = 30 - possible_transfer_amounts = list(5, 10, 15, 20, 30) - volume = 30 - inject_flags = INJECT_CHECK_PENETRATE_THICK - -/obj/item/mod/module/circuit - name = "MOD circuit adapter module" - desc = "A popular aftermarket module, seen in wide varieties with wide applications by those across the galaxy. \ - This is able to fit any sort of integrated circuit, hooking it into controls in the suit and displaying information \ - to the HUD. Useful for universal translation, or perhaps as a calculator." - module_type = MODULE_USABLE - complexity = 3 - idle_power_cost = DEFAULT_CELL_DRAIN * 0.5 - incompatible_modules = list(/obj/item/mod/module/circuit) - cooldown_time = 0.5 SECONDS - var/obj/item/integrated_circuit/circuit - -/obj/item/mod/module/circuit/Initialize(mapload) - . = ..() - circuit = new() - AddComponent(/datum/component/shell, \ - list(new /obj/item/circuit_component/mod()), \ - capacity = SHELL_CAPACITY_LARGE, \ - shell_flags = SHELL_FLAG_CIRCUIT_UNREMOVABLE, \ - starting_circuit = circuit, \ - ) - -/obj/item/mod/module/circuit/on_install() - circuit.set_cell(mod.cell) - -/obj/item/mod/module/circuit/on_uninstall() - circuit.set_cell(mod.cell) - -/obj/item/mod/module/circuit/on_suit_activation() - circuit.set_on(TRUE) - -/obj/item/mod/module/circuit/on_suit_deactivation() - circuit.set_on(FALSE) - -/obj/item/mod/module/circuit/on_use() - . = ..() - if(!.) - return - circuit.interact(mod.wearer) - -/obj/item/circuit_component/mod - display_name = "MOD" - desc = "Used to send and receive signals from a MODsuit." - - var/obj/item/mod/module/attached_module - - var/datum/port/input/module_to_select - var/datum/port/input/toggle_suit - var/datum/port/input/select_module - - var/datum/port/output/wearer - var/datum/port/output/selected_module - -/obj/item/circuit_component/mod/populate_ports() - // Input Signals - module_to_select = add_input_port("Module to Select", PORT_TYPE_STRING) - toggle_suit = add_input_port("Toggle Suit", PORT_TYPE_SIGNAL) - select_module = add_input_port("Select Module", PORT_TYPE_SIGNAL) - // States - wearer = add_output_port("Wearer", PORT_TYPE_ATOM) - selected_module = add_output_port("Selected Module", PORT_TYPE_ATOM) - -/obj/item/circuit_component/mod/register_shell(atom/movable/shell) - if(istype(shell, /obj/item/mod/module)) - attached_module = shell - RegisterSignal(attached_module, COMSIG_MOVABLE_MOVED, .proc/on_move) - -/obj/item/circuit_component/mod/unregister_shell(atom/movable/shell) - UnregisterSignal(attached_module, COMSIG_MOVABLE_MOVED) - attached_module = null - -/obj/item/circuit_component/mod/input_received(datum/port/input/port) - var/obj/item/mod/module/module - for(var/obj/item/mod/module/potential_module as anything in attached_module.mod.modules) - if(potential_module.name == module_to_select.value) - module = potential_module - if(COMPONENT_TRIGGERED_BY(toggle_suit, port)) - INVOKE_ASYNC(attached_module.mod, /obj/item/mod/control.proc/toggle_activate, attached_module.mod.wearer) - if(module && COMPONENT_TRIGGERED_BY(select_module, port)) - INVOKE_ASYNC(module, /obj/item/mod/module.proc/on_select) - -/obj/item/circuit_component/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced) - SIGNAL_HANDLER - if(istype(source.loc, /obj/item/mod/control)) - RegisterSignal(source.loc, COMSIG_MOD_MODULE_SELECTED, .proc/on_module_select) - RegisterSignal(source.loc, COMSIG_ITEM_EQUIPPED, .proc/equip_check) - equip_check() - else if(istype(old_loc, /obj/item/mod/control)) - UnregisterSignal(old_loc, list(COMSIG_MOD_MODULE_SELECTED, COMSIG_ITEM_EQUIPPED)) - selected_module.set_output(null) - wearer.set_output(null) - -/obj/item/circuit_component/mod/proc/on_module_select() - SIGNAL_HANDLER - selected_module.set_output(attached_module.mod.selected_module) - -/obj/item/circuit_component/mod/proc/equip_check() - SIGNAL_HANDLER - - if(!attached_module.mod?.wearer) - return - wearer.set_output(attached_module.mod.wearer) - -/obj/item/mod/module/clamp - name = "MOD hydraulic clamp module" - desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \ - However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \ - A lot of people would say that loading cargo is a dull job, but you could not disagree more." - icon_state = "clamp" - module_type = MODULE_ACTIVE - complexity = 3 - use_power_cost = DEFAULT_CELL_DRAIN - incompatible_modules = list(/obj/item/mod/module/clamp) - cooldown_time = 0.5 SECONDS - var/max_crates = 5 - var/list/stored_crates = list() - -/obj/item/mod/module/clamp/on_select_use(atom/target) - . = ..() - if(!.) - return - if(!mod.wearer.Adjacent(target)) - return - if(istype(target, /obj/structure/closet/crate)) - var/atom/movable/picked_crate = target - if(length(stored_crates) >= max_crates) - balloon_alert(mod.wearer, "too many crates!") - return - if(!do_after(mod.wearer, 1 SECONDS, target = target)) - balloon_alert(mod.wearer, "interrupted!") - return - stored_crates += picked_crate - picked_crate.forceMove(src) - balloon_alert(mod.wearer, "picked up [picked_crate]") - playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) - drain_power(use_power_cost) - else if(length(stored_crates)) - var/turf/target_turf = get_turf(target) - if(target_turf.is_blocked_turf()) - return - if(!do_after(mod.wearer, 1 SECONDS, target = target)) - balloon_alert(mod.wearer, "interrupted!") - return - if(target_turf.is_blocked_turf()) - return - var/atom/movable/dropped_crate = pop(stored_crates) - dropped_crate.forceMove(target_turf) - balloon_alert(mod.wearer, "dropped [dropped_crate]") - playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) - drain_power(use_power_cost) - -/obj/item/mod/module/clamp/on_uninstall() - for(var/atom/movable/crate as anything in stored_crates) - crate.forceMove(drop_location()) - stored_crates -= crate - -/obj/item/mod/module/bikehorn - name = "MOD bike horn module" - desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \ - precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound." - icon_state = "bikehorn" - module_type = MODULE_USABLE - complexity = 1 - use_power_cost = DEFAULT_CELL_DRAIN - incompatible_modules = list(/obj/item/mod/module/bikehorn) - cooldown_time = 1 SECONDS - -/obj/item/mod/module/bikehorn/on_use() - . = ..() - if(!.) - return - playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE) - drain_power(use_power_cost) - -/obj/item/mod/module/drill - name = "MOD drill module" - desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \ - your drill is surely the one that both pierces and creates the heavens." - icon_state = "drill" - module_type = MODULE_ACTIVE - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN - incompatible_modules = list(/obj/item/mod/module/drill) - cooldown_time = 0.5 SECONDS - -/obj/item/mod/module/drill/on_activation() - . = ..() - if(!.) - return - RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, .proc/bump_mine) - -/obj/item/mod/module/drill/on_deactivation() - . = ..() - if(!.) - return - UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP) - -/obj/item/mod/module/drill/on_select_use(atom/target) - . = ..() - if(!.) - return - if(!mod.wearer.Adjacent(target)) - return - if(istype(target, /turf/closed/mineral)) - var/turf/closed/mineral/mineral_turf = target - mineral_turf.gets_drilled(mod.wearer) - drain_power(use_power_cost) - -/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity) - SIGNAL_HANDLER - if(!istype(bumped_into, /turf/closed/mineral) || !drain_power(use_power_cost)) - return - var/turf/closed/mineral/mineral_turf = bumped_into - mineral_turf.gets_drilled(mod.wearer) - return COMPONENT_CANCEL_ATTACK_CHAIN - -/obj/item/mod/module/orebag - name = "MOD ore bag module" - desc = "An integrated ore storage system installed into the suit, \ - this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \ - It's recommended by Nakamura Engineering to actually deposit that ore at local refineries." - icon_state = "ore" - module_type = MODULE_USABLE - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN * 0.2 - incompatible_modules = list(/obj/item/mod/module/orebag) - cooldown_time = 0.5 SECONDS - var/list/ores = list() - -/obj/item/mod/module/orebag/on_equip() - RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/ore_pickup) - -/obj/item/mod/module/orebag/on_unequip() - UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) - -/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced) - SIGNAL_HANDLER - - for(var/obj/item/stack/ore/ore in get_turf(mod.wearer)) - INVOKE_ASYNC(src, .proc/move_ore, ore) - playsound(src, "rustle", 50, TRUE) - -/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore) - for(var/obj/item/stack/stored_ore as anything in ores) - if(!ore.can_merge(stored_ore)) - continue - ore.merge(stored_ore) - if(QDELETED(ore)) - return - break - ore.forceMove(src) - ores += ore - -/obj/item/mod/module/orebag/on_use() - . = ..() - if(!.) - return - for(var/obj/item/ore as anything in ores) - ore.forceMove(drop_location()) - ores -= ore - drain_power(use_power_cost) - -/obj/item/mod/module/microwave_beam - name = "MOD microwave beam module" - desc = "An oddly domestic device, this module is installed into the user's palm, \ - hooking up with culinary scanners located in the helmet to blast food with precise microwave radiation, \ - allowing them to cook food from a distance, with the greatest of ease. Not recommended for use against grapes." - icon_state = "microwave_beam" - module_type = MODULE_ACTIVE - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN * 5 - incompatible_modules = list(/obj/item/mod/module/microwave_beam) - cooldown_time = 10 SECONDS - -/obj/item/mod/module/microwave_beam/on_select_use(atom/target) - . = ..() - if(!.) - return - if(!istype(target, /obj/item)) - return - if(!isturf(target.loc)) - balloon_alert(mod.wearer, "must be on the floor!") - return - var/obj/item/microwave_target = target - var/datum/effect_system/spark_spread/spark_effect = new() - spark_effect.set_up(2, 1, mod.wearer) - spark_effect.start() - mod.wearer.Beam(target,icon_state="lightning[rand(1,12)]", time = 5) - if(microwave_target.microwave_act()) - playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, FALSE) - else - balloon_alert(mod.wearer, "can't be microwaved!") - var/datum/effect_system/spark_spread/spark_effect_two = new() - spark_effect_two.set_up(2, 1, microwave_target) - spark_effect_two.start() - drain_power(use_power_cost) - -/obj/item/mod/module/organ_thrower - name = "MOD organ thrower module" - desc = "A device recovered from a crashed Interdyne Pharmaceuticals vessel, \ - this module has been unearthed for better or for worse. \ - It's an arm-mounted device utilizing technology similar to modern-day part replacers, \ - capable of storing and inserting organs into open patients. \ - It's recommended by the DeForest Medical Corporation to not inform patients it has been used." - icon_state = "organ_thrower" - module_type = MODULE_ACTIVE - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN - incompatible_modules = list(/obj/item/mod/module/organ_thrower) - cooldown_time = 0.5 SECONDS - var/max_organs = 5 - var/organ_list = list() - -/obj/item/mod/module/organ_thrower/on_select_use(atom/target) - . = ..() - if(!.) - return - var/mob/living/carbon/human/wearer_human = mod.wearer - if(istype(target, /obj/item/organ)) - if(!wearer_human.Adjacent(target)) - return - var/atom/movable/organ = target - if(length(organ_list) >= max_organs) - balloon_alert(mod.wearer, "too many organs!") - return - organ_list += organ - organ.forceMove(src) - balloon_alert(mod.wearer, "picked up [organ]") - playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) - drain_power(use_power_cost) - return - if(!length(organ_list)) - return - var/atom/movable/fired_organ = pop(organ_list) - var/obj/projectile/organ/projectile = new /obj/projectile/organ(mod.wearer.loc, fired_organ) - projectile.preparePixelProjectile(target, mod.wearer) - projectile.firer = mod.wearer - playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) - INVOKE_ASYNC(projectile, /obj/projectile.proc/fire) - drain_power(use_power_cost) - -/obj/projectile/organ - name = "organ" - damage = 0 - nodamage = TRUE - hitsound = 'sound/effects/attackblob.ogg' - hitsound_wall = 'sound/effects/attackblob.ogg' - var/obj/item/organ/organ - -/obj/projectile/organ/Initialize(mapload, obj/item/stored_organ) - . = ..() - if(!stored_organ) - return INITIALIZE_HINT_QDEL - appearance = stored_organ.appearance - stored_organ.forceMove(src) - organ = stored_organ - -/obj/projectile/organ/Destroy() - organ = null - return ..() - -/obj/projectile/organ/on_hit(atom/target) - . = ..() - if(!ishuman(target)) - organ.forceMove(drop_location()) - organ = null - return - var/mob/living/carbon/human/organ_receiver = target - var/succeed = FALSE - if(organ_receiver.surgeries.len) - for(var/datum/surgery/procedure as anything in organ_receiver.surgeries) - if(procedure.location != organ.zone) - continue - if(!istype(procedure, /datum/surgery/organ_manipulation)) - continue - var/datum/surgery_step/surgery_step = procedure.get_surgery_step() - if(!istype(surgery_step, /datum/surgery_step/manipulate_organs)) - continue - succeed = TRUE - break - if(succeed) - var/list/organs_to_boot_out = organ_receiver.getorganslot(organ.slot) - for(var/obj/item/organ/organ_evacced as anything in organs_to_boot_out) - if(organ_evacced.organ_flags & ORGAN_UNREMOVABLE) - continue - organ_evacced.Remove(target) - organ_evacced.forceMove(get_turf(target)) - organ.Insert(target) - else - organ.forceMove(drop_location()) - organ = null - -/obj/item/mod/module/pathfinder - name = "MOD pathfinder module" - desc = "This module, brought to you by Nakamura Engineering, has two components. \ - The first component is a series of thrusters and a computerized location subroutine installed into the \ - very control unit of the suit, allowing it flight at highway speeds, \ - and to be able to locate the second part of the system; \ - a pathfinding implant installed into the base of the user's spine, \ - broadcasting their location to the suit and allowing them to recall it to their back at any time. \ - Nakamura Engineering swears up and down there's airbrakes." - icon_state = "pathfinder" - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN * 10 - incompatible_modules = list(/obj/item/mod/module/pathfinder) - var/obj/item/implant/mod/implant - -/obj/item/mod/module/pathfinder/Initialize(mapload) - . = ..() - implant = new(src) - -/obj/item/mod/module/pathfinder/Destroy() - implant = null - return ..() - -/obj/item/mod/module/pathfinder/examine(mob/user) - . = ..() - if(implant) - . += span_notice("Use it on a human to implant them.") - else - . += span_warning("The implant is missing.") - -/obj/item/mod/module/pathfinder/attack(mob/living/target, mob/living/user, params) - if(!ishuman(target) || !implant) - return - if(!do_after(user, 1.5 SECONDS, target = target)) - balloon_alert(user, "interrupted!") - return - if(!implant.implant(target, user)) - balloon_alert(user, "can't implant!") - return - if(target == user) - to_chat(user, span_notice("You implant yourself with [implant].")) - else - target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant].")) - playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6) - icon_state = "pathfinder_empty" - implant = null - -/obj/item/mod/module/pathfinder/proc/attach(mob/living/user) - if(!ishuman(user)) - return - var/mob/living/carbon/human/human_user = user - if(human_user.back && !human_user.dropItemToGround(human_user.back)) - return - if(!human_user.equip_to_slot_if_possible(mod, mod.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) - return - human_user.update_action_buttons(TRUE) - balloon_alert(human_user, "[mod] attached") - playsound(mod, 'sound/machines/ping.ogg', 50, TRUE) - drain_power(use_power_cost) - -/obj/item/implant/mod - name = "MOD pathfinder implant" - desc = "Lets you recall a MODsuit to you at any time." - actions_types = list(/datum/action/item_action/mod_recall) - var/obj/item/mod/module/pathfinder/module - var/image/jet_icon - -/obj/item/implant/mod/Initialize(mapload) - . = ..() - if(!istype(loc, /obj/item/mod/module/pathfinder)) - return INITIALIZE_HINT_QDEL - module = loc - jet_icon = image(icon = 'icons/obj/mod.dmi', icon_state = "mod_jet", layer = LOW_ITEM_LAYER) - -/obj/item/implant/mod/Destroy() - if(module?.mod?.ai_controller) - end_recall(successful = FALSE) - module = null - jet_icon = null - return ..() - -/obj/item/implant/mod/get_data() - var/dat = {"Implant Specifications:
- Name: Nakamura Engineering Pathfinder Implant
- Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"} - return dat - -/obj/item/implant/mod/proc/recall() - if(!module?.mod) - balloon_alert(imp_in, "no connected suit!") - return FALSE - if(module.mod.open) - balloon_alert(imp_in, "suit is open!") - return FALSE - if(module.mod.ai_controller) - balloon_alert(imp_in, "already in transit!") - return FALSE - if(ismob(get_atom_on_turf(module.mod))) - balloon_alert(imp_in, "already on someone!") - return FALSE - if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE) - balloon_alert(imp_in, "too far away!") - return FALSE - var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod) - module.mod.ai_controller = mod_ai - mod_ai.current_movement_target = imp_in - mod_ai.blackboard[BB_MOD_TARGET] = imp_in - mod_ai.blackboard[BB_MOD_IMPLANT] = src - module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP - module.mod.AddElement(/datum/element/movetype_handler) - ADD_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT) - animate(module.mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y) - module.mod.add_overlay(jet_icon) - RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, .proc/on_move) - balloon_alert(imp_in, "suit recalled") - return TRUE - -/obj/item/implant/mod/proc/end_recall(successful = TRUE) - if(!module?.mod) - return - QDEL_NULL(module.mod.ai_controller) - module.mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP - REMOVE_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT) - module.mod.RemoveElement(/datum/element/movetype_handler) - module.mod.cut_overlay(jet_icon) - module.mod.transform = matrix() - UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED) - if(!successful) - balloon_alert(imp_in, "suit lost connection!") - -/obj/item/implant/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced) - SIGNAL_HANDLER - - var/matrix/mod_matrix = matrix() - mod_matrix.Turn(get_angle(source, imp_in)) - source.transform = mod_matrix - -/datum/action/item_action/mod_recall - name = "Recall MOD" - desc = "Recall a MODsuit anyplace, anytime." - check_flags = AB_CHECK_CONSCIOUS - background_icon_state = "bg_tech_blue" - icon_icon = 'icons/mob/actions/actions_mod.dmi' - button_icon_state = "recall" - COOLDOWN_DECLARE(recall_cooldown) - var/obj/item/implant/mod/implant - -/datum/action/item_action/mod_recall/New(Target) - ..() - implant = Target - -/datum/action/item_action/mod_recall/Trigger() - . = ..() - if(!.) - return - if(!COOLDOWN_FINISHED(src, recall_cooldown)) - implant.balloon_alert(implant.imp_in, "on cooldown!") - return - if(implant.recall()) - COOLDOWN_START(src, recall_cooldown, 15 SECONDS) - -/obj/item/mod/module/dna_lock - name = "MOD DNA lock module" - desc = "A module which engages with the various locks and seals tied to the suit's systems, \ - enabling it to only be worn by someone corresponding with the user's exact DNA profile; \ - however, this incredibly sensitive module is shorted out by EMPs. Luckily, cloning has been outlawed." - icon_state = "dnalock" - module_type = MODULE_USABLE - complexity = 2 - use_power_cost = DEFAULT_CELL_DRAIN * 3 - incompatible_modules = list(/obj/item/mod/module/dna_lock) - cooldown_time = 0.5 SECONDS - var/dna = null - -/obj/item/mod/module/dna_lock/on_install() - RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/on_mod_activation) - RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, .proc/on_emp) - RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, .proc/on_emag) - -/obj/item/mod/module/dna_lock/on_uninstall() - UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) - UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT) - UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT) - -/obj/item/mod/module/dna_lock/on_use() - . = ..() - if(!.) - return - dna = mod.wearer.dna.unique_enzymes - balloon_alert(mod.wearer, "dna updated") - drain_power(use_power_cost) - -/obj/item/mod/module/dna_lock/emp_act(severity) - . = ..() - if(. & EMP_PROTECT_SELF) - return - on_emp(src, severity) - -/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card) - . = ..() - on_emag(src, user, emag_card) - -/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity) - SIGNAL_HANDLER - - dna = null - -/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card) - SIGNAL_HANDLER - - dna = null - -/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source) - SIGNAL_HANDLER - - if(!dna || (mod.wearer.has_dna() && mod.wearer.dna.unique_enzymes == dna)) - return - balloon_alert(mod.wearer, "dna locked!") - return MOD_CANCEL_ACTIVATE - -/obj/item/mod/module/armor_booster - name = "MOD armor booster module" - desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \ - giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \ - However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \ - so this extra armor provides zero ability for extravehicular activity while deployed." - icon_state = "armor_booster" - module_type = MODULE_TOGGLE - active_power_cost = DEFAULT_CELL_DRAIN * 0.3 - removable = FALSE - incompatible_modules = list(/obj/item/mod/module/armor_booster) - cooldown_time = 0.5 SECONDS - overlay_state_inactive = "module_armorbooster_off" - overlay_state_active = "module_armorbooster_on" - var/remove_pressure_protection = TRUE - var/added_slowdown = -0.5 - var/list/armor_values = list(MELEE = 40, BULLET = 50, LASER = 30, ENERGY = 40) - var/list/spaceproofed = list() - -/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing) - overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" - overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]" - return ..() - -/obj/item/mod/module/armor_booster/on_activation() - . = ..() - if(!.) - return - playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - mod.slowdown += added_slowdown - mod.wearer.update_equipment_speed_mods() - var/list/parts = mod.mod_parts + mod - for(var/obj/item/part as anything in parts) - part.armor = part.armor.modifyRating(arglist(armor_values)) - if(!remove_pressure_protection || !isclothing(part)) - continue - var/obj/item/clothing/clothing_part = part - if(clothing_part.clothing_flags & STOPSPRESSUREDAMAGE) - clothing_part.clothing_flags &= ~STOPSPRESSUREDAMAGE - clothing_part.heat_protection = NONE - clothing_part.cold_protection = NONE - spaceproofed[clothing_part] = TRUE - -/obj/item/mod/module/armor_booster/on_deactivation() - . = ..() - if(!.) - return - playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - mod.slowdown -= added_slowdown - mod.wearer.update_equipment_speed_mods() - var/list/parts = mod.mod_parts + mod - var/list/removed_armor = armor_values.Copy() - for(var/armor_type in removed_armor) - removed_armor[armor_type] = -removed_armor[armor_type] - for(var/obj/item/part as anything in parts) - part.armor = part.armor.modifyRating(arglist(removed_armor)) - if(!remove_pressure_protection || !isclothing(part)) - continue - var/obj/item/clothing/clothing_part = part - if(spaceproofed[clothing_part]) - clothing_part.clothing_flags |= STOPSPRESSUREDAMAGE - clothing_part.heat_protection = initial(clothing_part.heat_protection) - clothing_part.cold_protection = initial(clothing_part.cold_protection) - spaceproofed = list() - -/obj/item/mod/module/armor_booster/elite - name = "MOD elite armor booster module" - armor_values = list(MELEE = 60, BULLET = 60, LASER = 50, ENERGY = 60) - added_slowdown = -0.25 - -/obj/item/mod/module/energy_shield - name = "MOD energy shield module" - desc = "A personal, protective forcefield typically seen in military applications. \ - This advanced deflector shield is essentially a scaled down version of those seen on starships, \ - and the power cost can be an easy indicator of this. However, it is capable of blocking nearly any incoming attack, \ - though with its' low amount of separate charges, the user remains mortal." - icon_state = "energy_shield" - complexity = 3 - idle_power_cost = DEFAULT_CELL_DRAIN * 0.5 - use_power_cost = DEFAULT_CELL_DRAIN * 2 - incompatible_modules = list(/obj/item/mod/module/energy_shield) - var/max_charges = 3 - var/recharge_start_delay = 20 SECONDS - var/charge_increment_delay = 1 SECONDS - var/charge_recovery = 1 - var/lose_multiple_charges = FALSE - var/recharge_path = null - var/shield_icon_file = 'icons/effects/effects.dmi' - var/shield_icon = "shield-red" - var/charges - -/obj/item/mod/module/energy_shield/Initialize(mapload) - . = ..() - charges = max_charges - -/obj/item/mod/module/energy_shield/on_suit_activation() - mod.AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \ - charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, recharge_path = recharge_path, starting_charges = charges, shield_icon_file = shield_icon_file, shield_icon = shield_icon) - RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, .proc/shield_reaction) - -/obj/item/mod/module/energy_shield/on_suit_deactivation() - var/datum/component/shielded/shield = mod.GetComponent(/datum/component/shielded) - charges = shield.current_charges - qdel(shield) - UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS) - -/obj/item/mod/module/energy_shield/proc/shield_reaction(mob/living/carbon/human/owner, atom/movable/hitby, damage = 0, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) - if(SEND_SIGNAL(mod, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, 0, damage, attack_type) & COMPONENT_HIT_REACTION_BLOCK) - drain_power(use_power_cost) - return SHIELD_BLOCK - return NONE - -/obj/item/mod/module/energy_shield/wizard - name = "MOD battlemage shield module" - desc = "The caster wielding this spell gains a visible barrier around them, channeling arcane power through \ - specialized runes engraved onto the surface of the suit to generate a wall of force. \ - This shield can perfectly nullify attacks ranging from high-caliber rifles to magic missiles, \ - though can also be drained by more mundane attacks. It will not protect the caster from social ridicule." - icon_state = "battlemage_shield" - idle_power_cost = DEFAULT_CELL_DRAIN * 0 //magic - use_power_cost = DEFAULT_CELL_DRAIN * 0 //magic too - max_charges = 15 - recharge_start_delay = 0 SECONDS - charge_recovery = 8 - shield_icon_file = 'icons/effects/magic.dmi' - shield_icon = "mageshield" - recharge_path = /obj/item/wizard_armour_charge - -/obj/item/mod/module/plasma_stabilizer - name = "MOD plasma stabilizer module" - desc = "This system essentially forms an atmosphere of its' own inside the suit, \ - safely ejecting oxygen from the inside and allowing the wearer, a plasmaman, \ - to have their internal plasma circulate around them somewhat like a sauna. \ - This prevents them from self-igniting, and leads to greater comfort overall. \ - The purple glass of the visor seems to be constructed for nostalgic purposes." - icon_state = "plasma_stabilizer" - complexity = 1 - idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 - incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer) - overlay_state_inactive = "module_plasma" - -/obj/item/mod/module/plasma_stabilizer/on_equip() - ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT) - -/obj/item/mod/module/plasma_stabilizer/on_unequip() - REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT) - -/obj/item/mod/module/anti_magic - name = "MOD magic nullifier module" - desc = "A series of obsidian rods installed into critical points around the suit, \ - vibrated at a certain low frequency to enable them to resonate. \ - This creates a low-range, yet strong, magic nullification field around the user, \ - aided by a full replacement of the suit's normal coolant with holy water. \ - Spells will spall right off this field, though it'll do nothing to help others believe you about all this." - icon_state = "magic_nullifier" - removable = FALSE - incompatible_modules = list(/obj/item/mod/module/anti_magic) - -/obj/item/mod/module/anti_magic/on_suit_activation() - ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT) - ADD_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT) - -/obj/item/mod/module/anti_magic/on_suit_deactivation() - REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT) - REMOVE_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT) - -/obj/item/mod/module/anti_magic/wizard - name = "MOD magic neutralizer module" - desc = "The caster wielding this spell gains an invisible barrier around them, channeling arcane power through \ - specialized runes engraved onto the surface of the suit to generate anti-magic field. \ - The field will neutralize all magic that comes into contact with the user. \ - It will not protect the caster from social ridicule." - icon_state = "magic_neutralizer" - -/obj/item/mod/module/anti_magic/wizard/on_suit_activation() - ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT) - -/obj/item/mod/module/anti_magic/wizard/on_suit_deactivation() - REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT) - -/obj/item/mod/module/kinesis //TODO POST-MERGE MAKE NOT SUCK ASS, MAKE BALLER AS FUCK - name = "MOD kinesis module" - desc = "A modular plug-in to the forearm, this module was presumed lost for many years, \ - despite the suits it used to be mounted on still seeing some circulation. \ - This piece of technology allows the user to generate precise anti-gravity fields, \ - letting them move objects as small as a titanium rod to as large as industrial machinery. \ - Oddly enough, it doesn't seem to work on living creatures." - icon_state = "kinesis" -// module_type = MODULE_ACTIVE - module_type = MODULE_TOGGLE -// complexity = 3 - complexity = 0 - active_power_cost = DEFAULT_CELL_DRAIN*0.75 -// use_power_cost = DEFAULT_CELL_DRAIN*3 - removable = FALSE - incompatible_modules = list(/obj/item/mod/module/kinesis) - cooldown_time = 0.5 SECONDS - var/has_tk = FALSE - -/obj/item/mod/module/kinesis/on_activation() - . = ..() - if(!.) - return - if(mod.wearer.dna.check_mutation(TK)) - has_tk = TRUE - else - mod.wearer.dna.add_mutation(TK) - -/obj/item/mod/module/kinesis/on_deactivation() - . = ..() - if(!.) - return - if(has_tk) - has_tk = FALSE - return - mod.wearer.dna.remove_mutation(TK) - -/obj/item/mod/module/insignia - name = "MOD insignia module" - desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \ - the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \ - they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \ - color patterns to and from the suit." - icon_state = "insignia" - removable = FALSE - incompatible_modules = list(/obj/item/mod/module/insignia) - overlay_state_inactive = "insignia" - -/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing) - overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" - . = ..() - for(var/mutable_appearance/appearance as anything in .) - appearance.color = color - -/obj/item/mod/module/insignia/commander - color = "#4980a5" - -/obj/item/mod/module/insignia/security - color = "#b30d1e" - -/obj/item/mod/module/insignia/engineer - color = "#e9c80e" - -/obj/item/mod/module/insignia/medic - color = "#ebebf5" - -/obj/item/mod/module/insignia/janitor - color = "#7925c7" - -/obj/item/mod/module/insignia/clown - color = "#ff1fc7" - -/obj/item/mod/module/insignia/chaplain - color = "#f0a00c" diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm new file mode 100644 index 00000000000..4412b1dea05 --- /dev/null +++ b/code/modules/mod/modules/modules_antag.dm @@ -0,0 +1,208 @@ +//Antag modules for MODsuits + +//Armor Booster + +/obj/item/mod/module/armor_booster + name = "MOD armor booster module" + desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \ + giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \ + However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \ + so this extra armor provides zero ability for extravehicular activity while deployed." + icon_state = "armor_booster" + module_type = MODULE_TOGGLE + active_power_cost = DEFAULT_CELL_DRAIN * 0.3 + removable = FALSE + incompatible_modules = list(/obj/item/mod/module/armor_booster) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_armorbooster_off" + overlay_state_active = "module_armorbooster_on" + var/remove_pressure_protection = TRUE + var/added_slowdown = -0.5 + var/list/armor_values = list(MELEE = 40, BULLET = 50, LASER = 30, ENERGY = 40) + var/list/spaceproofed = list() + +/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing) + overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" + overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]" + return ..() + +/obj/item/mod/module/armor_booster/on_activation() + . = ..() + if(!.) + return + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + mod.slowdown += added_slowdown + mod.wearer.update_equipment_speed_mods() + var/list/parts = mod.mod_parts + mod + for(var/obj/item/part as anything in parts) + part.armor = part.armor.modifyRating(arglist(armor_values)) + if(!remove_pressure_protection || !isclothing(part)) + continue + var/obj/item/clothing/clothing_part = part + if(clothing_part.clothing_flags & STOPSPRESSUREDAMAGE) + clothing_part.clothing_flags &= ~STOPSPRESSUREDAMAGE + clothing_part.heat_protection = NONE + clothing_part.cold_protection = NONE + spaceproofed[clothing_part] = TRUE + +/obj/item/mod/module/armor_booster/on_deactivation() + . = ..() + if(!.) + return + playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + mod.slowdown -= added_slowdown + mod.wearer.update_equipment_speed_mods() + var/list/parts = mod.mod_parts + mod + var/list/removed_armor = armor_values.Copy() + for(var/armor_type in removed_armor) + removed_armor[armor_type] = -removed_armor[armor_type] + for(var/obj/item/part as anything in parts) + part.armor = part.armor.modifyRating(arglist(removed_armor)) + if(!remove_pressure_protection || !isclothing(part)) + continue + var/obj/item/clothing/clothing_part = part + if(spaceproofed[clothing_part]) + clothing_part.clothing_flags |= STOPSPRESSUREDAMAGE + clothing_part.heat_protection = initial(clothing_part.heat_protection) + clothing_part.cold_protection = initial(clothing_part.cold_protection) + spaceproofed = list() + +/obj/item/mod/module/armor_booster/elite + name = "MOD elite armor booster module" + armor_values = list(MELEE = 60, BULLET = 60, LASER = 50, ENERGY = 60) + added_slowdown = -0.25 + +//Energy Shield + +/obj/item/mod/module/energy_shield + name = "MOD energy shield module" + desc = "A personal, protective forcefield typically seen in military applications. \ + This advanced deflector shield is essentially a scaled down version of those seen on starships, \ + and the power cost can be an easy indicator of this. However, it is capable of blocking nearly any incoming attack, \ + though with its' low amount of separate charges, the user remains mortal." + icon_state = "energy_shield" + complexity = 3 + idle_power_cost = DEFAULT_CELL_DRAIN * 0.5 + use_power_cost = DEFAULT_CELL_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/energy_shield) + var/max_charges = 3 + var/recharge_start_delay = 20 SECONDS + var/charge_increment_delay = 1 SECONDS + var/charge_recovery = 1 + var/lose_multiple_charges = FALSE + var/recharge_path = null + var/shield_icon_file = 'icons/effects/effects.dmi' + var/shield_icon = "shield-red" + var/charges + +/obj/item/mod/module/energy_shield/Initialize(mapload) + . = ..() + charges = max_charges + +/obj/item/mod/module/energy_shield/on_suit_activation() + mod.AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \ + charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, recharge_path = recharge_path, starting_charges = charges, shield_icon_file = shield_icon_file, shield_icon = shield_icon) + RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, .proc/shield_reaction) + +/obj/item/mod/module/energy_shield/on_suit_deactivation() + var/datum/component/shielded/shield = mod.GetComponent(/datum/component/shielded) + charges = shield.current_charges + qdel(shield) + UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS) + +/obj/item/mod/module/energy_shield/proc/shield_reaction(mob/living/carbon/human/owner, atom/movable/hitby, damage = 0, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) + if(SEND_SIGNAL(mod, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, 0, damage, attack_type) & COMPONENT_HIT_REACTION_BLOCK) + drain_power(use_power_cost) + return SHIELD_BLOCK + return NONE + +/obj/item/mod/module/energy_shield/wizard + name = "MOD battlemage shield module" + desc = "The caster wielding this spell gains a visible barrier around them, channeling arcane power through \ + specialized runes engraved onto the surface of the suit to generate a wall of force. \ + This shield can perfectly nullify attacks ranging from high-caliber rifles to magic missiles, \ + though can also be drained by more mundane attacks. It will not protect the caster from social ridicule." + icon_state = "battlemage_shield" + idle_power_cost = DEFAULT_CELL_DRAIN * 0 //magic + use_power_cost = DEFAULT_CELL_DRAIN * 0 //magic too + max_charges = 15 + recharge_start_delay = 0 SECONDS + charge_recovery = 8 + shield_icon_file = 'icons/effects/magic.dmi' + shield_icon = "mageshield" + recharge_path = /obj/item/wizard_armour_charge + +//Magic Nullifier + +/obj/item/mod/module/anti_magic + name = "MOD magic nullifier module" + desc = "A series of obsidian rods installed into critical points around the suit, \ + vibrated at a certain low frequency to enable them to resonate. \ + This creates a low-range, yet strong, magic nullification field around the user, \ + aided by a full replacement of the suit's normal coolant with holy water. \ + Spells will spall right off this field, though it'll do nothing to help others believe you about all this." + icon_state = "magic_nullifier" + removable = FALSE + incompatible_modules = list(/obj/item/mod/module/anti_magic) + +/obj/item/mod/module/anti_magic/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT) + +/obj/item/mod/module/anti_magic/on_suit_deactivation() + REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT) + +/obj/item/mod/module/anti_magic/wizard + name = "MOD magic neutralizer module" + desc = "The caster wielding this spell gains an invisible barrier around them, channeling arcane power through \ + specialized runes engraved onto the surface of the suit to generate anti-magic field. \ + The field will neutralize all magic that comes into contact with the user. \ + It will not protect the caster from social ridicule." + icon_state = "magic_neutralizer" + +/obj/item/mod/module/anti_magic/wizard/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT) + +/obj/item/mod/module/anti_magic/wizard/on_suit_deactivation() + REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT) + +//Insignia + +/obj/item/mod/module/insignia + name = "MOD insignia module" + desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \ + the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \ + they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \ + color patterns to and from the suit." + icon_state = "insignia" + removable = FALSE + incompatible_modules = list(/obj/item/mod/module/insignia) + overlay_state_inactive = "insignia" + +/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing) + overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" + . = ..() + for(var/mutable_appearance/appearance as anything in .) + appearance.color = color + +/obj/item/mod/module/insignia/commander + color = "#4980a5" + +/obj/item/mod/module/insignia/security + color = "#b30d1e" + +/obj/item/mod/module/insignia/engineer + color = "#e9c80e" + +/obj/item/mod/module/insignia/medic + color = "#ebebf5" + +/obj/item/mod/module/insignia/janitor + color = "#7925c7" + +/obj/item/mod/module/insignia/clown + color = "#ff1fc7" + +/obj/item/mod/module/insignia/chaplain + color = "#f0a00c" diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm new file mode 100644 index 00000000000..294c2a21861 --- /dev/null +++ b/code/modules/mod/modules/modules_engineering.dm @@ -0,0 +1,207 @@ +//Engineering modules for MODsuits + +//Welding Protection + +/obj/item/mod/module/welding + name = "MOD welding protection module" + desc = "A module installed into the visor of the suit, this projects a \ + polarized, holographic overlay in front of the user's eyes. It's rated high enough for \ + immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights." + icon_state = "welding" + complexity = 1 + incompatible_modules = list(/obj/item/mod/module/welding) + overlay_state_inactive = "module_welding" + +/obj/item/mod/module/welding/on_suit_activation() + mod.helmet.flash_protect = FLASH_PROTECTION_WELDER + +/obj/item/mod/module/welding/on_suit_deactivation() + mod.helmet.flash_protect = initial(mod.helmet.flash_protect) + +//T-Ray Scan + +/obj/item/mod/module/t_ray + name = "MOD t-ray scan module" + desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \ + to essentially echolocate things beneath the floor, mostly cables and pipes. \ + A staple of atmospherics work, and counter-smuggling work." + icon_state = "tray" + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CELL_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/t_ray) + cooldown_time = 0.5 SECONDS + var/range = 2 + +/obj/item/mod/module/t_ray/on_active_process(delta_time) + t_ray_scan(mod.wearer, 8, range) + +//Magnetic Stability + +/obj/item/mod/module/magboot + name = "MOD magnetic stability module" + desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \ + excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \ + However, these basic models do not feature computerized systems to automatically toggle them on and off, \ + so numerous users report a certain stickiness to their steps." + icon_state = "magnet" + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CELL_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/magboot) + cooldown_time = 0.5 SECONDS + var/slowdown_active = 0.5 + +/obj/item/mod/module/magboot/on_activation() + . = ..() + if(!.) + return + ADD_TRAIT(mod.wearer, TRAIT_NEGATES_GRAVITY, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) + mod.slowdown += slowdown_active + mod.wearer.update_gravity(mod.wearer.has_gravity()) + mod.wearer.update_equipment_speed_mods() + +/obj/item/mod/module/magboot/on_deactivation() + . = ..() + if(!.) + return + REMOVE_TRAIT(mod.wearer, TRAIT_NEGATES_GRAVITY, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT) + mod.slowdown -= slowdown_active + mod.wearer.update_gravity(mod.wearer.has_gravity()) + mod.wearer.update_equipment_speed_mods() + +/obj/item/mod/module/magboot/advanced + name = "MOD advanced magnetic stability module" + removable = FALSE + complexity = 0 + slowdown_active = 0 + +//Emergency Tether + +/obj/item/mod/module/tether + name = "MOD emergency tether module" + desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \ + While some older models of cargo-oriented grapples have capacities of a few tons, \ + these are only capable of working in zero-gravity environments, a blessing to some Engineers." + icon_state = "tether" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CELL_DRAIN + incompatible_modules = list(/obj/item/mod/module/tether) + cooldown_time = 1.5 SECONDS + +/obj/item/mod/module/tether/on_use() + if(mod.wearer.has_gravity(get_turf(src))) + balloon_alert(mod.wearer, "too much gravity!") + playsound(src, 'sound/weapons/gun/general/dry_fire.ogg', 25, TRUE) + return FALSE + return ..() + +/obj/item/mod/module/tether/on_select_use(atom/target) + . = ..() + if(!.) + return + var/obj/projectile/tether = new /obj/projectile/tether(mod.wearer.loc) + tether.preparePixelProjectile(target, mod.wearer) + tether.firer = mod.wearer + INVOKE_ASYNC(tether, /obj/projectile.proc/fire) + drain_power(use_power_cost) + +/obj/projectile/tether + name = "tether" + icon_state = "tether_projectile" + icon = 'icons/obj/mod.dmi' + pass_flags = PASSTABLE + damage = 0 + nodamage = TRUE + range = 10 + hitsound = 'sound/weapons/batonextend.ogg' + hitsound_wall = 'sound/weapons/batonextend.ogg' + suppressed = SUPPRESSED_VERY + hit_threshhold = LATTICE_LAYER + var/line + +/obj/projectile/tether/fire(setAngle) + if(firer) + line = firer.Beam(src, "line", 'icons/obj/mod.dmi') + ..() + +/obj/projectile/tether/on_hit(atom/target) + . = ..() + if(firer) + firer.throw_at(target, 10, 1, firer, FALSE, FALSE, null, MOVE_FORCE_NORMAL, TRUE) + +/obj/projectile/tether/Destroy() + QDEL_NULL(line) + return ..() + +//Radiation Protection + +/obj/item/mod/module/rad_protection + name = "MOD radiation protection module" + desc = "A module utilizing polymers and reflective shielding to protect the user against ionizing radiation; \ + a common danger in space. This comes with software to notify the wearer that they're even in a radioactive area, \ + giving a voice to an otherwise silent killer." + icon_state = "radshield" + complexity = 2 + idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/rad_protection) + tgui_id = "rad_counter" + var/perceived_threat_level + +/obj/item/mod/module/rad_protection/on_suit_activation() + AddComponent(/datum/component/geiger_sound) + ADD_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) + RegisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION, .proc/on_pre_potential_irradiation) + for(var/obj/item/part in mod.mod_parts) + ADD_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) + +/obj/item/mod/module/rad_protection/on_suit_deactivation() + qdel(GetComponent(/datum/component/geiger_sound)) + REMOVE_TRAIT(mod.wearer, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, MOD_TRAIT) + UnregisterSignal(mod.wearer, COMSIG_IN_RANGE_OF_IRRADIATION) + for(var/obj/item/part in mod.mod_parts) + REMOVE_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT) + +/obj/item/mod/module/rad_protection/add_ui_data() + . = ..() + .["userradiated"] = mod.wearer ? HAS_TRAIT(mod.wearer, TRAIT_IRRADIATED) : 0 + .["usertoxins"] = mod.wearer ? mod.wearer.getToxLoss() : 0 + .["threatlevel"] = perceived_threat_level + +/obj/item/mod/module/rad_protection/proc/on_pre_potential_irradiation(datum/source, datum/radiation_pulse_information/pulse_information, insulation_to_target) + SIGNAL_HANDLER + + perceived_threat_level = get_perceived_radiation_danger(pulse_information, insulation_to_target) + addtimer(VARSET_CALLBACK(src, perceived_threat_level, null), TIME_WITHOUT_RADIATION_BEFORE_RESET, TIMER_UNIQUE | TIMER_OVERRIDE) + +//Constructor + +/obj/item/mod/module/constructor + name = "MOD constructor module" + desc = "This module entirely occupies the wearer's forearm, notably causing conflict with \ + advanced arm servos meant to carry crewmembers. However, it functions as an \ + extremely advanced construction hologram scanner, as well as containing the \ + latest engineering schematics combined with inbuilt memory to help the user build walls." + icon_state = "constructor" + module_type = MODULE_USABLE + complexity = 2 + idle_power_cost = DEFAULT_CELL_DRAIN * 0.2 + use_power_cost = DEFAULT_CELL_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/constructor, /obj/item/mod/module/quick_carry) + cooldown_time = 11 SECONDS + +/obj/item/mod/module/constructor/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) + +/obj/item/mod/module/constructor/on_suit_deactivation() + REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT) + +/obj/item/mod/module/constructor/on_use() + . = ..() + if(!.) + return + rcd_scan(src, fade_time = 10 SECONDS) + drain_power(use_power_cost) diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm new file mode 100644 index 00000000000..0aa781070a6 --- /dev/null +++ b/code/modules/mod/modules/modules_general.dm @@ -0,0 +1,657 @@ +//General modules for MODsuits + +//Storage + +/obj/item/mod/module/storage + name = "MOD storage module" + desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \ + the surface of the suit, useful for storing various bits, and or bobs." + icon_state = "storage" + complexity = 3 + incompatible_modules = list(/obj/item/mod/module/storage) + var/datum/component/storage/concrete/storage + var/max_w_class = WEIGHT_CLASS_NORMAL + var/max_combined_w_class = 15 + var/max_items = 7 + +/obj/item/mod/module/storage/Initialize(mapload) + . = ..() + storage = AddComponent(/datum/component/storage/concrete) + storage.max_w_class = max_w_class + storage.max_combined_w_class = max_combined_w_class + storage.max_items = max_items + storage.allow_big_nesting = TRUE + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE) + +/obj/item/mod/module/storage/on_install() + var/datum/component/storage/modstorage = mod.AddComponent(/datum/component/storage, storage) + modstorage.max_w_class = max_w_class + modstorage.max_combined_w_class = max_combined_w_class + modstorage.max_items = max_items + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, FALSE) + +/obj/item/mod/module/storage/on_uninstall() + var/datum/component/storage/modstorage = mod.GetComponent(/datum/component/storage) + storage.slaves -= modstorage + qdel(modstorage) + SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE) + +/obj/item/mod/module/storage/large_capacity + name = "MOD expanded storage module" + desc = "Reverse engineered by Nakamura Engineering from Donk Corporation designs, this system of hidden compartments \ + is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \ + whether smuggling, or simply hauling." + icon_state = "storage_large" + max_combined_w_class = 21 + max_items = 14 + +/obj/item/mod/module/storage/syndicate + name = "MOD syndicate storage module" + desc = "A storage system using nanotechnology developed by Cybersun Industries, these compartments use \ + esoteric technology to compress the physical matter of items put inside of them, \ + essentially shrinking items for much easier and more portable storage." + icon_state = "storage_syndi" + max_combined_w_class = 30 + max_items = 21 + +/obj/item/mod/module/storage/bluespace + name = "MOD bluespace storage module" + desc = "A storage system developed by Nanotrasen, these compartments employ \ + miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside." + icon_state = "storage_large" + max_w_class = WEIGHT_CLASS_GIGANTIC + max_combined_w_class = 60 + max_items = 21 + + +//Ion Jetpack + +/obj/item/mod/module/jetpack + name = "MOD ion jetpack module" + desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \ + Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \ + charge from the suit's cell. Some say this isn't Nakamura Engineering's first foray into jet-enabled suits." + icon_state = "jetpack" + module_type = MODULE_TOGGLE + complexity = 3 + active_power_cost = DEFAULT_CELL_DRAIN * 0.5 + use_power_cost = DEFAULT_CELL_DRAIN + incompatible_modules = list(/obj/item/mod/module/jetpack) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_jetpack" + overlay_state_active = "module_jetpack_on" + var/stabilizers = FALSE + var/full_speed = FALSE + var/datum/effect_system/trail_follow/ion/ion_trail + +/obj/item/mod/module/jetpack/Initialize(mapload) + . = ..() + ion_trail = new + ion_trail.auto_process = FALSE + ion_trail.set_up(src) + +/obj/item/mod/module/jetpack/Destroy() + QDEL_NULL(ion_trail) + return ..() + +/obj/item/mod/module/jetpack/on_activation() + . = ..() + if(!.) + return + ion_trail.start() + RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/move_react) + RegisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE, .proc/pre_move_react) + RegisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE, .proc/spacemove_react) + if(full_speed) + mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + +/obj/item/mod/module/jetpack/on_deactivation(mob/user) + . = ..() + if(!.) + return + ion_trail.stop() + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_PRE_MOVE) + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_SPACEMOVE) + if(full_speed) + mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) + +/obj/item/mod/module/jetpack/get_configuration() + . = ..() + .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers) + +/obj/item/mod/module/jetpack/configure_edit(key, value) + switch(key) + if("stabilizers") + stabilizers = text2num(value) + +/obj/item/mod/module/jetpack/proc/move_react(mob/user) + SIGNAL_HANDLER + + if(!active)//If jet dont work, it dont work + return + if(!isturf(mod.wearer.loc))//You can't use jet in nowhere or from mecha/closet + return + if(!(mod.wearer.movement_type & FLOATING) || mod.wearer.buckled)//You don't want use jet in gravity or while buckled. + return + if(mod.wearer.pulledby)//You don't must use jet if someone pull you + return + if(mod.wearer.throwing)//You don't must use jet if you thrown + return + if(user.client && length(user.client.keys_held & user.client.movement_keys))//You use jet when press keys. yes. + allow_thrust() + +/obj/item/mod/module/jetpack/proc/pre_move_react(mob/user) + SIGNAL_HANDLER + + ion_trail.oldposition = get_turf(src) + +/obj/item/mod/module/jetpack/proc/spacemove_react(mob/user, movement_dir) + SIGNAL_HANDLER + + if(active && (stabilizers || movement_dir)) + return COMSIG_MOVABLE_STOP_SPACEMOVE + +/obj/item/mod/module/jetpack/proc/allow_thrust() + if(!drain_power(use_power_cost)) + return + ion_trail.generate_effect() + return TRUE + +//Eating Apparatus + +/obj/item/mod/module/mouthhole + name = "MOD eating apparatus module" + desc = "A favorite by Miners, this modification to the helmet utilizes a nanotechnology barrier infront of the mouth \ + to allow eating and drinking while retaining protection and atmosphere. \ + However, it will do nothing to improve the taste of a goliath steak." + icon_state = "apparatus" + complexity = 1 + incompatible_modules = list(/obj/item/mod/module/mouthhole) + overlay_state_inactive = "module_apparatus" + var/former_flags = NONE + var/former_visor_flags = NONE + +/obj/item/mod/module/mouthhole/on_install() + former_flags = mod.helmet.flags_cover + former_visor_flags = mod.helmet.visor_flags_cover + mod.helmet.flags_cover &= ~HEADCOVERSMOUTH + mod.helmet.visor_flags_cover &= ~HEADCOVERSMOUTH + +/obj/item/mod/module/mouthhole/on_uninstall() + if(!(former_flags & HEADCOVERSMOUTH)) + mod.helmet.flags_cover |= HEADCOVERSMOUTH + if(!(former_visor_flags & HEADCOVERSMOUTH)) + mod.helmet.visor_flags_cover |= HEADCOVERSMOUTH + +//EMP Shield + +/obj/item/mod/module/emp_shield + name = "MOD EMP shield module" + desc = "A field inhibitor installed into the suit, protecting it against feedback such as \ + electromagnetic pulses that would otherwise damage the electronic systems of the suit or devices on the wearer. \ + However, it will take from the suit's power to do so. Luckily, your PDA already has one of these." + icon_state = "empshield" + complexity = 1 + idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/emp_shield) + +/obj/item/mod/module/emp_shield/on_install() + mod.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + +/obj/item/mod/module/emp_shield/on_uninstall() + mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS) + +//Flashlight + +/obj/item/mod/module/flashlight + name = "MOD flashlight module" + desc = "A simple pair of flashlights installed on the left and right sides of the helmet, \ + useful for providing light in a variety of ranges and colors. \ + Some survivalists prefer the color green for their illumination, for reasons unknown." + icon_state = "flashlight" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CELL_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/flashlight) + cooldown_time = 0.5 SECONDS + overlay_state_inactive = "module_light" + light_system = MOVABLE_LIGHT_DIRECTIONAL + light_color = COLOR_WHITE + light_range = 3 + light_power = 1 + light_on = FALSE + var/base_power = DEFAULT_CELL_DRAIN * 0.1 + var/min_range = 2 + var/max_range = 5 + +/obj/item/mod/module/flashlight/on_activation() + . = ..() + if(!.) + return + set_light_flags(light_flags | LIGHT_ATTACHED) + set_light_on(active) + active_power_cost = base_power * light_range + +/obj/item/mod/module/flashlight/on_deactivation() + . = ..() + if(!.) + return + set_light_flags(light_flags & ~LIGHT_ATTACHED) + set_light_on(active) + +/obj/item/mod/module/flashlight/on_process(delta_time) + . = ..() + if(!.) + return + active_power_cost = base_power * light_range + +/obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing) + . = ..() + if(!active) + return + var/mutable_appearance/light_icon = mutable_appearance('icons/mob/mod.dmi', "module_light_on", layer = standing.layer + 0.2) + light_icon.appearance_flags = RESET_COLOR + light_icon.color = light_color + . += light_icon + +/obj/item/mod/module/flashlight/get_configuration() + . = ..() + .["light_color"] = add_ui_configuration("Light Color", "color", light_color) + .["light_range"] = add_ui_configuration("Light Range", "number", light_range) + +/obj/item/mod/module/flashlight/configure_edit(key, value) + switch(key) + if("light_color") + value = input(usr, "Pick new light color", "Flashlight Color") as color|null + if(!value) + return + if(is_color_dark(value, 50)) + balloon_alert(mod.wearer, "too dark!") + return + set_light_color(value) + mod.wearer.update_inv_back() + if("light_range") + set_light_range(clamp(value, min_range, max_range)) + +//Dispenser + +/obj/item/mod/module/dispenser + name = "MOD burger dispenser module" + desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \ + This can draw incredible amounts of power from the suit's cell to create edible organic matter in the \ + palm of the wearer's glove; however, research seemed to have entirely stopped at burgers. \ + Notably, all attempts to get it to dispense Earl Grey tea have failed." + icon_state = "dispenser" + module_type = MODULE_USABLE + complexity = 3 + use_power_cost = DEFAULT_CELL_DRAIN * 2 + incompatible_modules = list(/obj/item/mod/module/dispenser) + cooldown_time = 5 SECONDS + var/dispense_type = /obj/item/food/burger/plain + var/dispense_time = 0 SECONDS + +/obj/item/mod/module/dispenser/on_use() + . = ..() + if(!.) + return + if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod)) + balloon_alert(mod.wearer, "interrupted!") + return + var/obj/item/dispensed = new dispense_type(mod.wearer.loc) + mod.wearer.put_in_hands(dispensed) + balloon_alert(mod.wearer, "[dispensed] dispensed") + playsound(src, 'sound/machines/click.ogg', 100, TRUE) + drain_power(use_power_cost) + +//Longfall + +/obj/item/mod/module/longfall + name = "MOD longfall module" + desc = "Useful for protecting both the suit and the wearer, \ + utilizing commonplace systems to convert the possible damage from a fall into kinetic charge, \ + as well as internal gyroscopes to ensure the user's safe falling. \ + Useful for mining, monorail tracks, or even skydiving!" + icon_state = "longfall" + complexity = 1 + use_power_cost = DEFAULT_CELL_DRAIN * 5 + incompatible_modules = list(/obj/item/mod/module/longfall) + +/obj/item/mod/module/longfall/on_suit_activation() + RegisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT, .proc/z_impact_react) + +/obj/item/mod/module/longfall/on_suit_deactivation() + UnregisterSignal(mod.wearer, COMSIG_LIVING_Z_IMPACT) + +/obj/item/mod/module/longfall/proc/z_impact_react(datum/source, levels, turf/fell_on) + if(!drain_power(use_power_cost*levels)) + return + new /obj/effect/temp_visual/mook_dust(fell_on) + mod.wearer.Stun(levels * 1 SECONDS) + to_chat(mod.wearer, span_notice("[src] protects you from the damage!")) + return NO_Z_IMPACT_DAMAGE + +//Thermal Regulator + +/obj/item/mod/module/thermal_regulator + name = "MOD thermal regulator module" + desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \ + flexible cooling lines. This circulates coolant at various user-controlled temperatures, \ + ensuring they're comfortable; even if they're some that like it hot." + icon_state = "regulator" + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CELL_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/thermal_regulator) + cooldown_time = 0.5 SECONDS + var/temperature_setting = BODYTEMP_NORMAL + var/min_temp = 293.15 + var/max_temp = 318.15 + +/obj/item/mod/module/thermal_regulator/get_configuration() + . = ..() + .["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C) + +/obj/item/mod/module/thermal_regulator/configure_edit(key, value) + switch(key) + if("temperature_setting") + temperature_setting = clamp(value + T0C, min_temp, max_temp) + +/obj/item/mod/module/thermal_regulator/on_active_process(delta_time) + mod.wearer.adjust_bodytemperature(get_temp_change_amount((temperature_setting - mod.wearer.bodytemperature), 0.08 * delta_time)) + +//Pathfinder + +/obj/item/mod/module/pathfinder + name = "MOD pathfinder module" + desc = "This module, brought to you by Nakamura Engineering, has two components. \ + The first component is a series of thrusters and a computerized location subroutine installed into the \ + very control unit of the suit, allowing it flight at highway speeds, \ + and to be able to locate the second part of the system; \ + a pathfinding implant installed into the base of the user's spine, \ + broadcasting their location to the suit and allowing them to recall it to their back at any time. \ + Nakamura Engineering swears up and down there's airbrakes." + icon_state = "pathfinder" + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN * 10 + incompatible_modules = list(/obj/item/mod/module/pathfinder) + var/obj/item/implant/mod/implant + +/obj/item/mod/module/pathfinder/Initialize(mapload) + . = ..() + implant = new(src) + +/obj/item/mod/module/pathfinder/Destroy() + implant = null + return ..() + +/obj/item/mod/module/pathfinder/examine(mob/user) + . = ..() + if(implant) + . += span_notice("Use it on a human to implant them.") + else + . += span_warning("The implant is missing.") + +/obj/item/mod/module/pathfinder/attack(mob/living/target, mob/living/user, params) + if(!ishuman(target) || !implant) + return + if(!do_after(user, 1.5 SECONDS, target = target)) + balloon_alert(user, "interrupted!") + return + if(!implant.implant(target, user)) + balloon_alert(user, "can't implant!") + return + if(target == user) + to_chat(user, span_notice("You implant yourself with [implant].")) + else + target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant].")) + playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6) + icon_state = "pathfinder_empty" + implant = null + +/obj/item/mod/module/pathfinder/proc/attach(mob/living/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/human_user = user + if(human_user.back && !human_user.dropItemToGround(human_user.back)) + return + if(!human_user.equip_to_slot_if_possible(mod, mod.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) + return + human_user.update_action_buttons(TRUE) + balloon_alert(human_user, "[mod] attached") + playsound(mod, 'sound/machines/ping.ogg', 50, TRUE) + drain_power(use_power_cost) + +/obj/item/implant/mod + name = "MOD pathfinder implant" + desc = "Lets you recall a MODsuit to you at any time." + actions_types = list(/datum/action/item_action/mod_recall) + var/obj/item/mod/module/pathfinder/module + var/image/jet_icon + +/obj/item/implant/mod/Initialize(mapload) + . = ..() + if(!istype(loc, /obj/item/mod/module/pathfinder)) + return INITIALIZE_HINT_QDEL + module = loc + jet_icon = image(icon = 'icons/obj/mod.dmi', icon_state = "mod_jet", layer = LOW_ITEM_LAYER) + +/obj/item/implant/mod/Destroy() + if(module?.mod?.ai_controller) + end_recall(successful = FALSE) + module = null + jet_icon = null + return ..() + +/obj/item/implant/mod/get_data() + var/dat = {"Implant Specifications:
+ Name: Nakamura Engineering Pathfinder Implant
+ Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"} + return dat + +/obj/item/implant/mod/proc/recall() + if(!module?.mod) + balloon_alert(imp_in, "no connected suit!") + return FALSE + if(module.mod.open) + balloon_alert(imp_in, "suit is open!") + return FALSE + if(module.mod.ai_controller) + balloon_alert(imp_in, "already in transit!") + return FALSE + if(ismob(get_atom_on_turf(module.mod))) + balloon_alert(imp_in, "already on someone!") + return FALSE + if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE) + balloon_alert(imp_in, "too far away!") + return FALSE + var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod) + module.mod.ai_controller = mod_ai + mod_ai.current_movement_target = imp_in + mod_ai.blackboard[BB_MOD_TARGET] = imp_in + mod_ai.blackboard[BB_MOD_IMPLANT] = src + module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP + module.mod.AddElement(/datum/element/movetype_handler) + ADD_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT) + animate(module.mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y) + module.mod.add_overlay(jet_icon) + RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, .proc/on_move) + balloon_alert(imp_in, "suit recalled") + return TRUE + +/obj/item/implant/mod/proc/end_recall(successful = TRUE) + if(!module?.mod) + return + QDEL_NULL(module.mod.ai_controller) + module.mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP + REMOVE_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT) + module.mod.RemoveElement(/datum/element/movetype_handler) + module.mod.cut_overlay(jet_icon) + module.mod.transform = matrix() + UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED) + if(!successful) + balloon_alert(imp_in, "suit lost connection!") + +/obj/item/implant/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced) + SIGNAL_HANDLER + + var/matrix/mod_matrix = matrix() + mod_matrix.Turn(get_angle(source, imp_in)) + source.transform = mod_matrix + +/datum/action/item_action/mod_recall + name = "Recall MOD" + desc = "Recall a MODsuit anyplace, anytime." + check_flags = AB_CHECK_CONSCIOUS + background_icon_state = "bg_tech_blue" + icon_icon = 'icons/mob/actions/actions_mod.dmi' + button_icon_state = "recall" + COOLDOWN_DECLARE(recall_cooldown) + var/obj/item/implant/mod/implant + +/datum/action/item_action/mod_recall/New(Target) + ..() + implant = Target + +/datum/action/item_action/mod_recall/Trigger() + . = ..() + if(!.) + return + if(!COOLDOWN_FINISHED(src, recall_cooldown)) + implant.balloon_alert(implant.imp_in, "on cooldown!") + return + if(implant.recall()) + COOLDOWN_START(src, recall_cooldown, 15 SECONDS) + +//DNA Lock + +/obj/item/mod/module/dna_lock + name = "MOD DNA lock module" + desc = "A module which engages with the various locks and seals tied to the suit's systems, \ + enabling it to only be worn by someone corresponding with the user's exact DNA profile; \ + however, this incredibly sensitive module is shorted out by EMPs. Luckily, cloning has been outlawed." + icon_state = "dnalock" + module_type = MODULE_USABLE + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN * 3 + incompatible_modules = list(/obj/item/mod/module/dna_lock) + cooldown_time = 0.5 SECONDS + var/dna = null + +/obj/item/mod/module/dna_lock/on_install() + RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/on_mod_activation) + RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, .proc/on_mod_removal) + RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, .proc/on_emp) + RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, .proc/on_emag) + +/obj/item/mod/module/dna_lock/on_uninstall() + UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) + UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL) + UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT) + UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT) + +/obj/item/mod/module/dna_lock/on_use() + . = ..() + if(!.) + return + dna = mod.wearer.dna.unique_enzymes + balloon_alert(mod.wearer, "dna updated") + drain_power(use_power_cost) + +/obj/item/mod/module/dna_lock/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + on_emp(src, severity) + +/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card) + . = ..() + on_emag(src, user, emag_card) + +/obj/item/mod/module/dna_lock/proc/dna_check() + if(!dna || (mod.wearer.has_dna() && mod.wearer.dna.unique_enzymes == dna)) + return TRUE + balloon_alert(mod.wearer, "dna locked!") + return FALSE + +/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity) + SIGNAL_HANDLER + + dna = null + +/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card) + SIGNAL_HANDLER + + dna = null + +/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source) + SIGNAL_HANDLER + + if(!dna_check()) + return MOD_CANCEL_ACTIVATE + +/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source) + SIGNAL_HANDLER + + if(!dna_check()) + return MOD_CANCEL_REMOVAL + +//Plasma Stabilizer + +/obj/item/mod/module/plasma_stabilizer + name = "MOD plasma stabilizer module" + desc = "This system essentially forms an atmosphere of its' own inside the suit, \ + safely ejecting oxygen from the inside and allowing the wearer, a plasmaman, \ + to have their internal plasma circulate around them somewhat like a sauna. \ + This prevents them from self-igniting, and leads to greater comfort overall. \ + The purple glass of the visor seems to be constructed for nostalgic purposes." + icon_state = "plasma_stabilizer" + complexity = 1 + idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer) + overlay_state_inactive = "module_plasma" + +/obj/item/mod/module/plasma_stabilizer/on_equip() + ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT) + +/obj/item/mod/module/plasma_stabilizer/on_unequip() + REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION, MOD_TRAIT) + +//Kinesis + +/obj/item/mod/module/kinesis //TODO POST-MERGE MAKE NOT SUCK ASS, MAKE BALLER AS FUCK + name = "MOD kinesis module" + desc = "A modular plug-in to the forearm, this module was presumed lost for many years, \ + despite the suits it used to be mounted on still seeing some circulation. \ + This piece of technology allows the user to generate precise anti-gravity fields, \ + letting them move objects as small as a titanium rod to as large as industrial machinery. \ + Oddly enough, it doesn't seem to work on living creatures." + icon_state = "kinesis" +// module_type = MODULE_ACTIVE + module_type = MODULE_TOGGLE +// complexity = 3 + complexity = 0 + active_power_cost = DEFAULT_CELL_DRAIN*0.75 +// use_power_cost = DEFAULT_CELL_DRAIN*3 + removable = FALSE + incompatible_modules = list(/obj/item/mod/module/kinesis) + cooldown_time = 0.5 SECONDS + var/has_tk = FALSE + +/obj/item/mod/module/kinesis/on_activation() + . = ..() + if(!.) + return + if(mod.wearer.dna.check_mutation(TK)) + has_tk = TRUE + else + mod.wearer.dna.add_mutation(TK) + +/obj/item/mod/module/kinesis/on_deactivation() + . = ..() + if(!.) + return + if(has_tk) + has_tk = FALSE + return + mod.wearer.dna.remove_mutation(TK) diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm new file mode 100644 index 00000000000..d3f1c465fbe --- /dev/null +++ b/code/modules/mod/modules/modules_medical.dm @@ -0,0 +1,203 @@ +//Medical modules for MODsuits + +//Health Analyzer + +#define HEALTH_SCAN "Health" +#define WOUND_SCAN "Wound" +#define CHEM_SCAN "Chemical" + +/obj/item/mod/module/health_analyzer + name = "MOD health analyzer module" + desc = "A module installed into the glove of the suit. This is a high-tech biological scanning suite, \ + allowing the user indepth information on the vitals and injuries of others even at a distance, \ + all with the flick of the wrist. Data is displayed in a convenient package on HUD in the helmet, \ + but it's up to you to do something with it." + icon_state = "health" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN + incompatible_modules = list(/obj/item/mod/module/health_analyzer) + cooldown_time = 0.5 SECONDS + var/mode = HEALTH_SCAN + var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN) + +/obj/item/mod/module/health_analyzer/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!isliving(target)) + return + switch(mode) + if(HEALTH_SCAN) + healthscan(mod.wearer, target) + if(WOUND_SCAN) + woundscan(mod.wearer, target) + if(CHEM_SCAN) + chemscan(mod.wearer, target) + drain_power(use_power_cost) + +/obj/item/mod/module/health_analyzer/get_configuration() + . = ..() + .["mode"] = add_ui_configuration("Scan Mode", "list", mode, modes) + +/obj/item/mod/module/health_analyzer/configure_edit(key, value) + switch(key) + if("mode") + mode = value + +#undef HEALTH_SCAN +#undef WOUND_SCAN +#undef CHEM_SCAN + +//Quick Carry + +/obj/item/mod/module/quick_carry + name = "MOD quick carry module" + desc = "A suite of advanced servos, redirecting power from the suit's arms to help carry the wounded; \ + or simply for fun. However, Nanotrasen has locked the module's ability to assist in hand-to-hand combat." + icon_state = "carry" + complexity = 1 + idle_power_cost = DEFAULT_CELL_DRAIN * 0.3 + incompatible_modules = list(/obj/item/mod/module/quick_carry, /obj/item/mod/module/constructor) + +/obj/item/mod/module/quick_carry/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/on_suit_deactivation() + REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_CARRY, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/advanced + name = "MOD advanced quick carry module" + removable = FALSE + complexity = 0 + +/obj/item/mod/module/quick_carry/on_suit_activation() + ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) + ADD_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) + +/obj/item/mod/module/quick_carry/on_suit_deactivation() + REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT) + REMOVE_TRAIT(mod.wearer, TRAIT_FASTMED, MOD_TRAIT) + +//Injector + +/obj/item/mod/module/injector + name = "MOD injector module" + desc = "A module installed into the wrist of the suit, this functions as a high-capacity syringe, \ + with a tip fine enough to locate the emergency injection ports on any suit of armor, \ + penetrating it with ease. Even yours." + icon_state = "injector" + module_type = MODULE_ACTIVE + complexity = 1 + active_power_cost = DEFAULT_CELL_DRAIN * 0.3 + device = /obj/item/reagent_containers/syringe/mod + incompatible_modules = list(/obj/item/mod/module/injector) + cooldown_time = 0.5 SECONDS + +/obj/item/reagent_containers/syringe/mod + name = "MOD injector syringe" + desc = "A high-capacity syringe, with a tip fine enough to locate \ + the emergency injection ports on any suit of armor, penetrating it with ease. Even yours." + icon_state = "mod_0" + base_icon_state = "mod" + amount_per_transfer_from_this = 30 + possible_transfer_amounts = list(5, 10, 15, 20, 30) + volume = 30 + inject_flags = INJECT_CHECK_PENETRATE_THICK + +//Organ Thrower + +/obj/item/mod/module/organ_thrower + name = "MOD organ thrower module" + desc = "A device recovered from a crashed Interdyne Pharmaceuticals vessel, \ + this module has been unearthed for better or for worse. \ + It's an arm-mounted device utilizing technology similar to modern-day part replacers, \ + capable of storing and inserting organs into open patients. \ + It's recommended by the DeForest Medical Corporation to not inform patients it has been used." + icon_state = "organ_thrower" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN + incompatible_modules = list(/obj/item/mod/module/organ_thrower) + cooldown_time = 0.5 SECONDS + var/max_organs = 5 + var/organ_list = list() + +/obj/item/mod/module/organ_thrower/on_select_use(atom/target) + . = ..() + if(!.) + return + var/mob/living/carbon/human/wearer_human = mod.wearer + if(istype(target, /obj/item/organ)) + if(!wearer_human.Adjacent(target)) + return + var/atom/movable/organ = target + if(length(organ_list) >= max_organs) + balloon_alert(mod.wearer, "too many organs!") + return + organ_list += organ + organ.forceMove(src) + balloon_alert(mod.wearer, "picked up [organ]") + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + drain_power(use_power_cost) + return + if(!length(organ_list)) + return + var/atom/movable/fired_organ = pop(organ_list) + var/obj/projectile/organ/projectile = new /obj/projectile/organ(mod.wearer.loc, fired_organ) + projectile.preparePixelProjectile(target, mod.wearer) + projectile.firer = mod.wearer + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + INVOKE_ASYNC(projectile, /obj/projectile.proc/fire) + drain_power(use_power_cost) + +/obj/projectile/organ + name = "organ" + damage = 0 + nodamage = TRUE + hitsound = 'sound/effects/attackblob.ogg' + hitsound_wall = 'sound/effects/attackblob.ogg' + var/obj/item/organ/organ + +/obj/projectile/organ/Initialize(mapload, obj/item/stored_organ) + . = ..() + if(!stored_organ) + return INITIALIZE_HINT_QDEL + appearance = stored_organ.appearance + stored_organ.forceMove(src) + organ = stored_organ + +/obj/projectile/organ/Destroy() + organ = null + return ..() + +/obj/projectile/organ/on_hit(atom/target) + . = ..() + if(!ishuman(target)) + organ.forceMove(drop_location()) + organ = null + return + var/mob/living/carbon/human/organ_receiver = target + var/succeed = FALSE + if(organ_receiver.surgeries.len) + for(var/datum/surgery/procedure as anything in organ_receiver.surgeries) + if(procedure.location != organ.zone) + continue + if(!istype(procedure, /datum/surgery/organ_manipulation)) + continue + var/datum/surgery_step/surgery_step = procedure.get_surgery_step() + if(!istype(surgery_step, /datum/surgery_step/manipulate_organs)) + continue + succeed = TRUE + break + if(succeed) + var/list/organs_to_boot_out = organ_receiver.getorganslot(organ.slot) + for(var/obj/item/organ/organ_evacced as anything in organs_to_boot_out) + if(organ_evacced.organ_flags & ORGAN_UNREMOVABLE) + continue + organ_evacced.Remove(target) + organ_evacced.forceMove(get_turf(target)) + organ.Insert(target) + else + organ.forceMove(drop_location()) + organ = null diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm new file mode 100644 index 00000000000..62c86b81975 --- /dev/null +++ b/code/modules/mod/modules/modules_science.dm @@ -0,0 +1,162 @@ +//Science modules for MODsuits + +//Reagent Scanner + +/obj/item/mod/module/reagent_scanner + name = "MOD reagent scanner module" + desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \ + containers and projecting the information in an easy-to-read format on the wearer's display. \ + It cannot detect flavors, so that's up to you." + icon_state = "scanner" + module_type = MODULE_TOGGLE + complexity = 1 + active_power_cost = DEFAULT_CELL_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/reagent_scanner) + cooldown_time = 0.5 SECONDS + +/obj/item/mod/module/reagent_scanner/on_activation() + . = ..() + if(!.) + return + ADD_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) + +/obj/item/mod/module/reagent_scanner/on_deactivation() + . = ..() + if(!.) + return + REMOVE_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT) + +/obj/item/mod/module/reagent_scanner/advanced + name = "MOD advanced reagent scanner module" + complexity = 0 + removable = FALSE + var/explosion_detection_dist = 21 + +/obj/item/mod/module/reagent_scanner/advanced/on_activation() + . = ..() + if(!.) + return + mod.wearer.research_scanner++ + RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, .proc/sense_explosion) + +/obj/item/mod/module/reagent_scanner/advanced/on_deactivation() + . = ..() + if(!.) + return + mod.wearer.research_scanner-- + RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION) + +/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(datum/source, turf/epicenter, + devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range) + SIGNAL_HANDLER + var/turf/wearer_turf = get_turf(mod.wearer) + if(wearer_turf.z != epicenter.z) + return + if(get_dist(epicenter, wearer_turf) > explosion_detection_dist) + return + to_chat(mod.wearer, span_notice("Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]")) + +//Circuit Adapter + +/obj/item/mod/module/circuit + name = "MOD circuit adapter module" + desc = "A popular aftermarket module, seen in wide varieties with wide applications by those across the galaxy. \ + This is able to fit any sort of integrated circuit, hooking it into controls in the suit and displaying information \ + to the HUD. Useful for universal translation, or perhaps as a calculator." + module_type = MODULE_USABLE + complexity = 3 + idle_power_cost = DEFAULT_CELL_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/circuit) + cooldown_time = 0.5 SECONDS + var/obj/item/integrated_circuit/circuit + +/obj/item/mod/module/circuit/Initialize(mapload) + . = ..() + circuit = new() + AddComponent(/datum/component/shell, \ + list(new /obj/item/circuit_component/mod()), \ + capacity = SHELL_CAPACITY_LARGE, \ + shell_flags = SHELL_FLAG_CIRCUIT_UNREMOVABLE, \ + starting_circuit = circuit, \ + ) + +/obj/item/mod/module/circuit/on_install() + circuit.set_cell(mod.cell) + +/obj/item/mod/module/circuit/on_uninstall() + circuit.set_cell(mod.cell) + +/obj/item/mod/module/circuit/on_suit_activation() + circuit.set_on(TRUE) + +/obj/item/mod/module/circuit/on_suit_deactivation() + circuit.set_on(FALSE) + +/obj/item/mod/module/circuit/on_use() + . = ..() + if(!.) + return + circuit.interact(mod.wearer) + +/obj/item/circuit_component/mod + display_name = "MOD" + desc = "Used to send and receive signals from a MODsuit." + + var/obj/item/mod/module/attached_module + + var/datum/port/input/module_to_select + var/datum/port/input/toggle_suit + var/datum/port/input/select_module + + var/datum/port/output/wearer + var/datum/port/output/selected_module + +/obj/item/circuit_component/mod/populate_ports() + // Input Signals + module_to_select = add_input_port("Module to Select", PORT_TYPE_STRING) + toggle_suit = add_input_port("Toggle Suit", PORT_TYPE_SIGNAL) + select_module = add_input_port("Select Module", PORT_TYPE_SIGNAL) + // States + wearer = add_output_port("Wearer", PORT_TYPE_ATOM) + selected_module = add_output_port("Selected Module", PORT_TYPE_ATOM) + +/obj/item/circuit_component/mod/register_shell(atom/movable/shell) + if(istype(shell, /obj/item/mod/module)) + attached_module = shell + RegisterSignal(attached_module, COMSIG_MOVABLE_MOVED, .proc/on_move) + +/obj/item/circuit_component/mod/unregister_shell(atom/movable/shell) + UnregisterSignal(attached_module, COMSIG_MOVABLE_MOVED) + attached_module = null + +/obj/item/circuit_component/mod/input_received(datum/port/input/port) + var/obj/item/mod/module/module + for(var/obj/item/mod/module/potential_module as anything in attached_module.mod.modules) + if(potential_module.name == module_to_select.value) + module = potential_module + if(COMPONENT_TRIGGERED_BY(toggle_suit, port)) + INVOKE_ASYNC(attached_module.mod, /obj/item/mod/control.proc/toggle_activate, attached_module.mod.wearer) + if(module && COMPONENT_TRIGGERED_BY(select_module, port)) + INVOKE_ASYNC(module, /obj/item/mod/module.proc/on_select) + +/obj/item/circuit_component/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced) + SIGNAL_HANDLER + if(istype(source.loc, /obj/item/mod/control)) + RegisterSignal(source.loc, COMSIG_MOD_MODULE_SELECTED, .proc/on_module_select) + RegisterSignal(source.loc, COMSIG_ITEM_EQUIPPED, .proc/equip_check) + equip_check() + else if(istype(old_loc, /obj/item/mod/control)) + UnregisterSignal(old_loc, list(COMSIG_MOD_MODULE_SELECTED, COMSIG_ITEM_EQUIPPED)) + selected_module.set_output(null) + wearer.set_output(null) + +/obj/item/circuit_component/mod/proc/on_module_select() + SIGNAL_HANDLER + selected_module.set_output(attached_module.mod.selected_module) + +/obj/item/circuit_component/mod/proc/equip_check() + SIGNAL_HANDLER + + if(!attached_module.mod?.wearer) + return + wearer.set_output(attached_module.mod.wearer) diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm new file mode 100644 index 00000000000..8daa732ee81 --- /dev/null +++ b/code/modules/mod/modules/modules_security.dm @@ -0,0 +1,125 @@ +//Security modules for MODsuits + +//Cloaking + +/obj/item/mod/module/stealth + name = "MOD prototype cloaking module" + desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \ + to bend light around the user, as well as mimetic materials to make the surface of the suit match the \ + surroundings based off sensor data. For some reason, this tech is rarely seen." + icon_state = "cloak" + module_type = MODULE_TOGGLE + complexity = 4 + active_power_cost = DEFAULT_CELL_DRAIN * 2 + use_power_cost = DEFAULT_CELL_DRAIN * 10 + incompatible_modules = list(/obj/item/mod/module/stealth) + cooldown_time = 5 SECONDS + var/bumpoff = TRUE + var/stealth_alpha = 50 + +/obj/item/mod/module/stealth/on_activation() + . = ..() + if(!.) + return + if(bumpoff) + RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, .proc/unstealth) + RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_unarmed_attack) + RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, .proc/on_bullet_act) + RegisterSignal(mod.wearer, list(COMSIG_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED), .proc/unstealth) + animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS) + drain_power(use_power_cost) + +/obj/item/mod/module/stealth/on_deactivation() + . = ..() + if(!.) + return + if(bumpoff) + UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP) + UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW, COMSIG_CARBON_CUFF_ATTEMPTED)) + animate(mod.wearer, alpha = 255, time = 1.5 SECONDS) + +/obj/item/mod/module/stealth/proc/unstealth(datum/source) + SIGNAL_HANDLER + + to_chat(mod.wearer, span_warning("[src] gets discharged from contact!")) + do_sparks(2, TRUE, src) + drain_power(use_power_cost) + on_deactivation() + +/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target) + SIGNAL_HANDLER + + if(!isliving(target)) + return + unstealth(source) + +/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/projectile/projectile) + SIGNAL_HANDLER + + if(projectile.nodamage) + return + unstealth(source) + +/obj/item/mod/module/stealth/ninja + name = "MOD advanced cloaking module" + desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \ + The field has been tuned to be even more responsive and fast-acting, with enough stability to \ + continue operation of the field even if the user bumps into others. \ + The draw on the power cell has been reduced drastically, \ + making this perfect for activities like standing near sentry turrets for extended periods of time." + icon_state = "cloak_ninja" + bumpoff = FALSE + stealth_alpha = 20 + active_power_cost = DEFAULT_CELL_DRAIN + use_power_cost = DEFAULT_CELL_DRAIN * 5 + cooldown_time = 3 SECONDS + +//Holster + +/obj/item/mod/module/holster + name = "MOD holster module" + desc = "Based off typical storage compartments, this system allows the suit to holster a \ + standard firearm across its surface and allow for extremely quick retrieval. \ + While some users prefer the chest, others the forearm for quick deployment, \ + some law enforcement prefer the holster to extend from the thigh." + icon_state = "holster" + module_type = MODULE_USABLE + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN * 0.5 + incompatible_modules = list(/obj/item/mod/module/holster) + cooldown_time = 0.5 SECONDS + var/obj/item/gun/holstered + +/obj/item/mod/module/holster/on_use() + . = ..() + if(!.) + return + if(!holstered) + var/obj/item/gun/holding = mod.wearer.get_active_held_item() + if(!holding) + balloon_alert(mod.wearer, "nothing to holster!") + return + if(!istype(holding) || holding.w_class > WEIGHT_CLASS_BULKY) + balloon_alert(mod.wearer, "it doesn't fit!") + return + if(mod.wearer.transferItemToLoc(holding, src, FALSE, FALSE)) + holstered = holding + balloon_alert(mod.wearer, "weapon holstered") + playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) + drain_power(use_power_cost) + else if(mod.wearer.put_in_active_hand(holstered, FALSE, TRUE)) + balloon_alert(mod.wearer, "weapon drawn") + holstered = null + playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) + drain_power(use_power_cost) + else + balloon_alert(mod.wearer, "holster full!") + +/obj/item/mod/module/holster/on_uninstall() + if(holstered) + holstered.forceMove(drop_location()) + holstered = null + +/obj/item/mod/module/holster/Destroy() + QDEL_NULL(holstered) + return ..() diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm new file mode 100644 index 00000000000..6712837aa07 --- /dev/null +++ b/code/modules/mod/modules/modules_service.dm @@ -0,0 +1,58 @@ +//Service modules for MODsuits + +//Bike Horn + +/obj/item/mod/module/bikehorn + name = "MOD bike horn module" + desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \ + precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound." + icon_state = "bikehorn" + module_type = MODULE_USABLE + complexity = 1 + use_power_cost = DEFAULT_CELL_DRAIN + incompatible_modules = list(/obj/item/mod/module/bikehorn) + cooldown_time = 1 SECONDS + +/obj/item/mod/module/bikehorn/on_use() + . = ..() + if(!.) + return + playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE) + drain_power(use_power_cost) + +//Microwave Beam + +/obj/item/mod/module/microwave_beam + name = "MOD microwave beam module" + desc = "An oddly domestic device, this module is installed into the user's palm, \ + hooking up with culinary scanners located in the helmet to blast food with precise microwave radiation, \ + allowing them to cook food from a distance, with the greatest of ease. Not recommended for use against grapes." + icon_state = "microwave_beam" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN * 5 + incompatible_modules = list(/obj/item/mod/module/microwave_beam) + cooldown_time = 10 SECONDS + +/obj/item/mod/module/microwave_beam/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!istype(target, /obj/item)) + return + if(!isturf(target.loc)) + balloon_alert(mod.wearer, "must be on the floor!") + return + var/obj/item/microwave_target = target + var/datum/effect_system/spark_spread/spark_effect = new() + spark_effect.set_up(2, 1, mod.wearer) + spark_effect.start() + mod.wearer.Beam(target,icon_state="lightning[rand(1,12)]", time = 5) + if(microwave_target.microwave_act()) + playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, FALSE) + else + balloon_alert(mod.wearer, "can't be microwaved!") + var/datum/effect_system/spark_spread/spark_effect_two = new() + spark_effect_two.set_up(2, 1, microwave_target) + spark_effect_two.start() + drain_power(use_power_cost) diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm new file mode 100644 index 00000000000..5b6096b9d79 --- /dev/null +++ b/code/modules/mod/modules/modules_supply.dm @@ -0,0 +1,169 @@ +//Supply modules for MODsuits + +//Internal GPS + +/obj/item/mod/module/gps + name = "MOD internal GPS module" + desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \ + down to the exact coordinates. This information is fed to a central database viewable from the device itself, \ + though using it to help people is up to you." + icon_state = "gps" + module_type = MODULE_ACTIVE + complexity = 1 + active_power_cost = DEFAULT_CELL_DRAIN * 0.3 + device = /obj/item/gps/mod + incompatible_modules = list(/obj/item/mod/module/gps) + cooldown_time = 0.5 SECONDS + +/obj/item/gps/mod + name = "MOD internal GPS" + desc = "Common Nanotrasen technology that calcaulates the user's position from anywhere in space, down to their coordinates." + icon_state = "gps-b" + gpstag = "MOD0" + +//Hydraulic Clamp + +/obj/item/mod/module/clamp + name = "MOD hydraulic clamp module" + desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \ + However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \ + A lot of people would say that loading cargo is a dull job, but you could not disagree more." + icon_state = "clamp" + module_type = MODULE_ACTIVE + complexity = 3 + use_power_cost = DEFAULT_CELL_DRAIN + incompatible_modules = list(/obj/item/mod/module/clamp) + cooldown_time = 0.5 SECONDS + var/max_crates = 5 + var/list/stored_crates = list() + +/obj/item/mod/module/clamp/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!mod.wearer.Adjacent(target)) + return + if(istype(target, /obj/structure/closet/crate)) + var/atom/movable/picked_crate = target + if(length(stored_crates) >= max_crates) + balloon_alert(mod.wearer, "too many crates!") + return + if(!do_after(mod.wearer, 1 SECONDS, target = target)) + balloon_alert(mod.wearer, "interrupted!") + return + stored_crates += picked_crate + picked_crate.forceMove(src) + balloon_alert(mod.wearer, "picked up [picked_crate]") + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + drain_power(use_power_cost) + else if(length(stored_crates)) + var/turf/target_turf = get_turf(target) + if(target_turf.is_blocked_turf()) + return + if(!do_after(mod.wearer, 1 SECONDS, target = target)) + balloon_alert(mod.wearer, "interrupted!") + return + if(target_turf.is_blocked_turf()) + return + var/atom/movable/dropped_crate = pop(stored_crates) + dropped_crate.forceMove(target_turf) + balloon_alert(mod.wearer, "dropped [dropped_crate]") + playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE) + drain_power(use_power_cost) + +/obj/item/mod/module/clamp/on_uninstall() + for(var/atom/movable/crate as anything in stored_crates) + crate.forceMove(drop_location()) + stored_crates -= crate + +//Drill + +/obj/item/mod/module/drill + name = "MOD drill module" + desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \ + your drill is surely the one that both pierces and creates the heavens." + icon_state = "drill" + module_type = MODULE_ACTIVE + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN + incompatible_modules = list(/obj/item/mod/module/drill) + cooldown_time = 0.5 SECONDS + +/obj/item/mod/module/drill/on_activation() + . = ..() + if(!.) + return + RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, .proc/bump_mine) + +/obj/item/mod/module/drill/on_deactivation() + . = ..() + if(!.) + return + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP) + +/obj/item/mod/module/drill/on_select_use(atom/target) + . = ..() + if(!.) + return + if(!mod.wearer.Adjacent(target)) + return + if(istype(target, /turf/closed/mineral)) + var/turf/closed/mineral/mineral_turf = target + mineral_turf.gets_drilled(mod.wearer) + drain_power(use_power_cost) + +/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity) + SIGNAL_HANDLER + if(!istype(bumped_into, /turf/closed/mineral) || !drain_power(use_power_cost)) + return + var/turf/closed/mineral/mineral_turf = bumped_into + mineral_turf.gets_drilled(mod.wearer) + return COMPONENT_CANCEL_ATTACK_CHAIN + +//Ore Bag + +/obj/item/mod/module/orebag + name = "MOD ore bag module" + desc = "An integrated ore storage system installed into the suit, \ + this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \ + It's recommended by Nakamura Engineering to actually deposit that ore at local refineries." + icon_state = "ore" + module_type = MODULE_USABLE + complexity = 2 + use_power_cost = DEFAULT_CELL_DRAIN * 0.2 + incompatible_modules = list(/obj/item/mod/module/orebag) + cooldown_time = 0.5 SECONDS + var/list/ores = list() + +/obj/item/mod/module/orebag/on_equip() + RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/ore_pickup) + +/obj/item/mod/module/orebag/on_unequip() + UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED) + +/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced) + SIGNAL_HANDLER + + for(var/obj/item/stack/ore/ore in get_turf(mod.wearer)) + INVOKE_ASYNC(src, .proc/move_ore, ore) + playsound(src, "rustle", 50, TRUE) + +/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore) + for(var/obj/item/stack/stored_ore as anything in ores) + if(!ore.can_merge(stored_ore)) + continue + ore.merge(stored_ore) + if(QDELETED(ore)) + return + break + ore.forceMove(src) + ores += ore + +/obj/item/mod/module/orebag/on_use() + . = ..() + if(!.) + return + for(var/obj/item/ore as anything in ores) + ore.forceMove(drop_location()) + ores -= ore + drain_power(use_power_cost) diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm new file mode 100644 index 00000000000..858963cb45f --- /dev/null +++ b/code/modules/mod/modules/modules_visor.dm @@ -0,0 +1,99 @@ +//Visor modules for MODsuits + +//Base Visor + +/obj/item/mod/module/visor + name = "MOD visor module" + desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you." + module_type = MODULE_TOGGLE + complexity = 2 + active_power_cost = DEFAULT_CELL_DRAIN*0.3 + incompatible_modules = list(/obj/item/mod/module/visor) + cooldown_time = 0.5 SECONDS + var/hud_type + var/list/visor_traits = list() + +/obj/item/mod/module/visor/on_activation() + . = ..() + if(!.) + return + if(hud_type) + var/datum/atom_hud/hud = GLOB.huds[hud_type] + hud.add_hud_to(mod.wearer) + for(var/trait in visor_traits) + ADD_TRAIT(mod.wearer, trait, MOD_TRAIT) + mod.wearer.update_sight() + +/obj/item/mod/module/visor/on_deactivation() + . = ..() + if(!.) + return + if(hud_type) + var/datum/atom_hud/hud = GLOB.huds[hud_type] + hud.remove_hud_from(mod.wearer) + for(var/trait in visor_traits) + REMOVE_TRAIT(mod.wearer, trait, MOD_TRAIT) + mod.wearer.update_sight() + +//Medical Visor + +/obj/item/mod/module/visor/medhud + name = "MOD medical visor module" + desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \ + biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \ + access data such as patient files in a convenient readout. They say these also let you see behind you." + icon_state = "medhud_visor" + hud_type = DATA_HUD_MEDICAL_ADVANCED + visor_traits = list(TRAIT_MEDICAL_HUD) + +//Diagnostic Visor + +/obj/item/mod/module/visor/diaghud + name = "MOD diagnostic visor module" + desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \ + from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \ + and integrity of such. They say these also let you see behind you." + icon_state = "diaghud_visor" + hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED + visor_traits = list(TRAIT_DIAGNOSTIC_HUD) + +//Security Visor + +/obj/item/mod/module/visor/sechud + name = "MOD security visor module" + desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \ + plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \ + and generally know who to shoot. They say these also let you see behind you." + icon_state = "sechud_visor" + hud_type = DATA_HUD_SECURITY_ADVANCED + visor_traits = list(TRAIT_SECURITY_HUD) + +//Meson Visor + +/obj/item/mod/module/visor/meson + name = "MOD meson visor module" + desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \ + technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \ + through walls, regardless of lighting conditions. They say these also let you see behind you." + icon_state = "meson_visor" + visor_traits = list(TRAIT_MESON_VISION, TRAIT_SUPERMATTER_MADNESS_IMMUNE) + +//Thermal Visor + +/obj/item/mod/module/visor/thermal + name = "MOD thermal visor module" + desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \ + the thermal radiation output of objects near the user. While it can detect the heat output of even something as \ + small as a rodent, it still produces irritating red overlay. They say these also let you see behind you." + icon_state = "thermal_visor" + visor_traits = list(TRAIT_THERMAL_VISION) + +//Night Visor + +/obj/item/mod/module/visor/night + name = "MOD night visor module" + desc = "A heads-up display installed into the visor of the suit. Typical for both civilian and military applications, \ + this allows the user to perceive their surroundings while in complete darkness, enhancing the view by tenfold; \ + yet brightening everything into a spooky green glow. They say these also let you see behind you." + icon_state = "night_visor" + visor_traits = list(TRAIT_TRUE_NIGHT_VISION) diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 0bcae529653..cef3f1d3015 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -88,6 +88,9 @@ qdel(I) labels_left = initial(labels_left) //Yes, it's capped at its initial value +/obj/item/hand_labeler/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user) + return !mode + /obj/item/hand_labeler/borg name = "cyborg-hand labeler" diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 4c389562530..efac11ad1fa 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -1150,6 +1150,7 @@ id = "mod_welding" materials = list(/datum/material/iron = 500, /datum/material/glass = 500) build_path = /obj/item/mod/module/welding + department_type = MODULE_ENGINEERING /datum/design/module/mod_t_ray name = "MOD Module: T-Ray Scanner" @@ -1229,7 +1230,7 @@ id = "mod_reagent_scanner" materials = list(/datum/material/glass = 1000) build_path = /obj/item/mod/module/reagent_scanner - department_type = MODULE_MEDICAL + department_type = MODULE_SCIENCE /datum/design/module/mod_gps name = "MOD Module: Internal GPS" diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 65926816ef7..42ef60e1ea0 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -886,8 +886,9 @@ if(!proximity) return if(!istype(C)) - // applying this to vehicles is handled in the ridable element, see [/datum/element/ridable/proc/check_potion] - to_chat(user, span_warning("The potion can only be used on items or vehicles!")) + to_chat(user, span_warning("The potion can only be used on objects!")) + return + if(SEND_SIGNAL(C, COMSIG_SPEED_POTION_APPLIED, src, user) & SPEED_POTION_SUCCESSFUL) return if(isitem(C)) var/obj/item/I = C @@ -901,6 +902,13 @@ C.add_atom_colour("#FF0000", FIXED_COLOUR_PRIORITY) qdel(src) +/obj/item/slimepotion/speed/attackby_storage_insert(datum/component/storage, atom/storage_holder, mob/user) + . = ..() + if(!isitem(storage_holder)) + return + var/obj/item/storage_item = storage_holder + return storage_item.slowdown <= 0 + /obj/item/slimepotion/fireproof name = "slime chill potion" desc = "A potent chemical mix that will fireproof any article of clothing. Has three uses." diff --git a/tgstation.dme b/tgstation.dme index 34c59dbabe4..7852d99ae8e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3299,7 +3299,15 @@ #include "code\modules\mod\mod_types.dm" #include "code\modules\mod\mod_ui.dm" #include "code\modules\mod\modules\_module.dm" -#include "code\modules\mod\modules\modules.dm" +#include "code\modules\mod\modules\modules_antag.dm" +#include "code\modules\mod\modules\modules_engineering.dm" +#include "code\modules\mod\modules\modules_general.dm" +#include "code\modules\mod\modules\modules_medical.dm" +#include "code\modules\mod\modules\modules_science.dm" +#include "code\modules\mod\modules\modules_security.dm" +#include "code\modules\mod\modules\modules_service.dm" +#include "code\modules\mod\modules\modules_supply.dm" +#include "code\modules\mod\modules\modules_visor.dm" #include "code\modules\modular_computers\laptop_vendor.dm" #include "code\modules\modular_computers\computers\_modular_computer_shared.dm" #include "code\modules\modular_computers\computers\item\computer.dm"