• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GML [SOLVED] for_loop/math problem, making buttons display properly

Dr_Nomz

Member
I have a chest that displays it's inventory slots (buttons) when opened, and I got it set up to have multiple pages for it's inventory, which is great, except that it isn't displaying correctly.

The way it works is it's in a case statement, once the next page button is pressed, it switches the case(page) from 0 to 1, displaying the for loop for the rest of the slots instead.

But it shows them like it would if they were all drawn at the same time, because of how it's set up, even though it's supposed to start from the top just like the first loop.

This is the first loop:
Code:
  for (c = 0; c < 10; c += 1){
    var ix = x1+840+((c%2)*104);
    var iy = y2+101+(floor(c/2)*104);
    draw_sprite(spr_Item_Slot_3,0,ix,iy)
    button[c].show_button=true;
    button[c].x = ix;
    button[c].y = iy;
  }
On the second, it has for(c = 10; c < 20; c += 1) and displays them below where the old buttons used to be. How do I make it so they display correctly, just like the first ones?
 
Create
Code:
inv_pos = 0;
Draw
Code:
 for (c = inv_pos; c < inv_pos+10; c += 1){
   var ix = x1+840+(((c mod max_inv_no)%2)*104);
   var iy = y2+101+(floor((c mod max_inv_no)/2)*104);
   draw_sprite(spr_Item_Slot_3,0,ix,iy)
   button[c].show_button=true;
   button[c].x = ix;
   button[c].y = iy;
  }
Next Page Click
Code:
inv_pos += 10;
inv_pos = inv_pos mod max_inv_no+1; // This'll set it back to 0 if you go past the max number of inventory slots
Probably the easiest way to do what you want with the least tweaking of your current code.

EDIT: I didn't see the maths in your code, hahaha, coolgiraffe has done what I should've when I posted this. I also changed the maths in this post so it should work properly.
 
Last edited:
I'm not sure if understand the problem correctly, but if you want each page to be shown in the same position you might want to add some variables.
Code:
var currentPage = 0; //which page is displayed
var pageSize = 10; //how many slots are in each page

for (c = 0; c <10; c += 1){
   var ix = x1+840+((c%2)*104);
   var iy = y2+101+(floor(c/2)*104);
   draw_sprite(spr_Item_Slot_3,0,ix,iy)

   var pos = c + (currentPage * pageSize); //for example: if the second page is displayed and c = 2, then pos = 2+1*10 = 12

   button[pos].show_button=true;
   button[pos].x = ix;
   button[pos].y = iy;
  }
let me know if this works since its pretty hard for me to understand your code :)
 

Dr_Nomz

Member
Create
Code:
inv_pos = 0;
Draw
Code:
 for (c = inv_pos; c < inv_pos+10; c += 1){
   var ix = x1+840+(((c mod max_inv_no)%2)*104);
   var iy = y2+101+(floor((c mod max_inv_no)/2)*104);
   draw_sprite(spr_Item_Slot_3,0,ix,iy)
   button[c].show_button=true;
   button[c].x = ix;
   button[c].y = iy;
  }
Next Page Click
Code:
inv_pos += 10;
inv_pos = inv_pos mod max_inv_no+1; // This'll set it back to 0 if you go past the max number of inventory slots
Probably the easiest way to do what you want with the least tweaking of your current code.

EDIT: I didn't see the maths in your code, hahaha, coolgiraffe has done what I should've when I posted this. I also changed the maths in this post so it should work properly.
I definitely LIKE where this is going, but I can't seem to implement it properly. First try didn't seem to change the pages at all, second caused a crash.

Here, I'll just post the code here and let you take a look, this is the FULL draw event. Bare in mind it's pretty massive, but it'll give you a better idea of how works.

Create event first:
Code:
alpha = false;
buffer=0;
page=0;

global.container_transfer = false;
show_container = false;
container_space = 20;

for (var c = 0; c < container_space; c += 1) {
  button[c] = instance_create(0, 0, obj_Box_slot2);
  with (button[c]) {
    show_button=false;
    container_slot[c] = -1;
    container_count[c] = 0;
    slot = c;
    number = c;
  }
}
Code:
///Draw Box Inventory
draw_self();
//Key press events
if keyboard_check_pressed(vk_escape){
  show_container=false
}
if global.container_transfer=false{
if (point_in_rectangle(mouse_x,mouse_y,bbox_left-buffer,bbox_top-buffer,bbox_right+buffer,bbox_bottom+buffer)){
if keyboard_check_pressed(ord("E")){
  show_container=!show_container;
  global.container_transfer=!global.container_transfer;
  if showInv=false{
    showInv=true;
  }
}
}
}
if keyboard_check_pressed(ord('0')) && showInv=true{
  alpha = true
}
if keyboard_check_pressed(ord('9')) && showInv=true{
  alpha = false
}

