Files

217 lines
10 KiB
Plaintext

// VENTCRAWLING
/mob/living/proc/notify_ventcrawler_on_login()
var/ventcrawler = HAS_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS) || HAS_TRAIT(src, TRAIT_VENTCRAWLER_NUDE)
if(!ventcrawler)
return
to_chat(src, span_notice("You can ventcrawl! Use alt+click on vents to quickly travel about the station."))
/mob/living/carbon/human/notify_ventcrawler_on_login()
if(!ismonkey(src))
return ..()
if(!istype(head, /obj/item/clothing/head/helmet/monkey_sentience)) //don't notify them about ventcrawling if they're wearing the sentience helmet, because they can't ventcrawl with it on, and if they take it off they'll no longer be in control of the mob.
return ..()
/// Checks if the mob is able to enter the vent, and provides feedback if they are unable to.
/mob/living/proc/can_enter_vent(obj/machinery/atmospherics/components/ventcrawl_target, provide_feedback = TRUE)
// Being able to always ventcrawl trumps being only able to ventcrawl when wearing nothing
var/required_nudity = HAS_TRAIT(src, TRAIT_VENTCRAWLER_NUDE) && !HAS_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS)
// Cache the vent_movement bitflag var from atmos machineries
var/vent_movement = ventcrawl_target.vent_movement
if(!Adjacent(ventcrawl_target))
return
// BUBBER ADDITION START - VENTCRAWLING SIGNAL
var/signal_result = SEND_SIGNAL(src, COMSIG_CAN_VENTCRAWL, ventcrawl_target, provide_feedback)
if(!HAS_TRAIT(src, TRAIT_VENTCRAWLER_NUDE) && !HAS_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS) && !signal_result)
return
// BUBBER ADDITION END - VENTCRAWLING SIGNAL
if(stat)
if(provide_feedback)
to_chat(src, span_warning("You must be conscious to do this!"))
return
if(HAS_TRAIT(src, TRAIT_IMMOBILIZED))
if(provide_feedback)
to_chat(src, span_warning("You currently can't move into the vent!"))
return
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
if(provide_feedback)
to_chat(src, span_warning("You need to be able to use your hands to ventcrawl!"))
return
if(has_buckled_mobs())
if(provide_feedback)
to_chat(src, span_warning("You can't vent crawl with other creatures on you!"))
return
if(buckled)
if(provide_feedback)
to_chat(src, span_warning("You can't vent crawl while buckled!"))
return
if(iscarbon(src) && required_nudity && !signal_result) // BUBBER CHANGE - VENTCRAWLING SIGNAL
if(length(get_equipped_items(INCLUDE_POCKETS|INCLUDE_HELD)))
if(provide_feedback)
to_chat(src, span_warning("You can't crawl around in the ventilation ducts with items!"))
return
if(ventcrawl_target.welded)
if(provide_feedback)
// Add cooldown to prevent welded vent message spam during movement
if(COOLDOWN_FINISHED(src, welded_vent_message_cd))
to_chat(src, span_warning("You can't crawl around a welded vent!"))
COOLDOWN_START(src, welded_vent_message_cd, 2 SECONDS)
return
if(!(vent_movement & VENTCRAWL_ENTRANCE_ALLOWED))
if(provide_feedback)
to_chat(src, span_warning("You can't enter this vent!"))
return
return TRUE
/// Handles the entrance and exit on ventcrawling
/mob/living/proc/handle_ventcrawl(obj/machinery/atmospherics/components/ventcrawl_target)
if(!can_enter_vent(ventcrawl_target))
return
var/has_client = !isnull(client) // clientless mobs can do this too! this is just stored in case the client disconnects while we sleep in do_after.
//Handle the exit here
if(HAS_TRAIT(src, TRAIT_MOVE_VENTCRAWLING) && istype(loc, /obj/machinery/atmospherics) && movement_type & VENTCRAWLING)
// BUBBER ADDITION START - VENTCRAWLING SIGNAL
var/exit_time = SEND_SIGNAL(src, COMSIG_VENTCRAWL_PRE_EXIT, ventcrawl_target)
if(!exit_time)
exit_time = 1 SECONDS
// BUBBER ADDITION END - VENTCRAWLING SIGNAL
to_chat(src, span_notice("You begin climbing out from the ventilation system..."))
if(has_client && isnull(client))
return
if(!do_after(src, exit_time, target = ventcrawl_target)) // BUBBER CHANGE - VENTCRAWLING SIGNAL
SEND_SIGNAL(src, COMSIG_VENTCRAWL_PRE_CANCEL, ventcrawl_target) // BUBBER ADDITION - VENTCRAWLING SIGNAL
return
if(ventcrawl_target.welded) // in case it got welded during our sleep
to_chat(src, span_warning("You can't crawl around a welded vent!"))
return
visible_message(span_notice("[src] scrambles out from the ventilation ducts!"), span_notice("You scramble out from the ventilation ducts."))
forceMove(ventcrawl_target.loc)
REMOVE_TRAIT(src, TRAIT_MOVE_VENTCRAWLING, VENTCRAWLING_TRAIT)
update_pipe_vision()
SEND_SIGNAL(src, COMSIG_VENTCRAWL_EXIT, ventcrawl_target) // BUBBER ADDITION - VENTCRAWLING SIGNAL
//Entrance here
else
var/datum/pipeline/vent_parent = ventcrawl_target.parents[1]
if(vent_parent && (vent_parent.members.len || vent_parent.other_atmos_machines))
// BUBBER ADDITION START - VENTCRAWLING SIGNAL
var/enter_time = SEND_SIGNAL(src, COMISG_VENTCRAWL_PRE_ENTER, ventcrawl_target)
if(!enter_time)
enter_time = 2.5 SECONDS // Default time if the signal doesn't return anything
// BUBBER ADDITION END - VENTCRAWLING SIGNAL
ventcrawl_target.flick_overlay_static(image('icons/effects/vent_indicator.dmi', "arrow", ABOVE_MOB_LAYER, dir = get_dir(src.loc, ventcrawl_target.loc)), 2 SECONDS)
visible_message(span_notice("[src] begins climbing into the ventilation system...") ,span_notice("You begin climbing into the ventilation system..."))
if(!do_after(src, enter_time, target = ventcrawl_target, extra_checks = CALLBACK(src, PROC_REF(can_enter_vent), ventcrawl_target))) // BUBBER CHANGE - VENTCRAWLING SIGNAL
SEND_SIGNAL(src, COMSIG_VENTCRAWL_PRE_CANCEL, ventcrawl_target) // BUBBER ADDITION - VENTCRAWLING SIGNAL
return
if(has_client && isnull(client))
return
if(ventcrawl_target.welded) // in case it got welded during our sleep
to_chat(src, span_warning("You can't crawl around a welded vent!"))
return
ventcrawl_target.flick_overlay_static(image('icons/effects/vent_indicator.dmi', "insert", ABOVE_MOB_LAYER), 1 SECONDS)
visible_message(span_notice("[src] scrambles into the ventilation ducts!"), span_notice("You climb into the ventilation ducts."))
move_into_vent(ventcrawl_target)
SEND_SIGNAL(src, COMSIG_VENTCRAWL_ENTER, ventcrawl_target) // BUBBER ADDITION - VENTCRAWLING SIGNAL
else
to_chat(src, span_warning("This ventilation duct is not connected to anything!"))
/mob/living/basic/slime/can_enter_vent(obj/machinery/atmospherics/components/ventcrawl_target, provide_feedback = TRUE)
if(buckled)
if(provide_feedback)
to_chat(src, span_warning("You can't vent crawl while feeding!"))
return
return ..()
/**
* Moves living mob directly into the vent as a ventcrawler
*
* Arguments:
* * ventcrawl_target - The vent into which we are moving the mob
*/
/mob/living/proc/move_into_vent(obj/machinery/atmospherics/components/ventcrawl_target)
forceMove(ventcrawl_target)
ADD_TRAIT(src, TRAIT_MOVE_VENTCRAWLING, VENTCRAWLING_TRAIT)
update_pipe_vision()
/**
* Everything related to pipe vision on ventcrawling is handled by update_pipe_vision().
* Called on exit, entrance, and pipenet differences (e.g. moving to a new pipenet).
* One important thing to note however is that the movement of the client's eye is handled by the relaymove() proc in /obj/machinery/atmospherics.
* We move first and then call update. Dont flip this around
*/
/mob/living/proc/update_pipe_vision(full_refresh = FALSE)
if(!isnull(ai_controller) && isnull(client)) // we don't care about pipe vision if we have an AI controller with no client (typically means we are clientless).
return
// Take away all the pipe images if we're not doing anything with em
if(isnull(client) || !HAS_TRAIT(src, TRAIT_MOVE_VENTCRAWLING) || !istype(loc, /obj/machinery/atmospherics) || !(movement_type & VENTCRAWLING))
for(var/image/current_image in pipes_shown)
client.images -= current_image
pipes_shown.len = 0
pipetracker = null
for(var/atom/movable/screen/plane_master/lighting as anything in hud_used.get_true_plane_masters(LIGHTING_PLANE))
lighting.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#4d4d4d")
for(var/atom/movable/screen/plane_master/pipecrawl as anything in hud_used.get_true_plane_masters(PIPECRAWL_IMAGES_PLANE))
pipecrawl.hide_plane(src)
return
// We're gonna color the lighting plane to make it darker while ventcrawling, so things look nicer
// This is a bit hacky but it makes the background darker, which has a nice effect
for(var/atom/movable/screen/plane_master/lighting as anything in hud_used.get_true_plane_masters(LIGHTING_PLANE))
lighting.add_atom_colour("#4d4d4d", TEMPORARY_COLOUR_PRIORITY)
for(var/atom/movable/screen/plane_master/pipecrawl as anything in hud_used.get_true_plane_masters(PIPECRAWL_IMAGES_PLANE))
pipecrawl.unhide_plane(src)
var/obj/machinery/atmospherics/current_location = loc
var/list/our_pipenets = current_location.return_pipenets()
// We on occasion want to do a full rebuild. this lets us do that
if(full_refresh)
for(var/image/current_image in pipes_shown)
client.images -= current_image
pipes_shown.len = 0
pipetracker = null
if(!pipetracker)
pipetracker = new()
var/turf/our_turf = get_turf(src)
// We're getting the smallest "range" arg we can pass to the spatial grid and still get all the stuff we need
// We preload a bit more then we need so movement looks ok
var/list/view_range = getviewsize(client.view)
pipetracker.set_bounds(view_range[1] + 1, view_range[2] + 1)
var/list/entered_exited_pipes = pipetracker.recalculate_type_members(our_turf, SPATIAL_GRID_CONTENTS_TYPE_ATMOS)
var/list/pipes_gained = entered_exited_pipes[1]
var/list/pipes_lost = entered_exited_pipes[2]
for(var/obj/machinery/atmospherics/pipenet_part as anything in pipes_lost)
if(!pipenet_part.pipe_vision_img)
continue
client.images -= pipenet_part.pipe_vision_img
pipes_shown -= pipenet_part.pipe_vision_img
for(var/obj/machinery/atmospherics/pipenet_part as anything in pipes_gained)
// If the machinery is not part of our net or is not meant to be seen, continue
var/list/thier_pipenets = pipenet_part.return_pipenets()
if(!length(thier_pipenets & our_pipenets))
continue
if(!(pipenet_part.vent_movement & VENTCRAWL_CAN_SEE))
continue
if(!pipenet_part.pipe_vision_img)
var/turf/their_turf = get_turf(pipenet_part)
pipenet_part.pipe_vision_img = image(pipenet_part, pipenet_part.loc, dir = pipenet_part.dir)
SET_PLANE(pipenet_part.pipe_vision_img, PIPECRAWL_IMAGES_PLANE, their_turf)
client.images += pipenet_part.pipe_vision_img
pipes_shown += pipenet_part.pipe_vision_img