9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00
This commit is contained in:
XiaoMoMi
2025-12-04 05:57:51 +08:00
8 changed files with 164 additions and 13 deletions

View File

@@ -47,6 +47,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key SURFACE_SPREADING_BLOCK = Key.from("craftengine:surface_spreading_block"); public static final Key SURFACE_SPREADING_BLOCK = Key.from("craftengine:surface_spreading_block");
public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block"); public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block");
public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block");
public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block");
public static void init() { public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -92,5 +93,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(SURFACE_SPREADING_BLOCK, SurfaceSpreadingBlockBehavior.FACTORY); register(SURFACE_SPREADING_BLOCK, SurfaceSpreadingBlockBehavior.FACTORY);
register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY); register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY);
register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY);
register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY);
} }
} }

View File

@@ -0,0 +1,104 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
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.world.BukkitWorldManager;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.BlockSettings;
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.loot.LootContext;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.condition.AllOfCondition;
import net.momirealms.craftengine.core.plugin.context.event.EventConditions;
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.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
public class DropExperienceBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final NumberProvider amount;
private final Condition<Context> conditions;
public DropExperienceBlockBehavior(CustomBlock customBlock, NumberProvider amount, Condition<Context> conditions) {
super(customBlock);
this.amount = amount;
this.conditions = conditions;
}
@Override
public void spawnAfterBreak(Object thisBlock, Object[] args, Callable<Object> superMethod) {
boolean dropExperience = (boolean) args[4]; // 通常来说是 false
Item<ItemStack> item = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(args[3]));
if (!dropExperience) {
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
if (state == null) {
return;
}
BlockSettings settings = state.settings();
if (settings.requireCorrectTool()) {
if (item.isEmpty()) {
return;
}
boolean cannotBreak = !settings.isCorrectTool(item.id())
&& (!settings.respectToolComponent()
|| !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(args[3], state.customBlockState().literalObject()));
if (cannotBreak) {
return;
}
}
}
World world = BukkitWorldManager.instance().wrap(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1]));
BlockPos pos = LocationUtils.fromBlockPos(args[2]);
tryDropExperience(world, pos, item);
}
private void tryDropExperience(World world, BlockPos pos, Item<ItemStack> item) {
Vec3d dropPos = Vec3d.atCenterOf(pos);
ContextHolder holder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, new WorldPosition(world, dropPos))
.withParameter(DirectContextParameters.ITEM_IN_HAND, item)
.build();
LootContext context = new LootContext(world, null, 1.0f, holder);
if (this.conditions != null && !this.conditions.test(context)) {
return;
}
int finalAmount = this.amount.getInt(context);
if (finalAmount <= 0) {
return;
}
world.dropExp(dropPos, finalAmount);
}
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 0));
Condition<Context> conditions = null;
List<Condition<Context>> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "conditions", "condition"), EventConditions::fromMap);
if (conditionList.size() == 1) {
conditions = conditionList.getFirst();
} else if (!conditionList.isEmpty()) {
conditions = new AllOfCondition<>(conditionList);
}
return new DropExperienceBlockBehavior(block, amount, conditions);
}
}
}

View File

@@ -173,7 +173,9 @@ public class SimpleStorageBlockEntity extends BlockEntity {
public void updateOpenBlockState(boolean open) { public void updateOpenBlockState(boolean open) {
ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos); ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos);
if (state == null || state.behavior() != this.behavior) return; if (state == null || state.behavior() != this.behavior) return;
Property<Boolean> property = this.behavior.openProperty(); SimpleStorageBlockBehavior behavior = state.behavior().getAs(SimpleStorageBlockBehavior.class).orElse(null);
if (behavior == null) return;
Property<Boolean> property = behavior.openProperty();
if (property == null) return; if (property == null) return;
super.world.world().setBlockState(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags()); super.world.world().setBlockState(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags());
} }

View File

