Standard response for using inventory item on itself? (solved)

Started by Akril15, Mon 07/03/2011 01:30:34

Previous topic - Next topic

Akril15

I've run into another small problem with my latest project -- I'm trying to find a way to display a specific message whenever the player uses an inventory item on itself. The way I see it, the only way I could accomplish this would be to go through every inventory item and insert some code like this in the UseInv section:

Code: ags

if (player.ActiveInventory==iBanana) {
Display("I can't use that on itself!");
}
else unhandled_event(5,3); //use inventory item on inventory item


If I didn't have so many inventory items, this wouldn't be much of a problem. However, since I do (and may make even more inventory-item-rich games in the future), is there any way to designate a standard "I can't use an inventory item on itself!" message without having to insert it in each item's UseInv section?

monkey0506

You could link every inventory item's "Use inventory item on this item" function to the same function (by copy/pasting the function name into the box instead of clicking on the "...").

I'm thinking something such as this:

Code: ags
function Inventory_UseInv()
{
  // link every inventory's handler here
  if ((game.inv_activated <= 0) || (game.inv_activated > Game.InventoryItemCount) || (player.ActiveInventory == null)) return; // I'm not sure what the "null" value is for game.inv_activated..
  // we'll define some pointers to make this a bit simpler
  InventoryItem *clickedItem = inventory[game.inv_activated];
  InventoryItem *usedItem = player.ActiveInventory;
  if (usedItem.ID < clickedItem.ID)
  {
    // we check this to prevent having to duplicate code..again, it just makes things simpler
    InventoryItem *temp = clickedItem;
    clickedItem = usedItem;
    usedItem = temp;
  }
  if (clickedItem == usedItem)
  {
    player.Say("I can't use the %s on itself!", clickedItem.Name);
    return;
  }
  else if (clickedItem == iBanana)
  {
    // handle iBanana's use-inv functions here..
  }
  else if (clickedItem == iToupee)
  {
    // ...
  }
  // ...
  // if an event has been handled, return from the function that way you don't have to type unhandled_event everywhere..you can just let it fall through to here
  unhandled_event(5, 3);
}


If you really want separate functions for each inventory item, you could call it from within this function, but I'd say that this method actually makes combining inventory items simpler overall as it saves a lot of unnecessary code duplication.

Ryan Timothy B

Why not just eliminate the ability to use an inventory item on itself? It's quite odd that you'd even allow such a behavior. Why not just have it deselect the inventory item whenever you click it on itself.



If you really wanted to get more professional, how about having 2 sprites for the inventory item. One normal sprite, and the other to suggest that it's the currently held inventory item, whether that's a grayed out version or an outline of the inventory item, or whatever. Your best bet would be to probably use a View with 2 frames per Loop. The first frame being the normal graphic, while the second being the active graphic. Then you just change the inventory items graphic according to the inventory ID number and depending on what frame, and voila. Of course the Loop graphics would have to be in the proper order to match the inventory ID numbers.

Then using an extender function like so:

Code: ags

void normalGraphic(this InventoryItem*)
{
  if (this == null) return;
  if (this.ID > Game.GetLoopCountForView(VINVENTORY) return;
  ViewFrame *frame = Game.GetViewFrame(VINVENTORY, this.ID-1, 0);
  inventory[this.ID].Graphic = frame.Graphic;
}

void grayGraphic(this InventoryItem*)
{
  if (this == null) return;
  if (this.ID > Game.GetLoopCountForView(VINVENTORY) return;  
  ViewFrame *frame = Game.GetViewFrame(VINVENTORY, this.ID-1, 1);
  inventory[this.ID].Graphic = frame.Graphic;
}


It first checks if the inventory item is null, if it is it aborts the function. Then it checks if there is even enough Loops in the VINVENTORY view to accommodate for the current inventory ID, which also returns if there isn't.

Then you simply just call  iBanana.grayGraphic()  to change it to gray, or  iBanana.normalGraphic()  to change it back to normal.
It's just a suggestion. Do with it as you will.

Akril15

Quote from: Ryan Timothy on Mon 07/03/2011 02:56:06
Why not just eliminate the ability to use an inventory item on itself? It's quite odd that you'd even allow such a behavior. Why not just have it deselect the inventory item whenever you click it on itself.
Well, to be honest, I was considering rendering the active inventory item unclickable somehow, but when I searched the forums, I couldn't find any posts that addressed that problem, so I got the impression that that sort of thing wasn't impossible and I had to resort to using a default "I can't use that on itself" message -- looks like I was wrong again.

Your grayed-out inventory icon idea sounds great, and I may end up using it (I had a little problem implementing it at first, but it turned out that the "if (this.ID > Game.GetLoopCountForView(VINVENTORY)" lines were just missing a closing parenthesis). Since I'm still a beginning scripter and want to make sure I'm not missing something, in order to call the two functions, do I have to put lines of code like the following...

Code: ags
if (player.ActiveInventory==iBanana) iBanana.GrayIcon();
else if (player.ActiveInventory!=iBanana) iBanana.NormalIcon();


...in repeatedly_execute for each individual inventory item? Also, I'm still not sure how to make the active inventory item unclickable. Could you please explain how that's done?

Thanks!

monkey0506

There's no such thing as InventoryItem.Clickable or anything like that, so you would have to manually intercept it in on_mouse_click or on_event:

Code: ags
// on_mouse_click:
  if (button == eMouseLeftInv)
  {
    InventoryItem *clickedItem = inventory[game.inv_activated];
    if (player.ActiveInventory == clickedItem)
    {
      player.ActiveInventory = null;
      return;
    }
    // else ...
  }


Unless I'm missing something though you wouldn't need to call those functions from rep ex..you would call them at the same time as the player's active inventory is being set within the script.

Akril15

I must still be missing something. I now have this code in the global script's on_mouse_click section:

Code: ags

if (gInv.Visible==true) {  //gInv - sierra-style inventory window
   if (button == eMouseLeftInv) {
    InventoryItem *clickedItem = inventory[game.inv_activated];
    if (player.ActiveInventory == clickedItem)
    {
      player.ActiveInventory = null;
      clickedItem.grayGraphic();
      return;
        }
   else clickedItem.normalGraphic();
       }

The game starts up fine, but the code doesn't work. The active inventory item's icon doesn't change when the item is selected, and I still get a "I can't do that" message when I use the item on itself (which I don't want). What am I doing wrong now?

(By the way, I don't have to set the "handle inventory clicks in script" option to true, do? For some reason, I was under the impression that I had to do that.)

Khris

Quote from: Akril15 on Mon 07/03/2011 17:54:38
(By the way, I don't have to set the "handle inventory clicks in script" option to true, do? For some reason, I was under the impression that I had to do that.)
Yes you do, otherwise button will never be eMouseLeftInv.

Plus, the check for gInv being visible is redundant, you can't click an inv item if the GUI isn't visible.

Akril15

Okay, thanks -- I made sure "handle inventory clicks in script" was set to true. I had some trouble at first, and I had to go through several different threads and tried several different codes before I realized that the reason my inventory items were no longer responding to my cursor was because of the kind of inventory window I was using. However, my inventory is behaving strangely now.

If I select an inventory item, it becomes the active inventory item but the icon isn't grayed out. Stranger still, if I then select the pointer and click on the same item again, the icon grays out but the cursor remains unchanged, and if I use the item on itself, the item's icon changes back to normal and the cursor becomes the Walk icon. Also, the active inventory item now doesn't interact with any of the other inventory items.

I guess I'll post my entire on_mouse_click section here, since I'm completely out of ideas now.
Code: ags

function on_mouse_click(int button) {
  //InventoryItem *i = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
  InventoryItem *clickedItem = inventory[game.inv_activated];
  // called when a mouse button is clicked. button is either LEFT or RIGHT
   
if (button == eMouseLeftInv) { // left click on inventory item
   
   if (player.ActiveInventory == clickedItem)
    {
      player.ActiveInventory = null;
      clickedItem.grayGraphic();
      return;
        }
   else clickedItem.normalGraphic();
   
     if (mouse.Mode == eModeTouch)  { //select inv item
        player.ActiveInventory = inventory[game.inv_activated];
            }
     else if (mouse.Mode == eModeLookat)  { //look at inv item
        inventory[game.inv_activated].RunInteraction( eModeLookat );
      }
      else if (mouse.Mode == eModeInvtouch) { ///touch inv item
      inventory[game.inv_activated].RunInteraction( eModeInvtouch);
          }
      }
      
 else if (IsGamePaused() == 1) {
    // Game is paused, so do nothing (ie. don't allow mouse click)
  }
  else if (mouse.IsButtonDown(eMouseLeft)) {
    ProcessClick(mouse.x, mouse.y, mouse.Mode);
    }
    
  else {   // right-click, so cycle cursor
    mouse.SelectNextMode();
      }


}
#sectionend on_mouse_click  // DO NOT EDIT OR REMOVE THIS LINE


Also, I've got Ryan's codes for the "gray-out" feature at the beginning of the global script file:
Code: ags

void normalGraphic(this InventoryItem*)
{
  if (this == null) return;
  if (this.ID > Game.GetLoopCountForView(VINVENTORY)) return;
  ViewFrame *frame = Game.GetViewFrame(VINVENTORY, this.ID-1, 0);
  inventory[this.ID].Graphic = frame.Graphic;
}

void grayGraphic(this InventoryItem*)
{
  if (this == null) return;
  if (this.ID > Game.GetLoopCountForView(VINVENTORY)) return;  
  ViewFrame *frame = Game.GetViewFrame(VINVENTORY, this.ID-1, 1);
  inventory[this.ID].Graphic = frame.Graphic;
}

Ryan Timothy B

#8
First off, sorry for the missing brace on my code above, I wrote it all in the browser. :P Glad you didn't have much trouble finding out the issue.

Now for your current problem. The reason why it grays out the graphic when you deselect it and makes the graphic normal when you select it, is because that's exactly what you're telling it to do.

Snippet from your code:
Code: ags

if (player.ActiveInventory == clickedItem)
{
  player.ActiveInventory = null;
  clickedItem.grayGraphic();
  return;
}
else clickedItem.normalGraphic();


Here you're checking the Active inventory item and seeing if it matches the currently Clicked item. Then you're removing it from being the players active item, and you're then using clickedItem.grayGraphic when you should be using clickedItem.normalGraphic.

Because you want it to deselect the current inventory item if you're clicking on itself, therefor it should go back to normal graphic. And the else should be clickedItem.grayGraphic();


But anyway, overall it should look something like this (I believe):

Code: ags

function on_mouse_click(int button) 
{
  InventoryItem *clickedItem = inventory[game.inv_activated];
  
  if (IsGamePaused() == 1) 
  {
    // Game is paused, so do nothing (ie. don't allow mouse click)
  }
  else if (button == eMouseLeftInv)  // left click on inventory item
  { 
    if (mouse.Mode == eModeUseinv && player.ActiveInventory != null)
    {
      if (player.ActiveInventory == clickedItem) //deselect the inventory item when clicking on itself
      {
        player.ActiveInventory = null;
        clickedItem.normalGraphic();
        mouse.Mode = eModeTouch; // <-- Change this to whatever default for mouse.Mode should be when inv is deselected
      }
      else clickedItem.RunInteraction(mouse.Mode);  //run the inventory on inventory interaction
    }
    else if (mouse.Mode == eModeTouch)  //select inv item
    { 
      player.ActiveInventory = clickedItem;
      clickedItem.grayGraphic();
    }
    else if (mouse.Mode == eModeLookat)  //look at inv item
    { 
      clickedItem.RunInteraction(eModeLookat);
    }
  }
  else if (button == eMouseLeft) 
  {
    ProcessClick(mouse.x, mouse.y, mouse.Mode);
  }
  else // right-click, so cycle cursor 
  {   
    mouse.SelectNextMode();
  }
}


It's been quite a few months since I've last tinkered with inventory clicks and such, but I believe this will work... I think.


There's a few things I changed around. You had the if game is paused mouse clicks below your inventory. Now, if your inventory window actually does pause the game, then you did have it in the right spot and you can just simply move the pause below it. Otherwise, you don't want to be able to select inventory while the game is paused, only if your inventory window doesn't pause the game.

Your indenting and brace locations seem kind of erratic. You'll actually notice that once you start to use proper indentations, you'll actually be able to read and understand your code so much faster.


Also something I didn't mention earlier, you'll have to change the default graphic for an inventory item once you pick it up. Something like so in the on_event function:
Code: ags

function on_event (EventType event, int data)
{
  if (event == eEventAddInventory) inventory[data].normalGraphic();
}

It'll change the graphic to normal every time you pick up an inventory item. You could do it whenever you Lose an inventory item as well, but I wouldn't bother unless you needed to.
The reason you should change the graphic to normal is so that if you were using a currently selected item on something and then removed it from your inventory. If you picked it back up, it would be gray.

monkey0506

There's no need to check mouse.IsButtonDown in on_mouse_click unless you're trying to do something like a verbcoin interface or something like that. You should just use else if (button == eMouseLeft).

Regarding the indentation..you'll also find that AGS handles this automatically. You actually have to go out of your way and manually edit it to get your indentation to look like that unless you've disabled the option in the preferences.

Ryan Timothy B

Good spot Monkey. I changed it in my script above. I simply copied his code without reading it fully I guess. :P

Akril15

All right -- thank you very much for walking me through all of that, Ryan. I'm still pretty new to scripting, and explaining it like you did really helps.

Incidentally, I discovered that changing position of the IfGamePaused statement like I did in the last batch of code I posted turned out to be a Very Bad Thing -- I couldn't type words into the text field when I was saving a game. Fortunately, that's not happening anymore.

The whole interface seems to be working okay now. The only noticeable drawback is that if I select an inventory item, select the "select inventory item" button, then select another inventory item, the first inventory item stays grayed out. I think I might leave out the "gray out" option since I don't have the "use inventory item on itself" problem to deal with anymore.

Thanks a lot, everyone! I really appreciate your taking the time to help.

SMF spam blocked by CleanTalk