Skip to content

Commit 11d5b71

Browse files
Player Pick Item Event (#8331)
1 parent 6ea0b5e commit 11d5b71

5 files changed

Lines changed: 422 additions & 0 deletions

File tree

src/main/java/org/skriptlang/skript/bukkit/misc/MiscModule.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package org.skriptlang.skript.bukkit.misc;
22

3+
import ch.njol.skript.Skript;
34
import org.skriptlang.skript.addon.AddonModule;
45
import org.skriptlang.skript.addon.HierarchicalAddonModule;
56
import org.skriptlang.skript.addon.SkriptAddon;
7+
import org.skriptlang.skript.bukkit.misc.elements.expressions.ExprPickedItem;
68
import org.skriptlang.skript.bukkit.misc.elements.expressions.ExprWithYawPitch;
9+
import org.skriptlang.skript.bukkit.misc.events.EvtPlayerPickItem;
710

811
public class MiscModule extends HierarchicalAddonModule {
912

@@ -16,6 +19,12 @@ protected void loadSelf(SkriptAddon addon) {
1619
register(addon,
1720
ExprWithYawPitch::register
1821
);
22+
if (Skript.classExists("io.papermc.paper.event.player.PlayerPickBlockEvent")) {
23+
register(addon,
24+
EvtPlayerPickItem::register,
25+
ExprPickedItem::register
26+
);
27+
}
1928
}
2029

2130
@Override
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package org.skriptlang.skript.bukkit.misc.elements.expressions;
2+
3+
import ch.njol.skript.aliases.ItemType;
4+
import ch.njol.skript.doc.*;
5+
import ch.njol.skript.lang.EventRestrictedSyntax;
6+
import ch.njol.skript.lang.Expression;
7+
import ch.njol.skript.lang.SkriptParser.ParseResult;
8+
import ch.njol.skript.lang.util.SimpleExpression;
9+
import ch.njol.util.Kleenean;
10+
import ch.njol.util.coll.CollectionUtils;
11+
import io.papermc.paper.event.player.PlayerPickBlockEvent;
12+
import io.papermc.paper.event.player.PlayerPickEntityEvent;
13+
import io.papermc.paper.event.player.PlayerPickItemEvent;
14+
import org.bukkit.block.Block;
15+
import org.bukkit.entity.Entity;
16+
import org.bukkit.event.Event;
17+
import org.jetbrains.annotations.Nullable;
18+
import org.skriptlang.skript.registration.SyntaxInfo;
19+
import org.skriptlang.skript.registration.SyntaxRegistry;
20+
21+
import java.util.Locale;
22+
23+
@Name("Picked Item/Block/Entity")
24+
@Description("The item, block, or entity picked by a player using the pick block key (default middle mouse button).")
25+
@Example("""
26+
on player pick item:
27+
send "You picked %the picked item%!" to the player
28+
""")
29+
@Since("INSERT VERSION")
30+
@RequiredPlugins("1.21.5+")
31+
@Keywords({"pick", "picked", "picked item", "picked block", "picked entity"})
32+
public class ExprPickedItem extends SimpleExpression<Object> implements EventRestrictedSyntax {
33+
34+
public static void register(SyntaxRegistry registry) {
35+
registry.register(SyntaxRegistry.EXPRESSION, SyntaxInfo.Expression.builder(ExprPickedItem.class, Object.class)
36+
.supplier(ExprPickedItem::new)
37+
.addPattern("[the] picked (item|1:block|2:entity)")
38+
.build());
39+
}
40+
41+
private enum PickType {
42+
ITEM,
43+
BLOCK,
44+
ENTITY,
45+
}
46+
47+
private PickType pickType;
48+
49+
@Override
50+
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
51+
pickType = PickType.values()[parseResult.mark];
52+
return true;
53+
}
54+
55+
@Override
56+
protected Object @Nullable [] get(Event event) {
57+
return switch (pickType) {
58+
case ITEM -> {
59+
if (event instanceof PlayerPickBlockEvent pickBlockEvent) {
60+
yield new ItemType[] {new ItemType(pickBlockEvent.getBlock())};
61+
} else if (event instanceof PlayerPickEntityEvent pickEntityEvent) {
62+
yield new ItemType[] {new ItemType(pickEntityEvent.getEntity().getPickItemStack())};
63+
} else {
64+
yield null;
65+
}
66+
}
67+
case BLOCK -> {
68+
if (event instanceof PlayerPickBlockEvent pickBlockEvent) {
69+
yield new Block[] {pickBlockEvent.getBlock()};
70+
} else {
71+
yield null;
72+
}
73+
}
74+
case ENTITY -> {
75+
if (event instanceof PlayerPickEntityEvent pickEntityEvent) {
76+
yield new Entity[] {pickEntityEvent.getEntity()};
77+
} else {
78+
yield null;
79+
}
80+
}
81+
};
82+
}
83+
84+
@Override
85+
public boolean isSingle() {
86+
return true;
87+
}
88+
89+
@Override
90+
public Class<?> getReturnType() {
91+
return switch (pickType) {
92+
case ITEM -> ItemType.class;
93+
case BLOCK -> Block.class;
94+
case ENTITY -> Entity.class;
95+
};
96+
}
97+
98+
@Override
99+
public Class<? extends Event>[] supportedEvents() {
100+
return CollectionUtils.array(PlayerPickItemEvent.class);
101+
}
102+
103+
@Override
104+
public String toString(@Nullable Event event, boolean debug) {
105+
return "the picked " + pickType.name().toLowerCase(Locale.ENGLISH);
106+
}
107+
108+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package org.skriptlang.skript.bukkit.misc.events;
2+
3+
import ch.njol.skript.aliases.ItemType;
4+
import ch.njol.skript.classes.data.DefaultComparators;
5+
import ch.njol.skript.entity.EntityData;
6+
import ch.njol.skript.lang.Literal;
7+
import ch.njol.skript.lang.SkriptEvent;
8+
import ch.njol.skript.lang.SkriptParser.ParseResult;
9+
import ch.njol.skript.lang.SyntaxStringBuilder;
10+
import ch.njol.skript.registrations.EventConverter;
11+
import ch.njol.skript.registrations.EventValues;
12+
import ch.njol.skript.util.Patterns;
13+
import ch.njol.skript.util.slot.InventorySlot;
14+
import ch.njol.skript.util.slot.Slot;
15+
import ch.njol.util.coll.CollectionUtils;
16+
import io.papermc.paper.event.player.PlayerPickBlockEvent;
17+
import io.papermc.paper.event.player.PlayerPickEntityEvent;
18+
import io.papermc.paper.event.player.PlayerPickItemEvent;
19+
import org.bukkit.block.Block;
20+
import org.bukkit.block.data.BlockData;
21+
import org.bukkit.entity.Entity;
22+
import org.bukkit.event.Event;
23+
import org.jetbrains.annotations.Nullable;
24+
import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos;
25+
import org.skriptlang.skript.docs.Origin;
26+
import org.skriptlang.skript.lang.comparator.Relation;
27+
import org.skriptlang.skript.registration.SyntaxRegistry;
28+
29+
public class EvtPlayerPickItem extends SkriptEvent {
30+
31+
private static final Patterns<PickType> PATTERNS = new Patterns<>(new Object[][]{
32+
{"[player] pick[ing] [of] [an|any] item", PickType.ANY},
33+
{"[player] pick[ing] [of] [a|any] block", PickType.BLOCK},
34+
{"[player] pick[ing] [of] [an|any] entity", PickType.ENTITY},
35+
{"[player] pick[ing] [of] %entitydata/itemtype/blockdata%", null}
36+
});
37+
38+
public static void register(SyntaxRegistry registry) {
39+
registry.register(BukkitSyntaxInfos.Event.KEY, BukkitSyntaxInfos.Event.builder(EvtPlayerPickItem.class, "Player Pick Item")
40+
.supplier(EvtPlayerPickItem::new)
41+
.addEvents(CollectionUtils.array(PlayerPickBlockEvent.class, PlayerPickEntityEvent.class))
42+
.addPatterns(PATTERNS.getPatterns())
43+
.addDescription("Called when a player picks an item, block or an entity" +
44+
" using the pick block key (default middle mouse button).",
45+
"The past event-slot represents the slot containing the item that will be put into the players hotbar," +
46+
" or nothing, if the item is not in the inventory.",
47+
"The event-slot represents the slot in the hotbar where the picked item will be placed.",
48+
"Both event-slots may be set to new slots.")
49+
.addExample("""
50+
on player picking a diamond block:
51+
cancel event
52+
send "You cannot pick diamond blocks!" to the player
53+
""")
54+
.addSince("INSERT VERSION")
55+
.addRequiredPlugin("1.21.5+")
56+
.build());
57+
58+
EventValues.registerEventValue(PlayerPickItemEvent.class, Slot.class, new EventConverter<>() {
59+
@Override
60+
public void set(PlayerPickItemEvent event, @Nullable Slot slot) {
61+
if (!(slot instanceof InventorySlot inventorySlot) || inventorySlot.getInventory() != event.getPlayer().getInventory())
62+
return;
63+
event.setSourceSlot(inventorySlot.getIndex());
64+
}
65+
66+
@Override
67+
public @Nullable Slot convert(PlayerPickItemEvent event) {
68+
int source = event.getSourceSlot();
69+
if (source == -1)
70+
return null;
71+
return new InventorySlot(event.getPlayer().getInventory(), source);
72+
}
73+
}, EventValues.TIME_PAST);
74+
EventValues.registerEventValue(PlayerPickItemEvent.class, Slot.class, new EventConverter<>() {
75+
@Override
76+
public void set(PlayerPickItemEvent event, @Nullable Slot slot) {
77+
if (!(slot instanceof InventorySlot inventorySlot) || inventorySlot.getInventory() != event.getPlayer().getInventory())
78+
return;
79+
event.setTargetSlot(inventorySlot.getIndex());
80+
}
81+
82+
@Override
83+
public Slot convert(PlayerPickItemEvent event) {
84+
return new InventorySlot(event.getPlayer().getInventory(), event.getTargetSlot());
85+
}
86+
});
87+
}
88+
89+
private @Nullable PickType pickType;
90+
private @Nullable Literal<?> type;
91+
92+
@Override
93+
public boolean init(Literal<?>[] args, int matchedPattern, ParseResult parseResult) {
94+
pickType = PATTERNS.getInfo(matchedPattern);
95+
if (pickType == null)
96+
type = args[0];
97+
return true;
98+
}
99+
100+
@Override
101+
public boolean check(Event event) {
102+
if (pickType != null) {
103+
return switch (pickType) {
104+
case ANY -> true;
105+
case BLOCK -> event instanceof PlayerPickBlockEvent;
106+
case ENTITY -> event instanceof PlayerPickEntityEvent;
107+
};
108+
}
109+
110+
Block pickedBlock;
111+
Entity pickedEntity;
112+
if (event instanceof PlayerPickBlockEvent pickBlockEvent) {
113+
pickedBlock = pickBlockEvent.getBlock();
114+
pickedEntity = null;
115+
} else if (event instanceof PlayerPickEntityEvent pickEntityEvent) {
116+
pickedEntity = pickEntityEvent.getEntity();
117+
pickedBlock = null;
118+
} else {
119+
assert false;
120+
return false;
121+
}
122+
assert type != null;
123+
return type.check(event, object -> switch (object) {
124+
case EntityData<?> entityData when pickedEntity != null -> entityData.isInstance(pickedEntity);
125+
case ItemType itemType when pickedEntity != null -> {
126+
Relation comparison = DefaultComparators.entityItemComparator.compare(EntityData.fromEntity(pickedEntity), itemType);
127+
yield Relation.EQUAL.isImpliedBy(comparison);
128+
}
129+
case ItemType itemType -> itemType.isOfType(pickedBlock);
130+
case BlockData blockData when pickedBlock != null -> pickedBlock.getBlockData().matches(blockData);
131+
default -> false;
132+
});
133+
}
134+
135+
@Override
136+
public String toString(@Nullable Event event, boolean debug) {
137+
SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug);
138+
builder.append("player picking");
139+
if (pickType != null) {
140+
switch (pickType) {
141+
case ANY -> builder.append("an item");
142+
case BLOCK -> builder.append("a block");
143+
case ENTITY -> builder.append("an entity");
144+
}
145+
} else if (type != null) {
146+
builder.append(type);
147+
}
148+
return builder.toString();
149+
}
150+
151+
private enum PickType {
152+
ANY,
153+
BLOCK,
154+
ENTITY,
155+
}
156+
157+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.skriptlang.skript.test.tests.syntaxes.events;
2+
3+
import ch.njol.skript.Skript;
4+
import ch.njol.skript.sections.EffSecSpawn;
5+
import ch.njol.skript.test.runner.SkriptJUnitTest;
6+
import org.bukkit.Bukkit;
7+
import org.bukkit.Material;
8+
import org.bukkit.block.Block;
9+
import org.bukkit.entity.Entity;
10+
import org.bukkit.entity.Pig;
11+
import org.bukkit.entity.Player;
12+
import org.bukkit.event.Event;
13+
import org.easymock.EasyMock;
14+
import org.junit.Before;
15+
import org.junit.Test;
16+
17+
public class EvtPlayerPickItemTest extends SkriptJUnitTest {
18+
19+
private static final boolean SUPPORTS_PICK_EVENT = Skript.classExists("io.papermc.paper.event.player.PlayerPickBlockEvent");
20+
21+
private Player player;
22+
private Pig pickedEntity;
23+
private Block pickedBlock;
24+
25+
@Before
26+
public void setUp() {
27+
if (!SUPPORTS_PICK_EVENT)
28+
return;
29+
player = EasyMock.niceMock(Player.class);
30+
pickedEntity = spawnTestPig();
31+
pickedBlock = setBlock(Material.DIRT);
32+
}
33+
34+
@Test
35+
@SuppressWarnings("UnstableApiUsage")
36+
public void test() {
37+
if (!SUPPORTS_PICK_EVENT)
38+
return;
39+
Event pickBlockEvent = createPickBlockEvent();
40+
Event pickEntityEvent = createPickEntityEvent();
41+
Bukkit.getPluginManager().callEvent(pickBlockEvent);
42+
43+
Entity previous = EffSecSpawn.lastSpawned;
44+
EffSecSpawn.lastSpawned = pickedEntity;
45+
Bukkit.getPluginManager().callEvent(pickEntityEvent);
46+
EffSecSpawn.lastSpawned = previous;
47+
}
48+
49+
private Event createPickBlockEvent() {
50+
Event event = null;
51+
try {
52+
Class<?> eventClass = Class.forName("io.papermc.paper.event.player.PlayerPickBlockEvent");
53+
event = (Event) eventClass.getConstructor(Player.class, Block.class, boolean.class, int.class, int.class)
54+
.newInstance(player, pickedBlock, true, 0, 0);
55+
} catch (Exception ignored) {}
56+
return event;
57+
}
58+
59+
private Event createPickEntityEvent() {
60+
Event event = null;
61+
try {
62+
Class<?> eventClass = Class.forName("io.papermc.paper.event.player.PlayerPickEntityEvent");
63+
event = (Event) eventClass.getConstructor(Player.class, Entity.class, boolean.class, int.class, int.class)
64+
.newInstance(player, pickedEntity, true, 0, 0);
65+
} catch (Exception ignored) {}
66+
return event;
67+
}
68+
69+
}

0 commit comments

Comments
 (0)