if show_container=true{
  var x1,x2,y1,y2;
  x1 = view_xview[0];
  x2 = x1 + view_wview[0];
  y1 = view_yview[0];
  y2 = y1 + 104;
 
  if alpha = true{
  draw_set_alpha(0.8);
  draw_sprite(spr_Inventory_6,0,x2+310,y1+150);
  draw_set_alpha(1);
  }
  if alpha = false{
  draw_sprite(spr_Inventory_6,0,x2+310,y1+150);
  draw_set_alpha(1);
  }
 
  //Draw pages
  switch(page){
  case 0:{ //Page 0
  for (c = 10; c < 20; c += 1){
  button[c].show_button=false;}
    for (c = 0; c < 10; c += 1){
    var ix = x1+840+((c%2)*104);
    var iy = y2+101+(floor(c/2)*104);
    draw_sprite(spr_Item_Slot_3,0,ix,iy)
    button[c].show_button=true;
    button[c].x = ix;
    button[c].y = iy;
  }}break;
  case 1:{ //Page 1
  for (c = 0; c < 10; c += 1){
  button[c].show_button=false;}
    for (c = 10; c < 20; c += 1){
    var ix = x1+840+((c%2)*104);
    var iy = y2+101+(floor(c/2)*104);
    draw_sprite(spr_Item_Slot_3,0,ix,iy)
    button[c].show_button=true;
    button[c].x = ix;
    button[c].y = iy;
  }}break;
  }//Draw pages end */
 
  //Close the inventory.
  if keyboard_check_pressed(vk_tab){
    show_container=false;
    global.container_transfer=false;
  }
//Draw next page buttons.
var xx,yy;
  xx = view_xview[0];
  yy = view_yview[0];

  draw_set_font(fnt_1);
  draw_set_color(c_yellow);
  draw_text(xx+600,yy+100,page);
  draw_sprite(spr_Item_Slot_3,0,xx+650,yy+350);
  draw_sprite(spr_Item_Slot_3,0,xx+650,yy+450);
  if(point_in_rectangle(mouse_x,mouse_y,xx+600,yy+300,xx+700,yy+400)){
    draw_set_color(c_white);
    draw_rectangle(xx+600,yy+300,xx+700,yy+400,0);
    if mouse_check_button_pressed(mb_left){
      page=0;
  }}
  if(point_in_rectangle(mouse_x,mouse_y,xx+600,yy+400,xx+700,yy+500)){
    draw_set_color(c_white);
    draw_rectangle(xx+600,yy+400,xx+700,yy+500,0);
    if mouse_check_button_pressed(mb_left){
      page=1;
  }}
} //Hide buttons
if show_container=false{
  for (c = 0; c < container_space; c += 1){
    button[c].show_button=false;
  }
}
 

Dr_Nomz

Member
Basically it's a heavily modified version of this tutorial:
Also making heavy use of his mouse cursor tutorial, part of his expanded tutorial series:

Basically what I'm doing with it is making it so it starts out on "page 0", and every time I hit the one of the buttons drawn, it'll change the page, switching the case to draw the other buttons and hide the first page. (Since each button has it's own "visible" value)

But I MUCH prefer the concept Refresher gave. So I'd like instead for the buttons to function like he intended, being a page UP/DOWN respectively would be REALLY awesome.

Container_Space is essentially the "maxItems" value of the chest, meaning if I just used it as is, it would draw 20 inventory slots on the screen. However, I only want about 10 of those at a time to be displayed, hence the page variable.

The reason I need that variable at all is because it won't work otherwise. It needs to start off with it's maximum inventory slots, and use all of them from start to finish. It's pretty limiting in that way, but this system in it's current form has no real need for a "dynamic" inventory that grows or shrinks anyway, and I prefer the page setup I have now. (It's also WAY more simple to manage and deal with with in code and in game.)
 
Last edited:

Dr_Nomz

Member
Hey CoolGiraffe got it! Thanks! :D Now I need to implement the page up and down idea Refresher had.

Code:
//Draw pages
  switch(page){
  case 0:{ //Page 0
  for (c = 10; c < 20; c += 1){
  button[c].show_button=false;}
  }break;
  case 1:{ //Page 1
  for (c = 0; c < 10; c += 1){
  button[c].show_button=false;}
  }break;
  }//Draw pages end */
 
  //Close the inventory.
  if keyboard_check_pressed(vk_tab){
    show_container=false;
    global.container_transfer=false;
  }
//Draw next page buttons.

//Everything down here requires better optimization, but the major thing is up there.^

var xx,yy;
  xx = view_xview[0];
  yy = view_yview[0];

  draw_set_font(fnt_1);
  draw_set_color(c_yellow);
  draw_text(xx+600,yy+100,page);
  draw_sprite(spr_Item_Slot_3,0,xx+650,yy+350);
  draw_sprite(spr_Item_Slot_3,0,xx+650,yy+450);
  if(point_in_rectangle(mouse_x,mouse_y,xx+600,yy+300,xx+700,yy+400)){
    draw_set_color(c_white);
    draw_rectangle(xx+600,yy+300,xx+700,yy+400,0);
    if mouse_check_button_pressed(mb_left){
      page+=1;
  }}
  if(point_in_rectangle(mouse_x,mouse_y,xx+600,yy+400,xx+700,yy+500)){
    draw_set_color(c_white);
    draw_rectangle(xx+600,yy+400,xx+700,yy+500,0);
    if mouse_check_button_pressed(mb_left){
      page-=1;
  }}
  if page>1{
  page=0;}
  if page<0{
  page=1;}
The way my buttons work is they have their own local variable determining if they're visible or not, so every time the inventory closes each button has to be hidden by updating it's show_button variable. This is equally true for changing pages, hiding the first ones, then showing the latter ones.

This code works fine, but it's already unmanageable and it'll be even worse with more that two pages. The main thing is that it has to be able to use a for loop to hide the correct buttons based on what the current page is.

The other thing is I really need a better way to draw my buttons
 

TheouAegis

Member
Try not to think in terms of absolutes. Your code says if page is greater than 1; that's an absolute. Your page_count is ceil(container_space/10). So page=page mod page_count (sometimes negatives don't give the right modulo, so the longhand method may be required).

The slots you'd show are the range of [page*10, page*10+9]. This even means you don't need to specify how many slots in the create event, you could just specify how many pages the chest will have. Up to you.
 

Dr_Nomz

Member
This is how I did it, up button didn't work, page down button gave an out of range error:
Code:
    if mouse_check_button_pressed(mb_left){
      page+=1;
      page=page mod pageSize;
  }
I still need at least 2 buttons, 1 for moving the pages up and one for moving them down. (In my case I used page-=1)

I also really need this optimized too:
Code:
  switch(page){
  case 0:{ //Page 0
  for (c = 10; c < 20; c += 1){
  button[c].show_button=false;}
  }break;
  case 1:{ //Page 1
  for (c = 0; c < 10; c += 1){
  button[c].show_button=false;}
  }break;
  }
So that it automatically knows which buttons to hide based on what page is being used. Like it's fine as is, it works, but I need to manually code it for every page I decide to add.
 

TheouAegis

Member
Why is page 0 using buttons 10 thru 19, but page 1 is buttons 0 thru 9? Page 0 should use buttons 0 thru 9 and page 1 should use buttons 10 thru 19. Then it's a no-brainer:

for(var p=0; p<pageCount; p++)
for(var c=0; c<10;c++) {
button[c+p*10].show_button=p==page;​
}
 

Dr_Nomz

Member
That's weird, had an unexpected symbol at the end I couldn't see... after it says page;
Any reason for that? Otherwise I'm testing it right now.

Code:
Push :: Execution Error - Variable Index [0,20] out of range [1,20] - -1.button(100025,20)
 at gml_Object_obj_Box_2_DrawEvent_1 (line 58) -   button[c+p*10].show_button=p==page;
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Object_obj_Box_2_DrawEvent_1 (line 58)
Uh you're not supposed to put anything in button[c]'s brackets.

"c" is the variable that determines the ID of the button, so filling the brackets creates that error.

Also how is your code setting the button's show_button variable to true/false? I can't tell where that would be happening.

Code:
for(var p=0; p<pageSize; p++){
  for(var c=0; c<10;c++) {
  button[c].show_button=p==page; //+p*10
  }}
That kept it from crashing when opened, but trying to set the page higher just sets it back to 0. And lowering the page number crashes the game.
 
Last edited:

TheouAegis

Member
What are you setting pageSize to?

The error is saying your array wasn't set right and/or pageSize is wrong. The fact it was trying to go up to 20 in the error message suggests perhaps also your version of GMS1 is running arithmetic operations out of order...

button[(p*10)+c].show_button=p==page;

Assuming container_space is always a multiple of 10 and pageSize is always equal to container_space/10, then that line should work.

The reason your alteration caused the error message to go away is because you only look at button[0] thru button[9]; but doesn't properly address the out of bounds error. It also doesn't cover the other 10 buttons.

show_button is getting set to true or false by the formula p==page.

The invisible character was probably the TAB character. lol My bad.
 

Dr_Nomz

Member
Tab can be typed as a character? Huh. Well I'll take a look at this and see what I can do.

EDIT:
Code:
Push :: Execution Error - Variable Index [0,20] out of range [1,20] - -1.button(100025,20)
 at gml_Object_obj_Box_2_DrawEvent_1 (line 58) -   button[(p*10)+c].show_button=p==page;
Yeah, still out of range. You can't have ANYTHING in that array at all other than the C, or it won't work. :/

Code:
page=0;
container_space = 20;
Also just to point out what the variables are, page is what page of slots is displayed currently, so if it changes to 1, slots 10-20 or whatever are displayed, for example.

Container space is what creates the item slots. 20 is what it has when it's created, and that variable can't be changed later on in game.
Code:
  var pageSize = 10; //how many slots are in each page
And in the draw event I use this var for determining how many slots are displayed in each page, basically Giraffe's code.

Here I'll just post my Draw Event for the relevent code, maybe looking it over will give a better idea of how it works.
Code:
var pageSize = 10; //how many slots are in each page
  for (c = 0; c <10; c += 1){
    var ix = x1+840+((c%2)*104);
    var iy = y2+101+(floor(c/2)*104);
    draw_sprite(spr_Item_Slot_3,0,ix,iy)
    var pos = c + (page * pageSize); //for example: if the second page is displayed and c = 2, then pos = 2+1*10 = 12
    //Code by CoolGiraffe
    button[pos].show_button=true;
    button[pos].x = ix;
    button[pos].y = iy;
  } //Draw Buttons end
 
  //Draw pages
  for(var p=0; p<pageSize; p++){
  for(var c=0; c<10;c++) {
  button[(p*10)+c].show_button=p==page;
  }}

EDIT: Just messed around a bit, got it to work properly-ish with pos.
Code:
for(var p=0; p<pageSize; p++){
  for(var c=0; c<10;c++) {
  button[pos].show_button=p==page;
  }}
Except that it doesn't hide any of the buttons at all, and going UP a page with page+=1 doesn't set it back to 0, it just gives an out of range error. Same for trying to go back down.
 
Last edited:

TheouAegis

Member
Oh, I thought pageSize was an odd name...

Code:
var pageCount = ceil(container_space/10);
for(var p=0; p<pageCount; p++)
for(var c=0; c<pageSize; c++)
{
if (p*pageSize)+c < container_space
button[(p*pageSize)+c].show_button=p==page;
else break;
}
 

Dr_Nomz

Member
It seems to work great! :D Except for the small detail that it doesn't let me go over/under the total page count without breaking it.
Code:
Push :: Execution Error - Variable Index [0,20] out of range [1,20] - -1.button(100025,20)
 at gml_Object_obj_Box_2_DrawEvent_1 (line 50) -     button[pos].show_button=true;
Is there a way to have it automatically recognize that it has to go back to page 0 if it goes over, and page 1 if it goes under, so it doesn't crash like this?

Or do I have to keep using this?
if page>1{
page=0;}
if page<0{
page=1;}
I don't really mind this, but it would be SO nice if I didn't have to manually code for this check every time I make an object or add a page.
 

TheouAegis

Member
That's what the mod was for. At least for going up.
Code:
var pageCount = ceil(container_space/10);


if mouse_check_button_pressed(mb_left){
     page+=1;
     page=page mod pageCount;
  }
It didn't work before for the same reason - the first time we used pageSize instead of pageCount.
 

Dr_Nomz

Member
That is so nice, it works perfectly. How about for moving down though? Using the same code gave the same errors as before, but using DIV just made it do nothing.
 

TheouAegis

Member
If

page=(page -1) mod pageCount

gives an out of range error, then itmeans negative mod anything is returning a negative. You could try

page-=1;
page = (page+pageCount) mod pageCount;
 
Top