diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 713573526e..9b0913f44a 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -302,6 +302,10 @@ Number of ticks remaining until an entity can use a portal again. Number of ticks an entity sits in a portal. +### `query(e, 'item_cooldown', item)` + +Ticks remaining of item usage cooldown, e.g., from pearl throwing or horn blowing. + ### `query(e, 'item')` The item triple (name, count, nbt) if its an item or item frame entity, `null` otherwise. @@ -717,6 +721,11 @@ Sets a custom number of ticks remaining until an entity can use a portal again. Sets a custom number of ticks an entity sits in a portal. +### `modify(e, 'item_cooldown', item, ticks)` + +Sets a custom number of cooldown ticks before the item can be used again. +Gives white overlay, like pearl shooting/horn blowing cooldown + ### `modify(e, 'dismount')` Dismounts riding entity. diff --git a/src/main/java/carpet/fakes/ItemCooldownsInterface.java b/src/main/java/carpet/fakes/ItemCooldownsInterface.java new file mode 100644 index 0000000000..4a22185c51 --- /dev/null +++ b/src/main/java/carpet/fakes/ItemCooldownsInterface.java @@ -0,0 +1,7 @@ +package carpet.fakes; + +import net.minecraft.world.item.Item; + +public interface ItemCooldownsInterface { + int getCooldownTicks(Item item); +} diff --git a/src/main/java/carpet/mixins/CooldownInstanceAccessor.java b/src/main/java/carpet/mixins/CooldownInstanceAccessor.java new file mode 100644 index 0000000000..df8fab2357 --- /dev/null +++ b/src/main/java/carpet/mixins/CooldownInstanceAccessor.java @@ -0,0 +1,11 @@ +package carpet.mixins; + +import net.minecraft.world.item.ItemCooldowns; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ItemCooldowns.CooldownInstance.class) +public interface CooldownInstanceAccessor { + @Accessor("endTime") + int getEndTime(); +} diff --git a/src/main/java/carpet/mixins/ItemCooldowns_scarpetEntityMixin.java b/src/main/java/carpet/mixins/ItemCooldowns_scarpetEntityMixin.java new file mode 100644 index 0000000000..5f042cb28e --- /dev/null +++ b/src/main/java/carpet/mixins/ItemCooldowns_scarpetEntityMixin.java @@ -0,0 +1,25 @@ +package carpet.mixins; + +import carpet.fakes.ItemCooldownsInterface; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemCooldowns; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Map; + +@Mixin(ItemCooldowns.class) +public class ItemCooldowns_scarpetEntityMixin implements ItemCooldownsInterface { + @Shadow @Final private Map cooldowns; + + @Shadow private int tickCount; + + @Override + public int getCooldownTicks(Item item) { + if(!cooldowns.containsKey(item)) return 0; + + var cooldown = ((CooldownInstanceAccessor) cooldowns.get(item)); + return cooldown.getEndTime()-tickCount; + } +} diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 140c30e639..15f6e0b2da 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -1,5 +1,6 @@ package carpet.script.value; +import carpet.fakes.ItemCooldownsInterface; import carpet.script.external.Vanilla; import carpet.script.utils.Tracer; import carpet.script.CarpetContext; @@ -64,6 +65,7 @@ import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.WitherSkull; import net.minecraft.world.entity.vehicle.AbstractMinecart; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; @@ -503,6 +505,15 @@ public Value get(String what, @Nullable Value arg) put("pickup_delay", (e, a) -> (e instanceof final ItemEntity ie) ? new NumericValue(Vanilla.ItemEntity_getPickupDelay(ie)) : Value.NULL); put("portal_cooldown", (e, a) -> new NumericValue(Vanilla.Entity_getPublicNetherPortalCooldown(e))); put("portal_timer", (e, a) -> new NumericValue(Vanilla.Entity_getPortalTimer(e))); + put("item_cooldown", (e, a)->{ + if(!(e instanceof ServerPlayer player)) + throw new InternalExpressionException("Can only query 'item_cooldown' for players"); + + Item item = NBTSerializableValue.parseItem(a.getString(), e.getServer().registryAccess()).getItem(); + int ticks = ((ItemCooldownsInterface) player.getCooldowns()).getCooldownTicks(item); + + return NumericValue.of(ticks); + }); // ItemEntity -> despawn timer via ssGetAge put("is_baby", (e, a) -> (e instanceof final LivingEntity le) ? BooleanValue.of(le.isBaby()) : Value.NULL); put("target", (e, a) -> { @@ -1436,6 +1447,20 @@ else if (a.isNull()) Vanilla.Entity_setPortalTimer(e, NumericValue.asNumber(v).getInt()); }); + put("item_cooldown", (e, v)->{ + if(!(e instanceof ServerPlayer player)) + throw new InternalExpressionException("Can only modify 'item_cooldown' for players"); + + if(!(v instanceof ListValue lv) || lv.length()!=2) + throw new InternalExpressionException("'item_cooldown' requires 2 parameters, an item and a cooldown duration"); + + List values = lv.getItems(); + Item item = NBTSerializableValue.parseItem(values.get(0).getString(), e.getServer().registryAccess()).getItem(); + int ticks = NumericValue.asNumber(values.get(1), "item cooldown duration").getInt(); + + player.getCooldowns().addCooldown(item, ticks); + }); + put("ai", (e, v) -> { if (e instanceof final Mob mob) diff --git a/src/main/resources/carpet.accesswidener b/src/main/resources/carpet.accesswidener index ebfed8ee50..3ee3473ba7 100644 --- a/src/main/resources/carpet.accesswidener +++ b/src/main/resources/carpet.accesswidener @@ -9,6 +9,7 @@ accessible class net/minecraft/world/level/border/WorldBorder$StaticBorderExtent accessible class net/minecraft/server/MinecraftServer$ReloadableResources accessible class net/minecraft/world/level/biome/Biome$ClimateSettings accessible class net/minecraft/world/level/block/entity/SculkSensorBlockEntity$VibrationUser +accessible class net/minecraft/world/item/ItemCooldowns$CooldownInstance accessible method net/minecraft/world/level/border/WorldBorder getListeners ()Ljava/util/List; diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index f9daa6206f..17b9154309 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -81,6 +81,8 @@ "PieceGeneratorSupplier_plopMixin", "ServerGamePacketListenerImpl_scarpetEventsMixin", "ServerPlayerGameMode_scarpetEventsMixin", + "ItemCooldowns_scarpetEntityMixin", + "CooldownInstanceAccessor", "Player_parrotMixin", "SaplingBlock_desertShrubsMixin", "EntityMixin",