Merge pull request #15985 from KrissKr0ss/modsuits

[READY FOR MERGE] Modsuits
This commit is contained in:
SandPoot
2023-05-06 10:30:23 -03:00
committed by GitHub
88 changed files with 6960 additions and 42 deletions

View File

@@ -35,6 +35,7 @@
#define MEDAL_HOT_DAMN "Hot Damn!"
#define MEDAL_CAYENNE_DISK "Very Important Piscis"
#define MEDAL_TRAM_SURFER "Tram Surfer"
#define MEDAL_SPRINGLOCK "The Man Inside the Modsuit"
//Skill medal hub IDs
#define MEDAL_LEGENDARY_MINER "Legendary Miner"

View File

@@ -474,6 +474,8 @@
#define COMSIG_ITEM_AFTERATTACK "item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, params)
#define COMSIG_ITEM_ALT_AFTERATTACK "item_alt_afterattack" //from base of obj/item/altafterattack(): (atom/target, mob/user, proximity, params)
#define COMSIG_ITEM_EQUIPPED "item_equip" //from base of obj/item/equipped(): (/mob/equipper, slot)
/// A mob has just unequipped an item.
#define COMSIG_MOB_UNEQUIPPED_ITEM "mob_unequipped_item"
// Do not grant actions on equip.
#define COMPONENT_NO_GRANT_ACTIONS 1
#define COMSIG_ITEM_DROPPED "item_drop" //from base of obj/item/dropped(): (mob/user)

View File

@@ -0,0 +1,14 @@
/// From /datum/surgery/New(): (datum/surgery/surgery, surgery_location (body zone), obj/item/bodypart/targeted_limb)
#define COMSIG_MOB_SURGERY_STARTED "mob_surgery_started"
/// From /datum/surgery_step/success(): (datum/surgery_step/step, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results)
#define COMSIG_MOB_SURGERY_STEP_SUCCESS "mob_surgery_step_success"
/// From /obj/item/shockpaddles/proc/do_success(): (obj/item/shockpaddles/source)
#define COMSIG_DEFIBRILLATOR_SUCCESS "defib_success"
#define COMPONENT_DEFIB_STOP (1<<0)
/// From /datum/surgery/can_start(): (mob/source, datum/surgery/surgery, mob/living/patient)
#define COMSIG_SURGERY_STARTING "surgery_starting"
#define COMPONENT_CANCEL_SURGERY (1<<0)
#define COMPONENT_FORCE_SURGERY (1<<1)

View File

@@ -0,0 +1,35 @@
//MODsuit signals
/// Called when a module is selected to be the active one from on_select(obj/item/mod/module/module)
#define COMSIG_MOD_MODULE_SELECTED "mod_module_selected"
/// Called when a MOD deploys one or more of its parts.
#define COMSIG_MOD_DEPLOYED "mod_deployed"
/// Called when a MOD retracts one or more of its parts.
#define COMSIG_MOD_RETRACTED "mod_retracted"
/// Called when a MOD is finished toggling itself.
#define COMSIG_MOD_TOGGLED "mod_toggled"
/// Called when a MOD activation is called from toggle_activate(mob/user)
#define COMSIG_MOD_ACTIVATE "mod_activate"
/// Cancels the suit's activation
#define MOD_CANCEL_ACTIVATE (1 << 0)
/// Called when a MOD finishes having a module removed from it.
#define COMSIG_MOD_MODULE_REMOVED "mod_module_removed"
/// Called when a MOD finishes having a module added to it.
#define COMSIG_MOD_MODULE_ADDED "mod_module_added"
/// Called when a MOD is having modules removed from crowbar_act(mob/user, obj/crowbar)
#define COMSIG_MOD_MODULE_REMOVAL "mod_module_removal"
/// Cancels the removal of modules
#define MOD_CANCEL_REMOVAL (1 << 0)
/// Called when a module attempts to activate, however it does. At the end of checks so you can add some yourself, or work on trigger behavior (mob/user)
#define COMSIG_MODULE_TRIGGERED "mod_module_triggered"
/// Cancels activation, with no message. Include feedback on your cancel.
#define MOD_ABORT_USE (1<<0)
/// Called when a module activates, after all checks have passed and cooldown started.
#define COMSIG_MODULE_ACTIVATED "mod_module_activated"
/// Called when a module deactivates, after all checks have passed.
#define COMSIG_MODULE_DEACTIVATED "mod_module_deactivated"
/// Called when a module is used, after all checks have passed and cooldown started.
#define COMSIG_MODULE_USED "mod_module_used"
/// Called when the MODsuit wearer is set.
#define COMSIG_MOD_WEARER_SET "mod_wearer_set"
/// Called when the MODsuit wearer is unset.
#define COMSIG_MOD_WEARER_UNSET "mod_wearer_unset"

View File

@@ -0,0 +1,2 @@
///Sent by /datum/reagents/proc/reaction, used for /obj/item/mod/module/springlock
#define COMSIG_ATOM_EXPOSE_REAGENTS "atom_expose_reagents"

40
code/__DEFINES/mod.dm Normal file
View File

@@ -0,0 +1,40 @@
/// Default value for the max_complexity var on MODsuits
#define DEFAULT_MAX_COMPLEXITY 15
/// Default cell drain per process on MODsuits
#define DEFAULT_CHARGE_DRAIN 5
/// Default time for a part to seal
#define MOD_ACTIVATION_STEP_TIME (2 SECONDS)
/// Passive module, just acts when put in naturally.
#define MODULE_PASSIVE 0
/// Usable module, does something when you press a button.
#define MODULE_USABLE 1
/// Toggle module, you turn it on/off and it does stuff.
#define MODULE_TOGGLE 2
/// Actively usable module, you may only have one selected at a time.
#define MODULE_ACTIVE 3
//Defines used by the theme for clothing flags and similar
#define CONTROL_LAYER "control_layer"
#define HELMET_FLAGS "helmet_flags"
#define CHESTPLATE_FLAGS "chestplate_flags"
#define GAUNTLETS_FLAGS "gauntlets_flags"
#define BOOTS_FLAGS "boots_flags"
#define UNSEALED_LAYER "unsealed_layer"
#define UNSEALED_CLOTHING "unsealed_clothing"
#define SEALED_CLOTHING "sealed_clothing"
#define UNSEALED_INVISIBILITY "unsealed_invisibility"
#define SEALED_INVISIBILITY "sealed_invisibility"
#define UNSEALED_COVER "unsealed_cover"
#define SEALED_COVER "sealed_cover"
#define CAN_OVERSLOT "can_overslot"
//Defines used to override MOD clothing's icon and worn icon files in the skin.
#define MOD_ICON_OVERRIDE "mod_icon_override"
#define MOD_WORN_ICON_OVERRIDE "mod_worn_icon_override"
/// Global list of all /datum/mod_theme
GLOBAL_LIST_INIT(mod_themes, setup_mod_themes())

View File

@@ -158,6 +158,18 @@
#define TRAIT_CALCIUM_HEALER "calcium_healer"
#define TRAIT_MAGIC_CHOKE "magic_choke"
#define TRAIT_CAPTAIN_METABOLISM "captain-metabolism"
/// Like antimagic, but doesn't block the user from casting
#define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock"
/// Gives us turf, mob and object vision through walls
#define TRAIT_XRAY_VISION "xray_vision"
/// Gives us mob vision through walls and slight night vision
#define TRAIT_THERMAL_VISION "thermal_vision"
/// Gives us turf vision through walls and slight night vision
#define TRAIT_MESON_VISION "meson_vision"
/// Gives us Night vision
#define TRAIT_TRUE_NIGHT_VISION "true_night_vision"
/// Lets us scan reagents
#define TRAIT_REAGENT_SCANNER "reagent_scanner"
#define TRAIT_ABDUCTOR_TRAINING "abductor-training"
#define TRAIT_ABDUCTOR_SCIENTIST_TRAINING "abductor-scientist-training"
#define TRAIT_SURGEON "surgeon"
@@ -213,8 +225,6 @@
#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item"
#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman.
#define TRAIT_FREESPRINT "free_sprinting"
#define TRAIT_XRAY_VISION "xray_vision"
#define TRAIT_THERMAL_VISION "thermal_vision"
#define TRAIT_NO_TELEPORT "no-teleport" //you just can't
#define TRAIT_NO_INTERNALS "no-internals"
#define TRAIT_TOXIC_ALCOHOL "alcohol_intolerance"
@@ -387,3 +397,5 @@
#define STATION_TRAIT_FILLED_MAINT "station_trait_filled_maint"
#define STATION_TRAIT_EMPTY_MAINT "station_trait_empty_maint"
#define STATION_TRAIT_PDA_GLITCHED "station_trait_pda_glitched"
/// Trait applied by MODsuits.
#define MOD_TRAIT "mod"

View File

@@ -53,6 +53,8 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/airlock_painter/decal/tile = 1,
/obj/item/stack/cable_coil/random = 4,
/obj/item/stack/cable_coil/random/five = 6,
/obj/item/mod/construction/broken_core = 5,
/obj/item/mod/module/springlock = 1,
/obj/item/stack/medical/suture = 1,
/obj/item/stack/rods/ten = 9,
/obj/item/stack/rods/twentyfive = 1,

View File

@@ -91,6 +91,8 @@ GLOBAL_LIST_EMPTY(radial_menus)
else
py_shift = 32
restrict_to_dir(NORTH) //I was going to parse screen loc here but that's more effort than it's worth.
else if(hudfix_method && AM.loc)
anchor = get_atom_on_turf(anchor)
//Sets defaults
//These assume 45 deg min_angle

View File

@@ -171,3 +171,9 @@
desc = "Lights out, guerilla radio!"
database_id = MEDAL_TRAM_SURFER
icon = "tram_surfer"
/datum/award/achievement/misc/springlock
name = "The Man Inside the MODsuit"
desc = "Ignore the warning label on a springlock MODsuit."
database_id = MEDAL_SPRINGLOCK
icon = "springlock"

View File

@@ -554,6 +554,19 @@
subcategory = CAT_MISCELLANEOUS
category = CAT_MISCELLANEOUS
/datum/crafting_recipe/mod_core
name = "MOD core"
result = /obj/item/mod/construction/core
tools = list(TOOL_SCREWDRIVER)
time = 10 SECONDS
reqs = list(/obj/item/stack/cable_coil = 5,
/obj/item/stack/rods = 2,
/obj/item/stack/sheet/glass = 1,
/obj/item/organ/heart = 1
)
subcategory = CAT_MISCELLANEOUS
category = CAT_MISCELLANEOUS
//////////////
//Banners/////
//////////////

View File

@@ -20,14 +20,22 @@ GLOBAL_LIST_EMPTY(GPS_list)
/datum/component/gps/item
var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user.
var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown
/// UI state of GPS, altering when it can be used.
var/datum/ui_state/state = null
/datum/component/gps/item/Initialize(_gpstag = "COM0", emp_proof = FALSE, starton = TRUE)
/datum/component/gps/item/Initialize(_gpstag = "COM0", emp_proof = FALSE, starton = TRUE, state = null, overlay_state = "working")
. = ..()
if(. == COMPONENT_INCOMPATIBLE || !isitem(parent))
return COMPONENT_INCOMPATIBLE
if(isnull(state))
state = GLOB.default_state
src.state = state
var/atom/A = parent
if(starton)
A.add_overlay("working")
if(overlay_state)
A.add_overlay(overlay_state)
else
tracking = FALSE
A.name = "[initial(A.name)] ([gpstag])"
@@ -93,6 +101,9 @@ GLOBAL_LIST_EMPTY(GPS_list)
ui.open()
ui.set_autoupdate(updating)
/datum/component/gps/item/ui_state(mob/user)
return state
/datum/component/gps/item/ui_data(mob/user)
var/list/data = list()
data["power"] = tracking

56
code/datums/wires/mod.dm Normal file
View File

@@ -0,0 +1,56 @@
/datum/wires/mod
holder_type = /obj/item/mod/control
proper_name = "MOD control unit"
req_knowledge = JOB_SKILL_MASTER
req_skill = JOB_SKILL_TRAINED
/datum/wires/mod/New(atom/holder)
wires = list(WIRE_DISABLE, WIRE_SHOCK, WIRE_INTERFACE)
add_duds(3)
..()
/datum/wires/mod/interactable(mob/user)
if(!..())
return FALSE
var/obj/item/mod/control/mod = holder
return mod.open
/datum/wires/mod/get_status()
var/obj/item/mod/control/mod = holder
var/list/status = list()
status += "The orange light is [mod.seconds_electrified ? "on" : "off"]."
status += "The red light is [mod.malfunctioning ? "off" : "blinking"]."
status += "The yellow light is [mod.interface_break ? "off" : "on"]."
return status
/datum/wires/mod/on_pulse(wire)
var/obj/item/mod/control/mod = holder
switch(wire)
if(WIRE_DISABLE)
mod.malfunctioning = TRUE
if(WIRE_SHOCK)
mod.seconds_electrified = MACHINE_DEFAULT_ELECTRIFY_TIME
if(WIRE_INTERFACE)
mod.interface_break = !mod.interface_break
/datum/wires/mod/on_cut(wire, mend)
var/obj/item/mod/control/mod = holder
switch(wire)
if(WIRE_HACK)
if(!mend)
mod.req_access = list()
if(WIRE_DISABLE)
mod.malfunctioning = !mend
if(WIRE_SHOCK)
if(mend)
mod.seconds_electrified = MACHINE_NOT_ELECTRIFIED
else
mod.seconds_electrified = MACHINE_ELECTRIFIED_PERMANENT
if(WIRE_INTERFACE)
mod.interface_break = !mend
/datum/wires/mod/ui_act(action, params)
var/obj/item/mod/control/mod = holder
if(!issilicon(usr) && mod.seconds_electrified && mod.shock(usr))
return FALSE
return ..()

View File

@@ -1,6 +1,6 @@
/obj/machinery/recharge_station
name = "cyborg recharging station"
desc = "This device recharges cyborgs and resupplies them with materials."
name = "recharging station"
desc = "This device recharges energy dependent lifeforms, like cyborgs, ethereals and MODsuit users."
icon = 'icons/obj/objects.dmi'
icon_state = "borgcharger0"
density = FALSE
@@ -10,7 +10,7 @@
req_access = list(ACCESS_ROBOTICS)
state_open = TRUE
circuit = /obj/item/circuitboard/machine/cyborgrecharger
occupant_typecache = list(/mob/living/silicon/robot)
occupant_typecache = list(/mob/living/silicon/robot, /mob/living/carbon/human)
var/recharge_speed
var/repairs

View File

@@ -10,11 +10,15 @@
var/obj/item/clothing/suit/space/suit = null
var/obj/item/clothing/head/helmet/space/helmet = null
var/obj/item/clothing/mask/mask = null
var/obj/item/mod/control/mod = null
var/obj/item/storage = null
var/suit_type = null
var/helmet_type = null
var/mask_type = null
/// What type of MOD the unit starts with when spawned.
var/mod_type = null
/// What type of additional item the unit starts with when spawned.
var/storage_type = null
state_open = FALSE
@@ -27,6 +31,8 @@
var/uv_cycles = 6
var/message_cooldown
var/breakout_time = 300
/// How fast it charges cells in a suit
var/charge_rate = 250
/obj/machinery/suit_storage_unit/standard_unit
suit_type = /obj/item/clothing/suit/space/eva
@@ -38,21 +44,50 @@
mask_type = /obj/item/clothing/mask/gas/sechailer
storage_type = /obj/item/tank/jetpack/oxygen/captain
/obj/machinery/suit_storage_unit/captainmod
mask_type = /obj/item/clothing/mask/gas/sechailer
storage_type = /obj/item/tank/jetpack/oxygen/captain
mod_type = /obj/item/mod/control/pre_equipped/magnate
/obj/machinery/suit_storage_unit/engine
suit_type = /obj/item/clothing/suit/space/hardsuit/engine
mask_type = /obj/item/clothing/mask/breath
storage_type= /obj/item/clothing/shoes/magboots
/obj/machinery/suit_storage_unit/enginemod
mask_type = /obj/item/clothing/mask/breath
mod_type = /obj/item/mod/control/pre_equipped/engineering
/obj/machinery/suit_storage_unit/atmos
suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos
mask_type = /obj/item/clothing/mask/breath
storage_type = /obj/item/watertank/atmos
/obj/machinery/suit_storage_unit/atmosmod
mask_type = /obj/item/clothing/mask/breath
storage_type = /obj/item/watertank/atmos
mod_type = /obj/item/mod/control/pre_equipped/atmospheric
/obj/machinery/suit_storage_unit/ce
suit_type = /obj/item/clothing/suit/space/hardsuit/engine/elite
mask_type = /obj/item/clothing/mask/breath
storage_type= /obj/item/clothing/shoes/magboots/advance
/obj/machinery/suit_storage_unit/cemod
mask_type = /obj/item/clothing/mask/breath
storage_type = /obj/item/clothing/shoes/magboots/advance
mod_type = /obj/item/mod/control/pre_equipped/advanced
/obj/machinery/suit_storage_unit/security
suit_type = /obj/item/clothing/suit/space/hardsuit/security
mask_type = /obj/item/clothing/mask/gas/sechailer
storage_type = /obj/item/tank/jetpack/oxygen/security
/obj/machinery/suit_storage_unit/securitymod
mask_type = /obj/item/clothing/mask/gas/sechailer
mod_type = /obj/item/mod/control/pre_equipped/security
/obj/machinery/suit_storage_unit/hos
suit_type = /obj/item/clothing/suit/space/hardsuit/security/hos
mask_type = /obj/item/clothing/mask/gas/sechailer
@@ -63,6 +98,11 @@
mask_type = /obj/item/clothing/mask/gas
storage_type = /obj/item/watertank/atmos
/obj/machinery/suit_storage_unit/hosmod
mask_type = /obj/item/clothing/mask/gas/sechailer
storage_type = /obj/item/tank/internals/oxygen
mod_type = /obj/item/mod/control/pre_equipped/safeguard
/obj/machinery/suit_storage_unit/mining
suit_type = /obj/item/clothing/suit/hooded/explorer/standard
mask_type = /obj/item/clothing/mask/gas/explorer
@@ -71,6 +111,16 @@
suit_type = /obj/item/clothing/suit/space/hardsuit/mining
mask_type = /obj/item/clothing/mask/breath
/obj/machinery/suit_storage_unit/mining/evahos
suit_type = null
mask_type = /obj/item/clothing/mask/breath
mod_type = /obj/item/mod/control/pre_equipped/mining
/obj/machinery/suit_storage_unit/medicalmod
mask_type = /obj/item/clothing/mask/breath/medical
storage_type = /obj/item/tank/internals/oxygen
mod_type = /obj/item/mod/control/pre_equipped/medical
/obj/machinery/suit_storage_unit/cmo
suit_type = /obj/item/clothing/suit/space/hardsuit/medical
mask_type = /obj/item/clothing/mask/breath
@@ -81,15 +131,29 @@
helmet_type = /obj/item/clothing/head/helmet/space/eva/paramedic
mask_type = /obj/item/clothing/mask/breath
/obj/machinery/suit_storage_unit/cmomod
mask_type = /obj/item/clothing/mask/breath/medical
storage_type = /obj/item/tank/internals/oxygen
mod_type = /obj/item/mod/control/pre_equipped/rescue
/obj/machinery/suit_storage_unit/rd
suit_type = /obj/item/clothing/suit/space/hardsuit/rd
mask_type = /obj/item/clothing/mask/breath
/obj/machinery/suit_storage_unit/rdmod
mask_type = /obj/item/clothing/mask/breath
mod_type = /obj/item/mod/control/pre_equipped/research
/obj/machinery/suit_storage_unit/syndicate
suit_type = /obj/item/clothing/suit/space/hardsuit/syndi
mask_type = /obj/item/clothing/mask/gas/syndicate
storage_type = /obj/item/tank/jetpack/oxygen/harness
/obj/machinery/suit_storage_unit/syndicatemod
mask_type = /obj/item/clothing/mask/gas/syndicate
storage_type = /obj/item/tank/jetpack/oxygen/harness
mod_type = /obj/item/mod/control/pre_equipped/nuclear
/obj/machinery/suit_storage_unit/ert/command
suit_type = /obj/item/clothing/suit/space/hardsuit/ert
mask_type = /obj/item/clothing/mask/breath
@@ -129,6 +193,8 @@
helmet = new helmet_type(src)
if(mask_type)
mask = new mask_type(src)
if(mod_type)
mod = new mod_type(src)
if(storage_type)
storage = new storage_type(src)
update_icon()
@@ -137,6 +203,7 @@
QDEL_NULL(suit)
QDEL_NULL(helmet)
QDEL_NULL(mask)
QDEL_NULL(mod)
QDEL_NULL(storage)
return ..()
@@ -154,7 +221,7 @@
. += "broken"
else
. += "open"
if(suit)
if(suit || mod)
. += "suit"
if(helmet)
. += "helm"
@@ -175,6 +242,7 @@
helmet = null
suit = null
mask = null
mod = null
storage = null
occupant = null
@@ -241,6 +309,8 @@
qdel(suit) // Delete everything but the occupant.
mask = null
qdel(mask)
mod = null
qdel(mod)
storage = null
qdel(storage)
// The wires get damaged too.
@@ -262,6 +332,9 @@
if(mask)
things_to_clear += mask
things_to_clear += mask.GetAllContents()
if(mod)
things_to_clear += mod
things_to_clear += mod.GetAllContents()
if(storage)
things_to_clear += storage
things_to_clear += storage.GetAllContents()
@@ -279,6 +352,20 @@
if(occupant)
dump_contents()
/obj/machinery/suit_storage_unit/process(delta_time)
var/obj/item/stock_parts/cell/cell
if(mod)
if(!istype(mod))
return
if(!mod.cell)
return
cell = mod.cell
else
return
use_power(charge_rate * delta_time)
cell.give(charge_rate * delta_time)
/obj/machinery/suit_storage_unit/proc/shock(mob/user, prb)
if(!prob(prb))
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
@@ -353,6 +440,13 @@
if(!user.transferItemToLoc(I, src))
return
mask = I
else if(istype(I, /obj/item/mod/control))
if(mod)
to_chat(user, span_warning("The unit already contains a MOD!"))
return
if(!user.transferItemToLoc(I, src))
return
mod = I
else
if(storage)
to_chat(user, "<span class='warning'>The auxiliary storage compartment is full!</span>")
@@ -405,6 +499,10 @@
data["mask"] = mask.name
else
data["mask"] = null
if(mod)
data["mod"] = mod.name
else
data["mod"] = null
if(storage)
data["storage"] = storage.name
else
@@ -435,7 +533,7 @@
if("uv")
if(occupant && safeties)
return
else if(!helmet && !mask && !suit && !storage && !occupant)
else if(!helmet && !mask && !mod && !suit && !storage && !occupant)
return
else
if(occupant)
@@ -447,7 +545,7 @@
if(!state_open)
return
var/static/list/valid_items = list("helmet", "suit", "mask", "storage")
var/static/list/valid_items = list("helmet", "suit", "mask", "mod", "storage")
var/item_name = params["item"]
if(item_name in valid_items)
var/obj/item/I = vars[item_name]

View File

@@ -399,7 +399,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(throwing)
throwing.finalize(FALSE)
if(loc == user)
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(I = src))
return
. = FALSE
@@ -428,7 +428,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(throwing)
throwing.finalize(FALSE)
if(loc == user)
if(!user.temporarilyRemoveItemFromInventory(src))
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
return
pickup(user)
@@ -1271,3 +1271,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/proc/update_action_buttons(status_only = FALSE, force = FALSE)
for(var/datum/action/current_action as anything in actions)
current_action.UpdateButtonIcon(status_only, force)
/// Special stuff you want to do when an outfit equips this item.
/obj/item/proc/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
return

View File

@@ -461,6 +461,8 @@
cooldown = TRUE
busy = FALSE
update_icon()
if(SEND_SIGNAL(src, COMSIG_DEFIBRILLATOR_SUCCESS) & COMPONENT_DEFIB_STOP)
return
if(req_defib)
defib.cooldowncheck(user)
else
@@ -514,6 +516,8 @@
cooldown = TRUE
busy = FALSE
update_icon()
if(SEND_SIGNAL(src, COMSIG_DEFIBRILLATOR_SUCCESS) & COMPONENT_DEFIB_STOP)
return
if(!req_defib)
recharge(60)
if(req_defib && (defib.cooldowncheck(user)))
@@ -632,6 +636,8 @@
defib.deductcharge(revivecost)
cooldown = 1
update_icon()
if(SEND_SIGNAL(src, COMSIG_DEFIBRILLATOR_SUCCESS) & COMPONENT_DEFIB_STOP)
return
if(req_defib)
defib.cooldowncheck(user)
else

View File

@@ -239,9 +239,13 @@
return
/mob/living/carbon/get_jetpack()
var/obj/item/tank/jetpack/J = back
if(istype(J))
return J
var/obj/item/I = back
if(istype(I, /obj/item/tank/jetpack))
return I
else if(istype(I, /obj/item/mod/control))
var/obj/item/mod/control/C = I
for(var/obj/item/mod/module/jetpack/J in C.modules)
return J
/mob/living/carbon/human/get_jetpack()
var/obj/item/tank/jetpack/J = ..()

View File

@@ -32,6 +32,8 @@
new /obj/item/clothing/head/beret/ce/white(src)
new /obj/item/storage/bag/construction(src)
new /obj/item/storage/bag/material(src)
new /obj/item/mod/construction/armor/advanced(src)
new /obj/item/mod/module/rad_protection(src)
/obj/structure/closet/secure_closet/engineering_electrical
name = "electrical supplies locker"

View File

@@ -103,6 +103,8 @@
new /obj/item/storage/lockbox/medal/medical(src)
new /obj/item/clothing/suit/hooded/wintercoat/cmo(src)
new /obj/item/clothing/head/beret/cmo/blue(src)
new /obj/item/mod/construction/armor/rescue(src)
new /obj/item/mod/module/health_analyzer(src)
/obj/structure/closet/secure_closet/animal
name = "animal control"

View File

@@ -31,4 +31,4 @@
new /obj/item/circuitboard/machine/techfab/department/science(src)
new /obj/item/storage/photo_album/RD(src)
new /obj/item/clothing/suit/hooded/wintercoat/rd(src)
new /obj/item/mod/construction/armor/research(src)

View File

@@ -17,6 +17,8 @@
new /obj/item/gun/energy/e_gun(src)
new /obj/item/door_remote/captain(src)
new /obj/item/storage/photo_album/Captain(src)
new /obj/item/mod/construction/armor/magnate(src)
new /obj/item/mod/module/holster(src)
/obj/structure/closet/secure_closet/hop
name = "\proper head of personnel's locker"
@@ -86,6 +88,9 @@
new /obj/item/circuitboard/machine/techfab/department/security(src)
new /obj/item/storage/photo_album/HoS(src)
new /obj/item/clothing/suit/hooded/wintercoat/hos(src)
new /obj/item/mod/construction/armor/safeguard(src)
new /obj/item/mod/module/jetpack(src)
new /obj/item/mod/module/holster(src)
/obj/structure/closet/secure_closet/warden
name = "\proper warden's locker"

View File

@@ -238,3 +238,14 @@
contains = list(/obj/item/raw_anomaly_core/pyro)
crate_name = "raw pyro anomaly"
crate_type = /obj/structure/closet/crate/secure/science
/datum/supply_pack/science/mod_core
name = "MOD core Crate"
desc = "Three cores, perfect for any MODsuit construction! Naturally harvested™, of course."
cost = CARGO_CRATE_VALUE * 3
access = ACCESS_ROBOTICS
contains = list(/obj/item/mod/construction/core,
/obj/item/mod/construction/core,
/obj/item/mod/construction/core)
crate_name = "MOD core crate"
crate_type = /obj/structure/closet/crate/secure/science

View File

@@ -337,6 +337,7 @@
on_item_dropped(I)
if(I.dropped(src) == ITEM_RELOCATED_BY_DROPPED)
return FALSE
SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, I, force, newloc, no_move, invdrop, silent)
return TRUE
//This is a SAFE proc. Use this instead of equip_to_slot()!

View File