@@ -27,13 +27,17 @@ items:
texture: minecraft:item/custom/topaz texture: minecraft:item/custom/topaz
blocks: blocks:
default:topaz_ore: default:topaz_ore:
behavior:
type: drop_experience_block
amount: 3~7
condition:
type: enchantment
predicate: minecraft:silk_touch<=0
loot: loot:
template: default:loot_table/ore template: default:loot_table/ore
arguments: arguments:
ore_drop: default:topaz ore_drop: default:topaz
ore_block: default:topaz_ore ore_block: default:topaz_ore
min_exp: 3
max_exp: 7
settings: settings:
template: default:settings/ore template: default:settings/ore
arguments: arguments:
@@ -45,13 +49,17 @@ blocks:
arguments: arguments:
path: minecraft:block/custom/topaz_ore path: minecraft:block/custom/topaz_ore
default:deepslate_topaz_ore: default:deepslate_topaz_ore:
behavior:
type: drop_experience_block
amount: 3~7
condition:
type: enchantment
predicate: minecraft:silk_touch<=0
loot: loot:
template: default:loot_table/ore template: default:loot_table/ore
arguments: arguments:
ore_drop: default:topaz ore_drop: default:topaz
ore_block: default:deepslate_topaz_ore ore_block: default:deepslate_topaz_ore
min_exp: 3
max_exp: 7
settings: settings:
template: default:settings/deepslate_ore template: default:settings/deepslate_ore
arguments: arguments:

View File

@@ -180,7 +180,6 @@ templates:
# ore_block: the ore block # ore_block: the ore block
# ore_drop: the drops of the ore material # ore_drop: the drops of the ore material
# ore_drop_count: the amount of the ore materials # ore_drop_count: the amount of the ore materials
# exp: the exp to drop
default:loot_table/ore: default:loot_table/ore:
pools: pools:
- rolls: 1 - rolls: 1
@@ -203,8 +202,6 @@ templates:
formula: formula:
type: ore_drops type: ore_drops
- type: explosion_decay - type: explosion_decay
- type: drop_exp
count: ${exp:-2~4}
# Using Silk Touch or shears will cause the leaves block itself to drop. # Using Silk Touch or shears will cause the leaves block itself to drop.
# Using Fortune, however, increases the drop rates of sticks and saplings. # Using Fortune, however, increases the drop rates of sticks and saplings.

View File

@@ -162,6 +162,7 @@ warning.config.recipe.smithing_transform.post_processor.missing_type: "<yellow>I
warning.config.recipe.smithing_transform.post_processor.invalid_type: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is using an invalid post processor type '<arg:2>'.</yellow>" warning.config.recipe.smithing_transform.post_processor.invalid_type: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is using an invalid post processor type '<arg:2>'.</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'components' for post-processors 'keep_components'.</yellow>" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'components' for post-processors 'keep_components'.</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'tags' for post-processors 'keep_tags'.</yellow>" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'tags' for post-processors 'keep_tags'.</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required argument 'paths' for post-processors 'keep_custom_data'.</yellow>"
warning.config.recipe.smithing_transform.missing_base: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required 'base' argument.</yellow>" warning.config.recipe.smithing_transform.missing_base: "<yellow>Issue found in file <arg:0> - The smithing transform recipe '<arg:1>' is missing the required 'base' argument.</yellow>"
warning.config.recipe.smithing_trim.missing_base: "<yellow>Issue found in file <arg:0> - The smithing trim recipe '<arg:1>' is missing the required 'base' argument.</yellow>" warning.config.recipe.smithing_trim.missing_base: "<yellow>Issue found in file <arg:0> - The smithing trim recipe '<arg:1>' is missing the required 'base' argument.</yellow>"
warning.config.recipe.smithing_trim.missing_template_type: "<yellow>Issue found in file <arg:0> - The smithing trim recipe '<arg:1>' is missing the required 'template-type' argument.</yellow>" warning.config.recipe.smithing_trim.missing_template_type: "<yellow>Issue found in file <arg:0> - The smithing trim recipe '<arg:1>' is missing the required 'template-type' argument.</yellow>"

View File

