mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-09 07:57:50 +00:00
* The TGS thing * Revert the 516 revert * Further segment the world/New() proc * Fixes an issue here
221 lines
7.4 KiB
Plaintext
221 lines
7.4 KiB
Plaintext
//default maximum distance of a raycast, can be overridden
|
|
#define RAY_CAST_DEFAULT_MAX_DISTANCE 50
|
|
|
|
//step size of a raycast, used to calculate one step by multiplying with floored direction vector
|
|
#define RAY_CAST_STEP 0.25
|
|
|
|
//used to tell cast() to not have a hit limit (default value of max_hits)
|
|
#define RAY_CAST_UNLIMITED_HITS 0
|
|
|
|
//Return values for raycast_hit_check
|
|
#define RAY_CAST_NO_HIT_EXIT -1
|
|
// use x < 0 for costum no_hit_exit defines
|
|
#define RAY_CAST_NO_HIT_CONTINUE 0
|
|
//use 0 <= x < 1 for costum hit_continue defines
|
|
#define RAY_CAST_HIT_CONTINUE 1
|
|
//use 1 < x for costum hit_exit defines
|
|
#define RAY_CAST_HIT_EXIT 2
|
|
|
|
/ray
|
|
var/z //the z-level we are casting our ray in
|
|
var/_vector/origin //the origin of the ray
|
|
var/_vector/origin_floored //the floored origin vector
|
|
var/_vector/direction //direction of the ray
|
|
var/original_damage //original damage of the ray when applicable
|
|
var/turf/final_turf
|
|
var/turf/previous_turf
|
|
|
|
/ray/proc/toString()
|
|
return "\[Ray\](\n- origin = " + origin.toString() + "\n- origin_floored = "+ origin_floored.toString() + "\n- direction = " + direction.toString() + "\n- z-level = " + num2text(z) + "\n)"
|
|
|
|
//use atom2vector for the origin, atoms2vector for the direction
|
|
/ray/New(var/_vector/p_origin, var/_vector/p_direction, var/z)
|
|
origin = p_origin
|
|
origin_floored = origin.floored() //to save us from calculating it all over again
|
|
direction = p_direction.chebyshev_normalized()
|
|
src.z = z
|
|
|
|
/ray/Destroy()
|
|
origin = null
|
|
origin_floored = null
|
|
direction = null
|
|
..()
|
|
|
|
//check if ray equals other ray
|
|
/ray/proc/equals(var/ray/other_ray)
|
|
return src.direction.equals(other_ray.direction) && src.hitsPoint(other_ray.origin)
|
|
|
|
//checks if another ray overlaps this one
|
|
/ray/proc/overlaps(var/ray/other_ray)
|
|
if(!(direction.equals(other_ray.direction) || direction.equals(other_ray.direction*-1))) //direction is normalized, so we can check like this
|
|
return FALSE
|
|
|
|
return hitsPoint(other_ray.origin)
|
|
|
|
//returns true if point is on our ray (can be called with a max distance)
|
|
/ray/proc/hitsPoint(var/_vector/point, var/max_distance = 0)
|
|
if(origin.equals(point)) //the easy way out
|
|
return TRUE
|
|
|
|
if(direction.x == 0)
|
|
return point.x == origin.x
|
|
|
|
if(direction.y == 0)
|
|
return point.y != origin.y
|
|
|
|
var/c_x = (point.x - origin.x) / direction.x
|
|
var/c_y = (point.y - origin.y) / direction.y
|
|
return (c_x == c_y && (!max_distance || c_x <= max_distance ))
|
|
|
|
//returns rebound angle of hit atom
|
|
//assumes atom is 1x1 octogonal box
|
|
//TODO: entry vector (0.4,1) on the surface normal (1,-1). result is (1.6, -0.2)
|
|
// wrong?
|
|
/ray/proc/getReboundOnAtom(var/rayCastHit/hit)
|
|
//calc where we hit the atom
|
|
var/_vector/hit_point = hit.point_raw
|
|
var/atom/movable/resolved_hit_atom = hit.hit_atom?.get()
|
|
var/_vector/hit_atom_loc = atom2vector(resolved_hit_atom) + new /_vector(0.5, 0.5)
|
|
|
|
var/_vector/hit_vector = hit_point - hit_atom_loc
|
|
|
|
//we assume every atom is a octogonal, hence we use all_vectors
|
|
//here we calculate the "face" of the octagonal atom we want to rebound on
|
|
var/entry_byond_dir = vector2ClosestDir(hit_vector)
|
|
var/_vector/entry_dir = dir2vector(entry_byond_dir)
|
|
|
|
return src.direction.mirrorWithNormal(entry_dir)
|
|
|
|
|
|
//gets a point along the ray
|
|
/ray/proc/getPoint(var/distance)
|
|
var/_vector/path = direction * distance
|
|
return origin + path
|
|
|
|
//inherit and override this for costum logic
|
|
//=> for possible return values check defines at top of file
|
|
//=> can also use costum return values
|
|
/ray/proc/raycast_hit_check(var/rayCastHitInfo/info)
|
|
return new /rayCastHit(info, RAY_CAST_HIT_CONTINUE)
|
|
|
|
//returns list of raycasthits
|
|
/ray/proc/cast(var/max_distance = RAY_CAST_DEFAULT_MAX_DISTANCE, var/max_hits = RAY_CAST_UNLIMITED_HITS, var/ignore_origin = TRUE)
|
|
//calculating a step and its distance to use in the loop
|
|
var/_vector/a_step = direction * RAY_CAST_STEP
|
|
var/step_distance = a_step.chebyshev_norm()
|
|
|
|
//setting up our pointer and distance to track where we are
|
|
var/_vector/pointer = new /_vector(0,0)
|
|
var/distance = 0
|
|
|
|
//positions list to easier check if we already found this position (since we are moving in tiny steps, not full numbers)
|
|
var/list/_vector/positions = list()
|
|
|
|
//our result
|
|
var/list/rayCastHit/hits = list()
|
|
|
|
var/turf/T = vector2turf(origin.floored(), z)
|
|
previous_turf = T
|
|
final_turf = T
|
|
|
|
while(distance < max_distance)
|
|
//moving one step further
|
|
pointer += a_step
|
|
distance += step_distance
|
|
|
|
//calculating our current position in world space (its two lines cause byond)
|
|
var/_vector/new_position_unfloored = origin + pointer
|
|
var/_vector/new_position = new_position_unfloored.floored()
|
|
|
|
//check if we already checked this (floored) vector
|
|
var/exists = FALSE
|
|
for(var/_vector/V in positions)
|
|
if(V.equals(new_position))
|
|
exists = TRUE
|
|
if(exists)
|
|
continue
|
|
|
|
//check if this is origin and if we should ignore it
|
|
if(ignore_origin && new_position.equals(origin_floored))
|
|
continue
|
|
|
|
//getting the turf at our current (floored) vector
|
|
T = vector2turf(new_position, z)
|
|
if (!T.density)
|
|
previous_turf = final_turf
|
|
final_turf = T
|
|
|
|
//trying hit at turf
|
|
var/rayCastHitInfo/info = new /rayCastHitInfo(src, makeweakref(T), new_position, new_position_unfloored, distance)
|
|
var/rayCastHit/hit = raycast_hit_check(info)
|
|
switch(hit.hit_code())
|
|
if(RAY_CAST_NO_HIT_EXIT)
|
|
return hits
|
|
if(RAY_CAST_NO_HIT_CONTINUE)
|
|
//nothing happens
|
|
if(RAY_CAST_HIT_CONTINUE)
|
|
hits += hit
|
|
if(RAY_CAST_HIT_EXIT)
|
|
hits += hit
|
|
return hits
|
|
|
|
if(max_hits && max_hits >= hits.len)
|
|
return hits
|
|
|
|
//trying hit on every atom inside the turf
|
|
for(var/atom/movable/A in T)
|
|
info = new /rayCastHitInfo(src, makeweakref(A), new_position, new_position_unfloored, distance)
|
|
hit = raycast_hit_check(info)
|
|
switch(hit.hit_code())
|
|
if(RAY_CAST_NO_HIT_EXIT)
|
|
return hits
|
|
if(RAY_CAST_NO_HIT_CONTINUE)
|
|
//nothing happens
|
|
if(RAY_CAST_HIT_CONTINUE)
|
|
hits += hit
|
|
if(RAY_CAST_HIT_EXIT)
|
|
hits += hit
|
|
return hits
|
|
|
|
//adding our position so we know we already checked this one
|
|
positions += new_position
|
|
|
|
CHECK_TICK
|
|
|
|
return hits
|
|
|
|
var/list/ray_draw_icon_cache = list()
|
|
|
|
/ray/proc/draw(var/draw_distance = RAY_CAST_DEFAULT_MAX_DISTANCE, var/icon='icons/obj/projectiles.dmi', var/icon_state = "laser", var/starting_distance=0.7, var/distance_from_endpoint=-0.5, var/step_size=0.5, var/lifetime=3, var/fade=TRUE, var/color_override=null, var/color_shift=null)
|
|
var/distance_pointer = starting_distance
|
|
var/angle = direction.toAngle()
|
|
var/max_distance = draw_distance - distance_from_endpoint
|
|
while(distance_pointer < max_distance)
|
|
var/_vector/point
|
|
if(distance_pointer > max_distance - step_size) //last loop
|
|
point = getPoint(max_distance - step_size)
|
|
else
|
|
point = getPoint(distance_pointer)
|
|
var/_vector/point_floored = point.floored()
|
|
|
|
var/_vector/pixels = (point - point_floored - new /_vector(0.5, 0.5)) * WORLD_ICON_SIZE
|
|
|
|
var/turf/T = locate(point_floored.x, point_floored.y, z)
|
|
|
|
var/obj/effect/overlay/beam/I = new (T, lifetime=lifetime, fade=fade, src_icon = icon, icon_state = icon_state, base_damage = original_damage, col_override = color_override, col_shift = color_shift)
|
|
I.transform = matrix().Turn(angle)
|
|
I.pixel_x = pixels.x
|
|
I.pixel_y = pixels.y
|
|
I.plane = EFFECTS_PLANE
|
|
I.layer = PROJECTILE_LAYER
|
|
|
|
distance_pointer += step_size
|
|
|
|
//helper proc to get first hit
|
|
/ray/proc/getFirstHit(var/max_distance = RAY_CAST_DEFAULT_MAX_DISTANCE, var/ignore_origin = TRUE)
|
|
var/list/result = cast(max_distance, 1, ignore_origin)
|
|
if(result.len)
|
|
return result[1]
|
|
else
|
|
return null
|