#define ALLOWED_MOVEMENT_RADIUS 25
/mob/abstract/eye/base_planner
/// The location we're spawned at, and not allowed to leave too far from.
var/turf/spawn_point
/// A list of selectable ghost objects, an assoc list.
var/list/object_selection_list = list(
"Girder" = /obj/structure/blueprint/girder,
"Window frame" = /obj/structure/blueprint/window_frame,
"Airlock frame" = /obj/structure/blueprint/airlock_frame,
"Turf plating" = /obj/structure/blueprint/turf_plating
)
/// Chosen ghost object to be placed at the target turf.
var/selected_object
/// List of room template datums.
var/list/room_template_choices = list()
/// Chosen room template.
var/selected_room_template
/// On what z-levels the blueprints can be used to modify or create areas.
var/list/valid_z_levels = list()
/// Used for notifying eye user about their movement is restricted.
var/warning_time
/mob/abstract/eye/base_planner/Initialize()
. = ..()
addtimer(CALLBACK(src, PROC_REF(get_spawn_point)), 0.1 SECONDS)
/mob/abstract/eye/base_planner/proc/get_spawn_point()
spawn_point = get_turf(src)
/mob/abstract/eye/base_planner/check_allowed_movement(turf/step)
if(get_dist(step, spawn_point) > ALLOWED_MOVEMENT_RADIUS)
if(world.time > warning_time + 2 SECONDS)
warning_time = world.time
to_chat(owner, SPAN_WARNING("You can't see beyond this point from your current location!"))
return FALSE
return TRUE
/mob/abstract/eye/base_planner/Destroy()
spawn_point = null
return ..()
/mob/abstract/eye/base_planner/can_ztravel(var/direction)
return FALSE // needed to avoid some exploits and efficiency in move restrictions
/mob/abstract/eye/base_planner/proc/choose_blueprint_object()
var/choice = tgui_input_list(usr, "Choose an object to place.", "Object Selection", object_selection_list)
if(!choice)
return
selected_object = object_selection_list[choice]
/mob/abstract/eye/base_planner/release(var/mob/user)
if(owner && owner.client && user == owner)
owner.client.images.Cut()
. = ..()
/mob/abstract/eye/base_planner/ClickOn(atom/A, params)
params = params2list(params)
var/turf/T = get_turf(A)
if(!canClick())
return
if(params["left"])
put_selected_object(T)
if(params["ctrl"])
remove_object(T)
if(params["middle"])
copy_object(T)
/mob/abstract/eye/base_planner/proc/put_selected_object(var/turf/selected_turf)
if(!selected_object)
to_chat(owner, SPAN_WARNING("You need to select an object first!"))
return
if(selected_turf.density)
to_chat(owner, SPAN_WARNING("This place isn't suitable for placing blueprints!"))
return
new selected_object(selected_turf)
/mob/abstract/eye/base_planner/proc/remove_object(var/turf/selected_turf)
var/found_object = locate(/obj/structure/blueprint) in selected_turf
if(!found_object)
to_chat(owner, SPAN_WARNING("There is nothing to remove here."))
return
qdel(found_object)
/mob/abstract/eye/base_planner/proc/copy_object(var/turf/selected_turf)
var/obj/structure/blueprint/found_object = locate(/obj/structure/blueprint) in selected_turf
if(!found_object)
to_chat(owner, SPAN_WARNING("There is nothing to copy here."))
return
selected_object = found_object.type
to_chat(owner, SPAN_NOTICE("\The [found_object] has been copied."))
/mob/abstract/eye/base_planner/proc/place_template()
var/datum/room_template/base_planner_template/ER = new()
ER.template_selection(get_turf(src))
/mob/abstract/eye/base_planner/proc/erase_all()
if(tgui_alert(owner, "This will erase EVERY blueprint objects in your near vicinity, do you wish to proceed?", "Erase All", list("Confirm", "Cancel")) != "Confirm")
to_chat(owner, SPAN_NOTICE("You decided not to erase everything."))
return
for(var/obj/structure/blueprint/B in range(20, src))
qdel(B)
to_chat(owner, SPAN_NOTICE("You successfully erased nearby blueprint objects."))
/mob/abstract/eye/base_planner/additional_sight_flags()
return SEE_TURFS|SEE_OBJS|BLIND
/mob/abstract/eye/base_planner/apply_visual(mob/living/M)
M.overlay_fullscreen("blueprints", /atom/movable/screen/fullscreen/blueprints/less_alpha)
M.add_client_color(/datum/client_color/monochrome)
/mob/abstract/eye/base_planner/remove_visual(mob/living/M)
M.clear_fullscreen("blueprints", 0)
M.remove_client_color(/datum/client_color/monochrome)
// ---- Blueprint Objects
ABSTRACT_TYPE(/obj/structure/blueprint)
desc = "A construction plan component. Someone is going to finish this, eventually."
icon = 'icons/obj/item/base_planning_blueprints.dmi'
density = FALSE
anchored = TRUE
alpha = 200
color = "#54809C"
/// Object path that this ghost object represents.
var/obj/target_object_path
/// Turf path that this ghost object represents.
var/turf/target_turf_path
/// Target atom name, incase we can't get the name for some reason.
var/target_atom_name = "blueprint"
/// Path for required material to build `target_object_path`. /obj/item/stack/material/steel/
var/obj/item/stack/required_material
/// Required amount of required material.
var/required_amount
/// Required time to complete this blueprint.
var/build_time = 3 SECONDS
/// Whether someone is currently building blueprint this or not.
var/currently_being_built = FALSE
/// A list of type paths that this object will ignore when it's looking to delete incompatible blueprint objects in its turf.
var/list/whitelisted_types = list()
/obj/structure/blueprint/Initialize()
. = ..()
if(!target_object_path && !target_turf_path)
return INITIALIZE_HINT_QDEL
name = target_object_path ? "[target_object_path.name] blueprint" : "[target_turf_path.name] blueprint"
remove_incompatible_types()
AddOverlays("in_progress")
/obj/structure/blueprint/mechanics_hints()
. += ..()
. += "You can click this with [required_amount] amounts of [required_material.name] to build [target_object_path?.name || target_atom_name]!"
. += "You can click this with an empty hand on harm intent to remove it."
/obj/structure/blueprint/proc/remove_incompatible_types()
var/turf/T = get_turf(src)
for(var/obj/structure/blueprint/colliding_atom in T)
if(colliding_atom == src || is_type_in_list(colliding_atom, whitelisted_types))
continue
qdel(colliding_atom)
/obj/structure/blueprint/attackby(obj/item/attacking_item, mob/user)
. = ..()
if(!istype(attacking_item, required_material) || currently_being_built)
return
var/obj/item/stack/material/mat_stack = attacking_item
var/current_amount = mat_stack.get_amount()
if(current_amount < required_amount)
to_chat(user, SPAN_WARNING("You need [required_amount - current_amount] more \the [mat_stack] to complete this!"))
return
currently_being_built = TRUE
if(!do_after(user, build_time) || !mat_stack.use(required_amount))
currently_being_built = FALSE
return
var/turf/T = get_turf(src)
if(target_object_path)
var/obj/solid_object = new target_object_path(get_turf(src))
solid_object.dir = src.dir
else
T.ChangeTurf(target_turf_path)
playsound(src, 'sound/items/Deconstruct.ogg', 80, 1)
for(var/obj/structure/flora/F in T)
qdel(F)
qdel(src) // blueprint served its purpose, now it can embrace the oblivion
/obj/structure/blueprint/attack_hand(mob/user)
. = ..()
if(user.a_intent == I_HURT)
user.visible_message(SPAN_NOTICE("[user] removes \the [src]."), SPAN_NOTICE("You remove \the [src]."))
qdel(src)
/obj/structure/blueprint/girder
icon_state = "girder"
color = COLOR_BLUE_GRAY
target_object_path = /obj/structure/girder
required_material = /obj/item/stack/material/steel
required_amount = 2
/obj/structure/blueprint/window_frame
icon_state = "window_frame"
target_object_path = /obj/structure/window_frame
required_material = /obj/item/stack/material/steel
required_amount = 4
/obj/structure/blueprint/airlock_frame
icon_state = "airlock_frame"
target_object_path = /obj/structure/door_assembly/door_assembly_generic
required_material = /obj/item/stack/material/steel
required_amount = 4
whitelisted_types = list(
/obj/structure/blueprint/turf_plating
)
/obj/structure/blueprint/turf_plating
icon_state = "plating"
layer = BELOW_OBJ_LAYER
target_turf_path = /turf/simulated/floor/plating
target_atom_name = "plating"
required_material = /obj/item/stack/tile/floor
required_amount = 1
build_time = 0.1 SECONDS
whitelisted_types = list(
/obj/structure/blueprint/airlock_frame
)
#undef ALLOWED_MOVEMENT_RADIUS