LUACN论坛

 找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
热搜: YJWOW MagicStone BoL

【Ni辅助保姆级入门教程】五、Ni辅助脚本编写心得

  [复制链接]
发表于 2025-12-19 16:51:36 | 显示全部楼层
感谢大佬的分享~
我先来学习下
回复 支持 反对

使用道具 举报

发表于 2025-12-21 19:38:54 | 显示全部楼层
感谢大佬的教学 一直想学NI怎么写
回复 支持 反对

使用道具 举报

发表于 2025-12-29 13:44:34 | 显示全部楼层
要写好脚本需要哪三方面的知识?
回复 支持 反对

使用道具 举报

发表于 2026-1-7 21:01:37 来自手机 | 显示全部楼层
感谢大佬传授知识,撒花撒花
回复 支持 反对

使用道具 举报

发表于 2026-3-3 13:36:37 | 显示全部楼层
前来观摩
回复 支持 反对

使用道具 举报

发表于 2026-3-3 14:35:46 | 显示全部楼层

辛苦了感谢大佬得奉献
回复 支持 反对

使用道具 举报

发表于 2026-5-16 10:13:15 | 显示全部楼层
写脚本的时候,如果发现脚本的运行逻辑和自己想要的不一样,但是又没有报错,需要多学习
回复 支持 反对

使用道具 举报

发表于 2026-5-17 23:33:04 | 显示全部楼层
谢谢分享,值得学习!!!!!!!!!!!!!!!!!!!!!
回复 支持 反对

使用道具 举报

发表于 2026-9-5 14:58:48 | 显示全部楼层




local format = format;



local p = "player"

local t = "target"





-- 搜寻附近的可攻击目标

-- maxDistance:最大距离范围,只搜寻该范围内的怪

function searchTarget(maxDistance)

    local hasTarget = ni.unit.exists(t);

    maxDistance = tonumber(maxDistance) or 40;



    if not hasTarget then

        ni.debug.print("目标搜寻:当前没有目标,寻找新目标");

    elseif UnitIsDeadOrGhost(t) then

        ni.debug.print("目标搜寻:目标死亡,寻找新目标");

    elseif not UnitCanAttack(p, t) then

        ni.debug.print("目标搜寻:目标不能攻击,寻找新目标");

    elseif not ni.player.los(t) then

        ni.debug.print("目标搜寻:目标不在视野中,寻找新目标");

    elseif ni.unit.isimmune(t) then

        ni.debug.print("目标搜寻:目标免疫攻击,寻找新目标");

    elseif ni.unit.threat(p, t) == -1 then

        ni.debug.print("目标搜寻:目标没有进入战斗,寻找新目标");

    elseif ni.unit.los(p, t) then

        -- 有可攻击的目标,后面会继续判断该目标是否是最近的目标

    end

   

    -- 找到最近的那个敌人

    local enemies = ni.player.enemiesinrange(maxDistance);

    local enemy = nil;

    for i = 1, #enemies do

        if ni.unit.los(p, enemies
.guid) and ni.unit.threat(p, enemies.guid) ~= -1 then

            if enemy == nil or enemy.distance > enemies
.distance then

                enemy = enemies
;

            end

        end

    end

   

    if enemy == nil then

        ni.debug.print("目标搜寻:附近没有可攻击目标");

        

        if hasTarget then

            ni.player.runtext("/cleartarget");

        end

        return false;

    end

   

    if hasTarget then

        if UnitGUID(t) == enemy.guid then

            ni.debug.print("目标搜寻:当前目标就是最近目标,不用切换");

            return true;

        else

            ni.player.runtext("/cleartarget");

        end

    end

   

    -- 将最近的敌人设置为目标

    ni.player.target(enemy.guid);

   

    -- 面对敌人

    if not ni.unit.isfacing(p, t, 90) then

        --ni.player.moveto(enemy.guid)

        ni.player.lookat(t);

    end

   

    ni.debug.print(format("目标搜寻:搜寻到可攻击目标【%s】", enemy.name));

    return true;

end





-- 智能输出

local function SmartAttack()

    -- 人物死亡或灵魂状态

    if UnitIsDeadOrGhost(p) then

        return;

    end

   

    -- 在载具上或移动中

    if UnitInVehicle(p) or ni.unit.ismoving(p) then

        return;

    end



    -- 如果没进战斗

    if not UnitAffectingCombat(p) then

        return;

    end

   

    -- 切换目标

    local targetExist = searchTarget(50);



    -- 玩家当前血量

    local health = ni.player.hp();

    -- 是否有活着的宠物

    local hasPet = (UnitExists("pet") and not UnitIsDead("pet"));

    -- 当前蓝量

    local mana = ni.power.current(p);



    -- 生命低于80%时向目标释放胁迫

