Describe the bug
When creating new abilities and putting them into the Ability Container's Ability Array, the resulting order seems to be reversed. The UI below indicates an array with the following order:
- "Regenerate"
- "Sacrifice"
- Ability with ui_name "fake_pos2"
- Ability with ui_name "fake_pos3".
The latter two abilities are dummies which have ui_names indicating the expected position in the array. You can see this ordering reflecting in the following screenshot.
However, when I attempt to active Regenerate using the following code
var ability = ability_container.granted_abilities[0]
ability_container.activate_one(ability)
it instead activates the "fake_pos3" ability (or it would if it weren't a placeholder). When we print the array using the following code:
for idx in len(ability_container.granted_abilities):
print(idx,ability_container.granted_abilities[idx].ui_name)
We would expect to see the index printed along the ability ui_name associated with the index. Instead we see the following screenshot:
In the screenshot, the order is reversed relative to the expectation.
To Reproduce
Steps to reproduce the behavior:
- Initialize a character and add a GameplayAttributes with a GameplayEffect as its child. Also add an AbilityContainer. My tree looks like this:
2. Add at least two Abilities to the AbilityContainer. I chose to add 4 to verify the behavior.
3. Attach a script to the player and retrieve the AbilityContainer and AttributeMap.
4. In the script, identify the contents of the AbilityContainer's granted_abilities[Ability] array. I used the following code:
```
func _input(event):
if event.is_action_pressed("ability_q"):
for idx in len(ability_container.granted_abilities):
print(idx,ability_container.granted_abilities[idx].ui_name)
In my case, this yields output of
0Fake_pos3
1fake_pos2
2Sacrifice
3Regenerate
**Expected behavior**
I would expect the output instead to be:
0Regenerate
1Sacrifice
2fake_pos2
3Fake_pos3
**Screenshots**
Included inline
**Desktop (please complete the following information):**
- OS: MacOS Sonoma 14.1
- Godot version 4.1.3
**Additional context**
I've found a fairly simple fix for my case. In ability_container.gd, add `granted_abilities.reverse()` to the function after granting the abilities, i.e.
func _ready() -> void:
gameplay_attribute_map = get_node(gameplay_attribute_map_path)
grant_all_abilities()
granted_abilities.reverse()
However, this may not fix the issue when abilities are appended to the array as a part of the game, i.e. not a runtime. I suspect the issue lies in grant() or in grant_all_abilities(), but I'm still a bit new to Godot and am not 100% sure where the issue is arising. My best guess is that in grant_all_abilities(), when iterating through the abilities it grants them in reverse order because cursor is set to -1. When I add print statements to the function
```func grant_all_abilities() -> int:
var granted = 0
var cursor = -1
for i in abilities.size():
var ability = abilities[cursor]
for ab in abilities:
print(ab.ui_name)
print(i,cursor,ability.ui_name)
if grant(ability):
granted += 1
else:
cursor -= 1
return granted
It yields the following:
Regenerate
Sacrifice
fake_pos2
Fake_pos3
0-1Fake_pos3
Regenerate
Sacrifice
fake_pos2
1-1fake_pos2
Regenerate
Sacrifice
2-1Sacrifice
Regenerate
3-1Regenerate
It seems to grant from the back of the abilities array first when granting, making it so Regenerate is added last despite being listed first. It might be that iterating in a different way would correct the ordering, but it may be that you have a reason for iterating in this way and I'm still new enough to Godot that I'm unsure the proper way.
Reproducible project
Here's the link to my project. The relevant files are archetype_base.gd in the root directory and the ability_container.gd script found in the addons folder.