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

BlockDropItemEvent #7075

Merged
merged 20 commits into from
Oct 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.AbstractVillager;
import org.bukkit.entity.AreaEffectCloud;
Expand Down Expand Up @@ -1964,5 +1965,31 @@ public RegainReason get(EntityRegainHealthEvent event) {
return event.getRegainReason();
}
}, EventValues.TIME_NOW);

// BlockDropItemEvent
EventValues.registerEventValue(BlockDropItemEvent.class, Block.class, new Getter<Block, BlockDropItemEvent>() {
@Override
public Block get(BlockDropItemEvent event) {
return new BlockStateBlock(event.getBlockState());
}
}, EventValues.TIME_PAST);
EventValues.registerEventValue(BlockDropItemEvent.class, Player.class, new Getter<Player, BlockDropItemEvent>() {
@Override
public Player get(BlockDropItemEvent event) {
return event.getPlayer();
}
}, EventValues.TIME_NOW);
EventValues.registerEventValue(BlockDropItemEvent.class, ItemStack[].class, new Getter<ItemStack[], BlockDropItemEvent>() {
@Override
public ItemStack[] get(BlockDropItemEvent event) {
return event.getItems().stream().map(Item::getItemStack).toArray(ItemStack[]::new);
}
}, EventValues.TIME_NOW);
EventValues.registerEventValue(BlockDropItemEvent.class, Entity[].class, new Getter<Entity[], BlockDropItemEvent>() {
@Override
public Entity[] get(BlockDropItemEvent event) {
return event.getItems().toArray(Entity[]::new);
}
}, EventValues.TIME_NOW);
}
}
56 changes: 38 additions & 18 deletions src/main/java/ch/njol/skript/events/EvtBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.Event;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.event.block.BlockFadeEvent;
import org.bukkit.event.block.BlockFormEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.*;
import org.bukkit.event.hanging.HangingBreakEvent;
import org.bukkit.event.hanging.HangingEvent;
import org.bukkit.event.hanging.HangingPlaceEvent;
Expand All @@ -37,6 +32,7 @@

import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import org.jetbrains.annotations.NotNull;
import org.skriptlang.skript.lang.comparator.Relation;
import ch.njol.skript.classes.data.DefaultComparators;
import ch.njol.skript.entity.EntityData;
Expand Down Expand Up @@ -74,6 +70,30 @@ public class EvtBlock extends SkriptEvent {
.description("Called when a block is created, but not by a player, e.g. snow forms due to snowfall, water freezes in cold biomes. This isn't called when block spreads (mushroom growth, water physics etc.), as it has its own event (see <a href='#spread'>spread event</a>).")
.examples("on form of snow:", "on form of a mushroom:")
.since("1.0, 2.6 (BlockData support)");
Skript.registerEvent("Block Drop", EvtBlock.class, BlockDropItemEvent.class, "block drop[ping] [[of] %-itemtypes/blockdatas%]")
.description(
"Called when a block broken by a player drops something.",
"<ul>",
"<li>event-player: The player that broke the block</li>",
"<li>past event-block: The block that was broken</li>",
"<li>event-block: The block after being broken</li>",
"<li>event-items (or drops): The drops of the block</li>",
"<li>event-entities: The entities of the dropped items</li>",
"</ul>",
"",
"If the breaking of the block leads to others being broken, such as torches, they will appear" +
"in \"event-items\" and \"event-entities\"."
)
.examples(
"on block drop:",
"\tbroadcast event-player",
"\tbroadcast past event-block",
"\tbroadcast event-block",
"\tbroadcast event-items",
"\tbroadcast event-entities",
"on block drop of oak log:"
)
.since("INSERT VERSION");
}

@Nullable
Expand Down Expand Up @@ -101,26 +121,26 @@ public boolean check(final Event event) {
ItemType item;
BlockData blockData = null;

if (event instanceof BlockFormEvent) {
BlockFormEvent blockFormEvent = (BlockFormEvent) event;
if (event instanceof BlockFormEvent blockFormEvent) {
BlockState newState = blockFormEvent.getNewState();
item = new ItemType(newState.getBlockData());
blockData = newState.getBlockData();
} else if (event instanceof BlockEvent) {
BlockEvent blockEvent = (BlockEvent) event;
} else if (event instanceof BlockDropItemEvent blockDropItemEvent) {
Block block = blockDropItemEvent.getBlock();
item = new ItemType(block);
blockData = block.getBlockData();
} else if (event instanceof BlockEvent blockEvent) {
Block block = blockEvent.getBlock();
item = new ItemType(block);
blockData = block.getBlockData();
} else if (event instanceof PlayerBucketFillEvent) {
PlayerBucketFillEvent playerBucketFillEvent = ((PlayerBucketFillEvent) event);
} else if (event instanceof PlayerBucketFillEvent playerBucketFillEvent) {
Block block = playerBucketFillEvent.getBlockClicked();
item = new ItemType(block);
blockData = block.getBlockData();
} else if (event instanceof PlayerBucketEmptyEvent) {
PlayerBucketEmptyEvent playerBucketEmptyEvent = ((PlayerBucketEmptyEvent) event);
} else if (event instanceof PlayerBucketEmptyEvent playerBucketEmptyEvent) {
item = new ItemType(playerBucketEmptyEvent.getItemStack());
} else if (event instanceof HangingEvent) {
final EntityData<?> d = EntityData.fromEntity(((HangingEvent) event).getEntity());
} else if (event instanceof HangingEvent hangingEvent) {
final EntityData<?> d = EntityData.fromEntity((hangingEvent.getEntity()));
return types.check(event, o -> {
if (o instanceof ItemType)
return Relation.EQUAL.isImpliedBy(DefaultComparators.entityItemComparator.compare(d, ((ItemType) o)));
Expand All @@ -144,8 +164,8 @@ else if (o instanceof BlockData && finalBlockData != null)
}

@Override
public String toString(final @Nullable Event e, final boolean debug) {
return "break/place/burn/fade/form of " + Classes.toString(types);
public String toString(@Nullable Event event, boolean debug) {
return "break/place/burn/fade/form/drop of " + Classes.toString(types);
}

}
87 changes: 55 additions & 32 deletions src/main/java/ch/njol/skript/expressions/ExprDrops.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
import ch.njol.skript.util.Experience;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
import org.bukkit.entity.Item;
import org.bukkit.event.Event;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
Expand All @@ -59,40 +62,51 @@ public class ExprDrops extends SimpleExpression<ItemType> {
Skript.registerExpression(ExprDrops.class, ItemType.class, ExpressionType.SIMPLE, "[the] drops");
}

private boolean isDeathEvent;

@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
if (!getParser().isCurrentEvent(EntityDeathEvent.class)) {
Skript.error("The expression 'drops' can only be used in death events", ErrorQuality.SEMANTIC_ERROR);
if (!getParser().isCurrentEvent(EntityDeathEvent.class, BlockDropItemEvent.class)) {
Skript.error("The expression 'drops' can only be used in death events and block drop events");
return false;
}
if (getParser().isCurrentEvent(EntityDeathEvent.class))
isDeathEvent = true;
return true;
}

@Override
@Nullable
protected ItemType[] get(Event e) {
if (!(e instanceof EntityDeathEvent))
return null;

return ((EntityDeathEvent) e).getDrops()
.stream()
.map(ItemType::new)
.toArray(ItemType[]::new);
protected ItemType @Nullable [] get(Event event) {
if (event instanceof EntityDeathEvent entityDeathEvent) {
return entityDeathEvent.getDrops()
.stream()
.map(ItemType::new)
.toArray(ItemType[]::new);
} else if (event instanceof BlockDropItemEvent blockDropItemEvent) {
return blockDropItemEvent.getItems()
.stream()
.map(Item::getItemStack)
.map(ItemType::new)
.toArray(ItemType[]::new);
}
TheAbsolutionism marked this conversation as resolved.
Show resolved Hide resolved
assert false;
return new ItemType[0];
}

@Override
@Nullable
public Class<?>[] acceptChange(ChangeMode mode) {
public Class<?> @Nullable [] acceptChange(ChangeMode mode) {
if (getParser().getHasDelayBefore().isTrue()) {
Skript.error("Can't change the drops after the event has already passed");
return null;
}
switch (mode) {
case ADD:
case REMOVE:
case REMOVE_ALL:
case SET:
return CollectionUtils.array(ItemType[].class, Inventory[].class, Experience[].class);
if (isDeathEvent)
return CollectionUtils.array(ItemType[].class, Inventory[].class, Experience[].class);
else
return CollectionUtils.array(ItemType[].class, Inventory[].class);
case DELETE: // handled by EffClearDrops
case RESET:
default:
Expand All @@ -101,38 +115,47 @@ public Class<?>[] acceptChange(ChangeMode mode) {
}

@Override
public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
if (!(event instanceof EntityDeathEvent))
public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
List<ItemStack> drops = null;
int originalExperience = 0;
if (event instanceof EntityDeathEvent entityDeathEvent) {
drops = entityDeathEvent.getDrops();
originalExperience = entityDeathEvent.getDroppedExp();
} else if (event instanceof BlockDropItemEvent blockDropItemEvent) {
drops = blockDropItemEvent.getItems()
.stream()
.map(Item::getItemStack)
.toList();
} else {
return;
}

List<ItemStack> drops = ((EntityDeathEvent) event).getDrops();
int originalExperience = ((EntityDeathEvent) event).getDroppedExp();
assert delta != null;

// separate the delta into experience and drops to make it easier to handle
int deltaExperience = -1; // Skript does not support negative experience, so -1 is a safe "unset" value
boolean removeAllExperience = false;
List<ItemType> deltaDrops = new ArrayList<>();
for (Object o : delta) {
if (o instanceof Experience) {
if (o instanceof Experience experience) {
// Special case for `remove xp from the drops`
if ((((Experience) o).getInternalXP() == -1 && mode == ChangeMode.REMOVE) || mode == ChangeMode.REMOVE_ALL) {
if ((experience.getInternalXP() == -1 && mode == ChangeMode.REMOVE) || mode == ChangeMode.REMOVE_ALL) {
removeAllExperience = true;
}
// add the value even if we're removing all experience, just so we know that experience was changed
if (deltaExperience == -1) {
deltaExperience = ((Experience) o).getXP();
deltaExperience = experience.getXP();
} else {
deltaExperience += ((Experience) o).getXP();
deltaExperience += experience.getXP();
}
} else if (o instanceof Inventory) {
} else if (o instanceof Inventory inventory) {
// inventories are unrolled into their contents
for (ItemStack item : ((Inventory) o).getContents()) {
for (ItemStack item : inventory.getContents()) {
if (item != null)
deltaDrops.add(new ItemType(item));
}
} else if (o instanceof ItemType) {
deltaDrops.add((ItemType) o);
} else if (o instanceof ItemType itemType) {
deltaDrops.add(itemType);
} else {
assert false;
}
Expand All @@ -144,20 +167,20 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
// todo: All the experience stuff should be removed from this class for 2.8 and given to ExprExperience

// handle experience
if (deltaExperience > -1) {
if (deltaExperience > -1 && event instanceof EntityDeathEvent entityDeathEvent) {
switch (mode) {
TheAbsolutionism marked this conversation as resolved.
Show resolved Hide resolved
case SET:
((EntityDeathEvent) event).setDroppedExp(deltaExperience);
entityDeathEvent.setDroppedExp(deltaExperience);
break;
case ADD:
((EntityDeathEvent) event).setDroppedExp(originalExperience + deltaExperience);
entityDeathEvent.setDroppedExp(originalExperience + deltaExperience);
break;
case REMOVE:
((EntityDeathEvent) event).setDroppedExp(originalExperience - deltaExperience);
entityDeathEvent.setDroppedExp(originalExperience - deltaExperience);
// fallthrough to check for removeAllExperience
case REMOVE_ALL:
if (removeAllExperience)
((EntityDeathEvent) event).setDroppedExp(0);
entityDeathEvent.setDroppedExp(0);
break;
case DELETE:
case RESET:
Expand Down