听dj战歌,就上傲气战歌网!2015年传奇家族玩家最喜爱的家族战歌网
战歌推荐:战歌网 战歌网dj Mc战歌网 DJ战歌网下载 激情战歌-冰雪战歌网 客服Q:350317
新闻搜索:

发几个老文章 是介绍如何制作插件的

作者:     来源:    发表时间:2011-04-17 13:19

Advance Programming

WOW进阶讨论 (Advance Programming)

原文链接
作者: cherngje(宇桐)    
日期: 2004/12/31 02:10:07

Chapter One

玩了WOW有一段时间了,除了游戏本身所提供的基本功能之外,其他WOW的功能我都尽量的尝试.在这里我提供一下我个人的心得.

相信很多的玩家都知道,WOW有所谓的合法外挂(COSMOS),但是WOW又非常的严格的抓所谓的非法外挂,那么合法的外挂与非法的外挂差别在哪?

其实所谓的合法外挂,只是在于一些有心的玩家利用了WOW所提供的功能,以及指令,另外在WOW的游戏核心上,另外写了一些辅助性质的SCRIPT来帮助游戏介面的简易操作.至于非法外挂,就是利用任合游戏本身所没提供的功能来增加自身的利益(包括了所谓第三者程式third party program).

既然WOW本身有提供这样的功能,身为玩家的我们为什么不尽量的利用这些好处呢?有鉴于台湾玩家在获得这些资讯上的困难(因为有关于这方面的讨论,大部分都是英文),我在这里提出讨论,或许能带给台湾的玩家另外一种的游戏体验.

暴风雪在WOW的官方网站上有很明确的提到,UI的修改是玩家对于游戏整体体验中的一部分.但是什么是UI?UI修改又是什么?

UI是USER INTERFACE(使用者介面),在WOW中,所有的UI都是模组化的,每一个按钮,每一个视窗都是属于某一个模组的定义.玩家在玩游戏的时候,并不是直接的对于游戏核心下达游戏指令,而是先在UI上面的动作,然后在藉由UI的模组上下达游戏核心指令.例如我们按下游戏中攻击的按钮,WOW会先检查这个按钮的定义是什么,然后才执行这个定义下的指令.

那么修改UI的好处又是什么呢?这样说好了.WOW本身所提供的UI,其背后的定义都是最基本的,最简化的.比如说快捷键按钮的设计,以及每个指令动作.在按钮以及视窗的设计上,使以简单容易上手为主要目的.但是对于进阶玩家而言,又会觉得WOW所提供的快捷键按钮不够用,所以自己在UI上面加以修改,这样就不但可以自行决定快捷键按钮的数目,也可以把这些按钮摆在自己最喜欢的地方.

另外关于指令动作上(键盘与老鼠在UI上面的指令),WOW基本的UI设定都是一个指令一个动作.我们按一个按钮,WOW做一个动作.但是我们可以自行设定UI,达到玩家的下达一个指令,WOW同时做很多动作.

比如说,WOW给玩家在攻击上的选择是,按TAB会帮玩家找出画面上距离最近的敌人(原始设定是20码的距离范围内),然后玩家必须按攻击的按钮或是T,在这样的情况下才会攻击.玩家下达了两个指令,做了两个动作.但是在UI的修改上,我们不但可以修改画面上距离最近的敌人的距离范围(适合远距离攻击的玩家),我们还可以设定按下TAB按键,不但会自动定位距离最近的敌人,还会自动开始攻击.

在这个讨论串当中,我主要是针对指令UI上的设定提出讨论,因为关于画面上的设定已经有很多现成的选择(COSMOS).因此我将针对CLASS上面辅助型的UI设定.在藉由讨论上,让台湾的玩家们也有机会自行设计出最方便,最有效率的攻击,法术施展,以及宠物控制.

当然我绝对欢迎玩家针对任何的UI设定提出讨论,但是我不见得有能力回答.毕竟我个人比较不喜欢去研究画面的排列,所以这方面花的功夫也比较少.



Chapter Two

上一篇文章介绍了UI修改的最基本观念,在这篇文章中我将提供UI修改上的最基本的动作,工具以及资料来源.


在我们要对UI做任何的修改或是制作模组之前,我们必须先了结WOW的UI基本结构.在WOW的资料夹里面,我们可以找的一个DATA的子目录.在这个子目录里面,包括了所有WOW会运用到的档案,包括了模组,图形,字型等等.这些都压缩在所谓的MPQ档案里面.拿我个人的档案来做例子,我的MPQ档在C:Program FilesWorld of WarcraftData下面.而所有UI可以运用到的档案都压缩在interface.mpq档里.

要检查MPQ档案里面有些什么,我个人建议WinMPQ这个程式.为了避免大家担心我放病毒,在这里我建议大家自行上GOOGLE查哪里可以下载WinMPQ.

因为游戏的UI的基本设定都在interface.mpq,所以我不建议大家直接对于interface.mpq里面的档案直接修改.大家可以把所需要的档案解压出来.只要做完修改后不存回interface.mpq里面,在这样的情况下,都不会影响到基本设定.

那么大家会问修改完之后的档案该存在哪呢?首先到WOW的目录里面,然后开一个先的子目录叫做interface.以我个人的例子是C:Program FilesWorld of Warcraftinterface.然后大家可以把修改好或是制作好的档案存在这个资料夹下面.当WOW载入游戏资料的时候,WOW会自动到这个资料夹里面检查哪些模组因该载入.

虽然UI修改可以运用到WOW现有的档案,但是为了方便教学,我在这里提出的例子将不会用到WOW现有的档案.事实上,除了要做游戏画面上的设定,大部分的情况下,我们不见得要用到WOW现有的UI档案,只要我们制作模组用的都是WOW所提供的核心指令就可以了.

