自定义机制
本页将以甘蔗为例,大致实现一个类似于原版甘蔗的机制。
首先为item和block创建两个类
package net.momirealms.customcrops.api.example;
import net.momirealms.customcrops.api.core.item.AbstractCustomCropsItem;
import net.momirealms.customcrops.common.util.Key;
public class SugarCaneItem extends AbstractCustomCropsItem {
private static SugarCaneItem instance;
public SugarCaneItem() {
super(Key.key("customcrops", "sugarcane_item"));
}
public static SugarCaneItem instance() {
if (instance == null) {
instance = new SugarCaneItem();
}
return instance;
}
}
package net.momirealms.customcrops.api.example;
import net.momirealms.customcrops.api.core.block.AbstractCustomCropsBlock;
import net.momirealms.customcrops.common.util.Key;
public class SugarCaneBlock extends AbstractCustomCropsBlock {
private static SugarCaneBlock instance;
private SugarCaneBlock() {
super(Key.key("customcrops", "sugarcane_block"));
}
// 此方法用于验证一个方块是否属于此机制
// id 将是来自自定义物品插件(例如 Oraxen)的物品 ID
@Override
public boolean isBlockInstance(String id) {
return id.equals("customcrops:sugarcane_block_id");
}
public static SugarCaneBlock instance() {
if (instance == null) {
instance = new SugarCaneBlock();
}
return instance;
}
@Override
public NamedTextColor insightColor() {
return NamedTextColor.YELLOW;
}
}
然后在onEnable()
方法中注册
package net.momirealms.customcrops.api.example;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.Registries;
import net.momirealms.customcrops.api.core.RegistryAccess;
import net.momirealms.customcrops.api.event.CustomCropsReloadEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
public class YourPlugin extends JavaPlugin implements Listener {
@Override
public void onEnable() {
RegistryAccess access = BukkitCustomCropsPlugin.getInstance().getRegistryAccess();
access.registerBlockMechanic(SugarCaneBlock.instance());
access.registerItemMechanic(SugarCaneItem.instance());
getServer().getPluginManager().registerEvents(this, this);
}
@EventHandler
public void onPluginReload(CustomCropsReloadEvent event) {
// ITEMS 和 BLOCKS 是临时映射,用于将物品 ID 与机制类型关联起来
// 例如,如果你有多个具有相同机制的方块,比如红色羊毛、绿色羊毛和灰色羊毛,你需要在这个注册表中注册它们。
// 当插件重新加载以应用配置文件的更改时,这个注册表会被清空
// 注意:绝不要使用标注了 @DONOTUSE 的注册表
for (String sugarcaneItem : List.of("customcrops:sugarcane_item_id")) {
Registries.ITEMS.register(sugarcaneItem, SugarCaneItem.instance());
}
for (String sugarcaneBlock : List.of("customcrops:sugarcane_block_id")) {
Registries.BLOCKS.register(sugarcaneBlock, SugarCaneBlock.instance());
}
}
}
然后我们可以创建一些基本逻辑,例如放置甘蔗方块
package net.momirealms.customcrops.api.example;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.ExistenceForm;
import net.momirealms.customcrops.api.core.InteractionResult;
import net.momirealms.customcrops.api.core.item.AbstractCustomCropsItem;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
import net.momirealms.customcrops.api.core.wrapper.WrappedInteractEvent;
import net.momirealms.customcrops.api.util.LocationUtils;
import net.momirealms.customcrops.common.util.Key;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.List;
public class SugarCaneItem extends AbstractCustomCropsItem {
private static SugarCaneItem instance;
public SugarCaneItem() {
super(Key.key("customcrops", "sugarcane_item"));
}
public static SugarCaneItem instance() {
if (instance == null) {
instance = new SugarCaneItem();
}
return instance;
}
@Override
public InteractionResult interactAt(WrappedInteractEvent wrapped) {
// 甘蔗应该种植在方块上
if (wrapped.existenceForm() != ExistenceForm.BLOCK)
return InteractionResult.PASS;
// 甘蔗应该种植在方块的上表面
if (wrapped.clickedBlockFace() != BlockFace.UP)
return InteractionResult.PASS;
// 获取被点击方块的位置
Location location = wrapped.location();
Block block = location.getBlock();
// 应该种植在沙子或泥土上
if (block.getType() != Material.SAND && block.getType() != Material.RED_SAND && block.getType() != Material.DIRT) {
return InteractionResult.PASS;
}
// 应该种植在水附近
outer: {
for (BlockFace face : List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST)) {
if (block.getRelative(face).getType() == Material.WATER) {
break outer;
}
}
return InteractionResult.PASS;
}
// 检查这个地方是否适合种植
Location sugarcaneLocation = location.clone().add(0,1,0);
if (!suitableForPlant(sugarcaneLocation)) {
return InteractionResult.PASS;
}
// 减少物品的数量
wrapped.itemInHand().setAmount(wrapped.itemInHand().getAmount() - 1);
// 获取 CustomCrops 世界并添加方块状态
CustomCropsWorld<?> world = wrapped.world();
CustomCropsBlockState newBlockState = SugarCaneBlock.instance().createBlockState();
world.addBlockState(Pos3.from(sugarcaneLocation), newBlockState);
// 放置方块
BukkitCustomCropsPlugin.getInstance().getItemManager().placeBlock(sugarcaneLocation, "customcrops:sugarcane_block_id");
return InteractionResult.COMPLETE;
}
private boolean suitableForPlant(Location location) {
Block block = location.getBlock();
if (block.getType() != Material.AIR) return false;
Location center = LocationUtils.toBlockCenterLocation(location);
Collection<Entity> entities = center.getWorld().getNearbyEntities(center, 0.5,0.5,0.5);
entities.removeIf(entity -> (entity instanceof Player || entity instanceof Item));
return entities.isEmpty();
}
}
现在你应该可以种植甘蔗了(我在 gif 中使用菠萝作为替代品,因为我还没有准备甘蔗模型)
然后我们可以配置甘蔗的生长/破坏逻辑
package net.momirealms.customcrops.api.example;
import net.momirealms.customcrops.api.BukkitCustomCropsPlugin;
import net.momirealms.customcrops.api.core.block.AbstractCustomCropsBlock;
import net.momirealms.customcrops.api.core.world.CustomCropsBlockState;
import net.momirealms.customcrops.api.core.world.CustomCropsWorld;
import net.momirealms.customcrops.api.core.world.Pos3;
import net.momirealms.customcrops.api.core.wrapper.WrappedBreakEvent;
import net.momirealms.customcrops.api.misc.NamedTextColor;
import net.momirealms.customcrops.api.util.LocationUtils;
import net.momirealms.customcrops.common.util.Key;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Collection;
import java.util.Optional;
public class SugarCaneBlock extends AbstractCustomCropsBlock {
private static SugarCaneBlock instance;
private SugarCaneBlock() {
super(Key.key("customcrops", "sugarcane_block"));
}
@Override
public boolean isBlockInstance(String id) {
return id.equals("customcrops:sugarcane_block_id");
}
public static SugarCaneBlock instance() {
if (instance == null) {
instance = new SugarCaneBlock();
}
return instance;
}
@Override
public void randomTick(CustomCropsBlockState state, CustomCropsWorld<?> world, Pos3 location, boolean offlineTick) {
// 首先检查是否是顶层方块
Pos3 upper = location.add(0,1,0);
Optional<CustomCropsBlockState> upperState = world.getBlockState(upper);
if (upperState.isPresent() && upperState.get().type() instanceof SugarCaneBlock) {
return;
}
// 获取当前高度
int height = 1;
for (int i = 1; i < 4; i++) {
Pos3 under = location.add(0,-i,0);
Optional<CustomCropsBlockState> optionalState = world.getBlockState(under);
if (optionalState.isPresent() && optionalState.get().type() instanceof SugarCaneBlock) {
height++;
} else {
break;
}
}
if (height >= 4) return;
// 检查是否有足够的空间生长
Location upperBukkitLocation = upper.toLocation(world.bukkitWorld());
// 切换到主线程
BukkitCustomCropsPlugin.getInstance().getScheduler().sync().run(() -> {
if (!suitableForGrow(upperBukkitLocation)) {
return;
}
// 放置上层方块
BukkitCustomCropsPlugin.getInstance().getItemManager().placeBlock(upperBukkitLocation, "customcrops:sugarcane_block_id");
world.addBlockState(upper, createBlockState());
}, upperBukkitLocation);
}
private boolean suitableForGrow(Location location) {
Block block = location.getBlock();
if (block.getType() != Material.AIR) return false;
Location center = LocationUtils.toBlockCenterLocation(location);
Collection<Entity> entities = center.getWorld().getNearbyEntities(center, 0.5,0.5,0.5);
entities.removeIf(entity -> (entity instanceof Player || entity instanceof Item));
return entities.isEmpty();
}
@Override
public void onBreak(WrappedBreakEvent event) {
Location location = event.location();
Pos3 pos3 = Pos3.from(location);
CustomCropsWorld<?> world = event.world();
// 构建物品
ItemStack itemToDrop = BukkitCustomCropsPlugin.getInstance().getItemManager().build(null, "customcrops:sugarcane_item_id");
// 破坏上层方块
for (int i = 0; i < 4; i++) {
Pos3 upper = pos3.add(0, i,0);
Optional<CustomCropsBlockState> optionalState = world.getBlockState(upper);
if (optionalState.isPresent() && optionalState.get().type() instanceof SugarCaneBlock) {
world.removeBlockState(upper);
Location temp = upper.toLocation(world.bukkitWorld());
BukkitCustomCropsPlugin.getInstance().getItemManager().removeBlock(temp);
temp.getWorld().dropItemNaturally(temp, itemToDrop);
} else {
break;
}
}
}
@Override
public NamedTextColor insightColor() {
return NamedTextColor.YELLOW;
}
}
最后更新于