WoW:AddOn tutorial: Difference between revisions

From AddOn Studio
Jump to navigation Jump to search
(Initial upload)
 
(Funky error occurring if there is a comment after the .xml file but not after the .toc file)
Line 1: Line 1:
I'll try to keep this brief. Note: The formatting alone makes this look longer than it really is. This AddOn will give you your characters (x, y) coordinates located under the Mini map and it will updated in real time as you move around. Minor programming experience with any language is recommended.  
I'll try to keep this brief. Note: The formatting alone makes this look longer than it really is. This AddOn will give you your characters (x, y) coordinates located under the Mini map and it will updated in real time as you move around. Minor programming experience with any language is recommended.


Begin:
Begin:
Line 20: Line 20:


==== eCoordinates.toc contents Start ====
==== eCoordinates.toc contents Start ====
  ## Interface: 60200  -- Required. game version. <br>
  ## Interface: 60200  -- Required. game version.  
## Title: eCoordinates -- Name shown when viewing AddOn list<br>
<nowiki>##</nowiki> Title: eCoordinates -- Name shown when viewing AddOn list
## Version: 1.0        -- <br>
<nowiki>##</nowiki> Version: 1.0        --  
## Notes: Simple GPS  -- Popup title seen with mouseover on AddOn title in load screen <br>
<nowiki>##</nowiki> Notes: Simple GPS  -- Popup title seen with mouseover on AddOn title in load screen  
## Author: eSaith  -- not require<br>
<nowiki>##</nowiki> Author: eSaith  -- not require<br><br>
<br>
  eCoordinates.lua  -- required, lets WoW know what to look for
  eCoordinates.lua  -- required, lets WoW know what to look for<br>
  eCoordinates.xml   
  eCoordinates.xml  -- required, let's WoW know what to look for


[ /run print((select(4, GetBuildInfo()))) ]    -- Will give you your Interface number. Replace 60200 with your current version
[ /run print((select(4, GetBuildInfo()))) ]    -- Will give you your Interface number. Replace 60200 with your current version


==== eCoordinates.xml contents ====
==== eCoordinates.xml contents ====
  <Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd">  <br>
  <Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd">   
  <Script File="eCoordinates.lua"/><br>
  <Script File="eCoordinates.lua"/>
<br>
  <Frame name="eCoordinates" parent="UIParent">
  <Frame name="eCoordinates" parent="UIParent"><br>
     <Scripts>
     <Scripts><br>
         <OnLoad function="eCoordinates_OnLoad"></OnLoad>
         <OnLoad function="eCoordinates_OnLoad"></OnLoad><br>
         <OnEvent function="eCoordinates_OnEvent"></OnEvent>
         <OnEvent function="eCoordinates_OnEvent"></OnEvent><br>
     </Scripts><br></Frame>
     </Scripts><br>
</Frame><br>
  </Ui>
  </Ui>
Bare bones:
Bare bones:
Line 62: Line 59:


eCoordinates.lua contents - programming. It may look like a lot, and a lot is happening but I will try to be as clear and concise as I can be. Don't fret if it takes a minute to figure out!
eCoordinates.lua contents - programming. It may look like a lot, and a lot is happening but I will try to be as clear and concise as I can be. Don't fret if it takes a minute to figure out!
  local zone = nil<br>
  local zone = nil
  local TimeSinceLastUpdate = 0<br>
  local TimeSinceLastUpdate = 0<br>
  local function UpdateCoordinates(self, elapsed)<br>
  local function UpdateCoordinates(self, elapsed)
if zone ~= GetRealZoneText() then<br>
    if zone ~= GetRealZoneText() then<br> zone = GetRealZoneText()<br> SetMapToCurrentZone()<br>   end<br>
zone = GetRealZoneText()<br>
    TimeSinceLastUpdate = TimeSinceLastUpdate + elapsed<br>
SetMapToCurrentZone()<br>
  if TimeSinceLastUpdate > .5 then <br>         TimeSinceLastUpdate = 0<br>       local posX, posY = GetPlayerMapPosition("player"); <br>     local x = math.floor(posX * 10000)/100<br>     local y = math.floor(posY*10000)/100<br>     eCoordinatesFontString:SetText("|c98FB98ff("..x..", "..y..")") <br>
end<br>
  end
<br>
TimeSinceLastUpdate = TimeSinceLastUpdate + elapsed<br>
  if TimeSinceLastUpdate > .5 then <br>
