9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 18:39:20 +00:00

Add furniture spawn, remove, and replace functions

Introduces SpawnFurnitureFunction, RemoveFurnitureFunction, and ReplaceFurnitureFunction for event contexts, enabling dynamic furniture manipulation via context functions. Adds BlockStateHitBox type and registration, with cleanup logic in BukkitFurniture. Updates parameter providers and function registries to support new features.
This commit is contained in:
Arubik
2025-07-03 11:27:06 -05:00
parent 7aedabc638
commit b8ed85e519
10 changed files with 560 additions and 23 deletions

View File

@@ -1,5 +1,32 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.Vector;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ItemDisplay;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
@@ -8,7 +35,16 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.Collider;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.ExternalModel;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.furniture.FurnitureElement;
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
import net.momirealms.craftengine.core.entity.furniture.HitBox;
import net.momirealms.craftengine.core.entity.furniture.Seat;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.ArrayUtils;
import net.momirealms.craftengine.core.util.Key;
@@ -16,18 +52,6 @@ import net.momirealms.craftengine.core.util.QuaternionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.*;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
public class BukkitFurniture implements Furniture {
private final Key id;
@@ -184,6 +208,12 @@ public class BukkitFurniture implements Furniture {
if (!isValid()) {
return;
}
// Clean up BlockStateHitBoxes before destroying the furniture
for (HitBox hitBox : this.hitBoxes.values()) {
if (hitBox instanceof net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BlockStateHitBox) {
((net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BlockStateHitBox) hitBox).removePlacedBlock();
}
}
this.baseEntity().remove();
for (Collider entity : this.colliderEntities) {
if (entity != null)

View File

@@ -0,0 +1,201 @@
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.bukkit.Material;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.entity.furniture.AbstractHitBox;
import net.momirealms.craftengine.core.entity.furniture.Collider;
import net.momirealms.craftengine.core.entity.furniture.HitBox;
import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory;
import net.momirealms.craftengine.core.entity.furniture.HitBoxTypes;
import net.momirealms.craftengine.core.entity.furniture.Seat;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.LazyReference;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
public class BlockStateHitBox extends AbstractHitBox {
public static final Factory FACTORY = new Factory();
private final LazyReference<BlockStateWrapper> lazyBlockState;
private final boolean dropContainer;
private WorldPosition placedPosition;
private BlockStateWrapper originalBlockState;
public BlockStateHitBox(Seat[] seats, Vector3f position, LazyReference<BlockStateWrapper> lazyBlockState,
boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile, boolean dropContainer) {
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
this.lazyBlockState = lazyBlockState;
this.dropContainer = dropContainer;
}
public LazyReference<BlockStateWrapper> blockState() {
return lazyBlockState;
}
public boolean dropContainer() {
return dropContainer;
}
@Override
public Key type() {
return HitBoxTypes.BLOCKSTATE;
}
@Override
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated,
BiConsumer<Object, Boolean> packets, Consumer<Collider> collider,
BiConsumer<Integer, AABB> aabb) {
Vector3f offset = conjugated.transform(new Vector3f(position()));
World world = position.world();
int blockX = (int) Math.floor(position.x() + offset.x);
int blockY = (int) Math.floor(position.y() + offset.y);
int blockZ = (int) Math.floor(position.z() - offset.z);
// Store the placed position for later removal
this.placedPosition = new WorldPosition(world, blockX, blockY, blockZ);
// Store the original block state before placing our block
try {
// Get the bukkit block data from the world
org.bukkit.World bukkitWorld = (org.bukkit.World) world.platformWorld();
org.bukkit.block.data.BlockData blockData = bukkitWorld.getBlockAt(blockX, blockY, blockZ).getBlockData();
this.originalBlockState = BlockStateUtils.toPackedBlockState(blockData);
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to get original block state", e);
// Fallback to air
this.originalBlockState = CraftEngine.instance().blockManager().createPackedBlockState("minecraft:air");
}
// Place the block
BlockStateWrapper blockStateWrapper = lazyBlockState.get();
if (blockStateWrapper != null) {
world.setBlockAt(blockX, blockY, blockZ, blockStateWrapper, 3); // UPDATE_ALL flags
}
// If the block can be used on, add AABB for interaction
if (canUseItemOn()) {
aabb.accept(entityId[0], new AABB(blockX, blockY, blockZ, blockX + 1, blockY + 1, blockZ + 1));
}
}
@Override
public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<AABB> aabbs) {
if (blocksBuilding()) {
Vector3f offset = conjugated.transform(new Vector3f(position()));
int blockX = (int) Math.floor(x + offset.x);
int blockY = (int) Math.floor(y + offset.y);
int blockZ = (int) Math.floor(z - offset.z);
aabbs.accept(new AABB(blockX, blockY, blockZ, blockX + 1, blockY + 1, blockZ + 1));
}
}
@Override
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
return new int[] {entityIdSupplier.get()};
}
/**
* Removes the placed block and handles container drops if needed
*/
public void removePlacedBlock() {
if (placedPosition == null) return;
World world = placedPosition.world();
int x = (int) placedPosition.x();
int y = (int) placedPosition.y();
int z = (int) placedPosition.z();
// Drop container contents if the flag is enabled
if (dropContainer) {
dropContainerContents(world, x, y, z);
}
// Restore the original block state
if (originalBlockState != null) {
world.setBlockAt(x, y, z, originalBlockState, 3);
} else {
// Fallback to air if no original state was stored
BlockStateWrapper airState = CraftEngine.instance().blockManager().createPackedBlockState("minecraft:air");
if (airState != null) {
world.setBlockAt(x, y, z, airState, 3);
}
}
}
/**
* Drops the contents of a container block
*/
private void dropContainerContents(World world, int x, int y, int z) {
try {
// Get the bukkit world and block
org.bukkit.World bukkitWorld = (org.bukkit.World) world.platformWorld();
if (bukkitWorld == null) return;
org.bukkit.block.Block block = bukkitWorld.getBlockAt(x, y, z);
if (block.getState() instanceof InventoryHolder inventoryHolder) {
org.bukkit.inventory.Inventory inventory = inventoryHolder.getInventory();
org.bukkit.Location dropLocation = block.getLocation().add(0.5, 0.5, 0.5);
// Drop all items in the inventory
for (ItemStack itemStack : inventory.getContents()) {
if (itemStack != null && itemStack.getType() != Material.AIR) {
bukkitWorld.dropItemNaturally(dropLocation, itemStack);
}
}
// Clear the inventory
inventory.clear();
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to drop container contents for BlockStateHitBox", e);
}
}
public static class Factory implements HitBoxFactory {
@Override
public HitBox create(Map<String, Object> arguments) {
Vector3f position = net.momirealms.craftengine.core.util.MiscUtils.getAsVector3f(
arguments.getOrDefault("position", "0"), "position");
String blockStateString = ResourceConfigUtils.requireNonEmptyStringOrThrow(
arguments.get("block-state"), "warning.config.furniture.hitbox.blockstate.missing_block_state");
boolean canUseOn = ResourceConfigUtils.getAsBoolean(
arguments.getOrDefault("can-use-item-on", false), "can-use-item-on");
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(
arguments.getOrDefault("blocks-building", true), "blocks-building");
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(
arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile");
boolean dropContainer = ResourceConfigUtils.getAsBoolean(
arguments.getOrDefault("drop-container", true), "drop-container");
LazyReference<BlockStateWrapper> lazyBlockState = LazyReference.lazyReference(
() -> CraftEngine.instance().blockManager().createPackedBlockState(blockStateString));
return new BlockStateHitBox(
HitBoxFactory.getSeats(arguments),
position,
lazyBlockState,
canUseOn,
blocksBuilding,
canBeHitByProjectile,
dropContainer
);
}
}
}

View File

@@ -11,5 +11,6 @@ public class BukkitHitBoxTypes extends HitBoxTypes {
register(SHULKER, ShulkerHitBox.FACTORY);
register(HAPPY_GHAST, HappyGhastHitBox.FACTORY);
register(CUSTOM, CustomHitBox.FACTORY);
register(BLOCKSTATE, BlockStateHitBox.FACTORY);
}
}

View File

@@ -1,5 +1,8 @@
package net.momirealms.craftengine.core.entity.furniture;
import java.util.Map;
import java.util.Optional;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
@@ -8,14 +11,12 @@ import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
import java.util.Map;
import java.util.Optional;
public class HitBoxTypes {
public static final Key INTERACTION = Key.of("minecraft:interaction");
public static final Key SHULKER = Key.of("minecraft:shulker");
public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast");
public static final Key CUSTOM = Key.of("minecraft:custom");
public static final Key BLOCKSTATE = Key.of("minecraft:blockstate");
public static void register(Key key, HitBoxFactory factory) {
Holder.Reference<HitBoxFactory> holder = ((WritableRegistry<HitBoxFactory>) BuiltInRegistries.HITBOX_FACTORY)

View File

@@ -1,7 +1,40 @@
package net.momirealms.craftengine.core.plugin.context.event;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.*;
import net.momirealms.craftengine.core.plugin.context.function.ActionBarFunction;
import net.momirealms.craftengine.core.plugin.context.function.BreakBlockFunction;
import net.momirealms.craftengine.core.plugin.context.function.CancelEventFunction;
import net.momirealms.craftengine.core.plugin.context.function.CommandFunction;
import net.momirealms.craftengine.core.plugin.context.function.CommonFunctions;
import net.momirealms.craftengine.core.plugin.context.function.DropLootFunction;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory;
import net.momirealms.craftengine.core.plugin.context.function.LevelerExpFunction;
import net.momirealms.craftengine.core.plugin.context.function.MessageFunction;
import net.momirealms.craftengine.core.plugin.context.function.OpenWindowFunction;
import net.momirealms.craftengine.core.plugin.context.function.ParticleFunction;
import net.momirealms.craftengine.core.plugin.context.function.PlaceBlockFunction;
import net.momirealms.craftengine.core.plugin.context.function.PlaySoundFunction;
import net.momirealms.craftengine.core.plugin.context.function.PotionEffectFunction;
import net.momirealms.craftengine.core.plugin.context.function.RemoveCooldownFunction;
import net.momirealms.craftengine.core.plugin.context.function.RemoveFurnitureFunction;
import net.momirealms.craftengine.core.plugin.context.function.RemovePotionEffectFunction;
import net.momirealms.craftengine.core.plugin.context.function.ReplaceFurnitureFunction;
import net.momirealms.craftengine.core.plugin.context.function.RunFunction;
import net.momirealms.craftengine.core.plugin.context.function.SetCooldownFunction;
import net.momirealms.craftengine.core.plugin.context.function.SetCountFunction;
import net.momirealms.craftengine.core.plugin.context.function.SetFoodFunction;
import net.momirealms.craftengine.core.plugin.context.function.SetSaturationFunction;
import net.momirealms.craftengine.core.plugin.context.function.SpawnFurnitureFunction;
import net.momirealms.craftengine.core.plugin.context.function.SwingHandFunction;
import net.momirealms.craftengine.core.plugin.context.function.TitleFunction;
import net.momirealms.craftengine.core.plugin.context.function.UpdateInteractionFunction;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
@@ -12,8 +45,6 @@ import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.ResourceKey;
import java.util.*;
public class EventFunctions {
static {
@@ -39,6 +70,9 @@ public class EventFunctions {
register(CommonFunctions.LEVELER_EXP, new LevelerExpFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.SET_COOLDOWN, new SetCooldownFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.REMOVE_COOLDOWN, new RemoveCooldownFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.SPAWN_FURNITURE, new SpawnFurnitureFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.REMOVE_FURNITURE, new RemoveFurnitureFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.REPLACE_FURNITURE, new ReplaceFurnitureFunction.FactoryImpl<>(EventConditions::fromMap));
}
public static void register(Key key, FunctionFactory<PlayerOptionalContext> factory) {

View File

@@ -27,4 +27,7 @@ public final class CommonFunctions {
public static final Key DROP_LOOT = Key.of("craftengine:drop_loot");
public static final Key SWING_HAND = Key.of("craftengine:swing_hand");
public static final Key LEVELER_EXP = Key.of("craftengine:leveler_exp");
public static final Key SPAWN_FURNITURE = Key.of("craftengine:spawn_furniture");
public static final Key REMOVE_FURNITURE = Key.of("craftengine:remove_furniture");
public static final Key REPLACE_FURNITURE = Key.of("craftengine:replace_furniture");
}

View File

@@ -0,0 +1,60 @@
package net.momirealms.craftengine.core.plugin.context.function;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.WorldPosition;
public class RemoveFurnitureFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final boolean dropLoot;
private final boolean playSound;
public RemoveFurnitureFunction(boolean dropLoot, boolean playSound, List<Condition<CTX>> predicates) {
super(predicates);
this.dropLoot = dropLoot;
this.playSound = playSound;
}
@Override
public void runInternal(CTX ctx) {
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
if (optionalWorldPosition.isPresent()) {
// Buscar muebles en el contexto
Optional<Furniture> optionalFurniture = ctx.getOptionalParameter(DirectContextParameters.FURNITURE);
if (optionalFurniture.isPresent()) {
Furniture furniture = optionalFurniture.get();
if (furniture.isValid()) {
furniture.destroy();
// TODO: Implementar lógica para dropear loot y reproducir sonidos
// usando this.dropLoot y this.playSound cuando sea necesario
}
}
}
}
@Override
public Key type() {
return CommonFunctions.REMOVE_FURNITURE;
}
public static class FactoryImpl<CTX extends Context> extends AbstractFactory<CTX> {
public FactoryImpl(java.util.function.Function<Map<String, Object>, Condition<CTX>> factory) {
super(factory);
}
@Override
public Function<CTX> create(Map<String, Object> arguments) {
boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot");
boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound");
return new RemoveFurnitureFunction<>(dropLoot, playSound, getPredicates(arguments));
}
}
}

View File

@@ -0,0 +1,109 @@
package net.momirealms.craftengine.core.plugin.context.function;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.plugin.CraftEngine;
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.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.WorldPosition;
public class ReplaceFurnitureFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final Key newFurnitureId;
private final NumberProvider x;
private final NumberProvider y;
private final NumberProvider z;
private final NumberProvider pitch;
private final NumberProvider yaw;
private final AnchorType anchorType;
private final boolean dropLoot;
private final boolean playSound;
public ReplaceFurnitureFunction(Key newFurnitureId, NumberProvider x, NumberProvider y, NumberProvider z,
NumberProvider pitch, NumberProvider yaw, AnchorType anchorType,
boolean dropLoot, boolean playSound, List<Condition<CTX>> predicates) {
super(predicates);
this.newFurnitureId = newFurnitureId;
this.x = x;
this.y = y;
this.z = z;
this.pitch = pitch;
this.yaw = yaw;
this.anchorType = anchorType;
this.dropLoot = dropLoot;
this.playSound = playSound;
}
@Override
public void runInternal(CTX ctx) {
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
Optional<Furniture> optionalOldFurniture = ctx.getOptionalParameter(DirectContextParameters.FURNITURE);
if (optionalWorldPosition.isPresent() && optionalOldFurniture.isPresent()) {
Furniture oldFurniture = optionalOldFurniture.get();
// Obtener la nueva posición o usar la actual del mueble
double xPos = this.x.getDouble(ctx);
double yPos = this.y.getDouble(ctx);
double zPos = this.z.getDouble(ctx);
float pitchValue = this.pitch.getFloat(ctx);
float yawValue = this.yaw.getFloat(ctx);
WorldPosition newPosition = new WorldPosition(optionalWorldPosition.get().world(), xPos, yPos, zPos, pitchValue, yawValue);
// Obtener el nuevo mueble
Optional<CustomFurniture> optionalNewFurniture = CraftEngine.instance().furnitureManager().furnitureById(this.newFurnitureId);
if (optionalNewFurniture.isPresent()) {
CustomFurniture newFurniture = optionalNewFurniture.get();
AnchorType anchor = this.anchorType != null ? this.anchorType : newFurniture.getAnyAnchorType();
// Remover el mueble antiguo
if (oldFurniture.isValid()) {
oldFurniture.destroy();
// TODO: Implementar lógica para dropear loot usando this.dropLoot
}
// Colocar el nuevo mueble
FurnitureExtraData extraData = FurnitureExtraData.builder().anchorType(anchor).build();
CraftEngine.instance().furnitureManager().place(newPosition, newFurniture, extraData, this.playSound);
}
}
}
@Override
public Key type() {
return CommonFunctions.REPLACE_FURNITURE;
}
public static class FactoryImpl<CTX extends Context> extends AbstractFactory<CTX> {
public FactoryImpl(java.util.function.Function<Map<String, Object>, Condition<CTX>> factory) {
super(factory);
}
@Override
public Function<CTX> create(Map<String, Object> arguments) {
String furnitureIdStr = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("furniture-id"), "warning.config.function.replace_furniture.missing_furniture_id");
Key furnitureId = Key.of(furnitureIdStr);
NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", "<arg:position.x>"));
NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>"));
NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>"));
NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "<arg:pitch>"));
NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "<arg:yaw>"));
AnchorType anchorType = Optional.ofNullable(arguments.get("anchor-type")).map(o -> AnchorType.valueOf(o.toString().toUpperCase())).orElse(null);
boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot");
boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound");
return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments));
}
}
}

