| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- package com.hexdragon.blacksmithre.block.anvil;
- import com.hexdragon.corere.item.EnchantmentHelperRe;
- import com.hexdragon.corere.item.ItemHelperRe;
- import com.hexdragon.blacksmithre.registry.RegMain;
- import net.minecraft.block.AnvilBlock;
- import net.minecraft.block.BlockState;
- import net.minecraft.enchantment.Enchantment;
- import net.minecraft.enchantment.EnchantmentHelper;
- import net.minecraft.entity.player.PlayerEntity;
- import net.minecraft.entity.player.PlayerInventory;
- import net.minecraft.inventory.CraftResultInventory;
- import net.minecraft.inventory.IInventory;
- import net.minecraft.inventory.Inventory;
- import net.minecraft.inventory.container.Container;
- import net.minecraft.inventory.container.Slot;
- import net.minecraft.item.ItemStack;
- import net.minecraft.nbt.CompoundNBT;
- import net.minecraft.nbt.StringNBT;
- import net.minecraft.tags.BlockTags;
- import net.minecraft.tileentity.TileEntity;
- import net.minecraft.util.IWorldPosCallable;
- import net.minecraft.util.NonNullList;
- import net.minecraft.util.math.MathHelper;
- import net.minecraftforge.common.MinecraftForge;
- import net.minecraftforge.event.AnvilUpdateEvent;
- import javax.annotation.Nonnull;
- import java.util.Map;
- public class AnvilContainerRe extends Container {
- // 构造函数
- public final IWorldPosCallable worldPosCallable;
- private final PlayerEntity player;
- public AnvilContainerRe(int id, PlayerInventory playerInventory) {
- // 当从 Screen 渲染线程触发,就会从这里进行调用
- this(id, playerInventory, IWorldPosCallable.DUMMY, new Inventory(2));
- }
- public AnvilContainerRe(int id, PlayerInventory playerInventory, IWorldPosCallable worldPosCallable, IInventory tileEntity) {
- super(RegMain.containerAnvil.get(), id);
- this.worldPosCallable = worldPosCallable;
- this.player = playerInventory.player;
- this.inputInventory = tileEntity; // 与 NBT 中的物品栏同步
- Constuct(playerInventory);
- onCraftMatrixChanged(inputInventory); // 初始化输出
- }
- // 输入与输出物品槽
- public IInventory inputInventory;
- public final CraftResultInventory outputInventory = new CraftResultInventory();
- // 构造页面槽位
- private void Constuct(PlayerInventory playerInventory) {
- this.addSlot(new Slot(this.inputInventory, 0, 62, 28));
- this.addSlot(new Slot(this.inputInventory, 1, 80, 28));
- this.addSlot(new Slot(this.outputInventory, 2, 138, 28) {
- // 禁止将物品放在输出格
- public boolean isItemValid(ItemStack stack) {
- return false;
- }
- // 触发玩家拿走物品的事件
- public ItemStack onTake(PlayerEntity thePlayer, ItemStack stack) {
- return AnvilContainerRe.this.onTakeOutput(thePlayer, stack);
- }
- });
- // 增加物品栏与快捷栏槽位
- int yPositionStart = 68;
- for (int i = 0; i < 3; ++i) {
- for (int j = 0; j < 9; ++j) {
- this.addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, yPositionStart + i * 18));
- }
- }
- for (int k = 0; k < 9; ++k) {
- this.addSlot(new Slot(playerInventory, k, 8 + k * 18, 58 + yPositionStart));
- }
- }
- // 消耗的物品合成原料个数
- public int materialCost;
- // 当输入物品改变时更新输出
- public void updateRepairOutput() {
- // 获取输入物品,并检查不为空
- ItemStack leftItem = this.inputInventory.getStackInSlot(0);
- ItemStack middleItem = this.inputInventory.getStackInSlot(1);
- if (leftItem.isEmpty() || middleItem.isEmpty()) {
- this.outputInventory.setInventorySlotContents(0, ItemStack.EMPTY);
- return;
- }
- // 初始化
- this.materialCost = 0;
- // 调用钩子函数
- if (!onAnvilChange(this, leftItem, middleItem, outputInventory, leftItem.getDisplayName().getString(), 0))
- return;
- // 将可损坏的物品优先视作左侧
- if (middleItem.isDamageable() && !leftItem.isDamageable()) {
- ItemStack swapItem = leftItem.copy();
- leftItem = middleItem; middleItem = swapItem;
- }
- ItemStack outputItem = leftItem.copy();
- // 根据腐朽诅咒附魔获取修复比
- final float enchantmentRadio = MathHelper.clamp(1 - Math.max(EnchantmentHelper.getEnchantmentLevel(RegMain.enchDecay.get(), leftItem), EnchantmentHelper.getEnchantmentLevel(RegMain.enchDecay.get(), middleItem)) * 0.7f, 0f, 1f);
- // 判断配方情况
- if (outputItem.isDamageable() && outputItem.getItem().getIsRepairable(leftItem, middleItem)) {
- // 使用原材料修复物品
- // 根据难度与附魔获取实际修复比
- final float[] radio = {0};
- worldPosCallable.consume((world, pos) -> radio[0] = new float[]{1f, 1f, 0.98f, 0.95f}[world.getDifficulty().getId()] * enchantmentRadio);
- // 获取每个材料修复的耐久度
- int mendingPerMaterial = (int) (outputItem.getMaxDamage() / ItemHelperRe.getDamageableItemMaterialCost(outputItem) * radio[0]);
- if (mendingPerMaterial <= 0) {
- this.outputInventory.setInventorySlotContents(0, ItemStack.EMPTY);
- return;
- }
- // 获取消耗的材料个数
- int materialCost, mending = mendingPerMaterial;
- for (materialCost = 0; mending > 0 && materialCost < middleItem.getCount(); ++materialCost) {
- outputItem.setDamage(outputItem.getDamage() - mending);
- mending = Math.min(outputItem.getDamage(), mendingPerMaterial);
- }
- this.materialCost = materialCost;
- } else if (leftItem.getItem() != middleItem.getItem() || !leftItem.isDamageable()) {
- // 配方无效,直接结束
- this.outputInventory.setInventorySlotContents(0, ItemStack.EMPTY);
- return;
- } else {
- // 使用两个相同物品进行修复
- // 根据难度获取奖励的耐久度百分比
- final int[] radio = {0};
- worldPosCallable.consume((world, pos) -> radio[0] = new int[]{25, 25, 15, 5}[world.getDifficulty().getId()]);
- // 获取修复后的 Damage
- int dur1 = leftItem.getMaxDamage() - leftItem.getDamage();
- int dur2 = middleItem.getMaxDamage() - middleItem.getDamage();
- int durNew = (int) (dur1 + (dur2 + outputItem.getMaxDamage() * radio[0] / 100 + 1) * enchantmentRadio);
- int newDamage = outputItem.getMaxDamage() - durNew;
- if (newDamage < 0) newDamage = 0;
- if (newDamage < outputItem.getDamage()) outputItem.setDamage(newDamage);
- // 将第二个物品的诅咒转移到第一个物品
- Map<Enchantment, Integer> outputEnchantments = EnchantmentHelper.getEnchantments(outputItem);
- Map<Enchantment, Integer> curses = EnchantmentHelperRe.getCurseEnchantments(middleItem);
- if (curses.size() > 0) outputEnchantments.putAll(curses);
- EnchantmentHelper.setEnchantments(outputEnchantments, outputItem);
- }
- // 设置输出
- this.outputInventory.setInventorySlotContents(0, outputItem);
- this.detectAndSendChanges();
- }
- // 在玩家从输出格拿走物品时触发:损坏铁砧、清空输入
- private ItemStack onTakeOutput(PlayerEntity player, ItemStack itemStack) {
- // 清空输入
- if (this.materialCost > 0) {
- boolean reverseInput = !this.inputInventory.getStackInSlot(0).isDamageable();
- this.inputInventory.setInventorySlotContents(reverseInput ? 1 : 0, ItemStack.EMPTY);
- ItemStack itemstack = this.inputInventory.getStackInSlot(reverseInput ? 0 : 1);
- if (!itemstack.isEmpty() && itemstack.getCount() > this.materialCost) {
- itemstack.shrink(this.materialCost);
- this.inputInventory.setInventorySlotContents(reverseInput ? 0 : 1, itemstack);
- } else {
- this.inputInventory.setInventorySlotContents(reverseInput ? 0 : 1, ItemStack.EMPTY);
- }
- } else {
- this.inputInventory.setInventorySlotContents(0, ItemStack.EMPTY);
- this.inputInventory.setInventorySlotContents(1, ItemStack.EMPTY);
- }
- // 触发铁砧的随机损坏
- // 对于原版:“平均每个铁砧能用 25 次,相当于每用一次铁砧就消耗了 1.24 个用于合成铁砧的铁锭”
- float baseBreakChance = net.minecraftforge.common.ForgeHooks.onAnvilRepair(player, itemStack, AnvilContainerRe.this.inputInventory.getStackInSlot(0), AnvilContainerRe.this.inputInventory.getStackInSlot(1));
- this.worldPosCallable.consume((world, blockPos) -> {
- BlockState blockstate = world.getBlockState(blockPos);
- // 考虑到铁砧现在没有等级消耗,略微增加损耗速率是比较平衡的
- // 下界合金砧的耐久在普通难度下为普通铁砧的 20 倍,平均能用 400 次
- float[] ironChance = {0.75f, 1f, 1.25f, 1.75f};
- float[] netheriteChance = {0f, 0f, 0.0625f, 0.1f};
- float newBreakChance = baseBreakChance * ((blockstate.get(RegMain.blockStateMaterial) == 0) ? ironChance : netheriteChance)[world.getDifficulty().getId()];
- if (!player.abilities.isCreativeMode && blockstate.isIn(BlockTags.ANVIL) && player.getRNG().nextFloat() < newBreakChance) {
- BlockState newBlockState = AnvilBlock.damage(blockstate);
- if (newBlockState == null) {
- world.removeBlock(blockPos, false);
- world.playEvent(1029, blockPos, 0);
- } else {
- // TODO : <验证> 铁砧损坏等级增加时的物品处理是否在多人工作正常
- // TODO : 解决 Mixin config mixins.blacksmithre.json does not specify "minVersion" property
- // 保存当前物品并清空物品栏,避免在更改方块时爆出
- AnvilTileEntity tileEntity = (AnvilTileEntity) world.getTileEntity(blockPos);
- ItemStack itemStack0 = tileEntity.getItems().get(0).copy();
- ItemStack itemStack1 = tileEntity.getItems().get(1).copy();
- tileEntity.inventory.clear();
- // 放置损坏后的方块
- world.setBlockState(blockPos, newBlockState, 2);
- // 把物品放回新方块
- AnvilTileEntity newTileEntity = (AnvilTileEntity) world.getTileEntity(blockPos);
- newTileEntity.inventory.set(0, itemStack0);
- newTileEntity.inventory.set(1, itemStack1);
- newTileEntity.markDirty();
- // 让玩家再次打开 GUI
- // TODO : 想办法让玩家的鼠标在铁砧损坏时不会移动到中心(因为实际上 GUI 已经被关闭后重新打开了)
- player.openContainer(newTileEntity.getBlockState().getContainer(world, blockPos));
- world.playEvent(1030, blockPos, 0);
- }
- } else {
- world.playEvent(1030, blockPos, 0);
- }
- });
- return itemStack;
- }
- /*
- * --------------------------------------------------------------
- * 以下方法直接使用原本的代码,不需要进行修改
- * --------------------------------------------------------------
- */
- // 接口: 当输入物品改变时触发更新
- public void onCraftMatrixChanged(IInventory inventoryIn) {
- super.onCraftMatrixChanged(inventoryIn);
- if (inventoryIn == this.inputInventory) {
- this.updateRepairOutput();
- }
- }
- // 接口: 决定玩家是否可以使用该方块
- public boolean canInteractWith(PlayerEntity playerIn) {
- return this.worldPosCallable.applyOrElse((world, blockPos) -> world.getBlockState(blockPos).isIn(BlockTags.ANVIL) && playerIn.getDistanceSq((double) blockPos.getX() + 0.5D, (double) blockPos.getY() + 0.5D, (double) blockPos.getZ() + 0.5D) <= 64.0D, true);
- }
- // 接口: 当玩家使用 Shift+左键 快速转移物品时触发,需要尝试转移物品
- public ItemStack transferStackInSlot(PlayerEntity playerIn, int index) {
- ItemStack itemstack = ItemStack.EMPTY;
- Slot slot = this.inventorySlots.get(index);
- if (slot != null && slot.getHasStack()) {
- ItemStack itemstack1 = slot.getStack();
- itemstack = itemstack1.copy();
- if (index == 2) {
- if (!this.mergeItemStack(itemstack1, 3, 39, true)) {
- return ItemStack.EMPTY;
- }
- slot.onSlotChange(itemstack1, itemstack);
- } else if (index != 0 && index != 1) {
- if (index < 39) {
- if (!this.mergeItemStack(itemstack1, 0, 2, false)) {
- return ItemStack.EMPTY;
- }
- }
- } else if (!this.mergeItemStack(itemstack1, 3, 39, false)) {
- return ItemStack.EMPTY;
- }
- if (itemstack1.isEmpty()) {
- slot.putStack(ItemStack.EMPTY);
- } else {
- slot.onSlotChanged();
- }
- if (itemstack1.getCount() == itemstack.getCount()) {
- return ItemStack.EMPTY;
- }
- slot.onTake(playerIn, itemstack1);
- }
- return itemstack;
- }
- // 铁砧输入改变时 Forge 触发的钩子事件(从 ForgeHooks.java 中复制)
- public static boolean onAnvilChange(AnvilContainerRe container, @Nonnull ItemStack left, @Nonnull ItemStack right, IInventory outputSlot, String name, int baseCost) {
- AnvilUpdateEvent e = new AnvilUpdateEvent(left, right, name, baseCost);
- if (MinecraftForge.EVENT_BUS.post(e)) return false;
- if (e.getOutput().isEmpty()) return true;
- outputSlot.setInventorySlotContents(0, e.getOutput());
- container.materialCost = e.getMaterialCost();
- return false;
- }
- }
|