skip to main content
paste
bin
.ca
type · paste · share
⌘
K
Family
The bin family
pastebin.ca
hub
Share text and code with expiry and privacy controls.
imagebin.ca
Upload and share images with direct links.
filebin.ca
Drop a file and get a shareable link.
notebin.ca
Write Markdown notes with durable links.
turl.ca
Short, reputation-checked links.
attn.ca
Notifications and alerts for your services.
voicebin.ca
Record and share short voice clips.
dnsbin.ca
Inspect DNS and debug records.
Docs
Sign in
?
← back to paste
›
Edit / fork
Line to target
#2S8esRpR5J
public / public
new version
anonymous
created 13 hours ago
Expires in 3 months
9.5 KB
syntax:
lua
Your changes create a new paste linked to this one — the original is untouched.
new version
Your changes create a new paste linked to this one — the original is untouched.
Title (optional)
Filename
Syntax
lua
text
bash
c
cpp
css
diff
dockerfile
go
html
ini
java
javascript
json
kotlin
lua
makefile
markdown
nginx
php
python
ruby
rust
shellscript
sql
swift
toml
typescript
xml
yaml
Visibility
Public feed
Access
public
Expires
90 days
10 min
1 hour
1 day
7 days
30 days
90 days
custom…
Custom expiry
Change note
(optional)
This paste will be listed on the public feed. Change Visibility if you only want people with the link to see it.
Create new version
Cancel
Paste or type…
-- Monster Target Indicator(仇恨连线)—— 每只大怪用红线连接当前锁定的猎人 / 随从猎人 / 猫狗。 -- 数据:snow.enemy.EnemyCharacterBase.getCurrentTargetCharacter()。招式之间目标为空时保留 1.5 秒,换目标立即切线。 -- 目标与世界坐标在 PlayerManager.update 前置钩子内读取;re.on_frame 只投影、绘制坐标快照。 -- 使用 REFramework 背景绘图列表(旧版回退到 draw),不依赖 d2d,也不修改游戏内的名字。 local CONFIG_FILE = "TargetLines.json" local config = { enabled = true } -- 菜单只有一个开关(2026-10-31 用户:"只需要 On/off 就行,英文版") local hasCfg = false pcall(function() hasCfg = fs.glob("TargetLines%.json$")[1] ~= nil end) if hasCfg then pcall(function() local c = json.load_file(CONFIG_FILE) if type(c) == "table" then for k, v in pairs(c) do if config[k] ~= nil then config[k] = v end end end end) end local function saveConfig() pcall(json.dump_file, CONFIG_FILE, config) end if not hasCfg then saveConfig() end -- 第一次启动写一份默认配置(免得日志里每次报"读不到") local HOLD = 1.5 local MAX_TARGET_DISTANCE = 50.0 local MONSTER_Y = 1.0 local PLAYER_Y = 0.9 local OTOMO_Y = 0.5 -- 从外到内叠加静态荧光;线宽以 1080p 为基准,颜色使用 ABGR。 local LINE_LAYERS = { { width = 7, color = 0x183030FF }, { width = 5, color = 0x383030FF }, { width = 3, color = 0xFF3030FF }, { width = 1, color = 0xFFD0D0FF }, } local HEAD_NAMES = { "Head_00", "Head", "Head_01", "head" } local lines = {} -- 仅保存复制后的世界坐标,不包含游戏对象 local bossLast = {} -- 怪物地址 → { target = 托管对象引用, kind, t } local headCache = {} -- 怪物地址 → { mesh, joint };joint = false 表示没有找到骨骼 local kindCache = {} local lastStatus local diag = { hook = false, err = nil } local function clearState() lines, bossLast, headCache, kindCache = {}, {}, {}, {} lastStatus = nil end local function kindOf(ch) local addr = ch:get_address() local kind = kindCache[addr] if kind == nil then local name = ch:get_type_definition():get_full_name() if name:find("^snow%.player%.") then kind = "player" elseif name:find("^snow%.otomo%.") then kind = "otomo" else kind = false end kindCache[addr] = kind end return kind end local function offsetPosition(pos, height) if not pos then return nil end return Vector3f.new(pos.x, pos.y + height, pos.z) end local function monsterPosition(enemy, addr, newHeads, rootPos) -- 头部骨骼的查找或读取失败不影响连线,改用怪物根位置。 local ok, pos = pcall(function() local mesh = enemy:call("get_RefMesh") if not mesh then return nil end local rec = headCache[addr] if not rec or rec.mesh:get_address() ~= mesh:get_address() then local go = mesh:call("get_GameObject") local transform = go and go:call("get_Transform") local joint if transform then for _, name in ipairs(HEAD_NAMES) do joint = transform:call("getJointByName", name) if joint then break end end end rec = { mesh = mesh, joint = joint or false } end newHeads[addr] = rec if rec.joint and sdk.is_managed_object(rec.joint) then return rec.joint:call("get_Position") end end) if ok and pos then return offsetPosition(pos, 0) end return offsetPosition(rootPos, MONSTER_Y) end local function collectBoss(enemy, addr, now, newHeads) if enemy:call("checkDie") == true or enemy:call("get_IsCombatMode") ~= true then return end local last = bossLast[addr] local target = enemy:call("getCurrentTargetCharacter") local hasCurrentTarget = target ~= nil local kind if target then if not sdk.is_managed_object(target) then return end kind = kindOf(target) if not kind then return end -- 明确锁定其他对象时,不保留上一目标 elseif not last or now - last.t >= HOLD then return else target, kind = last.target, last.kind end -- 当前目标和缓冲目标每帧都用原始根位置检查距离,超距清理优先于 1.5 秒缓冲。 -- 不返回 last 即同时丢弃该怪物的连线与缓冲;读取抛错由外层逐怪 pcall 隔离。 if not sdk.is_managed_object(target) then return end local rootPos = enemy:call("get_Pos") local targetPos = target:call("get_Pos") if not rootPos or not targetPos then return end local dx, dy, dz = rootPos.x - targetPos.x, rootPos.y - targetPos.y, rootPos.z - targetPos.z if dx * dx + dy * dy + dz * dz > MAX_TARGET_DISTANCE * MAX_TARGET_DISTANCE then return end -- 仅游戏返回的有效目标能建立或刷新缓冲;沿用旧目标时不延长缓冲时间。 if hasCurrentTarget then last = { target = target, kind = kind, t = now } end local to = offsetPosition(targetPos, kind == "otomo" and OTOMO_Y or PLAYER_Y) local from = monsterPosition(enemy, addr, newHeads, rootPos) if not from then return end return last, { from = from, to = to } end -- ===== 每帧采集(PlayerManager.update 前置钩子)===== local function tick() if not config.enabled then clearState(); return end local em = sdk.get_managed_singleton("snow.enemy.EnemyManager") local pm = sdk.get_managed_singleton("snow.player.PlayerManager") local qm = sdk.get_managed_singleton("snow.QuestManager") if not em or not pm or not qm then clearState(); return end local status = qm:get_field("_QuestStatus") if status ~= lastStatus then clearState() lastStatus = status end local now = os.clock() local newLines, newLast, newHeads = {}, {}, {} for i = 0, (em:call("getBossEnemyCount") or 0) - 1 do local enemy = em:call("getBossEnemy", i) if enemy then local addr = enemy:get_address() local ok, last, line = pcall(collectBoss, enemy, addr, now, newHeads) if ok then newLast[addr] = last if line then newLines[#newLines + 1] = line end else -- 单只怪读取失败时丢弃它的旧状态,其他怪照常更新。 newHeads[addr] = nil diag.err = tostring(last) end end end bossLast, headCache, lines = newLast, newHeads, newLines end do local ok, err = pcall(function() local td = sdk.find_type_definition("snow.player.PlayerManager") local method = td and td:get_method("update") if not method then error("PlayerManager.update is unavailable") end sdk.hook(method, function() local updated, updateErr = pcall(tick) if not updated then clearState() diag.err = tostring(updateErr) end end, function(retval) return retval end) end) diag.hook = ok if not ok then diag.err = tostring(err) end end re.on_script_reset(clearState) -- ===== 内置绘图:只读取世界坐标快照 ===== local function backgroundDrawList() if type(imgui.get_background_draw_list) ~= "function" then return nil end local ok, list = pcall(function() local drawList = imgui.get_background_draw_list() if drawList and type(drawList.add_line) == "function" then return drawList end end) if ok then return list end end local function drawGlowLine(from, to, drawList, scale) local dx, dy = to.x - from.x, to.y - from.y local lengthSquared = dx * dx + dy * dy if lengthSquared == 0 then return end if drawList then for _, layer in ipairs(LINE_LAYERS) do drawList:add_line(from, to, layer.color, layer.width * scale) end return end -- 旧版没有背景绘图接口时,以四边形和端点圆形模拟同样的分层粗线。 local length = math.sqrt(lengthSquared) local nx, ny = -dy / length, dx / length for _, layer in ipairs(LINE_LAYERS) do local radius = layer.width * scale * 0.5 local ox, oy = nx * radius, ny * radius draw.filled_quad(from.x + ox, from.y + oy, from.x - ox, from.y - oy, to.x - ox, to.y - oy, to.x + ox, to.y + oy, layer.color) draw.filled_circle(from.x, from.y, radius, layer.color, 16) draw.filled_circle(to.x, to.y, radius, layer.color, 16) end end re.on_frame(function() if not config.enabled then return end local snapshot = lines local ok, err = pcall(function() local display = imgui.get_display_size() if not display or display.y <= 0 then return end local scale = display.y / 1080.0 local drawList = backgroundDrawList() for _, line in ipairs(snapshot) do local from = draw.world_to_screen(line.from) local to = draw.world_to_screen(line.to) if from and to then drawGlowLine(from, to, drawList, scale) end end end) if not ok then lines = {} diag.err = tostring(err) end end) -- ===== 菜单 ===== re.on_draw_ui(function() if not imgui.tree_node("Monster Target Indicator") then return end local changed, enabled = imgui.checkbox("Enabled", config.enabled) if changed then config.enabled = enabled clearState() saveConfig() end if not diag.hook then imgui.text("Hook failed (different game version?)") end if diag.err then imgui.text("Error: " .. diag.err) end imgui.tree_pop() end)