Placing items and fixing their look in the inventory

As we’ve seen in the previous lesson, we have a working inventory. We can move stacks of items around, split them, and swap them with one another. But:

In this lesson, we’ll address these issues.

Fixing the blueprints’ appearance

The BlueprintEntity previews don’t fit in the InventoryPanel because we sized them to fit cells in the game world, not in the inventory.

We need to add a way to switch preview for the blueprints between the inventory and the world.

Whenever we move the blueprint in the inventory, it should turn into a small icon, changing its size and position offset. When we place it over the world’s grid, it should look the way it is now.

To do so, we’ll offset the node’s position and change its size on the fly. Another valid solution would be to have two previews for the blueprint and show and hide them as the mouse cursor hovers or leaves the inventory. Either would work.

It would also be nice to have a spot in the project where we can set the inventory slots’ size for any script to access it without requiring a reference to InventoryPanel.

We can use the project settings to store values that are accessible project-wide. Godot’s settings window not only allows you to update predefined options, but you can also add any settings you’d like and access them in code.

Open the Project Settings… window. At the top of the editor, enter game_gui in Category, inventory_size in Property, set the Type to float and click Add. This will create a setting accessible under the path game_gui/inventory_size in our code. We can access it from any script, anytime.

The window should jump to your new setting. I set the size to 75 pixels.

Now, we can use this new setting to size blueprints, panels, and fix the label. Open BlueprintEntity.gd and add the following functions.

## Sets the position and scale of the blueprint to fit inventory panels and hides
## world-specific sprites like the power direction indicators.
func display_as_inventory_icon() -> void:
    # We can retrieve the panel size from the project settings by calling `ProjectSettings.get_setting()`
    # It takes a path to the property as its argument, which works like node property paths.
    var panel_size: float = ProjectSettings.get_setting("game_gui/inventory_size")
    
    # Set the position. Horizontally, it's halfway across. Vertically, 
    # we move the graphics and collision so that the machine's origin is on the
    # tile's floor. With our isometric graphic style, this corresponds to 75% of the height.
    position = Vector2(panel_size * 0.5, panel_size * 0.75)
    
    # The sprites for blueprints are 100x100, so the scale is the desired size divided by 100.
    scale = Vector2(panel_size / 100.0, panel_size / 100.0)
    
    # We modulate the blueprint when it's in an invalid location, but we don't want it to stay 
    # that color in the inventory. So we reset the modulate color to white.
    modulate = Color.white

    # In the inventory, we need to hide the power indicator if this node has one.
    if _power_direction:
        _power_direction.hide()


## This function resets the blueprint's scale, position offset, and power indicators to display 
## in the game world.
func display_as_world_entity() -> void:
    scale = Vector2.ONE
    position = Vector2.ZERO
    if _power_direction:
        _power_direction.show()

We need a place to call this mode change for the inventory. We do our inventory management inside of InventoryPanel.gd, so open that script. In the held_item setter, _set_held_item(), we call display_as_inventory_icon() on the held_item when we add it as a child.

func _set_held_item(value: BlueprintEntity) -> void:
    #...

    if held_item:
        #...
        held_item.display_as_inventory_icon()

This looks much better.

Moving the stack count label

The label for the stack size is in the top left in a default position. It looks fine in the inventory, but it ends up under the mouse cursor when we pick up an item. You can still read it, but it’s still a little thing we can fix.

Go to InventoryPanel.tscn and select the Label. In the Inspector, set its Align property to Right. That way, if we make the label bounding box bigger, the text will grow in from the right. Also, apply the Layout -> Top Right option to it and offset the label from the parent’s bounding box a bit. This will anchor it in the panel’s top-right corner.

Go to the InventoryPanel.gd script. We can use the game_gui/inventory_size setting to force the panel size to follow our global setting. We’ll also use it for the mouse’s preview.

func _ready() -> void:
    var panel_size: float = ProjectSettings.get_setting("game_gui/inventory_size")

    # Force the panel's size and min size to match the project setting.
    rect_min_size = Vector2(panel_size, panel_size)
    rect_size = rect_min_size

To fix it for the mouse’s inventory, go to DragPreview.tscn and select the Label. Set its Align property to Right, and apply Layout -> Top Right to the node.

Then, open DragPreview.gd and repeat what we’ve done in the InventoryPanel.gd’s _ready() function.

