From 0397cbb8774d7162ab09c43a127d560553858efe Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Thu, 19 Jul 2018 16:32:13 -0700 Subject: [PATCH] Refactors projectile pathing (gameplay change, this will make projectiles more accurate in their linetracing), hitscan light effect improvements (#38933) cl experimental: Hitscanning tracers now have lighting effects experimental: Projectile pathing has been changed - instead of moving to a turf 32 pixels forward using byond's built in step_towards() proc, it now increments 2 pixel 16 times (subject to change). While this will increase the processing overhead, this will more or less eliminate cases of "projectiles clipping past corners in some angles but not others". --- .../subsystem/processing/projectiles.dm | 17 +++ .../projectiles/projectile_effects.dm | 7 +- .../temporary_visuals/projectiles/tracer.dm | 10 +- .../projectiles/guns/misc/beam_rifle.dm | 7 +- code/modules/projectiles/projectile.dm | 106 ++++++++++++------ 5 files changed, 105 insertions(+), 42 deletions(-) diff --git a/code/controllers/subsystem/processing/projectiles.dm b/code/controllers/subsystem/processing/projectiles.dm index 60d84c767b3..b0930d3d071 100644 --- a/code/controllers/subsystem/processing/projectiles.dm +++ b/code/controllers/subsystem/processing/projectiles.dm @@ -4,3 +4,20 @@ PROCESSING_SUBSYSTEM_DEF(projectiles) stat_tag = "PP" flags = SS_NO_INIT|SS_TICKER var/global_max_tick_moves = 10 + var/global_pixel_speed = 2 + var/global_iterations_per_move = 16 + +/datum/controller/subsystem/processing/projectiles/proc/set_pixel_speed(new_speed) + global_pixel_speed = new_speed + for(var/i in processing) + var/obj/item/projectile/P = i + if(istype(P)) //there's non projectiles on this too. + P.set_pixel_speed(new_speed) + +/datum/controller/subsystem/processing/projectiles/vv_edit_var(var_name, var_value) + switch(var_name) + if(NAMEOF(src, global_pixel_speed)) + set_pixel_speed(var_value) + return TRUE + else + return ..() diff --git a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm index dee1affd842..acb41c9a357 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm @@ -4,9 +4,6 @@ icon_state = "nothing" layer = ABOVE_MOB_LAYER anchored = TRUE - light_power = 1 - light_range = 2 - light_color = "#00ffff" mouse_opacity = MOUSE_OPACITY_TRANSPARENT appearance_flags = 0 @@ -53,3 +50,7 @@ for(var/i in 1 to increment) pixel_x += round((sin(angle_override)+16*sin(angle_override)*2), 1) pixel_y += round((cos(angle_override)+16*cos(angle_override)*2), 1) + +/obj/effect/projectile_lighting/Initialize(mapload, color, range, intensity) + . = ..() + set_light(range, intensity, color) diff --git a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm index 3b8148dba3c..44ec6839d66 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm @@ -1,10 +1,18 @@ -/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! +/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! if(!istype(starting) || !istype(ending) || !ispath(beam_type)) return var/datum/point/midpoint = point_midpoint_points(starting, ending) var/obj/effect/projectile/tracer/PB = new beam_type + if(isnull(light_color_override)) + light_color_override = color PB.apply_vars(angle_between_points(starting, ending), midpoint.return_px(), midpoint.return_py(), color, pixel_length_between_points(starting, ending) / world.icon_size, midpoint.return_turf(), 0) . = PB + if(light_range > 0 && light_intensity > 0) + var/list/turf/line = getline(starting.return_turf(), ending.return_turf()) + for(var/i in line) + var/turf/T = i + QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity), qdel_in > 0? qdel_in : 5) + line = null if(qdel_in) QDEL_IN(PB, qdel_in) diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index b4abd5878ea..fb137da2f41 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -555,10 +555,10 @@ if(highlander && istype(gun)) QDEL_LIST(gun.current_tracers) for(var/datum/point/p in beam_segments) - gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0) + gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) else for(var/datum/point/p in beam_segments) - generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration) + generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) if(cleanup) QDEL_LIST(beam_segments) beam_segments = null @@ -572,6 +572,9 @@ nodamage = TRUE damage = 0 constant_tracer = TRUE + hitscan_light_range = 0 + hitscan_light_intensity = 0 + hitscan_light_color_override = "#99ff99" /obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit(atom/target) qdel(src) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 1ae5b31b3fc..6bb475e617d 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -36,7 +36,6 @@ var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location! var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel - var/pixel_speed = 32 //pixels per move - DO NOT FUCK WITH THIS UNLESS YOU ABSOLUTELY KNOW WHAT YOU ARE DOING OR UNEXPECTED THINGS /WILL/ HAPPEN! var/Angle = 0 var/original_angle = 0 //Angle at firing var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle @@ -55,6 +54,17 @@ var/muzzle_type var/impact_type + //Fancy hitscan lighting effects! + var/hitscan_light_intensity = 1.5 + var/hitscan_light_range = 0.75 + var/hitscan_light_color_override + var/muzzle_flash_intensity = 3 + var/muzzle_flash_range = 1.5 + var/muzzle_flash_color_override + var/impact_light_intensity = 3 + var/impact_light_range = 2 + var/impact_light_color_override + //Homing var/homing = FALSE var/atom/homing_target @@ -287,8 +297,8 @@ var/datum/point/vector/current = trajectory if(!current) var/turf/T = get_turf(src) - current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, pixel_speed) - var/datum/point/vector/v = current.return_vector_after_increments(moves) + current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, SSprojectiles.global_pixel_speed) + var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move) return v.return_turf() /obj/item/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle) @@ -320,7 +330,7 @@ time_offset += MODULUS(elapsed_time_deciseconds, speed) for(var/i in 1 to required_moves) - pixel_move(required_moves) + pixel_move(1, FALSE) /obj/item/projectile/proc/fire(angle, atom/direct_target) //If no angle needs to resolve it from xo/yo! @@ -351,14 +361,14 @@ trajectory_ignore_forcemove = TRUE forceMove(starting) trajectory_ignore_forcemove = FALSE - trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, pixel_speed) + trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed) last_projectile_move = world.time fired = TRUE if(hitscan) process_hitscan() if(!(datum_flags & DF_ISPROCESSING)) START_PROCESSING(SSprojectiles, src) - pixel_move(1) //move it now! + pixel_move(1, FALSE) //move it now! /obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides. Angle = new_angle @@ -391,6 +401,20 @@ /obj/item/projectile/proc/before_z_change(atom/oldloc, atom/newloc) +/obj/item/projectile/vv_edit_var(var_name, var_value) + switch(var_name) + if(NAMEOF(src, Angle)) + setAngle(var_value) + return TRUE + else + return ..() + +/obj/item/projectile/proc/set_pixel_speed(new_speed) + if(trajectory) + trajectory.set_speed(new_speed) + return TRUE + return FALSE + /obj/item/projectile/proc/record_hitscan_start(datum/point/pcache) if(pcache) beam_segments = list() @@ -405,12 +429,14 @@ stoplag(1) continue if(safety-- <= 0) - qdel(src) - stack_trace("WARNING: [type] projectile encountered infinite recursion during hitscanning in [__FILE__]/[__LINE__]!") + if(loc) + Bump(loc) + if(!QDELETED(src)) + qdel(src) return //Kill! - pixel_move(1, 1, TRUE) + pixel_move(1, TRUE) -/obj/item/projectile/proc/pixel_move(moves, trajectory_multiplier = 1, hitscanning = FALSE) +/obj/item/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE) if(!loc || !trajectory) return last_projectile_move = world.time @@ -420,32 +446,36 @@ transform = M if(homing) process_homing() - trajectory.increment(trajectory_multiplier) - var/turf/T = trajectory.return_turf() - if(!istype(T)) - qdel(src) - return - if(T.z != loc.z) - var/old = loc - before_z_change(loc, T) - trajectory_ignore_forcemove = TRUE - forceMove(T) - trajectory_ignore_forcemove = FALSE - after_z_change(old, loc) - if(!hitscanning) - pixel_x = trajectory.return_px() - pixel_y = trajectory.return_py() - else - step_towards(src, T) - if(!hitscanning) - pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier - pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier - if(!hitscanning) + var/forcemoved = FALSE + for(var/i in 1 to SSprojectiles.global_iterations_per_move) + if(QDELETED(src)) + return + trajectory.increment(trajectory_multiplier) + var/turf/T = trajectory.return_turf() + if(!istype(T)) + qdel(src) + return + if(T.z != loc.z) + var/old = loc + before_z_change(loc, T) + trajectory_ignore_forcemove = TRUE + forceMove(T) + trajectory_ignore_forcemove = FALSE + after_z_change(old, loc) + if(!hitscanning) + pixel_x = trajectory.return_px() + pixel_y = trajectory.return_py() + forcemoved = TRUE + hitscan_last = loc + else if(T != loc) + step_towards(src, T) + hitscan_last = loc + if(can_hit_target(original, permutated)) + Bump(original) + if(!hitscanning && !forcemoved) + pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move + pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW) - if(isturf(loc)) - hitscan_last = loc - if(can_hit_target(original, permutated)) - Bump(original) Range() /obj/item/projectile/proc/process_homing() //may need speeding up in the future performance wise. @@ -561,7 +591,7 @@ return if(tracer_type) for(var/datum/point/p in beam_segments) - generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration) + generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) if(muzzle_type && duration > 0) var/datum/point/p = beam_segments[1] var/atom/movable/thing = new muzzle_type @@ -569,6 +599,8 @@ var/matrix/M = new M.Turn(original_angle) thing.transform = M + 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) if(impacting && impact_type && duration > 0) var/datum/point/p = beam_segments[beam_segments[beam_segments.len]] @@ -577,6 +609,8 @@ var/matrix/M = new M.Turn(Angle) thing.transform = M + 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) if(cleanup) cleanup_beam_segments()