MODsuit module update: new stuff and improved old stuff (#67042)

Doubles the range of the MOD Pathfinder AI
Fixes modules rendering below the suit.
Adds the ability for modules to be used when inactive.
Documents/cleans up some code.
Updates some old descriptions and explains some concepts better.
Armor Booster and Ash Accretion can no longer boost your speed over no slowdown at all.
Makes flashlight module start with 4 instead of 3 range, so it's better for people that don't know about configuration.
Doubles t-ray module range, from 2 to 4 (t-ray scanner is 3).
Puts the noslip module lower in progression, lowers its' price to 2.
New sprites for the magnetic harness module by Onule.
Brings back the holster module, it can now be used when the suit is inactive, can be printed with security suit research.
Adds the power kick module for the ERT Commander. It's a powerful kick.
This commit is contained in:
Fikou
2022-05-18 01:19:31 -04:00
committed by GitHub
parent 30838d991c
commit f501b1e49e
26 changed files with 429 additions and 123 deletions
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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
-2
View File
@@ -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
+39
View File
@@ -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
+1 -1
View File
@@ -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
-1
View File
@@ -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"
+4 -5
View File
@@ -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
+4 -4
View File
@@ -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
+27 -24
View File
@@ -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))
+4 -4
View File
@@ -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)
@@ -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. \
+169 -4
View File
@@ -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())
@@ -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)
+5 -7
View File
@@ -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)
. = ..()
@@ -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, \
@@ -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)
@@ -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 ..()
+12 -11
View File
@@ -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)
@@ -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
@@ -1530,6 +1530,7 @@
"mod_stealth",
"mod_mag_harness",
"mod_pathfinder",
"mod_holster",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+23 -1
View File
@@ -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 \
+24 -8
View File
@@ -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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 42 KiB

+43 -43
View File
@@ -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"
}
}
}
+1 -1
View File
@@ -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."