View File

@@ -0,0 +1,93 @@
package net.momirealms.craftengine.core.plugin.context.function;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.plugin.CraftEngine;
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.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
public class SpawnFurnitureFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final Key furnitureId;
private final NumberProvider x;
private final NumberProvider y;
private final NumberProvider z;
private final NumberProvider pitch;
private final NumberProvider yaw;
private final AnchorType anchorType;
private final boolean playSound;
public SpawnFurnitureFunction(Key furnitureId, NumberProvider x, NumberProvider y, NumberProvider z,
NumberProvider pitch, NumberProvider yaw, AnchorType anchorType,
boolean playSound, List<Condition<CTX>> predicates) {
super(predicates);
this.furnitureId = furnitureId;
this.x = x;
this.y = y;
this.z = z;
this.pitch = pitch;
this.yaw = yaw;
this.anchorType = anchorType;
this.playSound = playSound;
}
@Override
public void runInternal(CTX ctx) {
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
if (optionalWorldPosition.isPresent()) {
World world = optionalWorldPosition.get().world();
double xPos = this.x.getDouble(ctx);
double yPos = this.y.getDouble(ctx);
double zPos = this.z.getDouble(ctx);
float pitchValue = this.pitch.getFloat(ctx);
float yawValue = this.yaw.getFloat(ctx);
WorldPosition position = new WorldPosition(world, xPos, yPos, zPos, pitchValue, yawValue);
Optional<CustomFurniture> optionalFurniture = CraftEngine.instance().furnitureManager().furnitureById(this.furnitureId);
if (optionalFurniture.isPresent()) {
CustomFurniture furniture = optionalFurniture.get();
AnchorType anchor = this.anchorType != null ? this.anchorType : furniture.getAnyAnchorType();
FurnitureExtraData extraData = FurnitureExtraData.builder().anchorType(anchor).build();
CraftEngine.instance().furnitureManager().place(position, furniture, extraData, this.playSound);
}
}
}
@Override
public Key type() {
return CommonFunctions.SPAWN_FURNITURE;
}
public static class FactoryImpl<CTX extends Context> extends AbstractFactory<CTX> {
public FactoryImpl(java.util.function.Function<Map<String, Object>, Condition<CTX>> factory) {
super(factory);
}
@Override
public Function<CTX> create(Map<String, Object> arguments) {
String furnitureIdStr = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("furniture-id"), "warning.config.function.spawn_furniture.missing_furniture_id");
Key furnitureId = Key.of(furnitureIdStr);
NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", "<arg:position.x>"));
NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>"));
NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>"));
NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "<arg:pitch>"));
NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "<arg:yaw>"));
AnchorType anchorType = Optional.ofNullable(arguments.get("anchor-type")).map(o -> AnchorType.valueOf(o.toString().toUpperCase())).orElse(null);
boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound");
return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, playSound, getPredicates(arguments));
}
}
}

View File

@@ -1,20 +1,25 @@
package net.momirealms.craftengine.core.plugin.context.parameter;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
public class FurnitureParameterProvider implements ChainParameterProvider<Furniture> {
private static final Map<ContextKey<?>, Function<Furniture, Object>> CONTEXT_FUNCTIONS = new HashMap<>();
static {
CONTEXT_FUNCTIONS.put(DirectContextParameters.ID, Furniture::id);
CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Furniture::uuid);
CONTEXT_FUNCTIONS.put(DirectContextParameters.ANCHOR_TYPE, Furniture::anchorType);
CONTEXT_FUNCTIONS.put(DirectContextParameters.X, furniture -> furniture.position().x());
CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, furniture -> furniture.position().y());
CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, furniture -> furniture.position().z());
CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, furniture -> furniture.position().yRot());
CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, furniture -> furniture.position().xRot());
}
@SuppressWarnings("unchecked")