09-23-12, 12:59 PM | #1 |
Help with my addon
Hello WoWinterface!
Recently I have been trying to "learn" LUA, to modify addons to ensure my UI ran as smooth as I'd like. I finally took the jump to make my own addon! I've run into quite a snag though >.< The addon works great, after the first login, and reloading of my UI, but when I login I am prompted with this error: Code:
Message: Interface\AddOns\ClamHolyPower\core.lua:67: attempt to index field '?' (a nil value) Time: 09/23/12 14:54:03 Count: 2 Stack: Interface\AddOns\ClamHolyPower\core.lua:67: in function <Interface\AddOns\ClamHolyPower\core.lua:64> Locals: p = 0 (for index) = 1 (for limit) = 5 (for step) = 1 i = 1 (*temporary) = nil (*temporary) = nil (*temporary) = "1" (*temporary) = "attempt to index field '?' (a nil value)" _G = <table> { ContainerFrame5Item7 = ContainerFrame5Item7 { } MultiCastActionButton6Cooldown = MultiCastActionButton6Cooldown { } MerchantItem9ItemButtonStock = MerchantItem9ItemButtonStock { } GetTrainerServiceTypeFilter = <function> defined =[C]:-1 UNIT_NAMES_COMBATLOG_TOOLTIP = "Color unit names." UNIT_NAMEPLATES_TYPE_TOOLTIP_3 = "This method avoids overlapping nameplates by spreading them out horizontally and vertically." SetTrainerServiceTypeFilter = <function> defined =[C]:-1 EventTraceFrameButton7HideButton = EventTraceFrameButton7HideButton { } SPELL_FAILED_CUSTOM_ERROR_71 = "This partygoer wants to dance with you." CompactUnitFrameProfilesGeneralOptionsFrameHealthTextDropdownButtonNormalTexture = CompactUnitFrameProfilesGeneralOptionsFrameHealthTextDropdownButtonNormalTexture { } TutorialFrameLeft19 = TutorialFrameLeft19 { } MultiCastActionButton2Cooldown = MultiCastActionButton2Cooldown { } ERR_TRADE_EQUIPPED_BAG = "You can't trade equipped bags." PVP_RANK_6_1 = "Corporal" BOOKTYPE_PROFESSION = "professions" OPTION_TOOLTIP_SHOW_MULTIBAR4 = "Toggles an additional optional actionbar on the right side of the screen." InterfaceOptionsDisplayPanelShowAggroPercentageText = InterfaceOptionsDisplayPanelShowAggroPercentageText { } VideoOptionsFrameCategoryFrameButton17ToggleHighlightTexture = VideoOptionsFrameCategoryFrameButton17ToggleHighlightTexture { } MerchantItem3AltCurrencyFrameItem1Text = MerchantItem3AltCurrencyFrameItem1Text { } OPTION_TOOLTIP_ACTION_BUTTON_USE_KEY_DOWN = "Action button keybinds will respond on key down, rather than on key up." BINDING_NAME_NAMEPLATES = "Show Enemy Name Plates" CHAT_HEADER_SUFFIX = ": " IsReferAFriendLinked = <function> defined =[C]:-1 RaidTray_Buff_OnUpdate = <function> defined @Interface\FrameXML\BuffFrame.lua:149 QuestDetailBotLeftCorner = QuestDetailBotLeftCorner { } ItemTextFrameInsetInsetTopRightCorner = ItemTextFrameInsetInsetTopRightCorner { } CALENDAR_RAID_RESET_DESCRIPTION = "%1$s resets at %2$s." CHAT_CONFIG_OTHER_COMBAT = <table> { } CONSOLIDATED_BUFFS_PER_ROW = 4 TutorialFrameRight19 = TutorialFrameRight19 { } MoneyFrame_OnEvent = <function> defined @Interface\FrameXML\MoneyFrame.lua:233 BN_UNABLE_TO_RESOLVE_NAME = "Unable to whisper '%s'. Battle.net may be unavailable." WatchFrameItem_OnEvent = <function> defined @Interface\FrameXML\WatchFrame.lua:1367 InterfaceOptionsCombatTextPanelFCTDropDown_OnClick = <function> defined @Interface\FrameXML\InterfaceOptionsPanels.lua:1419 LFGTeleport = <function> defined =[C]:-1 SpellButton6Cooldown = SpellButton6Cooldown { } ToggleEncounterJournal = <function> defined @Interface\FrameXML\UIParent.lua:617 Graphics_QualityText = Graphics_QualityText { } TalentMicroButtonAlertShadowTopLeft = TalentMicroButtonAlertShadowTopLeft { } ROGUE_COMBAT_CORE_ABILITY_4 = "Use for damage when you have 5 combo points. Primary finishing move." Advanced_GraphicsAPIDropDownButtonHighlightTexture = Advanced_GraphicsAPIDropDownButtonHighlightTexture { } CompactUnitFrameProfilesSaveButton = CompactUnitFrameProfilesSaveButton { } BankFrameItem17SearchOverlay = BankFrameItem17SearchOverlay { } ACTION_SPELL_MISSED_POSSESSIVE = "1" ChannelMemberButton10SpeakerFrameOn = ChannelMemberButton10SpeakerFrameOn { } EventTraceFrameTitleButton = EventTraceFrameTitleButton { } Is64BitClient = <function> defined =[C]:-1 SecureCmdItemParse = <function> defined @Interface\FrameXML\ChatFrame.lua:1047 SLASH_STOPATTACK1 = "/stopattack" MultiCastActionButton4HotKey = MultiCastActionButton4HotK Lua Code:
I really can not understand why my addon is throwing errors on the first login, but works 100% fine after I reload the UI. Thank you very much for the time and help! |
|
09-23-12, 01:18 PM | #2 |
It is possible that you are asking for some information about your character too early when first logging in. There are a few things that are not available right away on first login, but will be when reloading. (When you reload your UI, your character does not exit and re-enter the world.) Add print() statements to your code at various places to help debug.
For instance, after line 35 add: Code:
print(maxhp) |
|
09-23-12, 01:20 PM | #3 |
I was in the middle of editing my post, when I figured I'd give ONE more refresh!
That is it! UnitPowerMax("player", 9) is not returning a value upon logging in, setting Maxhp to 5 manually solves the issue. Thank you so much for your time anyways, Seerah! |
|
09-23-12, 01:22 PM | #4 |
You have local maxhp = UnitPowerMax("player", 9) at file scope, meaning it tries to run immediately upon the client reading your addon file.
It's possible that information is not available to the client yet. It's best if you do all your initialization at the PLAYER_LOGIN event (including registering the rest of the events you want to monitor) |
|
09-23-12, 01:27 PM | #5 |
Learning these things one step at a time heh.
I really appreciate the help Seerah and Dridzt. I will look at registering "UNIT_POWER" after "PLAYER_LOGIN" as well. Going to do some reorganizing =] |
|
09-23-12, 02:26 PM | #6 | |
This line should be throwing an error too since HIGH is not a valid DrawLayer. It might not be doing so now since the code as-is isn't even running the line right now because of issues already posted above.
__________________
WoWInterface AddOns
|
||
09-23-12, 09:24 PM | #7 |
@SDPhantom: There are no further errors. The code ran "fine", after I sorted out the call to a nil value (Unit Power Max isn't defined on startup, hence the error, manually defining it or calling it after Player_Login works). The code has yet to error during normal use (instances and the like).
I'll make sure to change it to ARTWORK. Thank you very much for your input! EDIT: Also, anyone who is reading this, I wanted to inquire about my function for drawing the indicators. Like I said, other than editing blatantly obvious aspects of popular addons, I have less than three full days of LUA experience. When I was trying to come up with a way to draw all the indicators with one function, I stumbled upon a website describing dynamic variables ( _G["ind"..i] in my case). Someone on that website mentioned that this is not the best way to create them, and results in a memory leak? Is this true, false? Can anyone offer some insight? Thank you everyone for your help, it is helping immensely. Last edited by Clamsoda : 09-23-12 at 09:32 PM. |
|
09-23-12, 10:07 PM | #8 |
It's not a memory leak, but it's also one of worst possible methods. First, you're putting things in the global namespace (the area shared by all addons and the default UI) that don't need to be there (they should be local to your addon). Second, you're putting things in the global namespace with completely generic names like "ind4". If you need to put something in the global namespace (many addons put their main addon object there, for example) you should give it a name that both describes its function, and identifies its origin, like "ClamsodaAddonIndicator4". In this case, though, these objects should not be global.
A better way is to store the objects in an indexed table, and access them that way. Here is your original code, with all of the initialization stuff moved into the PLAYER_LOGIN event handler, the indicators moved into a table, the UNIT_POWER event handler rewritten more efficiently, and a few other minor changes. Code:
--Disable ClamHolyPower if player is not a paladin. if select(2, UnitClass("player")) ~= "PALADIN" then return DisableAddOn("ClamHolyPower") end ------------------- -- CONFIGURATION -- ------------------- -- Background Configuration -- local bkr = 0 -- Background color: Red local bkg = 0 -- Background color: Green local bkb = 0 -- Background color: Blue local bka = 0.60 -- Background alpha: 0 = Transparent 1 = Opaque local hwe = false -- Hide the background when holy power is empty. -- Indicator Configuration -- local itex = "Interface\\AddOns\\ClamHolyPower\\flatsmooth" -- Path pointing to the texture used for the indicator. local ir = 1 -- Indicator color: Red local ig = 0.8 -- Indicator color: Green local ib = 0 -- Indicator color: Blue -- Frame Configuration -- local fposx = -193.25 -- Frame position: X local fposy = -243 -- Frame position: Y local fwidth = 231 -- Frame width. local fheight = 11 -- Frame height. ----------------------- -- END CONFIGURATION -- ----------------------- -- Pertinent Variables local maxhp local vedges -- Create ClamHolyPowerFrame local f = CreateFrame("Frame", "ClamHolyPowerFrame", UIParent) f:SetScript("OnEvent", function(self, event, ...) return self[event](self, event, ...) end) f:RegisterEvent("PLAYER_LOGIN") function f:PLAYER_LOGIN() -- set these values here instead of at runtime maxhp = UnitPowerMax("player", 9) vedges = maxhp + 1 -- Defines the amount of vertical 1px borders there will be. Defined as maximum holy power + 1 self:SetFrameStrata("BACKGROUND") self:SetPoint("CENTER",fposx,fposy) self:SetWidth(fwidth) self:SetHeight(fheight) -- Create Background Frame -- local bk = self:CreateTexture(nil, "BACKGROUND") bk:SetTexture(bkr, bkg, bkb, bka) bk:SetAllPoints(true) self.background = bk -- Create Indicator Frames -- local iwidth = floor((fwidth - vedges) / maxhp) -- Attempt to use the frame width to dictate the width of each indicator. May need to floor/ciel/tweak local iheight = fheight - 2 -- Attempt to use the frame height to dictate the height of each indicator. Should need no tweaking self.indicators = {} for i = 1, maxhp do local ind = self:CreateTexture(nil, "HIGH") ind:SetTexture(itex) -- Just anchor each indicator relative to the previous one, instead of doing math. if i == 1 then ind:SetPoint("LEFT", self.indicators[i-1], "RIGHT", 1, 0) else ind:SetPoint("LEFT", f, 1, 0) end ind:SetWidth(iwidth) ind:SetHeight(iheight) ind:SetVertexColor(ir, ig, ib) self.indicators[i] = ind end self:UnregisterEvent("PLAYER_LOGIN") -- only register UNIT_POWER for the player unit self:RegisterUnitEvent("UNIT_POWER", "player") end function f:UNIT_POWER(event, unit, powerType) if powerType ~= HOLY_POWER" then -- Don't waste time updating if holy power didn't change. return end local p = UnitPower("player", 9) if p > 0 then for i = 1, #self.indicators do if i > p then self.indicators[i]:Hide() else self.indicators[i]:Show() end end self:Show() else self:Hide() end end
__________________
Retired author of too many addons. Message me if you're interested in taking over one of my addons. Don’t message me about addon bugs or programming questions. |
|
09-23-12, 10:31 PM | #9 |
Very very interesting insight. I can understand how my method could be a pollutant.
I copied your pasted code verbatim, and it doesn't work. I fixed the un-finished string on line 84, and it still doesn't work as intended. It isn't erroring, but, is not producing the correct results, and admittedly I am overwhelmed by a lot of your changes (though greatly inspired!) The most obvious change, other than the initialization changes, is the change to my create indicator function. I love how simple you made it, and I love that you got rid of my ever-so-clunky math >.> If you could help me troubleshoot the code you wrote, that'd be very helpful. I feel like I am in a foreign land without a map! |
|
09-23-12, 10:41 PM | #10 |
Well depending what UnitPowerMax() returns in PLAYER_LOGIN there's a good chance not enough frames (or none at all) for hopo are initialized.
Also doesn't paladin (don't play one) have some talent that can increase the number, re-talenting or re-speccing should also be taken into account (OR in the UNIT_POWER handler the current hopo returned checked against the existence of sufficient frames and fill in any missing) Last edited by Dridzt : 09-23-12 at 10:48 PM. |
|
09-23-12, 10:45 PM | #11 |
Well, here is how it is operating (ignore the hide when empty condition):
0 holy power - background is hidden 1 holy power - background shows (no indicators) 2 holy power - 1st indicator shows (positioned correctly) all subsequent holy power does NOT create a new indicator. Like I said, I am very lost in Phanx's code - so I am trying to wrap my head around the syntax, AND the logic. EDIT: Dridzt, holy power max does change, yes. The way I wrote my addon, it checked for UnitMaxPower, and generated enough indicators to match that, at appropriate widths. Unfortunately, UnitMaxPower isn't initializing properly in MY code, do to a value not returning on PLAYER_LOGIN, so I am using a static value of 5 for testing. EDIT: I was able to achieve proper functionality by replacing: Lua Code:
With my old, math heavy function. One-thousandth Edit >.>: I am only one step removed from getting my addon to work perfectly with the majority of Phanx's changes. I am noticing the frame doesn't update after a reload of the UI, or if I login with holy power, until the UNIT_POWER event fires. I pasted the code from f:UNIT_POWER into f:PLAYER_LOGIN(), and it works great, but obviously I don't want the exact same code in two places. I can't seem to get f:PLAYER_LOGIN() to run f:UNIT_POWER() one time, just to force an update, seeing as the function is event driven. Last edited by Clamsoda : 09-23-12 at 11:13 PM. |
|
09-23-12, 11:23 PM | #12 |
Gave her a go around, Dridzt.
Code:
Message: Interface\AddOns\ClamHolyPower\core.lua:79: attempt to index local 'self' (a nil value) Time: 09/24/12 01:19:25 Count: 1 Stack: Interface\AddOns\ClamHolyPower\core.lua:79: in function `?' Interface\AddOns\ClamHolyPower\core.lua:40: in function <Interface\AddOns\ClamHolyPower\core.lua:39> Locals: self = nil bk = <unnamed> { 0 = <userdata> } (*temporary) = <function> defined =[C]:-1 (*temporary) = <unnamed> { 0 = <userdata> } (*temporary) = true (*temporary) = <userdata> (*temporary) = 0 (*temporary) = 0.6 (*temporary) = "attempt to index local 'self' (a nil value)" maxhp = 5 vedges = 6 f = ClamHolyPowerFrame { CreateIndicators = <function> defined @Interface\AddOns\ClamHolyPower\core.lua:45 UNIT_POWER = <function> defined @Interface\AddOns\ClamHolyPower\core.lua:94 PLAYER_LOGIN = <function> defined @Interface\AddOns\ClamHolyPower\core.lua:65 UNIT_MAXPOWER = <function> defined @Interface\AddOns\ClamHolyPower\core.lua:116 0 = <userdata> OnEvent = <function> defined @Interface\AddOns\ClamHolyPower\core.lua:39 } fposx = -193.25 fposy = -243 fwidth = 231 fheight = 11 bkr = 0 bkg = 0 bkb = 0 bka = 0.6 As for your code, might you explain to me some of your changes? As you guys know I am lost and lost and lost Trying to keep up though guys, I appreciate every single response! |
|
09-23-12, 11:29 PM | #13 |
Code:
--Disable ClamHolyPower if player is not a paladin. if select(2, UnitClass("player")) ~= "PALADIN" then return DisableAddOn("ClamHolyPower") end ------------------- -- CONFIGURATION -- ------------------- -- Background Configuration -- local bkr = 0 -- Background color: Red local bkg = 0 -- Background color: Green local bkb = 0 -- Background color: Blue local bka = 0.60 -- Background alpha: 0 = Transparent 1 = Opaque local hwe = false -- Hide the background when holy power is empty. -- Indicator Configuration -- local itex = "Interface\\AddOns\\ClamHolyPower\\flatsmooth" -- Path pointing to the texture used for the indicator. local ir = 1 -- Indicator color: Red local ig = 0.8 -- Indicator color: Green local ib = 0 -- Indicator color: Blue -- Frame Configuration -- local fposx = -193.25 -- Frame position: X local fposy = -243 -- Frame position: Y local fwidth = 231 -- Frame width. local fheight = 11 -- Frame height. ----------------------- -- END CONFIGURATION -- ----------------------- -- Pertinent Variables local maxhp local vedges -- Create ClamHolyPowerFrame local f = CreateFrame("Frame", "ClamHolyPowerFrame", UIParent) f.OnEvent = function(self,event,...) return self[event] and self[event](...) end f:SetScript("OnEvent", f.OnEvent) f:RegisterEvent("PLAYER_LOGIN") function f.CreateIndicators(maxhp) if not maxhp or maxhp <=0 then return end vedges = maxhp + 1 -- Defines the amount of vertical 1px borders there will be. Defined as maximum holy power + 1 local iwidth = floor((fwidth - vedges) / maxhp) -- Attempt to use the frame width to dictate the width of each indicator. May need to floor/ciel/tweak local iheight = fheight - 2 -- Attempt to use the frame height to dictate the height of each indicator. Should need no tweaking for i=1,maxhp do if not f.indicators[i] then local ind = f:CreateTexture(nil, "OVERLAY") ind:SetTexture(itex) -- Just anchor each indicator relative to the previous one, instead of doing math. if i == 1 then ind:SetPoint("LEFT", f, 1, 0) else ind:SetPoint("LEFT", f.indicators[i-1], "RIGHT", 1, 0) end ind:SetWidth(iwidth) ind:SetHeight(iheight) ind:SetVertexColor(ir, ig, ib) f.indicators[i] = ind f.indicators[i]:Hide() end end end function f.PLAYER_LOGIN() -- set these values here instead of at runtime maxhp = UnitPowerMax("player", SPELL_POWER_HOLY_POWER) f:SetFrameStrata("BACKGROUND") f:SetPoint("CENTER",fposx,fposy) f:SetWidth(fwidth) f:SetHeight(fheight) if hwe then f:Hide() end -- Create Background Frame -- local bk = f:CreateTexture(nil, "BACKGROUND") bk:SetTexture(bkr, bkg, bkb, bka) bk:SetAllPoints(true) f.background = bk f.indicators = {} f.CreateIndicators(maxhp) f:UnregisterEvent("PLAYER_LOGIN") -- only register UNIT_POWER|MAXPOWER for the player unit f:RegisterUnitEvent("UNIT_POWER", "player") f:RegisterUnitEvent("UNIT_MAXPOWER", "player") end function f.UNIT_POWER(...) local _, powerType = ... if powerType ~= "HOLY_POWER" then -- Don't waste time updating if holy power didn't change. return end local p = UnitPower("player", SPELL_POWER_HOLY_POWER) if p then for i = 1, #f.indicators do if i > p then f.indicators[i]:Hide() else f.indicators[i]:Show() end end if p > 0 then f:Show() else if hwe then f:Hide() end end end end function f.UNIT_MAXPOWER(...) local _, powerType = ... if powerType ~= "HOLY_POWER" then return end local maxhp = UnitPowerMax("player", SPELL_POWER_HOLY_POWER) f.CreateIndicators(maxhp) end Edit: And grrr.. another in-place edit, didn't notice the event functions were called with : notation. (f:EVENT(...) means the first parameter is the frame itself, f.EVENT(...) means the first parameter is the event's arg1, the way I did the OnEvent function the . notation is the correct one since I'm only passing the event arguments - the self[event](...) part) Last edited by Dridzt : 09-24-12 at 12:12 AM. |
|
09-23-12, 11:37 PM | #14 |
Dridzt, I tired your revised code, the background and all 5 indicators are present, at all times, and that does not change.
I was able to use your indicator SetPoint function where Phanx's did not work, to successfully replace my math heavy function. Thanks much for that! Edit for your edit: I tried that code, works fairly well. When logging in or reloading the UI, the background and all 5 indicators are present, but as soon as UNIT_POWER fires, it works flawlessly! Last edited by Clamsoda : 09-23-12 at 11:40 PM. |
|
09-23-12, 11:44 PM | #15 |
Try the last revision, I just added a
f.indicators[i]:Hide() in the CreateIndicators() function so they start out hidden by default and visibility is controlled by UNIT_POWER as it should. You can do f:Hide() in your PLAYER_LOGIN as well to hide the background at the beginning if you want that as well. Last edited by Dridzt : 09-23-12 at 11:46 PM. |
|
09-23-12, 11:52 PM | #16 |
Well, that is what I am working on right now. Your code is working great, but I want to use the Hide When Empty(hwe) variable to allow the player to hide, or not hide the background when holy power is empty.
I am trying to integrate that logic into your code, with little success. |
|
09-23-12, 11:59 PM | #17 |
I changed it a little again, try that one.
Btw, the whole thing can be written in less lines of code and in a sense simplified, but I thought better keep it relatively verbose and simple before diving into metatables and such (and I'm assuming Phanx opted for verbosity for the same reason) since it's meant to be a learning experience. Last edited by Dridzt : 09-24-12 at 12:02 AM. |
|
09-24-12, 12:02 AM | #18 |
It doesn't like what you changed at all haha. It is funny, looking at your code, I made the EXACT same implementations for HWE's logic, and got the same failed result:
It works perfectly when hwe is set to true, but when it it set to false, it add's HP and doesn't remove it. Perhaps we need to update the indicators, so they hide and not the background? I tried forcing f.indicators:Hide(), but they aren't going away! Last edited by Clamsoda : 09-24-12 at 12:05 AM. |
|
09-24-12, 12:12 AM | #19 |
How about edit 'I ve lost count' ?
|
|
09-24-12, 12:16 AM | #20 |
Hahaha, it works great Dridzt.
I can't thank you, Phanx, SDPhantom, and Seerah enough for helping me. You guys have made my foray into addon making such a fun one. I apologize that I wasn't able to produce any solutions to the problems in your code, Dridzt - but I appreciate you sticking around to see it through =]. I am open to any information, suggestions, potential improvements to the code, it is all a learning experience! |
|
WoWInterface » Developer Discussions » Lua/XML Help » Help with my addon |
«
Previous Thread
|
Next Thread
»
|
Display Modes |
Linear Mode |
Switch to Hybrid Mode |
Switch to Threaded Mode |
|
|