@@ -28,8 +28,8 @@
if(prob(40))
if(prob(25))
audible_message("<span class='warning'>You hear something rumbling inside [src]'s stomach...</span>", \
"<span class='warning'>You hear something rumbling.</span>", 4,\
"<span class='userdanger'>Something is rumbling inside your stomach!</span>")
"<span class='warning'>You hear something rumbling.</span>", 4,\
"<span class='userdanger'>Something is rumbling inside your stomach!</span>")
var/obj/item/I = user.get_active_held_item()
if(I && I.force)
var/d = rand(round(I.force / 4), I.force)
@@ -625,12 +625,21 @@
if(M.name == XRAY)
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = max(see_in_dark, 8)
if(HAS_TRAIT(src, TRAIT_TRUE_NIGHT_VISION))
lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE)
see_in_dark = max(see_in_dark, 8)
if(HAS_TRAIT(src, TRAIT_MESON_VISION))
sight |= SEE_TURFS
lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
if(HAS_TRAIT(src, TRAIT_THERMAL_VISION))
sight |= (SEE_MOBS)
sight |= SEE_MOBS
lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
if(HAS_TRAIT(src, TRAIT_XRAY_VISION))
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
see_in_dark = max(see_in_dark, 8)
if(see_override)

View File

@@ -16,9 +16,15 @@
if(istype(T) && movement_dir && T.allow_thrust(0.01))
return 1
var/obj/item/tank/jetpack/J = get_jetpack()
if(istype(J) && (movement_dir || J.stabilizers) && J.allow_thrust(0.01, src))
return 1
var/obj/item/I = get_jetpack()
if(istype(I, /obj/item/tank/jetpack))
var/obj/item/tank/jetpack/J = I
if((movement_dir || J.stabilizers) && J.allow_thrust(0.01, src))
return 1
else if(istype(I, /obj/item/mod/module/jetpack))
var/obj/item/mod/module/jetpack/J = I
if((movement_dir || J.stabilizers) && J.allow_thrust())
return 1
/mob/living/carbon/Moved()
. = ..()

View File

@@ -308,7 +308,14 @@
if(equip_to_slot_if_possible(thing, ITEM_SLOT_BACK))
update_inv_hands()
return
if(!SEND_SIGNAL(equipped_back, COMSIG_CONTAINS_STORAGE)) // not a storage item
var/datum/component/storage/storage = equipped_back.GetComponent(/datum/component/storage)
if(istype(equipped_back, /obj/item/mod/control))
var/obj/item/mod/control/C = equipped_back
for(var/obj/item/mod/module/storage/S in C.modules)
if(S.stored)
equipped_back = S.stored
storage = S.stored.GetComponent(/datum/component/storage)
if(!storage)
if(!thing)
equipped_back.attack_hand(src)
else
@@ -318,10 +325,11 @@
if(!SEND_SIGNAL(equipped_back, COMSIG_TRY_STORAGE_INSERT, thing, src))
to_chat(src, "<span class='warning'>You can't fit anything in!</span>")
return
if(!equipped_back.contents.len) // nothing to take out
to_chat(src, "<span class='warning'>There's nothing in your backpack to take out!</span>")
var/atom/real_location = storage.real_location()
if(!real_location.contents.len) // nothing to take out
to_chat(src, "<span class='warning'>There's nothing in your [equipped_back.name] to take out!</span>")
return
var/obj/item/stored = equipped_back.contents[equipped_back.contents.len]
var/obj/item/stored = real_location.contents[real_location.contents.len]
if(!stored || stored.on_found(src))
return
stored.attack_hand(src) // take out thing from backpack

View File

@@ -38,8 +38,8 @@
var/can_be_carded = TRUE
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list(), "Burglar"=list())
var/viewalerts = 0
var/icon/holo_icon//Female is assigned when AI is created.
var/obj/vehicle/sealed/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
var/icon/holo_icon //Female is assigned when AI is created.
var/obj/controlled_equipment //A piece of equipment, to determine whether to relaymove or use the AI eye.
var/radio_enabled = TRUE //Determins if a carded AI can speak with its built in radio or not.
radiomod = ";" //AIs will, by default, state their laws on the internal radio.
var/obj/item/pda/ai/aiPDA
@@ -99,6 +99,13 @@
var/display_icon_override
var/emote_display = "Neutral" //text string of the current emote we set for the status displays, to prevent logins resetting it.
var/datum/robot_control/robot_control
/// Station alert datum for showing alerts UI
var/datum/station_alert/alert_control
///remember AI's last location
var/atom/lastloc
interaction_range = INFINITY
/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
. = ..()
if(!target_ai) //If there is no player/brain inside.
@@ -173,10 +180,21 @@
GLOB.ai_list -= src
GLOB.shuttle_caller_list -= src
SSshuttle.autoEvac()
qdel(eyeobj) // No AI, no Eye
QDEL_NULL(eyeobj) // No AI, no Eye
QDEL_NULL(spark_system)
QDEL_NULL(malf_picker)
QDEL_NULL(doomsday_device)
// TODO: Why these no work?
// QDEL_NULL(robot_control)
QDEL_NULL(aiMulti)
// QDEL_NULL(alert_control)
malfhack = null
. = ..()
current = null
Bot = null
controlled_equipment = null
linked_core = null
apc_override = null
return ..()
/mob/living/silicon/ai/IgniteMob()
fire_stacks = 0
@@ -410,7 +428,7 @@
if (href_list["ai_take_control"]) //Mech domination
var/obj/vehicle/sealed/mecha/M = locate(href_list["ai_take_control"])
if(controlled_mech)
if(controlled_equipment)
to_chat(src, "<span class='warning'>You are already loaded into an onboard computer!</span>")
return
if(!GLOB.cameranet.checkCameraVis(M))

View File

@@ -199,6 +199,13 @@
/mob/living/silicon/pai/restrained(ignore_grab)
. = FALSE
/mob/living/silicon/pai/can_interact_with(atom/target)
if(istype(target, /obj/item/mod/control)) // A poor workaround for enabling MODsuit control
var/obj/item/mod/control/C = target
if(C.ai == src)
return TRUE
return ..()
// See software.dm for Topic()
/mob/living/silicon/pai/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)

View File

@@ -722,7 +722,7 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
return pick(protection_sources)
else
return src
if((magic && HAS_TRAIT(src, TRAIT_ANTIMAGIC)) || (holy && HAS_TRAIT(src, TRAIT_HOLY)))
if((magic && HAS_TRAIT(src, TRAIT_ANTIMAGIC)) || (!self && magic && HAS_TRAIT(src, TRAIT_ANTIMAGIC_NO_SELFBLOCK)) || (holy && HAS_TRAIT(src, TRAIT_HOLY)))
return src
//You can buckle on mobs if you're next to them since most are dense

View File

@@ -585,7 +585,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
//Can the mob see reagents inside of containers?
/mob/proc/can_see_reagents()
return stat == DEAD || silicon_privileges //Dead guys and silicons can always see reagents
return stat == DEAD || silicon_privileges || HAS_TRAIT(src, TRAIT_REAGENT_SCANNER) //Dead guys and silicons can always see reagents
/mob/proc/is_blind()
SHOULD_BE_PURE(TRUE)

View File

@@ -0,0 +1,94 @@
/datum/action/item_action/mod
background_icon_state = "bg_tech_blue"
icon_icon = 'icons/mob/actions/actions_mod.dmi'
check_flags = AB_CHECK_CONSCIOUS
var/obj/item/mod/control/mod
/// Whether this action is intended for the AI. Stuff breaks a lot if this is done differently.
var/ai_action = FALSE
/datum/action/item_action/mod/New(Target)
..()
if(!istype(Target, /obj/item/mod/control))
qdel(src)
return
if(ai_action)
background_icon_state = "bg_tech"
/datum/action/item_action/mod/Grant(mob/user)
mod = target
if(ai_action && user != mod.ai)
return
else if(!ai_action && user == mod.ai)
return
return ..()
/datum/action/item_action/mod/Remove(mob/user)
if(ai_action && mod && user != mod.ai)
return
else if(!ai_action && mod && user == mod.ai)
return
return ..()
/datum/action/item_action/mod/Trigger(trigger_flags)
if(!IsAvailable())
return FALSE
if(mod.malfunctioning && prob(75))
mod.balloon_alert(usr, "button malfunctions!")
return FALSE
return TRUE
/datum/action/item_action/mod/deploy
name = "Deploy MODsuit"
desc = "Deploy/Conceal a part of the MODsuit."
button_icon_state = "deploy"
/datum/action/item_action/mod/deploy/Trigger()
if(!IsAvailable())
return FALSE
mod.choose_deploy(usr)
return TRUE
/datum/action/item_action/mod/deploy/ai
ai_action = TRUE
/datum/action/item_action/mod/activate
name = "Activate MODsuit"
desc = "Activate/Deactivate the MODsuit."
button_icon_state = "activate"
/datum/action/item_action/mod/activate/Trigger()
if(!IsAvailable())
return FALSE
mod.toggle_activate(usr)
return TRUE
/datum/action/item_action/mod/activate/ai
ai_action = TRUE
/datum/action/item_action/mod/module
name = "Toggle Module"
desc = "Toggle a MODsuit module."
button_icon_state = "module"
/datum/action/item_action/mod/module/Trigger()
if(!IsAvailable())
return FALSE
mod.quick_module(usr)
return TRUE
/datum/action/item_action/mod/module/ai
ai_action = TRUE
/datum/action/item_action/mod/panel
name = "MODsuit Panel"
desc = "Open the MODsuit's panel."
button_icon_state = "panel"
/datum/action/item_action/mod/panel/Trigger()
if(!IsAvailable())
return FALSE
mod.ui_interact(usr)
return TRUE
/datum/action/item_action/mod/panel/ai
ai_action = TRUE

View File

