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 not nil and eventType 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 equal 1.
    MyAddonDB.kills 还存在吗?如果不是,请编写它并使其等于 1

  • If it does exist, get the value of MyAddonDB.kills and add 1 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 to 1. If it does exist, we're going to get the amount that MyAddonDB.kills has stored already and add 1 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 of MyAddonDB.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 or 0 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")