[solved]I'm missing something in textbox understanding oO

Started by lafouine88, Tue 16/04/2024 10:31:56

Previous topic - Next topic

lafouine88

Hi guys

I'm newb in using textboxes, but it seemed quite intuitive and very handy. Though it seems there is something I didn't get, or maybe I just coded wrong something else. Anyway it got me so crazy for the last couple of days that I decided to ask for help.

The textbox is basically a list of spells that you can buy. To be able to know which spell to display (the data is in a struct array), I use the textbox index. But since this index changes when you remove an item of the list, I used an extra variable in my spell Array named sorts.index. This variable goes up when you buy a spell above it in the list. This way, using i+sorts.index it always show the right icon.

This is the code (it was much more complete initially but I reduced it to the basics) :

Code: ags
function btntrainwar_OnClick(GUIControl *control, MouseButton button)
{
 int i=Sortswar.SelectedIndex;  //sortswar is the textbox name

   for(int j=i;j<6;j++){////6 max spells
   sorts[j].index+=1;
   }
Sortswar.RemoveItem(i);

 }

and
 
Code: ags
if(Sortswar.SelectedIndex==i){Icontrainer.NormalGraphic=sorts[i+sorts[i].index].icone30;//to display the right icon(and price etc)

I knew it was not going to be clear with words so I made a little video.

https://streamable.com/ylkoa3

As you can see in the video. It works perfectly most of the time. When you buy a spell, the spells below get indexed+1 while their textbox index goes down automatically. The sum stays the same and so the display is good.
But there is one specific combination, the one I recorded than ends up not working well. On the last spell purchase of the video (the 4th one) the index of the spell bellow don't increase and it ends with a display bug.

I really don't get why this would do this, the code seems easy and logical. Do you have any ideas???

Thanks a lot

Khris

The curious thing here isn't that it breaks in the end, it's rather that it appears to work in the beginning :-D

The basic idea is sound, however when you remove an item, the list index of the spells further down gets decreased by one, which means each spell now uses the wrong sorts[].index. This doesn't matter as long as it's 1 for every spell, but if a spell that had +3 as its .index gets shifted to a lower list index, that spell could now end up using the lower .index of, say, +2 of the spell above it.
This additional -1 screws up the associations of the list items and the spell array.

To put it another way: to get the correct sorts[] element to read the .index, you would need the already fixed index, but if the index is already fixed, you no longer need the sorts[].index value.
Implementing your solution requires an independent array of index correction values with its values getting shifted exactly like the list items.

Another way to fix this is looking up the list item string in the spell array:
Code: ags
  int si = -1;
  for (int i = 0; i < 6; i++) if (sorts[i].name == Sortswar.Items[Sortswar.SelectedIndex]) si = i;
  // populate GUI using sorts[si] info

eri0o

One hack that I use sometimes with things like this is have a dumb character invisible character that is not in any room and associate an inventory window to them, then add or remove items to that inventory. It may be a pain in the long run if there are too many items but if there aren't many it works alright.

This card game uses the inventory window as an example: https://github.com/ericoporto/dungeonhands

Inventory Items have a weird small limit of 300 but I think this may be something that is possible to remove in a new AGS version - I have no idea why their limit is so small.

One nice thing is they give you separation of data and presentation for free when using a GUI control InventoryWindow

lafouine88

Quote from: Khris on Tue 16/04/2024 12:15:08The curious thing here isn't that it breaks in the end, it's rather that it appears to work in the beginning :-D
You start to think you understand coding and then this happens...XD

Thanks for the reply khris. I think I understand now what goes wrong, through I still don t get why :confused:
Thé way I see it,the sorts[].index is independant of the textbox. The function changes the amounts once(it s single action,not repeatedly), and once it is donne, it removes the selected item. This makes the textbox array move up,sure, but why would it change again the sorts[].index value?

I will go for the changes you suggested and I m sure it s gonna work, but for future use I would like to understand.

@eri0o . I use an invisible characters for the inventory, and After Reading some posts here I thought I would do it again for crafting and training, but for some reason I forgot and went for a textbox, it seemed adapted, maybe it s not that much "^^

eri0o

Uhm, I can't quite tell what is what reading your code (I usually prefix my GUI Controls like lstboxSpells or invwinSpells) but one way that I could imagine breaking this problem is, let's say you have an issue with a list of things, let's name it "list data" and the listbox has to be synced to this "list data". Then you can create one function that given something in the listbox gives me the match on the "list data" - and vice-versa. Assuming the "list data" is the original source of truth you can create a function that clear the entire listbox and fills it with elements from "list data", let's call it "RefreshListUi".

Now if you remove (or add) something from "list data", you just call "RefreshListUi" and then it doesn't matter what was added or remove it should be clear.

Also, in AGS If your "list data" has no specific order and is unique, you can use a Dictionary to quickly have an object that you can add and remove things.

Khris

The problem becomes obvious if you look at this part:
Code: ags
  sorts[i+sorts[i].index].icone30

You are storing the index offset for each spell in a place you can only access if you already know the offset. You don't however, so you simply use the list item index without the offset instead (which will accidentally work in some cases, then quickly fail).

The problem isn't that the .index values are changed, it's that you're reading the wrong .index value. If you remove every spell except sorts[3], it's the only list item left and therefore item #0. Which means in the code piece above, the value of i will be 0. Which means you won't be reading sorts[3].index (which contains the value you're looking for) but sorts[0].index instead. You will then add whatever sorts[0].index is (most likely 0) to i (also 0) and again end up with an index value of 0, which means you're now reading sorts[0].icone30 instead of sorts[3].icone30.

Edit: you can probably also fix your code by using sorts[a].index to store the offset for list item #a, as opposed to sorts[a]. In fact, this seems what you wanted to do from the start I guess. Which means there's a tiny bug somewhere. Let me check.

Edit2: found the bug: the list of offsets, regardless where it is stored, needs to lose items along with the list. Otherwise you keep using offsets of items that are already removed from the list. Increasing the values starting from the selected item isn't enough; you also need to shift the values.

Here's the fix of your original code:
Code: ags
  for (int j = i; j < 5; j++) { // 6 max spells
    sorts[j].index = sorts[j + 1].index + 1; // add one and move up the list
  }

lafouine88

Quote from: Khris on Tue 16/04/2024 17:14:29The problem becomes obvious if you look at this part:
Code: ags
  sorts[i+sorts[i].index].icone30
Now you mention it, yes it does^^

Thanks a lot for your help guys  :wink:

SMF spam blocked by CleanTalk