Skip to content

Commit

Permalink
BlockDropItemEvent (#7075)
Browse files Browse the repository at this point in the history
* Starter Commit

* Test Variations

Logging for a copy,  will be removed

* PR Ready

* Stuff

Forgot to add back the return if none of the events in ExprDrops

* ;

* Requested Changes

* Requested Changes

* Requested Changes

* Requested Changes

* Requested Changes

* Requested Change

---------

Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com>
  • Loading branch information
TheAbsolutionism and sovdeeth authored Oct 13, 2024
1 parent 0d19871 commit 790c779
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 50 deletions.
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);
}
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) {
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

0 comments on commit 790c779

Please sign in to comment.