# func _ready() -> void:
    #...
    
    var panel_size: float = ProjectSettings.get_setting("game_gui/inventory_size")
    rect_min_size = Vector2(panel_size, panel_size)
    rect_size = rect_min_size

You can now make the inventory as big or as small as you like by changing the project settings.

And the label is no longer obscured by the mouse.

Placing inventory entities

At the moment, we generate blueprints and store them in the EntityPlacer with keyboard shortcuts.

But we now have a working inventory, so we should get rid of the temporary code that’s in EntityPlacer and replace the placer’s blueprint variable with the mouse’s inventory.

Head to EntityPlacer.gd, and let’s start with some spring cleaning. Get rid of the elif event.is_action_pressed("quickbar_...") blocks in _unhandled_input(). We don’t need them anymore.

To access the mouse’s inventory, we need the GUI node since it’s the one that has a getter for DragPreview. Add a new _gui: Control variable, and inside of setup(), add a new parameter for the GUI to store it there.

var _gui: Control


func setup(
    # Don't forget to add the `gui` argument!
    gui: Control,
    #...
) -> void:
    _gui = gui
    #...

In turn, we need to get the GUI node in Simulation.gd and to pass it along to EntityPlacer.

onready var _gui := $CanvasLayer/GUI

func _ready() -> void:
    #$Timer.start(simulation_speed)
    _entity_placer.setup(_gui, _tracker, _ground, _flat_entities, _player)
    #...

Now, in EntityPlacer.gd, wherever we refer to the _blueprint variable, we can replace it with _gui.blueprint and remove the temporary _blueprint variable.

Remove the line that says var _blueprint: BlueprintEntity at the top of the script and press Ctrl+R to open the script editor’s search and replace function.

In the first field, type in _blueprint, in the second _gui.blueprint. Make sure to turn on the Whole Words option on the right so the tool doesn’t replace variable names like has_placeable_blueprint!

Click Replace All to fix all the errors.

Now, if you run the game, you’ll notice something is wrong. The blueprint isn’t the right size, and it’s floating some distance away from the mouse.

We can fix the size by calling display_as_world_entity() on the blueprint. But what about this strange position offset? The reason this is happening is because of the CanvasLayer node we added for the user interface.

When the blueprint was a child of EntityPlacer, its global position took the camera into account. But under a separate CanvasLayer, there is no camera to influence it. We need to take the camera-modified position and transform it back into coordinates relative to the viewport.

Thankfully, we can get the current viewport’s Transform2D to do that using get_viewport_transform(), and use Transform2D’s xform() function. The xform() function transforms coordinates using this transform matrix, converting them from the CanvasLayer’s coordinate system to the root viewport’s.

At the start of EntityPlacer._move_blueprint_in_world(), replace the line where we set the blueprint’s global position with the following.

func _move_blueprint_in_world(cellv: Vector2) -> void:
    # Set the blueprint's position and scale back to origin
    _gui.blueprint.display_as_world_entity()
    
    # Snap the blueprint's position to the mouse with an offset, transformed into
    # viewport coordinates using `Transform2D.xform()`.
    _gui.blueprint.global_position = get_viewport_transform().xform(
        map_to_world(cellv) + POSITION_OFFSET
    )
    #...

You can now grab one of your items and place it in the world, so long as you move the cursor outside the inventory window.

That brings us to three more problems:

  1. If you place batteries, their stack count doesn’t go down, so you can keep adding them to infinity. When placing an entity, we should reduce the blueprint’s stack count and destroy it when we run out.
  2. The inventory window is in the way, as I said. We can provide a keyboard key to toggle its visibility.
  3. And the blueprint still snaps to the grid when we move around while inside the inventory window.

It feels like we’re playing whack-a-mole, doesn’t it? This is typical of UI programming, where we have to tackle many details.

Reducing stack count

Like we did in the InventoryPanel script, when we place an entity, we need to reduce the stack count and destroy the blueprint when its number falls to 0. In EntityPlacer’s _place_entity(), let’s do that and detect the case of having none left.

Add the following lines at the bottom of the function.

func _place_entity(cellv: Vector2) -> void:
    #...
    #_tracker.place_entity(new_entity, cellv)
    
    if _gui.blueprint.stack_count == 1:
        _gui.destroy_blueprint()
    else:
        _gui.blueprint.stack_count -= 1
        _gui.update_label()

With that, when you place all the items you have in a stack, the blueprint should disappear completely.

Toggling the inventory window

