9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-23 17:09:19 +00:00

feat(block): 实现粒子效果

This commit is contained in:
jhqwqmc
2025-09-14 14:26:38 +08:00
parent 8032d15470
commit e28c29aef9
15 changed files with 242 additions and 13 deletions

View File

@@ -34,6 +34,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key BOUNCING_BLOCK = Key.from("craftengine:bouncing_block");
public static final Key DIRECTIONAL_ATTACHED_BLOCK = Key.from("craftengine:directional_attached_block");
public static final Key LIQUID_FLOWABLE_BLOCK = Key.from("craftengine:liquid_flowable_block");
public static final Key PARTICLE_BLOCK = Key.from("craftengine:particle_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -66,5 +67,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(BOUNCING_BLOCK, BouncingBlockBehavior.FACTORY);
register(DIRECTIONAL_ATTACHED_BLOCK, DirectionalAttachedBlockBehavior.FACTORY);
register(LIQUID_FLOWABLE_BLOCK, LiquidFlowableBlockBehavior.FACTORY);
register(PARTICLE_BLOCK, ParticleBlockBehavior.FACTORY);
}
}

View File

@@ -0,0 +1,75 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.BaseParticleBlockEntity;
import net.momirealms.craftengine.bukkit.block.entity.ParticleBlockEntity;
import net.momirealms.craftengine.bukkit.block.entity.WallParticleBlockEntity;
import net.momirealms.craftengine.core.block.BlockBehavior;
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.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.Vec3d;
import java.util.List;
import java.util.Map;
public class ParticleBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior {
public static final Factory FACTORY = new Factory();
private final List<ParticleData> particles;
private final boolean inWall;
public ParticleBlockBehavior(CustomBlock customBlock, List<ParticleData> particles, boolean inWall) {
super(customBlock);
this.particles = particles;
this.inWall = inWall;
}
public List<ParticleData> particles() {
return particles;
}
@Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
return EntityBlockBehavior.blockEntityTypeHelper(this.inWall ? BukkitBlockEntityTypes.WALL_PARTICLE : BukkitBlockEntityTypes.PARTICLE);
}
@Override
public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) {
return this.inWall ? new WallParticleBlockEntity(pos, state) : new ParticleBlockEntity(pos, state);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
if (this.particles().isEmpty()) return null;
return EntityBlockBehavior.createTickerHelper(BaseParticleBlockEntity::tick);
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
List<ParticleData> particles = ResourceConfigUtils.parseConfigAsList(arguments.getOrDefault("particles", List.of()), ParticleData::fromMap);
boolean inWall = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("in-wall", false), "in-wall");
return new ParticleBlockBehavior(block, particles, inWall);
}
}
public record ParticleData(Key particle, Vec3d locationOffset, Vec3d offset, int count, double speed) {
public static ParticleData fromMap(Map<String, Object> arguments) {
Key particle = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("type"), "warning.config.block.behavior.particle.missing_type"));
Vec3d locationOffset = ResourceConfigUtils.getAsVec3d(arguments.get("location-offset"), "location-offset");
Vec3d offset = ResourceConfigUtils.getAsVec3d(arguments.get("offset"), "offset");
int count = ResourceConfigUtils.getAsInt(arguments.getOrDefault("count", 1), "count");
double speed = ResourceConfigUtils.getAsDouble(arguments.get("speed"), "speed");
return new ParticleData(particle, locationOffset, offset, count, speed);
}
}
}

View File

@@ -105,10 +105,9 @@ public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements E
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
return (BlockEntityType<T>) BukkitBlockEntityTypes.SIMPLE_STORAGE;
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_STORAGE);
}
@Override

View File

@@ -0,0 +1,27 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.block.behavior.ParticleBlockBehavior;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.World;
public abstract class BaseParticleBlockEntity extends BlockEntity {
protected final ParticleBlockBehavior behavior;
protected int tickCount;
public BaseParticleBlockEntity(BlockEntityType<? extends BlockEntity> type, BlockPos pos, ImmutableBlockState blockState) {
super(type, pos, blockState);
this.behavior = super.blockState.behavior().getAs(ParticleBlockBehavior.class).orElseThrow();
}
public static void tick(CEWorld ceWorld, BlockPos blockPos, ImmutableBlockState state, BaseParticleBlockEntity particle) {
particle.tickCount++;
if (particle.tickCount % 10 != 0) return;
particle.animateTick(state, ceWorld.world(), blockPos);
}
public abstract void animateTick(ImmutableBlockState state, World level, BlockPos pos);
}

