diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index ed0636dcb75..776b01d3a11 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -92,6 +92,13 @@ What is the naming convention for planes or layers? // Mob planes #define MOB_PLANE -25 #define BELOW_MOB_LAYER 3.9 // Should be converted to plane swaps + #define MECH_BASE_LAYER 4.01 + #define MECH_INTERMEDIATE_LAYER 4.02 + #define MECH_PILOT_LAYER 4.03 + #define MECH_LEG_LAYER 4.04 + #define MECH_COCKPIT_LAYER 4.05 + #define MECH_ARM_LAYER 4.06 + #define MECH_GEAR_LAYER 4.07 #define ABOVE_MOB_LAYER 4.1 // Should be converted to plane swaps #define ABOVE_MOB_PLANE -24 diff --git a/code/_onclick/MouseDrag.dm b/code/_onclick/MouseDrag.dm new file mode 100644 index 00000000000..8dec586d207 --- /dev/null +++ b/code/_onclick/MouseDrag.dm @@ -0,0 +1,15 @@ +//If we intercept it return true else return false +/atom/proc/RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user) + return FALSE + +/mob/proc/OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) + if(istype(loc, /atom)) + var/atom/A = loc + if(A.RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, src)) + return + + if(over_object && !incapacitated()) + var/obj/item/gun/gun = get_active_hand() + if(istype(gun) && gun.can_autofire()) + set_dir(get_dir(src, over_object)) + gun.Fire(get_turf(over_object), src, params, (get_dist(over_object, src) <= 1), FALSE) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index d7759ed12f6..9bdb8fa8a70 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -79,6 +79,11 @@ var/obj/mecha/M = loc return M.click_action(A, src, params) + else if(!isturf(loc) && !isnull(loc)) // If you are in Nullspace you should be adminhelping not clicking. + var/atom/movable/AM = loc + if(AM.allow_click_through(A, src)) + return AM.click_through(A, src, params) + if(restrained()) setClickCooldown(10) RestrainedClickOn(A) @@ -141,6 +146,12 @@ trigger_aiming(TARGET_CAN_CLICK) return 1 +/atom/movable/proc/click_through(var/atom/target, var/atom/movable/source, var/params) + return FALSE + +/atom/movable/proc/allow_click_through(var/atom/target, var/atom/movable/source) + return FALSE + /mob/proc/setClickCooldown(var/timeout) next_click = max(world.time + timeout, next_click) diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index d1822f88ab2..e7ae7b0f3b4 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -13,6 +13,7 @@ var/repair = 0 var/list/chargable_types = list( /obj/mecha, + /mob/living/exosuit, /mob/living/silicon/robot/platform ) @@ -58,7 +59,7 @@ var/done = FALSE var/obj/mecha/mech = charging var/obj/item/cell/cell = charging.get_cell() - if(cell) + if(cell) var/t = min(charge, cell.maxcharge - cell.charge) if(t > 0) if(istype(mech)) diff --git a/code/game/objects/items/weapons/id cards/station_ids.dm b/code/game/objects/items/weapons/id cards/station_ids.dm index 41c68343f7f..264fc239359 100644 --- a/code/game/objects/items/weapons/id cards/station_ids.dm +++ b/code/game/objects/items/weapons/id cards/station_ids.dm @@ -8,7 +8,7 @@ SPECIES_TESHARI = 'icons/mob/species/teshari/id.dmi' ) - var/access = list() + var/list/access = list() var/registered_name = "Unknown" // The name registered_name on the card var/registered_id_string = "ID Card" slot_flags = SLOT_ID | SLOT_EARS diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index c0a2f0cbc5d..e844d0f9000 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -361,6 +361,12 @@ else . = ..() +/client/MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) + . = ..() + var/mob/living/M = mob + if(istype(M)) + M.OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) + /client/proc/last_activity_seconds() return inactivity / 10 diff --git a/code/modules/exosuits/_exosuit_setup.dm b/code/modules/exosuits/_exosuit_setup.dm new file mode 100644 index 00000000000..c052b069bc7 --- /dev/null +++ b/code/modules/exosuits/_exosuit_setup.dm @@ -0,0 +1,44 @@ +GLOBAL_DATUM_INIT(default_hardpoint_background, /image, null) +GLOBAL_DATUM_INIT(hardpoint_error_icon, /image, null) +GLOBAL_DATUM_INIT(hardpoint_bar_empty, /image, null) + +GLOBAL_LIST_INIT(hardpoint_bar_cache, new) +GLOBAL_LIST_INIT(mech_damage_overlay_cache, new) +GLOBAL_LIST_INIT(mech_image_cache, new) +GLOBAL_LIST_INIT(mech_icon_cache, new) +GLOBAL_LIST_INIT(mech_weapon_overlays, icon_states('icons/mob/mecha/mech_weapon_overlays.dmi')) + +#define HARDPOINT_BACK "back" +#define HARDPOINT_LEFT_HAND "left hand" +#define HARDPOINT_RIGHT_HAND "right hand" +#define HARDPOINT_LEFT_SHOULDER "left shoulder" +#define HARDPOINT_RIGHT_SHOULDER "right shoulder" +#define HARDPOINT_HEAD "head" + +// No software required: taser. light, radio. +#define MECH_SOFTWARE_UTILITY "utility equipment" // Plasma torch, clamp, drill. +#define MECH_SOFTWARE_MEDICAL "medical support systems" // Sleeper. +#define MECH_SOFTWARE_WEAPONS "standard weapon systems" // Ballistics and energy weapons. +#define MECH_SOFTWARE_ADVWEAPONS "advanced weapon systems" // Railguns, missile launcher. +#define MECH_SOFTWARE_ENGINEERING "advanced engineering systems" // RCD. + +// EMP damage points before various effects occur. +#define EMP_GUI_DISRUPT 5 // 1 ion rifle shot == 8. +#define EMP_MOVE_DISRUPT 10 // 2 shots. +#define EMP_ATTACK_DISRUPT 20 // 3 shots. + +//About components +#define MECH_COMPONENT_DAMAGE_UNDAMAGED 1 +#define MECH_COMPONENT_DAMAGE_DAMAGED 2 +#define MECH_COMPONENT_DAMAGE_DAMAGED_BAD 3 +#define MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL 4 + +//Construction +#define EXOFRAME_REINFORCED 1 +#define EXOFRAME_REINFORCED_SECURE 2 +#define EXOFRAME_REINFORCED_WELDED 3 + +#define EXOFRAME_WIRED 1 +#define EXOFRAME_WIRED_ADJUSTED 2 + +#define BAR_CAP 12 diff --git a/code/modules/exosuits/component/_components.dm b/code/modules/exosuits/component/_components.dm new file mode 100644 index 00000000000..c07a5187140 --- /dev/null +++ b/code/modules/exosuits/component/_components.dm @@ -0,0 +1,160 @@ +/obj/item/mech_component + icon = 'icons/mob/mecha/mech_parts_held.dmi' + w_class = ITEMSIZE_HUGE + gender = PLURAL + color = COLOR_GUNMETAL + + var/on_mech_icon = 'icons/mob/mecha/mech_parts.dmi' + var/exosuit_desc_string + var/total_damage = 0 + var/brute_damage = 0 + var/burn_damage = 0 + var/max_damage = 60 + var/damage_state = MECH_COMPONENT_DAMAGE_UNDAMAGED + var/list/has_hardpoints = list() + var/decal + var/power_use = 0 + matter = list(MATERIAL_STEEL = 15000, MATERIAL_PLASTIC = 1000, MATERIAL_OSMIUM = 500) + dir = SOUTH + +/obj/item/mech_component/proc/query_color(var/mob/living/user) + if(istype(user)) + var/input = input("Modify part color:", "Color", color) as color|null + if(input) + return set_colour(input) + +/obj/item/mech_component/proc/set_colour(new_colour) + var/last_colour = color + color = new_colour + return color != last_colour + +/obj/item/mech_component/emp_act(var/severity) + take_burn_damage(rand((10 - (severity*3)),15-(severity*4))) + for(var/obj/item/thing in contents) + thing.emp_act(severity) + +/obj/item/mech_component/examine(mob/user) + . = ..() + if(ready_to_install()) + . += SPAN_NOTICE("It is ready for installation.") + else + . += show_missing_parts(user) + +//These icons have multiple directions but before they're attached we only want south. +/obj/item/mech_component/set_dir() + ..(SOUTH) + +/obj/item/mech_component/proc/show_missing_parts(var/mob/user) + return + +/obj/item/mech_component/proc/prebuild() + return + +/obj/item/mech_component/proc/install_component(var/obj/item/thing, var/mob/user) + if(!user.unEquip(thing, src)) + return FALSE + user.visible_message(SPAN_NOTICE("\The [user] installs \the [thing] in \the [src].")) + thing.forceMove(src) + update_components() + return TRUE + +/obj/item/mech_component/proc/update_health() + total_damage = min(max_damage, brute_damage + burn_damage) + damage_state = between(MECH_COMPONENT_DAMAGE_UNDAMAGED, round((total_damage/max_damage) * 4), MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL) + +/obj/item/mech_component/proc/ready_to_install() + return TRUE + +/obj/item/mech_component/proc/repair_brute_damage(var/amt) + take_brute_damage(-amt) + +/obj/item/mech_component/proc/repair_burn_damage(var/amt) + take_burn_damage(-amt) + +/obj/item/mech_component/proc/take_brute_damage(var/amt) + brute_damage = max(0, brute_damage + amt) + update_health() + if(total_damage == max_damage) + take_component_damage(amt,0) + +/obj/item/mech_component/proc/take_burn_damage(var/amt) + burn_damage = max(0, burn_damage + amt) + update_health() + if(total_damage == max_damage) + take_component_damage(0,amt) + +/obj/item/mech_component/proc/take_component_damage(var/brute, var/burn) + var/list/damageable_components = list() + for(var/obj/item/robot_parts/robot_component/RC in contents) + damageable_components += RC + if(!damageable_components.len) return + var/obj/item/robot_parts/robot_component/RC = pick(damageable_components) + if(RC.take_damage(brute, burn)) + qdel(RC) + update_components() + +/obj/item/mech_component/attackby(var/obj/item/thing, var/mob/user) + if(thing.has_tool_quality(TOOL_SCREWDRIVER)) + if(contents.len) + var/obj/item/removed = input("Which component would you like to remove") as null|anything in contents + if(!removed) + return + user.visible_message(SPAN_NOTICE("\The [user] removes \the [removed] from \the [src].")) + removed.forceMove(user.loc) + playsound(user.loc, 'sound/effects/pop.ogg', 50, 0) + update_components() + else + to_chat(user, SPAN_WARNING("There is nothing to remove.")) + return + if(istype(thing, /obj/item/weldingtool)) + repair_brute_generic(thing, user) + return + if(istype(thing, /obj/item/stack/cable_coil)) + repair_burn_generic(thing, user) + return + + if(thing.has_tool_quality(TOOL_MULTITOOL)) + query_color(user) + return + + return ..() + +/obj/item/mech_component/proc/update_components() + return + +/obj/item/mech_component/proc/repair_brute_generic(var/obj/item/weldingtool/WT, var/mob/user) + if(!istype(WT)) + return + if(!brute_damage) + to_chat(user, SPAN_NOTICE("You inspect \the [src] but find nothing to repair.")) + return + if(!WT.welding) + to_chat(user, SPAN_WARNING("Turn \the [WT] on, first.")) + return + if(WT.remove_fuel(1, user)) + if(do_after(user, 10, src) && brute_damage) + repair_brute_damage(10) + to_chat(user, SPAN_NOTICE("You mend the damage to \the [src].")) + playsound(user.loc, 'sound/items/Welder.ogg', 25, 1) + +/obj/item/mech_component/proc/repair_burn_generic(var/obj/item/stack/cable_coil/CC, var/mob/user) + if(!istype(CC)) + return + if(!burn_damage) + to_chat(user, SPAN_NOTICE("\The [src]'s wiring doesn't need replacing.")) + return + + if(CC.get_amount() < 6) + to_chat(user, SPAN_WARNING("You need at least 6 units of cable to repair this section.")) + return + + user.visible_message("\The [user] begins replacing the wiring of \the [src]...") + + if(do_after(user, 10, src) && burn_damage) + if(QDELETED(CC) || QDELETED(src) || !CC.use(6)) + return + + repair_burn_damage(25) + to_chat(user, SPAN_NOTICE("You mend the damage to \the [src]'s wiring.")) + playsound(user.loc, 'sound/items/Deconstruct.ogg', 25, 1) + return diff --git a/code/modules/exosuits/component/armour.dm b/code/modules/exosuits/component/armour.dm new file mode 100644 index 00000000000..3caf7df715e --- /dev/null +++ b/code/modules/exosuits/component/armour.dm @@ -0,0 +1,54 @@ +/obj/item/robot_parts/robot_component/armour/exosuit + name = "exosuit armour plating" + armor = list( + melee = 70, + bullet = 30, + laser = 40, + energy = 10, + bomb = 30, + bio = 100, + rad = 20 + ) + origin_tech = list(TECH_MATERIAL = 1) + +/obj/item/robot_parts/robot_component/armour/exosuit/radproof + name = "radiation-proof armour plating" + desc = "A fully enclosed radiation hardened shell designed to protect the pilot from radiation" + armor = list( + melee = 70, + bullet = 30, + laser = 40, + energy = 10, + bomb = 30, + bio = 100, + rad = 100 + ) + origin_tech = list(TECH_MATERIAL = 3) + +/obj/item/robot_parts/robot_component/armour/exosuit/em + name = "EM-shielded armour plating" + desc = "A shielded plating that surrounds the eletronics and protects them from electromagnetic radiation" + armor = list( + melee =70, + bullet = 25, + laser = 25, + energy = 100, + bomb = 10, + bio = 100, + rad = 20 + ) + origin_tech = list(TECH_MATERIAL = 3) + +/obj/item/robot_parts/robot_component/armour/exosuit/combat + name = "heavy combat plating" + desc = "Plating designed to deflect incoming attacks and explosions" + armor = list( + melee = 70, + bullet = 60, + laser = 70, + energy = 10, + bomb = 60, + bio = 100, + rad = 20 + ) + origin_tech = list(TECH_MATERIAL = 5) diff --git a/code/modules/exosuits/component/arms.dm b/code/modules/exosuits/component/arms.dm new file mode 100644 index 00000000000..673565c0e78 --- /dev/null +++ b/code/modules/exosuits/component/arms.dm @@ -0,0 +1,111 @@ +/obj/item/mech_component/manipulators + name = "arms" + pixel_y = -12 + icon_state = "loader_arms" + has_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + + var/melee_damage = 15 + var/action_delay = 15 + var/obj/item/robot_parts/robot_component/actuator/motivator + power_use = 10 + +/obj/item/mech_component/manipulators/Destroy() + QDEL_NULL(motivator) + . = ..() + +/obj/item/mech_component/manipulators/show_missing_parts(var/mob/user) + if(!motivator) + . += SPAN_WARNING("It is missing an actuator.") + +/obj/item/mech_component/manipulators/ready_to_install() + return motivator + +/obj/item/mech_component/manipulators/prebuild() + motivator = new(src) + +/obj/item/mech_component/manipulators/attackby(var/obj/item/thing, var/mob/user) + if(istype(thing,/obj/item/robot_parts/robot_component/actuator)) + if(motivator) + to_chat(user, SPAN_WARNING("\The [src] already has an actuator installed.")) + else if(install_component(thing, user)) + motivator = thing + return + return ..() + +/obj/item/mech_component/manipulators/update_components() + motivator = locate() in src + +/* + * Variants + */ + +/obj/item/mech_component/manipulators/combat + name = "combat arms" + exosuit_desc_string = "flexible, advanced manipulators" + icon_state = "combat_arms" + melee_damage = 5 + action_delay = 10 + power_use = 50 + +/obj/item/mech_component/manipulators/industrial + name = "exosuit arms" + exosuit_desc_string = "heavy-duty industrial lifters" + icon_state = "industrial_arms" + max_damage = 70 + power_use = 30 + desc = "The Industrial Digital Interaction Manifolds allow you poke untold dangers from the relative safety of your cockpit." + +/obj/item/mech_component/manipulators/heavy + name = "heavy arms" + exosuit_desc_string = "super-heavy reinforced manipulators" + icon_state = "heavy_arms" + desc = "Designed to function where any other piece of equipment would have long fallen apart, the Hephaestus Superheavy Lifter series can take a beating and excel at delivering it." + melee_damage = 25 + action_delay = 15 + max_damage = 90 + power_use = 60 + +/obj/item/mech_component/manipulators/light + name = "light arms" + exosuit_desc_string = "lightweight, segmented manipulators" + icon_state = "light_arms" + melee_damage = 5 + action_delay = 15 + max_damage = 40 + power_use = 10 + desc = "As flexible as they are fragile, these Vey-Med manipulators can follow a pilot's movements in close to real time." + +/obj/item/mech_component/manipulators/mercenary + name = "mercenary arms" + exosuit_desc_string = "super-mercenary reinforced manipulators" + icon_state = "mercenary_arms" + desc = "Designed to function where any other piece of equipment would have long fallen apart, the Supermercenary Lifter series can take a beating and excel at delivering it." + melee_damage = 25 + action_delay = 15 + max_damage = 90 + power_use = 60 + +/obj/item/mech_component/manipulators/modern + name = "modern arms" + exosuit_desc_string = "flexible, advanced manipulators" + icon_state = "modern_arms" + melee_damage = 5 + action_delay = 10 + power_use = 50 + +/obj/item/mech_component/manipulators/powerloader + name = "exosuit arms" + exosuit_desc_string = "heavy-duty industrial lifters" + max_damage = 70 + power_use = 30 + desc = "The Xion Industrial Digital Interaction Manifolds allow you poke untold dangers from the relative safety of your cockpit." + +/obj/item/mech_component/manipulators/sleek + name = "sleek arms" + exosuit_desc_string = "sleekweight, segmented manipulators" + icon_state = "sleek_arms" + melee_damage = 5 + action_delay = 15 + max_damage = 40 + power_use = 10 + desc = "As flexible as they are fragile, these Vey-Med manipulators can follow a pilot's movements in close to real time." diff --git a/code/modules/exosuits/component/body.dm b/code/modules/exosuits/component/body.dm new file mode 100644 index 00000000000..7a695927690 --- /dev/null +++ b/code/modules/exosuits/component/body.dm @@ -0,0 +1,389 @@ +/obj/item/mech_component/chassis + name = "body" + icon_state = "loader_body" + gender = NEUTER + + var/mech_health = 300 + var/obj/item/cell/cell + var/obj/item/robot_parts/robot_component/diagnosis_unit/diagnostics + var/obj/item/robot_parts/robot_component/armour/exosuit/armour + var/obj/machinery/portable_atmospherics/canister/air_supply + var/datum/gas_mixture/cockpit + var/transparent_cabin = FALSE + var/hide_pilot = FALSE + var/hatch_descriptor = "cockpit" + var/list/pilot_positions + var/pilot_coverage = 100 + var/min_pilot_size = MOB_SMALL + var/max_pilot_size = MOB_LARGE + has_hardpoints = list(HARDPOINT_BACK, HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER) + +/obj/item/mech_component/chassis/Destroy() + QDEL_NULL(cell) + QDEL_NULL(diagnostics) + QDEL_NULL(armour) + QDEL_NULL(air_supply) + . = ..() + +/obj/item/mech_component/chassis/update_components() + diagnostics = locate() in src + cell = locate() in src + armour = locate() in src + air_supply = locate() in src + +/obj/item/mech_component/chassis/show_missing_parts(var/mob/user) + if(!cell) + . += SPAN_WARNING("It is missing a power cell.") + if(!diagnostics) + . += SPAN_WARNING("It is missing a diagnostics unit.") + if(!armour) + . += SPAN_WARNING("It is missing exosuit armour plating.") + +/obj/item/mech_component/chassis/Initialize() + . = ..() + cockpit = new(20) + if(loc) + var/datum/gas_mixture/air = loc.return_air() + if(air) + cockpit.equalize(air) + air_supply = new /obj/machinery/portable_atmospherics/canister/air(src) + + if(isnull(pilot_positions)) + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 0), + "[SOUTH]" = list("x" = 8, "y" = 0), + "[EAST]" = list("x" = 8, "y" = 0), + "[WEST]" = list("x" = 8, "y" = 0) + ) + ) + +/obj/item/mech_component/chassis/proc/update_air(var/take_from_supply) + + var/changed + if(!take_from_supply || pilot_coverage < 100) + var/turf/T = get_turf(src) + if(!T) + return + cockpit.equalize(T.return_air()) + changed = TRUE + else if(air_supply) + var/env_pressure = cockpit.return_pressure() + var/pressure_delta = air_supply.release_pressure - env_pressure + if((air_supply.air_contents.temperature > 0) && (pressure_delta > 0)) + var/transfer_moles = calculate_transfer_moles(air_supply.air_contents, cockpit, pressure_delta) + transfer_moles = min(transfer_moles, (air_supply.release_flow_rate/air_supply.air_contents.volume)*air_supply.air_contents.total_moles) + pump_gas_passive(air_supply, air_supply.air_contents, cockpit, transfer_moles) + changed = TRUE + if(changed) + cockpit.react() + +/obj/item/mech_component/chassis/ready_to_install() + return (cell && diagnostics && armour) + +/obj/item/mech_component/chassis/prebuild() + diagnostics = new(src) + cell = new /obj/item/cell/high(src) + cell.charge = cell.maxcharge + +/obj/item/mech_component/chassis/attackby(var/obj/item/thing, var/mob/user) + if(istype(thing,/obj/item/robot_parts/robot_component/diagnosis_unit)) + if(diagnostics) + to_chat(user, SPAN_WARNING("\The [src] already has a diagnostic system installed.")) + return + if(install_component(thing, user)) diagnostics = thing + else if(istype(thing, /obj/item/cell)) + if(cell) + to_chat(user, SPAN_WARNING("\The [src] already has a cell installed.")) + return + if(install_component(thing,user)) cell = thing + else if(istype(thing, /obj/item/robot_parts/robot_component/armour/exosuit)) + if(armour) + to_chat(user, SPAN_WARNING("\The [src] already has armour installed.")) + return + if(install_component(thing, user)) + armour = thing + else + return ..() + +/obj/item/mech_component/chassis/MouseDrop_T(atom/dropping, mob/user) + var/obj/machinery/portable_atmospherics/canister/C = dropping + if(istype(C) && do_after(user, 5, src)) + to_chat(user, SPAN_NOTICE("You install the canister in the [src].")) + if(air_supply) + air_supply.forceMove(get_turf(src)) + air_supply = null + C.forceMove(src) + update_components() + else . = ..() + +/obj/item/mech_component/chassis/proc/get_armor_values() + if(armour) + return armour.armor + + return list( + melee =5, + bullet = 5, + laser = 5, + energy = 5, + bomb = 0, + bio = 5, + rad = 5 + ) + +/obj/item/mech_component/chassis/proc/get_armor(var/type) + var/list/checking = get_armor_values() + return checking[type] + +/* + * Variants + */ + +/obj/item/mech_component/chassis/combat + name = "sealed exosuit chassis" + hatch_descriptor = "canopy" + pilot_coverage = 100 + transparent_cabin = TRUE + exosuit_desc_string = "an armoured chassis" + icon_state = "combat_body" + power_use = 40 + mech_health = 350 + +/obj/item/mech_component/chassis/combat/prebuild() + . = ..() + armor = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src) + +/obj/item/mech_component/chassis/combat/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 8), + "[SOUTH]" = list("x" = 8, "y" = 8), + "[EAST]" = list("x" = 4, "y" = 8), + "[WEST]" = list("x" = 12, "y" = 8) + ) + ) + + . = ..() + +/obj/item/mech_component/chassis/industrial + name = "open exosuit chassis" + hatch_descriptor = "roll cage" + pilot_coverage = 40 + exosuit_desc_string = "an industrial rollcage" + desc = "An industrial roll cage. Technically OSHA compliant. Technically." + icon_state = "industrial_body" + max_damage = 100 + power_use = 0 + +/obj/item/mech_component/chassis/industrial/prebuild() + . = ..() + armour = new /obj/item/robot_parts/robot_component/armour/exosuit(src) + +/obj/item/mech_component/chassis/industrial/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 8), + "[SOUTH]" = list("x" = 8, "y" = 8), + "[EAST]" = list("x" = 8, "y" = 8), + "[WEST]" = list("x" = 8, "y" = 8) + ), + list( + "[NORTH]" = list("x" = 8, "y" = 16), + "[SOUTH]" = list("x" = 8, "y" = 16), + "[EAST]" = list("x" = 0, "y" = 16), + "[WEST]" = list("x" = 16, "y" = 16) + ) + ) + . = ..() + +/obj/item/mech_component/chassis/heavy + name = "reinforced exosuit chassis" + hatch_descriptor = "hatch" + desc = "The HI-Koloss chassis is a veritable juggernaut, capable of protecting a pilot even in the most hostile of environments. It handles like a battlecruiser, however." + pilot_coverage = 100 + exosuit_desc_string = "a heavily armoured chassis" + icon_state = "heavy_body" + max_damage = 150 + mech_health = 500 + power_use = 50 + has_hardpoints = list(HARDPOINT_BACK) + +/obj/item/mech_component/chassis/heavy/prebuild() + . = ..() + armor = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src) + +/obj/item/mech_component/chassis/light + name = "light exosuit chassis" + hatch_descriptor = "canopy" + pilot_coverage = 100 + transparent_cabin = TRUE + hide_pilot = TRUE //Sprite too small, legs clip through, so for now hide pilot + exosuit_desc_string = "an open and light chassis" + icon_state = "light_body" + max_damage = 50 + power_use = 5 + mech_health = 200 + has_hardpoints = list(HARDPOINT_BACK) + desc = "The Veymed Odysseus series cockpits combine ultralight materials and clear aluminum laminates to provide an optimized cockpit experience." + +/obj/item/mech_component/chassis/light/prebuild() + . = ..() + armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src) + +/obj/item/mech_component/chassis/light/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = -2), + "[SOUTH]" = list("x" = 8, "y" = -2), + "[EAST]" = list("x" = 1, "y" = -2), + "[WEST]" = list("x" = 9, "y" = -2) + ) + ) + . = ..() + +/obj/item/mech_component/chassis/mercenary + name = "reinforced exosuit chassis" + hatch_descriptor = "hatch" + desc = "The HI-Koloss chassis is a veritable juggernaut, capable of protecting a pilot even in the most hostile of environments. It handles like a battlecruiser, however." + pilot_coverage = 100 + exosuit_desc_string = "a heavily armoured chassis" + icon_state = "mercenary_body" + max_damage = 150 + mech_health = 500 + power_use = 50 + has_hardpoints = list(HARDPOINT_BACK) + +/obj/item/mech_component/chassis/mercenary/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 8), + "[SOUTH]" = list("x" = 9, "y" = 2), + "[EAST]" = list("x" = 4, "y" = 8), + "[WEST]" = list("x" = 12, "y" = 8) + ) + ) + . = ..() + +/obj/item/mech_component/chassis/mercenary/prebuild() + . = ..() + armour = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src) + +/obj/item/mech_component/chassis/pod + name = "spherical exosuit chassis" + hatch_descriptor = "hatch" + pilot_coverage = 100 + transparent_cabin = TRUE + exosuit_desc_string = "a spherical chassis" + icon_state = "pod_body" + max_damage = 70 + power_use = 5 + has_hardpoints = list(HARDPOINT_BACK) + desc = "The Katamari series cockpits won a massive government tender a few years back. No one is sure why, but these terrible things keep popping up on every government-run facility." + +/obj/item/mech_component/chassis/pod/prebuild() + . = ..() + armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src) + +/obj/item/mech_component/chassis/pod/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 4), + "[SOUTH]" = list("x" = 8, "y" = 4), + "[EAST]" = list("x" = 12, "y" = 4), + "[WEST]" = list("x" = 4, "y" = 4) + ), + list( + "[NORTH]" = list("x" = 8, "y" = 8), + "[SOUTH]" = list("x" = 8, "y" = 8), + "[EAST]" = list("x" = 10, "y" = 8), + "[WEST]" = list("x" = 6, "y" = 8) + ) + ) + . = ..() + +/obj/item/mech_component/chassis/pod/prebuild() + . = ..() + armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src) + +/obj/item/mech_component/chassis/modern + name = "sealed exosuit chassis" + hatch_descriptor = "canopy" + pilot_coverage = 100 + hide_pilot = TRUE + exosuit_desc_string = "an armoured chassis" + icon_state = "modern_body" + power_use = 40 + mech_health = 350 + +/obj/item/mech_component/chassis/modern/prebuild() + . = ..() + armor = new /obj/item/robot_parts/robot_component/armour/exosuit/combat(src) + +/obj/item/mech_component/chassis/modern/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 8), + "[SOUTH]" = list("x" = 8, "y" = 8), + "[EAST]" = list("x" = 4, "y" = 8), + "[WEST]" = list("x" = 12, "y" = 8) + ) + ) + . = ..() + +/obj/item/mech_component/chassis/powerloader + name = "open exosuit chassis" + hatch_descriptor = "roll cage" + pilot_coverage = 40 + exosuit_desc_string = "an industrial rollcage" + desc = "A Xion industrial brand roll cage. Technically OSHA compliant. Technically." + max_damage = 100 + power_use = 0 + +/obj/item/mech_component/chassis/powerloader/prebuild() + . = ..() + armour = new /obj/item/robot_parts/robot_component/armour/exosuit(src) + +/obj/item/mech_component/chassis/powerloader/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 8), + "[SOUTH]" = list("x" = 8, "y" = 8), + "[EAST]" = list("x" = 8, "y" = 8), + "[WEST]" = list("x" = 8, "y" = 8) + ), + list( + "[NORTH]" = list("x" = 8, "y" = 16), + "[SOUTH]" = list("x" = 8, "y" = 16), + "[EAST]" = list("x" = 0, "y" = 16), + "[WEST]" = list("x" = 16, "y" = 16) + ) + ) + . = ..() + +/obj/item/mech_component/chassis/sleek + name = "sleek exosuit chassis" + hatch_descriptor = "canopy" + pilot_coverage = 100 + transparent_cabin = TRUE + exosuit_desc_string = "an open and sleek chassis" + icon_state = "sleek_body" + max_damage = 50 + mech_health = 200 + power_use = 5 + has_hardpoints = list(HARDPOINT_BACK) + desc = "The Veymed Odysseus series cockpits combine ultrasleek materials and clear aluminium laminates to provide an optimized cockpit experience." + +/obj/item/mech_component/chassis/sleek/prebuild() + . = ..() + armour = new /obj/item/robot_parts/robot_component/armour/exosuit/radproof(src) + +/obj/item/mech_component/chassis/sleek/Initialize() + pilot_positions = list( + list( + "[NORTH]" = list("x" = 8, "y" = 0), + "[SOUTH]" = list("x" = 8, "y" = 0), + "[EAST]" = list("x" = 3, "y" = 0), + "[WEST]" = list("x" = 13, "y" = 0) + ) + ) + . = ..() diff --git a/code/modules/exosuits/component/frame.dm b/code/modules/exosuits/component/frame.dm new file mode 100644 index 00000000000..783c5700da0 --- /dev/null +++ b/code/modules/exosuits/component/frame.dm @@ -0,0 +1,332 @@ +/obj/item/frame_holder + matter = list(MATERIAL_STEEL = 175000, MATERIAL_PLASTIC = 50000, MATERIAL_OSMIUM = 30000) + +/obj/item/frame_holder/Initialize(mapload, var/newloc) + ..() + new /obj/structure/heavy_vehicle_frame(newloc) + return INITIALIZE_HINT_QDEL + +/obj/structure/heavy_vehicle_frame + name = "exosuit frame" + desc = "The frame for an exosuit, apparently." + icon = 'icons/mob/mecha/mech_parts.dmi' + icon_state = "backbone" + density = 1 + pixel_x = -8 + + var/datum/material/material = null + + // Holders for the final product. + var/obj/item/mech_component/manipulators/arms + var/obj/item/mech_component/propulsion/legs + var/obj/item/mech_component/sensors/head + var/obj/item/mech_component/chassis/body + var/is_wired = 0 + var/is_reinforced = 0 + var/set_name + dir = SOUTH + +/obj/structure/heavy_vehicle_frame/proc/set_colour(var/new_colour) + var/painted_component = FALSE + for(var/obj/item/mech_component/comp in list(body, arms, legs, head)) + if(comp.set_colour(new_colour)) + painted_component = TRUE + if(painted_component) + update_icon() + +/obj/structure/heavy_vehicle_frame/Destroy() + QDEL_NULL(arms) + QDEL_NULL(legs) + QDEL_NULL(head) + QDEL_NULL(body) + . = ..() + +/obj/structure/heavy_vehicle_frame/examine(mob/user) + . = ..() + if(!arms) + . += SPAN_WARNING("It is missing manipulators.") + if(!legs) + . += SPAN_WARNING("It is missing propulsion.") + if(!head) + . += SPAN_WARNING("It is missing sensors.") + if(!body) + . += SPAN_WARNING("It is missing a chassis.") + if(is_wired == EXOFRAME_WIRED) + . += SPAN_WARNING("It has not had its wiring adjusted.") + else if(!is_wired) + . += SPAN_WARNING("It has not yet been wired.") + if(is_reinforced == EXOFRAME_REINFORCED) + . += SPAN_WARNING("It has not had its internal reinforcement secured.") + else if(is_reinforced == EXOFRAME_REINFORCED_SECURE) + . += SPAN_WARNING("It has not had its internal reinforcement welded in.") + else if(!is_reinforced) + . += SPAN_WARNING("It does not have any internal reinforcement.") + +/obj/structure/heavy_vehicle_frame/update_icon() + var/list/new_overlays = get_mech_images(list(legs, head, body, arms), layer) + if(body) + density = TRUE + overlays += get_mech_image(null, "[body.icon_state]_cockpit", body.icon, body.color) + if(body.pilot_coverage < 100 || body.transparent_cabin) + new_overlays += get_mech_image(null, "[body.icon_state]_open_overlay", body.icon, body.color) + else + density = FALSE + overlays = new_overlays + if(density != opacity) + set_opacity(density) + +/obj/structure/heavy_vehicle_frame/set_dir() + ..(SOUTH) + +/obj/structure/heavy_vehicle_frame/attackby(var/obj/item/thing, var/mob/user) + + // Removing components. + if(thing.has_tool_quality(TOOL_CROWBAR)) + if(is_reinforced == EXOFRAME_REINFORCED) + if(!do_after(user, 5) || !material) + return + user.visible_message(SPAN_NOTICE("\The [user] crowbars the reinforcement off \the [src].")) + material.place_sheet(src.loc, 10) + material = null + is_reinforced = 0 + return + + if(!arms && !body && !legs && !head) + to_chat(user, SPAN_WARNING("There are no components to remove.")) + return + + var/to_remove = input("Which component would you like to remove") as null|anything in list(arms, body, legs, head) + + if(!to_remove) + return + + if(uninstall_component(to_remove, user)) + if(to_remove == arms) + arms = null + else if(to_remove == body) + body = null + else if(to_remove == legs) + legs = null + else if(to_remove == head) + head = null + + update_icon() + return + + // Final construction step. + else if(thing.has_tool_quality(TOOL_SCREWDRIVER)) + + // Check for basic components. + if(!(arms && legs && head && body)) + to_chat(user, SPAN_WARNING("There are still parts missing from \the [src].")) + return + + // Check for wiring. + if(is_wired < EXOFRAME_WIRED_ADJUSTED) + if(is_wired == EXOFRAME_WIRED) + to_chat(user, SPAN_WARNING("\The [src]'s wiring has not been adjusted!")) + else + to_chat(user, SPAN_WARNING("\The [src] is not wired!")) + return + + // Check for basing metal internal plating. + if(is_reinforced < EXOFRAME_REINFORCED_WELDED) + if(is_reinforced == EXOFRAME_REINFORCED) + to_chat(user, SPAN_WARNING("\The [src]'s internal reinforcement has not been secured!")) + else if(is_reinforced == EXOFRAME_REINFORCED_SECURE) + to_chat(user, SPAN_WARNING("\The [src]'s internal reinforcement has not been welded down!")) + else + to_chat(user, SPAN_WARNING("\The [src] has no internal reinforcement!")) + return + + visible_message(SPAN_NOTICE("\The [user] begins tightening screws, flipping connectors and finishing off \the [src].")) + if(!do_after(user, 50, src)) + return + + if(is_reinforced < EXOFRAME_REINFORCED_WELDED || is_wired < EXOFRAME_WIRED_ADJUSTED || !(arms && legs && head && body) || QDELETED(src) || QDELETED(user)) + return + + // We're all done. Finalize the exosuit and pass the frame to the new system. + var/mob/living/exosuit/M = new(get_turf(src), src) + visible_message(SPAN_NOTICE("\The [user] finishes off \the [M].")) + playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1) + + arms = null + legs = null + head = null + body = null + qdel(src) + + return + + // Installing wiring. + else if(istype(thing, /obj/item/stack/cable_coil)) + + if(is_wired) + to_chat(user, SPAN_WARNING("\The [src] has already been wired.")) + return + + var/obj/item/stack/cable_coil/CC = thing + if(CC.get_amount() < 10) + to_chat(user, SPAN_WARNING("You need at least ten units of cable to complete the exosuit.")) + return + + user.visible_message("\The [user] begins wiring \the [src]...") + + if(!do_after(user, 30)) + return + + if(!CC || !user || !src || CC.get_amount() < 10 || is_wired) + return + + CC.use(10) + user.visible_message("\The [user] installs wiring in \the [src].") + playsound(user.loc, 'sound/items/Deconstruct.ogg', 50, 1) + is_wired = EXOFRAME_WIRED + // Securing wiring. + else if(thing.has_tool_quality(TOOL_WIRECUTTER)) + if(!is_wired) + to_chat(user, "There is no wiring in \the [src] to neaten.") + return + + user.visible_message("\The [user] begins adjusting the wiring inside \the [src]...") + var/last_wiring_state = is_wired + if(!do_after(user, 30) || last_wiring_state != is_wired) + return + + visible_message("\The [user] [(is_wired == EXOFRAME_WIRED_ADJUSTED) ? "snips some of" : "neatens"] the wiring in \the [src].") + playsound(user.loc, 'sound/items/Wirecutter.ogg', 100, 1) + is_wired = (is_wired == EXOFRAME_WIRED_ADJUSTED) ? EXOFRAME_WIRED : EXOFRAME_WIRED_ADJUSTED + // Installing metal. + else if(istype(thing, /obj/item/stack/material)) + var/obj/item/stack/material/M = thing + if(M.material) + if(is_reinforced) + to_chat(user, SPAN_WARNING("There is already a material reinforcement installed in \the [src].")) + return + if(M.get_amount() < 10) + to_chat(user, SPAN_WARNING("You need at least ten sheets to reinforce \the [src].")) + return + + visible_message("\The [user] begins layering the interior of the \the [src] with \the [M].") + + if(!do_after(user, 30) || is_reinforced) + return + + visible_message("\The [user] reinforces \the [src] with \the [M].") + playsound(user.loc, 'sound/items/Deconstruct.ogg', 50, 1) + material = M.material + is_reinforced = EXOFRAME_REINFORCED + M.use(10) + else + return ..() + // Securing metal. + else if(thing.has_tool_quality(TOOL_WRENCH)) + if(!is_reinforced) + to_chat(user, SPAN_WARNING("There is no metal to secure inside \the [src].")) + return + if(is_reinforced == EXOFRAME_REINFORCED_WELDED) + to_chat(user, SPAN_WARNING("\The [src]'s internal reinforcment has been welded in.")) + return + + var/last_reinforced_state = is_reinforced + visible_message("\The [user] begins adjusting the metal reinforcement inside \the [src].") + + if(!do_after(user, 4 SECONDS, src) || last_reinforced_state != is_reinforced) + return + + visible_message("\The [user] [(is_reinforced == 2) ? "unsecures" : "secures"] the metal reinforcement inside \the [src].") + playsound(user.loc, 'sound/items/Ratchet.ogg', 100, 1) + is_reinforced = (is_reinforced == EXOFRAME_REINFORCED_SECURE) ? EXOFRAME_REINFORCED : EXOFRAME_REINFORCED_SECURE + // Welding metal. + else if(istype(thing, /obj/item/weldingtool)) + var/obj/item/weldingtool/WT = thing + if(!is_reinforced) + to_chat(user, SPAN_WARNING("There is no metal to secure inside \the [src].")) + return + if(is_reinforced == EXOFRAME_REINFORCED) + to_chat(user, SPAN_WARNING("The reinforcement inside \the [src] has not been secured.")) + return + if(!WT.isOn()) + to_chat(user, SPAN_WARNING("Turn \the [WT] on, first.")) + return + if(WT.remove_fuel(1, user)) + + var/last_reinforced_state = is_reinforced + visible_message("\The [user] begins welding the metal reinforcement inside \the [src].") + if(!do_after(user, 20) || last_reinforced_state != is_reinforced) + return + + visible_message("\The [user] [(is_reinforced == EXOFRAME_REINFORCED_WELDED) ? "unwelds the reinforcement from" : "welds the reinforcement into"] \the [src].") + is_reinforced = (is_reinforced == EXOFRAME_REINFORCED_WELDED) ? EXOFRAME_REINFORCED_SECURE : EXOFRAME_REINFORCED_WELDED + playsound(user.loc, 'sound/items/Welder.ogg', 50, 1) + else + to_chat(user, SPAN_WARNING("Not enough fuel!")) + return + // Installing basic components. + else if(istype(thing,/obj/item/mech_component/manipulators)) + if(arms) + to_chat(user, SPAN_WARNING("\The [src] already has manipulators installed.")) + return + if(install_component(thing, user)) + if(arms) + thing.dropInto(loc) + return + arms = thing + else if(istype(thing,/obj/item/mech_component/propulsion)) + if(legs) + to_chat(user, SPAN_WARNING("\The [src] already has a propulsion system installed.")) + return + if(install_component(thing, user)) + if(legs) + thing.dropInto(loc) + return + legs = thing + else if(istype(thing,/obj/item/mech_component/sensors)) + if(head) + to_chat(user, SPAN_WARNING("\The [src] already has a sensor array installed.")) + return + if(install_component(thing, user)) + if(head) + thing.dropInto(loc) + return + head = thing + else if(istype(thing,/obj/item/mech_component/chassis)) + if(body) + to_chat(user, SPAN_WARNING("\The [src] already has an outer chassis installed.")) + return + if(install_component(thing, user)) + if(body) + thing.dropInto(loc) + return + body = thing + else + return ..() + update_icon() + +/obj/structure/heavy_vehicle_frame/proc/install_component(var/obj/item/thing, var/mob/user) + var/obj/item/mech_component/MC = thing + if(istype(MC) && !MC.ready_to_install()) + to_chat(user, SPAN_WARNING("\The [MC] [MC.gender == PLURAL ? "are" : "is"] not ready to install.")) + return 0 + if(user) + visible_message(SPAN_NOTICE("\The [user] begins installing \the [thing] into \the [src].")) + if(!user.canUnEquip(thing) || !do_after(user, 30) || user.get_active_hand() != thing) + return + if(!user.unEquip(thing)) + return + thing.forceMove(src) + visible_message(SPAN_NOTICE("\The [user] installs \the [thing] into \the [src].")) + playsound(user.loc, 'sound/machines/click.ogg', 50, 1) + return 1 + +/obj/structure/heavy_vehicle_frame/proc/uninstall_component(var/obj/item/component, var/mob/user) + if(!istype(component) || (component.loc != src) || !istype(user)) + return FALSE + if(!do_after(user, 40) || component.loc != src) + return FALSE + user.visible_message(SPAN_NOTICE("\The [user] crowbars \the [component] off \the [src].")) + component.forceMove(get_turf(src)) + user.put_in_hands(component) + playsound(user.loc, 'sound/items/Deconstruct.ogg', 50, 1) + return TRUE diff --git a/code/modules/exosuits/component/head.dm b/code/modules/exosuits/component/head.dm new file mode 100644 index 00000000000..a6258ca561c --- /dev/null +++ b/code/modules/exosuits/component/head.dm @@ -0,0 +1,237 @@ +/obj/item/mech_component/sensors + name = "head" + icon_state = "loader_head" + gender = NEUTER + + var/vision_flags = 0 + var/see_invisible = 0 + var/obj/item/robot_parts/robot_component/radio/radio + var/obj/item/robot_parts/robot_component/camera + var/obj/item/mech_component/control_module/software + has_hardpoints = list(HARDPOINT_HEAD) + var/active_sensors = 0 + power_use = 15 + +/obj/item/mech_component/sensors/Destroy() + QDEL_NULL(camera) + QDEL_NULL(radio) + QDEL_NULL(software) + . = ..() + +/obj/item/mech_component/sensors/show_missing_parts(var/mob/user) + if(!radio) + . += SPAN_WARNING("It is missing a radio.") + if(!camera) + . += SPAN_WARNING("It is missing a camera.") + if(!software) + . += SPAN_WARNING("It is missing a software control module.") + +/obj/item/mech_component/sensors/prebuild() + radio = new(src) + camera = new(src) + +/obj/item/mech_component/sensors/update_components() + radio = locate() in src + camera = locate() in src + software = locate() in src + +/obj/item/mech_component/sensors/proc/get_sight() + var/flags = 0 + if(total_damage >= 0.8 * max_damage) + flags |= BLIND + else if(active_sensors) + flags |= vision_flags + + return flags + +/obj/item/mech_component/sensors/proc/get_invisible() + var/invisible = 0 + if((total_damage <= 0.8 * max_damage) && active_sensors) + invisible = see_invisible + return invisible + + + +/obj/item/mech_component/sensors/ready_to_install() + return (radio && camera) + +/obj/item/mech_component/sensors/attackby(var/obj/item/thing, var/mob/user) + if(istype(thing, /obj/item/mech_component/control_module)) + if(software) + to_chat(user, SPAN_WARNING("\The [src] already has a control module installed.")) + return + if(install_component(thing, user)) software = thing + else if(istype(thing,/obj/item/robot_parts/robot_component/radio)) + if(radio) + to_chat(user, SPAN_WARNING("\The [src] already has a radio installed.")) + return + if(install_component(thing, user)) radio = thing + else if(istype(thing,/obj/item/robot_parts/robot_component/camera)) + if(camera) + to_chat(user, SPAN_WARNING("\The [src] already has a camera installed.")) + return + if(install_component(thing, user)) camera = thing + else + return ..() + +/obj/item/mech_component/control_module + name = "exosuit control module" + desc = "A clump of circuitry and software chip docks, used to program exosuits." + icon_state = "control" + icon = 'icons/mob/mecha/mech_equipment.dmi' + gender = NEUTER + var/list/installed_software = list() + var/max_installed_software = 2 + +/obj/item/mech_component/control_module/examine(mob/user) + . = ..() + . += SPAN_NOTICE("It has [max_installed_software - LAZYLEN(installed_software)] empty slot\s remaining out of [max_installed_software].") + +/obj/item/mech_component/control_module/attackby(var/obj/item/thing, var/mob/user) + + if(istype(thing, /obj/item/weapon/circuitboard/exosystem)) + install_software(thing, user) + return + + if(thing.has_tool_quality(TOOL_SCREWDRIVER)) + var/result = ..() + update_software() + return result + else + return ..() + +/obj/item/mech_component/control_module/proc/install_software(var/obj/item/weapon/circuitboard/exosystem/software, var/mob/user) + if(installed_software.len >= max_installed_software) + if(user) + to_chat(user, SPAN_WARNING("\The [src] can only hold [max_installed_software] software modules.")) + return + if(user && !user.unEquip(software)) + return + + if(user) + to_chat(user, SPAN_NOTICE("You load \the [software] into \the [src]'s memory.")) + + software.forceMove(src) + update_software() + +/obj/item/mech_component/control_module/proc/update_software() + installed_software = list() + for(var/obj/item/weapon/circuitboard/exosystem/program in contents) + installed_software |= program.contains_software + +/* + * Variants + */ + +/obj/item/mech_component/sensors/combat + name = "combat sensors" + gender = PLURAL + exosuit_desc_string = "high-resolution thermal sensors" + icon_state = "combat_head" + vision_flags = SEE_MOBS + see_invisible = SEE_INVISIBLE_NOLIGHTING + power_use = 200 + +/obj/item/mech_component/sensors/combat/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_WEAPONS, MECH_SOFTWARE_ADVWEAPONS) + +/obj/item/mech_component/sensors/industrial + name = "exosuit sensors" + gender = PLURAL + exosuit_desc_string = "simple collision detection sensors" + desc = "A primitive set of sensors designed to work in tandem with most MKI Eyeball platforms." + icon_state = "industrial_head" + max_damage = 100 + power_use = 0 + +/obj/item/mech_component/sensors/industrial/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING) + +/obj/item/mech_component/sensors/heavy + name = "heavy sensors" + exosuit_desc_string = "a reinforced monoeye" + desc = "A solitary sensor moves inside a recessed slit in the armour plates." + icon_state = "heavy_head" + max_damage = 120 + power_use = 0 + +/obj/item/mech_component/sensors/heavy/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_WEAPONS, MECH_SOFTWARE_ADVWEAPONS) + +/obj/item/mech_component/sensors/light + name = "light sensors" + gender = PLURAL + exosuit_desc_string = "advanced sensor array" + icon_state = "light_head" + max_damage = 30 + vision_flags = SEE_TURFS + see_invisible = SEE_INVISIBLE_NOLIGHTING + power_use = 50 + desc = "A series of high resolution optical sensors. They can overlay several images to give the pilot a sense of location even in total darkness. " + +/obj/item/mech_component/sensors/light/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_MEDICAL) + +/obj/item/mech_component/sensors/mercenary + name = "mercenary sensors" + exosuit_desc_string = "a reinforced monoeye" + desc = "A solitary sensor moves inside a recessed slit in the armour plates." + icon_state = "mercenary_head" + max_damage = 120 + power_use = 0 + +/obj/item/mech_component/sensors/mercenary/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_WEAPONS) + +/obj/item/mech_component/sensors/modern + name = "modern sensors" + gender = PLURAL + exosuit_desc_string = "high-resolution thermal sensors" + icon_state = "modern_head" + vision_flags = SEE_MOBS + see_invisible = SEE_INVISIBLE_NOLIGHTING + power_use = 200 + +/obj/item/mech_component/sensors/modern/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_WEAPONS, MECH_SOFTWARE_ADVWEAPONS) + +/obj/item/mech_component/sensors/powerloader + name = "exosuit sensors" + gender = PLURAL + exosuit_desc_string = "simple collision detection sensors" + desc = "A primitive set of sensors designed to work in tandem with most MKI Eyeball platforms." + max_damage = 100 + power_use = 0 + +/obj/item/mech_component/sensors/powerloader/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING) + +/obj/item/mech_component/sensors/sleek + name = "sleek sensors" + gender = PLURAL + exosuit_desc_string = "advanced sensor array" + icon_state = "sleek_head" + max_damage = 30 + vision_flags = SEE_TURFS + see_invisible = SEE_INVISIBLE_NOLIGHTING + power_use = 50 + desc = "A series of high resolution optical sensors. They can overlay several images to give the pilot a sense of location even in total darkness. " + +/obj/item/mech_component/sensors/sleek/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_MEDICAL) diff --git a/code/modules/exosuits/component/legs.dm b/code/modules/exosuits/component/legs.dm new file mode 100644 index 00000000000..7dc23a78f09 --- /dev/null +++ b/code/modules/exosuits/component/legs.dm @@ -0,0 +1,137 @@ +/obj/item/mech_component/propulsion + name = "legs" + pixel_y = 12 + icon_state = "loader_legs" + var/move_delay = 5 + var/turn_delay = 5 + var/obj/item/robot_parts/robot_component/actuator/motivator + power_use = 50 + +/obj/item/mech_component/propulsion/Destroy() + QDEL_NULL(motivator) + . = ..() + +/obj/item/mech_component/propulsion/show_missing_parts(var/mob/user) + if(!motivator) + . += SPAN_WARNING("It is missing an actuator.") + +/obj/item/mech_component/propulsion/ready_to_install() + return motivator + +/obj/item/mech_component/propulsion/update_components() + motivator = locate() in src + +/obj/item/mech_component/propulsion/attackby(var/obj/item/thing, var/mob/user) + if(istype(thing,/obj/item/robot_parts/robot_component/actuator)) + if(motivator) + to_chat(user, SPAN_WARNING("\The [src] already has an actuator installed.")) + return + if(install_component(thing, user)) motivator = thing + else + return ..() + +/obj/item/mech_component/propulsion/prebuild() + motivator = new(src) + +/obj/item/mech_component/propulsion/proc/can_move_on(var/turf/location, var/turf/target_loc) + return isatom(location) && istype(target_loc) + +/* + * Variants + */ + +/obj/item/mech_component/propulsion/combat + name = "combat legs" + exosuit_desc_string = "sleek hydraulic legs" + icon_state = "combat_legs" + move_delay = 3 + turn_delay = 3 + power_use = 20 + +/obj/item/mech_component/propulsion/industrial + name = "exosuit legs" + exosuit_desc_string = "reinforced hydraulic legs" + desc = "Wide and stable but not particularly fast." + icon_state = "industrial_legs" + max_damage = 70 + move_delay = 4 + turn_delay = 4 + power_use = 10 + +/obj/item/mech_component/propulsion/heavy + name = "heavy legs" + exosuit_desc_string = "heavy hydraulic legs" + desc = "Oversized actuators struggle to move these armoured legs. " + icon_state = "heavy_legs" + move_delay = 5 + turn_delay = 5 + max_damage = 160 + power_use = 100 + +/obj/item/mech_component/propulsion/spider + name = "quadlegs" + exosuit_desc_string = "hydraulic quadlegs" + desc = "Xion Industrial's arachnid series boasts more leg per leg than the leading competitor." + icon_state = "spiderlegs" + max_damage = 80 + move_delay = 4 + turn_delay = 1 + power_use = 25 + +/obj/item/mech_component/propulsion/tracks + name = "tracks" + exosuit_desc_string = "armored tracks" + desc = "A classic brought back. The Hephaestus' Landmaster class tracks are impervious to most damage and can maintain top speed regardless of load. Watch out for corners." + icon_state = "tracks" + max_damage = 150 + move_delay = 2 //It´s fast + turn_delay = 7 + power_use = 150 + color = COLOR_WHITE + +/obj/item/mech_component/propulsion/light + name = "light legs" + exosuit_desc_string = "aerodynamic electromechanic legs" + icon_state = "light_legs" + move_delay = 2 + turn_delay = 3 + max_damage = 40 + power_use = 5 + desc = "The electrical systems driving these legs are almost totally silent. Unfortunately slamming a plate of metal against the ground is not." + +/obj/item/mech_component/propulsion/mercenary + name = "mercenary legs" + exosuit_desc_string = "mercenary hydraulic legs" + desc = "Oversized actuators struggle to move these armoured legs. " + icon_state = "mercenary_legs" + move_delay = 5 + turn_delay = 5 + max_damage = 160 + power_use = 100 + +/obj/item/mech_component/propulsion/modern + name = "modern legs" + exosuit_desc_string = "sleek hydraulic legs" + icon_state = "modern_legs" + move_delay = 3 + turn_delay = 3 + power_use = 20 + +/obj/item/mech_component/propulsion/powerloader + name = "exosuit legs" + exosuit_desc_string = "reinforced hydraulic legs" + desc = "Wide and stable but not particularly fast." + max_damage = 70 + move_delay = 4 + turn_delay = 4 + power_use = 10 + +/obj/item/mech_component/propulsion/sleek + name = "sleek legs" + exosuit_desc_string = "aerodynamic electromechanic legs" + icon_state = "sleek_legs" + move_delay = 2 + turn_delay = 3 + max_damage = 40 + power_use = 5 + desc = "The electrical systems driving these legs are almost totally silent. Unfortunately slamming a plate of metal against the ground is not." diff --git a/code/modules/exosuits/component/software.dm b/code/modules/exosuits/component/software.dm new file mode 100644 index 00000000000..b6e89a650a4 --- /dev/null +++ b/code/modules/exosuits/component/software.dm @@ -0,0 +1,39 @@ +#define T_BOARD_MECH(name) "exosuit software (" + (name) + ")" + +/obj/item/weapon/circuitboard/exosystem + name = "exosuit software template" + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + item_state = "electronic" + var/list/contains_software = list() + +/obj/item/weapon/circuitboard/exosystem/engineering + name = T_BOARD_MECH(MECH_SOFTWARE_ENGINEERING) + contains_software = list(MECH_SOFTWARE_ENGINEERING) + origin_tech = list(TECH_DATA = 1) + +/obj/item/weapon/circuitboard/exosystem/utility + name = T_BOARD_MECH(MECH_SOFTWARE_UTILITY) + contains_software = list(MECH_SOFTWARE_UTILITY) + icon_state = "mcontroller" + origin_tech = list(TECH_DATA = 1) + +/obj/item/weapon/circuitboard/exosystem/medical + name = T_BOARD_MECH(MECH_SOFTWARE_MEDICAL) + contains_software = list(MECH_SOFTWARE_MEDICAL) + icon_state = "mcontroller" + origin_tech = list(TECH_DATA = 3,TECH_BIO = 2) + +/obj/item/weapon/circuitboard/exosystem/weapons + name = T_BOARD_MECH(MECH_SOFTWARE_WEAPONS) + contains_software = list(MECH_SOFTWARE_WEAPONS) + icon_state = "mainboard" + origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 3) + +/obj/item/weapon/circuitboard/exosystem/advweapons + name = T_BOARD_MECH(MECH_SOFTWARE_ADVWEAPONS) + contains_software = list(MECH_SOFTWARE_ADVWEAPONS) + icon_state = "mainboard" + origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 5) + +#undef T_BOARD_MECH diff --git a/code/modules/exosuits/equipment/_equipment.dm b/code/modules/exosuits/equipment/_equipment.dm new file mode 100644 index 00000000000..6a9d652aa13 --- /dev/null +++ b/code/modules/exosuits/equipment/_equipment.dm @@ -0,0 +1,117 @@ +// Defining all of this here so it's centralized. +// Used by the exosuit HUD to get a 1-10 value representing charge, ammo, etc. +/obj/item/mech_equipment + name = "exosuit hardpoint system" + icon = 'icons/mob/mecha/mech_equipment.dmi' + icon_state = "" + matter = list(MATERIAL_STEEL = 10000, MATERIAL_PLASTIC = 5000, MATERIAL_OSMIUM = 500) + force = 10 + + var/restricted_hardpoints + var/mob/living/exosuit/owner + var/list/restricted_software + var/equipment_delay = 0 + var/active_power_use = 1 KILOWATTS // How much does it consume to perform and accomplish usage + var/passive_power_use = 0 // For gear that for some reason takes up power even if it's supposedly doing nothing (mech will idly consume power) + var/mech_layer = MECH_GEAR_LAYER //For the part where it's rendered as mech gear + var/require_adjacent = TRUE + +/obj/item/mech_equipment/attack() //Generally it's not desired to be able to attack with items + return FALSE + +/obj/item/mech_equipment/proc/powercheck(var/mob/living/user) + if (owner && loc == owner && ((user in owner.pilots) || user == owner)) + var/obj/item/cell/OwnerCell = owner.get_cell() + if(OwnerCell && OwnerCell.check_charge(active_power_use * CELLRATE)) + return TRUE + to_chat(user, SPAN_WARNING("The power indicator flashes briefly as you attempt to use \the [src]")) + return FALSE + +/obj/item/mech_equipment/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params) + if(require_adjacent) + if(!inrange) + return 0 + + if(target in owner.contents) + return FALSE + + return powercheck(user) + +/obj/item/mech_equipment/attack_self(var/mob/user) + return powercheck(user) + +/obj/item/mech_equipment/proc/installed(var/mob/living/exosuit/_owner) + owner = _owner + //generally attached. Nothing should be able to grab it + canremove = FALSE + +/obj/item/mech_equipment/proc/uninstalled() + owner = null + canremove = TRUE + +/obj/item/mech_equipment/Destroy() + owner = null + . = ..() + +/obj/item/mech_equipment/proc/get_effective_obj() + return src + +/obj/item/mech_equipment/proc/MouseDragInteraction() + return FALSE + +/obj/item/mech_equipment/mob_can_unequip(mob/M, slot, disable_warning) + return ..() && !owner + +/obj/item/mech_equipment/mounted_system + var/holding_type + var/obj/item/holding + +/obj/item/mech_equipment/mounted_system/attack_self(var/mob/user) + . = ..() + if(. && holding) + return holding.attack_self(user) + +/obj/item/mech_equipment/mounted_system/proc/forget_holding() + if(holding) //It'd be strange for this to be called with this var unset + GLOB.destroyed_event.unregister(holding, src, .proc/forget_holding) + holding = null + qdel(src) + +/obj/item/mech_equipment/mounted_system/Initialize() + . = ..() + if(holding_type) + holding = new holding_type(src) + GLOB.destroyed_event.register(holding, src, .proc/forget_holding) + if(holding) + if(!icon_state) + icon = holding.icon + icon_state = holding.icon_state + name = holding.name + desc = "[holding.desc] This one is suitable for installation on an exosuit." + + +/obj/item/mech_equipment/mounted_system/Destroy() + if(holding) + QDEL_NULL(holding) + . = ..() + + +/obj/item/mech_equipment/mounted_system/get_effective_obj() + return (holding ? holding : src) + +/obj/item/mech_equipment/mounted_system/get_hardpoint_status_value() + return (holding ? holding.get_hardpoint_status_value() : null) + +/obj/item/mech_equipment/mounted_system/get_hardpoint_maptext() + return (holding ? holding.get_hardpoint_maptext() : null) + +/obj/item/proc/get_hardpoint_status_value() + return null + +/obj/item/proc/get_hardpoint_maptext() + return null + +/obj/item/mech_equipment/mounted_system/get_cell() + if(owner && loc == owner) + return owner.get_cell() + return null diff --git a/code/modules/exosuits/equipment/combat.dm b/code/modules/exosuits/equipment/combat.dm new file mode 100644 index 00000000000..0b9ba3ff265 --- /dev/null +++ b/code/modules/exosuits/equipment/combat.dm @@ -0,0 +1,504 @@ +/obj/item/mech_equipment/mounted_system/taser + name = "mounted electrolaser carbine" + desc = "A dual fire mode electrolaser system connected to the exosuit's targetting system." + icon_state = "mech_taser" + holding_type = /obj/item/gun/energy/taser/carbine/mounted/mech + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_WEAPONS) + +/obj/item/mech_equipment/mounted_system/taser/ion + name = "mounted ion rifle" + desc = "An exosuit-mounted ion rifle. Handle with care." + icon_state = "mech_ionrifle" + holding_type = /obj/item/gun/energy/ionrifle/mounted/mech + +/obj/item/mech_equipment/mounted_system/taser/laser + name = "\improper CH-PS \"Immolator\" laser" + desc = "An exosuit-mounted laser rifle. Handle with care." + icon_state = "mech_lasercarbine" + holding_type = /obj/item/gun/energy/laser/mounted/mech + +/obj/item/gun/energy/taser/carbine/mounted/mech + name = "mounted electrolaser carbine" + use_external_power = TRUE + self_recharge = TRUE + +/obj/item/gun/energy/ionrifle/mounted/mech + use_external_power = TRUE + self_recharge = TRUE + +/obj/item/gun/energy/laser/mounted/mech + name = "\improper CH-PS \"Immolator\" laser" + use_external_power = TRUE + self_recharge = TRUE + +/obj/item/gun/energy/get_hardpoint_maptext() + return "[round(power_supply.charge / charge_cost)]/[round(power_supply.maxcharge / charge_cost)]" + +/obj/item/gun/energy/get_hardpoint_status_value() + var/obj/item/cell/C = get_cell() + if(istype(C)) + return C.charge/C.maxcharge + return null + +/* +/obj/item/mech_equipment/shields + name = "exosuit shield droid" + desc = "The Hephaestus Armature system is a well liked energy deflector system designed to stop any projectile before it has a chance to become a threat." + icon_state = "shield_droid" + var/obj/aura/mechshield/aura = null + var/max_charge = 250 + var/charge = 250 + var/last_recharge = 0 + var/charging_rate = 7500 * CELLRATE + var/cooldown = 3.5 SECONDS //Time until we can recharge again after a blocked impact + restricted_hardpoints = list(HARDPOINT_BACK) + restricted_software = list(MECH_SOFTWARE_WEAPONS) + +/obj/item/mech_equipment/shields/installed(var/mob/living/exosuit/_owner) + . = ..() + aura = new(owner, src) + +/obj/item/mech_equipment/shields/uninstalled() + QDEL_NULL(aura) + . = ..() + +/obj/item/mech_equipment/shields/attack_self(var/mob/user) + . = ..() + if(.) + toggle() + +/obj/item/mech_equipment/shields/proc/stop_damage(var/damage) + var/difference = damage - charge + charge = Clamp(charge - damage, 0, max_charge) + + last_recharge = world.time + + if(difference > 0) + for(var/mob/pilot in owner.pilots) + to_chat(pilot, SPAN_DANGER("Warning: Deflector shield failure detect, shutting down")) + toggle() + playsound(owner.loc,'sound/mecha/internaldmgalarm.ogg',35,1) + return difference + else return 0 + +/obj/item/mech_equipment/shields/proc/toggle() + if(!aura) + return + aura.toggle() + playsound(owner,'sounds/flash.ogg',35,1) + update_icon() + if(aura.active) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + owner.update_icon() + +/obj/item/mech_equipment/shields/on_update_icon() + . = ..() + if(!aura) + return + if(aura.active) + icon_state = "shield_droid_a" + else + icon_state = "shield_droid" + +/obj/item/mech_equipment/shields/Process() + if(charge >= max_charge) + return + if((world.time - last_recharge) < cooldown) + return + var/obj/item/cell/cell = owner.get_cell() + + var/actual_required_power = Clamp(max_charge - charge, 0, charging_rate) + + charge += cell.use(actual_required_power) + +/obj/item/mech_equipment/shields/get_hardpoint_status_value() + return charge / max_charge + +/obj/item/mech_equipment/shields/get_hardpoint_maptext() + return "[(aura && aura.active) ? "ONLINE" : "OFFLINE"]: [round((charge / max_charge) * 100)]%" + +/obj/aura/mechshield + icon = 'icons/mecha/shield.dmi' + name = "mechshield" + var/obj/item/mech_equipment/shields/shields = null + var/active = 0 + layer = ABOVE_HUMAN_LAYER + var/north_layer = MECH_UNDER_LAYER + plane = MOB_PLANE + pixel_x = 8 + pixel_y = 4 + mouse_opacity = 0 + +/obj/aura/mechshield/Initialize(var/maploading, var/obj/item/mech_equipment/shields/holder) + . = ..() + shields = holder + +/obj/aura/mechshield/added_to(var/mob/living/target) + . = ..() + target.vis_contents += src + set_dir() + GLOB.dir_set_event.register(user, src, /obj/aura/mechshield/proc/update_dir) + +/obj/aura/mechshield/proc/update_dir(var/user, var/old_dir, var/dir) + set_dir(dir) + +/obj/aura/mechshield/set_dir(new_dir) + . = ..() + if(dir == NORTH) + layer = north_layer + else layer = initial(layer) + +/obj/aura/mechshield/Destroy() + if(user) + GLOB.dir_set_event.unregister(user, src, /obj/aura/mechshield/proc/update_dir) + user.vis_contents -= src + shields = null + . = ..() + +/obj/aura/mechshield/proc/toggle() + active = !active + + update_icon() + + if(active) + flick("shield_raise", src) + else + flick("shield_drop", src) + + +/obj/aura/mechshield/on_update_icon() + . = ..() + if(active) + icon_state = "shield" + else + icon_state = "shield_null" + +/obj/aura/mechshield/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(!active) + return + if(shields) + if(shields.charge) + P.damage = shields.stop_damage(P.damage) + user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash and crackle.")) + flick("shield_impact", src) + playsound(user,'sound/effects/basscannon.ogg',35,1) + //light up the night. + new /obj/effect/effect/smoke/illumination(user.loc, 5, 4, 1, "#ffffff") + if(P.damage <= 0) + return AURA_FALSE|AURA_CANCEL + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user) + spark_system.start() + playsound(loc, "sparks", 25, 1) + +/obj/aura/mechshield/hitby(atom/movable/M, var/datum/thrownthing/TT) + . = ..() + if(!active) + return + if(shields.charge && TT.speed <= 5) + user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash briefly as they deflect \the [M].")) + flick("shield_impact", src) + playsound(user,'sound/effects/basscannon.ogg',10,1) + return AURA_FALSE|AURA_CANCEL + //Too fast! +*/ + + +//Melee! As a general rule I would recommend using regular objects and putting logic in them. +/obj/item/mech_equipment/mounted_system/melee + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_UTILITY) + +/obj/item/material/knife/machete/mech + name = "mechete" + desc = "That thing is too big to be called a machete. Too big, too thick, too heavy, and too rough, it is more like a large hunk of iron." + w_class = ITEMSIZE_HUGE + slot_flags = 0 + force_divisor = 0.34 // 20 with hardness 60 (steel) + thrown_force_divisor = 1.75 // 20 with weight 20 (steel) + can_dull = FALSE // A blade this massive isn't sharp because it has a honed edge. + +/obj/item/material/knife/machete/mech/apply_hit_effect(mob/living/target, mob/living/user, hit_zone) + . = ..() + if (.) + user.do_attack_animation(target) + if (target.mob_size < user.mob_size) //Damaging attacks overwhelm smaller mobs + target.throw_at(get_edge_target_turf(target,get_dir(user, target)),1, 1) + +/obj/item/material/knife/machete/mech/resolve_attackby(atom/A, mob/user, attack_modifier, click_parameters) + //Case 1: Default, you are hitting something that isn't a mob. Just do whatever, this isn't dangerous or op. + if (!isliving(A)) + return ..() + + user.visible_message(SPAN_DANGER("\The [user] swings \the [src] at \the [A]!")) + playsound(user, 'sound/mecha/mechmove03.ogg', 35, 1) + return ..() + +/obj/item/material/knife/machete/mech/attack_self(mob/living/user) + . = ..() + if (user.a_intent != I_HURT) + return + var/obj/item/mech_equipment/mounted_system/melee/mechete/MC = loc + if (istype(MC)) + //SPIN BLADE ATTACK GO! + var/mob/living/exosuit/E = MC.owner + if (E) + E.setClickCooldown(1.35 SECONDS) + E.visible_message(SPAN_DANGER("\The [E] swings \the [src] back, preparing for an attack!"), blind_message = SPAN_DANGER("You hear the loud hissing of hydraulics!")) + playsound(E, 'sound/mecha/mechmove03.ogg', 35, 1) + if (do_after(E, 1.2 SECONDS, get_turf(user)) && E && MC) + for (var/mob/living/M in orange(1, E)) + attack(M, E, E.zone_sel.selecting, FALSE) + E.spin(0.65 SECONDS, 0.125 SECONDS) + playsound(E, 'sound/mecha/mechturn.ogg', 40, 1) + +/obj/item/material/knife/machete/mech/durasteel/Initialize(ml, material_key) + . = ..(ml, MAT_DURASTEEL) + +/obj/item/mech_equipment/mounted_system/melee/mechete + icon_state = "mech_blade" + holding_type = /obj/item/material/knife/machete/mech + +/obj/item/mech_equipment/mounted_system/melee/mechete/durasteel + holding_type = /obj/item/material/knife/machete/mech/durasteel + +/* +//Ballistic shield +/obj/item/mech_equipment/ballistic_shield + name = "exosuit ballistic shield" + desc = "This formidable line of defense, sees widespread use in planetary peacekeeping operations and military formations alike." + icon_state = "mech_shield" //Rendering is handled by aura due to layering issues: TODO, figure out a better way to do this + var/obj/aura/mech_ballistic/aura = null + var/last_push = 0 + var/chance = 60 //For attacks from the front, diminishing returns + var/last_max_block = 0 //Blocking during a perfect block window resets this, else there is an anti spam + var/max_block = 60 // Should block most things + var/blocking = FALSE + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_UTILITY) + +/obj/item/mech_equipment/ballistic_shield/installed(mob/living/exosuit/_owner) + . = ..() + aura = new(owner, src) + +/obj/item/mech_equipment/ballistic_shield/uninstalled() + QDEL_NULL(aura) + . = ..() + +/obj/item/mech_equipment/ballistic_shield/afterattack(atom/target, mob/living/user, inrange, params) + . = ..() + if (. && user.a_intent == I_HURT && (last_push + 1.6 SECONDS < world.time)) + owner.visible_message(SPAN_WARNING("\The [owner] retracts \the [src], preparing to push with it!"), blind_message = SPAN_WARNING("You hear the whine of hydraulics and feel a rush of air!")) + owner.setClickCooldown(0.7 SECONDS) + last_push = world.time + if (do_after(owner, 0.5 SECONDS, get_turf(owner)) && owner) + owner.visible_message(SPAN_WARNING("\The [owner] slams the area in front \the [src]!"), blind_message = SPAN_WARNING("You hear a loud hiss and feel a strong gust of wind!")) + playsound(src ,'sound/effects/bang.ogg',35,1) + var/list/turfs = list() + var/front = get_step(get_turf(owner), owner.dir) + turfs += front + turfs += get_step(front, turn(owner.dir, -90)) + turfs += get_step(front, turn(owner.dir, 90)) + for(var/turf/T in turfs) + for(var/mob/living/M in T) + if (!M.Adjacent(owner)) + continue + M.attack_generic(owner, (owner.arms ? owner.arms.melee_damage * 1.2 : 0), "slammed") + M.throw_at(get_edge_target_turf(owner ,owner.dir),5, 2) + do_attack_effect(T, "smash") + +/obj/item/mech_equipment/ballistic_shield/attack_self(mob/user) + . = ..() + if (.) //FORM A SHIELD WALL! + if (last_max_block + 2 SECONDS < world.time) + owner.visible_message(SPAN_WARNING("\The [owner] raises \the [src], locking it in place!"), blind_message = SPAN_WARNING("You hear the whir of motors and scratching metal!")) + playsound(src ,'sound/effects/bamf.ogg',35,1) + owner.setClickCooldown(0.8 SECONDS) + blocking = TRUE + last_max_block = world.time + do_after(owner, 0.75 SECONDS, get_turf(user)) + blocking = FALSE + else + to_chat(user, SPAN_WARNING("You are not ready to block again!")) + +/obj/item/mech_equipment/ballistic_shield/proc/block_chance(damage, pen, atom/source, mob/attacker) + if (damage > max_block || pen > max_block) + return 0 + + var/effective_block = blocking ? chance * 1.5 : chance + + var/conscious_pilot_exists = FALSE + for (var/mob/living/pilot in owner.pilots) + if (!pilot.incapacitated()) + conscious_pilot_exists = TRUE + break + + if (!conscious_pilot_exists) + effective_block *= 0.5 //Who is going to block anything? + + //Bit copypasta but I am doing something different from normal shields + var/attack_dir = 0 + if (istype(source, /obj/item/projectile)) + var/obj/item/projectile/P = source + attack_dir = get_dir(get_turf(src), P.starting) + else if (attacker) + attack_dir = get_dir(get_turf(src), get_turf(attacker)) + else if (source) + attack_dir = get_dir(get_turf(src), get_turf(source)) + + if (attack_dir == turn(owner.dir, -90) || attack_dir == turn(owner.dir, 90)) + effective_block *= 0.8 + else if (attack_dir == turn(owner.dir, 180)) + effective_block = 0 + + return effective_block + +/obj/item/mech_equipment/ballistic_shield/proc/on_block_attack() + if (blocking) + //Reset timer for maximum chainblocks + last_max_block = 0 + +/obj/aura/mech_ballistic + icon = 'icons/mecha/ballistic_shield.dmi' + name = "mech_ballistic_shield" + var/obj/item/mech_equipment/ballistic_shield/shield = null + layer = MECH_UNDER_LAYER + plane = DEFAULT_PLANE + mouse_opacity = 0 + +/obj/aura/mech_ballistic/Initialize(maploading, obj/item/mech_equipment/ballistic_shield/holder) + . = ..() + shield = holder + + //Get where we are attached so we know what icon to use + if (holder && holder.owner) + var/mob/living/exosuit/E = holder.owner + for (var/hardpoint in E.hardpoints) + var/obj/item/mech_equipment/hardpoint_object = E.hardpoints[hardpoint] + if (holder == hardpoint_object) + icon_state = "mech_shield_[hardpoint]" + var/image/I = image(icon, "[icon_state]_over") + I.layer = ABOVE_HUMAN_LAYER + overlays.Add(I) + +/obj/aura/mech_ballistic/added_to(mob/living/target) + . = ..() + add_vis_contents(target, src) + set_dir(target.dir) + global.events_repository.register(/decl/observ/dir_set, user, src, /obj/aura/mech_ballistic/proc/update_dir) + +/obj/aura/mech_ballistic/proc/update_dir(user, old_dir, dir) + set_dir(dir) + +/obj/aura/mech_ballistic/Destroy() + if (user) + global.events_repository.unregister(/decl/observ/dir_set, user, src, /obj/aura/mech_ballistic/proc/update_dir) + remove_vis_contents(user, src) + shield = null + . = ..() + +/obj/aura/mech_ballistic/bullet_act(obj/item/projectile/P, def_zone) + . = ..() + if (shield) + if (prob(shield.block_chance(P.damage, P.armor_penetration, source = P))) + user.visible_message(SPAN_WARNING("\The [P] is blocked by \the [user]'s [shield].")) + user.bullet_impact_visuals(P, def_zone, 0) + shield.on_block_attack() + return AURA_FALSE|AURA_CANCEL + +/obj/aura/mech_ballistic/hitby(atom/movable/M, datum/thrownthing/TT) + . = ..() + if (shield) + var/throw_damage = 0 + if (istype(M,/obj/)) + var/obj/O = M + throw_damage = O.throwforce*(TT.speed/THROWFORCE_SPEED_DIVISOR) + + if (prob(shield.block_chance(throw_damage, 0, source = M, attacker = TT.thrower))) + user.visible_message(SPAN_WARNING("\The [M] bounces off \the [user]'s [shield].")) + playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) + shield.on_block_attack() + return AURA_FALSE|AURA_CANCEL + +/obj/aura/mech_ballistic/attackby(obj/item/I, mob/user) + . = ..() + if (shield) + if (prob(shield.block_chance(I.force, I.armor_penetration, source = I, attacker = user))) + user.visible_message(SPAN_WARNING("\The [I] is blocked by \the [user]'s [shield].")) + playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) + return AURA_FALSE|AURA_CANCEL +*/ +/obj/item/mech_equipment/flash + name = "exosuit flash" + icon_state = "mech_flash" + restricted_hardpoints = list(HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER) + restricted_software = list(MECH_SOFTWARE_WEAPONS) + active_power_use = 7 KILOWATTS + var/flash_range = 5 + var/next_use = 0 + var/halloss_per_flash = 25 + origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 3) + +/obj/item/mech_equipment/flash/proc/area_flash() + if(world.time < next_use) + to_chat(owner, SPAN_WARNING("\The [src] is recharging!")) + return + + owner.setClickCooldown(5) + next_use = world.time + 15 SECONDS + + playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) + for (var/mob/living/O in oviewers(flash_range, owner)) + flash(O) + +/obj/item/mech_equipment/flash/proc/flash(var/mob/living/L) + if(!istype(L)) + return + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.stat == DEAD) + return + if(H.eyecheck() > 0) + return + var/flash_strength = 5 * H.species.flash_mod + if(flash_strength <= 0) + return + H.Confuse(flash_strength + 5) + H.Blind(flash_strength) + H.eye_blurry = max(H.eye_blurry, flash_strength + 5) + H.flash_eyes() + H.adjustHalLoss(halloss_per_flash * (flash_strength / 5)) // Should take four flashes to stun. + H.apply_damage(flash_strength * H.species.flash_burn/5, BURN, BP_HEAD, 0, 0, "Photon burns") + else if(isrobot(L) && prob(30)) + var/mob/living/silicon/robot/R = L + if(R.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in R + if(shield?.active) + shield.adjust_flash_count(R, 1) + +/obj/item/mech_equipment/flash/attack_self(mob/user) + . = ..() + if(.) + if(world.time < next_use) + to_chat(user, SPAN_WARNING("\The [src] is recharging!")) + return + next_use = world.time + 20 + area_flash() + owner.setClickCooldown(5) + +/obj/item/mech_equipment/flash/afterattack(atom/target, mob/living/user, inrange, params) + . = ..() + if(.) + if(world.time < next_use) + to_chat(user, SPAN_WARNING("\The [src] is recharging!")) + return + var/mob/living/O = target + owner.setClickCooldown(5) + next_use = world.time + 15 + + playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) + flash(O) diff --git a/code/modules/exosuits/equipment/combat_projectile.dm b/code/modules/exosuits/equipment/combat_projectile.dm new file mode 100644 index 00000000000..e18f624e621 --- /dev/null +++ b/code/modules/exosuits/equipment/combat_projectile.dm @@ -0,0 +1,165 @@ +/obj/item/mech_equipment/mounted_system/projectile/attackby(var/obj/item/O, var/mob/user) + var/obj/item/gun/projectile/automatic/A = holding + if(!istype(A)) + return + if(O.has_tool_quality(TOOL_CROWBAR)) + A.unload_ammo(user) + to_chat(user, SPAN_NOTICE("You remove the ammo magazine from the [src].")) + else if(istype(O, A.magazine_type)) + A.load_ammo(O, user) + to_chat(user, SPAN_NOTICE("You load the ammo magazine into the [src].")) + + return ..() + +/obj/item/mech_equipment/mounted_system/projectile/attack_self(var/mob/user) + . = ..() + if(. && holding) + var/obj/item/gun/M = holding + return M.switch_firemodes(user) + +/obj/item/gun/projectile/automatic/get_hardpoint_status_value() + if(!isnull(ammo_magazine)) + return ammo_magazine.stored_ammo.len + +/obj/item/gun/projectile/automatic/get_hardpoint_maptext() + if(!isnull(ammo_magazine)) + return "[ammo_magazine.stored_ammo.len]/[ammo_magazine.max_ammo]" + return 0 + +//Weapons below this. +/obj/item/mech_equipment/mounted_system/projectile + name = "mounted submachine gun" + icon_state = "mech_ballistic" + holding_type = /obj/item/gun/projectile/automatic/advanced_smg/mech + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_WEAPONS) + origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 6, TECH_ENGINEERING = 5) + +/obj/item/gun/projectile/automatic/advanced_smg/mech + caliber = "9mm" + one_handed_penalty = FALSE + magazine_type = /obj/item/ammo_magazine/mech/smg_top + allowed_magazines = list(/obj/item/ammo_magazine/mech/smg_top) + + firemodes = list( + list(mode_name="semi auto", burst=1, fire_delay=null, burst_accuracy=null, dispersion=null), + list(mode_name="3-round bursts", burst=3, fire_delay=null, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 1.6, 2.4, 2.4), autofire_enabled=0), + list(mode_name="short bursts", burst=5, fire_delay=null, burst_accuracy=list(0,-1,-1,-1,-2), dispersion=list(1.6, 1.6, 2.0, 2.0, 2.4), autofire_enabled=0), + list(mode_name="full auto", burst=1, fire_delay=null, burst_delay=1, burst_accuracy=list(0,-1,-1,-1,-2), dispersion=list(1.6, 1.6, 2.0, 2.0, 2.4), autofire_enabled=1) + ) + +/obj/item/mech_equipment/mounted_system/projectile/assault_rifle + name = "mounted assault rifle" + icon_state = "mech_ballistic2" + holding_type = /obj/item/gun/projectile/automatic/sts35/mech + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_WEAPONS) + origin_tech = list(TECH_DATA = 4, TECH_COMBAT = 8, TECH_ENGINEERING = 6) + +/obj/item/gun/projectile/automatic/sts35/mech + caliber = "5.45mm" + magazine_type = /obj/item/ammo_magazine/mech/rifle + allowed_magazines = list(/obj/item/ammo_magazine/mech/rifle) + + firemodes = list( + list(mode_name="semi auto", burst=1, fire_delay=null, burst_accuracy=null, dispersion=null, autofire_enabled=0), + list(mode_name="3-round bursts", burst=3, fire_delay=null, burst_accuracy=list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), autofire_enabled=0), + list(mode_name="full auto", burst=1, fire_delay=null, burst_delay=1, burst_accuracy = list(0,-1,-1), dispersion=list(0.0, 0.6, 1.0), autofire_enabled=1) + ) + +/obj/item/mech_equipment/mounted_system/projectile/machine + name = "mounted machine gun" + icon_state = "mech_machine_gun" + holding_type = /obj/item/gun/projectile/automatic/l6_saw/mech + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_WEAPONS) + +/obj/item/gun/projectile/automatic/l6_saw/mech + caliber = "5.45mm" + one_handed_penalty = FALSE + magazine_type = /obj/item/ammo_magazine/mech/rifle/drum + allowed_magazines = list(/obj/item/ammo_magazine/mech/rifle/drum) + +// Magazines below this. + +/obj/item/ammo_magazine/mech + icon_state = "ashot-mag" + desc = "A large magazine for a mech's gun. Looks way too big for a normal gun." + +/obj/item/ammo_magazine/mech/attack_self(mob/user) + to_chat(user, SPAN_WARNING("It's pretty hard to extract ammo from a magazine that fits on a mech. You'll have to do it one round at a time.")) + return + +/obj/item/ammo_magazine/mech/smg_top + name = "large 7mm magazine" + ammo_type = /obj/item/ammo_casing/a9mm + caliber = "9mm" + max_ammo = 90 + +/obj/item/ammo_magazine/mech/rifle + name = "large assault rifle magazine" + caliber = "5.45mm" + icon_state = "m545" + ammo_type = /obj/item/ammo_casing/a545 + max_ammo = 100 + +/obj/item/ammo_magazine/mech/rifle/drum + name = "large machine gun magazine" + caliber = "5.45mm" + icon_state = "a545" + ammo_type = /obj/item/ammo_casing/a545 + max_ammo = 300 +/* +// Handling for auto-fire mechanic +/mob/living/exosuit/can_autofire(obj/item/gun/autofiring, atom/autofiring_at) + if(autofiring.autofiring_by != src) + return FALSE + var/client/C = current_user ? current_user.client : client + + if(!C || !C.mob || C.mob.incapacitated()) + return FALSE + + if(!(autofiring_at in view(C.view, src))) + return FALSE + if(!(get_dir(src, autofiring_at) & dir)) + return FALSE + if(!(autofiring in selected_system)) // Make sure the gun is still selected. + return FALSE + return TRUE + +/obj/item/mech_equipment/mounted_system/projectile/MouseDownInteraction(atom/object, location, control, params, mob/user) + var/obj/item/gun/gun = holding + if(istype(object) && (isturf(object) || isturf(object.loc)) && istype(gun)) + if(user != src) + if(!user.incapacitated()) + gun.set_autofire(object, owner, FALSE) // Passed gun-firer is still the exosuit since all checks need to be done on the suit. + owner.current_user = user + else + if(!owner.incapacitated()) + gun.set_autofire(object, owner, FALSE) + owner.current_user = null + +/obj/item/mech_equipment/mounted_system/projectile/MouseUpInteraction(atom/object, location, control, params, mob/user) + var/obj/item/gun/gun = holding + if(istype(gun)) + gun.clear_autofire() + if(owner) // In case the owning exosuit has been gibbed etc. + owner.current_user = null + +/obj/item/mech_equipment/mounted_system/projectile/MouseDragInteraction(atom/src_object, atom/over_object, src_location, over_location, src_control, over_control, params, mob/user) + var/obj/item/gun/gun = holding + if(!owner) + gun?.clear_autofire() + return + if(!istype(gun)) + owner?.current_user = null + return + if(istype(over_object) && (isturf(over_object) || isturf(over_object.loc))) + if(user.incapacitated() || (user != owner && user != owner.current_user)) + gun.clear_autofire() + return + gun.set_autofire(over_object, owner, FALSE) + return + + gun.clear_autofire() +*/ diff --git a/code/modules/exosuits/equipment/engineering.dm b/code/modules/exosuits/equipment/engineering.dm new file mode 100644 index 00000000000..f2c52ff3035 --- /dev/null +++ b/code/modules/exosuits/equipment/engineering.dm @@ -0,0 +1,38 @@ + +/obj/item/mech_equipment/mounted_system/rcd + icon_state = "mech_rcd" + holding_type = /obj/item/rcd/mounted + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_ENGINEERING) + +/obj/item/rcd/mounted/get_hardpoint_maptext() + var/obj/item/mech_equipment/mounted_system/MS = loc + if(istype(MS) && MS.owner) + var/obj/item/cell/C = MS.owner.get_cell() + if(istype(C)) + return "[round(C.charge)]/[round(C.maxcharge)]" + return null + +/obj/item/rcd/mounted/get_hardpoint_status_value() + var/obj/item/mech_equipment/mounted_system/MS = loc + if(istype(MS) && MS.owner) + var/obj/item/cell/C = MS.owner.get_cell() + if(istype(C)) + return C.charge/C.maxcharge + return null + +/obj/item/extinguisher/mech + max_water = 4000 //Good is gooder + icon_state = "mech_exting" + +/obj/item/extinguisher/mech/get_hardpoint_maptext() + return "[reagents.total_volume]/[max_water]" + +/obj/item/extinguisher/mech/get_hardpoint_status_value() + return reagents.total_volume/max_water + +/obj/item/mech_equipment/mounted_system/extinguisher + icon_state = "mech_exting" + holding_type = /obj/item/extinguisher/mech + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_ENGINEERING) diff --git a/code/modules/exosuits/equipment/medical.dm b/code/modules/exosuits/equipment/medical.dm new file mode 100644 index 00000000000..4c4ab25bc6e --- /dev/null +++ b/code/modules/exosuits/equipment/medical.dm @@ -0,0 +1,77 @@ +/obj/item/mech_equipment/sleeper + name = "\improper exosuit sleeper" + desc = "An exosuit-mounted sleeper designed to mantain patients stabilized on their way to medical facilities." + icon_state = "mech_sleeper" + restricted_hardpoints = list(HARDPOINT_BACK) + restricted_software = list(MECH_SOFTWARE_MEDICAL) + equipment_delay = 30 //don't spam it on people pls + active_power_use = 0 //Usage doesn't really require power. We don't want people stuck inside + origin_tech = list(TECH_DATA = 2, TECH_BIO = 3) + passive_power_use = 1.5 KILOWATTS + var/obj/machinery/sleeper/mounted/sleeper = null + +/obj/item/mech_equipment/sleeper/Initialize() + . = ..() + sleeper = new /obj/machinery/sleeper/mounted(src) + sleeper.forceMove(src) + +/obj/item/mech_equipment/sleeper/Destroy() + sleeper.go_out() //If for any reason you weren't outside already. + QDEL_NULL(sleeper) + . = ..() + +/obj/item/mech_equipment/sleeper/uninstalled() + . = ..() + sleeper.go_out() + +/obj/item/mech_equipment/sleeper/attack_self(var/mob/user) + . = ..() + if(.) + sleeper.ui_interact(user) + +/obj/item/mech_equipment/sleeper/attackby(var/obj/item/I, var/mob/user) + if(istype(I, /obj/item/reagent_containers/glass)) + sleeper.attackby(I, user) + else return ..() + +/obj/item/mech_equipment/sleeper/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params) + . = ..() + if(.) + if(ishuman(target) && !sleeper.occupant) + owner.visible_message(SPAN_NOTICE("\The [src] is lowered down to load [target]")) + sleeper.go_in(target, user) + else to_chat(user, SPAN_WARNING("You cannot load that in!")) + +/obj/item/mech_equipment/sleeper/get_hardpoint_maptext() + if(sleeper && sleeper.occupant) + return "[sleeper.occupant]" + +/obj/machinery/sleeper/mounted + name = "\improper mounted sleeper" + density = 0 + anchored = 0 + idle_power_usage = 0 + active_power_usage = 0 //It'd be hard to handle, so for now all power is consumed by mech sleeper object + stasis_level = 0 + interact_offline = TRUE + +/obj/machinery/sleeper/mounted/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.mech_state) + . = ..() + +/obj/machinery/sleeper/mounted/nano_host() + var/obj/item/mech_equipment/sleeper/S = loc + if(istype(S)) + return S.owner + return null + +//You cannot modify these, it'd probably end with something in nullspace. In any case basic meds are plenty for an ambulance +/obj/machinery/sleeper/mounted/attackby(var/obj/item/I, var/mob/user) + if(istype(I, /obj/item/reagent_containers/glass)) + if(!user.unEquip(I, src)) + return + + if(beaker) + beaker.forceMove(get_turf(src)) + user.visible_message("\The [user] removes \the [beaker] from \the [src].", "You remove \the [beaker] from \the [src].") + beaker = I + user.visible_message("\The [user] adds \a [I] to \the [src].", "You add \a [I] to \the [src].") diff --git a/code/modules/exosuits/equipment/utility.dm b/code/modules/exosuits/equipment/utility.dm new file mode 100644 index 00000000000..b2ffb9b20d6 --- /dev/null +++ b/code/modules/exosuits/equipment/utility.dm @@ -0,0 +1,337 @@ +/obj/item/mech_equipment/clamp + name = "mounted clamp" + desc = "A large, heavy industrial cargo loading clamp." + icon_state = "mech_clamp" + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_UTILITY) + var/obj/carrying + origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mech_equipment/clamp/resolve_attackby(atom/A, mob/user, attack_modifier, click_parameters) + if(istype(A, /obj/structure/closet) && owner) + return 0 + return ..() + +/obj/item/mech_equipment/clamp/attack_hand(mob/user) + if(owner && (user in owner.pilots)) + if(!owner.hatch_closed && carrying) + if(user.put_in_active_hand(carrying)) + owner.visible_message(SPAN_NOTICE("\The [user] carefully grabs \the [carrying] from \the [src].")) + carrying = null + . = ..() + +/obj/item/mech_equipment/clamp/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params) + . = ..() + + if(. && !carrying) + if(istype(target, /obj)) + + + var/obj/O = target + if(length(O.buckled_mobs)) + return + if(locate(/mob/living) in O) + to_chat(user,"You can't load living things into the cargo compartment.") + return + + if(O.anchored) + to_chat(user, "[target] is firmly secured.") + return + + + owner.visible_message(SPAN_NOTICE("\The [owner] begins loading \the [O].")) + if(do_after(owner, 20, O, 0, 1)) + O.forceMove(src) + carrying = O + owner.visible_message(SPAN_NOTICE("\The [owner] loads \the [O] into its cargo compartment.")) + + + //attacking - Cannot be carrying something, cause then your clamp would be full + else if(istype(target,/mob/living)) + var/mob/living/M = target + if(user.a_intent == I_HURT) + log_and_message_admins(user, "attempted to clamp [M] with [src]") + owner.setClickCooldown(owner.arms ? owner.arms.action_delay * 3 : 30) //This is an inefficient use of your powers + if(prob(33)) + owner.visible_message(SPAN_DANGER("[owner] swings its [src] in a wide arc at [target] but misses completely!")) + return + M.attack_generic(owner, (owner.arms ? owner.arms.melee_damage * 1.5 : 0), "slammed") //Honestly you should not be able to do this without hands, but still + M.throw_at(get_edge_target_turf(owner ,owner.dir),5, 2) + to_chat(user, "You slam [target] with [src.name].") + owner.visible_message(SPAN_DANGER("[owner] slams [target] with the hydraulic clamp.")) + else + step_away(M, owner) + to_chat(user, "You push [target] out of the way.") + owner.visible_message("[owner] pushes [target] out of the way.") + +/obj/item/mech_equipment/clamp/attack_self(var/mob/user) + . = ..() + if(.) + if(!carrying) + to_chat(user, SPAN_WARNING("You are not carrying anything in \the [src].")) + else + owner.visible_message(SPAN_NOTICE("\The [owner] unloads \the [carrying].")) + carrying.forceMove(get_turf(src)) + carrying = null + +/obj/item/mech_equipment/clamp/get_hardpoint_maptext() + if(carrying) + return carrying.name + . = ..() + +/obj/item/mech_equipment/clamp/uninstalled() + if(carrying) + carrying.dropInto(loc) + carrying = null + . = ..() + +// A lot of this is copied from floodlights. +/obj/item/mech_equipment/light + name = "floodlight" + desc = "An exosuit-mounted light." + icon_state = "mech_floodlight" + item_state = "mech_floodlight" + restricted_hardpoints = list(HARDPOINT_HEAD) + mech_layer = MECH_INTERMEDIATE_LAYER + + var/on = 0 + var/l_max_bright = 0.9 + var/l_inner_range = 1 + var/l_outer_range = 6 + origin_tech = list(TECH_MATERIAL = 1, TECH_ENGINEERING = 1) + +/obj/item/mech_equipment/light/attack_self(var/mob/user) + . = ..() + if(.) + on = !on + to_chat(user, "You switch \the [src] [on ? "on" : "off"].") + update_icon() + owner.update_icon() + +/obj/item/mech_equipment/light/update_icon() + if(on) + icon_state = "[initial(icon_state)]-on" + set_light(l_max_bright, l_inner_range, l_outer_range) + else + icon_state = "[initial(icon_state)]" + set_light(0, 0) + +/obj/item/mech_equipment/light/uninstalled() + on = FALSE + update_icon() + . = ..() + +#define CATAPULT_SINGLE 1 +#define CATAPULT_AREA 2 + +/obj/item/mech_equipment/catapult + name = "gravitational catapult" + desc = "An exosuit-mounted gravitational catapult." + icon_state = "mech_clamp" + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_UTILITY) + var/mode = CATAPULT_SINGLE + var/atom/movable/locked + equipment_delay = 30 //Stunlocks are not ideal + origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4, TECH_MAGNET = 4) + require_adjacent = FALSE + +/obj/item/mech_equipment/catapult/get_hardpoint_maptext() + var/string + if(locked) + string = locked.name + " - " + if(mode == 1) + string += "Single" + else string += "Area" + return string + + +/obj/item/mech_equipment/catapult/attack_self(var/mob/user) + . = ..() + if(.) + mode = mode == CATAPULT_SINGLE ? CATAPULT_AREA : CATAPULT_SINGLE + to_chat(user, SPAN_NOTICE("You set \the [src] to [mode == CATAPULT_SINGLE ? "single" : "multi"]-target mode.")) + update_icon() + + +/obj/item/mech_equipment/catapult/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params) + . = ..() + if(.) + + switch(mode) + if(CATAPULT_SINGLE) + if(!locked) + var/atom/movable/AM = target + if(!istype(AM) || AM.anchored || !AM.simulated) + to_chat(user, SPAN_NOTICE("Unable to lock on [target].")) + return + locked = AM + to_chat(user, SPAN_NOTICE("Locked on [AM].")) + return + else if(target != locked) + if(locked in view(owner)) + locked.throw_at(target, 14, 1.5, owner) + log_and_message_admins("used [src] to throw [locked] at [target].", user, owner.loc) + locked = null + + var/obj/item/cell/C = owner.get_cell() + if(istype(C)) + C.use(active_power_use * CELLRATE) + + else + locked = null + to_chat(user, SPAN_NOTICE("Lock on [locked] disengaged.")) + if(CATAPULT_AREA) + + var/list/atoms = list() + if(isturf(target)) + atoms = range(target,3) + else + atoms = orange(target,3) + for(var/atom/movable/A in atoms) + if(A.anchored || !A.simulated) continue + var/dist = 5-get_dist(A,target) + A.throw_at(get_edge_target_turf(A,get_dir(target, A)),dist,0.7) + + + log_and_message_admins("used [src]'s area throw on [target].", user, owner.loc) + var/obj/item/cell/C = owner.get_cell() + if(istype(C)) + C.use(active_power_use * CELLRATE * 2) //bit more expensive to throw all + + + +#undef CATAPULT_SINGLE +#undef CATAPULT_AREA + + +/obj/item/material/drill_head + var/durability = 0 + name = "drill head" + desc = "A replaceable drill head usually used in exosuit drills." + icon_state = "drill_head" + +/obj/item/material/drill_head/Initialize() + . = ..() + durability = 2 * material.integrity + +/obj/item/mech_equipment/drill + name = "drill" + desc = "This is the drill that'll pierce the heavens!" + icon_state = "mech_drill" + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND) + restricted_software = list(MECH_SOFTWARE_UTILITY) + equipment_delay = 10 + + //Drill can have a head + var/obj/item/material/drill_head/drill_head + origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + + + +/obj/item/mech_equipment/drill/Initialize() + . = ..() + drill_head = new /obj/item/material/drill_head(src, MAT_STEEL)//You start with a basic steel head + +/obj/item/mech_equipment/drill/attack_self(var/mob/user) + . = ..() + if(.) + if(drill_head) + owner.visible_message(SPAN_WARNING("[owner] revs the [drill_head], menancingly.")) + playsound(src, 'sound/weapons/circsawhit.ogg', 50, 1) + + +/obj/item/mech_equipment/drill/afterattack(var/atom/target, var/mob/living/user, var/inrange, var/params) + . = ..() + if(.) + if(isobj(target)) + var/obj/target_obj = target + if(target_obj.unacidable) + return + if(istype(target,/obj/item/material/drill_head)) + var/obj/item/material/drill_head/DH = target + if(drill_head) + owner.visible_message(SPAN_NOTICE("\The [owner] detaches the [drill_head] mounted on the [src].")) + drill_head.forceMove(owner.loc) + DH.forceMove(src) + drill_head = DH + owner.visible_message(SPAN_NOTICE("\The [owner] mounts the [drill_head] on the [src].")) + return + + if(drill_head == null) + to_chat(user, SPAN_WARNING("Your drill doesn't have a head!")) + return + + var/obj/item/cell/C = owner.get_cell() + if(istype(C)) + C.use(active_power_use * CELLRATE) + owner.visible_message(SPAN_DANGER("\The [owner] starts to drill \the [target]."), SPAN_DANGER("You hear a large drill.")) + + var/T = target.loc + + //Better materials = faster drill! + var/delay = max(5, 20 - drill_head.material.protectiveness) + owner.setClickCooldown(delay) //Don't spamclick! + if(drill_head && do_after(owner, delay, target)) + if(src == owner.selected_system) + if(drill_head.durability <= 0) + drill_head.shatter() + drill_head = null + return + else if(drill_head.durability <= (drill_head.material.integrity * 0.2) && prob(20)) + to_chat(user, SPAN_NOTICE("\The [drill_head] emits a terrible screeching!")) + if(istype(target, /turf/simulated/wall)) + var/turf/simulated/wall/W = target + if(max(W.material.hardness, W.reinf_material ? W.reinf_material.hardness : 0) > drill_head.material.hardness) + to_chat(user, SPAN_WARNING("\The [target] is too hard to drill through with this drill head.")) + return + target.ex_act(2) + drill_head.durability -= 1 + log_and_message_admins("used [src] on the wall [W].", user, owner.loc) + else if(istype(target, /turf/simulated/mineral)) + for(var/turf/simulated/mineral/M in range(target,1)) + if(get_dir(owner,M)&owner.dir) + M.GetDrilled() + drill_head.durability -= 1 + else if(target.loc == T) + target.ex_act(2) + drill_head.durability -= 1 + log_and_message_admins("[src] used to drill [target].", user, owner.loc) + + + if(owner.hardpoints.len) //if this isn't true the drill should not be working to be fair + for(var/hardpoint in owner.hardpoints) + var/obj/item/I = owner.hardpoints[hardpoint] + if(!istype(I)) + continue + var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in I //clamps work, but anythin that contains an ore crate internally is valid + if(ore_box) + for(var/obj/item/ore/ore in range(T,1)) + if(get_dir(owner,ore)&owner.dir) + ore.Move(ore_box) + break // Already placed our ore in a container, don't go to another. + + playsound(src, 'sound/weapons/circsawhit.ogg', 50, 1) + + else + to_chat(user, "You must stay still while the drill is engaged!") + + + return 1 + + + +/* +/obj/item/mech_equipment/mounted_system/taser/plasma + name = "mounted plasma cutter" + desc = "An industrial plasma cutter mounted onto the chassis of the mech. " + icon_state = "railauto" //TODO: Make a new sprite that doesn't get sec called on you. + holding_type = /obj/item/gun/energy/plasmacutter/mounted/mech + restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND, HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER) + restricted_software = list(MECH_SOFTWARE_UTILITY) + origin_tech = list(TECH_MATERIAL = 4, TECH_PHORON = 4, TECH_ENGINEERING = 6, TECH_COMBAT = 3) + +/obj/item/gun/energy/plasmacutter/mounted/mech + use_external_power = TRUE + has_safety = FALSE +*/ diff --git a/code/modules/exosuits/exosuit.dm b/code/modules/exosuits/exosuit.dm new file mode 100644 index 00000000000..91a03790758 --- /dev/null +++ b/code/modules/exosuits/exosuit.dm @@ -0,0 +1,199 @@ + +// Big stompy robots. +/mob/living/exosuit + name = "exosuit" + desc = "A powerful machine piloted from a cockpit, but worn like a suit of armour." + description_info = "Interacting with the exosuit with an empty hand will attempt to remove the pilot, if the hatch is open." + density = TRUE + opacity = TRUE + anchored = TRUE + default_pixel_x = -8 + default_pixel_y = 0 + status_flags = PASSEMOTES + a_intent = I_HURT + mob_size = MOB_LARGE + + var/emp_damage = 0 + + var/obj/item/radio/radio + + var/datum/material/material + + var/wreckage_path = /obj/structure/exosuit_wreckage + var/mech_turn_sound = 'sound/mecha/mechturn.ogg' + var/mech_step_sound = 'sound/mecha/mechstep.ogg' + + // Access updating/container. + var/obj/item/card/id/access_card + var/list/saved_access = list() + var/sync_access = 1 + + // Mob currently piloting the exosuit. + var/list/pilots + var/list/pilot_overlays + + // Visible external components. Not strictly accurately named for non-humanoid machines (submarines) but w/e + var/obj/item/mech_component/manipulators/arms + var/obj/item/mech_component/propulsion/legs + var/obj/item/mech_component/sensors/head + var/obj/item/mech_component/chassis/body + + // Invisible components. + var/datum/effect_system/spark_spread/sparks + + // Equipment tracking vars. + var/obj/item/mech_equipment/selected_system + var/selected_hardpoint + var/list/hardpoints = list() + var/hardpoints_locked + var/maintenance_protocols + + // Cockpit access vars. + var/hatch_closed = FALSE + var/hatch_locked = FALSE + + //Air! + var/use_air = FALSE + + var/next_step = 0 + + // Interface stuff. + var/list/hud_elements = list() + var/list/hardpoint_hud_elements = list() + var/obj/screen/movable/exosuit/health/hud_health + var/obj/screen/movable/exosuit/toggle/hatch_open/hud_open + var/obj/screen/movable/exosuit/power/hud_power + +/mob/living/exosuit/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame) + . = ..() + + if(!access_card) access_card = new (src) + + pixel_x = default_pixel_x + pixel_y = default_pixel_y + sparks = new(src) + + // Grab all the supplied components. + if(source_frame) + if(source_frame.set_name) + name = source_frame.set_name + if(source_frame.arms) + source_frame.arms.forceMove(src) + arms = source_frame.arms + if(source_frame.legs) + source_frame.legs.forceMove(src) + legs = source_frame.legs + if(source_frame.head) + source_frame.head.forceMove(src) + head = source_frame.head + if(source_frame.body) + source_frame.body.forceMove(src) + body = source_frame.body + if(source_frame.material) + material = source_frame.material + + updatehealth() + + // Generate hardpoint list. + var/list/component_descriptions + for(var/obj/item/mech_component/comp in list(arms, legs, head, body)) + if(comp.exosuit_desc_string) + LAZYADD(component_descriptions, comp.exosuit_desc_string) + if(LAZYLEN(comp.has_hardpoints)) + for(var/hardpoint in comp.has_hardpoints) + hardpoints[hardpoint] = null + + if(head && head.radio) + radio = new(src) + + if(LAZYLEN(component_descriptions)) + desc = "[desc] It has been built with [english_list(component_descriptions)]." + + // Create HUD. + InitializeHud() + + // Build icon. + update_icon() + +/mob/living/exosuit/Destroy() + + selected_system = null + + for(var/thing in pilots) + var/mob/pilot = thing + if(pilot.client) + pilot.client.screen -= hud_elements + pilot.client.images -= hud_elements + pilot.forceMove(get_turf(src)) + pilots = null + + for(var/thing in hud_elements) + qdel(thing) + hud_elements.Cut() + + for(var/hardpoint in hardpoints) + qdel(hardpoints[hardpoint]) + hardpoints.Cut() + + QDEL_NULL(access_card) + QDEL_NULL(arms) + QDEL_NULL(legs) + QDEL_NULL(head) + QDEL_NULL(body) + + for(var/hardpoint in hardpoint_hud_elements) + var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[hardpoint] + H.owner = null + H.holding = null + qdel(H) + hardpoint_hud_elements.Cut() + + . = ..() + +/mob/living/exosuit/IsAdvancedToolUser() // If there's one pilot that's intelligent, assumedly they will keep the gaggle together. + if(LAZYLEN(pilots)) + for(var/mob/living/L in pilots) + if(L.IsAdvancedToolUser() && L.stat == CONSCIOUS) + return TRUE + return FALSE + return TRUE + +/mob/living/exosuit/examine(mob/user) + . = ..() + if(LAZYLEN(pilots) && (!hatch_closed || body.pilot_coverage < 100 || body.transparent_cabin)) + to_chat(user, "It is being piloted by [english_list(pilots, nothing_text = "nobody")].") + if(hardpoints.len) + to_chat(user, "It has the following hardpoints:") + for(var/hardpoint in hardpoints) + var/obj/item/I = hardpoints[hardpoint] + to_chat(user, "- [hardpoint]: [istype(I) ? "[I]" : "nothing"].") + else + to_chat(user, "It has no visible hardpoints.") + + for(var/obj/item/mech_component/thing in list(arms, legs, head, body)) + if(!thing) + continue + var/damage_string = "destroyed" + switch(thing.damage_state) + if(MECH_COMPONENT_DAMAGE_UNDAMAGED) + damage_string = "undamaged" + if(MECH_COMPONENT_DAMAGE_DAMAGED) + damage_string = "damaged" + if(MECH_COMPONENT_DAMAGE_DAMAGED_BAD) + damage_string = "badly damaged" + if(MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL) + damage_string = "almost destroyed" + to_chat(user, "Its [thing.name] [thing.gender == PLURAL ? "are" : "is"] [damage_string].") + + to_chat(user, "It menaces with reinforcements of [material].") + +/mob/living/exosuit/return_air() + return (body && body.pilot_coverage >= 100 && hatch_closed) ? body.cockpit : loc.return_air() + +/mob/living/exosuit/GetIdCard() + return access_card + +/mob/living/exosuit/set_dir() + . = ..() + if(.) + update_pilots() diff --git a/code/modules/exosuits/exosuit_construction.dm b/code/modules/exosuits/exosuit_construction.dm new file mode 100644 index 00000000000..eaf89eee1c8 --- /dev/null +++ b/code/modules/exosuits/exosuit_construction.dm @@ -0,0 +1,192 @@ +/mob/living/exosuit/proc/dismantle() + + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + var/obj/structure/heavy_vehicle_frame/frame = new(get_turf(src)) + for(var/hardpoint in hardpoints) + remove_system(hardpoint, force = 1) + hardpoints.Cut() + + if(arms) + frame.arms = arms + arms.forceMove(frame) + arms = null + if(legs) + frame.legs = legs + legs.forceMove(frame) + legs = null + if(body) + frame.body = body + body.forceMove(frame) + body = null + if(head) + frame.head = head + head.forceMove(frame) + head = null + + frame.is_wired = EXOFRAME_WIRED_ADJUSTED + frame.is_reinforced = EXOFRAME_REINFORCED_WELDED + frame.set_name = name + frame.name = "frame of \the [frame.set_name]" + frame.material = material + frame.update_icon() + + qdel(src) + +/mob/living/exosuit/proc/forget_module(var/module_to_forget) + //Realistically a module disappearing without being uninstalled is wrong and a bug or adminbus + var/target = null + for(var/hardpoint in hardpoints) + if(hardpoints[hardpoint] == module_to_forget) + target = hardpoint + break + + hardpoints[target] = null + + if(target == selected_hardpoint) + clear_selected_hardpoint() + + GLOB.destroyed_event.unregister(module_to_forget, src, .proc/forget_module) + + var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[target] + H.holding = null + + hud_elements -= module_to_forget + refresh_hud() + update_icon() + + for(var/mob/pilot in pilots) + pilot?.client.screen -= module_to_forget + +/mob/living/exosuit/proc/can_install_system(var/obj/item/system, var/system_hardpoint, var/mob/user) + if(hardpoints_locked) + if(user) + to_chat(user, SPAN_WARNING("Hardpoint system access is locked.")) + return FALSE + if(hardpoints[system_hardpoint]) + if(user) + to_chat(user, SPAN_WARNING("[system_hardpoint] is already in use.")) + return FALSE + + return TRUE + +/mob/living/exosuit/proc/do_install_system(var/obj/item/system, var/system_hardpoint) + var/obj/item/mech_equipment/ME = system + if(istype(ME)) + if(ME.restricted_hardpoints && !(system_hardpoint in ME.restricted_hardpoints)) + return FALSE + if(ME.restricted_software) + if(!head || !head.software) + return FALSE + var/list/found_software = ME.restricted_software & head.software.installed_software + if(!LAZYLEN(found_software)) + return FALSE + ME.installed(src) + GLOB.destroyed_event.register(system, src, .proc/forget_module) + + system.forceMove(src) + hardpoints[system_hardpoint] = system + + var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[system_hardpoint] + H.holding = system + + system.screen_loc = H.screen_loc + system.hud_layerise() + + hud_elements |= system + refresh_hud() + update_icon() + + return TRUE + +/mob/living/exosuit/proc/install_system(var/obj/item/system, var/system_hardpoint, var/mob/user) + if(!can_install_system(system, system_hardpoint, user)) + return FALSE + + if(user) + user.visible_message(SPAN_NOTICE("\The [user] begins to install \the [system] into \the [src].")) + if(!do_after(user, 30, src) || user.get_active_hand() != system) + return FALSE + + if(user.unEquip(system)) + to_chat(user, SPAN_NOTICE("You install \the [system] in \the [src]'s [system_hardpoint].")) + playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1) + else return FALSE + + return do_install_system(system, system_hardpoint) + +/mob/living/exosuit/proc/install_system_initialize(var/obj/item/system, var/system_hardpoint) + if(!can_install_system(system, system_hardpoint)) + return FALSE + + return do_install_system(system, system_hardpoint) + +/mob/living/exosuit/proc/can_remove_system(var/system_hardpoint, var/mob/user, var/force) + if(hardpoints_locked && !force) + if(user) + to_chat(user, SPAN_WARNING("Hardpoint system access is locked.")) + return FALSE + if(!hardpoints[system_hardpoint]) + if(user) + to_chat(user, SPAN_NOTICE("[system_hardpoint] is unoccupied.")) + return FALSE + + return TRUE + +/mob/living/exosuit/proc/do_remove_system(var/system_hardpoint) + var/obj/item/system = hardpoints[system_hardpoint] + hardpoints[system_hardpoint] = null + + if(system_hardpoint == selected_hardpoint) + clear_selected_hardpoint() + + var/obj/item/mech_equipment/ME = system + if(istype(ME)) + ME.uninstalled() + system.forceMove(get_turf(src)) + system.screen_loc = null + system.layer = initial(system.layer) + GLOB.destroyed_event.unregister(system, src, .proc/forget_module) + + var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[system_hardpoint] + H.holding = null + + for(var/thing in pilots) + var/mob/pilot = thing + if(pilot && pilot.client) + pilot.client.screen -= system + + hud_elements -= system + refresh_hud() + update_icon() + +/mob/living/exosuit/proc/remove_system(var/system_hardpoint, var/mob/user, var/force) + + if(!can_remove_system(system_hardpoint, user, force)) + return FALSE + + var/obj/item/system = hardpoints[system_hardpoint] + if(user) + var/delay = 30 + if(delay > 0) + user.visible_message(SPAN_NOTICE("\The [user] begins trying to remove \the [system] from \the [src].")) + if(!do_after(user, delay, src) || hardpoints[system_hardpoint] != system) + return FALSE + + do_remove_system(system_hardpoint) + + if(user) + system.forceMove(get_turf(user)) + user.put_in_hands(system) + to_chat(user, SPAN_NOTICE("You remove \the [system] from \the [src]'s [system_hardpoint].")) + playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1) + + return TRUE + +/mob/living/exosuit/proc/remove_system_initialize(var/system_hardpoint, var/mob/exosuit, var/force) + + if(!can_remove_system(system_hardpoint, null, force)) + return FALSE + + do_remove_system(system_hardpoint) + + return TRUE diff --git a/code/modules/exosuits/exosuit_damage.dm b/code/modules/exosuits/exosuit_damage.dm new file mode 100644 index 00000000000..bc2741cd5f1 --- /dev/null +++ b/code/modules/exosuits/exosuit_damage.dm @@ -0,0 +1,144 @@ +/mob/living/exosuit/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) + if(!effect || (blocked >= 100)) + return 0 + if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage))) + if(effect > 0 && effecttype == IRRADIATE) + effect = max((1-(getarmor(null, "rad")/100))*effect/(blocked+1),0) + var/mob/living/pilot = pick(pilots) + return pilot.apply_effect(effect, effecttype, blocked) + if(!(effecttype in list(AGONY, STUTTER, EYE_BLUR, DROWSY, WEAKEN))) + . = ..() + +/mob/living/exosuit/resolve_item_attack(var/obj/item/I, var/mob/living/user, var/def_zone) + if(!I.force) + user.visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly with \the [I].")) + return + + if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage))) + var/mob/living/pilot = pick(pilots) + return pilot.resolve_item_attack(I, user, def_zone) + + return def_zone //Careful with effects + +/mob/living/exosuit/hitby(var/atom/movable/AM, var/speed) + if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage))) + var/mob/living/pilot = pick(pilots) + return pilot.hitby(AM, speed) + . = ..() + +/mob/living/exosuit/getarmor(def_zone, type) + var/armorval = 0 + if(body) + armorval = body.get_armor(type) + if(isnull(armorval)) + armorval = 0 + + for(var/thing in modifiers) + var/datum/modifier/M = thing + var/modifier_armor = LAZYACCESS(M.armor_percent, type) + if(modifier_armor) + armorval += modifier_armor + + return armorval + +/mob/living/exosuit/getsoak(def_zone, type) + return round(getarmor(def_zone, type) / 10) + +/mob/living/exosuit/updatehealth() + maxHealth = body.mech_health + health = maxHealth-(getFireLoss()+getBruteLoss()) + +/mob/living/exosuit/adjustFireLoss(var/amount, var/obj/item/mech_component/MC = pick(list(arms, legs, body, head))) + if(MC) + MC.take_burn_damage(amount) + MC.update_health() + +/mob/living/exosuit/adjustBruteLoss(var/amount, var/obj/item/mech_component/MC = pick(list(arms, legs, body, head))) + if(MC) + MC.take_brute_damage(amount) + MC.update_health() + +/mob/living/exosuit/proc/zoneToComponent(var/zone) + switch(zone) + if(O_EYES , BP_HEAD) + return head + if(BP_L_ARM , BP_R_ARM) + return arms + if(BP_L_LEG , BP_R_LEG) + return legs + else + return body + +// Override for exosuits, since they don't take many types of damage a 'living' thing does. +/mob/living/exosuit/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null) + if(Debug2) + to_world_log("## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked].") + if(!damage || (blocked >= 100)) + return 0 + if(soaked) + if(soaked >= round(damage*0.8)) + damage -= round(damage*0.8) + else + damage -= soaked + + var/initial_blocked = blocked + + var/target = zoneToComponent(def_zone) + + if((damagetype == BRUTE || damagetype == BURN) && prob(25+(damage*2))) + sparks.set_up(3,0,src) + sparks.start() + + blocked = (100-blocked)/100 + switch(damagetype) + if(BRUTE) + adjustBruteLoss(damage * blocked, target) + if(BURN) + adjustFireLoss(damage * blocked, target) + if(SEARING) + apply_damage(round(damage / 3), BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) + apply_damage(round(damage / 3 * 2), BRUTE, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) + if(ELECTROCUTE) + electrocute_act(damage, used_weapon, 1.0, def_zone) + if(BIOACID) + if(isSynthetic()) + apply_damage(damage, BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) // Handle it as normal burn. + + flash_weak_pain() + updatehealth() + return 1 + +/mob/living/exosuit/getFireLoss() + var/total = 0 + for(var/obj/item/mech_component/MC in list(arms, legs, body, head)) + if(MC) + total += MC.burn_damage + return total + +/mob/living/exosuit/getBruteLoss() + var/total = 0 + for(var/obj/item/mech_component/MC in list(arms, legs, body, head)) + if(MC) + total += MC.brute_damage + return total + +/mob/living/exosuit/emp_act(var/severity) + + var/ratio = getarmor(null, "energy")/100 + + if(ratio >= 0.5) + for(var/mob/living/m in pilots) + to_chat(m, SPAN_NOTICE("Your Faraday shielding absorbed the pulse!")) + return + else if(ratio > 0) + for(var/mob/living/m in pilots) + to_chat(m, SPAN_NOTICE("Your Faraday shielding mitigated the pulse!")) + + emp_damage += round((12 - (severity*3))*( 1 - ratio)) + if(severity <= 3) + for(var/obj/item/thing in list(arms,legs,head,body)) + thing.emp_act(severity) + if(!hatch_closed || !prob(body.pilot_coverage)) + for(var/thing in pilots) + var/mob/pilot = thing + pilot.emp_act(severity) diff --git a/code/modules/exosuits/exosuit_damage_immunity.dm b/code/modules/exosuits/exosuit_damage_immunity.dm new file mode 100644 index 00000000000..3e6993b4cf5 --- /dev/null +++ b/code/modules/exosuits/exosuit_damage_immunity.dm @@ -0,0 +1,58 @@ + +/* +/mob/living/exosuit/Stun() + return 0 +*/ + +/mob/living/exosuit/Weaken() + return 0 + +/* +/mob/living/exosuit/Paralyse() + return 0 +*/ + +/mob/living/exosuit/getOxyLoss() + return 0 + +/mob/living/exosuit/setOxyLoss() + return 0 + +/mob/living/exosuit/adjustOxyLoss() + return 0 + +/mob/living/exosuit/getToxLoss() + return 0 + +/mob/living/exosuit/setToxLoss() + return 0 + +/mob/living/exosuit/adjustToxLoss() + return 0 + +/mob/living/exosuit/getBrainLoss() + return 0 + +/mob/living/exosuit/setBrainLoss() + return 0 + +/mob/living/exosuit/adjustBrainLoss() + return 0 + +/mob/living/exosuit/getCloneLoss() + return 0 + +/mob/living/exosuit/setCloneLoss() + return 0 + +/mob/living/exosuit/adjustCloneLoss() + return 0 + +/mob/living/exosuit/getHalLoss() + return 0 + +/mob/living/exosuit/setHalLoss() + return 0 + +/mob/living/exosuit/adjustHalLoss() + return 0 diff --git a/code/modules/exosuits/exosuit_icon.dm b/code/modules/exosuits/exosuit_icon.dm new file mode 100644 index 00000000000..68b5d93f45e --- /dev/null +++ b/code/modules/exosuits/exosuit_icon.dm @@ -0,0 +1,80 @@ +/proc/get_mech_image(var/decal, var/cache_key, var/cache_icon, var/image_colour, var/overlay_layer = FLOAT_LAYER) + var/use_key = "[cache_key]-[cache_icon]-[decal ? decal : "none"]-[image_colour ? image_colour : "none"]" + if(!GLOB.mech_image_cache[use_key]) + var/image/I = image(icon = cache_icon, icon_state = cache_key) + if(image_colour) + var/image/masked_color = image(icon = cache_icon, icon_state = "[cache_key]_mask") + masked_color.color = image_colour + masked_color.blend_mode = BLEND_MULTIPLY + I.overlays.Add(masked_color) + if(decal) + var/decal_key = "[decal]-[cache_key]" + if(!GLOB.mech_icon_cache[decal_key]) + var/template_key = "template-[cache_key]" + if(!GLOB.mech_icon_cache[template_key]) + GLOB.mech_icon_cache[template_key] = icon(cache_icon, "[cache_key]_mask") + var/icon/decal_icon = icon('icons/mob/mecha/mech_decals.dmi',decal) + decal_icon.Blend(GLOB.mech_icon_cache[template_key], ICON_MULTIPLY) + GLOB.mech_icon_cache[decal_key] = decal_icon + I.overlays += get_mech_image(null, decal_key, GLOB.mech_icon_cache[decal_key]) + I.appearance_flags |= RESET_COLOR + I.layer = overlay_layer + I.plane = FLOAT_PLANE + GLOB.mech_image_cache[use_key] = I + return GLOB.mech_image_cache[use_key] + +/proc/get_mech_images(var/list/components = list(), var/overlay_layer = FLOAT_LAYER) + var/list/all_images = list() + for(var/obj/item/mech_component/comp in components) + all_images += get_mech_image(comp.decal, comp.icon_state, comp.on_mech_icon, comp.color, overlay_layer) + return all_images + +/mob/living/exosuit/update_icon() + overlays.Cut() + var/list/new_overlays = get_mech_images(list(body, head), MECH_BASE_LAYER) + if(body && !hatch_closed) + new_overlays += get_mech_image(body.decal, "[body.icon_state]_cockpit", body.on_mech_icon, MECH_BASE_LAYER) + update_pilots(FALSE) + if(LAZYLEN(pilot_overlays)) + new_overlays += pilot_overlays + if(body) + new_overlays += get_mech_image(body.decal, "[body.icon_state]_overlay[hatch_closed ? "" : "_open"]", body.on_mech_icon, body.color, MECH_COCKPIT_LAYER) + if(arms) + new_overlays += get_mech_image(arms.decal, arms.icon_state, arms.on_mech_icon, arms.color, MECH_ARM_LAYER) + if(legs) + new_overlays += get_mech_image(legs.decal, legs.icon_state, legs.on_mech_icon, legs.color, MECH_LEG_LAYER) + for(var/hardpoint in hardpoints) + var/obj/item/mech_equipment/hardpoint_object = hardpoints[hardpoint] + if(hardpoint_object) + var/use_icon_state = "[hardpoint_object.icon_state]_[hardpoint]" + if(use_icon_state in GLOB.mech_weapon_overlays) + new_overlays += get_mech_image(null, use_icon_state, 'icons/mob/mecha/mech_weapon_overlays.dmi', null, hardpoint_object.mech_layer ) + + . = ..() + + overlays |= new_overlays + +/mob/living/exosuit/proc/update_pilots(var/update_overlays = TRUE) + if(update_overlays && LAZYLEN(pilot_overlays)) + overlays -= pilot_overlays + pilot_overlays = null + if(!body || ((body.pilot_coverage < 100 || body.transparent_cabin) && !body.hide_pilot)) + for(var/i = 1 to LAZYLEN(pilots)) + var/mob/pilot = pilots[i] + var/image/draw_pilot = new + draw_pilot.appearance = pilot + draw_pilot.layer = MECH_PILOT_LAYER + (body ? ((LAZYLEN(body.pilot_positions)-i)*0.001) : 0) + draw_pilot.plane = FLOAT_PLANE + if(body && i <= LAZYLEN(body.pilot_positions)) + var/list/offset_values = body.pilot_positions[i] + var/list/directional_offset_values = offset_values["[dir]"] + draw_pilot.pixel_x = pilot.default_pixel_x + directional_offset_values["x"] + draw_pilot.pixel_y = pilot.default_pixel_y + directional_offset_values["y"] + draw_pilot.pixel_z = 0 + draw_pilot.transform = null + LAZYADD(pilot_overlays, draw_pilot) + if(update_overlays && LAZYLEN(pilot_overlays)) + overlays += pilot_overlays + +/mob/living/exosuit/regenerate_icons() + return diff --git a/code/modules/exosuits/exosuit_interaction.dm b/code/modules/exosuits/exosuit_interaction.dm new file mode 100644 index 00000000000..ac0671f0380 --- /dev/null +++ b/code/modules/exosuits/exosuit_interaction.dm @@ -0,0 +1,442 @@ + +/mob/living/MouseDrop(atom/over) + if(usr == src && usr != over) + if(istype(over, /mob/living/exosuit)) + var/mob/living/exosuit/exosuit = over + if(exosuit.body) + exosuit.enter(src) + return + to_chat(usr, SPAN_WARNING("You cannot pilot an exosuit that lacks a cockpit.")) + return + return ..() + +/mob/living/exosuit/MouseDrop_T(atom/dropping, mob/user) + var/obj/machinery/portable_atmospherics/canister/C = dropping + if(istype(C)) + body.MouseDrop_T(dropping, user) + else . = ..() + +/mob/living/exosuit/RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user) + if(user && (user in pilots) && user.loc == src) + return OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, user) + return ..() + +/mob/living/exosuit/OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user) + if(!user || incapacitated() || user.incapacitated()) + return FALSE + + if(!(user in pilots) && user != src) + return FALSE + + //This is handled at active module level really, it is the one who has to know if it's supposed to act + if(selected_system) + return selected_system.MouseDragInteraction(src_object, over_object, src_location, over_location, src_control, over_control, params, user) + +/mob/living/exosuit/allow_click_through(atom/A, mob/user) + if((user in pilots)) + return TRUE + . = ..() + +/mob/living/exosuit/click_through(atom/A, mob/user, params) + return ClickOn(A, params, user) + +/mob/living/exosuit/proc/incompetent_operation(var/mob/living/user) // Advanced tool users shouldn't currently have a chance of randomly blundering.. a -siffet-, however... + if(user.IsAdvancedToolUser()) + return FALSE + return TRUE + +/mob/living/exosuit/ClickOn(var/atom/A, var/params, var/mob/user) + if(!user) + user = src + + if(!checkClickCooldown()) // Hard check, before anything else, to avoid crashing + return + + setClickCooldown(1) + + if(!user || incapacitated() || user.incapacitated()) + return + + if(!loc) return + var/adj = A.Adjacent(src) // Why in the fuck isn't Adjacent() commutative. + + var/modifiers = params2list(params) + + if(modifiers["shift"]) + user.ShiftClickOn(A) + return + + if(!(user in pilots) && user != src) + return + + // Are we facing the target? + if(A.loc != src && !(get_dir(src, A) & dir)) + return + + if(!arms) + to_chat(user, SPAN_WARNING("\The [src] has no manipulators!")) + setClickCooldown(3) + return + + if(!arms.motivator || !arms.motivator.is_functional()) + to_chat(user, SPAN_WARNING("Your motivators are damaged! You can't use your manipulators!")) + setClickCooldown(15) + return + + var/obj/item/cell/MyC = get_cell() + if(!MyC.checked_use(arms.power_use * CELLRATE)) + to_chat(user, SPAN_WARNING("Error: Power levels insufficient.")) + + // User is not necessarily the exosuit, or the same person, so update intent. + if(user != src) + a_intent = user.a_intent + if(user.zone_sel) + zone_sel.set_selected_zone(user.zone_sel.selecting) + else + zone_sel.set_selected_zone(BP_TORSO) + // You may attack the target with your exosuit FIST if you're malfunctioning. + var/atom/movable/AM = A + var/fail_prob = (user != src && istype(AM) && AM.loc != src && incompetent_operation(user)) ? 15 : 0 + var/failed = FALSE + if(fail_prob && prob(fail_prob)) + to_chat(user, SPAN_DANGER("Your incompetence leads you to engage the wrong system with the exosuit!")) + failed = TRUE + else if(emp_damage > EMP_ATTACK_DISRUPT && prob(emp_damage*2)) + to_chat(user, SPAN_DANGER("The wiring sparks as you attempt to control the exosuit!")) + failed = TRUE + + if(!failed) + if(selected_system) + if(selected_system == A) + selected_system.attack_self(user) + setClickCooldown(5) + return + + // Mounted non-exosuit systems have some hacky loc juggling + // to make sure that they work. + var/system_moved = FALSE + var/obj/item/temp_system + var/obj/item/mech_equipment/ME + if(istype(selected_system, /obj/item/mech_equipment)) + ME = selected_system + temp_system = ME.get_effective_obj() + if(temp_system in ME) + system_moved = 1 + temp_system.forceMove(src) + else + temp_system = selected_system + + // Slip up and attack yourself maybe. + failed = FALSE + if(fail_prob && prob(fail_prob)) + to_chat(user, SPAN_DANGER("You artlessly shove the exosuit controls the wrong way!")) + failed = TRUE + else if(emp_damage>EMP_MOVE_DISRUPT && prob(10)) + failed = TRUE + + if(failed) + var/list/other_atoms = orange(1, A) + A = null + while(LAZYLEN(other_atoms)) + var/atom/picked = pick_n_take(other_atoms) + if(istype(picked) && picked.simulated) + A = picked + break + if(!A) + A = src + adj = A.Adjacent(src) + + var/resolved + + if(adj) resolved = temp_system.resolve_attackby(A, src, params) + if(!resolved && A && temp_system) + var/mob/ruser = src + if(!system_moved) //It's more useful to pass along clicker pilot when logic is fully mechside + ruser = user + temp_system.afterattack(A,ruser,adj,params) + if(system_moved) //We are using a proxy system that may not have logging like mech equipment does + log_and_message_admins("used [temp_system] targetting [A]", user, src.loc) + //Mech equipment subtypes can add further click delays + var/extra_delay = 0 + if(ME != null) + ME = selected_system + extra_delay = ME.equipment_delay + setClickCooldown(arms ? arms.action_delay + extra_delay : 15 + extra_delay) + if(system_moved) + temp_system.forceMove(selected_system) + return + + if(A == src) + setClickCooldown(5) + return attack_self(user) + else if(adj) + setClickCooldown(arms ? arms.action_delay : 15) + return A.attack_generic(src, arms.melee_damage, "attacked") + return + +/mob/living/exosuit/proc/set_hardpoint(var/hardpoint_tag) + clear_selected_hardpoint() + if(hardpoints[hardpoint_tag]) + // Set the new system. + selected_system = hardpoints[hardpoint_tag] + selected_hardpoint = hardpoint_tag + return 1 // The element calling this proc will set its own icon. + return 0 + +/mob/living/exosuit/proc/clear_selected_hardpoint() + + if(selected_hardpoint) + for(var/hardpoint in hardpoints) + if(hardpoint != selected_hardpoint) + continue + var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[hardpoint] + if(istype(H)) + H.icon_state = "hardpoint" + break + selected_system = null + selected_hardpoint = null + +/mob/living/exosuit/proc/check_enter(var/mob/user) + if(!user || user.incapacitated()) + return FALSE + if(!user.Adjacent(src)) + return FALSE + if(hatch_locked) + to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked.")) + return FALSE + if(hatch_closed) + to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is closed.")) + return FALSE + if(LAZYLEN(pilots) >= LAZYLEN(body.pilot_positions)) + to_chat(user, SPAN_WARNING("\The [src] is occupied to capacity.")) + return FALSE + if(user.mob_size < body.min_pilot_size || user.mob_size > body.max_pilot_size) + to_chat(user, SPAN_WARNING("You cannot pilot an exosuit of this size.")) + return FALSE + + return TRUE + +/mob/living/exosuit/proc/enter(var/mob/user) + if(!check_enter(user)) + return + to_chat(user, SPAN_NOTICE("You start climbing into \the [src]...")) + if(!do_after(user, 25)) + return + if(!check_enter(user)) + return + to_chat(user, SPAN_NOTICE("You climb into \the [src].")) + user.forceMove(src) + LAZYDISTINCTADD(pilots, user) + synchronize_access() + playsound(src, 'sound/mecha/hydraulic.ogg', 50, 1) + user.playsound_local(null, 'sound/mecha/nominal.ogg', 50) + if(user.client) user.client.screen |= hud_elements +// LAZYDISTINCTADD(user.additional_vision_handlers, src) + update_pilots() +// user.PushClickHandler(/datum/click_handler/default/mech) + return 1 + +/mob/living/exosuit/proc/synchronize_access() + access_card.access.Cut() + access_card.access = saved_access.Copy() + if(sync_access) + for(var/mob/pilot in pilots) + access_card.access |= pilot.GetAccess() + to_chat(pilot, SPAN_NOTICE("Security access permissions synchronized.")) + +/mob/living/exosuit/proc/eject(var/mob/user, var/silent) + if(!user || !(user in src.contents)) + return + if(hatch_closed) + if(hatch_locked) + if(!silent) + to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked.")) + return + hud_open.toggled() + if(!silent) + to_chat(user, SPAN_NOTICE("You open the hatch and climb out of \the [src].")) + else + if(!silent) + to_chat(user, SPAN_NOTICE("You climb out of \the [src].")) + +// user.RemoveClickHandler(/datum/click_handler/default/mech) + user.dropInto(loc) +// LAZYREMOVE(user.additional_vision_handlers, src) + if(user.client) + user.client.screen -= hud_elements + user.client.eye = user + if(user in pilots) + a_intent = I_HURT + LAZYREMOVE(pilots, user) + UNSETEMPTY(pilots) + update_pilots() + return 1 +/* +/mob/living/exosuit/proc/handle_equip_install(var/obj/item/mech_equipment/prospective, var/mob/user) + if(hardpoints_locked) + to_chat(user, SPAN_WARNING("Hardpoint system access is disabled.")) + return + + var/obj/item/mech_equipment/realThing = prospective + if(realThing.owner) + return + + var/free_hardpoints = list() + for(var/hardpoint in hardpoints) + if(hardpoints[hardpoint] == null) + free_hardpoints += hardpoint + var/to_place = input("Where would you like to install it?") as null|anything in (realThing.restricted_hardpoints & free_hardpoints) + if(install_system(prospective, to_place, user)) + return + to_chat(user, SPAN_WARNING("\The [src] could not be installed in that hardpoint.")) + +/mob/living/exosuit/proc/handle_equip_uninstall(var/mob/user) + if(hardpoints_locked) + to_chat(user, SPAN_WARNING("Hardpoint system access is disabled.")) + return + + var/list/parts = list() + for(var/hardpoint in hardpoints) + if(hardpoints[hardpoint]) + parts += hardpoint + + var/to_remove = input("Which component would you like to remove") as null|anything in parts + + if(remove_system(to_remove, user)) + return + to_chat(user, SPAN_WARNING("\The [src] has no hardpoint systems to remove.")) + +/mob/living/exosuit/proc/handle_dismantling(var/obj/item/tool, var/mob/user) + if(!maintenance_protocols) + to_chat(user, SPAN_WARNING("The securing bolts are not visible while maintenance protocols are disabled.")) + return + + visible_message(SPAN_WARNING("\The [user] begins unwrenching the securing bolts holding \the [src] together.")) + var/delay = 60 * tool.toolspeed + if(!do_after(user, delay) || !maintenance_protocols) + return + visible_message(SPAN_NOTICE("\The [user] loosens and removes the securing bolts, dismantling \the [src].")) + dismantle() + +/mob/living/exosuit/proc/handle_repairing(var/obj/item/tool, var/mob/user) + if(istype(tool, /obj/item/weldingtool) && getBruteLoss()) + var/list/damaged_parts = list() + for(var/obj/item/mech_component/MC in list(arms, legs, body, head)) + if(MC && MC.brute_damage) + damaged_parts += MC + var/obj/item/mech_component/to_fix = input(user,"Which component would you like to fix") as null|anything in damaged_parts + if(Adjacent(user) && user.stat != DEAD && !QDELETED(to_fix) && (to_fix in src) && to_fix.brute_damage) + to_fix.repair_brute_generic(thing, user) + + if(istype(thing, /obj/item/stack/cable_coil) && getFireLoss()) + var/list/damaged_parts = list() + for(var/obj/item/mech_component/MC in list(arms, legs, body, head)) + if(MC && MC.burn_damage) + damaged_parts += MC + var/obj/item/mech_component/to_fix = input(user,"Which component would you like to fix") as null|anything in damaged_parts + if(Adjacent(user) && user.stat != DEAD && !QDELETED(to_fix) && (to_fix in src) && to_fix.burn_damage) + to_fix.repair_burn_generic(thing, user) + +/mob/living/exosuit/proc/handle_powercell(var/obj/item/thing, var/mob/user) + if(!maintenance_protocols) + to_chat(user, SPAN_WARNING("The cell compartment remains locked while maintenance protocols are disabled.")) + return + if(thing.has_tool_quality(TOOL_CROWBAR)) + if(!body) + to_chat(user, SPAN_WARNING("The exosuit lacks a cell compartment!")) + return + if(body.cell) // If there's a cell, remove it. + var/delay = 20 * thing.toolspeed + if(!do_after(user, delay) || !maintenance_protocols || !body || !body.cell) + return + + user.put_in_hands(body.cell) + to_chat(user, SPAN_NOTICE("You remove \the [body.cell] from \the [src].")) + playsound(user.loc, 'sound/items/Crowbar.ogg', 50, 1) + visible_message(SPAN_NOTICE("\The [user] pries out \the [body.cell] using the \the [thing].")) + body.cell = null + else // No return after this to_chat, incase you have a cell with the tool quality of a crowbar, like some sort of.. emergency cell. + to_chat(user, SPAN_WARNING("There is no cell here for you to remove!")) + + if(istype(thing, /obj/item/cell)) + if(!body || body.cell) + to_chat(user, SPAN_WARNING("There is already a cell in there!")) + return + + if(user.unEquip(thing)) + thing.forceMove(body) + body.cell = thing + to_chat(user, SPAN_NOTICE("You install \the [body.cell] into \the [src].")) + playsound(user.loc, 'sound/items/Screwdriver.ogg', 50, 1) + visible_message(SPAN_NOTICE("\The [user] installs \the [body.cell] into \the [src].")) +*/ +/mob/living/exosuit/attackby(var/obj/item/thing, var/mob/user) +/* + if(istype(thing, /obj/item/kit/decal)) + user.visible_message(SPAN_NOTICE("\The [user] opens \the [thing] and spends some quality time customising \the [src].")) + var/obj/item/kit/paint/exo/P = thing + P.customize(src, user) + return TRUE +*/ + if(user.a_intent != I_HURT) + if(istype(thing, /obj/item/mech_equipment)) +// handle_equip_install(thing, user) + return + + if(thing.has_tool_quality(TOOL_MULTITOOL)) +// handle_equip_uninstall(user) + return + + else if(thing.has_tool_quality(TOOL_WRENCH)) +// handle_dismantling(thing, user) + return + + else if(istype(thing, /obj/item/weldingtool) || istype(thing, /obj/item/stack/cable_coil)) +// handle_repairing(thing, user) + return + + else if(thing.has_tool_quality(TOOL_CROWBAR) || istype(thing, /obj/item/cell)) +// handle_powercell(thing, user) + return + return ..() + +/mob/living/exosuit/attack_hand(var/mob/user) + // Drag the pilot out if possible. + if(user.a_intent == I_HURT) + if(!LAZYLEN(pilots)) + to_chat(user, SPAN_WARNING("There is nobody inside \the [src].")) + else if(!hatch_closed) + var/mob/pilot = pick(pilots) + user.visible_message(SPAN_DANGER("\The [user] is trying to pull \the [pilot] out of \the [src]!")) + if(do_after(user, 30) && user.Adjacent(src) && (pilot in pilots) && !hatch_closed) + user.visible_message(SPAN_DANGER("\The [user] drags \the [pilot] out of \the [src]!")) + eject(pilot, silent=1) + return + + // Otherwise toggle the hatch. + if(hatch_locked) + to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked.")) + return + hatch_closed = !hatch_closed + to_chat(user, SPAN_NOTICE("You [hatch_closed ? "close" : "open"] the [body.hatch_descriptor].")) + hud_open.update_icon() + update_icon() + return + +/mob/living/exosuit/proc/attack_self(var/mob/user) + return visible_message("\The [src] pokes itself.") + +/mob/living/exosuit/proc/rename(var/mob/user) + if(user != src && !(user in pilots)) + return + var/new_name = sanitize(input("Enter a new exosuit designation.", "Exosuit Name") as text|null, max_length = MAX_NAME_LEN) + if(!new_name || new_name == name || (user != src && !(user in pilots))) + return + name = new_name + to_chat(user, SPAN_NOTICE("You have redesignated this exosuit as \the [name].")) + +/mob/living/exosuit/get_inventory_slot(obj/item/I) + for(var/h in hardpoints) + if(hardpoints[h] == I) + return h + return 0 diff --git a/code/modules/exosuits/exosuit_life.dm b/code/modules/exosuits/exosuit_life.dm new file mode 100644 index 00000000000..52b290f2bd6 --- /dev/null +++ b/code/modules/exosuits/exosuit_life.dm @@ -0,0 +1,123 @@ +/mob/living/exosuit/handle_disabilities() + return + +/mob/living/exosuit/Life() + + for(var/thing in pilots) + var/mob/pilot = thing + if(pilot.loc != src) // Admin jump or teleport/grab. + if(pilot.client) + pilot.client.screen -= hud_elements + LAZYREMOVE(pilots, pilot) + UNSETEMPTY(pilots) + update_pilots() + + if(radio) + radio.on = (head && head.radio && head.radio.is_functional()) + + body.update_air(hatch_closed && use_air) + + if((client || LAZYLEN(pilots)) && get_cell()) + var/obj/item/cell/MyC = get_cell() + MyC.drain_power(0, 0, calc_power_draw()) + + updatehealth() + if(health <= 0 && stat != DEAD) + death() + + ..() //Handles stuff like environment + + handle_hud_icons() + + lying = FALSE // Fuck off, carp. + handle_vision() + +/mob/living/exosuit/get_cell() + return body ? body.cell : null + +/mob/living/exosuit/proc/calc_power_draw() + //Passive power stuff here. You can also recharge cells or hardpoints if those make sense + var/total_draw = 0 + for(var/hardpoint in hardpoints) + var/obj/item/mech_equipment/I = hardpoints[hardpoint] + if(!istype(I)) + continue + total_draw += I.passive_power_use + + if(head && head.active_sensors) + total_draw += head.power_use + + if(body) + total_draw += body.power_use + + return total_draw + +/mob/living/exosuit/handle_environment(var/datum/gas_mixture/environment) + if(!environment) return + //Mechs and vehicles in general can be assumed to just tend to whatever ambient temperature + if(abs(environment.temperature - bodytemperature) > 10 ) + bodytemperature += ((environment.temperature - bodytemperature) / 3) + + if(environment.temperature > material.melting_point * 1.25 ) //A bit higher because I like to assume there's a difference between a mech and a wall + apply_damage(damage = (environment.temperature - (material.melting_point))/5 , damagetype = BURN) + //A possibility is to hook up interface icons here. But this works pretty well in my experience + if(prob(5)) + visible_message(SPAN_DANGER("\The [src]'s hull bends and buckles under the intense heat!")) + + +/mob/living/exosuit/death(var/gibbed) + + // Salvage moves into the wreck unless we're exploding violently. + var/obj/wreck = new wreckage_path(get_turf(src), src, gibbed) + wreck.name = "wreckage of \the [name]" + if(!gibbed) + if(arms.loc != src) + arms = null + if(legs.loc != src) + legs = null + if(head.loc != src) + head = null + if(body.loc != src) + body = null + + // Eject the pilot. + if(LAZYLEN(pilots)) + hatch_locked = 0 // So they can get out. + for(var/pilot in pilots) + eject(pilot, silent=1) + + // Handle the rest of things. + ..(gibbed, (gibbed ? "explodes!" : "grinds to a halt before collapsing!")) + if(!gibbed) qdel(src) + +/mob/living/exosuit/gib() + death(1) + + // Get a turf to play with. + var/turf/T = get_turf(src) + if(!T) + qdel(src) + return + + // Hurl our component pieces about. + var/list/stuff_to_throw = list() + for(var/obj/item/thing in list(arms, legs, head, body)) + if(thing) stuff_to_throw += thing + for(var/hardpoint in hardpoints) + if(hardpoints[hardpoint]) + var/obj/item/thing = hardpoints[hardpoint] + thing.screen_loc = null + stuff_to_throw += thing + for(var/obj/item/thing in stuff_to_throw) + thing.forceMove(T) + thing.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(3,6),40) + explosion(T, -1, 0, 2) + qdel(src) + return + +/mob/living/exosuit/handle_vision() + if(head) + sight = head.get_sight() + see_invisible = head.get_invisible() + if(body && (body.pilot_coverage < 100 || body.transparent_cabin)) + sight &= ~BLIND diff --git a/code/modules/exosuits/exosuit_movement.dm b/code/modules/exosuits/exosuit_movement.dm new file mode 100644 index 00000000000..bd016528806 --- /dev/null +++ b/code/modules/exosuits/exosuit_movement.dm @@ -0,0 +1,70 @@ + +/mob/living/exosuit/can_ztravel() + if(Allow_Spacemove()) //Handle here + return 1 + + for(var/turf/simulated/T in trange(1,src)) + if(T.density) + return 1 + +/mob/living/exosuit/can_fall() // Ignore anchored. + if(is_incorporeal()) + return FALSE + if(hovering) + return FALSE + return !(can_overcome_gravity()) + +/mob/living/exosuit/relaymove(var/mob/user, var/direction) + . = FALSE + if(length(pilots) && (user in pilots) && world.time > next_step) + . = Move(get_step(src, direction), direction) + +/mob/living/exosuit/Move(atom/newloc, direct, movetime) + . = FALSE + + if(incapacitated()) + return FALSE + + if(!legs) + to_chat(src, SPAN_WARNING("\The [src] has no means of propulsion!")) + next_step = world.time + 3 // Just to stop them from getting spammed with messages. + return FALSE + + if(!legs.motivator || !legs.motivator.is_functional()) + to_chat(src, SPAN_WARNING("Your motivators are damaged! You can't move!")) + next_step = world.time + 15 + return FALSE + + if(maintenance_protocols) + to_chat(src, SPAN_WARNING("Maintenance protocols are in effect.")) + next_step = world.time + 3 // Just to stop them from getting spammed with messages. + return FALSE + + var/obj/item/cell/C = get_cell() + if(!C || !C.check_charge(legs.power_use * CELLRATE)) + to_chat(src, SPAN_WARNING("The power indicator flashes briefly.")) + next_step = world.time + 3 //On fast exosuits this got annoying fast + return FALSE + + if(emp_damage >= EMP_MOVE_DISRUPT && prob(30)) + direct = pick(GLOB.alldirs - dir) // We have diagonal movement, we're gonna use it damnit. + + C.use(legs.power_use * CELLRATE) + + if(!direct) + direct = get_dir(src, newloc) + + if(dir != direct) + playsound(loc, mech_turn_sound, 40,1) + set_dir(direct) + next_step = world.time + legs.turn_delay + return FALSE + . = FALSE + if(!newloc) + newloc = get_step(src, direct) + if(newloc && legs && legs.can_move_on(loc, newloc)) + . = ..(newloc, direct) + next_step = world.time + legs.move_delay + if(. && !istype(loc, /turf/space)) + playsound(src.loc, mech_step_sound, 40, 1) + return . diff --git a/code/modules/exosuits/exosuit_wreckage.dm b/code/modules/exosuits/exosuit_wreckage.dm new file mode 100644 index 00000000000..919c9df77be --- /dev/null +++ b/code/modules/exosuits/exosuit_wreckage.dm @@ -0,0 +1,83 @@ +/obj/structure/exosuit_wreckage + name = "wreckage" + desc = "It might have some salvagable parts." + density = 1 + opacity = 1 + anchored = 1 + icon_state = "wreck" + icon = 'icons/mob/mecha/mech_part_items.dmi' + var/prepared + +/obj/structure/exosuit_wreckage/Initialize(var/newloc, var/mob/living/exosuit/exosuit, var/gibbed) + if(exosuit) + name = "wreckage of \the [exosuit.name]" + if(!gibbed) + for(var/obj/item/thing in list(exosuit.arms, exosuit.legs, exosuit.head, exosuit.body)) + if(thing && prob(40)) + thing.forceMove(src) + for(var/hardpoint in exosuit.hardpoints) + if(exosuit.hardpoints[hardpoint] && prob(40)) + var/obj/item/thing = exosuit.hardpoints[hardpoint] + if(exosuit.remove_system_initialize(hardpoint)) + thing.forceMove(src) + + return ..() + +/obj/structure/exosuit_wreckage/powerloader/Initialize(var/newloc) + var/mob/living/exosuit/Exo = new /mob/living/exosuit/premade/powerloader(src) + return ..(newloc, Exo, FALSE) + +/obj/structure/exosuit_wreckage/attack_hand(var/mob/user) + if(contents.len) + var/obj/item/thing = pick(contents) + if(istype(thing)) + thing.forceMove(get_turf(user)) + user.put_in_hands(thing) + to_chat(user, "You retrieve \the [thing] from \the [src].") + return + return ..() + +/obj/structure/exosuit_wreckage/attackby(var/obj/item/W, var/mob/user) + + var/cutting + if(istype(W, /obj/item/weldingtool)) + var/obj/item/weldingtool/WT = W + if(WT.welding) + WT.remove_fuel(1,user) + cutting = TRUE + else + to_chat(user, SPAN_WARNING("Turn the torch on, first.")) + else if(istype(W, /obj/item/pickaxe/plasmacutter)) + cutting = TRUE + + if(cutting) + if(!prepared) + prepared = 1 + to_chat(user, SPAN_NOTICE("You partially dismantle \the [src].")) + else + to_chat(user, SPAN_WARNING("\The [src] has already been weakened.")) + return 1 + + else if(W.has_tool_quality(TOOL_WRENCH)) + if(prepared) + to_chat(user, SPAN_NOTICE("You finish dismantling \the [src].")) + new /obj/item/stack/material/steel(get_turf(src),rand(5,10)) + qdel(src) + else + to_chat(user, SPAN_WARNING("It's too solid to dismantle. Try cutting through some of the bigger bits.")) + return 1 + else if(istype(W) && W.force > 20) + visible_message(SPAN_DANGER("\The [src] has been smashed with \the [W] by \the [user]!")) + if(prob(20)) + new /obj/item/stack/material/steel(get_turf(src),rand(1,3)) + qdel(src) + return 1 + return ..() + +/obj/structure/exosuit_wreckage/Destroy() + for(var/obj/thing in contents) + if(prob(65)) + thing.forceMove(get_turf(src)) + else + qdel(thing) + ..() diff --git a/code/modules/exosuits/interface/_interface.dm b/code/modules/exosuits/interface/_interface.dm new file mode 100644 index 00000000000..1bd8be68be2 --- /dev/null +++ b/code/modules/exosuits/interface/_interface.dm @@ -0,0 +1,214 @@ +/mob/living/exosuit/proc/refresh_hud() + if(LAZYLEN(pilots)) + for(var/thing in pilots) + var/mob/pilot = thing + if(pilot.client) + pilot.client.screen |= hud_elements + if(client) + client.screen |= hud_elements + +/mob/living/exosuit/proc/InitializeHud() + zone_sel = new + if(!LAZYLEN(hud_elements)) + var/i = 1 + for(var/hardpoint in hardpoints) + var/obj/screen/movable/exosuit/hardpoint/H = new(src, hardpoint) + H.screen_loc = "1:6,[15-i]" //temp + hud_elements |= H + hardpoint_hud_elements[hardpoint] = H + i++ + + var/list/additional_hud_elements = list( + /obj/screen/movable/exosuit/toggle/maint, + /obj/screen/movable/exosuit/eject, + /obj/screen/movable/exosuit/toggle/hardpoint, + /obj/screen/movable/exosuit/toggle/hatch, + /obj/screen/movable/exosuit/toggle/hatch_open, + /obj/screen/movable/exosuit/radio, + /obj/screen/movable/exosuit/rename, + /obj/screen/movable/exosuit/toggle/camera + ) + if(body && body.pilot_coverage >= 100) + additional_hud_elements += /obj/screen/movable/exosuit/toggle/air + i = 0 + var/pos = 7 + for(var/additional_hud in additional_hud_elements) + var/obj/screen/movable/exosuit/M = new additional_hud(src) + M.screen_loc = "1:6,[pos]:[i * -12]" + hud_elements |= M + i++ + if(i == 3) + pos-- + i = 0 + + hud_health = new /obj/screen/movable/exosuit/health(src) + hud_health.screen_loc = "EAST-1:28,CENTER-3:11" + hud_elements |= hud_health + hud_open = locate(/obj/screen/movable/exosuit/toggle/hatch_open) in hud_elements + hud_power = new /obj/screen/movable/exosuit/power(src) + hud_power.screen_loc = "EAST-1:12,CENTER-4:25" + hud_elements |= hud_power + + refresh_hud() + +/mob/living/exosuit/create_mob_hud(datum/hud/HUD) + ..() + + var/ui_style = 'icons/mob/screen1.dmi' + if(HUD.ui_style) + ui_style = HUD.ui_style + var/ui_color = "#ffffff" + var/ui_alpha = 255 + + var/obj/screen/using + //Intent Backdrop + using = new /obj/screen() + using.name = "act_intent" + using.icon = ui_style + using.icon_state = "intent_"+a_intent + using.screen_loc = ui_acti + using.color = ui_color + using.alpha = ui_alpha + HUD.adding += using + HUD.action_intent = using + + hud_elements |= using + + //Small intent quarters + var/icon/ico + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),1,ico.Height()/2,ico.Width()/2,ico.Height()) + using = new /obj/screen() + using.name = I_HELP + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM //These sit on the intent box + HUD.adding += using + HUD.help_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,ico.Height()/2,ico.Width(),ico.Height()) + using = new /obj/screen() + using.name = I_DISARM + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + HUD.adding += using + HUD.disarm_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,1,ico.Width(),ico.Height()/2) + using = new /obj/screen() + using.name = I_GRAB + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + HUD.adding += using + HUD.grab_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),1,1,ico.Width()/2,ico.Height()/2) + using = new /obj/screen() + using.name = I_HURT + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + HUD.adding += using + HUD.hurt_intent = using + + //Move intent (walk/run) + using = new /obj/screen() + using.name = "mov_intent" + using.icon = ui_style + using.icon_state = (IS_RUNNING(src) ? "running" : "walking") + using.screen_loc = ui_movi + using.color = ui_color + using.alpha = ui_alpha + HUD.adding += using + HUD.move_intent = using + + //Resist button + using = new /obj/screen() + using.name = "resist" + using.icon = ui_style + using.icon_state = "act_resist" + using.screen_loc = ui_pull_resist + using.color = ui_color + using.alpha = ui_alpha + HUD.hotkeybuttons += using + + //Pull button + pullin = new /obj/screen() + pullin.icon = ui_style + pullin.icon_state = "pull0" + pullin.name = "pull" + pullin.screen_loc = ui_pull_resist + HUD.hotkeybuttons += pullin + hud_elements |= pullin + + zone_sel = new /obj/screen/zone_sel( null ) + zone_sel.icon = ui_style + zone_sel.color = ui_color + zone_sel.alpha = ui_alpha + zone_sel.cut_overlays() + zone_sel.add_overlay(image('icons/mob/zone_sel.dmi', "[zone_sel.selecting]")) + hud_elements |= zone_sel + +/mob/living/exosuit/handle_hud_icons() + for(var/hardpoint in hardpoint_hud_elements) + var/obj/screen/movable/exosuit/hardpoint/H = hardpoint_hud_elements[hardpoint] + if(H) H.update_system_info() + handle_hud_icons_health() + var/obj/item/cell/C = get_cell() + if(istype(C)) + hud_power.maptext = "[round(C.charge)]/[round(C.maxcharge)]" + else hud_power.maptext = "CHECK POWER" + refresh_hud() + +/mob/living/exosuit/handle_hud_icons_health() + + hud_health.overlays.Cut() + + var/obj/item/cell/MyC = get_cell() + if(!body || !MyC || (MyC.charge <= 0)) + return + + if(!body.diagnostics || !body.diagnostics.is_functional() || ((emp_damage>EMP_GUI_DISRUPT) && prob(emp_damage*2))) + if(!GLOB.mech_damage_overlay_cache["critfail"]) + GLOB.mech_damage_overlay_cache["critfail"] = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="dam_error") + hud_health.overlays |= GLOB.mech_damage_overlay_cache["critfail"] + return + + var/list/part_to_state = list("legs" = legs,"body" = body,"head" = head,"arms" = arms) + for(var/part in part_to_state) + var/state = 0 + var/obj/item/mech_component/MC = part_to_state[part] + if(MC) + if((emp_damage>EMP_GUI_DISRUPT) && prob(emp_damage*3)) + state = rand(0,4) + else + state = MC.damage_state + if(!GLOB.mech_damage_overlay_cache["[part]-[state]"]) + var/image/I = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="dam_[part]") + switch(state) + if(1) + I.color = "#00ff00" + if(2) + I.color = "#f2c50d" + if(3) + I.color = "#ea8515" + if(4) + I.color = "#ff0000" + else + I.color = "#f5f5f0" + GLOB.mech_damage_overlay_cache["[part]-[state]"] = I + hud_health.overlays |= GLOB.mech_damage_overlay_cache["[part]-[state]"] diff --git a/code/modules/exosuits/interface/screen_objects.dm b/code/modules/exosuits/interface/screen_objects.dm new file mode 100644 index 00000000000..65006bd96bf --- /dev/null +++ b/code/modules/exosuits/interface/screen_objects.dm @@ -0,0 +1,250 @@ +// Screen objects hereon out. +/obj/screen/movable/exosuit + name = "hardpoint" + icon = 'icons/mob/mecha/mech_hud.dmi' + icon_state = "hardpoint" + var/mob/living/exosuit/owner + +/obj/screen/movable/exosuit/radio + name = "radio" + icon_state = "radio" + +/obj/screen/movable/exosuit/radio/Click() + if(..()) + if(owner.radio) + owner.radio.attack_self(usr) + else + to_chat(usr, SPAN_WARNING("There is no radio installed.")) + +/obj/screen/movable/exosuit/Initialize() + . = ..() + var/mob/living/exosuit/newowner = loc + if(!istype(newowner)) + return qdel(src) + owner = newowner + +/obj/screen/movable/exosuit/Click() + return (!owner || !usr.incapacitated() && (usr == owner || usr.loc == owner)) + +/obj/screen/movable/exosuit/hardpoint + name = "hardpoint" + var/hardpoint_tag + var/obj/item/holding + + maptext_x = 34 + maptext_y = 3 + maptext_width = 72 + +/obj/screen/movable/exosuit/hardpoint/MouseDrop() + ..() + if(holding) holding.screen_loc = screen_loc + +/obj/screen/movable/exosuit/hardpoint/proc/update_system_info() + + // No point drawing it if we have no item to use or nobody to see it. + if(!holding || !owner) + return + + var/has_pilot_with_client = owner.client + if(!has_pilot_with_client && LAZYLEN(owner.pilots)) + for(var/thing in owner.pilots) + var/mob/pilot = thing + if(pilot.client) + has_pilot_with_client = TRUE + break + if(!has_pilot_with_client) + return + + var/list/new_overlays = list() + var/obj/item/cell/OwnerCell = owner.get_cell() + if(!OwnerCell || (OwnerCell.charge <= 0)) + overlays.Cut() + return + + maptext = holding.get_hardpoint_maptext() + + var/ui_damage = (!owner.body.diagnostics || !owner.body.diagnostics.is_functional() || ((owner.emp_damage>EMP_GUI_DISRUPT) && prob(owner.emp_damage))) + + var/value = holding.get_hardpoint_status_value() + if(isnull(value)) + overlays.Cut() + return + + if(ui_damage) + value = -1 + maptext = "ERROR" + else + if((owner.emp_damage>EMP_GUI_DISRUPT) && prob(owner.emp_damage*2)) + if(prob(10)) + value = -1 + else + value = rand(1,BAR_CAP) + else + value = round(value * BAR_CAP) + + // Draw background. + if(!GLOB.default_hardpoint_background) + GLOB.default_hardpoint_background = image(icon = 'icons/mob/mecha/mech_hud.dmi', icon_state = "bar_bkg") + GLOB.default_hardpoint_background.pixel_x = 34 + new_overlays += GLOB.default_hardpoint_background + + if(value == 0) + if(!GLOB.hardpoint_bar_empty) + GLOB.hardpoint_bar_empty = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="bar_flash") + GLOB.hardpoint_bar_empty.pixel_x = 24 + GLOB.hardpoint_bar_empty.color = "#ff0000" + new_overlays += GLOB.hardpoint_bar_empty + else if(value < 0) + if(!GLOB.hardpoint_error_icon) + GLOB.hardpoint_error_icon = image(icon='icons/mob/mecha/mech_hud.dmi',icon_state="bar_error") + GLOB.hardpoint_error_icon.pixel_x = 34 + new_overlays += GLOB.hardpoint_error_icon + else + value = min(value, BAR_CAP) + // Draw statbar. + if(!LAZYLEN(GLOB.hardpoint_bar_cache)) + for(var/i=0;i5) + bar.color = "#00ff00" + else if(i>1) + bar.color = "#ffff00" + else + bar.color = "#ff0000" + GLOB.hardpoint_bar_cache += bar + for(var/i=0;i<=value;i++) + new_overlays += GLOB.hardpoint_bar_cache[i] + overlays = new_overlays + +/obj/screen/movable/exosuit/hardpoint/Initialize(mapload, var/newtag) + . = ..() + hardpoint_tag = newtag + name = "hardpoint ([hardpoint_tag])" + +/obj/screen/movable/exosuit/hardpoint/Click(var/location, var/control, var/params) + + if(!(..())) + return + + var/modifiers = params2list(params) + if(modifiers["ctrl"]) + if(owner.hardpoints_locked) + to_chat(usr, SPAN_WARNING("Hardpoint ejection system is locked.")) + return + if(owner.remove_system(hardpoint_tag)) + to_chat(usr, SPAN_NOTICE("You disengage and discard the system mounted to your [hardpoint_tag] hardpoint.")) + else + to_chat(usr, SPAN_DANGER("You fail to remove the system mounted to your [hardpoint_tag] hardpoint.")) + return + + if(owner.selected_hardpoint == hardpoint_tag) + icon_state = "hardpoint" + owner.clear_selected_hardpoint() + else + if(owner.set_hardpoint(hardpoint_tag)) + icon_state = "hardpoint_selected" + +/obj/screen/movable/exosuit/eject + name = "eject" + icon_state = "eject" + +/obj/screen/movable/exosuit/eject/Click() + if(..()) + owner.eject(usr) + +/obj/screen/movable/exosuit/rename + name = "rename" + icon_state = "rename" + +/obj/screen/movable/exosuit/power + name = "power" + icon_state = null + + maptext_width = 64 + +/obj/screen/movable/exosuit/rename/Click() + if(..()) + owner.rename(usr) + +/obj/screen/movable/exosuit/toggle + name = "toggle" + var/toggled + +/obj/screen/movable/exosuit/toggle/Click() + if(..()) toggled() + +/obj/screen/movable/exosuit/toggle/proc/toggled() + toggled = !toggled + icon_state = "[initial(icon_state)][toggled ? "_enabled" : ""]" + return toggled + +/obj/screen/movable/exosuit/toggle/air + name = "air" + icon_state = "air" + +/obj/screen/movable/exosuit/toggle/air/toggled() + owner.use_air = ..() + to_chat(usr, SPAN_NOTICE("Auxiliary atmospheric system [owner.use_air ? "enabled" : "disabled"].")) + +/obj/screen/movable/exosuit/toggle/maint + name = "toggle maintenance protocol" + icon_state = "maint" + +/obj/screen/movable/exosuit/toggle/maint/toggled() + owner.maintenance_protocols = ..() + to_chat(usr, SPAN_NOTICE("Maintenance protocols [owner.maintenance_protocols ? "enabled" : "disabled"].")) + +/obj/screen/movable/exosuit/toggle/hardpoint + name = "toggle hardpoint lock" + icon_state = "hardpoint_lock" + +/obj/screen/movable/exosuit/toggle/hardpoint/toggled() + owner.hardpoints_locked = ..() + to_chat(usr, SPAN_NOTICE("Hardpoint system access is now [owner.hardpoints_locked ? "disabled" : "enabled"].")) + +/obj/screen/movable/exosuit/toggle/hatch + name = "toggle hatch lock" + icon_state = "hatch_lock" + +/obj/screen/movable/exosuit/toggle/hatch/toggled() + if(!owner.hatch_locked && !owner.hatch_closed) + to_chat(usr, SPAN_WARNING("You cannot lock the hatch while it is open.")) + return + owner.hatch_locked = ..() + to_chat(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is [owner.hatch_locked ? "now" : "no longer" ] locked.")) + +/obj/screen/movable/exosuit/toggle/hatch_open + name = "open or close hatch" + icon_state = "hatch_status" + +/obj/screen/movable/exosuit/toggle/hatch_open/toggled() + if(owner.hatch_locked && owner.hatch_closed) + to_chat(usr, SPAN_WARNING("You cannot open the hatch while it is locked.")) + return + owner.hatch_closed = ..() + to_chat(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is now [owner.hatch_closed ? "closed" : "open" ].")) + owner.update_icon() + +// This is basically just a holder for the updates the exosuit does. +/obj/screen/movable/exosuit/health + name = "exosuit integrity" + icon_state = "health" + +//Controls if cameras set the vision flags +/obj/screen/movable/exosuit/toggle/camera + name = "toggle camera matrix" + icon_state = "camera" + +/obj/screen/movable/exosuit/toggle/camera/toggled() + if(!owner.head) + to_chat(usr, SPAN_WARNING("I/O Error: Camera systems not found.")) + return + if(!owner.head.vision_flags) + to_chat(usr, SPAN_WARNING("Alternative sensor configurations not found. Contact manufacturer for more details.")) + return + owner.head.active_sensors = ..() + to_chat(usr, SPAN_NOTICE("[owner.head.name] advanced sensor mode is [owner.head.active_sensors ? "now" : "no longer" ] active.")) + + +#undef BAR_CAP diff --git a/code/modules/exosuits/premade/_premade.dm b/code/modules/exosuits/premade/_premade.dm new file mode 100644 index 00000000000..1a32541abe2 --- /dev/null +++ b/code/modules/exosuits/premade/_premade.dm @@ -0,0 +1,156 @@ +//GLOBAL_LIST_INIT(mech_decals, (icon_states('icons/mecha/mech_decals.dmi')-list("template", "mask"))) + +/mob/living/exosuit/premade + name = "impossible exosuit" + desc = "It seems to be saying 'please let me die'." + var/decal + +/mob/living/exosuit/premade/Initialize() + if(arms) + arms.decal = decal + arms.prebuild() + if(legs) + legs.decal = decal + legs.prebuild() + if(head) + head.decal = decal + head.prebuild() + if(body) + body.decal = decal + body.prebuild() + if(!material) + material = get_material_by_name(MAT_STEEL) + . = ..() + + spawn_mech_equipment() + +/mob/living/exosuit/premade/proc/spawn_mech_equipment() + install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD) + +/mob/living/exosuit/premade/random + name = "mismatched exosuit" + desc = "It seems to have been roughly thrown together and then spraypainted a single colour." + +/mob/living/exosuit/premade/random/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame, var/super_random = FALSE, var/using_boring_colours = FALSE) + //if(!prob(100/(LAZYLEN(GLOB.mech_decals)+1))) + // decal = pick(GLOB.mech_decals) + + var/list/use_colours + if(using_boring_colours) + use_colours = list( + COLOR_DARK_GRAY, + COLOR_GRAY40, + COLOR_DARK_BROWN, + COLOR_GRAY, + COLOR_RED_GRAY, + COLOR_BROWN, + COLOR_GREEN_GRAY, + COLOR_BLUE_GRAY, + COLOR_PURPLE_GRAY, + COLOR_BEIGE, + COLOR_PALE_GREEN_GRAY, + COLOR_PALE_RED_GRAY, + COLOR_PALE_PURPLE_GRAY, + COLOR_PALE_BLUE_GRAY, + COLOR_SILVER, + COLOR_GRAY80, + COLOR_OFF_WHITE, + COLOR_GUNMETAL, + COLOR_HULL, + COLOR_TITANIUM, + COLOR_DARK_GUNMETAL, + COLOR_BRONZE, + COLOR_BRASS + ) + else + use_colours = list( + COLOR_NAVY_BLUE, + COLOR_GREEN, + COLOR_DARK_GRAY, + COLOR_MAROON, + COLOR_PURPLE, + COLOR_VIOLET, + COLOR_OLIVE, + COLOR_BROWN_ORANGE, + COLOR_DARK_ORANGE, + COLOR_GRAY40, + COLOR_SEDONA, + COLOR_DARK_BROWN, + COLOR_BLUE, + COLOR_DEEP_SKY_BLUE, + COLOR_LIME, + COLOR_CYAN, + COLOR_TEAL, + COLOR_RED, + COLOR_PINK, + COLOR_ORANGE, + COLOR_YELLOW, + COLOR_GRAY, + COLOR_RED_GRAY, + COLOR_BROWN, + COLOR_GREEN_GRAY, + COLOR_BLUE_GRAY, + COLOR_SUN, + COLOR_PURPLE_GRAY, + COLOR_BLUE_LIGHT, + COLOR_RED_LIGHT, + COLOR_BEIGE, + COLOR_PALE_GREEN_GRAY, + COLOR_PALE_RED_GRAY, + COLOR_PALE_PURPLE_GRAY, + COLOR_PALE_BLUE_GRAY, + COLOR_LUMINOL, + COLOR_SILVER, + COLOR_GRAY80, + COLOR_OFF_WHITE, + COLOR_NT_RED, + COLOR_BOTTLE_GREEN, + COLOR_PALE_BTL_GREEN, + COLOR_GUNMETAL, + COLOR_MUZZLE_FLASH, + COLOR_CHESTNUT, + COLOR_BEASTY_BROWN, + COLOR_WHEAT, + COLOR_CYAN_BLUE, + COLOR_LIGHT_CYAN, + COLOR_PAKISTAN_GREEN, + COLOR_HULL, + COLOR_AMBER, + COLOR_COMMAND_BLUE, + COLOR_SKY_BLUE, + COLOR_PALE_ORANGE, + COLOR_CIVIE_GREEN, + COLOR_TITANIUM, + COLOR_DARK_GUNMETAL, + COLOR_BRONZE, + COLOR_BRASS, + COLOR_INDIGO + ) + + var/mech_colour = super_random ? FALSE : pick(use_colours) + if(!arms) + var/armstype = pick(typesof(/obj/item/mech_component/manipulators)-/obj/item/mech_component/manipulators) + arms = new armstype(src) + arms.color = mech_colour ? mech_colour : pick(use_colours) + if(!legs) + var/legstype = pick(typesof(/obj/item/mech_component/propulsion)-/obj/item/mech_component/propulsion) + legs = new legstype(src) + legs.color = mech_colour ? mech_colour : pick(use_colours) + if(!head) + var/headtype = pick(typesof(/obj/item/mech_component/sensors)-/obj/item/mech_component/sensors) + head = new headtype(src) + head.color = mech_colour ? mech_colour : pick(use_colours) + if(!body) + var/bodytype = pick(typesof(/obj/item/mech_component/chassis)-/obj/item/mech_component/chassis) + body = new bodytype(src) + body.color = mech_colour ? mech_colour : pick(use_colours) + . = ..() + +// Used for spawning/debugging. +/mob/living/exosuit/premade/random/normal + +/mob/living/exosuit/premade/random/boring/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame) + return ..(mapload, source_frame, using_boring_colours = TRUE) + +/mob/living/exosuit/premade/random/extra/Initialize(mapload, var/obj/structure/heavy_vehicle_frame/source_frame) + return ..(mapload, source_frame, super_random = TRUE) diff --git a/code/modules/exosuits/premade/combat.dm b/code/modules/exosuits/premade/combat.dm new file mode 100644 index 00000000000..abcbd41a15b --- /dev/null +++ b/code/modules/exosuits/premade/combat.dm @@ -0,0 +1,24 @@ +/mob/living/exosuit/premade/combat + name = "combat exosuit" + desc = "A sleek, modern combat exosuit." + +/mob/living/exosuit/premade/combat/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/combat(src) + arms.color = COLOR_DARK_GUNMETAL + if(!legs) + legs = new /obj/item/mech_component/propulsion/combat(src) + legs.color = COLOR_DARK_GUNMETAL + if(!head) + head = new /obj/item/mech_component/sensors/combat(src) + head.color = COLOR_DARK_GUNMETAL + if(!body) + body = new /obj/item/mech_component/chassis/combat(src) + body.color = COLOR_DARK_GUNMETAL + + . = ..() + +/mob/living/exosuit/premade/combat/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND) diff --git a/code/modules/exosuits/premade/exploration.dm b/code/modules/exosuits/premade/exploration.dm new file mode 100644 index 00000000000..5219a1f2c31 --- /dev/null +++ b/code/modules/exosuits/premade/exploration.dm @@ -0,0 +1,36 @@ +/mob/living/exosuit/premade/light/exploration + name = "exploration mech" + desc = "It looks a bit charred." + +/obj/item/mech_component/manipulators/industrial/exploration + color = COLOR_PURPLE + +/obj/item/mech_component/chassis/pod/exploration + color = COLOR_GUNMETAL + +/obj/item/mech_component/propulsion/tracks/exploration + color = COLOR_GUNMETAL + +/mob/living/exosuit/premade/light/exploration/Initialize() + if(!body) + body = new /obj/item/mech_component/chassis/pod/exploration(src) + if(!legs) + legs = new /obj/item/mech_component/propulsion/tracks/exploration(src) + if(!arms) + arms = new /obj/item/mech_component/manipulators/industrial/exploration(src) + + . = ..() + + //Damage it + var/list/parts = list(arms,legs,head,body) + var/obj/item/mech_component/damaged = pick(parts) + damaged.take_burn_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED) + if(prob(33)) + parts -= damaged + damaged = pick(parts) + damaged.take_burn_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED) + +/mob/living/exosuit/premade/light/exploration/spawn_mech_equipment() + install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD) + install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_LEFT_HAND) diff --git a/code/modules/exosuits/premade/heavy.dm b/code/modules/exosuits/premade/heavy.dm new file mode 100644 index 00000000000..b94d23986be --- /dev/null +++ b/code/modules/exosuits/premade/heavy.dm @@ -0,0 +1,25 @@ +/mob/living/exosuit/premade/heavy + name = "Heavy exosuit" + desc = "A heavily armored combat exosuit." + +/mob/living/exosuit/premade/heavy/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/heavy(src) + arms.color = COLOR_TITANIUM + if(!legs) + legs = new /obj/item/mech_component/propulsion/heavy(src) + legs.color = COLOR_TITANIUM + if(!head) + head = new /obj/item/mech_component/sensors/heavy(src) + head.color = COLOR_TITANIUM + if(!body) + body = new /obj/item/mech_component/chassis/heavy(src) + body.color = COLOR_TITANIUM + + return ..() + +/mob/living/exosuit/premade/heavy/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND) +// install_system_initialize(new /obj/item/mech_equipment/shields(src), HARDPOINT_BACK) diff --git a/code/modules/exosuits/premade/industrial.dm b/code/modules/exosuits/premade/industrial.dm new file mode 100644 index 00000000000..1bbdd39fb46 --- /dev/null +++ b/code/modules/exosuits/premade/industrial.dm @@ -0,0 +1,112 @@ +/mob/living/exosuit/premade/industrial + name = "power loader" + desc = "An ancient, but well-liked cargo handling exosuit." + +/obj/item/mech_component/manipulators/industrial/painted + color = "#ffbc37" + +/obj/item/mech_component/propulsion/industrial/painted + color = "#ffbc37" + +/obj/item/mech_component/sensors/industrial/painted + color = "#ffbc37" + +/obj/item/mech_component/chassis/industrial/painted + color = "#ffdc37" + +/mob/living/exosuit/premade/industrial/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/industrial/painted(src) + if(!legs) + legs = new /obj/item/mech_component/propulsion/industrial/painted(src) + if(!head) + head = new /obj/item/mech_component/sensors/industrial/painted(src) + if(!body) + body = new /obj/item/mech_component/chassis/industrial/painted(src) + . = ..() + +/mob/living/exosuit/premade/industrial/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND) + +/mob/living/exosuit/premade/industrial/mechete/Initialize() + . = ..() + + if (arms) + arms.color = "#6c8aaf" + if (legs) + legs.color = "#6c8aaf" + if (head) + head.color = "#6c8aaf" + if (body) + body.color = "#6c8aaf" + +/mob/living/exosuit/premade/industrial/mechete/spawn_mech_equipment() + install_system_initialize(new /obj/item/mech_equipment/mounted_system/melee/mechete(src), HARDPOINT_RIGHT_HAND) + +/mob/living/exosuit/premade/industrial/flames_red + name = "APLU \"Firestarter\"" + desc = "An ancient, but well-liked cargo handling exosuit. This one has cool red flames." + decal = "flames_red" + +/mob/living/exosuit/premade/industrial/flames_blue + name = "APLU \"Burning Chrome\"" + desc = "An ancient, but well-liked cargo handling exosuit. This one has cool blue flames." + decal = "flames_blue" + + +/mob/living/exosuit/premade/industrial/firefighter + name = "firefighting exosuit" + desc = "A mix and match of industrial parts designed to withstand fires." + +/mob/living/exosuit/premade/firefighter/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/industrial(src) + arms.color = "#385b3c" + if(!legs) + legs = new /obj/item/mech_component/propulsion/industrial(src) + legs.color = "#385b3c" + if(!head) + head = new /obj/item/mech_component/sensors/industrial(src) + head.color = "#385b3c" + if(!body) + body = new /obj/item/mech_component/chassis/heavy(src) + body.color = "#385b3c" + + material = get_material_by_name(MAT_PLASTEEL) + + return ..() + +/mob/living/exosuit/premade/firefighter/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/extinguisher(src), HARDPOINT_RIGHT_HAND) + +/obj/item/mech_component/sensors/firefighter/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING) + +/mob/living/exosuit/premade/industrial/old + name = "weathered power loader" + desc = "An ancient, but well-liked cargo handling exosuit. The paint is starting to flake. Perhaps some maintenance is in order?" + +/mob/living/exosuit/premade/industrial/old/Initialize() + . = ..() + var/list/parts = list(arms,legs,head,body) + for(var/obj/item/mech_component/MC in parts) + if(prob(35)) + MC.color = rgb(255,rand(188, 225),rand(55, 136)) + //Damage it + var/obj/item/mech_component/damaged = pick(parts) + damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED) + if(prob(33)) + parts -= damaged + damaged = pick(parts) + damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED) + +/mob/living/exosuit/premade/industrial/old/spawn_mech_equipment() + install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD) + install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND) diff --git a/code/modules/exosuits/premade/light.dm b/code/modules/exosuits/premade/light.dm new file mode 100644 index 00000000000..e4eda4fccf3 --- /dev/null +++ b/code/modules/exosuits/premade/light.dm @@ -0,0 +1,25 @@ +/mob/living/exosuit/premade/light + name = "light exosuit" + desc = "A light and agile exosuit." + +/mob/living/exosuit/premade/light/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/light(src) + arms.color = COLOR_OFF_WHITE + if(!legs) + legs = new /obj/item/mech_component/propulsion/light(src) + legs.color = COLOR_OFF_WHITE + if(!head) + head = new /obj/item/mech_component/sensors/light(src) + head.color = COLOR_OFF_WHITE + if(!body) + body = new /obj/item/mech_component/chassis/light(src) + body.color = COLOR_OFF_WHITE + + return ..() + +/mob/living/exosuit/premade/light/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/catapult(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/sleeper(src), HARDPOINT_BACK) + install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD) diff --git a/code/modules/exosuits/premade/mercenary.dm b/code/modules/exosuits/premade/mercenary.dm new file mode 100644 index 00000000000..e2cbcfce99c --- /dev/null +++ b/code/modules/exosuits/premade/mercenary.dm @@ -0,0 +1,46 @@ +/mob/living/exosuit/premade/mercenary + name = "mercenary exosuit" + desc = "A heavily armored combat exosuit." + +/obj/item/mech_component/manipulators/mercenary/painted + color = COLOR_TITANIUM + +/obj/item/mech_component/propulsion/mercenary/painted + color = COLOR_TITANIUM + +/obj/item/mech_component/sensors/mercenary/painted + color = COLOR_TITANIUM + +/obj/item/mech_component/chassis/mercenary/painted + color = COLOR_TITANIUM + +/mob/living/exosuit/premade/mercenary/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/mercenary/painted(src) + if(!legs) + legs = new /obj/item/mech_component/propulsion/mercenary/painted(src) + if(!head) + head = new /obj/item/mech_component/sensors/mercenary/painted(src) + if(!body) + body = new /obj/item/mech_component/chassis/mercenary/painted(src) + . = ..() + +/mob/living/exosuit/premade/mercenary/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND) + +/mob/living/exosuit/premade/mercenary/merc/Initialize() + . = ..() + if(arms) + arms.color = COLOR_RED + if(legs) + legs.color = COLOR_RED + if(head) + head.color = COLOR_RED + if(body) + body.color = COLOR_DARK_GUNMETAL + +/mob/living/exosuit/premade/mercenary/merc/spawn_mech_equipment() + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/laser(src), HARDPOINT_RIGHT_HAND) diff --git a/code/modules/exosuits/premade/modern.dm b/code/modules/exosuits/premade/modern.dm new file mode 100644 index 00000000000..5f4458de095 --- /dev/null +++ b/code/modules/exosuits/premade/modern.dm @@ -0,0 +1,24 @@ +/mob/living/exosuit/premade/modern + name = "modern exosuit" + desc = "A sleek, modern modern exosuit." + +/mob/living/exosuit/premade/modern/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/modern(src) + arms.color = COLOR_DARK_GUNMETAL + if(!legs) + legs = new /obj/item/mech_component/propulsion/modern(src) + legs.color = COLOR_DARK_GUNMETAL + if(!head) + head = new /obj/item/mech_component/sensors/modern(src) + head.color = COLOR_DARK_GUNMETAL + if(!body) + body = new /obj/item/mech_component/chassis/modern(src) + body.color = COLOR_DARK_GUNMETAL + + return ..() + +/mob/living/exosuit/premade/modern/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/taser/ion(src), HARDPOINT_RIGHT_HAND) diff --git a/code/modules/exosuits/premade/powerloader.dm b/code/modules/exosuits/premade/powerloader.dm new file mode 100644 index 00000000000..83a21468e7c --- /dev/null +++ b/code/modules/exosuits/premade/powerloader.dm @@ -0,0 +1,92 @@ +/mob/living/exosuit/premade/powerloader + name = "power loader" + desc = "An ancient, but well-liked cargo handling exosuit." + +/mob/living/exosuit/premade/powerloader/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/powerloader(src) + arms.color = "#ffbc37" + if(!legs) + legs = new /obj/item/mech_component/propulsion/powerloader(src) + legs.color = "#ffbc37" + if(!head) + head = new /obj/item/mech_component/sensors/powerloader(src) + head.color = "#ffbc37" + if(!body) + body = new /obj/item/mech_component/chassis/powerloader(src) + body.color = "#ffdc37" + + body.armour = new /obj/item/robot_parts/robot_component/armour/exosuit(src) + + return ..() + +/mob/living/exosuit/premade/powerloader/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND) + +/mob/living/exosuit/premade/powerloader/flames_red + name = "APLU \"Firestarter\"" + desc = "An ancient, but well-liked cargo handling exosuit. This one has cool red flames." + decal = "flames_red" + +/mob/living/exosuit/premade/powerloader/flames_blue + name = "APLU \"Burning Chrome\"" + desc = "An ancient, but well-liked cargo handling exosuit. This one has cool blue flames." + decal = "flames_blue" + + +/mob/living/exosuit/premade/firefighter + name = "firefighting exosuit" + desc = "A mix and match of industrial parts designed to withstand fires." + +/mob/living/exosuit/premade/firefighter/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/powerloader(src) + arms.color = "#385b3c" + if(!legs) + legs = new /obj/item/mech_component/propulsion/powerloader(src) + legs.color = "#385b3c" + if(!head) + head = new /obj/item/mech_component/sensors/powerloader(src) + head.color = "#385b3c" + if(!body) + body = new /obj/item/mech_component/chassis/heavy(src) + body.color = "#385b3c" + + . = ..() + + material = get_material_by_name(MAT_PLASTEEL) + +/mob/living/exosuit/premade/firefighter/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/drill(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/mounted_system/extinguisher(src), HARDPOINT_RIGHT_HAND) + +/obj/item/mech_component/sensors/firefighter/prebuild() + ..() + software = new(src) + software.installed_software = list(MECH_SOFTWARE_UTILITY, MECH_SOFTWARE_ENGINEERING) + +/mob/living/exosuit/premade/powerloader/old + name = "weathered power loader" + desc = "An ancient, but well-liked cargo handling exosuit. The paint is starting to flake. Perhaps some maintenance is in order?" + +/mob/living/exosuit/premade/powerloader/old/Initialize() + . = ..() + var/list/parts = list(arms,legs,head,body) + for(var/obj/item/mech_component/MC in parts) + if(prob(35)) + MC.color = rgb(255,rand(188, 225),rand(55, 136)) + //Damage it + var/obj/item/mech_component/damaged = pick(parts) + damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED) + if(prob(33)) + parts -= damaged + damaged = pick(parts) + damaged.take_brute_damage((damaged.max_damage / 4 ) * MECH_COMPONENT_DAMAGE_DAMAGED) + +/mob/living/exosuit/premade/powerloader/old/spawn_mech_equipment() + install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD) + install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/clamp(src), HARDPOINT_RIGHT_HAND) diff --git a/code/modules/exosuits/premade/sleek.dm b/code/modules/exosuits/premade/sleek.dm new file mode 100644 index 00000000000..07db10859f1 --- /dev/null +++ b/code/modules/exosuits/premade/sleek.dm @@ -0,0 +1,33 @@ +/mob/living/exosuit/premade/sleek + name = "sleek exosuit" + desc = "A sleek and agile exosuit." + +/obj/item/mech_component/manipulators/sleek/painted + color = COLOR_OFF_WHITE + +/obj/item/mech_component/propulsion/sleek/painted + color = COLOR_OFF_WHITE + +/obj/item/mech_component/sensors/sleek/painted + color = COLOR_OFF_WHITE + +/obj/item/mech_component/chassis/sleek/painted + color = COLOR_OFF_WHITE + +/mob/living/exosuit/premade/sleek/Initialize() + if(!arms) + arms = new /obj/item/mech_component/manipulators/sleek/painted(src) + if(!legs) + legs = new /obj/item/mech_component/propulsion/sleek/painted(src) + if(!head) + head = new /obj/item/mech_component/sensors/sleek/painted(src) + if(!body) + body = new /obj/item/mech_component/chassis/sleek/painted(src) + + return ..() + +/mob/living/exosuit/premade/sleek/spawn_mech_equipment() + ..() + install_system_initialize(new /obj/item/mech_equipment/catapult(src), HARDPOINT_LEFT_HAND) + install_system_initialize(new /obj/item/mech_equipment/sleeper(src), HARDPOINT_BACK) + install_system_initialize(new /obj/item/mech_equipment/light(src), HARDPOINT_HEAD) diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 09bc9d02748..f95e445fd6f 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -238,24 +238,35 @@ var/burn = 0 var/icon_state_broken = "broken" + var/max_damage + +/obj/item/robot_parts/robot_component/proc/is_functional() + return (brute + burn) < max_damage + /obj/item/robot_parts/robot_component/binary_communication_device name = "binary communication device" desc = "A module used for binary communications over encrypted frequencies, commonly used by synthetic robots." icon_state = "binradio" icon_state_broken = "binradio_broken" + max_damage = 40 + /obj/item/robot_parts/robot_component/actuator name = "actuator" desc = "A modular, hydraulic actuator used by exosuits and robots alike for movement and manipulation." icon_state = "motor" icon_state_broken = "motor_broken" + max_damage = 50 + /obj/item/robot_parts/robot_component/armour name = "armour plating" desc = "A pair of flexible, adaptable armor plates, used to protect the internals of robots." icon_state = "armor" icon_state_broken = "armor_broken" + max_damage = 90 + /obj/item/robot_parts/robot_component/armour_platform name = "platform armour plating" desc = "A pair of reinforced armor plates, used to protect the internals of robots." @@ -263,6 +274,8 @@ icon_state_broken = "armor_broken" color = COLOR_GRAY80 + max_damage = 140 + /obj/item/robot_parts/robot_component/armour_light name = "lightweight armour plating" desc = "A pair of flexible, light armor plates, used to protect the internals of robots equipped with anti-gravity frames." @@ -275,14 +288,21 @@ icon_state = "camera" icon_state_broken = "camera_broken" + max_damage = 40 + /obj/item/robot_parts/robot_component/diagnosis_unit name = "diagnosis unit" desc = "An internal computer and sensors used by robots and exosuits to accurately diagnose any system discrepancies on their components." icon_state = "analyser" icon_state_broken = "analyser_broken" + max_damage = 30 + /obj/item/robot_parts/robot_component/radio name = "radio" desc = "A modular, multi-frequency radio used by robots and exosuits to enable communication systems. Comes with built-in subspace receivers." icon_state = "radio" icon_state_broken = "radio_broken" + + max_damage = 40 + diff --git a/code/modules/nano/interaction/exosuit.dm b/code/modules/nano/interaction/exosuit.dm new file mode 100644 index 00000000000..97dbf341387 --- /dev/null +++ b/code/modules/nano/interaction/exosuit.dm @@ -0,0 +1,6 @@ +GLOBAL_DATUM_INIT(mech_state, /datum/topic_state/default/mech, new) + +/datum/topic_state/default/mech/can_use_topic(var/mob/living/exosuit/src_object, var/mob/user) + if(istype(src_object) && (user in src_object.pilots)) + return ..() + return STATUS_CLOSE diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 6c8264212dc..326a3c2eb31 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -225,6 +225,10 @@ Fire(A,user,params) //Otherwise, fire normally. */ +// Can the weapon be click-dragged to fire on turfs when able. +/obj/item/gun/proc/can_autofire() + return FALSE + /obj/item/gun/attack(atom/A, mob/living/user, def_zone) if (A == user && user.zone_sel.selecting == O_MOUTH && !mouthshoot) handle_suicide(user) @@ -404,7 +408,7 @@ if(muzzle_flash) set_light(0) - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) // Similar to the above proc, but does not require a user, which is ideal for things like turrets. /obj/item/gun/proc/Fire_userless(atom/target) @@ -497,7 +501,7 @@ /obj/item/gun/proc/handle_click_empty(mob/user) if (user) user.visible_message("*click click*", "*click*") - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) else src.visible_message("*click click*") playsound(src, 'sound/weapons/empty.ogg', 100, 1) @@ -729,7 +733,7 @@ var/datum/firemode/new_mode = firemodes[sel_mode] new_mode.apply_to(src) to_chat(user, "\The [src] is now set to [new_mode.name].") - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) return new_mode @@ -752,12 +756,12 @@ /obj/item/gun/equipped(mob/living/user, slot) // When a gun is equipped to your hands, we'll add the HUD to the user. Pending porting over TGMC guncode where wielding is far more sensible. if(user?.hud_used) if(slot == slot_l_hand || slot == slot_r_hand) - user.hud_used.add_ammo_hud(user, src) + user?.hud_used?.add_ammo_hud(user, src) else - user.hud_used.remove_ammo_hud(user, src) + user?.hud_used?.remove_ammo_hud(user, src) return ..() /obj/item/gun/dropped(mob/living/user) // Ditto as above, we remove the HUD. Pending porting TGMC code to clean up this fucking nightmare of spaghetti. if(user?.hud_used) - user.hud_used.remove_ammo_hud(user, src) + user?.hud_used?.remove_ammo_hud(user, src) ..() diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 58369fd5ad9..c974e6c1cc2 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -115,7 +115,7 @@ if(!power_supply.checked_use(charge_cost)) return null var/mob/living/M = loc // TGMC Ammo HUD if(istype(M)) // TGMC Ammo HUD - M?.hud_used.update_ammo_hud(M, src) + M?.hud_used?.update_ammo_hud(M, src) return new projectile_type(src) /obj/item/gun/energy/proc/load_ammo(var/obj/item/C, mob/user) @@ -137,7 +137,7 @@ playsound(src, 'sound/weapons/flipblade.ogg', 50, 1) update_icon() update_held_icon() - user.hud_used.update_ammo_hud(user, src) // TGMC Ammo HUD + user?.hud_used?.update_ammo_hud(user, src) // TGMC Ammo HUD else to_chat(user, "This cell is not fitted for [src].") return @@ -154,7 +154,7 @@ playsound(src, 'sound/weapons/empty.ogg', 50, 1) update_icon() update_held_icon() - user.hud_used.update_ammo_hud(user, src) // TGMC Ammo HUD + user?.hud_used?.update_ammo_hud(user, src) // TGMC Ammo HUD else to_chat(user, "[src] does not have a power cell.") @@ -172,7 +172,7 @@ if(isrobot(src.loc)) var/mob/living/silicon/robot/R = src.loc return R.cell - if(istype(src.loc, /obj/item/rig_module)) + else 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 @@ -180,6 +180,11 @@ var/obj/item/rig/suit = H.get_rig() if(istype(suit)) return suit.cell + else if(istype(loc, /atom/movable)) + var/atom/movable/AM = loc + var/obj/item/cell/C = AM.get_cell() + if(C) + return C return null /obj/item/gun/energy/examine(mob/user, distance, infix, suffix) diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index c6f0206b0f7..88e5965822e 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -38,6 +38,29 @@ charge_cost = 800 recharge_time = 0.5 SECONDS +/obj/item/gun/energy/taser/carbine + name = "electrolaser carbine" + desc = "The NT Mk44 NL is a high capacity gun used for lesss-than-lethal takedowns. It can switch between high and low intensity stun shots." + icon = 'icons/obj/gun2.dmi' + icon_state = "tasercarbine" + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BELT|SLOT_BACK + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3) + force = 8 + accuracy = 1 + projectile_type = /obj/item/projectile/beam/stun + wielded_item_state = "tasercarbine-wielded" + + firemodes = list( + list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun, charge_cost = 300), + list(mode_name="shock", projectile_type=/obj/item/projectile/beam/shock, charge_cost = 480), + ) + +/obj/item/gun/energy/taser/carbine/mounted + name = "mounted electrolaser carbine" + self_recharge = TRUE + use_external_power = TRUE + /obj/item/gun/energy/crossbow name = "mini energy-crossbow" desc = "A weapon favored by many mercenary stealth specialists." diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 6a64d4fce86..6075782f1e1 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -57,7 +57,7 @@ var/mob/living/M = loc if(istype(M)) - M?.hud_used.update_ammo_hud(M, src) + M?.hud_used?.update_ammo_hud(M, src) if (chambered) return chambered.BB @@ -105,7 +105,7 @@ var/mob/living/M = loc if(istype(M)) - M?.hud_used.update_ammo_hud(M, src) + M?.hud_used?.update_ammo_hud(M, src) //Attempts to load A into src, depending on the type of thing being loaded and the load_method @@ -125,7 +125,7 @@ AM.loc = src ammo_magazine = AM user.visible_message("[user] inserts [AM] into [src].", "You insert [AM] into [src].") - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) playsound(src, 'sound/weapons/flipblade.ogg', 50, 1) if(SPEEDLOADER) if(loaded.len >= max_shells) @@ -140,10 +140,10 @@ loaded += C AM.stored_ammo -= C //should probably go inside an ammo_magazine proc, but I guess less proc calls this way... count++ - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) if(count) user.visible_message("[user] reloads [src].", "You load [count] round\s into [src].") - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) playsound(src, 'sound/weapons/empty.ogg', 50, 1) AM.update_icon() else if(istype(A, /obj/item/ammo_casing)) @@ -179,7 +179,7 @@ sleep(1 SECOND) update_icon() - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) //attempts to unload src. If allow_dump is set to 0, the speedloader unloading method will be disabled /obj/item/gun/projectile/proc/unload_ammo(mob/user, var/allow_dump=1) @@ -189,7 +189,7 @@ playsound(src, 'sound/weapons/empty.ogg', 50, 1) ammo_magazine.update_icon() ammo_magazine = null - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) else if(loaded.len) //presumably, if it can be speed-loaded, it can be speed-unloaded. if(allow_dump && (load_method & SPEEDLOADER)) @@ -208,11 +208,11 @@ user.put_in_hands(C) user.visible_message("[user] removes \a [C] from [src].", "You remove \a [C] from [src].") playsound(src, 'sound/weapons/empty.ogg', 50, 1) - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) else to_chat(user, "[src] is empty.") update_icon() - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) /obj/item/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob) ..() @@ -243,7 +243,7 @@ ammo_magazine.update_icon() ammo_magazine = null update_icon() //make sure to do this after unsetting ammo_magazine - user.hud_used.update_ammo_hud(user, src) + user?.hud_used?.update_ammo_hud(user, src) /obj/item/gun/projectile/examine(mob/user, distance, infix, suffix) . = ..() diff --git a/code/modules/research/designs/circuits/circuits.dm b/code/modules/research/designs/circuits/circuits.dm index a56f10dff79..47b47f2f8fa 100644 --- a/code/modules/research/designs/circuits/circuits.dm +++ b/code/modules/research/designs/circuits/circuits.dm @@ -669,6 +669,45 @@ CIRCUITS BELOW build_path = /obj/item/circuitboard/microwave/advanced sort_string = "HACAA" +/datum/design/circuit/exosuit_engineering + name = "exosuit engineering software" + id = "exosoft_engi" + req_tech = list(TECH_DATA = 1) + build_path = /obj/item/weapon/circuitboard/exosystem/engineering + sort_string = "XABAA" + materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500) + +/datum/design/circuit/exosuit_utility + name = "exosuit utility software" + id = "exosoft_utility" + req_tech = list(TECH_DATA = 1) + build_path = /obj/item/weapon/circuitboard/exosystem/utility + sort_string = "XABAB" + materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500) + +/datum/design/circuit/exosuit_medical + name = "exosuit medical software" + id = "exosoft_medical" + req_tech = list(TECH_DATA = 3,TECH_BIO = 2) + build_path = /obj/item/weapon/circuitboard/exosystem/medical + sort_string = "XABAC" + materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500) + +/datum/design/circuit/exosuit_weapons + name = "exosuit weapons software" + id = "exosoft_weapons" + req_tech = list(TECH_DATA = 4, TECH_COMBAT = 3) + build_path = /obj/item/weapon/circuitboard/exosystem/weapons + sort_string = "XABAD" + materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500) + +/datum/design/circuit/exosuit_advweapons + name = "exosuit advweapons software" + id = "exosoft_advweapons" + req_tech = list(TECH_DATA = 4, TECH_COMBAT = 5) + build_path = /obj/item/weapon/circuitboard/exosystem/advweapons + sort_string = "XABAE" + materials = list(MAT_GLASS = 2000, MAT_COPPER = 1500) /* I have no idea how this was even running before, but it doesn't seem to be necessary. /////////////////////////////////// @@ -677,4 +716,4 @@ CIRCUITS BELOW /datum/design/circuit/shield req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3) materials = list("$glass" = 2000, "sacid" = 20, "$phoron" = 10000, "$diamond" = 5000, "$gold" = 10000) -*/ \ No newline at end of file +*/ diff --git a/code/modules/research/designs/exosuits/categories.dm b/code/modules/research/designs/exosuits/categories.dm new file mode 100644 index 00000000000..5093d6eb82b --- /dev/null +++ b/code/modules/research/designs/exosuits/categories.dm @@ -0,0 +1,15 @@ +/* + * Exosuits! Nebbay Mechs, but filling the middle ground. + */ + +/datum/design/item/mechfab/trueexo + category = "Exosuit" + time = 60 + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4) + +/datum/design/item/mechfab/trueexo/equip + category = "Exosuit Equipment" + time = 45 + +/datum/design/item/mechfab/trueexo/armor + category = "Exosuit Armor" diff --git a/code/modules/research/designs/exosuits/combat.dm b/code/modules/research/designs/exosuits/combat.dm new file mode 100644 index 00000000000..71dffebdf5e --- /dev/null +++ b/code/modules/research/designs/exosuits/combat.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/combat + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 5, TECH_DATA = 4) + materials = list(MAT_PLASTEEL = 7500, MAT_COPPER = 1500) + +/datum/design/item/mechfab/trueexo/combat/chassis + name = "Combat Chassis" + id = "exosuit_combat_chassis" + build_path = /obj/item/mech_component/chassis/combat + materials = list(MAT_PLASTEEL = 10000, MAT_COPPER = 2500) + +/datum/design/item/mechfab/trueexo/combat/arms + name = "Combat Arms" + id = "exosuit_combat_arms" + build_path = /obj/item/mech_component/manipulators/combat + +/datum/design/item/mechfab/trueexo/combat/legs + name = "Combat Legs" + id = "exosuit_combat_legs" + build_path = /obj/item/mech_component/propulsion/combat + +/datum/design/item/mechfab/trueexo/combat/head + name = "Combat Head" + id = "exosuit_combat_head" + build_path = /obj/item/mech_component/sensors/combat diff --git a/code/modules/research/designs/exosuits/components.dm b/code/modules/research/designs/exosuits/components.dm new file mode 100644 index 00000000000..f8d7cb3efe2 --- /dev/null +++ b/code/modules/research/designs/exosuits/components.dm @@ -0,0 +1,35 @@ + +/datum/design/item/mechfab/trueexo/spine + name = "Exosuit Spine" + id = "exosuit_spine" + build_path = /obj/structure/heavy_vehicle_frame + time = 120 + materials = list(MAT_STEEL = 10000) + +/datum/design/item/mechfab/trueexo/armor/normal + name = "Standard Exosuit Armor" + id = "exosuit_armor_norm" + build_path = /obj/item/robot_parts/robot_component/armour/exosuit + req_tech = list(TECH_MATERIAL = 1) + materials = list(MAT_STEEL = 7500) + +/datum/design/item/mechfab/trueexo/armor/radproof + name = "Radiation Hardened Exosuit Armor" + id = "exosuit_armor_rad" + build_path = /obj/item/robot_parts/robot_component/armour/exosuit/radproof + req_tech = list(TECH_MATERIAL = 3, TECH_PHORON = 3) + materials = list(MAT_STEEL = 5000, MAT_LEAD = 2500) + +/datum/design/item/mechfab/trueexo/armor/em + name = "Ionized Exosuit Armor" + id = "exosuit_armor_em" + build_path = /obj/item/robot_parts/robot_component/armour/exosuit/em + req_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 5) + materials = list(MAT_STEEL = 5000, MAT_METALHYDROGEN = 2500) + +/datum/design/item/mechfab/trueexo/armor/combat + name = "Combat Exosuit Armor" + id = "exosuit_armor_combat" + build_path = /obj/item/robot_parts/robot_component/armour/exosuit/combat + req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5) + materials = list(MAT_TITANIUM = 5000, MAT_DIAMOND = 3000) diff --git a/code/modules/research/designs/exosuits/engineering.dm b/code/modules/research/designs/exosuits/engineering.dm new file mode 100644 index 00000000000..e1673702208 --- /dev/null +++ b/code/modules/research/designs/exosuits/engineering.dm @@ -0,0 +1,16 @@ + +/datum/design/item/mechfab/trueexo/equip/engineering + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_POWER = 2) + +/datum/design/item/mechfab/trueexo/equip/engineering/rcd + name = "Mounted RCD" + id = "exosuit_tool_rcd" + build_path = /obj/item/mech_equipment/mounted_system/rcd + materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 1500) + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_POWER = 5) + +/datum/design/item/mechfab/trueexo/equip/engineering/extinguisher + name = "Mounted Extinguisher" + id = "exosuit_tool_extinguisher" + build_path = /obj/item/mech_equipment/mounted_system/extinguisher + materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 1500) diff --git a/code/modules/research/designs/exosuits/heavy.dm b/code/modules/research/designs/exosuits/heavy.dm new file mode 100644 index 00000000000..04cd6588cbc --- /dev/null +++ b/code/modules/research/designs/exosuits/heavy.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/heavyhead + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 3, TECH_DATA = 4) + materials = list(MAT_TITANIUM = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 2000) + +/datum/design/item/mechfab/trueexo/heavy/chassis + name = "heavy Chassis" + id = "exosuit_heavy_chassis" + build_path = /obj/item/mech_component/chassis/heavy + materials = list(MAT_TITANIUM = 10000, MAT_PLASTEEL = 5000, MAT_COPPER = 2500) + +/datum/design/item/mechfab/trueexo/heavy/arms + name = "heavy Arms" + id = "exosuit_heavy_arms" + build_path = /obj/item/mech_component/manipulators/heavy + +/datum/design/item/mechfab/trueexo/heavy/legs + name = "heavy Legs" + id = "exosuit_heavy_legs" + build_path = /obj/item/mech_component/propulsion/heavy + +/datum/design/item/mechfab/trueexo/heavy/head + name = "heavy Head" + id = "exosuit_heavy_head" + build_path = /obj/item/mech_component/sensors/heavy diff --git a/code/modules/research/designs/exosuits/industrial.dm b/code/modules/research/designs/exosuits/industrial.dm new file mode 100644 index 00000000000..4fb07a11a9f --- /dev/null +++ b/code/modules/research/designs/exosuits/industrial.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/industrial + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4) + materials = list(MAT_STEEL = 7500, MAT_COPPER = 1500) + +/datum/design/item/mechfab/trueexo/industrial/chassis + name = "Industrial Chassis" + id = "exosuit_industrial_chassis" + build_path = /obj/item/mech_component/chassis/industrial + materials = list(MAT_STEEL = 10000, MAT_COPPER = 2500) + +/datum/design/item/mechfab/trueexo/industrial/arms + name = "Industrial Arms" + id = "exosuit_industrial_arms" + build_path = /obj/item/mech_component/manipulators/industrial + +/datum/design/item/mechfab/trueexo/industrial/legs + name = "Industrial Legs" + id = "exosuit_industrial_legs" + build_path = /obj/item/mech_component/propulsion/industrial + +/datum/design/item/mechfab/trueexo/industrial/head + name = "Industrial Head" + id = "exosuit_industrial_head" + build_path = /obj/item/mech_component/sensors/industrial diff --git a/code/modules/research/designs/exosuits/light.dm b/code/modules/research/designs/exosuits/light.dm new file mode 100644 index 00000000000..b3123fb1570 --- /dev/null +++ b/code/modules/research/designs/exosuits/light.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/light + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4, TECH_BIO = 2) + materials = list(MAT_STEEL = 5500, MAT_COPPER = 1000) + +/datum/design/item/mechfab/trueexo/light/chassis + name = "Light Chassis" + id = "exosuit_light_chassis" + build_path = /obj/item/mech_component/chassis/light + materials = list(MAT_STEEL = 7600, MAT_COPPER = 2000) + +/datum/design/item/mechfab/trueexo/light/arms + name = "Light Arms" + id = "exosuit_light_arms" + build_path = /obj/item/mech_component/manipulators/light + +/datum/design/item/mechfab/trueexo/light/legs + name = "Light Legs" + id = "exosuit_light_legs" + build_path = /obj/item/mech_component/propulsion/light + +/datum/design/item/mechfab/trueexo/light/head + name = "Light Head" + id = "exosuit_light_head" + build_path = /obj/item/mech_component/sensors/light diff --git a/code/modules/research/designs/exosuits/medical.dm b/code/modules/research/designs/exosuits/medical.dm new file mode 100644 index 00000000000..876a7f6d7c5 --- /dev/null +++ b/code/modules/research/designs/exosuits/medical.dm @@ -0,0 +1,9 @@ + +/datum/design/item/mechfab/trueexo/equip/medical + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_BIO = 3) + +/datum/design/item/mechfab/trueexo/equip/medical/sleeper + name = "Portable Sleeper" + id = "exosuit_tool_sleeper" + build_path = /obj/item/mech_equipment/sleeper + materials = list(MAT_STEEL = 5000, MAT_ALUMINIUM = 1500) diff --git a/code/modules/research/designs/exosuits/mercenary.dm b/code/modules/research/designs/exosuits/mercenary.dm new file mode 100644 index 00000000000..4220d328563 --- /dev/null +++ b/code/modules/research/designs/exosuits/mercenary.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/mercenaryhead + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 3, TECH_DATA = 4) + materials = list(MAT_TITANIUM = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 2000) + +/datum/design/item/mechfab/trueexo/mercenary/chassis + name = "mercenary Chassis" + id = "exosuit_mercenary_chassis" + build_path = /obj/item/mech_component/chassis/mercenary + materials = list(MAT_TITANIUM = 10000, MAT_PLASTEEL = 5000, MAT_COPPER = 2500) + +/datum/design/item/mechfab/trueexo/mercenary/arms + name = "mercenary Arms" + id = "exosuit_mercenary_arms" + build_path = /obj/item/mech_component/manipulators/mercenary + +/datum/design/item/mechfab/trueexo/mercenary/legs + name = "mercenary Legs" + id = "exosuit_mercenary_legs" + build_path = /obj/item/mech_component/propulsion/mercenary + +/datum/design/item/mechfab/trueexo/mercenary/head + name = "mercenary Head" + id = "exosuit_mercenary_head" + build_path = /obj/item/mech_component/sensors/mercenary diff --git a/code/modules/research/designs/exosuits/modern.dm b/code/modules/research/designs/exosuits/modern.dm new file mode 100644 index 00000000000..88deb7d004c --- /dev/null +++ b/code/modules/research/designs/exosuits/modern.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/modern + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 4, TECH_DATA = 4) + materials = list(MAT_PLASTEEL = 7500, MAT_COPPER = 1500) + +/datum/design/item/mechfab/trueexo/modern/chassis + name = "Modern Chassis" + id = "exosuit_modern_chassis" + build_path = /obj/item/mech_component/chassis/modern + materials = list(MAT_PLASTEEL = 10000, MAT_COPPER = 2500) + +/datum/design/item/mechfab/trueexo/modern/arms + name = "Modern Arms" + id = "exosuit_modern_arms" + build_path = /obj/item/mech_component/manipulators/modern + +/datum/design/item/mechfab/trueexo/modern/legs + name = "Modern Legs" + id = "exosuit_modern_legs" + build_path = /obj/item/mech_component/propulsion/modern + +/datum/design/item/mechfab/trueexo/modern/head + name = "Modern Head" + id = "exosuit_modern_head" + build_path = /obj/item/mech_component/sensors/modern diff --git a/code/modules/research/designs/exosuits/pod.dm b/code/modules/research/designs/exosuits/pod.dm new file mode 100644 index 00000000000..d1ffd38c579 --- /dev/null +++ b/code/modules/research/designs/exosuits/pod.dm @@ -0,0 +1,9 @@ +/datum/design/item/mechfab/trueexo/pod + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_DATA = 3) + materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 2500, MAT_COPPER = 1500) + +/datum/design/item/mechfab/trueexo/pod/chassis + name = "Pod Chassis" + id = "exosuit_pod_chassis" + build_path = /obj/item/mech_component/chassis/pod + materials = list(MAT_STEEL = 7500, MAT_PLASTIC = 2500, MAT_COPPER = 2500) diff --git a/code/modules/research/designs/exosuits/powerloader.dm b/code/modules/research/designs/exosuits/powerloader.dm new file mode 100644 index 00000000000..9bc0e8668af --- /dev/null +++ b/code/modules/research/designs/exosuits/powerloader.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/powerloader + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_DATA = 4) + materials = list(MAT_STEEL = 7500, MAT_COPPER = 1500) + +/datum/design/item/mechfab/trueexo/powerloader/chassis + name = "Powerloader Chassis" + id = "exosuit_powerloader_chassis" + build_path = /obj/item/mech_component/chassis/powerloader + materials = list(MAT_STEEL = 10000, MAT_COPPER = 2500) + +/datum/design/item/mechfab/trueexo/powerloader/arms + name = "Powerloader Arms" + id = "exosuit_powerloader_arms" + build_path = /obj/item/mech_component/manipulators/powerloader + +/datum/design/item/mechfab/trueexo/powerloader/legs + name = "Powerloader Legs" + id = "exosuit_powerloader_legs" + build_path = /obj/item/mech_component/propulsion/powerloader + +/datum/design/item/mechfab/trueexo/powerloader/head + name = "Powerloader Head" + id = "exosuit_powerloader_head" + build_path = /obj/item/mech_component/sensors/powerloader diff --git a/code/modules/research/designs/exosuits/sleek.dm b/code/modules/research/designs/exosuits/sleek.dm new file mode 100644 index 00000000000..0ddbb170706 --- /dev/null +++ b/code/modules/research/designs/exosuits/sleek.dm @@ -0,0 +1,24 @@ +/datum/design/item/mechfab/trueexo/sleek + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_DATA = 4, TECH_BIO = 3) + materials = list(MAT_STEEL = 5500, MAT_COPPER = 1000) + +/datum/design/item/mechfab/trueexo/sleek/chassis + name = "sleek Chassis" + id = "exosuit_sleek_chassis" + build_path = /obj/item/mech_component/chassis/sleek + materials = list(MAT_STEEL = 7600, MAT_COPPER = 2000) + +/datum/design/item/mechfab/trueexo/sleek/arms + name = "sleek Arms" + id = "exosuit_sleek_arms" + build_path = /obj/item/mech_component/manipulators/sleek + +/datum/design/item/mechfab/trueexo/sleek/legs + name = "sleek Legs" + id = "exosuit_sleek_legs" + build_path = /obj/item/mech_component/propulsion/sleek + +/datum/design/item/mechfab/trueexo/sleek/head + name = "sleek Head" + id = "exosuit_sleek_head" + build_path = /obj/item/mech_component/sensors/sleek diff --git a/code/modules/research/designs/exosuits/spider.dm b/code/modules/research/designs/exosuits/spider.dm new file mode 100644 index 00000000000..49cc4a366ba --- /dev/null +++ b/code/modules/research/designs/exosuits/spider.dm @@ -0,0 +1,8 @@ +/datum/design/item/mechfab/trueexo/spider + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_DATA = 3) + materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 1500) + +/datum/design/item/mechfab/trueexo/spider/legs + name = "Spider Legs" + id = "exosuit_spider_legs" + build_path = /obj/item/mech_component/propulsion/spider diff --git a/code/modules/research/designs/exosuits/tracks.dm b/code/modules/research/designs/exosuits/tracks.dm new file mode 100644 index 00000000000..ad97f420711 --- /dev/null +++ b/code/modules/research/designs/exosuits/tracks.dm @@ -0,0 +1,8 @@ +/datum/design/item/mechfab/trueexo/tracks + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_DATA = 3) + materials = list(MAT_STEEL = 5000, MAT_PLASTEEL = 2500, MAT_COPPER = 1500) + +/datum/design/item/mechfab/trueexo/tracks/legs + name = "Tank Treads" + id = "exosuit_tracks_legs" + build_path = /obj/item/mech_component/propulsion/tracks diff --git a/code/modules/research/designs/exosuits/utility.dm b/code/modules/research/designs/exosuits/utility.dm new file mode 100644 index 00000000000..b45f206b4fc --- /dev/null +++ b/code/modules/research/designs/exosuits/utility.dm @@ -0,0 +1,31 @@ + +/datum/design/item/mechfab/trueexo/equip/utility + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2) + +/datum/design/item/mechfab/trueexo/equip/utility/drill + name = "Exosuit Drill" + id = "exosuit_tool_drill" + req_tech = list(TECH_MATERIAL = 1) + materials = list(MAT_STEEL = 7500) + build_path = /obj/item/mech_equipment/drill + +/datum/design/item/mechfab/trueexo/equip/utility/gravipult + name = "Exosuit Gravipult" + id = "exosuit_tool_gravipult" + req_tech = list(TECH_MATERIAL = 4, TECH_BLUESPACE = 3, TECH_ENGINEERING = 4) + materials = list(MAT_STEEL = 7500, MAT_PHORON = 4000) + build_path = /obj/item/mech_equipment/catapult + +/datum/design/item/mechfab/trueexo/equip/utility/light + name = "Exosuit Floodlight" + id = "exosuit_tool_light" + req_tech = list(TECH_MATERIAL = 1) + materials = list(MAT_ALUMINIUM = 7000, MAT_GLASS = 2000) + build_path = /obj/item/mech_equipment/light + +/datum/design/item/mechfab/trueexo/equip/utility/clamp + name = "Exosuit Clamp" + id = "exosuit_tool_clamp" + req_tech = list(TECH_MATERIAL = 1) + materials = list(MAT_STEEL = 7500) + build_path = /obj/item/mech_equipment/clamp diff --git a/code/modules/research/designs/exosuits/weapon_melee.dm b/code/modules/research/designs/exosuits/weapon_melee.dm new file mode 100644 index 00000000000..7e07cfd8dab --- /dev/null +++ b/code/modules/research/designs/exosuits/weapon_melee.dm @@ -0,0 +1,22 @@ + +/datum/design/item/mechfab/trueexo/equip/melee + req_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 2) + +/datum/design/item/mechfab/trueexo/equip/melee/mechete + name = "Exosuit Machete" + id = "exosuit_weapon_mechete" + materials = list(MAT_STEEL = 8000, MAT_ALUMINIUM = 3000) + build_path = /obj/item/mech_equipment/mounted_system/melee/mechete + +/datum/design/item/mechfab/trueexo/equip/melee/mechete/durasteel + name = "Durasteel Exosuit Machete" + id = "exosuit_weapon_durasteel_mechete" + materials = list(MAT_TITANIUM = 5000, MAT_DURASTEEL = 6000) + build_path = /obj/item/mech_equipment/mounted_system/melee/mechete/durasteel + +/datum/design/item/mechfab/trueexo/equip/melee/flash + name = "Exosuit H.I. Flash" + id = "exosuit_weapon_flash" + materials = list(MAT_STEEL = 5000) + build_path = /obj/item/mech_equipment/flash + req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 3, TECH_MAGNET = 4) diff --git a/code/modules/research/designs/exosuits/weapon_ranged.dm b/code/modules/research/designs/exosuits/weapon_ranged.dm new file mode 100644 index 00000000000..583f33eb3fe --- /dev/null +++ b/code/modules/research/designs/exosuits/weapon_ranged.dm @@ -0,0 +1,63 @@ + +/datum/design/item/mechfab/trueexo/equip/gun + req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 3) + +/datum/design/item/mechfab/trueexo/equip/gun/smg + name = "Exosuit SMG" + id = "exosuit_weapon_smg" + materials = list(MAT_STEEL = 5000) + build_path = /obj/item/mech_equipment/mounted_system/projectile + +/datum/design/item/mechfab/trueexo/equip/gun/smg_ammo + name = "Exosuit SMG Ammunition" + id = "exosuit_weapon_smg_ammo" + materials = list(MAT_STEEL = 4000, MAT_PLASTIC = 500, MAT_GRAPHITE = 500) + build_path = /obj/item/ammo_magazine/mech/smg_top + +/datum/design/item/mechfab/trueexo/equip/gun/rifle + name = "Exosuit Rifle" + id = "exosuit_weapon_rifle" + materials = list(MAT_STEEL = 7000, MAT_GRAPHITE = 1000) + req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 4, TECH_MAGNET = 2) + build_path = /obj/item/mech_equipment/mounted_system/projectile/assault_rifle + +/datum/design/item/mechfab/trueexo/equip/gun/rifle_ammo + name = "Exosuit Rifle Ammunition" + id = "exosuit_weapon_rifle_ammo" + materials = list(MAT_STEEL = 4000, MAT_PLASTIC = 500, MAT_GRAPHITE = 500) + req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 4, TECH_MAGNET = 2) + build_path = /obj/item/ammo_magazine/mech/rifle + +/datum/design/item/mechfab/trueexo/equip/gun/lmg + name = "Exosuit LMG" + id = "exosuit_weapon_lmg" + materials = list(MAT_STEEL = 7000, MAT_GRAPHITE = 1000) + req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ILLEGAL = 2, TECH_MAGNET = 2) + build_path = /obj/item/mech_equipment/mounted_system/projectile/machine + +/datum/design/item/mechfab/trueexo/equip/gun/lmg_ammo + name = "Exosuit LMG Ammunition" + id = "exosuit_weapon_lmg_ammo" + materials = list(MAT_PLASTEEL = 4000, MAT_PLASTIC = 500, MAT_GRAPHITE = 500) + req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ILLEGAL = 2, TECH_MAGNET = 2) + build_path = /obj/item/ammo_magazine/mech/rifle/drum + +/datum/design/item/mechfab/trueexo/equip/gun/taser + name = "Exosuit Taser Carbine" + id = "exosuit_weapon_taser" + materials = list(MAT_ALUMINIUM = 5000, MAT_PLASTIC = 2000) + build_path = /obj/item/mech_equipment/mounted_system/taser + +/datum/design/item/mechfab/trueexo/equip/gun/ion + name = "Exosuit Ion Rifle" + id = "exosuit_weapon_ion" + materials = list(MAT_ALUMINIUM = 5000, MAT_OSMIUM = 1000, MAT_URANIUM = 1000) + req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 4, TECH_MAGNET = 4) + build_path = /obj/item/mech_equipment/mounted_system/taser/ion + +/datum/design/item/mechfab/trueexo/equip/gun/laser + name = "Exosuit Laser Rifle" + id = "exosuit_weapon_laser" + materials = list(MAT_ALUMINIUM = 5000, MAT_STEEL = 2000) + req_tech = list(TECH_MATERIAL = 4, TECH_COMBAT = 3) + build_path = /obj/item/mech_equipment/mounted_system/taser/laser diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm index 1c6007ab5f8..32ac13cc117 100644 --- a/code/modules/research/mechfab_designs.dm +++ b/code/modules/research/mechfab_designs.dm @@ -292,16 +292,16 @@ /datum/design/item/mecha build_type = MECHFAB - category = "Exosuit Equipment" + category = "Mecha Equipment" time = 10 materials = list(MAT_STEEL = 7500) /datum/design/item/mecha/AssembleDesignDesc() if(!desc) - desc = "Allows for the construction of \a '[item_name]' exosuit module." + desc = "Allows for the construction of \a '[item_name]' mecha module." /datum/design/item/mecha/tracking - name = "Exosuit Tracking Beacon" + name = "Mecha Tracking Beacon" id = "mech_tracker" time = 5 materials = list(MAT_STEEL = 375) @@ -364,7 +364,7 @@ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/taser/rigged /datum/design/item/mecha/shocker - name = "Exosuit Electrifier" + name = "Mecha Electrifier" desc = "A device to electrify the external portions of a mecha in order to increase its defensive capabilities." id = "mech_shocker" req_tech = list(TECH_COMBAT = 3, TECH_POWER = 6, TECH_MAGNET = 1) @@ -409,7 +409,7 @@ /datum/design/item/mecha/weapon/laser_rigged name = "Jury-Rigged Welder-Laser" - desc = "Allows for the construction of a welder-laser assembly package for non-combat exosuits." + desc = "Allows for the construction of a welder-laser assembly package for non-combat mechas." id = "mech_laser_rigged" req_tech = list(TECH_COMBAT = 2, TECH_MAGNET = 2) build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/riggedlaser @@ -528,28 +528,28 @@ // *** Nonweapon modules /datum/design/item/mecha/wormhole_gen name = "Wormhole Generator" - desc = "An exosuit module that can generate small quasi-stable wormholes." + desc = "A mecha module that can generate small quasi-stable wormholes." id = "mech_wormhole_gen" req_tech = list(TECH_BLUESPACE = 3, TECH_MAGNET = 2) build_path = /obj/item/mecha_parts/mecha_equipment/wormhole_generator /datum/design/item/mecha/teleporter name = "Teleporter" - desc = "An exosuit module that allows teleportation to any position in view." + desc = "An mecha module that allows teleportation to any position in view." id = "mech_teleporter" req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5) build_path = /obj/item/mecha_parts/mecha_equipment/teleporter /datum/design/item/mecha/teleporter name = "Cloaking Device" - desc = "A device that renders the exosuit invisible to the naked eye, though not to thermal detection. Uses large amounts of energy." + desc = "A device that renders the mecha invisible to the naked eye, though not to thermal detection. Uses large amounts of energy." id = "mech_cloaking" req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5) build_path = /obj/item/mecha_parts/mecha_equipment/cloak /datum/design/item/mecha/rcd name = "RCD" - desc = "An exosuit-mounted rapid construction device." + desc = "An mecha-mounted rapid construction device." id = "mech_rcd" time = 120 materials = list(MAT_STEEL = 20000, MAT_PLASTIC = 10000, MAT_PHORON = 18750, MAT_SILVER = 15000, MAT_GOLD = 15000) @@ -558,14 +558,14 @@ /datum/design/item/mecha/gravcatapult name = "Gravitational Catapult" - desc = "An exosuit-mounted gravitational catapult." + desc = "An mecha-mounted gravitational catapult." id = "mech_gravcatapult" req_tech = list(TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_ENGINEERING = 3) build_path = /obj/item/mecha_parts/mecha_equipment/gravcatapult /datum/design/item/mecha/repair_droid name = "Repair Droid" - desc = "Automated repair droid, exosuits' best companion. BEEP BOOP" + desc = "Automated repair droid, mechas' best companion. BEEP BOOP" id = "mech_repair_droid" req_tech = list(TECH_MAGNET = 3, TECH_DATA = 3, TECH_ENGINEERING = 3) materials = list(MAT_STEEL = 7500, MAT_GOLD = 750, MAT_SILVER = 1500, MAT_GLASS = 3750) @@ -582,7 +582,7 @@ /datum/design/item/mecha/omni_shield name = "Omni Shield" - desc = "Integral shield projector. Can only protect the exosuit, but has no weak angles." + desc = "Integral shield projector. Can only protect the mecha, but has no weak angles." id = "mech_shield_omni" req_tech = list(TECH_PHORON = 3, TECH_MAGNET = 6, TECH_ILLEGAL = 4) materials = list(MAT_STEEL = 8000, MAT_GOLD = 2000, MAT_SILVER = 3000, MAT_PHORON = 5000, MAT_GLASS = 3750) @@ -645,7 +645,7 @@ /datum/design/item/mecha/ccw_armor name = "CCW Armor Booster" - desc = "Exosuit close-combat armor booster." + desc = "Mecha close-combat armor booster." id = "mech_ccw_armor" req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4) materials = list(MAT_STEEL = 11250, MAT_SILVER = 3750) @@ -653,7 +653,7 @@ /datum/design/item/mecha/proj_armor name = "Ranged Armor Booster" - desc = "Exosuit projectile armor booster." + desc = "Mecha projectile armor booster." id = "mech_proj_armor" req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ENGINEERING = 3) materials = list(MAT_STEEL = 15000, MAT_GOLD = 3750) @@ -661,7 +661,7 @@ /datum/design/item/mecha/diamond_drill name = "Diamond Drill" - desc = "A diamond version of the exosuit drill. It's harder, better, faster, stronger." + desc = "A diamond version of the mecha drill. It's harder, better, faster, stronger." id = "mech_diamond_drill" req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3) materials = list(MAT_STEEL = 7500, MAT_DIAMOND = 4875) @@ -692,8 +692,8 @@ build_path = /obj/item/mecha_parts/mecha_equipment/tool/orescanner/advanced /datum/design/item/mecha/runningboard - name = "Powered Exosuit Running Board" - desc = "A running board with a power-lifter attachment, to quickly catapult exosuit pilots into the cockpit. Only fits to working exosuits." + name = "Powered Mecha Running Board" + desc = "A running board with a power-lifter attachment, to quickly catapult mecha pilots into the cockpit. Only fits to working mechas." id = "mech_runningboard" req_tech = list(TECH_MATERIAL = 6) materials = list(MAT_STEEL = 10000) @@ -757,7 +757,7 @@ /datum/design/item/mecha/generator_nuclear name = "Nuclear Reactor" - desc = "Exosuit-held nuclear reactor. Converts uranium and everyone's health to energy." + desc = "Mecha-held nuclear reactor. Converts uranium and everyone's health to energy." id = "mech_generator_nuclear" req_tech = list(TECH_POWER= 3, TECH_ENGINEERING = 3, TECH_MATERIAL = 3) materials = list(MAT_STEEL = 7500, MAT_SILVER = 375, MAT_GLASS = 750) @@ -1075,150 +1075,150 @@ req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 5, TECH_PHORON = 3, TECH_MAGNET = 4, TECH_POWER = 6) materials = list(MAT_STEEL = 10000, MAT_GLASS = 6000, MAT_SILVER = 4000) -// Exosuit Internals +// Mecha Internals -/datum/design/item/mechfab/exointernal - category = "Exosuit Internals" +/datum/design/item/mechfab/mechainternal + category = "Mecha Internals" time = 30 req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3) -/datum/design/item/mechfab/exointernal/stan_armor +/datum/design/item/mechfab/mechainternal/stan_armor name = "Armor Plate (Standard)" - category = "Exosuit Internals" - id = "exo_int_armor_standard" + category = "Mecha Internals" + id = "mecha_int_armor_standard" req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) materials = list(MAT_STEEL = 10000) build_path = /obj/item/mecha_parts/component/armor -/datum/design/item/mechfab/exointernal/light_armor +/datum/design/item/mechfab/mechainternal/light_armor name = "Armor Plate (Lightweight)" - category = "Exosuit Internals" - id = "exo_int_armor_lightweight" + category = "Mecha Internals" + id = "mecha_int_armor_lightweight" req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 3) materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 3000) build_path = /obj/item/mecha_parts/component/armor/lightweight -/datum/design/item/mechfab/exointernal/reinf_armor +/datum/design/item/mechfab/mechainternal/reinf_armor name = "Armor Plate (Reinforced)" - category = "Exosuit Internals" - id = "exo_int_armor_reinforced" + category = "Mecha Internals" + id = "mecha_int_armor_reinforced" req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4) materials = list(MAT_STEEL = 20000, MAT_PLASTEEL = 10000) build_path = /obj/item/mecha_parts/component/armor/reinforced -/datum/design/item/mechfab/exointernal/mining_armor +/datum/design/item/mechfab/mechainternal/mining_armor name = "Armor Plate (Blast)" - category = "Exosuit Internals" - id = "exo_int_armor_blast" + category = "Mecha Internals" + id = "mecha_int_armor_blast" req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4) materials = list(MAT_STEEL = 20000, MAT_PLASTEEL = 10000) build_path = /obj/item/mecha_parts/component/armor/mining -/datum/design/item/mechfab/exointernal/gygax_armor +/datum/design/item/mechfab/mechainternal/gygax_armor name = "Armor Plate (Marshal)" - category = "Exosuit Internals" - id = "exo_int_armor_gygax" + category = "Mecha Internals" + id = "mecha_int_armor_gygax" req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_COMBAT = 2) materials = list(MAT_STEEL = 40000, MAT_DIAMOND = 8000) build_path = /obj/item/mecha_parts/component/armor/marshal -/datum/design/item/mechfab/exointernal/darkgygax_armor +/datum/design/item/mechfab/mechainternal/darkgygax_armor name = "Armor Plate (Blackops)" - category = "Exosuit Internals" - id = "exo_int_armor_dgygax" + category = "Mecha Internals" + id = "mecha_int_armor_dgygax" req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_COMBAT = 4, TECH_ILLEGAL = 2) materials = list(MAT_PLASTEEL = 20000, MAT_DIAMOND = 10000, MAT_GRAPHITE = 20000) build_path = /obj/item/mecha_parts/component/armor/marshal/reinforced -/datum/design/item/mechfab/exointernal/durand_armour +/datum/design/item/mechfab/mechainternal/durand_armour name = "Armor Plate (Military)" - id = "exo_int_armor_durand" + id = "mecha_int_armor_durand" req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_COMBAT = 2) materials = list(MAT_STEEL = 40000, MAT_PLASTEEL = 9525, MAT_URANIUM = 8000) build_path = /obj/item/mecha_parts/component/armor/military -/datum/design/item/mechfab/exointernal/marauder_armour +/datum/design/item/mechfab/mechainternal/marauder_armour name = "Armor Plate (Cutting Edge)" - id = "exo_int_armor_marauder" + id = "mecha_int_armor_marauder" req_tech = list(TECH_MATERIAL = 8, TECH_ENGINEERING = 7, TECH_COMBAT = 6, TECH_ILLEGAL = 4) materials = list(MAT_DURASTEEL = 40000, MAT_GRAPHITE = 8000, MAT_OSMIUM = 8000) build_path = /obj/item/mecha_parts/component/armor/military/marauder -/datum/design/item/mechfab/exointernal/phazon_armour +/datum/design/item/mechfab/mechainternal/phazon_armour name = "Armor Plate (Janus)" - id = "exo_int_armor_phazon" + id = "mecha_int_armor_phazon" req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 6, TECH_COMBAT = 6, TECH_ILLEGAL = 4) materials = list(MAT_MORPHIUM = 40000, MAT_DURASTEEL = 8000, MAT_OSMIUM = 8000) build_path = /obj/item/mecha_parts/component/armor/alien -/datum/design/item/mechfab/exointernal/stan_hull +/datum/design/item/mechfab/mechainternal/stan_hull name = "Hull (Standard)" - category = "Exosuit Internals" - id = "exo_int_hull_standard" + category = "Mecha Internals" + id = "mecha_int_hull_standard" req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) materials = list(MAT_STEEL = 10000) build_path = /obj/item/mecha_parts/component/hull -/datum/design/item/mechfab/exointernal/durable_hull +/datum/design/item/mechfab/mechainternal/durable_hull name = "Hull (Durable)" - category = "Exosuit Internals" - id = "exo_int_hull_durable" + category = "Mecha Internals" + id = "mecha_int_hull_durable" req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) materials = list(MAT_STEEL = 8000, MAT_PLASTEEL = 5000) build_path = /obj/item/mecha_parts/component/hull/durable -/datum/design/item/mechfab/exointernal/light_hull +/datum/design/item/mechfab/mechainternal/light_hull name = "Hull (Lightweight)" - category = "Exosuit Internals" - id = "exo_int_hull_light" + category = "Mecha Internals" + id = "mecha_int_hull_light" req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 4) materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 3000) build_path = /obj/item/mecha_parts/component/hull/lightweight -/datum/design/item/mechfab/exointernal/stan_gas +/datum/design/item/mechfab/mechainternal/stan_gas name = "Life-Support (Standard)" - category = "Exosuit Internals" - id = "exo_int_lifesup_standard" + category = "Mecha Internals" + id = "mecha_int_lifesup_standard" req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) materials = list(MAT_STEEL = 10000) build_path = /obj/item/mecha_parts/component/gas -/datum/design/item/mechfab/exointernal/reinf_gas +/datum/design/item/mechfab/mechainternal/reinf_gas name = "Life-Support (Reinforced)" - category = "Exosuit Internals" - id = "exo_int_lifesup_reinforced" + category = "Mecha Internals" + id = "mecha_int_lifesup_reinforced" req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 4) materials = list(MAT_STEEL = 8000, MAT_PLASTEEL = 8000, MAT_GRAPHITE = 1000) build_path = /obj/item/mecha_parts/component/gas/reinforced -/datum/design/item/mechfab/exointernal/stan_electric +/datum/design/item/mechfab/mechainternal/stan_electric name = "Electrical Harness (Standard)" - category = "Exosuit Internals" - id = "exo_int_electric_standard" + category = "Mecha Internals" + id = "mecha_int_electric_standard" req_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2) materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 1000) build_path = /obj/item/mecha_parts/component/electrical -/datum/design/item/mechfab/exointernal/efficient_electric +/datum/design/item/mechfab/mechainternal/efficient_electric name = "Electrical Harness (High)" - category = "Exosuit Internals" - id = "exo_int_electric_efficient" + category = "Mecha Internals" + id = "mecha_int_electric_efficient" req_tech = list(TECH_POWER = 4, TECH_ENGINEERING = 4, TECH_DATA = 2) materials = list(MAT_STEEL = 5000, MAT_PLASTIC = 3000, MAT_SILVER = 3000) build_path = /obj/item/mecha_parts/component/electrical/high_current -/datum/design/item/mechfab/exointernal/stan_actuator +/datum/design/item/mechfab/mechainternal/stan_actuator name = "Actuator Lattice (Standard)" - category = "Exosuit Internals" - id = "exo_int_actuator_standard" + category = "Mecha Internals" + id = "mecha_int_actuator_standard" req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) materials = list(MAT_STEEL = 10000) build_path = /obj/item/mecha_parts/component/actuator -/datum/design/item/mechfab/exointernal/hispeed_actuator +/datum/design/item/mechfab/mechainternal/hispeed_actuator name = "Actuator Lattice (Overclocked)" - category = "Exosuit Internals" - id = "exo_int_actuator_overclock" + category = "Mecha Internals" + id = "mecha_int_actuator_overclock" req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_POWER = 4) materials = list(MAT_PLASTEEL = 10000, MAT_OSMIUM = 3000, MAT_GOLD = 5000) build_path = /obj/item/mecha_parts/component/actuator/hispeed diff --git a/icons/mob/mecha/backup.dmi b/icons/mob/mecha/backup.dmi new file mode 100644 index 00000000000..a499acda172 Binary files /dev/null and b/icons/mob/mecha/backup.dmi differ diff --git a/icons/mob/mecha/mech_bay.dmi b/icons/mob/mecha/mech_bay.dmi new file mode 100644 index 00000000000..e46f668f0d0 Binary files /dev/null and b/icons/mob/mecha/mech_bay.dmi differ diff --git a/icons/mob/mecha/mech_decals.dmi b/icons/mob/mecha/mech_decals.dmi new file mode 100644 index 00000000000..a7c85588703 Binary files /dev/null and b/icons/mob/mecha/mech_decals.dmi differ diff --git a/icons/mob/mecha/mech_equipment.dmi b/icons/mob/mecha/mech_equipment.dmi new file mode 100644 index 00000000000..8ddc0a6b493 Binary files /dev/null and b/icons/mob/mecha/mech_equipment.dmi differ diff --git a/icons/mob/mecha/mech_fab.dmi b/icons/mob/mecha/mech_fab.dmi new file mode 100644 index 00000000000..3a2ab9e4880 Binary files /dev/null and b/icons/mob/mecha/mech_fab.dmi differ diff --git a/icons/mob/mecha/mech_hud.dmi b/icons/mob/mecha/mech_hud.dmi new file mode 100644 index 00000000000..9755c384712 Binary files /dev/null and b/icons/mob/mecha/mech_hud.dmi differ diff --git a/icons/mob/mecha/mech_part_items.dmi b/icons/mob/mecha/mech_part_items.dmi new file mode 100644 index 00000000000..b6072b7ca02 Binary files /dev/null and b/icons/mob/mecha/mech_part_items.dmi differ diff --git a/icons/mob/mecha/mech_parts.dmi b/icons/mob/mecha/mech_parts.dmi new file mode 100644 index 00000000000..59c49387a1e Binary files /dev/null and b/icons/mob/mecha/mech_parts.dmi differ diff --git a/icons/mob/mecha/mech_parts_held.dmi b/icons/mob/mecha/mech_parts_held.dmi new file mode 100644 index 00000000000..35a8f763ce7 Binary files /dev/null and b/icons/mob/mecha/mech_parts_held.dmi differ diff --git a/icons/mob/mecha/mech_weapon_overlays.dmi b/icons/mob/mecha/mech_weapon_overlays.dmi new file mode 100644 index 00000000000..448593ae336 Binary files /dev/null and b/icons/mob/mecha/mech_weapon_overlays.dmi differ diff --git a/icons/mob/mecha/shield.dmi b/icons/mob/mecha/shield.dmi new file mode 100644 index 00000000000..c4c41e9180b Binary files /dev/null and b/icons/mob/mecha/shield.dmi differ diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi index b662e0e71e0..da2ce69903f 100644 Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ diff --git a/polaris.dme b/polaris.dme index 7632f1ce87b..4f317be6a1e 100644 --- a/polaris.dme +++ b/polaris.dme @@ -131,6 +131,7 @@ #include "code\_onclick\cyborg.dm" #include "code\_onclick\drag_drop.dm" #include "code\_onclick\item_attack.dm" +#include "code\_onclick\MouseDrag.dm" #include "code\_onclick\observer.dm" #include "code\_onclick\other_mobs.dm" #include "code\_onclick\rig.dm" @@ -1886,6 +1887,42 @@ #include "code\modules\examine\descriptions\telecomms.dm" #include "code\modules\examine\descriptions\turfs.dm" #include "code\modules\examine\descriptions\weapons.dm" +#include "code\modules\exosuits\_exosuit_setup.dm" +#include "code\modules\exosuits\exosuit.dm" +#include "code\modules\exosuits\exosuit_construction.dm" +#include "code\modules\exosuits\exosuit_damage.dm" +#include "code\modules\exosuits\exosuit_damage_immunity.dm" +#include "code\modules\exosuits\exosuit_icon.dm" +#include "code\modules\exosuits\exosuit_interaction.dm" +#include "code\modules\exosuits\exosuit_life.dm" +#include "code\modules\exosuits\exosuit_movement.dm" +#include "code\modules\exosuits\exosuit_wreckage.dm" +#include "code\modules\exosuits\component\_components.dm" +#include "code\modules\exosuits\component\armour.dm" +#include "code\modules\exosuits\component\arms.dm" +#include "code\modules\exosuits\component\body.dm" +#include "code\modules\exosuits\component\frame.dm" +#include "code\modules\exosuits\component\head.dm" +#include "code\modules\exosuits\component\legs.dm" +#include "code\modules\exosuits\component\software.dm" +#include "code\modules\exosuits\equipment\_equipment.dm" +#include "code\modules\exosuits\equipment\combat.dm" +#include "code\modules\exosuits\equipment\combat_projectile.dm" +#include "code\modules\exosuits\equipment\engineering.dm" +#include "code\modules\exosuits\equipment\medical.dm" +#include "code\modules\exosuits\equipment\utility.dm" +#include "code\modules\exosuits\interface\_interface.dm" +#include "code\modules\exosuits\interface\screen_objects.dm" +#include "code\modules\exosuits\premade\_premade.dm" +#include "code\modules\exosuits\premade\combat.dm" +#include "code\modules\exosuits\premade\exploration.dm" +#include "code\modules\exosuits\premade\heavy.dm" +#include "code\modules\exosuits\premade\industrial.dm" +#include "code\modules\exosuits\premade\light.dm" +#include "code\modules\exosuits\premade\mercenary.dm" +#include "code\modules\exosuits\premade\modern.dm" +#include "code\modules\exosuits\premade\powerloader.dm" +#include "code\modules\exosuits\premade\sleek.dm" #include "code\modules\ext_scripts\irc.dm" #include "code\modules\ext_scripts\python.dm" #include "code\modules\fishing\fishing.dm" @@ -2694,6 +2731,7 @@ #include "code\modules\nano\interaction\conscious.dm" #include "code\modules\nano\interaction\contained.dm" #include "code\modules\nano\interaction\default.dm" +#include "code\modules\nano\interaction\exosuit.dm" #include "code\modules\nano\interaction\interactive.dm" #include "code\modules\nano\interaction\inventory.dm" #include "code\modules\nano\interaction\inventory_deep.dm" @@ -3063,6 +3101,24 @@ #include "code\modules\research\designs\circuits\ai_modules.dm" #include "code\modules\research\designs\circuits\circuits.dm" #include "code\modules\research\designs\circuits\disks.dm" +#include "code\modules\research\designs\exosuits\categories.dm" +#include "code\modules\research\designs\exosuits\combat.dm" +#include "code\modules\research\designs\exosuits\components.dm" +#include "code\modules\research\designs\exosuits\engineering.dm" +#include "code\modules\research\designs\exosuits\heavy.dm" +#include "code\modules\research\designs\exosuits\industrial.dm" +#include "code\modules\research\designs\exosuits\light.dm" +#include "code\modules\research\designs\exosuits\medical.dm" +#include "code\modules\research\designs\exosuits\mercenary.dm" +#include "code\modules\research\designs\exosuits\modern.dm" +#include "code\modules\research\designs\exosuits\pod.dm" +#include "code\modules\research\designs\exosuits\powerloader.dm" +#include "code\modules\research\designs\exosuits\sleek.dm" +#include "code\modules\research\designs\exosuits\spider.dm" +#include "code\modules\research\designs\exosuits\tracks.dm" +#include "code\modules\research\designs\exosuits\utility.dm" +#include "code\modules\research\designs\exosuits\weapon_melee.dm" +#include "code\modules\research\designs\exosuits\weapon_ranged.dm" #include "code\modules\security levels\keycard authentication.dm" #include "code\modules\security levels\security levels.dm" #include "code\modules\shieldgen\directional_shield.dm"