在制作模组之前,我先介绍一下每一个模组所需要的最基本的档案.每个模组都需要一个介绍档,toc档.定义档,xml,以及核心档,lua档.这三种档案都可以用笔记本或是任何的文书处理程式来做修改.

为了方便介绍,我举出一个简单的例子来解释这三个档案.现在让我们设计个 Hi 模组.这个模组当你执行的时候,他会在你的游戏画面上出现一个 Hi 的资讯.

因为我们这个模组叫做Hi,所以我们需要三个档案

hi.toc
hi.xml
hi.lua
(模组的名称最好与档案的名称相对应)

现在让我介绍一下hi.toc的格式:

## Interface: 4150
## Title: Hi
## Notes: just want to say hi
## OptionalDeps:
## Dependencies:
hi.xml

在## Interface:后面的数字代表了目前WOW的版本.制作模组的作者有义务维持模组支援最新版的WOW,所以每当WOW做出更新的时候,模组的制作人得检查模组是否还能工作,当确定还能工作的时候,就可以更新射个数字.更新这个数字有两个意义:第一,WOW会检查每个模组的这个数字,如果与WOW版本相辅合,那么就会载入,不然就会档掉.第二,玩家可以确定这个模组的版本是可以使用的版本.

我在这里不建议除了模组的制作人修改这个数字.因为有时候模组的设计可能很复杂,在不是制作人的情况下,很可能不知道到底没有更新的模组是否还跟最新版的WOW相容.如果玩家自行更改这个数字让它跟WOW的版本数字一样的话,WOW就会自动载入这个模组.如果模组不相容的情况下,很可能会造成游戏上的困扰.

写到这里,玩家可能会问要去哪里找WOW最新的版本数字.当执行WOW游戏的时候,在登入的画面上的最左下方,可以找到这个资讯.以目前我写这个教学的时间而言,WOW最新版所提供的资讯是Version 1.2.1 (4150).大家要用的是4150这个数字,而不是1.2.1这个数字.

在## Title:后面,模组制作人打的字元就是这个模组的名称.

在## Notes:后面,模组制作人打的字元就是这个模组的简介

## OptionalDeps:后面, 模组制作人标示出这个模组是否有其他的额外的模组可以支援这个模组.当这个地方有标示其他的模组的时候,并不代表玩家们也必须要有这个额外模组,只是有了这个额外的模组,可以让这个么模组的功能更加完整.

## Dependencies:后面, 模组制作人得标示出这个模组必须要使用的的档案(包括了必须使用到其他模组的档案,请注意,上面选择性模组的档案并不需要在这里标示).

因为这个教学模组并不需要用到其他的模组,所以只需要标示出模组定义档 hi.xml.

hi.xml的介绍:

基本上XML可以当做网页设计档案(HTML)来看,可是严格的来说,XML是拿来作为定义上或是资料传输上所使用的.如果玩家对于XML的语法以及如何运用有兴趣,可以到http://www.w3.org/XML/查询更详细的资料

现在我只介绍XML在WOW模组设计上该如何运用.

Code:
 
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/">
<script file="hi.lua"/>

<Frame name="hi_core">
<scripts>
<OnLoad>
this:RegisterEvent("VARIABLES_LOADED");
</OnLoad>
<OnEvent>
if (event == "VARIABLES_LOADED") then
hi_initialize();
end
</OnEvent>
</Scripts>
</Frame>
</Ui>


 



大家可以忽略前面几行<Ui xmlns ......... />.基本上每一个WOW模组XML档都必须要有这几行.

重要的是 <script file="hi.lua"/> 在这里大家得定义出模组的核心档案是什么.以我们目前的例子,核心档案是hi.lua.

在 <Frame name="hi_core"> 这里定义出模组在游戏画面中要使用的的子画面的名称.这个名称必须是独一无二的,避免与其他的模组画面冲突到.

在上面的例子中我们可以看到<OnLoad>和<OnEvent>的标示,基本上<OnLoad>的意思是当WOW在载入游戏介面的时候,因该要做出什么动作.以我们的例子,我们要在WOW游戏里注册一个变数叫做"VARIABLES_LOADED".
<OnEvent>的意思是,如果发生了什么事件,那么该做出什么动作来反应.以我们的例子,如果注册"VARIABLES_LOADED"成功的话,我们就执行hi_initialize(). 当然,如果没有注册成功,也就不会有事件发生,这代表了我们的模组没有载入到游戏里面,所以我们也就没有后续动作.

hi.lua的介绍:
lua 是一个script语言.WOW所有的UI模组都是建立在这个语言之上.对于lua想要更深入的了解,请到官方网站:http://www.lua.org/manual/5.0/ 在这里可以查到所有关于lua的语法以及使用方式.

现在让我介绍我们目前的例子:

Code:
 
function hi_initialize()
SlashCmdList["HIHI"] = hi_command;
SLASH_HIHI1 = "/hihi";
SLASH_HIHI2 = "/您好";
end

function hi_command(msg)
message(msg);
end


 



大家可以看到我们写两个小function 一个叫hi_initialize 一个叫 hi_command. 在hi.xml里面,如果模组顺利在入的话hi_initialize是第一个被呼叫的.在这里我们自行设定了一些"/"指令.我想大家因该对于游戏里面的一些"/"指令并不陌生.

