openwf/Scripts/敌人生成.pluto

266 lines
8.6 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 刷怪脚本:带 AI 与等级设置
local commands = {}
-- 全局设置:是否把坐标保留 2 位小数
local stripCoordinates = true -- 设为 false 显示完整坐标
-- 全局设置:是否在游戏内聊天输出
local showInChat = false -- 设为 false 仅控制台输出
-- 全局设置:网格间距(按网格铺开生成时的距离)
local gridSpacing = 3 -- 默认 3 米
-- 全局设置:是否全部生成在同一坐标
local spawnInSameLocation = false -- 设为 true 则同点生成
-- 回调序列号(用于生成唯一的回调名称)
local callbackSeq = 0
-- 工具函数
local function round2(n)
return math.floor(n * 100 + 0.5) / 100
end
local function countTable(t)
local n = 0
for _ in pairs(t) do n = n + 1 end
return n
end
-- 加载外部敌人名称映射表(使用 loadfile
local function loadNameOverrides()
local paths = {
"OpenWF/Scripts/EnemyNameOverrides.pluto",
"Scripts/EnemyNameOverrides.pluto",
}
for _, path in ipairs(paths) do
local chunk, err = loadfile(path)
if chunk then
local success, data = pcall(chunk)
if success and type(data) == "table" then
print("✓ 已加载 " .. countTable(data) .. " 个敌人名称映射(" .. path .. "")
return data
end
end
end
print("⚠ 警告:无法加载 EnemyNameOverrides.pluto所有敌人需要使用完整路径")
return {}
end
local nameOverrides = loadNameOverrides()
-- 基于覆盖表构建 名称<->路径 的双向索引(兼容旧格式:路径->名称,或新格式:名称->路径)
local nameToPath = {}
local pathToName = {}
local function normalizeName(s)
if not s then return s end
-- 去掉所有空白字符,避免“苦 难”“苦难 ”等变体匹配失败
return (s:gsub("%s+", ""))
end
local function addIndex(name, path)
if not name or name == "" or not path or path == "" then return end
if not nameToPath[name] then nameToPath[name] = path end
local n2 = normalizeName(name)
if n2 ~= name and not nameToPath[n2] then nameToPath[n2] = path end
if not pathToName[path] then pathToName[path] = name end
end
local function rebuildIndexes()
nameToPath = {}
pathToName = {}
for k, v in pairs(nameOverrides) do
if type(k) == "string" and type(v) == "string" and k ~= "" and v ~= "" then
if k:sub(1,1) == "/" then
addIndex(v, k)
else
addIndex(k, v)
end
end
end
end
rebuildIndexes()
local function logSpawn(enemyName, pos, level, note)
local x, y, z = pos.x, pos.y, pos.z
if stripCoordinates then
x = round2(x)
y = round2(y)
z = round2(z)
end
local tag = note and (" " .. note) or ""
local msg = string.format("已生成 '%s'%s 坐标: %.2f, %.2f, %.2f 等级: %d", enemyName, tag, x, y, z, level)
print(msg)
if showInChat then
chat_system_reply(msg)
end
end
local function resolveDisplayName(path, agent, fallback)
return pathToName[path] or fallback
end
local function computeSpawnPosition(basePosition, index, count)
if spawnInSameLocation then
return basePosition
end
local gridSize = math.ceil(math.sqrt(count))
local i0 = index - 1 -- zero-based for grid math
local col = i0 % gridSize
local row = math.floor(i0 / gridSize)
return basePosition + Vector(col * gridSpacing, 0, row * gridSpacing)
end
-- 实际生成敌人的函数(提取重复代码)
local function spawnEnemies(enemyType, path, count, level, note)
local basePosition = gRegion:GetLocalPlayerAvatar():GetPosition()
local npcMgr = gRegion:GetNpcMgr()
local agentType = Type(path)
for i = 1, count do
local spawnPosition = computeSpawnPosition(basePosition, i, count)
local agent = npcMgr:CreateAgentAtPosition(agentType, spawnPosition, ZERO_ROTATION, EMPTY_SYMBOL, level)
local displayName = resolveDisplayName(path, agent, enemyType)
logSpawn(displayName, spawnPosition, level, note)
end
end
-- 请求资源并在回调后生成
local function requestAndSpawn(enemyType, path, count, level, callbackName)
print("请求加载资源: " .. path)
gGameRules:RequestResource(path, callbackName)
local callbackReceived = false
local fallbackTriggered = false
local fallbackTimeout = 180 -- 超时 180 帧(约 3 秒 @ 60fps
repeat
while evt := owf_next_event() do
if evt.type == OWF_EVT_CALLBACK and evt.name == callbackName then
callbackReceived = true
print("已接收回调,资源加载完成: " .. path)
spawnEnemies(enemyType, path, count, level, nil)
return
end
end
fallbackTimeout = fallbackTimeout - 1
if fallbackTimeout <= 0 and not callbackReceived then
fallbackTriggered = true
print("未在超时时间内收到回调,进入直接生成回退路径。")
break
end
yield()
until callbackReceived
if fallbackTriggered then
spawnEnemies(enemyType, path, count, level, "(回退)")
elseif not callbackReceived then
print("错误:资源加载失败或不可用: " .. path)
end
end
commands["生成"] = function(text)
local parts = text:split(" ")
local enemyOrPath = parts[2]
-- 参数验证
if not enemyOrPath or enemyOrPath == "" then
local usage = "用法: /生成 <名称或路径> [数量] [等级]\n示例: /生成 苦难 5 100"
print(usage)
if showInChat then chat_system_reply(usage) end
return
end
local count = tonumber(parts[3]) or 1
local level = tonumber(parts[4]) or 10
local path = enemyOrPath
-- 若输入不是路径,尝试按"名称->路径"映射解析
if path and path:sub(1, 1) ~= "/" then
local mapped = nameToPath[path] or nameToPath[normalizeName(path)]
if mapped then
path = mapped
else
print("警告:未找到名称 '" .. enemyOrPath .. "' 的映射,将尝试作为路径使用")
end
end
print(string.format("开始生成 | 路径: %s | 数量: %d | 等级: %d", path, count, level))
callbackSeq = callbackSeq + 1
local callbackName = "streamed_enemy_" .. tostring(callbackSeq)
requestAndSpawn(enemyOrPath, path, count, level, callbackName)
end
-- 调试:查询名称映射情况
commands["查名"] = function(text)
local parts = text:split(" ")
local key = parts[2]
if not key or key == "" then
local usage = "用法: /查名 <名称>"
print(usage)
if showInChat then chat_system_reply(usage) end
return
end
local norm = normalizeName(key)
local direct = nameOverrides[key]
local directNorm = nameOverrides[norm]
local mapped = nameToPath[key] or nameToPath[norm]
print(string.format("查名 | key='%s' norm='%s' | 覆盖表[key]=%s 覆盖表[norm]=%s | 映射=%s",
key, norm, tostring(direct), tostring(directNorm), tostring(mapped)))
end
-- 重载命令(重新从外部文件加载)
commands["重载名表"] = function(text)
local newData = loadNameOverrides()
if newData and countTable(newData) > 0 then
nameOverrides = newData
rebuildIndexes()
local msg = "✓ 名称映射已重载,条目: " .. tostring(countTable(nameOverrides))
print(msg)
if showInChat then chat_system_reply(msg) end
else
local msg = "✗ 重载失败:无法加载 EnemyNameOverrides.pluto"
print(msg)
if showInChat then chat_system_reply(msg) end
end
end
-- 帮助命令
commands["帮助"] = function(text)
local help = [[
可用命令:
/生成 <名称或路径> [数量] [等级] - 生成敌人
/查名 <名称> - 查询敌人名称映射
/重载名表 - 重新加载外部名称映射文件
/帮助 - 显示此帮助信息
示例:
/生成 苦难 5 100
/生成 /Lotus/Types/Enemies/Acolytes/AreaCasterAcolyteAgent 1 150
/查名 苦难
]]
print(help)
if showInChat then chat_system_reply(help) end
end
-- 订阅聊天命令并处理事件
for prefix in commands do
chat_subscribe_prefix("/" .. prefix, true)
end
repeat
while evt := owf_next_event() do
if evt.type == OWF_EVT_SUBMIT_CHAT_MESSAGE and evt.text and evt.text:sub(1,1) == "/" then
local cmd = evt.text:match("^/([^%s]+)")
local f = cmd and commands[cmd]
if f then f(evt.text) end
end
end
until yield()
-- 示例:/生成 苦难 5 100