diff --git a/code/__HELPERS/#maths.dm b/code/__HELPERS/#maths.dm index 7eea79742148..bbdb58e57b24 100644 --- a/code/__HELPERS/#maths.dm +++ b/code/__HELPERS/#maths.dm @@ -84,53 +84,6 @@ GLOBAL_LIST_INIT(sqrtTable, list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, return "[round((powerused * 0.000001),0.001)] MW" return "[round((powerused * 0.000000001),0.0001)] GW" -/** - * Get a list of turfs in a line from `starting_atom` to `ending_atom`. - * - * Uses the ultra-fast [Bresenham Line-Drawing Algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm). - */ -/proc/get_line(atom/starting_atom, atom/ending_atom) - var/current_x_step = starting_atom.x//start at x and y, then add 1 or -1 to these to get every turf from starting_atom to ending_atom - var/current_y_step = starting_atom.y - var/starting_z = starting_atom.z - - var/list/line = list(get_turf(starting_atom))//get_turf(atom) is faster than locate(x, y, z) - - var/x_distance = ending_atom.x - current_x_step //x distance - var/y_distance = ending_atom.y - current_y_step - - var/abs_x_distance = abs(x_distance)//Absolute value of x distance - var/abs_y_distance = abs(y_distance) - - var/x_distance_sign = SIGN(x_distance) //Sign of x distance (+ or -) - var/y_distance_sign = SIGN(y_distance) - - var/x = abs_x_distance >> 1 //Counters for steps taken, setting to distance/2 - var/y = abs_y_distance >> 1 //Bit-shifting makes me l33t. It also makes get_line() unnessecarrily fast. - - if(abs_x_distance >= abs_y_distance) //x distance is greater than y - for(var/distance_counter in 0 to (abs_x_distance - 1))//It'll take abs_x_distance steps to get there - y += abs_y_distance - - if(y >= abs_x_distance) //Every abs_y_distance steps, step once in y direction - y -= abs_x_distance - current_y_step += y_distance_sign - - current_x_step += x_distance_sign //Step on in x direction - line += locate(current_x_step, current_y_step, starting_z)//Add the turf to the list - else - for(var/distance_counter in 0 to (abs_y_distance - 1)) - x += abs_x_distance - - if(x >= abs_y_distance) - x -= abs_y_distance - current_x_step += x_distance_sign - - current_y_step += y_distance_sign - line += locate(current_x_step, current_y_step, starting_z) - return line - - ///chances are 1:value. anyprob(1) will always return true /proc/anyprob(value) return (rand(1,value)==value) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 3d8b35de6304..d1696ffecc35 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1534,88 +1534,47 @@ GLOBAL_LIST_INIT(WALLITEMS, list( /proc/format_text(text) return replacetext(replacetext(text,"\proper ",""),"\improper ","") -/proc/getline(atom/M, atom/N, include_from_atom = TRUE)//Ultra-Fast Bresenham Line-Drawing Algorithm - var/px=M.x //starting x - var/py=M.y - var/line[] = list(locate(px,py,M.z)) - var/dx=N.x-px //x distance - var/dy=N.y-py - var/dxabs=abs(dx)//Absolute value of x distance - var/dyabs=abs(dy) - var/sdx=sign(dx) //Sign of x distance (+ or -) - var/sdy=sign(dy) - var/x=dxabs>>1 //Counters for steps taken, setting to distance/2 - var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. - var/j //Generic integer for counting - if(dxabs>=dyabs) //x distance is greater than y - for(j=0;j=dxabs) //Every dyabs steps, step once in y direction - y-=dxabs - py+=sdy - px+=sdx //Step on in x direction - if(j > 0 || include_from_atom) - line+=locate(px,py,M.z)//Add the turf to the list - else - for(j=0;j=dyabs) - x-=dyabs - px+=sdx - py+=sdy - if(j > 0 || include_from_atom) - line+=locate(px,py,M.z) - return line +/** + * Get a list of turfs in a line from `start_atom` to `end_atom`. + * + * Based on a linear interpolation method from [Red Blob Games](https://www.redblobgames.com/grids/line-drawing/#optimization). + * + * Arguments: + * * start_atom - starting point of the line + * * end_atom - ending point of the line + * * include_start_atom - when truthy includes start_atom in the list, default TRUE + * + * Returns: + * list - turfs from start_atom (in/exclusive) to end_atom (inclusive) + */ +/proc/get_line(atom/start_atom, atom/end_atom, include_start_atom = TRUE) + var/turf/start_turf = get_turf(start_atom) + var/turf/end_turf = get_turf(end_atom) + var/start_z = start_turf.z -//Bresenham's algorithm. This one deals efficiently with all 8 octants. -//Just don't ask me how it works. -/proc/getline2(atom/from_atom, atom/to_atom, include_from_atom = TRUE) - if(!from_atom || !to_atom) return 0 - var/list/turf/turfs = list() - - var/cur_x = from_atom.x - var/cur_y = from_atom.y - - var/w = to_atom.x - from_atom.x - var/h = to_atom.y - from_atom.y - var/dx1 = 0 - var/dx2 = 0 - var/dy1 = 0 - var/dy2 = 0 - if(w < 0) - dx1 = -1 - dx2 = -1 - else if(w > 0) - dx1 = 1 - dx2 = 1 - if(h < 0) dy1 = -1 - else if(h > 0) dy1 = 1 - var/longest = abs(w) - var/shortest = abs(h) - if(!(longest > shortest)) - longest = abs(h) - shortest = abs(w) - if(h < 0) dy2 = -1 - else if (h > 0) dy2 = 1 - dx2 = 0 - - var/numerator = longest >> 1 - var/i - for(i = 0; i <= longest; i++) - if(i > 0 || include_from_atom) - turfs += locate(cur_x,cur_y,from_atom.z) - numerator += shortest - if(!(numerator < longest)) - numerator -= longest - cur_x += dx1 - cur_y += dy1 - else - cur_x += dx2 - cur_y += dy2 + var/list/line = list() + if(include_start_atom) + line += start_turf + var/step_count = get_dist(start_turf, end_turf) + if(!step_count) + return line - return turfs + //as step_count and step size (1) are known can pre-calculate a lerp step, tiny number (1e-5) for rounding consistency + var/step_x = (end_turf.x - start_turf.x) / step_count + 1e-5 + var/step_y = (end_turf.y - start_turf.y) / step_count + 1e-5 + + //locate() truncates the fraction, adding 0.5 so its effectively rounding to nearest coords for free + var/x = start_turf.x + 0.5 + var/y = start_turf.y + 0.5 + for(var/step in 1 to (step_count - 1)) //increment then locate() skips start_turf (in 1), since end_turf is known can skip that step too (step_count - 1) + x += step_x + y += step_y + line += locate(x, y, start_z) + line += end_turf + + return line //Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm. diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index 6504db0d9f0c..c60f7ceed628 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -294,7 +294,7 @@ Quick adjacency (to turf): var/turf/curT = get_turf(A) var/is_turf = isturf(A) - for(var/turf/T in getline2(A, src)) + for(var/turf/T in get_line(A, src)) if(curT == T) continue if(T.density) diff --git a/code/game/objects/items/devices/binoculars.dm b/code/game/objects/items/devices/binoculars.dm index a9b7706bcfb7..84da7d9acff4 100644 --- a/code/game/objects/items/devices/binoculars.dm +++ b/code/game/objects/items/devices/binoculars.dm @@ -123,7 +123,7 @@ to_chat(user, SPAN_WARNING("INVALID TARGET: target must be on the surface.")) return FALSE if(user.sight & SEE_TURFS) - var/list/turf/path = getline2(user, targeted_atom, include_from_atom = FALSE) + var/list/turf/path = get_line(user, targeted_atom, include_start_atom = FALSE) for(var/turf/T in path) if(T.opacity) to_chat(user, SPAN_WARNING("There is something in the way of the laser!")) diff --git a/code/game/objects/items/hoverpack.dm b/code/game/objects/items/hoverpack.dm index 027b9d77f581..02a2d4be779a 100644 --- a/code/game/objects/items/hoverpack.dm +++ b/code/game/objects/items/hoverpack.dm @@ -180,7 +180,7 @@ var/t_dist = get_dist(user, t_turf) if(!(t_dist > max_distance)) return - var/list/turf/path = getline2(user, t_turf, FALSE) + var/list/turf/path = get_line(user, t_turf, FALSE) warning.forceMove(path[max_distance]) /obj/item/hoverpack/proc/can_use_hoverpack(mob/living/carbon/human/user) diff --git a/code/modules/cm_aliens/XenoStructures.dm b/code/modules/cm_aliens/XenoStructures.dm index 7e4f7996d3f8..1f27df25fc52 100644 --- a/code/modules/cm_aliens/XenoStructures.dm +++ b/code/modules/cm_aliens/XenoStructures.dm @@ -577,7 +577,7 @@ var/turf/last_turf = loc var/atom/temp_atom = new acid_type() var/current_pos = 1 - for(var/i in getline(src, current_mob)) + for(var/i in get_line(src, current_mob)) current_turf = i if(LinkBlocked(temp_atom, last_turf, current_turf)) qdel(temp_atom) diff --git a/code/modules/defenses/bell_tower.dm b/code/modules/defenses/bell_tower.dm index 8ef4fe913079..6939557342f0 100644 --- a/code/modules/defenses/bell_tower.dm +++ b/code/modules/defenses/bell_tower.dm @@ -110,7 +110,7 @@ if(M.get_target_lock(faction)) return - var/list/turf/path = getline2(src, linked_bell, include_from_atom = TRUE) + var/list/turf/path = get_line(src, linked_bell) for(var/turf/PT in path) if(PT.density) return diff --git a/code/modules/defenses/sentry.dm b/code/modules/defenses/sentry.dm index 999df7c22579..bfb44a38a6a5 100644 --- a/code/modules/defenses/sentry.dm +++ b/code/modules/defenses/sentry.dm @@ -404,7 +404,7 @@ targets.Remove(A) continue - var/list/turf/path = getline2(src, A, include_from_atom = FALSE) + var/list/turf/path = get_line(src, A, include_start_atom = FALSE) if(!path.len || get_dist(src, A) > sentry_range) if(A == target) target = null diff --git a/code/modules/defenses/tesla_coil.dm b/code/modules/defenses/tesla_coil.dm index 8dc8e6498ba1..663643ec1b06 100644 --- a/code/modules/defenses/tesla_coil.dm +++ b/code/modules/defenses/tesla_coil.dm @@ -125,7 +125,7 @@ if(!istype(M)) return FALSE - var/list/turf/path = getline2(src, M, include_from_atom = FALSE) + var/list/turf/path = get_line(src, M, include_start_atom = FALSE) var/blocked = FALSE for(var/turf/T in path) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/boiler/boiler_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/boiler/boiler_powers.dm index cc4e95421ab8..c749b0adb5ba 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/boiler/boiler_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/boiler/boiler_powers.dm @@ -29,7 +29,7 @@ var/range = base_range + stacks*range_per_stack var/damage = base_damage + stacks*damage_per_stack var/turfs_visited = 0 - for (var/turf/turf in getline2(get_turf(xeno), affected_atom)) + for (var/turf/turf in get_line(get_turf(xeno), affected_atom)) if(turf.density || turf.opacity) break diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm index 63cc4cb93431..569e72e3ecba 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm @@ -481,7 +481,7 @@ // Build our list of target turfs based on if (spray_type == ACID_SPRAY_LINE) - X.do_acid_spray_line(getline2(X, A, include_from_atom = FALSE), spray_effect_type, spray_distance) + X.do_acid_spray_line(get_line(X, A, include_start_atom = FALSE), spray_effect_type, spray_distance) else if (spray_type == ACID_SPRAY_CONE) X.do_acid_spray_cone(get_turf(A), spray_effect_type, spray_distance) @@ -927,7 +927,7 @@ if(distance > 2) return FALSE - var/list/turf/path = getline2(stabbing_xeno, targetted_atom, include_from_atom = FALSE) + var/list/turf/path = get_line(stabbing_xeno, targetted_atom, include_start_atom = FALSE) for(var/turf/path_turf as anything in path) if(path_turf.density) to_chat(stabbing_xeno, SPAN_WARNING("There's something blocking our strike!")) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm index 4ec301a17819..24aa94727ca9 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm @@ -189,7 +189,7 @@ if(distance > 2) return - var/list/turf/path = getline2(xeno, hit_target, include_from_atom = FALSE) + var/list/turf/path = get_line(xeno, hit_target, include_start_atom = FALSE) for(var/turf/path_turf as anything in path) if(path_turf.density) to_chat(xeno, SPAN_WARNING("There's something blocking us from striking!")) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm index 966e9ce84309..7a0b0ee6d0df 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_powers.dm @@ -13,7 +13,7 @@ return //X = xeno user, A = target atom - var/list/turf/target_turfs = getline2(source_xeno, targetted_atom, include_from_atom = FALSE) + var/list/turf/target_turfs = get_line(source_xeno, targetted_atom, include_start_atom = FALSE) var/length_of_line = LAZYLEN(target_turfs) if(length_of_line > 3) target_turfs = target_turfs.Copy(1, 4) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm index 8ed720c7ed91..88eae071a3fc 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm @@ -379,7 +379,7 @@ if(A.z != M.z) return FALSE - var/list/turf/path = getline2(M, A, include_from_atom = FALSE) + var/list/turf/path = get_line(M, A, include_start_atom = FALSE) var/distance = 0 for(var/turf/T in path) if(distance >= max_distance) diff --git a/code/modules/movement/launching/launching.dm b/code/modules/movement/launching/launching.dm index e3eccf8dd1c2..7ea15355deab 100644 --- a/code/modules/movement/launching/launching.dm +++ b/code/modules/movement/launching/launching.dm @@ -173,7 +173,7 @@ add_temp_pass_flags(pass_flags) var/turf/start_turf = get_step_towards(src, LM.target) - var/list/turf/path = getline2(start_turf, LM.target) + var/list/turf/path = get_line(start_turf, LM.target) var/last_loc = loc var/early_exit = FALSE diff --git a/code/modules/projectiles/gun_attachables.dm b/code/modules/projectiles/gun_attachables.dm index 0f3fde8f3c9b..9116f4687fcd 100644 --- a/code/modules/projectiles/gun_attachables.dm +++ b/code/modules/projectiles/gun_attachables.dm @@ -2940,7 +2940,7 @@ Defined in conflicts.dm of the #defines folder. /obj/item/attachable/attached_gun/flamer/proc/unleash_flame(atom/target, mob/living/user) set waitfor = 0 - var/list/turf/turfs = getline2(user,target) + var/list/turf/turfs = get_line(user,target) var/distance = 0 var/turf/prev_T var/stop_at_turf = FALSE diff --git a/code/modules/projectiles/guns/flamer/flamer.dm b/code/modules/projectiles/guns/flamer/flamer.dm index b9582cf5adb2..62e37e4f7c3b 100644 --- a/code/modules/projectiles/guns/flamer/flamer.dm +++ b/code/modules/projectiles/guns/flamer/flamer.dm @@ -214,7 +214,7 @@ if(R.rangefire == -1) max_range = current_mag.reagents.max_fire_rad - var/turf/temp[] = getline2(get_turf(user), get_turf(target)) + var/turf/temp[] = get_line(get_turf(user), get_turf(target)) var/turf/to_fire = temp[2] diff --git a/code/modules/projectiles/guns/flamer/flameshape.dm b/code/modules/projectiles/guns/flamer/flameshape.dm index ae6c2dec0a67..3e5e398c91e8 100644 --- a/code/modules/projectiles/guns/flamer/flameshape.dm +++ b/code/modules/projectiles/guns/flamer/flameshape.dm @@ -74,7 +74,7 @@ for(var/dirn in dirs) var/endturf = get_ranged_target_turf(F, dirn, fire_spread_amount) - var/list/turfs = getline2(source_turf, endturf) + var/list/turfs = get_line(source_turf, endturf) var/turf/prev_T = source_turf for(var/turf/T in turfs) @@ -124,7 +124,7 @@ var/distance = 1 var/stop_at_turf = FALSE - var/list/turfs = getline2(source_turf, F.target_clicked) + var/list/turfs = get_line(source_turf, F.target_clicked) for(var/turf/T in turfs) if(istype(T, /turf/open/space)) break @@ -174,7 +174,7 @@ user = F.weapon_cause_data.resolve_mob() var/unleash_dir = user.dir - var/list/turf/turfs = getline2(F, F.target_clicked) + var/list/turf/turfs = get_line(F, F.target_clicked) var/distance = 1 var/hit_dense_atom_mid = FALSE var/turf/prev_T = user.loc diff --git a/code/modules/projectiles/guns/shotguns.dm b/code/modules/projectiles/guns/shotguns.dm index c3b4906c1b29..4d753ce83b8c 100644 --- a/code/modules/projectiles/guns/shotguns.dm +++ b/code/modules/projectiles/guns/shotguns.dm @@ -1019,7 +1019,7 @@ can cause issues with ammo types getting mixed up during the burst. if(!T) //Off edge of map. throw_turfs.Remove(T) continue - var/list/turf/path = getline2(get_step_towards(src, T), T) //Same path throw code will calculate from. + var/list/turf/path = get_line(get_step_towards(src, T), T) //Same path throw code will calculate from. if(!path.len) throw_turfs.Remove(T) continue diff --git a/code/modules/projectiles/guns/smartgun.dm b/code/modules/projectiles/guns/smartgun.dm index 8ac629310132..9c1e2bbbd7ca 100644 --- a/code/modules/projectiles/guns/smartgun.dm +++ b/code/modules/projectiles/guns/smartgun.dm @@ -497,7 +497,7 @@ if((angledegree*2) > angle_list[angle]) continue - path = getline2(user, M) + path = get_line(user, M) if(path.len) var/blocked = FALSE diff --git a/code/modules/projectiles/guns/specialist/sniper.dm b/code/modules/projectiles/guns/specialist/sniper.dm index 673de1a59602..bdb0ba02f3ab 100644 --- a/code/modules/projectiles/guns/specialist/sniper.dm +++ b/code/modules/projectiles/guns/specialist/sniper.dm @@ -198,7 +198,7 @@ return TRUE /datum/action/item_action/specialist/aimed_shot/proc/check_shot_is_blocked(mob/firer, mob/target, obj/projectile/P) - var/list/turf/path = getline2(firer, target, include_from_atom = FALSE) + var/list/turf/path = get_line(firer, target, include_start_atom = FALSE) if(!path.len || get_dist(firer, target) > P.ammo.max_range) return TRUE diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index f87b86a20c1f..3ed10129f4d6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -216,7 +216,7 @@ if(ammo.bonus_projectiles_amount && ammo.bonus_projectiles_type) ammo.fire_bonus_projectiles(src) - path = getline2(starting, target_turf) + path = get_line(starting, target_turf) p_x += clamp((rand()-0.5)*scatter*3, -8, 8) p_y += clamp((rand()-0.5)*scatter*3, -8, 8) update_angle(starting, target_turf) @@ -381,7 +381,7 @@ /obj/projectile/proc/retarget(atom/new_target, keep_angle = FALSE) var/turf/current_turf = get_turf(src) - path = getline2(current_turf, new_target) + path = get_line(current_turf, new_target) path.Cut(1, 2) // remove the turf we're already on var/atom/source = keep_angle ? original : current_turf update_angle(source, new_target)