diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java
index b3370a11e..995afd952 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java
@@ -59,6 +59,15 @@ public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper imp
return this;
}
+ @Override
+ public BlockStateWrapper cycleProperty(String propertyName, boolean backwards) {
+ return getImmutableBlockState().map(state -> {
+ Property> property = state.owner().value().getProperty(propertyName);
+ if (property == null) return null;
+ return state.cycle(property, backwards).customBlockState();
+ }).orElse(this);
+ }
+
@Override
public boolean hasProperty(String propertyName) {
return getImmutableBlockState().map(state -> state.owner().value().getProperty(propertyName) != null).orElse(false);
diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java
index b298f6d66..4b7b3989c 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java
@@ -54,4 +54,11 @@ public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
if (newState == super.blockState) return this;
return BlockRegistryMirror.byId(BlockStateUtils.blockStateToId(newState));
}
+
+ @Override
+ public BlockStateWrapper cycleProperty(String propertyName, boolean backwards) {
+ Object newState = this.accessor.cycleProperty(propertyName, backwards);
+ if (newState == super.blockState) return this;
+ return BlockRegistryMirror.byId(BlockStateUtils.blockStateToId(newState));
+ }
}
diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml
index b69c610d0..75303d550 100644
--- a/common-files/src/main/resources/translations/en.yml
+++ b/common-files/src/main/resources/translations/en.yml
@@ -479,6 +479,7 @@ warning.config.function.when.missing_source: "Issue found in file Issue found in file - The config '' is missing the required 'rules' argument for 'if_else' function."
warning.config.function.update_block_property.missing_properties: "Issue found in file - The config '' is missing the required 'properties' argument for 'update_block_property' function."
warning.config.function.transform_block.missing_block: "Issue found in file - The config '' is missing the required 'block' argument for 'transform_block' function."
+warning.config.function.cycle_block_property.missing_property: "Issue found in file - The config '' is missing the required 'property' argument for 'cycle_block_property' function."
warning.config.selector.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for selector."
warning.config.selector.invalid_type: "Issue found in file - The config '' is using an invalid selector type ''."
warning.config.selector.invalid_target: "Issue found in file - The config '' is using an invalid selector target ''."
diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml
index 886cb003e..b51193bcb 100644
--- a/common-files/src/main/resources/translations/zh_cn.yml
+++ b/common-files/src/main/resources/translations/zh_cn.yml
@@ -478,6 +478,7 @@ warning.config.function.when.missing_source: "在文件 发现
warning.config.function.if_else.missing_rules: "在文件 发现问题 - 配置项 '' 缺少 'if_else' 函数所需的 'rules' 参数"
warning.config.function.update_block_property.missing_properties: "在文件 发现问题 - 配置项 '' 缺少 'update_block_property' 函数所需的 'properties' 参数"
warning.config.function.transform_block.missing_block: "在文件 发现问题 - 配置项 '' 缺少 'transform_block' 函数所需的 'block' 参数"
+warning.config.function.cycle_block_property.missing_property: "在文件 发现问题 - 配置项 '' 缺少 'cycle_block_property' 函数所需的 'property' 参数"
warning.config.selector.missing_type: "在文件 发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数"
warning.config.selector.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的选择器类型 ''"
warning.config.selector.invalid_target: "在文件 发现问题 - 配置项 '' 使用了无效的选择器目标 ''"
diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java
index 4b86c95c3..4323daf06 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java
@@ -23,6 +23,8 @@ public interface BlockStateWrapper extends Comparable {
BlockStateWrapper withProperty(String propertyName, String propertyValue);
+ BlockStateWrapper cycleProperty(String propertyName, boolean backwards);
+
String getAsString();
boolean isCustom();
diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java
index c46a69faf..2c837d796 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java
@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.registry.Holder;
+import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.sparrow.nbt.CompoundTag;
@@ -152,6 +153,16 @@ public final class ImmutableBlockState {
return this.owner;
}
+ public > ImmutableBlockState cycle(Property property, boolean backwards) {
+ T currentValue = get(property);
+ List values = property.possibleValues();
+ return with(property, getRelative(values, currentValue, backwards));
+ }
+
+ private static T getRelative(List values, T currentValue, boolean backwards) {
+ return backwards ? MiscUtils.findPreviousInIterable(values, currentValue) : getNextValue(values, currentValue);
+ }
+
public > ImmutableBlockState cycle(Property property) {
T currentValue = get(property);
List values = property.possibleValues();
diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java
index 41aa0cd1a..f25d3e14d 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java
@@ -18,5 +18,10 @@ public interface StatePropertyAccessor {
Object withProperty(String propertyName, String value);
@NotNull
- Object cycleProperty(String propertyName);
+ default Object cycleProperty(String propertyName) {
+ return cycleProperty(propertyName, false);
+ }
+
+ @NotNull
+ Object cycleProperty(String propertyName, boolean backwards);
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java
index 74592b40a..447ce05f0 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java
@@ -54,6 +54,7 @@ public class EventFunctions {
register(CommonFunctions.ALTERNATIVES, new IfElseFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap));
register(CommonFunctions.WHEN, new WhenFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap));
register(CommonFunctions.DAMAGE_ITEM, new DamageItemFunction.FactoryImpl<>(EventConditions::fromMap));
+ register(CommonFunctions.CYCLE_BLOCK_PROPERTY, new CycleBlockPropertyFunction.FactoryImpl<>(EventConditions::fromMap));
}
public static void register(Key key, FunctionFactory factory) {
diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java
index 5cd4a5d0a..bc638a23d 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java
@@ -45,4 +45,5 @@ public final class CommonFunctions {
public static final Key ALL_OF = Key.of("craftengine:all_of");
public static final Key DUMMY = Key.of("craftengine:dummy");
public static final Key DAMAGE_ITEM = Key.of("craftengine:damage_item");
+ public static final Key CYCLE_BLOCK_PROPERTY = Key.of("craftengine:cycle_block_property");
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java
new file mode 100644
index 000000000..c7b2ac317
--- /dev/null
+++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java
@@ -0,0 +1,75 @@
+package net.momirealms.craftengine.core.plugin.context.function;
+
+import net.momirealms.craftengine.core.block.BlockStateWrapper;
+import net.momirealms.craftengine.core.block.UpdateOption;
+import net.momirealms.craftengine.core.entity.player.Player;
+import net.momirealms.craftengine.core.plugin.context.Condition;
+import net.momirealms.craftengine.core.plugin.context.Context;
+import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
+import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
+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.ResourceConfigUtils;
+import net.momirealms.craftengine.core.world.ExistingBlock;
+import net.momirealms.craftengine.core.world.World;
+import net.momirealms.craftengine.core.world.WorldPosition;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class CycleBlockPropertyFunction extends AbstractConditionalFunction {
+ private final String property;
+ private final NumberProvider x;
+ private final NumberProvider y;
+ private final NumberProvider z;
+ private final NumberProvider updateFlags;
+
+ public CycleBlockPropertyFunction(List> predicates, String property, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags) {
+ super(predicates);
+ this.property = property;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.updateFlags = updateFlags;
+ }
+
+ @Override
+ protected void runInternal(CTX ctx) {
+ Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
+ if (optionalWorldPosition.isEmpty()) return;
+ World world = optionalWorldPosition.get().world();
+ int x = MiscUtils.fastFloor(this.x.getDouble(ctx));
+ int y = MiscUtils.fastFloor(this.y.getDouble(ctx));
+ int z = MiscUtils.fastFloor(this.z.getDouble(ctx));
+ ExistingBlock blockAt = world.getBlockAt(x, y, z);
+ boolean isSecondaryUseActive = ctx.getOptionalParameter(DirectContextParameters.PLAYER)
+ .map(Player::isSecondaryUseActive)
+ .orElse(false);
+ BlockStateWrapper wrapper = blockAt.blockState().cycleProperty(this.property, isSecondaryUseActive);
+ world.setBlockAt(x, y, z, wrapper, this.updateFlags.getInt(ctx));
+ }
+
+ @Override
+ public Key type() {
+ return CommonFunctions.CYCLE_BLOCK_PROPERTY;
+ }
+
+ public static class FactoryImpl extends AbstractFactory {
+
+ public FactoryImpl(java.util.function.Function