@@ -0,0 +1,242 @@
/// Creates a radial menu from which the user chooses parts of the suit to deploy/retract. Repeats until all parts are extended or retracted.
/obj/item/mod/control/proc/choose_deploy(mob/user)
if(!length(mod_parts))
return
var/list/display_names = list()
var/list/items = list()
for(var/obj/item/piece as anything in mod_parts)
display_names[piece.name] = REF(piece)
var/image/piece_image = image(icon = piece.icon, icon_state = piece.icon_state)
items += list(piece.name = piece_image)
var/pick = show_radial_menu(user, src, items, custom_check = FALSE, require_near = TRUE, tooltips = TRUE)
if(!pick)
return
var/part_reference = display_names[pick]
var/obj/item/part = locate(part_reference) in mod_parts
if(!istype(part) || user.incapacitated())
return
if((active && part != helmet) || activating) // SKYRAT EDIT - Let the hair flow - ORIGINAL: if(active || activating)
balloon_alert(user, "deactivate the suit first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
var/parts_to_check = mod_parts - part
if(part.loc == src)
deploy(user, part)
for(var/obj/item/piece as anything in parts_to_check)
if(piece.loc != src)
continue
choose_deploy(user)
break
else
conceal(user, part)
for(var/obj/item/piece as anything in parts_to_check)
if(piece.loc == src)
continue
choose_deploy(user)
break
/// Deploys a part of the suit onto the user.
/obj/item/mod/control/proc/deploy(mob/user, part)
var/obj/item/piece = part
if(piece == gauntlets && wearer.gloves)
gauntlets.overslot = wearer.gloves
wearer.transferItemToLoc(gauntlets.overslot, gauntlets, force = TRUE)
if(piece == boots && wearer.shoes)
boots.overslot = wearer.shoes
wearer.transferItemToLoc(boots.overslot, boots, force = TRUE)
if(wearer.equip_to_slot_if_possible(piece, piece.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
ADD_TRAIT(piece, TRAIT_NODROP, MOD_TRAIT)
if(!user)
return TRUE
wearer.visible_message(span_notice("[wearer]'s [piece] deploy[piece.p_s()] with a mechanical hiss."),
span_notice("[piece] deploy[piece.p_s()] with a mechanical hiss."),
span_hear("You hear a mechanical hiss."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
return TRUE
else if(piece.loc != src)
if(!user)
return FALSE
balloon_alert(user, "[piece] already deployed!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
else
if(!user)
return FALSE
balloon_alert(user, "bodypart clothed!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
/// Retract a part of the suit from the user
/obj/item/mod/control/proc/conceal(mob/user, part)
var/obj/item/piece = part
REMOVE_TRAIT(piece, TRAIT_NODROP, MOD_TRAIT)
if(wearer)
wearer.transferItemToLoc(piece, src, force = TRUE)
if(piece == gauntlets)
gauntlets.show_overslot()
if(piece == boots)
boots.show_overslot()
if(!user)
return
wearer.visible_message(span_notice("[wearer]'s [piece] retract[piece.p_s()] back into [src] with a mechanical hiss."),
span_notice("[piece] retract[piece.p_s()] back into [src] with a mechanical hiss."),
span_hear("You hear a mechanical hiss."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
/// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on
/obj/item/mod/control/proc/toggle_activate(mob/user, force_deactivate = FALSE)
if(!wearer)
if(!force_deactivate)
balloon_alert(user, "put suit on back!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(!force_deactivate && (SEND_SIGNAL(src, COMSIG_MOD_ACTIVATE, user) & MOD_CANCEL_ACTIVATE))
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
for(var/obj/item/part as anything in mod_parts)
if(!force_deactivate && part.loc == src)
balloon_alert(user, "deploy all parts first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(!cell?.charge && !force_deactivate)
balloon_alert(user, "suit not powered!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(open && !force_deactivate)
balloon_alert(user, "close the suit panel!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(activating)
if(!force_deactivate)
balloon_alert(user, "suit already [active ? "shutting down" : "starting up"]!")
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 || module.allowed_inactive)
continue
module.on_deactivation()
activating = TRUE
to_chat(wearer, span_notice("MODsuit [active ? "shutting down" : "starting up"]."))
if(ai)
to_chat(ai, span_notice("MODsuit [active ? "shutting down" : "starting up"]."))
if(force_deactivate)
seal_part(boots, seal = FALSE)
seal_part(gauntlets, seal = FALSE)
seal_part(chestplate,seal = FALSE)
seal_part(helmet, seal = FALSE)
finish_activation(on = FALSE)
activating = FALSE
to_chat(wearer, span_notice("Systems shut down. Parts unsealed. Goodbye, [wearer]."))
if(ai)
to_chat(ai, span_notice("<b>SYSTEMS DEACTIVATED. GOODBYE: \"[ai]\"</b>"))
playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
return TRUE
if(do_after(wearer, activation_step_time, target = wearer, required_mobility_flags = NONE))
to_chat(wearer, span_notice("[boots] [active ? "relax their grip on your legs" : "seal around your feet"]."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(boots, seal = !active)
else
return toggle_activate_fail()
if(do_after(wearer, activation_step_time, target = wearer, required_mobility_flags = NONE))
to_chat(wearer, span_notice("[gauntlets] [active ? "become loose around your fingers" : "tighten around your fingers and wrists"]."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(gauntlets, seal = !active)
else
return toggle_activate_fail()
if(do_after(wearer, activation_step_time, target = wearer, required_mobility_flags = NONE))
to_chat(wearer, span_notice("[chestplate] [active ? "releases your chest" : "cinches tightly against your chest"]."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(chestplate,seal = !active)
else
return toggle_activate_fail()
if(do_after(wearer, activation_step_time, target = wearer, required_mobility_flags = NONE))
to_chat(wearer, span_notice("[helmet] hisses [active ? "open" : "closed"]."))
playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
seal_part(helmet, seal = !active)
else
return toggle_activate_fail()
if(do_after(wearer, activation_step_time, target = wearer, required_mobility_flags = NONE))
to_chat(wearer, span_notice("Systems [active ? "shut down. Parts unsealed. Goodbye" : "started up. Parts sealed. Welcome"], [wearer]."))
if(ai)
to_chat(ai, span_notice("<b>SYSTEMS [active ? "DEACTIVATED. GOODBYE" : "ACTIVATED. WELCOME"]: \"[ai]\"</b>"))
finish_activation(on = !active)
if(active)
playsound(src, 'sound/machines/synth_yes.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
SEND_SOUND(wearer, sound('sound/mecha/nominal.ogg',volume=50))
else
playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
else
return toggle_activate_fail()
activating = FALSE
return TRUE
/obj/item/mod/control/proc/toggle_activate_fail()
seal_part(boots, seal = active)
seal_part(gauntlets, seal = active)
seal_part(chestplate,seal = active)
seal_part(helmet, seal = active)
to_chat(wearer, span_warning("[active ? "Shut down" : "Start up"] cancelled."))
finish_activation(on = active)
activating = FALSE
return FALSE
///Seals or unseals the given part
/obj/item/mod/control/proc/seal_part(obj/item/clothing/part, seal)
if(seal)
part.clothing_flags |= part.visor_flags
part.flags_inv |= part.visor_flags_inv
part.flags_cover |= part.visor_flags_cover
part.heat_protection = initial(part.heat_protection)
part.cold_protection = initial(part.cold_protection)
else
part.flags_cover &= ~part.visor_flags_cover
part.flags_inv &= ~part.visor_flags_inv
part.clothing_flags &= ~part.visor_flags
part.heat_protection = NONE
part.cold_protection = NONE
part.icon_state = "[skin]-[initial(part.icon_state)][seal ? "-sealed" : ""]"
part.item_state = "[skin]-[initial(part.item_state)][seal ? "-sealed" : ""]"
if(part == boots)
wearer.update_inv_shoes()
if(part == gauntlets)
wearer.update_inv_gloves()
if(part == chestplate)
wearer.update_inv_wear_suit()
wearer.update_inv_w_uniform()
if(part == helmet)
if(seal)
helmet.alternate_worn_layer = null
else
helmet.alternate_worn_layer = helmet.alternate_layer
wearer.update_inv_head()
wearer.update_inv_wear_mask()
wearer.update_hair()
/// Finishes the suit's activation, starts processing
/obj/item/mod/control/proc/finish_activation(on)
icon_state = "[skin]-control[on ? "-sealed" : ""]"
slowdown = on ? slowdown_active : slowdown_inactive
if(on)
for(var/obj/item/mod/module/module as anything in modules)
module.on_suit_activation()
START_PROCESSING(SSobj, src)
else
for(var/obj/item/mod/module/module as anything in modules)
module.on_suit_deactivation()
STOP_PROCESSING(SSobj, src)
wearer.update_equipment_speed_mods()
active = on
wearer.update_inv_back()
/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits.
/obj/item/mod/control/proc/quick_activation()
var/seal = TRUE
for(var/obj/item/part in mod_parts)
if(!deploy(null, part))
seal = FALSE
if(!seal)
return
for(var/obj/item/part in mod_parts)
seal_part(part, seal = TRUE)
finish_activation(on = TRUE)

199
code/modules/mod/mod_ai.dm Normal file
View File

@@ -0,0 +1,199 @@
/obj/item/mod/control/transfer_ai(interaction, mob/user, mob/living/silicon/ai/intAI, obj/item/aicard/card)
. = ..()
if(!.)
return
if(!open) //mod must be open
balloon_alert(user, "suit must be open to transfer!")
return
switch(interaction)
if(AI_TRANS_TO_CARD)
if(!ai)
balloon_alert(user, "no AI in suit!")
return
if(!isAI(ai))
balloon_alert(user, "onboard AI cannot fit in this card!")
return
balloon_alert(user, "transferring to card...")
if(!do_after(user, 5 SECONDS, target = src))
balloon_alert(user, "interrupted!")
return
if(!ai)
return
intAI = ai
intAI.ai_restore_power()//So the AI initially has power.
intAI.control_disabled = TRUE
intAI.radio_enabled = FALSE
intAI.disconnect_shell()
intAI.forceMove(card)
card.AI = intAI
for(var/datum/action/action as anything in actions)
action.Remove(intAI)
intAI.controlled_equipment = null
intAI.remote_control = null
balloon_alert(intAI, "transferred to a card")
balloon_alert(user, "AI transferred to card")
ai = null
if(AI_TRANS_FROM_CARD) //Using an AI card to upload to the suit.
intAI = card.AI
if(!intAI)
balloon_alert(user, "no AI in card!")
return
if(ai)
balloon_alert(user, "already has AI!")
return
if(intAI.deployed_shell) //Recall AI if shelled so it can be checked for a client
intAI.disconnect_shell()
if(intAI.stat || !intAI.client)
balloon_alert(user, "AI unresponsive!")
return
balloon_alert(user, "transferring to suit...")
if(!do_after(user, 5 SECONDS, target = src))
balloon_alert(user, "interrupted!")
return
if(ai)
return
balloon_alert(user, "AI transferred to suit")
ai_enter_mod(intAI)
card.AI = null
/obj/item/mod/control/proc/ai_enter_mod(mob/living/silicon/ai/new_ai)
new_ai.control_disabled = FALSE
new_ai.radio_enabled = TRUE
new_ai.ai_restore_power()
new_ai.cancel_camera()
new_ai.controlled_equipment = src
new_ai.remote_control = src
new_ai.forceMove(src)
ai = new_ai
balloon_alert(new_ai, "transferred to a suit")
for(var/datum/action/action as anything in actions)
action.Grant(ai)
/**
* Simple proc to insert the pAI into the MODsuit.
*
* user - The person trying to put the pAI into the MODsuit.
* card - The pAI card we're slotting in the MODsuit.
*/
/obj/item/mod/control/proc/insert_pai(mob/user, obj/item/paicard/card)
if(ai)
balloon_alert(user, "AI already installed!")
return
if(!card.pai || !card.pai.mind)
balloon_alert(user, "pAI unresponsive!")
return
balloon_alert(user, "transferring to suit...")
if(!do_after(user, 5 SECONDS, target = src))
balloon_alert(user, "interrupted!")
return FALSE
if(!user.transferItemToLoc(card, src))
return
card.pai.canholo = FALSE
ai = card.pai
balloon_alert(user, "pAI transferred to suit")
balloon_alert(ai, "transferred to a suit")
ai.remote_control = src
for(var/datum/action/action as anything in actions)
action.Grant(ai)
return TRUE
/**
* Simple proc to extract the pAI from the MODsuit. It's the proc to call if you want to take it out,
* remove_pai() is there so atom_destruction() doesn't have any risk of sleeping.
*
* user - The person trying to take out the pAI from the MODsuit.
* forced - Whether or not we skip the checks and just eject the pAI. Defaults to FALSE.
* feedback - Whether to give feedback via balloon alerts or not. Defaults to TRUE.
*/
/obj/item/mod/control/proc/extract_pai(mob/user, forced = FALSE, feedback = TRUE)
if(!ai)
if(user && feedback)
balloon_alert(user, "no pAI to remove!")
return
if(!ispAI(ai))
if(user && feedback)
balloon_alert(user, "onboard AI cannot fit in this card!")
return
if(!forced)
if(!open)
if(user && feedback)
balloon_alert(user, "open the suit panel!")
return FALSE
if(!do_after(user, 5 SECONDS, target = src))
if(user && feedback)
balloon_alert(user, "interrupted!")
return FALSE
remove_pai(feedback)
if(feedback && user)
balloon_alert(user, "pAI removed from the suit")
/**
* Simple proc that handles the safe removal of the pAI from a MOD control unit.
*
* Arguments:
* * feedback - Whether or not we want to give balloon alert feedback to the ai. Defaults to FALSE.
*/
/obj/item/mod/control/proc/remove_pai(feedback = FALSE)
if(!ispAI(ai))
return
var/mob/living/silicon/pai/pai = ai
var/turf/drop_off = get_turf(src)
if(drop_off) // In case there's no drop_off, the pAI will simply get deleted.
pai.card.forceMove(drop_off)
for(var/datum/action/action as anything in actions)
if(action.owner == pai)
action.Remove(pai)
if(feedback)
balloon_alert(pai, "removed from a suit")
pai.remote_control = null
pai.canholo = TRUE
pai = null
ai = null
#define MOVE_DELAY 2
#define WEARER_DELAY 1
#define LONE_DELAY 5
#define CELL_PER_STEP (DEFAULT_CHARGE_DRAIN * 2.5)
#define AI_FALL_TIME (1 SECONDS)
/obj/item/mod/control/relaymove(mob/user, direction)
if((!active && wearer) || !cell || cell.charge < CELL_PER_STEP || user != ai || !COOLDOWN_FINISHED(src, cooldown_mod_move) || (wearer?.pulledby?.grab_state > GRAB_PASSIVE))
return FALSE
var/timemodifier = MOVE_DELAY * (ISDIAGONALDIR(direction) ? SQRT_2 : 1) * (wearer ? WEARER_DELAY : LONE_DELAY)
if(wearer && !wearer.Process_Spacemove(direction))
return FALSE
else if(!wearer && (!has_gravity() || !isturf(loc)))
return FALSE
COOLDOWN_START(src, cooldown_mod_move, movedelay * timemodifier + slowdown)
cell.charge = max(0, cell.charge - CELL_PER_STEP)
playsound(src, 'sound/mecha/mechmove01.ogg', 25, TRUE)
if(ismovable(wearer?.loc))
return wearer.loc.relaymove(wearer, direction)
else if(wearer)
ADD_TRAIT(wearer, TRAIT_MOBILITY_NOREST, MOD_TRAIT)
addtimer(CALLBACK(src, .proc/ai_fall), AI_FALL_TIME, TIMER_UNIQUE | TIMER_OVERRIDE)
var/atom/movable/mover = wearer || src
return step(mover, direction)
#undef MOVE_DELAY
#undef WEARER_DELAY
#undef LONE_DELAY
#undef CELL_PER_STEP
#undef AI_FALL_TIME
/obj/item/mod/control/proc/ai_fall()
if(!wearer)
return
REMOVE_TRAIT(wearer, TRAIT_MOBILITY_NOREST, MOD_TRAIT)
/obj/item/mod/control/ui_state(mob/user)
if(user == ai)
return GLOB.contained_state
return ..()

View File

@@ -0,0 +1,138 @@
/obj/item/clothing/head/helmet/space/mod
name = "MOD helmet"
desc = "A helmet for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing_anthro.dmi'
icon_state = "helmet"
item_state = "helmet"
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10)
body_parts_covered = HEAD
heat_protection = HEAD
cold_protection = HEAD
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
clothing_flags = THICKMATERIAL
resistance_flags = NONE
flash_protect = 0
clothing_flags = NONE
flags_inv = HIDEFACIALHAIR
flags_cover = NONE
visor_flags = THICKMATERIAL|STOPSPRESSUREDAMAGE
visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT
visor_flags_cover = HEADCOVERSMOUTH|HEADCOVERSEYES
var/alternate_layer = NECK_LAYER
var/obj/item/mod/control/mod
mutantrace_variation = STYLE_MUZZLE
/obj/item/clothing/head/helmet/space/mod/Destroy()
if(!QDELETED(mod))
mod.helmet = null
mod.mod_parts -= src
QDEL_NULL(mod)
return ..()
/obj/item/clothing/suit/space/mod
name = "MOD chestplate"
desc = "A chestplate for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing_anthro.dmi'
icon_state = "chestplate"
item_state = "chestplate"
blood_overlay_type = "armor"
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10)
body_parts_covered = CHEST|GROIN
heat_protection = CHEST|GROIN
cold_protection = CHEST|GROIN
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
clothing_flags = THICKMATERIAL
flags_inv = HIDETAUR
visor_flags = STOPSPRESSUREDAMAGE
visor_flags_inv = HIDEJUMPSUIT
allowed = list(/obj/item/flashlight, /obj/item/tank/internals)
resistance_flags = NONE
var/obj/item/mod/control/mod
mutantrace_variation = STYLE_DIGITIGRADE
/obj/item/clothing/suit/space/mod/Destroy()
if(!QDELETED(mod))
mod.chestplate = null
mod.mod_parts -= src
QDEL_NULL(mod)
return ..()
/obj/item/clothing/gloves/mod
name = "MOD gauntlets"
desc = "A pair of gauntlets for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
icon_state = "gauntlets"
item_state = "gauntlets"
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10)
body_parts_covered = HANDS|ARMS
heat_protection = HANDS|ARMS
cold_protection = HANDS|ARMS
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
clothing_flags = THICKMATERIAL
resistance_flags = NONE
var/obj/item/mod/control/mod
var/obj/item/clothing/overslot
mutantrace_variation = STYLE_NO_ANTHRO_ICON
/obj/item/clothing/gloves/mod/Destroy()
if(!QDELETED(mod))
mod.gauntlets = null
mod.mod_parts -= src
QDEL_NULL(mod)
return ..()
/// Replaces these gloves on the wearer with the overslot ones
/obj/item/clothing/gloves/mod/proc/show_overslot()
if(!overslot)
return
if(!mod.wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
mod.wearer.dropItemToGround(overslot, force = TRUE)
overslot = null
/obj/item/clothing/shoes/mod
name = "MOD boots"
desc = "A pair of boots for a MODsuit."
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
anthro_mob_worn_overlay = 'icons/mob/clothing/modsuit/mod_clothing_anthro.dmi'
icon_state = "boots"
item_state = "boots"
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10)
body_parts_covered = FEET|LEGS
heat_protection = FEET|LEGS
cold_protection = FEET|LEGS
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
clothing_flags = THICKMATERIAL
resistance_flags = NONE
item_flags = NONE
var/obj/item/mod/control/mod
var/obj/item/clothing/overslot
mutantrace_variation = STYLE_DIGITIGRADE
/obj/item/clothing/shoes/mod/Destroy()
if(!QDELETED(mod))
mod.boots = null
mod.mod_parts -= src
QDEL_NULL(mod)
return ..()
/// Replaces these shoes on the wearer with the overslot ones
/obj/item/clothing/shoes/mod/proc/show_overslot()
if(!overslot)
return
if(!mod.wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
mod.wearer.dropItemToGround(overslot, force = TRUE)
overslot = null
/obj/item/clothing/shoes/mod/negates_gravity()
return clothing_flags & NOSLIP

View File

@@ -0,0 +1,288 @@
/obj/item/mod/construction
desc = "A part used in MOD construction."
icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
item_state = "rack_parts"
/obj/item/mod/construction/helmet
name = "MOD helmet"
icon_state = "helmet"
/obj/item/mod/construction/chestplate
name = "MOD chestplate"
icon_state = "chestplate"
/obj/item/mod/construction/gauntlets
name = "MOD gauntlets"
icon_state = "gauntlets"
/obj/item/mod/construction/boots
name = "MOD boots"
icon_state = "boots"
/obj/item/mod/construction/core
name = "MOD core"
icon_state = "mod-core-standard"
desc = "Growing in the most lush, fertile areas of the planet Sprout, there is a crystal known as the Heartbloom. \
These rare, organic piezoelectric crystals are of incredible cultural significance to the artist castes of the Ethereals, \
owing to their appearance; which is exactly similar to that of an Ethereal's heart. \n\
Which one you have in your suit is unclear, but either way, \
it's been repurposed to be an internal power source for a Modular Outerwear Device."
/obj/item/mod/construction/broken_core
name = "broken MOD core"
icon_state = "mod-core"
desc = "An internal power source for a Modular Outerwear Device. You don't seem to be able to source any power from this one, though."
/obj/item/mod/construction/broken_core/examine(mob/user)
. = ..()
. += span_notice("You could repair it with a <b>screwdriver</b>...")
/obj/item/mod/construction/broken_core/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
if(!tool.use_tool(src, user, 5 SECONDS, volume = 30))
return
new /obj/item/mod/construction/core(drop_location())
qdel(src)
/obj/item/mod/construction/armor
name = "MOD armor plates"
desc = "Armor plates used to finish a MOD."
icon_state = "standard-plating"
var/datum/mod_theme/theme = /datum/mod_theme
/obj/item/mod/construction/armor/Initialize(mapload)
. = ..()
var/datum/mod_theme/used_theme = GLOB.mod_themes[theme]
name = "MOD [used_theme.name] armor plates"
desc = "[desc] [used_theme.desc]"
icon_state = "[used_theme.default_skin]-plating"
/obj/item/mod/construction/armor/engineering
theme = /datum/mod_theme/engineering
/obj/item/mod/construction/armor/atmospheric
theme = /datum/mod_theme/atmospheric
/obj/item/mod/construction/armor/advanced
theme = /datum/mod_theme/advanced
/obj/item/mod/construction/armor/mining
theme = /datum/mod_theme/mining
/obj/item/mod/construction/armor/medical
theme = /datum/mod_theme/medical
/obj/item/mod/construction/armor/rescue
theme = /datum/mod_theme/rescue
/obj/item/mod/construction/armor/security
theme = /datum/mod_theme/security
/obj/item/mod/construction/armor/safeguard
theme = /datum/mod_theme/safeguard
/obj/item/mod/construction/armor/research
theme = /datum/mod_theme/research
/obj/item/mod/construction/armor/cosmohonk
theme = /datum/mod_theme/cosmohonk
/obj/item/mod/construction/armor/magnate
theme = /datum/mod_theme/magnate
#define START_STEP "start"
#define CORE_STEP "core"
#define SCREWED_CORE_STEP "screwed_core"
#define HELMET_STEP "helmet"
#define CHESTPLATE_STEP "chestplate"
#define GAUNTLETS_STEP "gauntlets"
#define BOOTS_STEP "boots"
#define WRENCHED_ASSEMBLY_STEP "wrenched_assembly"
#define SCREWED_ASSEMBLY_STEP "screwed_assembly"
/obj/item/mod/construction/shell
name = "MOD shell"
icon_state = "mod-construction_start"
desc = "A MOD shell."
var/obj/item/core
var/obj/item/helmet
var/obj/item/chestplate
var/obj/item/gauntlets
var/obj/item/boots
var/step = START_STEP
/obj/item/mod/construction/shell/examine(mob/user)
. = ..()
var/display_text
switch(step)
if(START_STEP)
display_text = "It looks like it's missing a <b>MOD core</b>..."
if(CORE_STEP)
display_text = "The core seems <b>loose</b>..."
if(SCREWED_CORE_STEP)
display_text = "It looks like it's missing a <b>helmet</b>..."
if(HELMET_STEP)
display_text = "It looks like it's missing a <b>chestplate</b>..."
if(CHESTPLATE_STEP)
display_text = "It looks like it's missing <b>gauntlets</b>..."
if(GAUNTLETS_STEP)
display_text = "It looks like it's missing <b>boots</b>..."
if(BOOTS_STEP)
display_text = "The assembly seems <b>unsecured</b>..."
if(WRENCHED_ASSEMBLY_STEP)
display_text = "The assembly seems <b>loose</b>..."
if(SCREWED_ASSEMBLY_STEP)
display_text = "All it's missing is <b>external armor</b>..."
. += span_notice(display_text)
/obj/item/mod/construction/shell/attackby(obj/item/part, mob/user, params)
. = ..()
switch(step)
if(START_STEP)
if(!istype(part, /obj/item/mod/construction/core))
return
if(!user.transferItemToLoc(part, src))
balloon_alert(user, "core stuck to your hand!")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
balloon_alert(user, "core inserted")
core = part
step = CORE_STEP
if(CORE_STEP)
if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
if(part.use_tool(src, user, 0, volume=30))
balloon_alert(user, "core screwed")
step = SCREWED_CORE_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume=30))
core.forceMove(drop_location())
balloon_alert(user, "core taken out")
step = START_STEP
if(SCREWED_CORE_STEP)
if(istype(part, /obj/item/mod/construction/helmet)) //Construct
if(!user.transferItemToLoc(part, src))
balloon_alert(user, "helmet stuck to your hand!")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
balloon_alert(user, "helmet added")
helmet = part
step = HELMET_STEP
else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Deconstruct
if(part.use_tool(src, user, 0, volume=30))
balloon_alert(user, "core unscrewed")
step = CORE_STEP
if(HELMET_STEP)
if(istype(part, /obj/item/mod/construction/chestplate)) //Construct
if(!user.transferItemToLoc(part, src))
balloon_alert(user, "chestplate stuck to your hand!")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
balloon_alert(user, "chestplate added")
chestplate = part
step = CHESTPLATE_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume=30))
helmet.forceMove(drop_location())
balloon_alert(user, "helmet removed")
helmet = null
step = SCREWED_CORE_STEP
if(CHESTPLATE_STEP)
if(istype(part, /obj/item/mod/construction/gauntlets)) //Construct
if(!user.transferItemToLoc(part, src))
balloon_alert(user, "gauntlets stuck to your hand!")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
balloon_alert(user, "gauntlets added")
gauntlets = part
step = GAUNTLETS_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume=30))
chestplate.forceMove(drop_location())
balloon_alert(user, "chestplate removed")
chestplate = null
step = HELMET_STEP
if(GAUNTLETS_STEP)
if(istype(part, /obj/item/mod/construction/boots)) //Construct
if(!user.transferItemToLoc(part, src))
balloon_alert(user, "boots added")
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
balloon_alert(user, "You fit [part] onto [src].")
boots = part
step = BOOTS_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume=30))
gauntlets.forceMove(drop_location())
balloon_alert(user, "gauntlets removed")
gauntlets = null
step = CHESTPLATE_STEP
if(BOOTS_STEP)
if(part.tool_behaviour == TOOL_WRENCH) //Construct
if(part.use_tool(src, user, 0, volume=30))
balloon_alert(user, "assembly secured")
step = WRENCHED_ASSEMBLY_STEP
else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
if(part.use_tool(src, user, 0, volume=30))
boots.forceMove(drop_location())
balloon_alert(user, "boots removed")
boots = null
step = GAUNTLETS_STEP
if(WRENCHED_ASSEMBLY_STEP)
if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
if(part.use_tool(src, user, 0, volume=30))
balloon_alert(user, "assembly screwed")
step = SCREWED_ASSEMBLY_STEP
else if(part.tool_behaviour == TOOL_WRENCH) //Deconstruct
if(part.use_tool(src, user, 0, volume=30))
balloon_alert(user, "assembly unsecured")
step = BOOTS_STEP
if(SCREWED_ASSEMBLY_STEP)
if(istype(part, /obj/item/mod/construction/armor)) //Construct
var/obj/item/mod/construction/armor/external_armor = part
if(!user.transferItemToLoc(part, src))
return
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
balloon_alert(user, "suit finished")
var/obj/item/modsuit = new /obj/item/mod/control(drop_location(), external_armor.theme)
qdel(src)
user.put_in_hands(modsuit)
else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
if(part.use_tool(src, user, 0, volume=30))
balloon_alert(user, "assembly unscrewed")
step = SCREWED_ASSEMBLY_STEP
update_icon_state()
/obj/item/mod/construction/shell/update_icon_state()
. = ..()
icon_state = "mod-construction_[step]"
/obj/item/mod/construction/shell/Destroy()
QDEL_NULL(core)
QDEL_NULL(helmet)
QDEL_NULL(chestplate)
QDEL_NULL(gauntlets)
QDEL_NULL(boots)
return ..()
/obj/item/mod/construction/shell/handle_atom_del(atom/deleted_atom)
if(deleted_atom == core)
core = null
if(deleted_atom == helmet)
helmet = null
if(deleted_atom == chestplate)
chestplate = null
if(deleted_atom == gauntlets)
gauntlets = null
if(deleted_atom == boots)
boots = null
return ..()
#undef START_STEP
#undef CORE_STEP
#undef SCREWED_CORE_STEP
#undef HELMET_STEP
#undef CHESTPLATE_STEP
#undef GAUNTLETS_STEP
#undef BOOTS_STEP
#undef WRENCHED_ASSEMBLY_STEP
#undef SCREWED_ASSEMBLY_STEP

View File

@@ -0,0 +1,575 @@
/// MODsuits, trade-off between armor and utility
/obj/item/mod
name = "Base MOD"
desc = "You should not see this, yell at a coder!"
icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
mob_overlay_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
icon_state = "standard-control"
item_state = "standard-control"
mutantrace_variation = STYLE_NO_ANTHRO_ICON
/obj/item/mod/control
name = "MOD control unit"
desc = "The control unit of a Modular Outerwear Device, a powered, back-mounted suit that protects against various environments."
icon_state = "control"
item_state = "control"
w_class = WEIGHT_CLASS_BULKY
slot_flags = ITEM_SLOT_BACK
strip_delay = 10 SECONDS
slowdown = 2
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 10, RAD = 0)
actions_types = list(
/datum/action/item_action/mod/deploy,
/datum/action/item_action/mod/activate,
/datum/action/item_action/mod/module,
/datum/action/item_action/mod/panel,
/datum/action/item_action/mod/deploy/ai,
/datum/action/item_action/mod/activate/ai,
/datum/action/item_action/mod/module/ai,
/datum/action/item_action/mod/panel/ai,
)
resistance_flags = NONE
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
permeability_coefficient = 0.01
siemens_coefficient = 0.5
alternate_worn_layer = BODY_FRONT_LAYER
/// The MOD's theme, decides on some stuff like armor and statistics.
var/datum/mod_theme/theme = /datum/mod_theme
/// Looks of the MOD.
var/skin = "standard"
/// Theme of the MOD TGUI
var/ui_theme = "ntos"
/// If the suit is deployed and turned on.
var/active = FALSE
/// If the suit wire/module hatch is open.
var/open = FALSE
/// If the suit is malfunctioning.
var/malfunctioning = FALSE
/// If the suit is currently activating/deactivating.
var/activating = FALSE
/// How long the MOD is electrified for.
var/seconds_electrified = MACHINE_NOT_ELECTRIFIED
/// If the suit interface is broken.
var/interface_break = FALSE
/// How much module complexity can this MOD carry.
var/complexity_max = DEFAULT_MAX_COMPLEXITY
/// How much module complexity this MOD is carrying.
var/complexity = 0
/// Power usage of the MOD.
var/cell_drain = DEFAULT_CHARGE_DRAIN
/// Slowdown of the MOD when not active.
var/slowdown_inactive = 2
/// Slowdown of the MOD when active.
var/slowdown_active = 1
/// How long this MOD takes each part to seal.
var/activation_step_time = MOD_ACTIVATION_STEP_TIME
/// MOD cell.
var/obj/item/stock_parts/cell/cell
/// MOD helmet.
var/obj/item/clothing/head/helmet/space/mod/helmet
/// MOD chestplate.
var/obj/item/clothing/suit/space/mod/chestplate
/// MOD gauntlets.
var/obj/item/clothing/gloves/mod/gauntlets
/// MOD boots.
var/obj/item/clothing/shoes/mod/boots
/// List of parts (helmet, chestplate, gauntlets, boots).
var/list/mod_parts = list()
/// Modules the MOD should spawn with.
var/list/initial_modules = list()
/// Modules the MOD currently possesses.
var/list/modules = list()
/// Currently used module.
var/obj/item/mod/module/selected_module
/// AI/pAI mob inhabiting the MOD.
var/mob/living/silicon/ai
/// Delay between moves as AI.
var/movedelay = 0
/// Cooldown for AI moves.
COOLDOWN_DECLARE(cooldown_mod_move)
/// Person wearing the MODsuit.
var/mob/living/carbon/human/wearer
/obj/item/mod/control/Initialize(mapload, new_theme, new_skin)
. = ..()
if(new_theme)
theme = new_theme
theme = GLOB.mod_themes[theme]
slowdown_inactive = theme.slowdown_inactive
slowdown_active = theme.slowdown_active
slowdown = slowdown_inactive
complexity_max = theme.complexity_max
skin = new_skin || theme.default_skin
ui_theme = theme.ui_theme
cell_drain = theme.cell_drain
initial_modules += theme.inbuilt_modules
wires = new /datum/wires/mod(src)
if(ispath(cell))
cell = new cell(src)
helmet = new /obj/item/clothing/head/helmet/space/mod(src)
helmet.mod = src
mod_parts += helmet
chestplate = new /obj/item/clothing/suit/space/mod(src)
chestplate.mod = src
mod_parts += chestplate
gauntlets = new /obj/item/clothing/gloves/mod(src)
gauntlets.mod = src
mod_parts += gauntlets
boots = new /obj/item/clothing/shoes/mod(src)
boots.mod = src
mod_parts += boots
var/list/all_parts = mod_parts.Copy() + src
for(var/obj/item/piece as anything in all_parts)
piece.name = "[theme.name] [piece.name]"
piece.desc = "[piece.desc] [theme.desc]"
piece.armor = getArmor(arglist(theme.armor))
piece.resistance_flags = theme.resistance_flags
piece.heat_protection = NONE
piece.cold_protection = NONE
piece.max_heat_protection_temperature = theme.max_heat_protection_temperature
piece.min_cold_protection_temperature = theme.min_cold_protection_temperature
piece.permeability_coefficient = theme.permeability_coefficient
piece.siemens_coefficient = theme.siemens_coefficient
piece.icon_state = "[skin]-[initial(piece.icon_state)]"
piece.item_state = "[skin]-[initial(piece.item_state)]"
update_flags()
for(var/obj/item/mod/module/module as anything in initial_modules)
module = new module(src)
install(module)
RegisterSignal(src, COMSIG_ATOM_EXITED, .proc/on_exit)
movedelay = CONFIG_GET(number/movedelay/run_delay)
/obj/item/mod/control/Destroy()
if(active)
STOP_PROCESSING(SSobj, src)
var/atom/deleting_atom
if(!QDELETED(helmet))
deleting_atom = helmet
helmet.mod = null
helmet = null
mod_parts -= deleting_atom
qdel(deleting_atom)
if(!QDELETED(chestplate))
deleting_atom = chestplate
chestplate.mod = null
chestplate = null
mod_parts -= deleting_atom
qdel(deleting_atom)
if(!QDELETED(gauntlets))
deleting_atom = gauntlets
gauntlets.mod = null
gauntlets = null
mod_parts -= deleting_atom
qdel(deleting_atom)
if(!QDELETED(boots))
deleting_atom = boots
boots.mod = null
boots = null
mod_parts -= deleting_atom
qdel(deleting_atom)
for(var/obj/item/mod/module/module as anything in modules)
module.mod = null
modules -= module
QDEL_NULL(ai)
QDEL_NULL(wires)
QDEL_NULL(cell)
return ..()
/obj/item/mod/control/process(delta_time)
if(seconds_electrified > MACHINE_NOT_ELECTRIFIED)
seconds_electrified--
if((!cell || !cell.charge) && active && !activating)
power_off()
return PROCESS_KILL
var/malfunctioning_charge_drain = 0
if(malfunctioning)
malfunctioning_charge_drain = rand(1,20)
cell.charge = max(0, cell.charge - (cell_drain + malfunctioning_charge_drain)*delta_time)
update_cell_alert()
for(var/obj/item/mod/module/module as anything in modules)
if(malfunctioning && module.active && DT_PROB(5, delta_time))
module.on_deactivation()
module.on_process(delta_time)
/obj/item/mod/control/equipped(mob/user, slot)
..()
if(slot == ITEM_SLOT_BACK)
set_wearer(user)
else if(wearer)
unset_wearer()
/obj/item/mod/control/dropped(mob/user)
. = ..()
if(wearer)
unset_wearer()
/obj/item/mod/control/item_action_slot_check(slot)
if(slot == ITEM_SLOT_BACK)
return TRUE
/obj/item/mod/control/allow_attack_hand_drop(mob/user)
var/mob/living/carbon/carbon_user = user
if(!istype(carbon_user) || src != carbon_user.back)
return ..()
for(var/obj/item/part in mod_parts)
if(part.loc != src)
balloon_alert(carbon_user, "retract parts first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
return FALSE
return ..()
/obj/item/mod/control/MouseDrop(atom/over_object)
if(src != wearer?.back || !istype(over_object, /atom/movable/screen/inventory/hand))
return ..()
for(var/obj/item/part in mod_parts)
if(part.loc != src)
balloon_alert(wearer, "retract parts first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
return
if(!wearer.incapacitated())
var/atom/movable/screen/inventory/hand/ui_hand = over_object
if(wearer.putItemFromInventoryInHandIfPossible(src, ui_hand.held_index))
add_fingerprint(usr)
return ..()
/obj/item/mod/control/attack_hand(mob/user)
if(seconds_electrified && cell?.charge)
if(shock(user))
return
if(open && loc == user)
if(!cell)
balloon_alert(user, "no cell!")
return
balloon_alert(user, "removing cell...")
if(!do_after(user, 1.5 SECONDS, target = src))
balloon_alert(user, "interrupted!")
return
balloon_alert(user, "cell removed")
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
if(!user.put_in_hands(cell))
cell.forceMove(drop_location())
update_cell_alert()
return
return ..()
/obj/item/mod/control/AltClick(mob/user)
if(seconds_electrified && cell?.charge)
if(shock(user))
return
if(!open)
for(var/obj/item/mod/module/storage/S in modules)
if(S.stored)
playsound(user, "rustle", 50, 1, -5)
SEND_SIGNAL(S.stored, COMSIG_TRY_STORAGE_SHOW, wearer, TRUE)
return
. = ..()
/obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver)
. = ..()
if(.)
return TRUE
if(active || activating)
balloon_alert(user, "deactivate suit first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
balloon_alert(user, "[open ? "closing" : "opening"] panel...")
screwdriver.play_tool_sound(src, 100)
if(screwdriver.use_tool(src, user, 1 SECONDS))
if(active || activating)
balloon_alert(user, "deactivate suit first!")
screwdriver.play_tool_sound(src, 100)
balloon_alert(user, "panel [open ? "closed" : "opened"]")
open = !open
else
balloon_alert(user, "interrupted!")
return TRUE
/obj/item/mod/control/crowbar_act(mob/living/user, obj/item/crowbar)
. = ..()
if(!open)
balloon_alert(user, "open the panel first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(!allowed(user))
balloon_alert(user, "insufficient access!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
if(length(modules))
var/list/removable_modules = list()
for(var/obj/item/mod/module/module as anything in modules)
if(!module.removable)
continue
removable_modules += module
var/obj/item/mod/module/module_to_remove = tgui_input_list(user, "Which module to remove?", "Module Removal", removable_modules)
if(!module_to_remove?.mod)
return FALSE
uninstall(module_to_remove)
module_to_remove.forceMove(drop_location())
crowbar.play_tool_sound(src, 100)
return TRUE
balloon_alert(user, "no modules!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
/obj/item/mod/control/attackby(obj/item/attacking_item, mob/living/user, params)
if(istype(attacking_item, /obj/item/paicard))
if(!open) //mod must be open
balloon_alert(user, "suit must be open to transfer!")
return FALSE
insert_pai(user, attacking_item)
return TRUE
if(istype(attacking_item, /obj/item/mod/module))
if(!open)
balloon_alert(user, "open the panel first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
install(attacking_item, user)
return TRUE
else if(istype(attacking_item, /obj/item/stock_parts/cell))
if(!open)
balloon_alert(user, "open the panel first!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
if(cell)
balloon_alert(user, "cell already installed!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
attacking_item.forceMove(src)
cell = attacking_item
balloon_alert(user, "cell installed")
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
update_cell_alert()
return TRUE
else if(is_wire_tool(attacking_item) && open)
wires.interact(user)
return TRUE
else if(open && attacking_item.GetID())
update_access(user, attacking_item)
return TRUE
return ..()
/obj/item/mod/control/get_cell()
if(open)
return cell
/obj/item/mod/control/emp_act(severity)
. = ..()
to_chat(wearer, span_notice("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!"))
if(!active || !wearer || . & EMP_PROTECT_CONTENTS)
return
selected_module = null
wearer.apply_damage(10 / severity, BURN, spread_damage=TRUE)
to_chat(wearer, span_danger("You feel [src] heat up from the EMP, burning you slightly."))
if (wearer.stat < UNCONSCIOUS && prob(10))
wearer.emote("scream")
/obj/item/mod/control/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot)
if(visuals_only)
set_wearer(outfit_wearer) //we need to set wearer manually since it doesnt call equipped
quick_activation()
/obj/item/mod/control/doStrip(mob/stripper, mob/owner)
if(active && !toggle_activate(stripper, force_deactivate = TRUE))
return
for(var/obj/item/part in mod_parts)
conceal(null, part)
return ..()
/obj/item/mod/control/worn_overlays(isinhands = FALSE, icon_file)
. = ..()
if(!active)
return
for(var/obj/item/mod/module/module as anything in modules)
var/list/module_icons = module.generate_worn_overlay()
if(!length(module_icons))
continue
. += module_icons
/obj/item/mod/control/proc/set_wearer(mob/user)
wearer = user
RegisterSignal(wearer, COMSIG_ATOM_EXITED, .proc/on_exit)
RegisterSignal(wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, .proc/on_borg_charge)
update_cell_alert()
for(var/obj/item/mod/module/module as anything in modules)
module.on_equip()
/obj/item/mod/control/proc/unset_wearer()
for(var/obj/item/mod/module/module as anything in modules)
module.on_unequip()
UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_PROCESS_BORGCHARGER_OCCUPANT))
wearer.clear_alert("mod_charge")
wearer = null
/obj/item/mod/control/proc/update_flags()
var/list/used_skin = theme.skins[skin]
for(var/obj/item/clothing/part as anything in mod_parts)
var/used_category
if(part == helmet)
used_category = HELMET_FLAGS
helmet.alternate_worn_layer = used_skin["HELMET_LAYER"]
helmet.alternate_layer = used_skin["HELMET_LAYER"]
if(part == chestplate)
used_category = CHESTPLATE_FLAGS
if(part == gauntlets)
used_category = GAUNTLETS_FLAGS
if(part == boots)
used_category = BOOTS_FLAGS
var/list/category = used_skin[used_category]
part.clothing_flags = category[UNSEALED_CLOTHING] || NONE
part.visor_flags = category[SEALED_CLOTHING] || NONE
part.flags_inv = category[UNSEALED_INVISIBILITY] || NONE
part.visor_flags_inv = category[SEALED_INVISIBILITY] || NONE
part.flags_cover = category[UNSEALED_COVER] || NONE
part.visor_flags_cover = category[SEALED_COVER] || NONE
/obj/item/mod/control/proc/quick_module(mob/user)
if(!length(modules))
return
var/list/display_names = list()
var/list/items = list()
for(var/obj/item/mod/module/module as anything in modules)
if(module.module_type == MODULE_PASSIVE)
continue
display_names[module.name] = REF(module)
var/image/module_image = image(icon = module.icon, icon_state = module.icon_state)
items += list(module.name = module_image)
if(!length(items))
return
var/pick = show_radial_menu(user, src, items, custom_check = FALSE, require_near = TRUE)
if(!pick)
return
var/module_reference = display_names[pick]
var/obj/item/mod/module/selected_module = locate(module_reference) in modules
if(!istype(selected_module) || user.incapacitated())
return
selected_module.on_select()
/obj/item/mod/control/proc/set_mod_color(new_color)
var/list/all_parts = mod_parts + src
for(var/obj/item/part as anything in all_parts)
part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
part.add_atom_colour(new_color, FIXED_COLOUR_PRIORITY)
wearer?.regenerate_icons()
/obj/item/mod/control/proc/set_mod_skin(new_skin)
if(active)
CRASH("[src] tried to set skin while active!")
skin = new_skin
var/list/used_skin = theme.skins[new_skin]
if(used_skin[CONTROL_LAYER])
alternate_worn_layer = used_skin[CONTROL_LAYER]
var/list/skin_updating = mod_parts.Copy() + src
for(var/obj/item/piece as anything in skin_updating)
piece.icon_state = "[skin]-[initial(piece.icon_state)]"
update_flags()
wearer?.regenerate_icons()
/obj/item/mod/control/proc/shock(mob/living/user)
if(!istype(user) || cell?.charge < 1)
return FALSE
do_sparks(5, TRUE, src)
var/check_range = TRUE
return electrocute_mob(user, cell, src, 0.7, check_range)
/obj/item/mod/control/proc/install(module, mob/user)
var/obj/item/mod/module/new_module = module
for(var/obj/item/mod/module/old_module as anything in modules)
if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules))
if(user)
balloon_alert(user, "[new_module] incompatible with [old_module]!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
if(is_type_in_list(module, theme.module_blacklist))
if(user)
balloon_alert(user, "[src] doesn't accept [new_module]!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
var/complexity_with_module = complexity
complexity_with_module += new_module.complexity
if(complexity_with_module > complexity_max)
if(user)
balloon_alert(user, "[new_module] would make [src] too complex!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
new_module.forceMove(src)
modules += new_module
complexity += new_module.complexity
new_module.mod = src
new_module.on_install()
if(wearer)
new_module.on_equip()
if(user)
balloon_alert(user, "[new_module] added")
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
/obj/item/mod/control/proc/uninstall(module)
var/obj/item/mod/module/old_module = module
modules -= old_module
complexity -= old_module.complexity
if(active)
old_module.on_suit_deactivation()
if(old_module.active)
old_module.on_deactivation()
if(wearer)
old_module.on_unequip()
old_module.on_uninstall()
old_module.mod = null
/obj/item/mod/control/proc/update_access(mob/user, obj/item/card/id/card)
if(!allowed(user))
balloon_alert(user, "insufficient access!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
req_access = card.access.Copy()
balloon_alert(user, "access updated")
/obj/item/mod/control/proc/update_cell_alert()
if(!wearer)
return
if(!cell)
wearer.throw_alert("mod_charge", /atom/movable/screen/alert/nocell)
return
var/remaining_cell = cell.charge/cell.maxcharge
switch(remaining_cell)
if(0.75 to INFINITY)
wearer.clear_alert("mod_charge")
if(0.5 to 0.75)
wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 1)
if(0.25 to 0.5)
wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 2)
if(0.01 to 0.25)
wearer.throw_alert("mod_charge", /atom/movable/screen/alert/lowcell, 3)
else
wearer.throw_alert("mod_charge", /atom/movable/screen/alert/emptycell)
/obj/item/mod/control/proc/power_off()
balloon_alert(wearer, "no power!")
toggle_activate(wearer, force_deactivate = TRUE)
/obj/item/mod/control/proc/on_exit(datum/source, atom/movable/part, direction)
SIGNAL_HANDLER
if(part.loc == src)
return
if(part == cell)
cell = null
update_cell_alert()
return
if(part.loc == wearer)
return
if(modules.Find(part))
uninstall(part)
return
if(mod_parts.Find(part))
conceal(wearer, part)
if(active)
INVOKE_ASYNC(src, .proc/toggle_activate, wearer, TRUE)
return
/obj/item/mod/control/proc/on_borg_charge(datum/source, amount)
SIGNAL_HANDLER
if(!cell)
return
cell.give(amount)

View File

@@ -0,0 +1,207 @@
#define MODPAINT_MAX_COLOR_VALUE 1.25
#define MODPAINT_MIN_COLOR_VALUE 0
#define MODPAINT_MAX_SECTION_COLORS 2
#define MODPAINT_MIN_SECTION_COLORS 0.25
#define MODPAINT_MAX_OVERALL_COLORS 4
#define MODPAINT_MIN_OVERALL_COLORS 1.5
#define MODPAINT_MODE_RESKIN "reskin"
#define MODPAINT_MODE_REPAINT "repaint"
/obj/item/mod/paint
name = "MOD paint kit"
desc = "This kit will repaint your MODsuit to something unique."
icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
icon_state = "paintkit"
var/obj/item/mod/control/editing_mod
var/atom/movable/screen/map_view/proxy_view
var/list/current_color
var/mode = MODPAINT_MODE_RESKIN
/obj/item/mod/paint/Initialize(mapload)
. = ..()
current_color = color_matrix_identity()
/obj/item/mod/paint/examine(mob/user)
. = ..()
. += span_notice("It is currently on [mode] mode.")
/obj/item/mod/paint/attack_self(mob/user)
switch(mode)
if(MODPAINT_MODE_RESKIN)
mode = MODPAINT_MODE_REPAINT
if(MODPAINT_MODE_REPAINT)
mode = MODPAINT_MODE_RESKIN
to_chat(user, span_notice("Switched to [mode] mode."))
/obj/item/mod/paint/pre_attack(atom/attacked_atom, mob/living/user, params)
if(!istype(attacked_atom, /obj/item/mod/control))
return ..()
var/obj/item/mod/control/mod = attacked_atom
if(mod.active || mod.activating)
balloon_alert(user, "suit is active!")
return STOP_ATTACK_PROC_CHAIN
switch(mode)
if(MODPAINT_MODE_RESKIN)
paint_skin(mod, user)
if(MODPAINT_MODE_REPAINT)
if(editing_mod)
return STOP_ATTACK_PROC_CHAIN
editing_mod = mod
var/map_name = "color_matrix_proxy_[REF(user.client)]"
proxy_view = new()
proxy_view.name = "screen"
proxy_view.assigned_map = map_name
proxy_view.del_on_map_removal = FALSE
proxy_view.screen_loc = "[map_name]:1,1"
proxy_view.appearance = editing_mod.appearance
proxy_view.color = null
user.client.register_map_obj(proxy_view)
ui_interact(user)
return STOP_ATTACK_PROC_CHAIN
/obj/item/mod/paint/ui_interact(mob/user, datum/tgui/ui)
if(!editing_mod)
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "MODpaint", name)
ui.open()
/obj/item/mod/paint/ui_host()
return editing_mod
/obj/item/mod/paint/ui_close(mob/user)
. = ..()
editing_mod = null
QDEL_NULL(proxy_view)
current_color = color_matrix_identity()
/obj/item/mod/paint/ui_status(mob/user)
if(check_menu(editing_mod, user))
return ..()
return UI_CLOSE
/obj/item/mod/paint/ui_static_data(mob/user)
var/list/data = list()
data["mapRef"] = proxy_view.assigned_map
return data
/obj/item/mod/paint/ui_data(mob/user)
var/list/data = list()
data["currentColor"] = current_color
return data
/obj/item/mod/paint/ui_act(action, list/params)
. = ..()
if(.)
return
switch(action)
if("transition_color")
current_color = params["color"]
animate(proxy_view, time = 0.5 SECONDS, color = current_color)
if("confirm")
if(length(current_color) != 20) //20 is the length of a matrix identity list
return
for(var/color_value in current_color)
if(isnum(color_value))
continue
return
var/total_color_value = 0
var/list/total_colors = current_color.Copy()
total_colors.Cut(13, length(total_colors)) // 13 to 20 are just a and c, dont want to count them
var/red_value = current_color[1] + current_color[5] + current_color[9] //rr + gr + br
var/green_value = current_color[2] + current_color[6] + current_color[10] //rg + gg + bg
var/blue_value = current_color[3] + current_color[7] + current_color[11] //rb + gb + bb
if(red_value > MODPAINT_MAX_SECTION_COLORS)
balloon_alert(usr, "total red too high! ([red_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)")
return
else if(red_value < MODPAINT_MIN_SECTION_COLORS)
balloon_alert(usr, "total red too low! ([red_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)")
return
if(green_value > MODPAINT_MAX_SECTION_COLORS)
balloon_alert(usr, "total green too high! ([green_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)")
return
else if(green_value < MODPAINT_MIN_SECTION_COLORS)
balloon_alert(usr, "total green too low! ([green_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)")
return
if(blue_value > MODPAINT_MAX_SECTION_COLORS)
balloon_alert(usr, "total blue too high! ([blue_value*100]%/[MODPAINT_MAX_SECTION_COLORS*100]%)")
return
else if(blue_value < MODPAINT_MIN_SECTION_COLORS)
balloon_alert(usr, "total blue too low! ([blue_value*100]%/[MODPAINT_MIN_SECTION_COLORS*100]%)")
return
for(var/color_value in total_colors)
total_color_value += color_value
if(color_value > MODPAINT_MAX_COLOR_VALUE)
balloon_alert(usr, "one of colors too high! ([color_value*100]%/[MODPAINT_MAX_COLOR_VALUE*100]%")
return
else if(color_value < MODPAINT_MIN_COLOR_VALUE)
balloon_alert(usr, "one of colors too low! ([color_value*100]%/[MODPAINT_MIN_COLOR_VALUE*100]%")
return
if(total_color_value > MODPAINT_MAX_OVERALL_COLORS)
balloon_alert(usr, "total colors too high! ([total_color_value*100]%/[MODPAINT_MAX_OVERALL_COLORS*100]%)")
return
else if(total_color_value < MODPAINT_MIN_OVERALL_COLORS)
balloon_alert(usr, "total colors too low! ([total_color_value*100]%/[MODPAINT_MIN_OVERALL_COLORS*100]%)")
return
editing_mod.set_mod_color(current_color)
SStgui.close_uis(src)
/obj/item/mod/paint/proc/paint_skin(obj/item/mod/control/mod, mob/user)
if(length(mod.theme.skins) <= 1)
balloon_alert(user, "no alternate skins!")
return
var/list/skins = list()
for(var/mod_skin in mod.theme.skins)
skins[mod_skin] = image(icon = mod.icon, icon_state = "[mod_skin]-control")
var/pick = show_radial_menu(user, mod, skins, custom_check = CALLBACK(src, .proc/check_menu, mod, user), require_near = TRUE)
if(!pick)
balloon_alert(user, "no skin picked!")
return
mod.set_mod_skin(pick)
/obj/item/mod/paint/proc/check_menu(obj/item/mod/control/mod, mob/user)
if(user.incapacitated() || !user.is_holding(src) || !mod || mod.active || mod.activating)
return FALSE
return TRUE
#undef MODPAINT_MAX_COLOR_VALUE
#undef MODPAINT_MIN_COLOR_VALUE
#undef MODPAINT_MAX_SECTION_COLORS
#undef MODPAINT_MIN_SECTION_COLORS
#undef MODPAINT_MAX_OVERALL_COLORS
#undef MODPAINT_MIN_OVERALL_COLORS
#undef MODPAINT_MODE_RESKIN
#undef MODPAINT_MODE_REPAINT
/obj/item/mod/skin_applier
name = "MOD skin applier"
desc = "This one-use skin applier will add a skin to MODsuits of a specific type."
icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
icon_state = "skinapplier"
var/skin = "civilian"
var/compatible_theme = /datum/mod_theme
/obj/item/mod/skin_applier/Initialize(mapload)
. = ..()
name = "MOD [skin] skin applier"
/obj/item/mod/skin_applier/pre_attack(atom/attacked_atom, mob/living/user, params)
if(!istype(attacked_atom, /obj/item/mod/control))
return ..()
var/obj/item/mod/control/mod = attacked_atom
if(mod.active || mod.activating)
balloon_alert(user, "suit is active!")
return TRUE
if(!istype(mod.theme, compatible_theme))
balloon_alert(user, "incompatible theme!")
return TRUE
mod.set_mod_skin(skin)
balloon_alert(user, "skin applied")
qdel(src)
return TRUE
/obj/item/mod/skin_applier/honkerative
skin = "honkerative"
compatible_theme = /datum/mod_theme/syndicate

View File

@@ -0,0 +1,854 @@
/// Global proc that sets up all MOD themes as singletons in a list and returns it.
/proc/setup_mod_themes()
. = list()
for(var/path in typesof(/datum/mod_theme))
var/datum/mod_theme/new_theme = new path()
.[path] = new_theme
/// MODsuit theme, instanced once and then used by MODsuits to grab various statistics.
/datum/mod_theme
/// Theme name for the MOD.
var/name = "standard"
/// Description added to the MOD.
var/desc = "A civilian class suit by Nakamura Engineering, doesn't offer much other than slightly quicker movement."
/// Default skin of the MOD.
var/default_skin = "standard"
/// Armor shared across the MOD pieces.
var/armor = list(MELEE = 10, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 0, BIO = 100, FIRE = 25, ACID = 25, WOUND = 5, RAD = 0)
/// Resistance flags shared across the MOD pieces.
var/resistance_flags = NONE
/// Max heat protection shared across the MOD pieces.
var/max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
/// Max cold protection shared across the MOD pieces.
var/min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
/// Permeability shared across the MOD pieces.
var/permeability_coefficient = 0.01
/// Siemens shared across the MOD pieces.
var/siemens_coefficient = 0.5
/// How much modules can the MOD carry without malfunctioning.
var/complexity_max = DEFAULT_MAX_COMPLEXITY
/// How much battery power the MOD uses by just being on
var/cell_drain = DEFAULT_CHARGE_DRAIN
/// Slowdown of the MOD when not active.
var/slowdown_inactive = 1.25
/// Slowdown of the MOD when active.
var/slowdown_active = 0.75
/// Theme used by the MOD TGUI.
var/ui_theme = "ntos"
/// List of inbuilt modules. These are different from the pre-equipped suits, you should mainly use these for unremovable modules with 0 complexity.
var/list/inbuilt_modules = list()
/// Modules blacklisted from the MOD.
var/list/module_blacklist = list()
/// List of skins with their appropriate clothing flags.
var/list/skins = list(
"standard" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
"civilian" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/engineering
name = "engineering"
desc = "An engineer-fit suit with heat and shock resistance. Nakamura Engineering's classic."
default_skin = "engineering"
armor = list(MELEE = 10, BULLET = 5, LASER = 20, ENERGY = 10, BOMB = 10, BIO = 100, FIRE = 100, ACID = 25, WOUND = 10, RAD = 20)
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_inactive = 1.5
slowdown_active = 1
skins = list(
"engineering" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/atmospheric
name = "atmospheric"
desc = "An atmospheric-resistant suit by Nakamura Engineering, offering extreme heat resistance compared to the engineer suit."
default_skin = "atmospheric"
armor = list(MELEE = 10, BULLET = 5, LASER = 10, ENERGY = 15, BOMB = 10, BIO = 100, FIRE = 100, ACID = 75, WOUND = 10, RAD = 0)
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
slowdown_inactive = 1.5
slowdown_active = 1
skins = list(
"atmospheric" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDESNOUT,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR,
UNSEALED_COVER = HEADCOVERSMOUTH,
SEALED_COVER = HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/advanced
name = "advanced"
desc = "An advanced version of Nakamura Engineering's classic suit, shining with a white, acid and fire resistant polish."
default_skin = "advanced"
armor = list(MELEE = 15, BULLET = 5, LASER = 20, ENERGY = 15, BOMB = 50, BIO = 100, FIRE = 100, ACID = 90, WOUND = 10, RAD = 35)
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_inactive = 1
slowdown_active = 0.5
inbuilt_modules = list(/obj/item/mod/module/magboot/advanced)
skins = list(
"advanced" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/mining
name = "mining"
desc = "A high-power Nanotrasen mining suit, supporting more complexity at a bigger drain."
default_skin = "mining"
armor = list(MELEE = 15, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 30, BIO = 100, FIRE = 100, ACID = 75, WOUND = 15, RAD = 0)
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
cell_drain = DEFAULT_CHARGE_DRAIN * 2
complexity_max = DEFAULT_MAX_COMPLEXITY + 5
skins = list(
"mining" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/medical
name = "medical"
desc = "A lightweight suit by DeForest Medical Corporation, allows for easier movement."
default_skin = "medical"
armor = list(MELEE = 5, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 10, BIO = 100, FIRE = 60, ACID = 75, WOUND = 5, RAD = 0)
cell_drain = DEFAULT_CHARGE_DRAIN * 1.5
slowdown_inactive = 1
slowdown_active = 0.5
skins = list(
"medical" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
"corpsman" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/rescue
name = "rescue"
desc = "An advanced version of DeForest Medical Corporation's medical suit, designed for quick rescue of bodies from the most dangerous environments."
default_skin = "rescue"
armor = list(MELEE = 10, BULLET = 10, LASER = 5, ENERGY = 5, BOMB = 10, BIO = 100, FIRE = 100, ACID = 100, WOUND = 5, RAD = 0)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
cell_drain = DEFAULT_CHARGE_DRAIN * 1.5
slowdown_inactive = 0.75
slowdown_active = 0.25
inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced)
skins = list(
"rescue" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/research
name = "research"
desc = "A private military EOD suit by Aussec Armory, intended for explosive research. Bulky, but expansive."
default_skin = "research"
armor = list(MELEE = 20, BULLET = 15, LASER = 5, ENERGY = 5, BOMB = 100, BIO = 100, FIRE = 100, ACID = 100, WOUND = 15, RAD = 0)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
complexity_max = DEFAULT_MAX_COMPLEXITY + 5
slowdown_inactive = 2
slowdown_active = 1.5
inbuilt_modules = list(/obj/item/mod/module/reagent_scanner/advanced)
skins = list(
"research" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/security
name = "security"
desc = "An Apadyne Technologies security suit, offering shock protection and quicker speed, at the cost of carrying capacity."
default_skin = "security"
armor = list(MELEE = 15, BULLET = 15, LASER = 15, ENERGY = 15, BOMB = 25, BIO = 100, FIRE = 75, ACID = 75, WOUND = 15, RAD = 0)
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY - 5
slowdown_inactive = 1
slowdown_active = 0.5
skins = list(
"security" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE,
UNSEALED_COVER = HEADCOVERSMOUTH,
SEALED_COVER = HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/safeguard
name = "safeguard"
desc = "An Apadyne Technologies advanced security suit, offering greater speed and fire protection than the standard security model."
default_skin = "safeguard"
armor = list(MELEE = 15, BULLET = 15, LASER = 15, ENERGY = 15, BOMB = 40, BIO = 100, FIRE = 100, ACID = 95, WOUND = 15, RAD = 0)
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY - 5
slowdown_inactive = 0.75
slowdown_active = 0.25
skins = list(
"safeguard" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/magnate
name = "magnate"
desc = "A fancy, very protective suit for Nanotrasen's captains. Shock, fire and acid-proof while also having a large capacity and high speed."
default_skin = "magnate"
armor = list(MELEE = 20, BULLET = 15, LASER = 15, ENERGY = 15, BOMB = 50, BIO = 100, FIRE = 100, ACID = 100, WOUND = 15, RAD = 0)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY + 5
slowdown_inactive = 0.75
slowdown_active = 0.25
skins = list(
"magnate" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/cosmohonk
name = "cosmohonk"
desc = "A suit by Honk Ltd. Protects against low humor environments. Most of the tech went to lower the power cost."
default_skin = "cosmohonk"
armor = list(MELEE = 5, BULLET = 5, LASER = 20, ENERGY = 20, BOMB = 10, BIO = 100, FIRE = 60, ACID = 30, WOUND = 5, RAD = 0)
cell_drain = DEFAULT_CHARGE_DRAIN * 0.25
slowdown_inactive = 1.75
slowdown_active = 1.25
skins = list(
"cosmohonk" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR,
SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/syndicate
name = "syndicate"
desc = "A suit designed by Gorlex Marauders, offering armor ruled illegal in most of Spinward Stellar."
default_skin = "syndicate"
armor = list(MELEE = 15, BULLET = 20, LASER = 15, ENERGY = 15, BOMB = 35, BIO = 100, FIRE = 50, ACID = 90, WOUND = 25, RAD = 0)
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_inactive = 1
slowdown_active = 0.5
ui_theme = "syndicate"
inbuilt_modules = list()
skins = list(
"syndicate" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/elite
name = "elite"
desc = "An elite suit upgraded by Cybersun Industries, offering upgraded armor values."
default_skin = "elite"
armor = list(MELEE = 35, BULLET = 30, LASER = 35, ENERGY = 35, BOMB = 55, BIO = 100, FIRE = 100, ACID = 100, WOUND = 25, RAD = 0)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_inactive = 0.75
slowdown_active = 0.25
ui_theme = "syndicate"
inbuilt_modules = list()
skins = list(
"elite" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/enchanted
name = "enchanted"
desc = "The Wizard Federation's relatively low-tech MODsuit. Is very protective, though."
default_skin = "enchanted"
armor = list(MELEE = 40, BULLET = 40, LASER = 50, ENERGY = 50, BOMB = 35, BIO = 100, FIRE = 100, ACID = 100, WOUND = 30, RAD = 0)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY - 5
slowdown_inactive = 0.75
slowdown_active = 0.25
ui_theme = "wizard"
inbuilt_modules = list(/obj/item/mod/module/anti_magic/wizard)
skins = list(
"enchanted" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/prototype
name = "prototype"
desc = "A prototype modular suit powered by locomotives. While it is comfortable and has a big capacity, it remains very bulky and power-inefficient."
default_skin = "prototype"
armor = list(MELEE = 20, BULLET = 5, LASER = 10, ENERGY = 10, BOMB = 50, BIO = 100, FIRE = 100, ACID = 75, WOUND = 5, RAD = 0)
resistance_flags = FIRE_PROOF
complexity_max = DEFAULT_MAX_COMPLEXITY + 10
slowdown_inactive = 2.5
slowdown_active = 2
ui_theme = "hackerman"
inbuilt_modules = list(/obj/item/mod/module/kinesis)
skins = list(
"prototype" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/responsory
name = "responsory"
desc = "A high-speed rescue suit by Nanotrasen, intended for its' emergency response teams."
default_skin = "responsory"
armor = list(MELEE = 50, BULLET = 40, LASER = 50, ENERGY = 50, BOMB = 50, BIO = 100, FIRE = 100, ACID = 90, WOUND = 10, RAD = 0)
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_inactive = 0.5
slowdown_active = 0
skins = list(
"responsory" = list(
HELMET_LAYER = NECK_LAYER,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = NONE,
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
"inquisitory" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/apocryphal
name = "apocryphal"
desc = "A high-tech, only technically legal, armored suit created by a collaboration effort between Nanotrasen and Apadyne Technologies."
default_skin = "apocryphal"
armor = list(MELEE = 80, BULLET = 80, LASER = 50, ENERGY = 60, BOMB = 100, BIO = 100, FIRE = 100, ACID = 100, WOUND = 25, RAD = 0)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY + 10
skins = list(
"apocryphal" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEEARS|HIDEHAIR,
SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEYES|HIDEFACE|HIDESNOUT,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/corporate
name = "corporate"
desc = "A fancy, high-tech suit for Nanotrasen's high ranking officers."
default_skin = "corporate"
armor = list(MELEE = 50, BULLET = 40, LASER = 50, ENERGY = 50, BOMB = 50, BIO = 100, FIRE = 100, ACID = 100, WOUND = 15, RAD = 0)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_inactive = 0.5
slowdown_active = 0
skins = list(
"corporate" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
SEALED_INVISIBILITY = HIDEMASK|HIDEEYES|HIDEFACE,
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/debug
name = "debug"
desc = "Strangely nostalgic."
default_skin = "debug"
armor = list(MELEE = 50, BULLET = 50, LASER = 50, ENERGY = 50, BOMB = 100, BIO = 100, FIRE = 100, ACID = 100, WOUND = 0, RAD = 25)
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
complexity_max = 50
slowdown_inactive = 0.5
slowdown_active = 0
skins = list(
"debug" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE,
UNSEALED_COVER = HEADCOVERSMOUTH,
SEALED_COVER = HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL,
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
),
),
)
/datum/mod_theme/administrative
name = "administrative"
desc = "A suit made of adminium. Who comes up with these stupid mineral names?"
default_skin = "debug"
armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 100, BIO = 100, FIRE = 100, ACID = 100, WOUND = 100, RAD = 100)
resistance_flags = INDESTRUCTIBLE|LAVA_PROOF|FIRE_PROOF|UNACIDABLE|ACID_PROOF
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
complexity_max = 1000
cell_drain = DEFAULT_CHARGE_DRAIN * 0
slowdown_inactive = 0
slowdown_active = 0
skins = list(
"debug" = list(
HELMET_LAYER = null,
HELMET_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEEARS|HIDEHAIR|HIDESNOUT,
SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE,
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES,
),
CHESTPLATE_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
),
BOOTS_FLAGS = list(
UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
),
),
)

View File

@@ -0,0 +1,273 @@
/obj/item/mod/control/pre_equipped
cell = /obj/item/stock_parts/cell/high
var/applied_skin
/obj/item/mod/control/pre_equipped/Initialize(mapload, new_theme, new_skin)
new_skin = applied_skin
return ..()
/obj/item/mod/control/pre_equipped/standard
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
)
/obj/item/mod/control/pre_equipped/engineering
theme = /datum/mod_theme/engineering
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/magboot,
)
/obj/item/mod/control/pre_equipped/atmospheric
theme = /datum/mod_theme/atmospheric
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/t_ray,
)
/obj/item/mod/control/pre_equipped/advanced
theme = /datum/mod_theme/advanced
cell = /obj/item/stock_parts/cell/super
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/jetpack,
)
/obj/item/mod/control/pre_equipped/mining
theme = /datum/mod_theme/mining
cell = /obj/item/stock_parts/cell/high/plus
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/orebag,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/magboot,
/obj/item/mod/module/drill,
)
/obj/item/mod/control/pre_equipped/medical
theme = /datum/mod_theme/medical
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/health_analyzer,
/obj/item/mod/module/quick_carry,
)
/obj/item/mod/control/pre_equipped/rescue
theme = /datum/mod_theme/rescue
cell = /obj/item/stock_parts/cell/super
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/health_analyzer,
)
/obj/item/mod/control/pre_equipped/research
theme = /datum/mod_theme/research
cell = /obj/item/stock_parts/cell/super
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/t_ray,
)
/obj/item/mod/control/pre_equipped/security
theme = /datum/mod_theme/security
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/holster,
)
/obj/item/mod/control/pre_equipped/safeguard
theme = /datum/mod_theme/safeguard
cell = /obj/item/stock_parts/cell/super
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/jetpack,
/obj/item/mod/module/holster,
)
/obj/item/mod/control/pre_equipped/magnate
theme = /datum/mod_theme/magnate
cell = /obj/item/stock_parts/cell/hyper
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/holster,
/obj/item/mod/module/jetpack/advanced,
)
/obj/item/mod/control/pre_equipped/traitor
theme = /datum/mod_theme/syndicate
cell = /obj/item/stock_parts/cell/super
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/dna_lock,
/obj/item/mod/module/jetpack,
)
/obj/item/mod/control/pre_equipped/nuclear
theme = /datum/mod_theme/syndicate
cell = /obj/item/stock_parts/cell/hyper
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/visor/thermal,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/holster,
)
/obj/item/mod/control/pre_equipped/elite
theme = /datum/mod_theme/elite
cell = /obj/item/stock_parts/cell/bluespace
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/visor/thermal,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/holster,
)
/obj/item/mod/control/pre_equipped/prototype
theme = /datum/mod_theme/prototype
cell = /obj/item/stock_parts/cell/high/plus
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/tether,
)
/obj/item/mod/control/pre_equipped/responsory
theme = /datum/mod_theme/responsory
cell = /obj/item/stock_parts/cell/hyper
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/holster,
)
var/insignia_type = /obj/item/mod/module/insignia
/obj/item/mod/control/pre_equipped/responsory/Initialize(mapload, new_theme, new_skin)
initial_modules.Insert(1, insignia_type)
return ..()
/obj/item/mod/control/pre_equipped/responsory/commander
insignia_type = /obj/item/mod/module/insignia/commander
/obj/item/mod/control/pre_equipped/responsory/security
insignia_type = /obj/item/mod/module/insignia/security
/obj/item/mod/control/pre_equipped/responsory/engineer
insignia_type = /obj/item/mod/module/insignia/engineer
/obj/item/mod/control/pre_equipped/responsory/medic
insignia_type = /obj/item/mod/module/insignia/medic
/obj/item/mod/control/pre_equipped/responsory/janitor
insignia_type = /obj/item/mod/module/insignia/janitor
/obj/item/mod/control/pre_equipped/responsory/clown
insignia_type = /obj/item/mod/module/insignia/clown
/obj/item/mod/control/pre_equipped/responsory/chaplain
insignia_type = /obj/item/mod/module/insignia/chaplain
/obj/item/mod/control/pre_equipped/responsory/inquisitory
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/anti_magic,
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/holster,
)
applied_skin = "inquisitory"
/obj/item/mod/control/pre_equipped/responsory/inquisitory/commander
insignia_type = /obj/item/mod/module/insignia/commander
/obj/item/mod/control/pre_equipped/responsory/inquisitory/security
insignia_type = /obj/item/mod/module/insignia/security
/obj/item/mod/control/pre_equipped/responsory/inquisitory/medic
insignia_type = /obj/item/mod/module/insignia/medic
/obj/item/mod/control/pre_equipped/responsory/inquisitory/chaplain
insignia_type = /obj/item/mod/module/insignia/chaplain
/obj/item/mod/control/pre_equipped/apocryphal
theme = /datum/mod_theme/apocryphal
cell = /obj/item/stock_parts/cell/bluespace
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/holster,
/obj/item/mod/module/jetpack,
)
/obj/item/mod/control/pre_equipped/corporate
theme = /datum/mod_theme/corporate
cell = /obj/item/stock_parts/cell/bluespace
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/holster,
)
/obj/item/mod/control/pre_equipped/debug
theme = /datum/mod_theme/debug
cell = /obj/item/stock_parts/cell/bluespace
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/bikehorn,
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/tether,
) //one of every type of module, for testing if they all work correctly
/obj/item/mod/control/pre_equipped/administrative
theme = /datum/mod_theme/administrative
cell = /obj/item/stock_parts/cell/infinite/abductor
initial_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/welding,
/obj/item/mod/module/quick_carry/advanced,
/obj/item/mod/module/magboot/advanced,
/obj/item/mod/module/jetpack/advanced,
)
//these exist for the prefs menu
/obj/item/mod/control/pre_equipped/syndicate_empty
theme = /datum/mod_theme/syndicate
/obj/item/mod/control/pre_equipped/syndicate_empty/elite
theme = /datum/mod_theme/elite
INITIALIZE_IMMEDIATE(/obj/item/mod/control/pre_equipped/syndicate_empty)

View File

@@ -0,0 +1,78 @@
/obj/item/mod/control/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "MODsuit", name)
ui.open()
/obj/item/mod/control/ui_data(mob/user)
var/data = list()
data["interface_break"] = interface_break
data["malfunctioning"] = malfunctioning
data["open"] = open
data["active"] = active
data["complexity"] = complexity
data["selected_module"] = selected_module?.name
data["wearer_name"] = wearer ? (wearer.get_authentification_name("Unknown") || "Unknown") : "No Occupant"
data["wearer_job"] = wearer ? wearer.get_assignment("Unknown", "Unknown", FALSE) : "No Job"
data["AI"] = ai?.name
data["is_pAI"] = ai ? ispAI(ai) : FALSE
data["is_user_AI"] = ai ? user == ai : FALSE
data["cell"] = cell?.name
data["charge"] = cell ? round(cell.percent(), 1) : 0
data["modules"] = list()
for(var/obj/item/mod/module/module as anything in modules)
var/list/module_data = list(
name = module.name,
description = module.desc,
module_type = module.module_type,
active = module.active,
idle_power = module.idle_power_cost,
active_power = module.active_power_cost,
use_power = module.use_power_cost,
complexity = module.complexity,
cooldown_time = module.cooldown_time,
cooldown = round(COOLDOWN_TIMELEFT(module, cooldown_timer), 1 SECONDS),
id = module.tgui_id,
ref = REF(module),
configuration_data = module.get_configuration()
)
module_data += module.add_ui_data()
data["modules"] += list(module_data)
return data
/obj/item/mod/control/ui_static_data(mob/user)
var/data = list()
data["ui_theme"] = ui_theme
data["control"] = name
data["complexity_max"] = complexity_max
data["helmet"] = helmet?.name
data["chestplate"] = chestplate?.name
data["gauntlets"] = gauntlets?.name
data["boots"] = boots?.name
return data
/obj/item/mod/control/ui_act(action, params)
. = ..()
if(.)
return
if(malfunctioning && prob(75))
balloon_alert(usr, "button malfunctions!")
return
switch(action)
if("activate")
toggle_activate(usr)
if("select")
var/obj/item/mod/module/module = locate(params["ref"]) in modules
if(!module)
return
module.on_select()
if("configure")
var/obj/item/mod/module/module = locate(params["ref"]) in modules
if(!module)
return
module.configure_edit(params["key"], params["value"])
if("remove_pai")
if(ishuman(usr)) // Only the MODsuit's wearer should be removing the pAI.
var/mob/user = usr
extract_pai(user)
return TRUE

View File

@@ -0,0 +1,357 @@
/obj/item/mod/module
name = "MOD module"
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
icon_state = "module"
/// If it can be removed
var/removable = TRUE
/// If it's passive, togglable, usable or active
var/module_type = MODULE_PASSIVE
/// Is the module active
var/active = FALSE
/// How much space it takes up in the MOD
var/complexity = 0
/// Power use when idle
var/idle_power_cost = DEFAULT_CHARGE_DRAIN * 0
/// Power use when active
var/active_power_cost = DEFAULT_CHARGE_DRAIN * 0
/// Power use when used, we call it manually
var/use_power_cost = DEFAULT_CHARGE_DRAIN * 0
/// ID used by their TGUI
var/tgui_id
/// Linked MODsuit
var/obj/item/mod/control/mod
/// If we're an active module, what item are we?
var/obj/item/device
/// Overlay given to the user when the module is inactive
var/overlay_state_inactive
/// Overlay given to the user when the module is active
var/overlay_state_active
/// Overlay given to the user when the module is used, lasts until cooldown finishes
var/overlay_state_use
/// Icon file for the overlay.
var/overlay_icon_file = 'icons/mob/clothing/modsuit/mod_modules.dmi'
/// Does the overlay use the control unit's colors?
var/use_mod_colors = FALSE
/// What modules are we incompatible with?
var/list/incompatible_modules = list()
/// Cooldown after use
var/cooldown_time = 0
/// The mouse button needed to use this module
var/used_signal
/// 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)
/obj/item/mod/module/Initialize(mapload)
. = ..()
if(module_type != MODULE_ACTIVE)
return
if(ispath(device))
device = new device(src)
ADD_TRAIT(device, TRAIT_NODROP, MOD_TRAIT)
RegisterSignal(device, COMSIG_PARENT_PREQDELETED, .proc/on_device_deletion)
RegisterSignal(src, COMSIG_ATOM_EXITED, .proc/on_exit)
/obj/item/mod/module/Destroy()
mod?.uninstall(src)
if(device)
UnregisterSignal(device, COMSIG_PARENT_PREQDELETED)
QDEL_NULL(device)
return ..()
/obj/item/mod/module/examine(mob/user)
. = ..()
if(user.hud_list[DIAG_HUD] && user.client.images & user.hud_list[DIAG_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()
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()
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
/obj/item/mod/module/proc/on_select()
if(((!mod.active || mod.activating) && !allowed_inactive) || module_type == MODULE_PASSIVE)
if(mod.wearer)
balloon_alert(mod.wearer, "not active!")
return
if(module_type != MODULE_USABLE)
if(active)
on_deactivation()
else
on_activation()
else
on_use()
SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src)
/// Called when the module is activated
/obj/item/mod/module/proc/on_activation()
if(!COOLDOWN_FINISHED(src, cooldown_timer))
balloon_alert(mod.wearer, "on cooldown!")
return FALSE
if(!mod.active || mod.activating || !mod.cell?.charge)
balloon_alert(mod.wearer, "unpowered!")
return FALSE
if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
//specifically a to_chat because the user is phased out.
to_chat(mod.wearer, span_warning("You cannot activate this right now."))
return FALSE
if(module_type == MODULE_ACTIVE)
if(mod.selected_module && !mod.selected_module.on_deactivation())
return
mod.selected_module = src
if(device)
if(mod.wearer.put_in_hands(device))
balloon_alert(mod.wearer, "[device] extended")
RegisterSignal(mod.wearer, COMSIG_ATOM_EXITED, .proc/on_exit)
else
balloon_alert(mod.wearer, "can't extend [device]!")
return
else
update_signal()
balloon_alert(mod.wearer, "[src] activated, alt-click to use")
active = TRUE
mod.wearer.update_inv_back()
return TRUE
/// Called when the module is deactivated
/obj/item/mod/module/proc/on_deactivation()
active = FALSE
if(module_type == MODULE_ACTIVE)
mod.selected_module = null
if(device)
mod.wearer.transferItemToLoc(device, src, TRUE)
balloon_alert(mod.wearer, "[device] retracted")
UnregisterSignal(mod.wearer, COMSIG_ATOM_EXITED)
else
balloon_alert(mod.wearer, "[src] deactivated")
UnregisterSignal(mod.wearer, used_signal)
used_signal = null
mod.wearer.update_inv_back()
return TRUE
/// Called when the module is used
/obj/item/mod/module/proc/on_use()
if(!COOLDOWN_FINISHED(src, cooldown_timer))
return FALSE
if(!check_power(use_power_cost))
return FALSE
if(!allowed_in_phaseout && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
//specifically a to_chat because the user is phased out.
to_chat(mod.wearer, span_warning("You cannot activate this right now."))
return FALSE
COOLDOWN_START(src, cooldown_timer, cooldown_time)
addtimer(CALLBACK(mod.wearer, /mob.proc/update_inv_back), cooldown_time)
mod.wearer.update_inv_back()
return TRUE
/// Called when an activated module without a device is used
/obj/item/mod/module/proc/on_select_use(atom/target)
if(mod.wearer.incapacitated(ignore_grab = TRUE))
return FALSE
mod.wearer.face_atom(target)
if(!on_use())
return FALSE
return TRUE
/// Called when an activated module without a device is active and the user alt/middle-clicks
/obj/item/mod/module/proc/on_special_click(mob/source, atom/target)
SIGNAL_HANDLER
on_select_use(target)
return COMSIG_MOB_CANCEL_CLICKON
/// Called on the MODsuit's process
/obj/item/mod/module/proc/on_process(delta_time)
if(active)
if(!drain_power(active_power_cost * delta_time))
on_deactivation()
return FALSE
on_active_process(delta_time)
else
drain_power(idle_power_cost * delta_time)
return TRUE
/// Called on the MODsuit's process if it is an active module
/obj/item/mod/module/proc/on_active_process(delta_time)
return
/// Drains power from the suit cell
/obj/item/mod/module/proc/drain_power(amount)
if(!check_power(amount))
return FALSE
mod.cell.charge = max(0, mod.cell.charge - amount)
return TRUE
/obj/item/mod/module/proc/check_power(amount)
if(!mod.cell || (mod.cell.charge < amount))
return FALSE
return TRUE
/// Adds additional things to the MODsuit ui_data()
/obj/item/mod/module/proc/add_ui_data()
return list()
/// Creates a list of configuring options for this module
/obj/item/mod/module/proc/get_configuration()
return list()
/// Generates an element of the get_configuration list with a display name, type and value
/obj/item/mod/module/proc/add_ui_configuration(display_name, type, value, list/values)
return list("display_name" = display_name, "type" = type, "value" = value, "values" = values)
/// Receives configure edits from the TGUI and edits the vars
/obj/item/mod/module/proc/configure_edit(key, value)
return
/// Called when the device moves to a different place on active modules
/obj/item/mod/module/proc/on_exit(datum/source, atom/movable/part, direction)
SIGNAL_HANDLER
if(!active)
return
if(part.loc == src)
return
if(part.loc == mod.wearer)
return
if(part == device)
on_deactivation()
/// Called when the device gets deleted on active modules
/obj/item/mod/module/proc/on_device_deletion(datum/source)
SIGNAL_HANDLER
if(source == device)
device = null
qdel(src)
/// Generates an icon to be used for the suit's worn overlays
/obj/item/mod/module/proc/generate_worn_overlay()
. = list()
if(!mod.active)
return
var/used_overlay
if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer))
used_overlay = overlay_state_use
else if(overlay_state_active && active)
used_overlay = overlay_state_active
else if(overlay_state_inactive)
used_overlay = overlay_state_inactive
else
return
var/mutable_appearance/module_icon = mutable_appearance(overlay_icon_file, used_overlay)
if(!use_mod_colors)
module_icon.appearance_flags |= RESET_COLOR
. += module_icon
/// Updates the signal used by active modules to be activated
/obj/item/mod/module/proc/update_signal()
mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON
RegisterSignal(mod.wearer, mod.selected_module.used_signal, /obj/item/mod/module.proc/on_special_click)
/obj/item/mod/module/anomaly_locked
name = "MOD anomaly locked module"
desc = "A form of a module, locked behind an anomalous core to function."
incompatible_modules = list(/obj/item/mod/module/anomaly_locked)
/// The core item the module runs off.
var/obj/item/assembly/signaler/anomaly/core
/// Accepted types of anomaly cores.
var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly)
/// If this one starts with a core in.
var/prebuilt = FALSE
/obj/item/mod/module/anomaly_locked/Initialize(mapload)
. = ..()
if(!prebuilt || !length(accepted_anomalies))
return
var/core_path = pick(accepted_anomalies)
core = new core_path(src)
update_icon_state()
/obj/item/mod/module/anomaly_locked/Destroy()
QDEL_NULL(core)
return ..()
/obj/item/mod/module/anomaly_locked/examine(mob/user)
. = ..()
if(!length(accepted_anomalies))
return
if(core)
. += span_notice("There is a [core.name] installed in it. You could remove it with a <b>screwdriver</b>...")
else
var/list/core_list = list()
for(var/path in accepted_anomalies)
var/atom/core_path = path
core_list += initial(core_path.name)
. += span_notice("You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function.")
/obj/item/mod/module/anomaly_locked/on_select()
if(!core)
balloon_alert(mod.wearer, "no core!")
return
return ..()
/obj/item/mod/module/anomaly_locked/on_process(delta_time)
. = ..()
if(!core)
return FALSE
/obj/item/mod/module/anomaly_locked/on_active_process(delta_time)
if(!core)
return FALSE
return TRUE
/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params)
if(item.type in accepted_anomalies)
if(core)
balloon_alert(user, "core already in!")
return
if(!user.transferItemToLoc(item, src))
return
core = item
balloon_alert(user, "core installed")
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
update_icon_state()
else
return ..()
/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
if(!core)
balloon_alert(user, "no core!")
return
balloon_alert(user, "removing core...")
if(!do_after(user, 3 SECONDS, target = src))
balloon_alert(user, "interrupted!")
return
balloon_alert(user, "core removed")
core.forceMove(drop_location())
if(Adjacent(user) && !issilicon(user))
user.put_in_hands(core)
core = null
update_icon_state()
/obj/item/mod/module/anomaly_locked/update_icon_state()
icon_state = initial(icon_state) + (core ? "-core" : "")
return ..()

View File

@@ -0,0 +1,96 @@
/obj/item/mod/module/anti_magic/on_suit_activation()
ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT)
ADD_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT)
/obj/item/mod/module/anti_magic/on_suit_deactivation()
REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC, MOD_TRAIT)
REMOVE_TRAIT(mod.wearer, TRAIT_HOLY, MOD_TRAIT)
/obj/item/mod/module/anti_magic/wizard
name = "MOD magic neutralizer module"
desc = "The caster wielding this spell gains an invisible barrier around them, channeling arcane power through \
specialized runes engraved onto the surface of the suit to generate anti-magic field. \
The field will neutralize all magic that comes into contact with the user. \
It will not protect the caster from social ridicule."
icon_state = "magic_neutralizer"
/obj/item/mod/module/anti_magic/wizard/on_suit_activation()
ADD_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT)
/obj/item/mod/module/anti_magic/wizard/on_suit_deactivation()
REMOVE_TRAIT(mod.wearer, TRAIT_ANTIMAGIC_NO_SELFBLOCK, MOD_TRAIT)
/obj/item/mod/module/kinesis //TODO POST-MERGE MAKE NOT SUCK ASS, MAKE BALLER AS FUCK
name = "MOD kinesis module"
desc = "A modular plug-in to the forearm, this module was presumed lost for many years, \
despite the suits it used to be mounted on still seeing some circulation. \
This piece of technology allows the user to generate precise anti-gravity fields, \
letting them move objects as small as a titanium rod to as large as industrial machinery. \
Oddly enough, it doesn't seem to work on living creatures."
icon_state = "kinesis"
// module_type = MODULE_ACTIVE
module_type = MODULE_TOGGLE
// complexity = 3
complexity = 0
active_power_cost = DEFAULT_CHARGE_DRAIN*0.75
// use_power_cost = DEFAULT_CHARGE_DRAIN*3
removable = FALSE
incompatible_modules = list(/obj/item/mod/module/kinesis)
cooldown_time = 0.5 SECONDS
var/has_tk = FALSE
/obj/item/mod/module/kinesis/on_activation()
. = ..()
if(!.)
return
if(mod.wearer.dna.check_mutation(TK))
has_tk = TRUE
else
mod.wearer.dna.add_mutation(TK)
/obj/item/mod/module/kinesis/on_deactivation()
. = ..()
if(!.)
return
if(has_tk)
has_tk = FALSE
return
mod.wearer.dna.remove_mutation(TK)
/obj/item/mod/module/insignia
name = "MOD insignia module"
desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \
the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \
they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \
color patterns to and from the suit."
icon_state = "insignia"
removable = FALSE
incompatible_modules = list(/obj/item/mod/module/insignia)
overlay_state_inactive = "insignia"
/obj/item/mod/module/insignia/generate_worn_overlay()
overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
. = ..()
for(var/mutable_appearance/appearance as anything in .)
appearance.color = color
/obj/item/mod/module/insignia/commander
color = "#4980a5"
/obj/item/mod/module/insignia/security
color = "#b30d1e"
/obj/item/mod/module/insignia/engineer
color = "#e9c80e"
/obj/item/mod/module/insignia/medic
color = "#ebebf5"
/obj/item/mod/module/insignia/janitor
color = "#7925c7"
/obj/item/mod/module/insignia/clown
color = "#ff1fc7"
/obj/item/mod/module/insignia/chaplain
color = "#f0a00c"

View File

@@ -0,0 +1,215 @@
//Engineering modules for MODsuits
///Welding Protection - Makes the helmet protect from flashes and welding.
/obj/item/mod/module/welding
name = "MOD welding protection module"
desc = "A module installed into the visor of the suit, this projects a \
polarized, holographic overlay in front of the user's eyes. It's rated high enough for \
immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights."
icon_state = "welding"
complexity = 1
incompatible_modules = list(/obj/item/mod/module/welding)
overlay_state_inactive = "module_welding"
/obj/item/mod/module/welding/on_suit_activation()
mod.helmet.flash_protect = 2
/obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE)
if(deleting)
return
mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
///T-Ray Scan - Scans the terrain for undertile objects.
/obj/item/mod/module/t_ray
name = "MOD t-ray scan module"
desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \
to essentially echolocate things beneath the floor, mostly cables and pipes. \
A staple of atmospherics work, and counter-smuggling work."
icon_state = "tray"
module_type = MODULE_TOGGLE
complexity = 1
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 = 4
/obj/item/mod/module/t_ray/on_active_process(delta_time)
t_ray_scan(mod.wearer, 0.8 SECONDS, range)
///Magnetic Stability - Gives the user a slowdown but makes them negate gravity and be immune to slips.
/obj/item/mod/module/magboot
name = "MOD magnetic stability module"
desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \
excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \
However, these basic models do not feature computerized systems to automatically toggle them on and off, \
so numerous users report a certain stickiness to their steps."
icon_state = "magnet"
module_type = MODULE_TOGGLE
complexity = 2
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
incompatible_modules = list(/obj/item/mod/module/magboot)
cooldown_time = 0.5 SECONDS
/// Slowdown added onto the suit.
var/slowdown_active = 2
/obj/item/mod/module/magboot/on_activation()
. = ..()
if(!.)
return
mod.boots.clothing_flags |= NOSLIP
ADD_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT)
mod.slowdown += slowdown_active
mod.wearer.update_gravity(mod.wearer.has_gravity())
mod.wearer.update_equipment_speed_mods()
/obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
mod.boots.clothing_flags &= ~NOSLIP
REMOVE_TRAIT(mod.wearer, TRAIT_NOSLIPWATER, MOD_TRAIT)
mod.slowdown -= slowdown_active
mod.wearer.update_gravity(mod.wearer.has_gravity())
mod.wearer.update_equipment_speed_mods()
/obj/item/mod/module/magboot/advanced
name = "MOD advanced magnetic stability module"
removable = FALSE
complexity = 0
slowdown_active = 0
///Emergency Tether - Shoots a grappling hook projectile in 0g that throws the user towards it.
/obj/item/mod/module/tether
name = "MOD emergency tether module"
desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \
While some older models of cargo-oriented grapples have capacities of a few tons, \
these are only capable of working in zero-gravity environments, a blessing to some Engineers."
icon_state = "tether"
module_type = MODULE_ACTIVE
complexity = 3
use_power_cost = DEFAULT_CHARGE_DRAIN
incompatible_modules = list(/obj/item/mod/module/tether)
cooldown_time = 1.5 SECONDS
/obj/item/mod/module/tether/on_use()
if(mod.wearer.has_gravity(get_turf(src)))
balloon_alert(mod.wearer, "too much gravity!")
playsound(src, "gun_dry_fire", 25, TRUE)
return FALSE
return ..()
/obj/item/mod/module/tether/on_select_use(atom/target)
. = ..()
if(!.)
return
var/obj/item/projectile/tether = new /obj/item/projectile/tether(mod.wearer.loc)
tether.preparePixelProjectile(target, mod.wearer)
tether.firer = mod.wearer
playsound(src, 'sound/weapons/batonextend.ogg', 25, TRUE)
INVOKE_ASYNC(tether, /obj/item/projectile.proc/fire)
drain_power(use_power_cost)
/obj/item/projectile/tether
name = "tether"
icon_state = "tether_projectile"
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
damage = 0
nodamage = TRUE
range = 10
hitsound = 'sound/weapons/batonextend.ogg'
hitsound_wall = 'sound/weapons/batonextend.ogg'
suppressed = SUPPRESSED_VERY
hit_threshhold = LATTICE_LAYER
/// Reference to the beam following the projectile.
var/line
/obj/item/projectile/tether/fire(setAngle)
if(firer)
line = firer.Beam(src, "line", 'icons/obj/clothing/modsuit/mod_modules.dmi')
..()
/obj/item/projectile/tether/on_hit(atom/target)
. = ..()
if(firer)
firer.throw_at(target, 10, 1, firer, FALSE, FALSE, null, MOVE_FORCE_NORMAL, TRUE)
/obj/item/projectile/tether/Destroy()
QDEL_NULL(line)
return ..()
///Radiation Protection - Protects the user from radiation, gives them a geiger counter and rad info in the panel.
/obj/item/mod/module/rad_protection
name = "MOD radiation protection module"
desc = "A module utilizing polymers and reflective shielding to protect the user against ionizing radiation."
icon_state = "radshield"
complexity = 2
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
incompatible_modules = list(/obj/item/mod/module/rad_protection)
/obj/item/mod/module/rad_protection/on_suit_activation()
mod.armor = mod.armor.modifyRating(rad = 65)
mod.rad_flags = RAD_PROTECT_CONTENTS|RAD_NO_CONTAMINATE
for(var/obj/item/part in mod.mod_parts)
part.armor = mod.armor
part.rad_flags = mod.rad_flags
/obj/item/mod/module/rad_protection/on_suit_deactivation(deleting = FALSE)
mod.armor = mod.armor.modifyRating(rad = -65)
mod.rad_flags = NONE
for(var/obj/item/part in mod.mod_parts)
part.armor = mod.armor
part.rad_flags = mod.rad_flags
///Constructor - Lets you build quicker and create RCD holograms.
/obj/item/mod/module/constructor
name = "MOD constructor module"
desc = "This module entirely occupies the wearer's forearm, notably causing conflict with \
advanced arm servos meant to carry crewmembers. However, it contains the \
latest engineering schematics combined with inbuilt memory to help the user build walls."
icon_state = "constructor"
module_type = MODULE_USABLE
complexity = 2
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
use_power_cost = DEFAULT_CHARGE_DRAIN * 2
incompatible_modules = list(/obj/item/mod/module/constructor, /obj/item/mod/module/quick_carry)
cooldown_time = 11 SECONDS
/obj/item/mod/module/constructor/on_suit_activation()
ADD_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT)
/obj/item/mod/module/constructor/on_suit_deactivation(deleting = FALSE)
REMOVE_TRAIT(mod.wearer, TRAIT_QUICK_BUILD, MOD_TRAIT)
///Mister - Sprays water over an area.
/obj/item/mod/module/mister
name = "MOD water mister module"
desc = "A module containing a mister, able to spray it over areas."
icon_state = "mister"
module_type = MODULE_ACTIVE
complexity = 2
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
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)
create_reagents(volume, OPENCONTAINER)
return ..()
///Resin Mister - Sprays resin over an area.
/obj/item/mod/module/mister/atmos
name = "MOD resin mister module"
desc = "An atmospheric resin mister, able to fix up areas quickly."
device = /obj/item/extinguisher/mini/nozzle/mod
volume = 250
/obj/item/mod/module/mister/atmos/Initialize(mapload)
. = ..()
reagents.add_reagent(/datum/reagent/water, volume)
/obj/item/extinguisher/mini/nozzle/mod
name = "MOD atmospheric mister"
desc = "An atmospheric resin mister with three modes, mounted as a module."