@@ -159,6 +159,7 @@ warning.config.recipe.smithing_transform.post_processor.missing_type: "<yellow>
warning.config.recipe.smithing_transform.post_processor.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 使用了无效的后处理器类型 '<arg:2>'</yellow>" warning.config.recipe.smithing_transform.post_processor.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 使用了无效的后处理器类型 '<arg:2>'</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的 'keep_components' 后处理器缺少必需的 'components' 参数</yellow>" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的 'keep_components' 后处理器缺少必需的 'components' 参数</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的 'keep_tags' 后处理器缺少必需的 'tags' 参数</yellow>" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的 'keep_tags' 后处理器缺少必需的 'tags' 参数</yellow>"
warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 的 'keep_custom_data' 后处理器缺少必需的 'paths' 参数</yellow>"
warning.config.recipe.smithing_transform.missing_base: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 缺少必需的 'base' 参数</yellow>" warning.config.recipe.smithing_transform.missing_base: "<yellow>在文件 <arg:0> 发现问题 - 锻造升级配方 '<arg:1>' 缺少必需的 'base' 参数</yellow>"
warning.config.recipe.smithing_trim.missing_base: "<yellow>在文件 <arg:0> 发现问题 - 锻造纹饰配方 '<arg:1>' 缺少必需的 'base' 参数</yellow>" warning.config.recipe.smithing_trim.missing_base: "<yellow>在文件 <arg:0> 发现问题 - 锻造纹饰配方 '<arg:1>' 缺少必需的 'base' 参数</yellow>"
warning.config.recipe.smithing_trim.missing_template_type: "<yellow>在文件 <arg:0> 发现问题 - 锻造纹饰配方 '<arg:1>' 缺少必需的 'template-type' 参数</yellow>" warning.config.recipe.smithing_trim.missing_template_type: "<yellow>在文件 <arg:0> 发现问题 - 锻造纹饰配方 '<arg:1>' 缺少必需的 'template-type' 参数</yellow>"

View File

@@ -19,10 +19,7 @@ import net.momirealms.craftengine.core.util.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -232,10 +229,12 @@ public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecip
public static final Key KEEP_COMPONENTS = Key.of("craftengine:keep_components"); public static final Key KEEP_COMPONENTS = Key.of("craftengine:keep_components");
public static final Key KEEP_TAGS = Key.of("craftengine:keep_tags"); public static final Key KEEP_TAGS = Key.of("craftengine:keep_tags");
public static final Key MERGE_ENCHANTMENTS = Key.of("craftengine:merge_enchantments"); public static final Key MERGE_ENCHANTMENTS = Key.of("craftengine:merge_enchantments");
public static final Key KEEP_CUSTOM_DATA = Key.of("craftengine:keep_custom_data");
static { static {
if (VersionHelper.isOrAbove1_20_5()) { if (VersionHelper.isOrAbove1_20_5()) {
register(KEEP_COMPONENTS, KeepComponents.FACTORY); register(KEEP_COMPONENTS, KeepComponents.FACTORY);
register(KEEP_CUSTOM_DATA, KeepCustomData.FACTORY);
} else { } else {
register(KEEP_TAGS, KeepTags.FACTORY); register(KEEP_TAGS, KeepTags.FACTORY);
} }
@@ -315,6 +314,42 @@ public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecip
} }
} }
public static class KeepCustomData implements ItemDataProcessor {
public static final Factory FACTORY = new Factory();
private final List<String[]> paths;
public KeepCustomData(List<String[]> data) {
this.paths = data;
}
@Override
public void accept(Item<?> item1, Item<?> item2, Item<?> item3) {
for (String[] path : this.paths) {
Object dataObj = item1.getJavaTag((Object[]) path);
if (dataObj != null) {
item3.setTag(dataObj, (Object[]) path);
}
}
}
@Override
public Key type() {
return ItemDataProcessors.KEEP_CUSTOM_DATA;
}
public static class Factory implements ProcessorFactory {
@Override
public ItemDataProcessor create(Map<String, Object> arguments) {
List<String> paths = MiscUtils.getAsStringList(ResourceConfigUtils.requireNonNullOrThrow(
arguments.get("paths"),
"warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths")
);
return new KeepCustomData(paths.stream().map(it -> it.split("\\.")).toList());
}
}
}
public static class KeepComponents implements ItemDataProcessor { public static class KeepComponents implements ItemDataProcessor {
public static final Factory FACTORY = new Factory(); public static final Factory FACTORY = new Factory();
private final List<Key> components; private final List<Key> components;
@@ -339,6 +374,7 @@ public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecip
} }
public static class Factory implements ProcessorFactory { public static class Factory implements ProcessorFactory {
private static final Key CUSTOM_DATA = Key.of("minecraft", "custom_data");
@Override @Override
public ItemDataProcessor create(Map<String, Object> arguments) { public ItemDataProcessor create(Map<String, Object> arguments) {
@@ -347,7 +383,7 @@ public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecip
throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components"); throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components");
} }
List<String> components = MiscUtils.getAsStringList(componentsObj); List<String> components = MiscUtils.getAsStringList(componentsObj);
return new KeepComponents(components.stream().map(Key::of).toList()); return new KeepComponents(components.stream().map(Key::of).filter(it -> !CUSTOM_DATA.equals(it)).toList());
} }
} }
} }