diff --git a/code/datums/components/remote_view.dm b/code/datums/components/remote_view.dm index 06224e3c29a..3b5594a2c5a 100644 --- a/code/datums/components/remote_view.dm +++ b/code/datums/components/remote_view.dm @@ -5,305 +5,80 @@ VAR_PROTECTED/datum/remote_view_config/settings = null VAR_PROTECTED/mob/host_mob VAR_PROTECTED/atom/remote_view_target + // Specialty remote views that rely on another object to manage them + VAR_PROTECTED/show_message = FALSE + VAR_PRIVATE/obj/item/host_item + VAR_PRIVATE/datum/view_coordinator // The object containing the viewer_list, with look() and unlook() logic + VAR_PRIVATE/list/coordinated_viewers // list from the view_coordinator, lists in byond are pass by reference, so this is the SAME list as on the coordinator! -/datum/component/remote_view/Initialize(atom/focused_on, viewsize, vconfig_path) - . = ..() +/datum/component/remote_view/Initialize(atom/focused_on = null, viewsize = null, vconfig_path = null, obj/item/managing_item = null, tileoffset = 0, show_visible_messages = TRUE, datum/coordinator = null, list/viewer_list = null) if(!ismob(parent)) return COMPONENT_INCOMPATIBLE - // Set config + . = ..() + // to_chat(world, "======================================== STARTED VIEW on: [focused_on]") + + /** + * Set config + * All remote views use a vconfig_path to create a settings datum. This datum controls most features of the remote view. + * It also allows for unique overrides per view type, such as the UAV overriding the hud for special effects, without + * needing to make an entirely new type of remote view to do so. See code\datums\remote_view_config.dm + */ if(!vconfig_path) vconfig_path = /datum/remote_view_config settings = new vconfig_path - // Safety check, focus on ourselves if the target is deleted, and flag any movement to end the view. + + /** + * Safety check, focus on our own turf if the target is deleted, and flag any movement to end the view. + * + * IMPORTANT: + * Passing null for the focused_on target of the remote view will use the turf of the mob creating the view. + * This behavior is called "turf decoupling" and is used to correct a byond issue where mobs dropping mobs lock + * the view to the mob that dropped them. See release_remote_view() below. Creating a remote_view with no + * arguments will just cause turf decoupling behavior, and is safe to do. + */ host_mob = parent - if(QDELETED(focused_on)) - focused_on = host_mob + if(!focused_on || QDELETED(focused_on)) + focused_on = get_turf(host_mob) settings.forbid_movement = TRUE - // Begin remoteview - host_mob.reset_perspective(focused_on) // Must be done before registering the signals - if(settings.forbid_movement) - RegisterSignal(host_mob, COMSIG_MOVABLE_MOVED, PROC_REF(handle_hostmob_moved)) - else - RegisterSignal(host_mob, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(handle_hostmob_moved)) - RegisterSignal(host_mob, COMSIG_MOB_RESET_PERSPECTIVE, PROC_REF(on_reset_perspective)) - RegisterSignal(host_mob, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_forced_endview)) - // Upon any disruptive status effects - if(settings.will_stun) - RegisterSignal(host_mob, COMSIG_LIVING_STATUS_STUN, PROC_REF(handle_status_effects)) - if(settings.will_weaken) - RegisterSignal(host_mob, COMSIG_LIVING_STATUS_WEAKEN, PROC_REF(handle_status_effects)) - if(settings.will_paralyze) - RegisterSignal(host_mob, COMSIG_LIVING_STATUS_PARALYZE, PROC_REF(handle_status_effects)) - if(settings.will_sleep) - RegisterSignal(host_mob, COMSIG_LIVING_STATUS_SLEEP, PROC_REF(handle_status_effects)) - if(settings.will_blind) - RegisterSignal(host_mob, COMSIG_LIVING_STATUS_BLIND, PROC_REF(handle_status_effects)) - if(settings.will_death) - RegisterSignal(host_mob, COMSIG_MOB_DEATH, PROC_REF(handle_endview)) - // Handle relayed movement - if(settings.relay_movement) - RegisterSignal(host_mob, COMSIG_MOB_RELAY_MOVEMENT, PROC_REF(handle_relay_movement)) - RegisterSignal(host_mob, COMSIG_MOB_HANDLE_VISION, PROC_REF(handle_mob_vision_update)) - // Hud overrides - if(settings.override_entire_hud) - RegisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD, PROC_REF(handle_hud_override)) - if(settings.override_health_hud) - RegisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_HEALTH_ICON, PROC_REF(handle_hud_health)) - if(settings.override_darkvision_hud) - RegisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_DARKSIGHT, PROC_REF(handle_hud_darkvision)) - // Recursive move component fires this, we only want it to handle stuff like being inside a paicard when releasing turf lock - if(isturf(focused_on)) - RegisterSignal(host_mob, COMSIG_MOVABLE_ATTEMPTED_MOVE, PROC_REF(handle_recursive_moved)) // Focus on remote view remote_view_target = focused_on - if(host_mob != remote_view_target) // Some items just offset our view, so we set ourselves as the view target, don't double dip if so! - RegisterSignal(remote_view_target, COMSIG_QDELETING, PROC_REF(handle_endview)) - RegisterSignal(remote_view_target, COMSIG_MOB_RESET_PERSPECTIVE, PROC_REF(on_remotetarget_reset_perspective)) - RegisterSignal(remote_view_target, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_forced_endview)) - // If the user has already limited their HUD this avoids them having a HUD when they zoom in - if(settings.use_zoom_hud && host_mob.hud_used.hud_shown) - host_mob.toggle_zoom_hud() - // Set view to size, null is default - host_mob.set_viewsize(viewsize) + host_mob.reset_perspective(remote_view_target) // Must be done before registering the signals + host_mob.AddComponent(/datum/component/recursive_move) // Updates our parent tree if we already have it -/datum/component/remote_view/RegisterWithParent() - // Update the mob's vision after we attach. - host_mob.handle_vision() - host_mob.handle_regular_hud_updates() - settings.attached_to_mob(src, host_mob) + /** + * If a complex datum with a viewer list is coordinating this view (shuttle consoles for example) + * This is intended for views that required a managed list of coordinated_viewers. + * This allows the view_coordinator to decide things, like maximum number of viewers, or if actions disconnect all viewers at once. + * Calls look() and unlook() on the coordinator when the view starts and ends. + */ + if(coordinator) + if(!islist(viewer_list)) // BAD BAD BAD NO + CRASH("Passed a viewer_list that was not a list, or was null, to /datum/component/remote_view component. Ensure the viewer_list exists before passing it into AddComponent.") + coordinated_viewers = viewer_list + view_coordinator = coordinator + view_coordinator.look(host_mob) + LAZYDISTINCTADD(coordinated_viewers, WEAKREF(host_mob)) -/datum/component/remote_view/Destroy(force) - . = ..() - // Basic handling - if(settings.forbid_movement) - UnregisterSignal(host_mob, COMSIG_MOVABLE_MOVED) - else - UnregisterSignal(host_mob, COMSIG_MOVABLE_Z_CHANGED) - UnregisterSignal(host_mob, COMSIG_MOB_RESET_PERSPECTIVE) - UnregisterSignal(host_mob, COMSIG_REMOTE_VIEW_CLEAR) - // Status effects - if(settings.will_stun) - UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_STUN) - if(settings.will_weaken) - UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_WEAKEN) - if(settings.will_paralyze) - UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_PARALYZE) - if(settings.will_sleep) - UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_SLEEP) - if(settings.will_blind) - UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_BLIND) - if(isturf(remote_view_target)) - UnregisterSignal(host_mob, COMSIG_MOVABLE_ATTEMPTED_MOVE) - if(settings.will_death) - UnregisterSignal(host_mob, COMSIG_MOB_DEATH) - // Handle relayed movement - if(settings.relay_movement) - UnregisterSignal(host_mob, COMSIG_MOB_RELAY_MOVEMENT) - UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_VISION) - // Hud overrides - if(settings.override_entire_hud) - UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD) - if(settings.override_health_hud) - UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_HEALTH_ICON) - if(settings.override_darkvision_hud) - UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_DARKSIGHT) - // Cleanup remote view - if(host_mob != remote_view_target) // If target is not ourselves - UnregisterSignal(remote_view_target, COMSIG_QDELETING) - UnregisterSignal(remote_view_target, COMSIG_MOB_RESET_PERSPECTIVE) - UnregisterSignal(remote_view_target, COMSIG_REMOTE_VIEW_CLEAR) - // Reset to default size - host_mob.set_viewsize() - if(settings.use_zoom_hud && !host_mob.hud_used.hud_shown) - host_mob.toggle_zoom_hud() - // Update the mob's vision right away if it still exists - if(!QDELETED(host_mob)) - settings.detatch_from_mob(src, host_mob) - settings.handle_remove_visuals(src, host_mob) - host_mob.handle_vision() - host_mob.handle_regular_hud_updates() - host_mob = null - remote_view_target = null - // Clear settings - QDEL_NULL(settings) + /** + * If an item is coordinating this view (scopes/binoculars) + * Handles remote views that are managed by a held item. The held item must remain in the mob's + * inventory, and will call zoom() and unzoom() when it starts and ends. Optionally showing a message. + */ + if(isitem(managing_item)) + host_item = managing_item + // Unfortunately too many things read this to control item state for me to remove this. + // Oh well! better than GetComponent() everywhere. Lets just manage item/zoom in this component though... + host_item.zoom = TRUE + // Feedback + show_message = show_visible_messages + if(show_message) + host_mob.visible_message(span_filter_notice("[host_mob] peers through the [host_item.zoomdevicename ? "[host_item.zoomdevicename] of the [host_item.name]" : "[host_item.name]"].")) -// Signal handlers - -/datum/component/remote_view/proc/handle_hostmob_moved(atom/source, atom/oldloc, direction, forced, movetime) - SIGNAL_HANDLER - PROTECTED_PROC(TRUE) - RETURN_TYPE(null) - if(!host_mob) - return - end_view() - qdel(src) - -/datum/component/remote_view/proc/handle_recursive_moved(atom/source, atom/oldloc, atom/new_loc) - SIGNAL_HANDLER - PROTECTED_PROC(TRUE) - RETURN_TYPE(null) - ASSERT(isturf(remote_view_target)) - // This signal handler is for recursive move decoupling us from /datum/component/remote_view/mob_holding_item's turf focusing when dropped in an item like a paicard - // This signal is only hooked when we focus on a turf. Check the subtype for more info, this horrorshow took several days to make consistently behave. - if(!host_mob) - return - end_view() - qdel(src) - -/// By default pass this down, but we need unique handling for subtypes sometimes -/datum/component/remote_view/proc/handle_forced_endview(datum/source) - SIGNAL_HANDLER - PROTECTED_PROC(TRUE) - RETURN_TYPE(null) - handle_endview(source) - -/datum/component/remote_view/proc/handle_endview(datum/source) - SIGNAL_HANDLER - SHOULD_NOT_OVERRIDE(TRUE) - PRIVATE_PROC(TRUE) - RETURN_TYPE(null) - if(!host_mob) - return - end_view() - qdel(src) - -/datum/component/remote_view/proc/handle_status_effects(datum/source, amount, ignore_canstun) - SIGNAL_HANDLER - PROTECTED_PROC(TRUE) - RETURN_TYPE(null) - if(!host_mob) - return - // We don't really care what effect was caused, just that it was increasing the value and thus negatively affecting us. - if(amount <= 0) - return - if(host_mob.client && isturf(host_mob.client.eye) && host_mob.client.eye == get_turf(host_mob.client.mob)) // This handles turf decoupling being protected until we actually move. - return - handle_endview(source) - -/datum/component/remote_view/proc/on_reset_perspective(datum/source) - SIGNAL_HANDLER - PRIVATE_PROC(TRUE) - RETURN_TYPE(null) - if(!host_mob) - return - // Check if we're still remote viewing the SAME target! - if(host_mob.client.eye == remote_view_target) - return - // The object already changed it's view, lets not interupt it like the others - qdel(src) - -/datum/component/remote_view/proc/on_remotetarget_reset_perspective(datum/source) - SIGNAL_HANDLER - PRIVATE_PROC(TRUE) - RETURN_TYPE(null) - // Non-mobs can't do this anyway - if(!host_mob) - return - if(!ismob(remote_view_target)) - return - var/mob/remote_view_mob = remote_view_target - // This is an ugly one, but if we want to follow the other object properly we need to copy its state! - if(!remote_view_mob.client || !host_mob.client) - end_view() - qdel(src) - return - // Only continue to observe if their view location is the same as their turf. otherwise they are doing their own ACTUALLY-REMOTE viewing - // we just won't update it if you're trying to look at the remote view target of another mob as they remote view someone else! - if(get_turf(remote_view_mob) != get_turf(remote_view_mob.client.eye)) - host_mob.client.eye = remote_view_mob - host_mob.client.perspective = MOB_PERSPECTIVE - return - // Copy the view, do not use reset_perspective, because it will call our signal and end our view! - host_mob.client.eye = remote_view_mob.client.eye - host_mob.client.perspective = remote_view_mob.client.perspective - -/datum/component/remote_view/proc/end_view() - PROTECTED_PROC(TRUE) - RETURN_TYPE(null) - host_mob.reset_perspective() - -// Optional signal handlers for more advanced remote views - -/datum/component/remote_view/proc/handle_relay_movement(datum/source, direction) - SIGNAL_HANDLER - SHOULD_NOT_OVERRIDE(TRUE) - PRIVATE_PROC(TRUE) - if(!host_mob) - return FALSE - return settings.handle_relay_movement(src, host_mob, direction) - -/datum/component/remote_view/proc/handle_hud_override(datum/source) - SIGNAL_HANDLER - SHOULD_NOT_OVERRIDE(TRUE) - PRIVATE_PROC(TRUE) - if(!host_mob) - return - return settings.handle_hud_override(src, host_mob) - -/datum/component/remote_view/proc/handle_hud_health(datum/source) - SIGNAL_HANDLER - SHOULD_NOT_OVERRIDE(TRUE) - PRIVATE_PROC(TRUE) - if(!host_mob) - return - return settings.handle_hud_health(src, host_mob) - -/datum/component/remote_view/proc/handle_hud_darkvision(datum/source) - SIGNAL_HANDLER - SHOULD_NOT_OVERRIDE(TRUE) - RETURN_TYPE(null) - PRIVATE_PROC(TRUE) - if(!host_mob) - return - settings.handle_hud_darkvision(src, host_mob) - -/datum/component/remote_view/proc/handle_mob_vision_update(datum/source) - SIGNAL_HANDLER - SHOULD_NOT_OVERRIDE(TRUE) - PRIVATE_PROC(TRUE) - if(!host_mob) - return - return settings.handle_apply_visuals(src, host_mob) - -// Accessors - -/datum/component/remote_view/proc/get_host() - RETURN_TYPE(/mob) - return host_mob - -/datum/component/remote_view/proc/get_target() - RETURN_TYPE(/atom) - return remote_view_target - -/datum/component/remote_view/proc/get_coordinator() - RETURN_TYPE(/atom) - return null // For subtype - -/datum/component/remote_view/proc/looking_at_target_already(atom/target) - return (remote_view_target == target) - - -/** - * Remote view subtype where if the item used with it is moved or dropped the view ends too - */ -/datum/component/remote_view/item_zoom - VAR_PRIVATE/obj/item/host_item - VAR_PRIVATE/show_message - -/datum/component/remote_view/item_zoom/Initialize(atom/focused_on, viewsize, vconfig_path, obj/item/our_item, tileoffset, show_visible_messages) - . = ..() - host_item = our_item - RegisterSignal(host_item, COMSIG_QDELETING, PROC_REF(handle_endview)) - RegisterSignal(host_item, COMSIG_MOVABLE_MOVED, PROC_REF(handle_endview)) - RegisterSignal(host_item, COMSIG_ITEM_DROPPED, PROC_REF(handle_endview)) - RegisterSignal(host_item, COMSIG_ITEM_EQUIPPED, PROC_REF(handle_endview)) - RegisterSignal(host_item, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_forced_endview)) - // Unfortunately too many things read this to control item state for me to remove this. - // Oh well! better than GetComponent() everywhere. Lets just manage item/zoom in this component though... - our_item.zoom = TRUE - // Offset view - var/tilesize = 32 - var/viewoffset = tilesize * tileoffset + /** + * Offset view, optionally offset the final view. Used by binoculars. + * Having the target of the view, and the owner mob of the view be the same thing allows you to offset the view as desired from the view owner. + */ + var/viewoffset = WORLD_ICON_SIZE * tileoffset switch(host_mob.dir) if (NORTH) host_mob.client.pixel_x = 0 @@ -317,197 +92,244 @@ if (WEST) host_mob.client.pixel_x = -viewoffset host_mob.client.pixel_y = 0 - // Feedback - show_message = show_visible_messages - if(show_message) - host_mob.visible_message(span_filter_notice("[host_mob] peers through the [host_item.zoomdevicename ? "[host_item.zoomdevicename] of the [host_item.name]" : "[host_item.name]"].")) - host_mob.handle_vision() -/datum/component/remote_view/item_zoom/Destroy(force) - // Feedback - if(show_message) - host_mob.visible_message(span_filter_notice("[host_item.zoomdevicename ? "[host_mob] looks up from the [host_item.name]" : "[host_mob] lowers the [host_item.name]"].")) - host_item.zoom = FALSE - // return view offset - if(host_mob.client) +/datum/component/remote_view/Destroy(force) + // Clear item + if(host_item) + host_item.zoom = FALSE + // Feedback + if(show_message) + host_mob.visible_message(span_filter_notice("[host_item.zoomdevicename ? "[host_mob] looks up from the [host_item.name]" : "[host_mob] lowers the [host_item.name]"].")) + host_item = null + + // clear coordinator + if(view_coordinator) + view_coordinator.unlook(host_mob, FALSE) + LAZYREMOVE(coordinated_viewers, WEAKREF(host_mob)) + view_coordinator = null + coordinated_viewers = null + + // Reset to default size + host_mob.set_viewsize() + if(settings.use_zoom_hud && !host_mob.hud_used.hud_shown) + host_mob.toggle_zoom_hud() + + // Update the mob's vision right away if it still exists + if(!QDELETED(host_mob) && host_mob.client) + settings.detatch_from_mob(src, host_mob) + settings.handle_remove_visuals(src, host_mob) host_mob.client.pixel_x = 0 host_mob.client.pixel_y = 0 - host_mob.handle_vision() - // decouple - UnregisterSignal(host_item, COMSIG_QDELETING) - UnregisterSignal(host_item, COMSIG_MOVABLE_MOVED) - UnregisterSignal(host_item, COMSIG_ITEM_DROPPED) - UnregisterSignal(host_item, COMSIG_ITEM_EQUIPPED) - UnregisterSignal(host_item, COMSIG_REMOTE_VIEW_CLEAR) - host_item = null + host_mob.handle_vision() + host_mob.handle_regular_hud_updates() + + // Unregister remaining signals + // to_chat(world, "======================================== ENDED VIEW on: [remote_view_target]") . = ..() + // Finish cleanup + QDEL_NULL(settings) + host_mob = null + remote_view_target = null -/** - * Remote view subtype that stops if the remote view target is dead, or you lose access to the mremote mutation - */ +/datum/component/remote_view/RegisterWithParent() + RegisterSignal(host_mob, COMSIG_MOB_RESET_PERSPECTIVE, PROC_REF(on_reset_perspective)) + RegisterSignal(host_mob, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_endview)) + if(host_mob != remote_view_target) // Some items just offset our view, so we set ourselves as the view target, don't double dip if so! + RegisterSignal(remote_view_target, COMSIG_QDELETING, PROC_REF(handle_endview)) + RegisterSignal(remote_view_target, COMSIG_MOB_RESET_PERSPECTIVE, PROC_REF(on_remotetarget_reset_perspective)) + RegisterSignal(remote_view_target, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_endview)) + if(host_item) + RegisterSignal(host_item, COMSIG_QDELETING, PROC_REF(handle_endview)) + RegisterSignal(host_item, COMSIG_MOVABLE_MOVED, PROC_REF(handle_endview)) + RegisterSignal(host_item, COMSIG_ITEM_DROPPED, PROC_REF(handle_endview)) + RegisterSignal(host_item, COMSIG_ITEM_EQUIPPED, PROC_REF(handle_endview)) + RegisterSignal(host_item, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_endview)) + if(view_coordinator) + RegisterSignal(view_coordinator, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_endview)) + settings.register_signals(host_mob, src) + if(settings.relay_movement) // Handle relayed movement + RegisterSignal(host_mob, COMSIG_MOB_RELAY_MOVEMENT, PROC_REF(handle_relay_movement)) + if(settings.release_view_to_turf || isturf(remote_view_target)) // So it triggers if we are inside an item too... And if the item is being moved around by pull... + RegisterSignal(host_mob, COMSIG_MOVABLE_ATTEMPTED_MOVE, PROC_REF(handle_recursive_move)) + + // Update the mob's vision after we attach everything + host_mob.handle_vision() + host_mob.handle_regular_hud_updates() + settings.attached_to_mob(src, host_mob) + +/datum/component/remote_view/UnregisterFromParent() + UnregisterSignal(host_mob, COMSIG_MOB_RESET_PERSPECTIVE) + UnregisterSignal(host_mob, COMSIG_REMOTE_VIEW_CLEAR) + if(host_mob != remote_view_target) // If target is not ourselves + UnregisterSignal(remote_view_target, COMSIG_QDELETING) + UnregisterSignal(remote_view_target, COMSIG_MOB_RESET_PERSPECTIVE) + UnregisterSignal(remote_view_target, COMSIG_REMOTE_VIEW_CLEAR) + if(host_item) + UnregisterSignal(host_item, COMSIG_QDELETING) + UnregisterSignal(host_item, COMSIG_MOVABLE_MOVED) + UnregisterSignal(host_item, COMSIG_ITEM_DROPPED) + UnregisterSignal(host_item, COMSIG_ITEM_EQUIPPED) + UnregisterSignal(host_item, COMSIG_REMOTE_VIEW_CLEAR) + if(view_coordinator) + UnregisterSignal(view_coordinator, COMSIG_REMOTE_VIEW_CLEAR) + settings.unregister_signals(host_mob, src) + if(settings.relay_movement) // Handle relayed movement + UnregisterSignal(host_mob, COMSIG_MOB_RELAY_MOVEMENT) + if(settings.release_view_to_turf || isturf(remote_view_target)) + UnregisterSignal(host_mob, COMSIG_MOVABLE_ATTEMPTED_MOVE) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Signal handlers +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/datum/component/remote_view/proc/handle_hostmob_moved(atom/source, atom/oldloc, direction, forced, movetime) + SIGNAL_HANDLER + PROTECTED_PROC(TRUE) + RETURN_TYPE(null) + release_remote_view() + +/datum/component/remote_view/proc/handle_endview(datum/source) + SIGNAL_HANDLER + SHOULD_NOT_OVERRIDE(TRUE) + PRIVATE_PROC(TRUE) + RETURN_TYPE(null) + release_remote_view() + +/datum/component/remote_view/proc/handle_status_effects(datum/source, amount, ignore_canstun) + SIGNAL_HANDLER + PROTECTED_PROC(TRUE) + RETURN_TYPE(null) + // We don't really care what effect was caused, just that it was increasing the value and thus negatively affecting us. + if(amount <= 0) + return + if(host_mob.client && isturf(host_mob.client.eye) && host_mob.client.eye == get_turf(host_mob.client.mob)) // This handles turf decoupling being protected until we actually move. + return + handle_endview(source) + +/datum/component/remote_view/proc/on_reset_perspective(datum/source) + SIGNAL_HANDLER + PRIVATE_PROC(TRUE) + RETURN_TYPE(null) + // The object already changed it's view, lets not interupt it like the others + qdel(src) + +/datum/component/remote_view/proc/on_remotetarget_reset_perspective(datum/source) + SIGNAL_HANDLER + PRIVATE_PROC(TRUE) + RETURN_TYPE(null) + // Non-mobs can't do this anyway + if(!ismob(remote_view_target)) + return + var/mob/remote_view_mob = remote_view_target + // This is an ugly one, but if we want to follow the other object properly we need to copy its state! + if(!remote_view_mob.client || !host_mob.client) + release_remote_view() + return + // Only continue to observe if their view location is the same as their turf. otherwise they are doing their own ACTUALLY-REMOTE viewing + // we just won't update it if you're trying to look at the remote view target of another mob as they remote view someone else! + if(get_turf(remote_view_mob) != get_turf(remote_view_mob.client.eye)) + host_mob.client.eye = remote_view_mob + host_mob.client.perspective = MOB_PERSPECTIVE + return + // Copy the view, do not use reset_perspective, because it will call our signal and end our view! + host_mob.client.eye = remote_view_mob.client.eye + host_mob.client.perspective = remote_view_mob.client.perspective + +/datum/component/remote_view/proc/handle_recursive_move(datum/source, atom/old_loc, atom/current_loc) + SIGNAL_HANDLER + PROTECTED_PROC(TRUE) + RETURN_TYPE(null) + var/needs_to_force_turf_decouple = ismob(old_loc) && isturf(current_loc) // Handle an edge case where another mob is dropped by a mob with someone else inside them + release_remote_view(needs_to_force_turf_decouple) + +/datum/component/remote_view/proc/handle_relay_movement(datum/source, direction) + SIGNAL_HANDLER + SHOULD_NOT_OVERRIDE(TRUE) + PRIVATE_PROC(TRUE) + // I'd move this into the config datum if it didn't require the component to also be passed too. Lets avoid GetComponent on a hotpath. + return settings.handle_relay_movement(src, host_mob, direction) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Horrible byond hack +// This is only here to solve an issue where dropping a held mob while inside a mob will linger on the turf of that mob forever +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/datum/component/remote_view/proc/release_remote_view(force_turf_decouple = FALSE) + PROTECTED_PROC(TRUE) + RETURN_TYPE(null) + // to_chat(world, "RELEASING: [remote_view_target] : forced turf decouple? [force_turf_decouple]") + var/mob/cache_mob = host_mob + var/releases_to_turf = settings.release_view_to_turf + qdel(src) // Delete here so the remote view is nice and clean + if(QDELETED(cache_mob) || !cache_mob.client) + // to_chat(world, "--DELETED") + return + + // We're nothing special, ask the mob to check if it should start a new remote view (like if we were dropped into a belly from being held by another mob!) + if(!releases_to_turf && !force_turf_decouple) + // to_chat(world, "--Reset perspective") + cache_mob.reset_perspective() + return + + // Check to see if it's legal to release the view. Our top level object MUST NOT be a mob! + var/emergency = 0 + var/atom/movable/recursive_scan = cache_mob.loc + if(!force_turf_decouple && !isturf(recursive_scan)) // If our actual mob was placed on a turf, skip all of this recursion checking. We're good to decouple. + while(recursive_scan && !isturf(recursive_scan) && emergency++ < 64) + if(ismob(recursive_scan)) + // to_chat(world, "--held by mob still") + cache_mob.reset_perspective() // We're still inside another mob... Do not decouple to turf. Hold onto our current target. + return + recursive_scan = recursive_scan.loc + + // Focus on the turf, this prevents the view lingering on a mob forever + // to_chat(world, "--turf decouple") + // Decouple to turf, this is a hack. + // Yes this is required. + spawn(0) // Yes I hate it. + cache_mob.AddComponent(/datum/component/remote_view) + // Good luck refactoring this until the byond issue is fixed. This needs to happen AFTER the move resolves, but needs to be called by recursive move... + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Accessors +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/datum/component/remote_view/proc/get_host() + RETURN_TYPE(/mob) + return host_mob + +/datum/component/remote_view/proc/get_target() + RETURN_TYPE(/atom) + return remote_view_target + +/datum/component/remote_view/proc/get_coordinator() + RETURN_TYPE(/atom) + return view_coordinator + +/datum/component/remote_view/proc/looking_at_target_already(atom/target) + return (remote_view_target == target) + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Prefabs +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Gene remote view /datum/component/remote_view/mremote_mutation -/datum/component/remote_view/mremote_mutation/Initialize(atom/focused_on, viewsize, vconfig_path) - if(!ismob(focused_on)) // What are you doing? This gene only works on mob targets, if you adminbus this I will personally eat your face. - return COMPONENT_INCOMPATIBLE - . = ..() +/datum/component/remote_view/mremote_mutation/RegisterWithParent() // Remote view mutation stops viewing when mobs die or if we lose the mutation/gene RegisterSignal(host_mob, COMSIG_MOB_DNA_MUTATION, PROC_REF(on_mutation)) - if(host_mob != remote_view_target) - RegisterSignal(remote_view_target, COMSIG_MOB_DEATH, PROC_REF(handle_endview)) + RegisterSignal(remote_view_target, COMSIG_MOB_DEATH, PROC_REF(handle_endview)) -/datum/component/remote_view/mremote_mutation/Destroy(force) - UnregisterSignal(host_mob, COMSIG_MOB_DNA_MUTATION) - if(host_mob != remote_view_target) - UnregisterSignal(remote_view_target, COMSIG_MOB_DEATH) +/datum/component/remote_view/mremote_mutation/UnregisterFromParent() . = ..() + UnregisterSignal(host_mob, COMSIG_MOB_DNA_MUTATION) + UnregisterSignal(remote_view_target, COMSIG_MOB_DEATH) /datum/component/remote_view/mremote_mutation/proc/on_mutation(datum/source) SIGNAL_HANDLER PRIVATE_PROC(TRUE) - if(!host_mob) - return var/mob/remote_mob = remote_view_target if(host_mob.stat == CONSCIOUS && (mRemote in host_mob.mutations) && remote_mob && remote_mob.stat == CONSCIOUS) return - end_view() - qdel(src) - - -/** - * Remote view subtype that handles look() and unlook() procs while managing a list of viewers. Expects a viewer list stored by the object itself, passed in with AddComponent(). Ensure the list exists before passing it to the component or pass by reference will fail. - */ -/datum/component/remote_view/viewer_managed - VAR_PRIVATE/datum/view_coordinator // The object containing the viewer_list, with look() and unlook() logic - VAR_PRIVATE/list/viewers // list from the view_coordinator, lists in byond are pass by reference, so this is the SAME list as on the coordinator! If you pass a null this will explode. - -/datum/component/remote_view/viewer_managed/Initialize(atom/focused_on, viewsize, vconfig_path, datum/coordinator, list/viewer_list) - . = ..() - if(!islist(viewer_list)) // BAD BAD BAD NO - CRASH("Passed a viewer_list that was not a list, or was null, to /datum/component/remote_view/viewer_managed component. Ensure the viewer_list exists before passing it into AddComponent.") - viewers = viewer_list - view_coordinator = coordinator - view_coordinator.look(host_mob) - LAZYDISTINCTADD(viewers, WEAKREF(host_mob)) - RegisterSignal(view_coordinator, COMSIG_REMOTE_VIEW_CLEAR, PROC_REF(handle_forced_endview)) - -/datum/component/remote_view/viewer_managed/Destroy(force) - UnregisterSignal(view_coordinator, COMSIG_REMOTE_VIEW_CLEAR) - view_coordinator.unlook(host_mob, FALSE) - LAZYREMOVE(viewers, WEAKREF(host_mob)) - view_coordinator = null - viewers = null - . = ..() - -/datum/component/remote_view/viewer_managed/get_coordinator() - return view_coordinator - -/** - * Remote view subtype that is handling a byond bug where mobs changing their client eye from inside of - * and object will not have their eye change, and instead focus on any mob currently holding the item, - * and only be released once we move ourselves to a new turf. This subtype does some loc witchcraft - * to put us on a turf, change our view, and put us back without calling signals or move/enter. - * Hopefully this will not be needed someday in the future - Willbird - */ -#define MAX_RECURSIVE 64 -/datum/component/remote_view/mob_holding_item - var/needs_to_decouple = FALSE // if the current top level atom is a mob - -/datum/component/remote_view/mob_holding_item/Initialize(atom/focused_on, viewsize, vconfig_path) - if(!isobj(focused_on)) // You shouldn't be using this if so. - return COMPONENT_INCOMPATIBLE - . = ..() - // Items can be nested deeply, so we need to update on any parent reorganization or actual move. - host_mob.AddComponent(/datum/component/recursive_move) - RegisterSignal(host_mob, COMSIG_MOVABLE_ATTEMPTED_MOVE, PROC_REF(handle_recursive_moved)) // Doesn't need override, basetype only ever registers this signal if we're looking at a turf - // Check our inmob state - if(ismob(find_topmost_atom())) - needs_to_decouple = TRUE - -/datum/component/remote_view/mob_holding_item/Destroy(force) - UnregisterSignal(host_mob, COMSIG_MOVABLE_ATTEMPTED_MOVE) - . = ..() - -/datum/component/remote_view/mob_holding_item/handle_status_effects(datum/source, amount, ignore_canstun) - if(host_mob.loc == remote_view_target) // If we are still inside our holder or belly than don't bother spamming this - return - . = ..() - -/datum/component/remote_view/mob_holding_item/handle_hostmob_moved(atom/source, atom/oldloc) - // We handle this in recursive move - if(!host_mob) - return - if(isturf(host_mob.loc)) - if(oldloc == remote_view_target) - needs_to_decouple = TRUE - decouple_view_to_turf( host_mob, host_mob.loc) - return - -/datum/component/remote_view/mob_holding_item/handle_recursive_moved(atom/source, atom/oldloc, atom/new_loc) - if(!host_mob) - return - // default moved signal will handle this - if(isturf(host_mob.loc)) - return - // This only triggers when we are deeper in than our mob. See who is in charge of this clowncar... - // Loop upward until we find a mob or a turf. Mobs will hold our current view, turfs mean our bag-stack was dropped. - var/atom/top_most = find_topmost_atom() - if(isturf(top_most)) - if(needs_to_decouple) // Only need to do this if we were held by a mob prior, otherwise this triggers every move and is expensive for no reason - decouple_view_to_turf( host_mob, top_most) - return - if(ismob(top_most) || ismecha(top_most)) // Mobs and mechas both do this - host_mob.AddComponent(/datum/component/recursive_move) // Will rebuild parent chain. - needs_to_decouple = TRUE - return - -/// Get our topmost atom state, if it's a mob or a turf -/datum/component/remote_view/mob_holding_item/proc/find_topmost_atom() - var/atom/cur_parent = remote_view_target?.loc // first loc could be null - var/recursion = 0 // safety check - max iterations - while(!isnull(cur_parent) && (recursion < MAX_RECURSIVE)) - if(cur_parent == cur_parent.loc) //safety check incase a thing is somehow inside itself, cancel - log_runtime("REMOTE_VIEW: Parent is inside itself. ([host_mob]) ([host_mob.type]) : [MAX_RECURSIVE - recursion]") - return null - if(ismob(cur_parent) || ismecha(cur_parent) || isturf(cur_parent)) - return cur_parent - recursion++ - cur_parent = cur_parent.loc - - if(recursion >= MAX_RECURSIVE) // If we escaped due to iteration limit, cancel - log_runtime("REMOTE_VIEW: Turf search hit recursion limit. ([host_mob]) ([host_mob.type])") - return null - -/// Makes a new remote view focused on the release_turf argument. This remote view ends as soon as any movement happens. Even if we are inside many levels of objects due to our recursive_move listener -/datum/component/remote_view/mob_holding_item/proc/decouple_view_to_turf(mob/cache_mob, turf/release_turf) - if(needs_to_decouple) - // Yes this spawn is needed, yes I wish it wasn't. - spawn(0) - // Decouple the view to the turf on drop, or we'll be stuck on the mob that dropped us forever - if(!QDELETED(cache_mob) && cache_mob.client) - cache_mob.AddComponent(/datum/component/remote_view, focused_on = release_turf, viewsize = null, vconfig_path = /datum/remote_view_config/turf_decoupling) - cache_mob.client.eye = release_turf // Yes-- - cache_mob.client.perspective = EYE_PERSPECTIVE // --this is required too. - if(!isturf(cache_mob.loc)) // For stuff like paicards - cache_mob.AddComponent(/datum/component/recursive_move) // Will rebuild parent chain. - // If you somehow deleted before the decouple... Just fix this mess. - else - cache_mob.reset_perspective() - // Because nested vore bellies do NOT get handled correctly for recursive prey. We need to tell the belly's occupants to decouple too... Then their own belly's occupants... - // Yes, two loops is faster. Because we skip typechecking byondcode side and instead do it engine side when getting the contents of the mob, - // we also skip typechecking every /obj in the mob on the byondcode side... Evil wizard knowledge. - for(var/obj/belly/check_belly in cache_mob.contents) - SEND_SIGNAL(check_belly, COMSIG_REMOTE_VIEW_CLEAR) - for(var/obj/item/dogborg/sleeper/check_sleeper in cache_mob.contents) - SEND_SIGNAL(check_sleeper, COMSIG_REMOTE_VIEW_CLEAR) - qdel(src) - -/// We were forcibly disconnected, this situation is probably a recursive hellscape, so just decouple entirely and fix it when someone moves. -/datum/component/remote_view/mob_holding_item/handle_forced_endview(atom/source) - if(!host_mob) - return - needs_to_decouple = TRUE - decouple_view_to_turf( host_mob, get_turf(host_mob)) - -#undef MAX_RECURSIVE + release_remote_view() diff --git a/code/datums/datum.dm b/code/datums/datum.dm index e48bb5a2024..872745c8369 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -376,12 +376,12 @@ /// Begin coordinated remote viewing, this will call look() when the view begins, and unlook() when it ends. /datum/proc/start_coordinated_remoteview(mob/user, atom/target, list/viewer_managed_list, remote_view_config_path = null) ASSERT(islist(viewer_managed_list)) - user.AddComponent(/datum/component/remote_view/viewer_managed, focused_on = target, viewsize = null, vconfig_path = remote_view_config_path, coordinator = src, viewer_list = viewer_managed_list) + user.AddComponent(/datum/component/remote_view, focused_on = target, viewsize = null, vconfig_path = remote_view_config_path, coordinator = src, viewer_list = viewer_managed_list) -/// Called from /datum/component/remote_view/viewer_managed during Initilize(). +/// Called from /datum/component/remote_view during Initilize(). /datum/proc/look(mob/user) return -/// Called from /datum/component/remote_view/viewer_managed during Destroy() +/// Called from /datum/component/remote_view during Destroy() /datum/proc/unlook(mob/user) return diff --git a/code/datums/remote_view_config.dm b/code/datums/remote_view_config.dm index b4980d09874..3da56ab9b19 100644 --- a/code/datums/remote_view_config.dm +++ b/code/datums/remote_view_config.dm @@ -14,53 +14,117 @@ var/override_entire_hud = FALSE // Overrides all others, only this needs to be set if you do a fully custom hud var/override_health_hud = FALSE var/override_darkvision_hud = FALSE + // turf release flag + var/release_view_to_turf = FALSE + +/datum/remote_view_config/proc/register_signals(mob/host_mob, datum/component/remote_view/component) + RETURN_TYPE(null) + SHOULD_NOT_OVERRIDE(TRUE) + // Basic handling + if(forbid_movement) + component.RegisterSignal(host_mob, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/datum/component/remote_view, handle_hostmob_moved)) + else + component.RegisterSignal(host_mob, COMSIG_MOVABLE_Z_CHANGED, TYPE_PROC_REF(/datum/component/remote_view, handle_hostmob_moved)) + // Upon any disruptive status effects + if(will_stun) + component.RegisterSignal(host_mob, COMSIG_LIVING_STATUS_STUN, TYPE_PROC_REF(/datum/component/remote_view, handle_status_effects)) + if(will_weaken) + component.RegisterSignal(host_mob, COMSIG_LIVING_STATUS_WEAKEN, TYPE_PROC_REF(/datum/component/remote_view, handle_status_effects)) + if(will_paralyze) + component.RegisterSignal(host_mob, COMSIG_LIVING_STATUS_PARALYZE, TYPE_PROC_REF(/datum/component/remote_view, handle_status_effects)) + if(will_sleep) + component.RegisterSignal(host_mob, COMSIG_LIVING_STATUS_SLEEP, TYPE_PROC_REF(/datum/component/remote_view, handle_status_effects)) + if(will_blind) + component.RegisterSignal(host_mob, COMSIG_LIVING_STATUS_BLIND, TYPE_PROC_REF(/datum/component/remote_view, handle_status_effects)) + if(will_death) + component.RegisterSignal(host_mob, COMSIG_MOB_DEATH, TYPE_PROC_REF(/datum/component/remote_view, handle_endview)) + RegisterSignal(host_mob, COMSIG_MOB_HANDLE_VISION, PROC_REF(handle_apply_visuals)) + // Hud overrides + if(override_entire_hud) + RegisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD, PROC_REF(handle_hud_override)) + if(override_health_hud) + RegisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_HEALTH_ICON, PROC_REF(handle_hud_health)) + if(override_darkvision_hud) + RegisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_DARKSIGHT, PROC_REF(handle_hud_darkvision)) + +/datum/remote_view_config/proc/unregister_signals(mob/host_mob, datum/component/remote_view/component) + RETURN_TYPE(null) + SHOULD_NOT_OVERRIDE(TRUE) + // Basic handling + if(forbid_movement) + component.UnregisterSignal(host_mob, COMSIG_MOVABLE_MOVED) + else + component.UnregisterSignal(host_mob, COMSIG_MOVABLE_Z_CHANGED) + // Status effects + if(will_stun) + component.UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_STUN) + if(will_weaken) + component.UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_WEAKEN) + if(will_paralyze) + component.UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_PARALYZE) + if(will_sleep) + component.UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_SLEEP) + if(will_blind) + component.UnregisterSignal(host_mob, COMSIG_LIVING_STATUS_BLIND) + if(will_death) + component.UnregisterSignal(host_mob, COMSIG_MOB_DEATH) + UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_VISION) + // Hud overrides + if(override_entire_hud) + UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD) + if(override_health_hud) + UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_HEALTH_ICON) + if(override_darkvision_hud) + UnregisterSignal(host_mob, COMSIG_MOB_HANDLE_HUD_DARKSIGHT) /// Called when remote view component finishes attaching to the mob -/datum/remote_view_config/proc/attached_to_mob( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/proc/attached_to_mob(datum/component/remote_view/owner_component, mob/host_mob) RETURN_TYPE(null) return /// Called when remote view component is destroyed -/datum/remote_view_config/proc/detatch_from_mob( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/proc/detatch_from_mob(datum/component/remote_view/owner_component, mob/host_mob) RETURN_TYPE(null) return /// Handles relayed movement during a remote view. Override this in a subtype to handle specialized logic. If it returns true, the mob will not move, allowing you to handle remotely controlled movement. -/datum/remote_view_config/proc/handle_relay_movement( datum/component/remote_view/owner_component, mob/host_mob, datum/coordinator, atom/movable/remote_view_target, direction) +/datum/remote_view_config/proc/handle_relay_movement(datum/component/remote_view/owner_component, mob/host_mob, datum/coordinator, atom/movable/remote_view_target, direction) SIGNAL_HANDLER - // By default, we ask our remote_view_target to handle relaymove for us. - if(!remote_view_target) - return FALSE return remote_view_target.relaymove(host_mob, direction) /// Handles visual changes to mob's hud or flags when in use, it is fired every life tick. -/datum/remote_view_config/proc/handle_apply_visuals( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/proc/handle_apply_visuals(mob/host_mob) SIGNAL_HANDLER RETURN_TYPE(null) return /// Handles visual changes when ending the view -/datum/remote_view_config/proc/handle_remove_visuals( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/proc/handle_remove_visuals(datum/component/remote_view/owner_component, mob/host_mob) SIGNAL_HANDLER RETURN_TYPE(null) return /// Handles hud health indicator being replaced with a custom one (like showing the remote_view_target's health). -/datum/remote_view_config/proc/handle_hud_override( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/proc/handle_hud_override(mob/host_mob) SIGNAL_HANDLER return COMSIG_COMPONENT_HANDLED_HUD /// Handles hud health indicator being replaced with a custom one (like showing the remote_view_target's health). -/datum/remote_view_config/proc/handle_hud_health( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/proc/handle_hud_health(mob/host_mob) SIGNAL_HANDLER return COMSIG_COMPONENT_HANDLED_HEALTH_ICON /// Handles hud darkvision for if the remote view uses it's own logic for darkvision, or asks the remote_view_target to calculate it. -/datum/remote_view_config/proc/handle_hud_darkvision( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/proc/handle_hud_darkvision(mob/host_mob) SIGNAL_HANDLER RETURN_TYPE(null) return +/datum/remote_view_config/proc/get_component_coordinator(mob/host_mob) + RETURN_TYPE(/atom) + var/datum/component/remote_view/owner_component = host_mob.GetComponent(/datum/component/remote_view) + return owner_component.get_coordinator() + ////////////////////////////////////////////////////////////////////////////////////////////////// // Basic subtypes @@ -86,6 +150,7 @@ will_paralyze = FALSE will_sleep = FALSE will_blind = FALSE + release_view_to_turf = TRUE /// Remote view that only allows decoupling a turf view by movement. Seperate from effect_immune to allow for easier removal in the future if the underlying issue that makes this needed is someday fixed /datum/remote_view_config/turf_decoupling diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index bd9989d83f3..cb6c853fc5a 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -271,7 +271,7 @@ break if (user.stat == 2) return - user.AddComponent(/datum/component/remote_view/item_zoom, focused_on = target, vconfig_path = /datum/remote_view_config/camera_standard, our_item = src, viewsize = null, tileoffset = 0, show_visible_messages = FALSE) + user.AddComponent(/datum/component/remote_view, focused_on = target, vconfig_path = /datum/remote_view_config/camera_standard, managing_item = src, viewsize = null, tileoffset = 0, show_visible_messages = FALSE) /* /obj/item/cigarpacket diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 7a10d44fc30..ecf8617584a 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -154,7 +154,6 @@ Class Procs: if(ishuman(A)) var/mob/living/carbon/human/H = A H.forceMove(loc) - H.reset_perspective() else qdel(A) return ..() diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 0c543aca978..93d3ae149a2 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -9,6 +9,7 @@ desc = "Exosuit" description_info = "Alt click to strafe." icon = 'icons/mecha/mecha.dmi' + flags = REMOTEVIEW_ON_ENTER density = TRUE //Dense. To raise the heat. opacity = 1 //Opaque. Menacing. anchored = TRUE //No pulling around. diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 117356fbe78..65955d48bb8 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -874,7 +874,7 @@ GLOBAL_LIST_EMPTY(blood_overlays_by_type) can_zoom = FALSE if(!zoom && can_zoom) - M.AddComponent(/datum/component/remote_view/item_zoom, focused_on = M, vconfig_path = /datum/remote_view_config/zoomed_item, our_item = src, viewsize = viewsize, tileoffset = tileoffset, show_visible_messages = TRUE) + M.AddComponent(/datum/component/remote_view, focused_on = M, vconfig_path = /datum/remote_view_config/zoomed_item, managing_item = src, viewsize = viewsize, tileoffset = tileoffset, show_visible_messages = TRUE) return SEND_SIGNAL(src,COMSIG_REMOTE_VIEW_CLEAR) diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index ed30c8ced14..8fd04eba48f 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -104,6 +104,7 @@ GLOB.paikeys |= new_pai.ckey setPersonality(new_pai) new_pai.apply_preferences(new_pai.client) + new_pai.reset_perspective() // Update client view to card return new_pai /obj/item/paicard/tgui_interact(mob/user, datum/tgui/ui) diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm index 2a216c24b43..367eb278610 100644 --- a/code/game/objects/items/devices/spy_bug.dm +++ b/code/game/objects/items/devices/spy_bug.dm @@ -212,7 +212,7 @@ unpair(selected_camera) selected_camera = null return - user.AddComponent(/datum/component/remote_view/item_zoom, focused_on = selected_camera, vconfig_path = /datum/remote_view_config/camera_standard, our_item = src, viewsize = null, tileoffset = 0, show_visible_messages = TRUE) + user.AddComponent(/datum/component/remote_view, focused_on = selected_camera, vconfig_path = /datum/remote_view_config/camera_standard, managing_item = src, viewsize = null, tileoffset = 0, show_visible_messages = TRUE) /obj/item/bug_monitor/proc/can_use_cam(mob/user) if(!cameras.len) diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index ada3d8f7791..e27d0f8abdf 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -65,7 +65,6 @@ M.forceMove(loc) // Might be in a belly M.sdisabilities &= ~MUTE M.take_overall_damage((M.health - health - 100),0) //any new damage the statue incurred is transfered to the mob - M.reset_perspective() // Fixes a blackscreen flicker /obj/structure/closet/statue/open() return diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index 0e6536755fc..b34c6a8ceea 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -95,9 +95,7 @@ /obj/item/holder/Destroy() STOP_PROCESSING(SSobj, src) if(held_mob) - var/mob/cached_mob = held_mob dump_mob() - cached_mob.reset_perspective() // This case cannot be handled gracefully, make sure the mob view is cleaned up. if(ismob(loc)) var/mob/M = loc M.drop_from_inventory(src, loc) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 091b729a721..e15ffe55a20 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1528,48 +1528,6 @@ else clear_fullscreen("fear") - if(healths) - if(chem_effects[CE_PAINKILLER] > 100) - healths.icon_state = "health_numb" - else - // Generate a by-limb health display. - var/mutable_appearance/healths_ma = new(healths) - healths_ma.icon_state = "blank" - healths_ma.overlays = null - healths_ma.plane = PLANE_PLAYER_HUD - - var/no_damage = 1 - var/trauma_val = 0 // Used in calculating softcrit/hardcrit indicators. - if(!(species.flags & NO_PAIN)) - trauma_val = max(traumatic_shock,halloss)/getMaxHealth() - var/limb_trauma_val = trauma_val*0.3 - // Collect and apply the images all at once to avoid appearance churn. - var/list/health_images = list() - for(var/obj/item/organ/external/E in organs) - if(no_damage && (E.brute_dam || E.burn_dam)) - no_damage = 0 - health_images += E.get_damage_hud_image(limb_trauma_val) - - // Apply a fire overlay if we're burning. - if(on_fire || get_hallucination_component()?.get_hud_state() == HUD_HALLUCINATION_ONFIRE) - health_images += image('icons/mob/OnFire.dmi',"[get_fire_icon_state()]") - - // Show a general pain/crit indicator if needed. - if(get_hallucination_component()?.get_hud_state() == HUD_HALLUCINATION_CRIT) - trauma_val = 2 - if(trauma_val) - if(!(species.flags & NO_PAIN)) - if(trauma_val > 0.7) - health_images += image('icons/mob/screen1_health.dmi',"softcrit") - if(trauma_val >= 1) - health_images += image('icons/mob/screen1_health.dmi',"hardcrit") - else if(no_damage) - health_images += image('icons/mob/screen1_health.dmi',"fullhealth") - - healths_ma.add_overlay(health_images) - healths.appearance = healths_ma - - var/fat_alert = /atom/movable/screen/alert/fat var/hungry_alert = /atom/movable/screen/alert/hungry var/starving_alert = /atom/movable/screen/alert/starving @@ -1678,7 +1636,7 @@ var/no_damage = 1 var/trauma_val = 0 // Used in calculating softcrit/hardcrit indicators. if(!(species.flags & NO_PAIN)) - trauma_val = max(traumatic_shock,halloss)/species.total_health + trauma_val = max(traumatic_shock,halloss)/getMaxHealth() var/limb_trauma_val = trauma_val*0.3 // Collect and apply the images all at once to avoid appearance churn. var/list/health_images = list() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 8ca512f47a4..3fe960a1c4e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -274,13 +274,8 @@ client.perspective = MOB_PERSPECTIVE else if(isturf(new_eye)) - //Set to the turf unless it's our current turf - if(new_eye != loc) - client.perspective = EYE_PERSPECTIVE - client.set_eye(new_eye) - else - client.set_eye(client.mob) - client.perspective = MOB_PERSPECTIVE + client.perspective = EYE_PERSPECTIVE + client.set_eye(new_eye) else return TRUE //no setting eye to stupid things like areas or whatever else @@ -310,10 +305,7 @@ var/datum/component/remote_view/remote_comp = GetComponent(/datum/component/remote_view) if(remote_comp?.looking_at_target_already(loc)) return FALSE - if(isitem(loc) || isbelly(loc) || ismecha(loc)) // Requires more careful handling than structures because they are held by mobs - AddComponent(/datum/component/remote_view/mob_holding_item, focused_on = loc, viewsize = null, vconfig_path = /datum/remote_view_config/inside_object) - return TRUE - if(loc.flags & REMOTEVIEW_ON_ENTER) // Handle atoms that begin a remote view upon entering them. + if(isitem(loc) || (loc.flags & REMOTEVIEW_ON_ENTER)) // Handle atoms that begin a remote view upon entering them. AddComponent(/datum/component/remote_view, focused_on = loc, viewsize = null, vconfig_path = /datum/remote_view_config/inside_object) return TRUE return FALSE diff --git a/code/modules/tgui/modules/ntos-only/uav.dm b/code/modules/tgui/modules/ntos-only/uav.dm index 202ac8b0573..9e64d078d4f 100644 --- a/code/modules/tgui/modules/ntos-only/uav.dm +++ b/code/modules/tgui/modules/ntos-only/uav.dm @@ -195,14 +195,14 @@ override_health_hud = TRUE var/original_health_hud_icon -/datum/remote_view_config/uav_control/handle_relay_movement( datum/component/remote_view/owner_component, mob/host_mob, direction) +/datum/remote_view_config/uav_control/handle_relay_movement(datum/component/remote_view/owner_component, mob/host_mob, direction) var/datum/tgui_module/uav/tgui_owner = owner_component.get_coordinator() if(tgui_owner?.current_uav) return tgui_owner.current_uav.relaymove(host_mob, direction, tgui_owner.signal_strength) return FALSE -/datum/remote_view_config/uav_control/handle_apply_visuals( datum/component/remote_view/owner_component, mob/host_mob) - var/datum/tgui_module/uav/tgui_owner = owner_component.get_coordinator() +/datum/remote_view_config/uav_control/handle_apply_visuals(mob/host_mob) + var/datum/tgui_module/uav/tgui_owner = get_component_coordinator(host_mob) if(!tgui_owner) return if(get_dist(host_mob, tgui_owner.tgui_host()) > 1 || !tgui_owner.current_uav) @@ -216,24 +216,24 @@ else host_mob.clear_fullscreen("whitenoise", 0) -/datum/remote_view_config/uav_control/handle_remove_visuals( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/uav_control/handle_remove_visuals(datum/component/remote_view/owner_component, mob/host_mob) // Clear hud host_mob.clear_fullscreen("fishbed",0) host_mob.clear_fullscreen("scanlines",0) host_mob.clear_fullscreen("whitenoise",0) // We are responsible for restoring the health UI's icons on removal -/datum/remote_view_config/uav_control/attached_to_mob( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/uav_control/attached_to_mob(datum/component/remote_view/owner_component, mob/host_mob) original_health_hud_icon = host_mob.healths?.icon -/datum/remote_view_config/uav_control/detatch_from_mob( datum/component/remote_view/owner_component, mob/host_mob) +/datum/remote_view_config/uav_control/detatch_from_mob(datum/component/remote_view/owner_component, mob/host_mob) if(host_mob.healths && original_health_hud_icon) host_mob.healths.icon = original_health_hud_icon host_mob.healths.appearance = null // Show the uav health instead of the mob's while it is viewing -/datum/remote_view_config/uav_control/handle_hud_health( datum/component/remote_view/owner_component, mob/host_mob) - var/datum/tgui_module/uav/tgui_owner = owner_component.get_coordinator() +/datum/remote_view_config/uav_control/handle_hud_health(mob/host_mob) + var/datum/tgui_module/uav/tgui_owner = get_component_coordinator(host_mob) var/mutable_appearance/MA = new (host_mob.healths) MA.icon = 'icons/mob/screen1_robot_minimalist.dmi' diff --git a/code/modules/tgui/modules/overmap.dm b/code/modules/tgui/modules/overmap.dm index 8e9a451d880..87d752609a5 100644 --- a/code/modules/tgui/modules/overmap.dm +++ b/code/modules/tgui/modules/overmap.dm @@ -480,14 +480,14 @@ /datum/remote_view_config/overmap_ship_control relay_movement = TRUE -/datum/remote_view_config/overmap_ship_control/handle_relay_movement( datum/component/remote_view/owner_component, mob/host_mob, direction) - var/datum/tgui_module/ship/tgui_owner = owner_component.get_coordinator() +/datum/remote_view_config/overmap_ship_control/handle_relay_movement(datum/component/remote_view/owner_component, mob/host_mob, direction) + var/datum/tgui_module/ship/tgui_owner = get_component_coordinator(host_mob) if(tgui_owner?.linked) return tgui_owner.relaymove(host_mob, direction) return FALSE -/datum/remote_view_config/overmap_ship_control/handle_apply_visuals( datum/component/remote_view/owner_component, mob/host_mob) - var/datum/tgui_module/ship/tgui_owner = owner_component.get_coordinator() +/datum/remote_view_config/overmap_ship_control/handle_apply_visuals(mob/host_mob) + var/datum/tgui_module/ship/tgui_owner = get_component_coordinator(host_mob) if(!tgui_owner) return if(get_dist(host_mob, tgui_owner.tgui_host()) > 1 || !tgui_owner.linked) diff --git a/code/modules/vore/eating/belly_obj.dm b/code/modules/vore/eating/belly_obj.dm index dbf36d53ab0..53dbd7472d3 100644 --- a/code/modules/vore/eating/belly_obj.dm +++ b/code/modules/vore/eating/belly_obj.dm @@ -274,7 +274,7 @@ var/last_transfer_log = 0 // Prevent server message spam! var/next_transfer_log = 0 // Prevent server message spam! var/entrance_log_count = 0 // Entrance count before spawm - flags = NOREACT // We dont want bellies to start bubling nonstop due to people mixing when transfering and making different reagents + flags = NOREACT|REMOTEVIEW_ON_ENTER // We dont want bellies to start bubling nonstop due to people mixing when transfering and making different reagents //For serialization, keep this updated, required for bellies to save correctly. /obj/belly/vars_to_save()