Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Very rough Inventory Menu prototype #355

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft

Very rough Inventory Menu prototype #355

wants to merge 5 commits into from

Conversation

dyc3
Copy link
Collaborator

@dyc3 dyc3 commented Jun 6, 2023

Description

  • refactor handle_click_slot to primarily emit ClickSlot events, and handle those in a new system
  • fix cursor item updates not being sent if an inventory is open
  • very rough inventory menu behavior
  • only allow ClickMode::Click
  • emit MenuClickEvents for inventory menu clicks

related: #307

This PR makes it possible to do inventory menus. I don't particularly like the approach so far, and it doesn't quite cover all the use cases. I think I'll need to do some refactoring of how inventories work before continuing work on this.

Playground
use valence::client::despawn_disconnected_clients;
use valence::client::hand_swing::HandSwingEvent;
use valence::inventory::menu::{InventoryMenu, InventoryMenuPlugin, MenuClickEvent, OpenMenu};
use valence::network::ConnectionMode;
use valence::prelude::*;

#[allow(unused_imports)]
use crate::extras::*;

const SPAWN_Y: i32 = 64;

pub fn build_app(app: &mut App) {
    app.insert_resource(NetworkSettings {
        connection_mode: ConnectionMode::Offline,
        ..Default::default()
    })
    .add_plugins(DefaultPlugins)
    .add_startup_system(setup)
    .add_system(init_clients)
    .add_system(despawn_disconnected_clients)
    .add_system(toggle_gamemode_on_sneak.in_schedule(EventLoopSchedule))
    .add_plugin(InventoryMenuPlugin::default())
    .add_system(open_menu.in_schedule(EventLoopSchedule))
    .add_system(handle_menu.in_schedule(EventLoopSchedule));
}

fn setup(
    mut commands: Commands,
    server: Res<Server>,
    biomes: Query<&Biome>,
    dimensions: Query<&DimensionType>,
) {
    let mut instance = Instance::new(ident!("overworld"), &dimensions, &biomes, &server);

    for z in -5..5 {
        for x in -5..5 {
            instance.insert_chunk([x, z], Chunk::default());
        }
    }

    for z in -25..25 {
        for x in -25..25 {
            instance.set_block([x, SPAWN_Y, z], BlockState::GRASS_BLOCK);
        }
    }

    commands.spawn(instance);
}

fn init_clients(
    mut clients: Query<(&mut Location, &mut Position), Added<Client>>,
    instances: Query<Entity, With<Instance>>,
) {
    for (mut loc, mut pos) in &mut clients {
        loc.0 = instances.single();
        pos.set([0.5, SPAWN_Y as f64 + 1.0, 0.5]);
    }
}

// Add more systems here!

fn open_menu(mut events: EventReader<HandSwingEvent>, mut commands: Commands) {
    for event in events.iter() {
        println!("HandSwingEvent: {:?}", event);
        // let (client) = clients.get(event.client);
        if event.hand == Hand::Main {
            println!("opening menu");
            let mut inv = Inventory::new(InventoryKind::Generic3x3);
            inv.set_slot(3, Some(ItemStack::new(ItemKind::RedWool, 1, None)));
            inv.set_slot(5, Some(ItemStack::new(ItemKind::BlueWool, 1, None)));

            let menu = commands.spawn((inv, InventoryMenu {})).id();
            commands
                .entity(event.client)
                .insert((OpenInventory::new(menu), OpenMenu {}));
        }
    }
}

fn handle_menu(
    mut menu_clicks: EventReader<MenuClickEvent>,
    mut clients: Query<(&mut Client, &OpenInventory), With<OpenMenu>>,
    mut commands: Commands,
) {
    for click in menu_clicks.iter() {
        println!("MenuClickEvent: {:?}", click);

        let (mut client, open_inv) = clients.get_mut(click.client).unwrap();

        if click.slot_id == 3 {
            client.send_message("You clicked the red wool!");
        } else if click.slot_id == 5 {
            client.send_message("You clicked the blue wool!");
        } else {
            client.send_message("You clicked something else!");
        }

        commands.entity(click.client).remove::<OpenMenu>();
        commands.entity(click.client).remove::<OpenInventory>();
        commands.entity(open_inv.entity).despawn();
    }
}

@rj00a
Copy link
Member

rj00a commented Jun 7, 2023

I still believe it should be possible to implement menus from userspace without using raw packets. If users have the ability to reliably undo inventory actions (which they should), then it follows that menus are doable. We should then move this menu plugin to a separate crate (valence_ui?).

Regarding refactoring,

  • We need to clean up the inventory events a bit. See this for reference.
    • append Event to the names
    • add an Entity handle of the inventory being interacted with (?)
    • remove details like state_id
    • abstraction for buttons
    • combine creative inventory actions with non-creative actions (?)
    • maybe more...
  • Make the held item slot a separate modifiable component with change detection. (Use an enum for this?, rename to "selected hotbar slot" to avoid confusion?)
  • Tweak tests so we can make things actually private instead of #[doc(hidden)].

rj00a pushed a commit that referenced this pull request Jun 7, 2023
## Description

This moves the `held_item_slot` field in `ClientInventoryState` to a new
component: `HeldItem`

related: pr #355
@maxomatic458 maxomatic458 mentioned this pull request Oct 8, 2024
dyc3 added a commit that referenced this pull request Oct 11, 2024
# Objective
- ability to make inventories read only (player should be able to click
things in the inventory and still emit a click event, this can be useful
for creating inventory menus.
- closes  #427
- related #307 #355 
# Solution

- adds a public ``readonly: bool`` field to the ``Inventory`` component,
that will make any interactions with this item impossible (includes:
moving, shift moving, hotbar moving, dropping) if a player inventory is
readonly, then the player will also not be able to drop items (even when
not in the inventory), so the drop event will not be emitted (this could
be changed if requested)
- when implementing this i discovered a bug where a player is not able
to put a item from a open inventory in the offhand (by hitting F) that
will cause a desync. On the client the item will be in the offhand, but
if you try to interact with that it dissapears. (unrelated to this PR
and will not be fixed in this PR)

---------

Co-authored-by: Carson McManus <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants