NiftyGUI - how to check if draggable is in the droppable?

Hello,

I have been searching for an answer for 2 days now with no results. My problem is I’d like to create a game inventory with 30 droppables in it. The general rule in games is that items should not be allowed to be “stacked” in one droppable - meaning: there should be always maximum 1 draggable in every droppable, or no draggable at all. I wanted to check somehow during drag&drop operation ( or maybe somehow by creation of DroppableDropFilter, no luck yet though) if there’s already a draggable in there.

I’d be very grateful for any tip how could I accomplish this.

Regards!

The way nifty’s drag’n drop works is simple and is not made for inventory without you having to go custom…



In nifty, when you drag and drop, all it mainly does is check if the drop location is valid. If it is, that “item” is added to whatever is already there. So in short you could end up with 10 stack of 1 item in each square. Not ideal for an inventory. But there are some tricks you can use to make it work.



In your inventory screen controller implement DroppableDropFilter. Add the following method:



[java]

public boolean accept(Droppable dropSource, Draggable draggedItem, Droppable droppedAt) {

//… your stuff goes here …

}

[/java]



This method you will use to tell nifty if the drag’n drop is accepted or not. In Disenthral, the inventory works this way.



[java]

/**

  • Tells nifty never to move inventory items on its own. We do it manually
  • by adding/removing/relocating the image icons ourselves.

    *
  • @param dropSource The source droppable.
  • @param draggedItem The dragged item draggable.
  • @param droppedAt The destination droppable.
  • @return Always false.

    */

    public boolean accept(Droppable dropSource, Draggable draggedItem, Droppable droppedAt) {

    if (invAction.isValid(Integer.valueOf(lastTwo(draggedItem.getId()))) == InventoryActionMessage.Invalid ) {

    // This is an invalid action. Simply return and do nothing. The icon

    // will be moved back where it was.

    // System.out.println("Checked for source of " + dropSource.getId() + " and draggable " + draggedItem.getId() + " at destination of " + droppedAt.getId());

    return false;

    }

    if (isFree(droppedAt.getElement().getId())) {

    // Make an image at destination.

    addImage(lastTwo(droppedAt.getId()), invAction.getFilename(lastTwo(dropSource.getId())));

    // Broadcast item move.

    invAction.moveToSlot(this, lastTwo(dropSource.getElement().getId()), lastTwo(droppedAt.getElement().getId()));

    // Delete image at source.

    deleteImageTag(dropSource);

    return false;

    } else {

    if (canAddToStack(dropSource.getElement().getId(), droppedAt.getElement().getId())) {

    // There’s enough to stack. If there’s remainder

    // it’ll be dropped into the first open slot.

    deleteImageTag(dropSource);

    // Broadcast stacking.

    invAction.moveToSlot(this, lastTwo(dropSource.getElement().getId()), lastTwo(droppedAt.getElement().getId()));

    return false;

    } else {

    // This is not stackable. Simply return and do nothing. The icon

    // will be moved back where it was.

    return false;

    }

    }

    }

    [/java]



    We always return false so nifty doesn’t stack items internally and screw our inventory.



    What we do is when something is dropped we first check if “isFree” returns true (this is a free/empty slot. If it is, we add a new icon there then delete the item from where it was dragged. Essentially we destroy the old icon and make a new one at the free location.



    If it’s not free, we check if we can stack it. The “canAddToStack” method verifies if the item is the same and if we’ve got a full stack. If that works, we do the same as above; delete the original icon but add the number of items to the stack.



    Otherwise (if not free and not stackable) we simply do nothing and tell nifty it’s not accepted.



    That way, you rely on your own inventory management method (an inventory manager maybe) that will act with the given parameters. That manager should contain an ArrayList or a HashMap or whatever you want/need.



    Hopefully that can help you.
3 Likes

Thank you very much. It was most helpful. I was able to implement something similar (though much less elegant - still needs to be tweaked a little).

There’s another problem I have regarding this implementation - I have my inventory in a window control, and it seems that draggables , which were removed and re-created (according to the example above) in it are becoming visible only on DraggableDragStartedEvent of the window or (which is obvious) when I reload the entire screen. Any clues what method should I use to refresh the window somehow?



Best regards!

I don’t know how you do it, but in Disenthral, when an item is received, that item is linked with an icon. This icon will represent the actual item in a graphical way.



The game inserts that image in the appropriate slot in the inventory window (it’s a panel). When an icon is moved or stacked I simply remove that image element from nifty’s panel.



As you can see below, that’s an inventory slot definition in the screen panel.

[xml]

<panel height=“47” width=“49” childLayout=“absolute” id=“inv01” x=“0” y=“0” >

<control id=“invDrp01” name=“droppable” width=“100%” height=“100%” childLayout=“absolute” x=“0” y=“0”>

<control id=“invDrg01” name=“draggable” childLayout=“absolute” x=“0” y=“0”>

</control>

</control>

</panel>

[/xml]



When an icon is added, this is eventually called:

[java]

private void addImage(String slot, final String filename) {

// Only add if there’s nothing there already.

if (screen.findElementByName(“i” + slot) != null) {

return;

}

Element draggable = screen.findElementByName(“invDrg” + df.format(Integer.valueOf(slot)));

ImageBuilder ib = new ImageBuilder(“i” + df.format(Integer.valueOf(slot))) {

{

filename(filename);

x(“0”);

y(“0”);

}

};

ib.build(nifty, screen, draggable);

}

[/java]



Finally. When the dragged image is deleted, this is eventually called:

[java]

/**

  • Deletes the source image element then reset the draggable back where it
  • came from, ie the source.
  • We remove the source image because we’re stacking.
  • @param source The source where the drag started at.

    */

    private void deleteImageTag(Droppable source) {

    // System.out.println("Mark image tag " + lastTwo(source.getId()) + " for removal. ");

    Element ele = screen.findElementByName(“i” + lastTwo(source.getId()));

    screen.unregisterElementId(“i” + lastTwo(source.getId()));

    ele.setId(“toDelete”);

    ele.markForRemoval();

    }

    [/java]



    The “ele.setId(“toDelete”);” is just a precaution to rename the element in case something accessed the screen before the element could be removed.
4 Likes

Great, I really appreciate it.



I was trying to operate on Draggable objects, using DraggableBuilder to build and attach draggables to panels within droppables. It is working, but somehow Droppables constructed this way end up in left upper corner of the screen first (I could see only the part of text of the draggable) and after clicking the window header (but not the window body) they were automatically moved to droppables…



[java]

Element inventoryPanel = screen.findElementByName(elementName) ;

builder.build(nifty, screen, inventoryPanel);

[/java]

where builder is my DraggableBuilder object and inventoryPanel is a panel within a droppable. Nothing fancy, but it behaves strangely (probably due to my ignorance, but I am still learning :slight_smile: ).



The thing is a draggable has text and backgroundImage attributes, allowing me to create nice icons with overlaying text.(e.g. quantity of the items in one draggable). I could not implement the same thing with your approach, Madjack - image is completely overlaying any text.

But as I said, I really appreciate your help!

Cheers!

@gmork6 said:
...end up in left upper corner of the screen first


Use childLayout="absolute". If you don't you'll get weird results, like relocation to 0, 0 of the root screen/layer.


The thing is a draggable has text and backgroundImage attributes, allowing me to create nice icons with overlaying text.(e.g. quantity of the items in one draggable). I could not implement the same thing with your approach, Madjack - image is completely overlaying any text.


That result depends on a lot of factors. It depends on how you put that text and the overlay. Also, make sure as I said above, that your inventory slots are set to absolute and fix the appropriate x and y location (this would usually be 0, 0 in each panel of those draggable; this depends on your slot size and your icons sizes. Going from memory here).

If I remember right there's an overlay "effect" in nifty. I've never used it so I'm not sure how it works or what it does exactly, but this might be what you're looking for. As for the text, you could use the draggable's parent's coordinates and insert some bitmap text at the appropriate location.

Anyway, I think you've got all the ingredients to make that work. All you need now is some practice, reading and testing.

Good luck.

Overlay is a child layout. It puts all the components inside the panel on top of each other and filling the panel.



It sounds more like you need an absolute layout with the image and text positioned on top of each other within it. Items inside an absolute layout are always drawn first->last so add the image then the text and the text will appear on top of the image.

@zarch

There’s also an overlay effect.