Step 1: Filtering Our eventType Variable
第 1 步:过滤我们的 eventType 变量
First, we'll be filtering out eventType
so we aren't printing so much to the combat log. If you did print the eventType
to the chat box, I'm sure you saw a few events print. The one we're looking for specifically is "PARTY_KILL"
, letting us know the mob has died from us, or someone in our party.
首先,我们将过滤掉 eventType
,这样我们就不会向战斗日志打印太多内容。如果您确实将 eventType
打印到聊天框,我相信您看到了一些事件打印。我们特别要找的是 “PARTY_KILL”
,让我们知道暴徒已经从我们身边死了,或者我们队伍中的某个人已经死了。
How are we going to modify the code to only work with
"PARTY_KILL"
sub events?
我们将如何修改代码以仅适用于“PARTY_KILL”
子事件?
Well, let's use the following code:
好吧,让我们使用以下代码:
In our eventHandler function, we're going to insert this line of code inside of our event check code:
在我们的 eventHandler 函数中,我们将这行代码插入到我们的事件检查代码中:
if eventType and eventType == "PARTY_KILL" then end
This code states: 此代码指出:
If
eventType
is notnil
andeventType
is equal to "PARTY_KILL", then we can proceed.
如果eventType
不为nil
且eventType
等于 “PARTY_KILL”,则我们可以继续。
Both of the requirements in the if statement
need to be true for the code to proceed.if 语句
中的两个要求都需要为 true,代码才能继续。
We're making sure that eventType
is not nil
and that eventType
is equal to "PARTY_KILL"
as well.
我们确保 eventType
不为 nil
,并且 eventType
也等于 “PARTY_KILL
”。
If these are both true
then we need to debug by printing another nice message. Let's modify our above code to this:
如果这些都是真的
,那么我们需要通过打印另一个 nice 消息来调试。让我们将上面的代码修改为:
if eventType and eventType == "PARTY_KILL" then print("An enemy has been successfully defeated!") end
Nice, now let's head into the game and kill something to check if this works. If you get this message after killing an enemy, we can move on!
很好,现在让我们进入游戏并杀死一些东西以检查这是否有效。如果您在杀死敌人后收到此消息,我们可以继续前进!
Step 2: Finally, Let's Write to the Database
第 2 步:最后,让我们写入数据库
Since we have the message printing to our chat box when we kill something, we're going to modify our code to write to the MyAddonDB
database we created in our previous code. Since we're not actually logging what monster was killed, we can modify the code we have slightly to work for us in this context.
由于当我们杀死某些内容时,我们的聊天框会打印消息,因此我们将修改我们的代码以写入我们在前面的代码中创建的 MyAddonDB
数据库。由于我们实际上并没有记录杀死的怪物,因此我们可以稍微修改一下我们手头的代码,以便在这种情况下为我们工作。
To do this, we're going to need to handle two things:
为此,我们需要处理两件事:
Does
MyAddonDB.kills
exist yet? If not, write it and make it equal1
.MyAddonDB.kills
还存在吗?如果不是,请编写它并使其等于1
。If it does exist, get the value of
MyAddonDB.kills
and add1
to it.
如果它确实存在,则获取MyAddonDB.kills
的值并为其添加1
。
To do this, let's write the first part of our code below:
为此,让我们在下面编写代码的第一部分:
if eventType and eventType == "PARTY_KILL" then if not MyAddonDB.kills then MyAddonDB.kills = 1 end end
This is the first bullet point that our code needs to handle.
这是我们的代码需要处理的第一个要点。
If
MyAddonDB.kills
is nil, then we need to create it.
如果MyAddonDB.kills
为 nil,那么我们需要创建它。
You'll notice we used if not
, essentially meaning if there isn't MyAddonDB.kills
. We could also use if MyAddonDB.kills == nil then
, but the above short-hand method is quicker to write and clearer to read. Remember, we always want to check for nil
before attempting to modify a piece of data or access a piece of data.
您会注意到我们使用了 if not
,本质上是表示 if there isn't MyAddonDB.kills
.我们也可以使用 if MyAddonDB.kills == nil then
,但上述速记方法写得更快,读起来更清晰。请记住,我们总是希望在尝试修改一段数据或访问一段数据之前检查 nil
。
Now how do we handle the second bullet point of our code handling? Well, this is where the else
portion of an if then (elseif) else
statement comes in to play.
现在我们如何处理代码处理的第二个要点呢?嗯,这就是 if then (elseif) else
语句的 else
部分发挥作用的地方。
Let's modify our code to this now:
现在,让我们将代码修改为:
if eventType and eventType == "PARTY_KILL" then if not MyAddonDB.kills then MyAddonDB.kills = 1 else MyAddonDB.kills = MyAddonDB.kills + 1 end end
This is basically stating (in english):
这基本上是(用英语)说的:
If
MyAddonDB.kills
does not exist, let's make it and set it equal to1
. If it does exist, we're going to get the amount thatMyAddonDB.kills
has stored already and add1
to it.
如果MyAddonDB.kills
不存在,让我们将其设置为等于1
。如果它确实存在,我们将获取MyAddonDB.kills
已经存储的数量,并将1
加起来。
You might ask, why not just initialize MyAddonDB.kills
with 0
before ever accessing it? Great question, and the way we've handled the nil
check in our addon interface basically takes care of this. You can do this either way you prefer though.
你可能会问,为什么不在访问之前用 0
初始化 MyAddonDB.kills
呢?好问题,我们在插件界面中处理 nil
检查的方式基本上解决了这个问题。不过,你可以用自己喜欢的任何方式来做这件事。
Now, have this information storing in our database. To debug this and make sure it's actually working, we're going to use an addon I mentioned in Part 1 of this tutorial called DevTools to view our database.
现在,将此信息存储在我们的数据库中。为了调试它并确保它确实有效,我们将使用我在本教程的第 1 部分中提到的名为 DevTools 的插件来查看我们的数据库。
If you don't have the addon, go ahead and download it and install it using Curseforge. Once installed, login or /reload
your game and type /dev
in-game to open it.
如果您没有插件,请继续下载并使用 Curseforge 安装它。安装后,登录或/重新加载
您的游戏,然后键入 /dev
in-game 将其打开。
Once opened, in the bottom left text field, we're going to type MyAddonDB
and press the 'Enter' key. Our database should show in the top left-hand window of the DevTools
addon.
打开后,在左下角的文本字段中,我们将键入 MyAddonDB
并按“Enter”键。我们的数据库应该显示在 DevTools
插件的左上角。
Go ahead and click on MyAddonDB
to open it. In the right-hand window, we should see our MyAddonDB
table with (1) entry showing. If it says (0), we need to first kill a creature.
继续并单击 MyAddonDB
将其打开。在右侧窗口中,我们应该看到 MyAddonDB
表,其中显示了 (1) 个条目。如果它显示 (0),我们得先杀掉一个生物。
We can click on this table to view its content. It should show an entry called kills
and that entry should be equal to the amount of kills we have recorded.
我们可以单击此表查看其内容。它应该显示一个名为 kills
的条目,并且该条目应该等于我们记录的 kills 数量。
It goes without saying that we don't want our players checking DevTools
to see how many kills they have. We need to implement this within our addon's mainFrame
window so they can open our addon to view these stats. Let's go ahead and handle that.
不言而喻,我们不希望我们的玩家检查 DevTools
来查看他们有多少杀戮。我们需要在插件的 mainFrame
窗口中实现这一点,以便他们可以打开我们的插件来查看这些统计数据。让我们继续处理这个问题。
Step 3: Make Our Data Available in the Addon
第 3 步:在插件中提供我们的数据
In our mainFrame
of the addon, we have our playerName variable showing the character name and level. Let's put some additional information below this showing the player's total kills.
在插件的 mainFrame
中,我们有 playerName 变量,显示角色名称和等级。让我们在下面添加一些额外的信息,显示玩家的总击杀数。
Same as before, let's make a new variable called mainFrame.totalPlayerKills
and set it to our database value. However, we will need an if statement
here to make sure the data is not nil before we display it.
和以前一样,让我们创建一个名为 mainFrame.totalPlayerKills
的新变量,并将其设置为我们的数据库值。但是,我们在此处需要一个 if 语句
,以确保在显示数据之前数据不为 nil。
We can do an in-line if statement
by simply putting ()
's and an or
. This is short-hand if-else that we'll explain a little bit about below.
我们可以通过简单地输入 ()
和 or
来做一个内联的 if 语句
。这是简写 if-else,我们将在下面进行一些解释。
mainFrame.totalPlayerKills = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") mainFrame.totalPlayerKills:SetPoint("TOPLEFT", mainFrame.playerName, "BOTTOMLEFT", 0, -10) mainFrame.totalPlayerKills:SetText("Total Kills: " .. (MyAddonDB.kills or "0"))
As a quick side note, VSCode may display some orange/yellow lines below our totalPlayerKills and playerName variables. You can ignore these.
顺便说一句,VSCode 可能会在 totalPlayerKills 和 playerName 变量下方显示一些橙色/黄色线条。您可以忽略这些。
Now that we have our totalPlayerKills showing, let's explain it a bit... specifically this line: mainFrame.totalPlayerKills:SetText("Total Kills: " .. (MyAddonDB.kills or "0"))
现在我们已经显示了 totalPlayerKills,让我们稍微解释一下......具体来说,这行: mainFrame.totalPlayerKills:SetText("Total Kills: " .. (MyAddonDB.kills or "0"))
This line is going to always display "Total Kills: " and the number of kills we have. If MyAddonDB.kills does not exist, or returns nil
, it will instead write "0" afterward instead of giving us an error.
此行将始终显示 “Total Kills: ” 和我们拥有的击杀数。如果 MyAddonDB.kills 不存在,或者返回 nil
,它将在之后写入 “0” 而不是给我们一个错误。
If we just wrote mainFrame.totalPlayerKills:SetText("Total Kills: " .. MyAddonDB.kills)
we would throw an error for any new players that install our addon because they may attempt to open our addon before they kill a creature. We handle this by the "0" portion of the code that states:
如果我们只是编写 mainFrame.totalPlayerKills:SetText("Total Kills: " .. MyAddonDB.kills)
,我们会给任何安装我们插件的新玩家抛出一个错误,因为他们可能会在杀死生物之前尝试打开我们的插件。我们通过代码的 “0” 部分来处理这个问题,该部分声明:
If MyAddonDB.kills is
nil
then we'll resort to showing "0" instead ofMyAddonDB.kills
and ultimately throwing an error.
如果 MyAddonDB.kills 为nil
,那么我们将显示 “0” 而不是MyAddonDB.kills
,并最终引发错误。
We have set the point of our "Total Kills: " line by parenting it to our playerName
variable, and using the offset-Y
parameter in SetPoint()
. It will always be 10 units under our playerName
string of the addon.
我们通过将 “Total Kills: ” 行设置为我们的 playerName
变量的父子关系,并在 SetPoint()
中使用 offset-Y
参数来设置该行的点。它始终是插件的 playerName 字符串下的 10
个单位。
Now, we're not entirely done. You will notice that if we kill an enemy, our addon will not update our kill count that it shows. This is because font strings are not inherently dynamic. We can make this "appear" dynamic using the "OnShow"
function event. Let's do that now.
现在,我们还没有完全完成。您会注意到,如果我们杀死了一个敌人,我们的插件将不会更新它显示的击杀计数。这是因为字体字符串本身并不是动态的。我们可以使用 “OnShow”
函数事件使这个 “出现” 动态化。我们现在就开始吧。
Step 4: Updating Text When Opening the Addon
第 4 步:打开插件时更新文本
There are a few ways to do this, but our addon is light-weight, meaning there isn't much memory being used by it and it's not going to hinder performance by updating a font string, or even fifty, by opening the addon.
有几种方法可以做到这一点,但是我们的插件是轻量级的,这意味着它不会占用太多内存,并且不会通过更新字体字符串甚至打开插件来影响性能。
What we're going to do is modify the mainFrame:SetScript("OnShow", function())
we already have:
我们要做的是修改 mainFrame:SetScript("OnShow", function())
我们已经有的:
Let's make it say the below instead:
让我们改为说下面:
mainFrame:SetScript("OnShow", function() PlaySound(808) mainFrame.totalPlayerKills:SetText("Total Kills: " .. (MyAddonDB.kills or "0")) end)
Importantly, we also need to move this function to be below our mainFrame.totalPlayerKills
variable.
重要的是,我们还需要将此函数移动到 mainFrame.totalPlayerKills
变量的下方。
This tells our addon the following (in english):
这告诉我们的插件以下内容(英文):
Any time we open/show the mainFrame of our addon, we want to set our totalPlayerKills variable to equal
MyAddonDB.kills
or0
if we have none.
每当我们打开/显示插件的 mainFrame 时,我们都希望将 totalPlayerKills 变量设置为等于MyAddonDB.kills
,如果没有,则设置为0
。
There are many ways to handle this, but in our situation, this is the best method to handle this. (We will talk later about niche cases like a player killing a monster while the window is showing/open. This is considered a quality of life improvement and isn't needed right away, but we will implement it.)
有很多方法可以处理这个问题,但在我们的情况下,这是处理这个问题的最佳方法。(我们稍后会讨论一些小众情况,比如玩家在窗口显示/打开时杀死怪物。这被认为是生活质量的改善,不是立即需要的,但我们会实施它。
Step 5: Code Review 第 5 步:代码审查
And... we're done with player kills showing in our addon! Give yourself a pat on the back! You've made it very far and we should actually having a functioning addon! Before we proceed, our code should appear as such so far:
和。。。我们已经完成了插件中显示的玩家杀戮!拍拍自己的后背吧!您已经走了很远,我们实际上应该有一个正常运行的插件!在我们继续之前,我们的代码应该如下所示:
if not MyAddonDB then MyAddonDB = {} end local mainFrame = CreateFrame("Frame", "MyAddonMainFrame", UIParent, "BasicFrameTemplateWithInset") mainFrame:SetSize(500, 350) mainFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 0) mainFrame.TitleBg:SetHeight(30) mainFrame.title = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") mainFrame.title:SetPoint("TOPLEFT", mainFrame.TitleBg, "TOPLEFT", 5, -3) mainFrame.title:SetText("MyAddon") mainFrame:Hide() mainFrame:EnableMouse(true) mainFrame:SetMovable(true) mainFrame:RegisterForDrag("LeftButton") mainFrame:SetScript("OnDragStart", function(self) self:StartMoving() end) mainFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end) mainFrame:SetScript("OnHide", function() PlaySound(808) end) mainFrame.playerName = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") mainFrame.playerName:SetPoint("TOPLEFT", mainFrame, "TOPLEFT", 15, -35) mainFrame.playerName:SetText("Character: " .. UnitName("player") .. " (Level " .. UnitLevel("player") .. ")") mainFrame.totalPlayerKills = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") mainFrame.totalPlayerKills:SetPoint("TOPLEFT", mainFrame.playerName, "BOTTOMLEFT", 0, -10) mainFrame.totalPlayerKills:SetText("Total Kills: " .. (MyAddonDB.kills or "0")) mainFrame:SetScript("OnShow", function() PlaySound(808) mainFrame.totalPlayerKills:SetText("Total Kills: " .. (MyAddonDB.kills or "0")) end) SLASH_MYADDON1 = "/myaddon" SlashCmdList["MYADDON"] = function() if mainFrame:IsShown() then mainFrame:Hide() else mainFrame:Show() end end table.insert(UISpecialFrames, "MyAddonMainFrame") local eventListenerFrame = CreateFrame("Frame", "MyAddonEventListenerFrame", UIParent) local function eventHandler(self, event, ...) local _, eventType = CombatLogGetCurrentEventInfo() if event == "COMBAT_LOG_EVENT_UNFILTERED" then if eventType and eventType == "PARTY_KILL" then if not MyAddonDB.kills then MyAddonDB.kills = 1 else MyAddonDB.kills = MyAddonDB.kills + 1 end end end end eventListenerFrame:SetScript("OnEvent", eventHandler) eventListenerFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")