diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm new file mode 100644 index 0000000000..a83f02135c --- /dev/null +++ b/code/__DEFINES/ai.dm @@ -0,0 +1,6 @@ +///Mob the MOD is trying to attach to +#define BB_MOD_TARGET "BB_mod_target" +///The implant the AI was created from +#define BB_MOD_IMPLANT "BB_mod_implant" +///Range for a MOD AI controller. +#define MOD_AI_RANGE 100 diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm new file mode 100644 index 0000000000..be1e9b31c8 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -0,0 +1,3 @@ +/// from base of atom/movable/Process_Spacemove(): (movement_dir) +#define COMSIG_MOVABLE_SPACEMOVE "spacemove" + #define COMSIG_MOVABLE_STOP_SPACEMOVE (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm new file mode 100644 index 0000000000..be1e9b31c8 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -0,0 +1,3 @@ +/// from base of atom/movable/Process_Spacemove(): (movement_dir) +#define COMSIG_MOVABLE_SPACEMOVE "spacemove" + #define COMSIG_MOVABLE_STOP_SPACEMOVE (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index 5d2a471c0a..0fd09d9063 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -1,2 +1,6 @@ ///From base of mob/living/MobBump() (mob/living) #define COMSIG_LIVING_MOB_BUMP "living_mob_bump" + +///From base of mob/living/ZImpactDamage() (mob/living, levels, turf/t) +#define COMSIG_LIVING_Z_IMPACT "living_z_impact" + #define NO_Z_IMPACT_DAMAGE (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm new file mode 100644 index 0000000000..4c8ec4d6de --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mod.dm @@ -0,0 +1,7 @@ +//MODsuit signals +/// Called when a module is selected to be the active one from on_select() +#define COMSIG_MOD_MODULE_SELECTED "mod_module_selected" +/// 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) diff --git a/code/__DEFINES/mod.dm b/code/__DEFINES/mod.dm new file mode 100644 index 0000000000..03163497d4 --- /dev/null +++ b/code/__DEFINES/mod.dm @@ -0,0 +1,31 @@ +/// Default value for the max_complexity var on MODsuits +#define DEFAULT_MAX_COMPLEXITY 15 + +/// Default cell drain per process on MODsuits +#define DEFAULT_CELL_DRAIN 5 + +/// 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 HELMET_LAYER "helmet_layer" +#define HELMET_FLAGS "helmet_flags" +#define CHESTPLATE_FLAGS "chestplate_flags" +#define GAUNTLETS_FLAGS "gauntlets_flags" +#define BOOTS_FLAGS "boots_flags" + +#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" + +/// Global list of all /datum/mod_theme +GLOBAL_LIST_INIT(mod_themes, setup_mod_themes()) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 222dc5cd39..af5dccb3fd 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -158,6 +158,22 @@ #define TRAIT_CALCIUM_HEALER "calcium_healer" #define TRAIT_MAGIC_CHOKE "magic_choke" #define TRAIT_CAPTAIN_METABOLISM "captain-metabolism" +/// Prevents plasmamen from self-igniting +#define TRAIT_NOSELFIGNITION "no_selfignition" +/// 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" +/// Negates our gravity, letting us move normally on floors in 0-g +#define TRAIT_NEGATES_GRAVITY "negates_gravity" +/// 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" @@ -377,3 +393,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" diff --git a/code/__DEFINES/wiremod.dm b/code/__DEFINES/wiremod.dm new file mode 100644 index 0000000000..c242bcc7d6 --- /dev/null +++ b/code/__DEFINES/wiremod.dm @@ -0,0 +1,4 @@ +#define SHELL_FLAG_CIRCUIT_UNREMOVABLE (1<<0) + +/// Whether a circuit is not able to be modified +#define SHELL_FLAG_CIRCUIT_UNMODIFIABLE (1<<5) diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index b41b7356a8..9aa0f8be0a 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -51,6 +51,7 @@ GLOBAL_LIST_INIT(maintenance_loot, list( /obj/item/airlock_painter = 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/stack/medical/suture = 1, /obj/item/stack/rods/ten = 9, /obj/item/stack/rods/twentyfive = 1, diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 935379b721..b438afaec8 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -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 diff --git a/code/datums/ai/objects/mod.dm b/code/datums/ai/objects/mod.dm new file mode 100644 index 0000000000..ced2ffceb6 --- /dev/null +++ b/code/datums/ai/objects/mod.dm @@ -0,0 +1,48 @@ +/// An AI controller for the MODsuit pathfinder module. It's activated by implant and attaches itself to the user. +/datum/ai_controller/mod + blackboard = list( + BB_MOD_TARGET, + BB_MOD_IMPLANT, + ) + max_target_distance = MOD_AI_RANGE //a little spicy but its one specific item that summons it, and it doesnt run otherwise + ai_movement = /datum/ai_movement/jps + ///ID card generated from the suit's required access. Used for pathing. + var/obj/item/card/id/advanced/id_card + +/datum/ai_controller/mod/TryPossessPawn(atom/new_pawn) + if(!istype(new_pawn, /obj/item/mod/control)) + return AI_CONTROLLER_INCOMPATIBLE + var/obj/item/mod/control/mod = new_pawn + id_card = new /obj/item/card/id/advanced/simple_bot() + if(length(mod.req_access)) + id_card.set_access(mod.req_access) + return ..() //Run parent at end + +/datum/ai_controller/mod/UnpossessPawn(destroy) + QDEL_NULL(id_card) + return ..() //Run parent at end + +/datum/ai_controller/mod/SelectBehaviors(delta_time) + current_behaviors = list() + if(blackboard[BB_MOD_TARGET] && blackboard[BB_MOD_IMPLANT]) + queue_behavior(/datum/ai_behavior/mod_attach) + +/datum/ai_controller/mod/get_access() + return id_card + +/datum/ai_behavior/mod_attach + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT|AI_BEHAVIOR_MOVE_AND_PERFORM + +/datum/ai_behavior/mod_attach/perform(delta_time, datum/ai_controller/controller) + . = ..() + if(!controller.pawn.Adjacent(controller.blackboard[BB_MOD_TARGET])) + return + var/obj/item/implant/mod/implant = controller.blackboard[BB_MOD_IMPLANT] + implant.module.attach(controller.blackboard[BB_MOD_TARGET]) + finish_action(controller, TRUE) + +/datum/ai_behavior/mod_attach/finish_action(datum/ai_controller/controller, succeeded) + . = ..() + controller.blackboard[BB_MOD_TARGET] = null + var/obj/item/implant/mod/implant = controller.blackboard[BB_MOD_IMPLANT] + implant.end_recall(succeeded) diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm index ad37f770ed..7f6408f583 100644 --- a/code/datums/components/crafting/recipes/recipes_misc.dm +++ b/code/datums/components/crafting/recipes/recipes_misc.dm @@ -554,6 +554,19 @@ subcategory = CAT_MISCELLANEOUS category = CAT_MISCELLANEOUS +/datum/crafting_recipe/mod_core + name = "MOD core" + result = /obj/item/mod/construction/core + tool_behaviors = 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///// ////////////// diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index c0101140d6..1218e8b9af 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -369,9 +369,10 @@ //Tries to dump content /datum/component/storage/proc/dump_content_at(atom/dest_object, mob/M) var/atom/A = parent - var/atom/dump_destination = dest_object.get_dumping_location() - if(A.Adjacent(M) && dump_destination && M.Adjacent(dump_destination)) + var/atom/dump_destination = get_dumping_location(dest_object) + if(M.CanReach(A) && dump_destination && M.CanReach(dump_destination)) if(check_locked(null, M, TRUE)) + to_chat(M, "[parent] seems to be locked!") return FALSE if(dump_destination.storage_contents_dump_act(src, M)) playsound(A, "rustle", 50, 1, -5) diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index c54f6c971a..a4d5591653 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -348,12 +348,21 @@ /datum/preset_holoimage/engineer/rig outfit_type = /datum/outfit/job/engineer/gloved/rig +/datum/preset_holoimage/engineer/mod + outfit_type = /datum/outfit/job/engineer/mod + /datum/preset_holoimage/engineer/ce outfit_type = /datum/outfit/job/ce +/datum/preset_holoimage/engineer/ce/mod + outfit_type = /datum/outfit/job/ce/mod + /datum/preset_holoimage/engineer/ce/rig outfit_type = /datum/outfit/job/engineer/gloved/rig +/datum/preset_holoimage/engineer/atmos/mod + outfit_type = /datum/outfit/job/atmos/mod + /datum/preset_holoimage/engineer/atmos outfit_type = /datum/outfit/job/atmos @@ -449,6 +458,7 @@ SAY Oh, shit! DELAY 10 PRESET /datum/preset_holoimage/engineer/atmos/rig + PRESET /datum/preset_holoimage/engineer/atmos/mod LANGUAGE /datum/language/narsie NAME Unknown SAY RISE, MY LORD!! @@ -456,6 +466,7 @@ LANGUAGE /datum/language/common NAME Plastic PRESET /datum/preset_holoimage/engineer/rig + PRESET /datum/preset_holoimage/engineer/mod SAY Fuck, fuck, fuck! DELAY 20 SAY It's loose! CALL THE FUCKING SHUTT- diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm index 4c17874f97..c5221bdaf8 100755 --- a/code/datums/outfit.dm +++ b/code/datums/outfit.dm @@ -79,6 +79,10 @@ //Type path of item to go in left hand var/r_hand = null + + ///ID of the slot containing a gas tank + var/internals_slot = null + /// Any clothing accessory item var/accessory = null @@ -140,6 +144,15 @@ //to be overridden for toggling internals, id binding, access etc return +#define EQUIP_OUTFIT_ITEM(item_path, slot_name) if(##item_path) { \ + H.equip_to_slot_or_del(SSwardrobe.provide_type(##item_path), ##slot_name, TRUE); \ + var/obj/item/outfit_item = H.get_item_by_slot(##slot_name); \ + if (outfit_item && outfit_item.type == ##item_path) { \ + outfit_item.on_outfit_equip(H, visualsOnly, ##slot_name); \ + } \ +} + + /** * Equips all defined types and paths to the mob passed in * @@ -153,31 +166,32 @@ //Start with uniform,suit,backpack for additional slots if(uniform) - H.equip_to_slot_or_del(new uniform(H), ITEM_SLOT_ICLOTHING, TRUE) + EQUIP_OUTFIT_ITEM(uniform, ITEM_SLOT_ICLOTHING) if(suit) - H.equip_to_slot_or_del(new suit(H), ITEM_SLOT_OCLOTHING, TRUE) - if(back) - H.equip_to_slot_or_del(new back(H), ITEM_SLOT_BACK, TRUE) + EQUIP_OUTFIT_ITEM(suit, ITEM_SLOT_OCLOTHING) if(belt) - H.equip_to_slot_or_del(new belt(H), ITEM_SLOT_BELT, TRUE) + EQUIP_OUTFIT_ITEM(belt, ITEM_SLOT_BELT) if(gloves) - H.equip_to_slot_or_del(new gloves(H), ITEM_SLOT_GLOVES, TRUE) + EQUIP_OUTFIT_ITEM(gloves, ITEM_SLOT_GLOVES) if(shoes) - H.equip_to_slot_or_del(new shoes(H), ITEM_SLOT_FEET, TRUE) + EQUIP_OUTFIT_ITEM(shoes, ITEM_SLOT_FEET) if(head) - H.equip_to_slot_or_del(new head(H), ITEM_SLOT_HEAD, TRUE) + EQUIP_OUTFIT_ITEM(head, ITEM_SLOT_HEAD) if(mask) - H.equip_to_slot_or_del(new mask(H), ITEM_SLOT_MASK, TRUE) + EQUIP_OUTFIT_ITEM(mask, ITEM_SLOT_MASK) if(neck) - H.equip_to_slot_or_del(new neck(H), ITEM_SLOT_NECK, TRUE) + EQUIP_OUTFIT_ITEM(neck, ITEM_SLOT_NECK) if(ears) - H.equip_to_slot_or_del(new ears(H), ITEM_SLOT_EARS, TRUE) + EQUIP_OUTFIT_ITEM(ears, ITEM_SLOT_EARS) if(glasses) - H.equip_to_slot_or_del(new glasses(H), ITEM_SLOT_EYES, TRUE) + EQUIP_OUTFIT_ITEM(glasses, ITEM_SLOT_EYES) + if(back) + EQUIP_OUTFIT_ITEM(back, ITEM_SLOT_BACK) if(id) - H.equip_to_slot_or_del(new id(H), ITEM_SLOT_ID, TRUE) + EQUIP_OUTFIT_ITEM(id, ITEM_SLOT_ID) if(suit_store) - H.equip_to_slot_or_del(new suit_store(H), ITEM_SLOT_SUITSTORE, TRUE) + EQUIP_OUTFIT_ITEM(suit_store, ITEM_SLOT_SUITSTORE) + if(undershirt) H.undershirt = initial(undershirt.name) @@ -195,9 +209,9 @@ if(!visualsOnly) // Items in pockets or backpack don't show up on mob's icon. if(l_pocket) - H.equip_to_slot_or_del(new l_pocket(H), ITEM_SLOT_LPOCKET, TRUE) + EQUIP_OUTFIT_ITEM(l_pocket, ITEM_SLOT_LPOCKET) if(r_pocket) - H.equip_to_slot_or_del(new r_pocket(H), ITEM_SLOT_RPOCKET, TRUE) + EQUIP_OUTFIT_ITEM(r_pocket, ITEM_SLOT_RPOCKET) if(box) if(!backpack_contents) @@ -211,11 +225,7 @@ if(!isnum(number))//Default to 1 number = 1 for(var/i in 1 to number) - H.equip_to_slot_or_del(new path(H), ITEM_SLOT_BACKPACK, TRUE) - - if(!H.head && toggle_helmet && istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit)) - var/obj/item/clothing/suit/space/hardsuit/HS = H.wear_suit - HS.ToggleHelmet() + EQUIP_OUTFIT_ITEM(path, ITEM_SLOT_BACKPACK) post_equip(H, visualsOnly, preference_source) @@ -232,6 +242,9 @@ H.update_body() return TRUE +#undef EQUIP_OUTFIT_ITEM + + /** * Apply a fingerprint from the passed in human to all items in the outfit * diff --git a/code/datums/wires/mod.dm b/code/datums/wires/mod.dm new file mode 100644 index 0000000000..b5805557ea --- /dev/null +++ b/code/datums/wires/mod.dm @@ -0,0 +1,57 @@ +/datum/wires/mod + holder_type = /obj/item/mod/control + proper_name = "MOD control unit" + +/datum/wires/mod/New(atom/holder) + wires = list(WIRE_HACK, WIRE_DISABLE, WIRE_SHOCK, WIRE_INTERFACE) + add_duds(2) + ..() + +/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 green light is [mod.locked ? "on" : "off"]." + 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_HACK) + mod.locked = !mod.locked + 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 ..() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index e7d9898dd2..fd10507d59 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -911,7 +911,8 @@ user.active_storage.ui_show(user) return TRUE -/atom/proc/get_dumping_location(obj/item/storage/source,mob/user) +///Get the best place to dump the items contained in the source storage item? +/atom/proc/get_dumping_location() return null //This proc is called on the location of an atom when the atom is Destroy()'d diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index ef47a0e7f6..9dd54e043f 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -381,7 +381,7 @@ /obj/machinery/door/morgue icon = 'icons/obj/doors/doormorgue.dmi' -/obj/machinery/door/get_dumping_location(obj/item/storage/source,mob/user) +/obj/machinery/door/get_dumping_location() return null /obj/machinery/door/proc/lock() diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index 11ae19f7fa..634ffec74a 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -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 diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 74a05a1e7b..5e509a637b 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -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 @@ -38,21 +42,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/atmos/captain + 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/gas/atmos + storage_type = /obj/item/watertank/atmos + +/obj/machinery/suit_storage_unit/atmosmod + mask_type = /obj/item/clothing/mask/gas/atmos + 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 +96,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 +109,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 +129,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 +191,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 +201,7 @@ QDEL_NULL(suit) QDEL_NULL(helmet) QDEL_NULL(mask) + QDEL_NULL(mod) QDEL_NULL(storage) return ..() @@ -154,7 +219,7 @@ . += "broken" else . += "open" - if(suit) + if(suit || mod) . += "suit" if(helmet) . += "helm" @@ -175,6 +240,7 @@ helmet = null suit = null mask = null + mod = null storage = null occupant = null @@ -241,6 +307,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 +330,9 @@ if(mask) things_to_clear += mask things_to_clear += mask.GetAllContents() + if(mod) + things_to_clear += mod + things_to_clear += mod.get_all_contents() if(storage) things_to_clear += storage things_to_clear += storage.GetAllContents() @@ -279,13 +350,25 @@ if(occupant) dump_contents() -/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 - s.set_up(5, 1, src) - s.start() - if(electrocute_mob(user, src, src, 1, TRUE)) - return 1 +/obj/machinery/suit_storage_unit/process(delta_time) + var/obj/item/stock_parts/cell/cell + if(suit) + if(!istype(suit)) + return + if(!suit.cell) + return + cell = suit.cell + else 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/relaymove(mob/user) if(locked) @@ -353,6 +436,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, "The auxiliary storage compartment is full!") diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 0dcc23f3aa..5fd55eeb45 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -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 diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 503e2a9473..52ec7f796f 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -26,18 +26,22 @@ user.visible_message("[user] is trying to upload [user.p_them()]self into [src]! That's not going to work out well!") return BRUTELOSS -/obj/item/aicard/afterattack(atom/target, mob/user, proximity) - . = ..() - if(!proximity || !target) - return +/obj/item/aicard/pre_attack(atom/target, mob/living/user, params) if(AI) //AI is on the card, implies user wants to upload it. - log_combat(user, AI, "carded", src) + var/our_ai = AI target.transfer_ai(AI_TRANS_FROM_CARD, user, AI, src) + else //No AI on the card, therefore the user wants to download one. + target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) + if(AI) + log_combat(user, our_ai, "uploaded", src, "to [target].") + return TRUE else //No AI on the card, therefore the user wants to download one. target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) if(AI) log_combat(user, AI, "carded", src) - update_icon() //Whatever happened, update the card's state (icon, name) to match. + return TRUE + update_appearance() //Whatever happened, update the card's state (icon, name) to match. + return ..() /obj/item/aicard/update_icon() cut_overlays() diff --git a/code/game/objects/items/singularityhammer.dm b/code/game/objects/items/singularityhammer.dm index b9d66a5bd0..77dee74711 100644 --- a/code/game/objects/items/singularityhammer.dm +++ b/code/game/objects/items/singularityhammer.dm @@ -53,7 +53,13 @@ var/atom/movable/A = X if(A == wielder) continue - if(A && !A.anchored && !ishuman(X)) + if(isliving(A)) + var/mob/living/vortexed_mob = A + if(vortexed_mob.mob_negates_gravity()) + continue + else + vortexed_mob.Paralyze(2 SECONDS) + if(!A.anchored && !isobserver(A)) step_towards(A,pull) step_towards(A,pull) step_towards(A,pull) diff --git a/code/game/objects/items/storage/_storage.dm b/code/game/objects/items/storage/_storage.dm index cbaa1775eb..c7aa36a350 100644 --- a/code/game/objects/items/storage/_storage.dm +++ b/code/game/objects/items/storage/_storage.dm @@ -5,9 +5,6 @@ w_class = WEIGHT_CLASS_NORMAL var/component_type = /datum/component/storage/concrete -/obj/item/storage/get_dumping_location(obj/item/storage/source,mob/user) - return src - /obj/item/storage/Initialize(mapload) . = ..() PopulateContents() diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm index 06c38ead9f..76bfe63d27 100644 --- a/code/game/objects/items/tanks/jetpack.dm +++ b/code/game/objects/items/tanks/jetpack.dm @@ -19,6 +19,25 @@ ion_trail = new ion_trail.set_up(src) +/obj/item/tank/jetpack/Destroy() + QDEL_NULL(ion_trail) + return ..() + +/obj/item/tank/jetpack/item_action_slot_check(slot) + if(slot == ITEM_SLOT_BACK) + return TRUE + +/obj/item/tank/jetpack/equipped(mob/user, slot, initial) + . = ..() + if(on && slot != ITEM_SLOT_BACK) + turn_off(user) + +/obj/item/tank/jetpack/dropped(mob/user, silent) + . = ..() + if(on) + turn_off(user) + + /obj/item/tank/jetpack/populate_gas() if(gas_type) air_contents.set_moles(gas_type, ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C))) @@ -53,6 +72,7 @@ icon_state = "[initial(icon_state)]-on" ion_trail.start() RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/move_react) + RegisterSignal(user, COMSIG_MOVABLE_SPACEMOVE, .proc/spacemove_react) if(full_speed) user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed) else @@ -90,6 +110,13 @@ else ..() +/obj/item/tank/jetpack/proc/spacemove_react(mob/user, movement_dir) + SIGNAL_HANDLER + + if(on && (movement_dir || stabilizers)) + return COMSIG_MOVABLE_STOP_SPACEMOVE + + /obj/item/tank/jetpack/improvised name = "improvised jetpack" desc = "A jetpack made from two air tanks, a fire extinguisher and some atmospherics equipment. It doesn't look like it can hold much." diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 4ea25bec96..7ed99f895b 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -237,7 +237,7 @@ if(!anchored || current_size >= STAGE_FIVE) step_towards(src,S) -/obj/get_dumping_location(datum/component/storage/source,mob/user) +/obj/get_dumping_location() return get_turf(src) /** diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index bf06f06e6f..67d17eecfd 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -133,7 +133,7 @@ new mineral(loc) qdel(src) -/obj/structure/falsewall/get_dumping_location(obj/item/storage/source,mob/user) +/obj/structure/falsewall/get_dumping_location() return null /obj/structure/falsewall/examine_status(mob/user) //So you can't detect falsewalls by examine. diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 998285dd58..129744ab1c 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -273,7 +273,7 @@ C.add_delayedload(C.newavail() * 0.0375) // you can gain up to 3.5 via the 4x upgrades power is halved by the pole so thats 2x then 1X then .5X for 3.5x the 3 bounces shock. return ..() -/obj/structure/grille/get_dumping_location(datum/component/storage/source,mob/user) +/obj/structure/grille/get_dumping_location() return null /obj/structure/grille/broken // Pre-broken grilles for map placement diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index cf5e34fd8b..625d36f5c2 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -567,7 +567,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) take_damage(round(exposed_volume / 100), BURN, 0, 0) ..() -/obj/structure/window/get_dumping_location(obj/item/storage/source,mob/user) +/obj/structure/window/get_dumping_location() return null /obj/structure/window/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index cac03b0637..deda368e66 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -278,7 +278,7 @@ if(.) ChangeTurf(/turf/closed/wall/clockwork) -/turf/closed/wall/get_dumping_location(obj/item/storage/source, mob/user) +/turf/closed/wall/get_dumping_location() return null /turf/closed/wall/acid_act(acidpwr, acid_volume) diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm index 125bfe2034..6ec6554693 100644 --- a/code/modules/cargo/packs/science.dm +++ b/code/modules/cargo/packs/science.dm @@ -238,3 +238,15 @@ 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 + access_view = 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 diff --git a/tgstation.dme b/tgstation.dme index c45ca0f83f..1ced56efb1 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -26,6 +26,7 @@ #include "code\__DEFINES\achievements.dm" #include "code\__DEFINES\actionspeed_modifiers.dm" #include "code\__DEFINES\admin.dm" +#include "code\__DEFINES\ai.dm" #include "code\__DEFINES\antagonists.dm" #include "code\__DEFINES\atmospherics.dm" #include "code\__DEFINES\bitfields.dm" @@ -80,6 +81,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" @@ -136,6 +138,7 @@ #include "code\__DEFINES\vote.dm" #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\wall_dents.dm" +#include "code\__DEFINES\wiremod.dm" #include "code\__DEFINES\wires.dm" #include "code\__DEFINES\wounds.dm" #include "code\__DEFINES\_flags\_flags.dm" @@ -156,8 +159,11 @@ #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\dcs\signals\signals_mod.dm" #include "code\__DEFINES\dcs\signals\signals_subsystem.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movable.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_carbon.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm" #include "code\__DEFINES\mapping\maploader.dm" #include "code\__DEFINES\material\worth.dm" @@ -500,6 +506,7 @@ #include "code\datums\achievements\misc_scores.dm" #include "code\datums\achievements\skill_achievements.dm" #include "code\datums\actions\beam_rifle.dm" +#include "code\datums\ai\objects\mod.dm" #include "code\datums\announcers\_announcer.dm" #include "code\datums\announcers\default_announcer.dm" #include "code\datums\announcers\intern_announcer.dm" @@ -807,6 +814,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"