&#160; &#160; if health < 80 and targetExist and hasPet and ni.player.petcd("胁迫") == 0 then

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("胁迫", t);

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 没有目标时尝试切换到宠物的目标

&#160; &#160; if not targetExist then

&#160; &#160;&#160; &#160;&#160;&#160;local pt = "pettarget";

&#160; &#160;&#160; &#160;&#160;&#160;if hasPet and UnitExists(pt) then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if not UnitIsDeadOrGhost(pt)

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;and UnitCanAttack(p, pt)

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;and ni.player.los(pt)

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;and not ni.unit.isimmune(t) -- 免疫攻击

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;and ni.unit.distance(p, pt) <= 31 then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; -- 切换到宠物的目标

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; ni.player.target(pt);

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;end

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 正在施法中

&#160; &#160; if ni.unit.iscasting(p) or ni.unit.ischanneling(p) then

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 强击光环

&#160; &#160; if not ni.unit.aura(p, "强击光环") and ni.spell.available("强击光环") then

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("强击光环");

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; if mana <= 10 then

&#160; &#160;&#160; &#160;&#160;&#160;-- 蓝量小于10%时切换到蝰蛇守护

&#160; &#160;&#160; &#160;&#160;&#160;if not ni.unit.aura(p, "蝰蛇守护") and ni.spell.available("蝰蛇守护") then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("蝰蛇守护");

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; elseif mana < 80 then

&#160; &#160;&#160; &#160;&#160;&#160;-- 蓝量在10-80%时如果没有龙鹰守护也没有蝰蛇守护,则使用蝰蛇守护

&#160; &#160;&#160; &#160;&#160;&#160;if not ni.unit.aura(p, "龙鹰守护") and not ni.unit.aura(p, "蝰蛇守护") and ni.spell.available("蝰蛇守护") then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("蝰蛇守护");

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; else

&#160; &#160;&#160; &#160;&#160;&#160;-- 蓝量高于80%切换到龙鹰守护

&#160; &#160;&#160; &#160;&#160;&#160;if not ni.unit.aura(p, "龙鹰守护") and ni.spell.available("龙鹰守护") then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("龙鹰守护");

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 释放误导(这里的误导是一个光环,和官服不一致)

&#160; &#160; if hasPet and not ni.unit.aura(p, "误导") and ni.spell.available("误导") then

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("误导", "pet");

&#160; &#160;&#160; &#160;&#160;&#160;return

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 目标不在前方左右90度范围内时,转向面对目标

&#160; &#160; if not ni.unit.isfacing(p, t, 90) then

&#160; &#160;&#160; &#160;&#160;&#160;ni.player.lookat(t);

&#160; &#160; end

&#160; &#160;

&#160; &#160; if ni.unit.debuffremaining(t, "毒蛇钉刺", p) <= 0 and ni.spell.available("毒蛇钉刺") then&#160;&#160;

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("毒蛇钉刺", t);

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; local targetHealth = ni.unit.hp(t);

&#160; &#160; if targetHealth < 20 and ni.spell.available("杀戮射击") then

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("杀戮射击", t);

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; if hasPet and ni.spell.available("杀戮命令") then

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("杀戮命令");

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; if ni.spell.available("奥术射击") then

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("奥术射击", t);

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end



&#160; &#160; local isBoss = ni.unit.isboss(t);

&#160; &#160; if isBoss then

&#160; &#160;&#160; &#160;&#160;&#160;if ni.unit.debuffremaining(t, "猎人印记") <= 0 and ni.spell.available("猎人印记") then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("猎人印记", t);

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; end

&#160; &#160;

&#160; &#160; if UnitPlayerControlled(t) then

&#160; &#160;&#160; &#160;&#160;&#160;if ni.unit.debuffremaining(t, "瞄准射击") <= 0 and ni.spell.available("瞄准射击") then&#160; &#160;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("瞄准射击", t);

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; else

&#160; &#160;&#160; &#160;&#160;&#160;if ni.spell.available("多重射击") then&#160; &#160;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("多重射击", t);

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; end

&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160; if hasPet and ni.spell.available("狂野怒火") then

&#160; &#160;&#160; &#160;&#160;&#160;ni.spell.cast("狂野怒火");

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160; if isBoss then

&#160; &#160;&#160; &#160;&#160;&#160;if ni.spell.available("急速射击") then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("急速射击");

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160;&#160; &#160;&#160;&#160;if ni.unit.buffremaining(p, "急速射击") <= 0 and ni.spell.available("准备就绪") then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;ni.spell.cast("准备就绪");

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return;

&#160; &#160;&#160; &#160;&#160;&#160;end&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160; end

&#160; &#160;

&#160; &#160; ni.spell.cast("稳固射击", t);

end





local needLoot = true;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;-- 是否需要拾取

local lootToolName = "范围拾取"; -- 一键拾取道具名字





-- 自动拾取

local function AutoLoot()&#160; &#160;

&#160; &#160; -- 没有被标记为可以拾取

&#160; &#160; if not needLoot then

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 在坐骑上(且是移动中)或在载具中,则放弃拾取

&#160; &#160; if (ni.unit.ismounted(p) and ni.unit.ismoving(p)) or UnitInVehicle(p) then

&#160; &#160;&#160; &#160;&#160;&#160;needLoot = false;

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 公共CD中或被眩晕了

&#160; &#160; if ni.spell.gcd() or ni.player.isstunned() then

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 使用一键拾取道具进行拾取

&#160; &#160; if ni.player.hasitem(lootToolName) then

&#160; &#160;&#160; &#160;&#160;&#160;ni.player.useitem(lootToolName);

&#160; &#160; end

&#160; &#160; needLoot = false;

end





local queue = {

&#160; &#160; "Attack",

&#160; &#160; "Loot"

};



local abilities = {

&#160; &#160; ["Attack"] = SmartAttack,

&#160; &#160; ["Loot"] = AutoLoot

};





local function CombatEventCatcher(event, ...)

&#160; &#160; -- 处理战斗日志事件,当有目标死亡时标记可以进行一次范围拾取

&#160; &#160; if event == "COMBAT_LOG_EVENT_UNFILTERED" then&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160;&#160; &#160;&#160;&#160;local _, subevent, _, sourceName, _, _, destName = ...;

&#160; &#160;&#160; &#160;&#160;&#160;if subevent == "UNIT_DIED" and destName ~= UnitName(p) then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;needLoot = true;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; end

end



local function OnLoad()

&#160; &#160; ni.vars.debug = true

&#160; &#160; -- 启动脚本的时候注册游戏事件(不清楚游戏事件的可以先了解下wow的event机制)

&#160; &#160; ni.combatlog.registerhandler("Hunter-Auto-Attack-Event", CombatEventCatcher);

end



local function OnUnload()

&#160; &#160; -- 停止脚本的时候取消注册的游戏事件

&#160; &#160; ni.combatlog.unregisterhandler("Hunter-Auto-Attack-Event");

end



ni.bootstrap.profile("智能输出-猎人", queue, abilities, OnLoad, OnUnload);








所有输出逻辑都在SmartAttack()函数里。上面的代码注释已经非常详细了,具体逻辑就不再赘述了,只解释几个ni的函数。


ni.debug.print:用于输出调试信息,当 ni.vars.debug = true 的时候,会在游戏的综合窗口里打印日志信息。
ni.player.enemiesinrange:获取玩家周围指定范围内的敌人。实际调用的是:ni.unit.enemiesinrange(unit, range)。用这个可以获取任何单位指定范围内的敌人。
ni.unit.threat(unit1, unit2):获取unit1对unit2的威胁值状态,-1表示没有威胁,也就是unit2没有进入战斗。返回值的具体含义可以参考官方函数UnitThreatSituation。

ni.player.runtext:用于执行宏命令,比如代码中的 ni.player.runtext("/cleartarget") 就是执行 /cleartarget 这个清除目标的宏。
ni.unit.isfacing(unit1, unit2, 90):检查unit2是否在unit1正前方左右90度的扇形范围内。
ni.player.lookat(unit):转向,面对unit。
ni.unit.isimmune(unit):判断指定单位是否免疫攻击。
ni.player.isstunned():判断玩家是否被眩晕了。

ni.player.los(unit):判断玩家和指定目标是否有视线碰撞(两者之间有没有卡视角)。
ni.spell.available(spell):判断指定技能是否准备好可以释放了。
ni.player.hasitem(item):判断玩家背包中是否有指定物品。
ni.player.itemcd(item):获取指定物品的cd时间。
ni.player.itemready(item):判断指定物品是否准备好可以使用了。