View File

@@ -6,4 +6,6 @@ import net.momirealms.craftengine.core.block.entity.BlockEntityTypes;
public class BukkitBlockEntityTypes extends BlockEntityTypes {
public static final BlockEntityType<SimpleStorageBlockEntity> SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new);
public static final BlockEntityType<ParticleBlockEntity> PARTICLE = register(BlockEntityTypeKeys.PARTICLE, ParticleBlockEntity::new);
public static final BlockEntityType<WallParticleBlockEntity> WALL_PARTICLE = register(BlockEntityTypeKeys.WALL_PARTICLE, WallParticleBlockEntity::new);
}

View File

@@ -0,0 +1,31 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.block.behavior.ParticleBlockBehavior;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
public class ParticleBlockEntity extends BaseParticleBlockEntity {
public ParticleBlockEntity(BlockPos pos, ImmutableBlockState blockState) {
super(BukkitBlockEntityTypes.PARTICLE, pos, blockState);
}
@Override
public void animateTick(ImmutableBlockState state, World level, BlockPos pos) {
for (ParticleBlockBehavior.ParticleData particle : behavior.particles()) {
Vec3d location = particle.locationOffset().add(pos.x(), pos.y(), pos.z());
level.spawnParticle(
location,
particle.particle(),
particle.count(),
particle.offset().x(),
particle.offset().y(),
particle.offset().z(),
particle.speed(),
null, null
);
}
}
}

View File

@@ -0,0 +1,51 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.block.behavior.ParticleBlockBehavior;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
public class WallParticleBlockEntity extends BaseParticleBlockEntity {
public WallParticleBlockEntity(BlockPos pos, ImmutableBlockState blockState) {
super(BukkitBlockEntityTypes.WALL_PARTICLE, pos, blockState);
}
@Override
public void animateTick(ImmutableBlockState state, World level, BlockPos pos) {
Direction direction = null;
for (Property<?> property : state.getProperties()) {
if (!property.name().equals("facing")) continue;
if (property.valueClass() == Direction.class) {
direction = (Direction) state.get(property);
break;
} else if (property.valueClass() == HorizontalDirection.class) {
direction = ((HorizontalDirection) state.get(property)).toDirection();
break;
}
}
if (direction != null) {
direction = direction.opposite();
}
for (ParticleBlockBehavior.ParticleData particle : behavior.particles()) {
Vec3d location = particle.locationOffset().add(pos.x(), pos.y(), pos.z());
if (direction != null) {
location = location.add(0.27 * direction.stepX(), 0.22, 0.27 * direction.stepZ());
}
level.spawnParticle(
location,
particle.particle(),
particle.count(),
particle.offset().x(),
particle.offset().y(),
particle.offset().z(),
particle.speed(),
null, null
);
}
}
}

View File

