lane_geom.project(wps: table[], pos: table): number, number#arc length along the lane of the point nearest to pos, and its distance from the lane.
- 参数
wpstable[]waypoints in index orderpostablea point with GetX/GetY or x/y- 返回
numbernumber
A lane as a polyline: the point on it nearest to a creep, the heading there, and a walk of N units along it.
"requires": { "lane_geom": "^1.0" }local lane_geom = require("lib/lane_geom")玩家无需安装:脚本会自动带上库,取范围内的任一版本。发布前可在开发会话中修改 scripts/lib/lane_geom/ 来试验。
lane_geom.project(wps: table[], pos: table): number, number#arc length along the lane of the point nearest to pos, and its distance from the lane.
wps table[] waypoints in index orderpos table a point with GetX/GetY or x/ynumbernumberlane_geom.heading(wps: table[], pos: table, dir: integer, span?: number): number|nil, number|nil#unit heading of the lane at pos, going the way dir says (+1 = rising waypoint index,
wps table[]pos tabledir integerspan number default 150number|nilnumber|nil-1 = falling). taken over span units either side of the point, so a corner turns the
arrow over span rather than flipping it at the waypoint. nil for a lane of one point.
lane_geom.walk(wps: table[], pos: table, dist: number, dir: integer): number, number#where a creep that stands at pos is after walking dist along the lane: projected on
wps table[]pos tabledist numberdir integer +1 rising waypoint index, -1 fallingnumbernumberthe lane first, then along it, corners included. stops at the lane's end.
lane_geom.walk_point(wps: table[], pos: table, dist: number, dir: integer): table#walk as a Vector (z of the start point kept), for callers that keep Vectors.
wps table[]pos tabledist numberdir integertablelane_geom.angle(wps: table[], pos: table, dir: integer, span?: number): number|nil#heading as an angle in world axes, radians (atan2(dy, dx)); nil as heading.
wps table[]pos tabledir integerspan number default 150number|nilA lane as a polyline: the point on it nearest to a creep, the heading there, and a walk of N units
along it. The four uTEA copies of walk_along_waypoints (awareness, naga, natureprophet,
clockwerk) took the nearest waypoint and went straight to the next one, which cuts a corner and
points the wrong way near it; awareness lane_dir did the same for the minimap arrow (#191).
On the top and bot lanes of LANE_WAYPOINTS that was a heading off by more than 20 degrees on
11-14% of the lane, 61 at worst, and a fog walk of 1500 u that ended up to 250 u off the lane.
local LG = require("lib/lane_geom")
local dx, dy = LG.heading(wps, pos, -1) -- unit heading, falling waypoint index
local x, y = LG.walk(wps, pos, 1500, -1) -- after 1500 u on the lane
local v = LG.walk_point(wps, pos, 1500, -1) -- the same as a Vector, z of pos kept
local s, off = LG.project(wps, pos) -- arc length from waypoint 1, distance off the lane
wps is a list of points (Vector or {x=, y=}) in lane order. dir is +1 for rising index,
-1 for falling. heading is taken over 150 u either side of the point (span), so it turns
through a corner instead of flipping at the waypoint. Test: tools\lua\lua.exe scripts\lib\lane_geom\selftest.lua from the repo root.
1.0.0 版本的代码,即客户端拿到的内容。包内字节码由这些文件编译而来。
init.lua145 行--- the lane as a polyline: where a point stands on it, which way it runs there, and how far a
--- creep walks along it. one copy of what awareness, naga, natureprophet and clockwerk each
--- carried as "nearest waypoint, then straight to the next", which cuts every corner.
---@class lane_geom
local M = {}
local sqrt, atan2 = math.sqrt, math.atan2 or function(y, x) return math.atan(y, x) end
local function xy(p)
if p.GetX then return p:GetX(), p:GetY() end
return p.x, p.y
end
-- every waypoint as plain numbers, once per list: the callers hand over the same table each frame
local cache = setmetatable({}, { __mode = "k" })
local function flat(wps)
local f = cache[wps]
if f then return f end
f = { n = #wps, x = {}, y = {}, s = {} }
local acc = 0
for i = 1, #wps do
f.x[i], f.y[i] = xy(wps[i])
if i > 1 then
local dx, dy = f.x[i] - f.x[i - 1], f.y[i] - f.y[i - 1]
acc = acc + sqrt(dx * dx + dy * dy)
end
f.s[i] = acc
end
f.len = acc
cache[wps] = f
return f
end
-- arc length (from waypoint 1) of the point on the polyline closest to (px, py), and how far off
local function project(f, px, py)
local best_s, best_d2 = 0, math.huge
for i = 1, f.n - 1 do
local ax, ay = f.x[i], f.y[i]
local vx, vy = f.x[i + 1] - ax, f.y[i + 1] - ay
local l2 = vx * vx + vy * vy
local t = 0
if l2 > 0 then
t = ((px - ax) * vx + (py - ay) * vy) / l2
if t < 0 then t = 0 elseif t > 1 then t = 1 end
end
local qx, qy = ax + vx * t, ay + vy * t
local d2 = (px - qx) ^ 2 + (py - qy) ^ 2
if d2 < best_d2 then best_d2, best_s = d2, f.s[i] + t * sqrt(l2) end
end
return best_s, sqrt(best_d2)
end
-- the point at arc length s (clamped to the ends)
local function at(f, s)
if s <= 0 then return f.x[1], f.y[1] end
if s >= f.len then return f.x[f.n], f.y[f.n] end
for i = 1, f.n - 1 do
if s <= f.s[i + 1] then
local l = f.s[i + 1] - f.s[i]
local t = l > 0 and (s - f.s[i]) / l or 0
return f.x[i] + (f.x[i + 1] - f.x[i]) * t, f.y[i] + (f.y[i + 1] - f.y[i]) * t
end
end
return f.x[f.n], f.y[f.n]
end
--- arc length along the lane of the point nearest to `pos`, and its distance from the lane.
---@param wps table[] waypoints in index order
---@param pos table a point with GetX/GetY or x/y
---@return number s
---@return number off
function M.project(wps, pos)
if not wps or #wps < 2 or not pos then return 0, math.huge end
local px, py = xy(pos)
return project(flat(wps), px, py)
end
--- unit heading of the lane at `pos`, going the way `dir` says (+1 = rising waypoint index,
--- -1 = falling). taken over `span` units either side of the point, so a corner turns the
--- arrow over `span` rather than flipping it at the waypoint. nil for a lane of one point.
---@param wps table[]
---@param pos table
---@param dir integer
---@param span? number default 150
---@return number|nil dx
---@return number|nil dy
function M.heading(wps, pos, dir, span)
if not wps or #wps < 2 or not pos then return nil end
local f = flat(wps)
local px, py = xy(pos)
local s = project(f, px, py)
span = span or 150
local ax, ay = at(f, s - span)
local bx, by = at(f, s + span)
local dx, dy = bx - ax, by - ay
local len = sqrt(dx * dx + dy * dy)
if len < 1 then return nil end
if dir < 0 then dx, dy = -dx, -dy end
return dx / len, dy / len
end
--- where a creep that stands at `pos` is after walking `dist` along the lane: projected on
--- the lane first, then along it, corners included. stops at the lane's end.
---@param wps table[]
---@param pos table
---@param dist number
---@param dir integer +1 rising waypoint index, -1 falling
---@return number x
---@return number y
function M.walk(wps, pos, dist, dir)
local px, py = xy(pos)
if not wps or #wps < 2 then return px, py end
local f = flat(wps)
local s = project(f, px, py)
return at(f, s + (dir < 0 and -dist or dist))
end
--- `walk` as a Vector (z of the start point kept), for callers that keep Vectors.
---@param wps table[]
---@param pos table
---@param dist number
---@param dir integer
---@return table
function M.walk_point(wps, pos, dist, dir)
local x, y = M.walk(wps, pos, dist, dir)
local z = pos.GetZ and pos:GetZ() or pos.z or 0
local V = rawget(_G, "Vector")
if V then return V(x, y, z) end
return { x = x, y = y, z = z }
end
--- heading as an angle in world axes, radians (atan2(dy, dx)); nil as `heading`.
---@param wps table[]
---@param pos table
---@param dir integer
---@param span? number default 150
---@return number|nil
function M.angle(wps, pos, dir, span)
local dx, dy = M.heading(wps, pos, dir, span)
if not dx then return nil end
return atan2(dy, dx)
end
return M
pack.json2 行{ "kind": "lib", "version": "1.0.0" }
这个作品没有版本说明。
还没有已发布的脚本需要它。
Ground height and high-ground level at any point of the dota map.
"requires": { "ground": "^1.0" }local ground = require("lib/ground")Beta 0.10.54 preset: combo on F, last hit on B, every other bind moved off dota's own keys, hp cards and minimap marks on.
Where each enemy hero is while nobody sees him: the tick he left vision, his last cast in the fog, a teleport's landing, a position another script worked out, plus the enemy smokes.
"requires": { "fog_seen": "^1.0" }local fog_seen = require("lib/fog_seen")