From d5c03724cb7c9751637cd2b0eab1d63aedc933cf Mon Sep 17 00:00:00 2001 From: TemporalOroboros Date: Wed, 5 Jan 2022 00:55:52 -0800 Subject: [PATCH] Fixes aiming at things through cameras not aiming at the thing you aimed at. (#63654) You can click on things through cameras. This is one of the major appeals of BRPEDs. If you try to shoot at things while using a camera console to aim you will shoot at the angle between the camera you are looking through and the thing you are aiming at. This makes you aim at the thing you are aiming at from the perspective of yourself instead of the camera you are using. While it would be funny to watch a sniper gun down their own team aiming through a camera bug and shooting in the opposite direction they think they I'd prefer if it actually worked. --- .../objects/structures/deployable_turret.dm | 4 +- code/modules/power/singularity/emitter.dm | 2 +- code/modules/projectiles/projectile.dm | 124 ++++++++++++------ 3 files changed, 86 insertions(+), 44 deletions(-) diff --git a/code/game/objects/structures/deployable_turret.dm b/code/game/objects/structures/deployable_turret.dm index 01ea1a1b120..90303e727c7 100644 --- a/code/game/objects/structures/deployable_turret.dm +++ b/code/game/objects/structures/deployable_turret.dm @@ -116,7 +116,7 @@ var/turf/target_turf = get_turf(target_atom) if(istype(target_turf)) //They're hovering over something in the map. direction_track(controller, target_turf) - calculated_projectile_vars = calculate_projectile_angle_and_pixel_offsets(controller, modifiers) + calculated_projectile_vars = calculate_projectile_angle_and_pixel_offsets(controller, target_turf, modifiers) /obj/machinery/deployable_turret/proc/direction_track(mob/user, atom/targeted) if(user.incapacitated()) @@ -251,6 +251,6 @@ . = ..() var/modifiers = params2list(params) var/obj/machinery/deployable_turret/E = user.buckled - E.calculated_projectile_vars = calculate_projectile_angle_and_pixel_offsets(user, modifiers) + E.calculated_projectile_vars = calculate_projectile_angle_and_pixel_offsets(user, targeted_atom, modifiers) E.direction_track(user, targeted_atom) E.checkfire(targeted_atom, user) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 0ac6ef17530..fd376ee9cee 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -550,7 +550,7 @@ user.pixel_x = 8 user.pixel_y = -12 - emitter.last_projectile_params = calculate_projectile_angle_and_pixel_offsets(user, clickparams) + emitter.last_projectile_params = calculate_projectile_angle_and_pixel_offsets(user, null, clickparams) if(emitter.charge >= 10 && world.time > delay) emitter.charge -= 10 diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 5d2b59f47a0..7e8321e1e29 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -860,65 +860,107 @@ if(prob(50)) homing_offset_y = -homing_offset_y -//Spread is FORCED! -/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, modifiers, spread = 0) - if(!isnull(modifiers) && !islist(modifiers)) +/** + * Aims the projectile at a target. + * + * Must be passed at least one of a target or a list of click parameters. + * If only passed the click modifiers the source atom must be a mob with a client. + * + * Arguments: + * - [target][/atom]: (Optional) The thing that the projectile will be aimed at. + * - [source][/atom]: The initial location of the projectile or the thing firing it. + * - [modifiers][/list]: (Optional) A list of click parameters to apply to this operation. + * - deviation: (Optional) How the trajectory should deviate from the target in degrees. + * - //Spread is FORCED! + */ +/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, list/modifiers = null, deviation = 0) + if(!(isnull(modifiers) || islist(modifiers))) stack_trace("WARNING: Projectile [type] fired with non-list modifiers, likely was passed click params.") + modifiers = null + + var/turf/source_loc = get_turf(source) + var/turf/target_loc = get_turf(target) + if(isnull(source_loc)) + stack_trace("WARNING: Projectile [type] fired from nullspace.") + qdel(src) + return FALSE - var/turf/curloc = get_turf(source) - var/turf/targloc = get_turf(target) trajectory_ignore_forcemove = TRUE - forceMove(get_turf(source)) + forceMove(source_loc) trajectory_ignore_forcemove = FALSE - starting = get_turf(source) - original = target - if(targloc || !length(modifiers)) - yo = targloc.y - curloc.y - xo = targloc.x - curloc.x - set_angle(get_angle(src, targloc) + spread) - if(isliving(source) && length(modifiers)) - var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, modifiers) + starting = source_loc + original = target + if(length(modifiers)) + var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, target_loc && target, modifiers) + p_x = calculated[2] p_y = calculated[3] + set_angle(calculated[1] + deviation) + return TRUE - set_angle(calculated[1] + spread) - else if(targloc) - yo = targloc.y - curloc.y - xo = targloc.x - curloc.x - set_angle(get_angle(src, targloc) + spread) - else - stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!") - qdel(src) + if(target_loc) + yo = target_loc.y - source_loc.y + xo = target_loc.x - source_loc.x + set_angle(get_angle(src, target_loc) + deviation) + return TRUE -/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, modifiers) + stack_trace("WARNING: Projectile [type] fired without a target or mouse parameters to aim with.") + qdel(src) + return FALSE + +/** + * Calculates the pixel offsets and angle that a projectile should be launched at. + * + * Arguments: + * - [source][/atom]: The thing that the projectile is being shot from. + * - [target][/atom]: (Optional) The thing that the projectile is being shot at. + * - If this is not provided the source atom must be a mob with a client. + * - [modifiers][/list]: A list of click parameters used to modify the shot angle. + */ +/proc/calculate_projectile_angle_and_pixel_offsets(atom/source, atom/target, modifiers) var/p_x = 0 var/p_y = 0 var/angle = 0 if(LAZYACCESS(modifiers, ICON_X)) - p_x = text2num(LAZYACCESS(modifiers, ICON_X)) + p_x += text2num(LAZYACCESS(modifiers, ICON_X)) if(LAZYACCESS(modifiers, ICON_Y)) - p_y = text2num(LAZYACCESS(modifiers, ICON_Y)) - if(LAZYACCESS(modifiers, SCREEN_LOC)) - //Split screen-loc up into X+Pixel_X and Y+Pixel_Y - var/list/screen_loc_params = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",") + p_y += text2num(LAZYACCESS(modifiers, ICON_Y)) - //Split X+Pixel_X up into list(X, Pixel_X) - var/list/screen_loc_X = splittext(screen_loc_params[1],":") + if(target) + var/turf/source_loc = get_turf(source) + var/turf/target_loc = get_turf(target) + var/dx = ((target_loc.x - source_loc.x) * world.icon_size) + target.pixel_x + var/dy = ((target_loc.y - source_loc.y) * world.icon_size) + target.pixel_y - //Split Y+Pixel_Y up into list(Y, Pixel_Y) - var/list/screen_loc_Y = splittext(screen_loc_params[2],":") - var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 - var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 + angle = ATAN2(dy, dx) + return list(angle, p_x, p_y) - //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. - var/list/screenview = getviewsize(user.client.view) - var/screenviewX = screenview[1] * world.icon_size - var/screenviewY = screenview[2] * world.icon_size + if(!ismob(source) || !LAZYACCESS(modifiers, SCREEN_LOC)) + CRASH("Can't make trajectory calculations without a target or click modifiers and a client.") + + var/mob/user = source + if(!user.client) + CRASH("Can't make trajectory calculations without a target or click modifiers and a client.") - var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x - var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y - angle = ATAN2(y - oy, x - ox) + //Split screen-loc up into X+Pixel_X and Y+Pixel_Y + var/list/screen_loc_params = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",") + //Split X+Pixel_X up into list(X, Pixel_X) + var/list/screen_loc_X = splittext(screen_loc_params[1],":") + //Split Y+Pixel_Y up into list(Y, Pixel_Y) + var/list/screen_loc_Y = splittext(screen_loc_params[2],":") + + var/tx = (text2num(screen_loc_X[1]) - 1) * world.icon_size + text2num(screen_loc_X[2]) + var/ty = (text2num(screen_loc_Y[1]) - 1) * world.icon_size + text2num(screen_loc_Y[2]) + + //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. + var/list/screenview = getviewsize(user.client.view) + var/screenviewX = screenview[1] * world.icon_size + var/screenviewY = screenview[2] * world.icon_size + + var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x + var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y + angle = ATAN2(tx - oy, ty - ox) return list(angle, p_x, p_y) /obj/projectile/Destroy()