mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-16 20:52:33 +00:00
About The Pull Request I have received exemption from the freeze by Oranges. This PR was originally done for Skyrat-tg, as a bounty. Oranges took interest and Skyrat people approved taking it upstream so I did. (original PR Skyrat-SS13/Skyrat-tg#9912 ) Features: This PR adds "field of view" mechanics, which by default don't do anything. Once you pick up and wear a gas mask you'll notice a cone behind you. That cone will hide all mobs, projectiles and projectile effects. HOWEVER... hidden things in the cone (or for people who are blind or nearsighted) will play a visual effect. The fov cone shadow density can be adjusted Technicalities: Originally a component, but by Watermelon's advice I have made this a datumized behaviour instead. (semi-component) This way it is faster, less memory use etc. Once again a component People have native fov, which can be enabled by a config, by default it's off. (or by an admin verb) People have fov traits, which add the fov impairings to you, gas masks do that now. All clothes have a support for possibly applying them. Moves all things that should be hidden onto a new plane which is just above GAME_PLANE, that was we can efficiently filter them out of clients views. Being dead or extending your eye view (xenobio console) will disable the FOV mask. This PR was brought to you by Skyrat paying me aswell as rotational mathematics Why It's Good For The Game Oranges approves so it's good. Changelog cl add: Gas Masks now limit your field of view add: Added field of view mechanics. It's darkness can be tweaked by a pref add: Blind, nearsighted and fov-limited people will now see sounds as visual effects. /cl
129 lines
4.7 KiB
Plaintext
129 lines
4.7 KiB
Plaintext
/// Component which handles Field of View masking for clients. FoV attributes are at /mob/living
|
|
/datum/component/fov_handler
|
|
/// Currently applied x size of the fov masks
|
|
var/current_fov_x = BASE_FOV_MASK_X_DIMENSION
|
|
/// Currently applied y size of the fov masks
|
|
var/current_fov_y = BASE_FOV_MASK_Y_DIMENSION
|
|
/// Whether we are applying the masks now
|
|
var/applied_mask = FALSE
|
|
/// The angle of the mask we are applying
|
|
var/fov_angle = FOV_180_DEGREES
|
|
/// The blocker mask applied to a client's screen
|
|
var/atom/movable/screen/fov_blocker/blocker_mask
|
|
/// The shadow mask applied to a client's screen
|
|
var/atom/movable/screen/fov_shadow/visual_shadow
|
|
|
|
/datum/component/fov_handler/Initialize(fov_type = FOV_180_DEGREES)
|
|
if(!isliving(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
var/mob/living/mob_parent = parent
|
|
var/client/parent_client = mob_parent.client
|
|
if(!parent_client) //Love client volatility!!
|
|
qdel(src) //no QDEL hint for components, and we dont want this to print a warning regarding bad component application
|
|
return
|
|
|
|
blocker_mask = new
|
|
visual_shadow = new
|
|
visual_shadow.alpha = parent_client?.prefs.read_preference(/datum/preference/numeric/fov_darkness)
|
|
set_fov_angle(fov_type)
|
|
on_dir_change(mob_parent, mob_parent.dir, mob_parent.dir)
|
|
update_fov_size()
|
|
update_mask()
|
|
|
|
/datum/component/fov_handler/Destroy()
|
|
if(applied_mask)
|
|
remove_mask()
|
|
if(blocker_mask) // In a case of early deletion due to volatile client
|
|
QDEL_NULL(blocker_mask)
|
|
if(visual_shadow) // In a case of early deletion due to volatile client
|
|
QDEL_NULL(visual_shadow)
|
|
return ..()
|
|
|
|
/datum/component/fov_handler/proc/set_fov_angle(new_angle)
|
|
fov_angle = new_angle
|
|
blocker_mask.icon_state = "[fov_angle]"
|
|
visual_shadow.icon_state = "[fov_angle]_v"
|
|
|
|
/// Updates the size of the FOV masks by comparing them to client view size.
|
|
/datum/component/fov_handler/proc/update_fov_size()
|
|
SIGNAL_HANDLER
|
|
var/mob/parent_mob = parent
|
|
var/client/parent_client = parent_mob.client
|
|
if(!parent_client) //Love client volatility!!
|
|
return
|
|
var/list/view_size = getviewsize(parent_client.view)
|
|
if(view_size[1] == current_fov_x && view_size[2] == current_fov_y)
|
|
return
|
|
current_fov_x = BASE_FOV_MASK_X_DIMENSION
|
|
current_fov_y = BASE_FOV_MASK_Y_DIMENSION
|
|
var/matrix/new_matrix = new
|
|
var/x_shift = view_size[1] - current_fov_x
|
|
var/y_shift = view_size[2] - current_fov_y
|
|
var/x_scale = view_size[1] / current_fov_x
|
|
var/y_scale = view_size[2] / current_fov_y
|
|
current_fov_x = view_size[1]
|
|
current_fov_y = view_size[2]
|
|
visual_shadow.transform = blocker_mask.transform = new_matrix.Scale(x_scale, y_scale)
|
|
visual_shadow.transform = blocker_mask.transform = new_matrix.Translate(x_shift * 16, y_shift * 16)
|
|
|
|
/// Updates the mask application to client by checking `stat` and `eye`
|
|
/datum/component/fov_handler/proc/update_mask()
|
|
SIGNAL_HANDLER
|
|
var/mob/parent_mob = parent
|
|
var/client/parent_client = parent_mob.client
|
|
if(!parent_client) //Love client volatility!!
|
|
return
|
|
var/user_living = parent_mob != DEAD
|
|
var/user_extends_eye = parent_client.eye != parent_mob
|
|
var/should_apply_mask = user_living && !user_extends_eye
|
|
|
|
if(should_apply_mask == applied_mask)
|
|
return
|
|
|
|
if(should_apply_mask)
|
|
add_mask()
|
|
else
|
|
remove_mask()
|
|
|
|
/datum/component/fov_handler/proc/remove_mask()
|
|
var/mob/parent_mob = parent
|
|
var/client/parent_client = parent_mob.client
|
|
if(!parent_client) //Love client volatility!!
|
|
return
|
|
applied_mask = FALSE
|
|
parent_client.screen -= blocker_mask
|
|
parent_client.screen -= visual_shadow
|
|
|
|
/datum/component/fov_handler/proc/add_mask()
|
|
var/mob/parent_mob = parent
|
|
var/client/parent_client = parent_mob.client
|
|
if(!parent_client) //Love client volatility!!
|
|
return
|
|
applied_mask = TRUE
|
|
parent_client.screen += blocker_mask
|
|
parent_client.screen += visual_shadow
|
|
|
|
/// When a direction of the user changes, so do the masks
|
|
/datum/component/fov_handler/proc/on_dir_change(mob/source, old_dir, new_dir)
|
|
SIGNAL_HANDLER
|
|
blocker_mask.dir = new_dir
|
|
visual_shadow.dir = new_dir
|
|
|
|
/// When a mob logs out, delete the component
|
|
/datum/component/fov_handler/proc/mob_logout(mob/source)
|
|
SIGNAL_HANDLER
|
|
qdel(src)
|
|
|
|
/datum/component/fov_handler/RegisterWithParent()
|
|
. = ..()
|
|
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/on_dir_change)
|
|
RegisterSignal(parent, COMSIG_LIVING_DEATH, .proc/update_mask)
|
|
RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/update_mask)
|
|
RegisterSignal(parent, COMSIG_MOB_CLIENT_CHANGE_VIEW, .proc/update_fov_size)
|
|
RegisterSignal(parent, COMSIG_MOB_RESET_PERSPECTIVE, .proc/update_mask)
|
|
RegisterSignal(parent, COMSIG_MOB_LOGOUT, .proc/mob_logout)
|
|
|
|
/datum/component/fov_handler/UnregisterFromParent()
|
|
. = ..()
|
|
UnregisterSignal(parent, list(COMSIG_MOB_RESET_PERSPECTIVE, COMSIG_ATOM_DIR_CHANGE, COMSIG_LIVING_DEATH, COMSIG_LIVING_REVIVE, COMSIG_MOB_LOGOUT))
|