View File

@@ -0,0 +1,395 @@
//General modules for MODsuits
///Storage - Adds a storage component to the suit.
/obj/item/mod/module/storage
name = "MOD storage containment module"
desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \
the surface of the suit, useful for storing various bits, and or bobs."
icon_state = "storage"
complexity = 3
incompatible_modules = list(/obj/item/mod/module/storage)
module_type = MODULE_USABLE
cooldown_time = 0.5 SECONDS
allowed_inactive = TRUE
/// Bag we have stored.
var/obj/item/storage/backpack/stored
/obj/item/mod/module/storage/attackby(obj/item/I, mob/user, params)
if(!istype(I, /obj/item/storage/backpack))
return ..()
var/obj/item/storage/backpack/B = I
if(stored)
balloon_alert(user, "backpack already installed!")
return
if(!user.transferItemToLoc(B, src))
return
stored = B
balloon_alert(user, "backpack installed")
playsound(src, 'sound/machines/click.ogg', 30, TRUE)
/obj/item/mod/module/storage/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
if(!stored)
balloon_alert(user, "no backpack!")
return
balloon_alert(user, "removing backpack...")
if(!do_after(user, 3 SECONDS, target = src))
balloon_alert(user, "interrupted!")
return
balloon_alert(user, "backpack removed")
stored.forceMove(drop_location())
if(Adjacent(user) && !issilicon(user))
user.put_in_hands(stored)
stored = null
/obj/item/mod/module/storage/on_use()
. = ..()
if(!.)
return
if(!stored)
var/obj/item/storage/backpack/holding = mod.wearer.get_active_held_item()
if(!holding)
balloon_alert(mod.wearer, "no backpack installed!")
return
if(!istype(holding))
balloon_alert(mod.wearer, "it doesn't fit!")
return
if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE))
stored = holding
balloon_alert(mod.wearer, "backpack stored")
playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE)
else if(mod.wearer.put_in_active_hand(stored, forced = FALSE, ignore_animation = TRUE))
balloon_alert(mod.wearer, "backpack retrieved")
playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE)
else
balloon_alert(mod.wearer, "backpack storage full!")
/obj/item/mod/module/storage/Exited(atom/movable/gone, direction)
. = ..()
if(gone == stored)
stored = null
/obj/item/mod/module/storage/Destroy()
QDEL_NULL(stored)
return ..()
///Ion Jetpack - Lets the user fly freely through space using battery charge.
/obj/item/mod/module/jetpack
name = "MOD ion jetpack module"
desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \
Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \
charge from the suit's charge. Some say this isn't Nakamura Engineering's first foray into jet-enabled suits."
icon_state = "jetpack"
module_type = MODULE_TOGGLE
complexity = 3
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
use_power_cost = DEFAULT_CHARGE_DRAIN
incompatible_modules = list(/obj/item/mod/module/jetpack)
cooldown_time = 0.5 SECONDS
overlay_state_inactive = "module_jetpack"
overlay_state_active = "module_jetpack_on"
/// Do we stop the wearer from gliding in space.
var/stabilizers = FALSE
/// Do we give the wearer a speed buff.
var/full_speed = FALSE
var/datum/effect_system/trail_follow/ion/ion_trail
/obj/item/mod/module/jetpack/Initialize(mapload)
. = ..()
ion_trail = new
ion_trail.set_up(src)
/obj/item/mod/module/jetpack/Destroy()
QDEL_NULL(ion_trail)
return ..()
/obj/item/mod/module/jetpack/on_activation()
. = ..()
if(!.)
return
ion_trail.start()
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/move_react)
if(full_speed)
mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
else
mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack)
/obj/item/mod/module/jetpack/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
stabilizers = FALSE
ion_trail.stop()
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack)
/obj/item/mod/module/jetpack/get_configuration()
. = ..()
.["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers)
/obj/item/mod/module/jetpack/configure_edit(key, value)
switch(key)
if("stabilizers")
stabilizers = text2num(value)
/obj/item/mod/module/jetpack/proc/move_react(mob/user)
allow_thrust()
/obj/item/mod/module/jetpack/proc/allow_thrust(use_fuel = TRUE)
if(!active)
return FALSE
if(!use_fuel)
return check_power(use_power_cost)
if(!drain_power(use_power_cost))
return FALSE
return TRUE
/obj/item/mod/module/jetpack/advanced
name = "MOD advanced ion jetpack module"
desc = "An improvement on the previous model of electric thrusters. This one achieves higher speeds through \
mounting of more jets and a red paint applied on it."
icon_state = "jetpack_advanced"
overlay_state_inactive = "module_jetpackadv"
overlay_state_active = "module_jetpackadv_on"
full_speed = TRUE
///Eating Apparatus - Lets the user eat/drink with the suit on.
/obj/item/mod/module/mouthhole
name = "MOD eating apparatus module"
desc = "A favorite by Miners, this modification to the helmet utilizes a nanotechnology barrier infront of the mouth \
to allow eating and drinking while retaining protection and atmosphere. However, it won't free you from masks, \
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)
overlay_state_inactive = "module_apparatus"
/// Former flags of the helmet.
var/former_flags = NONE
/// Former visor flags of the helmet.
var/former_visor_flags = NONE
/obj/item/mod/module/mouthhole/on_install()
former_flags = mod.helmet.flags_cover
former_visor_flags = mod.helmet.visor_flags_cover
mod.helmet.flags_cover &= ~HEADCOVERSMOUTH
mod.helmet.visor_flags_cover &= ~HEADCOVERSMOUTH
/obj/item/mod/module/mouthhole/on_uninstall(deleting = FALSE)
if(deleting)
return
mod.helmet.flags_cover |= former_flags
mod.helmet.visor_flags_cover |= former_visor_flags
///EMP Shield - Protects the suit from EMPs.
/obj/item/mod/module/emp_shield
name = "MOD EMP shield module"
desc = "A field inhibitor installed into the suit, protecting it against feedback such as \
electromagnetic pulses that would otherwise damage the electronic systems of the suit or it's modules. \
However, it will take from the suit's power to do so."
icon_state = "empshield"
complexity = 1
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
incompatible_modules = list(/obj/item/mod/module/emp_shield)
/obj/item/mod/module/emp_shield/on_install()
mod.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
/obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE)
mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_WIRES|EMP_PROTECT_CONTENTS)
/obj/item/mod/module/emp_shield/advanced
name = "MOD advanced EMP shield module"
desc = "An advanced field inhibitor installed into the suit, protecting it against feedback such as \
electromagnetic pulses that would otherwise damage the electronic systems of the suit or electronic devices on the wearer, \
including augmentations. However, it will take from the suit's power to do so."
complexity = 2
/obj/item/mod/module/emp_shield/advanced/on_suit_activation()
mod.wearer.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS)
/obj/item/mod/module/emp_shield/advanced/on_suit_deactivation(deleting)
mod.wearer.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS)
///Flashlight - Gives the suit a customizable flashlight.
/obj/item/mod/module/flashlight
name = "MOD flashlight module"
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"
module_type = MODULE_TOGGLE
complexity = 1
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
incompatible_modules = list(/obj/item/mod/module/flashlight)
cooldown_time = 0.5 SECONDS
overlay_state_inactive = "module_light"
light_color = COLOR_WHITE
light_range = 4
light_power = 1
/// Charge drain per range amount.
var/base_power = DEFAULT_CHARGE_DRAIN * 0.1
/// Minimum range we can set.
var/min_range = 2
/// Maximum range we can set.
var/max_range = 5
/obj/item/mod/module/flashlight/on_activation()
. = ..()
if(!.)
return
mod.set_light(light_range, light_power, light_color)
active_power_cost = base_power * light_range
/obj/item/mod/module/flashlight/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
mod.set_light(0, 0)
/obj/item/mod/module/flashlight/on_process(delta_time)
active_power_cost = base_power * light_range
return ..()
/obj/item/mod/module/flashlight/generate_worn_overlay()
. = ..()
if(!active)
return
var/mutable_appearance/light_icon = mutable_appearance(overlay_icon_file, "module_light_on")
light_icon.appearance_flags = RESET_COLOR
light_icon.color = light_color
. += light_icon
/obj/item/mod/module/flashlight/get_configuration()
. = ..()
.["light_color"] = add_ui_configuration("Light Color", "color", light_color)
.["light_range"] = add_ui_configuration("Light Range", "number", light_range)
/obj/item/mod/module/flashlight/configure_edit(key, value)
switch(key)
if("light_color")
value = input(usr, "Pick new light color", "Flashlight Color") as color|null
if(!value)
return
var/list/hsl = rgb2hsl(hex2num(copytext(value,2,4)),hex2num(copytext(value,4,6)),hex2num(copytext(value,6,8)))
if(hsl[3] < 0.5)
balloon_alert(mod.wearer, "too dark!")
return
mod.set_light_color(value)
mod.wearer.regenerate_icons()
light_color = value
if("light_range")
mod.set_light_range(clamp(value, min_range, max_range))
light_range = clamp(value, min_range, max_range)
///Dispenser - Dispenses an item after a time passes.
/obj/item/mod/module/dispenser
name = "MOD burger dispenser module"
desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \
This can draw incredible amounts of power from the suit's charge to create edible organic matter in the \
palm of the wearer's glove; however, research seemed to have entirely stopped at burgers. \
Notably, all attempts to get it to dispense Earl Grey tea have failed."
icon_state = "dispenser"
module_type = MODULE_USABLE
complexity = 3
use_power_cost = DEFAULT_CHARGE_DRAIN * 2
incompatible_modules = list(/obj/item/mod/module/dispenser)
cooldown_time = 5 SECONDS
/// Path we dispense.
var/dispense_type = /obj/item/reagent_containers/food/snacks/burger/plain
/// Time it takes for us to dispense.
var/dispense_time = 0 SECONDS
/obj/item/mod/module/dispenser/on_use()
. = ..()
if(!.)
return
if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod))
balloon_alert(mod.wearer, "interrupted!")
return FALSE
var/obj/item/dispensed = new dispense_type(mod.wearer.loc)
mod.wearer.put_in_hands(dispensed)
balloon_alert(mod.wearer, "[dispensed] dispensed")
playsound(src, 'sound/machines/click.ogg', 100, TRUE)
drain_power(use_power_cost)
return dispensed
///Longfall
///Thermal Regulator - Naw.
///DNA Lock - Prevents people without the set DNA from activating the suit.
/obj/item/mod/module/dna_lock
name = "MOD DNA lock module"
desc = "A module which engages with the various locks and seals tied to the suit's systems, \
enabling it to only be worn by someone corresponding with the user's exact DNA profile; \
however, this incredibly sensitive module is shorted out by EMPs. Luckily, cloning has been outlawed."
icon_state = "dnalock"
module_type = MODULE_USABLE
complexity = 2
use_power_cost = DEFAULT_CHARGE_DRAIN * 3
incompatible_modules = list(/obj/item/mod/module/dna_lock)
cooldown_time = 0.5 SECONDS
/// The DNA we lock with.
var/dna = null
/obj/item/mod/module/dna_lock/on_install()
RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/on_mod_activation)
RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, .proc/on_mod_removal)
RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, .proc/on_emp)
RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, .proc/on_emag)
/obj/item/mod/module/dna_lock/on_uninstall(deleting = FALSE)
UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL)
UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT)
UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT)
/obj/item/mod/module/dna_lock/on_use()
. = ..()
if(!.)
return
dna = mod.wearer.dna.unique_enzymes
balloon_alert(mod.wearer, "dna updated")
drain_power(use_power_cost)
/obj/item/mod/module/dna_lock/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
on_emp(src, severity)
/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card)
. = ..()
on_emag(src, user, emag_card)
/obj/item/mod/module/dna_lock/proc/dna_check(mob/user)
if(!iscarbon(user))
return FALSE
var/mob/living/carbon/carbon_user = user
if(!dna || (carbon_user.has_dna() && carbon_user.dna.unique_enzymes == dna))
return TRUE
balloon_alert(user, "dna locked!")
return FALSE
/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity)
SIGNAL_HANDLER
dna = null
/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card)
SIGNAL_HANDLER
dna = null
/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source, mob/user)
SIGNAL_HANDLER
if(!dna_check(user))
return MOD_CANCEL_ACTIVATE
/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source, mob/user)
SIGNAL_HANDLER
if(!dna_check(user))
return MOD_CANCEL_REMOVAL
///Sign Language Translator - I want, but no