ni.player.useitem(item):使用背包中的指定物品。



其他ni的函数看不明白的可以去官方文档查看。


注意:官方文档更新不及时,如果发现api效果和文档描述不一致的时候,可以去 Loader.DLL.v0.0.65\addon\Core\ 下面的源码里查看具体的实现。


上面我们将输出逻辑全部写在了一个函数里,其实我们还可以利用queue顺序执行且可中断的机制,可以将逻辑拆成多个行为放到queue里。比如:
[Lua] 纯文本查看 复制代码

local queue = {

&#160; &#160; "死亡检查",

&#160; &#160; "切换目标",

&#160; &#160; "召唤宠物",

&#160; &#160; "守护检查",

&#160; &#160; "输出",

};



local abilities = {

&#160; &#160; ["死亡检查"] = function() if UnitIsDeadOrGhost("player") return true end end,

&#160; &#160; ["切换目标"] = function() ... end,

&#160; &#160; ["召唤宠物"] = function() ... end,

&#160; &#160; ["守护检查"] = function() ... end,

&#160; &#160; ["输出"] = function() ... end,

};




这样将整个输出逻辑拆分成多个小功能,看着更加的简单明了。



配置界面

上面脚本中有个自动拾取的功能。但是有时候我们不想自动拾取怎么办?是停止脚本还是临时修改脚本?
这时候可以增加一个配置界面,通过配置来开启/关闭自动拾取。见代码:
本帖隐藏的内容

[Lua] 纯文本查看 复制代码



local format = format;



local p = "player"

local t = "target"



local items = {

&#160; &#160; settingsfile = "Hunter.json",

&#160; &#160; { type = "title", text = "LR输出配置" },

&#160; &#160; { type = "separator" },

&#160; &#160; {

&#160; &#160;&#160; &#160;&#160;&#160;type = "entry",

&#160; &#160;&#160; &#160;&#160;&#160;text = "自动拾取",

&#160; &#160;&#160; &#160;&#160;&#160;tooltip = "目标死亡后进行自动拾取。",

&#160; &#160;&#160; &#160;&#160;&#160;enabled = false,

&#160; &#160;&#160; &#160;&#160;&#160;key = "AutoLoot"

&#160; &#160; },

};





local function GetSetting(name)

&#160; &#160; for k, v in ipairs(items) do

&#160; &#160;&#160; &#160;&#160;&#160;if v.type == "entry"

&#160; &#160;&#160; &#160;&#160;&#160;and v.key ~= nil

&#160; &#160;&#160; &#160;&#160;&#160;and v.key == name then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return v.value, v.enabled;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160;&#160; &#160;&#160;&#160;if v.type == "dropdown"

&#160; &#160;&#160; &#160;&#160;&#160;and v.key ~= nil

&#160; &#160;&#160; &#160;&#160;&#160;and v.key == name then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;for k2, v2 in pairs(v.menu) do

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; if v2.selected then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;return v2.value;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; end

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;end

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160;&#160; &#160;&#160;&#160;if v.type == "input"

&#160; &#160;&#160; &#160;&#160;&#160;and v.key ~= nil

&#160; &#160;&#160; &#160;&#160;&#160;and v.key == name then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;return v.value;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; end

end;





-- 搜寻附近的可攻击目标

-- maxDistance:最大距离范围,只搜寻该范围内的怪

function searchTarget(maxDistance)

&#160; &#160; ...

end





-- 智能输出

local function SmartAttack()

&#160; &#160; ...

end





local needLoot = true;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;-- 是否需要拾取

local lootToolName = "范围拾取"; -- 一键拾取道具名字





-- 自动拾取

local function AutoLoot()

&#160; &#160; local _, autoLootEnabled = GetSetting("AutoLoot")

&#160; &#160; if not autoLootEnabled then

&#160; &#160;&#160; &#160;&#160;&#160;-- 没有开启自动拾取

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 没有被标记为可以拾取

&#160; &#160; if not needLoot then

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 在坐骑上(且是移动中)或在载具中,则放弃拾取

&#160; &#160; if (ni.unit.ismounted(p) and ni.unit.ismoving(p)) or UnitInVehicle(p) then

&#160; &#160;&#160; &#160;&#160;&#160;needLoot = false;

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 公共CD中或被眩晕了

&#160; &#160; if ni.spell.gcd() or ni.player.isstunned() then

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; -- 使用一键拾取道具进行拾取

&#160; &#160; if ni.player.hasitem(lootToolName) then

