Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: Scavenger on Tue 19/11/2013 12:53:49

Title: Putting Active Inventory Back into Inventory Problems (SOLVED)
Post by: Scavenger on Tue 19/11/2013 12:53:49
Okay, so I'm trying to emulate the Discworld inventory system using AGS, and it's running pretty smoothly. I have double clicks, inventory items being takeable from the inventory, putting down items and picking them up again. But there's one thing, in my awful, awful code that I cannot get it to do for some reason, and it's been bugging me for the entire time I've been working on the game.

And that is, when I click on a space in the inventory occupied by the active inventory (the inventory slot while you have it active is blanked out, making it look empty) the item gets put down and immediately picked back up again, and I'm not sure why this is happening. I can't give the entire project, as it's so close to completion now, but here's the click code I'm using:

Code (AGS) Select

int mx, my, lastclick;
int clickdelay = 15;
bool itemvisible;
bool inventoryvisible;
int clicktype;
int clickbuffer;
InventoryItem* itemclicked;
int itemtimeout;

function left_click(bool single, ClickType type)
{
  if (single && type == eClickField)
  {
    // single-click on field code. not important to the example.
  }
  else if (!single && type == eClickField)
  {   
    // double-click on field code. Also not important
  }
  else if (single && type == eClickInventory && clickbuffer == 0) //Single Click on inventory.
    {
      bool newitem = false;
      InventoryItem* item = itemclicked;
      if (player.ActiveInventory != null)
          {
            if (item != player.ActiveInventory) newitem = true;
            ReturnInventory ();
            if (newitem)
                {
                  //Display ("NEWITEM");
                  player.ActiveInventory = item;
                  gItem.BackgroundGraphic = item.CursorGraphic;
                  item.Graphic = 32;
                  if (mouse.x - 16 > 0 ) gItem.X = mouse.x - 16;
                  if (mouse.y - 10 > 0 ) gItem.Y = mouse.y - 10;
                  gItem.Visible = true;
                }
            item = null;
            itemtimeout = 50; //To stop the lower conditional from being run, but doesn't work.
            return;
          }
      else if (player.ActiveInventory == null) //Should be run only when you don't have an inventory item, and have clicked on one.
          {                                        //For some reason, it's run directly after the above conditional?????
                //Display ("NO INV");
                if (itemtimeout) return; //This should stop the player from picking up the item right back up again, but it doesn't work!
                else
                    {
                      player.ActiveInventory = item;
                      gItem.BackgroundGraphic = item.CursorGraphic;
                      item.Graphic = 32;
                      if (mouse.x - 16 > 0 ) gItem.X = mouse.x - 16;
                      if (mouse.y - 10 > 0 ) gItem.Y = mouse.y - 10;
                      gItem.Visible = true;
                    }
          }   
     }
  else if (!single && type == eClickInventory && clickbuffer == 0)
    {
         if (player.ActiveInventory != null)
             {
                   if (inventory [game.inv_activated].IsInteractionAvailable(eModeUseinv))
                        {
                              inventory [game.inv_activated].RunInteraction (eModeUseinv);
                        }
         else unhandled_event (5, 3);
     }
   else
         {
            if (inventory [game.inv_activated].IsInteractionAvailable(eModeInteract))
                  {
                    inventory [game.inv_activated].RunInteraction (eModeInteract);
                  }
            else unhandled_event (5, 1);
         }
    }
}

function repeatedly_execute() {
  if (lastclick>0 && lastclick<=clickdelay) lastclick++;
  else if (lastclick>clickdelay)
    {
        lastclick=0;
        left_click(true, clicktype);
    }
 
  if (itemtimeout) itemtimeout --;
}


function on_mouse_click(MouseButton button)
{
  // called when a mouse button is clicked. button is either LEFT or RIGHT
 
    if (GUI.GetAtScreenXY (mouse.x, mouse.y) != gInventory && gInventory.Visible == true)
        {
              gInventory.Visible = false;
              inventoryvisible = false;
        }

    else if (button == eMouseLeft)
        {
               int mxdist = AbsInt(mouse.x - mx);
               int mydist = AbsInt(mouse.y - my);
   
               if (lastclick>0 && mxdist < 3 && mydist < 3)
                    {
                          lastclick=0;
                          left_click(false,  eClickField); 
                    }
                else
                    {
                          lastclick=1;
                          mx=mouse.x;
                          my=mouse.y;
                          clicktype = eClickField;
                    }
        }
    else if (button == eMouseRight)
        {
              if (player.ActiveInventory != null)
                  {
                        if (player.ActiveInventory != iWires) ReturnInventory (); //Special dispensation for game entity that isn't actually an inventory item.
                        else
                              {
                                    player.Say ("I'll just put these back.");
                                    ReturnInventory ();
                                    player.LoseInventory (iWires);
                              }
       
                  }
              else ProcessClick (mouse.x, mouse.y, eModeLookat);
        }
    else if (button == eMouseLeftInv && itemtimeout == 0)
        {
            //Display ("Clicking Inventory");
            if (GUIControl.GetAtScreenXY (mouse.x, mouse.y) == invInventory)
                {
                    int mxdist = AbsInt(mouse.x - mx);
                    int mydist = AbsInt(mouse.y - my);
   
                    if (lastclick>0 && InventoryItem.GetAtScreenXY (mouse.x, mouse.y) == itemclicked)
                        {
                            lastclick=0;
                            left_click(false, eClickInventory);
                            itemclicked = null;
                        }
                    else
                        {
                            lastclick=1;
                            mx=mouse.x;
                            my=mouse.y;
                            clicktype = eClickInventory;
                            itemclicked = InventoryItem.GetAtScreenXY (mouse.x, mouse.y);
                        }
                  }
         }
    else if (button == eMouseRightInv)
         {
               InventoryItem* itemlook = InventoryItem.GetAtScreenXY (mouse.x, mouse.y);
               itemlook.RunInteraction (eModeLookat);
         }
}

function ReturnInventory ()
{
  aDrop.Play (eAudioPriorityHigh, eOnce);
  InventoryItem* item;
  item = player.ActiveInventory;
  item.Graphic = gItem.BackgroundGraphic;
  gItem.Visible = false;
  itemvisible = false;
  player.ActiveInventory = null;
}


Again, everything BUT putting items back in their own slot works fine. If you try and put the item back in it's own slot, it just pops back onto the mouse again, or it stays in the inventory randomly if you double click or quickly move the mouse away.

The behaviour I want is this: When you single click on an item when you have an active inventory item, it swaps that item with the one you're holding. If you click on an empty space OR the space where the Active Inventory actually is (and invisible), it returns your active inventory to the inventory window and leaves you with nothing.

The current behaviour is this: When you single click on an item when you have an active inventory item, it swaps that item with the one you're holding. If you click on an empty space, it returns your inventory. If you click on the space your item is in normally, it puts the item down and immediately swaps it with itself, leaving you with the exact same item in hand.

There's got to be something simple I'm just blind to.

I have no idea what I'm doing wrong. I'm like 3 days from releasing this game and this bug is being nigh impossible to crush!

Thankyou for any help at all.
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Khris on Tue 19/11/2013 13:36:38
Could you clean up the indentation?
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Scavenger on Tue 19/11/2013 13:48:49
Like that? I edited it.
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Khris on Tue 19/11/2013 14:17:09
Maybe I'm missing something, but:
In on_mouse_click / eMouseLeftInv, you're calling left_click(false, eClickInventory);
Which means inside left_click(), single is false.

Yet the code you say doesn't work is inside if (single && ...) {}, so it looks like the game will run the block starting at line 57 instead?
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Scavenger on Tue 19/11/2013 14:31:54
That's the double click part of the inventory interaction. Basically, the code does this:

1) Player clicks the mouse.
2) Game sets a timer, which when it ends, the single click code will run.
While the timer is going on:
3a) The player clicks the mouse again.
4a) Game sees that the timer is going on, and runs the double click action.
or
3b) Player does not click the mouse.
4b) Timer runs out and runs the single click code from repeatedly_execute.

It works in all cases but the player single clicking the Active Inventory slot.
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Lupo4u2 on Tue 19/11/2013 14:52:13

Code (AGS) Select

//you set:
player.ActiveInventory = item;
//...
item = null;


and afterwards you check for:

Code (AGS) Select
if (player.ActiveInventory == null)

maybe that's the problem, because at that time ActiveInventory == null?
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Scavenger on Tue 19/11/2013 15:05:37
No, that can't be it, as you lose your ActiveInventory in a seperate conditional branch to the "pick item up" branch.


Code (AGS) Select

      if (player.ActiveInventory != null)
          {
                //player's Active Inventory is removed in this branch.
          }
      else if (player.ActiveInventory == null)
         {
                //Player's active inventory is updated in this branch.
         }


Somehow, both branches are being run one after the other, on the same click. I checked it with the commented out Displays, when you click on the same inventory item that you're carrying, it sets the item down and then runs the lower branch immediately afterward. Is this just a bug in the repeatedly_execute code?
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Lupo4u2 on Tue 19/11/2013 17:07:31
one guess what you can try is to put the else branch in { } and add the if statement inside it.
Maybe its a compiler issue with the if else, as i know that sometimes unexpected things like what you describe can happen.
(even i could not reproduce such misbehaviour with a if/else if test here)

try
Code (AGS) Select
if (player.ActiveInventory != null)
          {
                //player's Active Inventory is removed in this branch.
          }
      else
          {   
            if (player.ActiveInventory == null)
             {
                //Player's active inventory is updated in this branch.
             }
          }

does it fix the problem?
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Khris on Tue 19/11/2013 17:39:15
I was also thinking about this and did a quick check but couldn't reproduce it. On the other hand, the first branch calls a custom function, which might screw up the if-else-handling? This would be a pretty major bug though, and I'm sure somebody would have noticed by now.

I suspect the reason is that somehow repeatedly_execute calls left_click() twice in a row, even though lastclick is reset.
Title: Re: Putting Active Inventory Back into Inventory Problems
Post by: Scavenger on Tue 19/11/2013 18:09:25
I had this same problem with my other game - where functions called in repeatedly_execute would continue to be called even though there's no reason for it.

I'm loathed to do it, but I could package up my game project files and PM you a link so you can see the code in action. You may be able to figure out whether it's just my poor coding or an actual bug. (it's most likely my poor coding).

EDIT: Okay, fixed it, it was an unrelated on_event call I hadn't checked properly. I was using it to put items down in empty slots. Everything works as planned now.
Title: Re: Putting Active Inventory Back into Inventory Problems (SOLVED)
Post by: monkey0506 on Wed 20/11/2013 00:21:38
Just from looking at the code you'd posted, I'd figured you must be calling left_click from somewhere else. A bug that serious in rep_ex and/or conditional statements would never have survived this long.

Glad you got it sorted though! :)