TimeSinceLastUpdate = 0<br>
local posX, posY = GetPlayerMapPosition("player"); <br>
local x = math.floor(posX * 10000)/100<br>
local y = math.floor(posY*10000)/100<br>
eCoordinatesFontString:SetText("|c98FB98ff("..x..", "..y..")") <br>
  end <br>
  end<br>
  end<br>
  <br>
   
  function eCoordinates_OnLoad(self, event,...) <br>
  function eCoordinates_OnLoad(self, event,...)  
     self:RegisterEvent("ADDON_LOADED") <br>
     self:RegisterEvent("ADDON_LOADED")
  end<br>
  end<br>
<br>
  function eCoordinates_OnEvent(self, event, ...) <br>
  function eCoordinates_OnEvent(self, event, ...) <br>
     if event == "ADDON_LOADED" and ... == "eCoordinates" then<br>
     if event == "ADDON_LOADED" and ... == "eCoordinates" then<br>       self:UnregisterEvent("ADDON_LOADED") <br>     eCoordinates:SetSize(100, 50)<br>           eCoordinates:SetPoint("TOP", "Minimap", "BOTTOM", 5, -5)<br>       eCoordinates:SetScript("OnUpdate", UpdateCoordinates)<br>     local coordsFont =    eCoordinates:CreateFontString("eCoordinatesFontString", "ARTWORK", "GameFontNormal")<br>     coordsFont:SetPoint("CENTER", "eCoordinates", "CENTER", 0, 0)<br>     coordsFont:Show()<br>     eCoordinates:Show()
    self:UnregisterEvent("ADDON_LOADED") <br>
  end
eCoordinates:SetSize(100, 50)<br>
eCoordinates:SetPoint("TOP", "Minimap", "BOTTOM", 5, -5)<br>
eCoordinates:SetScript("OnUpdate", UpdateCoordinates)<br>
local coordsFont =    eCoordinates:CreateFontString("eCoordinatesFontString", "ARTWORK", "GameFontNormal")<br>
coordsFont:SetPoint("CENTER", "eCoordinates", "CENTER", 0, 0)<br>
coordsFont:Show()<br>
eCoordinates:Show() <br>
  end<br>
  end
  end


Line 100: Line 79:


Remember in the .xml file, line 5 we were looking for the first event OnLoad.  
Remember in the .xml file, line 5 we were looking for the first event OnLoad.  
  function eCoordinates_OnLoad(self, event,...) <br>
  function eCoordinates_OnLoad(self, event,...)  
     self:RegisterEvent("ADDON_LOADED") <br>
     self:RegisterEvent("ADDON_LOADED") <br>
  end
  end
Line 119: Line 98:
  function eCoordinates_OnEvent(self, event, ...) <br>
  function eCoordinates_OnEvent(self, event, ...) <br>
     if event == "ADDON_LOADED" and ... == "eCoordinates" then<br>
     if event == "ADDON_LOADED" and ... == "eCoordinates" then<br>
    self:UnregisterEvent("ADDON_LOADED") <br>
self:UnregisterEvent("ADDON_LOADED") <br>       eCoordinates:SetSize(100, 50)<br>       eCoordinates:SetPoint("TOP", "Minimap", "BOTTOM", 5, -5)<br> eCoordinates:SetScript("OnUpdate", UpdateCoordinates)<br> local coordsFont = eCoordinates:CreateFontString("eCoordinatesFontString", "ARTWORK", "GameFontNormal")<br>
eCoordinates:SetSize(100, 50)<br>
coordsFont:SetPoint("CENTER", "eCoordinates", "CENTER", 0, 0)<br> coordsFont:Show()<br> eCoordinates:Show()
eCoordinates:SetPoint("TOP", "Minimap", "BOTTOM", 5, -5)<br>
    end<br>
eCoordinates:SetScript("OnUpdate", UpdateCoordinates)<br>
local coordsFont = eCoordinates:CreateFontString("eCoordinatesFontString", "ARTWORK", "GameFontNormal")<br>
coordsFont:SetPoint("CENTER", "eCoordinates", "CENTER", 0, 0)<br>
coordsFont:Show()<br>
eCoordinates:Show() <br>
end<br>
  end
  end
  eCoordinates:SetSize(100, 50)
  eCoordinates:SetSize(100, 50)
Line 151: Line 124:
  -- NO CHANGES. Just a copy and paste from above for your convenience while reading so that you don;t have to scroll up as much
  -- NO CHANGES. Just a copy and paste from above for your convenience while reading so that you don;t have to scroll up as much


  local zone = nil<br>
  local zone = nil
  local TimeSinceLastUpdate = 0<br>
  local TimeSinceLastUpdate = 0<br>
  local function UpdateCoordinates(self, elapsed)<br>
  local function UpdateCoordinates(self, elapsed)
if zone ~= GetRealZoneText() then<br>
    if zone ~= GetRealZoneText() then<br> zone = GetRealZoneText()<br> SetMapToCurrentZone()<br>   end<br>
zone = GetRealZoneText()<br>
    TimeSinceLastUpdate = TimeSinceLastUpdate + elapsed<br>
SetMapToCurrentZone()<br>
  if TimeSinceLastUpdate > .5 then <br>         TimeSinceLastUpdate = 0<br>       local posX, posY = GetPlayerMapPosition("player"); <br>     local x = math.floor(posX * 10000)/100<br>     local y = math.floor(posY*10000)/100<br>     eCoordinatesFontString:SetText("|c98FB98ff("..x..", "..y..")") <br>
end<br>
  end
<br>
TimeSinceLastUpdate = TimeSinceLastUpdate + elapsed<br>
  if TimeSinceLastUpdate > .5 then <br>
TimeSinceLastUpdate = 0<br>
local posX, posY = GetPlayerMapPosition("player"); <br>
local x = math.floor(posX * 10000)/100<br>
local y = math.floor(posY*10000)/100<br>
eCoordinatesFontString:SetText("|c98FB98ff("..x..", "..y..")") <br>
  end <br>
  end
  end


Line 198: Line 162:
  local posX, posY = GetPlayerMapPosition("player");  
  local posX, posY = GetPlayerMapPosition("player");  
-- Call the Blizzard function, GetPlayerMapPosition("player"), that will return our x and y coordinate.  
-- Call the Blizzard function, GetPlayerMapPosition("player"), that will return our x and y coordinate.  
  local x = math.floor(posX * 10000)/100 <br>
  local x = math.floor(posX * 10000)/100 
  local y = math.floor(posY*10000)/100  
  local y = math.floor(posY*10000)/100  
-- The returned values will be between 0 and 1. A little math allows an arbitrary number, say.7865 into 78.65, for both the x and y values.
-- The returned values will be between 0 and 1. A little math allows an arbitrary number, say.7865 into 78.65, for both the x and y values.

Revision as of 06:25, 16 March 2016

I'll try to keep this brief. Note: The formatting alone makes this look longer than it really is. This AddOn will give you your characters (x, y) coordinates located under the Mini map and it will updated in real time as you move around. Minor programming experience with any language is recommended.

Begin: In a skeleton AddOn (bare bones) there are 3 files: an .xml, .toc, and .lua file all of which have the same name and located in a folder named the same which must be located in your Interface/AddOns folder. eg

C:\Program Files (x86)\World of Warcraft\Interface\AddOns\eCoordinates\

eCoordinates.toc

eCoordinates.xml

eCoordinates.lua

Open up any text editor. Create and save each of these files with their respected names and extensions. Note: If you have eCoordinates.toc.txt or similar, you will fail.


Warcraft will first read your .toc file as the program is first loading and logging into the server. Therefore, any edits must be saved prior to opening up WOW client for testing. If you edit this file at any point you must exit the game and reenter. The other two files may be edited while the game is open. Any changes you make to the .xml or .lua file can be seen after reloading [ /reload ] your screen.

Two dashes (--) are used to create comments in both .lua and .toc file. I may ask you to run something in your game client. It will be between 2 braces [ /reload ]. Copy and paste into your chat box while in-game

eCoordinates.toc contents Start

## Interface: 60200  -- Required. game version. 
## Title: eCoordinates -- Name shown when viewing AddOn list
## Version: 1.0        -- 
## Notes: Simple GPS  -- Popup title seen with mouseover on AddOn title in load screen 
## Author: eSaith  -- not require

eCoordinates.lua -- required, lets WoW know what to look for eCoordinates.xml

[ /run print((select(4, GetBuildInfo()))) ] -- Will give you your Interface number. Replace 60200 with your current version

eCoordinates.xml contents

<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd">   
<Script File="eCoordinates.lua"/>
<Frame name="eCoordinates" parent="UIParent">
    <Scripts>
        <OnLoad function="eCoordinates_OnLoad"></OnLoad>
        <OnEvent function="eCoordinates_OnEvent"></OnEvent>
    </Scripts>
</Frame> </Ui>

Bare bones: Line 1: <Ui xmlns="http://www.bliz" ... is required. All AddOns must have this. Therefore, you can ignore the meaning if you want

Line 2: <Script File="eCoordinates.lua"/> links this specific XML file to the specific eCoordinates.lua file. Later on, you may have multiple XML and LUA files. By then you won't need me to explain.

