diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index b0312b6a475..ff9d805a138 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -2,7 +2,7 @@ #define STOPSPRESSUREDMAGE 1 //This flag is used on the flags variable for SUIT and HEAD items which stop pressure damage. Note that the flag 1 was previous used as ONBACK, so it is possible for some code to use (flags & 1) when checking if something can be put on your back. Replace this code with (inv_flags & SLOT_BACK) if you see it anywhere To successfully stop you taking all pressure damage you must have both a suit and head item with this flag. #define NODROP 2 // This flag makes it so that an item literally cannot be removed at all, or at least that's how it should be. Only deleted. #define NOBLUDGEON 4 // when an item has this it produces no "X has been hit by Y with Z" message with the default handler -#define MASKINTERNALS 8 // mask allows internals +#define AIRTIGHT 8 // mask allows internals //#define USEDELAY 16 // 1 second extra delay on use (Can be used once every 2s) #define NOSHIELD 32 // weapon not affected by shield #define CONDUCT 64 // conducts electricity (metal etc.) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 14e5c7f06c0..640109b99cd 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -246,29 +246,41 @@ if(C.internals) C.internals.icon_state = "internal0" else - if(!istype(C.wear_mask, /obj/item/clothing/mask)) - C << "You are not wearing a mask." - return 1 - if(C.wear_mask.mask_adjusted) - C << "Put your mask on first." + + var/no_mask + if(!(C.wear_mask && C.wear_mask.flags & AIRTIGHT)) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(!(H.head && H.head.flags & AIRTIGHT)) + no_mask = 1 + + if(no_mask) + C << "You are not wearing a suitable mask or helmet." return 1 else var/list/nicename = null var/list/tankcheck = null var/breathes = "oxygen" //default, we'll check later var/list/contents = list() + var/from = "on" if(ishuman(C)) var/mob/living/carbon/human/H = C breathes = H.species.breath_type nicename = list ("suit", "back", "belt", "right hand", "left hand", "left pocket", "right pocket") tankcheck = list (H.s_store, C.back, H.belt, C.r_hand, C.l_hand, H.l_store, H.r_store) - else - - nicename = list("Right Hand", "Left Hand", "Back") + nicename = list("right hand", "left hand", "back") tankcheck = list(C.r_hand, C.l_hand, C.back) + // Rigs are a fucking pain since they keep an air tank in nullspace. + if(istype(C.back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = C.back + if(rig.air_supply) + from = "in" + nicename |= "hardsuit" + tankcheck |= rig.air_supply + for(var/i=1, iYou are now running on internals from [tankcheck[best]] on your [nicename[best]]." + C << "You are now running on internals from [tankcheck[best]] [from] your [nicename[best]]." C.internal = tankcheck[best] diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm new file mode 100644 index 00000000000..cf3fcd3313a --- /dev/null +++ b/code/_onclick/rig.dm @@ -0,0 +1,84 @@ + +#define MIDDLE_CLICK 0 +#define ALT_CLICK 1 +#define CTRL_CLICK 2 +#define MAX_HARDSUIT_CLICK_MODE 2 + +/client + var/hardsuit_click_mode = MIDDLE_CLICK + +/client/verb/toggle_hardsuit_mode() + set name = "Toggle Hardsuit Activation Mode" + set desc = "Switch between hardsuit activation modes." + set category = "OOC" + + hardsuit_click_mode++ + if(hardsuit_click_mode > MAX_HARDSUIT_CLICK_MODE) + hardsuit_click_mode = 0 + + switch(hardsuit_click_mode) + if(MIDDLE_CLICK) + src << "Hardsuit activation mode set to middle-click." + if(ALT_CLICK) + src << "Hardsuit activation mode set to alt-click." + if(CTRL_CLICK) + src << "Hardsuit activation mode set to control-click." + else + // should never get here, but just in case: + log_debug("Bad hardsuit click mode: [hardsuit_click_mode] - expected 0 to [MAX_HARDSUIT_CLICK_MODE]") + src << "Somehow you bugged the system. Setting your hardsuit mode to middle-click." + hardsuit_click_mode = MIDDLE_CLICK + +/mob/living/MiddleClickOn(atom/A) + if(client && client.hardsuit_click_mode == MIDDLE_CLICK) + if(HardsuitClickOn(A)) + return + ..() + +/mob/living/AltClickOn(atom/A) + if(client && client.hardsuit_click_mode == ALT_CLICK) + if(HardsuitClickOn(A)) + return + ..() + +/mob/living/CtrlClickOn(atom/A) + if(client && client.hardsuit_click_mode == CTRL_CLICK) + if(HardsuitClickOn(A)) + return + ..() + +/mob/living/proc/can_use_rig() + return 0 + +/mob/living/carbon/human/can_use_rig() + return 1 + +/mob/living/carbon/brain/can_use_rig() + return istype(loc, /obj/item/device/mmi) + +/mob/living/silicon/ai/can_use_rig() + return istype(loc, /obj/item/device/aicard) + +/mob/living/silicon/pai/can_use_rig() + return loc == card + +/mob/living/proc/HardsuitClickOn(var/atom/A, var/alert_ai = 0) + if(!can_use_rig() || (next_move > world.time)) + return 0 + var/obj/item/weapon/rig/rig = get_rig() + if(istype(rig) && !rig.offline && rig.selected_module) + if(src != rig.wearer) + if(rig.ai_can_move_suit(src, check_user_module = 1)) + message_admins("[key_name_admin(src, include_name = 1)] is trying to force \the [key_name_admin(rig.wearer, include_name = 1)] to use a hardsuit module.") + else + return 0 + rig.selected_module.engage(A, alert_ai) + if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns + changeNext_move(CLICK_CD_MELEE) + return 1 + return 0 + +#undef MIDDLE_CLICK +#undef ALT_CLICK +#undef CTRL_CLICK +#undef MAX_HARDSUIT_CLICK_MODE diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 17509ed951e..4432bc0008f 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -317,7 +317,7 @@ steam.start() -- spawns the effect /obj/effect/effect/bad_smoke/Move() ..() for(var/mob/living/carbon/M in get_turf(src)) - if (M.internal != null && M.wear_mask && (M.wear_mask.flags & MASKINTERNALS)) + if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) else M.drop_item() M.adjustOxyLoss(1) @@ -340,7 +340,7 @@ steam.start() -- spawns the effect /obj/effect/effect/bad_smoke/Crossed(mob/living/carbon/M as mob ) ..() if(istype(M, /mob/living/carbon)) - if (M.internal != null && M.wear_mask && (M.wear_mask.flags & MASKINTERNALS)) + if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) return else M.drop_item() @@ -595,7 +595,7 @@ steam.start() -- spawns the effect /obj/effect/effect/sleep_smoke/Move() ..() for(var/mob/living/carbon/M in get_turf(src)) - if (M.internal != null && M.wear_mask && (M.wear_mask.flags & MASKINTERNALS)) + if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) // if (M.wear_suit, /obj/item/clothing/suit/wizrobe && (M.hat, /obj/item/clothing/head/wizard) && (M.shoes, /obj/item/clothing/shoes/sandal)) // I'll work on it later else M.drop_item() @@ -611,7 +611,7 @@ steam.start() -- spawns the effect /obj/effect/effect/sleep_smoke/Crossed(mob/living/carbon/M as mob ) ..() if(istype(M, /mob/living/carbon)) - if (M.internal != null && M.wear_mask && (M.wear_mask.flags & MASKINTERNALS)) + if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) // if (M.wear_suit, /obj/item/clothing/suit/wizrobe && (M.hat, /obj/item/clothing/head/wizard) && (M.shoes, /obj/item/clothing/shoes/sandal)) // Work on it later return else @@ -687,7 +687,7 @@ steam.start() -- spawns the effect /obj/effect/effect/mustard_gas/Move() ..() for(var/mob/living/carbon/human/R in get_turf(src)) - if (R.internal != null && usr.wear_mask && (R.wear_mask.flags & MASKINTERNALS) && R.wear_suit != null && !istype(R.wear_suit, /obj/item/clothing/suit/storage/labcoat) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket && !istype(R.wear_suit, /obj/item/clothing/suit/armor))) + if (R.internal != null && usr.wear_mask && (R.wear_mask.flags & AIRTIGHT) && R.wear_suit != null && !istype(R.wear_suit, /obj/item/clothing/suit/storage/labcoat) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket && !istype(R.wear_suit, /obj/item/clothing/suit/armor))) else R.burn_skin(0.75) if (R.coughedtime != 1) @@ -701,7 +701,7 @@ steam.start() -- spawns the effect /obj/effect/effect/mustard_gas/Crossed(mob/living/carbon/human/R as mob ) ..() if (istype(R, /mob/living/carbon/human)) - if (R.internal != null && usr.wear_mask && (R.wear_mask.flags & MASKINTERNALS) && R.wear_suit != null && !istype(R.wear_suit, /obj/item/clothing/suit/storage/labcoat) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket && !istype(R.wear_suit, /obj/item/clothing/suit/armor))) + if (R.internal != null && usr.wear_mask && (R.wear_mask.flags & AIRTIGHT) && R.wear_suit != null && !istype(R.wear_suit, /obj/item/clothing/suit/storage/labcoat) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket) && !istype(R.wear_suit, /obj/item/clothing/suit/straight_jacket && !istype(R.wear_suit, /obj/item/clothing/suit/armor))) return R.burn_skin(0.75) if (R.coughedtime != 1) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 24fd10d631d..822bb161023 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -8,12 +8,12 @@ var/item_state = null var/lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' var/righthand_file = 'icons/mob/inhands/items_righthand.dmi' - + //Dimensions of the lefthand_file and righthand_file vars //eg: 32x32 sprite, 64x64 sprite, etc. var/inhand_x_dimension = 32 - var/inhand_y_dimension = 32 - + var/inhand_y_dimension = 32 + var/r_speed = 1.0 var/health = null var/hitsound = null @@ -317,6 +317,9 @@ if(slot_wear_mask) if(H.wear_mask) return 0 + if(H.head && H.head.flags & NODROP && H.head.flags & HEADCOVERSMOUTH) + H << "\The [H.head] is in the way." + return 0 if( !(slot_flags & SLOT_MASK) ) return 0 return 1 @@ -405,6 +408,9 @@ if(slot_glasses) if(H.glasses) return 0 + if(H.head && H.head.flags & NODROP && H.head.flags & HEADCOVERSMOUTH) + H << "\The [H.head] is in the way." + return 0 if( !(slot_flags & SLOT_EYES) ) return 0 return 1 @@ -713,7 +719,7 @@ /obj/item/proc/pwr_drain() return 0 // Process Kill - + /obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/weapon/storage if(!newLoc) return 0 @@ -722,4 +728,4 @@ S.remove_from_storage(src,newLoc) return 1 return 0 - + diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index da972725956..7c5c7e4a09f 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -12,6 +12,13 @@ var/looking_for_personality = 0 var/mob/living/silicon/pai/pai +/obj/item/device/paicard/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.forced_move(direction, user) + /obj/item/device/paicard/New() ..() overlays += "pai-off" diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 15fc8a7b0b5..aa0d0e116f1 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -63,10 +63,9 @@ var/allgases = G.carbon_dioxide + G.nitrogen + G.oxygen + G.toxins //fuck trace gases -Pete if(allgases >= 0.005) - return 1 + . = 1 qdel(G) - return /obj/item/weapon/tank/jetpack/ui_action_click() toggle() @@ -132,3 +131,33 @@ if(air_contents.carbon_dioxide < 10) user << text("\red The meter on the [src.name] indicates you are almost out of air!") playsound(user, 'sound/effects/alert.ogg', 50, 1) + + +/obj/item/weapon/tank/jetpack/rig + name = "jetpack" + var/obj/item/weapon/rig/holder + +/obj/item/weapon/tank/jetpack/rig/examine() + usr << "It's a jetpack. If you can see this, report it on the bug tracker." + return 0 + +/obj/item/weapon/tank/jetpack/rig/allow_thrust(num, mob/living/user as mob) + + if(!(src.on)) + return 0 + + if(!istype(holder) || !holder.air_supply) + return 0 + + var/obj/item/weapon/tank/pressure_vessel = holder.air_supply + + if((num < 0.005 || pressure_vessel.air_contents.total_moles() < num)) + src.ion_trail.stop() + return 0 + + var/datum/gas_mixture/G = pressure_vessel.air_contents.remove(num) + + var/allgases = G.carbon_dioxide + G.nitrogen + G.oxygen + G.toxins + if(allgases >= 0.005) + . = 1 + qdel(G) \ No newline at end of file diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 9fdb1c0e73e..db59ffc3492 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -120,8 +120,13 @@ data["maskConnected"] = 0 if(istype(loc,/mob/living/carbon)) var/mob/living/carbon/location = loc - if(location.internal == src || (location.wear_mask && (location.wear_mask.flags & MASKINTERNALS))) - data["maskConnected"] = 1 + if(location.internal == src) + if(location.wear_mask && (location.wear_mask.flags & AIRTIGHT)) + data["maskConnected"] = 1 + else if(ishuman(location)) + var/mob/living/carbon/human/H = location + if(H.head && (H.head.flags & AIRTIGHT)) + data["maskConnected"] = 1 // update the ui if it exists, returns null if no ui is passed/found ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -162,7 +167,16 @@ if (location.internals) location.internals.icon_state = "internal0" else - if(location.wear_mask && (location.wear_mask.flags & MASKINTERNALS)) + + var/can_open_valve + if(location.wear_mask && (location.wear_mask.flags & AIRTIGHT)) + can_open_valve = 1 + else if(istype(location,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = location + if(H.head && (H.head.flags & AIRTIGHT)) + can_open_valve = 1 + + if(can_open_valve) location.internal = src usr << "\blue You open \the [src] valve." if (location.internals) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index e32ebbc7687..36ef84ec83d 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -362,4 +362,4 @@ /turf/proc/get_lumcount() //Gets the lighting level of a given turf. if(lighting_overlay) return lighting_overlay.get_clamped_lum() - return 1 + return 1 \ No newline at end of file diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index 616fcfea38e..74d44d23f0e 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -3,7 +3,7 @@ name = "breath mask" icon_state = "breath" item_state = "breath" - flags = MASKCOVERSMOUTH | MASKINTERNALS + flags = MASKCOVERSMOUTH | AIRTIGHT w_class = 2 gas_transfer_coefficient = 0.10 permeability_coefficient = 0.50 diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 52f5e195d15..2c1258b428a 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -2,7 +2,7 @@ name = "gas mask" desc = "A face-covering mask that can be connected to an air supply." icon_state = "gas_alt" - flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS + flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT flags_inv = HIDEEARS|HIDEEYES|HIDEFACE w_class = 3.0 item_state = "gas_alt" @@ -62,7 +62,7 @@ name = "bane mask" desc = "Only when the station is in flames, do you have my permission to robust." icon_state = "bane_mask" - flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS + flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT flags_inv = HIDEEARS|HIDEEYES|HIDEFACE w_class = 3.0 item_state = "bane_mask" @@ -100,7 +100,7 @@ icon_state = "clown" item_state = "clown_hat" species_fit = list("Vox") - flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS | BLOCKHAIR + flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR /obj/item/clothing/mask/gas/clown_hat/attack_self(mob/user) @@ -123,7 +123,7 @@ desc = "Some pranksters are truly magical." icon_state = "wizzclown" item_state = "wizzclown" - flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS | BLOCKHAIR + flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR /obj/item/clothing/mask/gas/virusclown_hat name = "clown wig and mask" @@ -178,7 +178,7 @@ desc = "Twoooo!" icon_state = "owl" species_fit = list("Vox") - flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS | NODROP + flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | NODROP var/cooldown = 0 action_button_name = "Hoot" diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index fb2e5ec16ef..366d0d63d94 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -160,7 +160,7 @@ icon_state = "pennywise_mask" item_state = "pennywise_mask" species_fit = list("Vox") - flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS | BLOCKHAIR + flags = MASKCOVERSMOUTH | MASKCOVERSEYES | BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR // Bandanas /obj/item/clothing/mask/bandana diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm new file mode 100644 index 00000000000..8838c0bba78 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -0,0 +1,248 @@ +/* + * Contains + * /obj/item/rig_module/grenade_launcher + * /obj/item/rig_module/mounted + * /obj/item/rig_module/mounted/taser + * /obj/item/rig_module/shield + * /obj/item/rig_module/fabricator + * /obj/item/rig_module/device/flash + */ + +/obj/item/rig_module/device/flash + name = "mounted flash" + desc = "You are the law." + icon_state = "flash" + interface_name = "mounted flash" + interface_desc = "Stuns your target by blinding them with a bright light." + device_type = /obj/item/device/flash + +/obj/item/rig_module/grenade_launcher + + name = "mounted grenade launcher" + desc = "A shoulder-mounted micro-explosive dispenser." + selectable = 1 + icon_state = "grenade" + + interface_name = "integrated grenade launcher" + interface_desc = "Discharges loaded grenades against the wearer's location." + + var/fire_force = 30 + var/fire_distance = 10 + + charges = list( + list("flashbang", "flashbang", /obj/item/weapon/grenade/flashbang, 3), + list("smoke bomb", "smoke bomb", /obj/item/weapon/grenade/smokebomb, 3), + list("EMP grenade", "EMP grenade", /obj/item/weapon/grenade/empgrenade, 3), + ) + +/obj/item/rig_module/grenade_launcher/accepts_item(var/obj/item/input_device, var/mob/living/user) + + if(!istype(input_device) || !istype(user)) + return 0 + + var/datum/rig_charge/accepted_item + for(var/charge in charges) + var/datum/rig_charge/charge_datum = charges[charge] + if(input_device.type == charge_datum.product_type) + accepted_item = charge_datum + break + + if(!accepted_item) + return 0 + + if(accepted_item.charges >= 5) + user << "Another grenade of that type will not fit into the module." + return 0 + + user << "You slot \the [input_device] into the suit module." + user.unEquip(input_device) + qdel(input_device) + accepted_item.charges++ + return 1 + +/obj/item/rig_module/grenade_launcher/engage(atom/target) + + if(!..()) + return 0 + + if(!target) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!charge_selected) + H << "You have not selected a grenade type." + return 0 + + var/datum/rig_charge/charge = charges[charge_selected] + + if(!charge) + return 0 + + if(charge.charges <= 0) + H << "Insufficient grenades!" + return 0 + + charge.charges-- + var/obj/item/weapon/grenade/new_grenade = new charge.product_type(get_turf(H)) + H.visible_message("[H] launches \a [new_grenade]!") + new_grenade.throw_at(target,fire_force,fire_distance) + new_grenade.prime() + +/obj/item/rig_module/mounted + + name = "mounted laser cannon" + desc = "A shoulder-mounted battery-powered laser cannon mount." + selectable = 1 + usable = 1 + module_cooldown = 0 + icon_state = "lcannon" + + engage_string = "Configure" + + interface_name = "mounted laser cannon" + interface_desc = "A shoulder-mounted cell-powered laser cannon." + + var/gun_type = /obj/item/weapon/gun/energy/lasercannon/mounted + var/obj/item/weapon/gun/gun + +/obj/item/rig_module/mounted/New() + ..() + gun = new gun_type(src) + +/obj/item/rig_module/mounted/engage(atom/target) + + if(!..()) + return 0 + + if(!target) + gun.attack_self(holder.wearer) + return 1 + + gun.Fire(target,holder.wearer) + return 1 + +/obj/item/rig_module/mounted/egun + + name = "mounted energy gun" + desc = "A forearm-mounted energy projector." + icon_state = "egun" + + interface_name = "mounted energy gun" + interface_desc = "A forearm-mounted suit-powered energy gun." + + gun_type = /obj/item/weapon/gun/energy/gun/mounted + +/obj/item/rig_module/mounted/taser + + name = "mounted taser" + desc = "A palm-mounted nonlethal energy projector." + icon_state = "taser" + + usable = 0 + + suit_overlay_active = "mounted-taser" + suit_overlay_inactive = "mounted-taser" + + interface_name = "mounted energy gun" + interface_desc = "A shoulder-mounted cell-powered energy gun." + + gun_type = /obj/item/weapon/gun/energy/taser/mounted + +/obj/item/rig_module/mounted/energy_blade + + name = "energy blade projector" + desc = "A powerful cutting beam projector." + icon_state = "eblade" + + activate_string = "Project Blade" + deactivate_string = "Cancel Blade" + + interface_name = "spider fang blade" + interface_desc = "A lethal energy projector that can shape a blade projected from the hand of the wearer or launch radioactive darts." + + usable = 0 + selectable = 1 + toggleable = 1 + use_power_cost = 50 + active_power_cost = 10 + passive_power_cost = 0 + + gun_type = /obj/item/weapon/gun/energy/crossbow/ninja + +/obj/item/rig_module/mounted/energy_blade/process() + + if(holder && holder.wearer) + if(!(locate(/obj/item/weapon/melee/energy/blade) in holder.wearer)) + deactivate() + return 0 + + return ..() + +/obj/item/rig_module/mounted/energy_blade/activate() + + ..() + + var/mob/living/M = holder.wearer + + if(M.l_hand && M.r_hand) + M << "Your hands are full." + deactivate() + return + + var/obj/item/weapon/melee/energy/blade/blade = new(M) + M.put_in_hands(blade) + +/obj/item/rig_module/mounted/energy_blade/deactivate() + + ..() + + var/mob/living/M = holder.wearer + + if(!M) + return + + for(var/obj/item/weapon/melee/energy/blade/blade in M.contents) + M.unEquip(blade) + qdel(blade) + +/obj/item/rig_module/fabricator + + name = "matter fabricator" + desc = "A self-contained microfactory system for hardsuit integration." + selectable = 1 + usable = 1 + use_power_cost = 15 + icon_state = "enet" + + engage_string = "Fabricate Star" + + interface_name = "death blossom launcher" + interface_desc = "An integrated microfactory that produces floor tiles from thin air and electricity." + + var/fabrication_type = /obj/item/stack/tile + var/fire_force = 30 + var/fire_distance = 10 + +/obj/item/rig_module/fabricator/engage(atom/target) + + if(!..()) + return 0 + + var/mob/living/H = holder.wearer + + if(target) + var/obj/item/firing = new fabrication_type() + firing.forceMove(get_turf(src)) + H.visible_message("[H] launches \a [firing]!") + firing.throw_at(target,fire_force,fire_distance) + else + if(H.l_hand && H.r_hand) + H << "Your hands are full." + else + var/obj/item/new_weapon = new fabrication_type() + new_weapon.forceMove(H) + H << "You quickly fabricate \a [new_weapon]." + H.put_in_hands(new_weapon) + + return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm new file mode 100644 index 00000000000..2f51fe30c5b --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -0,0 +1,505 @@ +/* + * Contains + * /obj/item/rig_module/ai_container + * /obj/item/rig_module/datajack + * /obj/item/rig_module/power_sink + * /obj/item/rig_module/electrowarfare_suite + */ + +/obj/item/ai_verbs + name = "AI verb holder" + +/obj/item/ai_verbs/verb/hardsuit_interface() + set category = "Hardsuit" + set name = "Open Hardsuit Interface" + set src in usr + + if(!usr.loc || !usr.loc.loc || !istype(usr.loc.loc, /obj/item/rig_module)) + usr << "You are not loaded into a hardsuit." + return + + var/obj/item/rig_module/module = usr.loc.loc + if(!module.holder) + usr << "Your module is not installed in a hardsuit." + return + + module.holder.ui_interact(usr, nano_state = contained_state) + +/obj/item/rig_module/ai_container + + name = "IIS module" + desc = "An integrated intelligence system module suitable for most hardsuits." + icon_state = "IIS" + toggleable = 1 + usable = 1 + disruptive = 0 + activates_on_touch = 1 + + engage_string = "Eject AI" + activate_string = "Enable Dataspike" + deactivate_string = "Disable Dataspike" + + interface_name = "integrated intelligence system" + interface_desc = "A socket that supports a range of artificial intelligence systems." + + var/mob/integrated_ai // Direct reference to the actual mob held in the suit. + var/obj/item/ai_card // Reference to the MMI, posibrain, intellicard or pAI card previously holding the AI. + var/obj/item/ai_verbs/verb_holder + +/mob + var/get_rig_stats = 0 + +/obj/item/rig_module/ai_container/process() + if(integrated_ai) + var/obj/item/weapon/rig/rig = get_rig() + if(rig && rig.ai_override_enabled) + integrated_ai.get_rig_stats = 1 + else + integrated_ai.get_rig_stats = 0 + +/mob/living/Stat() + . = ..() + if(. && get_rig_stats) + var/obj/item/weapon/rig/rig = get_rig() + if(rig) + SetupStat(rig) + +/obj/item/rig_module/ai_container/proc/update_verb_holder() + if(!verb_holder) + verb_holder = new(src) + if(integrated_ai) + verb_holder.forceMove(integrated_ai) + else + verb_holder.forceMove(src) + +/obj/item/rig_module/ai_container/accepts_item(var/obj/item/input_device, var/mob/living/user) + + // Check if there's actually an AI to deal with. + var/mob/living/silicon/ai/target_ai + if(istype(input_device, /mob/living/silicon/ai)) + target_ai = input_device + else + target_ai = locate(/mob/living/silicon/ai) in input_device.contents + + var/obj/item/device/aicard/card = ai_card + + // Downloading from/loading to a terminal. + if(istype(input_device,/obj/machinery/computer/aifixer) || istype(input_device,/mob/living/silicon/ai) || istype(input_device,/obj/structure/AIcore/deactivated)) + + // If we're stealing an AI, make sure we have a card for it. + if(!card) + card = new /obj/item/device/aicard(src) + + // Terminal interaction only works with an intellicarded AI. + if(!istype(card)) + return 0 + + // Since we've explicitly checked for three types, this should be safe. + card.afterattack(input_device, user, 1) + + // If the transfer failed we can delete the card. + if(locate(/mob/living/silicon/ai) in card) + ai_card = card + integrated_ai = locate(/mob/living/silicon/ai) in card + else + eject_ai() + update_verb_holder() + return 1 + + if(istype(input_device,/obj/item/device/aicard)) + // We are carding the AI in our suit. + if(integrated_ai) + var/obj/item/device/aicard/ext_card = input_device + ext_card.afterattack(integrated_ai, user, 1) + // If the transfer was successful, we can clear out our vars. + if(integrated_ai.loc != src) + integrated_ai = null + eject_ai() + else + // You're using an empty card on an empty suit, idiot. + if(!target_ai) + return 0 + integrate_ai(input_device,user) + return 1 + + // Okay, it wasn't a terminal being touched, check for all the simple insertions. + if(input_device.type in list(/obj/item/device/paicard, /obj/item/device/mmi, /obj/item/device/mmi/posibrain)) + if(integrated_ai) + integrated_ai.attackby(input_device,user) + // If the transfer was successful, we can clear out our vars. + if(integrated_ai.loc != src) + integrated_ai = null + eject_ai() + else + integrate_ai(input_device,user) + return 1 + + return 0 + +/obj/item/rig_module/ai_container/engage(atom/target) + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!target) + if(ai_card) + if(istype(ai_card,/obj/item/device/aicard)) + ai_card.ui_interact(H, state = deep_inventory_state) + else + eject_ai(H) + update_verb_holder() + return 1 + + if(accepts_item(target,H)) + return 1 + + return 0 + +/obj/item/rig_module/ai_container/removed() + eject_ai() + ..() + +/obj/item/rig_module/ai_container/proc/eject_ai(var/mob/user) + + if(ai_card) + if(istype(ai_card, /obj/item/device/aicard)) + if(integrated_ai && !integrated_ai.stat) + if(user) + user << "You cannot eject your currently stored AI. Purge it manually." + return 0 + user << "You purge the remaining scraps of data from your previous AI, freeing it for use." + if(integrated_ai) + integrated_ai.ghostize() + qdel(integrated_ai) + integrated_ai = null + if(ai_card) + qdel(ai_card) + ai_card = null + else if(user) + user.put_in_hands(ai_card) + else + ai_card.forceMove(get_turf(src)) + ai_card = null + integrated_ai = null + update_verb_holder() + +/obj/item/rig_module/ai_container/proc/integrate_ai(var/obj/item/ai,var/mob/user) + if(!ai) return + + // The ONLY THING all the different AI systems have in common is that they all store the mob inside an item. + var/mob/living/ai_mob = locate(/mob/living) in ai.contents + if(ai_mob) + + if(ai_mob.key && ai_mob.client) + + if(istype(ai, /obj/item/device/aicard)) + + if(!ai_card) + ai_card = new /obj/item/device/aicard(src) + + var/obj/item/device/aicard/source_card = ai + var/obj/item/device/aicard/target_card = ai_card + if(istype(source_card) && istype(target_card)) + if(target_card.grab_ai(ai_mob, user)) + source_card.clear() + else + return 0 + else + return 0 + else + user.unEquip(ai) + ai.forceMove(src) + ai_card = ai + ai_mob << "You have been transferred to \the [holder]'s [src]." + user << "You load [ai_mob] into \the [holder]'s [src]." + + integrated_ai = ai_mob + + if(!(locate(integrated_ai) in ai_card)) + integrated_ai = null + eject_ai() + else + user << "There is no active AI within \the [ai]." + else + user << "There is no active AI within \the [ai]." + update_verb_holder() + return + +/obj/item/rig_module/datajack + + name = "datajack module" + desc = "A simple induction datalink module." + icon_state = "datajack" + toggleable = 1 + activates_on_touch = 1 + usable = 0 + + activate_string = "Enable Datajack" + deactivate_string = "Disable Datajack" + + interface_name = "contact datajack" + interface_desc = "An induction-powered high-throughput datalink suitable for hacking encrypted networks." + var/list/stored_research + +/obj/item/rig_module/datajack/New() + ..() + stored_research = list() + +/obj/item/rig_module/datajack/engage(atom/target) + + if(!..()) + return 0 + + if(target) + var/mob/living/carbon/human/H = holder.wearer + if(!accepts_item(target,H)) + return 0 + return 1 + +/obj/item/rig_module/datajack/accepts_item(var/obj/item/input_device, var/mob/living/user) + + if(istype(input_device,/obj/item/weapon/disk/tech_disk)) + user << "You slot the disk into [src]." + var/obj/item/weapon/disk/tech_disk/disk = input_device + if(disk.stored) + if(load_data(disk.stored)) + user << "Download successful; disk erased." + disk.stored = null + else + user << "The disk is corrupt. It is useless to you." + else + user << "The disk is blank. It is useless to you." + return 1 + + // I fucking hate R&D code. This typecheck spam would be totally unnecessary in a sane setup. + else if(istype(input_device,/obj/machinery)) + var/datum/research/incoming_files + if(istype(input_device,/obj/machinery/computer/rdconsole)) + var/obj/machinery/computer/rdconsole/input_machine = input_device + incoming_files = input_machine.files + else if(istype(input_device,/obj/machinery/r_n_d/server)) + var/obj/machinery/r_n_d/server/input_machine = input_device + incoming_files = input_machine.files + else if(istype(input_device,/obj/machinery/mecha_part_fabricator)) + var/obj/machinery/mecha_part_fabricator/input_machine = input_device + incoming_files = input_machine.files + + if(!incoming_files || !incoming_files.known_tech || !incoming_files.known_tech.len) + user << "Memory failure. There is nothing accessible stored on this terminal." + else + // Maybe consider a way to drop all your data into a target repo in the future. + if(load_data(incoming_files.known_tech)) + user << "Download successful; local and remote repositories synchronized." + else + user << "Scan complete. There is nothing useful stored on this terminal." + return 1 + return 0 + +/obj/item/rig_module/datajack/proc/load_data(var/incoming_data) + + if(islist(incoming_data)) + for(var/entry in incoming_data) + load_data(entry) + return 1 + + if(istype(incoming_data, /datum/tech)) + var/data_found + var/datum/tech/new_data = incoming_data + for(var/datum/tech/current_data in stored_research) + if(current_data.id == new_data.id) + data_found = 1 + if(current_data.level < new_data.level) + current_data.level = new_data.level + break + if(!data_found) + stored_research += incoming_data + return 1 + return 0 + +/obj/item/rig_module/electrowarfare_suite + + name = "electrowarfare module" + desc = "A bewilderingly complex bundle of fiber optics and chips." + icon_state = "ewar" + toggleable = 1 + usable = 0 + + activate_string = "Enable Countermeasures" + deactivate_string = "Disable Countermeasures" + + interface_name = "electrowarfare system" + interface_desc = "An active counter-electronic warfare suite that disrupts AI tracking." + +/obj/item/rig_module/electrowarfare_suite/activate() + + if(!..()) + return + + // This is not the best way to handle this, but I don't want it to mess with ling camo + var/mob/living/M = holder.wearer + M.digitalcamo++ + +/obj/item/rig_module/electrowarfare_suite/deactivate() + + if(!..()) + return + + var/mob/living/M = holder.wearer + M.digitalcamo = max(0,(M.digitalcamo-1)) + +/* //Not easily compatible with our current powernet, and is +/obj/item/rig_module/power_sink // quite stupid anyways, iyam + + name = "hardsuit power sink" + desc = "An heavy-duty power sink." + icon_state = "powersink" + toggleable = 1 + activates_on_touch = 1 + disruptive = 0 + + activate_string = "Enable Power Sink" + deactivate_string = "Disable Power Sink" + + interface_name = "niling d-sink" + interface_desc = "Colloquially known as a power siphon, this module drains power through the suit hands into the suit battery." + + var/atom/interfaced_with // Currently draining power from this device. + var/total_power_drained = 0 + var/drain_loc + +/obj/item/rig_module/power_sink/deactivate() + + if(interfaced_with) + if(holder && holder.wearer) + holder.wearer << "Your power sink retracts as the module deactivates." + drain_complete() + interfaced_with = null + total_power_drained = 0 + return ..() + +/obj/item/rig_module/power_sink/activate() + interfaced_with = null + total_power_drained = 0 + return ..() + +/obj/item/rig_module/power_sink/engage(atom/target) + + if(!..()) + return 0 + + //Target wasn't supplied or we're already draining. + if(interfaced_with) + return 0 + + if(!target) + return 1 + + // Are we close enough? + var/mob/living/carbon/human/H = holder.wearer + if(!target.Adjacent(H)) + return 0 + + // Is it a valid power source? + if(target.drain_power(1) <= 0) + return 0 + + H << "You begin draining power from [target]!" + interfaced_with = target + drain_loc = interfaced_with.loc + + holder.spark_system.start() + playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1) + + return 1 + +/obj/item/rig_module/power_sink/accepts_item(var/obj/item/input_device, var/mob/living/user) + var/can_drain = input_device.drain_power(1) + if(can_drain > 0) + engage(input_device) + return 1 + return 0 + +/obj/item/rig_module/power_sink/process() + + if(!interfaced_with) + return ..() + + var/mob/living/carbon/human/H + if(holder && holder.wearer) + H = holder.wearer + + if(!H || !istype(H)) + return 0 + + holder.spark_system.start() + playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1) + + if(!holder.cell) + H << "Your power sink flashes an error; there is no cell in your rig." + drain_complete(H) + return + + if(!interfaced_with || !interfaced_with.Adjacent(H) || !(interfaced_with.loc == drain_loc)) + H << "Your power sink retracts into its casing." + drain_complete(H) + return + + if(holder.cell.fully_charged()) + H << "Your power sink flashes an amber light; your rig cell is full." + drain_complete(H) + return + + // Attempts to drain up to 40kW, determines this value from remaining cell capacity to ensure we don't drain too much.. + var/to_drain = min(40000, ((holder.cell.maxcharge - holder.cell.charge) / CELLRATE)) + var/target_drained = interfaced_with.drain_power(0,0,to_drain) + if(target_drained <= 0) + H << "Your power sink flashes a red light; there is no power left in [interfaced_with]." + drain_complete(H) + return + + holder.cell.give(target_drained * CELLRATE) + total_power_drained += target_drained + + return 1 + +/obj/item/rig_module/power_sink/proc/drain_complete(var/mob/living/M) + + if(!interfaced_with) + if(M) M << "Total power drained: [round(total_power_drained/1000)]kJ." + else + if(M) M << "Total power drained from [interfaced_with]: [round(total_power_drained/1000)]kJ." + interfaced_with.drain_power(0,1,0) // Damage the victim. + + drain_loc = null + interfaced_with = null + total_power_drained = 0*/ + +/* +//Maybe make this use power when active or something +/obj/item/rig_module/emp_shielding + name = "\improper EMP dissipation module" + desc = "A bewilderingly complex bundle of fiber optics and chips." + toggleable = 1 + usable = 0 + + activate_string = "Enable active EMP shielding" + deactivate_string = "Disable active EMP shielding" + + interface_name = "active EMP shielding system" + interface_desc = "A highly experimental system that augments the hardsuit's existing EM shielding." + var/protection_amount = 20 + +/obj/item/rig_module/emp_shielding/activate() + if(!..()) + return + + holder.emp_protection += protection_amount + +/obj/item/rig_module/emp_shielding/deactivate() + if(!..()) + return + + holder.emp_protection = max(0,(holder.emp_protection - protection_amount)) +*/ diff --git a/code/modules/clothing/spacesuits/rig/modules/modules.dm b/code/modules/clothing/spacesuits/rig/modules/modules.dm new file mode 100644 index 00000000000..183255ca635 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/modules.dm @@ -0,0 +1,335 @@ +/* + * Rigsuit upgrades/abilities. + */ + +/datum/rig_charge + var/short_name = "undef" + var/display_name = "undefined" + var/product_type = "undefined" + var/charges = 0 + +/obj/item/rig_module + name = "hardsuit upgrade" + desc = "It looks pretty sciency." + icon = 'icons/obj/rig_modules.dmi' + icon_state = "module" + + var/damage = 0 + var/obj/item/weapon/rig/holder + + var/module_cooldown = 10 + var/next_use = 0 + + var/toggleable // Set to 1 for the device to show up as an active effect. + var/usable // Set to 1 for the device to have an on-use effect. + var/selectable // Set to 1 to be able to assign the device as primary system. + var/redundant // Set to 1 to ignore duplicate module checking when installing. + var/permanent // If set, the module can't be removed. + var/disruptive = 1 // Can disrupt by other effects. + var/activates_on_touch // If set, unarmed attacks will call engage() on the target. + + var/active // Basic module status + var/disruptable // Will deactivate if some other powers are used. + + var/use_power_cost = 0 // Power used when single-use ability called. + var/active_power_cost = 0 // Power used when turned on. + var/passive_power_cost = 0 // Power used when turned off. + + var/list/charges // Associative list of charge types and remaining numbers. + var/charge_selected // Currently selected option used for charge dispensing. + + // Icons. + var/suit_overlay + var/suit_overlay_active // If set, drawn over icon and mob when effect is active. + var/suit_overlay_inactive // As above, inactive. + var/suit_overlay_used // As above, when engaged. + + //Display fluff + var/interface_name = "hardsuit upgrade" + var/interface_desc = "A generic hardsuit upgrade." + var/engage_string = "Engage" + var/activate_string = "Activate" + var/deactivate_string = "Deactivate" + + var/list/stat_rig_module/stat_modules = new() + +/obj/item/rig_module/examine() + ..() + switch(damage) + if(0) + usr << "It is undamaged." + if(1) + usr << "It is badly damaged." + if(2) + usr << "It is almost completely destroyed." + +/obj/item/rig_module/attackby(obj/item/W as obj, mob/user as mob) + + if(istype(W,/obj/item/stack/nanopaste)) + + if(damage == 0) + user << "There is no damage to mend." + return + + user << "You start mending the damaged portions of \the [src]..." + + if(!do_after(user,30, target = src) || !W || !src) + return + + var/obj/item/stack/nanopaste/paste = W + damage = 0 + user << "You mend the damage to [src] with [W]." + paste.use(1) + return + + else if(istype(W,/obj/item/stack/cable_coil)) + + switch(damage) + if(0) + user << "There is no damage to mend." + return + if(2) + user << "There is no damage that you are capable of mending with such crude tools." + return + + var/obj/item/stack/cable_coil/cable = W + if(!cable.amount >= 5) + user << "You need five units of cable to repair \the [src]." + return + + user << "You start mending the damaged portions of \the [src]..." + if(!do_after(user, 30, target = src) || !W || !src) + return + + damage = 1 + user << "You mend some of damage to [src] with [W], but you will need more advanced tools to fix it completely." + cable.use(5) + return + ..() + +/obj/item/rig_module/New() + ..() + if(suit_overlay_inactive) + suit_overlay = suit_overlay_inactive + + if(charges && charges.len) + var/list/processed_charges = list() + for(var/list/charge in charges) + var/datum/rig_charge/charge_dat = new + + charge_dat.short_name = charge[1] + charge_dat.display_name = charge[2] + charge_dat.product_type = charge[3] + charge_dat.charges = charge[4] + + if(!charge_selected) charge_selected = charge_dat.short_name + processed_charges[charge_dat.short_name] = charge_dat + + charges = processed_charges + + stat_modules += new/stat_rig_module/activate(src) + stat_modules += new/stat_rig_module/deactivate(src) + stat_modules += new/stat_rig_module/engage(src) + stat_modules += new/stat_rig_module/select(src) + stat_modules += new/stat_rig_module/charge(src) + +// Called when the module is installed into a suit. +/obj/item/rig_module/proc/installed(var/obj/item/weapon/rig/new_holder) + holder = new_holder + return + +//Proc for one-use abilities like teleport. +/obj/item/rig_module/proc/engage() + + if(damage >= 2) + usr << "The [interface_name] is damaged beyond use!" + return 0 + + if(world.time < next_use) + usr << "You cannot use the [interface_name] again so soon." + return 0 + + if(!holder || (!(holder.flags & NODROP))) + usr << "The suit is not initialized." + return 0 + + if(usr.lying || usr.stat || usr.stunned || usr.paralysis || usr.weakened) + usr << "You cannot use the suit in this state." + return 0 + + if(holder.wearer && holder.wearer.lying) + usr << "The suit cannot function while the wearer is prone." + return 0 + + if(holder.security_check_enabled && !holder.check_suit_access(usr)) + usr << "Access denied." + return 0 + + if(!holder.check_power_cost(usr, use_power_cost, 0, src, (istype(usr,/mob/living/silicon ? 1 : 0) ) ) ) + return 0 + + next_use = world.time + module_cooldown + + return 1 + +// Proc for toggling on active abilities. +/obj/item/rig_module/proc/activate() + + if(active || !engage()) + return 0 + + active = 1 + + spawn(1) + if(suit_overlay_active) + suit_overlay = suit_overlay_active + else + suit_overlay = null + holder.update_icon() + + return 1 + +// Proc for toggling off active abilities. +/obj/item/rig_module/proc/deactivate() + + if(!active) + return 0 + + active = 0 + + spawn(1) + if(suit_overlay_inactive) + suit_overlay = suit_overlay_inactive + else + suit_overlay = null + if(holder) + holder.update_icon() + + return 1 + +// Called when the module is uninstalled from a suit. +/obj/item/rig_module/proc/removed() + deactivate() + holder = null + return + +// Called by the hardsuit each rig process tick. +/obj/item/rig_module/process() + if(active) + return active_power_cost + else + return passive_power_cost + +// Called by holder rigsuit attackby() +// Checks if an item is usable with this module and handles it if it is +/obj/item/rig_module/proc/accepts_item(var/obj/item/input_device) + return 0 + +/mob/living/carbon/human/Stat() + . = ..() + + if(. && istype(back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/R = back + SetupStat(R) + +/mob/proc/SetupStat(var/obj/item/weapon/rig/R) + if(R && (R.flags & NODROP) && R.installed_modules.len && statpanel("Hardsuit Modules")) + var/cell_status = R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "ERROR" + stat("Suit charge", cell_status) + for(var/obj/item/rig_module/module in R.installed_modules) + { + for(var/stat_rig_module/SRM in module.stat_modules) + if(SRM.CanUse()) + stat(SRM.module.interface_name,SRM) + } + +/stat_rig_module + parent_type = /atom/movable + var/module_mode = "" + var/obj/item/rig_module/module + +/stat_rig_module/New(var/obj/item/rig_module/module) + ..() + src.module = module + +/stat_rig_module/proc/AddHref(var/list/href_list) + return + +/stat_rig_module/proc/CanUse() + return 0 + +/stat_rig_module/Click() + if(CanUse()) + var/list/href_list = list( + "interact_module" = module.holder.installed_modules.Find(module), + "module_mode" = module_mode + ) + AddHref(href_list) + module.holder.Topic(usr, href_list) + +/stat_rig_module/DblClick() + return Click() + +/stat_rig_module/activate/New(var/obj/item/rig_module/module) + ..() + name = module.activate_string + if(module.active_power_cost) + name += " ([module.active_power_cost*10]A)" + module_mode = "activate" + +/stat_rig_module/activate/CanUse() + return module.toggleable && !module.active + +/stat_rig_module/deactivate/New(var/obj/item/rig_module/module) + ..() + name = module.deactivate_string + // Show cost despite being 0, if it means changing from an active cost. + if(module.active_power_cost || module.passive_power_cost) + name += " ([module.passive_power_cost*10]P)" + + module_mode = "deactivate" + +/stat_rig_module/deactivate/CanUse() + return module.toggleable && module.active + +/stat_rig_module/engage/New(var/obj/item/rig_module/module) + ..() + name = module.engage_string + if(module.use_power_cost) + name += " ([module.use_power_cost*10]E)" + module_mode = "engage" + +/stat_rig_module/engage/CanUse() + return module.usable + +/stat_rig_module/select/New() + ..() + name = "Select" + module_mode = "select" + +/stat_rig_module/select/CanUse() + if(module.selectable) + name = module.holder.selected_module == module ? "Selected" : "Select" + return 1 + return 0 + +/stat_rig_module/charge/New() + ..() + name = "Change Charge" + module_mode = "select_charge_type" + +/stat_rig_module/charge/AddHref(var/list/href_list) + var/charge_index = module.charges.Find(module.charge_selected) + if(!charge_index) + charge_index = 0 + else + charge_index = charge_index == module.charges.len ? 1 : charge_index+1 + + href_list["charge_type"] = module.charges[charge_index] + +/stat_rig_module/charge/CanUse() + if(module.charges && module.charges.len) + var/datum/rig_charge/charge = module.charges[module.charge_selected] + name = "[charge.display_name] ([charge.charges]C) - Change" + return 1 + return 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm new file mode 100644 index 00000000000..138978d2c06 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm @@ -0,0 +1,199 @@ +/* + * Contains + * /obj/item/rig_module/stealth_field + * /obj/item/rig_module/teleporter + * /obj/item/rig_module/fabricator/energy_net + * /obj/item/rig_module/self_destruct + */ + +/obj/item/rig_module/stealth_field + + name = "active camouflage module" + desc = "A robust hardsuit-integrated stealth module." + icon_state = "cloak" + + toggleable = 1 + disruptable = 1 + disruptive = 0 + + use_power_cost = 50 + active_power_cost = 10 + passive_power_cost = 0 + module_cooldown = 30 + + activate_string = "Enable Cloak" + deactivate_string = "Disable Cloak" + + interface_name = "integrated stealth system" + interface_desc = "An integrated active camouflage system." + + suit_overlay_active = "stealth_active" + suit_overlay_inactive = "stealth_inactive" + +/obj/item/rig_module/stealth_field/activate() + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + H << "You are now invisible to normal detection." + H.invisibility = INVISIBILITY_LEVEL_TWO + + anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null) + + H.visible_message("[H.name] vanishes into thin air!",1) + +/obj/item/rig_module/stealth_field/deactivate() + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + H << "You are now visible." + H.invisibility = 0 + + anim(get_turf(H), H,'icons/mob/mob.dmi',,"uncloak",,H.dir) + anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null) + + for(var/mob/O in oviewers(H)) + O.show_message("[H.name] appears from thin air!",1) + playsound(get_turf(H), 'sound/effects/stealthoff.ogg', 75, 1) + + +/obj/item/rig_module/teleporter + + name = "teleportation module" + desc = "A complex, sleek-looking, hardsuit-integrated teleportation module." + icon_state = "teleporter" + use_power_cost = 40 + redundant = 1 + usable = 1 + selectable = 1 + + engage_string = "Emergency Leap" + + interface_name = "VOID-shift phase projector" + interface_desc = "An advanced teleportation system. It is capable of pinpoint precision or random leaps forward." + +/obj/item/rig_module/teleporter/proc/phase_in(var/mob/M,var/turf/T) + + if(!M || !T) + return + + holder.spark_system.start() + playsound(T, 'sound/effects/phasein.ogg', 25, 1) + playsound(T, 'sound/effects/sparks2.ogg', 50, 1) + anim(T,M,'icons/mob/mob.dmi',,"phasein",,M.dir) + +/obj/item/rig_module/teleporter/proc/phase_out(var/mob/M,var/turf/T) + + if(!M || !T) + return + + playsound(T, "sparks", 50, 1) + anim(T,M,'icons/mob/mob.dmi',,"phaseout",,M.dir) + +/obj/item/rig_module/teleporter/engage(var/atom/target, var/notify_ai) + + if(!..()) return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!istype(H.loc, /turf)) + H << "You cannot teleport out of your current location." + return 0 + + var/turf/T + if(target) + T = get_turf(target) + else + T = get_teleport_loc(get_turf(H), H, rand(5, 9)) + + /*if(!T || T.density) + H << "You cannot teleport into solid walls." + return 0*///Who the fuck cares? Ninjas in walls are cool. + + if(T.z in config.admin_levels) + H << "You cannot use your teleporter on this Z-level." + return 0 + + phase_out(H,get_turf(H)) + H.forceMove(T) + phase_in(H,get_turf(H)) + + for(var/obj/item/weapon/grab/G in H.contents) + if(G.affecting) + phase_out(G.affecting,get_turf(G.affecting)) + G.affecting.forceMove(locate(T.x+rand(-1,1),T.y+rand(-1,1),T.z)) + phase_in(G.affecting,get_turf(G.affecting)) + + return 1 + +/* +/obj/item/rig_module/fabricator/energy_net + + name = "net projector" + desc = "Some kind of complex energy projector with a hardsuit mount." + icon_state = "enet" + + interface_name = "energy net launcher" + interface_desc = "An advanced energy-patterning projector used to capture targets." + + engage_string = "Fabricate Net" + + fabrication_type = /obj/item/weapon/energy_net + use_power_cost = 70 + +/obj/item/rig_module/fabricator/energy_net/engage(atom/target) + + if(holder && holder.wearer) + if(..(target) && target) + holder.wearer.Beam(target,"n_beam",,10) + return 1 + return 0*/ + +/obj/item/rig_module/self_destruct + + name = "self-destruct module" + desc = "Oh my God, Captain. A bomb." + icon_state = "deadman" + usable = 1 + active = 1 + permanent = 1 + + engage_string = "Detonate" + + interface_name = "dead man's switch" + interface_desc = "An integrated self-destruct module. When the wearer dies, so does the surrounding area. Do not press this button." + +/obj/item/rig_module/self_destruct/activate() + return + +/obj/item/rig_module/self_destruct/deactivate() + return + +/obj/item/rig_module/self_destruct/process() + + // Not being worn, leave it alone. + if(!holder || !holder.wearer || !holder.wearer.wear_suit == holder) + return 0 + + //OH SHIT. + if(holder.wearer.stat == 2) + engage() + +/obj/item/rig_module/self_destruct/engage() + explosion(get_turf(src), 1, 2, 4, 5) + if(holder && holder.wearer) + holder.wearer.unEquip(src) + qdel(holder) + qdel(src) + +/obj/item/rig_module/self_destruct/small/engage() + explosion(get_turf(src), 0, 0, 3, 4) + if(holder && holder.wearer) + holder.wearer.unEquip(src) + qdel(holder) + qdel(src) diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm new file mode 100644 index 00000000000..e0361326316 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -0,0 +1,489 @@ +/* Contains: + * /obj/item/rig_module/device + * /obj/item/rig_module/device/plasmacutter + * /obj/item/rig_module/device/healthscanner + * /obj/item/rig_module/device/drill + * /obj/item/rig_module/device/orescanner + * /obj/item/rig_module/device/rcd + * /obj/item/rig_module/device/anomaly_scanner + * /obj/item/rig_module/maneuvering_jets + * /obj/item/rig_module/foam_sprayer + * /obj/item/rig_module/device/broadcaster + * /obj/item/rig_module/chem_dispenser + * /obj/item/rig_module/chem_dispenser/injector + * /obj/item/rig_module/voice + * /obj/item/rig_module/device/paperdispenser + * /obj/item/rig_module/device/pen + * /obj/item/rig_module/device/stamp + */ + +/obj/item/rig_module/device + name = "mounted device" + desc = "Some kind of hardsuit mount." + usable = 0 + selectable = 1 + toggleable = 0 + disruptive = 0 + + var/device_type + var/obj/item/device + +/obj/item/rig_module/device/plasmacutter + name = "hardsuit plasma cutter" + desc = "A lethal-looking industrial cutter." + icon_state = "plasmacutter" + interface_name = "plasma cutter" + interface_desc = "A self-sustaining plasma arc capable of cutting through walls." + suit_overlay_active = "plasmacutter" + suit_overlay_inactive = "plasmacutter" + + device_type = /obj/item/weapon/gun/energy/plasmacutter + +/obj/item/rig_module/device/healthscanner + name = "health scanner module" + desc = "A hardsuit-mounted health scanner." + icon_state = "scanner" + interface_name = "health scanner" + interface_desc = "Shows an informative health readout when used on a subject." + + device_type = /obj/item/device/healthanalyzer + +/obj/item/rig_module/device/drill + name = "hardsuit drill mount" + desc = "A very heavy diamond-tipped drill." + icon_state = "drill" + interface_name = "mounted drill" + interface_desc = "A diamond-tipped industrial drill." + suit_overlay_active = "mounted-drill" + suit_overlay_inactive = "mounted-drill" + + device_type = /obj/item/weapon/pickaxe/diamonddrill + +/obj/item/rig_module/device/anomaly_scanner + name = "hardsuit anomaly scanner" + desc = "You think it's called an Elder Sarsparilla or something." + icon_state = "eldersasparilla" + interface_name = "Alden-Saraspova counter" + interface_desc = "An exotic particle detector commonly used by xenoarchaeologists." + engage_string = "Begin Scan" + usable = 1 + selectable = 0 + device_type = /obj/item/device/ano_scanner + +/obj/item/rig_module/device/orescanner + name = "ore scanner module" + desc = "A clunky old ore scanner." + icon_state = "scanner" + interface_name = "ore detector" + interface_desc = "A sonar system for detecting large masses of ore." + engage_string = "Begin Scan" + usable = 1 + selectable = 0 + device_type = /obj/item/device/mining_scanner +/* +/obj/item/rig_module/device/rcd + name = "RCD mount" + desc = "A cell-powered rapid construction device for a hardsuit." + icon_state = "rcd" + interface_name = "mounted RCD" + interface_desc = "A device for building or removing walls. Cell-powered." + usable = 1 + engage_string = "Configure RCD" + + device_type = /obj/item/weapon/rcd/mounted +*/ +/obj/item/rig_module/device/New() + ..() + if(device_type) device = new device_type(src) + +/obj/item/rig_module/device/engage(atom/target) + if(!..() || !device) + return 0 + + if(!target) + device.attack_self(holder.wearer) + return 1 + + var/turf/T = get_turf(target) + if(istype(T) && !T.Adjacent(get_turf(src))) + return 0 + + var/resolved = target.attackby(device,holder.wearer) + if(!resolved && device && target) + device.afterattack(target,holder.wearer,1) + return 1 + + + +/obj/item/rig_module/chem_dispenser + name = "mounted chemical dispenser" + desc = "A complex web of tubing and needles suitable for hardsuit use." + icon_state = "injector" + usable = 1 + selectable = 0 + toggleable = 0 + disruptive = 0 + + engage_string = "Inject" + + interface_name = "integrated chemical dispenser" + interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream." + + charges = list( + list("tricordrazine", "tricordrazine", 0, 80), + list("tramadol", "tramadol", 0, 80), + list("dexalin plus", "dexalinp", 0, 80), + list("antibiotics", "spaceacillin", 0, 80), + list("antitoxins", "anti_toxin", 0, 80), + list("nutrients", "nutriment", 0, 80), + list("hyronalin", "hyronalin", 0, 80), + list("radium", "radium", 0, 80) + ) + + var/max_reagent_volume = 80 //Used when refilling. + +/obj/item/rig_module/chem_dispenser/ninja + interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible." + + //just over a syringe worth of each. Want more? Go refill. Gives the ninja another reason to have to show their face. + charges = list( + list("tricordrazine", "tricordrazine", 0, 20), + list("tramadol", "tramadol", 0, 20), + list("dexalin plus", "dexalinp", 0, 20), + list("antibiotics", "spaceacillin", 0, 20), + list("antitoxins", "anti_toxin", 0, 20), + list("nutrients", "nutriment", 0, 80), + list("hyronalin", "hyronalin", 0, 20), + list("radium", "radium", 0, 20) + ) + +/obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user) + + if(!input_item.is_open_container()) + return 0 + + if(!input_item.reagents || !input_item.reagents.total_volume) + user << "\The [input_item] is empty." + return 0 + + // Magical chemical filtration system, do not question it. + var/total_transferred = 0 + for(var/datum/reagent/R in input_item.reagents.reagent_list) + for(var/chargetype in charges) + var/datum/rig_charge/charge = charges[chargetype] + if(charge.display_name == R.id) + + var/chems_to_transfer = R.volume + + if((charge.charges + chems_to_transfer) > max_reagent_volume) + chems_to_transfer = max_reagent_volume - charge.charges + + charge.charges += chems_to_transfer + input_item.reagents.remove_reagent(R.id, chems_to_transfer) + total_transferred += chems_to_transfer + + break + + if(total_transferred) + user << "You transfer [total_transferred] units into the suit reservoir." + else + user << "None of the reagents seem suitable." + return 1 + +/obj/item/rig_module/chem_dispenser/engage(atom/target) + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!charge_selected) + H << "You have not selected a chemical type." + return 0 + + var/datum/rig_charge/charge = charges[charge_selected] + + if(!charge) + return 0 + + var/chems_to_use = 10 + if(charge.charges <= 0) + H << "Insufficient chems!" + return 0 + else if(charge.charges < chems_to_use) + chems_to_use = charge.charges + + var/mob/living/carbon/target_mob + if(target) + if(istype(target,/mob/living/carbon)) + target_mob = target + else + return 0 + else + target_mob = H + + if(target_mob != H) + H << "You inject [target_mob] with [chems_to_use] unit\s of [charge.display_name]." + target_mob << "You feel a rushing in your veins as [chems_to_use] unit\s of [charge.display_name] [chems_to_use == 1 ? "is" : "are"] injected." + target_mob.reagents.add_reagent(charge.display_name, chems_to_use) + + charge.charges -= chems_to_use + if(charge.charges < 0) charge.charges = 0 + + return 1 + +/obj/item/rig_module/chem_dispenser/combat + + name = "combat chemical injector" + desc = "A complex web of tubing and needles suitable for hardsuit use." + + charges = list( + list("synaptizine", "synaptizine", 0, 30), + list("hyperzine", "hyperzine", 0, 30), + list("oxycodone", "oxycodone", 0, 30), + list("nutrients", "nutriment", 0, 80), + ) + + interface_name = "combat chem dispenser" + interface_desc = "Dispenses loaded chemicals directly into the bloodstream." + + +/obj/item/rig_module/chem_dispenser/injector + + name = "mounted chemical injector" + desc = "A complex web of tubing and a large needle suitable for hardsuit use." + usable = 0 + selectable = 1 + disruptive = 1 + + interface_name = "mounted chem injector" + interface_desc = "Dispenses loaded chemicals via an arm-mounted injector." + +/obj/item/rig_module/voice + + name = "hardsuit voice synthesiser" + desc = "A speaker box and sound processor." + icon_state = "megaphone" + usable = 1 + selectable = 0 + toggleable = 0 + disruptive = 0 + + engage_string = "Configure Synthesiser" + + interface_name = "voice synthesiser" + interface_desc = "A flexible and powerful voice modulator system." + + var/obj/item/voice_changer/voice_holder + +/obj/item/rig_module/voice/New() + ..() + voice_holder = new(src) + voice_holder.active = 0 + +/obj/item/rig_module/voice/installed() + ..() + holder.speech = src + +/obj/item/rig_module/voice/engage() + + if(!..()) + return 0 + + var/choice= input("Would you like to toggle the synthesiser or set the name?") as null|anything in list("Enable","Disable","Set Name") + + if(!choice) + return 0 + + switch(choice) + if("Enable") + active = 1 + voice_holder.active = 1 + usr << "You enable the speech synthesiser." + if("Disable") + active = 0 + voice_holder.active = 0 + usr << "You disable the speech synthesiser." + if("Set Name") + var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN) + if(!raw_choice) + return 0 + voice_holder.voice = raw_choice + usr << "You are now mimicking [voice_holder.voice]." + return 1 + +/obj/item/rig_module/maneuvering_jets + + name = "hardsuit maneuvering jets" + desc = "A compact gas thruster system for a hardsuit." + icon_state = "thrusters" + usable = 1 + toggleable = 1 + selectable = 0 + disruptive = 0 + + suit_overlay_active = "maneuvering_active" + suit_overlay_inactive = null //"maneuvering_inactive" + + engage_string = "Toggle Stabilizers" + activate_string = "Activate Thrusters" + deactivate_string = "Deactivate Thrusters" + + interface_name = "maneuvering jets" + interface_desc = "An inbuilt EVA maneuvering system that runs off the rig air supply." + + var/obj/item/weapon/tank/jetpack/rig/jets + +/obj/item/rig_module/maneuvering_jets/engage() + if(!..()) + return 0 + jets.toggle_rockets() + return 1 + +/obj/item/rig_module/maneuvering_jets/activate() + + if(active) + return 0 + + active = 1 + + spawn(1) + if(suit_overlay_active) + suit_overlay = suit_overlay_active + else + suit_overlay = null + holder.update_icon() + + if(!jets.on) + jets.toggle() + return 1 + +/obj/item/rig_module/maneuvering_jets/deactivate() + if(!..()) + return 0 + if(jets.on) + jets.toggle() + return 1 + +/obj/item/rig_module/maneuvering_jets/New() + ..() + jets = new(src) + +/obj/item/rig_module/maneuvering_jets/installed() + ..() + jets.holder = holder + jets.ion_trail.set_up(holder) + +/obj/item/rig_module/maneuvering_jets/removed() + ..() + jets.holder = null + jets.ion_trail.set_up(jets) + +/obj/item/rig_module/foam_sprayer + +/obj/item/rig_module/device/paperdispenser + name = "hardsuit paper dispenser" + desc = "Crisp sheets." + icon_state = "paper" + interface_name = "paper dispenser" + interface_desc = "Dispenses warm, clean, and crisp sheets of paper." + engage_string = "Dispense" + usable = 1 + selectable = 0 + device_type = /obj/item/weapon/paper_bin + +/obj/item/rig_module/device/paperdispenser/engage(atom/target) + + if(!..() || !device) + return 0 + + if(!target) + device.attack_hand(holder.wearer) + return 1 + +/obj/item/rig_module/device/pen + name = "mounted pen" + desc = "For mecha John Hancocks." + icon_state = "pen" + interface_name = "mounted pen" + interface_desc = "Signatures with style(tm)." + engage_string = "Change color" + usable = 1 + device_type = /obj/item/weapon/pen/multi + +/obj/item/rig_module/device/stamp + name = "mounted internal affairs stamp" + desc = "DENIED." + icon_state = "stamp" + interface_name = "mounted stamp" + interface_desc = "Leave your mark." + engage_string = "Toggle stamp type" + usable = 1 + var/iastamp + var/deniedstamp + +/obj/item/rig_module/device/stamp/New() + ..() + iastamp = new /obj/item/weapon/stamp/law(src) + deniedstamp = new /obj/item/weapon/stamp/denied(src) + device = iastamp + +/obj/item/rig_module/device/stamp/engage(atom/target) + if(!..() || !device) + return 0 + + if(!target) + if(device == iastamp) + device = deniedstamp + holder.wearer << "Switched to denied stamp." + else if(device == deniedstamp) + device = iastamp + holder.wearer << "Switched to internal affairs stamp." + return 1 + +/obj/item/rig_module/welding_tank + name = "welding fuel tank" + desc = "A bluespace welding fuel storage tank for a rigsuit." + icon_state = "welding_tank" + interface_name = "mounted welding fuel tank" + interface_desc = "A minitaure fuel tank used for storage of welding fuel, built into a hardsuit." + engage_string = "Dispense fuel" + + var/max_fuel = 300 + +/obj/item/rig_module/welding_tank/New() + ..() + + create_reagents(max_fuel) + reagents.add_reagent("fuel", max_fuel) + +/obj/item/rig_module/welding_tank/engage(atom/target) + if(!..() || !reagents) + return 0 + + if(!target) + if(get_fuel() >= 0) + var/obj/item/weapon/weldingtool/W = holder.wearer.get_active_hand() + if(istype(W)) + fill_welder(W) + else + W = holder.wearer.get_inactive_hand() + if(istype(W)) + fill_welder(W) + else + holder.wearer << "Your welding tank is out of fuel!" + +/obj/item/rig_module/welding_tank/proc/fill_welder(var/obj/item/weapon/weldingtool/W) + if(!istype(W)) + return 0 + + if(reagents) + if(get_fuel() >= W.max_fuel) + reagents.trans_to(W, W.max_fuel) + else + reagents.trans_to(W, W.max_fuel) + holder.wearer << "You hear a faint dripping as your hardsuit welding tank completely empties." + W.update_icon() + +/obj/item/rig_module/welding_tank/proc/get_fuel() + return reagents.get_reagent_amount("fuel") \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm new file mode 100644 index 00000000000..aa106eda02a --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -0,0 +1,191 @@ +/* + * Contains + * /obj/item/rig_module/vision + * /obj/item/rig_module/vision/multi + * /obj/item/rig_module/vision/meson + * /obj/item/rig_module/vision/thermal + * /obj/item/rig_module/vision/nvg + * /obj/item/rig_module/vision/medhud + * /obj/item/rig_module/vision/sechud + */ + +/datum/rig_vision + var/mode + var/obj/item/clothing/glasses/glasses + +/datum/rig_vision/nvg + mode = "night vision" +/datum/rig_vision/nvg/New() + glasses = new /obj/item/clothing/glasses/night + +/datum/rig_vision/thermal + mode = "thermal scanner" +/datum/rig_vision/thermal/New() + glasses = new /obj/item/clothing/glasses/thermal + +/datum/rig_vision/meson + mode = "meson scanner" +/datum/rig_vision/meson/New() + glasses = new /obj/item/clothing/glasses/meson + +/datum/rig_vision/sechud + mode = "security HUD" +/datum/rig_vision/sechud/New() + glasses = new /obj/item/clothing/glasses/hud/security + +/datum/rig_vision/medhud + mode = "medical HUD" +/datum/rig_vision/medhud/New() + glasses = new /obj/item/clothing/glasses/hud/health + +/obj/item/rig_module/vision + + name = "hardsuit visor" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "optics" + + interface_name = "optical scanners" + interface_desc = "An integrated multi-mode vision system." + + usable = 1 + toggleable = 1 + disruptive = 0 + + engage_string = "Cycle Visor Mode" + activate_string = "Enable Visor" + deactivate_string = "Disable Visor" + + var/datum/rig_vision/vision + var/list/vision_modes = list( + /datum/rig_vision/nvg, + /datum/rig_vision/thermal, + /datum/rig_vision/meson + ) + + var/vision_index + +/obj/item/rig_module/vision/multi + + name = "hardsuit optical package" + desc = "A complete visor system of optical scanners and vision modes." + icon_state = "fulloptics" + + + interface_name = "multi optical visor" + interface_desc = "An integrated multi-mode vision system." + + vision_modes = list(/datum/rig_vision/meson, + /datum/rig_vision/nvg, + /datum/rig_vision/thermal, + /datum/rig_vision/sechud, + /datum/rig_vision/medhud) + +/obj/item/rig_module/vision/meson + + name = "hardsuit meson scanner" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "meson" + + usable = 0 + + interface_name = "meson scanner" + interface_desc = "An integrated meson scanner." + + vision_modes = list(/datum/rig_vision/meson) + +/obj/item/rig_module/vision/thermal + + name = "hardsuit thermal scanner" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "thermal" + + usable = 0 + + interface_name = "thermal scanner" + interface_desc = "An integrated thermal scanner." + + vision_modes = list(/datum/rig_vision/thermal) + +/obj/item/rig_module/vision/nvg + + name = "hardsuit night vision interface" + desc = "A multi input night vision system for a hardsuit." + icon_state = "night" + + usable = 0 + + interface_name = "night vision interface" + interface_desc = "An integrated night vision system." + + vision_modes = list(/datum/rig_vision/nvg) + +/obj/item/rig_module/vision/sechud + + name = "hardsuit security hud" + desc = "A simple tactical information system for a hardsuit." + icon_state = "securityhud" + + usable = 0 + + interface_name = "security HUD" + interface_desc = "An integrated security heads up display." + + vision_modes = list(/datum/rig_vision/sechud) + +/obj/item/rig_module/vision/medhud + + name = "hardsuit medical hud" + desc = "A simple medical status indicator for a hardsuit." + icon_state = "healthhud" + + usable = 0 + + interface_name = "medical HUD" + interface_desc = "An integrated medical heads up display." + + vision_modes = list(/datum/rig_vision/medhud) + + +// There should only ever be one vision module installed in a suit. +/obj/item/rig_module/vision/installed() + ..() + holder.visor = src + +/obj/item/rig_module/vision/engage() + + var/starting_up = !active + + if(!..() || !vision_modes) + return 0 + + // Don't cycle if this engage() is being called by activate(). + if(starting_up) + holder.wearer << "You activate your visual sensors." + return 1 + + if(vision_modes.len > 1) + vision_index++ + if(vision_index > vision_modes.len) + vision_index = 1 + vision = vision_modes[vision_index] + + holder.wearer << "You cycle your sensors to [vision.mode] mode." + else + holder.wearer << "Your sensors only have one mode." + return 1 + +/obj/item/rig_module/vision/New() + ..() + + if(!vision_modes) + return + + vision_index = 1 + var/list/processed_vision = list() + + for(var/vision_mode in vision_modes) + var/datum/rig_vision/vision_datum = new vision_mode + if(!vision) vision = vision_datum + processed_vision += vision_datum + + vision_modes = processed_vision \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm new file mode 100644 index 00000000000..423154b1c4b --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -0,0 +1,895 @@ +#define ONLY_DEPLOY 1 +#define ONLY_RETRACT 2 +#define SEAL_DELAY 30 + +/* + * Defines the behavior of hardsuits/rigs/power armour. + */ + +/obj/item/weapon/rig + + name = "hardsuit control module" + icon = 'icons/obj/rig_modules.dmi' + desc = "A back-mounted hardsuit deployment and control mechanism." + slot_flags = SLOT_BACK + req_one_access = list() + req_access = list() + w_class = 4 + + // These values are passed on to all component pieces. + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + siemens_coefficient = 0.2 + permeability_coefficient = 0.1 + unacidable = 1 + + var/interface_path = "hardsuit.tmpl" + var/ai_interface_path = "hardsuit.tmpl" + var/interface_title = "Hardsuit Controller" + var/wearer_move_delay //Used for AI moving. + var/ai_controlled_move_delay = 10 + + // Keeps track of what this rig should spawn with. + var/suit_type = "hardsuit" + var/list/initial_modules + var/chest_type = /obj/item/clothing/suit/space/new_rig + var/helm_type = /obj/item/clothing/head/helmet/space/new_rig + var/boot_type = /obj/item/clothing/shoes/magboots/rig + var/glove_type = /obj/item/clothing/gloves/rig + var/cell_type = /obj/item/weapon/stock_parts/cell/high + var/air_type = /obj/item/weapon/tank/oxygen + + //Component/device holders. + var/obj/item/weapon/tank/air_supply // Air tank, if any. + var/obj/item/clothing/shoes/boots = null // Deployable boots, if any. + var/obj/item/clothing/suit/space/new_rig/chest // Deployable chestpiece, if any. + var/obj/item/clothing/head/helmet/space/new_rig/helmet = null // Deployable helmet, if any. + var/obj/item/clothing/gloves/rig/gloves = null // Deployable gauntlets, if any. + var/obj/item/weapon/stock_parts/cell/cell // Power supply, if any. + var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click) + var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time. + var/obj/item/rig_module/voice/speech // As above. + var/mob/living/carbon/human/wearer // The person currently wearing the rig. + var/image/mob_icon // Holder for on-mob icon. + var/list/installed_modules = list() // Power consumption/use bookkeeping. + + // Rig status vars. + var/open = 0 // Access panel status. + var/locked = 1 // Lock status. + var/subverted = 0 + var/interface_locked = 0 + var/control_overridden = 0 + var/ai_override_enabled = 0 + var/security_check_enabled = 1 + var/malfunctioning = 0 + var/malfunction_delay = 0 + var/electrified = 0 + var/locked_down = 0 + + var/seal_delay = SEAL_DELAY + var/sealing // Keeps track of seal status independantly of NODROP. + var/offline = 1 // Should we be applying suit maluses? + var/offline_slowdown = 3 // If the suit is deployed and unpowered, it sets slowdown to this. + var/vision_restriction + var/offline_vision_restriction = 1 // 0 - none, 1 - welder vision, 2 - blind. Maybe move this to helmets. + var/airtight = 1 //If set, will adjust AIRTIGHT and STOPSPRESSUREDMAGE flags on components. Otherwise it should leave them untouched. + + var/emp_protection = 0 + + // Wiring! How exciting. + var/datum/wires/rig/wires + var/datum/effect/effect/system/spark_spread/spark_system + +/obj/item/weapon/rig/examine() + usr << "This is \icon[src][src.name]." + usr << "[src.desc]" + if(wearer) + for(var/obj/item/piece in list(helmet,gloves,chest,boots)) + if(!piece || piece.loc != wearer) + continue + usr << "\icon[piece] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed." + + if(src.loc == usr) + usr << "The maintenance panel is [open ? "open" : "closed"]." + usr << "Hardsuit systems are [offline ? "offline" : "online"]." + +/obj/item/weapon/rig/New() + ..() + + item_state = icon_state + wires = new(src) + + if((!req_access || !req_access.len) && (!req_one_access || !req_one_access.len)) + locked = 0 + + spark_system = new() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + processing_objects |= src + + if(initial_modules && initial_modules.len) + for(var/path in initial_modules) + var/obj/item/rig_module/module = new path(src) + installed_modules += module + module.installed(src) + + // Create and initialize our various segments. + if(cell_type) + cell = new cell_type(src) + if(air_type) + air_supply = new air_type(src) + if(glove_type) + gloves = new glove_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_gauntlets + if(helm_type) + helmet = new helm_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_helmet + if(boot_type) + boots = new boot_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_boots + if(chest_type) + chest = new chest_type(src) + if(allowed) + chest.allowed = allowed + chest.slowdown = offline_slowdown + verbs |= /obj/item/weapon/rig/proc/toggle_chest + + for(var/obj/item/piece in list(gloves,helmet,boots,chest)) + if(!istype(piece)) + continue + piece.name = "[suit_type] [initial(piece.name)]" + piece.desc = "It seems to be part of a [src.name]." + piece.icon_state = "[initial(icon_state)]" + piece.min_cold_protection_temperature = min_cold_protection_temperature + piece.max_heat_protection_temperature = max_heat_protection_temperature + if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation. + piece.siemens_coefficient = siemens_coefficient + piece.permeability_coefficient = permeability_coefficient + piece.unacidable = unacidable + if(islist(armor)) + var/list/L = armor + piece.armor = L.Copy() + + update_icon(1) + +/obj/item/weapon/rig/Destroy() + for(var/obj/item/piece in list(gloves,boots,helmet,chest)) + var/mob/living/M = piece.loc + if(istype(M)) + M.unEquip(piece) + qdel(piece) + processing_objects -= src + qdel(wires) + wires = null + qdel(spark_system) + spark_system = null + return ..() + +/obj/item/weapon/rig/proc/suit_is_deployed() + if(!istype(wearer) || src.loc != wearer || wearer.back != src) + return 0 + if(helm_type && !(helmet && wearer.head == helmet)) + return 0 + if(glove_type && !(gloves && wearer.gloves == gloves)) + return 0 + if(boot_type && !(boots && wearer.shoes == boots)) + return 0 + if(chest_type && !(chest && wearer.wear_suit == chest)) + return 0 + return 1 + +/obj/item/weapon/rig/proc/reset() + offline = 2 + flags &= ~NODROP + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(!piece) continue + piece.icon_state = "[initial(icon_state)]" + if(airtight) + piece.flags &= ~(STOPSPRESSUREDMAGE|AIRTIGHT) + update_icon(1) + +/obj/item/weapon/rig/proc/toggle_seals(var/mob/living/carbon/human/M, var/mob/living/user, var/instant) + + if(sealing) return + + if(!check_power_cost(M, 1)) //costs tiny amount of power to unseal/seal + return 0 + + deploy(M, instant) + + var/seal_target = (flags & NODROP) + var/failed_to_seal + + flags |= NODROP // No removing the suit while unsealing. + sealing = 1 + + if(!seal_target && !suit_is_deployed()) + M.visible_message("[M]'s suit flashes an error light.","Your suit flashes an error light. It can't function properly without being fully deployed.") + failed_to_seal = 1 + + if(!failed_to_seal) + + if(!instant) + M.visible_message("[M]'s suit emits a quiet hum as it begins to adjust its seals.","With a quiet hum, the suit begins running checks and adjusting components.") + if(seal_delay && !do_after(user, seal_delay, target = M)) + if(user) user << "You must remain still while the suit is adjusting the components." + failed_to_seal = 1 + + if(!M) + failed_to_seal = 1 + else + for(var/list/piece_data in list(list(M.shoes,boots,"boots",boot_type),list(M.gloves,gloves,"gloves",glove_type),list(M.head,helmet,"helmet",helm_type),list(M.wear_suit,chest,"chest",chest_type))) + + var/obj/item/piece = piece_data[1] + var/obj/item/compare_piece = piece_data[2] + var/msg_type = piece_data[3] + var/piece_type = piece_data[4] + + if(!piece || !piece_type) + continue + + if(!istype(M) || !istype(piece) || !istype(compare_piece) || !msg_type) + if(M) M << "You must remain still while the suit is adjusting the components." + failed_to_seal = 1 + break + + if(!failed_to_seal && M.back == src && piece == compare_piece) + + if(seal_delay && !instant && !do_after(user, seal_delay, needhand = 0, target = M)) + failed_to_seal = 1 + + piece.icon_state = "[initial(icon_state)][!seal_target ? "_sealed" : ""]" + switch(msg_type) + if("boots") + M << "\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"]." + M.update_inv_shoes() + if("gloves") + M << "\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"]." + M.update_inv_gloves() + if("chest") + M << "\The [piece] [!seal_target ? "cinches tight again your chest" : "releases your chest"]." + M.update_inv_wear_suit() + if("helmet") + M << "\The [piece] hisses [!seal_target ? "closed" : "open"]." + M.update_inv_head() + if(helmet) + helmet.update_light(wearer) + + //sealed pieces become airtight, protecting against diseases + if (!seal_target) + piece.armor["bio"] = 100 + else + piece.armor["bio"] = src.armor["bio"] + + else + failed_to_seal = 1 + + if((M && !(istype(M) && M.back == src) && !istype(M,/mob/living/silicon)) || (!seal_target && !suit_is_deployed())) + failed_to_seal = 1 + + sealing = null + + if(failed_to_seal) + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(!piece) continue + piece.icon_state = "[initial(icon_state)][!seal_target ? "" : "_sealed"]" + if(seal_target) + flags |= NODROP + else + flags &= ~NODROP + if(airtight) + update_component_sealed() + update_icon(1) + return 0 + + // Success! + if(seal_target) + flags &= ~NODROP + else + flags |= NODROP + M << "Your entire suit [!(flags & NODROP) ? "loosens as the components relax" : "tightens around you as the components lock into place"]." + + if(!(flags & NODROP)) + for(var/obj/item/rig_module/module in installed_modules) + module.deactivate() + if(airtight) + update_component_sealed() + update_icon(1) + +/obj/item/weapon/rig/proc/update_component_sealed() + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(!(flags & NODROP)) + piece.flags &= ~STOPSPRESSUREDMAGE + piece.flags &= ~AIRTIGHT + else + piece.flags |= STOPSPRESSUREDMAGE + piece.flags |= AIRTIGHT + +/obj/item/weapon/rig/process() + + // If we've lost any parts, grab them back. + var/mob/living/M + for(var/obj/item/piece in list(gloves,boots,helmet,chest)) + if(piece.loc != src && !(wearer && piece.loc == wearer)) + if(istype(piece.loc, /mob/living)) + M = piece.loc + M.unEquip(piece) + piece.forceMove(src) + + if(!istype(wearer) || loc != wearer || wearer.back != src || (!(flags & NODROP)) || !cell || cell.charge <= 0) + if(!cell || cell.charge <= 0) + if(electrified > 0) + electrified = 0 + if(!offline) + if(istype(wearer)) + if(flags & NODROP) + if (offline_slowdown < 3) + wearer << "Your suit beeps stridently, and suddenly goes dead." + else + wearer << "Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit." + if(offline_vision_restriction == 1) + wearer << "The suit optics flicker and die, leaving you with restricted vision." + else if(offline_vision_restriction == 2) + wearer << "The suit optics drop out completely, drowning you in darkness." + if(!offline) + offline = 1 + else + if(offline) + offline = 0 + if(istype(wearer) && !wearer.wearing_rig) + wearer.wearing_rig = src + chest.slowdown = initial(slowdown) + + if(offline) + if(offline == 1) + for(var/obj/item/rig_module/module in installed_modules) + module.deactivate() + offline = 2 + chest.slowdown = offline_slowdown + return + + if(cell && cell.charge > 0 && electrified > 0) + electrified-- + + if(malfunction_delay > 0) + malfunction_delay-- + else if(malfunctioning) + malfunctioning-- + malfunction() + + for(var/obj/item/rig_module/module in installed_modules) + cell.use(module.process()*10) + +/obj/item/weapon/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai) + + if(!istype(user)) + return 0 + + var/fail_msg + + if(!user_is_ai) + var/mob/living/carbon/human/H = user + if(istype(H) && H.back != src) + fail_msg = "You must be wearing \the [src] to do this." + else if(user.incorporeal_move) + fail_msg = "You must be solid to do this." + if(sealing) + fail_msg = "The hardsuit is in the process of adjusting seals and cannot be activated." + else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat))) + fail_msg = "You are in no fit state to do that." + else if(!cell) + fail_msg = "There is no cell installed in the suit." + else if(cost && cell.charge < cost * 10) //TODO: Cellrate? + fail_msg = "Not enough stored power." + + if(fail_msg) + user << "[fail_msg]" + return 0 + + // This is largely for cancelling stealth and whatever. + if(mod && mod.disruptive) + for(var/obj/item/rig_module/module in (installed_modules - mod)) + if(module.active && module.disruptable) + module.deactivate() + + cell.use(cost*10) + return 1 + +/obj/item/weapon/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/nano_state = inventory_state) + if(!user) + return + + var/list/data = list() + + data["primarysystem"] = null + if(selected_module) + data["primarysystem"] = "[selected_module.interface_name]" + + data["ai"] = 0 + if(src.loc != user) + data["ai"] = 1 + + var/is_sealed = (flags & NODROP) //1 if NODROP, 0 if no-nodrop + data["seals"] = "[!is_sealed]" //1 if not NODROP (unsealed), 0 if NODROP (sealed) + data["sealing"] = "[src.sealing]" + data["helmet"] = (helmet ? "[helmet.name]" : "None.") + data["gauntlets"] = (gloves ? "[gloves.name]" : "None.") + data["boots"] = (boots ? "[boots.name]" : "None.") + data["chest"] = (chest ? "[chest.name]" : "None.") + + data["charge"] = cell ? round(cell.charge,1) : 0 + data["maxcharge"] = cell ? cell.maxcharge : 0 + data["chargestatus"] = cell ? Floor((cell.charge/cell.maxcharge)*50) : 0 + + data["emagged"] = subverted + data["coverlock"] = locked + data["interfacelock"] = interface_locked + data["aicontrol"] = control_overridden + data["aioverride"] = ai_override_enabled + data["securitycheck"] = security_check_enabled + data["malf"] = malfunction_delay + + + var/list/module_list = list() + var/i = 1 + for(var/obj/item/rig_module/module in installed_modules) + var/list/module_data = list( + "index" = i, + "name" = "[module.interface_name]", + "desc" = "[module.interface_desc]", + "can_use" = "[module.usable]", + "can_select" = "[module.selectable]", + "can_toggle" = "[module.toggleable]", + "is_active" = "[module.active]", + "engagecost" = module.use_power_cost*10, + "activecost" = module.active_power_cost*10, + "passivecost" = module.passive_power_cost*10, + "engagestring" = module.engage_string, + "activatestring" = module.activate_string, + "deactivatestring" = module.deactivate_string, + "damage" = module.damage + ) + + if(module.charges && module.charges.len) + + module_data["charges"] = list() + var/datum/rig_charge/selected = module.charges[module.charge_selected] + module_data["chargetype"] = selected ? "[selected.display_name]" : "none" + + for(var/chargetype in module.charges) + var/datum/rig_charge/charge = module.charges[chargetype] + module_data["charges"] += list(list("caption" = "[chargetype] ([charge.charges])", "index" = "[chargetype]")) + + module_list += list(module_data) + i++ + + if(module_list.len) + data["modules"] = module_list + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, ((src.loc != user) ? ai_interface_path : interface_path), interface_title, 480, 550, state = nano_state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/item/weapon/rig/update_icon(var/update_mob_icon) + + //TODO: Maybe consider a cache for this (use mob_icon as blank canvas, use suit icon overlay). + overlays.Cut() + if(!mob_icon || update_mob_icon) + var/species_icon = 'icons/mob/rig_back.dmi' + // Since setting mob_icon will override the species checks in + // update_inv_wear_suit(), handle species checks here. + if(wearer && sprite_sheets && sprite_sheets[wearer.get_species()]) + species_icon = sprite_sheets[wearer.get_species()] + mob_icon = image("icon" = species_icon, "icon_state" = "[icon_state]") + + if(installed_modules.len) + for(var/obj/item/rig_module/module in installed_modules) + if(module.suit_overlay) + chest.overlays += image("icon" = 'icons/mob/rig_modules.dmi', "icon_state" = "[module.suit_overlay]", "dir" = SOUTH) + + if(wearer) + wearer.update_inv_shoes() + wearer.update_inv_gloves() + wearer.update_inv_head() + wearer.update_inv_wear_suit() + wearer.update_inv_back() + return + +/obj/item/weapon/rig/proc/check_suit_access(var/mob/living/carbon/human/user) + + if(!security_check_enabled) + return 1 + + if(istype(user)) + if(malfunction_check(user)) + return 0 + if(user.back != src) + return 0 + else if(!src.allowed(user)) + user << "Unauthorized user. Access denied." + return 0 + + else if(!ai_override_enabled) + user << "Synthetic access disabled. Please consult hardware provider." + return 0 + + return 1 + +//TODO: Fix Topic vulnerabilities for malfunction and AI override. +/obj/item/weapon/rig/Topic(href,href_list) + if(!check_suit_access(usr)) + return 0 + + if(href_list["toggle_piece"]) + if(ishuman(usr) && (usr.stat || usr.stunned || usr.lying)) + return 0 + toggle_piece(href_list["toggle_piece"], wearer, user = usr) + else if(href_list["toggle_seals"]) + toggle_seals(wearer, usr) + else if(href_list["interact_module"]) + + var/module_index = text2num(href_list["interact_module"]) + + if(module_index > 0 && module_index <= installed_modules.len) + var/obj/item/rig_module/module = installed_modules[module_index] + switch(href_list["module_mode"]) + if("activate") + module.activate() + if("deactivate") + module.deactivate() + if("engage") + module.engage() + if("select") + selected_module = module + if("select_charge_type") + module.charge_selected = href_list["charge_type"] + else if(href_list["toggle_ai_control"]) + ai_override_enabled = !ai_override_enabled + notify_ai("Synthetic suit control has been [ai_override_enabled ? "enabled" : "disabled"].") + else if(href_list["toggle_suit_lock"]) + locked = !locked + + usr.set_machine(src) + src.add_fingerprint(usr) + return 0 + +/obj/item/weapon/rig/proc/notify_ai(var/message) + if(!message || !installed_modules || !installed_modules.len) + return + for(var/obj/item/rig_module/module in installed_modules) + for(var/mob/living/silicon/ai/ai in module.contents) + if(ai && ai.client && !ai.stat) + ai << "[message]" + +/obj/item/weapon/rig/equipped(mob/living/carbon/human/M) + ..() + + if(seal_delay > 0 && istype(M) && M.back == src) + M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") + if(!do_after(M, seal_delay, target = M)) + if(M && M.back == src) + M.back = null + M.unEquip(src) + src.forceMove(get_turf(src)) + return + + if(istype(M) && M.back == src) + M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") + wearer = M + wearer.wearing_rig = src + update_icon() + +/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode, var/force, var/mob/living/user) + + if(!force) //forces the hardsuit to ignore most checks, as the suit being dropped should retract all pieces regardless of power status + if(sealing || !cell || !cell.charge) //or else you have issues with shit getting stuck + return + + if(!istype(wearer) || !wearer.back == src) + return + + if(usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) // If the usr isn't wearing the suit it's probably an AI. + return + + var/obj/item/check_slot + var/equip_to + var/obj/item/use_obj + + if(!H) + return + + switch(piece) + if("helmet") + equip_to = slot_head + use_obj = helmet + check_slot = H.head + if("gauntlets") + equip_to = slot_gloves + use_obj = gloves + check_slot = H.gloves + if("boots") + equip_to = slot_shoes + use_obj = boots + check_slot = H.shoes + if("chest") + equip_to = slot_wear_suit + use_obj = chest + check_slot = H.wear_suit + + if(use_obj) + if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) + + var/mob/living/carbon/human/holder + + if(use_obj) + holder = use_obj.loc + if(istype(holder)) + if(use_obj && check_slot == use_obj) + H << "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly." + use_obj.flags &= ~NODROP + holder.unEquip(use_obj, 1) + use_obj.forceMove(src) + + else if (deploy_mode != ONLY_RETRACT) + if(check_slot) + if(check_slot != use_obj) + H << "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way." + return + else + use_obj.forceMove(H) + use_obj.flags &= ~NODROP + if(!H.equip_to_slot_if_possible(use_obj, equip_to, 0, 1)) + use_obj.forceMove(src) + else + H << "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly." + use_obj.flags |= NODROP + + if(piece == "helmet" && helmet) + helmet.update_light(H) + +/obj/item/weapon/rig/proc/deploy(mob/M,var/sealed) + + var/mob/living/carbon/human/H = M + + if(!H || !istype(H)) return + + if(H.back != src) + return + + if(sealed) + if(H.head) + var/obj/item/garbage = H.head + H.unEquip(garbage) + H.head = null + qdel(garbage) + + if(H.gloves) + var/obj/item/garbage = H.gloves + H.unEquip(garbage) + H.gloves = null + qdel(garbage) + + if(H.shoes) + var/obj/item/garbage = H.shoes + H.unEquip(garbage) + H.shoes = null + qdel(garbage) + + if(H.wear_suit) + var/obj/item/garbage = H.wear_suit + H.unEquip(garbage) + H.wear_suit = null + qdel(garbage) + + for(var/piece in list("helmet","gauntlets","chest","boots")) + toggle_piece(piece, H, ONLY_DEPLOY) + +/obj/item/weapon/rig/dropped(var/mob/user) + ..() + for(var/piece in list("helmet","gauntlets","chest","boots")) + toggle_piece(piece, user, ONLY_RETRACT, 1) + if(wearer) + wearer.wearing_rig = null + wearer = null + +//Todo +/obj/item/weapon/rig/proc/malfunction() + return 0 + +/obj/item/weapon/rig/emp_act(severity_class) + //set malfunctioning + if(emp_protection < 30) //for ninjas, really. + malfunctioning += 10 + if(malfunction_delay <= 0) + malfunction_delay = max(malfunction_delay, round(30/severity_class)) + + //drain some charge + if(cell) cell.emp_act(severity_class + 15) + + //possibly damage some modules + take_hit((100/severity_class), "electrical pulse", 1) + +/obj/item/weapon/rig/proc/shock(mob/user) + if (electrocute_mob(user, cell, src)) //electrocute_mob() handles removing charge from the cell, no need to do that here. + spark_system.start() + if(user.stunned) + return 1 + return 0 + +/obj/item/weapon/rig/proc/take_hit(damage, source, is_emp=0) + + if(!installed_modules.len) + return + + var/chance + if(!is_emp) + chance = 2*max(0, damage - (chest? chest.breach_threshold : 0)) + else + //Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average. + //that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules + chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1) + + if(!prob(chance)) + return + + //deal addition damage to already damaged module first. + //This way the chances of a module being disabled aren't so remote. + var/list/valid_modules = list() + var/list/damaged_modules = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.damage < 2) + valid_modules |= module + if(module.damage > 0) + damaged_modules |= module + + var/obj/item/rig_module/dam_module = null + if(damaged_modules.len) + dam_module = pick(damaged_modules) + else if(valid_modules.len) + dam_module = pick(valid_modules) + + if(!dam_module) return + + dam_module.damage++ + + if(!source) + source = "hit" + + if(wearer) + if(dam_module.damage >= 2) + wearer << "The [source] has disabled your [dam_module.interface_name]!" + else + wearer << "The [source] has damaged your [dam_module.interface_name]!" + dam_module.deactivate() + +/obj/item/weapon/rig/proc/malfunction_check(var/mob/living/carbon/human/user) + if(malfunction_delay) + if(offline) + user << "The suit is completely unresponsive." + else + user << "ERROR: Hardware fault. Rebooting interface..." + return 1 + return 0 + +/obj/item/weapon/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0) + + if(check_for_ai) + if(!(locate(/obj/item/rig_module/ai_container) in contents)) + return 0 + var/found_ai + for(var/obj/item/rig_module/ai_container/module in contents) + if(module.damage >= 2) + continue + if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) + found_ai = 1 + break + if(!found_ai) + return 0 + + if(check_user_module) + if(!user || !user.loc || !user.loc.loc) + return 0 + var/obj/item/rig_module/ai_container/module = user.loc.loc + if(!istype(module) || module.damage >= 2) + user << "Your host module is unable to interface with the suit." + return 0 + + if(offline || !cell || !cell.charge || locked_down) + if(user) user << "Your host rig is unpowered and unresponsive." + return 0 + if(!wearer || wearer.back != src) + if(user) user << "Your host rig is not being worn." + return 0 + if(!wearer.stat && !control_overridden && !ai_override_enabled) + if(user) user << "You are locked out of the suit servo controller." + return 0 + return 1 + +/obj/item/weapon/rig/proc/force_rest(var/mob/user) + if(!ai_can_move_suit(user, check_user_module = 1)) + return + wearer.lay_down() + user << "\The [wearer] is now [wearer.resting ? "resting" : "getting up"]." + +/obj/item/weapon/rig/proc/forced_move(var/direction, var/mob/user) + + // Why is all this shit in client/Move()? Who knows? + if(world.time < wearer_move_delay) + return + + if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1)) + return + + //This is sota the goto stop mobs from moving var + if(wearer.notransform || !wearer.canmove) + return + + if(locate(/obj/effect/stop/, wearer.loc)) + for(var/obj/effect/stop/S in wearer.loc) + if(S.victim == wearer) + return + + if(!wearer.lastarea) + wearer.lastarea = get_area(wearer.loc) + + if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0)) + if(!wearer.Process_Spacemove(0)) + return 0 + + if(malfunctioning) + direction = pick(cardinal) + + // Inside an object, tell it we moved. + if(isobj(wearer.loc) || ismob(wearer.loc)) + var/atom/O = wearer.loc + return O.relaymove(wearer, direction) + + if(isturf(wearer.loc)) + if(wearer.restrained())//Why being pulled while cuffed prevents you from moving + for(var/mob/M in range(wearer, 1)) + if(M.pulling == wearer) + if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M)) + user << "Your host is restrained! They can't move!" + return 0 + else + M.stop_pulling() + + if(wearer.pinned.len) + src << "Your host is pinned to a wall by [wearer.pinned[1]]!" + return 0 + + // AIs are a bit slower than regular and ignore move intent. + wearer.last_move_intent = world.time + ai_controlled_move_delay + wearer_move_delay = world.time + ai_controlled_move_delay + + var/tickcomp = 0 + if(config.Tickcomp) + tickcomp = ((1/(world.tick_lag))*1.3) - 1.3 + wearer_move_delay += tickcomp + + if(wearer.buckled) //if we're buckled to something, tell it we moved. + return wearer.buckled.relaymove(wearer, direction) + + if(cell.use(200)) //Arbitrary, TODO + wearer.Move(get_step(get_turf(wearer),direction),direction) + +// This returns the rig if you are contained inside one, but not if you are wearing it +/atom/proc/get_rig() + if(loc) + return loc.get_rig() + return null + +/obj/item/weapon/rig/get_rig() + return src + +/mob/living/carbon/human/get_rig() + return back + +#undef ONLY_DEPLOY +#undef ONLY_RETRACT +#undef SEAL_DELAY diff --git a/code/modules/clothing/spacesuits/rig/rig_attackby.dm b/code/modules/clothing/spacesuits/rig/rig_attackby.dm new file mode 100644 index 00000000000..d00d4b87abf --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/rig_attackby.dm @@ -0,0 +1,196 @@ +/obj/item/weapon/rig/attackby(obj/item/W as obj, mob/user as mob) + + if(!istype(user,/mob/living)) return 0 + + if(electrified != 0) + if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here. + return + + // Pass repair items on to the chestpiece. + if(chest && (istype(W,/obj/item/stack) || istype(W, /obj/item/weapon/weldingtool))) + return chest.attackby(W,user) + + // Lock or unlock the access panel. + if(W.GetID()) + if(subverted) + locked = 0 + user << "It looks like the locking system has been shorted out." + return + + if((!req_access || !req_access.len) && (!req_one_access || !req_one_access.len)) + locked = 0 + user << "\The [src] doesn't seem to have a locking mechanism." + return + + if(security_check_enabled && !src.allowed(user)) + user << "Access denied." + return + + locked = !locked + user << "You [locked ? "lock" : "unlock"] \the [src] access panel." + return + + else if(istype(W,/obj/item/weapon/crowbar)) + + if(!open && locked) + user << "The access panel is locked shut." + return + + open = !open + user << "You [open ? "open" : "close"] the access panel." + return + + if(open) + + // Hacking. + if(istype(W,/obj/item/weapon/wirecutters) || istype(W,/obj/item/device/multitool)) + if(open) + wires.Interact(user) + else + user << "You can't reach the wiring." + return + // Air tank. + if(istype(W,/obj/item/weapon/tank)) //Todo, some kind of check for suits without integrated air supplies. + + if(air_supply) + user << "\The [src] already has a tank installed." + return + + user.unEquip(W) + air_supply = W + W.forceMove(src) + user << "You slot [W] into [src] and tighten the connecting valve." + return + + // Check if this is a hardsuit upgrade or a modification. + else if(istype(W,/obj/item/rig_module)) + + if(istype(src.loc,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = src.loc + if(H.back == src) + user << "You can't install a hardsuit module while the suit is being worn." + return 1 + + if(!installed_modules) installed_modules = list() + if(installed_modules.len) + for(var/obj/item/rig_module/installed_mod in installed_modules) + if(!installed_mod.redundant && istype(installed_mod,W)) + user << "The hardsuit already has a module of that class installed." + return 1 + + var/obj/item/rig_module/mod = W + user << "You begin installing \the [mod] into \the [src]." + if(!do_after(user,40, target = src)) + return + if(!user || !W) + return + user << "You install \the [mod] into \the [src]." + user.unEquip(mod) + installed_modules |= mod + mod.forceMove(src) + mod.installed(src) + update_icon() + return 1 + + else if(!cell && istype(W,/obj/item/weapon/stock_parts/cell)) + + user << "You jack \the [W] into \the [src]'s battery mount." + user.unEquip(W) + W.forceMove(src) + src.cell = W + return + + else if(istype(W,/obj/item/weapon/wrench)) + + if(!air_supply) + user << "There is not tank to remove." + return + + if(user.r_hand && user.l_hand) + air_supply.forceMove(get_turf(user)) + else + user.put_in_hands(air_supply) + user << "You detach and remove \the [air_supply]." + air_supply = null + return + + else if(istype(W,/obj/item/weapon/screwdriver)) + + var/list/current_mounts = list() + if(cell) current_mounts += "cell" + if(installed_modules && installed_modules.len) current_mounts += "system module" + + var/to_remove = input("Which would you like to modify?") as null|anything in current_mounts + if(!to_remove) + return + + if(istype(src.loc,/mob/living/carbon/human) && to_remove != "cell") + var/mob/living/carbon/human/H = src.loc + if(H.back == src) + user << "You can't remove an installed device while the hardsuit is being worn." + return + + switch(to_remove) + + if("cell") + + if(cell) + user << "You detatch \the [cell] from \the [src]'s battery mount." + for(var/obj/item/rig_module/module in installed_modules) + module.deactivate() + if(user.r_hand && user.l_hand) + cell.forceMove(get_turf(user)) + else + cell.forceMove(user.put_in_hands(cell)) + cell = null + else + user << "There is nothing loaded in that mount." + + if("system module") + + var/list/possible_removals = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.permanent) + continue + possible_removals[module.name] = module + + if(!possible_removals.len) + user << "There are no installed modules to remove." + return + + var/removal_choice = input("Which module would you like to remove?") as null|anything in possible_removals + if(!removal_choice) + return + + var/obj/item/rig_module/removed = possible_removals[removal_choice] + user << "You detatch \the [removed] from \the [src]." + removed.forceMove(get_turf(src)) + removed.removed() + installed_modules -= removed + update_icon() + + return + + // If we've gotten this far, all we have left to do before we pass off to root procs + // is check if any of the loaded modules want to use the item we've been given. + for(var/obj/item/rig_module/module in installed_modules) + if(module.accepts_item(W,user)) //Item is handled in this proc + return + ..() + + +/obj/item/weapon/rig/attack_hand(var/mob/user) + + if(electrified != 0) + if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here. + return + ..() + +/obj/item/weapon/rig/emag_act(var/remaining_charges, var/mob/user) + if(!subverted) + req_access.Cut() + req_one_access.Cut() + locked = 0 + subverted = 1 + user << "You short out the access protocol for the suit." + return 1 diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces.dm b/code/modules/clothing/spacesuits/rig/rig_pieces.dm new file mode 100644 index 00000000000..9f8747ac9d6 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/rig_pieces.dm @@ -0,0 +1,134 @@ +/* + * Defines the helmets, gloves and shoes for rigs. + */ + +/obj/item/clothing/head/helmet/space/new_rig + name = "helmet" + flags = HEADCOVERSEYES | BLOCKHAIR | HEADCOVERSMOUTH | THICKMATERIAL + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEMASK + body_parts_covered = HEAD + heat_protection = HEAD + cold_protection = HEAD + var/brightness_on = 4 + var/on = 0 + sprite_sheets = list( + "Tajara" = 'icons/mob/species/tajaran/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi' + ) + species_restricted = null + + flash_protect = 2 + +/obj/item/clothing/head/helmet/space/new_rig/attack_self(mob/user) + if(!isturf(user.loc)) + user << "You cannot turn the light on while in this [user.loc]." //To prevent some lighting anomalities. + return + toggle_light(user) + +/obj/item/clothing/head/helmet/space/new_rig/proc/toggle_light(mob/user) + on = !on + icon_state = "rig[on]-[_color]" + + if(on) + set_light(brightness_on) + else + set_light(0) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_head() + +/obj/item/clothing/gloves/rig + name = "gauntlets" + flags = THICKMATERIAL + body_parts_covered = HANDS + heat_protection = HANDS + cold_protection = HANDS + species_restricted = null + gender = PLURAL + +/obj/item/clothing/shoes/magboots/rig + name = "boots" + body_parts_covered = FEET + cold_protection = FEET + heat_protection = FEET + species_restricted = null + gender = PLURAL + +/obj/item/clothing/suit/space/new_rig + name = "chestpiece" + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT|HIDETAIL + flags = STOPSPRESSUREDMAGE | THICKMATERIAL | AIRTIGHT + slowdown = 0 + //will reach 10 breach damage after 25 laser carbine blasts, 3 revolver hits, or ~1 PTR hit. Completely immune to smg or sts hits. + breach_threshold = 38 + resilience = 0.2 + can_breach = 1 + sprite_sheets = list( + "Tajara" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi' + ) + +//TODO: move this to modules +/obj/item/clothing/head/helmet/space/new_rig/proc/prevent_track() + return 0 + +/obj/item/clothing/gloves/rig/Touch(var/atom/A, var/proximity) + + if(!A || !proximity) + return 0 + + var/mob/living/carbon/human/H = loc + if(!istype(H) || !H.back) + return 0 + + var/obj/item/weapon/rig/suit = H.back + if(!suit || !istype(suit) || !suit.installed_modules.len) + return 0 + + for(var/obj/item/rig_module/module in suit.installed_modules) + if(module.active && module.activates_on_touch) + if(module.engage(A)) + return 1 + + return 0 + +//Rig pieces for non-spacesuit based rigs + +/obj/item/clothing/head/lightrig + name = "mask" + body_parts_covered = HEAD + heat_protection = HEAD + cold_protection = HEAD + flags = THICKMATERIAL|AIRTIGHT + +/obj/item/clothing/suit/lightrig + name = "suit" + allowed = list(/obj/item/device/flashlight) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + flags = THICKMATERIAL + +/obj/item/clothing/shoes/lightrig + name = "boots" + body_parts_covered = FEET + cold_protection = FEET + heat_protection = FEET + species_restricted = null + gender = PLURAL + +/obj/item/clothing/gloves/lightrig + name = "gloves" + flags = THICKMATERIAL + body_parts_covered = HANDS + heat_protection = HANDS + cold_protection = HANDS + species_restricted = null + gender = PLURAL diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm new file mode 100644 index 00000000000..89a7d9dbcde --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm @@ -0,0 +1,297 @@ +// Interface for humans. +/obj/item/weapon/rig/verb/hardsuit_interface() + + set name = "Open Hardsuit Interface" + set desc = "Open the hardsuit system interface." + set category = "Hardsuit" + set src = usr.contents + + if(wearer && wearer.back == src) + ui_interact(usr) + +/obj/item/weapon/rig/verb/toggle_vision() + + set name = "Toggle Visor" + set desc = "Turns your rig visor off or on." + set category = "Hardsuit" + set src = usr.contents + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!check_power_cost(usr)) + return + + if(!(flags & NODROP)) + usr << "The suit is not active." + return + + if(!check_suit_access(usr)) + return + + if(!visor) + usr << "The hardsuit does not have a configurable visor." + return + + if(!visor.active) + visor.activate() + else + visor.deactivate() + +/obj/item/weapon/rig/proc/toggle_helmet() + + set name = "Toggle Helmet" + set desc = "Deploys or retracts your helmet." + set category = "Hardsuit" + set src = usr.contents + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!check_suit_access(usr)) + return + + toggle_piece("helmet",wearer) + +/obj/item/weapon/rig/proc/toggle_chest() + + set name = "Toggle Chestpiece" + set desc = "Deploys or retracts your chestpiece." + set category = "Hardsuit" + set src = usr.contents + + if(!check_suit_access(usr)) + return + + toggle_piece("chest",wearer) + +/obj/item/weapon/rig/proc/toggle_gauntlets() + + set name = "Toggle Gauntlets" + set desc = "Deploys or retracts your gauntlets." + set category = "Hardsuit" + set src = usr.contents + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!check_suit_access(usr)) + return + + toggle_piece("gauntlets",wearer) + +/obj/item/weapon/rig/proc/toggle_boots() + + set name = "Toggle Boots" + set desc = "Deploys or retracts your boots." + set category = "Hardsuit" + set src = usr.contents + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!check_suit_access(usr)) + return + + toggle_piece("boots",wearer) + +/obj/item/weapon/rig/verb/deploy_suit() + + set name = "Deploy Hardsuit" + set desc = "Deploys helmet, gloves and boots." + set category = "Hardsuit" + set src = usr.contents + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!check_suit_access(usr)) + return + + if(!check_power_cost(usr)) + return + + deploy(wearer) + +/obj/item/weapon/rig/verb/toggle_seals_verb() + + set name = "Toggle Hardsuit" + set desc = "Activates or deactivates your rig." + set category = "Hardsuit" + set src = usr.contents + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!check_suit_access(usr)) + return + + toggle_seals(wearer) + +/obj/item/weapon/rig/verb/switch_vision_mode() + + set name = "Switch Vision Mode" + set desc = "Switches between available vision modes." + set category = "Hardsuit" + set src = usr.contents + + if(malfunction_check(usr)) + return + + if(!check_power_cost(usr, 0, 0, 0, 0)) + return + + if(!(flags & NODROP)) + usr << "The suit is not active." + return + + if(!visor) + usr << "The hardsuit does not have a configurable visor." + return + + if(!visor.active) + visor.activate() + + if(!visor.active) + usr << "The visor is suffering a hardware fault and cannot be configured." + return + + visor.engage() + +/obj/item/weapon/rig/verb/alter_voice() + + set name = "Configure Voice Synthesiser" + set desc = "Toggles or configures your voice synthesizer." + set category = "Hardsuit" + set src = usr.contents + + if(malfunction_check(usr)) + return + + if(!(flags & NODROP)) + usr << "The suit is not active." + return + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!speech) + usr << "The hardsuit does not have a speech synthesiser." + return + + speech.engage() + +/obj/item/weapon/rig/verb/select_module() + + set name = "Select Module" + set desc = "Selects a module as your primary system." + set category = "Hardsuit" + set src = usr.contents + + if(malfunction_check(usr)) + return + + if(!check_power_cost(usr, 0, 0, 0, 0)) + return + + if(!(flags & NODROP)) + usr << "The suit is not active." + return + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + var/list/selectable = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.selectable) + selectable |= module + + var/obj/item/rig_module/module = input("Which module do you wish to select?") as null|anything in selectable + + if(!istype(module)) + selected_module = null + usr << "Primary system is now: deselected." + return + + selected_module = module + usr << "Primary system is now: [selected_module.interface_name]." + +/obj/item/weapon/rig/verb/toggle_module() + + set name = "Toggle Module" + set desc = "Toggle a system module." + set category = "Hardsuit" + set src = usr.contents + + if(malfunction_check(usr)) + return + + if(!check_power_cost(usr, 0, 0, 0, 0)) + return + + if(!(flags & NODROP)) + usr << "The suit is not active." + return + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + var/list/selectable = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.toggleable) + selectable |= module + + var/obj/item/rig_module/module = input("Which module do you wish to toggle?") as null|anything in selectable + + if(!istype(module)) + return + + if(module.active) + usr << "You attempt to deactivate \the [module.interface_name]." + module.deactivate() + else + usr << "You attempt to activate \the [module.interface_name]." + module.activate() + +/obj/item/weapon/rig/verb/engage_module() + + set name = "Engage Module" + set desc = "Engages a system module." + set category = "Hardsuit" + set src = usr.contents + + if(malfunction_check(usr)) + return + + if(!(flags & NODROP)) + usr << "The suit is not active." + return + + if(!istype(wearer) || !wearer.back == src) + usr << "The hardsuit is not being worn." + return + + if(!check_power_cost(usr, 0, 0, 0, 0)) + return + + var/list/selectable = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.usable) + selectable |= module + + var/obj/item/rig_module/module = input("Which module do you wish to engage?") as null|anything in selectable + + if(!istype(module)) + return + + usr << "You attempt to engage the [module.interface_name]." + module.engage() \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/rig_wiring.dm b/code/modules/clothing/spacesuits/rig/rig_wiring.dm new file mode 100644 index 00000000000..98f8a137028 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/rig_wiring.dm @@ -0,0 +1,57 @@ +/datum/wires/rig + random = 1 + holder_type = /obj/item/weapon/rig + wire_count = 5 + +#define RIG_SECURITY 1 +#define RIG_AI_OVERRIDE 2 +#define RIG_SYSTEM_CONTROL 4 +#define RIG_INTERFACE_LOCK 8 +#define RIG_INTERFACE_SHOCK 16 +/* + * Rig security can be snipped to disable ID access checks on rig. + * Rig AI override can be pulsed to toggle whether or not the AI can take control of the suit. + * System control can be pulsed to toggle some malfunctions. + * Interface lock can be pulsed to toggle whether or not the interface can be accessed. + */ + +/datum/wires/rig/UpdateCut(var/index, var/mended) + + var/obj/item/weapon/rig/rig = holder + switch(index) + if(RIG_SECURITY) + if(mended) + rig.req_access = initial(rig.req_access) + rig.req_one_access = initial(rig.req_one_access) + if(RIG_INTERFACE_SHOCK) + rig.electrified = mended ? 0 : -1 + rig.shock(usr,100) + +/datum/wires/rig/UpdatePulsed(var/index) + + var/obj/item/weapon/rig/rig = holder + switch(index) + if(RIG_SECURITY) + rig.security_check_enabled = !rig.security_check_enabled + rig.visible_message("\The [rig] twitches as several suit locks [rig.security_check_enabled?"close":"open"].") + if(RIG_AI_OVERRIDE) + rig.ai_override_enabled = !rig.ai_override_enabled + rig.visible_message("A small red light on [rig] [rig.ai_override_enabled?"goes dead":"flickers on"].") + if(RIG_SYSTEM_CONTROL) + rig.malfunctioning += 10 + if(rig.malfunction_delay <= 0) + rig.malfunction_delay = 20 + rig.shock(usr,100) + if(RIG_INTERFACE_LOCK) + rig.interface_locked = !rig.interface_locked + rig.visible_message("\The [rig] clicks audibly as the software interface [rig.interface_locked?"darkens":"brightens"].") + if(RIG_INTERFACE_SHOCK) + if(rig.electrified != -1) + rig.electrified = 30 + rig.shock(usr,100) + +/datum/wires/rig/CanUse(var/mob/living/L) + var/obj/item/weapon/rig/rig = holder + if(rig.open) + return 1 + return 0 \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/suits/alien.dm b/code/modules/clothing/spacesuits/rig/suits/alien.dm new file mode 100644 index 00000000000..5f43c172a39 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/alien.dm @@ -0,0 +1,32 @@ +/obj/item/weapon/rig/unathi + name = "NT breacher chassis control module" + desc = "A cheap NT knock-off of an Unathi battle-rig. Looks like a fish, moves like a fish, steers like a cow." + suit_type = "NT breacher" + icon_state = "breacher_rig_cheap" + armor = list(melee = 60, bullet = 60, laser = 60, energy = 60, bomb = 70, bio = 100, rad = 50) + emp_protection = -20 + slowdown = 6 + offline_slowdown = 10 + vision_restriction = 1 + offline_vision_restriction = 2 + + chest_type = /obj/item/clothing/suit/space/new_rig + helm_type = /obj/item/clothing/head/helmet/space/new_rig/unathi + boot_type = /obj/item/clothing/shoes/magboots/rig/unathi + +/obj/item/weapon/rig/unathi/fancy + name = "breacher chassis control module" + desc = "An authentic Unathi breacher chassis. Huge, bulky and absurdly heavy. It must be like wearing a tank." + suit_type = "breacher chassis" + icon_state = "breacher_rig" + armor = list(melee = 90, bullet = 90, laser = 90, energy = 90, bomb = 90, bio = 100, rad = 80) //Takes TEN TIMES as much damage to stop someone in a breacher. In exchange, it's slow. + vision_restriction = 0 + +/obj/item/clothing/head/helmet/space/new_rig/unathi + species_restricted = list("Unathi") + +/obj/item/clothing/suit/space/new_rig/unathi + species_restricted = list("Unathi") + +/obj/item/clothing/shoes/magboots/rig/unathi + species_restricted = list("Unathi") \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/suits/combat.dm b/code/modules/clothing/spacesuits/rig/suits/combat.dm new file mode 100644 index 00000000000..cd37189a080 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/combat.dm @@ -0,0 +1,28 @@ +/obj/item/clothing/head/helmet/space/new_rig/combat + +/obj/item/weapon/rig/combat + name = "combat hardsuit control module" + desc = "A sleek and dangerous hardsuit for active combat." + icon_state = "security_rig" + suit_type = "combat hardsuit" + armor = list(melee = 80, bullet = 65, laser = 50, energy = 15, bomb = 80, bio = 100, rad = 60) + slowdown = 1 + offline_slowdown = 3 + offline_vision_restriction = 1 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/combat + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/melee/baton) + + +/obj/item/weapon/rig/combat/equipped + + + initial_modules = list( + /obj/item/rig_module/mounted, + /obj/item/rig_module/vision/thermal, + /obj/item/rig_module/grenade_launcher, + /obj/item/rig_module/ai_container, + // /obj/item/rig_module/power_sink, + /obj/item/rig_module/electrowarfare_suite, + /obj/item/rig_module/chem_dispenser/combat + ) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/suits/ert.dm b/code/modules/clothing/spacesuits/rig/suits/ert.dm new file mode 100644 index 00000000000..f10a859af9a --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/ert.dm @@ -0,0 +1,83 @@ +/obj/item/clothing/head/helmet/space/new_rig/ert + +/obj/item/weapon/rig/ert + name = "ERT-C hardsuit control module" + desc = "A suit worn by the commander of an Emergency Response Team. Has blue highlights. Armoured and space ready." + suit_type = "ERT commander" + icon_state = "ert_commander_rig" + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert + + req_access = list(access_cent_specops) + + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 100, rad = 100) + allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank, /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/crowbar, \ + /obj/item/weapon/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/wirecutters, /obj/item/weapon/wrench, /obj/item/device/multitool, \ + /obj/item/device/radio, /obj/item/device/analyzer,/obj/item/weapon/storage/briefcase/inflatable, /obj/item/weapon/melee/baton, /obj/item/weapon/gun, \ + /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller) + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/datajack, + ) + +/obj/item/weapon/rig/ert/engineer + name = "ERT-E suit control module" + desc = "A suit worn by the engineering division of an Emergency Response Team. Has orange highlights. Armoured and space ready." + suit_type = "ERT engineer" + icon_state = "ert_engineer_rig" + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 100, rad = 100) + siemens_coefficient = 0 + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/device/plasmacutter, + // /obj/item/rig_module/device/rcd + ) + +/obj/item/weapon/rig/ert/medical + name = "ERT-M suit control module" + desc = "A suit worn by the medical division of an Emergency Response Team. Has white highlights. Armoured and space ready." + suit_type = "ERT medic" + icon_state = "ert_medical_rig" + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/device/healthscanner, + /obj/item/rig_module/chem_dispenser/injector + ) + +/obj/item/weapon/rig/ert/security + name = "ERT-S suit control module" + desc = "A suit worn by the security division of an Emergency Response Team. Has red highlights. Armoured and space ready." + suit_type = "ERT security" + icon_state = "ert_security_rig" + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/grenade_launcher, + /obj/item/rig_module/mounted/egun, + ) + +/obj/item/weapon/rig/ert/assetprotection + name = "Heavy Asset Protection suit control module" + desc = "A heavy suit worn by the highest level of Asset Protection, don't mess with the person wearing this. Armoured and space ready." + suit_type = "heavy asset protection" + icon_state = "asset_protection_rig" + armor = list(melee = 60, bullet = 50, laser = 50,energy = 40, bomb = 40, bio = 100, rad = 100) + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/grenade_launcher, + /obj/item/rig_module/vision/multi, + /obj/item/rig_module/mounted/egun, + /obj/item/rig_module/chem_dispenser/injector, + /obj/item/rig_module/device/plasmacutter, + // /obj/item/rig_module/device/rcd, + /obj/item/rig_module/datajack + ) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm new file mode 100644 index 00000000000..fb6b0459a91 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/light.dm @@ -0,0 +1,123 @@ +// Light rigs are not space-capable, but don't suffer excessive slowdown or sight issues when depowered. +/obj/item/weapon/rig/light + name = "light suit control module" + desc = "A lighter, less armoured rig suit." + icon_state = "ninja_rig" + suit_type = "light suit" + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/restraints/handcuffs,/obj/item/weapon/tank,/obj/item/weapon/stock_parts/cell) + armor = list(melee = 50, bullet = 15, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0) + emp_protection = 10 + slowdown = 0 + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + offline_slowdown = 0 + offline_vision_restriction = 0 + + chest_type = /obj/item/clothing/suit/space/new_rig/light + helm_type = /obj/item/clothing/head/helmet/space/new_rig/light + boot_type = /obj/item/clothing/shoes/magboots/rig/light + glove_type = /obj/item/clothing/gloves/rig/light + +/obj/item/clothing/suit/space/new_rig/light + name = "suit" + breach_threshold = 18 //comparable to voidsuits + +/obj/item/clothing/gloves/rig/light + name = "gloves" + +/obj/item/clothing/shoes/magboots/rig/light + name = "shoes" + +/obj/item/clothing/head/helmet/space/new_rig/light + name = "hood" + +/obj/item/weapon/rig/light/hacker + name = "cybersuit control module" + suit_type = "cyber" + desc = "An advanced powered armour suit with many cyberwarfare enhancements. Comes with built-in insulated gloves for safely tampering with electronics." + icon_state = "hacker_rig" + + req_access = list(access_syndicate) + + airtight = 0 + seal_delay = 5 //not being vaccum-proof has an upside I guess + + helm_type = /obj/item/clothing/head/lightrig/hacker + chest_type = /obj/item/clothing/suit/lightrig/hacker + glove_type = /obj/item/clothing/gloves/lightrig/hacker + boot_type = /obj/item/clothing/shoes/lightrig/hacker + + initial_modules = list( + /obj/item/rig_module/ai_container, + // /obj/item/rig_module/power_sink, + /obj/item/rig_module/datajack, + /obj/item/rig_module/electrowarfare_suite, + /obj/item/rig_module/voice, + /obj/item/rig_module/vision, + ) + +//The cybersuit is not space-proof. It does however, have good siemens_coefficient values +/obj/item/clothing/head/lightrig/hacker + name = "HUD" + siemens_coefficient = 0.4 + flags = 0 + +/obj/item/clothing/suit/lightrig/hacker + siemens_coefficient = 0.4 + +/obj/item/clothing/shoes/lightrig/hacker + siemens_coefficient = 0.4 + flags = NOSLIP //All the other rigs have magboots anyways, hopefully gives the hacker suit something more going for it. + +/obj/item/clothing/gloves/lightrig/hacker + siemens_coefficient = 0 + +/obj/item/weapon/rig/light/ninja + name = "ominous suit control module" + suit_type = "ominous" + desc = "A unique, vaccum-proof suit of nano-enhanced armor designed specifically for Spider Clan assassins." + icon_state = "ninja_rig" + armor = list(melee = 50, bullet = 15, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 30) + emp_protection = 40 //change this to 30 if too high. + slowdown = 0 + + chest_type = /obj/item/clothing/suit/space/new_rig/light/ninja + glove_type = /obj/item/clothing/gloves/rig/light/ninja + + req_access = list(access_syndicate) + + initial_modules = list( + /obj/item/rig_module/teleporter, + /obj/item/rig_module/stealth_field, + /obj/item/rig_module/mounted/energy_blade, + /obj/item/rig_module/vision, + /obj/item/rig_module/voice, + /obj/item/rig_module/chem_dispenser, + /obj/item/rig_module/grenade_launcher, + /obj/item/rig_module/fabricator, + /obj/item/rig_module/ai_container, + // /obj/item/rig_module/power_sink, + /obj/item/rig_module/datajack, + /obj/item/rig_module/self_destruct + ) + + ..() + +/obj/item/clothing/gloves/rig/light/ninja + name = "insulated gloves" + siemens_coefficient = 0 + +/obj/item/clothing/suit/space/new_rig/light/ninja + breach_threshold = 38 //comparable to regular hardsuits + +/obj/item/weapon/rig/light/stealth + name = "stealth suit control module" + suit_type = "stealth" + desc = "A highly advanced and expensive suit designed for covert operations." + icon_state = "stealth_rig" + + req_access = list(access_syndicate) + + initial_modules = list( + /obj/item/rig_module/stealth_field, + /obj/item/rig_module/vision + ) diff --git a/code/modules/clothing/spacesuits/rig/suits/merc.dm b/code/modules/clothing/spacesuits/rig/suits/merc.dm new file mode 100644 index 00000000000..58a66aaa4b1 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/merc.dm @@ -0,0 +1,32 @@ +/obj/item/clothing/head/helmet/space/new_rig/merc + +/obj/item/weapon/rig/merc + name = "crimson hardsuit control module" + desc = "A blood-red hardsuit featuring some fairly illegal technology." + icon_state = "merc_rig" + suit_type = "crimson hardsuit" + armor = list(melee = 80, bullet = 65, laser = 50, energy = 15, bomb = 80, bio = 100, rad = 60) + slowdown = 1 + offline_slowdown = 3 + offline_vision_restriction = 1 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/merc + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/gun,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/restraints/handcuffs) + + initial_modules = list( + /obj/item/rig_module/mounted, + /obj/item/rig_module/vision/thermal, + /obj/item/rig_module/grenade_launcher, + /obj/item/rig_module/ai_container, + // /obj/item/rig_module/power_sink, + /obj/item/rig_module/electrowarfare_suite, + /obj/item/rig_module/chem_dispenser/combat, + // /obj/item/rig_module/fabricator/energy_net + ) + +//Has most of the modules removed +/obj/item/weapon/rig/merc/empty + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/electrowarfare_suite, //might as well + ) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm new file mode 100644 index 00000000000..4ddf6e419f3 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/station.dm @@ -0,0 +1,227 @@ +/obj/item/clothing/head/helmet/space/new_rig/industrial + +/obj/item/clothing/head/helmet/space/new_rig/ce + +/obj/item/clothing/head/helmet/space/new_rig/eva + +/obj/item/clothing/head/helmet/space/new_rig/hazmat + +/obj/item/clothing/head/helmet/space/new_rig/medical + +/obj/item/clothing/head/helmet/space/new_rig/hazard + +/obj/item/weapon/rig/internalaffairs + name = "augmented tie" + suit_type = "augmented suit" + desc = "Prepare for paperwork." + icon_state = "internalaffairs_rig" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + slowdown = 0 + offline_slowdown = 0 + offline_vision_restriction = 0 + + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/briefcase,/obj/item/weapon/storage/secure/briefcase) + + req_access = list() + req_one_access = list() + + glove_type = null + helm_type = null + boot_type = null + +/obj/item/weapon/rig/internalaffairs/equipped + + req_access = list(access_lawyer) + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/device/flash, + /obj/item/rig_module/device/paperdispenser, + /obj/item/rig_module/device/pen, + /obj/item/rig_module/device/stamp + ) + + glove_type = null + helm_type = null + boot_type = null + +/obj/item/weapon/rig/industrial + name = "industrial suit control module" + suit_type = "industrial hardsuit" + desc = "A heavy, powerful rig used by construction crews and mining corporations." + icon_state = "engineering_rig" + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 100, rad = 50) + slowdown = 3 + offline_slowdown = 10 + offline_vision_restriction = 2 + emp_protection = -20 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/industrial + + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd) + + req_access = list() + req_one_access = list() + + +/obj/item/weapon/rig/industrial/equipped + + initial_modules = list( + /obj/item/rig_module/device/plasmacutter, + /obj/item/rig_module/device/drill, + /obj/item/rig_module/device/orescanner, + // /obj/item/rig_module/device/rcd, + /obj/item/rig_module/vision/meson + ) + +/obj/item/weapon/rig/eva + name = "EVA suit control module" + suit_type = "EVA hardsuit" + desc = "A light rig for repairs and maintenance to the outside of habitats and vessels." + icon_state = "eva_rig" + armor = list(melee = 30, bullet = 10, laser = 20,energy = 25, bomb = 20, bio = 100, rad = 100) + slowdown = 0 + offline_slowdown = 1 + offline_vision_restriction = 1 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/eva + + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/toolbox,/obj/item/weapon/storage/briefcase/inflatable,/obj/item/device/t_scanner,/obj/item/weapon/rcd) + + req_access = list() + req_one_access = list() + +/obj/item/weapon/rig/eva/equipped + + initial_modules = list( + /obj/item/rig_module/device/plasmacutter, + /obj/item/rig_module/maneuvering_jets, + // /obj/item/rig_module/device/rcd, + /obj/item/rig_module/vision/meson + ) + +//Chief Engineer's rig. This is sort of a halfway point between the old hardsuits (voidsuits) and the rig class. +/obj/item/weapon/rig/ce + + name = "advanced voidsuit control module" + suit_type = "advanced voidsuit" + desc = "An advanced voidsuit that protects against hazardous, low pressure environments. Shines with a high polish." + icon_state = "ce_rig" + armor = list(melee = 40, bullet = 10, laser = 30,energy = 25, bomb = 40, bio = 100, rad = 100) + slowdown = 0 + offline_slowdown = 0 + offline_vision_restriction = 0 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/ce + + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd) + + + req_access = list() + req_one_access = list() + + boot_type = null + glove_type = null + +/obj/item/weapon/rig/ce/equipped + + req_access = list(access_ce) + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/device/plasmacutter, + // /obj/item/rig_module/device/rcd, + /obj/item/rig_module/vision/meson + ) + + chest_type = /obj/item/clothing/suit/space/new_rig/ce + boot_type = null + glove_type = null + +/obj/item/clothing/suit/space/new_rig/ce + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + +/obj/item/weapon/rig/hazmat + + name = "AMI control module" + suit_type = "hazmat hardsuit" + desc = "An Anomalous Material Interaction hardsuit that protects against the strangest energies the universe can throw at it." + icon_state = "science_rig" + armor = list(melee = 45, bullet = 5, laser = 45, energy = 80, bomb = 60, bio = 100, rad = 100) + slowdown = 1 + offline_vision_restriction = 1 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/hazmat + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert + + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/box/excavation,/obj/item/weapon/pickaxe,/obj/item/device/healthanalyzer,/obj/item/device/measuring_tape,/obj/item/device/ano_scanner,/obj/item/device/depth_scanner,/obj/item/device/core_sampler,/obj/item/device/gps,/obj/item/device/beacon_locator,/obj/item/device/radio/beacon,/obj/item/weapon/pickaxe/hand,/obj/item/weapon/storage/bag/fossils) + + req_access = list() + req_one_access = list() + +/obj/item/weapon/rig/hazmat/equipped + + req_access = list(access_rd) + + initial_modules = list( + /obj/item/rig_module/ai_container, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/device/anomaly_scanner + ) + +/obj/item/weapon/rig/medical + + name = "rescue suit control module" + suit_type = "rescue hardsuit" + desc = "A durable suit designed for medical rescue in high risk areas." + icon_state = "medical_rig" + armor = list(melee = 30, bullet = 15, laser = 20, energy = 60, bomb = 30, bio = 100, rad = 100) + slowdown = 1 + offline_vision_restriction = 1 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/medical + + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/firstaid,/obj/item/device/healthanalyzer,/obj/item/stack/medical,/obj/item/roller ) + + req_access = list() + req_one_access = list() + +/obj/item/weapon/rig/medical/equipped + + initial_modules = list( + /obj/item/rig_module/chem_dispenser/injector, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/device/healthscanner, + /obj/item/rig_module/vision/medhud + ) + +/obj/item/weapon/rig/hazard + name = "hazard hardsuit control module" + suit_type = "hazard hardsuit" + desc = "A Security hardsuit designed for prolonged EVA in dangerous environments." + icon_state = "hazard_rig" + armor = list(melee = 60, bullet = 40, laser = 30, energy = 15, bomb = 60, bio = 100, rad = 30) + slowdown = 1 + offline_slowdown = 3 + offline_vision_restriction = 1 + + helm_type = /obj/item/clothing/head/helmet/space/new_rig/hazard + + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/melee/baton) + + req_access = list() + req_one_access = list() + + +/obj/item/weapon/rig/hazard/equipped + + initial_modules = list( + /obj/item/rig_module/vision/sechud, + /obj/item/rig_module/maneuvering_jets, + /obj/item/rig_module/grenade_launcher, + /obj/item/rig_module/mounted/taser + ) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/void/merc.dm b/code/modules/clothing/spacesuits/void/merc.dm new file mode 100644 index 00000000000..322c1b96b15 --- /dev/null +++ b/code/modules/clothing/spacesuits/void/merc.dm @@ -0,0 +1,21 @@ +//Syndicate rig +/obj/item/clothing/head/helmet/space/void/merc + name = "blood-red voidsuit helmet" + desc = "An advanced helmet designed for work in special operations. Property of Gorlex Marauders." + icon_state = "rig0-syndie" + item_state = "syndie_helm" + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 35, bio = 100, rad = 60) + siemens_coefficient = 0.6 + species_restricted = list("Human") + +/obj/item/clothing/suit/space/void/merc + icon_state = "rig-syndie" + name = "blood-red voidsuit" + desc = "An advanced suit that protects against injuries during special operations. Property of Gorlex Marauders." + item_state = "syndie_voidsuit" + slowdown = 1 + w_class = 3 + armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 60) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/gun,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/restraints/handcuffs) + siemens_coefficient = 0.6 + species_restricted = list("Human", "Skrell") \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/void/station.dm b/code/modules/clothing/spacesuits/void/station.dm new file mode 100644 index 00000000000..4304190c9e1 --- /dev/null +++ b/code/modules/clothing/spacesuits/void/station.dm @@ -0,0 +1,83 @@ +// Station voidsuits +//Engineering rig +/obj/item/clothing/head/helmet/space/void/engineering + name = "engineering voidsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." + icon_state = "rig0-engineering" + item_state = "eng_helm" + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 80) + +/obj/item/clothing/suit/space/void/engineering + name = "engineering voidsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." + icon_state = "rig-engineering" + item_state = "eng_voidsuit" + slowdown = 1 + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 80) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd) + +//Mining rig +/obj/item/clothing/head/helmet/space/void/mining + name = "mining voidsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating." + icon_state = "rig0-mining" + item_state = "mining_helm" + armor = list(melee = 50, bullet = 5, laser = 20,energy = 5, bomb = 55, bio = 100, rad = 20) + +/obj/item/clothing/suit/space/void/mining + icon_state = "rig-mining" + name = "mining voidsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." + item_state = "mining_voidsuit" + armor = list(melee = 50, bullet = 5, laser = 20,energy = 5, bomb = 55, bio = 100, rad = 20) + +//Medical Rig +/obj/item/clothing/head/helmet/space/void/medical + name = "medical voidsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has minor radiation shielding." + icon_state = "rig0-medical" + item_state = "medical_helm" + armor = list(melee = 30, bullet = 5, laser = 20,energy = 5, bomb = 25, bio = 100, rad = 50) + +/obj/item/clothing/suit/space/void/medical + icon_state = "rig-medical" + name = "medical voidsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has minor radiation shielding." + item_state = "medical_voidsuit" + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/storage/firstaid,/obj/item/device/healthanalyzer,/obj/item/stack/medical) + armor = list(melee = 30, bullet = 5, laser = 20,energy = 5, bomb = 25, bio = 100, rad = 50) + + //Security +/obj/item/clothing/head/helmet/space/void/security + name = "security voidsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." + icon_state = "rig0-sec" + item_state = "sec_helm" + armor = list(melee = 60, bullet = 10, laser = 30, energy = 5, bomb = 45, bio = 100, rad = 10) + siemens_coefficient = 0.7 + +/obj/item/clothing/suit/space/void/security + icon_state = "rig-sec" + name = "security voidsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor." + item_state = "sec_voidsuit" + armor = list(melee = 60, bullet = 10, laser = 30, energy = 5, bomb = 45, bio = 100, rad = 10) + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/weapon/melee/baton) + siemens_coefficient = 0.7 + +//Atmospherics Rig (BS12) +/obj/item/clothing/head/helmet/space/void/atmos + desc = "A special helmet designed for work in a hazardous, low pressure environments. Has improved thermal protection and minor radiation shielding." + name = "atmospherics voidsuit helmet" + icon_state = "rig0-atmos" + item_state = "atmos_helm" + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 50) + max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/space/void/atmos + desc = "A special suit that protects against hazardous, low pressure environments. Has improved thermal protection and minor radiation shielding." + icon_state = "rig-atmos" + name = "atmos voidsuit" + item_state = "atmos_voidsuit" + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 50) + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm new file mode 100644 index 00000000000..7a82f2588b0 --- /dev/null +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -0,0 +1,241 @@ +//NASA Voidsuit +/obj/item/clothing/head/helmet/space/void + name = "void helmet" + desc = "A high-tech dark red space suit helmet. Used for AI satellite maintenance." + icon_state = "void" + + heat_protection = HEAD + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + + //Species-specific stuff. + species_restricted = list("Human") + sprite_sheets_refit = list( + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Tajara" = 'icons/mob/species/tajaran/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', + "Tajara" = 'icons/obj/clothing/species/tajaran/hats.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', + ) + +/obj/item/clothing/suit/space/void + name = "voidsuit" + icon_state = "void" + item_state = "void" + desc = "A high-tech dark red space suit. Used for AI satellite maintenance." + slowdown = 1 + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + + species_restricted = list("Human", "Skrell") + sprite_sheets_refit = list( + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Tajara" = 'icons/mob/species/tajaran/suit.dmi', + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', + "Tajara" = 'icons/obj/clothing/species/tajaran/suits.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', + ) + + //Breach thresholds, should ideally be inherited by most (if not all) voidsuits. + //With 0.2 resiliance, will reach 10 breach damage after 3 laser carbine blasts or 8 smg hits. + breach_threshold = 18 + can_breach = 1 + + //Inbuilt devices. + var/obj/item/clothing/shoes/magboots/boots = null // Deployable boots, if any. + var/obj/item/clothing/head/helmet/helmet = null // Deployable helmet, if any. + var/obj/item/weapon/tank/tank = null // Deployable tank, if any. + +/obj/item/clothing/suit/space/void/examine(user) + ..(user) + var/list/part_list = new + for(var/obj/item/I in list(helmet,boots,tank)) + part_list += "\a [I]" + user << "\The [src] has [english_list(part_list)] installed." + if(tank && in_range(src,user)) + user << "The wrist-mounted pressure gauge reads [max(round(tank.air_contents.return_pressure()),0)] kPa remaining in \the [tank]." + +/obj/item/clothing/suit/space/void/refit_for_species(var/target_species) + ..() + if(istype(helmet)) + helmet.refit_for_species(target_species) + if(istype(boots)) + boots.refit_for_species(target_species) + +/obj/item/clothing/suit/space/void/equipped(mob/M) + ..() + + var/mob/living/carbon/human/H = M + + if(!istype(H)) return + + if(H.wear_suit != src) + return + + if(boots) + if (H.equip_to_slot_if_possible(boots, slot_shoes)) + boots.flags |= NODROP + + if(helmet) + if(H.head) + M << "You are unable to deploy your suit's helmet as \the [H.head] is in the way." + else if (H.equip_to_slot_if_possible(helmet, slot_head)) + M << "Your suit's helmet deploys with a hiss." + helmet.flags |= NODROP + + if(tank) + if(H.s_store) //In case someone finds a way. + M << "Alarmingly, the valve on your suit's installed tank fails to engage." + else if (H.equip_to_slot_if_possible(tank, slot_s_store)) + M << "The valve on your suit's installed tank safely engages." + tank.flags |= NODROP + + +/obj/item/clothing/suit/space/void/dropped() + ..() + + var/mob/living/carbon/human/H + + if(helmet) + helmet.flags &= ~NODROP + H = helmet.loc + if(istype(H)) + if(helmet && H.head == helmet) + H.unEquip(helmet) + helmet.forceMove(src) + + if(boots) + boots.flags &= ~NODROP + H = boots.loc + if(istype(H)) + if(boots && H.shoes == boots) + H.unEquip(boots) + boots.forceMove(src) + + if(tank) + tank.flags &= ~NODROP + tank.forceMove(src) + +/obj/item/clothing/suit/space/void/verb/toggle_helmet() + + set name = "Toggle Helmet" + set category = "Object" + set src in usr + + if(!istype(src.loc,/mob/living)) return + + if(!helmet) + usr << "There is no helmet installed." + return + + var/mob/living/carbon/human/H = usr + + if(!istype(H)) return + if(H.stat) return + if(H.wear_suit != src) return + + if(H.head == helmet) + H << "You retract your suit helmet." + helmet.flags &= ~NODROP + H.unEquip(helmet) + helmet.forceMove(src) + else + if(H.head) + H << "You cannot deploy your helmet while wearing \the [H.head]." + return + if(H.equip_to_slot_if_possible(helmet, slot_head)) + helmet.pickup(H) + helmet.flags |= NODROP + H << "You deploy your suit helmet, sealing you off from the world." + helmet.update_light(H) + +/obj/item/clothing/suit/space/void/verb/eject_tank() + + set name = "Eject Voidsuit Tank" + set category = "Object" + set src in usr + + if(!istype(src.loc,/mob/living)) return + + if(!tank) + usr << "There is no tank inserted." + return + + var/mob/living/carbon/human/H = usr + + if(!istype(H)) return + if(H.stat) return + if(H.wear_suit != src) return + + H << "You press the emergency release, ejecting \the [tank] from your suit." + tank.flags &= ~NODROP + H.unEquip(tank) + src.tank = null + +/obj/item/clothing/suit/space/void/attackby(obj/item/W as obj, mob/user as mob) + + if(!istype(user,/mob/living)) return + + if(istype(src.loc,/mob/living)) + user << "How do you propose to modify a voidsuit while it is being worn?" + return + + if(istype(W,/obj/item/weapon/screwdriver)) + if(helmet || boots || tank) + var/choice = input("What component would you like to remove?") as null|anything in list(helmet,boots,tank) + if(!choice) return + + if(choice == tank) //No, a switch doesn't work here. Sorry. ~Techhead + user << "You pop \the [tank] out of \the [src]'s storage compartment." + tank.forceMove(get_turf(src)) + src.tank = null + else if(choice == helmet) + user << "You detatch \the [helmet] from \the [src]'s helmet mount." + helmet.forceMove(get_turf(src)) + src.helmet = null + else if(choice == boots) + user << "You detatch \the [boots] from \the [src]'s boot mounts." + boots.forceMove(get_turf(src)) + src.boots = null + else + user << "\The [src] does not have anything installed." + return + else if(istype(W,/obj/item/clothing/head/helmet/space)) + if(helmet) + user << "\The [src] already has a helmet installed." + else + user << "You attach \the [W] to \the [src]'s helmet mount." + user.drop_item() + W.forceMove(src) + src.helmet = W + return + else if(istype(W,/obj/item/clothing/shoes/magboots)) + if(boots) + user << "\The [src] already has magboots installed." + else + user << "You attach \the [W] to \the [src]'s boot mounts." + user.drop_item() + W.forceMove(src) + boots = W + return + else if(istype(W,/obj/item/weapon/tank)) + if(tank) + user << "\The [src] already has an airtank installed." + else if(istype(W,/obj/item/weapon/tank/plasma)) + user << "\The [W] cannot be inserted into \the [src]'s storage compartment." + else + user << "You insert \the [W] into \the [src]'s storage compartment." + user.drop_item() + W.forceMove(src) + tank = W + return + + ..() \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/void/wizard.dm b/code/modules/clothing/spacesuits/void/wizard.dm new file mode 100644 index 00000000000..a6547e7773f --- /dev/null +++ b/code/modules/clothing/spacesuits/void/wizard.dm @@ -0,0 +1,24 @@ +//Wizard Rig +/obj/item/clothing/head/helmet/space/void/wizard + name = "gem-encrusted voidsuit helmet" + desc = "A bizarre gem-encrusted helmet that radiates magical energies." + icon_state = "rig0-wiz" + item_state = "wiz_helm" + unacidable = 1 //No longer shall our kind be foiled by lone chemists with spray bottles! + armor = list(melee = 40, bullet = 20, laser = 20,energy = 20, bomb = 35, bio = 100, rad = 60) + siemens_coefficient = 0.7 + sprite_sheets_refit = null + sprite_sheets_obj = null + +/obj/item/clothing/suit/space/void/wizard + icon_state = "rig-wiz" + name = "gem-encrusted voidsuit" + desc = "A bizarre gem-encrusted suit that radiates magical energies." + item_state = "wiz_voidsuit" + slowdown = 1 + w_class = 3 + unacidable = 1 + armor = list(melee = 40, bullet = 20, laser = 20,energy = 20, bomb = 35, bio = 100, rad = 60) + siemens_coefficient = 0.7 + sprite_sheets_refit = null + sprite_sheets_obj = null diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index 913db148a95..423ef494c7f 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -15,7 +15,7 @@ var/const/MAX_ACTIVE_TIME = 400 icon_state = "facehugger" item_state = "facehugger" w_class = 1 //note: can be picked up by aliens unlike most other items of w_class below 4 - flags = MASKCOVERSMOUTH | MASKCOVERSEYES | MASKINTERNALS + flags = MASKCOVERSMOUTH | MASKCOVERSEYES | AIRTIGHT throw_range = 5 var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index b1cb613ffce..c9fe5410abf 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -149,6 +149,13 @@ brainmob.emp_damage += rand(0,10) ..() +/obj/item/device/mmi/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(rig) + rig.forced_move(direction, user) + /obj/item/device/mmi/Destroy() if(isrobot(loc)) var/mob/living/silicon/robot/borg = loc diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index fdf2193e91b..608f9b4a8e7 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -54,6 +54,7 @@ if(in_contents_of(/obj/mecha)) canmove = 1 use_me = 1 //If it can move, let it emote + if(istype(loc, /obj/item/device/mmi)) canmove = 1 //mmi won't move anyways so whatever else canmove = 0 return canmove diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 4307c6cbd0b..39a946c6a7d 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -139,6 +139,10 @@ // log_to_dd("k") sql_report_death(src) ticker.mode.check_win() //Calls the rounds wincheck, mainly for wizard, malf, and changeling now + + if(wearing_rig) + wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") + return ..(gibbed) /mob/living/carbon/human/proc/makeSkeleton() diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 414335a243e..0bac4c222e9 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -739,7 +739,7 @@ // Process toxic farts first. if(TOXIC_FARTS in mutations) for(var/mob/M in range(location,aoe_range)) - if (M.internal != null && M.wear_mask && (M.wear_mask.flags & MASKINTERNALS)) + if (M.internal != null && M.wear_mask && (M.wear_mask.flags & AIRTIGHT)) continue if(!airborne_can_reach(location,M,aoe_range)) continue diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index c9008f38398..3f805af1f4f 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -8,6 +8,7 @@ var/list/hud_list[10] var/datum/species/species //Contains icon generation and language information, set during New(). var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us. + var/obj/item/weapon/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call. /mob/living/carbon/human/New(var/new_loc, var/new_species = null, var/delay_ready_dna=0) @@ -257,6 +258,14 @@ stat("Internal Atmosphere Info", internal.name) stat("Tank Pressure", internal.air_contents.return_pressure()) stat("Distribution Pressure", internal.distribute_pressure) + + if(back && istype(back, /obj/item/weapon/rig)) + var/obj/item/weapon/rig/suit = back + var/cell_status = "ERROR" + if(suit.cell) + cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]" + stat(null, "Suit charge: [cell_status]") + if(mind) if(mind.changeling) stat("Chemical Storage", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]") @@ -1101,6 +1110,17 @@ if(istype(src.wear_mask, /obj/item/clothing/mask)) var/obj/item/clothing/mask/MT = src.wear_mask tinted += MT.tint + + //god help me + if(istype(back, /obj/item/weapon/rig)) + var/obj/item/weapon/rig/O = back + if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & HEAD)) + if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) + tinted = 2 + if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) + tinted = 3 + //im so sorry + return tinted diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index c0587ddee86..d09c61ce3b8 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -130,6 +130,9 @@ ..() /mob/living/carbon/human/Paralyse(amount) + // Notify our AI if they can now control the suit. + if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. + wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") ..() /mob/living/carbon/human/adjustCloneLoss(var/amount) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index b54c7f5ee36..3a2464d013e 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -58,10 +58,19 @@ if(..()) return 1 - //Do we have a working jetpack - if(istype(back, /obj/item/weapon/tank/jetpack) && isturf(loc)) //Second check is so you can't use a jetpack in a mech - var/obj/item/weapon/tank/jetpack/J = back - if((movement_dir || J.stabilization_on) && J.allow_thrust(0.01, src)) + //Do we have a working jetpack? + var/obj/item/weapon/tank/jetpack/thrust + if(back) + if(istype(back,/obj/item/weapon/tank/jetpack)) + thrust = back + else if(istype(back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = back + for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) + thrust = module.jets + break + + if(thrust) + if((movement_dir || thrust.stabilization_on) && thrust.allow_thrust(0.01, src)) return 1 return 0 diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 8967e1519d6..99e77bf7bd2 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -62,6 +62,11 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc //TODO: seperate this out // update the current life tick, can be used to e.g. only do something every 4 ticks life_tick++ + + // This is not an ideal place for this but it will do for now. + if(wearing_rig && wearing_rig.offline) + wearing_rig = null + var/datum/gas_mixture/environment = loc.return_air() in_stasis = 0 @@ -420,6 +425,24 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc // Health is in deep shit and we're not already dead return health <= 0 && stat != 2 +/mob/living/carbon/human/get_breath_from_internal(volume_needed=BREATH_VOLUME) + if(internal) + + var/obj/item/weapon/tank/rig_supply + if(istype(back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = back + if(!rig.offline && (rig.air_supply && internal == rig.air_supply)) + rig_supply = rig.air_supply + + if (!rig_supply && (!contents.Find(internal) || !((wear_mask && (wear_mask.flags & AIRTIGHT)) || (head && (head.flags & AIRTIGHT))))) + internal = null + + if(internal) + return internal.remove_air_volume(volume_needed) + else if(internals) + internals.icon_state = "internal0" + return null + /mob/living/carbon/human/proc/handle_breath(var/datum/gas_mixture/breath) if(status_flags & GODMODE) return 0 @@ -1178,7 +1201,7 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc see_invisible = SEE_INVISIBLE_LIVING seer = 0 - if(glasses || head) + if(glasses || head || back) if(glasses) var/obj/item/clothing/glasses/G = glasses if(istype(G)) @@ -1220,6 +1243,32 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc if(ANTAGHUD) process_antag_hud(src) + if(back && istype(back, /obj/item/weapon/rig)) ///ahhhg so snowflakey + var/obj/item/weapon/rig/rig = back ///but the way it does it now is incompatible with non-snowflake systems ahhhgg + if(rig.visor) + if(!rig.helmet || (head && rig.helmet == head)) + if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) + var/obj/item/clothing/glasses/G = rig.visor.vision.glasses + if(istype(G)) + see_in_dark = (G.darkness_view ? see_in_dark + G.darkness_view : species.darksight) // Otherwise we keep our darkness view with togglable nightvision. + if(G.vision_flags) // MESONS + sight |= G.vision_flags + if(!druggy) + see_invisible = SEE_INVISIBLE_MINIMUM + if(!G.see_darkness) + see_invisible = SEE_INVISIBLE_MINIMUM + + /* HUD shit goes here, as long as it doesn't modify sight flags */ + // The purpose of this is to stop xray and w/e from preventing you from using huds -- Love, Doohl + switch(G.HUDType) + if(SECHUD) + process_sec_hud(src,1) + if(MEDHUD) + process_med_hud(src,1) + if(ANTAGHUD) + process_antag_hud(src) + + else if(!seer) see_in_dark = species.darksight see_invisible = SEE_INVISIBLE_LIVING diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index c8bfc4402c2..0532b9ffdfd 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -67,6 +67,11 @@ return ..() /mob/living/carbon/human/proc/HasVoiceChanger() + if(istype(back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = back + if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) + return rig.speech.voice_holder.voice + for(var/obj/item/gear in list(wear_mask,wear_suit,head)) if(!gear) continue diff --git a/code/modules/mob/living/carbon/human/species/golem.dm b/code/modules/mob/living/carbon/human/species/golem.dm index 9c5a9ef851e..88e4d5a1962 100644 --- a/code/modules/mob/living/carbon/human/species/golem.dm +++ b/code/modules/mob/living/carbon/human/species/golem.dm @@ -67,7 +67,7 @@ desc = "sturdy adamantine feet" icon_state = "golem" item_state = "golem" - flags = NOSLIP | ABSTRACT | MASKINTERNALS | MASKCOVERSMOUTH | NODROP + flags = NOSLIP | ABSTRACT | AIRTIGHT | MASKCOVERSMOUTH | NODROP slowdown = SHOES_SLOWDOWN+1 diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 86c2d79eb7b..830cdc379fc 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -811,14 +811,32 @@ var/global/list/damage_icon_parts = list() /mob/living/carbon/human/update_inv_back(var/update_icons=1) if(back) back.screen_loc = ui_back //TODO + + //determine the icon to use + var/icon/overlay_icon if(back.icon_override) - overlays_standing[BACK_LAYER] = image("icon" = back.icon_override, "icon_state" = "[back.icon_state]") + overlay_icon = back.icon_override + else if(istype(back, /obj/item/weapon/rig)) + //If this is a rig and a mob_icon is set, it will take species into account in the rig update_icon() proc. + var/obj/item/weapon/rig/rig = back + overlay_icon = rig.mob_icon else if(back.sprite_sheets && back.sprite_sheets[species.name]) - overlays_standing[BACK_LAYER] = image("icon" = back.sprite_sheets[species.name], "icon_state" = "[back.icon_state]") + overlay_icon = back.sprite_sheets[species.name] else - overlays_standing[BACK_LAYER] = image("icon" = 'icons/mob/back.dmi', "icon_state" = "[back.icon_state]") + overlay_icon = icon('icons/mob/back.dmi', "[back.icon_state]") + + //determine state to use + var/overlay_state + if(back.item_state) + overlay_state = back.item_state + else + overlay_state = back.icon_state + + //create the image + overlays_standing[BACK_LAYER] = image(icon = overlay_icon, icon_state = overlay_state) else - overlays_standing[BACK_LAYER] = null + overlays_standing[BACK_LAYER] = null + if(update_icons) update_icons() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 248a3154388..8397ca9b043 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -1,8 +1,8 @@ -/mob/living/carbon/proc/get_breath_from_internal(volume_needed) +/mob/living/carbon/proc/get_breath_from_internal(var/volume_needed=BREATH_VOLUME) //hopefully this will allow overrides to specify a different default volume without breaking any cases where volume is passed in. if(internal) if (!contents.Find(internal)) internal = null - if (!wear_mask || !(wear_mask.flags & MASKINTERNALS) ) + if (!(wear_mask && (wear_mask.flags & AIRTIGHT))) internal = null if(internal) if (internals) @@ -11,4 +11,4 @@ else if (internals) internals.icon_state = "internal0" - return + return null \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 5c41901fa21..4375c9f3c04 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -101,7 +101,7 @@ var/list/ai_verbs_default = list( announcement.announcement_type = "A.I. Announcement" announcement.announcer = name announcement.newscast = 1 - + var/list/possibleNames = ai_names var/pickedName = null @@ -215,7 +215,7 @@ var/list/ai_verbs_default = list( /mob/living/silicon/ai/SetName(pickedName as text) ..() - + announcement.announcer = name if(eyeobj) @@ -332,19 +332,19 @@ var/list/ai_verbs_default = list( set name = "Show Crew Manifest" set category = "AI Commands" show_station_manifest() - + /mob/living/silicon/ai/var/message_cooldown = 0 /mob/living/silicon/ai/proc/ai_announcement_text() set category = "AI Commands" set name = "Make Station Announcement" - + if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) return if(message_cooldown) src << "Please allow one minute to pass between announcements." return - + var/input = input(usr, "Please write a message to announce to the station crew.", "A.I. Announcement") as message|null if(!input) return @@ -383,7 +383,7 @@ var/list/ai_verbs_default = list( /mob/living/silicon/ai/proc/ai_cancel_call() set name = "Recall Emergency Shuttle" set category = "AI Commands" - + if(check_unable(AI_CHECK_WIRELESS)) return @@ -999,3 +999,13 @@ var/list/ai_verbs_default = list( loc = card//Throw AI into the card. src << "You have been downloaded to a mobile storage device. Remote device connection severed." user << "Transfer successful: [name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory." + +// Pass lying down or getting up to our pet human, if we're in a rig. +/mob/living/silicon/ai/lay_down() + set name = "Rest" + set category = "IC" + + resting = 0 + var/obj/item/weapon/rig/rig = src.get_rig() + if(rig) + rig.force_rest(src) diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index 33055ac3d8e..15125136b94 100644 --- a/code/modules/mob/living/silicon/pai/life.dm +++ b/code/modules/mob/living/silicon/pai/life.dm @@ -25,8 +25,7 @@ else //Not stunned. src.stat = 0 - if(paralysis || stunned || weakened || buckled || resting || src.loc == card) canmove = 0 - else canmove = 1 + update_canmove() regular_hud_updates() if(src.secHUD == 1) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 8c79ef206a9..ab4031bab01 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -83,7 +83,6 @@ var/current_pda_messaging = null /mob/living/silicon/pai/New(var/obj/item/device/paicard) - canmove = 0 src.loc = paicard card = paicard sradio = new(src) @@ -348,8 +347,6 @@ last_special = world.time + 200 - canmove = 1 - //I'm not sure how much of this is necessary, but I would rather avoid issues. if(istype(card.loc,/mob)) var/mob/holder = card.loc @@ -417,16 +414,21 @@ verbs -= /mob/living/silicon/pai/proc/choose_verbs + /mob/living/silicon/pai/lay_down() set name = "Rest" set category = "IC" + // Pass lying down or getting up to our pet human, if we're in a rig. if(istype(src.loc,/obj/item/device/paicard)) resting = 0 + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.force_rest(src) else resting = !resting icon_state = resting ? "[chassis]_rest" : "[chassis]" - src << "\blue You are now [resting ? "resting" : "getting up"]" + src << "You are now [resting ? "resting" : "getting up"]" canmove = !resting @@ -445,7 +447,7 @@ "You apply some [W] at [src.name]'s damaged areas.") else user << "All [src.name]'s systems are nominal." - + return else if(W.force) visible_message("[user.name] attacks [src] with [W]!") @@ -495,7 +497,6 @@ card.loc = get_turf(card) src.forceMove(card) card.forceMove(card.loc) - canmove = 0 icon_state = "[chassis]" /mob/living/silicon/pai/Bump(atom/movable/AM as mob|obj, yes) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index e4c97421fdb..6e2d31f8630 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -200,6 +200,9 @@ return mob.remote_control.relaymove(mob, direct) if(isAI(mob)) + if(istype(mob.loc, /obj/item/device/aicard)) + var/obj/O = mob.loc + return O.relaymove(mob, direct) //aicards have special relaymove stuff return AIMove(n,direct,mob) if(!mob.canmove) diff --git a/code/modules/nano/interaction/inventory_deep.dm b/code/modules/nano/interaction/inventory_deep.dm new file mode 100644 index 00000000000..98078c02c1d --- /dev/null +++ b/code/modules/nano/interaction/inventory_deep.dm @@ -0,0 +1,10 @@ +/* + This state checks if src_object is contained anywhere in the user's inventory, including bags, etc. +*/ +/var/global/datum/topic_state/deep_inventory_state/deep_inventory_state = new() + +/datum/topic_state/deep_inventory_state/can_use_topic(var/src_object, var/mob/user) + if(!user.contains(src_object)) + return STATUS_CLOSE + + return user.shared_nano_interaction() diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 97a2029d49a..87efc023b12 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -11,6 +11,12 @@ var/modifystate var/can_charge = 1 + //self-recharging + var/self_recharge = 0 //if set, the weapon will recharge itself + var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically + var/recharge_time = 4 + var/charge_tick = 0 + /obj/item/weapon/gun/energy/emp_act(severity) if(power_supply) power_supply.use(round(power_supply.charge / severity)) @@ -23,7 +29,46 @@ else power_supply = new(src) power_supply.give(power_supply.maxcharge) - return + if(self_recharge) + processing_objects.Add(src) + update_icon() + +/obj/item/weapon/gun/energy/Destroy() + if(self_recharge) + processing_objects.Remove(src) + ..() + +/obj/item/weapon/gun/energy/process() + if(self_recharge) //Every [recharge_time] ticks, recharge a shot for the cyborg + charge_tick++ + if(charge_tick < recharge_time) return 0 + charge_tick = 0 + + if(!power_supply || power_supply.charge >= power_supply.maxcharge) + return 0 // check if we actually need to recharge + + if(use_external_power) + var/obj/item/weapon/stock_parts/cell/external = get_external_power_supply() + if(!external || !external.use(charge_cost)) //Take power from the borg... + return 0 + + power_supply.give(charge_cost) //... to recharge the shot + update_icon() + return 1 + +/obj/item/weapon/gun/energy/proc/get_external_power_supply() + if(isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + return R.cell + if(istype(src.loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = src.loc + if(module.holder && module.holder.wearer) + var/mob/living/carbon/human/H = module.holder.wearer + if(istype(H) && H.back) + var/obj/item/weapon/rig/suit = H.back + if(istype(suit)) + return suit.cell + return null /obj/item/weapon/gun/energy/process_chambered() if(in_chamber) return 1 diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index abede32d203..afde00f4aed 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -29,27 +29,8 @@ obj/item/weapon/gun/energy/laser/retro desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding." force = 10 origin_tech = null - var/charge_tick = 0 - -/obj/item/weapon/gun/energy/laser/captain/New() - ..() - processing_objects.Add(src) - -/obj/item/weapon/gun/energy/laser/captain/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/gun/energy/laser/captain/process() - charge_tick++ - if(charge_tick < 4) - return 0 - charge_tick = 0 - if(!power_supply) - return 0 - power_supply.give(1000) - update_icon() - return 1 + self_recharge = 1 /obj/item/weapon/gun/energy/laser/cyborg @@ -89,6 +70,13 @@ obj/item/weapon/gun/energy/laser/retro isHandgun() return 0 +/obj/item/weapon/gun/energy/lasercannon/mounted + name = "mounted laser cannon" + self_recharge = 1 + use_external_power = 1 + recharge_time = 10 + + /obj/item/weapon/gun/energy/lasercannon/cyborg/process_chambered() if(in_chamber) return 1 @@ -119,35 +107,15 @@ obj/item/weapon/gun/energy/laser/retro projectile_type = "/obj/item/projectile/lasertag/blue" origin_tech = "combat=1;magnets=2" clumsy_check = 0 - var/charge_tick = 0 - - special_check(var/mob/living/carbon/human/M) - if(ishuman(M)) - if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) - return 1 - M << "\red You need to be wearing your laser tag vest!" - return 0 - - New() - ..() - processing_objects.Add(src) - - - Destroy() - processing_objects.Remove(src) - return ..() - - - process() - charge_tick++ - if(charge_tick < 4) return 0 - charge_tick = 0 - if(!power_supply) return 0 - power_supply.give(1000) - update_icon() - return 1 + self_recharge = 1 +/obj/item/weapon/gun/energy/laser/bluetag/special_check(var/mob/living/carbon/human/M) + if(ishuman(M)) + if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) + return 1 + M << "\red You need to be wearing your laser tag vest!" + return 0 /obj/item/weapon/gun/energy/laser/redtag name = "laser tag gun" @@ -156,30 +124,12 @@ obj/item/weapon/gun/energy/laser/retro projectile_type = "/obj/item/projectile/lasertag/red" origin_tech = "combat=1;magnets=2" clumsy_check = 0 - var/charge_tick = 0 - special_check(var/mob/living/carbon/human/M) - if(ishuman(M)) - if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) - return 1 - M << "\red You need to be wearing your laser tag vest!" - return 0 + self_recharge = 1 - New() - ..() - processing_objects.Add(src) - - - Destroy() - processing_objects.Remove(src) - return ..() - - - process() - charge_tick++ - if(charge_tick < 4) return 0 - charge_tick = 0 - if(!power_supply) return 0 - power_supply.give(1000) - update_icon() - return 1 \ No newline at end of file +/obj/item/weapon/gun/energy/laser/redtag/special_check(var/mob/living/carbon/human/M) + if(ishuman(M)) + if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) + return 1 + M << "\red You need to be wearing your laser tag vest!" + return 0 \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index de92a6022e4..e4906df5620 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -35,8 +35,10 @@ else user.update_inv_r_hand() - - +/obj/item/weapon/gun/energy/gun/mounted + name = "mounted energy gun" + self_recharge = 1 + use_external_power = 1 /obj/item/weapon/gun/energy/gun/nuclear name = "Advanced Energy Gun" @@ -44,7 +46,6 @@ icon_state = "nucgun" origin_tech = "combat=3;materials=5;powerstorage=3" var/lightfail = 0 - var/charge_tick = 0 can_flashlight = 0 can_charge = 0 diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 0edf01caed3..917171351b3 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -40,25 +40,9 @@ projectile_type = "/obj/item/projectile/energy/floramut" origin_tech = "materials=2;biotech=3;powerstorage=3" modifystate = "floramut" - var/charge_tick = 0 var/mode = 0 //0 = mutate, 1 = yield boost -/obj/item/weapon/gun/energy/floragun/New() - ..() - processing_objects.Add(src) - -/obj/item/weapon/gun/energy/floragun/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/gun/energy/floragun/process() - charge_tick++ - if(charge_tick < 4) return 0 - charge_tick = 0 - if(!power_supply) return 0 - power_supply.give(1000) - update_icon() - return 1 + self_recharge = 1 /obj/item/weapon/gun/energy/floragun/attack_self(mob/living/user as mob) switch(mode) @@ -98,27 +82,9 @@ projectile_type = "/obj/item/projectile/meteor" cell_type = "/obj/item/weapon/stock_parts/cell/potato" clumsy_check = 0 //Admin spawn only, might as well let clowns use it. - var/charge_tick = 0 - var/recharge_time = 5 //Time it takes for shots to recharge (in ticks) - New() - ..() - processing_objects.Add(src) - - - Destroy() - processing_objects.Remove(src) - return ..() - - process() - charge_tick++ - if(charge_tick < recharge_time) return 0 - charge_tick = 0 - if(!power_supply) return 0 - power_supply.give(1000) - - update_icon() - return + self_recharge = 1 + recharge_time = 5 //Time it takes for shots to recharge (in ticks) /obj/item/weapon/gun/energy/meteorgun/pen @@ -351,36 +317,12 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/weapon/gun/energy/disabler/cyborg name = "cyborg disabler" desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." - var/charge_tick = 0 - var/recharge_time = 2.5 -/obj/item/weapon/gun/energy/disabler/cyborg/New() - ..() - processing_objects.Add(src) + self_recharge = 1 + use_external_power = 1 + recharge_time = 2.5 -/obj/item/weapon/gun/energy/disabler/cyborg/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/gun/energy/disabler/cyborg/process() //Every [recharge_time] ticks, recharge a shot for the cyborg - if(power_supply.charge == power_supply.maxcharge) - return 0 - charge_tick++ - if(charge_tick < recharge_time) - return 0 - charge_tick = 0 - - if(!power_supply) return 0 //sanity - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - if(R && R.cell) - if(R.cell.use(charge_cost/10)) //Take power from the borg... - power_supply.give(charge_cost) //... to recharge the shot - - update_icon() - return 1 - /* 3d printer 'pseudo guns' for borgs */ /obj/item/weapon/gun/energy/printer @@ -392,32 +334,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. cell_type = "/obj/item/weapon/stock_parts/cell/secborg" projectile_type = "/obj/item/projectile/bullet/midbullet3" charge_cost = 200 //Yeah, let's NOT give them a 300 round clip that recharges, 20 is more reasonable and will actually hurt the borg's battery for overuse. - var/charge_tick = 0 - var/recharge_time = 5 -/obj/item/weapon/gun/energy/printer/update_icon() - return - -/obj/item/weapon/gun/energy/printer/New() - ..() - processing_objects.Add(src) - -/obj/item/weapon/gun/energy/printer/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/gun/energy/printer/process() - if(power_supply.charge == power_supply.maxcharge) - return 0 - charge_tick++ - if(charge_tick < recharge_time) - return 0 - charge_tick = 0 - - if(!power_supply) return 0 //sanity - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - if(R && R.cell) - if(R.cell.use(charge_cost/10)) //Take power from the borg... - power_supply.give(charge_cost) //...to recharge the shot - return 1 \ No newline at end of file + self_recharge = 1 + use_external_power = 1 + recharge_time = 5 \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index d1f0e244d6a..c24bb897414 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -9,6 +9,11 @@ cell_type = "/obj/item/weapon/stock_parts/cell/crap" fire_delay = 15 +/obj/item/weapon/gun/energy/taser/mounted + name = "mounted taser gun" + self_recharge = 1 + use_external_power = 1 + /obj/item/weapon/gun/energy/taser/cyborg name = "taser gun" desc = "A small, low capacity gun used for non-lethal takedowns." @@ -16,35 +21,9 @@ fire_sound = 'sound/weapons/Taser.ogg' projectile_type = "/obj/item/projectile/energy/electrode" cell_type = "/obj/item/weapon/stock_parts/cell/secborg" - var/charge_tick = 0 - var/recharge_time = 10 //Time it takes for shots to recharge (in ticks) -/obj/item/weapon/gun/energy/taser/cyborg/New() - ..() - processing_objects.Add(src) - - -/obj/item/weapon/gun/energy/taser/cyborg/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/gun/energy/taser/cyborg/process() //Every [recharge_time] ticks, recharge a shot for the cyborg - if(power_supply.charge == power_supply.maxcharge) - return 0 - charge_tick++ - if(charge_tick < recharge_time) - return 0 - charge_tick = 0 - - if(!power_supply) return 0 //sanity - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - if(R && R.cell) - if(R.cell.use(charge_cost)) //Take power from the borg... - power_supply.give(charge_cost) //... to recharge the shot - - update_icon() - return 1 + self_recharge = 1 + recharge_time = 10 //Time it takes for shots to recharge (in ticks) /obj/item/weapon/gun/energy/stunrevolver name = "stun revolver" @@ -69,25 +48,12 @@ fire_sound = 'sound/weapons/Genhit.ogg' projectile_type = "/obj/item/projectile/energy/bolt" cell_type = "/obj/item/weapon/stock_parts/cell/crap" - var/charge_tick = 0 -/obj/item/weapon/gun/energy/crossbow/New() - ..() - processing_objects.Add(src) + self_recharge = 1 -/obj/item/weapon/gun/energy/crossbow/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/gun/energy/crossbow/process() - charge_tick++ - if(charge_tick < 4) - return 0 - charge_tick = 0 - if(!power_supply) - return 0 - power_supply.give(1000) - return 1 +/obj/item/weapon/gun/energy/crossbow/ninja + name = "energy dart thrower" + projectile_type = /obj/item/projectile/energy/dart /obj/item/weapon/gun/energy/crossbow/update_icon() return diff --git a/icons/mob/feet.dmi b/icons/mob/feet.dmi index e70a67c8c88..20dc03cbabb 100644 Binary files a/icons/mob/feet.dmi and b/icons/mob/feet.dmi differ diff --git a/icons/mob/hands.dmi b/icons/mob/hands.dmi index b11cdc50f30..a4934926809 100644 Binary files a/icons/mob/hands.dmi and b/icons/mob/hands.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 8e876c59017..5112dc2543f 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/rig_back.dmi b/icons/mob/rig_back.dmi new file mode 100644 index 00000000000..923939d323e Binary files /dev/null and b/icons/mob/rig_back.dmi differ diff --git a/icons/mob/rig_modules.dmi b/icons/mob/rig_modules.dmi new file mode 100644 index 00000000000..3d18b5435ce Binary files /dev/null and b/icons/mob/rig_modules.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index ef5f3bf49f6..09265b86263 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi index 0ce17039cf7..cf82594e6c6 100644 Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index f11982a0ee4..492d22ce3df 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi index 0a9a2409bb8..36ec05a86f9 100644 Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 58f28e25951..b1d3fa7b8ef 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/rig_modules.dmi b/icons/obj/rig_modules.dmi new file mode 100644 index 00000000000..90b1873d58e Binary files /dev/null and b/icons/obj/rig_modules.dmi differ diff --git a/nano/js/nano_base_helpers.js b/nano/js/nano_base_helpers.js index 40c6b6077e5..28a41518010 100644 --- a/nano/js/nano_base_helpers.js +++ b/nano/js/nano_base_helpers.js @@ -102,6 +102,10 @@ NanoBaseHelpers = function () parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); return parts.join("."); }, + // Capitalize the first letter of a string. From http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript + capitalizeFirstLetter: function(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + }, // Display a bar. Used to show health, capacity, etc. displayBar: function(value, rangeMin, rangeMax, styleClass, showText) { diff --git a/nano/templates/hardsuit.tmpl b/nano/templates/hardsuit.tmpl new file mode 100644 index 00000000000..b32bb621c8e --- /dev/null +++ b/nano/templates/hardsuit.tmpl @@ -0,0 +1,257 @@ + + + + +{{if data.interfacelock || data.malf > 0}} +
-- HARDSUIT INTERFACE OFFLINE --
+{{else}} + {{if data.aicontrol && data.ai != 1}} +
-- HARDSUIT CONTROL OVERRIDDEN BY AI --
+ {{else}} +
+
+
+ Power supply +
+
+ {{:helper.displayBar(data.chargestatus, 0, 50, (data.chargestatus >= 35) ? 'good' : (data.chargestatus >= 15) ? 'average' : 'bad')}} {{:data.charge}}/{{:data.maxcharge}} +
+
+
+
+
+ AI control: +
+
+ {{if data.aioverride}} +
ENABLED
+ {{else}} +
DISABLED
+ {{/if}} +
+
+ {{:helper.link('Toggle', 'circle-arrow-s', {'toggle_ai_control' : 1}, null)}} +
+
+
+
+ Suit status: +
+
+ {{if data.sealing == 1}} +
PROCESSING
+ {{else}} + {{if data.seals == 1}} +
INACTIVE
+ {{else}} +
ACTIVE
+ {{/if}} + {{/if}} +
+
+ {{:helper.link('Toggle', 'circle-arrow-s', {'toggle_seals' : 1}, null)}} +
+
+
+
+ Cover status: +
+
+ {{if data.emagged || !data.securitycheck}} +
ERROR - MAINTENANCE LOCK CONTROL OFFLINE
+ {{else}} + {{if data.coverlock}} +
LOCKED
+ {{else}} +
UNLOCKED
+ {{/if}} + {{/if}} +
+
+ {{:helper.link('Toggle', 'circle-arrow-s', {'toggle_suit_lock' : 1}, null)}} +
+
+
+
+ +

Hardware

+

Suit pieces

+ +
+
+
+ Helmet: +
+
+ {{:helper.capitalizeFirstLetter(data.helmet)}} +
+ {{if data.sealing != 1}} +
+ {{:helper.link('Toggle', 'circle-arrow-s', {'toggle_piece' : 'helmet'}, null)}} +
+ {{/if}} +
+
+
+ Gauntlets: +
+
+ {{:helper.capitalizeFirstLetter(data.gauntlets)}} +
+ {{if data.sealing != 1}} +
+ {{:helper.link('Toggle', 'circle-arrow-s', {'toggle_piece' : 'gauntlets'}, null)}} +
+ {{/if}} +
+
+
+ Boots: +
+
+ {{:helper.capitalizeFirstLetter(data.boots)}} +
+ {{if data.sealing != 1}} +
+ {{:helper.link('Toggle', 'circle-arrow-s', {'toggle_piece' : 'boots'}, null)}} +
+ {{/if}} +
+
+
+ Chestpiece: +
+
+ {{:helper.capitalizeFirstLetter(data.chest)}} +
+ {{if data.sealing != 1}} +
+ {{:helper.link('Toggle', 'circle-arrow-s', {'toggle_piece' : 'chest'}, null)}} +
+ {{/if}} +
+
+ +

