How does ItemActionWithAsyncFuncActionPanel work?

desukarhu

Member
Hi, decided to use forums for abit since discord seems quite busy.

I'm having big trouble getting a custom ItemActionWithAsyncFuncActionPanel to work properly. I have multiple characters and when I click equip on my actions panel, I want another action panel to open up (like in assign to hotbar panel) that lists "Current, (list of all characters), Cancel" action buttons.

This is what I have so far and it's not working:

C#:
    protected override void InvokeActionInternal (ItemInfo itemInfo, ItemUser itemUser) {
      // Clear old actions
      m_AsyncFuncActions.Clear ();

      // If we're equipping, show the async panel
      if (Name == "Equip") {
        // Add "Current" option, that has value of -1. This means currently selected character.
        m_AsyncFuncActions.Add (new AsyncFuncAction<int> ("Current", () => -1));

        // Loop through players party and add a asyncfuncaction
        for (int i = 0; i < Database.Instance.PlayerParty.Count; i++) {
          m_AsyncFuncActions.Add (new AsyncFuncAction<int> ((Database.Instance.PlayerParty [i].Name).ToString (), () => i));
        }

        base.InvokeActionInternal (itemInfo, itemUser);
      } else { // If not equipping, just unequip.
        EquipUnequip (itemInfo, itemUser);
      }
    }

This prints out the buttons nicely, so if I my party has 2 characters it looks like this: "Current, Mage, Warrior, Cancel", so that part works. But pressing the buttons is where it all breaks. I have this in InvokeWithAwaitedValue:

C#:
    protected override void InvokeWithAwaitedValue (ItemInfo itemInfo, ItemUser itemUser, int awaitedValue) {
      Debug.Log (awaitedValue);
      if (awaitedValue == -1) {
        // Get currently selected character
        m_SecondCollectionID = new ItemCollectionID ("Equipped_" + Database.Instance.GetCharacterIndex (Database.Instance.CurrentCharacter), ItemCollectionPurpose.None);
      } else {
        // Get character from players party with awaitedValue index
        m_SecondCollectionID = new ItemCollectionID ("Equipped_" + Database.Instance.PlayerParty [awaitedValue].ID.Value, ItemCollectionPurpose.None);
      }
      EquipUnequip (itemInfo, itemUser);
    }

The only button that works is "Current" that prints out "-1" properly at the Debug.Log. But for some reason all the other buttons print out "2", why is that? What am I doing wrong?
 
Ah that's a classic error, I make it sometimes but my IDE picks up on it and underlines the issue, which is one of the main reason I pay for Rider.

The problem is that the lambda function ()=> i takes a reference to the variable i, not it's value. Meaning that by the time you call the lambda function when clicking a button the value i will always by equal to playerParty.Count.

I usually fix the problem using this.

Code:
for (int i = 0; i < Database.Instance.PlayerParty.Count; i++) {
    var localI = i;
    m_AsyncFuncActions.Add (new AsyncFuncAction<int> ((Database.Instance.PlayerParty [i].Name).ToString (), () => localI));
}

now the lambda function saves a reference to localI, not i. Since localI is created within the loop, you'll have as many references as they are elements. This creates a bit of garbage but it's a trade off for simplicity of use.

I hope that helps
 
Ah that's a classic error, I make it sometimes but my IDE picks up on it and underlines the issue, which is one of the main reason I pay for Rider.

The problem is that the lambda function ()=> i takes a reference to the variable i, not it's value. Meaning that by the time you call the lambda function when clicking a button the value i will always by equal to playerParty.Count.

I usually fix the problem using this.

Code:
for (int i = 0; i < Database.Instance.PlayerParty.Count; i++) {
    var localI = i;
    m_AsyncFuncActions.Add (new AsyncFuncAction<int> ((Database.Instance.PlayerParty [i].Name).ToString (), () => localI));
}

now the lambda function saves a reference to localI, not i. Since localI is created within the loop, you'll have as many references as they are elements. This creates a bit of garbage but it's a trade off for simplicity of use.

I hope that helps
Ahh, yeah that fixed it!

I also needed the cancel button to return a specific value, so I edited the async panel script not to add a cancel button and instead added my own one in the action script.
 
Top