Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get_line() refactor #5452

Merged
merged 4 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions code/__HELPERS/#maths.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
115 changes: 37 additions & 78 deletions code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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;j++)//It'll take dxabs steps to get there
y+=dyabs
if(y>=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;j++)
x+=dxabs
if(x>=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.

Expand Down
2 changes: 1 addition & 1 deletion code/_onclick/adjacent.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/devices/binoculars.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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!"))
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/hoverpack.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/cm_aliens/XenoStructures.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/defenses/bell_tower.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/defenses/sentry.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/defenses/tesla_coil.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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!"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/movement/launching/launching.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/projectiles/gun_attachables.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/projectiles/guns/flamer/flamer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
6 changes: 3 additions & 3 deletions code/modules/projectiles/guns/flamer/flameshape.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/projectiles/guns/shotguns.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/projectiles/guns/smartgun.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/projectiles/guns/specialist/sniper.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions code/modules/projectiles/projectile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading