mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-30 20:39:10 +00:00
混合方块行为
This commit is contained in:
@@ -428,7 +428,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
.properties(properties)
|
||||
.settings(settings)
|
||||
.lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true)))
|
||||
.behavior(MiscUtils.castToMap(section.get("behavior"), true))
|
||||
.behavior(MiscUtils.getAsMapList(ResourceConfigUtils.get(section, "behavior", "behaviors")))
|
||||
.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
|
||||
@NotNull Map<String, VariantState> variantMapper,
|
||||
@NotNull BlockSettings settings,
|
||||
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
|
||||
@Nullable Map<String, Object> behavior,
|
||||
@Nullable List<Map<String, Object>> behavior,
|
||||
@Nullable LootTable<?> lootTable
|
||||
) {
|
||||
super(id, holder, properties, appearances, variantMapper, settings, events, behavior, lootTable);
|
||||
@@ -159,7 +159,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
|
||||
protected Map<String, Integer> appearances;
|
||||
protected Map<String, VariantState> variantMapper;
|
||||
protected BlockSettings settings;
|
||||
protected Map<String, Object> behavior;
|
||||
protected List<Map<String, Object>> behavior;
|
||||
protected LootTable<?> lootTable;
|
||||
protected Map<EventTrigger, List<Function<PlayerOptionalContext>>> events;
|
||||
|
||||
@@ -180,7 +180,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder behavior(Map<String, Object> behavior) {
|
||||
public Builder behavior(List<Map<String, Object>> behavior) {
|
||||
this.behavior = behavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
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.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public abstract class AbstractCanSurviveBlockBehavior extends BukkitBlockBehavior {
|
||||
|
||||
protected AbstractCanSurviveBlockBehavior(CustomBlock customBlock) {
|
||||
super(customBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object state = args[0];
|
||||
Object world = args[1];
|
||||
Object pos = args[2];
|
||||
return canSurvive(thisBlock, state, world, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object world = args[1];
|
||||
Object blockPos = args[2];
|
||||
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object level;
|
||||
Object blockPos;
|
||||
Object state = args[0];
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
level = args[1];
|
||||
blockPos = args[3];
|
||||
} else {
|
||||
level = args[3];
|
||||
blockPos = args[4];
|
||||
}
|
||||
int stateId = BlockStateUtils.blockStateToId(state);
|
||||
ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
|
||||
if (previousState == null || previousState.isEmpty()) {
|
||||
return state;
|
||||
}
|
||||
if (!canSurvive(thisBlock, new Object[] {state, level, blockPos}, () -> true)) {
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
|
||||
ContextHolder.Builder builder = ContextHolder.builder()
|
||||
.withParameter(DirectContextParameters.POSITION, position);
|
||||
for (Item<Object> item : previousState.getDrops(builder, world, null)) {
|
||||
world.dropItemNaturally(position, item);
|
||||
}
|
||||
world.playBlockSound(position, previousState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
protected abstract boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception;
|
||||
}
|
||||
@@ -12,9 +12,10 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
public static final Key STRIPPABLE_BLOCK = Key.from("craftengine:strippable_block");
|
||||
public static final Key SAPLING_BLOCK = Key.from("craftengine:sapling_block");
|
||||
public static final Key ON_LIQUID_BLOCK = Key.from("craftengine:on_liquid_block");
|
||||
public static final Key NEAR_LIQUID_BLOCK = Key.from("craftengine:near_liquid_block");
|
||||
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 VERTICAL_CROP_BLOCK = Key.from("craftengine:vertical_crop_block");
|
||||
public static final Key CROP_BLOCK = Key.from("craftengine:crop_block");
|
||||
public static final Key GRASS_BLOCK = Key.from("craftengine:grass_block");
|
||||
public static final Key LAMP_BLOCK = Key.from("craftengine:lamp_block");
|
||||
@@ -28,9 +29,10 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
register(STRIPPABLE_BLOCK, StrippableBlockBehavior.FACTORY);
|
||||
register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY);
|
||||
register(ON_LIQUID_BLOCK, OnLiquidBlockBehavior.FACTORY);
|
||||
register(NEAR_LIQUID_BLOCK, NearLiquidBlockBehavior.FACTORY);
|
||||
register(WATERLOGGED_BLOCK, WaterLoggedBlockBehavior.FACTORY);
|
||||
register(CONCRETE_POWDER_BLOCK, ConcretePowderBlockBehavior.FACTORY);
|
||||
register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY);
|
||||
register(VERTICAL_CROP_BLOCK, VerticalCropBlockBehavior.FACTORY);
|
||||
register(CROP_BLOCK, CropBlockBehavior.FACTORY);
|
||||
register(GRASS_BLOCK, GrassBlockBehavior.FACTORY);
|
||||
register(LAMP_BLOCK, LampBlockBehavior.FACTORY);
|
||||
|
||||
@@ -4,23 +4,13 @@ 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.BlockTags;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
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.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -28,76 +18,31 @@ import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class BushBlockBehavior extends BukkitBlockBehavior {
|
||||
public class BushBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
protected final List<Object> tagsCanSurviveOn;
|
||||
protected final Set<Object> blocksCansSurviveOn;
|
||||
protected final Set<String> customBlocksCansSurviveOn;
|
||||
protected final boolean any;
|
||||
protected final boolean stackable;
|
||||
|
||||
public BushBlockBehavior(CustomBlock block, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
public BushBlockBehavior(CustomBlock block, boolean stackable, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
super(block);
|
||||
this.stackable = stackable;
|
||||
this.tagsCanSurviveOn = tagsCanSurviveOn;
|
||||
this.blocksCansSurviveOn = blocksCansSurviveOn;
|
||||
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
|
||||
this.any = this.tagsCanSurviveOn.isEmpty() && this.blocksCansSurviveOn.isEmpty() && this.customBlocksCansSurviveOn.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlace(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object world = args[1];
|
||||
Object blockPos = args[2];
|
||||
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object level;
|
||||
Object blockPos;
|
||||
Object state = args[0];
|
||||
if (VersionHelper.isOrAbove1_21_2()) {
|
||||
level = args[1];
|
||||
blockPos = args[3];
|
||||
} else {
|
||||
level = args[3];
|
||||
blockPos = args[4];
|
||||
}
|
||||
if (!canSurvive(thisBlock, state, level, blockPos)) {
|
||||
int stateId = BlockStateUtils.blockStateToId(state);
|
||||
ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
|
||||
if (previousState != null && !previousState.isEmpty()) {
|
||||
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
|
||||
ContextHolder.Builder builder = ContextHolder.builder()
|
||||
.withParameter(DirectContextParameters.POSITION, position);
|
||||
for (Item<Object> item : previousState.getDrops(builder, world, null)) {
|
||||
world.dropItemNaturally(position, item);
|
||||
}
|
||||
world.playBlockSound(position, previousState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
}
|
||||
return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR);
|
||||
}
|
||||
return super.updateShape(thisBlock, args, superMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object state = args[0];
|
||||
Object world = args[1];
|
||||
Object pos = args[2];
|
||||
return canSurvive(thisBlock, state, world, pos);
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
return new BushBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right());
|
||||
boolean stackable = (boolean) arguments.getOrDefault("stackable", false);
|
||||
return new BushBlockBehavior(block, stackable, tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +72,8 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
|
||||
return new Tuple<>(mcTags, mcBlocks, customBlocks);
|
||||
}
|
||||
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws Exception {
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
@@ -151,12 +97,17 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
if (previousState != null && !previousState.isEmpty()) {
|
||||
if (this.customBlocksCansSurviveOn.contains(previousState.owner().value().id().toString())) {
|
||||
ImmutableBlockState belowCustomState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
if (belowCustomState != null && !belowCustomState.isEmpty()) {
|
||||
if (stackable) {
|
||||
if (belowCustomState.owner().value() == super.customBlock) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.customBlocksCansSurviveOn.contains(belowCustomState.owner().value().id().toString())) {
|
||||
return true;
|
||||
}
|
||||
if (this.customBlocksCansSurviveOn.contains(previousState.toString())) {
|
||||
if (this.customBlocksCansSurviveOn.contains(belowCustomState.toString())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,14 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
// TODO Inject FallingBlockEntity?
|
||||
public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
public class ConcretePowderBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final Key targetBlock;
|
||||
private final Key targetBlock; // TODO 更宽泛的,使用state,似乎也不是很好的方案?
|
||||
private Object defaultBlockState;
|
||||
private ImmutableBlockState defaultImmutableBlockState;
|
||||
|
||||
public ConcretePowderBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt, Key targetBlock) {
|
||||
super(block, hurtAmount, maxHurt);
|
||||
public ConcretePowderBlockBehavior(CustomBlock block, Key targetBlock) {
|
||||
super(block);
|
||||
this.targetBlock = targetBlock;
|
||||
}
|
||||
|
||||
@@ -79,7 +78,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
return super.updateStateForPlacement(context, state);
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to update state for placement " + context.getClickedPos(), e);
|
||||
}
|
||||
return super.updateStateForPlacement(context, state);
|
||||
@@ -118,7 +117,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.updateShape(thisBlock, args, superMethod);
|
||||
return args[0];
|
||||
}
|
||||
|
||||
private static boolean shouldSolidify(Object level, Object blockPos, Object blockState) throws ReflectiveOperationException {
|
||||
@@ -155,10 +154,8 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
float hurtAmount = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f), "hurt-amount");
|
||||
int hurtMax = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-hurt", -1), "max-hurt");
|
||||
String solidBlock = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("solid-block"), "warning.config.block.behavior.concrete.missing_solid");
|
||||
return new ConcretePowderBlockBehavior(block, hurtAmount, hurtMax, Key.of(solidBlock));
|
||||
return new ConcretePowderBlockBehavior(block, Key.of(solidBlock));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
|
||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.Vec3i;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
@@ -33,12 +32,10 @@ import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.World;
|
||||
|
||||
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 class CropBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final IntegerProperty ageProperty;
|
||||
private final float growSpeed;
|
||||
@@ -46,9 +43,8 @@ public class CropBlockBehavior extends BushBlockBehavior {
|
||||
private final boolean isBoneMealTarget;
|
||||
private final NumberProvider boneMealBonus;
|
||||
|
||||
public CropBlockBehavior(CustomBlock block, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn,
|
||||
Property<Integer> ageProperty, float growSpeed, int minGrowLight, boolean isBoneMealTarget, NumberProvider boneMealBonus) {
|
||||
super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
public CropBlockBehavior(CustomBlock block, Property<Integer> ageProperty, float growSpeed, int minGrowLight, boolean isBoneMealTarget, NumberProvider boneMealBonus) {
|
||||
super(block);
|
||||
this.ageProperty = (IntegerProperty) ageProperty;
|
||||
this.growSpeed = growSpeed;
|
||||
this.minGrowLight = minGrowLight;
|
||||
@@ -105,8 +101,10 @@ public class CropBlockBehavior extends BushBlockBehavior {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
return hasSufficientLight(world, blockPos) && super.canSurvive(thisBlock, state, world, blockPos);
|
||||
public boolean canSurvive(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object world = args[1];
|
||||
Object pos = args[2];
|
||||
return hasSufficientLight(world, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,13 +202,12 @@ public class CropBlockBehavior extends BushBlockBehavior {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
Property<Integer> ageProperty = (Property<Integer>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.crop.missing_age");
|
||||
int minGrowLight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("light-requirement", 9), "light-requirement");
|
||||
float growSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.125f), "grow-speed");
|
||||
boolean isBoneMealTarget = (boolean) arguments.getOrDefault("is-bone-meal-target", true);
|
||||
NumberProvider boneMealAgeBonus = NumberProviders.fromObject(arguments.getOrDefault("bone-meal-age-bonus", 1));
|
||||
return new CropBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight, isBoneMealTarget, boneMealAgeBonus);
|
||||
return new CropBlockBehavior(block, ageProperty, growSpeed, minGrowLight, isBoneMealTarget, boneMealAgeBonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
|
||||
blockPos = args[4];
|
||||
}
|
||||
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 2);
|
||||
return super.updateShape(thisBlock, args, superMethod);
|
||||
return args[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,6 +109,8 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
|
||||
@Override
|
||||
public void onLand(Object thisBlock, Object[] args) throws Exception {
|
||||
Object fallingBlock = args[4];
|
||||
Object level = args[0];
|
||||
Object pos = args[1];
|
||||
Object entityData = Reflections.field$Entity$entityData.get(fallingBlock);
|
||||
boolean isSilent = (boolean) Reflections.method$SynchedEntityData$get.invoke(entityData, Reflections.instance$Entity$DATA_SILENT);
|
||||
if (!isSilent) {
|
||||
@@ -116,8 +118,6 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
|
||||
if (immutableBlockState == null || immutableBlockState.isEmpty()) return;
|
||||
Object level = args[0];
|
||||
Object pos = args[1];
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
world.playBlockSound(Vec3d.atCenterOf(LocationUtils.fromBlockPos(pos)), immutableBlockState.sounds().landSound());
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import java.util.Set;
|
||||
public class HangingBlockBehavior extends BushBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
|
||||
public HangingBlockBehavior(CustomBlock block, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
public HangingBlockBehavior(CustomBlock block, boolean stackable, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
|
||||
super(block, stackable, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,7 +32,8 @@ public class HangingBlockBehavior extends BushBlockBehavior {
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, true);
|
||||
return new HangingBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right());
|
||||
boolean stackable = (boolean) arguments.getOrDefault("stackable", false);
|
||||
return new HangingBlockBehavior(block, stackable, tuple.left(), tuple.mid(), tuple.right());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ 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.LocationUtils;
|
||||
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.Property;
|
||||
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
@@ -23,6 +25,13 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
|
||||
this.litProperty = litProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
|
||||
Object level = context.getLevel().serverWorld();
|
||||
state = state.with(this.litProperty, FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(level, LocationUtils.toBlockPos(context.getClickedPos())));
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||
Object blockState = args[0];
|
||||
@@ -30,9 +39,16 @@ public class LampBlockBehavior extends BukkitBlockBehavior {
|
||||
if (state == null || state.isEmpty()) return;
|
||||
Object world = args[1];
|
||||
Object blockPos = args[2];
|
||||
if (state.get(this.litProperty) && !FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
|
||||
// TODO Call Event
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2);
|
||||
if (state.get(this.litProperty)) {
|
||||
if (!FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
|
||||
// TODO Call Event
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2);
|
||||
}
|
||||
} else {
|
||||
if (FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
|
||||
// TODO Call Event
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
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.LocationUtils;
|
||||
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.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
private static final List<Object> WATER = List.of(Reflections.instance$Fluids$WATER, Reflections.instance$Fluids$FLOWING_WATER);
|
||||
private static final List<Object> LAVA = List.of(Reflections.instance$Fluids$LAVA, Reflections.instance$Fluids$FLOWING_LAVA);
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final boolean onWater;
|
||||
private final boolean onLava;
|
||||
private final boolean stackable;
|
||||
private final BlockPos[] positions;
|
||||
|
||||
public NearLiquidBlockBehavior(CustomBlock block, BlockPos[] positions, boolean stackable, boolean onWater, boolean onLava) {
|
||||
super(block);
|
||||
this.onWater = onWater;
|
||||
this.onLava = onLava;
|
||||
this.stackable = stackable;
|
||||
this.positions = positions;
|
||||
}
|
||||
|
||||
public boolean onWater() {
|
||||
return this.onWater;
|
||||
}
|
||||
|
||||
public boolean onLava() {
|
||||
return this.onLava;
|
||||
}
|
||||
|
||||
public static class Factory implements BlockBehaviorFactory {
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
List<String> liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water")));
|
||||
boolean stackable = (boolean) arguments.getOrDefault("stackable", false);
|
||||
List<String> positionsToCheck = MiscUtils.getAsStringList(arguments.getOrDefault("positions", List.of()));
|
||||
if (positionsToCheck.isEmpty()) {
|
||||
return new NearLiquidBlockBehavior(block, new BlockPos[]{new BlockPos(0,-1,0)}, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava"));
|
||||
} else {
|
||||
BlockPos[] pos = new BlockPos[positionsToCheck.size()];
|
||||
for (int i = 0; i < pos.length; i++) {
|
||||
String[] split = positionsToCheck.get(i).split(",");
|
||||
pos[i] = new BlockPos(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
|
||||
}
|
||||
return new NearLiquidBlockBehavior(block, pos, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
if (this.stackable) {
|
||||
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z);
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
int id = BlockStateUtils.blockStateToId(belowState);
|
||||
if (!BlockStateUtils.isVanillaBlock(id)) {
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id);
|
||||
if (immutableBlockState.owner().value() == super.customBlock) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (BlockPos pos : positions) {
|
||||
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x + pos.x(), y + pos.y(), z + pos.z());
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
if (mayPlaceOn(belowState, world, belowPos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
|
||||
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, belowPos);
|
||||
Object fluidStateAbove = Reflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos));
|
||||
if (Reflections.method$FluidState$getType.invoke(fluidStateAbove) != Reflections.instance$Fluids$EMPTY) {
|
||||
return false;
|
||||
}
|
||||
if (this.onWater && (WATER.contains(Reflections.method$FluidState$getType.invoke(fluidState)) || Reflections.field$StateHolder$owner.get(belowState) == Reflections.instance$Blocks$ICE)) {
|
||||
return true;
|
||||
}
|
||||
if (this.onLava && LAVA.contains(Reflections.method$FluidState$getType.invoke(fluidState))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,30 @@
|
||||
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.LocationUtils;
|
||||
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.util.MiscUtils;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class OnLiquidBlockBehavior extends BushBlockBehavior {
|
||||
public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final boolean onWater;
|
||||
private final boolean onLava;
|
||||
private final boolean stackable;
|
||||
|
||||
public OnLiquidBlockBehavior(CustomBlock block, boolean onWater, boolean onLava) {
|
||||
super(block, List.of(), Set.of(), Set.of());
|
||||
public OnLiquidBlockBehavior(CustomBlock block, boolean stackable, boolean onWater, boolean onLava) {
|
||||
super(block);
|
||||
this.onWater = onWater;
|
||||
this.onLava = onLava;
|
||||
this.stackable = stackable;
|
||||
}
|
||||
|
||||
public boolean onWater() {
|
||||
@@ -34,12 +39,31 @@ public class OnLiquidBlockBehavior extends BushBlockBehavior {
|
||||
@Override
|
||||
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||
List<String> liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water")));
|
||||
return new OnLiquidBlockBehavior(block, liquidTypes.contains("water"), liquidTypes.contains("lava"));
|
||||
boolean stackable = (boolean) arguments.getOrDefault("stackable", false);
|
||||
return new OnLiquidBlockBehavior(block, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z);
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
return mayPlaceOn(belowState, world, belowPos);
|
||||
}
|
||||
|
||||
protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException {
|
||||
if (this.stackable) {
|
||||
int id = BlockStateUtils.blockStateToId(belowState);
|
||||
if (!BlockStateUtils.isVanillaBlock(id)) {
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id);
|
||||
if (immutableBlockState.owner().value() == super.customBlock) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, belowPos);
|
||||
Object fluidStateAbove = Reflections.method$Level$getFluidState.invoke(world, LocationUtils.above(belowPos));
|
||||
if (Reflections.method$FluidState$getType.invoke(fluidStateAbove) != Reflections.instance$Fluids$EMPTY) {
|
||||
|
||||
@@ -17,26 +17,23 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.Tuple;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
public class SaplingBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final Key feature;
|
||||
private final Property<Integer> stageProperty;
|
||||
private final double boneMealSuccessChance;
|
||||
private final float growSpeed;
|
||||
|
||||
public SaplingBlockBehavior(CustomBlock block, Key feature, Property<Integer> stageProperty, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn, double boneMealSuccessChance, float growSpeed) {
|
||||
super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
public SaplingBlockBehavior(CustomBlock block, Key feature, Property<Integer> stageProperty, double boneMealSuccessChance, float growSpeed) {
|
||||
super(block);
|
||||
this.feature = feature;
|
||||
this.stageProperty = stageProperty;
|
||||
this.boneMealSuccessChance = boneMealSuccessChance;
|
||||
@@ -53,7 +50,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
Object blockPos = args[2];
|
||||
Object blockState = args[0];
|
||||
Object aboveBlockPos = LocationUtils.above(blockPos);
|
||||
if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && (float) RandomUtils.generateRandomFloat(0, 1) < growSpeed) {
|
||||
if ((int) Reflections.method$LevelReader$getMaxLocalRawBrightness.invoke(world, aboveBlockPos) >= 9 && RandomUtils.generateRandomFloat(0, 1) < growSpeed) {
|
||||
increaseStage(world, blockPos, blockState, args[3]);
|
||||
}
|
||||
}
|
||||
@@ -175,8 +172,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
|
||||
String feature = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("feature"), "warning.config.block.behavior.sapling.missing_feature");
|
||||
Property<Integer> stageProperty = (Property<Integer>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("stage"), "warning.config.block.behavior.sapling.missing_stage");
|
||||
double boneMealSuccessChance = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45), "bone-meal-success-chance");
|
||||
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments, false);
|
||||
return new SaplingBlockBehavior(block, Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance,
|
||||
return new SaplingBlockBehavior(block, Key.of(feature), stageProperty, boneMealSuccessChance,
|
||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0), "grow-speed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,37 +15,32 @@ import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
public class VerticalCropBlockBehavior extends BukkitBlockBehavior {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private static final List<Object> WATER = List.of(Reflections.instance$Fluids$WATER, Reflections.instance$Fluids$FLOWING_WATER);
|
||||
private static final List<Object> LAVA = List.of(Reflections.instance$Fluids$LAVA, Reflections.instance$Fluids$FLOWING_LAVA);
|
||||
private static final List<Object> HORIZON_DIRECTIONS = List.of(Reflections.instance$Direction$NORTH, Reflections.instance$Direction$EAST, Reflections.instance$Direction$SOUTH, Reflections.instance$Direction$WEST);
|
||||
private final int maxHeight;
|
||||
private final boolean nearWater;
|
||||
private final boolean nearLava;
|
||||
private final IntegerProperty ageProperty;
|
||||
private final float growSpeed;
|
||||
private final boolean direction;
|
||||
|
||||
public SugarCaneBlockBehavior(CustomBlock customBlock, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn, Property<Integer> ageProperty,
|
||||
int maxHeight, boolean nearWater, boolean nearLava, float growSpeed) {
|
||||
super(customBlock, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
|
||||
this.nearWater = nearWater;
|
||||
this.nearLava = nearLava;
|
||||
public VerticalCropBlockBehavior(CustomBlock customBlock, Property<Integer> ageProperty,
|
||||
int maxHeight, float growSpeed, boolean direction) {
|
||||
super(customBlock);
|
||||
this.maxHeight = maxHeight;
|
||||
this.ageProperty = (IntegerProperty) ageProperty;
|
||||
this.growSpeed = growSpeed;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,11 +48,11 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
Object blockState = args[0];
|
||||
Object level = args[1];
|
||||
Object blockPos = args[2];
|
||||
if (!canSurvive(thisBlock, blockState, level, blockPos)) {
|
||||
if (!canSurvive(thisBlock, args, () -> true)) {
|
||||
int stateId = BlockStateUtils.blockStateToId(blockState);
|
||||
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
|
||||
if (currentState != null && !currentState.isEmpty()) {
|
||||
// break the sugar cane
|
||||
// break the crop
|
||||
FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false);
|
||||
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
|
||||
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos)));
|
||||
@@ -85,7 +80,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
}
|
||||
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 1);
|
||||
// return state, do not call super.
|
||||
return superMethod.call();
|
||||
return args[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,15 +89,15 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
Object level = args[1];
|
||||
Object blockPos = args[2];
|
||||
// above block is empty
|
||||
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, LocationUtils.above(blockPos)) == Reflections.instance$Blocks$AIR$defaultState) {
|
||||
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, (this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos))) == Reflections.instance$Blocks$AIR$defaultState) {
|
||||
int currentHeight = 1;
|
||||
BlockPos currentPos = LocationUtils.fromBlockPos(blockPos);
|
||||
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
|
||||
if (currentState != null && !currentState.isEmpty()) {
|
||||
while (true) {
|
||||
Object belowPos = LocationUtils.toBlockPos(currentPos.x(), currentPos.y() - currentHeight, currentPos.z());
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, belowPos);
|
||||
ImmutableBlockState belowImmutableState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(belowState));
|
||||
Object nextPos = LocationUtils.toBlockPos(currentPos.x(), this.direction ? currentPos.y() - currentHeight : currentPos.y() + currentHeight, currentPos.z());
|
||||
Object nextState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, nextPos);
|
||||
ImmutableBlockState belowImmutableState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(nextState));
|
||||
if (belowImmutableState != null && !belowImmutableState.isEmpty() && belowImmutableState.owner() == currentState.owner()) {
|
||||
currentHeight++;
|
||||
} else {
|
||||
@@ -116,11 +111,11 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
if (currentHeight < this.maxHeight) {
|
||||
int age = currentState.get(ageProperty);
|
||||
if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
|
||||
Object abovePos = LocationUtils.above(blockPos);
|
||||
Object nextPos = this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos);
|
||||
if (VersionHelper.isOrAbove1_21_5()) {
|
||||
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
|
||||
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
|
||||
} else {
|
||||
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, super.customBlock.defaultState().customBlockState().handle());
|
||||
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, nextPos, super.customBlock.defaultState().customBlockState().handle());
|
||||
}
|
||||
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
|
||||
} else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
|
||||
@@ -130,81 +125,16 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException {
|
||||
int y = FastNMS.INSTANCE.field$Vec3i$y(blockPos);
|
||||
int x = FastNMS.INSTANCE.field$Vec3i$x(blockPos);
|
||||
int z = FastNMS.INSTANCE.field$Vec3i$z(blockPos);
|
||||
Object belowPos = FastNMS.INSTANCE.constructor$BlockPos(x, y - 1, z);
|
||||
Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, belowPos);
|
||||
int id = BlockStateUtils.blockStateToId(belowState);
|
||||
// 如果下方是同种方块
|
||||
if (!BlockStateUtils.isVanillaBlock(id)) {
|
||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id);
|
||||
if (immutableBlockState.owner().value() == super.customBlock) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!super.mayPlaceOn(belowState, world, belowPos)) {
|
||||
return false;
|
||||
}
|
||||
// 如果不需要依靠流体
|
||||
if (!this.nearWater && !this.nearLava) {
|
||||
return true;
|
||||
}
|
||||
// 需要流体
|
||||
if (this.nearWater) {
|
||||
if (hasNearbyLiquid(world, belowPos, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.nearLava) {
|
||||
if (hasNearbyLiquid(world, belowPos, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasNearbyLiquid(Object world, Object blockPos, boolean waterOrLava) throws ReflectiveOperationException {
|
||||
for (Object direction : HORIZON_DIRECTIONS) {
|
||||
Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction);
|
||||
if (waterOrLava) {
|
||||
// water
|
||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, relativePos);
|
||||
if (Reflections.method$BlockStateBase$getBlock.invoke(blockState) == Reflections.instance$Blocks$ICE) {
|
||||
return true;
|
||||
}
|
||||
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos);
|
||||
Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState);
|
||||
if (WATER.contains(fluidType)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// lava
|
||||
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, relativePos);
|
||||
Object fluidType = Reflections.method$FluidState$getType.invoke(fluidState);
|
||||
if (LAVA.contains(fluidType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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, false);
|
||||
Property<Integer> ageProperty = (Property<Integer>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("age"), "warning.config.block.behavior.sugar_cane.missing_age");
|
||||
int maxHeight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-height", 3), "max-height");
|
||||
List<String> nearbyLiquids = MiscUtils.getAsStringList(arguments.getOrDefault("required-adjacent-liquids", List.of()));
|
||||
boolean nearWater = nearbyLiquids.contains("water");
|
||||
boolean nearLava = nearbyLiquids.contains("lava");
|
||||
return new SugarCaneBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, maxHeight, nearWater, nearLava,
|
||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1), "grow-speed"));
|
||||
boolean direction = arguments.getOrDefault("direction", "up").toString().equalsIgnoreCase("up");
|
||||
return new VerticalCropBlockBehavior(block, ageProperty, maxHeight,
|
||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1), "grow-speed"), direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,14 @@ public class LocationUtils {
|
||||
return toBlockPos(pos.x(), pos.y(), pos.z());
|
||||
}
|
||||
|
||||
public static Object above(Object blockPos) throws ReflectiveOperationException {
|
||||
public static Object above(Object blockPos) {
|
||||
return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos));
|
||||
}
|
||||
|
||||
public static Object below(Object blockPos) {
|
||||
return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) - 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos));
|
||||
}
|
||||
|
||||
public static Object toBlockPos(int x, int y, int z) {
|
||||
return FastNMS.INSTANCE.constructor$BlockPos(x, y, z);
|
||||
}
|
||||
@@ -49,7 +53,7 @@ public class LocationUtils {
|
||||
return new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
}
|
||||
|
||||
public static BlockPos fromBlockPos(Object pos) throws ReflectiveOperationException {
|
||||
public static BlockPos fromBlockPos(Object pos) {
|
||||
return new BlockPos(
|
||||
FastNMS.INSTANCE.field$Vec3i$x(pos),
|
||||
FastNMS.INSTANCE.field$Vec3i$y(pos),
|
||||
|
||||
Reference in New Issue
Block a user