Let’s add a keyboard input to toggle the inventory window’s visibility.

Go into GUI.tscn and hide the InventoryWindow node. Open up GUI.gd and add a new function for _unhandled_input().

Instead of only showing and hiding the inventory window, we create helper functions that we’ll extend later as we get into crafting and automation.

## If `true`, it means the GUI window is open.
onready var _is_open: bool = $HBoxContainer/InventoryWindow.visible


func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("toggle_inventory"):
        if _is_open:
            _close_inventories()
        else:
            _open_inventories()


## Shows the inventory window, crafting window
func _open_inventories() -> void:
    _is_open = true
    player_inventory.visible = true


## Hides the inventory window, crafting window, and any currently open machine GUI
func _close_inventories() -> void:
    _is_open = false
    player_inventory.visible = false

The functions look simple and maybe unnecessary for now. However, we’ll write more complex rules for them later: we’ll need to open crafting menus and close machine GUIs on top of the main inventory. So they’ll get bigger.

At least, now, you can press E to toggle the inventory’s visibility.

Preventing grid snap behind the inventory window

We want to address one last issue here: the blueprint snaps to the world grid even when our mouse cursor is over the inventory window.

This is because of the _process() function in EntityPlacer.gd. Every frame, whether it’s in inventory or not, we force the blueprint to update its position and snap to the grid.

We do this to keep the blueprint snapped to the grid even when the camera moves.

To fix this, we need a way of detecting when the mouse is over the inventory window. In GUI.gd, we need to get the parent that holds the inventory window. In _process(), we can do a bit of checking to test for if the mouse is currently in the GUI.

## If `true`, it means the mouse is over the `GUI` at the moment.
var mouse_in_gui := false

#...
## The parent container that holds the inventory window
onready var _gui_rect := $HBoxContainer


func _process(delta: float) -> void:
    var mouse_position := get_global_mouse_position()
    # if the mouse is inside the GUI rect and the GUI is open, set it true.
    mouse_in_gui = _is_open and _gui_rect.get_rect().has_point(mouse_position)

And back in EntityPlacer.gd’s _process() function, we can add an extra check for this new boolean that’s part of GUI.

func _process(_delta: float) -> void:
    #var has_placeable_blueprint: bool = _gui.blueprint and _gui.blueprint.placeable
    if has_placeable_blueprint and not _gui.mouse_in_gui:
        #_move_blueprint_in_world(world_to_map(get_global_mouse_position()))

And now, the blueprint will move fluidly when hovering the mouse, and snap to the grid when the mouse is over the game’s tilemap.

The last thing to fix is that the blueprint still looks like a world entity if it goes outside the window even once. We can fix that inside of DragPreview.gd’s _input() function. When it detects mouse movement, we can ensure the blueprint is an inventory icon and not a world entity.

func _input(event: InputEvent) -> void:
    if event is InputEventMouseMotion:
        if blueprint:
            blueprint.display_as_inventory_icon()
        #rect_global_position = event.global_position

Now, it should look correct in all cases.

Code reference

Here are the complete scripts we modified in this lesson.

BlueprintEntity.gd

class_name BlueprintEntity
extends Node2D

export var placeable := true
export var stack_size := 1

var stack_count := 1

onready var _power_direction := find_node("PowerDirection")


func rotate_blueprint() -> void:
    if not _power_direction:
        return

    var directions: int = _power_direction.output_directions
    var new_directions := 0

    if directions & Types.Direction.LEFT != 0:
        new_directions |= Types.Direction.UP

    if directions & Types.Direction.UP != 0:
        new_directions |= Types.Direction.RIGHT

    if directions & Types.Direction.RIGHT != 0:
        new_directions |= Types.Direction.DOWN

    if directions & Types.Direction.DOWN != 0:
        new_directions |= Types.Direction.LEFT

    _power_direction.output_directions = new_directions


func display_as_inventory_icon() -> void:
    var panel_size: float = ProjectSettings.get_setting("game_gui/inventory_size")
    
    position = Vector2(panel_size * 0.5, panel_size * 0.75)
    
    scale = Vector2(panel_size / 100.0, panel_size / 100.0)
    
    modulate = Color.white

    if _power_direction:
        _power_direction.hide()


func display_as_world_entity() -> void:
    scale = Vector2.ONE
    position = Vector2.ZERO
    if _power_direction:
        _power_direction.show()

InventoryPanel.gd

