feat(packages): ludic.aim - what the crosshair is on: a ray through the middle of the screen against upright cylinders widened by a fixed angular forgiveness, the nearest one the ground does not hide, reach floored at three metres, and which thing the use key means (that, nothing and say why, or the nearest); the probe of the ground's own answer against the aim; the camera, the ground and the body through a port
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
24a35f1174
commit
dc71fc986c
10 changed files with 399 additions and 0 deletions
67
packages/ludic.aim/ray.ludic
Normal file
67
packages/ludic.aim/ray.ludic
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# 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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue