关于TOC文件

  • Interface - 标记该插件用于wow的哪个游戏版本,如果标记值低于当前游戏版本将不能被加载,除非用户选择加载过期插件,所以,大版本更新时,最好修改这个标记值来符合新的游戏版本,可以在进入游戏后使用宏来获得当前游戏的版本号: /run print((select(4, GetBuildInfo())))
  • Title - 插件的名称,不需要和插件名一致,会被显示在选择人物界面的插件列表中。如果需要实现不同语种的客户端显示不同的文字的话(本地化机能),可以修改标签名,在标签名后加上一个"-",再跟上客户端的语种名就可以了,比如国服的实际标签名: Title-zhCN。如果客户端的语种对应的标签不存在,那么就使用默认的Title标签的值。
  • Notes - 在插件列表中,鼠标移到插件名称上时显示的提示信息。同样,可以用类似-zhCN这样的后缀来做本地化申明。
  • RequiredDeps, Dependencies, 或者任意以 “Dep” 开始的字符串 - 一些以逗号分割的其他插件的名字,表示本插件必须在这些插件都存在,且都已经被加载的情况下,才能被加载,也就是插件依赖。
  • OptionalDeps - 一些以逗号分割的其他插件的名字,表示如果这些插件存在,那么这些插件需要在本插件加载前,被加载,可选依赖。
  • LoadOnDemand - 如果这个值被设置为1,那么这个插件不会在游戏开始时就加载,而是由其他插件在需要时,另行加载,wow自带的一些插件都是如此设置的。
  • LoadWith - 一些以逗号分割的其他插件的名字, 如果本插件的LoadOnDemand为1,那么,当这些插件在游戏开始被加载时,也加载本插件。
  • LoadManagers - 一些以逗号分割的其他插件的名字, 如果这些插件都不存在,那么客户端会自动加载本插件,如果这些插件中的一个存在,那么,本插件将按照LoadOnDemand设置为1的情况处理。
  • SavedVariables - 一些以逗号分割的变量名称,这些变量一般是保存在游戏中的Lua全局环境中的表,这些变量的值在退出游戏时将被保存到硬盘上,在下一次游戏开始再被加载进入游戏,可以用来保存用户的配置信息等。这些变量的值对同一账户在同一服务器的所有角色都是通用的。
  • SavedVariablesPerCharacter - 类似SavedVariables,不过,每个角色都有各自不同的变量值,也就是说,SavedVariables适合保存通用配置,SavedVariablesPerCharacter适合保存个人配置。
  • DefaultState - 本插件安装后默认是否开启,如果设置成 “disabled”,那么只有在插件列表中勾选本插件后,才会被加载。默认值 “enabled”
  • Author - 作者名字
  • Version - 插件自身的版本号

关于lua文件加载

