Fixes reflection of hitscan beams against tiles (#56881)

Co-authored-by: Ranged <nickvanderkroon@gmail.com>
This commit is contained in:
NotRanged
2021-02-18 12:56:43 -08:00
committed by GitHub
co-authored by Ranged
parent f0071727d9
commit 0427c0c1ba
7 changed files with 97 additions and 61 deletions
+1 -1
View File
@@ -113,7 +113,7 @@
A.visible_message("<span class='danger'>[A] effortlessly swats the projectile aside! They can block bullets with their bare hands!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
P.firer = A
P.setAngle(rand(0, 360))//SHING
P.set_angle(rand(0, 360))//SHING
return BULLET_ACT_FORCE_PIERCE
return BULLET_ACT_HIT
+26 -10
View File
@@ -11,8 +11,8 @@
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
var/datum/point/P = new
P.x = a.x + (b.x - a.x) / 2
P.y = a.y + (b.y - a.y) / 2
P.x = a.x + (b.x - a.x) * 0.5
P.y = a.y + (b.y - a.y) * 0.5
P.z = a.z
return P
@@ -22,7 +22,8 @@
/proc/angle_between_points(datum/point/a, datum/point/b)
return ATAN2((b.y - a.y), (b.x - a.x))
/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
/// For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
/datum/position
var/x = 0
var/y = 0
var/z = 0
@@ -66,7 +67,8 @@
/datum/position/proc/return_point()
return new /datum/point(src)
/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
/// A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
/datum/point
var/x = 0
var/y = 0
var/z = 0
@@ -80,7 +82,8 @@
p.z = z
return p
/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom.
/// First argument can also be a /datum/position or /atom.
/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0)
if(istype(_x, /datum/position))
var/datum/position/P = _x
_x = P.x
@@ -99,9 +102,9 @@
/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x + 1
x = ((tile_x - 1) * world.icon_size) + world.icon_size * 0.5 + p_x + 1
if(!isnull(tile_y))
y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2 + p_y + 1
y = ((tile_y - 1) * world.icon_size) + world.icon_size * 0.5 + p_y + 1
if(!isnull(tile_z))
z = tile_z
@@ -130,10 +133,13 @@
return MODULUS(y, world.icon_size) - 16 - 1
/datum/point/vector
var/speed = 32 //pixels per iteration
/// Pixels per iteration
var/speed = 32
var/iteration = 0
var/angle = 0
var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step.
/// Calculated x movement amounts to prevent having to do trig every step.
var/mpx = 0
/// Calculated y movement amounts to prevent having to do trig every step.
var/mpy = 0
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
var/starting_y = 0
@@ -151,6 +157,15 @@
starting_y = y
starting_z = z
/// Same effect as initiliaze_location, but without setting the starting_x/y/z
/datum/point/vector/proc/set_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
x = ((tile_x - 1) * world.icon_size) + world.icon_size * 0.5 + p_x + 1
if(!isnull(tile_y))
y = ((tile_y - 1) * world.icon_size) + world.icon_size * 0.5 + p_y + 1
if(!isnull(tile_z))
z = tile_z
/datum/point/vector/copy_to(datum/point/vector/v = new)
..(v)
v.speed = speed
@@ -168,7 +183,8 @@
speed = pixel_speed
set_angle(new_angle)
/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
/// Calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
/datum/point/vector/proc/set_angle(new_angle)
if(isnull(angle))
return
angle = new_angle
+1 -1
View File
@@ -303,7 +303,7 @@
if((a_incidence_s < 90 && a_incidence_s < 90 - P.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > P.ricochet_incidence_leeway))
return FALSE
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
P.setAngle(new_angle_s)
P.set_angle(new_angle_s)
return TRUE
///Can the mover object pass this atom, while heading for the target turf
+7 -7
View File
@@ -26,9 +26,9 @@
add_overlay(deflector_overlay)
if(rotation_angle == -1)
setAngle(dir2angle(dir))
set_angle(dir2angle(dir))
else
setAngle(rotation_angle)
set_angle(rotation_angle)
if(admin)
can_rotate = FALSE
@@ -43,7 +43,7 @@
else
. += "<span class='notice'>Use screwdriver to unlock the rotation.</span>"
/obj/structure/reflector/proc/setAngle(new_angle)
/obj/structure/reflector/proc/set_angle(new_angle)
if(can_rotate)
rotation_angle = new_angle
if(deflector_overlay)
@@ -160,7 +160,7 @@
if(!user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, FALSE, !iscyborg(user)))
return
if(!isnull(new_angle))
setAngle(SIMPLIFY_DEGREES(new_angle))
set_angle(SIMPLIFY_DEGREES(new_angle))
return TRUE
/obj/structure/reflector/AltClick(mob/user)
@@ -195,7 +195,7 @@
if(abs(incidence) > 90 && abs(incidence) < 270)
return FALSE
var/new_angle = SIMPLIFY_DEGREES(rotation_angle + incidence)
P.setAngle(new_angle)
P.set_angle_centered(new_angle)
return ..()
//DOUBLE
@@ -219,7 +219,7 @@
/obj/structure/reflector/double/auto_reflect(obj/projectile/P, pdir, turf/ploc, pangle)
var/incidence = GET_ANGLE_OF_INCIDENCE(rotation_angle, (P.Angle + 180))
var/new_angle = SIMPLIFY_DEGREES(rotation_angle + incidence)
P.setAngle(new_angle)
P.set_angle_centered(new_angle)
return ..()
//BOX
@@ -241,7 +241,7 @@
anchored = TRUE
/obj/structure/reflector/box/auto_reflect(obj/projectile/P)
P.setAngle(rotation_angle)
P.set_angle_centered(rotation_angle)
return ..()
/obj/structure/reflector/ex_act()
@@ -90,7 +90,7 @@
var/new_angle_s = P.Angle + rand(120,240)
while(new_angle_s > 180) // Translate to regular projectile degrees
new_angle_s -= 360
P.setAngle(new_angle_s)
P.set_angle(new_angle_s)
return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
@@ -170,7 +170,7 @@
var/new_angle_s = P.Angle + rand(120,240)
while(new_angle_s > 180) // Translate to regular projectile degrees
new_angle_s -= 360
P.setAngle(new_angle_s)
P.set_angle(new_angle_s)
return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
+60 -40
View File
@@ -317,11 +317,11 @@
unlucky_sob = L
if(unlucky_sob)
setAngle(Get_Angle(src, unlucky_sob.loc))
set_angle(Get_Angle(src, unlucky_sob.loc))
/obj/projectile/proc/store_hitscan_collision(datum/point/pcache)
beam_segments[beam_index] = pcache
beam_index = pcache
/obj/projectile/proc/store_hitscan_collision(datum/point/point_cache)
beam_segments[beam_index] = point_cache
beam_index = point_cache
beam_segments[beam_index] = null
/obj/projectile/Bump(atom/A)
@@ -347,7 +347,7 @@
return FALSE
if(impacted[A]) // NEVER doublehit
return FALSE
var/datum/point/pcache = trajectory.copy_to()
var/datum/point/point_cache = trajectory.copy_to()
var/turf/T = get_turf(A)
if(ricochets < ricochets_max && check_ricochet_flag(A) && check_ricochet(A))
ricochets++
@@ -360,7 +360,7 @@
damage *= ricochet_decay_damage
range = decayedRange
if(hitscan)
store_hitscan_collision(pcache)
store_hitscan_collision(point_cache)
return TRUE
var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
@@ -663,9 +663,9 @@
if(QDELETED(src))
return
if(isnum(angle))
setAngle(angle)
set_angle(angle)
if(spread)
setAngle(Angle + ((rand() - 0.5) * spread))
set_angle(Angle + ((rand() - 0.5) * spread))
var/turf/starting = get_turf(src)
if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
if(isnull(xo) || isnull(yo))
@@ -673,12 +673,12 @@
qdel(src)
return
var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z)
setAngle(Get_Angle(src, target))
set_angle(Get_Angle(src, target))
original_angle = Angle
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
trajectory_ignore_forcemove = TRUE
forceMove(starting)
trajectory_ignore_forcemove = FALSE
@@ -692,23 +692,43 @@
START_PROCESSING(SSprojectiles, src)
pixel_move(1, FALSE) //move it now!
/obj/projectile/proc/setAngle(new_angle) //wrapper for overrides.
/obj/projectile/proc/set_angle(new_angle) //wrapper for overrides.
Angle = new_angle
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
transform = M
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
if(trajectory)
trajectory.set_angle(new_angle)
if(fired && hitscan && isloc(loc) && (loc != last_angle_set_hitscan_store))
last_angle_set_hitscan_store = loc
var/datum/point/pcache = new (src)
var/list/coordinates = trajectory.return_coordinates()
pcache.initialize_location(coordinates[1], coordinates[2], coordinates[3]) // Take the center of the hitscan collision tile, so it looks good on reflector boxes and the like
trajectory.initialize_location(coordinates[1], coordinates[2], coordinates[3]) // Sets the trajectory to it as well, to prevent a strange visual bug
store_hitscan_collision(pcache)
var/datum/point/point_cache = new (src)
point_cache = trajectory.copy_to()
store_hitscan_collision(point_cache)
return TRUE
/// Same as set_angle, but the reflection continues from the center of the object that reflects it instead of the side
/obj/projectile/proc/set_angle_centered(new_angle)
Angle = new_angle
if(!nondirectional_sprite)
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
if(trajectory)
trajectory.set_angle(new_angle)
var/list/coordinates = trajectory.return_coordinates()
trajectory.set_location(coordinates[1], coordinates[2], coordinates[3]) // Sets the trajectory to the center of the tile it bounced at
if(fired && hitscan && isloc(loc) && (loc != last_angle_set_hitscan_store)) // Handles hitscan projectiles
last_angle_set_hitscan_store = loc
var/datum/point/point_cache = new (src)
point_cache.initialize_location(coordinates[1], coordinates[2], coordinates[3]) // Take the center of the hitscan collision tile
store_hitscan_collision(point_cache)
return TRUE
/obj/projectile/forceMove(atom/target)
if(!isloc(target) || !isloc(loc) || !z)
return ..()
@@ -735,7 +755,7 @@
/obj/projectile/vv_edit_var(var_name, var_value)
switch(var_name)
if(NAMEOF(src, Angle))
setAngle(var_value)
set_angle(var_value)
return TRUE
else
return ..()
@@ -746,10 +766,10 @@
return TRUE
return FALSE
/obj/projectile/proc/record_hitscan_start(datum/point/pcache)
if(pcache)
/obj/projectile/proc/record_hitscan_start(datum/point/point_cache)
if(point_cache)
beam_segments = list()
beam_index = pcache
beam_index = point_cache
beam_segments[beam_index] = null //record start.
/obj/projectile/proc/process_hitscan()
@@ -772,9 +792,9 @@
return
last_projectile_move = world.time
if(!nondirectional_sprite && !hitscanning)
var/matrix/M = new
M.Turn(Angle)
transform = M
var/matrix/matrix = new
matrix.Turn(Angle)
transform = matrix
if(homing)
process_homing()
var/forcemoved = FALSE
@@ -814,7 +834,7 @@
PT.x += clamp(homing_offset_x, 1, world.maxx)
PT.y += clamp(homing_offset_y, 1, world.maxy)
var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
setAngle(Angle + clamp(angle, -homing_turn_speed, homing_turn_speed))
set_angle(Angle + clamp(angle, -homing_turn_speed, homing_turn_speed))
/obj/projectile/proc/set_homing_target(atom/A)
if(!A || (!isturf(A) && !isturf(A.loc)))
@@ -840,18 +860,18 @@
if(targloc || !params)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc) + spread)
set_angle(Get_Angle(src, targloc) + spread)
if(isliving(source) && params)
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
p_x = calculated[2]
p_y = calculated[3]
setAngle(calculated[1] + spread)
set_angle(calculated[1] + spread)
else if(targloc)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc) + spread)
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)
@@ -902,8 +922,8 @@
/obj/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE)
if(trajectory && beam_index)
var/datum/point/pcache = trajectory.copy_to()
beam_segments[beam_index] = pcache
var/datum/point/point_cache = trajectory.copy_to()
beam_segments[beam_index] = point_cache
generate_hitscan_tracers(null, null, impacting)
/obj/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3, impacting = TRUE)
@@ -917,9 +937,9 @@
var/datum/point/p = beam_segments[1]
var/atom/movable/thing = new muzzle_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(original_angle)
thing.transform = M
var/matrix/matrix = new
matrix.Turn(original_angle)
thing.transform = matrix
thing.color = color
thing.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color)
QDEL_IN(thing, duration)
@@ -927,9 +947,9 @@
var/datum/point/p = beam_segments[beam_segments[beam_segments.len]]
var/atom/movable/thing = new impact_type
p.move_atom_to_src(thing)
var/matrix/M = new
M.Turn(Angle)
thing.transform = M
var/matrix/matrix = new
matrix.Turn(Angle)
thing.transform = matrix
thing.color = color
thing.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color)
QDEL_IN(thing, duration)