class_name InventoryPanel
extends Panel

signal held_item_changed(panel, item)

var held_item: BlueprintEntity setget _set_held_item

var gui: Control

onready var count_label := $Label


func _ready() -> void:
    var panel_size: float = ProjectSettings.get_setting("game_gui/inventory_size")

    # Force the panel's size and min size to match the project setting and apply
    # the same size to the label.
    rect_min_size = Vector2(panel_size, panel_size)
    rect_size = rect_min_size


func _gui_input(event: InputEvent) -> void:
    var left_click := event.is_action_pressed("left_click")
    var right_click := event.is_action_pressed("right_click")

    if not (left_click or right_click):
        return

    if gui.blueprint:
        var blueprint_name := Library.get_entity_name_from(gui.blueprint)

        if held_item:
            var held_item_name := Library.get_entity_name_from(held_item)
            var item_is_same_type: bool = held_item_name == blueprint_name

            var stack_has_space: bool = held_item.stack_count < held_item.stack_size
            if item_is_same_type and stack_has_space:
                if left_click:
                    _stack_items()
                elif right_click:
                    _stack_items(true)
            else:
                if left_click:
                    _swap_items()
        else:
            if left_click:
                _grab_item()

            elif right_click:
                if gui.blueprint.stack_count > 1:
                    _grab_split_items()
                else:
                    _grab_item()
    elif held_item:
        if left_click:
            _release_item()
        elif right_click:
            if held_item.stack_count == 1:
                _release_item()
            else:
                _split_items()


func setup(_gui: Control) -> void:
    gui = _gui


func _set_held_item(value: BlueprintEntity) -> void:
    if held_item and held_item.get_parent() == self:
        remove_child(held_item)

    held_item = value

    if held_item:
        add_child(held_item)
        move_child(held_item, 0)
        held_item.display_as_inventory_icon()

    _update_label()
    emit_signal("held_item_changed", self, held_item)


func _update_label() -> void:
    var can_be_stacked := held_item and held_item.stack_size > 1

    if can_be_stacked:
        count_label.text = str(held_item.stack_count)
        count_label.show()
    else:
        count_label.text = str(1)
        count_label.hide()


func _stack_items(split := false) -> void:
    var count := int(
        min(
            gui.blueprint.stack_count / (2 if split else 1),
            held_item.stack_size - held_item.stack_count
        )
    )

    if split:
        gui.blueprint.stack_count -= count
        gui.update_label()
    else:
        if count < gui.blueprint.stack_count:
            gui.blueprint.stack_count -= count
            gui.update_label()
        else:
            gui.destroy_blueprint()

    held_item.stack_count += count
    _update_label()


func _swap_items() -> void:
    var item: BlueprintEntity = gui.blueprint
    gui.blueprint = null

    var current_item := held_item

    self.held_item = item
    gui.blueprint = current_item


func _grab_item() -> void:
    var item: BlueprintEntity = gui.blueprint

    gui.blueprint = null
    self.held_item = item


func _release_item() -> void:
    var item := held_item

    self.held_item = null
    gui.blueprint = item


func _split_items() -> void:
    var count := int(held_item.stack_count / 2.0)

    var new_stack := held_item.duplicate()
    new_stack.stack_count = count
    held_item.stack_count -= count

    gui.blueprint = new_stack
    _update_label()


func _grab_split_items() -> void:
    var count := int(gui.blueprint.stack_count / 2.0)

    var new_stack: BlueprintEntity = gui.blueprint.duplicate()
    new_stack.stack_count = count

    gui.blueprint.stack_count -= count
    gui.update_label()

    self.held_item = new_stack

DragPreview.gd

extends Control

var blueprint: BlueprintEntity setget _set_blueprint

onready var count_label := $Label


func _ready() -> void:
    set_as_toplevel(true)

    var panel_size: float = ProjectSettings.get_setting("game_gui/inventory_size")
    rect_min_size = Vector2(panel_size, panel_size)
    rect_size = rect_min_size


func _input(event: InputEvent) -> void:
    if event is InputEventMouseMotion:
        if blueprint:
            blueprint.display_as_inventory_icon()
        rect_global_position = event.global_position


func update_label() -> void:
    if blueprint and blueprint.stack_size > 1:
        count_label.text = str(blueprint.stack_count)
        count_label.show()
    else:
        count_label.hide()