Line 3: Create a new frame. Everything between <Frame ...> and </Frame> give the frame it's properties. This is how you can create a frame in XML

Line 4: <Scripts> - Formally states there will be action occurring with this frame.

Line 5: <OnLoad ...></OnLoad> - First event to look for. When the AddOn loads (logging in, reloading, etc) call the associated function ("eCoordinates_OnLoad") found in the .lua file (line 2, eCoordinates.lua)

Line 6: <OnEvent...></Event> - Next action to look for. When an event occurs in WoW, if we had previously registered for an event, WoW will let us know that the event had occurred and will call this function found in the .lua file for us

Line 7: </Script> - Self explanatory

Line 8: We started with <Ui ...> (Line 1). End with </Ui>.

Notice how all tags start and end with <Opening></Closing>

eCoordinates.lua contents - programming. It may look like a lot, and a lot is happening but I will try to be as clear and concise as I can be. Don't fret if it takes a minute to figure out!

local zone = nil
local TimeSinceLastUpdate = 0
local function UpdateCoordinates(self, elapsed) if zone ~= GetRealZoneText() then
zone = GetRealZoneText()
SetMapToCurrentZone()
end
TimeSinceLastUpdate = TimeSinceLastUpdate + elapsed
if TimeSinceLastUpdate > .5 then
TimeSinceLastUpdate = 0
local posX, posY = GetPlayerMapPosition("player");
local x = math.floor(posX * 10000)/100
local y = math.floor(posY*10000)/100
eCoordinatesFontString:SetText("|c98FB98ff("..x..", "..y..")")
end end
function eCoordinates_OnLoad(self, event,...) self:RegisterEvent("ADDON_LOADED") end
function eCoordinates_OnEvent(self, event, ...)
if event == "ADDON_LOADED" and ... == "eCoordinates" then
self:UnregisterEvent("ADDON_LOADED")
eCoordinates:SetSize(100, 50)
eCoordinates:SetPoint("TOP", "Minimap", "BOTTOM", 5, -5)
eCoordinates:SetScript("OnUpdate", UpdateCoordinates)
local coordsFont = eCoordinates:CreateFontString("eCoordinatesFontString", "ARTWORK", "GameFontNormal")
coordsFont:SetPoint("CENTER", "eCoordinates", "CENTER", 0, 0)
coordsFont:Show()
eCoordinates:Show() end end


Remember in the .xml file, line 5 we were looking for the first event OnLoad.

function eCoordinates_OnLoad(self, event,...) 
    self:RegisterEvent("ADDON_LOADED")	
end

All AddOns get loaded but in complete random order. All AddOns are partially loaded prior to the first AddOn completely loads. At some point when our AddOn is loading, WOW creates our frame stated in our .xml file. It just so happens, our frame in the .xml file is created -prior- to our AddOn being fully loaded. With this being the case, we let WoW know that we want to register for a certain event. More specifically, the "AddOn_LOADED" event. We want to know when our AddOn has been fully loaded. We may find that a few AddOn's may load prior to ours.

Note: Frequently, hundreds of events occur in WoW behind the scene. If we want to know about a specific event, we must register for it and WoW will send us a memo when this event does occur so that we may react to it. 

Looking back on line 6 in the .xml file, once -any- AddOn has fully loaded the "eCoordinates_OnEvent" function is called. We do a check to see if event == "AddOn_LOADED" and the ... == "eCoordinates".

Once our AddOn has loaded, event will equal "AddOn_LOADED" and the ... will equal "eCoordinates". The ... (called a vararg) looks a little confusing but it's just another variable. Do not make it more complicated than it is. From here, the rest of the if-body proceeds.

 self:UnregisterEvent("AddOn_LOADED") 

-- allows us to stop listening when any AddOn has finished loading. We dont care anymore at the point.

Time to make something we can actually see and use. Two things will be created. A new frame and a string. The string will allow us to see the x and y coordinates of our character. The string will sit inside of the frame. Both the frame and string must be visible for us to see their contents.

-- NO CHANGES. Just a copy and paste from above for your convenience while reading so that you don't have to scroll up as much
function eCoordinates_OnEvent(self, event, ...) 
if event == "ADDON_LOADED" and ... == "eCoordinates" then
self:UnregisterEvent("ADDON_LOADED")
eCoordinates:SetSize(100, 50)
eCoordinates:SetPoint("TOP", "Minimap", "BOTTOM", 5, -5)
eCoordinates:SetScript("OnUpdate", UpdateCoordinates)
local coordsFont = eCoordinates:CreateFontString("eCoordinatesFontString", "ARTWORK", "GameFontNormal")
coordsFont:SetPoint("CENTER", "eCoordinates", "CENTER", 0, 0)
coordsFont:Show()
eCoordinates:Show() end
end eCoordinates:SetSize(100, 50)

