Checking if a player is holding the right tool - gnembon/fabric-carpet GitHub Wiki

Checking if a player is holding the right tool

Why would you ever need this?

Probably not surprisingly, this comes in useful in many cases. If you want to change the functionality of a tool, or run a function when a player is holding a tool, you need to check whether that player is holding the tool you want or not.

How do you do that?

Glad you asked. For the sake of simplicity, let's make the appropriate tool any kind of hoe, since they don't have many more uses anyway and don't interact with most blocks in a special way in vanilla. Our final goal will be to write a private function, __holding_right_tool(player, tool), which checks if player player is holding the right tool tool in their main hand. But first let's look at how we would do that: according to the Scarpet API docs, we can retrieve information about what the player is holding by using the query(entity, 'holds', slot?) function. This function returns a list with 3 elements: [id, Count, nbt]. In this case we are not interested in how many hoes the player is holding (maximum is 1 anyway), nor about the nbt of the hoe; we just care that the player is using a hoe, so we are only interested in the first element of the list, id. To see whether player player is holding a diamond hoe in their main hand, for example, we can use query(player, 'holds', 'mainhand'):0 == 'diamond_hoe' So query(player, 'holds', 'mainhand') gives us the list with 3 elements and the :0 returns the element in the list at index 0 (the first element), i.e. the id that we are interested in. We then use == 'diamond_hoe' to check if the id of the item is 'diamond_hoe'. But we're not interested in diamond hoes, we want to know if the player is holding any kind of hoe. We can do this in a few ways; the most obvious one is probably to check if the item is equal to any of the hoes:

__holding_hoe(player) -> (
    item_in_main_hand = query(player, 'holds', 'mainhand'):0;
    item_in_main_hand == 'wooden_hoe'
 || item_in_main_hand == 'stone_hoe'
 || item_in_main_hand == 'iron_hoe'
 || item_in_main_hand == 'gold_hoe'
 || item_in_main_hand == 'diamond_hoe'
 || item_in_main_hand == 'netherite_hoe'
)

This works, but it's quite bulky and ugly. Luckily scarpet supports regex comparisons (if you don't know what that is, there are many tutorials online explaining), allowing us to test simply if the id of the item contains a string. In this case, all hoes have '_hoe' in their id, so we can use the ~ operator to check if that is found in our string:

__holding_hoe(player) -> (
    item_in_main_hand = query(player, 'holds', 'mainhand'):0;
    item_in_main_hand ~ '_hoe'
)

Much better. And it works, too. Now let's compact it even more. According to the docs, entity ~ feature is an alias for query(entity, feature). Since if slot? is not specified it defaults to 'mainhand', query(player, 'holds', 'mainhand') <=> query(player, 'holds') <=> player ~ 'holds'. To get the id of the item the player has in their main hand, therefore, we can just write (player ~ 'holds'):0, so in the end our function ends up looking like this: __holding_hoe(player) -> (player ~ 'holds'):0 ~ '_hoe' Note that even though the ~ operator is used twice here, it has different functions. This is explained in greater detail in the docs. Now the final step is to check if the player is simply holding the correct tool, not just whether they are holding a hoe. This means that if we want to check if a player is holding a pickaxe, we need to be able to check if (player ~ 'holds'):0 ~ '_pickaxe', so we need to be able to change the regex string. The easiest way to do this is by allowing the regex to be passed to the function as an argument. So our function would finally look like this: __holding_right_tool(player, tool) -> (player ~ 'holds'):0 ~ tool Pretty cool stuff. Now if we want to check if gnembon is holding a shovel, we write __holding_right_tool(player('gnembon'), '_shovel'), and the function does the job for us. If you wanna use this in the game rather than inside a carpet script (.sc file), you will first need to use /script run __holding_right_tool(player, tool) -> (player ~ 'holds'):0 ~ tool to define the function, and then use it with /script run __holding_right_tool(player('gnembon'), '_pickaxe').

What is this useful for?

The Scarpet repository has lots of examples where simpler functions are used to achieve more complex goals, so it is always nice to look through those programs. An example which uses a similar function to the our __holding_right_tool(player, tool) is the holy hand grenades script which has a __holds(entity, item_regex, enchantment) function which tests not only if the item the player is holding matches a given regex (what the funciton we created does), but also checks for enchantments:

__holds(entity, item_regex, enchantment) -> 
(
    if (entity~'gamemode_id'==3, return(0));
    for(l('mainhand','offhand'),
        holds = query(entity, 'holds', _);
        if( holds,
            l(what, count, nbt) = holds;
            if ((what ~ item_regex) && (enchs = nbt:'Enchantments[]'),
                if (type(enchs)!='list', enchs = l(enchs));
                for (enchs, 
                    if ( _:'id' == 'minecraft:'+enchantment,
                        lvl = max(lvl, _:'lvl')
                    )
                )	
            )
        )
    );
    lvl
);

In the snipet above, holds = query(entity, 'holds', _) checks both hands (unlike our function which only checks the main hand), and l(what, count, nbt) = holds assigns the values returned by the query() function to each variable; in the function we created, we only cared about what in this function is assigned to what, i.e. the item id. if (what ~ item_regex) is the final step in doing all which our function does; the rest is used to check the enchantments of the item too. This function doesn't only return a bool indicating whether the item id matches the provided regex, but instead only executes the code after if that is met. The function itself is used to determine what the max level enchantment the item has is, which is used in later parts of the code. In the holy hand grenades script, the item the player is holding was tested in order to determine whether the player is using the right item when the player triggers the __on_player_uses_item(player, item_tuple, hand) event:

__on_player_uses_item(player, item_tuple, hand) -> 
(
    if (hand != 'mainhand', return());
    power_level = __holds(player, 'fire_charge', 'power');
    if (power_level == 0, return());
    __deploy_missile(player, power_level)
);

In this case, when the event is triggered we check what the player holds using the __holds, and if the item id matches the regex 'fire_charge' (which only fire charges match, meaning the player is holding a fire charge), it then checks the max level 'power' enchantment the fire charge has. The higher the level, the bigger the boom. The missile is then deployed using __deploy_missile, which takes as an argument the fire charge power enchantment level to determine what size the explosion should be when the projectile collides with a block.

Any more examples?

Sure. Let's get inspired by modded minecraft and have a tool which 'refines' blocks, i.e. turns cobble to gravel and gravel to sand. We can use the hoe to do this, and every time the hoe is used on a block, we 'refine' it:

__holding_right_tool(player, tool) -> (player ~ 'holds'):0 ~ tool;

__on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) -> (
    if (!__holding_right_tool(player, '_hoe'), return());
    if (block(block) == block('cobblestone'),
        set(pos(block), block('gravel'))
    ,   block(block) == block('gravel'),
        set(pos(block), block('sand'))
    )
)

This would be a very crude implementation, since we would probably want the hoe to be damaged too. This is just a simple example showcasing our __holding_right_tool function though, no proper implementations here ;)