Unicopia/src/main/java/com/minelittlepony/unicopia/container/SpellbookScreenHandler.java

467 lines
16 KiB
Java
Raw Normal View History

2021-11-09 17:29:55 +01:00
package com.minelittlepony.unicopia.container;
2021-11-10 18:26:25 +01:00
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
2021-11-09 17:29:55 +01:00
import com.minelittlepony.unicopia.EquinePredicates;
2021-11-12 23:05:42 +01:00
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.entity.player.Pony;
2021-11-10 18:26:25 +01:00
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.item.URecipes;
import com.minelittlepony.unicopia.util.InventoryUtil;
import com.mojang.datafixers.util.Pair;
2021-11-09 17:29:55 +01:00
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.mob.MobEntity;
2021-11-09 17:29:55 +01:00
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
2021-11-10 18:26:25 +01:00
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.inventory.CraftingResultInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
2021-11-11 19:51:58 +01:00
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.screen.PlayerScreenHandler;
2021-11-09 17:29:55 +01:00
import net.minecraft.screen.ScreenHandler;
2021-11-12 23:05:42 +01:00
import net.minecraft.screen.ScreenHandlerContext;
2021-11-10 18:26:25 +01:00
import net.minecraft.screen.slot.CraftingResultSlot;
import net.minecraft.screen.slot.Slot;
import net.minecraft.server.network.ServerPlayerEntity;
2021-11-11 19:51:58 +01:00
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
2021-11-10 18:26:25 +01:00
public class SpellbookScreenHandler extends ScreenHandler {
private static final Identifier[] EMPTY_ARMOR_SLOT_TEXTURES = new Identifier[]{
PlayerScreenHandler.EMPTY_BOOTS_SLOT_TEXTURE,
PlayerScreenHandler.EMPTY_LEGGINGS_SLOT_TEXTURE,
PlayerScreenHandler.EMPTY_CHESTPLATE_SLOT_TEXTURE,
PlayerScreenHandler.EMPTY_HELMET_SLOT_TEXTURE
};
2021-11-10 18:26:25 +01:00
private final int MAX_INGREDIENTS;
private final int GEM_SLOT_INDEX;
private final int HOTBAR_START;
private final int HOTBAR_END;
private final SpellbookInventory input;
2021-11-10 18:26:25 +01:00
private OutputSlot gemSlot;
private final CraftingResultInventory result = new CraftingResultInventory();
2021-11-09 17:29:55 +01:00
2021-11-10 18:26:25 +01:00
private final PlayerInventory inventory;
2021-11-09 17:29:55 +01:00
2021-11-12 23:05:42 +01:00
private final ScreenHandlerContext context;
2021-11-10 18:26:25 +01:00
protected SpellbookScreenHandler(int syncId, PlayerInventory inv) {
2021-11-12 23:05:42 +01:00
this(syncId, inv, ScreenHandlerContext.EMPTY);
}
public SpellbookScreenHandler(int syncId, PlayerInventory inv, ScreenHandlerContext context) {
2021-11-09 17:29:55 +01:00
super(UScreenHandlers.SPELL_BOOK, syncId);
2021-11-10 18:26:25 +01:00
inventory = inv;
2021-11-12 23:05:42 +01:00
this.context = context;
2021-11-10 18:26:25 +01:00
2021-11-12 23:05:42 +01:00
List<int[]> grid = new ArrayList<>();
List<int[]> gemPos = new ArrayList<>();
2021-11-10 18:26:25 +01:00
createGrid(grid, gemPos);
GEM_SLOT_INDEX = MAX_INGREDIENTS = grid.size();
HOTBAR_START = GEM_SLOT_INDEX + 1;
HOTBAR_END = HOTBAR_START + 9;
input = new SpellbookInventory(this, MAX_INGREDIENTS, 1);
2021-11-10 18:26:25 +01:00
for (int i = 0; i < MAX_INGREDIENTS; i++) {
var pos = grid.get(i);
2021-11-12 23:05:42 +01:00
addSlot(new InputSlot(input, i, pos));
2021-11-10 18:26:25 +01:00
}
2021-11-12 23:05:42 +01:00
addSlot(gemSlot = new OutputSlot(inventory.player, input, result, 0, gemPos.get(0)));
2021-11-10 18:26:25 +01:00
for (int i = 0; i < 9; ++i) {
addSlot(new Slot(inventory, i, 121 + i * 18, 195));
}
for (int i = 0; i < PlayerInventory.MAIN_SIZE - 9; ++i) {
int x = i % 5;
int y = i / 5;
addSlot(new InventorySlot(inventory, i + 9, 225 + x * 20, 50 + y * 20));
}
for (int i = 0; i < 4; i++) {
final EquipmentSlot eq = EquipmentSlot.values()[5 - i];
addSlot(new InventorySlot(inventory, PlayerInventory.OFF_HAND_SLOT - i - 1, 340, 50 + (i * 20)) {
@Override
public int getMaxItemCount() {
return 1;
}
@Override
public boolean canInsert(ItemStack stack) {
return eq == MobEntity.getPreferredEquipmentSlot(stack);
}
@Override
public boolean canTakeItems(PlayerEntity playerEntity) {
ItemStack stack = getStack();
if (!stack.isEmpty() && !playerEntity.isCreative() && EnchantmentHelper.hasBindingCurse(stack)) {
return false;
}
return super.canTakeItems(playerEntity);
}
@Override
public Pair<Identifier, Identifier> getBackgroundSprite() {
return Pair.of(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, EMPTY_ARMOR_SLOT_TEXTURES[eq.getEntitySlotId()]);
}
});
}
addSlot(new InventorySlot(inventory, PlayerInventory.OFF_HAND_SLOT, 340, 150) {
@Override
public Pair<Identifier, Identifier> getBackgroundSprite() {
return Pair.of(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, PlayerScreenHandler.EMPTY_OFFHAND_ARMOR_SLOT);
}
});
2021-11-10 18:26:25 +01:00
onContentChanged(input);
2021-11-09 17:29:55 +01:00
}
@Override
public boolean canUse(PlayerEntity player) {
return EquinePredicates.IS_CASTER.test(player);
}
2021-11-10 18:26:25 +01:00
@Override
public void onContentChanged(Inventory inventory) {
2021-11-12 23:05:42 +01:00
context.run((world, pos) -> {
if (!world.isClient && !gemSlot.getStack().isEmpty()) {
world.getServer().getRecipeManager().getFirstMatch(URecipes.SPELLBOOK, input, world)
.filter(recipe -> result.shouldCraftRecipe(world, (ServerPlayerEntity)this.inventory.player, recipe))
.map(recipe -> recipe.craft(input))
.ifPresentOrElse(gemSlot::setCrafted, gemSlot::setUncrafted);
2021-12-22 10:15:09 +01:00
((ServerPlayerEntity)this.inventory.player).networkHandler.sendPacket(new ScreenHandlerSlotUpdateS2CPacket(syncId, nextRevision(), GEM_SLOT_INDEX, gemSlot.getStack()));
2021-11-12 23:05:42 +01:00
}
});
2021-11-10 18:26:25 +01:00
}
@Override
public ItemStack transferSlot(PlayerEntity player, int index) {
Slot sourceSlot = slots.get(index);
if (sourceSlot == null || !sourceSlot.hasStack()) {
return ItemStack.EMPTY;
}
ItemStack transferredStack = sourceSlot.getStack();
ItemStack stack = transferredStack.copy();
if (index >= HOTBAR_START) {
if (!gemSlot.hasStack() && gemSlot.canInsert(stack)) {
if (insertItem(transferredStack, GEM_SLOT_INDEX, GEM_SLOT_INDEX + 1, false)) {
onContentChanged(input);
2021-11-10 18:26:25 +01:00
return ItemStack.EMPTY;
}
}
if (insertItem(transferredStack, 0, GEM_SLOT_INDEX, false)) {
sourceSlot.onQuickTransfer(transferredStack, stack);
onContentChanged(input);
2021-11-10 18:26:25 +01:00
return ItemStack.EMPTY;
}
} else {
if (insertItem(transferredStack, HOTBAR_START, HOTBAR_END, true)) {
sourceSlot.onQuickTransfer(transferredStack, stack);
onContentChanged(input);
2021-11-10 18:26:25 +01:00
return ItemStack.EMPTY;
}
if (insertItem(transferredStack, HOTBAR_END + 27, HOTBAR_END + 27 + 4, false)) {
sourceSlot.onQuickTransfer(transferredStack, stack);
onContentChanged(input);
return ItemStack.EMPTY;
}
if (insertItem(transferredStack, HOTBAR_END, HOTBAR_END + 27, false)) {
sourceSlot.onQuickTransfer(transferredStack, stack);
onContentChanged(input);
return ItemStack.EMPTY;
}
2021-11-10 18:26:25 +01:00
}
if (transferredStack.getCount() == stack.getCount()) {
return ItemStack.EMPTY;
}
sourceSlot.onTakeItem(player, transferredStack);
return stack;
}
@Override
protected boolean insertItem(ItemStack stack, int startIndex, int endIndex, boolean fromLast) {
boolean success = false;
int i = fromLast ? endIndex - 1 : startIndex;
while (true) {
if (i < startIndex || i >= endIndex) {
break;
}
Slot slot = getSlot(i);
ItemStack current = slot.getStack();
if (!current.isEmpty() && ItemStack.canCombine(stack, current)) {
// abide by the slot's max item count when trying to insert stacks
int available = Math.min(Math.min(current.getMaxCount(), slot.getMaxItemCount()) - current.getCount(), stack.getCount());
if (available > 0) {
current.increment(available);
stack.decrement(available);
slot.markDirty();
success = true;
}
}
i += fromLast ? -1 : 1;
}
i = fromLast ? endIndex - 1 : startIndex;
while (true) {
if (i < startIndex || i >= endIndex) {
break;
}
Slot slot = getSlot(i);
ItemStack current = slot.getStack();
if (current.isEmpty() && slot.canInsert(stack)) {
if (stack.getCount() > slot.getMaxItemCount()) {
slot.setStack(stack.split(slot.getMaxItemCount()));
} else {
slot.setStack(stack.split(stack.getCount()));
}
slot.markDirty();
success = true;
break;
}
i += fromLast ? -1 : 1;
}
return success;
}
2021-11-10 18:26:25 +01:00
@Override
public void close(PlayerEntity playerEntity) {
gemSlot.setUncrafted();
super.close(playerEntity);
2021-11-12 23:05:42 +01:00
context.run((world, pos) -> {
dropInventory(playerEntity, input);
dropInventory(playerEntity, result);
});
2021-11-10 18:26:25 +01:00
}
2021-11-12 23:05:42 +01:00
/**
* Creates a hexagonal crafting grid.
* @param grid Output for normal slot positions.
* @param gemPos Output for the gem slot position.
*/
private static void createGrid(List<int[]> grid, List<int[]> gemPos) {
2021-11-10 18:26:25 +01:00
int cols = 4;
int spacing = 23;
int top = 34;
int left = 65;
for (int row = 0; row < 7; row++) {
for (int i = 0; i < cols; i++) {
2021-11-12 23:05:42 +01:00
int ring = 3;
if (row == 0 || row == 6) {
ring = 1;
} else if ((row == 1 || row == 5) && i > 0 && i < cols - 1) {
ring = 2;
} else {
if (i == 0 || i == cols - 1) {
ring = 1;
} else if (i == 1 || i == cols - 2) {
ring = 2;
}
}
(row == 3 && i == 3 ? gemPos : grid).add(new int[] {
left + (i * spacing),
top,
row == 3 && i == 3 ? 4 : ring
});
2021-11-10 18:26:25 +01:00
}
top += spacing * 0.9;
left -= (spacing / 2) * (row > 2 ? -1 : 1);
cols += row > 2 ? -1 : 1;
}
}
2021-11-12 23:05:42 +01:00
public interface SpellbookSlot {
int getRing();
}
2021-11-10 18:26:25 +01:00
public class SpellbookInventory extends CraftingInventory {
public SpellbookInventory(ScreenHandler handler, int width, int height) {
super(handler, width, height);
}
public ItemStack getItemToModify() {
2021-11-12 23:05:42 +01:00
return gemSlot.uncrafted.orElse(gemSlot.getStack());
}
public int getRing(int slot) {
Slot s = slots.get(slot);
return s instanceof SpellbookSlot ? ((SpellbookSlot)s).getRing() : 0;
}
public SpellTraits getTraits() {
return SpellTraits.union(InventoryUtil.slots(this)
.map(slot -> SpellTraits.of(getStack(slot)).multiply(getRingFactor(getRing(slot))))
.toArray(SpellTraits[]::new)
);
}
public static float getRingFactor(int ring) {
switch (ring) {
case 1: return 1;
case 2: return 0.6F;
case 3: return 0.3F;
default: return 0;
}
}
}
public class InventorySlot extends Slot implements SpellbookSlot {
public InventorySlot(Inventory inventory, int index, int x, int y) {
super(inventory, index, x, y);
}
@Override
public int getRing() {
return 0;
}
@Override
public boolean isEnabled() {
return SpellbookPage.getCurrent() == SpellbookPage.INVENTORY;
}
}
2021-11-10 18:26:25 +01:00
public class InputSlot extends Slot implements SpellbookSlot {
2021-11-12 23:05:42 +01:00
private final int ring;
public InputSlot(Inventory inventory, int index, int[] params) {
super(inventory, index, params[0], params[1]);
ring = params[2];
2021-11-10 18:26:25 +01:00
}
@Override
public int getMaxItemCount() {
return 1;
}
2021-11-12 23:05:42 +01:00
@Override
public int getRing() {
return ring;
}
2021-11-10 18:26:25 +01:00
}
public static class OutputSlot extends CraftingResultSlot implements SpellbookSlot {
private Optional<ItemStack> uncrafted = Optional.empty();
2021-11-12 23:05:42 +01:00
private final PlayerEntity player;
private final SpellbookInventory input;
2021-11-12 23:05:42 +01:00
private final int ring;
public OutputSlot(PlayerEntity player, SpellbookInventory input, Inventory inventory, int index, int[] params) {
super(player, input, inventory, index, params[0], params[1]);
this.player = player;
this.input = input;
2021-11-12 23:05:42 +01:00
this.ring = params[2];
2021-11-10 18:26:25 +01:00
}
public void setCrafted(ItemStack crafted) {
uncrafted = uncrafted.or(() -> Optional.of(getStack()));
ItemStack old = getStack();
2021-11-10 18:26:25 +01:00
setStack(crafted);
if (!ItemStack.areEqual(old, crafted)) {
player.playSound(SoundEvents.BLOCK_END_PORTAL_FRAME_FILL, SoundCategory.MASTER, 1, 0.3F);
}
2021-11-10 18:26:25 +01:00
}
public void setUncrafted() {
uncrafted = uncrafted.filter(stack -> {
player.playSound(SoundEvents.BLOCK_END_PORTAL_FRAME_FILL, SoundCategory.MASTER, 0.2F, 0.2F);
2021-11-10 18:26:25 +01:00
setStack(stack);
return false;
});
}
@Override
public boolean canInsert(ItemStack stack) {
return stack.getItem() == UItems.GEMSTONE;
}
@Override
public int getMaxItemCount() {
return 1;
}
2021-11-12 23:05:42 +01:00
@Override
public int getRing() {
return ring;
}
2021-11-10 18:26:25 +01:00
@Override
public void onTakeItem(PlayerEntity player, ItemStack stack) {
if (uncrafted.isPresent()) {
uncrafted = Optional.empty();
onCrafted(stack);
Pony pony = Pony.of(player);
InventoryUtil.iterate(input).forEach(s -> {
pony.getDiscoveries().unlock(s.getItem());
});
DefaultedList<ItemStack> defaultedList = player.world.getRecipeManager().getRemainingStacks(URecipes.SPELLBOOK, input, player.world);
for (int i = 0; i < defaultedList.size(); ++i) {
ItemStack itemStack = input.getStack(i);
ItemStack itemStack2 = defaultedList.get(i);
if (!itemStack.isEmpty()) {
input.removeStack(i, 1);
itemStack = input.getStack(i);
}
if (!itemStack2.isEmpty()) {
if (itemStack.isEmpty()) {
input.setStack(i, itemStack2);
2021-12-22 10:15:09 +01:00
} else if (ItemStack.areItemsEqualIgnoreDamage(itemStack, itemStack2) && ItemStack.areNbtEqual(itemStack, itemStack2)) {
itemStack2.increment(itemStack.getCount());
input.setStack(i, itemStack2);
} else if (!player.getInventory().insertStack(itemStack2)) {
player.dropItem(itemStack2, false);
}
}
}
2021-11-10 18:26:25 +01:00
}
}
}
2021-11-09 17:29:55 +01:00
}