/obj/machinery/smithing/kinetic_assembler name = "kinetic assembler" desc = "A smart assembler that takes components and combines them at the strike of a hammer." icon = 'icons/obj/machines/smithing_machines.dmi' icon_state = "assembler" max_integrity = 100 pixel_y = 0 bound_height = 32 bound_width = 32 bound_y = 0 operation_sound = 'sound/items/welder.ogg' /// Primary component var/obj/item/smithed_item/component/primary /// Secondary component var/obj/item/smithed_item/component/secondary /// Trim component var/obj/item/smithed_item/component/trim /// Finished product var/obj/item/finished_product /// Products that are produced in batches var/list/batched_item_types = list( /obj/item/smithed_item/lens, /obj/item/smithed_item/tool_bit ) /// Amount of extra items made in batches var/batch_extras = 1 /// Products that can not be affected by productivity bits var/list/no_productivity_types = list( /obj/item/kitchen/knife/smithed, ) /obj/machinery/smithing/kinetic_assembler/Initialize(mapload) . = ..() // Stock parts component_parts = list() component_parts += new /obj/item/circuitboard/kinetic_assembler(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stock_parts/micro_laser(null) component_parts += new /obj/item/stack/sheet/glass(null) batched_item_types = typecacheof(batched_item_types) no_productivity_types = typecacheof(no_productivity_types) RefreshParts() /obj/machinery/smithing/kinetic_assembler/Destroy() if(primary) primary.forceMove(src.loc) if(secondary) secondary.forceMove(src.loc) if(trim) trim.forceMove(src.loc) . = ..() /obj/machinery/smithing/kinetic_assembler/examine(mob/user) . = ..() if(primary || secondary || trim) . += SPAN_NOTICE("You can activate the machine with your hand, or remove a component by alt-clicking.") if(primary) . += SPAN_NOTICE("There is a [primary] in the primary slot.") if(secondary) . += SPAN_NOTICE("There is a [secondary] in the secondary slot.") if(trim) . += SPAN_NOTICE("There is a [trim] in the trim slot.") if(finished_product) . += SPAN_NOTICE("There is a nearly-complete [finished_product] on the assembler. To complete the product, strike it with your hammer!") /obj/machinery/smithing/kinetic_assembler/RefreshParts() var/operation_mult = 0 for(var/obj/item/stock_parts/component in component_parts) operation_mult += OPERATION_SPEED_MULT_PER_RATING * component.rating // Update our values operation_time = operation_time = max(ROUND_UP(initial(operation_time) * (1.3 - operation_mult)), 2) /obj/machinery/smithing/kinetic_assembler/update_icon_state() . = ..() if(panel_open) icon_state = "assembler_wires" else icon_state = "assembler" /obj/machinery/smithing/kinetic_assembler/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/I) . = ..() update_icon(UPDATE_ICON_STATE) /obj/machinery/smithing/kinetic_assembler/update_overlays() . = ..() overlays.Cut() if(panel_open) icon_state = "assembler_wires" /obj/machinery/smithing/kinetic_assembler/AltClick(mob/living/user) if(!Adjacent(user)) return if(!primary && !secondary && !trim) to_chat(user, SPAN_WARNING("There is no component to remove.")) return var/list/components = list("Primary", "Secondary", "Trim") var/removed = tgui_input_list(user, "Select a component to remove", src, components) if(!removed) return switch(removed) if("Primary") if(!primary) to_chat(user, SPAN_WARNING("There is no primary component to remove.")) return to_chat(user, SPAN_NOTICE("You remove [primary] from the primary component slot of [src].")) if(primary.burn_check(user)) primary.burn_user(user) primary.forceMove(user.loc) primary = null return user.put_in_hands(primary) primary = null return if("Secondary") if(!secondary) to_chat(user, SPAN_WARNING("There is no secondary component to remove.")) return to_chat(user, SPAN_NOTICE("You remove [secondary] from the secondary component slot of [src].")) if(secondary.burn_check(user)) secondary.burn_user(user) secondary.forceMove(user.loc) secondary = null return user.put_in_hands(secondary) secondary = null return if("Trim") if(!trim) to_chat(user, SPAN_WARNING("There is no trim component to remove.")) return to_chat(user, SPAN_NOTICE("You remove [trim] from the trim component slot of [src].")) if(trim.burn_check(user)) trim.burn_user(user) trim.forceMove(user.loc) trim = null return user.put_in_hands(trim) trim = null return /obj/machinery/smithing/kinetic_assembler/item_interaction(mob/living/user, obj/item/used, list/modifiers) if(istype(used, /obj/item/kitchen/utensil/fork)) return NONE if(istype(used, /obj/item/storage/part_replacer)) return ..() if(operating) to_chat(user, SPAN_WARNING("[src] is still operating!")) return ITEM_INTERACT_COMPLETE if(istype(used, /obj/item/hammer)) return ITEM_INTERACT_COMPLETE if(!istype(used, /obj/item/smithed_item/component)) to_chat(user, SPAN_WARNING("You feel like there's no reason to process [used].")) return ITEM_INTERACT_COMPLETE if(finished_product) to_chat(user, SPAN_WARNING("There is an almost finished [finished_product] in [src]!")) return ITEM_INTERACT_COMPLETE var/obj/item/smithed_item/component/comp = used if(comp.hammer_time) to_chat(user, SPAN_WARNING("[used] is not complete yet!")) return ITEM_INTERACT_COMPLETE if(comp.part_type == PART_PRIMARY) if(primary) to_chat(user, SPAN_NOTICE("You remove [primary] from the primary component slot of [src].")) primary.forceMove(src.loc) primary = null if(comp.flags & NODROP || !user.transfer_item_to(used, src)) to_chat(user, SPAN_WARNING("[comp] is stuck to your hand!")) return ITEM_INTERACT_COMPLETE to_chat(user, SPAN_NOTICE("You insert [comp] into the primary component slot of [src].")) primary = comp return ITEM_INTERACT_COMPLETE if(comp.part_type == PART_SECONDARY) if(secondary) to_chat(user, SPAN_NOTICE("You remove [secondary] from the secondary component slot of [src].")) secondary.forceMove(src.loc) secondary = null if(comp.flags & NODROP || !user.transfer_item_to(used, src)) to_chat(user, SPAN_WARNING("[comp] is stuck to your hand!")) return ITEM_INTERACT_COMPLETE to_chat(user, SPAN_NOTICE("You insert [comp] into the secondary component slot of [src].")) secondary = comp return ITEM_INTERACT_COMPLETE if(comp.part_type == PART_TRIM) if(trim) to_chat(user, SPAN_NOTICE("You remove [trim] from the trim component slot of [src].")) trim.forceMove(src.loc) trim = null if(comp.flags & NODROP || !user.transfer_item_to(used, src)) to_chat(user, SPAN_WARNING("[comp] is stuck to your hand!")) return ITEM_INTERACT_COMPLETE to_chat(user, SPAN_NOTICE("You insert [comp] into the trim component slot of [src].")) trim = comp return ITEM_INTERACT_COMPLETE /obj/machinery/smithing/kinetic_assembler/attack_hand(mob/user) if(!allowed(user) && !isobserver(user)) to_chat(user, SPAN_WARNING("Access denied.")) return FINISH_ATTACK if(finished_product) to_chat(user, SPAN_WARNING("[src] has a nearly-complete product!")) return FINISH_ATTACK if(!primary) to_chat(user, SPAN_WARNING("[src] lacks a primary component!")) return FINISH_ATTACK if(!secondary) to_chat(user, SPAN_WARNING("[src] lacks a secondary component!")) return FINISH_ATTACK if(!trim) to_chat(user, SPAN_WARNING("[src] lacks a trim component!")) return FINISH_ATTACK if(primary.finished_product != secondary.finished_product) to_chat(user, SPAN_WARNING("[primary] does not match [secondary]!")) return FINISH_ATTACK operate(operation_time, user) return FINISH_ATTACK /obj/machinery/smithing/kinetic_assembler/operate(loops, mob/living/user) ..() finished_product = new primary.finished_product(src) var/quality_list = list(primary.quality, secondary.quality, trim.quality) var/datum/smith_quality/lowest = quality_list[1] for(var/datum/smith_quality/quality in quality_list) if(quality.stat_mult < lowest.stat_mult) lowest = quality if(istype(finished_product, /obj/item/smithed_item)) var/obj/item/smithed_item/product = finished_product product.quality = lowest product.material = trim.material product.set_stats() product.update_appearance(UPDATE_NAME) finished_product = product else if(istype(finished_product, /obj/item/kitchen/knife/smithed)) // We need to separate these because of their different paths var/obj/item/kitchen/knife/smithed/product = finished_product product.quality = lowest product.material = trim.material product.set_stats() product.update_appearance(UPDATE_NAME) product.update_icon(UPDATE_OVERLAYS) finished_product = product qdel(primary) qdel(secondary) qdel(trim) primary = null secondary = null trim = null /obj/machinery/smithing/kinetic_assembler/hammer_act(mob/user, obj/item/i) if(operating) to_chat(user, SPAN_WARNING("[src] is still operating!")) return if(!finished_product) to_chat(user, SPAN_WARNING("There is no finished product ready!")) return playsound(src, 'sound/magic/fellowship_armory.ogg', 50, TRUE) finished_product.forceMove(src.loc) SSblackbox.record_feedback("tally", "smith_assembler_production", 1, "[finished_product.type]") // Modify based on productivity var/total_extras = clamp(round(1 * i.bit_productivity_mod / 2), 0, 2) if(is_type_in_typecache(finished_product, batched_item_types)) total_extras += clamp(round(batch_extras * i.bit_productivity_mod / 2), 1, 4) if(is_type_in_typecache(finished_product, no_productivity_types)) total_extras = 0 for(var/iterator in 1 to total_extras) var/obj/item/product if(istype(finished_product, /obj/item/smithed_item)) var/obj/item/smithed_item/current_product = finished_product var/obj/item/smithed_item/extra_product = new finished_product.type(src.loc) extra_product.quality = current_product.quality extra_product.material = current_product.material extra_product.set_stats() product = extra_product else if(istype(finished_product, /obj/item/kitchen/knife/smithed)) // We need to separate these because of their different paths var/obj/item/kitchen/knife/smithed/current_product = finished_product var/obj/item/kitchen/knife/smithed/extra_product = new finished_product.type(src.loc) extra_product.quality = current_product.quality extra_product.material = current_product.material extra_product.set_stats() product = extra_product else if(istype(finished_product, /obj/item/nuclear_rod)) product = new finished_product.type(get_turf(src)) product.update_appearance(UPDATE_NAME) product.scatter_atom() finished_product = null // MARK: Scientific Assembler /obj/machinery/smithing/scientific_assembler name = "scientific assembler" desc = "A smart assembler that takes slime cores, energy cells, and energy gun parts to produce energy guns." icon = 'icons/obj/machines/smithing_machines.dmi' icon_state = "assembler" max_integrity = 100 pixel_y = 0 bound_height = 32 bound_width = 32 bound_y = 0 operation_sound = 'sound/items/welder.ogg' req_one_access = list(ACCESS_TOX, ACCESS_XENOBIOLOGY, ACCESS_SMITH) /// Slime extract for the egun var/obj/item/slime_extract/slime_core /// The gun frame var/obj/item/smithed_item/component/egun_parts/parts /// The battery var/obj/item/stock_parts/cell/cell /obj/machinery/smithing/scientific_assembler/Initialize(mapload) . = ..() // Stock parts component_parts = list() component_parts += new /obj/item/circuitboard/scientific_assembler(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stock_parts/micro_laser(null) component_parts += new /obj/item/stack/sheet/glass(null) RefreshParts() /obj/machinery/smithing/scientific_assembler/examine(mob/user) . = ..() if(slime_core || parts || cell) . += SPAN_NOTICE("You can activate the machine with your hand, or remove a component by alt-clicking.") if(slime_core) . += SPAN_NOTICE("There is a [slime_core] in the core slot.") if(parts) . += SPAN_NOTICE("There is a [parts] in the gun frame slot.") if(cell) . += SPAN_NOTICE("There is a [cell] in the power cell slot.") /obj/machinery/smithing/scientific_assembler/RefreshParts() var/operation_mult = 0 for(var/obj/item/stock_parts/component in component_parts) operation_mult += OPERATION_SPEED_MULT_PER_RATING * component.rating // Update our values operation_time = operation_time = max(ROUND_UP(initial(operation_time) * (1.3 - operation_mult)), 2) /obj/machinery/smithing/scientific_assembler/update_icon_state() . = ..() if(panel_open) icon_state = "assembler_wires" else icon_state = "assembler" /obj/machinery/smithing/scientific_assembler/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/I) . = ..() update_icon(UPDATE_ICON_STATE) /obj/machinery/smithing/scientific_assembler/update_overlays() . = ..() overlays.Cut() if(panel_open) icon_state = "assembler_wires" /obj/machinery/smithing/scientific_assembler/item_interaction(mob/living/user, obj/item/used, list/modifiers) if(istype(used, /obj/item/kitchen/utensil/fork)) return NONE if(istype(used, /obj/item/storage/part_replacer)) return ..() if(operating) to_chat(user, SPAN_WARNING("[src] is still operating!")) return ITEM_INTERACT_COMPLETE if(istype(used, /obj/item/slime_extract)) var/obj/item/slime_extract/core = used if(!core.associated_gun_type) to_chat(user, SPAN_NOTICE("[core] is not capable of producing an energy gun!")) return ITEM_INTERACT_COMPLETE if(slime_core) to_chat(user, SPAN_NOTICE("You remove [slime_core] from the core component slot of [src].")) slime_core.forceMove(src.loc) slime_core = null if(used.flags & NODROP || !user.transfer_item_to(used, src)) to_chat(user, SPAN_WARNING("[used] is stuck to your hand!")) return ITEM_INTERACT_COMPLETE to_chat(user, SPAN_NOTICE("You insert [used] into the core component slot of [src].")) slime_core = used return ITEM_INTERACT_COMPLETE if(istype(used, /obj/item/smithed_item/component/egun_parts)) if(parts) to_chat(user, SPAN_NOTICE("You remove [parts] from the gun parts component slot of [src].")) parts.forceMove(src.loc) parts = null if(used.flags & NODROP || !user.transfer_item_to(used, src)) to_chat(user, SPAN_WARNING("[used] is stuck to your hand!")) return ITEM_INTERACT_COMPLETE var/obj/item/smithed_item/component/egun_parts/new_parts = used if(new_parts.hammer_time) to_chat(user, SPAN_WARNING("[new_parts] is not complete yet!")) return ITEM_INTERACT_COMPLETE to_chat(user, SPAN_NOTICE("You insert [used] into the gun parts component slot of [src].")) parts = used return ITEM_INTERACT_COMPLETE if(istype(used, /obj/item/stock_parts/cell)) if(cell) to_chat(user, SPAN_NOTICE("You remove [cell] from the power cell component slot of [src].")) cell.forceMove(src.loc) cell = null if(used.flags & NODROP || !user.transfer_item_to(used, src)) to_chat(user, SPAN_WARNING("[used] is stuck to your hand!")) return ITEM_INTERACT_COMPLETE to_chat(user, SPAN_NOTICE("You insert [used] into the power cell component slot of [src].")) cell = used return ITEM_INTERACT_COMPLETE to_chat(user, SPAN_WARNING("You feel like there's no reason to process [used].")) return ITEM_INTERACT_COMPLETE /obj/machinery/smithing/scientific_assembler/attack_hand(mob/user) if(!allowed(user) && !isobserver(user)) to_chat(user, SPAN_WARNING("Access denied.")) return FINISH_ATTACK if(!slime_core) to_chat(user, SPAN_WARNING("[src] lacks a slime core!")) return FINISH_ATTACK if(!parts) to_chat(user, SPAN_WARNING("[src] lacks an energy gun frame!")) return FINISH_ATTACK if(!cell) to_chat(user, SPAN_WARNING("[src] lacks a power cell!")) return FINISH_ATTACK operate(operation_time, user) return FINISH_ATTACK /obj/machinery/smithing/scientific_assembler/operate(loops, mob/living/user) ..() var/obj/item/gun/energy/finished_product = new slime_core.associated_gun_type(src) finished_product.forceMove(src.loc) qdel(slime_core) qdel(parts) qdel(cell) slime_core = null parts = null cell = null