-- Sets the size of the frame, but does not change the size of the font string.

eCoordinates:SetPoint("TOP", "Minimap", "BOTTOM", 5, -5) 

-- The "TOP" of our created frame will sit at the BOTTOM of the Minimap, with an offset x of 5 and y of -50

eCoordinates:SetScript("OnUpdate", UpdateCoordinates)

-- Similar to the .xml file. This frame will listen to the OnUpdate event. The OnUpdate occurs as fast as your FPS. Note: When an update occurs (~30 - 60x per second, UpdateCoordinates function will be called each time. This allows for your x and y coodinates to be updated very frequently)

Time to make the visual string.

local coordsFont = eCoordinates:CreateFontString("eCoordinatesFontString", "ARTWORK", "GameFontNormal")

-- Create a font string named eCoordinateFontString that will hold our x and y values. "ARTWORK" and "GameFontNormal" parameters are required.

coordsFont:SetPoint("CENTER", "eCoordinates", "CENTER", 0, 0)

-- required! This set's the location string to the frame. Otherwise, the font string wouldn't know where to locate itself compared to the frame and therefore would never show.

coordsFont:Show()

-- Show the font string so that we can see it

eCoordinates:Show()

-- show the frame so that we can see the frame and its contents - which includes the font string.


Half way done with the programming! :: Breather ::  ... and here we go!

-- NO CHANGES. Just a copy and paste from above for your convenience while reading so that you don;t have to scroll up as much
local zone = nil
local TimeSinceLastUpdate = 0
local function UpdateCoordinates(self, elapsed) if zone ~= GetRealZoneText() then
zone = GetRealZoneText()
SetMapToCurrentZone()
end
TimeSinceLastUpdate = TimeSinceLastUpdate + elapsed
if TimeSinceLastUpdate > .5 then
TimeSinceLastUpdate = 0
local posX, posY = GetPlayerMapPosition("player");
local x = math.floor(posX * 10000)/100
local y = math.floor(posY*10000)/100
eCoordinatesFontString:SetText("|c98FB98ff("..x..", "..y..")")
end end


local zone = nil    

-- A variable that let's us know if we have already asked the server and verified that we are in the correct zone. A check needs to be made once per zone to get an update of our location. Otherwise, the x and y values may be 0.

local TimeSinceLastUpdate = 0 

-- We will use this variable to sum up elapsed times the function, UpdateCoordinates, has been called.

local function UpdateCoordinates(self, elapsed)  

-- self if our frame. elapsed is the time since this function was last called.

if zone ~= GetRealZoneText() then  

-- Ask the server the current zone we are in. If it equals to our variable Zone then we have already set our AddOn to this zone. Otherwise, we haven't and continue into the if-block

zone = GetRealZoneText() 

-- set our variable to this current zone then

SetMapToCurrentZone()  

-- call a blizzard function SetMapToCurrentZone() so that our AddOn is set to this zone.

TimeSinceLastUpdate = TimeSinceLastUpdate + elapsed 

-- add up the saved prior to with the elapsed time specifically for the next code line

if TimeSinceLastUpdate > .5 then  

-- since updating 30 - 60+ times a second is not necessary, let's just update our current location once every half second.  

TimeSinceLastUpdate = 0  

-- If it's been more than a second, let update! First, clear our old summed elapsed time so we can start fresh again in ~1/60 seconds

local posX, posY = GetPlayerMapPosition("player");  

-- Call the Blizzard function, GetPlayerMapPosition("player"), that will return our x and y coordinate.

local x = math.floor(posX * 10000)/100 
local y = math.floor(posY*10000)/100  

-- The returned values will be between 0 and 1. A little math allows an arbitrary number, say.7865 into 78.65, for both the x and y values.

eCoordinatesFontString:SetText("|c98FB98ff("..x..", "..y..")")	

-- set the x and y coordinates to the font. The 98FB98 allows for a funky greenish color. And your done!


At this point, the function UpdateCoordinates will be called every ~1/60 seconds (or rather, 0.01666 repeating of course), but only allow an actual update every half second that updates our current location.

Programming suggestions. Take this a set at a time. You can use [print("Hello, World")] in any of the .lua functions to see if that function is ever called. If so, then in your chat box in-game you will see a "Hello, World" system message. Think console.log("Message Here") for web developers, etc. You should be able to copy and paste the first 3 boxes into the correct files and load up your game without any problems - just check the version number!