&#160; &#160;&#160; &#160;&#160;&#160;ni.player.useitem(lootToolName);

&#160; &#160; end

&#160; &#160; needLoot = false;

end





local queue = {

&#160; &#160; "Attack",

&#160; &#160; "Loot"

};



local abilities = {

&#160; &#160; ["Attack"] = SmartAttack,

&#160; &#160; ["Loot"] = AutoLoot

};





local function CombatEventCatcher(event, ...)

&#160; &#160; -- 处理战斗日志事件,当有目标死亡时标记可以进行一次范围拾取

&#160; &#160; if event == "COMBAT_LOG_EVENT_UNFILTERED" then&#160; &#160;&#160; &#160;&#160;&#160;

&#160; &#160;&#160; &#160;&#160;&#160;local _, subevent, _, sourceName, _, _, destName = ...;

&#160; &#160;&#160; &#160;&#160;&#160;if subevent == "UNIT_DIED" and destName ~= UnitName(p) then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;needLoot = true;

&#160; &#160;&#160; &#160;&#160;&#160;end

&#160; &#160; end

end



local function OnLoad()

&#160; &#160; ni.vars.debug = true

&#160; &#160;

&#160; &#160; -- 启动脚本的时候注册游戏事件(不清楚游戏事件的可以先了解下wow的event机制)

&#160; &#160; ni.combatlog.registerhandler("Hunter-Auto-Attack-Event", CombatEventCatcher);

&#160; &#160;

&#160; &#160; -- 添加配置界面

&#160; &#160; ni.GUI.AddFrame("Hunter-Auto-Attack-Ui", items);

end



local function OnUnload()

&#160; &#160; -- 停止脚本的时候取消注册的游戏事件

&#160; &#160; ni.combatlog.unregisterhandler("Hunter-Auto-Attack-Event");

&#160; &#160;

&#160; &#160; -- 销毁配置界面

&#160; &#160; ni.GUI.DestroyFrame("Hunter-Auto-Attack-Ui");

end



ni.bootstrap.profile("智能输出-猎人", queue, abilities, OnLoad, OnUnload);







添加配置界面的步骤如下:
第一步:定义界面配置项内容,也就是上面代码中的items。其中:
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;settingsfile:指定保存配置的文件名,将保存在 Loader.DLL.v0.0.65\addon\Settings\ 目录下面;
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ type = "title", text = "LR输出配置" }:type是"title",表示是一个标题(其实就是一个普通文本),text是界面上显示的文本内容;
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ type = "separator" }:type是"separator",表示是一个分割线;
&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ type = "entry", ... }:type是"entry"的有多种样式,text是界面上显示的文字,tooltip是鼠标放上去后的提示文字,enabled是选项的开启/关闭状态(默认状态),key是该项配置的唯一key,后续将通过这个key来获取该配置的值。


第二步:创建配置界面,OnLoad函数里的:ni.GUI.AddFrame("Hunter-Auto-Attack-Ui", items) 表示用items的配置信息创建一个配置界面。第一个参数是一个key,后续用来销毁界面(见OnUnload)。


由于上面代码是在OnLoad中创建配置界面的,所以当脚本启动后才能看得见配置脚本。配置界面在屏幕中间顶部,鼠标放上去后配置界面就会显示出来。


第三步:获取配置(具体见上面的AutoLoot()函数)。调用上面的 GetSetting() 方法获取配置的值,参数就是每个配置项的key。

配置项目除了上面的entry,还有input和dropdown:
[Lua] 纯文本查看 复制代码

-- 一个文本输入框



{

&#160; &#160;&#160; &#160;&#160;&#160;type = "input",

&#160; &#160;&#160; &#160;&#160;&#160;value = "",

&#160; &#160;&#160; &#160;&#160;&#160;width = 225,

&#160; &#160;&#160; &#160;&#160;&#160;height = 65,

&#160; &#160;&#160; &#160;&#160;&#160;wordwrap = true,

&#160; &#160;&#160; &#160;&#160;&#160;key = "WhatToPrint"

}



-- 值的读取方式:读取value中的值

local text =
GetSetting("WhatToPrint");




[Lua] 纯文本查看 复制代码

-- 下拉选择框



{

&#160; &#160;&#160; &#160;&#160;&#160;type = "dropdown",

&#160; &#160;&#160; &#160;&#160;&#160;menu = {

&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;-- selected = true 是默认选中项



&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ selected = true,&#160;&#160;value = 1, text = "跳跃" },

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ selected = false, value = 2, text = "向前移动" },

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ selected = false, value = 3, text = "向后移动" },

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ selected = false, value = 4, text = "向右平移" },

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{ selected = false, value = 5, text = "向左平移" },