View File

@@ -0,0 +1,157 @@
//Maint modules for MODsuits
///Springlock Mechanism - allows your modsuit to activate faster, but reagents are very dangerous.
/obj/item/mod/module/springlock
name = "MOD springlock module"
desc = "A module that spans the entire size of the MOD unit, sitting under the outer shell. \
This mechanical exoskeleton pushes out of the way when the user enters and it helps in booting \
up, but was taken out of modern suits because of the springlock's tendency to \"snap\" back \
into place when exposed to humidity. You know what it's like to have an entire exoskeleton enter you?"
icon_state = "springlock"
complexity = 3 // it is inside every part of your suit, so
incompatible_modules = list(/obj/item/mod/module/springlock)
/obj/item/mod/module/springlock/on_install()
mod.activation_step_time *= 0.5
/obj/item/mod/module/springlock/on_uninstall(deleting = FALSE)
mod.activation_step_time *= 2
/obj/item/mod/module/springlock/on_suit_activation()
RegisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS, .proc/on_wearer_exposed)
/obj/item/mod/module/springlock/on_suit_deactivation(deleting = FALSE)
UnregisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS)
///Signal fired when wearer is exposed to reagents
/obj/item/mod/module/springlock/proc/on_wearer_exposed(atom/source, list/reagents, datum/reagents/source_reagents, methods, volume_modifier, show_message, from_gas)
SIGNAL_HANDLER
if(!reagents.len)
return
if(!(methods == VAPOR || methods == PATCH || methods == TOUCH))
return //remove non-touch reagent exposure
to_chat(mod.wearer, span_danger("[src] makes an ominous click sound..."))
playsound(src, 'sound/items/modsuit/springlock.ogg', 75, TRUE)
addtimer(CALLBACK(src, .proc/snap_shut), rand(3 SECONDS, 5 SECONDS))
RegisterSignal(mod, COMSIG_MOD_ACTIVATE, .proc/on_activate_spring_block)
///Signal fired when wearer attempts to activate/deactivate suits
/obj/item/mod/module/springlock/proc/on_activate_spring_block(datum/source, user)
SIGNAL_HANDLER
balloon_alert(user, "springlocks aren't responding...?")
return MOD_CANCEL_ACTIVATE
///Delayed death proc of the suit after the wearer is exposed to reagents
/obj/item/mod/module/springlock/proc/snap_shut()
UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
if(!mod.wearer) //while there is a guaranteed user when on_wearer_exposed() fires, that isn't the same case for this proc
return
mod.wearer.visible_message("[src] inside [mod.wearer]'s [mod.name] snaps shut, mutilating the user inside!", span_userdanger("*SNAP*"))
mod.wearer.emote("scream")
playsound(mod.wearer, 'sound/effects/snap.ogg', 75, TRUE, frequency = 0.5)
playsound(mod.wearer, 'sound/effects/splat.ogg', 50, TRUE, frequency = 0.5)
mod.wearer.client?.give_award(/datum/award/achievement/misc/springlock, mod.wearer)
mod.wearer.apply_damage(500, BRUTE, forced = TRUE, spread_damage = TRUE, sharpness = SHARP_POINTY) //boggers, bogchamp, etc
if(!HAS_TRAIT(mod.wearer, TRAIT_NODEATH))
mod.wearer.death() //just in case, for some reason, they're still alive
flash_color(mod.wearer, flash_color = "#FF0000", flash_time = 10 SECONDS)
///Rave Visor - Pointless
///Tanner - Maybe another time
///Balloon Blower - Blows a balloon.
/obj/item/mod/module/balloon
name = "MOD balloon blower module"
desc = "A strange module invented years ago by some ingenious mimes. It blows balloons."
icon_state = "bloon"
module_type = MODULE_USABLE
complexity = 1
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
incompatible_modules = list(/obj/item/mod/module/balloon)
cooldown_time = 15 SECONDS
/obj/item/mod/module/balloon/on_use()
. = ..()
if(!.)
return
if(!do_after(mod.wearer, 10 SECONDS, target = mod))
return FALSE
mod.wearer.adjustOxyLoss(20)
playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE)
var/obj/item/toy/balloon/balloon = new(get_turf(src))
mod.wearer.put_in_hands(balloon)
drain_power(use_power_cost)
///Paper Dispenser - Dispenses (sometimes burning) paper sheets.
/obj/item/mod/module/paper_dispenser
name = "MOD paper dispenser module"
desc = "A simple module designed by the bureaucrats of Torch Bay. \
It dispenses 'warm, clean, and crisp sheets of paper' onto a nearby table. Usually."
icon_state = "paper_maker"
module_type = MODULE_USABLE
complexity = 1
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
incompatible_modules = list(/obj/item/mod/module/paper_dispenser)
cooldown_time = 5 SECONDS
/// The total number of sheets created by this MOD. The more sheets, them more likely they set on fire.
var/num_sheets_dispensed = 0
/obj/item/mod/module/paper_dispenser/on_use()
. = ..()
if(!.)
return
if(!do_after(mod.wearer, 1 SECONDS, target = mod))
return FALSE
var/obj/item/paper/crisp_paper = new(get_turf(src))
crisp_paper.desc = "It's crisp and warm to the touch. Must be fresh."
var/obj/structure/table/nearby_table = locate() in range(1, mod.wearer)
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
balloon_alert(mod.wearer, "dispensed paper[nearby_table ? " onto table":""]")
mod.wearer.put_in_hands(crisp_paper)
if(nearby_table)
mod.wearer.transferItemToLoc(crisp_paper, nearby_table.drop_location(), silent = FALSE)
// Up to a 30% chance to set the sheet on fire, +2% per sheet made
if(prob(min(num_sheets_dispensed * 2, 30)))
if(crisp_paper in mod.wearer.held_items)
mod.wearer.dropItemToGround(crisp_paper, force = TRUE)
crisp_paper.balloon_alert(mod.wearer, "PC LOAD LETTER!")
crisp_paper.visible_message(span_warning("[crisp_paper] bursts into flames, it's too crisp!"))
crisp_paper.fire_act(1000, 100)
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, \
able to switch between accept and deny modes."
icon_state = "stamp"
module_type = MODULE_ACTIVE
complexity = 1
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
device = /obj/item/stamp/mod
incompatible_modules = list(/obj/item/mod/module/stamp)
cooldown_time = 0.5 SECONDS
/obj/item/stamp/mod
name = "MOD electronic stamp"
desc = "A high-power stamp, able to switch between accept and deny mode when used."
/obj/item/stamp/mod/attack_self(mob/user, modifiers)
. = ..()
if(icon_state == "stamp-ok")
icon_state = "stamp-deny"
else
icon_state = "stamp-ok"
balloon_alert(user, "switched mode")
///Atrocinator - Perhaps another time

View File

@@ -0,0 +1,189 @@
//Medical modules for MODsuits
#define HEALTH_SCAN "Health"
#define WOUND_SCAN "Wound"
#define CHEM_SCAN "Chemical"
///Health Analyzer - Gives the user a ranged health analyzer and their health status in the panel.
/obj/item/mod/module/health_analyzer
name = "MOD health analyzer module"
desc = "A module installed into the glove of the suit. This is a high-tech biological scanning suite, \
allowing the user indepth information on the vitals and injuries of others even at a distance, \
all with the flick of the wrist. Data is displayed in a convenient package on HUD in the helmet, \
but it's up to you to do something with it."
icon_state = "health"
module_type = MODULE_ACTIVE
complexity = 2
use_power_cost = DEFAULT_CHARGE_DRAIN
incompatible_modules = list(/obj/item/mod/module/health_analyzer)
cooldown_time = 0.5 SECONDS
tgui_id = "health_analyzer"
/// Scanning mode, changes how we scan something.
var/mode = HEALTH_SCAN
/// List of all scanning modes.
var/static/list/modes = list(HEALTH_SCAN, WOUND_SCAN, CHEM_SCAN)
/obj/item/mod/module/health_analyzer/add_ui_data()
. = ..()
.["userhealth"] = mod.wearer?.health || 0
.["usermaxhealth"] = mod.wearer?.getMaxHealth() || 0
.["userbrute"] = mod.wearer?.getBruteLoss() || 0
.["userburn"] = mod.wearer?.getFireLoss() || 0
.["usertoxin"] = mod.wearer?.getToxLoss() || 0
.["useroxy"] = mod.wearer?.getOxyLoss() || 0
/obj/item/mod/module/health_analyzer/on_select_use(atom/target)
. = ..()
if(!.)
return
if(!isliving(target) || !mod.wearer.can_read(src))
return
switch(mode)
if(HEALTH_SCAN)
healthscan(mod.wearer, target)
if(WOUND_SCAN)
woundscan(mod.wearer, target)
if(CHEM_SCAN)
chemscan(mod.wearer, target)
drain_power(use_power_cost)
/obj/item/mod/module/health_analyzer/get_configuration()
. = ..()
.["mode"] = add_ui_configuration("Scan Mode", "list", mode, modes)
/obj/item/mod/module/health_analyzer/configure_edit(key, value)
switch(key)
if("mode")
mode = value
#undef HEALTH_SCAN
#undef WOUND_SCAN
#undef CHEM_SCAN
///Quick Carry - Lets the user carry bodies quicker.
/obj/item/mod/module/quick_carry
name = "MOD quick carry module"
desc = "A suite of advanced servos, redirecting power from the suit's arms to help carry the wounded; \
or simply for fun. However, Nanotrasen has locked the module's ability to assist in hand-to-hand combat."
icon_state = "carry"
complexity = 1
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
incompatible_modules = list(/obj/item/mod/module/quick_carry, /obj/item/mod/module/constructor)
/obj/item/mod/module/quick_carry/on_suit_activation()
ADD_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
/obj/item/mod/module/quick_carry/on_suit_deactivation(deleting = FALSE)
REMOVE_TRAIT(mod.wearer, TRAIT_QUICKER_CARRY, MOD_TRAIT)
/obj/item/mod/module/quick_carry/advanced
name = "MOD advanced quick carry module"
removable = FALSE
complexity = 0
///Injector - No piercing syringes, replace another time
///Organ Thrower
///Patrient Transport
///Defibrillator - Gives the suit an extendable pair of shock paddles.
/obj/item/mod/module/defibrillator
name = "MOD defibrillator module"
desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \
The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \
and a modded targeting computer determines the best position for the user to push. \
Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \
and counter-shock the heart, and the wearer returns to Medical a hero. Don't you even think about using it as a weapon; \
regulations on manufacture and software locks expressly forbid it."
icon_state = "defibrillator"
module_type = MODULE_ACTIVE
complexity = 2
use_power_cost = DEFAULT_CHARGE_DRAIN * 25
device = /obj/item/shockpaddles/mod
overlay_state_inactive = "module_defibrillator"
overlay_state_active = "module_defibrillator_active"
incompatible_modules = list(/obj/item/mod/module/defibrillator)
cooldown_time = 0.5 SECONDS
var/defib_cooldown = 5 SECONDS
/obj/item/mod/module/defibrillator/Initialize(mapload)
. = ..()
RegisterSignal(device, COMSIG_DEFIBRILLATOR_SUCCESS, .proc/on_defib_success)
/obj/item/mod/module/defibrillator/Destroy()
UnregisterSignal(device, COMSIG_DEFIBRILLATOR_SUCCESS)
. = ..()
/obj/item/mod/module/defibrillator/proc/on_defib_success(obj/item/shockpaddles/source)
drain_power(use_power_cost)
source.recharge(defib_cooldown)
return COMPONENT_DEFIB_STOP
/obj/item/shockpaddles/mod
name = "MOD defibrillator gauntlets"
req_defib = FALSE
icon_state = "defibgauntlets0"
item_state = "defibgauntlets0"
base_icon_state = "defibgauntlets"
/obj/item/mod/module/defibrillator/combat
name = "MOD combat defibrillator module"
desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \
The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \
and a modded targeting computer determines the best position for the user to push. \
Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \
and counter-shock the heart, and the wearer returns to Medical a hero. \
Interdyne Pharmaceutics marketed the domestic version of the Healing Hands as foolproof and unusable as a weapon. \
But when it came time to provide their operatives with usable medical equipment, they didn't hesitate to remove \
those in-built safeties. Operatives in the field can benefit from what they dub as 'Stun Gloves', able to apply shocks \
straight to a victims heart to disable them, or maybe even outright stop their heart with enough power."
complexity = 1
module_type = MODULE_ACTIVE
overlay_state_inactive = "module_defibrillator_combat"
overlay_state_active = "module_defibrillator_combat_active"
device = /obj/item/shockpaddles/syndicate/mod
defib_cooldown = 2.5 SECONDS
/obj/item/shockpaddles/syndicate/mod
name = "MOD combat defibrillator gauntlets"
req_defib = FALSE
icon_state = "syndiegauntlets0"
item_state = "syndiegauntlets0"
base_icon_state = "syndiegauntlets"
///Thread Ripper
///Surgical Processor - Lets you do advanced surgeries portably.
/obj/item/mod/module/surgical_processor
name = "MOD surgical processor module"
desc = "A module using an onboard surgical computer which can be connected to other computers to download and \
perform advanced surgeries on the go."
icon_state = "surgical_processor"
module_type = MODULE_ACTIVE
complexity = 2
active_power_cost = DEFAULT_CHARGE_DRAIN
device = /obj/item/surgical_processor/mod
incompatible_modules = list(/obj/item/mod/module/surgical_processor)
cooldown_time = 0.5 SECONDS
/obj/item/surgical_processor/mod
name = "MOD surgical processor"
/obj/item/mod/module/surgical_processor/preloaded
desc = "A module using an onboard surgical computer which can be connected to other computers to download and \
perform advanced surgeries on the go. This one came pre-loaded with some advanced surgeries."
device = /obj/item/surgical_processor/mod/preloaded
/obj/item/surgical_processor/mod/preloaded
advanced_surgeries = list(
/datum/surgery/advanced/pacify,
/datum/surgery/healing/combo/upgraded/femto,
/datum/surgery/advanced/brainwashing,
/datum/surgery/advanced/bioware/nerve_splicing,
/datum/surgery/advanced/bioware/nerve_grounding,
/datum/surgery/advanced/bioware/vein_threading,
/datum/surgery/advanced/bioware/muscled_veins,
/datum/surgery/advanced/bioware/ligament_hook,
/datum/surgery/advanced/bioware/ligament_reinforcement
)

View File

@@ -0,0 +1,135 @@
//Science modules for MODsuits
///Reagent Scanner - Lets the user scan reagents.
/obj/item/mod/module/reagent_scanner
name = "MOD reagent scanner module"
desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \
containers and projecting the information in an easy-to-read format on the wearer's display. \
It cannot detect flavors, so that's up to you."
icon_state = "scanner"
module_type = MODULE_TOGGLE
complexity = 1
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
incompatible_modules = list(/obj/item/mod/module/reagent_scanner)
cooldown_time = 0.5 SECONDS
/obj/item/mod/module/reagent_scanner/on_activation()
. = ..()
if(!.)
return
ADD_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT)
/obj/item/mod/module/reagent_scanner/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
REMOVE_TRAIT(mod.wearer, TRAIT_REAGENT_SCANNER, MOD_TRAIT)
/obj/item/mod/module/reagent_scanner/advanced
name = "MOD advanced reagent scanner module"
complexity = 0
removable = FALSE
var/explosion_detection_dist = 21
var/had_research_scanner = FALSE
/obj/item/mod/module/reagent_scanner/advanced/on_activation()
. = ..()
if(!.)
return
had_research_scanner = mod.wearer.research_scanner
mod.wearer.research_scanner = TRUE
RegisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION, .proc/sense_explosion)
/obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
mod.wearer.research_scanner = had_research_scanner
had_research_scanner = FALSE
UnregisterSignal(SSdcs, COMSIG_GLOB_EXPLOSION)
/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(datum/source, turf/epicenter,
devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
SIGNAL_HANDLER
var/turf/wearer_turf = get_turf(mod.wearer)
if(wearer_turf.z == epicenter.z)
return
if(get_dist(epicenter, wearer_turf) > explosion_detection_dist)
return
to_chat(mod.wearer, span_notice("Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]"))
///Anti-Gravity - Makes the user weightless.
/obj/item/mod/module/anomaly_locked/antigrav
name = "MOD anti-gravity module"
desc = "A module that uses a gravitational core to make the user completely weightless."
icon_state = "antigrav"
module_type = MODULE_TOGGLE
complexity = 3
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.7
incompatible_modules = list(/obj/item/mod/module/anomaly_locked)
cooldown_time = 0.5 SECONDS
accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav)
/obj/item/mod/module/anomaly_locked/antigrav/on_activation()
. = ..()
if(!.)
return
if(mod.wearer.has_gravity())
new /obj/effect/temp_visual/mook_dust(get_turf(src))
mod.wearer.AddElement(/datum/element/forced_gravity, 0)
mod.wearer.update_gravity(mod.wearer.has_gravity())
playsound(src, 'sound/effects/gravhit.ogg', 50)
/obj/item/mod/module/anomaly_locked/antigrav/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
mod.wearer.RemoveElement(/datum/element/forced_gravity, 0)
mod.wearer.update_gravity(mod.wearer.has_gravity())
if(deleting)
return
if(mod.wearer.has_gravity())
new /obj/effect/temp_visual/mook_dust(get_turf(src))
playsound(src, 'sound/effects/gravhit.ogg', 50)
/obj/item/mod/module/anomaly_locked/antigrav/prebuilt
prebuilt = TRUE
///Teleporter - Lets the user teleport to a nearby location.
/obj/item/mod/module/anomaly_locked/teleporter
name = "MOD teleporter module"
desc = "A module that uses a bluespace core to let the user transport their particles elsewhere."
icon_state = "teleporter"
module_type = MODULE_ACTIVE
complexity = 3
use_power_cost = DEFAULT_CHARGE_DRAIN * 5
cooldown_time = 5 SECONDS
accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace)
/// Time it takes to teleport
var/teleport_time = 3 SECONDS
/obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target)
. = ..()
if(!.)
return
var/turf/open/target_turf = get_turf(target)
if(!istype(target_turf) || is_blocked_turf(target_turf) || !(target_turf in view(mod.wearer)))
balloon_alert(mod.wearer, "invalid target!")
return
balloon_alert(mod.wearer, "teleporting...")
var/matrix/pre_matrix = matrix()
pre_matrix.Scale(4, 0.25)
var/matrix/post_matrix = matrix()
post_matrix.Scale(0.25, 4)
animate(mod.wearer, teleport_time, color = COLOR_CYAN, transform = pre_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT)
if(!do_after(mod.wearer, teleport_time, target = mod))
balloon_alert(mod.wearer, "interrupted!")
animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN)
return
animate(mod.wearer, teleport_time*0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN)
if(!do_teleport(mod.wearer, target_turf, asoundin = 'sound/effects/phasein.ogg'))
return
drain_power(use_power_cost)
/obj/item/mod/module/anomaly_locked/teleporter/prebuilt
prebuilt = TRUE

View File

@@ -0,0 +1,207 @@
//Security modules for MODsuits
///Cloaking - Lowers the user's visibility, can be interrupted by being touched or attacked.
/obj/item/mod/module/stealth
name = "MOD prototype cloaking module"
desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \
to bend light around the user, as well as mimetic materials to make the surface of the suit match the \
surroundings based off sensor data. For some reason, this tech is rarely seen."
icon_state = "cloak"
module_type = MODULE_TOGGLE
complexity = 4
active_power_cost = DEFAULT_CHARGE_DRAIN * 2
use_power_cost = DEFAULT_CHARGE_DRAIN * 10
incompatible_modules = list(/obj/item/mod/module/stealth)
cooldown_time = 5 SECONDS
/// Whether or not the cloak turns off on bumping.
var/bumpoff = TRUE
/// The alpha applied when the cloak is on.
var/stealth_alpha = 50
/obj/item/mod/module/stealth/on_activation()
. = ..()
if(!.)
return
if(bumpoff)
RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, .proc/unstealth)
RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_unarmed_attack)
RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, .proc/on_bullet_act)
RegisterSignal(mod.wearer, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW), .proc/unstealth)
animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS)
drain_power(use_power_cost)
/obj/item/mod/module/stealth/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
if(bumpoff)
UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP)
UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW))
animate(mod.wearer, alpha = 255, time = 1.5 SECONDS)
/obj/item/mod/module/stealth/proc/unstealth(datum/source)
SIGNAL_HANDLER
to_chat(mod.wearer, span_warning("[src] gets discharged from contact!"))
do_sparks(2, TRUE, src)
drain_power(use_power_cost)
on_deactivation(display_message = TRUE, deleting = FALSE)
/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target)
SIGNAL_HANDLER
if(!isliving(target))
return
unstealth(source)
/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/item/projectile/projectile)
SIGNAL_HANDLER
if(projectile.nodamage)
return
unstealth(source)
///Magnetic Harness - Automatically puts guns in your suit storage when you drop them.
/obj/item/mod/module/magnetic_harness
name = "MOD magnetic harness module"
desc = "Based off old TerraGov harness kits, this magnetic harness automatically attaches dropped guns back to the wearer."
icon_state = "mag_harness"
complexity = 2
use_power_cost = DEFAULT_CHARGE_DRAIN
incompatible_modules = list(/obj/item/mod/module/magnetic_harness)
/// Time before we activate the magnet.
var/magnet_delay = 0.8 SECONDS
/// The typecache of all guns we allow.
var/static/list/guns_typecache
/// The guns already allowed by the modsuit chestplate.
var/list/already_allowed_guns = list()
/obj/item/mod/module/magnetic_harness/Initialize(mapload)
. = ..()
if(!guns_typecache)
guns_typecache = typecacheof(list(/obj/item/gun/ballistic, /obj/item/gun/energy, /obj/item/gun/grenadelauncher, /obj/item/gun/chem, /obj/item/gun/syringe))
/obj/item/mod/module/magnetic_harness/on_install()
already_allowed_guns = guns_typecache & mod.chestplate.allowed
mod.chestplate.allowed |= guns_typecache
/obj/item/mod/module/magnetic_harness/on_uninstall(deleting = FALSE)
if(deleting)
return
mod.chestplate.allowed -= (guns_typecache - already_allowed_guns)
/obj/item/mod/module/magnetic_harness/on_suit_activation()
RegisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM, .proc/check_dropped_item)
/obj/item/mod/module/magnetic_harness/on_suit_deactivation(deleting = FALSE)
UnregisterSignal(mod.wearer, COMSIG_MOB_UNEQUIPPED_ITEM)
/obj/item/mod/module/magnetic_harness/proc/check_dropped_item(datum/source, obj/item/dropped_item, force, new_location)
SIGNAL_HANDLER
if(!is_type_in_typecache(dropped_item, guns_typecache))
return
if(new_location != get_turf(src))
return
addtimer(CALLBACK(src, .proc/pick_up_item, dropped_item), magnet_delay)
/obj/item/mod/module/magnetic_harness/proc/pick_up_item(obj/item/item)
if(!isturf(item.loc) || !item.Adjacent(mod.wearer))
return
if(!mod.wearer.equip_to_slot_if_possible(item, ITEM_SLOT_SUITSTORE, qdel_on_fail = FALSE, disable_warning = TRUE))
return
playsound(src, 'sound/items/modsuit/magnetic_harness.ogg', 50, TRUE)
balloon_alert(mod.wearer, "[item] reattached")
drain_power(use_power_cost)
///Pepper Shoulders
///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/revolverempty.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/revolverempty.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 ..()
///Megaphone - Lets you speak loud.
/obj/item/mod/module/megaphone
name = "MOD megaphone module"
desc = "A microchip megaphone linked to a MODsuit, for very important purposes, like: loudness."
icon_state = "megaphone"
module_type = MODULE_TOGGLE
complexity = 1
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
incompatible_modules = list(/obj/item/mod/module/megaphone)
cooldown_time = 0.5 SECONDS
/// List of spans we add to the speaker.
var/list/voicespan = list(SPAN_COMMAND)
/obj/item/mod/module/megaphone/on_activation()
. = ..()
if(!.)
return
RegisterSignal(mod.wearer, COMSIG_MOB_SAY, .proc/handle_speech)
/obj/item/mod/module/megaphone/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
UnregisterSignal(mod.wearer, COMSIG_MOB_SAY)
/obj/item/mod/module/megaphone/proc/handle_speech(datum/source, list/speech_args)
SIGNAL_HANDLER
speech_args[SPEECH_SPANS] |= voicespan
drain_power(use_power_cost)
///Criminal Capture
///Mirage grenade dispenser
///Projectile Dampener
///Active Sonar

