diff --git a/code/__DEFINES/dcs/datum_signals.dm b/code/__DEFINES/dcs/datum_signals.dm index e6e15a052db..19682ccd80d 100644 --- a/code/__DEFINES/dcs/datum_signals.dm +++ b/code/__DEFINES/dcs/datum_signals.dm @@ -68,6 +68,8 @@ #define COMPONENT_TWOHANDED_BLOCK_WIELD (1<<0) ///from base of datum/component/two_handed/proc/unwield(mob/living/carbon/user): (/mob/user) #define COMSIG_TWOHANDED_UNWIELD "twohanded_unwield" +///from base of /datum/component/forces_doors_open/proc/force_open_door(obj/item): (datum/source, mob/user, atom/target) +#define COMSIG_TWOHANDED_WIELDED_TRY_WIELD_INTERACT "twohanded_wielded_try_wield_interact" // /datum/action diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index 4b23a6c59af..5243e3473fd 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -280,8 +280,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_WIELDED "wielded" /// Wires on this will have their titles randomized for those with SHOW_WIRES #define TRAIT_OBSCURED_WIRES "obscured_wires" -/// Forces open doors after a delay specific to the item -#define TRAIT_FORCES_OPEN_DOORS_ITEM "forces_open_doors_item_varient" /// Makes the item no longer spit out a visible message when thrown #define TRAIT_NO_THROWN_MESSAGE "no_message_when_thrown" /// Makes the item not display a message on storage insertion diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index afd12fa9612..7e4d94bf4d1 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -133,7 +133,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SUPERMATTER_IMMUNE" = TRAIT_SUPERMATTER_IMMUNE, "TRAIT_BUTCHER_HUMANS" = TRAIT_BUTCHERS_HUMANS, "TRAIT_CMAGGED" = TRAIT_CMAGGED, - "TRAIT_FORCES_OPEN_DOORS" = TRAIT_FORCES_OPEN_DOORS_ITEM, "TRAIT_OBSCURED_WIRES" = TRAIT_OBSCURED_WIRES, "TRAIT_XENO_INTERACTABLE" = TRAIT_XENO_INTERACTABLE, "TRAIT_NO_THROWN_MESSAGE" = TRAIT_NO_THROWN_MESSAGE, diff --git a/code/datums/components/forces_doors_open.dm b/code/datums/components/forces_doors_open.dm new file mode 100644 index 00000000000..c099a927d92 --- /dev/null +++ b/code/datums/components/forces_doors_open.dm @@ -0,0 +1,117 @@ +/** + * /datum/component/forces_doors_open + * + * This component allows item to pry open doors. + * + */ + +/datum/component/forces_doors_open + /// The time it takes to open the airlock when forced + var/time_to_open + /// Whether the airlock can be forced open while powered. + var/can_force_open_while_powered + /// Whether the airlock can be forced open while unpowered. + var/can_force_open_while_unpowered + /// Whether the firedoor can be opened. + var/can_open_firedoors + /// The sound played when the airlock is forced open. + var/open_sound + /// Indicates whether no sound should be played when opening. + var/no_sound + +/datum/component/forces_doors_open/Initialize( + time_to_open = 5 SECONDS, + can_force_open_while_powered = TRUE, + can_force_open_while_unpowered = TRUE, + can_open_firedoors = TRUE, + open_sound = 'sound/machines/airlock_alien_prying.ogg', + no_sound = FALSE) + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + + src.time_to_open = time_to_open + src.can_force_open_while_powered = can_force_open_while_powered + src.can_force_open_while_unpowered = can_force_open_while_unpowered + src.can_open_firedoors = can_open_firedoors + src.open_sound = open_sound + src.no_sound = no_sound + + RegisterSignal(parent, COMSIG_INTERACTING, PROC_REF(on_interact)) + +/datum/component/forces_doors_open/proc/on_interact(datum/source, mob/user, atom/target) + SIGNAL_HANDLER // COMSIG_INTERACTING + + if(check_intent(user)) + return + + if(try_to_open_firedoor(target)) + return ITEM_INTERACT_COMPLETE + + if(try_to_force_open_airlock(user, target)) + return ITEM_INTERACT_COMPLETE + +/// check is user in harm intent +/datum/component/forces_doors_open/proc/check_intent(mob/user) + if(user.a_intent == INTENT_HARM) + return TRUE + +/// try to open firedoor +/datum/component/forces_doors_open/proc/try_to_open_firedoor(atom/target) + if(can_open_firedoors && istype(target, /obj/machinery/door/firedoor)) // open firedoor and dont open blastdoors and windowdoors + INVOKE_ASYNC(src, PROC_REF(open_unpowered_door), target) + return TRUE + +/// try to force open airlock +/datum/component/forces_doors_open/proc/try_to_force_open_airlock(mob/user, obj/machinery/door/airlock/airlock) + if(!istype(airlock, /obj/machinery/door/airlock)) // only airlocks have arePowerSystemsOn() + return + + if(SEND_SIGNAL(parent, COMSIG_TWOHANDED_WIELDED_TRY_WIELD_INTERACT) && airlock.arePowerSystemsOn()) + to_chat(user, "You need to be wielding [parent] to do that!") + return TRUE + + if(!airlock.density) + return TRUE + + // open unpowered + if(can_force_open_while_unpowered && !airlock.arePowerSystemsOn()) + INVOKE_ASYNC(src, PROC_REF(open_unpowered_door), airlock) + return TRUE + + // open powered + if(can_force_open_while_powered) + INVOKE_ASYNC(src, PROC_REF(open_powered_airlock), airlock, user) + return TRUE + +/// open airlock with delay +/datum/component/forces_doors_open/proc/open_powered_airlock(obj/machinery/door/airlock/airlock, mob/user) + if(!no_sound) + playsound(parent, open_sound, 100, 1) + + if(do_after_once(user, time_to_open, target = airlock, attempt_cancel_message = "You decide to stop prying [airlock] with [parent].")) + if(airlock.open(TRUE)) + return // successfully opened + + // opening failed + if(airlock.density) + to_chat(user, "Despite your attempts, [airlock] refuses to open.") + +/// open door without checks +/datum/component/forces_doors_open/proc/open_unpowered_door(obj/machinery/door/door) + door.open(TRUE) + +/// subtype for mantis blades +/datum/component/forces_doors_open/mantis/on_interact(datum/source, mob/user, atom/target) + if(check_intent(user)) + return + + if(try_to_open_firedoor(target)) + return ITEM_INTERACT_COMPLETE + + var/obj/item/melee/mantis_blade/secondblade = user.get_inactive_hand() + if(!istype(secondblade, /obj/item/melee/mantis_blade)) + to_chat(user, "You need a second [parent] to pry open doors!") + return ITEM_INTERACT_COMPLETE + + if(try_to_force_open_airlock(user, target)) + return ITEM_INTERACT_COMPLETE diff --git a/code/datums/components/two_handed.dm b/code/datums/components/two_handed.dm index df36672ac54..23e1856b65d 100644 --- a/code/datums/components/two_handed.dm +++ b/code/datums/components/two_handed.dm @@ -111,6 +111,7 @@ RegisterSignal(parent, COMSIG_ITEM_SHARPEN_ACT, PROC_REF(on_sharpen)) RegisterSignal(parent, COMSIG_CARBON_UPDATE_HANDCUFFED, PROC_REF(on_handcuff_user)) RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand)) + RegisterSignal(parent, COMSIG_TWOHANDED_WIELDED_TRY_WIELD_INTERACT, PROC_REF(check_unwielded)) // Remove all siginals registered to the parent item /datum/component/two_handed/UnregisterFromParent() @@ -307,6 +308,15 @@ // Clear any old refrence to an item that should be gone now offhand_item = null +/** + * Check if item is unwielded + * + * returns TRUE if unwielded + */ +/datum/component/two_handed/proc/check_unwielded() + SIGNAL_HANDLER // COMSIG_TWOHANDED_WIELDED_TRY_WIELD_INTERACT + return wielded ? FALSE : TRUE; + /** * on_attack triggers on attack with the parent item */ diff --git a/code/datums/uplink_items/uplink_general.dm b/code/datums/uplink_items/uplink_general.dm index ecbe37308bc..43de5d7988b 100644 --- a/code/datums/uplink_items/uplink_general.dm +++ b/code/datums/uplink_items/uplink_general.dm @@ -949,6 +949,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/autosurgeon/organ/syndicate/oneuse/scope_eyes cost = 10 +/datum/uplink_item/cyber_implants/mantis_kit + name = "'Naginata' Mantis Blades Kit" + desc = "A pair of devastating 'Naginata' concealable mantis blades, which retract into the arms of the user. \ + Their monomolecular edges will easily tear through flesh and armor alike, and can even pry open airlocks when used together. \ + When both blades are equipped, they enable the user to perform double attacks. \ + Can be used to parry incoming melee attacks." + reference = "MBK" + item = /obj/item/storage/box/syndie_kit/syndie_mantis + cost = 60 //////////////////////////////////////// // MARK: POINTLESS BADASSERY diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index df0d3bbd963..5fdc1775276 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1190,9 +1190,6 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) /obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I) if(operating) return - if(HAS_TRAIT(I, TRAIT_FORCES_OPEN_DOORS_ITEM)) - force_open_with_item(user, I) - return var/beingcrowbarred = FALSE if(I.tool_behaviour == TOOL_CROWBAR && I.tool_use_check(user, 0)) beingcrowbarred = TRUE @@ -1237,31 +1234,6 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) if(density && !open(1)) to_chat(user, "Despite your attempts, [src] refuses to open.") -/obj/machinery/door/airlock/proc/force_open_with_item(mob/living/user, obj/item/I) - /// Time it takes to open an airlock with an item with the TRAIT_FORCES_OPEN_DOORS_ITEM trait, 5 seconds for wielded items, 10 seconds for nonwielded items - var/time_to_open_airlock = 10 SECONDS - /// Can we open the airlock while unpowered? Wielded item's can't, but unwielded items can - var/can_force_open_while_unpowered = TRUE - if(I.GetComponent(/datum/component/two_handed)) - can_force_open_while_unpowered = FALSE - time_to_open_airlock = 5 SECONDS - if(!HAS_TRAIT(I, TRAIT_WIELDED)) - to_chat(user, "You need to be wielding [I] to do that!") - return - if(!density || prying_so_hard) - return - playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1) //is it aliens or just the CE being a dick? - if(!arePowerSystemsOn() && can_force_open_while_unpowered) - open(TRUE) - return - prying_so_hard = TRUE //so you dont pry the door when you are already trying to pry it - var/result = do_after(user, time_to_open_airlock, target = src) - prying_so_hard = FALSE - if(result) - open(TRUE) - if(density && !open(TRUE)) - to_chat(user, "Despite your attempts, [src] refuses to open.") - /obj/machinery/door/airlock/open(forced=0) if(operating || welded || locked || emagged) return 0 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index fd9fd32a64c..3ce720c1f8c 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -260,9 +260,6 @@ if(HAS_TRAIT(src, TRAIT_CMAGGED) && I.can_clean()) //If the cmagged door is being hit with cleaning supplies, don't open it, it's being cleaned! return - if(user.a_intent != INTENT_HARM && HAS_TRAIT(I, TRAIT_FORCES_OPEN_DOORS_ITEM)) - try_to_crowbar(user, I) - return TRUE else if(!(I.flags & NOBLUDGEON) && user.a_intent != INTENT_HARM) try_to_activate_door(user) return TRUE diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index ce5563fcb71..27366ab2e98 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -579,3 +579,12 @@ /obj/item/storage/box/syndie_kit/forgers_kit/populate_contents() new /obj/item/stamp/chameleon(src) new /obj/item/pen/chameleon(src) + +/obj/item/storage/box/syndie_kit/syndie_mantis + name = "\improper Mantis Blades kit" + desc = "A sleek box marked with a Cybersun logo. The label says it contains a pair of CX-12 'Naginata' mantis blades and accompanying autosurgeons." + +/obj/item/storage/box/syndie_kit/syndie_mantis/populate_contents() + new /obj/item/autosurgeon/organ/syndicate/oneuse/syndie_mantis(src) + new /obj/item/autosurgeon/organ/syndicate/oneuse/syndie_mantis/l(src) + diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index 31f6bd289c6..72a5e86ec3e 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -28,7 +28,7 @@ /obj/item/fireaxe/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_FORCES_OPEN_DOORS_ITEM, ROUNDSTART_TRAIT) + AddComponent(/datum/component/forces_doors_open) AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.7, _parryable_attack_types = MELEE_ATTACK, _parry_cooldown = (10 / 3) SECONDS, _requires_two_hands = TRUE) // 2.3333 seconds of cooldown for 30% uptime AddComponent(/datum/component/two_handed, force_unwielded = force_unwielded, force_wielded = force_wielded, icon_wielded = "[base_icon_state]1") @@ -1070,8 +1070,8 @@ /obj/item/supermatter_halberd/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_FORCES_OPEN_DOORS_ITEM, ROUNDSTART_TRAIT) ADD_TRAIT(src, TRAIT_SUPERMATTER_IMMUNE, ROUNDSTART_TRAIT) //so it can't be dusted by the SM + AddComponent(/datum/component/forces_doors_open) AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.25, _parryable_attack_types = ALL_ATTACK_TYPES, _parry_cooldown = (4 / 3) SECONDS, _requires_two_hands = TRUE) // 0.3333 seconds of cooldown for 75% uptime AddComponent(/datum/component/two_handed, force_wielded = 40, force_unwielded = force, icon_wielded = "[base_icon_state]1") diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index c6fd006e472..b485eca0da4 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -149,7 +149,7 @@ /obj/item/melee/arm_blade/Initialize(mapload, silent, new_parent_action) . = ..() parent_action = new_parent_action - ADD_TRAIT(src, TRAIT_FORCES_OPEN_DOORS_ITEM, ROUNDSTART_TRAIT) + AddComponent(/datum/component/forces_doors_open, time_to_open = 10 SECONDS) /obj/item/melee/arm_blade/Destroy() if(parent_action) diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index a5e0ff230ee..24b5de832aa 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -399,3 +399,15 @@ reagents_list = list("fuel" = 30) build_path = /obj/item/chemical_canister/pyrotechnics category = list("Weapons") + +/datum/design/nt_mantis + name = "'Scylla' mantis blade implant" + desc = "A reverse-engineered mantis blade implant. While the monomolecular edge was lost, they remain deadly weapons." + id = "mantis_blade_nt" + req_tech = list("materials" = 7, "engineering" = 6, "combat" = 7, "syndicate" = 6) + build_type = PROTOLATHE | MECHFAB + construction_time = 6 SECONDS + materials = list(MAT_METAL = 10000, MAT_SILVER = 2000, MAT_GOLD = 2000, MAT_TITANIUM = 3000, MAT_DIAMOND = 4000) + build_path = /obj/item/organ/internal/cyberimp/arm/nt_mantis + category = list("Medical", "Weapons") + diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 03738d2aa37..d3a4834906d 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -836,3 +836,99 @@ if(emp_proof) return muscle_implant.emp_act(severity, owner) + +// Mantis blades + +/obj/item/melee/mantis_blade + name = "mantis blade" + desc = "A blade designed to be hidden just beneath the skin. The brain is directly linked to this bad boy, allowing it to spring into action. \ + When both blades are equipped, they enable the user to perform double attacks." + icon = 'icons/obj/weapons/melee.dmi' + lefthand_file = 'icons/mob/inhands/implants_lefthand.dmi' + righthand_file = 'icons/mob/inhands/implants_righthand.dmi' + hitsound = 'sound/weapons/bladeslice.ogg' + new_attack_chain = TRUE + var/double_attack = TRUE + var/double_attack_cd = 1.5 // seconds, so every second attack + sharp = TRUE + w_class = WEIGHT_CLASS_BULKY + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "lacerated", "ripped", "diced", "cut") + +/obj/item/melee/mantis_blade/equipped(mob/user, slot, initial) + . = ..() + if(slot == ITEM_SLOT_LEFT_HAND) + transform = matrix(-1, 0, 0, 0, 1, 0) + else + transform = null + +// make double attack if blades in both hands and not on CD +/obj/item/melee/mantis_blade/attack(mob/living/target, mob/living/user, params) + var/obj/item/melee/mantis_blade/secondblade = user.get_inactive_hand() + if(!istype(secondblade, /obj/item/melee/mantis_blade) || !double_attack) + return ..() + + double_attack(target, user, params, secondblade) + return FINISH_ATTACK + +/obj/item/melee/mantis_blade/proc/double_attack(mob/living/target, mob/living/user, params, obj/item/melee/mantis_blade/secondblade) + // first attack + single_attack(target, user, params) + user.changeNext_move(CLICK_CD_MELEE) + // second attack + addtimer(CALLBACK(secondblade, PROC_REF(single_attack), target, user, params), 0.2 SECONDS) // not instant second attack + +/obj/item/melee/mantis_blade/proc/single_attack(mob/living/target, mob/living/user, params) + if(QDELETED(src)) + return + double_attack = FALSE + attack(target, user, params) + addtimer(VARSET_CALLBACK(src, double_attack, TRUE), double_attack_cd SECONDS) + +/obj/item/melee/mantis_blade/Initialize(mapload) + . = ..() + AddComponent(/datum/component/forces_doors_open/mantis) + +/obj/item/melee/mantis_blade/syndicate + name = "'Naginata' mantis blade" + icon_state = "syndie_mantis" + item_state = "syndie_mantis" + force = 20 + armour_penetration_percentage = 30 + +/obj/item/melee/mantis_blade/syndicate/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.35, _parryable_attack_types = NON_PROJECTILE_ATTACKS, _parry_cooldown = (4 / 3) SECONDS) // 0.3333 seconds of cooldown for 75% uptime, non projectile + +/obj/item/melee/mantis_blade/nt + name = "'Scylla' mantis blade" + icon_state = "mantis" + item_state = "mantis" + force = 18 + +/obj/item/melee/mantis_blade/nt/Initialize(mapload) + . = ..() + AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.35, _parryable_attack_types = NON_PROJECTILE_ATTACKS, _parry_cooldown = (5 / 3) SECONDS) // 0.666667 seconds for 60% uptime, non projectile + +// Mantis blades implants +/obj/item/organ/internal/cyberimp/arm/syndie_mantis + name = "'Naginata' mantis blade implants" + desc = "A powerful and concealable mantis blade with a monomolecular edge, produced by Cybersun Industries. Cuts through flesh and armor alike with ease." + origin_tech = "materials=5;combat=5;biotech=5;syndicate=4" + contents = newlist(/obj/item/melee/mantis_blade/syndicate) + icon_state = "syndie_mantis" + icon = 'icons/obj/weapons/melee.dmi' + +/obj/item/organ/internal/cyberimp/arm/syndie_mantis/l + parent_organ = "l_arm" + +/obj/item/organ/internal/cyberimp/arm/nt_mantis + name = "'Scylla' mantis blade implant" + desc = "A reverse-engineered mantis blade design produced by Nanotrasen. While still quite deadly, the loss of the monomolecular blade has drastically reduced its armor penetration capability." + origin_tech = "materials=5;combat=5;biotech=5;syndicate=4" + contents = newlist(/obj/item/melee/mantis_blade/nt) + icon_state = "mantis" + icon = 'icons/obj/weapons/melee.dmi' + +/obj/item/organ/internal/cyberimp/arm/nt_mantis/l + parent_organ = "l_arm" + diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index 92ee6f5ebfc..3c64e68538e 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -152,4 +152,10 @@ /obj/item/autosurgeon/organ/syndicate/oneuse/hardened_heart starting_organ = /obj/item/organ/internal/heart/cybernetic/upgraded/hardened +/obj/item/autosurgeon/organ/syndicate/oneuse/syndie_mantis + starting_organ = /obj/item/organ/internal/cyberimp/arm/syndie_mantis + +/obj/item/autosurgeon/organ/syndicate/oneuse/syndie_mantis/l + starting_organ = /obj/item/organ/internal/cyberimp/arm/syndie_mantis/l + #undef INFINITE diff --git a/icons/mob/inhands/implants_lefthand.dmi b/icons/mob/inhands/implants_lefthand.dmi index 74340cf1dc6..5206dc0cb98 100644 Binary files a/icons/mob/inhands/implants_lefthand.dmi and b/icons/mob/inhands/implants_lefthand.dmi differ diff --git a/icons/mob/inhands/implants_righthand.dmi b/icons/mob/inhands/implants_righthand.dmi index 36900ec8a30..4d4d29ea56d 100644 Binary files a/icons/mob/inhands/implants_righthand.dmi and b/icons/mob/inhands/implants_righthand.dmi differ diff --git a/icons/obj/weapons/melee.dmi b/icons/obj/weapons/melee.dmi index 8ee6b5cd9ec..87dfa467f41 100644 Binary files a/icons/obj/weapons/melee.dmi and b/icons/obj/weapons/melee.dmi differ diff --git a/paradise.dme b/paradise.dme index 0d4c052efc1..06372adc927 100644 --- a/paradise.dme +++ b/paradise.dme @@ -447,6 +447,7 @@ #include "code\datums\components\edit_complainer.dm" #include "code\datums\components\emissive_blocker.dm" #include "code\datums\components\footstep.dm" +#include "code\datums\components\forces_doors_open.dm" #include "code\datums\components\fullauto.dm" #include "code\datums\components\ghost_direct_control.dm" #include "code\datums\components\label.dm"