System modules

+ {{if data.seals == 1 || data.sealing == 1}} +

HARDSUIT SYSTEMS OFFLINE

+ {{else}} +

Selected primary system: + {{if data.primarysystem}} + {{:helper.capitalizeFirstLetter(data.primarysystem)}} + {{else}} + None + {{/if}} +

+ {{if data.modules}} +
+ {{for data.modules}} +
+
+
+
{{:helper.capitalizeFirstLetter(value.name)}}
+ {{if value.damage > 0}} +
+ {{if value.damage == 1}} + (
damaged
) + {{else}} + (
destroyed
) + {{/if}} +
+ {{/if}} +
+
+ Engage: {{:value.engagecost}} + Activate: {{:value.activecost}} + Passive: {{:value.passivecost}} +
+
+ {{:value.desc}} +
+
+ {{if value.can_use == 1}} +
+ {{:helper.link(value.engagestring, 'circle-arrow-s', {'interact_module' : value.index, 'module_mode' : 'engage'}, null)}} +
+ {{/if}} + {{if value.can_select == 1}} +
+ {{if value.name == data.primarysystem}} +
SELECTED
+ {{else}} + {{:helper.link('Select', 'circle-arrow-s', {'interact_module' : value.index, 'module_mode' : 'select'}, null)}} + {{/if}} +
+ {{/if}} + {{if value.can_toggle == 1}} +
+ {{if value.is_active == 1}} + {{:helper.link(value.deactivatestring, 'circle-arrow-s', {'interact_module' : value.index, 'module_mode' : 'deactivate'}, null)}} + {{else}} + {{:helper.link(value.activatestring, 'circle-arrow-s', {'interact_module' : value.index, 'module_mode' : 'activate'}, null)}} + {{/if}} +
+ {{/if}} +
+
+
+
+ {{if value.charges}} +
Stored charges
+
Selected:
{{:helper.capitalizeFirstLetter(value.chargetype)}} + {{for value.charges :itemValue:itemIndex}} +
+ {{:helper.link(helper.capitalizeFirstLetter(itemValue.caption), null, {'interact_module' : value.index, 'module_mode' : 'select_charge_type', 'charge_type' : itemValue.index}, null)}} +
+ {{/for}} + {{/if}} +
+
+
+ {{/for}} +
+ {{else}} + None. + {{/if}} + {{/if}} + {{/if}} +{{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 641a34617f1..bdb32953696 100644 --- a/paradise.dme +++ b/paradise.dme @@ -84,6 +84,7 @@ #include "code\_onclick\observer.dm" #include "code\_onclick\other_mobs.dm" #include "code\_onclick\overmind.dm" +#include "code\_onclick\rig.dm" #include "code\_onclick\telekinesis.dm" #include "code\_onclick\hud\_defines.dm" #include "code\_onclick\hud\action.dm" @@ -1036,6 +1037,27 @@ #include "code\modules\clothing\spacesuits\rig.dm" #include "code\modules\clothing\spacesuits\syndi.dm" #include "code\modules\clothing\spacesuits\void.dm" +#include "code\modules\clothing\spacesuits\rig\rig.dm" +#include "code\modules\clothing\spacesuits\rig\rig_attackby.dm" +#include "code\modules\clothing\spacesuits\rig\rig_pieces.dm" +#include "code\modules\clothing\spacesuits\rig\rig_verbs.dm" +#include "code\modules\clothing\spacesuits\rig\rig_wiring.dm" +#include "code\modules\clothing\spacesuits\rig\modules\combat.dm" +#include "code\modules\clothing\spacesuits\rig\modules\computer.dm" +#include "code\modules\clothing\spacesuits\rig\modules\modules.dm" +#include "code\modules\clothing\spacesuits\rig\modules\ninja.dm" +#include "code\modules\clothing\spacesuits\rig\modules\utility.dm" +#include "code\modules\clothing\spacesuits\rig\modules\vision.dm" +#include "code\modules\clothing\spacesuits\rig\suits\alien.dm" +#include "code\modules\clothing\spacesuits\rig\suits\combat.dm" +#include "code\modules\clothing\spacesuits\rig\suits\ert.dm" +#include "code\modules\clothing\spacesuits\rig\suits\light.dm" +#include "code\modules\clothing\spacesuits\rig\suits\merc.dm" +#include "code\modules\clothing\spacesuits\rig\suits\station.dm" +#include "code\modules\clothing\spacesuits\void\merc.dm" +#include "code\modules\clothing\spacesuits\void\station.dm" +#include "code\modules\clothing\spacesuits\void\void.dm" +#include "code\modules\clothing\spacesuits\void\wizard.dm" #include "code\modules\clothing\suits\alien.dm" #include "code\modules\clothing\suits\armor.dm" #include "code\modules\clothing\suits\bio.dm" @@ -1504,6 +1526,7 @@ #include "code\modules\nano\interaction\contained.dm" #include "code\modules\nano\interaction\default.dm" #include "code\modules\nano\interaction\inventory.dm" +#include "code\modules\nano\interaction\inventory_deep.dm" #include "code\modules\nano\interaction\physical.dm" #include "code\modules\nano\interaction\self.dm" #include "code\modules\nano\interaction\zlevel.dm"