View File

@@ -0,0 +1,66 @@
//Service modules for MODsuits
///Bike Horn - Plays a bike horn sound.
/obj/item/mod/module/bikehorn
name = "MOD bike horn module"
desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \
precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound."
icon_state = "bikehorn"
module_type = MODULE_USABLE
complexity = 1
use_power_cost = DEFAULT_CHARGE_DRAIN
incompatible_modules = list(/obj/item/mod/module/bikehorn)
cooldown_time = 1 SECONDS
/obj/item/mod/module/bikehorn/on_use()
. = ..()
if(!.)
return
playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE)
drain_power(use_power_cost)
///Microwave Beam - Microwaves items instantly.
/obj/item/mod/module/microwave_beam
name = "MOD microwave beam module"
desc = "An oddly domestic device, this module is installed into the user's palm, \
hooking up with culinary scanners located in the helmet to blast food with precise microwave radiation, \
allowing them to cook food from a distance, with the greatest of ease. Not recommended for use against grapes."
icon_state = "microwave_beam"
module_type = MODULE_ACTIVE
complexity = 2
use_power_cost = DEFAULT_CHARGE_DRAIN * 5
incompatible_modules = list(/obj/item/mod/module/microwave_beam)
cooldown_time = 10 SECONDS
var/obj/machinery/microwave/microwave
/obj/item/mod/module/microwave_beam/Initialize(mapload)
microwave = new()
. = ..()
/obj/item/mod/module/microwave_beam/Destroy()
QDEL_NULL(microwave)
. = ..()
/obj/item/mod/module/microwave_beam/on_select_use(atom/target)
. = ..()
if(!.)
return
if(!isitem(target))
return
if(!isturf(target.loc))
balloon_alert(mod.wearer, "must be on the floor!")
return
var/obj/item/microwave_target = target
var/turf/microwave_target_loc = target.loc
var/datum/effect_system/spark_spread/spark_effect = new()
spark_effect.set_up(2, 1, mod.wearer)
spark_effect.start()
mod.wearer.Beam(target,icon_state="lightning[rand(1,12)]", time = 5)
if(microwave_target.microwave_act(microwave))
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, FALSE)
else
balloon_alert(mod.wearer, "can't be microwaved!")
var/datum/effect_system/spark_spread/spark_effect_two = new()
spark_effect_two.set_up(2, 1, microwave_target_loc)
spark_effect_two.start()
drain_power(use_power_cost)

View File

@@ -0,0 +1,229 @@
//Supply modules for MODsuits
///Internal GPS - Extends a GPS you can use.
/obj/item/mod/module/gps
name = "MOD internal GPS module"
desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \
down to the exact coordinates. This information is fed to a central database viewable from the device itself, \
though using it to help people is up to you."
icon_state = "gps"
module_type = MODULE_USABLE
complexity = 1
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
incompatible_modules = list(/obj/item/mod/module/gps)
cooldown_time = 0.5 SECONDS
allowed_inactive = TRUE
/obj/item/mod/module/gps/Initialize(mapload)
. = ..()
AddComponent(/datum/component/gps/item, "MOD0", state = GLOB.deep_inventory_state, overlay_state = FALSE)
/obj/item/mod/module/gps/on_use()
. = ..()
if(!.)
return
attack_self(mod.wearer)
///Hydraulic Clamp - Lets you pick up and drop crates.
/obj/item/mod/module/clamp
name = "MOD hydraulic clamp module"
desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \
However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \
A lot of people would say that loading cargo is a dull job, but you could not disagree more."
icon_state = "clamp"
module_type = MODULE_ACTIVE
complexity = 3
use_power_cost = DEFAULT_CHARGE_DRAIN
incompatible_modules = list(/obj/item/mod/module/clamp)
cooldown_time = 0.5 SECONDS
overlay_state_inactive = "module_clamp"
overlay_state_active = "module_clamp_on"
/// Time it takes to load a crate.
var/load_time = 3 SECONDS
/// The max amount of crates you can carry.
var/max_crates = 3
/// The crates stored in the module.
var/list/stored_crates = list()
/obj/item/mod/module/clamp/on_select_use(atom/target)
. = ..()
if(!.)
return
if(!mod.wearer.Adjacent(target))
return
if(istype(target, /obj/structure/closet) || istype(target, /obj/structure/bigDelivery))
var/atom/movable/picked_crate = target
if(!check_crate_pickup(picked_crate))
return
playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
if(!do_after(mod.wearer, load_time, target = target))
balloon_alert(mod.wearer, "interrupted!")
return
if(!check_crate_pickup(picked_crate))
return
stored_crates += picked_crate
picked_crate.forceMove(src)
balloon_alert(mod.wearer, "picked up [picked_crate]")
drain_power(use_power_cost)
else if(length(stored_crates))
var/turf/target_turf = get_turf(target)
if(is_blocked_turf(target_turf))
return
playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
if(!do_after(mod.wearer, load_time, target = target))
balloon_alert(mod.wearer, "interrupted!")
return
if(is_blocked_turf(target_turf))
return
var/atom/movable/dropped_crate = pop(stored_crates)
dropped_crate.forceMove(target_turf)
balloon_alert(mod.wearer, "dropped [dropped_crate]")
drain_power(use_power_cost)
else
balloon_alert(mod.wearer, "invalid target!")
/obj/item/mod/module/clamp/on_suit_deactivation(deleting = FALSE)
if(deleting)
return
for(var/atom/movable/crate as anything in stored_crates)
crate.forceMove(drop_location())
stored_crates -= crate
/obj/item/mod/module/clamp/proc/check_crate_pickup(atom/movable/target)
if(length(stored_crates) >= max_crates)
balloon_alert(mod.wearer, "too many crates!")
return FALSE
for(var/mob/living/mob in target.GetAllContents())
if(mob.mob_size < MOB_SIZE_HUMAN)
continue
balloon_alert(mod.wearer, "crate too heavy!")
return FALSE
return TRUE
/obj/item/mod/module/clamp/loader
name = "MOD loader hydraulic clamp module"
icon_state = "clamp_loader"
complexity = 0
removable = FALSE
overlay_state_inactive = null
overlay_state_active = "module_clamp_loader"
load_time = 1 SECONDS
max_crates = 5
use_mod_colors = TRUE
///Drill - Lets you dig through rock and basalt.
/obj/item/mod/module/drill // TODO: Would be cooler with a built-in drill, but meh
name = "MOD pickaxe/drill storage module"
desc = "Provides a convenient storage compartment for pickaxes and drills."
icon_state = "drill"
complexity = 2
incompatible_modules = list(/obj/item/mod/module/drill)
cooldown_time = 0.5 SECONDS
allowed_inactive = TRUE
module_type = MODULE_USABLE
/// Pickaxe we have stored.
var/obj/item/pickaxe/stored
/obj/item/mod/module/drill/on_use()
. = ..()
if(!.)
return
if(!stored)
var/obj/item/pickaxe/holding = mod.wearer.get_active_held_item()
if(!holding)
balloon_alert(mod.wearer, "nothing to store!")
return
if(!istype(holding))
balloon_alert(mod.wearer, "it doesn't fit!")
return
if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE))
stored = holding
balloon_alert(mod.wearer, "mining instrument stored")
playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE)
else if(mod.wearer.put_in_active_hand(stored, forced = FALSE, ignore_animation = TRUE))
balloon_alert(mod.wearer, "mining instrument retrieved")
playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE)
else
balloon_alert(mod.wearer, "mining instrument storage full!")
/obj/item/mod/module/drill/on_uninstall(deleting = FALSE)
if(stored)
stored.forceMove(drop_location())
/obj/item/mod/module/drill/Exited(atom/movable/gone, direction)
. = ..()
if(gone == stored)
stored = null
/obj/item/mod/module/drill/Destroy()
QDEL_NULL(stored)
return ..()
/obj/item/mod/module/orebag // TODO
name = "MOD mining satchel storage module"
desc = "Provides a convenient storage department for a mining satchel."
icon_state = "ore"
module_type = MODULE_USABLE
complexity = 1
use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
incompatible_modules = list(/obj/item/mod/module/orebag)
cooldown_time = 0.5 SECONDS
allowed_inactive = TRUE
/// Pickaxe we have stored.
var/obj/item/storage/bag/ore/stored
/obj/item/mod/module/orebag/on_use()
. = ..()
if(!.)
return
if(!stored)
var/obj/item/storage/bag/ore/holding = mod.wearer.get_active_held_item()
if(!holding)
balloon_alert(mod.wearer, "nothing to store!")
return
if(!istype(holding))
balloon_alert(mod.wearer, "it doesn't fit!")
return
if(mod.wearer.transferItemToLoc(holding, src, force = FALSE, silent = TRUE))
stored = holding
balloon_alert(mod.wearer, "mining satchel stored")
playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE)
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/Pickup_ores)
else if(mod.wearer.put_in_active_hand(stored, forced = FALSE, ignore_animation = TRUE))
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
balloon_alert(mod.wearer, "mining satchel retrieved")
playsound(src, 'sound/weapons/revolverempty.ogg', 100, TRUE)
else
balloon_alert(mod.wearer, "mining satchel storage full!")
/obj/item/mod/module/orebag/on_uninstall(deleting = FALSE)
if(stored)
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
stored.forceMove(drop_location())
/obj/item/mod/module/orebag/on_equip()
if(stored)
RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, .proc/Pickup_ores)
/obj/item/mod/module/orebag/on_unequip()
if(stored)
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
/obj/item/mod/module/orebag/Exited(atom/movable/gone, direction)
. = ..()
if(gone == stored)
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
stored = null
/obj/item/mod/module/orebag/Destroy()
if(stored)
UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
QDEL_NULL(stored)
return ..()
/obj/item/mod/module/orebag/proc/Pickup_ores()
if(stored)
stored.Pickup_ores(mod.wearer)
// Ash accretion looks cool, but can't be arsed to implement
// Same with sphere transformation

View File

@@ -0,0 +1,91 @@
//Visor modules for MODsuits
///Base Visor - Adds a specific HUD and traits to you.
/obj/item/mod/module/visor
name = "MOD visor module"
desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you."
module_type = MODULE_TOGGLE
complexity = 2
active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
incompatible_modules = list(/obj/item/mod/module/visor)
cooldown_time = 0.5 SECONDS
/// The HUD type given by the visor.
var/hud_type
/// The traits given by the visor.
var/list/visor_traits = list()
/obj/item/mod/module/visor/on_activation()
. = ..()
if(!.)
return
if(hud_type)
var/datum/atom_hud/hud = GLOB.huds[hud_type]
hud.add_hud_to(mod.wearer)
for(var/trait in visor_traits)
ADD_TRAIT(mod.wearer, trait, MOD_TRAIT)
mod.wearer.update_sight()
/obj/item/mod/module/visor/on_deactivation(display_message = TRUE, deleting = FALSE)
. = ..()
if(!.)
return
if(hud_type)
var/datum/atom_hud/hud = GLOB.huds[hud_type]
hud.remove_hud_from(mod.wearer)
for(var/trait in visor_traits)
REMOVE_TRAIT(mod.wearer, trait, MOD_TRAIT)
mod.wearer.update_sight()
//Medical Visor - Gives you a medical HUD.
/obj/item/mod/module/visor/medhud
name = "MOD medical visor module"
desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \
biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \
access data such as patient files in a convenient readout. They say these also let you see behind you."
icon_state = "medhud_visor"
hud_type = DATA_HUD_MEDICAL_ADVANCED
//Diagnostic Visor - Gives you a diagnostic HUD.
/obj/item/mod/module/visor/diaghud
name = "MOD diagnostic visor module"
desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \
from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \
and integrity of such. They say these also let you see behind you."
icon_state = "diaghud_visor"
hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED
//Security Visor - Gives you a security HUD.
/obj/item/mod/module/visor/sechud
name = "MOD security visor module"
desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \
plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \
and generally know who to shoot. They say these also let you see behind you."
icon_state = "sechud_visor"
hud_type = DATA_HUD_SECURITY_ADVANCED
//Meson Visor - Gives you meson vision.
/obj/item/mod/module/visor/meson
name = "MOD meson visor module"
desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \
technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \
through walls, regardless of lighting conditions. They say these also let you see behind you."
icon_state = "meson_visor"
visor_traits = list(TRAIT_MESON_VISION)
//Thermal Visor - Gives you thermal vision.
/obj/item/mod/module/visor/thermal
name = "MOD thermal visor module"
desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \
the thermal radiation output of objects near the user. While it can detect the heat output of even something as \
small as a rodent, it still produces irritating red overlay. They say these also let you see behind you."
icon_state = "thermal_visor"
visor_traits = list(TRAIT_THERMAL_VISION)
//Night Visor - Gives you night vision.
/obj/item/mod/module/visor/night
name = "MOD night visor module"
desc = "A heads-up display installed into the visor of the suit. Typical for both civilian and military applications, \
this allows the user to perceive their surroundings while in complete darkness, enhancing the view by tenfold; \
yet brightening everything into a spooky green glow. They say these also let you see behind you."
icon_state = "night_visor"
visor_traits = list(TRAIT_TRUE_NIGHT_VISION)

View File

@@ -69,6 +69,8 @@
var/projectile_piercing = NONE
/// number of times we've pierced something. Incremented BEFORE bullet_act and on_hit proc!
var/pierces = 0
/// If objects are below this layer, we pass through them
var/hit_threshhold = PROJECTILE_HIT_THRESHHOLD_LAYER
/// "leftover" pixels for Range() calculation as pixel_move() was moved to simulated semi-pixel movement and Range() is in tiles.
var/pixels_range_leftover = 0
/// "leftover" tick pixels and stuff yeah, so we don't round off things and introducing tracing inaccuracy.
@@ -519,7 +521,7 @@
if(!isliving(target))
if(isturf(target)) // non dense turfs
return FALSE
if(target.layer < PROJECTILE_HIT_THRESHHOLD_LAYER)
if(target.layer < hit_threshhold)
return FALSE
else if(!direct_target) // non dense objects do not get hit unless specifically clicked
return FALSE

View File

@@ -890,6 +890,7 @@
R.reaction_turf(A, R.volume * volume_modifier, show_message, from_gas)
if("OBJ")
R.reaction_obj(A, R.volume * volume_modifier, show_message)
SEND_SIGNAL(A, COMSIG_ATOM_EXPOSE_REAGENTS, cached_reagents, src, method, volume_modifier, show_message, from_gas)
/datum/reagents/proc/holder_full()
if(total_volume >= maximum_volume)

View File