假设插件名为TestAddon,有两个Lua文件test.lua,another.lua`。

下面是加载插件的代码:

-- Load TestAddon
local TestAddon = {}f = loadfile("test.lua")
f("TestAddon", TestAddon)f = loadfile("another.lua")
f("TestAddon", TestAddon)
-- 上面两个参数,第一个表示插件名,第二个是一个table(这个table可以作为各个文件间交换数据的存储位置)

test.lua:

local addonName, addon = ...print(addonName .. " is loaded.")addon.TestAddon = 123

another.lua:

local addonName, addon = ...
print("TestAddon is " .. addon.TestAddon)

每次书写全局变量都需要addon.,开头也是很麻烦的事情,下面推荐一种普通插件的写法:

test.lua中:

-- 插件第一个lua文件中,以下一行代码确保代码以addon为环境,而不是_G
-- 在addon环境中可以访问_G的任意思变量;
-- 所定义的全部全局变量会保存在addon表中,而不是_G;
setfenv(1, setmetatable(select(2, ...), {__index = function(self, key) local v = _G[key];rawset(self, key, v);return vend
}))function testA()print("test case A")
end

another.lua中:

setfenv(1, select(2, ...))-- 在test.lua中定义的函数可以直接调用
testA()

插件的系统事件

local frame = CreateFrame("Frame")frame:RegsterEvent("ADDON_LOADED")frame:SetScript("OnEvent", function(self, event, ...)if event == "ADDON_LOADED" thenlocal addonName = ...print(addonName .. " is loaded.")end
end)

解释下上面的代码:

frame = CreateFrame(type[, name[, parent[, inheritFrom]])
  • type - 控件类型,本贴中,只需要用到最基本的Frame控件类型。
  • name - 控件名称,如果设定,生成的控件将以这个名字被保存到_G中,多数情况下可以省略,确保和其他插件制作的控件不会产生冲突。
  • parent - 每个界面控件都会附着到一个父界面控件,当父界面控件隐藏时,子界面元素也相应的会被隐藏,一般情况下,WOW提供了UIParent 和 WorldFrame 作为其他界面控件的父界面控件。
  • inheritFrom - 模版名称,模版具体介绍放在其他贴中。

因为我们创建的Frame控件仅用来处理系统事件,所以并不在意它如何被显示(实际上因为没有任何的显示设置,它不会被描绘,也无法被用户实际看到),我们可以省略所有参数来创建一个匿名的Frame界面控件。

frame:RegisterEvent("ADDON_LOADED")

RegisterEvent方法被用来将frame注册到监听 ADDON_LOADED 系统事件的列表中,当 ADDON_LOADED 系统事件发生时,frame会被通知,并进行处理。这个方法只有一个参数,就是需要注册的系统事件名称。

另外,如果需要取消系统事件监听的话,可以使用 UnregisterEvent 方法来取消一个系统事件的监听:

frame:UnregisterEvent("ADDON_LOADED")

wow中,使用CreateFrame创建的Frame对象本身含有一组预定义的方法,这些方法可以用来设置/获取Frame如何被显示,大小,位置等等。如果感兴趣的,可以利用Cube之类的代码编辑器运行下面的代码看看,Frame控件拥有的方法:
for k in pairs(getmetatable(UIParent).__index) do print(k) end

frame:SetScript("OnEvent", function(self, event, ...)if event == "ADDON_LOADED" thenlocal addonName = ...print( addonName .. " is loaded.")end
end)
-- https://bbs.nga.cn/read.php?&tid=6622128&pid=133288279&to=1

当系统事件发生时,系统会根据监听列表调用各个监听对象去处理系统事件,这里涉及到监听对象如何预先设置处理函数的问题。这点是通过注册控件事件来完成。

RegisterEvent 方法注册系统事件是将 frame 控件对象自己注册到系统事件的监听列表。

  • self - 控件对象本身
  • event - 系统事件名称,对于上面的例子,event只可能时 ADDON_LOADED
  • … - 系统事件参数,对于ADDON_LOADED事件来说,即被加载的插件名称

重要

使用 if 根据 event 判定的处理方式当我们需要处理大量系统事件时,会比较麻烦,所以,推荐的OnEvent处理代码如下:

-- 当某个系统事件发生时,查找控件对象本身,是否存在和系统事件同名的函数,如果有,就认为是系统事件处理函数,并调用;
-- 注意调用该函数时,控件对象作为第一个参数被传入,这种做法比较适合面向对象的处理思路frame:SetScript("OnEvent", function(self, event, ...)if type(self[event]) == "function" thenreturn self[event](self, ...)end
end)function frame:ADDON_LOADED(name)print(name .. " is loaded. ")
end

自定义宏命令(代码文件执行阶段)

以下是一个简单的宏命令:

_G.SLASH_TestCmd1 = "/test"
_G.SLASH_TestCmd2 = "/tt"_G.SlashCmdList.TestCmd = function(msg, input)print(msg)
end

_G中创建形如SLASH_ + 名称 + 数字来设置命令,在_G.SlashCmdList为 名称 设置处理函数,如下,上述命令调用;

/tt 这是一个测试

则会打印以下内容,“这是一个测试”;

插件初始化和SavedVariables(游戏进行间数据保存)

TestAddon.toc

## Interface: 50400
## Title: TestAddon
## Notes: A test addon
## DefaultState: Enabled
## LoadOnDemand: 0
## SavedVariables: TestAddonSaveTestAddon.lua

TestAddon.lua

local addonName, addon = ...-- 构建系统事件监听器
local frm = CreateFrame("Frame")
frame:SetScript("OnEvent", function (self, event, ...)   if type( self[event] ) == "function" then return self[ event ] ( self, ... ) end  end)-- 注册和处理系统事件
frm:RegisterEvent("ADDON_LOADED")function frm: ADDON_LOADED(name)-- 仅处理自身加载的情况if name == addonName then-- 获取或者创建SavedVariablesTestAddonSave = TestAddonSave or {}end
end

当退出游戏后,你可以在 WTF\Account[账户名]\SavedVariables\ 下面找到TestAddon.lua,它里面的内容是形如:

TestAddonSave = {}

PLAYER_LOGIN 进入游戏世界

AutoSell.lua:

local addonName, addon = ...-- 构建系统事件监听器
local frm = CreateFrame("Frame")
frame:SetScript("OnEvent", function (self, event, ...)   if type( self[event] ) == "function" then return self[ event ] ( self, ... ) end  end)-- 注册和处理系统事件
frm:RegisterEvent("PLAYER_LOGIN")-- 进入游戏后,监听商店界面打开事件
function frm:PLAYER_LOGIN( )self:RegisterEvent("MERCHANT_SHOW")
endfunction frm:MERCHANT_SHOW()-- 当商店界面打开时,遍历包裹,售出所有品质为0(灰色)的物品for bag = 0, NUM_BAG_FRAMES dofor slot = 1, GetContainerNumSlots(bag) dolocal itemId = GetContainerItemID(bag,slot)if itemId thenlocal _, _, itemRarity = GetItemInfo(itemId)if itemRarity == 0 thenUseContainerItem(bag,slot)endendendend
end

延时处理和定时器

/in 延时命令

/in 40 /raid 嗜血结束,该自定义命令实现了一个这样的功能,施放嗜血40秒后,通知团队时间结束;

-- 常用函数最好局部变量化,可以加快访问速度
local GetTime = GetTime
local tremove = tremove
local tinsert = tinsert
local floor = floor
local tonumber = tonumber
local strtrim = strtrim
local GetUnitName = GetUnitNamelocal TARGET_TOKEN_NOT_FOUND = TARGET_TOKEN_NOT_FOUND-- 事件监听用对象
local frm = CreateFrame("Frame")-- 隐藏frm后,OnUpdate 事件不会被触发,避免额外的CPU消耗
frm:Hide()-- 任务列表
local taskList = {}-- 任务有无标识
local hasTask = false-- 可重复使用的任务缓存表
-- 实际上同时运行的/in命令很少,每次创建一个新的table会造成额外的浪费
-- 所以复用每个table可以提升性能
local cache = {}-- 新建任务
local function newTask(delay, command)-- 优先使用缓存表,没有备用再创建新表local task = tremove(cache) or {}-- 替换 %t -> 目标名字if command:find("%%t") thencommand = command:gsub("%%t", GetUnitName("target") or TARGET_TOKEN_NOT_FOUND)end-- 替换 %f -> 焦点名字if command:find("%%f") thencommand = command:gsub("%%f", GetUnitName("focus") or TARGET_TOKEN_NOT_FOUND)end-- 补充 /if command:sub(1, 1) ~= "/" then command = "/" .. command end-- 因为以0.1秒为最小单位,所以乘10倍整数化利于后续处理task.Time = floor( ( GetTime() + delay ) * 10 )task.Command = command-- 添加任务进入任务列表taskList[task] = trueif not hasTask then-- 调整标记,并显示frm,开始监听OnUpdate事件hasTask = truefrm:Show()end
end-- 销毁任务
local function disposeTask(task)-- 移除任务taskList[task] = nil-- 放入缓存表备用tinsert(cache, task)-- 如果没有额外任务,调整标记,并隐藏frm,停止监听OnUpdate事件if not next(taskList) thenhasTask = falsefrm:Hide()end
end-- 运行后续命令,调用了WOW自带插件的函数,现在不用太在意如何实现
local function runTask(task)local command = task.Command-- 销毁任务disposeTask(task)-- 运行命令if not command then return endChatFrame10.editBox:SetText(command)ChatEdit_SendText(ChatFrame10.editBox)ChatFrame10.editBox:SetText("")
end-- 每0.1秒扫描任务表,所以用lastScan记录上次扫描时间*10 的整数值
local lastScan = 0frm:SetScript("OnUpdate", function(self, elapsed)local nowScan = floor( GetTime() * 10 )-- 因为一般情况下,两次OnUpdate间隔仅0.01-0.06左右,不满0.1秒-- 减少扫描次数来提高性能if lastScan == nowScan then return end-- 记录新时间lastScan = nowScan-- 扫描任务列表并执行for task in pairs(taskList) doif nowScan >= task.Time then-- 使用pcall确保不会因为错误中断扫描local ok, ret = pcall(runTask, task)-- 如果有错,交给其他错误程序处理if not ok then geterrorhandler()(ret) endendend
end)-- 注册命令行
SLASH_CMDIN1 = "/in"-- 处理命令
SlashCmdList.CMDIN = function(msg, input)if type(msg) == "string" then-- 分拆形如 '40.0 /raid 嗜血结束' -> '40.0', '/raid 嗜血结束'local delay, command = msg:match("([%d.]+)%s*(.+)")delay = tonumber(delay)command = strtrim(command or "")-- 检查输入,生成新的任务if delay and delay >= 0.1 and command ~= "" thenreturn newTask(delay, command)endend
end

定时器

写一个自动循环叫卖的小插件,命令名字是 /sell,/sell off 用来停止叫卖,/sell 30 /s 无脑循环叫卖 表示每 30 秒叫卖一次,那么,可以修改上面的代码:

-- 常用函数最好局部变量化,可以加快访问速度
local GetTime = GetTime
local tremove = tremove
local tinsert = tinsert
local floor = floor
local tonumber = tonumber
local strtrim = strtrim-- 事件监听用对象
local frm = CreateFrame("Frame")-- 隐藏frm后,OnUpdate 事件不会被触发,避免额外的CPU消耗
frm:Hide()-- 任务列表
local taskList = {}-- 任务有无标识
local hasTask = false-- 可重复使用的任务缓存表
-- 实际上同时运行的/in命令很少,每次创建一个新的table会造成额外的浪费
-- 所以复用每个table可以提升性能
local cache = {}-- 新建任务
local function newTask(delay, command)-- 优先使用缓存表,没有备用再创建新表local task = tremove(cache) or {}-- 补充 /if command:sub(1, 1) ~= "/" then command = "/" .. command end-- 因为以0.1秒为最小单位,所以乘10倍整数化利于后续处理task.Delay = delaytask.Time = floor( ( GetTime() + delay ) * 10 )task.Command = command-- 添加任务进入任务列表taskList[task] = trueif not hasTask then-- 调整标记,并显示frm,开始监听OnUpdate事件hasTask = truefrm:Show()end
end-- 销毁任务
local function disposeTask()if hasTask then-- 放入缓存表备用for task in pairs(taskList) dotinsert(cache, task)end-- 清空任务列表wipe(taskList)-- 调整标记,并隐藏frm,停止监听OnUpdate事件hasTask = falsefrm:Hide()end
end-- 运行后续命令,调用了WOW自带插件的函数,现在不用太在意如何实现
local function runTask(task)local command = task.Command-- 重新计算任务下次时间task.Time = floor( ( GetTime() + task.Delay ) * 10 )-- 运行命令if not command then return endChatFrame10.editBox:SetText(command)ChatEdit_SendText(ChatFrame10.editBox)ChatFrame10.editBox:SetText("")
end-- 每0.1秒扫描任务表,所以用lastScan记录上次扫描时间*10 的整数值
local lastScan = 0frm:SetScript("OnUpdate", function(self, elapsed)local nowScan = floor( GetTime() * 10 )-- 因为一般情况下,两次OnUpdate间隔仅0.01-0.06左右,不满0.1秒-- 减少扫描次数来提高性能if lastScan == nowScan then return end-- 记录新时间lastScan = nowScan-- 扫描任务列表并执行for task in pairs(taskList) doif nowScan >= task.Time then-- 使用pcall确保不会因为错误中断扫描local ok, ret = pcall(runTask, task)-- 如果有错,交给其他错误程序处理if not ok then geterrorhandler()(ret) endendend
end)-- 注册命令行
SLASH_CMDSELL1 = "/sell"-- 处理命令
SlashCmdList.CMDSELL = function(msg, input)if msg == "off" thenreturn disposeTask()elseif type(msg) == "string" then-- 分拆形如 '40.0 /say come on' -> '40.0', '/say come on'local delay, command = msg:match("([%d.]+)%s*(.+)")delay = tonumber(delay)command = strtrim(command or "")-- 检查输入,生成新的任务if delay and delay >= 0.1 and command ~= "" thenreturn newTask(delay, command)endend
end

wow插件补充说明篇相关推荐

  1. IDEA的Docker插件实战(Dockerfile篇)

    IDEA的Docker插件实战(Dockerfile篇) IntelliJ IDEA的Docker插件能帮助我们将当前工程制作成Docker镜像.运行在指定的远程机器上,是学习和开发阶段的好帮手,本文 ...

  2. vue二维码生成插件 - npm安装篇

    vue二维码生成插件 - npm安装篇 具体使用: 1. 安装: npm install vue-qr --save 2. 引入和声明 //在需要生成二维码的文件中引入比如qrCode.vue imp ...

  3. 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)

    简述: 很久没有发布Kotlin的实战相关的内容,这段时间在折腾Intellij IDEA的插件开发,折腾出了几个小插件,因为最近公司业务分离,原来堆在基础业务那边模块,都以模块的形式抽离出来,独立仓 ...

  4. WOW插件:ShortUnitFrame 2.1 发布(2007.7.17)

    作者: simonw From CWDG  2区 暗影之月, 人类牧师, 民族英雄 simonw的wow插件技术博客: http://blog.cwowaddon.com/simonw 1. 玩家窗体 ...

  5. WOW插件:ShortRobot 1.21 发布(2006.10.3)

    下载:http://files.cnblogs.com/simonw/ShortRobot.rar 作者:simonw, [2区 暗影之月 人类牧师 民族英雄] Email::i-simon AT m ...

  6. MAYA API插件编程--入门篇

    MAYA API插件编程--入门篇 作者:华文广          日期:2010.11.28 我们知道,MAYA是一个基于结点的插件式软件架构,这种开放式的软件架构是非常优秀的,它可以让用户非常方便 ...

  7. 实现 WOW 插件的简单步骤

    一.导出WOW接口文件 1.      带参数运行wow.exe –console 2.      在游戏中按下"`/~"键 3.      在魔兽控制台下输入"expo ...

  8. WOW插件:让ShortKey更好的为你工作(2006.10.4)

    ShortKey使用说明:http://simonw.cnblogs.com/archive/2005/10/20/258271.html 自ShortKey发布以来得到不少朋友的支持当然也包括疑问, ...

  9. IDEA全家桶式讲解 | IDEA安装、使用、断点调试、Git、插件 (第二篇)

    目录 一:JavaEE阶段需要掌握的IDEA技能 1. 配置Tomcat 2. 配置Maven 3. IDEA连接数据库 4. 方便的特殊功能 5. 断点调试(重点) 6. IDEA中常用Git协同开 ...

  10. 【转载】企业级服务器设计与实现经验之插件系统基础篇

    最初之所以要采用插件的形式进行开发,主要是为了解决功能服务的"热插拔"问题,在决定采用"框架+插件"的方式进行设计后,我们就更进一步,打算将一个个可以分割开来的 ...

