Thread Tools Display Modes
06/13/24, 01:20 PM   #1
RicAlmighty
Join Date: Jun 2024
Posts: 2
At my wits end. UseCollectible doesn't work for just one character

Hello all. An add-on that I used all the time (Armory Style Manager) has broken after the Gold Road update, so I went in to simplify and fix it. I am completely baffled though because the code works fine except for one of my characters (I have 8). When the add-on was working, it worked for all of my characters and honestly I did not change much in this specific area.

For some reason the call to
Code:
UseCollectible(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
results in a "COLLECTIBLE IS NOT READY YET" error for this one character, but not any of the others. For background, the failing character has 4 Armory Builds, and the others range from 2 to 3. It doesn't matter which build I switch to though as it fails every time with this character, but works with others.

For example, I will set a specific mount and personality for one Armory Build. The code fires after the event EVENT_ARMORY_BUILD_RESTORE_RESPONSE and iterates over the currently equipped items and attempts to replace the current items with the ones saved in my SavedVariables. I've triple checked the SavedVariables and they are fine. The code that saves the items works perfectly even for the character that is failing, it's only when I call UseCollectible() that I get the error for this one character.

At this point, I have no idea what else to do here. Perhaps there is just something borked with this one character? Did Gold Road change how this works in some way? I cannot understand why the same code works for the others but not for this one. I'd appreciate any insight or suggestions. Thanks

Relevant Code:

Lua Code:
  1. local function ArmoryBuildLoaded(_, result, buildIndex)
  2.     if result ~= ARMORY_BUILD_RESTORE_RESULT_SUCCESS then
  3.         d("Armory Load Failed")
  4.         return false
  5.     end
  6.  
  7.     ArmoryStyle.savedVariables.currentBuildIndex = buildIndex
  8.  
  9.     for collectibleType, id in pairs(ArmoryStyle.savedVariables.builds[buildIndex].collectibles) do
  10.         local eqippedCollectible = GetActiveCollectibleByType(collectibleType, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  11.         if eqippedCollectible ~= id then
  12.             if id ~= 0 then
  13.                 if IsCollectibleUnlocked(id) and IsCollectibleUsable(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER) and IsCollectibleValidForPlayer(id) then
  14.                     UseCollectible(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  15.                 else
  16.                     d("Error occurred Setting Collectible")
  17.                 end
  18.             else
  19.                 if eqippedCollectible ~= 0 then
  20.                     -- If a collectible is already equipped then we want to 'un-use' the current one
  21.                     UseCollectible(eqippedCollectible, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  22.                 end
  23.             end
  24.         end
  25.     end
  Reply With Quote
06/14/24, 06:59 AM   #2
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 5,067
As you call that in a for loop it might be a timing issue where collectibles cannot be used "directly after another".
Same like if you click some collectibles in your collections list and have to wait 9/45s afterwards.

Are you use the error happens at that line 14 and not at line 21 (because your 1 char already wears another collectible)?
You should add the same "IsCollectible* checks at that line 21 then and see if that helps, add some d() debug messages to see what hapepns when with that 1 character.


Lua Code:
  1. local function checkCollectibleUsableByPlayer(collectibleId)
  2.         return ( collectibleId ~= nil and collectibleId ~= 0
  3.                 and IsCollectibleUnlocked(collectibleId)
  4.                 and IsCollectibleUsable(collectibleId, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  5.                 and IsCollectibleValidForPlayer(collectibleId) )
  6.                     or false
  7.     end
  8.  
  9.     local function ArmoryBuildLoaded(_, result, buildIndex)
  10.         if result ~= ARMORY_BUILD_RESTORE_RESULT_SUCCESS then
  11.             d("Armory Load Failed")
  12.             return false
  13.         end
  14.  
  15.         ArmoryStyle.savedVariables.currentBuildIndex = buildIndex
  16.  
  17.         for collectibleType, id in pairs(ArmoryStyle.savedVariables.builds[buildIndex].collectibles) do
  18.             local eqippedCollectible = GetActiveCollectibleByType(collectibleType, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  19.             if eqippedCollectible ~= id then
  20.                 if id ~= 0 then
  21.                     if checkCollectibleUsableByPlayer(id) then
  22.                         UseCollectible(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  23.                     else
  24.                         d("Error occurred Setting Collectible: " ..tostring(id))
  25.                     end
  26.                 else
  27.                     if eqippedCollectible ~= 0 then
  28.                         if checkCollectibleUsableByPlayer(eqippedCollectible) then
  29.                             -- If a collectible is already equipped then we want to 'un-use' the current one
  30.                             UseCollectible(eqippedCollectible, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  31.                         else
  32.                             d("Error occurred with eqippedCollectible: " ..tostring(eqippedCollectible))
  33.                         end
  34.                     end
  35.                 end
  36.             end
  37.         end
  38.  
  39.         ...
  40.  
  41.     end

Last edited by Baertram : 06/14/24 at 07:03 AM.
  Reply With Quote
06/14/24, 09:48 AM   #3
RicAlmighty
Join Date: Jun 2024
Posts: 2
Thanks for the reply, I appreciate it. I agree with your assessment that it is a timing issue and that perhaps I am trying to assign too many collectibles in too short of a time frame.

The error "COLLECTIBLE IS NOT READY YET" was actually happening on line 21 (I used debug statements to verify that), so you're correct in that was the problem.

I modified that section of the code to add a delayed callback and it seems to have fixed the issue! I guess I overlooked the fact that the one character did have a lot of collectible switching between Armory builds whereas the others were more simple.

This is what I added:

Lua Code:
  1. if eqippedCollectible ~= id then
  2.             if id ~= 0 then
  3.                 if IsCollectibleUnlocked(id) and IsCollectibleUsable(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER) and IsCollectibleValidForPlayer(id) then
  4.                     local msg = zo_strformat("Equipping <<1>>", GetCollectibleName(id))
  5.                     --d(msg)
  6.                     zo_callLater(function() UseCollectible(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER) end, 1500)
  7.                 else
  8.                     d("Error occurred Setting Collectible")
  9.                 end
  10.             else
  11.                 if eqippedCollectible ~= 0 then
  12.                     -- If a collectible is already equipped then we want to 'un-use' the current one
  13.                     local msg = zo_strformat("Un-Equipping <<1>>", GetCollectibleName(eqippedCollectible))
  14.                     --d(msg)
  15.                     zo_callLater(function() UseCollectible(eqippedCollectible, GAMEPLAY_ACTOR_CATEGORY_PLAYER) end, 1500)
  16.                 end
  17.             end

It results in a slight delay where the costume will be applied very shortly after you exit the Armory UI, but it does work.

Thanks again for pointing me in the right direction!

Last edited by RicAlmighty : 06/14/24 at 09:50 AM.
  Reply With Quote
06/14/24, 10:40 AM   #4
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 5,067
Maybe there is even a better way using EVENT_MANAGER:RegisterForUpdate (which basically is the same as zo_callLater but you can reuse it with multiple calls, e.g. every 500ms).
Within the callback function you check ech time it is called with functions like

if IsCollectibleUnlocked(id) and IsCollectibleUsable(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER) and IsCollectibleValidForPlayer(id)

and maybe others that provide a return value like "Currently blocked because of reason BLOCK_REASON_ONE (I think there is such an API function like IsCollectibleBlocked which might return any blocked reasons or at least a true then)

if the item is still blocked. If not:
Unregister that update and equip the collectible then:

Lua Code:
  1. local function checkCollectibleUsableByPlayer(collectibleId)
  2.         return ( collectibleId ~= nil and collectibleId ~= 0
  3.                 and IsCollectibleUnlocked(collectibleId)
  4.                 and IsCollectibleUsable(collectibleId, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  5.                 and IsCollectibleValidForPlayer(collectibleId) )
  6.                     or false
  7.     end
  8.  
  9.     local uniqueEventUpdaterName = "MYADDON_NAME_COLLECTIBLE_EQUIP_UPDATER"
  10.     local triedAlreadyNTimes = 0
  11.     local function isStillBlocked(collectibleId)
  12.         triedAlreadyNTimes = triedAlreadyNTimes + 1
  13.         if triedAlreadyNTimes >= 10                                          --maximum 10 iterations = 5 seconds, to prevent an endless update check
  14.                 or not checkCollectibleUsableByPlayer(collectibleId) then    --can the player use this collectible at all?
  15.             EVENT_MANAGER:UnregisterForUpdate(uniqueEventUpdaterName)
  16.             triedAlreadyNTimes = 0
  17.  
  18.             return false
  19.         end
  20.  
  21.         if not IsCollectibleBlocked(collectibleId, GAMEPLAY_ACTOR_CATEGORY_PLAYER) then --Is it not blocked any longer?
  22.             EVENT_MANAGER:UnregisterForUpdate(uniqueEventUpdaterName)
  23.             triedAlreadyNTimes = 0
  24.  
  25.             UseCollectible(collectibleId, GAMEPLAY_ACTOR_CATEGORY_PLAYER)   --Use the collectible now
  26.             return true
  27.         end
  28.         return false
  29.     end
  30.  
  31.  
  32.     local function ArmoryBuildLoaded(_, result, buildIndex)
  33.         if result ~= ARMORY_BUILD_RESTORE_RESULT_SUCCESS then
  34.             d("Armory Load Failed")
  35.             return false
  36.         end
  37.  
  38.         ArmoryStyle.savedVariables.currentBuildIndex = buildIndex
  39.  
  40.         --Unregister any stil pending 2every 500ms" check
  41.         EVENT_MANAGER:UnregisterForUpdate(uniqueEventUpdaterName)
  42.  
  43.         for collectibleType, id in pairs(ArmoryStyle.savedVariables.builds[buildIndex].collectibles) do
  44.             local eqippedCollectible = GetActiveCollectibleByType(collectibleType, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  45.             if eqippedCollectible ~= id then
  46.                 if id ~= 0 then
  47.                     if checkCollectibleUsableByPlayer(id) then
  48.                         UseCollectible(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  49.  
  50.                         return --exit the for ... do loop here as 1 collectible was equiped!
  51.                     else
  52.                         d("Error occurred Setting Collectible: " ..tostring(id))
  53.  
  54.                         --Check every 500ms if the collectible can be equipped - max 5 seconds
  55.                         EVENT_MANAGER:RegisterForUpdate(uniqueEventUpdaterName, function() isStillBlocked(id) end, 500)
  56.  
  57.                         return --exit the for ... do loop here as 1 collectible will be equipped once it is not blocked anymore
  58.                     end
  59.                 else
  60.                     if eqippedCollectible ~= 0 then
  61.                         if checkCollectibleUsableByPlayer(eqippedCollectible) then
  62.                             -- If a collectible is already equipped then we want to 'un-use' the current one
  63.                             UseCollectible(eqippedCollectible, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  64.  
  65.                             return --exit the for ... do loop here as 1 collectible was equiped!
  66.                         else
  67.                             d("Error occurred with eqippedCollectible: " ..tostring(eqippedCollectible))
  68.  
  69.                             --Check every 500ms if the collectible can be equipped - max 5 seconds
  70.                             EVENT_MANAGER:RegisterForUpdate(uniqueEventUpdaterName, function() isStillBlocked(eqippedCollectible) end, 500)
  71.  
  72.                             return --exit the for ... do loop here as 1 collectible will be equipped once it is not blocked anymore
  73.                         end
  74.                     end
  75.                 end
  76.             end
  77.         end
  78.     end


I'm assuiming you only equip 1 collectible during that loop.
Else you'd have to create multiple of these EVENT_MANAGER:RegisterForUpdate with different unique names, one for each item slot to equip.
  Reply With Quote
06/17/24, 03:22 AM   #5
IsJustaGhost
AddOn Author - Click to view addons
Join Date: May 2020
Posts: 39
You can also call later using the collectible's cooldown time

I do that in one of my addons

Lua Code:
  1. if id ~= 0 then
  2.     if IsCollectibleUnlocked(id) and IsCollectibleUsable(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER) and IsCollectibleValidForPlayer(id) then
  3.         local remainingMs, durationMs = GetCollectibleCooldownAndDuration(id)
  4.         local msg = zo_strformat("Equipping <<1>>", GetCollectibleName(id))
  5.         --d(msg)
  6.         zo_callLater(function()
  7.             UseCollectible(id, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  8.         end, durationMs)
  9.     else
  10.         d("Error occurred Setting Collectible")
  11.     end
  12. else
  13.     if eqippedCollectible ~= 0 then
  14.         -- If a collectible is already equipped then we want to 'un-use' the current one
  15.         local remainingMs, durationMs = GetCollectibleCooldownAndDuration(eqippedCollectible)
  16.         local msg = zo_strformat("Un-Equipping <<1>>", GetCollectibleName(eqippedCollectible))
  17.         --d(msg)
  18.         zo_callLater(function()
  19.             UseCollectible(eqippedCollectible, GAMEPLAY_ACTOR_CATEGORY_PLAYER)
  20.         end, durationMs)
  21.     end
  22. end
remainingMs did not work but, durationMs did
Can even add a few MS for safety.

Last edited by IsJustaGhost : 06/17/24 at 03:27 AM.
  Reply With Quote

ESOUI » AddOns » AddOn Help/Support » At my wits end. UseCollectible doesn't work for just one character


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