diff --git a/src/main/java/com/wintercogs/beyonddimensions/BeyondDimensions.java b/src/main/java/com/wintercogs/beyonddimensions/BeyondDimensions.java index e38b175c..3d0f6ad6 100644 --- a/src/main/java/com/wintercogs/beyonddimensions/BeyondDimensions.java +++ b/src/main/java/com/wintercogs/beyonddimensions/BeyondDimensions.java @@ -18,6 +18,7 @@ import com.wintercogs.beyonddimensions.api.storage.key.impl.EnergyStackKey; import com.wintercogs.beyonddimensions.api.storage.key.impl.FluidStackKey; import com.wintercogs.beyonddimensions.api.storage.key.impl.ItemStackKey; +import com.wintercogs.beyonddimensions.api.storage.key.impl.PigStackKey; import com.wintercogs.beyonddimensions.common.init.*; import com.wintercogs.beyonddimensions.integration.IntegrationManager; import net.minecraft.resources.ResourceLocation; @@ -66,6 +67,7 @@ private void commonSetup(final FMLCommonSetupEvent event) StackKeyRegistry.registerType(ItemStackKey.EMPTY); StackKeyRegistry.registerType(FluidStackKey.EMPTY); StackKeyRegistry.registerType(EnergyStackKey.INSTANCE); + StackKeyRegistry.registerType(PigStackKey.INSTANCE); // 注册方块能力类型,用于动态为方块注册能力 CapabilityHelper.BlockCapabilityMap.put(ItemStackKey.ID, Capabilities.ItemHandler.BLOCK); diff --git a/src/main/java/com/wintercogs/beyonddimensions/api/dimensionnet/DimensionsNet.java b/src/main/java/com/wintercogs/beyonddimensions/api/dimensionnet/DimensionsNet.java index 3a384196..55da4cba 100644 --- a/src/main/java/com/wintercogs/beyonddimensions/api/dimensionnet/DimensionsNet.java +++ b/src/main/java/com/wintercogs/beyonddimensions/api/dimensionnet/DimensionsNet.java @@ -5,6 +5,7 @@ import com.wintercogs.beyonddimensions.api.storage.key.KeyAmount; import com.wintercogs.beyonddimensions.api.storage.key.impl.EnergyStackKey; import com.wintercogs.beyonddimensions.api.storage.key.impl.ItemStackKey; +import com.wintercogs.beyonddimensions.api.storage.key.impl.PigStackKey; import com.wintercogs.beyonddimensions.common.init.BDItems; import com.wintercogs.beyonddimensions.config.ServerConfigRuntime; import com.wintercogs.beyonddimensions.util.PlayerNameHelper; @@ -16,6 +17,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.saveddata.SavedData; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.common.NeoForge; @@ -93,6 +95,13 @@ public class DimensionsNet extends SavedData */ private int currentTime = 0; + /** + * 维度养猪场繁殖进度。正常猪被喂食后约需5分钟才能再次繁殖, + * 因此网络每6000 tick消耗两根胡萝卜并增加一只猪。 + */ + private int pigBreedingTime = 0; + private static final int PIG_BREEDING_INTERVAL = 6000; + /** * 构造函数 * @@ -380,6 +389,7 @@ public static DimensionsNet load(CompoundTag tag, HolderLookup.Provider registry // 读取倒计时 net.currentTime = tag.getInt("currentTime"); + net.pigBreedingTime = tag.getInt("pigBreedingTime"); if (tag.contains("Deleted")) net.deleted = tag.getBoolean("Deleted"); @@ -428,6 +438,7 @@ public static DimensionsNet load(CompoundTag tag, HolderLookup.Provider registry // 保存倒计时 tag.putInt("currentTime", this.currentTime); + tag.putInt("pigBreedingTime", this.pigBreedingTime); // 保存删除状态 tag.putBoolean("Deleted", this.deleted); @@ -725,9 +736,11 @@ else if (isManager(playerId)) @SubscribeEvent public void onServerTick(ServerTickEvent.Pre event) { - // 不对临时网络执行倒计时 - if (temporary || ServerConfigRuntime.crystalGenerateTime <= 0) - return; + if (temporary) return; + + tickPigBreeding(); + + if (ServerConfigRuntime.crystalGenerateTime <= 0) return; currentTime++; setDirty(); @@ -737,7 +750,40 @@ public void onServerTick(ServerTickEvent.Pre event) this.unifiedStorage.insert(new ItemStackKey(stack), stack.getCount(), false); currentTime = 0; } + } + + private void tickPigBreeding() + { + long pigs = unifiedStorage.getStackByKey(PigStackKey.INSTANCE).amount(); + ItemStack carrot = new ItemStack(Items.CARROT); + ItemStackKey carrotKey = new ItemStackKey(carrot); + long carrots = unifiedStorage.getStackByKey(carrotKey).amount(); + + if (pigs < 2 || carrots < 2) + { + if (pigBreedingTime != 0) + { + pigBreedingTime = 0; + setDirty(); + } + return; + } + + pigBreedingTime++; + if (pigBreedingTime % 20 == 0) setDirty(); + if (pigBreedingTime < PIG_BREEDING_INTERVAL) return; + + long requestedBirths = Math.min(pigs / 2, carrots / 2); + long remainder = unifiedStorage.insert(PigStackKey.INSTANCE, requestedBirths, true).amount(); + long births = requestedBirths - remainder; + if (births <= 0) return; + + long carrotCost = births * 2; + KeyAmount extracted = unifiedStorage.extract(carrotKey, carrotCost, false, false); + if (extracted.amount() != carrotCost) return; + unifiedStorage.insert(PigStackKey.INSTANCE, births, false); + pigBreedingTime = 0; } private void syncPlayerMembership(UUID playerId, boolean switchPrimary) diff --git a/src/main/java/com/wintercogs/beyonddimensions/api/longtype/PigType.java b/src/main/java/com/wintercogs/beyonddimensions/api/longtype/PigType.java new file mode 100644 index 00000000..d1449d2a --- /dev/null +++ b/src/main/java/com/wintercogs/beyonddimensions/api/longtype/PigType.java @@ -0,0 +1,38 @@ +package com.wintercogs.beyonddimensions.api.longtype; + +import com.mojang.serialization.Codec; +import net.minecraft.network.chat.Component; + +public final class PigType extends LongType +{ + public static final Codec CODEC = createCodec(PigType::new); + + public PigType(long amount) + { + this.stackCount = amount; + } + + @Override + public Component getName() + { + return Component.translatable("types.beyonddimensions.pig.name"); + } + + @Override + public PigType getEmpty() + { + return new PigType(0); + } + + @Override + public PigType copy() + { + return new PigType(stackCount); + } + + @Override + public PigType copyWithAmount(long amount) + { + return new PigType(amount); + } +} diff --git a/src/main/java/com/wintercogs/beyonddimensions/api/storage/key/impl/PigStackKey.java b/src/main/java/com/wintercogs/beyonddimensions/api/storage/key/impl/PigStackKey.java new file mode 100644 index 00000000..e06fa712 --- /dev/null +++ b/src/main/java/com/wintercogs/beyonddimensions/api/storage/key/impl/PigStackKey.java @@ -0,0 +1,147 @@ +package com.wintercogs.beyonddimensions.api.storage.key.impl; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.MapCodec; +import com.wintercogs.beyonddimensions.api.ids.BDConstants; +import com.wintercogs.beyonddimensions.api.longtype.PigType; +import com.wintercogs.beyonddimensions.api.storage.key.IStackRender; +import com.wintercogs.beyonddimensions.api.storage.key.KeyAmount; +import com.wintercogs.beyonddimensions.api.storage.key.render.PigStackKeyRender; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.stream.Stream; + +public final class PigStackKey extends LongStackKey +{ + public static final ResourceLocation ID = + ResourceLocation.fromNamespaceAndPath(BDConstants.MODID, "stack_type/pig"); + public static final PigStackKey INSTANCE = new PigStackKey(); + + public static final MapCodec TYPE_CODEC = new MapCodec<>() + { + @Override + public DataResult decode(com.mojang.serialization.DynamicOps ops, + com.mojang.serialization.MapLike input) + { + return DataResult.success(INSTANCE); + } + + @Override + public com.mojang.serialization.RecordBuilder encode(PigStackKey value, + com.mojang.serialization.DynamicOps ops, + com.mojang.serialization.RecordBuilder prefix) + { + return prefix; + } + + @Override + public Stream keys(com.mojang.serialization.DynamicOps ops) + { + return Stream.empty(); + } + }; + + public static final Codec CODEC = TYPE_CODEC.codec(); + + private PigStackKey() + { + this.stack = new PigType(0); + } + + @Override + public ResourceLocation getTypeID() + { + return ID; + } + + @Override + public MapCodec codec() + { + return TYPE_CODEC; + } + + @Override + public @Nullable KeyAmount fromStackObject(Object stack) + { + return stack instanceof PigType pig ? new KeyAmount(INSTANCE, pig.getStackCount()) : null; + } + + @Override + public @Nullable PigStackKey fromSourceObject(Object key, DataComponentPatch ignored) + { + return key instanceof PigType || key instanceof Number ? INSTANCE : null; + } + + @Override + public @NotNull PigType getSource() + { + return stack; + } + + @Override + public String getModId() + { + return "Minecraft"; + } + + @Override + public PigStackKey getEmpty() + { + return INSTANCE; + } + + @Override + public PigType getEmptyStack() + { + return new PigType(0); + } + + @Override + public boolean hasTag(TagKey tagKey) + { + return false; + } + + @Override + public Stream> getTags() + { + return Stream.empty(); + } + + @Override + public void serialize(RegistryFriendlyByteBuf buf) + { + } + + @Override + public @NotNull PigStackKey deserialize(RegistryFriendlyByteBuf buf) + { + return INSTANCE; + } + + @Override + public @NotNull CompoundTag serializeNBT(HolderLookup.Provider access) + { + return new CompoundTag(); + } + + @Override + public @NotNull PigStackKey deserializeNBT(CompoundTag nbt, HolderLookup.Provider access) + { + return INSTANCE; + } + + @Override + public @NotNull IStackRender getRender() + { + return PigStackKeyRender.INSTANCE; + } +} diff --git a/src/main/java/com/wintercogs/beyonddimensions/api/storage/key/render/PigStackKeyRender.java b/src/main/java/com/wintercogs/beyonddimensions/api/storage/key/render/PigStackKeyRender.java new file mode 100644 index 00000000..f067b405 --- /dev/null +++ b/src/main/java/com/wintercogs/beyonddimensions/api/storage/key/render/PigStackKeyRender.java @@ -0,0 +1,94 @@ +package com.wintercogs.beyonddimensions.api.storage.key.render; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.wintercogs.beyonddimensions.api.storage.key.IStackKey; +import com.wintercogs.beyonddimensions.api.storage.key.IStackRender; +import com.wintercogs.beyonddimensions.api.storage.key.impl.PigStackKey; +import com.wintercogs.beyonddimensions.util.StringFormat; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Items; +import net.minecraft.world.inventory.tooltip.TooltipComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.neoforged.neoforge.client.ClientTooltipFlag; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +public final class PigStackKeyRender implements IStackRender +{ + public static final PigStackKeyRender INSTANCE = new PigStackKeyRender(); + + private PigStackKeyRender() + { + } + + @Override + public void render(GuiGraphics gui, IStackKey key, int x, int y) + { + gui.renderFakeItem(new ItemStack(Items.PIG_SPAWN_EGG), x, y); + } + + @Override + public void renderAmount(GuiGraphics gui, long amount, int x, int y) + { + String text = getCountText(amount); + if (text.isEmpty()) return; + + float scale = 0.666f; + var pose = gui.pose(); + pose.pushPose(); + pose.translate(0, 0, 200); + pose.scale(scale, scale, scale); + RenderSystem.disableBlend(); + int width = Minecraft.getInstance().font.width(text); + int renderX = (int) ((x - 1 + 18.0f - width * scale) / scale); + int renderY = (int) ((y - 1 + 16.0f - 5.0f * scale) / scale); + gui.drawString(Minecraft.getInstance().font, text, renderX, renderY, 0xFFFFFF); + pose.popPose(); + } + + @Override + public String getCountText(long count) + { + return count < 0 ? "" : StringFormat.formatCount(count); + } + + @Override + public Component getDisplayName(IStackKey key) + { + return PigStackKey.INSTANCE.getRenderStack().getName(); + } + + @Override + public List getTooltipLines(IStackKey key, long amount, Item.TooltipContext context, + @Nullable net.minecraft.world.entity.player.Player player, + TooltipFlag flag) + { + return List.of(getDisplayName(key), + Component.translatable("istack.beyonddimensions.storage_num.long_type", amount)); + } + + @Override + public Optional getTooltipImage(IStackKey key) + { + return Optional.empty(); + } + + @Override + public void renderTooltip(GuiGraphics gui, Font font, IStackKey key, long amount, int mouseX, int mouseY) + { + var minecraft = Minecraft.getInstance(); + var context = minecraft.level == null ? Item.TooltipContext.EMPTY : Item.TooltipContext.of(minecraft.level); + gui.renderTooltip(minecraft.font, + getTooltipLines(key, amount, context, minecraft.player, + ClientTooltipFlag.of(minecraft.options.advancedItemTooltips + ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL)), + getTooltipImage(key), ItemStack.EMPTY, mouseX, mouseY); + } +} diff --git a/src/main/java/com/wintercogs/beyonddimensions/common/item/NetMagnetItem.java b/src/main/java/com/wintercogs/beyonddimensions/common/item/NetMagnetItem.java index ef3bfe0c..0105be0c 100644 --- a/src/main/java/com/wintercogs/beyonddimensions/common/item/NetMagnetItem.java +++ b/src/main/java/com/wintercogs/beyonddimensions/common/item/NetMagnetItem.java @@ -5,6 +5,7 @@ import com.wintercogs.beyonddimensions.api.storage.key.KeyAmount; import com.wintercogs.beyonddimensions.api.storage.key.impl.FluidStackKey; import com.wintercogs.beyonddimensions.api.storage.key.impl.ItemStackKey; +import com.wintercogs.beyonddimensions.api.storage.key.impl.PigStackKey; import com.wintercogs.beyonddimensions.common.init.BDDataComponents; import com.wintercogs.beyonddimensions.common.init.BDFluids; import com.wintercogs.beyonddimensions.common.machine.*; @@ -21,6 +22,7 @@ import net.minecraft.world.SimpleMenuProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ExperienceOrb; +import net.minecraft.world.entity.animal.Pig; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -118,6 +120,16 @@ public void workContent(ItemStack stack, Level level, Entity holder, int slotId, UnifiedStorage storage = NetedItem.getNet(stack).getUnifiedStorage(); + // 将范围内的猪收进维度网络。先模拟插入,成功后才移除实体。 + for (Pig pig : level.getEntitiesOfClass(Pig.class, searchArea, Entity::isAlive)) + { + if (storage.insert(PigStackKey.INSTANCE, 1, true).isEmpty()) + { + storage.insert(PigStackKey.INSTANCE, 1, false); + pig.discard(); + } + } + // 开始收集物品 if (hopperItemMode == HopperItemMode.ALLOW) { diff --git a/src/main/resources/assets/beyonddimensions/lang/en_us.json b/src/main/resources/assets/beyonddimensions/lang/en_us.json index 8a503c0f..4859f3f5 100644 --- a/src/main/resources/assets/beyonddimensions/lang/en_us.json +++ b/src/main/resources/assets/beyonddimensions/lang/en_us.json @@ -224,5 +224,6 @@ "msg.beyonddimensions.primary_net.switch.cleared": "Primary network cleared", "msg.beyonddimensions.primary_net.switch.none_available": "No available network to switch to", "msg.beyonddimensions.primary_net.switch.invalid_target": "That network is unavailable or you are no longer a member", - "msg.beyonddimensions.primary_net.switch.no_other": "No other primary network is available" + "msg.beyonddimensions.primary_net.switch.no_other": "No other primary network is available", + "types.beyonddimensions.pig.name": "Pigs" } diff --git a/src/main/resources/assets/beyonddimensions/lang/zh_cn.json b/src/main/resources/assets/beyonddimensions/lang/zh_cn.json index 208b6f55..9ea199fc 100644 --- a/src/main/resources/assets/beyonddimensions/lang/zh_cn.json +++ b/src/main/resources/assets/beyonddimensions/lang/zh_cn.json @@ -224,5 +224,6 @@ "msg.beyonddimensions.primary_net.switch.cleared": "已切换为无主网络", "msg.beyonddimensions.primary_net.switch.none_available": "当前没有可切换的网络", "msg.beyonddimensions.primary_net.switch.invalid_target": "目标网络不可用,或你已经不再属于该网络", - "msg.beyonddimensions.primary_net.switch.no_other": "没有其他可切换的主网络" + "msg.beyonddimensions.primary_net.switch.no_other": "没有其他可切换的主网络", + "types.beyonddimensions.pig.name": "猪" }