最新文章

  1. 物联网技术与应用(第1-2课时)(cont.)
  2. GCD Game 博弈论-Nim-质因数应用-质因数个数预处理
  3. 裸眼 3D 是什么效果?
  4. mongodb添加创建修改时间_mongodb副本集生产环境下部署案例,推荐一个主两个从三台机器...
  5. 阿里小程序云应用上线了,有哪些看点?
  6. 去哪儿-15-keep-alive
  7. 深度学习快速参考 | iBooker·ApacheCN
  8. Mac上如何在“系统信息”中查看是否兼容新产品?
  9. classpath路径浅谈
  10. imei模拟修改_自动修改android模拟设备号imei的小程序
  11. 需求文档(PRD文档)
  12. Extjs 例外被抛出且未被接住
  13. selenium 实战之 A级纳税人信息
  14. 如何使用Google图片反向搜索图片
  15. Jquery 漂浮广告的插件
  16. 【前端实战项目】手把手教你做出小米商城官网(HTML+CSS)
  17. learn the python in hard way习题36~39的附加习题
  18. 计算机图形学--实时光线追踪
  19. Kaldi-Timit 训练
  20. 安装autoconf

热门文章

  1. android 设置壁纸上下显示不全,默认锁屏壁纸及锁屏壁纸被拉伸显示不全的问题...
  2. html <font>中英文常用字体和颜色总结
  3. mysql 词频分析_09 使用python完成词频统计
  4. 7-13 统计工龄 (20分)
  5. 移动硬盘计算机无图标,移动硬盘不显示图标的处理方法
  6. java linux 消息队列_我的第一个Linux程序----利用消息队列来实现IPC
  7. 多少开发人员 饿了么_做个美团(饿了么)网站需要多少钱?
  8. (三)Lucene中Index.ANALYZED分词相关
  9. Excel自动插入jpg图片或png图片 VBA 工具 模块
  10. Mybatis 批量更新运行异常,数据库 postgres