# ray.ludic - the ray through the crosshair against an upright cylinder, the forgiveness around a # body, and whether anything stands 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? (the ground and every still thing: the port's) export function aim_clear_to(x: float, y: float, z: float) -> bool { return AimView.clear(AimView.x(), AimView.y(), AimView.z(), x, y, z) } # is the ray clear up to `back` short of t, where it enters a thing? Asked a body's width short, so # a solid thing (a trunk, a tent) whose collider is wider than its aim does not hide itself export function aim_clear_at(t: float, back: float) -> bool { let u = t - Math.max(back, 0.05) if u < 0.1 { return true } return aim_clear_to(AimView.x() + AimView.fx() * u, AimView.y() + AimView.fy() * u, AimView.z() + AimView.fz() * u) } # 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) }