WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   Saving values to different tables in a loop. (https://www.wowinterface.com/forums/showthread.php?t=47140)

Niketa 07-31-13 01:53 AM

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

Malsomnus 07-31-13 02:19 AM

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})

Phanx 07-31-13 03:02 AM

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.

Niketa 07-31-13 04:12 PM

Thank you guys. Helpful as usual. :)

Niketa 07-31-13 11:49 PM

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.

Malsomnus 08-01-13 02:05 AM

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 :p

Phanx 08-01-13 02:20 AM

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. :(

Malsomnus 08-01-13 02:29 AM

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.

Niketa 08-01-13 07:14 AM

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

myrroddin 08-01-13 07:41 AM

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

Phanx 08-01-13 05:02 PM

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


Niketa 08-01-13 09:52 PM

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. :D

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

myrroddin 08-02-13 04:47 PM

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

semlar 08-02-13 04:53 PM

Quote:

Originally Posted by myrroddin (Post 282309)
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

myrroddin 08-02-13 05:01 PM

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

semlar 08-02-13 05:11 PM

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.

Phanx 08-02-13 07:03 PM

Quote:

Originally Posted by myrroddin (Post 282309)
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.

Phanx 08-02-13 07:06 PM

Quote:

Originally Posted by semlar (Post 282313)
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


Niketa 08-02-13 10:35 PM

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. ><

myrroddin 08-03-13 02:20 AM

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.


All times are GMT -6. The time now is 07:33 AM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI