This is a reply to Kakjens on curse but if any others have questions or interest on this topic feel free to chime in. I'm posting here instead of on curse since this forum's code tag is very nice and curse's comment system isn't very good for complex/long posts.
He posted some code that loads a heal team with two of the most injured pets while shift is held down in a specific zone (I assume celestial tournament). I recommend making a separate addon to do this so the direct edits don't need re-copied each update.
Sorry in advance if any of this covers ground you're already familiar with.
First step is to set up your toc with a Rematch dependency:
Code: Select all
## Interface: 70000
## Title: RematchShiftTarget
## Notes: Does something different when Shift is held while targeting.
## Dependencies: Rematch
RematchShiftTarget.lua
This will wait to load your code until after Rematch has loaded. It's important to note that this will load
after Rematch loads but
before any SavedVariables loads, which includes RematchSaved which contains the teams. This will be important later.
The function we want to modify is Rematch:PLAYER_TARGET_CHANGED. In lua these two are equivalent:
Code: Select all
function Rematch:PLAYER_TARGET_CHANGED()
and
Code: Select all
function Rematch.PLAYER_TARGET_CHANGED(self)
self is the calling frame. For this particular function/event we don't need to care about it, except it's important to remember it's there and it should be passed along.
To do an outright replacement of the function (which you don't want to do) you could:
Code: Select all
function Rematch:PLAYER_TARGET_CHANGED()
print("Do my stuff")
end
Because your addon loads after Rematch does (since you declared it as a dependency), your version of Rematch.PLAYER_TARGET_CHANGED will replace the one defined in Main.lua.
This isn't what you wanted, of course. You want to modify the behavior of the existing function. You can do that a couple ways. If you want the entire function to run and then run your code
after the old code ran (aka post-hook), you can use one of two methods. This is a secure hook:
Code: Select all
local function my_PLAYER_TARGET_CHANGED(self)
print("Do my stuff")
end
hooksecurefunc(Rematch,"PLAYER_TARGET_CHANGED",my_PLAYER_TARGET_CHANGED)
Rematch.PLAYER_TARGET_CHANGED will do its thing and then your code will be called with the same parameters. This is a secure hook because it doesn't tamper with the original. It's most commonly used to post-hook Blizzard code, since tampering stuff in an execution path will taint everything your code manipulates.
An unsecure post-hook looks like:
Code: Select all
local old_PLAYER_TARGET_CHANGED = Rematch.PLAYER_TARGET_CHANGED
local function my_PLAYER_TARGET_CHANGED(self)
print("Do my stuff")
end
function Rematch:PLAYER_TARGET_CHANGED()
old_PLAYER_TARGET_CHANGED(self)
return my_PLAYER_TARGET_CHANGED(self)
end
This will save the pointer to the original function, then you can redefine the function to call the original and then your code. (Your code can be in the new Rematch:PLAYER_TARGET_CHANGED too; it's a separate function for clarity).
When you're doing an unsecure hook, it's very good practice to return the results as if you were the legitimate function. In this case there's nothing to return so it's not necessary, but it's critically important in most of the unsecure hooks you'll be doing.
But if you want to only do the original function some of the time, and your own stuff some of the time, you want a pre-hook like this:
Code: Select all
local old_PLAYER_TARGET_CHANGED = Rematch.PLAYER_TARGET_CHANGED
local function my_PLAYER_TARGET_CHANGED(self)
print("Do my stuff")
end
function Rematch:PLAYER_TARGET_CHANGED()
if IsShiftKeyDown() then
return my_PLAYER_TARGET_CHANGED(self)
else
return old_PLAYER_TARGET_CHANGED(self)
end
end
If Shift is down it will run my_PLAYER_TARGET_CHANGED and not the original at all. If Shift is not down it will run the original.
In your particular case you want to deal with saved teams (RematchSaved), which is not defined when your addon loads. You can do this in one of two ways. The first is the use the global RematchSaved directly:
Code: Select all
local old_PLAYER_TARGET_CHANGED = Rematch.PLAYER_TARGET_CHANGED
local function my_PLAYER_TARGET_CHANGED(self)
print("Do my stuff")
end
function Rematch:PLAYER_TARGET_CHANGED()
if IsShiftKeyDown() then
local name,npcID = Rematch:GetUnitNameandID("target")
if RematchSaved[npcID] then
print("A team is saved for",name)
end
return my_PLAYER_TARGET_CHANGED(self)
else
return old_PLAYER_TARGET_CHANGED(self)
end
end
If Shift is down when targeting it will print whether a team is saved for the target, and then run my_PLAYER_TARGET_CHANGED regardless.
If you use a local at the beginning like:
Code: Select all
local old_PLAYER_TARGET_CHANGED = Rematch.PLAYER_TARGET_CHANGED
local saved = RematchSaved -- this will fail!
local function my_PLAYER_TARGET_CHANGED(self)
print("Do my stuff")
end
function Rematch:PLAYER_TARGET_CHANGED()
if IsShiftKeyDown() then
local name,npcID = Rematch:GetUnitNameandID("target")
if saved[npcID] then
print("A team is saved for",name)
end
return my_PLAYER_TARGET_CHANGED(self)
else
return old_PLAYER_TARGET_CHANGED(self)
end
end
RematchSaved isn't defined when your addon loads and the local reference will continue to be nil even after the global is defined later.
In a traditional addon you'd wait until PLAYER_LOGIN. If you'd rather not create a frame to register for that event, you can use Rematch:InitModule(func) to do stuff. When you call that it will run your function after PLAYER_LOGIN and after Rematch has validated saved variables and stuff:
Code: Select all
local old_PLAYER_TARGET_CHANGED = Rematch.PLAYER_TARGET_CHANGED
local saved
Rematch:InitModule(function()
saved = RematchSaved
end)
local function my_PLAYER_TARGET_CHANGED(self)
print("Do my stuff")
end
function Rematch:PLAYER_TARGET_CHANGED()
if IsShiftKeyDown() then
local name,npcID = Rematch:GetUnitNameandID("target")
if saved[npcID] then
print("A team is saved for",name)
end
return my_PLAYER_TARGET_CHANGED(self)
else
return old_PLAYER_TARGET_CHANGED(self)
end
end
If you want to be consistent with the main addon you can make a local rematch=Rematch too and use rematch. It will be defined before your code loads and it's safe to assign:
Code: Select all
local rematch = Rematch
local old_PLAYER_TARGET_CHANGED = rematch.PLAYER_TARGET_CHANGED
local saved
rematch:InitModule(function()
saved = RematchSaved
end)
local function my_PLAYER_TARGET_CHANGED(self)
print("Do my stuff")
end
function Rematch:PLAYER_TARGET_CHANGED()
if IsShiftKeyDown() then
local name,npcID = rematch:GetUnitNameandID("target")
if saved[npcID] then
print("A team is saved for",name)
end
return my_PLAYER_TARGET_CHANGED(self)
else
return old_PLAYER_TARGET_CHANGED(self)
end
end
And now your code will persist and do its thing regardless of most changes made to Rematch.
I say most because the code is undergoing a great deal of change. However, Rematch.PLAYER_TARGET_CHANGED, Rematch.LoadTeam, etc will remain so chances are your code will continue relatively unchanged. The team format is changing slightly but the first index will remain the petID. But that will become obvious when it happens.
Maybe of particular interest to addon authors reading this (if any have gotten this far), but you'll be able to add your own tabs and panels to Rematch 5.0 very easily with a separate addon. I'm rebuilding the leveling queue to be a separate addon (so it can be disabled when someone is done leveling pets). In the future I may even add Discodoggy's earlier suggestion of a Records tab that will list the success of all pets. I mentioned back then it was a bit excessive for the addon to carry around for people who wouldn't use that feature; but as a separate addon it can go wild recording everything it wants.
Sorry for the long post!