Making the only visible inventory item active

Started by ManicMatt, Tue 09/03/2021 18:59:17

Previous topic - Next topic

ManicMatt

Hello (again),

My other hurdle is hurting my brain, I've been searching through forum results with no luck.

So my game doesn't support the mouse, keys only. I have a small inventory GUI that is constantly onscreen, and only displays one item. You press the tab key or the Q key to flick between different inventory items. But I cannot find any code that would make the currently visible item become the active inventory item. Does such a piece of code exist?

eri0o


Crimson Wizard

That said, "active inventory item" is just a convention. You may make your own InventoryItem* variable (or an integer refering to item's ID) and assign it,  then use it to handle using items in game.

ManicMatt

#3
Thank you but I do not believe either are what I am after.

Let's say there's 5 inventory items held by the player. Pressing a key will replace the current visible inventory item displayed in a GUI inventory box with the next one, and so on. So in my code it is just:

Code: ags
else if (keycode == eKeyTab)
  {
   InventoryWindow1.ScrollDown();
}


So this isn't going to identify which item appears next, so I can't just code in that iKey (For example) will be active upon pressing a key without being able to detect what item is currently visible. That's why I was hoping there's a way for AGS to detect which inventory item is shown and automatically make that the active item, so when the player presses the enter key it will use the active item on a region/hotspot.

Matti

I don't really have time right now but I think you should be able to keep track of the index (or the shown row of the inventory window which should be equal to the item index) and use the ItemAtIndex property to retrieve the currently shown item. Another way would be to check InventoryItem.GetAtScreenXY(int x, int y).

Crimson Wizard

#5
InventoryWindow has TopItem property to know first visible items, InvWindow.ItemsPerRow to know number of items visible per row and InvWindow.RowCount to know number of visible rows.
Using these together lets you may find out which items are visible at the moment. If you need only first visible, that would be TopItem.

You may retrieve actual inventory item using InvWindow.ItemAtIndex[], where you pass the item index.


Khris

I'd probably do this:

Code: ags
function activateNextInvItem() {
  for (int i = 0; i < invMain.ItemCount; i++) {
    if (invMain.ItemAtIndex[i] == player.ActiveInventory) {
      // move to next item, including wrapping around
      player.ActiveInventory = invMain.ItemAtIndex[(i + 1) % invMain.ItemCount];
      break;
    }
  }
  // actually show sprite (instead of visible inv window)
  if (player.ActiveInventory) {
    btnInvItem.Graphic = player.ActiveInventory.Graphic;
    guiInv.Visible = true;
  }
  else guiInv.Visible = false; // hide active item GUI if player loses active item / has none
}


That way I avoid having to rely on GetInvItemAt and other such commands; it'll always just work. The InventoryWindow is just used for its array, which also conveniently keeps the order in which items were picked up.
Also, since I'm using a button to display the active item, I have full control over what's shown on screen.

ManicMatt

Thanks everyone! I tried Khris' code out but I don't think I know what I'm doing, it's too complicated for me. I get the game to run but nothing seems to happen.

The closest I have with Crimson and Matti's suggestions is

Code: ags
  else if (keycode == eKeyTab)
  {
    InventoryWindow1.ScrollDown();
  player.ActiveInventory = InventoryWindow1.ItemAtIndex[1];
  }
  else if (keycode == eKeyQ)
  {
   InventoryWindow1.ScrollUp();
   
    player.ActiveInventory = InventoryWindow1.ItemAtIndex[0];
 
  }


But this doesn't actually seem to go up and down, as the third item in my inventory doesn't appear as an active item. I just seem to be activating inventory items 1 and 2. You said about keeping track of the index, but I have no idea how to implement that. Help!

Khris

I posted just the function, you need to also call it:

Code: ags
  else if (keyCode == eKeyTab) activateNextInvItem();


My code also relies on a GUI (guiInv) created to show the active item on a button (btnInvItem).

As for "keeping track of the index", this translates to "global int variable" that is used in place of a hard-coded 0 and 1.

ManicMatt

#9
Okay, I'll give it another go!

The problems I am now having:

invMain is an undefined symbol, should I be renaming this to the name of my inventory GUI?

If I do that, I then get

"GlobalScript.asc(28): Error (line 28): property 'Button::Graphic' is read-only"

The manual states this is a read only command too? Do I change it to "btnInvItem.NormalGraphic;"?

Making this change also means the game runs but there's no inventory on screen. I think that's what happened when I first tried your code, I'm clearly doing something wrong here. Please advise!

Matti

You need to use Button.NormalGraphic instead of Button.Graphic.

ManicMatt

Okay so what happens now is, I see the graphic, I press tab, and then the inventory image dissapears, but at no point are items active because before, the mouse cursor would appear with the active inventory item. (Something else I need to work on)

I'm wondering if my GUI set up is wrong. Is guiInv supposed to contain InventoryWindow1, and btnInvItem inside it?

The code currently looks like this:

Code: ags

    function activateNextInvItem() {
      for (int i = 0; i < InventoryWindow1.ItemCount; i++) {
        if (InventoryWindow1.ItemAtIndex[i] == player.ActiveInventory) {
          // move to next item, including wrapping around
          player.ActiveInventory = InventoryWindow1.ItemAtIndex[(i + 1) % InventoryWindow1.ItemCount];
          break;
        }
      }
      // actually show sprite (instead of visible inv window)
      if (player.ActiveInventory) {
        btnInvItem.NormalGraphic = player.ActiveInventory.Graphic;
        guiInv.Visible = true;
      }
      else guiInv.Visible = false; // hide active item GUI if player loses active item / has none
    }

Khris

#12
Sorry for that, it was primarily example code, not really meant to be used as-is without further adjustments.

The inventory window is only used for internal inventory management; it stores the items in the order in which the player receives them. It has to exist, but is not used "visually" in my code.

Here's a revised version:
Code: ags
function activateNextInvItem() {

  InvWindow *iw = InventoryWindow1; // assigned to player, does not have to be visible(?)

  if (player.ActiveInventory == null) {
    // if no item is active, make first item active
    if (InventoryWindow1.ItemCount > 0) player.ActiveInventory = InventoryWindow1.ItemAtIndex(0);
  }
  else {
    // move to item after current one
    for (int i = 0; i < iw.ItemCount; i++) {
      if (iw.ItemAtIndex[i] == player.ActiveInventory) {
        player.ActiveInventory = iw.ItemAtIndex[(i + 1) % iw.ItemCount];
        break;
      }
    }
  }
}


Now we need to display the active item:
Code: ags
function repeatedly_execute_always() {

  GUI *ig = guiInv;                 // used to display item
  BUtton *ib = btnInvItem;          // ditto

  // actually show sprite (instead of visible inv window)
  if (player.ActiveInventory) {
    // set graphic, do not react to hovering or clicks
    ib.NormalGraphic = player.ActiveInventory.Graphic;
    ib.MouseOverGraphic = player.ActiveInventory.Graphic;
    ib.PushedGraphic = player.ActiveInventory.Graphic;
    ig.Visible = true;
  }
  else ig.Visible = false; // hide active item GUI if player loses active item / has none
}


You should probably also add this (make item active after receiving it):
Code: ags
function on_event(EventType event, int data) {
  if (event == eEventAddInventory) player.ActiveInventory = inventory[data]; // set received item as active
}

ManicMatt

Thanks Khris, you're awesome as always!

However the code as is didn't work for me, I had to change it to this and now it works, although it doesn't show any inventory items until I press the Tab key first but given the character won't start with any inventory items it might not be a problem. This is how it looks now, also note you had accidently used () instead of [] for ItemAtIndex which is easily done. Just pointing this out just in case anyone in the future is looking at this thread for advice.

Code: ags
    function activateNextInvItem() {
     
      InvWindow *iw = InventoryWindow1; // assigned to player, does not have to be visible(?)
     
      if (player.ActiveInventory == null && InventoryWindow1.ItemCount > 1)
    // if no item is active, make first item active
    
    player.ActiveInventory = InventoryWindow1.ItemAtIndex[0];
      else {
      // move to item after current one
      for (int i = 0; i < iw.ItemCount; i++) {
      if (iw.ItemAtIndex[i] == player.ActiveInventory) {
      player.ActiveInventory = iw.ItemAtIndex[(i + 1) % iw.ItemCount];
      break;
      }
 }
 }
 }


I also had to change "BUtton" in the second piece of code to "Button", just a typo. Easily spotted for me, and I risk sounding like an arse, but again just pointing it out for anyone intending to use your code.  :smiley:

So unless you can see some error in my changes, I think this case is closed?

Thanks again!  :cheesy:

Khris

Great, glad you got it to work :)
When I find the time I'll put my code in an actual game and test it properly, then fix my code in the previous post. Fingers crossed :-D

Also, if the character starts out with one or more inventory items, try putting
Code: ags
  player.ActiveInventory = iWallet;

in room_Load or game_start and it should appear.

SMF spam blocked by CleanTalk