Thread Tools Display Modes
07-31-13, 01:53 AM   #1
Niketa
A Wyrmkin Dreamwalker
 
Niketa's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2013
Posts: 54
Saving values to different tables in a loop.

I'm trying to go through each mount a player has and add that to a table "flying", "ground" or "aquatic" within the table "mounts". With the code I currently have it, it doesn't add them to a table.

However, if I take out the bit of code to add to ground or aquatic (the elseifs) and have it just add to the flying table it works just fine (and I don't think it's just the flying in particular, but rather I can only add to one sub-table rather than multiple ones).

Is there an error in my code or is it just not possible with this approach?


Lua Code:
  1. local mounts = {}
  2.     mounts.flying = {}
  3.     mounts.ground = {}
  4.     mounts.aquatic = {}
  5.  
  6. local amount = GetNumCompanions("mount")
  7.  
  8. local f, g, a = 1
  9.  
  10. for i = 1, amount do
  11.     local creatureID, creatureName, creatureSpellID, icon, issummoned, mountType = GetCompanionInfo("mount",i)
  12.    
  13.     if mountType == 7 or mountType == 15 or mountType == 23 or mountType == 31 then
  14.         mounts.flying[f] = {creatureID, creatureName, creatureSpellID, icon, issummoned, mountType}
  15.         --print(mounts.flying[f][2])
  16.         f = f + 1
  17.     elseif mountType == 29 then
  18.         mounts.ground[g] = {creatureID, creatureName, creatureSpellID, icon, issummoned, mountType}
  19.         --print(mounts.ground[g][2])
  20.         g = g + 1
  21.     elseif mountType == 12 then
  22.         mounts.aquatic[a] = {creatureID, creatureName, creatureSpellID, icon, issummoned, mountType}
  23.         --print(mounts.aquatic[a][2])
  24.         a = a + 1
  25.     end
  26. end
  Reply With Quote
07-31-13, 02:19 AM   #2
Malsomnus
A Cobalt Mageweaver
AddOn Author - Click to view addons
Join Date: Apr 2013
Posts: 203
First thing that pops up for me is the line:
Lua Code:
  1. local f, g, a = 1

Is it possible that what you meant to write is
Lua Code:
  1. local f, g, a = 1, 1, 1
?

