diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm index 076efefc7d5..2b931df61e4 100644 --- a/code/__DEFINES/ai.dm +++ b/code/__DEFINES/ai.dm @@ -108,7 +108,7 @@ ///The implant the AI was created from #define BB_MOD_IMPLANT "BB_mod_implant" ///Range for a MOD AI controller. -#define MOD_AI_RANGE 100 +#define MOD_AI_RANGE 200 ///Vending machine AI controller blackboard keys #define BB_VENDING_CURRENT_TARGET "BB_vending_current_target" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 514dedc6c71..dd300d721a4 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -594,7 +594,7 @@ #define SHOES_LAYER 20 /// Ears layer (Spessmen have ears? Wow) #define EARS_LAYER 19 -/// Suit layer (armor, hardsuits, etc.) +/// Suit layer (armor, coats, etc.) #define SUIT_LAYER 18 /// Glasses layer #define GLASSES_LAYER 17 diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm index b6f8596f05d..fd298f3bb88 100644 --- a/code/modules/clothing/head/_head.dm +++ b/code/modules/clothing/head/_head.dm @@ -1,8 +1,6 @@ /obj/item/clothing/head name = BODY_ZONE_HEAD icon = 'icons/obj/clothing/hats.dmi' - icon_state = "tophat" - inhand_icon_state = "that" body_parts_covered = HEAD slot_flags = ITEM_SLOT_HEAD var/blockTracking = 0 //For AI tracking diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index c8d522ddf5a..9a110965423 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -426,3 +426,42 @@ if(client?.combo_hud_enabled && client?.prefs?.toggles & COMBOHUD_LIGHTING) return LIGHTING_PLANE_ALPHA_INVISIBLE return initial(lighting_alpha) + +/// Returns a generic path of the object based on the slot +/proc/get_path_by_slot(slot_id) + switch(slot_id) + if(ITEM_SLOT_BACK) + return /obj/item/storage/backpack + if(ITEM_SLOT_MASK) + return /obj/item/clothing/mask + if(ITEM_SLOT_NECK) + return /obj/item/clothing/neck + if(ITEM_SLOT_HANDCUFFED) + return /obj/item/restraints/handcuffs + if(ITEM_SLOT_LEGCUFFED) + return /obj/item/restraints/legcuffs + if(ITEM_SLOT_BELT) + return /obj/item/storage/belt + if(ITEM_SLOT_ID) + return /obj/item/card/id/advanced + if(ITEM_SLOT_EARS) + return /obj/item/clothing/ears + if(ITEM_SLOT_EYES) + return /obj/item/clothing/glasses + if(ITEM_SLOT_GLOVES) + return /obj/item/clothing/gloves + if(ITEM_SLOT_HEAD) + return /obj/item/clothing/head + if(ITEM_SLOT_FEET) + return /obj/item/clothing/shoes + if(ITEM_SLOT_OCLOTHING) + return /obj/item/clothing/suit + if(ITEM_SLOT_ICLOTHING) + return /obj/item/clothing/under + if(ITEM_SLOT_LPOCKET) + return /obj/item + if(ITEM_SLOT_RPOCKET) + return /obj/item + if(ITEM_SLOT_SUITSTORE) + return /obj/item + return null diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index 6a2b6a2ffa7..9fbea364319 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -145,7 +145,7 @@ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) return FALSE for(var/obj/item/mod/module/module as anything in modules) - if(!module.active) + if(!module.active || module.allowed_inactive) continue module.on_deactivation(display_message = FALSE) activating = TRUE diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm index 2c71d366d72..fc7d0c55ac1 100644 --- a/code/modules/mod/mod_clothes.dm +++ b/code/modules/mod/mod_clothes.dm @@ -9,7 +9,6 @@ heat_protection = HEAD cold_protection = HEAD obj_flags = IMMUTABLE_SLOW - var/alternate_layer = NECK_LAYER /obj/item/clothing/suit/mod name = "MOD chestplate" diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index 6927c600eb3..df59c077e0a 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -622,13 +622,12 @@ CRASH("[src] tried to set skin while active!") skin = new_skin var/list/used_skin = theme.skins[new_skin] - alternate_worn_layer = used_skin[CONTROL_LAYER] + if(used_skin[CONTROL_LAYER]) + alternate_worn_layer = used_skin[CONTROL_LAYER] var/list/skin_updating = mod_parts + src for(var/obj/item/part as anything in skin_updating) - if(used_skin[MOD_ICON_OVERRIDE]) - part.icon = used_skin[MOD_ICON_OVERRIDE] - if(used_skin[MOD_WORN_ICON_OVERRIDE]) - part.worn_icon = used_skin[MOD_WORN_ICON_OVERRIDE] + part.icon = used_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi' + part.worn_icon = used_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi' part.icon_state = "[skin]-[initial(part.icon_state)]" for(var/obj/item/clothing/part as anything in mod_parts) var/used_category diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm index 467ce90330e..69736850748 100644 --- a/code/modules/mod/mod_types.dm +++ b/code/modules/mod/mod_types.dm @@ -242,11 +242,11 @@ /obj/item/mod/control/pre_equipped/responsory/commander insignia_type = /obj/item/mod/module/insignia/commander - additional_module = /obj/item/mod/module/noslip + additional_module = /obj/item/mod/module/power_kick /obj/item/mod/control/pre_equipped/responsory/security insignia_type = /obj/item/mod/module/insignia/security - additional_module = /obj/item/mod/module/gps + additional_module = /obj/item/mod/module/pepper_shoulders /obj/item/mod/control/pre_equipped/responsory/engineer insignia_type = /obj/item/mod/module/insignia/engineer @@ -281,11 +281,11 @@ /obj/item/mod/control/pre_equipped/responsory/inquisitory/commander insignia_type = /obj/item/mod/module/insignia/commander - additional_module = /obj/item/mod/module/noslip + additional_module = /obj/item/mod/module/power_kick /obj/item/mod/control/pre_equipped/responsory/inquisitory/security insignia_type = /obj/item/mod/module/insignia/security - additional_module = /obj/item/mod/module/gps + additional_module = /obj/item/mod/module/pepper_shoulders /obj/item/mod/control/pre_equipped/responsory/inquisitory/medic insignia_type = /obj/item/mod/module/insignia/medic diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm index 328c0a7b344..1cf5b66269b 100644 --- a/code/modules/mod/modules/_module.dm +++ b/code/modules/mod/modules/_module.dm @@ -43,6 +43,8 @@ var/list/pinned_to = list() /// If we're allowed to use this module while phased out. var/allowed_in_phaseout = FALSE + /// If we're allowed to use this module while the suit is disabled. + var/allowed_inactive = FALSE /// Timer for the cooldown COOLDOWN_DECLARE(cooldown_timer) @@ -68,33 +70,10 @@ if(HAS_TRAIT(user, TRAIT_DIAGNOSTIC_HUD)) . += span_notice("Complexity level: [complexity]") -/// Called from MODsuit's install() proc, so when the module is installed. -/obj/item/mod/module/proc/on_install() - return - -/// Called from MODsuit's uninstall() proc, so when the module is uninstalled. -/obj/item/mod/module/proc/on_uninstall(deleting = FALSE) - return - -/// Called when the MODsuit is activated -/obj/item/mod/module/proc/on_suit_activation() - return - -/// Called when the MODsuit is deactivated -/obj/item/mod/module/proc/on_suit_deactivation(deleting = FALSE) - return - -/// Called when the MODsuit is equipped -/obj/item/mod/module/proc/on_equip() - return - -/// Called when the MODsuit is unequipped -/obj/item/mod/module/proc/on_unequip() - return /// Called when the module is selected from the TGUI, radial or the action button /obj/item/mod/module/proc/on_select() - if(!mod.active || mod.activating || module_type == MODULE_PASSIVE) + if(((!mod.active || mod.activating) && !allowed_inactive) || module_type == MODULE_PASSIVE) if(mod.wearer) balloon_alert(mod.wearer, "not active!") return @@ -212,6 +191,30 @@ /obj/item/mod/module/proc/on_active_process(delta_time) return +/// Called from MODsuit's install() proc, so when the module is installed. +/obj/item/mod/module/proc/on_install() + return + +/// Called from MODsuit's uninstall() proc, so when the module is uninstalled. +/obj/item/mod/module/proc/on_uninstall(deleting = FALSE) + return + +/// Called when the MODsuit is activated +/obj/item/mod/module/proc/on_suit_activation() + return + +/// Called when the MODsuit is deactivated +/obj/item/mod/module/proc/on_suit_deactivation(deleting = FALSE) + return + +/// Called when the MODsuit is equipped +/obj/item/mod/module/proc/on_equip() + return + +/// Called when the MODsuit is unequipped +/obj/item/mod/module/proc/on_unequip() + return + /// Drains power from the suit charge /obj/item/mod/module/proc/drain_power(amount) if(!check_power(amount)) diff --git a/code/modules/mod/modules/module_kinesis.dm b/code/modules/mod/modules/module_kinesis.dm index 840ae7d45fc..efb33a72659 100644 --- a/code/modules/mod/modules/module_kinesis.dm +++ b/code/modules/mod/modules/module_kinesis.dm @@ -243,11 +243,11 @@ var/list/view = getviewsize(kinesis_user.client.view) icon_x *= view[1]/FULLSCREEN_OVERLAY_RESOLUTION_X icon_y *= view[2]/FULLSCREEN_OVERLAY_RESOLUTION_Y - var/our_x = round(icon_x / world.icon_size) - var/our_y = round(icon_y / world.icon_size) + var/our_x = round(icon_x / world.icon_size, 1) + var/our_y = round(icon_y / world.icon_size, 1) var/mob_x = kinesis_user.x var/mob_y = kinesis_user.y var/mob_z = kinesis_user.z given_turf = locate(mob_x+our_x-round(view[1]/2),mob_y+our_y-round(view[2]/2),mob_z) - given_x = round(icon_x - world.icon_size * our_x) - given_y = round(icon_y - world.icon_size * our_y) + given_x = round(icon_x - world.icon_size * our_x, 1) + given_y = round(icon_y - world.icon_size * our_y, 1) diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm index 2de81c0222b..505e6953dcd 100644 --- a/code/modules/mod/modules/module_pathfinder.dm +++ b/code/modules/mod/modules/module_pathfinder.dm @@ -3,8 +3,8 @@ 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; \ + very control unit of the suit, allowing it flight at highway speeds using the suit's access locks \ + to navigate through the station, 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 person at any time. \ The implant is stored in the module and needs to be injected in a human to function. \ diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm index 6ee8deb0ad9..eabac9359c6 100644 --- a/code/modules/mod/modules/modules_antag.dm +++ b/code/modules/mod/modules/modules_antag.dm @@ -18,8 +18,10 @@ use_mod_colors = TRUE /// Whether or not this module removes pressure protection. var/remove_pressure_protection = TRUE - /// Slowdown added to the suit. - var/added_slowdown = -0.5 + /// Speed added to the control unit. + var/speed_added = 0.5 + /// Speed that we actually added. + var/actual_speed_added = 0 /// Armor values added to the suit parts. var/list/armor_values = list(MELEE = 25, BULLET = 30, LASER = 15, ENERGY = 15) /// List of parts of the suit that are spaceproofed, for giving them back the pressure protection. @@ -38,7 +40,8 @@ if(!.) return playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - mod.slowdown += added_slowdown + actual_speed_added = max(0, min(mod.slowdown_active, speed_added)) + mod.slowdown -= actual_speed_added mod.wearer.update_equipment_speed_mods() var/list/parts = mod.mod_parts + mod for(var/obj/item/part as anything in parts) @@ -56,7 +59,7 @@ return if(!deleting) playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - mod.slowdown -= added_slowdown + mod.slowdown += actual_speed_added mod.wearer.update_equipment_speed_mods() var/list/parts = mod.mod_parts + mod var/list/removed_armor = armor_values.Copy() @@ -288,3 +291,165 @@ /obj/projectile/bullet/incendiary/backblast/flamethrower range = 6 + +///Power kick - Lets the user launch themselves at someone to kick them. +/obj/item/mod/module/power_kick + name = "MOD power kick module" + desc = "This module uses high-power myomer to generate an incredible amount of energy, transferred into the power of a kick." + icon_state = "power_kick" + module_type = MODULE_ACTIVE + removable = FALSE + use_power_cost = DEFAULT_CHARGE_DRAIN*5 + incompatible_modules = list(/obj/item/mod/module/power_kick) + cooldown_time = 5 SECONDS + /// Damage on kick. + var/damage = 20 + /// The wound bonus of the kick. + var/wounding_power = 35 + /// How long we knockdown for on the kick. + var/knockdown_time = 2 SECONDS + +/obj/item/mod/module/power_kick/on_select_use(atom/target) + . = ..() + if(!.) + return + mod.wearer.visible_message(span_warning("[mod.wearer] starts charging a kick!"), \ + blind_message = span_hear("You hear a charging sound.")) + playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE) + balloon_alert(mod.wearer, "you start charging...") + animate(mod.wearer, 0.3 SECONDS, pixel_z = 16, flags = ANIMATION_RELATIVE|SINE_EASING|EASE_OUT) + addtimer(CALLBACK(mod.wearer, /atom.proc/SpinAnimation, 3, 2), 0.3 SECONDS) + if(!do_after(mod.wearer, 1 SECONDS, target = mod)) + animate(mod.wearer, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE|SINE_EASING|EASE_IN) + return + animate(mod.wearer) + drain_power(use_power_cost) + playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE) + var/angle = get_angle(mod.wearer, target) + 180 + mod.wearer.transform = mod.wearer.transform.Turn(angle) + RegisterSignal(mod.wearer, COMSIG_MOVABLE_IMPACT, .proc/on_throw_impact) + mod.wearer.throw_at(target, range = 7, speed = 2, thrower = mod.wearer, spin = FALSE, gentle = TRUE, callback = CALLBACK(src, .proc/on_throw_end, mod.wearer, -angle)) + +/obj/item/mod/module/power_kick/proc/on_throw_end(mob/user, angle) + if(!user) + return + user.transform = user.transform.Turn(angle) + animate(user, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE|SINE_EASING|EASE_IN) + +/obj/item/mod/module/power_kick/proc/on_throw_impact(mob/living/source, atom/target, datum/thrownthing/thrownthing) + SIGNAL_HANDLER + + UnregisterSignal(source, COMSIG_MOVABLE_IMPACT) + if(!mod?.wearer) + return + if(isliving(target)) + var/mob/living/living_target = target + living_target.apply_damage(damage, BRUTE, mod.wearer.zone_selected, wound_bonus = wounding_power) + living_target.Knockdown(knockdown_time) + else if(target.uses_integrity) + target.take_damage(damage, BRUTE, MELEE) + else + return + mod.wearer.do_attack_animation(target, ATTACK_EFFECT_SMASH) + +///Chameleon - lets the suit disguise as any item that would fit on that slot. +/obj/item/mod/module/chameleon + name = "MOD chameleon module" + desc = "A module using chameleon technology to disguise the suit as another object." + icon_state = "chameleon" + module_type = MODULE_USABLE + complexity = 2 + incompatible_modules = list(/obj/item/mod/module/chameleon) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + /// A list of all the items the suit can disguise as. + var/list/possible_disguises = list() + /// The path of the item we're disguised as. + var/obj/item/current_disguise + +/obj/item/mod/module/chameleon/on_install() + var/list/all_disguises = sort_list(subtypesof(get_path_by_slot(mod.slot_flags)), /proc/cmp_typepaths_asc) + for(var/clothing_path in all_disguises) + var/obj/item/clothing = clothing_path + if(!initial(clothing.icon_state)) + continue + var/chameleon_item_name = "[initial(clothing.name)] ([initial(clothing.icon_state)])" + possible_disguises[chameleon_item_name] = clothing_path + +/obj/item/mod/module/chameleon/on_uninstall(deleting = FALSE) + if(current_disguise) + return_look() + possible_disguises = null + +/obj/item/mod/module/chameleon/on_use() + if(mod.active || mod.activating) + balloon_alert(mod.wearer, "suit active!") + return + . = ..() + if(!.) + return + if(current_disguise) + return_look() + return + var/picked_name = tgui_input_list(mod.wearer, "Select look to change into", "Chameleon Settings", possible_disguises) + if(!possible_disguises[picked_name] || mod.active || mod.activating) + return + current_disguise = possible_disguises[picked_name] + update_look() + +/obj/item/mod/module/chameleon/proc/update_look() + mod.name = initial(current_disguise.name) + mod.desc = initial(current_disguise.desc) + mod.icon_state = initial(current_disguise.icon_state) + mod.icon = initial(current_disguise.icon) + mod.worn_icon = initial(current_disguise.worn_icon) + mod.alternate_worn_layer = initial(current_disguise.alternate_worn_layer) + mod.lefthand_file = initial(current_disguise.lefthand_file) + mod.righthand_file = initial(current_disguise.righthand_file) + mod.worn_icon_state = initial(current_disguise.worn_icon_state) + mod.inhand_icon_state = initial(current_disguise.inhand_icon_state) + mod.wearer.update_clothing(mod.slot_flags) + RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/return_look) + +/obj/item/mod/module/chameleon/proc/return_look() + mod.name = "[mod.theme.name] [initial(mod.name)]" + mod.desc = "[initial(mod.desc)] [mod.theme.desc]" + mod.icon_state = "[mod.skin]-[initial(mod.icon_state)]" + var/list/mod_skin = mod.theme.skins[mod.skin] + mod.icon = mod_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi' + mod.worn_icon = mod_skin[MOD_WORN_ICON_OVERRIDE] || 'icons/mob/clothing/modsuit/mod_clothing.dmi' + mod.alternate_worn_layer = mod_skin[CONTROL_LAYER] + mod.lefthand_file = initial(mod.lefthand_file) + mod.righthand_file = initial(mod.righthand_file) + mod.worn_icon_state = null + mod.inhand_icon_state = null + mod.wearer.update_clothing(mod.slot_flags) + current_disguise = null + UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) + +///Plate Compression - Compresses the suit to normal size +/obj/item/mod/module/plate_compression + name = "MOD plate compression module" + desc = "A module that keeps the suit in a very tightly fit state, lowering the overall size. \ + Due to the pressure on all the parts, typical storage modules do not fit." + icon_state = "plate_compression" + complexity = 2 + incompatible_modules = list(/obj/item/mod/module/plate_compression, /obj/item/mod/module/storage) + /// The size we set the suit to. + var/new_size = WEIGHT_CLASS_NORMAL + /// The suit's size before the module is installed. + var/old_size + +/obj/item/mod/module/plate_compression/on_install() + old_size = mod.w_class + mod.w_class = new_size + +/obj/item/mod/module/plate_compression/on_uninstall(deleting = FALSE) + mod.w_class = old_size + old_size = null + if(!mod.loc) + return + var/datum/component/storage/holding_storage = mod.loc.GetComponent(/datum/component/storage) + if(!holding_storage || holding_storage.max_w_class >= mod.w_class) + return + mod.forceMove(drop_location()) diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm index 343000e19dd..87a5c18885a 100644 --- a/code/modules/mod/modules/modules_engineering.dm +++ b/code/modules/mod/modules/modules_engineering.dm @@ -28,11 +28,11 @@ icon_state = "tray" module_type = MODULE_TOGGLE complexity = 1 - active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2 + active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5 incompatible_modules = list(/obj/item/mod/module/t_ray) cooldown_time = 0.5 SECONDS /// T-ray scan range. - var/range = 2 + var/range = 4 /obj/item/mod/module/t_ray/on_active_process(delta_time) t_ray_scan(mod.wearer, 0.8 SECONDS, range) @@ -121,6 +121,7 @@ hitsound_wall = 'sound/weapons/batonextend.ogg' suppressed = SUPPRESSED_VERY hit_threshhold = LATTICE_LAYER + /// Reference to the beam following the projectile. var/line /obj/projectile/tether/fire(setAngle) @@ -217,6 +218,7 @@ device = /obj/item/reagent_containers/spray/mister incompatible_modules = list(/obj/item/mod/module/mister) cooldown_time = 0.5 SECONDS + /// Volume of our reagent holder. var/volume = 500 /obj/item/mod/module/mister/Initialize(mapload) diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 9cd01e043b3..286feb9eace 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -7,7 +7,7 @@ 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) + incompatible_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/plate_compression) /// The storage component of the module. var/datum/component/storage/concrete/storage /// Max weight class of items in the storage. @@ -186,7 +186,7 @@ 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 won't free you from masks, \ - and it will do nothing to improve the taste of a goliath steak." + lets pepper spray pass through and 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) @@ -228,7 +228,7 @@ ///Flashlight - Gives the suit a customizable 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, \ + desc = "A simple pair of configurable 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" @@ -240,7 +240,7 @@ overlay_state_inactive = "module_light" light_system = MOVABLE_LIGHT_DIRECTIONAL light_color = COLOR_WHITE - light_range = 3 + light_range = 4 light_power = 1 light_on = FALSE /// Charge drain per range amount. @@ -266,10 +266,8 @@ set_light_on(active) /obj/item/mod/module/flashlight/on_process(delta_time) - . = ..() - if(!.) - return active_power_cost = base_power * light_range + return ..() /obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing) . = ..() diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm index c06781e31ad..b30364dfb3d 100644 --- a/code/modules/mod/modules/modules_maint.dm +++ b/code/modules/mod/modules/modules_maint.dm @@ -237,6 +237,8 @@ drain_power(use_power_cost) num_sheets_dispensed++ + +///Stamper - Extends a stamp that can switch between accept/deny modes. /obj/item/mod/module/stamp name = "MOD stamper module" desc = "A module installed into the wrist of the suit, this functions as a high-power stamp, \ diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm index 40a110c8bb4..9436271222e 100644 --- a/code/modules/mod/modules/modules_medical.dm +++ b/code/modules/mod/modules/modules_medical.dm @@ -128,7 +128,9 @@ use_power_cost = DEFAULT_CHARGE_DRAIN incompatible_modules = list(/obj/item/mod/module/organ_thrower, /obj/item/mod/module/microwave_beam) cooldown_time = 0.5 SECONDS + /// How many organs the module can hold. var/max_organs = 5 + /// A list of all our organs. var/organ_list = list() /obj/item/mod/module/organ_thrower/on_select_use(atom/target) @@ -165,6 +167,7 @@ nodamage = TRUE hitsound = 'sound/effects/attackblob.ogg' hitsound_wall = 'sound/effects/attackblob.ogg' + /// A reference to the organ we "are". var/obj/item/organ/organ /obj/projectile/organ/Initialize(mapload, obj/item/stored_organ) diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm index 24076d99d1d..7edb6235d78 100644 --- a/code/modules/mod/modules/modules_security.dm +++ b/code/modules/mod/modules/modules_security.dm @@ -168,3 +168,54 @@ return mod.wearer.visible_message(span_warning("[src] reacts to the attack with a smoke of pepper spray!"), span_notice("Your [src] releases a cloud of pepper spray!")) on_use() + +///Holster - Instantly holsters any not huge gun. +/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 + incompatible_modules = list(/obj/item/mod/module/holster) + cooldown_time = 0.5 SECONDS + allowed_inactive = TRUE + /// Gun we have holstered. + 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, force = FALSE, silent = TRUE)) + holstered = holding + balloon_alert(mod.wearer, "weapon holstered") + playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) + else if(mod.wearer.put_in_active_hand(holstered, forced = FALSE, ignore_animation = TRUE)) + balloon_alert(mod.wearer, "weapon drawn") + playsound(src, 'sound/weapons/gun/revolver/empty.ogg', 100, TRUE) + else + balloon_alert(mod.wearer, "holster full!") + +/obj/item/mod/module/holster/on_uninstall(deleting = FALSE) + if(holstered) + holstered.forceMove(drop_location()) + +/obj/item/mod/module/holster/Exited(atom/movable/gone, direction) + . = ..() + if(gone == holstered) + holstered = null + +/obj/item/mod/module/holster/Destroy() + QDEL_NULL(holstered) + return ..() diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index e31cec58105..87fe631eeb6 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -253,12 +253,12 @@ mod.wearer.transform = mod.wearer.transform.Turn(angle) mod.wearer.throw_at(get_ranged_target_turf_direct(mod.wearer, target, power), \ range = power, speed = max(round(0.2*power), 1), thrower = mod.wearer, spin = FALSE, \ - callback = CALLBACK(src, .proc/on_throw_end, target, -angle)) + callback = CALLBACK(src, .proc/on_throw_end, mod.wearer, -angle)) -/obj/item/mod/module/hydraulic/proc/on_throw_end(atom/target, angle) - if(!mod?.wearer) +/obj/item/mod/module/hydraulic/proc/on_throw_end(mob/user, angle) + if(!user) return - mod.wearer.transform = mod.wearer.transform.Turn(angle) + user.transform = user.transform.Turn(angle) /obj/item/mod/module/disposal_connector name = "MOD disposal selector module" @@ -370,6 +370,8 @@ var/list/armor_values = list(MELEE = 4, BULLET = 1, LASER = 2, ENERGY = 2, BOMB = 4) /// Speed added when you're fully covered in ash. var/speed_added = 0.5 + /// Speed that we actually added. + var/actual_speed_added = 0 /// Turfs that let us accrete ash. var/static/list/accretion_turfs /// Turfs that let us keep ash. @@ -440,7 +442,8 @@ mod.wearer.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) //make them super light animate(mod.wearer, 1 SECONDS, color = null, flags = ANIMATION_PARALLEL) playsound(src, 'sound/effects/sparks1.ogg', 100, TRUE) - mod.slowdown -= speed_added + actual_speed_added = max(0, min(mod.slowdown_active, speed_added)) + mod.slowdown -= actual_speed_added mod.wearer.update_equipment_speed_mods() else if(is_type_in_typecache(mod.wearer.loc, keep_turfs)) return @@ -448,7 +451,7 @@ if(traveled_tiles <= 0) return if(traveled_tiles == max_traveled_tiles) - mod.slowdown += speed_added + mod.slowdown += actual_speed_added mod.wearer.update_equipment_speed_mods() traveled_tiles-- var/list/parts = mod.mod_parts + mod @@ -481,7 +484,7 @@ . = ..() if(!.) return - playsound(src, 'sound/items/modsuit/ballin.ogg', 100) + playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE) mod.wearer.add_filter("mod_ball", 1, alpha_mask_filter(icon = icon('icons/mob/clothing/modsuit/mod_modules.dmi', "ball_mask"), flags = MASK_INVERSE)) mod.wearer.add_filter("mod_blur", 2, angular_blur_filter(size = 15)) mod.wearer.add_filter("mod_outline", 3, outline_filter(color = "#000000AA")) @@ -503,7 +506,7 @@ if(!.) return if(!deleting) - playsound(src, 'sound/items/modsuit/ballout.ogg', 100) + playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE, frequency = -1) mod.wearer.base_pixel_y = 0 animate(mod.wearer, animate_time, pixel_y = mod.wearer.base_pixel_y) addtimer(CALLBACK(mod.wearer, /atom.proc/remove_filter, list("mod_ball", "mod_blur", "mod_outline")), animate_time) @@ -583,8 +586,6 @@ var/damage = 15 /// Damage multiplier on hostile fauna. var/fauna_boost = 4 - /// Damage multiplier on objects - var/object_boost = 2 /// Image overlaid on explosion. var/static/image/explosion_image @@ -609,5 +610,5 @@ for(var/mob/living/mob in range(1, src)) mob.apply_damage(12 * (ishostile(mob) ? fauna_boost : 1), BRUTE, spread_damage = TRUE) for(var/obj/object in range(1, src)) - object.take_damage(damage * object_boost, BRUTE, BOMB) + object.take_damage(damage, BRUTE, BOMB) qdel(src) diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index c8149d81cb0..5fbfd23cbc6 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -1345,10 +1345,17 @@ /datum/design/module/mister_atmos name = "MOD Module: Resin Mister" id = "mod_mister_atmos" - materials = list(/datum/material/iron = 1500, /datum/material/glass = 500, /datum/material/silver = 1000, /datum/material/titanium = 1500, /datum/material/uranium = 500) + materials = list(/datum/material/glass = 1000, /datum/material/titanium = 1500) build_path = /obj/item/mod/module/mister/atmos department_type = MODULE_ENGINEERING +/datum/design/module/mod_holster + name = "MOD Module: Holster" + id = "mod_holster" + materials = list(/datum/material/iron = 1500, /datum/material/glass = 500) + build_path = /obj/item/mod/module/holster + department_type = MODULE_SECURITY + //MODsuit bepis modules /datum/design/module/disposal diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index d4153a77733..21bba620f12 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1530,6 +1530,7 @@ "mod_stealth", "mod_mag_harness", "mod_pathfinder", + "mod_holster", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm index c6934a95eeb..da367c904a6 100644 --- a/code/modules/uplink/uplink_items/nukeops.dm +++ b/code/modules/uplink/uplink_items/nukeops.dm @@ -47,7 +47,7 @@ /datum/uplink_item/bundles_tc/firestarter name = "Spetsnaz Pyro bundle" - desc = "For systematic suppression of carbon lifeforms in close quarters: Contains a lethal New Russian backpack spray, Elite hardsuit, \ + desc = "For systematic suppression of carbon lifeforms in close quarters: Contains an Elite MODsuit with a flamethrower attachment, \ Stechkin APS machine pistol, two incendiary magazines, a minibomb and a stimulant syringe. \ Order NOW and comrade Boris will throw in an extra tracksuit." item = /obj/item/storage/backpack/duffelbag/syndie/firestarter @@ -638,6 +638,28 @@ cost = 15 purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS +/datum/uplink_item/suits/energy_shield + name = "MODsuit Energy Shield Module" + desc = "An energy shield module for a MODsuit. The shields can handle up to three impacts \ + within a short duration and will rapidly recharge while not under fire." + item = /obj/item/mod/module/energy_shield + cost = 15 + purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS + +/datum/uplink_item/suits/injector + name = "MODsuit Injector Module" + desc = "An injector module for a MODsuit. It is an extendable piercing injector with 30u capacity." + item = /obj/item/mod/module/injector + cost = 2 + purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS + +/datum/uplink_item/suits/holster + name = "MODsuit Holster Module" + desc = "A holster module for a MODsuit. It can stealthily store any not too heavy gun inside it." + item = /obj/item/mod/module/holster + cost = 2 + purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS + /datum/uplink_item/device_tools/magboots name = "Blood-Red Magboots" desc = "A pair of magnetic boots with a Syndicate paintjob that assist with freer movement in space or on-station \ diff --git a/code/modules/uplink/uplink_items/suits.dm b/code/modules/uplink/uplink_items/suits.dm index 6290da8b5e2..c6cbac65c99 100644 --- a/code/modules/uplink/uplink_items/suits.dm +++ b/code/modules/uplink/uplink_items/suits.dm @@ -11,7 +11,7 @@ /datum/uplink_item/suits/infiltrator_bundle name = "Infiltrator Case" desc = "Developed by Roseus Galactic in conjunction with the Gorlex Marauders to produce a functional suit for urban operations, \ - this suit proves to be cheaper than your standard issue hardsuit, with none of the movement restrictions of the outdated spacesuits employed by the company. \ + this suit proves to be cheaper than your standard issue MODsuit, with none of the movement restrictions of the outdated spacesuits employed by the company. \ Comes with an armor vest, helmet, sneaksuit, sneakboots, specialized combat gloves and a high-tech balaclava. The case is also rather useful as a storage container." item = /obj/item/storage/toolbox/infiltrator cost = 6 @@ -50,6 +50,29 @@ progression_minimum = 20 MINUTES cost = 2 +/datum/uplink_item/suits/chameleon + name = "MODsuit Chameleon Module" + desc = "A MODsuit module that lets the suit disguise itself as other objects." + item = /obj/item/mod/module/chameleon + progression_minimum = 20 MINUTES + cost = 2 + +/datum/uplink_item/suits/plate_compression + name = "MODsuit Plate Compression Module" + desc = "A MODsuit module that lets the suit compress into a smaller size. Not compatible with storage modules." + item = /obj/item/mod/module/plate_compression + progression_minimum = 20 MINUTES + cost = 2 + +// Medium progression cost + +/datum/uplink_item/suits/noslip + name = "MODsuit Anti-Slip Module" + desc = "A MODsuit module preventing the user from slipping on water." + item = /obj/item/mod/module/noslip + progression_minimum = 90 MINUTES + cost = 2 + // Very high progression cost /datum/uplink_item/suits/modsuit/elite_traitor @@ -61,10 +84,3 @@ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS) progression_minimum = 90 MINUTES cost = 16 - -/datum/uplink_item/suits/noslip - name = "MODsuit Anti-Slip Module" - desc = "A MODsuit module preventing the user from slipping on water." - item = /obj/item/mod/module/noslip - progression_minimum = 90 MINUTES - cost = 4 diff --git a/icons/effects/magic.dmi b/icons/effects/magic.dmi index 50c16cd7fdd..480332df134 100644 Binary files a/icons/effects/magic.dmi and b/icons/effects/magic.dmi differ diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi index c93a816b2bc..e41fc164a2a 100644 Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ diff --git a/strings/french_replacement.json b/strings/french_replacement.json index 4c89d988127..d99718e7e4d 100644 --- a/strings/french_replacement.json +++ b/strings/french_replacement.json @@ -3,69 +3,69 @@ "french": { "yes": "oui", "no": "non", - "I'm": "j'", - "am": "suis", + "I'm": "j'", + "am": "suis", "a": "un", - "and": "et", + "and": "et", "the": "l'", "I": "j'", "for": "pour", "with": "avec", "of": "de", - - + + "assistant": "ravageur", "assistants": "ravageurs", "baby": [ - "enfant", + "enfant", "petit baguette" - ], - "bad": "mal", + ], + "bad": "mal", "bye": [ - "bon voyage", + "bon voyage", "adieu", "au revoir" - ], + ], "cake": "gateau", - "captain": "capitaine", + "captain": "capitaine", "changeling": "changeur", "cheese": [ - "brie", + "brie", "roquefort", "camembert" - ], + ], "cigarette": "clope", - "cook": "cuisinier", - "dad": "papa", - "enemy": "silly english dog", - "friend": "ami", - "good": "bon", - "greytide": "les gitans", + "cook": "cuisinier", + "dad": "papa", + "enemy": "silly english dog", + "friend": "ami", + "good": "bon", + "greytide": "les gitans", "greytider": "les gitans", "greytiders": "les gitans", - "hardsuit": "burkini", + "modsuit": "burkini", "hello": [ - "'allo", + "'allo", "bonjour", "salut" - ], + ], "maint": "les banlieues", "meat": [ - "coq au vin", + "coq au vin", "boeuf" - ], - "mom": "maman", - "my": "mon", + ], + "mom": "maman", + "my": "mon", "nuke": [ - "grand bombe", + "grand bombe", "la baguette ultime" ], - "op": "boche", - "operative": "boche", - "operatives": "boches", - "ops": "boches", - "urity": "urite", - "security": "securite", + "op": "boche", + "operative": "boche", + "operatives": "boches", + "ops": "boches", + "urity": "urite", + "security": "securite", "shit": "merde", "shitcurity": [ "gendarmerie", @@ -75,17 +75,17 @@ "gendarmerie", "les keufs" ], - "spaghetti": "macaroni", - "spicy": "epice", - "thanks": "merci", + "spaghetti": "macaroni", + "spicy": "epice", + "thanks": "merci", "tomato": "tomate", - "traitor": "collaborateur", - "want": "envie", - "what's": "quel est", - "who's": "qui est", - "why": "porquois", + "traitor": "collaborateur", + "want": "envie", + "what's": "quel est", + "who's": "qui est", + "why": "porquois", "wine": "vin", "wizard": "sorcier" } - -} \ No newline at end of file + +} diff --git a/strings/memories.json b/strings/memories.json index 10cda761ea4..52c54ebece1 100644 --- a/strings/memories.json +++ b/strings/memories.json @@ -417,7 +417,7 @@ ], "nuke_code_starts":[ "The number %NUKE_CODE written on a sticky note with the words \"FOR SYNDICATE EYES ONLY\" scrawled next to it.", - "A piece of paper with the number %NUKE_CODE being handed to %PROTAGONIST from a figure in a blood-red hardsuit." + "A piece of paper with the number %NUKE_CODE being handed to %PROTAGONIST from a figure in a blood-red MODsuit." ], "playing_cards_names":[ "The %CARDGAME of %PROTAGONIST with %PLAYERS."