@@ -0,0 +1,372 @@
//MODsuit construction
/datum/design/mod_shell
name = "MOD Shell"
desc = "A 'Nakamura Engineering' designed shell for a Modular Suit."
id = "mod_shell"
build_type = MECHFAB
materials = list(/datum/material/iron = 10000, /datum/material/plasma = 5000)
construction_time = 25 SECONDS
build_path = /obj/item/mod/construction/shell
category = list("MODsuit Chassis")
/datum/design/mod_helmet
name = "MOD Helmet"
desc = "A 'Nakamura Engineering' designed helmet for a Modular Suit."
id = "mod_helmet"
build_type = MECHFAB
materials = list(/datum/material/iron = 5000)
construction_time = 10 SECONDS
build_path = /obj/item/mod/construction/helmet
category = list("MODsuit Chassis")
/datum/design/mod_chestplate
name = "MOD Chestplate"
desc = "A 'Nakamura Engineering' designed chestplate for a Modular Suit."
id = "mod_chestplate"
build_type = MECHFAB
materials = list(/datum/material/iron = 5000)
construction_time = 10 SECONDS
build_path = /obj/item/mod/construction/chestplate
category = list("MODsuit Chassis")
/datum/design/mod_gauntlets
name = "MOD Gauntlets"
desc = "'Nakamura Engineering' designed gauntlets for a Modular Suit."
id = "mod_gauntlets"
build_type = MECHFAB
materials = list(/datum/material/iron = 5000)
construction_time = 10 SECONDS
build_path = /obj/item/mod/construction/gauntlets
category = list("MODsuit Chassis")
/datum/design/mod_boots
name = "MOD Boots"
desc = "'Nakamura Engineering' designed boots for a Modular Suit."
id = "mod_boots"
build_type = MECHFAB
materials = list(/datum/material/iron = 5000)
construction_time = 10 SECONDS
build_path = /obj/item/mod/construction/boots
category = list("MODsuit Chassis")
/datum/design/mod_plating
name = "MOD External Plating"
desc = "External plating for a MODsuit."
id = "mod_plating_standard"
build_type = PROTOLATHE | MECHFAB
materials = list(/datum/material/iron = 6000, /datum/material/glass = 3000, /datum/material/plasma = 1000)
construction_time = 15 SECONDS
build_path = /obj/item/mod/construction/armor
category = list("MODsuit Chassis", "MODsuit Designs")
departmental_flags = DEPARTMENTAL_FLAG_ALL
research_icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
research_icon_state = "standard-plating"
/datum/design/mod_plating/New()
. = ..()
var/obj/item/mod/construction/armor/armor_type = build_path
var/datum/mod_theme/theme = GLOB.mod_themes[initial(armor_type.theme)]
desc = "External plating for a MODsuit. [theme.desc]"
/datum/design/mod_plating/engineering
name = "MOD Engineering Plating"
id = "mod_plating_engineering"
build_path = /obj/item/mod/construction/armor/engineering
materials = list(/datum/material/iron = 6000, /datum/material/gold = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000)
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
research_icon_state = "engineering-plating"
/datum/design/mod_plating/atmospheric
name = "MOD Atmospheric Plating"
id = "mod_plating_atmospheric"
build_path = /obj/item/mod/construction/armor/atmospheric
materials = list(/datum/material/iron = 6000, /datum/material/titanium = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000)
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
research_icon_state = "atmospheric-plating"
/datum/design/mod_plating/mining
name = "MOD Mining Plating"
id = "mod_plating_mining"
build_path = /obj/item/mod/construction/armor/mining
materials = list(/datum/material/iron = 6000, /datum/material/titanium = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000)
departmental_flags = DEPARTMENTAL_FLAG_CARGO
research_icon_state = "atmospheric-mining"
/datum/design/mod_plating/medical
name = "MOD Medical Plating"
id = "mod_plating_medical"
build_path = /obj/item/mod/construction/armor/medical
materials = list(/datum/material/iron = 6000, /datum/material/silver = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000)
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
research_icon_state = "medical-plating"
/datum/design/mod_plating/security
name = "MOD Security Plating"
id = "mod_plating_security"
build_path = /obj/item/mod/construction/armor/security
materials = list(/datum/material/iron = 6000, /datum/material/uranium = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000)
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
research_icon_state = "security-plating"
/datum/design/mod_plating/cosmohonk
name = "MOD Cosmohonk Plating"
id = "mod_plating_cosmohonk"
build_path = /obj/item/mod/construction/armor/cosmohonk
materials = list(/datum/material/iron = 6000, /datum/material/bananium = 2000, /datum/material/glass = 1000, /datum/material/plasma = 1000)
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
research_icon_state = "cosmohonk-plating"
/datum/design/mod_paint_kit
name = "MOD Paint Kit"
desc = "A paint kit for Modular Suits."
id = "mod_paint_kit"
build_type = MECHFAB
materials = list(/datum/material/iron = 1000, /datum/material/plastic = 500)
construction_time = 5 SECONDS
build_path = /obj/item/mod/paint
category = list("MODsuit Modules", "MODsuit Designs")
//MODsuit modules
/datum/design/module
name = "MOD Module"
build_type = PROTOLATHE | MECHFAB
construction_time = 1 SECONDS
materials = list(/datum/material/iron = 1000, /datum/material/glass = 1000)
build_path = /obj/item/mod/module
category = list("MODsuit Modules", "MODsuit Designs")
departmental_flags = DEPARTMENTAL_FLAG_ALL
/datum/design/module/New()
. = ..()
var/obj/item/mod/module/module = build_path
desc = "[initial(module.desc)] It uses [initial(module.complexity)] complexity."
/datum/design/module/mod_storage
name = "Storage Module"
id = "mod_storage"
materials = list(/datum/material/iron = 2500, /datum/material/glass = 500)
build_path = /obj/item/mod/module/storage
/datum/design/module/mod_visor_medhud
name = "Medical Visor Module"
id = "mod_visor_medhud"
materials = list(/datum/material/silver = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/visor/medhud
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/module/mod_visor_diaghud
name = "Diagnostic Visor Module"
id = "mod_visor_diaghud"
materials = list(/datum/material/gold = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/visor/diaghud
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/module/mod_visor_sechud
name = "Security Visor Module"
id = "mod_visor_sechud"
materials = list(/datum/material/titanium = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/visor/sechud
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/module/mod_visor_meson
name = "Meson Visor Module"
id = "mod_visor_meson"
materials = list(/datum/material/uranium = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/visor/meson
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_visor_welding
name = "Welding Protection Module"
id = "mod_welding"
materials = list(/datum/material/iron = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/welding
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_t_ray
name = "T-Ray Scanner Module"
id = "mod_t_ray"
materials = list(/datum/material/iron = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/t_ray
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_health_analyzer
name = "Health Analyzer Module"
id = "mod_health_analyzer"
materials = list(/datum/material/iron = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/health_analyzer
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/module/mod_stealth
name = "Cloak Module"
id = "mod_stealth"
materials = list(/datum/material/iron = 1000, /datum/material/bluespace = 500)
build_path = /obj/item/mod/module/stealth
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/module/mod_jetpack
name = "Ion Jetpack Module"
id = "mod_jetpack"
materials = list(/datum/material/iron = 1500, /datum/material/plasma = 1000)
build_path = /obj/item/mod/module/jetpack
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_magboot
name = "Magnetic Stabilizator Module"
id = "mod_magboot"
materials = list(/datum/material/iron = 1000, /datum/material/gold = 500)
build_path = /obj/item/mod/module/magboot
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_mag_harness
name = "Magnetic Harness Module"
id = "mod_mag_harness"
materials = list(/datum/material/iron = 1500, /datum/material/silver = 500)
build_path = /obj/item/mod/module/magnetic_harness
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/module/mod_tether
name = "Emergency Tether Module"
id = "mod_tether"
materials = list(/datum/material/iron = 1000, /datum/material/silver = 500)
build_path = /obj/item/mod/module/tether
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_mouthhole
name = "Eating Apparatus Module"
id = "mod_mouthhole"
materials = list(/datum/material/iron = 1500)
build_path = /obj/item/mod/module/mouthhole
/datum/design/module/mod_rad_protection
name = "Radiation Protection Module"
id = "mod_rad_protection"
materials = list(/datum/material/iron = 1000, /datum/material/uranium = 1000)
build_path = /obj/item/mod/module/rad_protection
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_emp_shield
name = "EMP Shield Module"
id = "mod_emp_shield"
materials = list(/datum/material/iron = 1000, /datum/material/plasma = 1000)
build_path = /obj/item/mod/module/emp_shield
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/module/mod_flashlight
name = "Flashlight Module"
id = "mod_flashlight"
materials = list(/datum/material/iron = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/flashlight
/datum/design/module/mod_reagent_scanner
name = "Reagent Scanner Module"
id = "mod_reagent_scanner"
materials = list(/datum/material/glass = 1000)
build_path = /obj/item/mod/module/reagent_scanner
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
/datum/design/module/mod_gps
name = "Internal GPS Module"
id = "mod_gps"
materials = list(/datum/material/iron = 500, /datum/material/glass = 500)
build_path = /obj/item/mod/module/gps
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO
/datum/design/module/mod_constructor
name = "Constructor Module"
id = "mod_constructor"
materials = list(/datum/material/iron = 1000, /datum/material/titanium = 500)
build_path = /obj/item/mod/module/constructor
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_quick_carry
name = "Quick Carry Module"
id = "mod_quick_carry"
materials = list(/datum/material/iron = 1000, /datum/material/titanium = 500)
build_path = /obj/item/mod/module/quick_carry
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/module/mod_bikehorn
name = "Bike Horn Module"
id = "mod_bikehorn"
materials = list(/datum/material/plastic = 500, /datum/material/iron = 500)
build_path = /obj/item/mod/module/bikehorn
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
/datum/design/module/mod_microwave_beam
name = "Microwave Beam Module"
id = "mod_microwave_beam"
materials = list(/datum/material/iron = 1000, /datum/material/uranium = 500)
build_path = /obj/item/mod/module/microwave_beam
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
/datum/design/module/mod_clamp
name = "Crate Clamp Module"
id = "mod_clamp"
materials = list(/datum/material/iron = 2000)
build_path = /obj/item/mod/module/clamp
departmental_flags = DEPARTMENTAL_FLAG_CARGO
/datum/design/module/mod_drill
name = "Drill Module"
id = "mod_drill"
materials = list(/datum/material/silver = 1000, /datum/material/iron = 2000)
build_path = /obj/item/mod/module/drill
departmental_flags = DEPARTMENTAL_FLAG_CARGO
/datum/design/module/mod_orebag
name = "Ore Bag Module"
id = "mod_orebag"
materials = list(/datum/material/iron = 1500)
build_path = /obj/item/mod/module/orebag
departmental_flags = DEPARTMENTAL_FLAG_CARGO
/datum/design/module/mod_dna_lock
name = "DNA Lock Module"
id = "mod_dna_lock"
materials = list(/datum/material/diamond = 500, /datum/material/glass = 1000)
build_path = /obj/item/mod/module/dna_lock
/datum/design/module/mister_atmos
name = "Resin Mister Module"
id = "mod_mister_atmos"
materials = list(/datum/material/glass = 1000, /datum/material/titanium = 1500)
build_path = /obj/item/mod/module/mister/atmos
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/module/mod_holster
name = "Holster Module"
id = "mod_holster"
materials = list(/datum/material/iron = 1500, /datum/material/glass = 500)
build_path = /obj/item/mod/module/holster
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/module/surgicalprocessor
name = "Surgical Processor Module"
id = "mod_surgicalprocessor"
materials = list(/datum/material/titanium = 250, /datum/material/glass = 1000, /datum/material/silver = 1500)
build_path = /obj/item/mod/module/surgical_processor
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/module/defibrillator
name = "Defibrillator Module"
id = "mod_defib"
materials = list(/datum/material/titanium = 250, /datum/material/diamond = 1000, /datum/material/silver = 1500)
build_path = /obj/item/mod/module/defibrillator
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
//MODsuit anomalock modules
/datum/design/module/mod_antigrav
name = "Anti-Gravity Module"
id = "mod_antigrav"
materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/uranium = 2000)
build_path = /obj/item/mod/module/anomaly_locked/antigrav
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/module/mod_teleporter
name = "Teleporter Module"
id = "mod_teleporter"
materials = list(/datum/material/iron = 2500, /datum/material/glass = 2000, /datum/material/bluespace = 2000)
build_path = /obj/item/mod/module/anomaly_locked/teleporter
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE

View File

@@ -15,7 +15,8 @@
"Weapons",
"Ammo",
"Firing Pins",
"Computer Parts"
"Computer Parts",
"MODsuit Designs"
)
production_animation = "protolathe_n"
allowed_buildtypes = PROTOLATHE

View File

@@ -26,7 +26,8 @@
"Subspace Telecomms",
"Research Machinery",
"Misc. Machinery",
"Computer Parts"
"Computer Parts",
"MODsuit Designs"
)
console_link = FALSE
production_animation = "protolathe_n"

View File

@@ -0,0 +1,125 @@
/datum/techweb_node/mod_basic
id = "mod"
starting_node = TRUE
display_name = "Basic Modular Suits"
description = "Specialized back mounted power suits with various different modules."
design_ids = list(
"mod_shell",
"mod_helmet",
"mod_chestplate",
"mod_gauntlets",
"mod_boots",
"mod_plating_standard",
"mod_paint_kit",
"mod_storage",
"mod_welding",
"mod_mouthhole",
"mod_flashlight",
)
/datum/techweb_node/mod_advanced
id = "mod_advanced"
display_name = "Advanced Modular Suits"
description = "More advanced modules, to improve modular suits."
prereq_ids = list("mod", "robotics")
design_ids = list(
"mod_plating_mining",
"mod_visor_diaghud",
"mod_gps",
"mod_reagent_scanner",
"mod_clamp",
"mod_drill",
"mod_orebag",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/mod_engineering
id = "mod_engineering"
display_name = "Engineering Modular Suits"
description = "Engineering suits, for powered engineers."
prereq_ids = list("mod_advanced", "engineering")
design_ids = list(
"mod_plating_engineering",
"mod_visor_meson",
"mod_t_ray",
"mod_magboot",
"mod_tether",
"mod_constructor",
"mod_mister_atmos",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/mod_advanced_engineering
id = "mod_advanced_engineering"
display_name = "Advanced Engineering Modular Suits"
description = "Advanced Engineering suits, for advanced powered engineers."
prereq_ids = list("mod_engineering", "adv_engi")
design_ids = list(
"mod_plating_atmospheric",
"mod_jetpack",
"mod_rad_protection",
"mod_emp_shield",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
/datum/techweb_node/mod_medical
id = "mod_medical"
display_name = "Medical Modular Suits"
description = "Medical suits for quick rescue purposes."
prereq_ids = list("mod_advanced", "biotech")
design_ids = list(
"mod_plating_medical",
"mod_visor_medhud",
"mod_health_analyzer",
"mod_quick_carry",
"mod_dna_lock",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/mod_advanced_medical
id = "mod_advanced_medical"
display_name = "Advanced Medical Modular Suits"
description = "Advanced medical suits for quicker rescue purposes."
prereq_ids = list("mod_medical", "adv_biotech")
design_ids = list(
"mod_defib",
"mod_surgicalprocessor",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500)
/datum/techweb_node/mod_security
id = "mod_security"
display_name = "Security Modular Suits"
description = "Security suits for space crime handling."
prereq_ids = list("mod_advanced", "sec_basic")
design_ids = list(
"mod_plating_security",
"mod_visor_sechud",
"mod_stealth",
"mod_mag_harness",
"mod_holster",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/mod_entertainment
id = "mod_entertainment"
display_name = "Entertainment Modular Suits"
description = "Powered suits for protection against low-humor environments."
prereq_ids = list("mod_advanced", "clown")
design_ids = list(
"mod_plating_cosmohonk",
"mod_bikehorn",
"mod_microwave_beam",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/mod_anomaly
id = "mod_anomaly"
display_name = "Anomalock Modular Suits"
description = "Modules for modular suits that require anomaly cores to function."
prereq_ids = list("mod_advanced", "anomaly_research")
design_ids = list(
"mod_antigrav",
"mod_teleporter",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)

View File

@@ -68,6 +68,15 @@
var/obj/item/surgical_processor/SP = locate() in R.module.modules
if(SP)
advanced_surgeries |= SP.advanced_surgeries
else
var/obj/item/surgical_processor/SP
for(var/obj/item/surgical_processor/processor in user.held_items)
SP = processor
break
if(!SP)
SP = locate(/obj/item/surgical_processor) in get_turf(user)
if(SP)
advanced_surgeries |= SP.advanced_surgeries
var/turf/T = get_turf(patient)
var/obj/structure/table/optable/table = locate(/obj/structure/table/optable, T)

View File

@@ -817,7 +817,7 @@
mecha_flags &= ~SILICON_PILOT
AI.forceMove(card)
card.AI = AI
AI.controlled_mech = null
AI.controlled_equipment = null
AI.remote_control = null
to_chat(AI, "<span class='notice'>You have been downloaded to a mobile storage device. Wireless connection offline.</span>")
to_chat(user, "<span class='boldnotice'>Transfer successful</span>: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.")
@@ -856,7 +856,7 @@
mecha_flags |= SILICON_PILOT
moved_inside(AI)
AI.cancel_camera()
AI.controlled_mech = src
AI.controlled_equipment = src
AI.remote_control = src
AI.mobility_flags = ALL //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow.
if(interaction == AI_MECH_HACK)
@@ -1083,7 +1083,7 @@
AI.linked_core = null
return
to_chat(AI, "<span class='notice'>Returning to core...</span>")
AI.controlled_mech = null
AI.controlled_equipment = null
AI.remote_control = null
mob_container = AI
newloc = get_turf(AI.linked_core)

View File

@@ -57,6 +57,8 @@
"Exosuit Equipment",
"Exosuit Ammunition",
"Cyborg Upgrade Modules",
"MODsuit Chassis",
"MODsuit Modules",
"Cybernetics",
"Implants",
"Control Interfaces",

View File

@@ -35,7 +35,7 @@
AI.death() //The damage is not enough to kill the AI, but to be 'corrupted files' in need of repair.
AI.forceMove(src) //Put the dead AI inside the wreckage for recovery
add_overlay(mutable_appearance('icons/obj/projectiles.dmi', "green_laser")) //Overlay for the recovery beacon
AI.controlled_mech = null
AI.controlled_equipment = null
AI.remote_control = null
/obj/structure/mecha_wreckage/Destroy()

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
sound/mecha/hydraulic.ogg Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -80,6 +80,7 @@
#include "code\__DEFINES\menu.dm"
#include "code\__DEFINES\misc.dm"
#include "code\__DEFINES\mobs.dm"
#include "code\__DEFINES\mod.dm"
#include "code\__DEFINES\monkeys.dm"
#include "code\__DEFINES\move_force.dm"
#include "code\__DEFINES\movement.dm"
@@ -159,6 +160,9 @@
#include "code\__DEFINES\dcs\signals.dm"
#include "code\__DEFINES\dcs\signals\signals_painting.dm"
#include "code\__DEFINES\dcs\signals\signals_screentips.dm"
#include "code\__DEFINES\dcs\signals\signals_medical.dm"
#include "code\__DEFINES\dcs\signals\signals_mod.dm"
#include "code\__DEFINES\dcs\signals\signals_reagent.dm"
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm"
@@ -819,6 +823,7 @@
#include "code\datums\wires\emitter.dm"
#include "code\datums\wires\explosive.dm"
#include "code\datums\wires\microwave.dm"
#include "code\datums\wires\mod.dm"
#include "code\datums\wires\mulebot.dm"
#include "code\datums\wires\particle_accelerator.dm"
#include "code\datums\wires\r_n_d.dm"
@@ -2968,6 +2973,27 @@
#include "code\modules\mob\living\simple_animal\slime\slime.dm"
#include "code\modules\mob\living\simple_animal\slime\slime_mobility.dm"
#include "code\modules\mob\living\simple_animal\slime\subtypes.dm"
#include "code\modules\mod\mod_actions.dm"
#include "code\modules\mod\mod_activation.dm"
#include "code\modules\mod\mod_ai.dm"
#include "code\modules\mod\mod_clothes.dm"
#include "code\modules\mod\mod_construction.dm"
#include "code\modules\mod\mod_control.dm"
#include "code\modules\mod\mod_paint.dm"
#include "code\modules\mod\mod_theme.dm"
#include "code\modules\mod\mod_types.dm"
#include "code\modules\mod\mod_ui.dm"
#include "code\modules\mod\modules\_module.dm"
#include "code\modules\mod\modules\modules.dm"
#include "code\modules\mod\modules\modules_engineering.dm"
#include "code\modules\mod\modules\modules_general.dm"
#include "code\modules\mod\modules\modules_maint.dm"
#include "code\modules\mod\modules\modules_medical.dm"
#include "code\modules\mod\modules\modules_science.dm"
#include "code\modules\mod\modules\modules_security.dm"
#include "code\modules\mod\modules\modules_service.dm"
#include "code\modules\mod\modules\modules_supply.dm"
#include "code\modules\mod\modules\modules_visor.dm"
#include "code\modules\modular_computers\laptop_vendor.dm"
#include "code\modules\modular_computers\computers\_modular_computer_shared.dm"
#include "code\modules\modular_computers\computers\item\computer.dm"
@@ -3381,6 +3407,7 @@
#include "code\modules\research\designs\medical_designs.dm"
#include "code\modules\research\designs\mining_designs.dm"
#include "code\modules\research\designs\misc_designs.dm"
#include "code\modules\research\designs\mod_designs.dm"
#include "code\modules\research\designs\nanite_designs.dm"
#include "code\modules\research\designs\power_designs.dm"
#include "code\modules\research\designs\smelting_designs.dm"
@@ -3450,6 +3477,7 @@
#include "code\modules\research\techweb\nodes\mecha_nodes.dm"
#include "code\modules\research\techweb\nodes\medical_nodes.dm"
#include "code\modules\research\techweb\nodes\misc_nodes.dm"
#include "code\modules\research\techweb\nodes\mod_nodes.dm"
#include "code\modules\research\techweb\nodes\nanites_nodes.dm"
#include "code\modules\research\techweb\nodes\robotics_nodes.dm"
#include "code\modules\research\techweb\nodes\syndicate_nodes.dm"

View File

@@ -0,0 +1,151 @@
import { useBackend } from '../backend';
import { Box, Stack, Section, ByondUi, Slider, Flex, Button } from '../components';
import { Window } from '../layouts';
import { capitalize } from 'common/string';
const colorToMatrix = (param) => {
switch (param) {
case 'red':
return [
1, 0, 0, 0, 0.25, 0.5, 0, 0, 0.25, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0,
];
case 'yellow':
return [
0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0.25, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0,
0,
];
case 'green':
return [
0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0.25, 0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0,
];
case 'teal':
return [
0.25, 0.25, 0.25, 0, 0, 0.5, 0.5, 0, 0, 0.5, 0.5, 0, 0, 0, 0, 1, 0, 0,
0, 0,
];
case 'blue':
return [
0.25, 0, 0.25, 0, 0, 0.5, 0.25, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
];
case 'purple':
return [
0.5, 0, 0.5, 0, 0.25, 0.5, 0.25, 0, 0.5, 0, 0.5, 0, 0, 0, 0, 1, 0, 0, 0,
0,
];
}
};
const displayText = (param) => {
switch (param) {
case 'r':
return 'Red';
case 'g':
return 'Green';
case 'b':
return 'Blue';
}
};
export const MODpaint = (props, context) => {
const { act, data } = useBackend(context);
const { mapRef, currentColor } = data;
const [
[rr, rg, rb, ra],
[gr, gg, gb, ga],
[br, bg, bb, ba],
[ar, ag, ab, aa],
[cr, cg, cb, ca],
] = currentColor;
const presets = ['red', 'yellow', 'green', 'teal', 'blue', 'purple'];
const prefixes = ['r', 'g', 'b'];
return (
<Window width={600} height={365}>
<Window.Content>
<Stack fill>
<Stack.Item fill width="30%">
{[0, 1, 2].map((row) => (
<Section
key={row}
title={`${displayText(prefixes[row])} turns to:`}>
{[0, 1, 2].map((col) => (
<Flex key={col}>
<Flex.Item align="left" width="30%">
<Box inline textColor="label">
{`${displayText(prefixes[col])}:`}
</Box>
</Flex.Item>
<Flex.Item align="right" width="70%">
<Slider
inline
textAlign="right"
value={currentColor[row * 4 + col] * 100}
minValue={0}
maxValue={125}
step={1}
stepPixelSize={0.75}
format={(value) => `${value}%`}
onDrag={(e, value) => {
let retColor = currentColor;
retColor[row * 4 + col] = value / 100;
act('transition_color', { color: retColor });
}}
/>
</Flex.Item>
</Flex>
))}
</Section>
))}
</Stack.Item>
<Stack.Item width="25%">
<Section height="70%" title="Presets">
<Box textAlign="center">
{presets.map((preset) => (
<Button
key={preset}
height="50px"
width="50px"
color={preset}
tooltipPosition="top"
tooltip={capitalize(preset)}
onClick={() =>
act('transition_color', { color: colorToMatrix(preset) })}
/>
))}
</Box>
</Section>
<Section textAlign="center" fontSize="28px">
<Button
height="50px"
width="50px"
icon="question"
color="average"
tooltipPosition="top"
tooltip="This is a color matrix. Think of it as editing the image in 3 layers, red, green, and blue, rather than editing the final image like with RGB."
/>
<Button
height="50px"
width="50px"
icon="check"
color="good"
tooltipPosition="top"
tooltip="Confirm changes!"
onClick={() => act('confirm')}
/>
</Section>
</Stack.Item>
<Stack.Item width="45%">
<Section fill title="Preview">
<ByondUi
height="230px"
params={{
id: mapRef,
type: 'map',
}}
/>
</Section>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};

View File

@@ -0,0 +1,754 @@
import { useBackend, useLocalState } from '../backend';
import { Button, ColorBox, LabeledList, ProgressBar, Section, Collapsible, Box, Icon, Stack, Table, Dimmer, NumberInput, Flex, AnimatedNumber, Dropdown } from '../components';
import { Window } from '../layouts';
const ConfigureNumberEntry = (props, context) => {
const { name, value, module_ref } = props;
const { act } = useBackend(context);
return (
<NumberInput
value={value}
minValue={-50}
maxValue={50}
stepPixelSize={5}
width="39px"
onChange={(e, value) =>
act('configure', {
'key': name,
'value': value,
'ref': module_ref,
})}
/>
);
};
const ConfigureBoolEntry = (props, context) => {
const { name, value, module_ref } = props;
const { act } = useBackend(context);
return (
<Button.Checkbox
checked={value}
onClick={() =>
act('configure', {
'key': name,
'value': !value,
'ref': module_ref,
})}
/>
);
};
const ConfigureColorEntry = (props, context) => {
const { name, value, module_ref } = props;
const { act } = useBackend(context);
return (
<>
<Button
icon="paint-brush"
onClick={() =>
act('configure', {
'key': name,
'ref': module_ref,
})}
/>
<ColorBox color={value} mr={0.5} />
</>
);
};
const ConfigureListEntry = (props, context) => {
const { name, value, values, module_ref } = props;
const { act } = useBackend(context);
return (
<Dropdown
displayText={value}
options={values}
onSelected={(value) =>
act('configure', {
'key': name,
'value': value,
'ref': module_ref,
})}
/>
);
};
const ConfigureDataEntry = (props, context) => {
const { name, display_name, type, value, values, module_ref } = props;
const configureEntryTypes = {
number: <ConfigureNumberEntry {...props} />,
bool: <ConfigureBoolEntry {...props} />,
color: <ConfigureColorEntry {...props} />,
list: <ConfigureListEntry {...props} />,
};
return (
<Box>
{display_name}: {configureEntryTypes[type]}
</Box>
);
};
const RadCounter = (props, context) => {
const { active, userradiated, usertoxins, usermaxtoxins, threatlevel }
= props;
return (
<Stack fill textAlign="center">
<Stack.Item grow>
<Section
title="Radiation Level"
color={active && userradiated ? 'bad' : 'good'}>
{active && userradiated ? 'IRRADIATED' : 'RADIATION-FREE'}
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Toxins Level">
<ProgressBar
value={active ? usertoxins / usermaxtoxins : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={usertoxins} />
</ProgressBar>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section
title="Hazard Level"
color={active && threatlevel ? 'bad' : 'good'}
bold>
{active && threatlevel ? threatlevel : 0}
</Section>
</Stack.Item>
</Stack>
);
};
const HealthAnalyzer = (props, context) => {
const {
active,
userhealth,
usermaxhealth,
userbrute,
userburn,
usertoxin,
useroxy,
} = props;
return (
<>
<Section title="Health">
<ProgressBar
value={active ? userhealth / usermaxhealth : 0}
ranges={{
good: [0.5, Infinity],
average: [0.2, 0.5],
bad: [-Infinity, 0.2],
}}>
<AnimatedNumber value={active ? userhealth : 0} />
</ProgressBar>
</Section>
<Stack textAlign="center">
<Stack.Item grow>
<Section title="Brute">
<ProgressBar
value={active ? userbrute / usermaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={active ? userbrute : 0} />
</ProgressBar>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Burn">
<ProgressBar
value={active ? userburn / usermaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={active ? userburn : 0} />
</ProgressBar>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Toxin">
<ProgressBar
value={active ? usertoxin / usermaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={active ? usertoxin : 0} />
</ProgressBar>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Suffocation">
<ProgressBar
value={active ? useroxy / usermaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={active ? useroxy : 0} />
</ProgressBar>
</Section>
</Stack.Item>
</Stack>
</>
);
};
const StatusReadout = (props, context) => {
const {
active,
statustime,
statusid,
statushealth,
statusmaxhealth,
statusbrute,
statusburn,
statustoxin,
statusoxy,
statustemp,
statusnutrition,
statusfingerprints,
statusdna,
statusviruses,
} = props;
return (
<>
<Stack textAlign="center">
<Stack.Item grow>
<Section title="Operation Time">
{active ? statustime : '00:00:00'}
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Operation Number">
{active ? statusid || '0' : '???'}
</Section>
</Stack.Item>
</Stack>
<Section title="Health">
<ProgressBar
value={active ? statushealth / statusmaxhealth : 0}
ranges={{
good: [0.5, Infinity],
average: [0.2, 0.5],
bad: [-Infinity, 0.2],
}}>
<AnimatedNumber value={active ? statushealth : 0} />
</ProgressBar>
</Section>
<Stack textAlign="center">
<Stack.Item grow>
<Section title="Brute">
<ProgressBar
value={active ? statusbrute / statusmaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={active ? statusbrute : 0} />
</ProgressBar>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Burn">
<ProgressBar
value={active ? statusburn / statusmaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={active ? statusburn : 0} />
</ProgressBar>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Toxin">
<ProgressBar
value={active ? statustoxin / statusmaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={statustoxin} />
</ProgressBar>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Suffocation">
<ProgressBar
value={active ? statusoxy / statusmaxhealth : 0}
ranges={{
good: [-Infinity, 0.2],
average: [0.2, 0.5],
bad: [0.5, Infinity],
}}>
<AnimatedNumber value={statusoxy} />
</ProgressBar>
</Section>
</Stack.Item>
</Stack>
<Stack textAlign="center">
<Stack.Item grow>
<Section title="Body Temperature">{active ? statustemp : 0}</Section>
</Stack.Item>
<Stack.Item grow>
<Section title="Nutrition Status">
{active ? statusnutrition : 0}
</Section>
</Stack.Item>
</Stack>
<Section title="DNA">
<LabeledList>
<LabeledList.Item label="Fingerprints">
{active ? statusfingerprints : '???'}
</LabeledList.Item>
<LabeledList.Item label="Unique Enzymes">
{active ? statusdna : '???'}
</LabeledList.Item>
</LabeledList>
</Section>
{!!active && !!statusviruses && (
<Section title="Diseases">
<Table>
<Table.Row header>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="signature"
tooltip="Name"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="wind"
tooltip="Type"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="bolt"
tooltip="Stage"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="flask"
tooltip="Cure"
tooltipPosition="top"
/>
</Table.Cell>
</Table.Row>
{statusviruses.map((virus) => {
return (
<Table.Row key={virus.name}>
<Table.Cell textAlign="center">{virus.name}</Table.Cell>
<Table.Cell textAlign="center">{virus.type}</Table.Cell>
<Table.Cell textAlign="center">
{virus.stage}/{virus.maxstage}
</Table.Cell>
<Table.Cell textAlign="center">{virus.cure}</Table.Cell>
</Table.Row>
);
})}
</Table>
</Section>
)}
</>
);
};
const ID2MODULE = {
rad_counter: RadCounter,
health_analyzer: HealthAnalyzer,
status_readout: StatusReadout,
};
const LockedInterface = () => (
<Section align="center" fill>
<Icon color="red" name="exclamation-triangle" size={15} />
<Box fontSize="30px" color="red">
ERROR: INTERFACE UNRESPONSIVE
</Box>
</Section>
);
const LockedModule = (props, context) => {
const { act, data } = useBackend(context);
return (
<Dimmer>
<Stack>
<Stack.Item fontSize="16px" color="blue">
SUIT UNPOWERED
</Stack.Item>
</Stack>
</Dimmer>
);
};
const ConfigureScreen = (props, context) => {
const { configuration_data, module_ref } = props;
const configuration_keys = Object.keys(configuration_data);
return (
<Dimmer backgroundColor="rgba(0, 0, 0, 0.8)">
<Stack vertical>
{configuration_keys.map((key) => {
const data = configuration_data[key];
return (
<Stack.Item key={data.key}>
<ConfigureDataEntry
name={key}
display_name={data.display_name}
type={data.type}
value={data.value}
values={data.values}
module_ref={module_ref}
/>
</Stack.Item>
);
})}
<Stack.Item>
<Box>
<Button
fluid
onClick={props.onExit}
icon="times"
textAlign="center">
Exit
</Button>
</Box>
</Stack.Item>
</Stack>
</Dimmer>
);
};
const displayText = (param) => {
switch (param) {
case 1:
return 'Use';
case 2:
return 'Toggle';
case 3:
return 'Select';
}
};
const ParametersSection = (props, context) => {
const { act, data } = useBackend(context);
const {
active,
malfunctioning,
open,
selected_module,
complexity,
complexity_max,
wearer_name,
wearer_job,
AI,
is_pAI,
is_user_AI,
} = data;
const status = malfunctioning
? 'Malfunctioning'
: active
? 'Active'
: 'Inactive';
return (
<Section title="Parameters">
<LabeledList>
<LabeledList.Item
label="Status"
buttons={
<Button
icon="power-off"
content={active ? 'Deactivate' : 'Activate'}
onClick={() => act('activate')}
/>
}>
{status}
</LabeledList.Item>
<LabeledList.Item label="Cover">
{open ? 'Open' : 'Closed'}
</LabeledList.Item>
<LabeledList.Item label="Selected Module">
{selected_module || 'None'}
</LabeledList.Item>
<LabeledList.Item label="Complexity">
{complexity} ({complexity_max})
</LabeledList.Item>
<LabeledList.Item label="Occupant">
{wearer_name}, {wearer_job}
</LabeledList.Item>
<LabeledList.Item
label="Onboard AI"
buttons={
AI && is_pAI && !is_user_AI ? (
<Button
icon="eject"
content="Eject pAI"
onClick={() => act('remove_pai')}
/>
) : (<> </>)
}>
{AI || 'None'}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const HardwareSection = (props, context) => {
const { act, data } = useBackend(context);
const {
active,
control,
helmet,
chestplate,
gauntlets,
boots,
cell,
charge,
} = data;
return (
<Section title="Hardware">
<Collapsible title="Parts">
<LabeledList>
<LabeledList.Item label="Control Unit">{control}</LabeledList.Item>
<LabeledList.Item label="Helmet">{helmet || 'None'}</LabeledList.Item>
<LabeledList.Item label="Chestplate">
{chestplate || 'None'}
</LabeledList.Item>
<LabeledList.Item label="Gauntlets">
{gauntlets || 'None'}
</LabeledList.Item>
<LabeledList.Item label="Boots">{boots || 'None'}</LabeledList.Item>
</LabeledList>
</Collapsible>
<Collapsible title="Cell">
{(cell && (
<LabeledList>
<LabeledList.Item label="Cell Type">{cell}</LabeledList.Item>
<LabeledList.Item label="Cell Charge">
<ProgressBar
value={charge / 100}
content={charge + '%'}
ranges={{
good: [0.6, Infinity],
average: [0.3, 0.6],
bad: [-Infinity, 0.3],
}}
/>
</LabeledList.Item>
</LabeledList>
)) || (
<Box color="bad" textAlign="center">
No Cell Detected
</Box>
)}
</Collapsible>
</Section>
);
};
const InfoSection = (props, context) => {
const { act, data } = useBackend(context);
const { active, modules } = data;
const info_modules = modules.filter((module) => !!module.id);
return (
<Section title="Info">
<Stack vertical>
{(info_modules.length !== 0
&& info_modules.map((module) => {
const Module = ID2MODULE[module.id];
return (
<Stack.Item key={module.ref}>
{!active && <LockedModule />}
<Module {...module} active={active} />
</Stack.Item>
);
})) || <Box textAlign="center">No Info Modules Detected</Box>}
</Stack>
</Section>
);
};
const ModuleSection = (props, context) => {
const { act, data } = useBackend(context);
const { complexity_max, modules } = data;
const [configureState, setConfigureState] = useLocalState(
context,
'module_configuration',
null
);
return (
<Section title="Modules" fill>
<Flex direction="column">
{(modules.length !== 0
&& modules.map((module) => {
return (
<Flex.Item key={module.ref}>
<Collapsible title={module.name}>
<Section>
{configureState === module.ref && (
<ConfigureScreen
configuration_data={module.configuration_data}
module_ref={module.ref}
onExit={() => setConfigureState(null)}
/>
)}
<Table>
<Table.Row header>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="save"
tooltip="Complexity"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="plug"
tooltip="Idle Power Cost"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="lightbulb"
tooltip="Active Power Cost"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="bolt"
tooltip="Use Power Cost"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="hourglass-half"
tooltip="Cooldown"
tooltipPosition="top"
/>
</Table.Cell>
<Table.Cell textAlign="center">
<Button
color="transparent"
icon="tasks"
tooltip="Actions"
tooltipPosition="top"
/>
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell textAlign="center">
{module.complexity}/{complexity_max}
</Table.Cell>
<Table.Cell textAlign="center">
{module.idle_power}
</Table.Cell>
<Table.Cell textAlign="center">
{module.active_power}
</Table.Cell>
<Table.Cell textAlign="center">
{module.use_power}
</Table.Cell>
<Table.Cell textAlign="center">
{(module.cooldown > 0 && module.cooldown / 10) || '0'}
/{module.cooldown_time / 10}s
</Table.Cell>
<Table.Cell textAlign="center">
<Button
onClick={() => act('select', { 'ref': module.ref })}
icon="bullseye"
selected={module.active}
tooltip={displayText(module.module_type)}
tooltipPosition="left"
disabled={!module.module_type}
/>
<Button
onClick={() => setConfigureState(module.ref)}
icon="cog"
selected={configureState === module.ref}
tooltip="Configure"
tooltipPosition="left"
disabled={module.configuration_data.length === 0}
/>
</Table.Cell>
</Table.Row>
</Table>
<Box>{module.description}</Box>
</Section>
</Collapsible>
</Flex.Item>
);
})) || (
<Flex.Item>
<Box textAlign="center">No Modules Detected</Box>
</Flex.Item>
)}
</Flex>
</Section>
);
};
export const MODsuit = (props, context) => {
const { act, data } = useBackend(context);
const { ui_theme, interface_break } = data;
return (
<Window
width={400}
height={525}
theme={ui_theme}
title="MOD Interface Panel"
resizable>
<Window.Content scrollable={!interface_break}>
{(!!interface_break && <LockedInterface />) || (
<Stack vertical fill>
<Stack.Item>
<ParametersSection />
</Stack.Item>
<Stack.Item>
<HardwareSection />
</Stack.Item>
<Stack.Item>
<InfoSection />
</Stack.Item>
<Stack.Item grow>
<ModuleSection />
</Stack.Item>
</Stack>
)}
</Window.Content>
</Window>
);
};

View File

@@ -14,6 +14,7 @@ export const SuitStorageUnit = (props, context) => {
suit,
helmet,
mask,
mod,
storage,
} = data;
return (
@@ -89,6 +90,15 @@ export const SuitStorageUnit = (props, context) => {
item: 'mask',
})} />
</LabeledList.Item>
<LabeledList.Item label="MOD">
<Button
icon={mod ? 'square' : 'square-o'}
content={mod || 'Empty'}
disabled={!mod}
onClick={() => act('dispense', {
item: 'mod',
})} />
</LabeledList.Item>
<LabeledList.Item label="Storage">
<Button
icon={storage ? 'square' : 'square-o'}