&#160; &#160;&#160; &#160;&#160;&#160;},

&#160; &#160;&#160; &#160;&#160;&#160;key = "ActionEvent"

}



-- 值的读取方式:读取value中的值

local value =
GetSetting("ActionEvent");





另外entry还有其他用法:
[Lua] 纯文本查看 复制代码

{

&#160; &#160;&#160; &#160;&#160;&#160;type = "entry",

&#160; &#160;&#160; &#160;&#160;&#160;text = ni.spell.icon("乱射") .. " AOE配置",

&#160; &#160;&#160; &#160;&#160;&#160;tooltip = "周围怪物数量达到设定值后使用|cFF00FF00乱射|r。",

&#160; &#160;&#160; &#160;&#160;&#160;enabled = false,

&#160; &#160;&#160; &#160;&#160;&#160;value = 3,

&#160; &#160;&#160; &#160;&#160;&#160;min = 2,

&#160; &#160;&#160; &#160;&#160;&#160;max = 10,

&#160; &#160;&#160; &#160;&#160;&#160;step = 1,

&#160; &#160;&#160; &#160;&#160;&#160;width = 40,

&#160; &#160;&#160; &#160;&#160;&#160;key = "AoE"

}





在配置项可以显示图标,比如上面的 text = ni.spell.icon("乱射") .. " AOE配置" 就是在文字 AOE配置 前面添加乱射技能的图标。


更加详细的可以参考 Loader.DLL.v0.0.65\addon\Rotations\Generic\GUIExample.lua。




配置使用优化
上面在读取配置信息的时候使用的 GetSetting() 函数,是通过遍历items来查看对应key的值。还有另外一种方式:
[Lua] 纯文本查看 复制代码

local enables = {

&#160; &#160;&#160;&#160;AutoLootEnabled = false,

};



local function GUICallback(key, item_type, value)

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; if item_type == "enabled" then

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;enables[key] = value;

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; end

end



local items = {

&#160; &#160;&#160; &#160;&#160; &#160;settingsfile = "Hunter.json",

&#160; &#160;&#160; &#160;&#160; &#160;callback = GUICallback,

&#160; &#160;&#160; &#160;&#160; &#160;{ type = "title", text = "LR输出配置" },

&#160; &#160;&#160; &#160;&#160; &#160;{ type = "separator" },

&#160; &#160;&#160; &#160;&#160; &#160;{

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;type = "entry",

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;text = "自动拾取",

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;tooltip = "目标死亡后进行自动拾取。",

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;enabled = enables.AutoLootEnabled,

&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;key = "AutoLootEnabled"

&#160; &#160;&#160; &#160;&#160; &#160;},

};




在 items 中可以添加一个 callback,当配置被修改后会将最新的值回调给GUICallback,然后在GUICallback中将最新的值保存起来,要使用的地方直接使用 enables.AutoLootEnabled 来判断就可以了:
[Lua] 纯文本查看 复制代码

local function AutoLoot()

&#160; &#160; -- 直接使用 enables.AutoLootEnabled 进行判断

&#160; &#160; if not enables.AutoLootEnabled then

&#160; &#160;&#160; &#160;&#160;&#160;-- 没有开启自动拾取

&#160; &#160;&#160; &#160;&#160;&#160;return;

&#160; &#160; end

&#160; &#160;

&#160; &#160; ...

end






配置项的key命名规则建议使用lua的变量命名规则:以字母或下划线开头,不包含空格。比如上面的 key = "AutoLootEnabled" 。这样我们就可以直接将key当做变量使用,比如:

[Lua] 纯文本查看 复制代码

if not enables.AutoLootEnabled then

&#160; &#160; return

end


如果包含了空格或者是中文,比如:key = "Auto&#160;&#160;Loot&#160;&#160;Enabled"。那么在使用的时候就只能这样:
[Lua] 纯文本查看 复制代码

if not enables["Auto&#160;&#160;Loot&#160;&#160;Enabled"] then

&#160; &#160; return

end
回复 支持 反对

使用道具 举报

发表于 2026-9-29 18:17:04 | 显示全部楼层
学无止境~学无止境啊~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

小黑屋|手机版|Archiver|LUACN论坛

GMT+8, 2026-10-10 09:18 AM , Processed in 0.302035 second(s), 3 queries , Gzip On, Redis On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表