同样的我们也可以帮我们的模组设定这些"/"指令.
SlashCmdList["HIHI"] = hi_command; 代表的意思是如果我们么组的"/"指令被呼叫的话,我们因该执行hi_command这一个function.
SLASH_HIHI1 = "/hihi";
SLASH_HIHI2 = "/您好";
这里是帮我们的"/"指令定出名称,目前我还不确定我们是否可以自行自做出中文指令,但是我想只要可以在WOW输入中文,上面的设计就因该没问题.(所以还请可以在WOW输入中文的玩家测试一下.
这里要稍微注意的一点是SlashCmdList["HIHI"]里面的HIHI必须要和SLASH_HIHI1和SLASH_HIHI2 的HIHI依样.如果我们把HIHI改成NIHAO 那么就会变成SlashCmdList["NIHAO"], SLASH_NIHAO1, 以及SLASH_NIHAO2. 至于HIHI 或是 NIHAO 后面的 1 和 2 只是变数,代表了同样的指令,有哪些变种的指令.

当我们打入了"/hihi"或是"/您好" 就会执行下面的function
function hi_command(msg)
message(msg);
end
基本上只是在游戏画面上出现一个视窗,然后视窗上面显示我们在指令后面打入的讯息.

例如我们输入"/hihi 测试"

那么出现的视窗就会显示 "测试" 在那上面.

好了,我们写好这三个档案了.现在我们要告诉WOW载入我们的HI模组.
首先,到"C:Program FilesWorld of Warcraftinterface"这个子目录里面.如果还没有这个子目录的话,自行开一个新的.然后在这个目录里面开一个新的目录叫做"AddOns"所有玩家设计的模组都因该摆在这个目录里面.然后在"AddOns"这个目录里面在开一个目录叫做"HI".最后把我们做好的三个档案存到这个目录里面.
"C:Program FilesWorld of WarcraftinterfaceAddOnsHI"

现在我们可以测试这个模组了喔.

稍微介绍一下WOW简易管理模组的方式.如果当玩家有安装自行设计的模组的话(比如说COSMOS),在进入角色选单的时候,我们可以看到画面的左下方有一个Addons的按钮.大家可以在者里自行选择所希望开启的模组.在模组名子前面打勾,表示选择角色之后,游戏就会开始载入模组.没打勾就代表不会载入. 请注意,WOW有模组载入的上限.所果所有载入的模组用到的资源超过32MB,游戏会自动关闭.这时候就需要去管理介面关掉一些模组喔.

下一篇文章,我将教大家如何在lua档案里面运用WOW的核心指令,以及如何只使用3个档案,就可以写出很有帮助的SCRIPT.



Chapter Three

在来一系列的文章,我要教猎人的玩家如何利用模组设计,有效率的执行远距离,近距离,以及宠物攻击. 顺便介绍一些WOW的核心指令.


我想玩过网路游戏的玩家,多多少少都因该听过外挂.什么是外挂?其实就是能自动的帮助玩家执行一些指令.基本上很类似MACRO(没听过MACRO的人,因该有听过EZSCRIPT吧)

WOW本身就有提供MACRO这样的功能.只要我们在游戏中输入"MACRO"之后,我们就可以开始设计我们自己的辅助指令.但是WOW的MACRO有很多限制.第一,WOW的MACRO是半自动的,意思就是说,你一定要按下某一个键盘按键,或是老鼠按键,才能执行你所设定的MACRO.第二,游戏中的MACRO有字数上的限制.最多只能输入255个英文字元.第三,WOW的MACRO无法设定时间上面的间隔.在MACRO里面的所有的指令会被一次执行.第四,如果你所执行的很多指令中有很多都有指令冷却时间(例如魔法的COOLDOWM),那么MACRO只会执行第一个可以执行的指令.

同样的,当我们设计模组的时候,我们也同样受限于这样的限制,但是却没有255个英文字元的限定,在这样的情况下,我们可以自做更复杂的指令方式.

今天我要教大家的模组是猎人辅助模组.

WOW的猎人是属于远距离攻击的单位,最需要控制的是如何有要的利用各式的ASPECT以及宠物攻击.当近距离攻击的时候,猎人需要用到ASPECT OF MONKEY,当远距离攻击的时候,需要用到ASPECT OF HAWK. 但是我想很多人因该都会遇到,同时指示宠物攻击,玩家攻击,以及转换ASPECT的时候,会手忙脚乱.现在我们的目标是如何设计一个模组可以让我们在操作上更顺畅.

猎人辅助模组设计目标:
1:玩家主动攻击的时候,宠物也会主动攻击(WOW的基本模式是,玩家按攻击,然后还要按宠物攻击).

2:玩家与宠物永远是攻击同一个目标,以最快最有效率的方式来减少战斗画面上的危机.避免宠物与玩家通时攻击不同的敌人,造成最后的结果是玩家与宠物同时阵亡.

3:有效的辨识玩家的攻击方式,然后执行最有效率的ASPECT.例如玩家近距离攻击的时候不是在ASPECT OF HAWK的状况下.

4:有效的运用远距离攻击技能(包括了节省MANA,快速选择敌人目标等等)

好了,有了以上的目标,现在开始让我们来设计.首先,来制作模组所需要的最基本的三个档案.因为我替我的模组设计的名称叫做HunterAssistant(猎人辅助),所以我的三个基本档案叫做:
HunterAssistant.toc
HunterAssistant.xml
HunterAssistant.lua

Code:
 
HunterAssistant.toc:
## Interface: 4150
## Title: Hunter Assistant V0.1
## Notes: Macro scripts for assisting Hunter
## OptionalDeps:
## Dependencies:
HunterAssistant.xml

HunterAssistant.xml:
<?xml version="1.0" encoding="utf-8" ?>
<Ui xmlns="http://www.blizzard.com/wow/ui/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.blizzard.com/wow/ui/">
<script file="HunterAssistant.lua"/>

<Frame name="HunterAssistantFrame">
<scripts>
<OnLoad>
this:RegisterEvent("VARIABLES_LOADED");
</OnLoad>
<OnEvent>
if (event == "VARIABLES_LOADED") then
HunterAssistant_init();
end
</OnEvent>
</Scripts>
</Frame>
</Ui>


 



我将不在第这两个档案多做解释.有疑问的,请参考上一篇文章.毕竟我只使用的最简单的设计.


现在的重点LUA核心档.我先把档案内容列出来,然后在以另外的文章详细的解释每个指令.

HunterAssistant.lua:

Code:
 
function HunterAssistant_init()
-- define slash command here
SlashCmdList["HA_HunterMarkI"] = HA_HunterMarkI;
SLASH_HA_HunterMarkI1 = "/HAHunterMarkI";
SLASH_HA_HunterMarkI2 = "/hahmi";

SlashCmdList["HA_HunterMarkII"] = HA_HunterMarkII;
SLASH_HA_HunterMarkII1 = "/HAHunterMarkII";
SLASH_HA_HunterMarkII2 = "/hahmii";

SlashCmdList["HA_AttackMelee"] = HA_AttackMelee;
SLASH_HA_AttackMelee1 = "/HAAttackMelee";
SLASH_HA_AttackMelee2 = "/haam";

SlashCmdList["HA_AttackRangeI"] = HA_AttackRangeI;
SLASH_HA_AttackRangeI1 = "/HAAttackRangeI";
SLASH_HA_AttackRangeI2 = "/haari";

SlashCmdList["HA_AttackRangeII"] = HA_AttackRangeII;
SLASH_HA_AttackRangeII1 = "/HAAttackRangeII";
SLASH_HA_AttackRangeII2 = "/haarii";

SlashCmdList["HA_StingSerpentI"] = HA_StingSerpentI;
SLASH_HA_StingSerpentI1 = "/HAStingSerpentI";
SLASH_HA_StingSerpentI2 = "/hasseri";

SlashCmdList["HA_StingSerpentII"] = HA_StingSerpentII;
SLASH_HA_StingSerpentII1 = "/HAStingSerpentII";
SLASH_HA_StingSerpentII2 = "/hasserii";

SlashCmdList["HA_StingSerpentIII"] = HA_StingSerpentIII;
SLASH_HA_StingSerpentII1 = "/HAStingSerpentIII";
SLASH_HA_StingSerpentII2 = "/hasseriii";

SlashCmdList["HunterTest"] = HunterTest_command;
SLASH_HunterTest1 = "/HunterTest";
SLASH_HunterTest2 = "/ht";
end


 






Code:
 
function HA_AttackMelee()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Attack");
x=1
found=false;
while (UnitBuff("player",x)) do
if(string.find(UnitBuff("player",x), "Monkey")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Monkey");
end;
end;
end


 






Code:
 
function HA_AttackRangeI()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Auto Shot");
x=1
found=false;
while (UnitBuff("player",x)) do
if(string.find(UnitBuff("player",x), "Raven")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Hawk(Rank 1)");
end;
end;
end


 






Code:
 
function HA_AttackRangeII()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Auto Shot");
x=1
found=false;
while (UnitBuff("player",x)) do
if(string.find(UnitBuff("player",x), "Raven")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Hawk(Rank 2)");
end;
end;
end


 






Code:
 
function HA_HunterMarkI()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Hunter's Mark(Rank 1)");
end;
end


 






Code:
 
function HA_HunterMarkII()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Hunter's Mark(Rank 2)");
end;
end


 






Code:
 
function HA_StingSerpentI()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
x=1
found=false;
while (UnitDebuff("target",x)) do
if(string.find(UnitDebuff("target",x), "Quickshot")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Serpent Sting(Rank 1)");
end;
end;
end


 






Code:
 
function HA_StingSerpentII()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
x=1
found=false;
while (UnitDebuff("target",x)) do
if(string.find(UnitDebuff("target",x), "Quickshot")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Serpent Sting(Rank 2)");
end;
end;
end


 






Code:
 
function HA_StingSerpentIII()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
x=1
found=false;
while (UnitDebuff("target",x)) do
if(string.find(UnitDebuff("target",x), "Quickshot")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Serpent Sting(Rank 3)");
end;
end;
end


 






Code:
 
function HunterTest_command()
x=1
found=false;
while(UnitBuff("player",x)) do
if(string.find(UnitBuff("player",x), "Raven")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Hawk(Rank 1)");
end;
if(found) then
CastSpellByName("Aspect of the Monkey");
end;
end


 




Chapter Four
首先我要说声抱歉,因为我没注意到基地的文章发表没有办法显示"TAB"的空格.这样在查看程式码,一定会造成困扰.如果大家在看程式码会眼睛痛的时候,还请见谅.

现在,让我们研究一下HunterAssistant.lua第一段:

function HunterAssistant_init()
-- define slash command here
SlashCmdList["HA_HunterMarkI"] = HA_HunterMarkI;
SLASH_HA_HunterMarkI1 = "/HAHunterMarkI";
SLASH_HA_HunterMarkI2 = "/hahmi";

SlashCmdList["HA_HunterMarkII"] = HA_HunterMarkII;
SLASH_HA_HunterMarkII1 = "/HAHunterMarkII";
SLASH_HA_HunterMarkII2 = "/hahmii";

SlashCmdList["HA_AttackMelee"] = HA_AttackMelee;
SLASH_HA_AttackMelee1 = "/HAAttackMelee";
SLASH_HA_AttackMelee2 = "/haam";

SlashCmdList["HA_AttackRangeI"] = HA_AttackRangeI;
SLASH_HA_AttackRangeI1 = "/HAAttackRangeI";
SLASH_HA_AttackRangeI2 = "/haari";

SlashCmdList["HA_AttackRangeII"] = HA_AttackRangeII;
SLASH_HA_AttackRangeII1 = "/HAAttackRangeII";
SLASH_HA_AttackRangeII2 = "/haarii";

SlashCmdList["HA_StingSerpentI"] = HA_StingSerpentI;
SLASH_HA_StingSerpentI1 = "/HAStingSerpentI";
SLASH_HA_StingSerpentI2 = "/hasseri";

SlashCmdList["HA_StingSerpentII"] = HA_StingSerpentII;
SLASH_HA_StingSerpentII1 = "/HAStingSerpentII";
SLASH_HA_StingSerpentII2 = "/hasserii";

SlashCmdList["HA_StingSerpentIII"] = HA_StingSerpentIII;
SLASH_HA_StingSerpentII1 = "/HAStingSerpentIII";
SLASH_HA_StingSerpentII2 = "/hasseriii";

SlashCmdList["HunterTest"] = HunterTest_command;
SLASH_HunterTest1 = "/HunterTest";
SLASH_HunterTest2 = "/ht";
end

附注:HA_代表的意思是HunterAssistant的缩写.

其实这里没有什么特别的.这里只是帮模组设定一些简单的"/"指令.将来我们可以直接把这些指令运用到游戏的MACRO中. 请注意的一点是,每一个指令都有一个FUNCTION相对应.另外,我每一个指令都一个全名指令,以及一个简写指令(方便使用).在指令命名设计上,我以看到指令名称就大概能猜到使用的结果是什么.还有就是每个指令前面加上HA_这样能有效的避免跟其他模组的指令冲突到.另外有几个指令后面有罗马数字,这些是代表了所使用到的猎人技能是有等级高低的.比如说HA_HunterMarkI使用的是等级1的Hunter's Mark,HA_HunterMarkII使用的是等级2的Hunter's Mark.

另外,我的指令排列是以英文顺序为主.这样方便我个人寻找BUG.

现在让我们继续往下看.

function HA_AttackMelee()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Attack");
x=1
found=false;
while (UnitBuff("player",x)) do
if(string.find(UnitBuff("player",x), "Monkey")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Monkey");
end;
end;
end

这一个function的主要目的是近距离攻击.首先第一行是AssistUnit("pet");这代表了什么?其实这是WOW的核心指令之一.AssistUnit()的意思是帮助某一个单位的意思.这个指令可以接受不同的变数,包括玩家单位,NPC友好单位,以及宠物单位.当执行了这个指令之后,你的敌人对象就会变成你所帮助的那个单位的敌人是同一个.在这里"pet"的意思就是主角单位的宠物.

我在这里会用AssistUnit("pet")开头的原因是,当你的宠物在攻击的时候敌人的时候,你跟你的宠物所选择的敌人不见得是同一个,所以我们要确定主角跟宠物攻击的对象是同一个.

(注意:目前我这样的设计好像有点逻辑上的问题.思考逻辑到底是主角该帮主宠物还是宠物帮助主角?游戏中的基本设定是主角受到攻击,宠物就会自动帮助第一个攻击主角的敌人,可是如果好几个人攻击主角,宠物还是会先专心的把第一个敌人解决掉.所以我一开始会这样设计,专心的先帮助宠物解决掉第一个敌人.可是当遇到我就是有特殊原因要先解决掉其他的敌人的时候(例如某些特殊任务),我这样的设计反而造成困扰,因为只要我一攻击,我的对象就就转换到宠物的敌人了.所以在未来的版本中,我可能会设定另一种攻击是,宠物永远帮助主角攻击同一个敌人,使用的指令是 PetAssistUnit("Player"). "Player"就是代表主角的意思.)

下一行, if(not UnitExists("target")) then
TargetNearestEnemy();
end;
这里的意思是检查执行帮忙的指令之后(AssistUnit())到底有没有敌人?如果主角的宠物没有在战斗状态,那么我们敌人的状态就是没有选择.UnitExists("target")) 就是检查我们有没有选择任何的目标.这个也是WOW的核心指令之一. 当然如果我们有选择目标,可是目标是有好单位呢?不用担心,WOW的设计本来就是无法攻击友好单位的.那么TargetNearestEnemy()又是什么意思呢?就是自动选择距离最近的非有好单位当作目标选项.所以这一段的意思就是,如果主角跟主角的宠物都没有目标的话,帮主角动挑一个最近的非有好单位当作目标.

在来
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Attack");
这里代表的是,如果有目标的话,宠物攻击,然后主角攻击.PetAttack()是宠物攻击的指令,CastSpellByName("Attack")的意思是主角施展技能,这个技能是攻击(Attack). 这些全都是WOW的核心指令喔,酷吧.

继续
x=1
found=false;
while (UnitBuff("player",x)) do

found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Monkey");
end;
end;
end

这里我定义了一个变数X,以及一个变数FOUND(中文是找到的意思),这两个变数不是WOW的专有变数,而是LUA的语法喔.为什么需要这两个变数?因为当我们在近距离攻击的时候,我必须要确定主角是在于ASPECT OF MONKEY 之下,有了这些变数可以方便我来做检查.

while (UnitBuff("player",x))的意思就是如果在主角的身上可以找到Buff的话. UnitBuff("player",1)会在寻找主角身上第一个buff的资料,然后传回讯息.如果没找到,就会传回一个否定的讯息.UnitBuff("player",2)就是寻找第二个buff的资料.

if(string.find(UnitBuff("player",x), "Monkey"))
string.find是lua语言上的指令,不是WOW的核心指令.这个指令的目的是寻找一个讯息中的字串是否有特殊的字元.所以这一整段的意思是找寻主角身上的buff的资讯是否有MONKEY(猴子)的字串.如果有的话,就把FOUND设定为TRUE.

if(not found) then
CastSpellByName("Aspect of the Monkey");
这一段就是如果从头到尾都没有找相关的buff的话,代表了主角没有施展"Aspect of the Monkey",所以我们就执行施展技能的指令CastSpellByName("Aspect of the Monkey").

写到这里,让我顺便提出一些要点.不晓得大家有没有发现到,在这个function里面,从头到尾只有一个指令需要冷却时间那就是CastSpellByName("Aspect of the Monkey").其他,比如说攻击,全部都不需要冷却时间.这也就是为什么这么多指令都可以转变成一个单一指令.但是因为CastSpellByName("Aspect of the Monkey")需要冷却时间,所以我把她摆在最后执行,避免错误.另外一点就是,很多人在找寻主角身上的buff资料的时候,喜欢直接用UnitBuff("player",1)却没有想到其实主角身上可能有很多的buff呢.

所以我这样的设计一定可以确保如果主角已经施展了Aspect of the Monkey,就绝对不会重复施展第二次(浪费mana喔).


至于这个function 里面的一些IF, WHILE, NOT等等的运用以及正确的语法,就请大家到LUA的官方网站去看.我就不多解释了.

在来是猎人的普通远距离攻击
function HA_AttackRangeI()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Auto Shot");
x=1
found=false;
while (UnitBuff("player",x)) do
if(string.find(UnitBuff("player",x), "Raven")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Hawk(Rank 1)");
end;
end;
end

这一段其实跟近距离的普通攻击是同样的逻辑,但是CastSpellByName("Auto Shot") 这个是猎人专有的远距离自动攻击的指令.同样的这个指令可以跟其他需要冷却时间的指令同时使用.另外CastSpellByName("Aspect of the Hawk(Rank 1)") 这是执行第一级的ASPECT OF THE HAWK. 在我们模组function HA_AttackRangeII()里面就会执行第二级的ASPECT OF THE HAWK, CastSpellByName("Aspect of the Hawk(Rank 2)")


function HA_HunterMarkI()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
CastSpellByName("Hunter's Mark(Rank 1)");
end;
end
这一段没有什么特别的,唯一的好处就是当主角开始使用MARK的技能的时候,就开始命令宠物攻击.我们不需要按两个动作.另外,Hunter's Mark 也是有等级之分的,所以我写了其他相对应的等级指令
注意:(这个FUNCTION我还没有完全的完成,因为我没有检查是否敌人已经被MARK过了,也就是说这个指令允许重复MARK敌人,浪费MANA)

function HA_StingSerpentI()
AssistUnit("pet");
if(not UnitExists("target")) then
TargetNearestEnemy();
end;
if(UnitExists("target")) then
PetAttack();
x=1
found=false;
while (UnitDebuff("target",x)) do
if(string.find(UnitDebuff("target",x), "Quickshot")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Serpent Sting(Rank 1)");
end;
end;
end

这一个跟普通远距离攻击的指令很像,但是并不是使用普通的AUTO SHOT,而是使用了Serpent Sting.另外这一个指令不会检查主角本身是否施展了ASPECT OF HAWK.因为Serpent Sting跟aspect of the hawk都要冷却时间,两个写在一起,只会执行其中的一个,那么就一点意义都没有了.既然这样,为什么要写这一个指令?其实这个指令会检查敌人身上是否已经被施展了Serpent Sting,毕竟Serpent Sting没有加成效过,所以我们要避免重复施展Serpent Sting.而WOW的基本Serpent Sting指令是不会做这样的检查的,所以我们还得一直盯着敌人的资讯看着,避免浪费MANA.可是设计了另外的这个指令,玩家根本不用担心浪费MANA,只要想到了就执行一下,反证会自动检查,也不会中断AOTU SHOT的动作,更不会浪费MANA.另外,这个指令也有设计其他不同等级的相对应指令.

最后
function HunterTest_command()
x=1
found=false;
while(UnitBuff("player",x)) do
if(string.find(UnitBuff("player",x), "Raven")) then
found=true;
end;
x=x+1;
end;
if(not found) then
CastSpellByName("Aspect of the Hawk(Rank 1)");
end;
if(found) then
CastSpellByName("Aspect of the Monkey");
end;
end
这个指令只是我设计好玩拿来测试用的,当执行这个指令的时候,主角会交互的使用ASPECT OF MONKEY 或者 ASPECT OF HAWK.

模组的核心指令介绍到这里,下一篇文章将教玩家如何在游戏里运用这些指令.

目前我们的模组有了最基本的三个档案了.现在让我们把这三个档案放入C:Program FilesWorld of WarcraftInterfaceAddOnsHunterAssistant这个资料夹里面.然后执行游戏.

现在让我解释一下WOW上面所谓的斜线指令("/指令")该如何运用.

大家因该都清楚如何的运用感情指令,或是交谈指令.这些都是斜线指令.但是WOW还有一些斜线指令是无法直接的输入, 比如说"/attack". 基本上这是WOW官方设计的攻击指令,其背后只是很简单的呼叫了CastSpellByName("Attack"). 但是为什么我们无法直接使用这些指令呢?因为暴风雪为了避免玩家利用其他的程式来设计非法的MACRO.

怎么说呢?因为WOW的MACRO没有时间间隔设计,避免玩家设计机器人,那么玩家设计机器人就必须利用其他的程式.如果,设计机器人,机器人程式就可以直接的模拟键盘输入指令,而不会影响到玩家对于滑鼠的操做.所以WOW把很多的斜线指令的操作设定成只有在WOW的MACRO下才能使用.你设计一个MACRO以及MACRO图像按钮,你要执行这个MACRO,你就必须对这个按钮按一下,WOW才会执行这个动作.

当然,还是有其他的办法摆脱暴风雪这样的设计,但是我不会对类似这样的问题做出任和解说.因为暴风雪尽力的维护游戏免于外挂的侵入,身为玩家的我也有义务要帮助暴风雪.

有这样的限制的斜线指令包括了所有的会使用到执行特殊技能的指令.所以当我们设计的模组有用到执行特殊技能的时候,我们的斜线指令也只能在WOW下面的MACRO才能执行.(当然,我们的猎人辅助模组还是运用在MACRO下面比较好,毕竟在战斗的时候谁还有时间慢慢的的用键盘输入指令,我们又不是机器人)

要使用WOW的MACRO很简单,只要在游戏中输入"/macro"或是按下Esc 然后选择macro的选项

在macor的画面,我们必须帮macro选择一个图像按钮,替macro命名,然后输入我们想要给macro的指令.

现在让我列出猎人辅助模组的简易斜线指令:
Hunter's Mark 包括了不同的等级
/hahmi
/hahmii

Melee Attack(近距离攻击)
/haam"

Range Attack (远距离攻击)包括了不同的等级
/haari
/haarii

Serpent Sting(远距离特技)包括了不同的等级
/hasseri
/hasserii
/hasseriii

现在我们只要把macro的图像按钮选择好,然后给个名子,最后在底下输入指令的地方,挑一个你想要执行的辅助指令输入就好了. 注意喔,一个macro只能使用模组的一个指令喔,因为就算你输入了很多个,在执行macro的时候,游戏也只会执行情中的一个而已.(冷却时间的考量)

当你设定好了macro之后,WALA~~~,你就可以把这个图形按钮抓到快捷列上面使用了~~~就像是使用任何其他的基本图形按钮依样喔.好处是,你按一次按钮,却等于其他没有用辅助模组人按好几次按钮依样喔.还会自动帮你做一些额外的检查呢.有效率了多吧.


下一次,我将教大家如何设计辅助模组可以帮大家在半自动的施展BUFF,对象也会半自动的选择队友,宠物,或是自己.


附注1:因为我个人玩了10角色,包括了联盟的每一个CLASS,在未来的日子里,我将会不定期的替自己写模组.如此同时,我也会拿上来与大家分享.但是我将不会再花这么多的时间详细解释每一个指令.除非有网友提出问题,那时我会在尽力回答.

附注2:有关WOW的核心开放给玩家使用的指令,大家可以到这个网页找到.但是这里不是完整的列表,说明也不是很清楚,不过大家将就的用噜.
http://www.wowwiki.com/World_of_Warcraft_API

附注3:最有效率的研究模组的方式,就是到官网的UI Customization的论坛参与讨论喔.也许大家会找到一些意想不到,但是又很有帮助的模组喔.我个人就已经发现有人设计了一个挖矿采要的模组,这个模组会把你最到过的挖矿以及采药的地点纪录下来,然后显示在地图上.怎么样,很有用吧.
http://forums.worldofwarcraft.com/board.as...e-customization

Chapter Five
或许我在模组解释以及使用教学方面,因该采用更简单的例子来让大家能更容易上手.所以我决定不要直接的把我个人写好的模组拿上来,而是先的教大家如何使用WOW的MACRO,以及呼叫一些WOW的指令.



首先让我重新的介绍一下WOW的MACOR
(基本资料来源 魔兽台湾:http://www.wowtw.game.tw/)


在"魔兽台湾"的网页里,我们可以找到很清楚的MACOR中文定义以及解释:

宏[Macro]使你能够创建自己独创且非常酷的命令集,你可以通过点击一个按钮便完成一系列的命令。宏拥有许多用法。

宏的创建

输入“/macro”或者点击谈话[talk]按钮并且选择宏[Macro]选项,将会弹出一个对话框,里面记录着你现有的宏。在开始的时候里面应该没有任何宏。
点击对话框底部的”new”按钮,将会弹出另外一个对话框,其中要求你输入宏的名称并且为这个宏选择一个图标。
给你的宏命名,选择一个容易记忆的名字。例如,输入“assist[协助]”。
选择一个图标。这里我们选择的是一把剑。
点击“Okay”按钮。你现在能够在你的宏对话框里看到这个宏的名称(assist),在名称的旁边有你刚才选择的图标(剑)。
现在在assist[协助]图标上点鼠标左键加亮它。除了“new”按钮外,同时也有一个按钮用来改变你的宏的名称和图标,以便你以后想改变它的名称或者选择一个不同的图标。
一旦你的“assist”图标是加亮状态,你就可以向下移动你的鼠标指针进入“enter macro commands[输入宏命令]的区域。在这里你可以输入你想让这个宏按钮实现的命令。
输入“/assist Nebu[协助Nebu]”。
现在移动你的鼠标指针回到剑的图标(assist[协助]宏),在这个图标上压下鼠标的左键不松开。
现在拖动图标到你的动作条的空槽里去。
当你要使用这个新的宏的时候,键入这个动作按钮对应的数字或者直接右键点击按钮本身。
现在你拥有了一个宏按钮,它将在其他玩家(Nebu)攻击的时候协助他。无论何时,当Nebu攻击目标的时候,点击这个按钮后你的目标将会变为他正在攻击的目标。


以上是"魔兽台湾"给予MACRO的中文解释,以及一个很简单的例子.

现在让我们稍微加强一下上面的例子.

上面的例子里面,我们看到了MACRO的内容只有一行

/assist Nebu

现在我们来加强一下,让这个MACRO不但会协助,还会马上攻击.改过的MACRO会像底下这样.

/assist Nebu
/attack

好了,现在我们的MACRO有两行了.这个MACRO的目的是,协助Nebu(指定Nebu的敌人),然后攻击.

那么现在让我们写一个很简单以及实用的MACRO.加设Nebu是一个牧师,我们要帮Nebu写一个自动帮自己使用基本治疗的MACRO.

/target Nebu
/cast Lesser Heal(Rank 1)

以上的MACRO在执行的时候,每次都会选择Nebu作为治疗的对象.其中,/target Nebu 是选择Nebu为对象,/cast Lesser Heal(Rank 1)是对于对象施展Lesser Heal第一级的法术.

我想以上的例子因该已经很清楚的解释了基本的MACRO该如何使用.可是在在"魔兽台湾"的网页里也有提到:

宏的限制
宏是有长度限制的,如果你的信息太长了,就缩减一些!(显然)

这一点是没有错的,因为WOW的MACRO有255个英文字元的限制.我们该如何突破这个限制呢?

首先让我们先了解一下WOW的官方斜线指令.

上面那些MACRO里所用到的斜线指令,其实背后都有呼叫一些核心指令.例如:

/assist Nebu 呼叫了 AssistUnit("Nebu")
/attack 呼叫了 Attack("Target")
/target Nebu 呼叫了 TargetByName("Nebu")
/cast Lesser Heal(Rank 1) 呼叫了 CastSpellByName("Lesser Heal(Rank 1)")

这些核心指令除了可以使用官方的斜线指令来呼叫之外,我们还有其他几种方式来使用这些核心指令.

第一种方式,直接在MACRO里使用这些核心指令.要直接使用这些核心指令,我们必须用到一个斜线指令叫做/Script.

以下是把上面的MACRO例子改成使用核心指令的方式.
第一个MACRO 帮助Nebu攻击敌人
/Script AssistUnit("Nebu");
/Script Attack("Target");
第二个MACRO选择Nebu作为治疗的对象
/Script TargetByName("Nebu");
/Script CastSpellByName("Lesser Heal(Rank 1)");


要注意的一点是,当使用了/Script的时候,MACRO的每一行指令都要有";"作为结尾.

直接使用核心指令的好处是什么?因为WOW并没有帮每一个核心指令写对应的斜线指令,而很多的核心指令的功能却是非常强大的.

核心指令除了能直接利用在MACRO之外,另一个用途就是可以使用在模组里面使用.关于使用上的例子,大家可以参考我前几篇文章所提供的猎人辅助模组.在这里我们就可以利用模组所提供的斜线指令来变相的突破了MACRO上面的字元限制了.

希望这一篇文章能更容易的让大家了解斜线指令,核心指令,MACRO,以及模组的用途.

>※littlethe(东周小星星)提到:
>我是个programmer,
>macro在程式中是指巨集指令,
>若我没有弄错的话,
>这个macro是不是用于制定"连续动作"用的?
>
>按个键,
>人物就会做出连续预设动作,
>
>若是这样,
>那wow可还真特别,
>开放玩家编写macro,
>
>等我拿到wow时,
>要好好的来玩看看

恩~没有错喔

可是WOW本身的MACRO有很多的限制.可是如果玩家使用非游戏本身的MACRO来玩WOW,暴风雪就可以砍此玩家的帐号.

WOW本身的MACRO先天上就没有时间的设计.也就是说,MACRO里面所有的动作指令,通通当作及时指令(当然,非常严格的来说还是会有先后顺序上的差别,可是时间上的差别只有不到0.1秒.)这样的设计是避免玩家制作机器人.

另外,WOW很多的技能有冷却时间,如果把多个有冷却时间的技能写入MACRO,那么MACRO只会执行第一个达到条件的技能,其他有冷却时间的技能都会被忽略.
>※littlethe(东周小星星)提到:
>虾米??
>那这样的话,
>那岂不就不能使用连续法术了?
>法术也有cast time不是么?
>还是wow的法术都是direct的?
>哇累...
>本来想说能不能来个先闪电再插补血丈后嗜血之类的


WOW的技能大约能分为三种,即时的,有冷却时间的,需要施展时间的.

即时的就是可以一直连续使用.

有冷却时间的就是在使用过后,有一个时间限制是不能使用同一招.另外,使用这种技能之后,还会有大约不到一秒的时间无法使用任何其他的技能.

需要施展时间的,当使用的时候,在技能完全施展之前,无法使用任何其他的技能,除非先取消掉.

所以只要是有被时间限制上的技能,在MACRO里,就只有一个可以被驱动,而且最好是摆在MACRO的最后一行.如果摆在太前面,那么很可能就会使得后面的即时技能被挡掉.

>※willllee(balmung)提到:
>那可以用macro来配合其他程式吗?


使用任何不是魔兽纪元本身所提供的功能来执行游戏,都违反了魔兽纪元的使用条约.所以我无法在这里回答您所提出的其他的相关问题.


但是有一点可以确定的是,魔兽纪元所提供的核心指令,已经可以做出广大的环境判断了.包括判定血量多少,队友以及其宠物的状态,敌人的状态,等等.

目前官方论坛上已经有很多的模组能提供类似的功能.包括当自身血量低于某百分比,执行动作的时候,会先帮自己补血(魔法或是物件,取决于是否在战斗中),以及根据队友的血量百分比来决定宠物会先帮助哪个队友攻击.

如果有兴趣的网友,甚至可以写出根据敌人的状态做出不同的攻击或是反应的模组.

基本上,这些都可以只用到核心指令来达到的.

数据统计中!!

最新评论共有  位网友发表了评论
发表评论(评论内容:请文明参与评论,禁止谩骂攻击!)
不能超过250字节,请自觉遵守互联网相关政策法规.
昵称:    发表评论 (Ctrl+Enter快速回复)
推荐新闻

关于本站 | 合作加盟 | 合作说明 | 免责声明 | 广告服务 | 网站地图

健康游戏忠告:抵制不良游戏 拒绝盗版游戏 注意自我保护 谨防受骗上当 适度游戏益脑 沉迷游戏伤身 合理安排时间 享受健康生活

如有意见和建议,请惠赐E-mail至350317@qq.com 联系QQ:350317

Copyright © 2010-2013 Www.27zG.CoM
苏ICP备11049833号