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

feat(bukkit): 添加作物方块行为

This commit is contained in:
jhqwqmc
2025-03-28 10:25:51 +08:00
parent 24b04901b1
commit 5b5145199a
4 changed files with 158 additions and 0 deletions

View File

@@ -15,6 +15,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key WATERLOGGED_BLOCK = Key.from("craftengine:waterlogged_block");
public static final Key CONCRETE_POWDER_BLOCK = Key.from("craftengine:concrete_powder_block");
public static final Key SUGARCANE_BLOCK = Key.from("craftengine:sugar_cane_block");
public static final Key CROP_BLOCK = Key.from("craftengine:crop_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -28,5 +29,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(WATERLOGGED_BLOCK, WaterLoggedBlockBehavior.FACTORY);
register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY);
register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY);
register(CROP_BLOCK, CropBlockBehavior.FACTORY);
}
}

View File

@@ -0,0 +1,120 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
import net.momirealms.craftengine.core.block.properties.Property;
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 java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
public class CropBlockBehavior extends BushBlockBehavior {
public static final Factory FACTORY = new Factory();
private final IntegerProperty ageProperty;
private final float growSpeed;
private final int minGrowLight;
public CropBlockBehavior(List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn,
Property<Integer> ageProperty, float growSpeed, int minGrowLight) {
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
this.ageProperty = (IntegerProperty) ageProperty;
this.growSpeed = growSpeed;
this.minGrowLight = minGrowLight;
}
public final boolean isMaxAge(Object state) {
return this.getAge(state) >= this.ageProperty.max;
}
public static ImmutableBlockState getCEBlockState(Object nmsState) {
return BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(nmsState));
}
public final int getAge(Object state) {
return getCEBlockState(state).get(ageProperty);
}
public Object getStateForAge(Object state, int age) {
ImmutableBlockState afterState = getCEBlockState(state).owner().value().defaultState().with(ageProperty, age);
return afterState.customBlockState().handle();
}
public void growCrops(Object level, Object pos, Object state) throws InvocationTargetException, IllegalAccessException {
int i = this.getAge(state) + RandomUtils.generateRandomInt(2, 5);
int maxAge = this.ageProperty.max;
if (i > maxAge) {
i = maxAge;
}
Reflections.method$ServerLevel$sendBlockUpdated.invoke(level, pos, state, getStateForAge(state, i), 3);
}
private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
return (int) Reflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0);
}
private boolean hasSufficientLight(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
return getRawBrightness(level, pos) >= minGrowLight - 1;
}
@Override
public void randomTick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
Object state = args[0];
Object level = args[1];
Object pos = args[2];
if (getRawBrightness(level, pos) >= minGrowLight) {
int age = this.getAge(state);
if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) >= this.growSpeed) {
Reflections.method$ServerLevel$sendBlockUpdated.invoke(level, pos, state, getStateForAge(state, age + 1), 3);
}
}
}
@Override
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
return hasSufficientLight(world, blockPos) && super.canSurvive(thisBlock, state, world, blockPos);
}
@Override
public boolean isBoneMealSuccess(Object thisBlock, Object[] args) {
return true;
}
@Override
public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) {
Object state = args[2];
return !this.isMaxAge(state);
}
@Override
public void performBoneMeal(Object thisBlock, Object[] args) throws Exception {
this.growCrops(args[0], args[2], args[3]);
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
Property<Integer> ageProperty = (Property<Integer>) block.getProperty("age");
if (ageProperty == null) {
throw new IllegalArgumentException("age property not set for crop");
}
// 存活条件是最小生长亮度-1
int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("min-grow-light", 9));
float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1));
return new CropBlockBehavior(tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight);
}
}
}

View File

@@ -5739,4 +5739,36 @@ public class Reflections {
clazz$CraftEventFactory, boolean.class, clazz$Level, clazz$BlockPos, clazz$BlockState
)
);
public static final Class<?> clazz$BlockAndTintGetter = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.BlockAndTintGetter"),
BukkitReflectionUtils.assembleMCClass("world.level.IBlockLightAccess")
)
);
public static final Method method$BlockAndTintGetter$getRawBrightness = requireNonNull(
ReflectionUtils.getMethod(
clazz$BlockAndTintGetter, int.class, clazz$BlockPos, int.class
)
);
public static final Field field$Level$random = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$Level, clazz$RandomSource, 0
)
);
public static final Class<?> clazz$Mth = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("util.Mth"),
BukkitReflectionUtils.assembleMCClass("util.MathHelper")
)
);
public static final Method method$nextInt = requireNonNull(
ReflectionUtils.getMethod(
clazz$Mth, int.class, clazz$RandomSource, int.class, int.class
)
);
}