mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-31 20:53:34 +00:00
192 lines
7.3 KiB
Plaintext
192 lines
7.3 KiB
Plaintext
/// Camera eyes are remote-control mobs that can move and see throughout the global cameranet.
|
|
/// They're used in AI eyes, holograms, advanced camera consoles, abductor consoles, shuttle consoles,
|
|
/// and xenobiology consoles. When created, the user with which they are initialized will be granted control,
|
|
/// and their movements will be relayed to the camera eye instead. When destroyed, the user's control of the
|
|
/// camera eye will be released; if they were previously remote controlling another object (such as another
|
|
/// camera eye) then they will be put back in control of that object; otherwise they will return to their body.
|
|
/mob/camera/eye
|
|
name = "Inactive Camera Eye"
|
|
icon = 'icons/obj/abductor.dmi'
|
|
icon_state = "camera_target"
|
|
alpha = 127
|
|
invisibility = INVISIBILITY_HIGH
|
|
|
|
/// The list of camera chunks currently visible to the camera eye.
|
|
var/list/visible_camera_chunks = list()
|
|
/// The user controlling the eye.
|
|
var/mob/living/user
|
|
/// The thing that the user was previously remote controlling before this eye.
|
|
var/user_previous_remote_control
|
|
/// The object that created the eye.
|
|
var/origin
|
|
/// If true, speech near the camera eye will be relayed to its controller.
|
|
var/relay_speech = FALSE
|
|
/// Sets the camera eye visibility range; does not expand viewport, only affects cameranet obscuring
|
|
var/static_visibility_range = 16
|
|
/// Toggles whether this eye is detectable by AI Detectors.
|
|
var/ai_detector_visible = TRUE
|
|
/// Toggles whether the eye's icon should be visible to its user.
|
|
var/visible_icon = FALSE
|
|
/// The list of cameranets that this camera eye can see and access.
|
|
var/list/networks = list("SS13")
|
|
/// The in-memory image of the camera eye's icon.
|
|
var/image/user_image
|
|
|
|
// Camera acceleration settings
|
|
// Initially, the camera moves one turf per move. If there is no movement for
|
|
// cooldown_rate in deciseconds, the camera will reset to this movement rate.
|
|
// Every move otherwise increases sprint by acceleration_rate, until sprint
|
|
// exceeds sprint_threshold, and the movement rate increases by one per move.
|
|
// The movement rate is 1 + round(sprint / sprint_threshold).
|
|
|
|
/// The maximum sprint value - this caps acceleration
|
|
var/max_sprint = 50
|
|
/// The minimum sprint needed to increase base velocity
|
|
var/sprint_threshold = 20
|
|
/// The amount that sprint is increased per move
|
|
var/acceleration_rate = 0.5
|
|
/// Keeps track of acceleration - movement rate is 1 + round(sprint / sprint_threshold)
|
|
var/sprint = 10
|
|
/// The absolute time that sprint will reset to its initial value
|
|
var/cooldown = 0
|
|
/// The time after which sprint should be reset to its initial state, if no movements are made
|
|
var/cooldown_rate = 5
|
|
/// Toggles camera acceleration on or off.
|
|
var/acceleration = 1
|
|
|
|
/mob/camera/eye/Initialize(mapload, owner_name, camera_origin, mob/living/user)
|
|
. = ..()
|
|
name = "Camera Eye ([owner_name])"
|
|
origin = camera_origin
|
|
give_control(user)
|
|
update_visibility()
|
|
refresh_visible_icon()
|
|
if(!validate_active_cameranet())
|
|
return INITIALIZE_HINT_QDEL
|
|
|
|
/// Validates that there is an active cameranet. If strict is 0, does nothing.
|
|
/// Returns 1 if there is an active cameranet. Warns the user and returns 0 if there is not.
|
|
/mob/camera/eye/proc/validate_active_cameranet(strict = 0)
|
|
var/camera = first_active_camera()
|
|
if(strict && !camera)
|
|
to_chat(user, "<span class='warning'>ERROR: No linked and active camera network found.</span>")
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Returns the turf of the first active camera in the global cameranet.
|
|
/mob/camera/eye/proc/first_active_camera()
|
|
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
|
|
if(!C.can_use())
|
|
continue
|
|
if(length(C.network & networks))
|
|
return get_turf(C)
|
|
|
|
/// Updates what the global cameranet can see with respect to this eye and its user's client.
|
|
/mob/camera/eye/proc/update_visibility()
|
|
GLOB.cameranet.visibility(src, user.client)
|
|
|
|
/// Refreshes user_image in the user's client.images.
|
|
/mob/camera/eye/proc/refresh_visible_icon()
|
|
if(visible_icon && user.client)
|
|
user.client.images -= user_image
|
|
user_image = image(icon,loc,icon_state,FLY_LAYER)
|
|
user.client.images += user_image
|
|
|
|
/// Sets the camera eye's location to T, updates global cameranet visibility, and refreshes user_images.
|
|
/mob/camera/eye/set_loc(T)
|
|
if(user)
|
|
T = get_turf(T)
|
|
..(T)
|
|
update_visibility()
|
|
refresh_visible_icon()
|
|
|
|
/// Disables independent movement by camera eyes; camera eyes must be controlled by relaymove.
|
|
/mob/camera/eye/Move()
|
|
return FALSE
|
|
|
|
/// If `usr` is an AI, set the camera eye's location to the location of the atom clicked.
|
|
/atom/proc/move_camera_by_click()
|
|
if(is_ai(usr))
|
|
var/mob/living/silicon/ai/AI = usr
|
|
if(AI.eyeobj && (AI.client.eye == AI.eyeobj) && (AI.eyeobj.z == z))
|
|
AI.camera_follow = null
|
|
if(isturf(loc) || isturf(src))
|
|
AI.eyeobj.set_loc(src)
|
|
|
|
/// Returns the user's client, if it exists; otherwise returns null.
|
|
/mob/camera/eye/proc/get_viewer_client()
|
|
return user?.client
|
|
|
|
/// Removes obscured chunk images and user_images from the user's client.images.
|
|
/mob/camera/eye/proc/remove_images()
|
|
var/client/C = get_viewer_client()
|
|
if(!C)
|
|
return
|
|
for(var/datum/camerachunk/chunk as anything in visible_camera_chunks)
|
|
C.images -= chunk.obscured
|
|
if(visible_icon)
|
|
C.images -= user_image
|
|
|
|
/// Calls `remove_images`, changes the user's remote control from this camera eye to `user_previous_remote_control`.
|
|
/mob/camera/eye/proc/release_control()
|
|
if(!istype(user))
|
|
return
|
|
if(user.client)
|
|
user.reset_perspective(user.client.mob)
|
|
remove_images()
|
|
user.remote_control = null
|
|
if(user_previous_remote_control)
|
|
user.reset_perspective(user_previous_remote_control)
|
|
user.remote_control = user_previous_remote_control
|
|
user_previous_remote_control = null
|
|
user = null
|
|
|
|
/// Forces this eye's current user to release control, renames this eye, and grants `new_user` control of this eye.
|
|
/mob/camera/eye/proc/give_control(mob/new_user)
|
|
if(!istype(new_user))
|
|
return
|
|
release_control()
|
|
user = new_user
|
|
rename_camera(user.name)
|
|
if(istype(user.remote_control))
|
|
user_previous_remote_control = user.remote_control
|
|
user.remote_control = src
|
|
user.reset_perspective(src)
|
|
|
|
/// Renames the camera eye (only visible in observer Orbit menu)
|
|
/mob/camera/eye/proc/rename_camera(new_name)
|
|
name = "Camera Eye ([new_name])"
|
|
|
|
/// Remove this eye from all chunks containing it.
|
|
/mob/camera/eye/proc/release_chunks()
|
|
for(var/datum/camerachunk/chunk as anything in visible_camera_chunks)
|
|
chunk.remove(src)
|
|
|
|
/mob/camera/eye/Destroy()
|
|
release_control()
|
|
release_chunks()
|
|
return ..()
|
|
|
|
/// Called when the user controlling this eye attempts to move; uses camera acceleration settings.
|
|
/mob/camera/eye/relaymove(mob/user,direct)
|
|
var/initial = initial(sprint)
|
|
|
|
if(cooldown && cooldown < world.timeofday)
|
|
sprint = initial
|
|
|
|
for(var/i in 0 to sprint step sprint_threshold)
|
|
var/turf/next_step= get_turf(get_step(src, direct))
|
|
if(next_step)
|
|
set_loc(next_step)
|
|
|
|
cooldown = world.timeofday + cooldown_rate
|
|
if(acceleration)
|
|
sprint = min(sprint + acceleration_rate, max_sprint)
|
|
else
|
|
sprint = initial
|
|
|
|
/// If `relay_speech` is truthy, allows the camera eye's user to hear speech spoken at the eye's location.
|
|
/mob/camera/eye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
|
|
if(relay_speech)
|
|
user.hear_say(message_pieces, verb, italics, speaker, speech_sound, sound_vol, sound_frequency)
|