+ Reply to Thread
Page 1 of 3 1 2 3 LastLast
Results 1 to 15 of 33
Like Tree1Likes

Thread: New to Lua and Addon Development, a couple questions

  1. #1
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default New to Lua and Addon Development, a couple questions

    Hi all,

    I'm currently learning Lua and addon development. I have a couple questions about an addon I'd like to write.

    I was playing around last night with some event triggers, and think I have a handle on that. What I'd like to do is pull the name of the group member the triggering event applied to and (for now) print it.

    After looking through the APIs and some addon code, it seems I need to use:

    details = Inspect.Unit.Detail(unit)

    in order to get the name of the unit. What I'm confused on is where the argument "unit" comes from. Do I need to create a function to get the unit ID or specify a specific unit, or is this something that is automatically generated based on the event?

    For instance, if I was to use this code (from a tutorial I found):

    function myEventHandler()
    print("Something has died.")
    end
    table.insert(Event.Combat.Death, {myEventHandler, "DeathAlert", "aCombatDeathEvent"})

    If I wanted to display a unit name instead of the word "something".

    Thanks in advance for your help!

    ~ Llokii
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  2. #2
    RIFT Guide Writer Noshei's Avatar
    Join Date
    Feb 2011
    Posts
    1,886

    Default

    So a few things to note.

    First off, you are using the old event system. What you want to do to hook into the event is this:

    Code:
    Command.Event.Attach(Event.Combat.Death, myEventHandler, "aCombatDeathEvent")
    second, you need to be ready for a few parameters to be passed to your function from the event, so you want your function to be defined like this:

    Code:
    function myEventHandler(hEvent, unit)
    the hEvent bit is just some information about what triggered the event, generally this will be an empty table, but there are cases where it isn't. Even though it will be an empty table a lot of the time, you have to expect the parameter anyways. The unit parameter is the one you care about and is the information about the about what happened with regards to this event.

    For this event the unit parameter will be a table with at least 1 key value pair and possible more. Lets take a look at the documentation for this Event and we will find this:

    Code:
    	Event.Combat.Death
    		Event documentation:
    			Signals the death of a unit. All units referenced by this event will be accessible within this event's handlers.
    				Event.Combat.Death(info)
    		Parameters:
    			info: Detailed information table about this event, containing several named parameters.
    		Parameter members:
    			caster:	The unit ID for this event's initiator, if one exists.
    			casterName:	The name of this event's initiator, if available.
    			target:	The unit ID for the target.
    			targetName:	The name of the target, if available.
    So we know we should always get the target key, and possibly more. In this case the target key will be the unit ID for the unit that has died. So we will need to do an inspect to get the name of the target, but since the targets name might be included in the parameter we should check if that exists first.

    So lets put all of this together:

    Code:
    local function myEventHandler(hEvent, unit)
    	if unit then
    		if unit.targetName then
    			print(unit.targetName .. " has died!")
    		else
    			local details = Inspect.Unit.Detail(unit.target)
    			print(details.name .. " has died!")
    		end
    	end
    end
    Command.Event.Attach(Event.Combat.Death, myEventHandler, "aCombatDeathEvent")
    So a few things you may ask about, I put local before function so that it isn't a global function. It is best to always make your functions local unless you really need it to be global. If you aren't sure if you need it to be global, you most likely don't. I did a test to check is the unit table existed, this isn't exactly necessary, but it can sometimes save you from getting an error if for some reason the event triggered without passing any parameters.

  3. #3
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default

    Thanks Noshei! That actually clears things up quite a bit I'm gonna play with this more when I get home from work tonight.

    I appreciate the time you took to explain that!
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  4. #4
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default

    So I got a bunch of the functionality working, but I'm running into a problem passing values between functions.

    I'm passing the unit name from the main function to an "add" function, and it passes fine. I then pass the name from the "add" function to a bar settings function, but the name is not passing.


    main function
    Code:
    local function AggroAlert(hEvent, units)
    	if units then
    		for id,value in pairs(units) do
    			local details = Inspect.Unit.Detail(id)
    			if value then
    				-- TO DO: Don't display tanks
    				-- if details.role ~= "tank" then
    				local name = details.name
    				print(name .. " has gained aggro")
    				aggroAdd(id, name)
    				-- end (end if statement)
    			else
    				-- TO DO: Don't display tanks
    				-- if details.role ~= "tank"
    				print(name .. " has lost aggro")
    				aggroRemove(id)
    				-- end (end if statement)
    			end
    		end
    	end
    end
    Command.Event.Attach(Event.Unit.Detail.Aggro, AggroAlert, "anAggroAlert")
    "add" function
    Code:
    local function aggroAdd(id, name)
    	print ("ID = " .. id .. " and name = " .. name)
    	resync = true
    	--just to make sure
    	if activeBars[id] then
    		aggroRemove(id)
    	end
    	
    	local bar = table.remove(inactiveBars)
    	if not bar then
    		bar = createBar()
    	end
    	bar:SetVisible(true)
    	bar.aggroBar(name)
    	activeBars[id] = bar
    	refresh()
    end
    Bar settings function
    Code:
    local function createBar()
    	local bar = UI.CreateFrame("Frame", "bar", context)
    	
    
    	-- Set an initial vertical pin to make some of our calculations work properly
    	bar:SetPoint("TOP", UIParent, "TOP")
    
    	bar.text = UI.CreateFrame("Text", "Text", bar)
    	-- Solid background - this is the actual "bar" part of it.
    	bar.solid = UI.CreateFrame("Frame", "Solid", bar)
    	bar.solid:SetLayer(-1)  -- Put it behind every other element.
    
    	bar.text:SetText("test")
    	bar.text:SetHeight(bar.text:GetHeight())  -- Set an initial height. We'll be overriding this, we just want to make sure it works.
    
    	bar.text:SetPoint("TOPLEFT", bar, "TOPRIGHT") -- The text is pinned to the top-right corner of the icon.
    	bar:SetPoint("BOTTOM", bar.text, "BOTTOM")  -- The bar is set to always be as high as the text is.
    
    	-- Set the solid bar to fill the entire bar.
    	bar.solid:SetPoint("TOPLEFT", bar, "TOPLEFT")
    	bar.solid:SetPoint("BOTTOMRIGHT", bar, "BOTTOMRIGHT")
    
    	-- This is hardcoded, but in a full fleshed-out addon, it would be set by the user.
    	-- This could be done now with slash commands, but couldn't be saved yet.
    	bar:SetWidth(320)
    	  
    
    	-- If a bar is left clicked, we want to target that unit
    --	bar:EventAttach(Event.UI.Input.Mouse.Left.Click, "target@mouseover")
    
    
    	--set up bars
    	function bar:aggroBar(name)
    		print (name .. " in aggroBar function")
    		--self.details = details
    		local player = name
    		if player then
    				print(player .. " has aggro (aggroBar)")
    				-- red-themed.  ***** MODIFY THIS *****
    				self:SetBackgroundColor(0.5, 0.3, 0.3, 0.3)
    				self.solid:SetBackgroundColor(0.5, 0.3, 0.3)
    				self.text:SetFontSize(20)
    				self.text:SetHeight(self.text:GetHeight())
    				self.text:SetText(player)
    				self.solid:SetVisible(true)
    		else
    				print ("No name was passed")
    				--self.solid:SetVisible(false)
    				--self.text:SetText("")
    		end
    	end
    	return bar
    end
    The name is not getting passed to the aggroBar function within createBar(). I have a handful of prints in there that I'm using for debugging, and this is where I noticed it's not getting passed along.
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  5. #5
    RIFT Guide Writer Noshei's Avatar
    Join Date
    Feb 2011
    Posts
    1,886

    Default

    So my simple suggestion would be to move your bar:aggroBar(name) function out side of the createBar() function. I'm unsure if this is the problem, and everything looks like it should work, but this would be a good place to start.

    Also I noticed you are using EventAttach for a macro, you don't want to do this. You want to use EventMacroSet

    Code:
    EventMacroSet
    	Function documentation:
    		Sets the macro that will be triggered when this event occurs.
    		Permitted only on a frame with "restricted" SecureMode while the addon environment is not secured.
    			Layout:EventMacroSet(handle, macro)   -- eventFrame, string/nil
    	Parameters:
    			handle:	A handle to a frame event, usually pulled out of the "Event.UI." hierarchy.
    			macro:	The macro to trigger. nil to clear the macro.

  6. #6
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default

    Quote Originally Posted by Noshei View Post
    So my simple suggestion would be to move your bar:aggroBar(name) function out side of the createBar() function. I'm unsure if this is the problem, and everything looks like it should work, but this would be a good place to start.

    Also I noticed you are using EventAttach for a macro, you don't want to do this. You want to use EventMacroSet

    Code:
    EventMacroSet
    	Function documentation:
    		Sets the macro that will be triggered when this event occurs.
    		Permitted only on a frame with "restricted" SecureMode while the addon environment is not secured.
    			Layout:EventMacroSet(handle, macro)   -- eventFrame, string/nil
    	Parameters:
    			handle:	A handle to a frame event, usually pulled out of the "Event.UI." hierarchy.
    			macro:	The macro to trigger. nil to clear the macro.
    I'll give that a shot when I get home. On the mouse over macro, I'mnot sure iI'm going to implement that. From what I've read, the context and frame both need to be restricted in order for the left click function to work, but I can't update the bars real time (add or remove bars) in combat if they're restricted. Is that correct?
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  7. #7
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default

    Code:
    local function AggroAlert(debug, units)
    	if not units then return end
     
    	local inspect = Inspect.Unit.Detail -- Try to not iterate/loop global functions
    	local match = string.match
     
    	for k,v in pairs(units) do
    		local details = inspect(k) -- Inspect is local
    		
    		if not details or not details.name then break end -- Details does not exist for this unit, move onto the next k,v
    		
    		local name = details.name
    		
    		if v and match(name or "", "%a+") then -- The unit must have a name, and not be blank
    			
    			print(name .. " has gained aggro")
    			
    			aggroAdd(k, name)
    			
    		end
    		
    		if not v and match(name or "", "%a+") then -- the not v means not true/present so make sure you use nil or false for v
    			
    			print(name .. " has lost aggro")
    			
    			aggroRemove(k)
    		end
    	end
    end
    
    Command.Event.Attach(Event.Unit.Detail.Aggro, AggroAlert, "anAggroAlert")
    
    
    
    local function aggroAdd(id, name)
    	local bar = (table.remove(inactiveBars) or createBar()) -- I guessed the table.remove is just a run, hopefully it returns (exists) as nil, so bar will be createBar
    
    	print("ID = " .. id .. " and name = " .. name)
    	resync = true
    
    	if activeBars[id] then
    		aggroRemove(id)
    	end
    
    	activeBars[id] = bar
    	bar:SetVisible(true)
    	bar.aggroBar(name) -- Noticed your using a . here
    	refresh()
    end
    
    
    
    local function createBar()
    	local bar = UI.CreateFrame("Frame", "bar", context)
    	
    	bar:SetPoint("TOP", UIParent, "TOP")
    
    	bar.text = UI.CreateFrame("Text", "Text", bar)
    
    	bar.solid = UI.CreateFrame("Frame", "Solid", bar)
    	bar.solid:SetLayer(-1)
    
    	bar.text:SetText("test")
    	bar.text:SetHeight(bar.text:GetHeight())
    
    	bar.text:SetPoint("TOPLEFT", bar, "TOPRIGHT")
    	bar:SetPoint("BOTTOM", bar.text, "BOTTOM")
    
    	bar.solid:SetPoint("TOPLEFT", bar, "TOPLEFT")
    	bar.solid:SetPoint("BOTTOMRIGHT", bar, "BOTTOMRIGHT")
    
    	bar:SetWidth(320)
    
    	function bar.aggroBar(name) -- Using . rather than : as : isnt needed for mapping a function, might also fix your problem
    		if not name then dump("Name was missing ended") return end -- Debug dump, we dont like nils
    		
    		local player = name
    		
    		print(name .. " in aggroBar function")
    		
    		if player then
    			print(player .. " has aggro (aggroBar)")
    			
    			self:SetBackgroundColor(0.5, 0.3, 0.3, 0.3)
    			self.solid:SetBackgroundColor(0.5, 0.3, 0.3)
    			self.text:SetFontSize(20)
    			self.text:SetHeight(self.text:GetHeight())
    			self.text:SetText(player)
    			self.solid:SetVisible(true)
    		else
    			print("No name was passed")
    		end
    	end
    
    	return bar
    end
    Im hoping that should fix all your issues there including nils
    Last edited by Ferather; 11-20-2014 at 11:21 AM.

  8. #8
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default

    Thanks guys, I got everything working yesterday using Noshei's suggestions, and I added in the checks for nil values from Ferather. So far, everything seems to be working well

    Now to add in saved variables (position, bar height/width, etc), and the ability to move the bars using drag and drop instead of typing in the position.

    Also, was I understanding the "restricted" portion correctly, that new bars would not render during combat if the context and frame are restricted in order to make the on mouse click work?
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  9. #9
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default

    Restricted frames if remember correctly must have everything they need added/mapped before they go into restricted mode.

    So you may need to preset the number of bars you need first and add every function code to them at the addon start, you could start them as Setvisible(false), this saves rendering costs.

    Some parts may be over come by using a table as your holder of variables as tables can update without changing the mapping.
    Last edited by Ferather; 11-21-2014 at 08:35 AM.

  10. #10
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default

    Quote Originally Posted by Ferather View Post
    Restricted frames if remember correctly must have everything they need added/mapped before they go into restricted mode.

    So you may need to preset the number of bars you need first and add every function code to them at the addon start, you could start them as Setvisible(false), this saves rendering costs.

    Some parts may be over come by using a table as your holder of variables as tables can update without changing the mapping.
    The bars are created and added to a table, and the table is called to render the bars. So if I add the event function when the bar is created, before it's added to the table, the click function should still work even though the frame is restricted?

    also, thanks again to both of you for all your help. I was able to get the saved variables working, as well as a draggable bar for placement. A few more tweaks and it'll be ready to rock.
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  11. #11
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default

    Source

    Frame:SetSecureMode()

    Sets the security mode of a frame.

    Frame:EventMacroSet and Frame:SetMouseoverUnit require a frame in 'restricted' mode and for Inspect.System.Secure to be false (i.e. not in combat).

    Some other functions such as positioning can operate in 'normal' or 'restricted' mode, but not if in 'restricted' mode when Inspect.System.Secure is true.

    This allows you to create 'UnitFrames' but prevents addons from making changes in combat that would allow the creation of addons with a single button that heals the raid member with the lowest health for an example.

    The parents of 'restricted' frame must be 'restricted' themselves, but they can have 'normal' children.

  12. #12
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default

    Quote Originally Posted by Ferather View Post
    Source

    Frame:SetSecureMode()

    Sets the security mode of a frame.

    Frame:EventMacroSet and Frame:SetMouseoverUnit require a frame in 'restricted' mode and for Inspect.System.Secure to be false (i.e. not in combat).

    Some other functions such as positioning can operate in 'normal' or 'restricted' mode, but not if in 'restricted' mode when Inspect.System.Secure is true.

    This allows you to create 'UnitFrames' but prevents addons from making changes in combat that would allow the creation of addons with a single button that heals the raid member with the lowest health for an example.

    The parents of 'restricted' frame must be 'restricted' themselves, but they can have 'normal' children.
    Ok, I haven't had a chance to add the restricted to the context and frames yet. I'll have to look at a couple of the raid frame examples to see how they handle the left click to select setting.
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  13. #13
    RIFT Fan Site Operator Llokii's Avatar
    Join Date
    Feb 2011
    Posts
    456

    Default

    Out of curiosity, does Unit.Inspect.Details return NPC's as well as players?

    I'm wondering if this does, if I can use it to determine what NPC's are in combat with my group.
    Last edited by Llokii; 11-26-2014 at 07:52 AM.
    www.riftraidguides.com

    Llokii@greybriar | Llokia@greybriar
    <The Dawntreaders>

  14. #14
    RIFT Guide Writer Noshei's Avatar
    Join Date
    Feb 2011
    Posts
    1,886

    Default

    Quote Originally Posted by Llokii View Post
    Out of curiosity, does Unit.Inspect.Details return NPC's as well as players?

    I'm wondering if this does, if I can use it to determine what NPC's are in combat with my group.
    Inspect.Unit.Detail works on any character in game, player or not. It does generally return less information for NPC's though.

    As for checking if a NPC is in combat, that parameter is not returned for NPC's, at least not that I have seen.

  15. #15
    Shield of Telara Adelea's Avatar
    Join Date
    Mar 2011
    Posts
    734

    Default

    Quote Originally Posted by Noshei View Post
    Inspect.Unit.Detail works on any character in game, player or not. It does generally return less information for NPC's though.

    As for checking if a NPC is in combat, that parameter is not returned for NPC's, at least not that I have seen.
    Inspect.Unit.Detail().tagged

    Property is true if tagged by you, or "other" if by someone else.

    Not sure if its set if you just aggro something by running past without actually engaging.
    Last edited by Adelea; 11-26-2014 at 10:04 AM.
    http://forums.riftgame.com/image.php?type=sigpic&userid=125779&dateline=13553  38065

+ Reply to Thread
Page 1 of 3 1 2 3 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts