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

On Click BlockData #7130

Merged
merged 13 commits into from
Oct 31, 2024
137 changes: 62 additions & 75 deletions src/main/java/ch/njol/skript/events/EvtClick.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.events;

import ch.njol.util.Predicate;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Vehicle;
Expand Down Expand Up @@ -55,37 +39,37 @@ public class EvtClick extends SkriptEvent {
/**
* Tracks PlayerInteractEvents to deduplicate them.
*/
public static final ClickEventTracker interactTracker = new ClickEventTracker(Skript.getInstance());
public final static ClickEventTracker interactTracker = new ClickEventTracker(Skript.getInstance());

static {
Class<? extends PlayerEvent>[] eventTypes = CollectionUtils.array(
PlayerInteractEvent.class, PlayerInteractEntityEvent.class, PlayerInteractAtEntityEvent.class
);
Skript.registerEvent("Click", EvtClick.class, eventTypes,
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] [on %-entitydata/itemtype%] [(with|using|holding) %-itemtype%]",
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] (with|using|holding) %itemtype% on %entitydata/itemtype%")
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] [on %-entitydata/itemtype/blockdata%] [(with|using|holding) %-itemtype%]",
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] (with|using|holding) %itemtype% on %entitydata/itemtype/blockdata%")
.description("Called when a user clicks on a block, an entity or air with or without an item in their hand.",
"Please note that rightclick events with an empty hand while not looking at a block are not sent to the server, so there's no way to detect them.",
"Also note that a leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'damage' event.")
.examples("on click:",
"on rightclick holding a fishing rod:",
"on leftclick on a stone or obsidian:",
"on rightclick on a creeper:",
"on click with a sword:")
.since("1.0");
"on click with a sword:",
"on click on chest[facing=north]:",
"on click on campfire[lit=true]:")
.since("1.0, INSERT VERSION (blockdata)");
}

/**
* Only trigger when one of these is interacted with.
*/
@Nullable
private Literal<?> type;
private @Nullable Literal<?> type;

/**
* Only trigger when then item player clicks with is one of these.
* Only trigger when the item that the player clicks with is one of these.
*/
@Nullable
private Literal<ItemType> tools;
private @Nullable Literal<ItemType> tools;

/**
* Click types to trigger.
Expand All @@ -97,18 +81,18 @@ public class EvtClick extends SkriptEvent {
public boolean init(Literal<?>[] args, int matchedPattern, ParseResult parseResult) {
click = parseResult.mark == 0 ? ANY : parseResult.mark;
type = args[matchedPattern];
if (type != null && !ItemType.class.isAssignableFrom(type.getReturnType())) {
if (type != null && !ItemType.class.isAssignableFrom(type.getReturnType()) && !BlockData.class.isAssignableFrom(type.getReturnType())) {
Literal<EntityData<?>> entitydata = (Literal<EntityData<?>>) type;
if (click == LEFT) {
if (Vehicle.class.isAssignableFrom(entitydata.getSingle().getType())) {
Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event.");
Skript.error("A leftclick on a vehicle entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event.");
} else {
Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'damage' event.");
}
return false;
} else if (click == ANY) {
if (Vehicle.class.isAssignableFrom(entitydata.getSingle().getType())) {
Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event. " +
Skript.error("A leftclick on a vehicle entity is an attack and thus not covered by the 'click' event, but the 'vehicle damage' event. " +
"Change this event to a rightclick to fix this warning message.");
} else {
Skript.error("A leftclick on an entity is an attack and thus not covered by the 'click' event, but the 'damage' event. " +
Expand All @@ -125,12 +109,11 @@ public boolean check(Event event) {
Block block;
Entity entity;

if (event instanceof PlayerInteractEntityEvent) {
PlayerInteractEntityEvent clickEvent = ((PlayerInteractEntityEvent) event);
Entity clicked = clickEvent.getRightClicked();
if (event instanceof PlayerInteractEntityEvent interactEntityEvent) {
Entity clicked = interactEntityEvent.getRightClicked();

// Usually, don't handle these events
if (clickEvent instanceof PlayerInteractAtEntityEvent) {
if (interactEntityEvent instanceof PlayerInteractAtEntityEvent) {
// But armor stands are an exception
// Later, there may be more exceptions...
if (!(clicked instanceof ArmorStand))
Expand All @@ -142,82 +125,86 @@ public boolean check(Event event) {

// PlayerInteractAtEntityEvent called only once for armor stands
if (!(event instanceof PlayerInteractAtEntityEvent)) {
if (!interactTracker.checkEvent(clickEvent.getPlayer(), clickEvent, clickEvent.getHand())) {
if (!interactTracker.checkEvent(interactEntityEvent.getPlayer(), interactEntityEvent, interactEntityEvent.getHand())) {
return false; // Not first event this tick
}
}

entity = clicked;
block = null;
} else if (event instanceof PlayerInteractEvent) {
PlayerInteractEvent clickEvent = ((PlayerInteractEvent) event);

} else if (event instanceof PlayerInteractEvent interactEvent) {
// Figure out click type, filter non-click events
Action a = clickEvent.getAction();
Action action = interactEvent.getAction();
int click;
switch (a) {
case LEFT_CLICK_AIR:
case LEFT_CLICK_BLOCK:
click = LEFT;
break;
case RIGHT_CLICK_AIR:
case RIGHT_CLICK_BLOCK:
click = RIGHT;
break;
case PHYSICAL: // Not a click event
default:
switch (action) {
case LEFT_CLICK_AIR, LEFT_CLICK_BLOCK -> click = LEFT;
case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> click = RIGHT;
default -> {
return false;
}
}
if ((this.click & click) == 0)
return false; // We don't want to handle this kind of events

EquipmentSlot hand = clickEvent.getHand();
EquipmentSlot hand = interactEvent.getHand();
assert hand != null; // Not PHYSICAL interaction
if (!interactTracker.checkEvent(clickEvent.getPlayer(), clickEvent, hand)) {
if (!interactTracker.checkEvent(interactEvent.getPlayer(), interactEvent, hand)) {
return false; // Not first event this tick
}

block = clickEvent.getClickedBlock();
block = interactEvent.getClickedBlock();
entity = null;
} else {
assert false;
return false;
}

if (tools != null && !tools.check(event, new Checker<ItemType>() {
@Override
public boolean check(final ItemType t) {
if (event instanceof PlayerInteractEvent) {
return t.isOfType(((PlayerInteractEvent) event).getItem());
} else { // PlayerInteractEntityEvent doesn't have item associated with it
PlayerInventory invi = ((PlayerInteractEntityEvent) event).getPlayer().getInventory();
ItemStack item = ((PlayerInteractEntityEvent) event).getHand() == EquipmentSlot.HAND
? invi.getItemInMainHand() : invi.getItemInOffHand();
return t.isOfType(item);
}

Checker<ItemType> checker = itemType -> {
if (event instanceof PlayerInteractEvent interactEvent) {
return itemType.isOfType(interactEvent.getItem());
} else {
PlayerInventory invi = ((PlayerInteractEntityEvent) event).getPlayer().getInventory();
ItemStack item = ((PlayerInteractEntityEvent) event).getHand() == EquipmentSlot.HAND
? invi.getItemInMainHand() : invi.getItemInOffHand();
TheAbsolutionism marked this conversation as resolved.
Show resolved Hide resolved
return itemType.isOfType(item);
}
})) {
};

if (tools != null && !tools.check(event, checker))
return false;
}


if (type != null) {
BlockData blockDataCheck = block != null ? block.getBlockData() : null;
return type.check(event, new Checker<Object>() {
@Override
public boolean check(final Object o) {
public boolean check(Object object) {
if (entity != null) {
return o instanceof EntityData ? ((EntityData<?>) o).isInstance(entity) : Relation.EQUAL.isImpliedBy(DefaultComparators.entityItemComparator.compare(EntityData.fromEntity(entity), (ItemType) o));
} else {
return o instanceof EntityData ? false : ((ItemType) o).isOfType(block);
if (object instanceof EntityData<?> entityData) {
return entityData.isInstance(entity);
} else {
Relation compare = DefaultComparators.entityItemComparator.compare(EntityData.fromEntity(entity), (ItemType) object);
return Relation.EQUAL.isImpliedBy(compare);
}
} else if (object instanceof ItemType itemType) {
return itemType.isOfType(block);
} else if (blockDataCheck != null && object instanceof BlockData blockData) {
return blockDataCheck.matches(blockData);
}
return false;
}
});
}
return true;
}

@Override
public String toString(@Nullable Event e, boolean debug) {
return (click == LEFT ? "left" : click == RIGHT ? "right" : "") + "click" + (type != null ? " on " + type.toString(e, debug) : "") + (tools != null ? " holding " + tools.toString(e, debug) : "");
public String toString(@Nullable Event event, boolean debug) {
return switch (click) {
case LEFT -> "left";
case RIGHT -> "right";
default -> "";
} + "click" + (type != null ? " on " + type.toString(event, debug) : "") +
(tools != null ? " holding " + tools.toString(event, debug) : "");
}

}