func destroy_blueprint() -> void:
    if blueprint:
        remove_child(blueprint)
        blueprint.queue_free()
        blueprint = null
        update_label()


func _set_blueprint(value: BlueprintEntity) -> void:
    if blueprint and blueprint.get_parent() == self:
        remove_child(blueprint)

    blueprint = value
    if blueprint:
        add_child(blueprint)
        move_child(blueprint, 0)

    update_label()

EntityPlacer.gd

extends TileMap

const MAXIMUM_WORK_DISTANCE := 275.0
const POSITION_OFFSET := Vector2(0,25)
const DECONSTRUCT_TIME := 0.3

var _gui: Control
var _tracker: EntityTracker
var _ground: TileMap
var _flat_entities: Node2D
var _player: KinematicBody2D
var _current_deconstruct_location := Vector2.ZERO

onready var _deconstruct_timer := $Timer


func _unhandled_input(event: InputEvent) -> void:
    var global_mouse_position := get_global_mouse_position()

    var has_placeable_blueprint: bool = _gui.blueprint and _gui.blueprint.placeable
    var is_close_to_player := (
        global_mouse_position.distance_to(_player.global_position)
        < MAXIMUM_WORK_DISTANCE
    )

    var cellv := world_to_map(global_mouse_position)
    var cell_is_occupied := _tracker.is_cell_occupied(cellv)
    var is_on_ground := _ground.get_cellv(cellv) == 0

    if event is InputEventMouseButton:
        _abort_deconstruct()

    if event.is_action_pressed("left_click"):
        if has_placeable_blueprint:
            if not cell_is_occupied and is_close_to_player and is_on_ground:
                _place_entity(cellv)
                _update_neighboring_flat_entities(cellv)

    elif event.is_action_pressed("right_click") and not has_placeable_blueprint:
        if cell_is_occupied and is_close_to_player:
            _deconstruct(global_mouse_position, cellv)

    elif event is InputEventMouseMotion:
        if cellv != _current_deconstruct_location:
            _abort_deconstruct()
        if has_placeable_blueprint:
            _move_blueprint_in_world(cellv)

    elif event.is_action_pressed("drop") and _gui.blueprint:
        remove_child(_gui.blueprint)
        _gui.blueprint = null

    elif event.is_action_pressed("rotate_blueprint") and _gui.blueprint:
        _gui.blueprint.rotate_blueprint()


func _process(_delta: float) -> void:
    var has_placeable_blueprint: bool = _gui.blueprint and _gui.blueprint.placeable
    if has_placeable_blueprint and not _gui.mouse_in_gui:
        _move_blueprint_in_world(world_to_map(get_global_mouse_position()))


func setup(gui: Control, tracker: EntityTracker, ground: TileMap, flat_entities: YSort, player: KinematicBody2D) -> void:
    _gui = gui
    _tracker = tracker
    _ground = ground
    _player = player
    _flat_entities = flat_entities

    for child in get_children():
        if child is Entity:
            var map_position := world_to_map(child.global_position)
            _tracker.place_entity(child, map_position)


func _move_blueprint_in_world(cellv: Vector2) -> void:
    _gui.blueprint.display_as_world_entity()
    _gui.blueprint.global_position = get_viewport_transform().xform(
        map_to_world(cellv) + POSITION_OFFSET
    )

    var is_close_to_player := (
        get_global_mouse_position().distance_to(_player.global_position)
        < MAXIMUM_WORK_DISTANCE
    )
    var is_on_ground: bool = _ground.get_cellv(cellv) == 0
    var cell_is_occupied := _tracker.is_cell_occupied(cellv)

    if not cell_is_occupied and is_close_to_player and is_on_ground:
        _gui.blueprint.modulate = Color.white
    else:
        _gui.blueprint.modulate = Color.red

    if _gui.blueprint is WireBlueprint:
        WireBlueprint.set_sprite_for_direction(_gui.blueprint.sprite, _get_powered_neighbors(cellv))


func _place_entity(cellv: Vector2) -> void:
    var entity_name := Library.get_entity_name_from(_gui.blueprint)
    var new_entity: Node2D = Library.entities[entity_name].instance()

    if _gui.blueprint is WireBlueprint:
        var directions := _get_powered_neighbors(cellv)
        _flat_entities.add_child(new_entity)
        WireBlueprint.set_sprite_for_direction(new_entity.sprite, directions)
    else:
        add_child(new_entity)

    new_entity.global_position = map_to_world(cellv) + POSITION_OFFSET
    new_entity._setup(_gui.blueprint)
    _tracker.place_entity(new_entity, cellv)

    if _gui.blueprint.stack_count == 1:
        _gui.destroy_blueprint()
    else:
        _gui.blueprint.stack_count -= 1
        _gui.update_label()


func _deconstruct(event_position: Vector2, cellv: Vector2) -> void:
    _deconstruct_timer.connect(
        "timeout", self, "_finish_deconstruct", [cellv], CONNECT_ONESHOT
    )
    _deconstruct_timer.start(DECONSTRUCT_TIME)
    _current_deconstruct_location = cellv


func _finish_deconstruct(cellv: Vector2) -> void:
    var entity := _tracker.get_entity_at(cellv)
    _tracker.remove_entity(cellv)
    _update_neighboring_flat_entities(cellv)


func _abort_deconstruct() -> void:
    if _deconstruct_timer.is_connected("timeout", self, "_finish_deconstruct"):
        _deconstruct_timer.disconnect("timeout", self, "_finish_deconstruct")
    _deconstruct_timer.stop()


func _get_powered_neighbors(cellv: Vector2) -> int:
    var direction := 0

    for neighbor in Types.NEIGHBORS.keys():
        var key: Vector2 = cellv + Types.NEIGHBORS[neighbor]

        if _tracker.is_cell_occupied(key):
            var entity: Node = _tracker.get_entity_at(key)
            if (
                entity.is_in_group(Types.POWER_MOVERS)
                or entity.is_in_group(Types.POWER_RECEIVERS)
                or entity.is_in_group(Types.POWER_SOURCES)
            ):
                direction |= neighbor

    return direction


func _update_neighboring_flat_entities(cellv: Vector2) -> void:
    for neighbor in Types.NEIGHBORS.keys():
        var key: Vector2 = cellv + Types.NEIGHBORS[neighbor]
        var object = _tracker.get_entity_at(key)

        if object and object is WireEntity:
            var tile_directions := _get_powered_neighbors(key)
            WireBlueprint.set_sprite_for_direction(object.sprite, tile_directions)

Simulation.gd

extends Node

const BARRIER_ID := 1
const INVISIBLE_BARRIER_ID := 2

var _tracker := EntityTracker.new()

export var simulation_speed := 1.0 / 30.0

onready var _ground := $GameWorld/GroundTiles
onready var _entity_placer := $GameWorld/YSort/EntityPlacer
onready var _player := $GameWorld/YSort/Player
onready var _flat_entities := $GameWorld/FlatEntities
onready var _power_system := PowerSystem.new()
onready var _gui := $CanvasLayer/GUI


func _ready() -> void:
    $Timer.start(simulation_speed)
    _entity_placer.setup(_gui, _tracker, _ground, _flat_entities, _player)
    var barriers: Array = _ground.get_used_cells_by_id(BARRIER_ID)

    for cellv in barriers:
        _ground.set_cellv(cellv, INVISIBLE_BARRIER_ID)


func _on_Timer_timeout() -> void:
    Events.emit_signal("systems_ticked", simulation_speed)

GUI.gd

extends CenterContainer

var blueprint: BlueprintEntity setget _set_blueprint, _get_blueprint

var mouse_in_gui := false

onready var _is_open: bool = $HBoxContainer/InventoryWindow.visible

onready var player_inventory := $HBoxContainer/InventoryWindow
onready var _drag_preview := $DragPreview
onready var _gui_rect := $HBoxContainer


func _ready() -> void:
    player_inventory.setup(self)


func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("toggle_inventory"):
        if _is_open:
            _close_inventories()
        else:
            _open_inventories()


func _process(delta: float) -> void:
    var mouse_position := get_global_mouse_position()
    mouse_in_gui = _is_open and _gui_rect.get_rect().has_point(mouse_position)


func destroy_blueprint() -> void:
    _drag_preview.destroy_blueprint()


func update_label() -> void:
    _drag_preview.update_label()


func _set_blueprint(value: BlueprintEntity) -> void:
    if not is_inside_tree():
        yield(self, "ready")
    _drag_preview.blueprint = value


func _get_blueprint() -> BlueprintEntity:
    return _drag_preview.blueprint


func _open_inventories() -> void:
    _is_open = true
    player_inventory.visible = true


func _close_inventories() -> void:
    _is_open = false
    player_inventory.visible = false