[Edit]
It really looks like lines 18, 20, 22 and 24 should complain about indexing/ arithmetic with nil values. Do you get these errors?
Anyway instead of managing indexes you can just use:
Lua Code:
  1. table.insert (mounts.flying, {creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
__________________
SanityCheck - If you've ever said the words "Sorry, I forgot" then you need this add-on.

Remember, every time you post a comment on an add-on, a kitten gets its wings!

Last edited by Malsomnus : 07-31-13 at 02:25 AM.
  Reply With Quote
07-31-13, 03:02 AM   #3
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
tinsert is definitely the way to go here. Keeping track of the indices yourself is just masochistic.

Also, the mountType returned by GetCompanionInfo is a bitfield, not a decimal value, so rather than trying to account for all possible combinations, you should just use bit operators to check the specific flags you're interested in:

Code:
if bit.band(mountType, 0x8) ~= 0 then
    -- aquatic
elseif bit.band(mountType, 0x2) ~= 0 then
    -- flying
elseif bit.band(mountType, 0x1) ~= 0 then
   -- ground
end
The order is fairly important, as mounts can be flagged as flying and ground, for example; the above prioritizes any flight-capable mount as a flying mount, so it will only consider mounts to be ground mounts if they can't fly.

On a side note, you're creating your mounts table in the least efficient way possible. Instead of this:

Code:
local mounts = {}
    mounts.flying = {}
    mounts.ground = {}
    mounts.aquatic = {}
...do this:
Code:
local mounts = {
    flying = {},
    ground = {},
    aquatic = {}
}
Works out exactly the same, and doesn't cost all the extra lookups.
__________________
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.

Last edited by Phanx : 07-31-13 at 04:46 PM.
  Reply With Quote
07-31-13, 04:12 PM   #4
Niketa
A Wyrmkin Dreamwalker
 
Niketa's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2013
Posts: 54
Thank you guys. Helpful as usual.
  Reply With Quote
07-31-13, 11:49 PM   #5
Niketa
A Wyrmkin Dreamwalker
 
Niketa's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2013
Posts: 54
When I first log on the tables don't automatically populate and I have to /reload to get them to populate.

I've tried leaving the code outside of any event, tried PLAYER_LOGIN, PLAYER_ENTERING_WORLD and PLAYER_ALIVE.
  Reply With Quote
08-01-13, 02:05 AM   #6
Malsomnus
A Cobalt Mageweaver
AddOn Author - Click to view addons
Join Date: Apr 2013
Posts: 203
Ah, I hate data that isn't available on PLAYER_ENTERING_WORLD. I've found myself several times putting such code in a function that gets called from OnUpdate after it counts a few seconds.
I'd love to hear if anybody knows a correct way of doing that
__________________
SanityCheck - If you've ever said the words "Sorry, I forgot" then you need this add-on.

Remember, every time you post a comment on an add-on, a kitten gets its wings!
  Reply With Quote
08-01-13, 02:20 AM   #7
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Post your entire code, please. Polling with an OnUpdate script is never the best way (or even a good way) to find out when some data becomes available.
__________________
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.
  Reply With Quote
08-01-13, 02:29 AM   #8
Malsomnus
A Cobalt Mageweaver
AddOn Author - Click to view addons
Join Date: Apr 2013
Posts: 203
Not polling, just waiting a few seconds. It seems to work consistently. I know it's horrible, I just haven't found any event that can actually tell me when certain data (e.g. my gear, my raid roster) is available, and it wouldn't surprise me if the same applies to mounts.
__________________
SanityCheck - If you've ever said the words "Sorry, I forgot" then you need this add-on.

Remember, every time you post a comment on an add-on, a kitten gets its wings!
  Reply With Quote
08-01-13, 07:14 AM   #9
Niketa
A Wyrmkin Dreamwalker
 
Niketa's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2013
Posts: 54
Lua Code:
  1. SLASH_MOUNTAHOLIC1 = "/mountaholic"
  2.  
  3. function SlashCmdList.MOUNTAHOLIC(msg, editbox)
  4.     if msg == "a" then -- aquatic
  5.         Mountaholic_aquatic()
  6.     elseif msg == "g" then -- ground
  7.         Mountaholic_ground()
  8.     elseif msg == "gf" then -- ground favorites
  9.         Mountaholic_favground()
  10.     elseif msg == "f" then -- flying
  11.         Mountaholic_flying()
  12.     elseif msg == "ff" then -- flying favorites
  13.         Mountaholic_favflying()
  14.     elseif msg == "v" then -- vendor
  15.         Mountaholic_vendor()
  16.     else
  17.         InterfaceOptionsFrame_OpenToCategory(Mountaholic_Options)
  18.     end
  19. end
  20.  
  21. local mounts = {aquatic = {}, flying = {}, flyingf = {}, ground = {}, groundf = {}, vendor = {}}
  22. local amount = GetNumCompanions("mount")
  23.  
  24. for i = 1, amount do
  25.     local creatureID, creatureName, creatureSpellID, icon, issummoned, mountType = GetCompanionInfo("mount",i)
  26.                        
  27.     if creatureName == "Traveler's Tundra Mammoth" or creatureName == "Grand Expedition Yak" then
  28.         tinsert(mounts.vendor, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  29.     end
  30.                        
  31.     if bit.band(mountType, 0x01) == 0 and bit.band(mountType, 0x02) == 0 and bit.band(mountType, 0x08) ~= 0 then
  32.         tinsert(mounts.aquatic, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  33.     elseif bit.band(mountType, 0x02) ~= 0 then
  34.         tinsert(mounts.flying, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  35.     elseif bit.band(mountType, 0x01) ~= 0 then
  36.         tinsert(mounts.ground, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  37.     end
  38. end
  39.            
  40. local sizea, sizef, sizeff, sizeg, sizegf, sizev = table.getn(mounts.aquatic), table.getn(mounts.flying), table.getn(mounts.flyingf), table.getn(mounts.ground), table.getn(mounts.groundf), table.getn(mounts.vendor)
  41.  
  42.  
  43. function Mountaholic_aquatic() -- aquatic
  44.     if not IsSwimming() then
  45.         print("You can't use an aquatic mount here! Try this instead.")
  46.         Mountaholic_flying()
  47.     else
  48.         local rand = mounts.aquatic[math.random(1, sizea)]
  49.         CallCompanion("MOUNT",rand[1])
  50.     end
  51. end
  52.  
  53. function Mountaholic_flying() -- flying
  54.     if not IsFlyableArea() then
  55.         Mountaholic_ground()
  56.     else
  57.         local rand = mounts.flying[math.random(1, sizef)]
  58.         CallCompanion("MOUNT",rand[1])
  59.     end
  60. end
  61.  
  62. function Mountaholic_ground() -- ground
  63.     local rand = mounts.ground[math.random(1, sizeg)]
  64.     CallCompanion("MOUNT",rand[1])
  65. end
  66.  
  67. function Mountaholic_favflying() -- favorite flying
  68.     if not IsFlyableArea() then
  69.         Mountaholic_ground()
  70.     elseif sizeff == 0 then
  71.         print("You have no favorite flying mounts! Here, have a random one instead.")
  72.         Mountaholic_flying()
  73.     else   
  74.         local rand = mounts.flyingf[math.random(1, sizeff)]
  75.         CallCompanion("MOUNT",rand[1])
  76.     end
  77. end
  78.  
  79. function Mountaholic_favground() -- favorite ground
  80.     if sizegf == 0 then
  81.         print("You have no favorite ground mounts! Here, have a random one instead.")
  82.         Mountaholic_ground()
  83.     else   
  84.         local rand = mounts.groundf[math.random(1, sizegf)]
  85.         CallCompanion("MOUNT",rand[1])
  86.     end
  87. end
  88.  
  89. function Mountaholic_vendor() -- vendor
  90.     local rand = mounts.vendor[math.random(1, sizev)]
  91.     CallCompanion("MOUNT",rand[1])
  92. end
  Reply With Quote
08-01-13, 07:41 AM   #10
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
To work on clients other than English, use the CreatureID instead of the CreatureName. Also, there are two Mammoths, one per faction. Change the following code accordingly:
Lua Code:
  1. local AllianceMammoth = 61425 -- Alliance version of Traveler's Tundra Mammoth
  2. local HordeMammoth = 61447 -- Horde version of Traveler's Tundra Mammoth
  3. local GrandExpedYak = 122708 -- Grand Expedition Yak
  4.  
  5. for i = 1, amount do -- should rename "amount" to "numMounts" for clarity...
  6.     local creatureID, creatureName, creatureSpellID, icon, issummoned, mountType = GetCompanionInfo("mount", i)
  7.     if creatureID == (AllianceMammoth or HordeMammoth or GrandExpedYak) then
  8.         tinsert(mounts.vendor, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  9.     end
  10.    
  11.     -- rest of code
  12. end
  Reply With Quote
08-01-13, 05:02 PM   #11
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Based on the default UI's code, I would guess you should watch for COMPANION_LEARNED and COMPANION_UNLEARNED events to detect when the list of known mounts changes:

Code:
function MountJournal_OnLoad(self)
  self:RegisterEvent("COMPANION_LEARNED");
  self:RegisterEvent("COMPANION_UNLEARNED");
  self:RegisterEvent("COMPANION_UPDATE");
  self.ListScrollFrame.update = MountJournal_UpdateMountList;
  self.ListScrollFrame.scrollBar.doNotHide = true;
  HybridScrollFrame_CreateButtons(self.ListScrollFrame, "MountListButtonTemplate", 44, 0);
end
 
function MountJournal_OnEvent(self, event, ...)
  if ( event == "COMPANION_LEARNED" or event == "COMPANION_UNLEARNED" or event == "COMPANION_UPDATE" ) then
    local companionType = ...;
    if ( not companionType or companionType == "MOUNT" ) then
      if ( event ~= "COMPANION_UPDATE" ) then
        --Companion updates don't change who's on our list.
        MountJournal_DirtyList(self);
      end
      MountJournal_UpdateMountList();
      MountJournal_UpdateMountDisplay();
    end
  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.
  Reply With Quote
08-01-13, 09:52 PM   #12
Niketa
A Wyrmkin Dreamwalker
 
Niketa's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2013
Posts: 54
myrroddin, I followed your suggestion but you were watching creatureID when you defined the creatureSpellID so I switched that up.

Phanx, it looks like COMPANION_UPDATE did the trick to load them up when I first log in. Thanks.

Lua Code:
  1. local events = CreateFrame("Frame")
  2. local sizea, sizef, sizeff, sizeg, sizegf, sizev, mounts = nil, nil, nil, nil, nil, nil, nil
  3.  
  4. events:RegisterEvent("COMPANION_LEARNED")
  5. events:RegisterEvent("COMPANION_UPDATE")
  6. events:RegisterEvent("PLAYER_ENTERING_WORLD")
  7.  
  8. events:SetScript("OnEvent",function(self, event, ...)
  9.     return self[event] and self[event](self, event, ...)
  10. end)
  11.  
  12. function events:COMPANION_UPDATE(event, companionType)
  13.     mounts = {aquatic = {}, flying = {}, flyingf = {}, ground = {}, groundf = {}, vendor = {}}
  14.     local numMounts = GetNumCompanions("mount")
  15.  
  16.     for i = 1, numMounts do
  17.         local creatureID, creatureName, creatureSpellID, icon, issummoned, mountType = GetCompanionInfo("mount",i)
  18.         local AllianceMammoth, HordeMammoth, GrandExpedYak = 61425, 61447, 122708
  19.                    
  20.         if creatureSpellID == AllianceMammoth or creatureSpellID == HordeMammoth or creatureSpellID == GrandExpedYak then
  21.             tinsert(mounts.vendor, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  22.         end
  23.                                
  24.         if bit.band(mountType, 0x01) == 0 and bit.band(mountType, 0x02) == 0 and bit.band(mountType, 0x08) ~= 0 then
  25.             tinsert(mounts.aquatic, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  26.         elseif bit.band(mountType, 0x02) ~= 0 then
  27.             tinsert(mounts.flying, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  28.         elseif bit.band(mountType, 0x01) ~= 0 then
  29.             tinsert(mounts.ground, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  30.         end
  31.     end
  32.                    
  33.     sizea, sizef, sizeff, sizeg, sizegf, sizev = table.getn(mounts.aquatic), table.getn(mounts.flying), table.getn(mounts.flyingf), table.getn(mounts.ground), table.getn(mounts.groundf), table.getn(mounts.vendor)
  34.        
  35.     self:UnregisterEvent("COMPANION_UPDATE")
  36. end
  37.  
  38. events.PLAYER_ENTERING_WORLD = events.COMPANION_UPDATE
  Reply With Quote
08-02-13, 04:47 PM   #13
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Ah, I did not know I was looking up the creatureSpellID when I hit WowHead, sorry! One more suggestion: move the variable definition outside the for loop. Right now you are (re)defining the variables on every loop, creating unnecessary garbage collection and a great deal of inefficiency.
Lua Code:
  1. local AllianceMammoth, HordeMammoth, GrandExpedYak = 61425, 61447, 122708
  2. for i = 1, numMounts do
  3.     if creatureSpellID == AllianceMammoth or creatureSpellID == HordeMammoth or creatureSpellID == GrandExpedYak then
  4.         tinsert(mounts.vendor, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  5.     end
  6. end
I am wondering about the chained or code. Right now, you have
Lua Code:
  1. if creatureSpellID == AllianceMammoth or creatureSpellID == HordeMammoth or creatureSpellID == GrandExpedYak then
  2.     -- code
  3. end
Is this not the same thing (psuedocode to make it shorter)? I'm musing about Lua, and if it works this way.
Lua Code:
  1. if var == 1 or 2 or 3 then
  2.     -- code
  3. end
  4.  
  5. -- same as
  6. if var == 1 or var == 2 or var == 3 then
  7.     -- code
  8. end

Last edited by myrroddin : 08-02-13 at 04:50 PM. Reason: or should not be capitalized...
  Reply With Quote
08-02-13, 04:53 PM   #14
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
Originally Posted by myrroddin View Post
Lua Code:
  1. if var == 1 or 2 or 3 then
  2.     -- code
  3. end
This is like saying..
Lua Code:
  1. if var == 1 then
  2.   -- code
  3. elseif 2 then
  4.   -- code
  5. elseif 3 then
  6.   -- code
  7. end
  Reply With Quote
08-02-13, 05:01 PM   #15
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Semlar, that is what I thought. It would return true if var was equal to 1 or 2 or 3. You shouldn't have to keep checking if var, if var, if var... That seems overly verbose.

The reason I asked is because with two choices, it works fine.
Lua Code:
  1. -- would be true for 1 or 2
  2. if var == 1 or 2 then
  3.  
  4. -- so why can't it work the same way for more than two choices?
  5. if var == 1 or 2 or 3

Last edited by myrroddin : 08-02-13 at 05:04 PM.
  Reply With Quote
08-02-13, 05:11 PM   #16
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
I think maybe I wasn't clear.

You can't do "if var == 1 or 2 or 3", it's not the same as "if var == 1 or var == 2 or var == 3", it will always return true because "if 2" will always evaluate to true.
  Reply With Quote
08-02-13, 07:03 PM   #17
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by myrroddin View Post
Right now you are (re)defining the variables on every loop, creating unnecessary garbage collection and a great deal of inefficiency.
Lua Code:
  1. local AllianceMammoth, HordeMammoth, GrandExpedYak = 61425, 61447, 122708
  2. for i = 1, numMounts do
  3.     if creatureSpellID == AllianceMammoth or creatureSpellID == HordeMammoth or creatureSpellID == GrandExpedYak then
  4.         tinsert(mounts.vendor, {i, creatureID, creatureName, creatureSpellID, icon, issummoned, mountType})
  5.     end
  6. end
Only the first line in the code you quoted creates variables that could be defined at the file level. The other 3 lines must be done inside the loop over all of the player's mounts, as all the variables it references do not exist outside the loop.

However, redefining 3 number values whenever COMPANION_UPDATE fires is hardly "a great deal of inefficiency", especially for the purposes of a beginner still trying to figure out the basic logic.
__________________
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.
  Reply With Quote
08-02-13, 07:06 PM   #18
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by semlar View Post
You can't do "if var == 1 or 2 or 3", it's not the same as "if var == 1 or var == 2 or var == 3", it will always return true because "if 2" will always evaluate to true.
To clarify even further:

Code:
if var == 1 or 2 or 3 then
   -- do X
end
Is functionally identical to:

Code:
if var == 1 then
   -- do x
elseif 2 then
   -- do x
elseif 3 then
   -- do x
end
It is NOT the same as:

Code:
if var == 1 then
   -- do x
elseif var == 2 then
   -- do x
elseif var == 3 then
   -- do x
end
If you want to do that third thing in one go, you need to write it this way:

Code:
if var == 1 or var == 2 or var == 3 then
   -- do x
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.
  Reply With Quote
08-02-13, 10:35 PM   #19
Niketa
A Wyrmkin Dreamwalker
 
Niketa's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2013
Posts: 54
Actually he's right. The code he quoted was his correction. I had the mammoths defined *inside* the loop, meaning for each mount I kept defining the mammoths, not for each COMPANION_UPDATE. I've fixed that. Also the COMPANION_UPDATE should only ever run once when logging in... and that same code only ever gets run again on PLAYER_ENTERING_WORLD so really it shouldn't be happening all that often.

Btw, just want to say thank you all for the help. There aren't any real good tutorials for lua/WoW addons so having you guys here is very helpful. Hopefully eventually I'll start thinking of these things so you guys don't have to tell me. ><
  Reply With Quote
08-03-13, 02:20 AM   #20
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Niketa, glad you have it working!

Semlar and Phanx, thank you for the clarifications! I was looking for a syntactic shortcut where none exists. Strangely, I have has to validate if more than one var == something, but thus far in any code I've written have I had to validate one var against multiple values. Odd that, but now I know.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Saving values to different tables in a loop.


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off