Files
Bubberstation/code/modules/shuttle/mobile_port/variants/custom/blueprints.dm
die 0204ab8fdd Canreach refactor (#93165)
## About The Pull Request
ports https://github.com/DaedalusDock/daedalusdock/pull/1144
ports https://github.com/DaedalusDock/daedalusdock/pull/1147

full credit to @Kapu1178 for the juice

instead of `reacher.CanReach(target)` we now do
`target.CanBeReachedBy(reacher)`, this allows us to give special
behavior to atoms which we want to reach, which is exactly what I need
for a feature I'm working on.
## Why It's Good For The Game
allows us to be more flexible with reachability
## Changelog
🆑
refactor: refactored how reaching items works, report any oddities with
being unable to reach something you should be able to!
/🆑
2025-10-07 20:28:59 +02:00

783 lines
32 KiB
Plaintext

/obj/effect/client_image_holder/shuttle_construction_visualization
image_icon = 'icons/effects/alphacolors.dmi'
image_state = "transparent"
image_layer = ABOVE_NORMAL_TURF_LAYER
image_plane = ABOVE_GAME_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
persist_without_seers = TRUE
/datum/proximity_monitor/advanced/shuttle_construction_visualizer
edge_is_a_field = TRUE
var/mob/user
var/list/image_holders = list()
var/obj/item/shuttle_blueprints/parent
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/New(atom/_host, range, _ignore_if_not_on_turf)
. = ..()
parent = _host
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/Destroy()
. = ..()
parent = null
QDEL_LIST_ASSOC_VAL(image_holders)
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/set_user(mob/new_user)
if(user)
for(var/turf in image_holders)
var/obj/effect/client_image_holder/holder = image_holders[turf]
holder.remove_seer(user)
UnregisterSignal(user, list(COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT))
var/client/client = user.client
if(client)
UnregisterSignal(client, list(COMSIG_VIEW_SET, COMSIG_CLIENT_SET_EYE))
if(new_user)
user = new_user
for(var/turf in image_holders)
var/obj/effect/client_image_holder/holder = image_holders[turf]
holder.add_seer(user)
RegisterSignal(user, COMSIG_MOB_LOGIN, PROC_REF(on_user_login))
RegisterSignal(user, COMSIG_MOB_LOGOUT, PROC_REF(unregister_client))
var/client/client = user.client
if(client)
register_client(client)
else
unregister_client()
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/register_client(client/client)
var/atom/eye = client.eye
if(eye)
set_host(client.eye)
var/list/view_size = getviewsize(client.view)
set_range(CEILING(max(view_size[1], view_size[2])/2, 1)+1)
RegisterSignal(client, COMSIG_VIEW_SET, PROC_REF(on_view_set))
RegisterSignal(client, COMSIG_CLIENT_SET_EYE, PROC_REF(on_set_eye))
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/unregister_client()
SIGNAL_HANDLER
set_host(parent)
set_range(0)
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/on_user_login(mob/source)
SIGNAL_HANDLER
register_client(source.client)
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/on_view_set(datum/source, new_view)
SIGNAL_HANDLER
var/list/view_size = getviewsize(new_view)
set_range(CEILING(max(view_size[1], view_size[2])/2, 1)+1)
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/on_set_eye(datum/source, atom/old_eye, atom/new_eye)
SIGNAL_HANDLER
set_host(new_eye)
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/setup_field_turf(turf/target)
var/obj/effect/client_image_holder/shuttle_construction_visualization/holder = new(target, list())
image_holders[target] = holder
evaluate_turf_overlay(holder, target)
RegisterSignals(target, list(
COMSIG_TURF_CHANGE,
COMSIG_TURF_AREA_CHANGED,
SIGNAL_ADDTRAIT(TRAIT_SHUTTLE_CONSTRUCTION_TURF),
SIGNAL_REMOVETRAIT(TRAIT_SHUTTLE_CONSTRUCTION_TURF)
), PROC_REF(on_turf_updated))
if(user)
holder.add_seer(user)
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/cleanup_field_turf(turf/target)
qdel(image_holders[target])
image_holders -= target
UnregisterSignal(target, list(
COMSIG_TURF_CHANGE,
COMSIG_TURF_AREA_CHANGED,
SIGNAL_ADDTRAIT(TRAIT_SHUTTLE_CONSTRUCTION_TURF),
SIGNAL_REMOVETRAIT(TRAIT_SHUTTLE_CONSTRUCTION_TURF)
))
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/on_turf_updated(turf/source)
SIGNAL_HANDLER
evaluate_turf_overlay(image_holders[source], source)
/datum/proximity_monitor/advanced/shuttle_construction_visualizer/proc/evaluate_turf_overlay(obj/effect/client_image_holder/holder, turf/target)
var/area/turf_area = target.loc
if(HAS_TRAIT(target, TRAIT_SHUTTLE_CONSTRUCTION_TURF))
holder.image_state = "green"
if(turf_area.allow_shuttle_docking)
if(!GLOB.custom_areas[turf_area] && turf_area.apc)
var/obj/machinery/power/apc/apc = turf_area.apc
var/datum/component/wall_mounted/wallmount_comp = apc.GetComponent(/datum/component/wall_mounted)
var/turf/apc_turf = get_turf(apc)
if(target == apc_turf || target == wallmount_comp.hanging_wall_turf)
holder.image_state = "red"
else
holder.image_state = "red"
else
holder.image_state = "transparent"
if(GLOB.custom_areas[turf_area] && HAS_TRAIT(turf_area, TRAIT_HAS_SHUTTLE_CONSTRUCTION_TURF))
if(turf_area.apc)
var/obj/machinery/power/apc/apc = turf_area.apc
var/datum/component/wall_mounted/wallmount_comp = apc.GetComponent(/datum/component/wall_mounted)
var/turf/apc_turf = get_turf(apc)
if(HAS_TRAIT(apc_turf, TRAIT_SHUTTLE_CONSTRUCTION_TURF) || HAS_TRAIT(wallmount_comp.hanging_wall_turf, TRAIT_SHUTTLE_CONSTRUCTION_TURF))
holder.image_state = "red"
holder.regenerate_image()
/obj/item/shuttle_blueprints
name = "shuttle blueprints"
desc = "A blank sheet of synthetic engineering-grade paper."
icon = 'icons/obj/scrolls.dmi'
icon_state = "shuttle_blueprints0"
base_icon_state = "shuttle_blueprints"
inhand_icon_state = "blueprints"
attack_verb_continuous = list("attacks", "baps", "hits")
attack_verb_simple = list("attack", "bap", "hit")
interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_ALLOW_USER_LOCATION | INTERACT_ATOM_IGNORE_MOBILITY
var/base_desc = "A blank sheet of synthetic engineering-grade paper."
var/linked_desc = "A sheet of synthetic engineering-grade paper with shuttle schematics printed on it."
//A weakref to the mobile docking port of the shuttle these blueprints are linked to, if any.
var/datum/weakref/shuttle_ref
//Whether the holder can visualize shuttle frames (and any turfs preventing them from becoming complete shuttles)
var/visualize_frame_turfs = FALSE
var/offset_x = 0
var/offset_y = 0
var/datum/proximity_monitor/advanced/shuttle_construction_visualizer/prox_monitor
/obj/item/shuttle_blueprints/Initialize(mapload)
. = ..()
prox_monitor = new(src, 0, FALSE)
update_appearance()
/obj/item/shuttle_blueprints/equipped(mob/user, slot, initial)
. = ..()
var/static/list/connections = list(COMSIG_ITEM_PRE_ATTACK = PROC_REF(christen_check))
if(slot == ITEM_SLOT_HANDS)
AddComponent(/datum/component/connect_inventory, user, connections, allowed_slots = ITEM_SLOT_HANDS)
/obj/item/shuttle_blueprints/dropped(mob/user, silent)
. = ..()
stop_visualizing(user)
qdel(GetComponent(/datum/component/connect_inventory))
/obj/item/shuttle_blueprints/proc/christen_check(obj/item/reagent_containers/cup/glass/bottle/source, atom/attacked, mob/living/user)
SIGNAL_HANDLER
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!istype(shuttle))
return
if(!iswallturf(attacked))
return
if(!istype(source))
return
if(SSshuttle.get_containing_shuttle(attacked) != shuttle)
return
if(HAS_TRAIT_FROM(user, TRAIT_ATTEMPTING_CHRISTENING, REF(shuttle)))
return
. = COMPONENT_CANCEL_ATTACK_CHAIN
if(shuttle.master_blueprint?.resolve() != src)
to_chat(user, span_warning("Only the master blueprint for \the [shuttle] grants you the right to rechristen [shuttle.p_them()]!"))
return
var/turf/user_turf = get_turf(user)
if(user_turf && isshuttleturf(user_turf))
to_chat(user, span_warning("You can't rechristen \the [shuttle] from inside of [shuttle.p_them()]!"))
return
if(!source.isGlass)
to_chat(user, span_warning("You can't break [source] against [attacked]!"))
return
if(source.reagents.total_volume < CHEMICAL_QUANTISATION_LEVEL)
to_chat(user, span_warning("You should put some christening fluid in [source]!"))
return
INVOKE_ASYNC(src, PROC_REF(christen), user, shuttle, attacked, user.active_hand_index)
/obj/item/shuttle_blueprints/proc/christen(mob/living/user, obj/docking_port/mobile/custom/shuttle, atom/attacked, hand)
var/trait_source = REF(shuttle)
ADD_TRAIT(user, TRAIT_ATTEMPTING_CHRISTENING, trait_source)
var/new_name = reject_bad_name(tgui_input_text(user, "What would you like to rechristen \the [shuttle] as?", "Shuttle Rechristening", max_length = 128), allow_numbers = TRUE, strict = TRUE, cap_after_symbols = FALSE)
if(QDELETED(user))
return
REMOVE_TRAIT(user, TRAIT_ATTEMPTING_CHRISTENING, trait_source)
if(!new_name)
user.balloon_alert(user, "cancelled")
return
new_name = apply_text_macros(new_name)
var/obj/item/hitting_implement = (locate(/obj/item/reagent_containers/cup/glass/bottle) in user.held_items) || user.get_item_for_held_index(hand)
if(!attacked.IsReachableBy(user, hitting_implement.reach))
user.balloon_alert(user, "out of range!")
return
var/obj/item/reagent_containers/cup/glass/bottle/bottle = hitting_implement
bottle = istype(bottle) && bottle.isGlass && bottle
var/shuttle_exists = !QDELETED(shuttle)
if(!shuttle_exists)
to_chat(user, span_warning("Wasn't there supposed to be a shuttle here?"))
var/has_blueprints = !QDELETED(src) && (src in user.gather_belongings())
var/turf/attacked_turf = attacked
var/is_closed_turf = isclosedturf(attacked_turf)
var/is_shuttle_turf = isshuttleturf(attacked_turf)
var/off_shuttle = SSshuttle.get_containing_shuttle(get_turf(user)) != shuttle
var/has_bottle = istype(bottle)
var/bottle_empty = bottle && bottle.reagents.total_volume < CHEMICAL_QUANTISATION_LEVEL
var/is_clumsy = HAS_TRAIT(user, TRAIT_CLUMSY)
var/clumsy_check = is_clumsy && prob(25)
if(!(has_blueprints && is_closed_turf && is_shuttle_turf && off_shuttle && has_bottle && !clumsy_check && !bottle_empty))
var/msg = "[user] tries to christen the shuttle with [hitting_implement ? "[hitting_implement.get_examine_name()]" : "[user.p_their()] empty fist"]"
var/self_msg = "You try to christen the shuttle with [hitting_implement ? "[hitting_implement.get_examine_name()]" : "your empty fist"]"
if(!is_closed_turf || clumsy_check)
msg += ", but miss[user.p_es()], hitting [user.p_themselves()] instead"
self_msg += ", but miss, hitting yourself instead"
msg += "!"
self_msg += "!"
if(is_clumsy)
msg += " What a klutz!"
var/old_combat_mode = user.combat_mode
user.set_combat_mode(TRUE)
// Stupid code to bypass pacifism checks, since unintentional hits like this one shouldn't be blocked by pacifism
var/list/pacifism_sources = GET_TRAIT_SOURCES(user, TRAIT_PACIFISM)
REMOVE_TRAIT(user, TRAIT_PACIFISM, pacifism_sources)
if(hitting_implement)
hitting_implement.attack(user, user)
bottle?.smash(user, user) // Because smashing a bottle isn't part of its main melee attack chain
else
user.resolve_unarmed_attack(user)
if(length(pacifism_sources))
ADD_TRAIT(user, TRAIT_PACIFISM, pacifism_sources)
user.set_combat_mode(old_combat_mode)
else
if(!shuttle_exists)
msg += ", but there was no shuttle to christen!"
self_msg += ", but there was no shuttle to christen!"
else if(!off_shuttle)
msg += ", but you're suppossed to do that <i>outside</i> of [shuttle.p_them()]!"
self_msg += ", but you're suppossed to do that <i>outside</i> of [shuttle.p_them()]!"
else if(!is_shuttle_turf)
msg += ", but miss[user.p_es()], hitting [attacked] instead!"
self_msg += ", but miss, hitting [attacked] instead!"
else if(!has_bottle)
if(hitting_implement)
msg += " - which isn't a glass bottle!"
self_msg += " - which isn't a glass bottle!"
else
msg += "! That's not how you're supposed to do it!"
self_msg += "! Aren't you supposed to use a glass bottle?"
else if(bottle_empty)
msg += ", but there's no christening fluid in [bottle]!"
self_msg += ", but there's no christening fluid in [bottle]!"
else if(!has_blueprints)
msg += ", but [user.p_have()] no right to do so!"
self_msg += ", but have no right to do so!"
user.do_attack_animation(attacked, used_item = hitting_implement)
if(hitting_implement && !bottle)
if(hitting_implement.force)
playsound(hitting_implement, 'sound/items/weapons/smash.ogg', hitting_implement.get_clamped_volume(), TRUE, hitting_implement.stealthy_audio ? SILENCED_SOUND_EXTRARANGE : -1, falloff_distance = 0)
else
playsound(hitting_implement, 'sound/items/weapons/tap.ogg', hitting_implement.get_clamped_volume(), TRUE, -1)
else if(bottle)
bottle.smash(attacked, user)
else
playsound(user, 'sound/effects/bang.ogg', 50, TRUE)
user.visible_message(span_warning(msg), span_warning(self_msg))
else
user.visible_message(
span_notice("[user] christens the shuttle as <b>\the [new_name]</b> with [hitting_implement.get_examine_name()]\
[(bottle.reagents.total_volume < 30) ? "" : ", though the dearth of christening fluid makes for an unimpressive display"]."),
span_notice("You christen the shuttle as <b>\the [new_name]</b> with [hitting_implement.get_examine_name()].")
)
user.do_attack_animation(attacked, used_item = bottle)
bottle.smash(attacked, user)
shuttle.name = new_name
rename_area(shuttle.default_area, new_name)
/obj/item/shuttle_blueprints/proc/start_visualizing(mob/user)
visualize_frame_turfs = TRUE
RegisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_USER_SCOPED), PROC_REF(stop_visualizing))
prox_monitor.set_user(user)
prox_monitor.recalculate_field()
/obj/item/shuttle_blueprints/proc/stop_visualizing(mob/user)
SIGNAL_HANDLER
visualize_frame_turfs = FALSE
UnregisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_USER_SCOPED))
prox_monitor.set_user(null)
/obj/item/shuttle_blueprints/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ShuttleBlueprints", name)
ui.open()
RegisterSignal(user, COMSIG_ENTER_AREA, PROC_REF(on_user_enter_area))
/obj/item/shuttle_blueprints/ui_close(mob/user)
. = ..()
UnregisterSignal(user, COMSIG_ENTER_AREA)
/obj/item/shuttle_blueprints/proc/on_user_enter_area(mob/source, area/new_area)
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(shuttle && shuttle.default_area == new_area)
update_static_data(source)
/obj/item/shuttle_blueprints/ui_state(mob/user)
return GLOB.hands_state
/obj/item/shuttle_blueprints/ui_static_data(mob/user)
var/list/data = list()
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(get_area(user) == shuttle?.default_area && length(shuttle?.shuttle_areas) > 1)
var/list/neighboring_areas = list()
var/room_turfs = detect_room(get_turf(user), max_size = CONFIG_GET(number/max_shuttle_size), extra_check = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(custom_shuttle_room_check), shuttle, neighboring_areas))
if(room_turfs)
data["apcInMergeRegion"] = shuttle.default_area.apc && room_turfs[get_turf(shuttle.default_area.apc)]
var/list/area_refs = list()
for(var/area/neighboring_area in neighboring_areas)
area_refs[REF(neighboring_area)] = neighboring_area.name
data["neighboringAreas"] = area_refs
return data
/obj/item/shuttle_blueprints/ui_data(mob/user)
var/data = list()
var/turf/current_turf = get_turf(user)
var/obj/docking_port/mobile/custom/linked_shuttle = shuttle_ref?.resolve()
var/linked_to_shuttle = istype(linked_shuttle)
var/on_shuttle_frame = HAS_TRAIT(current_turf, TRAIT_SHUTTLE_CONSTRUCTION_TURF)
var/datum/shuttle_frame/frame = GLOB.shuttle_frames_by_turf[current_turf]
data["linkedShuttle"] = linked_to_shuttle && shuttle_ref.reference
data["visualizing"] = visualize_frame_turfs
data["onShuttleFrame"] = on_shuttle_frame
if(on_shuttle_frame)
data["maxShuttleSize"] = CONFIG_GET(number/max_shuttle_size)
if(!linked_to_shuttle)
var/obj/docking_port/mobile/custom/loc_shuttle = SSshuttle.get_containing_shuttle(current_turf)
data["tooManyShuttles"] = length(SSshuttle.custom_shuttles) >= CONFIG_GET(number/max_shuttle_count)
var/on_custom_shuttle = istype(loc_shuttle)
data["onCustomShuttle"] = on_custom_shuttle
if(on_custom_shuttle)
data["masterExists"] = loc_shuttle?.master_blueprint?.resolve()
else if(on_shuttle_frame)
data["size"] = length(frame.turfs) - length(frame.shuttle_covered_turfs)
data["problems"] = shuttle_build_check(current_turf)
else
var/obj/item/shuttle_blueprints/master = linked_shuttle.master_blueprint?.resolve()
data["masterExists"] = master
data["isMaster"] = master == src
var/area/current_area = get_area(current_turf)
var/area/default_area = linked_shuttle.default_area
data["onShuttle"] = linked_shuttle.shuttle_areas[current_area]
data["inDefaultArea"] = default_area == current_area
data["currentArea"] = list(name = current_area.name, ref = REF(current_area))
data["defaultApc"] = !!default_area.apc
var/list/apcs = list()
for(var/area/area as anything in linked_shuttle.shuttle_areas - default_area)
apcs[REF(area)] = !!area.apc
data["apcs"] = apcs
data["idle"] = linked_shuttle.mode == SHUTTLE_IDLE
if(on_shuttle_frame)
data["size"] = length(frame.turfs) - length(frame.shuttle_covered_turfs) + linked_shuttle.turf_count
data["problems"] = shuttle_expand_check(current_turf, linked_shuttle)
return data
/obj/item/shuttle_blueprints/proc/link_to_shuttle(obj/docking_port/mobile/custom/shuttle, is_master = FALSE)
shuttle_ref = WEAKREF(shuttle)
if(is_master)
shuttle.master_blueprint = WEAKREF(src)
RegisterSignal(shuttle, COMSIG_QDELETING, PROC_REF(on_shuttle_deleted))
update_appearance()
/obj/item/shuttle_blueprints/proc/on_shuttle_deleted()
SIGNAL_HANDLER
unlink(removing = TRUE)
/obj/item/shuttle_blueprints/proc/unlink(removing = FALSE)
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref.resolve()
if(!QDELETED(shuttle))
UnregisterSignal(shuttle, COMSIG_QDELETING)
shuttle_ref = null
update_appearance()
/obj/item/shuttle_blueprints/update_name(updates)
. = ..()
var/obj/docking_port/mobile/shuttle = shuttle_ref?.resolve()
if(shuttle)
name = get_linked_name(shuttle)
else
name = initial(name)
/obj/item/shuttle_blueprints/proc/get_linked_name(obj/docking_port/mobile/shuttle)
return "\improper [shuttle.name] blueprints"
/obj/item/shuttle_blueprints/update_desc(updates)
. = ..()
desc = shuttle_ref?.resolve() ? linked_desc : base_desc
/obj/item/shuttle_blueprints/update_icon_state()
. = ..()
icon_state = "[base_icon_state][!!shuttle_ref]"
/obj/item/shuttle_blueprints/vv_edit_var(vname, vval)
. = ..()
if(!.)
return
if(vname == NAMEOF(src, desc))
if(shuttle_ref?.resolve())
linked_desc = vval
else
base_desc = vval
update_desc()
/obj/item/shuttle_blueprints/examine(mob/user)
. = ..()
. += get_shuttle_tip()
/obj/item/shuttle_blueprints/proc/get_shuttle_tip()
. = list()
if(!shuttle_ref)
. += span_notice("It can be used to construct a custom shuttle.")
return
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref.resolve()
if(!shuttle)
. += span_notice("It has the plans for a shuttle that no longer exists. It can be reused to construct a new shuttle.")
else
. += span_notice("It has the plans for \the [shuttle] on it, and can be used to expand [shuttle.p_them()] or modify [shuttle.p_their()] areas.")
if(shuttle.master_blueprint.resolve() == src)
. += span_notice("This is the master blueprint for \the [shuttle]. You can copy it to a blank set of blueprints, or to an engineering cyborg with a shuttle database module installed.")
/obj/item/shuttle_blueprints/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
. = ..()
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!istype(shuttle))
return
if(shuttle.master_blueprint?.resolve() != src)
return
if(istype(interacting_with, /obj/item/shuttle_blueprints))
var/obj/item/shuttle_blueprints/other_blueprints = interacting_with
var/obj/docking_port/mobile/other_shuttle = other_blueprints.shuttle_ref?.resolve()
if(istype(other_shuttle))
return
balloon_alert(user, "copying blueprints...")
if(!do_after(user, 5 SECONDS, other_blueprints))
balloon_alert(user, "interrupted!")
return ITEM_INTERACT_FAILURE
other_blueprints.link_to_shuttle(shuttle)
balloon_alert(user, "copied")
return ITEM_INTERACT_SUCCESS
if(istype(interacting_with, /mob/living/silicon/robot))
var/mob/living/silicon/robot/borg = interacting_with
var/obj/item/shuttle_blueprints/borg/other_blueprints = (locate() in borg.model.modules) || (locate() in borg.held_items)
if(!other_blueprints)
return
if(other_blueprints.shuttles.Find(shuttle_ref))
balloon_alert(user, "already has these blueprints!")
balloon_alert(user, "copying blueprints...")
if(!do_after(user, 5 SECONDS, borg))
balloon_alert(user, "interrupted")
return ITEM_INTERACT_FAILURE
if(QDELETED(other_blueprints))
return ITEM_INTERACT_FAILURE
other_blueprints.shuttles |= shuttle_ref
balloon_alert(user, "copied")
return ITEM_INTERACT_SUCCESS
/obj/item/shuttle_blueprints/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("toggleVisualization")
if(visualize_frame_turfs)
stop_visualizing(usr)
else
start_visualizing(usr)
return TRUE
if("tryBuildShuttle")
var/shuttle_dir = params["dir"]
if(!(shuttle_dir in GLOB.cardinals))
return TRUE
var/list/shuttle_turfs = list()
var/list/shuttle_areas = list()
var/turf/shuttle_origin = get_turf(usr)
var/check_status = shuttle_build_check(shuttle_origin, shuttle_turfs, shuttle_areas)
if(check_status & ORIGIN_NOT_ON_SHUTTLE)
balloon_alert(usr, "not on shuttle frame!")
return TRUE
if(check_status & TOO_MANY_SHUTTLES)
balloon_alert(usr, "too many shuttles exist!")
return TRUE
if(check_status & ABOVE_MAX_SHUTTLE_SIZE)
balloon_alert(usr, "frame too big!")
return TRUE
if(check_status & CUSTOM_AREA_NOT_COMPLETELY_CONTAINED)
balloon_alert(usr, "frame must completely enclose custom areas!")
return TRUE
if(check_status & INTERSECTS_NON_WHITELISTED_AREA)
balloon_alert(usr, "frame overlaps disallowed areas!")
return TRUE
if(check_status & CONTAINS_APC_OF_NON_CUSTOM_AREA)
balloon_alert(usr, "frame contains apc of non-custom area!")
return TRUE
var/obj/docking_port/mobile/custom/shuttle = create_shuttle(
usr,
shuttle_origin,
shuttle_turfs,
shuttle_areas,
shuttle_dir,
name = "\improper Unnamed Shuttle",
id = "custom_[length(SSshuttle.custom_shuttles)+1]"
)
link_to_shuttle(shuttle, TRUE)
return TRUE
if("tryLinkShuttle")
if(shuttle_ref?.resolve())
balloon_alert(usr, "already linked!")
return TRUE
var/obj/docking_port/mobile/custom/shuttle = SSshuttle.get_containing_shuttle(usr)
if(!shuttle)
balloon_alert(usr, "not on shuttle!")
return TRUE
if(!istype(shuttle))
balloon_alert(usr, "incompatible shuttle type!")
return TRUE
var/obj/item/shuttle_blueprints/master = shuttle.master_blueprint?.resolve()
if(master && (master != src))
balloon_alert(usr, "master blueprint already exists!")
return TRUE
link_to_shuttle(shuttle, TRUE)
return TRUE
if("promoteToMaster")
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!shuttle)
balloon_alert(usr, "not linked!")
return TRUE
var/obj/item/shuttle_blueprints/master = shuttle.master_blueprint?.resolve()
if(master)
balloon_alert(usr, "master blueprint already exists!")
return TRUE
shuttle.master_blueprint = WEAKREF(src)
return TRUE
if("createNewArea")
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!shuttle)
balloon_alert(usr, "not linked!")
return TRUE
var/area_name = params["name"]
if(!area_name)
balloon_alert(usr, "no name given!")
return TRUE
var/area/current_area = get_area(usr)
var/area/default_area = shuttle.default_area
if(current_area != default_area)
balloon_alert(usr, "must be in default area!")
return TRUE
var/list/turfs = detect_room(get_turf(usr), max_size = CONFIG_GET(number/max_shuttle_size), extra_check = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(custom_shuttle_room_check), shuttle, null))
if(!length(turfs))
balloon_alert(usr, "invalid room!")
return TRUE
var/area/shuttle/custom/new_area = new()
new_area.name = area_name
shuttle.shuttle_areas[new_area] = TRUE
set_turfs_to_area(turfs, new_area)
new_area.reg_in_areas_in_z()
new_area.create_area_lighting_objects()
for(var/obj/machinery/door/firedoor/firelock as anything in default_area.firedoors)
firelock.CalculateAffectingAreas()
new_area.power_change()
return TRUE
if("releaseArea")
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!shuttle)
balloon_alert(usr, "not linked!")
return TRUE
var/area/current_area = get_area(usr)
if(!shuttle.shuttle_areas[current_area])
balloon_alert(usr, "not on shuttle!")
return TRUE
var/area/default_area = shuttle.default_area
if(current_area == default_area)
balloon_alert(usr, "can't release default area!")
return TRUE
var/obj/machinery/power/apc/current_area_apc = current_area.apc
var/obj/machinery/power/apc/default_area_apc = default_area.apc
if(current_area_apc && default_area_apc)
balloon_alert(usr, "remove the apc first!")
return TRUE
var/list/turfs = current_area.get_turfs_by_zlevel(shuttle.z)
set_turfs_to_area(turfs, default_area)
for(var/obj/machinery/door/firedoor/firelock as anything in default_area.firedoors)
firelock.CalculateAffectingAreas()
shuttle.shuttle_areas -= current_area
qdel(current_area)
return TRUE
if("mergeIntoArea")
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!shuttle)
balloon_alert(usr, "not linked!")
return TRUE
var/area/current_area = get_area(usr)
var/area/default_area = shuttle.default_area
if(current_area != default_area)
balloon_alert(usr, "must be in default area!")
return TRUE
var/area/merge_area = locate(params["area"])
if(!istype(merge_area))
return TRUE
if(!shuttle.shuttle_areas[merge_area])
return TRUE
if(merge_area == default_area)
return TRUE
var/list/actual_adjacent_areas = list()
var/list/turfs = detect_room(get_turf(usr), max_size = CONFIG_GET(number/max_shuttle_size), extra_check = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(custom_shuttle_room_check), shuttle, actual_adjacent_areas))
if(!length(turfs))
balloon_alert(usr, "invalid room!")
return TRUE
if(!actual_adjacent_areas[merge_area])
balloon_alert(usr, "selected area not connected to room!")
return TRUE
if(merge_area.apc && default_area.apc && turfs[get_turf(default_area.apc)])
balloon_alert(usr, "remove the apc first!")
return TRUE
set_turfs_to_area(turfs, merge_area)
for(var/obj/machinery/door/firedoor/firelock as anything in merge_area.firedoors + default_area.firedoors)
firelock.CalculateAffectingAreas()
return TRUE
if("renameArea")
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!shuttle)
balloon_alert(usr, "not linked!")
return TRUE
var/area/current_area = get_area(usr)
if(!shuttle.shuttle_areas[current_area])
balloon_alert(usr, "not on shuttle!")
return TRUE
var/area/default_area = shuttle.default_area
if(current_area == default_area)
balloon_alert(usr, "can't rename default area!")
return TRUE
var/new_name = params["name"]
if(!new_name)
balloon_alert(usr, "no name given!")
return TRUE
rename_area(current_area, new_name)
return TRUE
if("expandWithFrame")
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!shuttle)
balloon_alert(usr, "not linked!")
return TRUE
var/list/turfs = list()
var/list/areas = list()
var/turf/origin = get_turf(usr)
var/check_status = shuttle_expand_check(origin, shuttle, turfs, areas)
if(check_status & ORIGIN_NOT_ON_SHUTTLE)
balloon_alert(usr, "not on shuttle frame!")
return TRUE
if(check_status & FRAME_NOT_ADJACENT_TO_LINKED_SHUTTLE)
balloon_alert(usr, "not connected to linked shuttle!")
return TRUE
if(check_status & ABOVE_MAX_SHUTTLE_SIZE)
balloon_alert(usr, "frame too big!")
return TRUE
if(check_status & CUSTOM_AREA_NOT_COMPLETELY_CONTAINED)
balloon_alert(usr, "frame must completely enclose custom areas!")
return TRUE
if(check_status & INTERSECTS_NON_WHITELISTED_AREA)
balloon_alert(usr, "frame overlaps disallowed areas!")
return TRUE
if(check_status & CONTAINS_APC_OF_NON_CUSTOM_AREA)
balloon_alert(usr, "frame includes apc of non-custom area!")
return TRUE
expand_shuttle(usr, shuttle, turfs, areas)
return TRUE
if("cleanupEmptyTurfs")
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref?.resolve()
if(!shuttle)
balloon_alert(usr, "not linked!")
return TRUE
var/obj/item/shuttle_blueprints/master = shuttle.master_blueprint?.resolve()
if(master && master != src)
balloon_alert(usr, "not master blueprints!")
clear_empty_shuttle_turfs(shuttle)
/obj/item/shuttle_blueprints/crude
name = "crude shuttle blueprints"
desc = "This is just a sheet of paper thoroughly covered in what could either be crayon or spraypaint."
icon_state = "shuttle_blueprints_crude0"
base_icon_state = "shuttle_blueprints_crude"
base_desc = "This is just a sheet of paper thoroughly covered in what could either be crayon or spraypaint."
linked_desc = "This is just a crude doodle of a shuttle drawn on a background of what could either be crayon or spraypaint."
/obj/item/shuttle_blueprints/borg
name = "shuttle blueprint database"
desc = "A module designed to store the plans for one or more shuttles."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "shuttle_database0"
base_icon_state = "shuttle_database"
damtype = BURN // In case fantasy affixes or adminbus end up making this actually capable of hurting someone.
attack_verb_continuous = list("attacks", "scans", "analyzes")
attack_verb_simple = list("attack", "scan", "analyze")
base_desc = "A module designed to store the plans for one or more shuttles."
linked_desc = "A module designed to store the plans for one or more shuttles."
var/list/shuttles = list()
/obj/item/shuttle_blueprints/borg/get_shuttle_tip()
. = list()
if(!shuttle_ref)
if(!length(shuttles))
. += span_notice("It does not have any shuttle plans stored.")
else
. += span_notice("It does not currently have a shuttle plan loaded.")
. += span_notice("In this mode, it can be used to construct a custom shuttle.")
return
var/obj/docking_port/mobile/custom/shuttle = shuttle_ref.resolve()
if(!shuttle)
. += span_notice("The currently loaded plans are for a shuttle that no longer exists. It will default to shuttle construction mode.")
else
. += span_notice("It has the plans for \the [shuttle] currently loaded, and can be used to expand [shuttle.p_them()] or modify [shuttle.p_their()] areas.")
if(shuttle.master_blueprint.resolve() == src)
. += span_notice("This is the master blueprint for \the [shuttle]. You can copy it to a blank set of blueprints, or to another engineering cyborg with a shuttle database module installed.")
/obj/item/shuttle_blueprints/borg/unlink(removing)
if(removing)
shuttles -= shuttle_ref
..()
/obj/item/shuttle_blueprints/borg/ui_data(mob/user)
var/list/data = ..()
var/list/shuttle_data = list()
var/list/shuttle_name_count = list()
for(var/datum/weakref/shuttle_weakref as anything in shuttles)
var/obj/docking_port/mobile/shuttle = shuttle_weakref.resolve()
if(!shuttle)
continue
var/shuttle_name = shuttle.name
if(!shuttle_name_count[shuttle_name])
shuttle_name_count[shuttle_name] = 0
var/ref = shuttle_weakref.reference
shuttle_data[ref] = shuttle_name
if(shuttle_name_count[shuttle_name]++ > 1)
shuttle_data[ref] += " ([shuttle_name_count[shuttle_name]])"
if(length(shuttle_data))
data["shuttles"] = shuttle_data
return data
/obj/item/shuttle_blueprints/borg/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("switchShuttle")
var/ref = params["ref"]
var/obj/docking_port/mobile/shuttle = locate(ref)
if(!shuttle)
return TRUE
if(shuttles.Find(WEAKREF(shuttle)))
link_to_shuttle(shuttle)
return TRUE
if("unsetShuttle")
unlink()
return TRUE
/obj/item/shuttle_blueprints/borg/link_to_shuttle(obj/docking_port/mobile/custom/shuttle, is_master)
. = ..()
shuttles |= WEAKREF(shuttle)
/obj/item/shuttle_blueprints/borg/get_linked_name(obj/docking_port/mobile/shuttle)
name = "shuttle blueprint database ([shuttle.name])"