@@ -106,11 +106,11 @@ public class BukkitWorld implements World {
}
@Override
public void spawnParticle(Position location, Key particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @NotNull Context context) {
public void spawnParticle(Position location, Key particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @Nullable Context context) {
Particle particleType = ParticleUtils.getParticle(particle);
if (particleType == null) return;
org.bukkit.World platformWorld = platformWorld();
platformWorld.spawnParticle(particleType, location.x(), location.y(), location.z(), count, xOffset, yOffset, zOffset, speed, extraData == null ? null : ParticleUtils.toBukkitParticleData(extraData, context, platformWorld, location.x(), location.y(), location.z()));
platformWorld.spawnParticle(particleType, location.x(), location.y(), location.z(), count, xOffset, yOffset, zOffset, speed, extraData == null || context == null ? null : ParticleUtils.toBukkitParticleData(extraData, context, platformWorld, location.x(), location.y(), location.z()));
}
@Override

View File

@@ -70,6 +70,12 @@ blocks:
support-types:
- center
- type: liquid_flowable_block
- type: particle_block
particles:
- type: smoke
location-offset: 0.5,0.7,0.5
- type: flame
location-offset: 0.5,0.7,0.5
default:amethyst_wall_torch:
loot:
template: default:loot_table/basic
@@ -88,6 +94,13 @@ blocks:
behavior:
- type: directional_attached_block
- type: liquid_flowable_block
- type: particle_block
in-wall: true
particles:
- type: smoke
location-offset: 0.5,0.7,0.5
- type: flame
location-offset: 0.5,0.7,0.5
states:
properties:
facing:

View File

@@ -73,6 +73,7 @@ warning.config.type.float: "<yellow>Issue found in file <arg:0> - Failed to load
warning.config.type.double: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to double type for option '<arg:3>'.</yellow>"
warning.config.type.quaternionf: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Quaternionf type for option '<arg:3>'.</yellow>"
warning.config.type.vector3f: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Vector3f type for option '<arg:3>'.</yellow>"
warning.config.type.vec3d: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Vec3d type for option '<arg:3>'.</yellow>"
warning.config.type.map: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Cannot cast '<arg:2>' to Map type for option '<arg:3>'.</yellow>"
warning.config.type.snbt.invalid_syntax: "<yellow>Issue found in file <arg:0> - Failed to load '<arg:1>': Invalid snbt syntax '<arg:2>'.</yellow>"
warning.config.number.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for number argument.</yellow>"
@@ -304,10 +305,10 @@ warning.config.block.behavior.trapdoor.missing_open: "<yellow>Issue found in fil
warning.config.block.behavior.trapdoor.missing_powered: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'powered' property for 'trapdoor_block' behavior.</yellow>"
warning.config.block.behavior.stackable.missing_property: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required '<arg:2>' property for 'stackable_block' behavior.</yellow>"
warning.config.block.behavior.stackable.missing_items: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'items' argument for 'stackable_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_facing: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'facing' argument for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_in_wall: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'in_wall' argument for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_open: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'powered' argument for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_powered: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'open' argument for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_facing: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'facing' property for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_in_wall: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'in_wall' property for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_open: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'powered' property for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.fence_gate.missing_powered: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'open' property for 'fence_gate_block' behavior.</yellow>"
warning.config.block.behavior.slab.missing_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'type' property for 'slab_block' behavior.</yellow>"
warning.config.block.behavior.stairs.missing_facing: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'facing' property for 'stairs_block' behavior.</yellow>"
warning.config.block.behavior.stairs.missing_half: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'half' property for 'stairs_block' behavior.</yellow>"
@@ -317,8 +318,9 @@ warning.config.block.behavior.sofa.missing_shape: "<yellow>Issue found in file <
warning.config.block.behavior.pressure_plate.missing_powered: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'powered' property for 'pressure_plate_block' behavior.</yellow>"
warning.config.block.behavior.grass.missing_feature: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'feature' argument for 'grass_block' behavior.</yellow>"
warning.config.block.behavior.double_high.missing_half: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'half' property for 'double_block' behavior.</yellow>"
warning.config.block.behavior.change_over_time.missing_next_block: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'next_block' property for 'change_over_time_block' behavior.</yellow>"
warning.config.block.behavior.change_over_time.missing_next_block: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'next_block' argument for 'change_over_time_block' behavior.</yellow>"
warning.config.block.behavior.surface_attached.missing_facing: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'facing' property for 'surface_attached_block' behavior.</yellow>"
warning.config.block.behavior.particle.missing_type: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'type' argument for 'particle_block' behavior.</yellow>"
warning.config.model.generation.missing_parent: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'parent' argument in 'generation' section.</yellow>"
warning.config.model.generation.conflict: "<yellow>Issue found in file <arg:0> - Failed to generate model for '<arg:1>' as two or more configurations attempt to generate different json models with the same path: '<arg:2>'.</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid display position '<arg:2>' in 'generation.display' section. Allowed display positions: [<arg:3>]</yellow>"

View File

@@ -73,6 +73,7 @@ warning.config.type.float: "<yellow>在文件 <arg:0> 发现问题 - 无法加
warning.config.type.double: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为双精度类型 (选项 '<arg:3>')</yellow>"
warning.config.type.quaternionf: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为四元数类型 (选项 '<arg:3>')</yellow>"
warning.config.type.vector3f: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为三维向量类型 (选项 '<arg:3>')</yellow>"
warning.config.type.vec3d: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为双精度浮点数三维向量类型 (选项 '<arg:3>')</yellow>"
warning.config.type.map: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无法将 '<arg:2>' 转换为映射类型 (选项 '<arg:3>')</yellow>"
warning.config.type.snbt.invalid_syntax: "<yellow>在文件 <arg:0> 发现问题 - 无法加载 '<arg:1>': 无效的 SNBT 语法 '<arg:2>'</yellow>"
warning.config.number.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少数字类型所需的 'type' 参数</yellow>"
@@ -311,8 +312,9 @@ warning.config.block.behavior.sofa.missing_shape: "<yellow>在文件 <arg:0> 发
warning.config.block.behavior.pressure_plate.missing_powered: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'pressure_plate_block' 行为缺少必需的 'powered' 属性</yellow>"
warning.config.block.behavior.grass.missing_feature: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'grass_block' 行为缺少必需的 'feature' 参数</yellow>"
warning.config.block.behavior.double_high.missing_half: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'double_block' 行为缺少必需的 'half' 属性</yellow>"
warning.config.block.behavior.change_over_time.missing_next_block: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'change_over_time_block' 行为缺少必需的 'next-block' 配置项</yellow>"
warning.config.block.behavior.change_over_time.missing_next_block: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'change_over_time_block' 行为缺少必需的 'next-block' 参数</yellow>"
warning.config.block.behavior.surface_attached.missing_facing: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'surface_attached_block' 行为缺少必需的 'facing' 属性</yellow>"
warning.config.block.behavior.particle.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 'particle' 段落缺少必需的 'type' 参数</yellow>"
warning.config.model.generation.missing_parent: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 'generation' 段落缺少必需的 'parent' 参数</yellow>"
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 发现问题 - 无法为 '<arg:1>' 生成模型 存在多个配置尝试使用相同路径 '<arg:2>' 生成不同的 JSON 模型</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 在 'generation.display' 区域使用了无效的 display 位置类型 '<arg:2>'. 可用展示类型: [<arg:3>]</yellow>"

View File

@@ -20,7 +20,12 @@ public interface EntityBlockBehavior {
}
@SuppressWarnings("unchecked")
static <E extends BlockEntity> BlockEntityTicker<E> createTickerHelper(BlockEntityTicker<? super E> ticker) {
static <E extends BlockEntity, T extends BlockEntity> BlockEntityTicker<E> createTickerHelper(BlockEntityTicker<? super T> ticker) {
return (BlockEntityTicker<E>) ticker;
}
@SuppressWarnings("unchecked")
static <E extends BlockEntity> BlockEntityType<E> blockEntityTypeHelper(BlockEntityType<?> type) {
return (BlockEntityType<E>) type;
}
}

View File

@@ -7,5 +7,6 @@ public final class BlockEntityTypeKeys {
public static final Key UNSAFE_COMPOSITE = Key.of("craftengine:unsafe_composite");
public static final Key SIMPLE_STORAGE = Key.of("craftengine:simple_storage");
public static final Key SEAT = Key.of("craftengine:seat");
public static final Key PARTICLE = Key.of("craftengine:particle");
public static final Key WALL_PARTICLE = Key.of("craftengine:wall_particle");
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.util;
import com.mojang.datafixers.util.Either;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.world.Vec3d;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -261,4 +262,22 @@ public final class ResourceConfigUtils {
}
}
}
public static Vec3d getAsVec3d(Object o, String option) {
if (o == null) return new Vec3d(0, 0, 0);
if (o instanceof List<?> list && list.size() == 3) {
return new Vec3d(Double.parseDouble(list.get(0).toString()), Double.parseDouble(list.get(1).toString()), Double.parseDouble(list.get(2).toString()));
} else {
String stringFormat = o.toString();
String[] split = stringFormat.split(",");
if (split.length == 3) {
return new Vec3d(Double.parseDouble(split[0]), Double.parseDouble(split[1]), Double.parseDouble(split[2]));
} else if (split.length == 1) {
double d = Double.parseDouble(split[0]);
return new Vec3d(d, d, d);
} else {
throw new LocalizedResourceConfigException("warning.config.type.vec3d", stringFormat, option);
}
}
}
}

View File

@@ -58,7 +58,7 @@ public interface World {
void levelEvent(int id, BlockPos pos, int data);
void spawnParticle(Position location, Key particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @NotNull Context context);
void spawnParticle(Position location, Key particle, int count, double xOffset, double yOffset, double zOffset, double speed, @Nullable ParticleData extraData, @Nullable Context context);
long time();