Files
Bubberstation/code/game/machinery/constructable_frame.dm
T
SyncIt21 bb6aee565f Bluespace poly crystals can be used to complete machine frames (#75692)
## About The Pull Request
Fixes #47650

Either by hand or by RPED you can now use blue space poly crystal to
finish a machine. If you are using a RPED then it will attempt to look
for singular bluespace crystals first and only if it cannot find it will
it then look for the bluespace poly crystals

Also removed some redundant types from the RPED allowed bluespace types
list cause they were not necessary

## Changelog
🆑
fix: bluespace poly crystals can be used to complete machine frames
either by hand or by RPED
/🆑
2023-05-31 14:31:03 -04:00

439 lines
16 KiB
Plaintext

/obj/structure/frame
name = "frame"
desc = "A generic looking construction frame. One day this will be something greater."
icon = 'icons/obj/stock_parts.dmi'
icon_state = "box_0"
density = TRUE
max_integrity = 250
var/obj/item/circuitboard/machine/circuit = null
var/state = 1
/obj/structure/frame/examine(user)
. = ..()
if(circuit)
. += "It has \a [circuit] installed."
/obj/structure/frame/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
new /obj/item/stack/sheet/iron(loc, 5)
if(circuit)
circuit.forceMove(loc)
circuit = null
qdel(src)
/obj/structure/frame/machine
name = "machine frame"
desc = "The standard frame for most station appliances. Its appearance and function is controlled by the inserted board."
var/list/components = null
var/list/req_components = null
var/list/req_component_names = null // user-friendly names of components
/obj/structure/frame/machine/examine(user)
. = ..()
if(state != 3)
return
if(!length(req_components))
. += span_info("It requires no components.")
return .
if(!req_component_names)
stack_trace("[src]'s req_components list has items but its req_component_names list is null!")
return
var/list/nice_list = list()
for(var/component in req_components)
if(!ispath(component))
stack_trace("An item in [src]'s req_components list is not a path!")
continue
if(!req_components[component])
continue
nice_list += list("[req_components[component]] [req_component_names[component]]\s")
. += span_info("It requires [english_list(nice_list, "no more components")].")
/**
* Collates the displayed names of the machine's components
*
* Arguments:
* * specific_parts - If true, the component should not use base name, but a specific tier
*/
/obj/structure/frame/machine/proc/update_namelist(specific_parts)
if(!req_components)
return
req_component_names = list()
for(var/component_path in req_components)
if(!ispath(component_path))
continue
if(ispath(component_path, /obj/item/stack))
var/obj/item/stack/stack_path = component_path
if(initial(stack_path.singular_name))
req_component_names[component_path] = initial(stack_path.singular_name)
else
req_component_names[component_path] = initial(stack_path.name)
else if(ispath(component_path, /datum/stock_part))
var/datum/stock_part/stock_part = component_path
var/obj/item/physical_object_type = initial(stock_part.physical_object_type)
req_component_names[component_path] = initial(physical_object_type.name)
else if(ispath(component_path, /obj/item/stock_parts))
var/obj/item/stock_parts/stock_part = component_path
if(!specific_parts && initial(stock_part.base_name))
req_component_names[component_path] = initial(stock_part.base_name)
else
req_component_names[component_path] = initial(stock_part.name)
else if(ispath(component_path, /obj/item))
var/obj/item/part = component_path
req_component_names[component_path] = initial(part.name)
else
stack_trace("Invalid component part [component_path] in [type], couldn't get its name")
req_component_names[component_path] = "[component_path] (this is a bug)"
/obj/structure/frame/machine/proc/get_req_components_amt()
var/amt = 0
for(var/path in req_components)
amt += req_components[path]
return amt
/**
* install the circuitboard in this frame
* * board - the machine circuitboard to install
* * user - the player
* * by_hand - is the player installing the board by hand or from the RPED. Used to decide how to transfer the board into the frame
*/
/obj/structure/frame/machine/proc/install_board(obj/item/circuitboard/machine/board, mob/user, by_hand)
if(!board.build_path)
to_chat(user, span_warning("This circuitboard seems to be broken."))
return
if(!anchored && board.needs_anchored)
to_chat(user, span_warning("The frame needs to be secured first!"))
return
if(by_hand && !user.transferItemToLoc(board, src))
return
else if(!board.forceMove(src))
return
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE)
to_chat(user, span_notice("You add the circuit board to the frame."))
circuit = board
icon_state = "box_2"
state = 3
components = list()
//add circuit board as the first component to the list of components
//required for part_replacer to locate it while exchanging parts so it does not early return in /obj/machinery/proc/exchange_parts
components += circuit
req_components = board.req_components.Copy()
update_namelist(board.specific_parts)
return TRUE
/obj/structure/frame/machine/attackby(obj/item/P, mob/living/user, params)
switch(state)
if(1)
if(istype(P, /obj/item/circuitboard/machine))
to_chat(user, span_warning("The frame needs wiring first!"))
return
else if(istype(P, /obj/item/circuitboard))
to_chat(user, span_warning("This frame does not accept circuit boards of this type!"))
return
if(istype(P, /obj/item/stack/cable_coil))
if(!P.tool_start_check(user, amount=5))
return
to_chat(user, span_notice("You start to add cables to the frame..."))
if(P.use_tool(src, user, 20, volume=50, amount=5))
to_chat(user, span_notice("You add cables to the frame."))
state = 2
icon_state = "box_1"
return
if(P.tool_behaviour == TOOL_SCREWDRIVER && !anchored)
user.visible_message(span_warning("[user] disassembles the frame."), \
span_notice("You start to disassemble the frame..."), span_hear("You hear banging and clanking."))
if(P.use_tool(src, user, 40, volume=50))
if(state == 1)
to_chat(user, span_notice("You disassemble the frame."))
var/obj/item/stack/sheet/iron/M = new (loc, 5)
if (!QDELETED(M))
M.add_fingerprint(user)
qdel(src)
return
if(P.tool_behaviour == TOOL_WRENCH)
var/turf/ground = get_turf(src)
if(!anchored && ground.is_blocked_turf(exclude_mobs = TRUE, source_atom = src))
to_chat(user, span_notice("You fail to secure [src]."))
return
to_chat(user, span_notice("You start [anchored ? "un" : ""]securing [src]..."))
if(P.use_tool(src, user, 40, volume=75))
if(state == 1)
to_chat(user, span_notice("You [anchored ? "un" : ""]secure [src]."))
set_anchored(!anchored)
return
if(2)
if(P.tool_behaviour == TOOL_WRENCH)
to_chat(user, span_notice("You start [anchored ? "un" : ""]securing [src]..."))
if(P.use_tool(src, user, 40, volume=75))
to_chat(user, span_notice("You [anchored ? "un" : ""]secure [src]."))
set_anchored(!anchored)
return
if(istype(P, /obj/item/storage/part_replacer) && P.contents.len)
var/obj/item/storage/part_replacer/replacer = P
// map of circuitboard names to the board
var/list/circuit_boards = list()
for(var/obj/item/circuitboard/machine/board in replacer.contents)
circuit_boards[board.name] = board
if(!length(circuit_boards))
return
//if there is only one board directly install it else pick from list
var/obj/item/circuitboard/machine/target_board
if(circuit_boards.len == 1)
for(var/board_name in circuit_boards)
target_board = circuit_boards[board_name]
else
var/option = tgui_input_list(user, "Select Circuitboard To Install"," Available Boards", circuit_boards)
target_board = circuit_boards[option]
if(!target_board)
return
//install board
if(install_board(target_board, user, FALSE))
replacer.play_rped_sound()
//attack this frame again with the rped so it can install stock parts since its now in state 3
attackby(replacer, user, params)
return
if(istype(P, /obj/item/circuitboard/machine))
var/obj/item/circuitboard/machine/machine_board = P
install_board(machine_board, user, TRUE)
return
else if(istype(P, /obj/item/circuitboard))
to_chat(user, span_warning("This frame does not accept circuit boards of this type!"))
return
if(P.tool_behaviour == TOOL_WIRECUTTER)
P.play_tool_sound(src)
to_chat(user, span_notice("You remove the cables."))
state = 1
icon_state = "box_0"
new /obj/item/stack/cable_coil(drop_location(), 5)
return
if(3)
if(P.tool_behaviour == TOOL_CROWBAR)
P.play_tool_sound(src)
state = 2
circuit.forceMove(drop_location())
components.Remove(circuit)
//spawn stack components from the circuitboards requested components since they no longer exist inside components
for(var/component in circuit.req_components)
if(!ispath(component, /obj/item/stack))
continue
var/obj/item/stack/stack_path = component
var/stack_amount = circuit.req_components[component] - req_components[component]
if(stack_amount > 0)
new stack_path(drop_location(), stack_amount)
circuit = null
if(components.len == 0)
to_chat(user, span_notice("You remove the circuit board."))
else
to_chat(user, span_notice("You remove the circuit board and other components."))
dump_contents()
desc = initial(desc)
req_components = null
components = null
icon_state = "box_1"
return
if(P.tool_behaviour == TOOL_WRENCH && !circuit.needs_anchored)
to_chat(user, span_notice("You start [anchored ? "un" : ""]securing [src]..."))
if(P.use_tool(src, user, 40, volume=75))
to_chat(user, span_notice("You [anchored ? "un" : ""]secure [src]."))
set_anchored(!anchored)
return
if(P.tool_behaviour == TOOL_SCREWDRIVER)
var/component_check = TRUE
for(var/R in req_components)
if(req_components[R] > 0)
component_check = FALSE
break
if(component_check)
P.play_tool_sound(src)
var/obj/machinery/new_machine = new circuit.build_path(loc)
if(istype(new_machine))
// Machines will init with a set of default components. Move to nullspace so we don't trigger handle_atom_del, then qdel.
// Finally, replace with this frame's parts.
if(new_machine.circuit)
// Move to nullspace and delete.
new_machine.circuit.moveToNullspace()
QDEL_NULL(new_machine.circuit)
for(var/obj/old_part in new_machine.component_parts)
old_part.moveToNullspace()
qdel(old_part)
// Set anchor state
new_machine.set_anchored(anchored)
// Assign the circuit & parts & move them all at once into the machine
// no need to seperatly move circuit board as its already part of the components list
new_machine.circuit = circuit
new_machine.component_parts = components
for (var/obj/new_part in components)
new_part.forceMove(new_machine)
//Inform machine that its finished & cleanup
new_machine.RefreshParts()
new_machine.on_construction(user)
components = null
qdel(src)
return
if(istype(P, /obj/item/storage/part_replacer))
/**
* more efficient return so no if conditions after this are executed.
* Required when the rped is re attacking the frame after installing circuitboard so it returns quickly
*/
if(!P.contents.len || !get_req_components_amt())
return
var/obj/item/storage/part_replacer/replacer = P
var/play_sound = FALSE
var/list/part_list = replacer.get_sorted_parts() //parts sorted in order of tier
for(var/path in req_components)
var/target_path
if(ispath(path, /datum/stock_part))
var/datum/stock_part/datum_part = path
target_path = initial(datum_part.physical_object_base_type)
else
target_path = path
var/obj/item/part
while(req_components[path] > 0 && (part = look_for(part_list, target_path, ispath(path, /obj/item/stack/ore/bluespace_crystal) ? /obj/item/stack/sheet/bluespace_crystal : null)))
part_list -= part
if(istype(part,/obj/item/stack))
var/obj/item/stack/S = part
var/used_amt = min(round(S.get_amount()), req_components[path])
var/stack_name = S.singular_name
if(!used_amt || !S.use(used_amt))
continue
req_components[path] -= used_amt
to_chat(user, span_notice("You add [used_amt] [stack_name] to [src]."))
play_sound = TRUE
else if(replacer.atom_storage.attempt_remove(part, src))
var/stock_part_datum = GLOB.stock_part_datums_per_object[part.type]
if (!isnull(stock_part_datum))
components += stock_part_datum
qdel(part)
else
components += part
part.forceMove(src)
req_components[path]--
to_chat(user, span_notice("You add [part] to [src]."))
play_sound = TRUE
if(play_sound)
replacer.play_rped_sound()
return
for(var/stock_part_base in req_components)
if (req_components[stock_part_base] == 0)
continue
var/stock_part_path
if(ispath(stock_part_base, /obj/item))
stock_part_path = stock_part_base
else if(ispath(stock_part_base, /datum/stock_part))
var/datum/stock_part/stock_part_datum_type = stock_part_base
stock_part_path = initial(stock_part_datum_type.physical_object_type)
else
stack_trace("Bad stock part in req_components: [stock_part_base]")
continue
//if we require an bluespace crystall and we have an full sheet of them we can allow that
if(ispath(stock_part_path, /obj/item/stack/ore/bluespace_crystal) && istype(P, /obj/item/stack/sheet/bluespace_crystal))
//allow it
else if(!istype(P, stock_part_path))
continue
if(isstack(P))
var/obj/item/stack/S = P
var/used_amt = min(round(S.get_amount()), req_components[stock_part_path])
if(used_amt && S.use(used_amt))
req_components[stock_part_path] -= used_amt
to_chat(user, span_notice("You add [P] to [src]."))
return
// We might end up qdel'ing the part if it's a stock part datum.
// In practice, this doesn't have side effects to the name,
// but academically we should not be using an object after it's deleted.
var/part_name = "[P]"
if (ispath(stock_part_base, /datum/stock_part))
// We can't just reuse stock_part_path here or its singleton,
// or else putting in a tier 2 part will deconstruct to a tier 1 part.
var/stock_part_datum = GLOB.stock_part_datums_per_object[P.type]
if (isnull(stock_part_datum))
stack_trace("[P.type] does not have an associated stock part datum!")
continue
components += stock_part_datum
// We regenerate the stock parts on deconstruct.
// This technically means we lose unique qualities of the stock part, but
// it's worth it for how dramatically this simplifies the code.
// The only place I can see it affecting anything is like...RPG qualities. :P
qdel(P)
else if(user.transferItemToLoc(P, src))
components += P
else
break
to_chat(user, span_notice("You add [part_name] to [src]."))
req_components[stock_part_base]--
return TRUE
to_chat(user, span_warning("You cannot add that to the machine!"))
return FALSE
if(user.combat_mode)
return ..()
/// returns instance of path1 in list else path2 in list
/obj/structure/frame/machine/proc/look_for(list/parts, path1, path2 = null)
//look for path1 in list
var/part = locate(path1) in parts
if(!isnull(part))
return part
//optional look for path2 in list
if(!isnull(path2))
part = locate(path2) in parts
return part
/obj/structure/frame/machine/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
if(state >= 2)
new /obj/item/stack/cable_coil(loc , 5)
dump_contents()
..()
/obj/structure/frame/machine/dump_contents()
for (var/component in components)
if (ismovable(component))
var/atom/movable/atom_component = component
atom_component.forceMove(drop_location())
else if (istype(component, /datum/stock_part))
var/datum/stock_part/stock_part_datum = component
var/physical_object_type = initial(stock_part_datum.physical_object_type)
new physical_object_type(drop_location())
else
stack_trace("Invalid component [component] was found in constructable frame")