ludic/packages/ludic.aim/ray.ludic

67 lines
2.7 KiB
Text

# ray.ludic - the ray through the crosshair against an upright cylinder, the forgiveness around a
# body, and the ground in front of a point
# t along the ray where it enters the cylinder at (cx, cz), radius r, from y0 up to y1; or -1
export function aim_ray_cyl(cx: float, y0: float, y1: float, cz: float, r: float) -> float {
let ox = AimView.x() - cx
let oz = AimView.z() - cz
let dx = AimView.fx()
let dy = AimView.fy()
let dz = AimView.fz()
let a = dx * dx + dz * dz
var t0 = -10000.0
var t1 = 10000.0
if a < 0.000001 {
if ox * ox + oz * oz > r * r { return -1.0 } # straight up or down: inside or nothing
} else {
let b = 2.0 * (ox * dx + oz * dz)
let disc = b * b - 4.0 * (a * (ox * ox + oz * oz - r * r))
if disc < 0.0 { return -1.0 }
t0 = (-b - Math.sqrt(disc)) / (2.0 * a)
t1 = (-b + Math.sqrt(disc)) / (2.0 * a)
}
let cy = AimView.y()
if Math.abs(dy) > 0.000001 {
let ta = (y0 - cy) / dy
let tb = (y1 - cy) / dy
t0 = Math.max(t0, Math.min(ta, tb))
t1 = Math.min(t1, Math.max(ta, tb))
} else if cy < y0 or cy > y1 { return -1.0 }
if t0 > t1 or t1 < 0.0 { return -1.0 }
return Math.max(t0, 0.0)
}
# How much wider than itself a thing is treated as at `dist` metres: a fixed angle (twelve pixels of
# a 1080-tall screen), so a branch twenty metres off can be aimed at and the near ones are not easy
# to hit by accident
export function aim_slack_m(dist: float) -> float { return dist * (12.0 / 540.0) * Math.tan(AimView.fov() * 0.5) }
# metres from the body to (x, z)
export function aim_range(x: float, z: float) -> float {
let dx = x - AimView.body_x()
let dz = z - AimView.body_z()
return Math.sqrt(dx * dx + dz * dz)
}
# Is the line from the camera to a point clear of the ground? Without it the crosshair reads through a
# rise; within arm's length nothing is hidden, or a prop on a slope hides behind its own slope.
export function aim_clear_to(x: float, y: float, z: float) -> bool {
let cx = AimView.x()
let cy = AimView.y()
let cz = AimView.z()
if (x - cx) * (x - cx) + (z - cz) * (z - cz) < 9.0 { return true }
for s in 1 .. 10 {
let f = float(s) / 10.0
if AimView.ground(Math.lerp(cx, x, f), Math.lerp(cz, z, f)) - 0.35 > Math.lerp(cy, y, f) { return false }
}
return true
}
# how far the ray passes from a body's axis as a share of its width: 0 dead centre, 1 at the edge
export function aim_miss(cx: float, cz: float, r: float) -> float {
let ox = AimView.x() - cx
let oz = AimView.z() - cz
let a = Math.sqrt(AimView.fx() * AimView.fx() + AimView.fz() * AimView.fz())
if a < 0.0001 { return 0.0 }
let proj = (ox * AimView.fx() + oz * AimView.fz()) / a
return Math.sqrt(Math.max(ox * ox + oz * oz - proj * proj, 0.0)) / Math.max(r, 0.01)
}