9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-28 19:39:11 +00:00

swing hand/particles for crop

This commit is contained in:
XiaoMoMi
2025-03-29 02:24:24 +08:00
parent 81ac75adb0
commit 8b357432d6
6 changed files with 150 additions and 6 deletions

View File

@@ -1,7 +1,9 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
@@ -13,6 +15,7 @@ import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.util.Tuple;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import org.bukkit.World;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@@ -38,6 +41,10 @@ public class CropBlockBehavior extends BushBlockBehavior {
return state.get(ageProperty);
}
public boolean isMaxAge(ImmutableBlockState state) {
return state.get(ageProperty) == ageProperty.max;
}
private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
return (int) Reflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0);
}
@@ -93,12 +100,31 @@ public class CropBlockBehavior extends BushBlockBehavior {
if (immutableBlockState == null || immutableBlockState.isEmpty()) {
return;
}
boolean sendParticles = false;
Object visualState = immutableBlockState.vanillaBlockState().handle();
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, pos, visualState);
if (!is) {
sendParticles = true;
}
} else {
sendParticles = true;
}
int i = this.getAge(immutableBlockState) + RandomUtils.generateRandomInt(2, 5);
int maxAge = this.ageProperty.max;
if (i > maxAge) {
i = maxAge;
}
Reflections.method$Level$setBlock.invoke(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
if (sendParticles) {
World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
int y = FastNMS.INSTANCE.field$Vec3i$y(pos);
int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 12, 0.2, 0.2, 0.2);
}
}
public static class Factory implements BlockBehaviorFactory {

View File

@@ -3,10 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.FeatureUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
@@ -98,8 +95,34 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
}
@Override
public boolean isBoneMealSuccess(Object thisBlock, Object[] args) {
return RandomUtils.generateRandomDouble(0d, 1d) < this.boneMealSuccessChance;
public boolean isBoneMealSuccess(Object thisBlock, Object[] args) throws Exception {
boolean success = RandomUtils.generateRandomDouble(0d, 1d) < this.boneMealSuccessChance;
Object level = args[0];
Object blockPos = args[2];
Object blockState = args[3];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState == null || immutableBlockState.isEmpty()) {
return false;
}
boolean sendParticles = false;
Object visualState = immutableBlockState.vanillaBlockState().handle();
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, level, blockPos, visualState);
if (!is) {
sendParticles = true;
}
} else {
sendParticles = true;
}
if (sendParticles) {
World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 12, 0.2, 0.2, 0.2);
}
return success;
}
@Override

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.compatibility.item.NeigeItemsProvider;
import net.momirealms.craftengine.bukkit.item.behavior.AxeItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BoneMealItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory;
@@ -50,6 +51,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
registerVanillaItemExtraBehavior(AxeItemBehavior.INSTANCE, ItemKeys.AXES);
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
registerVanillaItemExtraBehavior(BoneMealItemBehavior.INSTANCE, ItemKeys.BONE_MEAL);
}
private static BukkitItemManager instance;

View File

@@ -0,0 +1,73 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.block.behavior.CropBlockBehavior;
import net.momirealms.craftengine.bukkit.block.behavior.SaplingBlockBehavior;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorldBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.block.Block;
import java.nio.file.Path;
import java.util.Map;
public class BoneMealItemBehavior extends ItemBehavior {
public static final Factory FACTORY = new Factory();
public static final BoneMealItemBehavior INSTANCE = new BoneMealItemBehavior();
@Override
public InteractionResult useOnBlock(UseOnContext context) {
BukkitWorldBlock clicked = (BukkitWorldBlock) context.getLevel().getBlockAt(context.getClickedPos());
Block block = clicked.block();
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state == null || state.isEmpty()) return InteractionResult.PASS;
boolean shouldHandle =false;
if (state.behavior() instanceof CropBlockBehavior blockBehavior) {
if (!blockBehavior.isMaxAge(state)) {
shouldHandle = true;
}
} else if (state.behavior() instanceof SaplingBlockBehavior) {
shouldHandle = true;
}
if (!shouldHandle) return InteractionResult.PASS;
boolean sendSwing = false;
try {
Object visualState = state.vanillaBlockState().handle();
Object visualStateBlock = Reflections.method$BlockStateBase$getBlock.invoke(visualState);
if (Reflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
boolean is = (boolean) Reflections.method$BonemealableBlock$isValidBonemealTarget.invoke(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
if (!is) {
sendSwing = true;
}
} else {
sendSwing = true;
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to check visual state bone meal state", e);
}
if (sendSwing) {
context.getPlayer().swingHand(context.getHand());
}
return InteractionResult.SUCCESS;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -11,6 +11,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
public static final Key AXE_ITEM = Key.from("craftengine:axe_item");
public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item");
public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item");
public static final Key BONE_MEAL_ITEM = Key.from("craftengine:bone_meal_item");
public static void init() {
register(EMPTY, EmptyItemBehavior.FACTORY);
@@ -20,5 +21,6 @@ public class BukkitItemBehaviors extends ItemBehaviors {
register(AXE_ITEM, AxeItemBehavior.FACTORY);
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY);
register(BUCKET_ITEM, BucketItemBehavior.FACTORY);
register(BONE_MEAL_ITEM, BoneMealItemBehavior.FACTORY);
}
}

View File

@@ -0,0 +1,18 @@
package net.momirealms.craftengine.bukkit.util;
import org.bukkit.Particle;
public class ParticleUtils {
public static Particle getParticle(String particle) {
try {
return Particle.valueOf(particle);
} catch (IllegalArgumentException e) {
return switch (particle) {
case "REDSTONE" -> Particle.valueOf("DUST");
case "VILLAGER_HAPPY" -> Particle.valueOf("HAPPY_VILLAGER");
default -> Particle.valueOf(particle);
};
}
}
}