package electroblob.wizardry.client.gui; import electroblob.wizardry.Wizardry; import electroblob.wizardry.block.BlockBookshelf; import electroblob.wizardry.client.DrawingUtils; import electroblob.wizardry.client.gui.GuiButtonTurnPage.Type; import electroblob.wizardry.data.SpellGlyphData; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.packet.PacketLectern; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.TileEntityLectern; import electroblob.wizardry.util.GeometryUtils; import electroblob.wizardry.util.ISpellSortable; import electroblob.wizardry.util.ParticleBuilder; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.client.audio.SoundHandler; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.resources.I18n; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.network.play.client.CPacketCloseWindow; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; public class GuiLectern extends GuiSpellInfo implements ISpellSortable { private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/lectern.png"); /** The distance of the page buttons from the bottom outside corners of the GUI. */ private static final int PAGE_BUTTON_INSET_X = 22, PAGE_BUTTON_INSET_Y = 13; /** The distance between adjacent page turn buttons. */ private static final int PAGE_BUTTON_SPACING = 20; /** The distance of the sort buttons from the top left corner of the GUI. */ private static final int SORT_BUTTON_INSET_X = 96, SORT_BUTTON_INSET_Y = 20; /** The distance between adjacent sort buttons. */ private static final int SORT_BUTTON_SPACING = 13; /** The distance of the spell buttons from the top outside corners of the GUI. */ private static final int SPELL_BUTTON_INSET_X = 23, SPELL_BUTTON_INSET_Y = 44; /** The distance between adjacent spell buttons (in both x and y). */ private static final int SPELL_BUTTON_SPACING = 38; private static final int SPELL_ROWS = 3, SPELL_COLUMNS = 3; public static final int SPELL_BUTTON_COUNT = SPELL_ROWS * SPELL_COLUMNS * 2; // x2 because there are 2 pages private final TileEntityLectern lectern; private GuiButton nextPageButton; private GuiButton prevPageButton; private GuiButton lastPageButton; private GuiButton firstPageButton; private GuiButton indexButton; private GuiButton locateButton; private GuiButton[] sortButtons = new GuiButton[3]; private GuiButtonSpell[] spellButtons = new GuiButtonSpell[SPELL_BUTTON_COUNT]; /** The spell currently being viewed, or null if the index is being viewed. */ private Spell currentSpell; /** The available spells in nearby bookshelves; should not contain duplicates. */ private List availableSpells = new ArrayList<>(); /** The spells matching the current search criteria, sorted according to the current sort settings. Will be a subset * of {@link GuiLectern#availableSpells}. */ private List matchingSpells; private ISpellSortable.SortType sortType = ISpellSortable.SortType.TIER; private boolean sortDescending = false; private GuiTextField searchField; private boolean searchNeedsClearing; private int currentPage = 0; public GuiLectern(TileEntityLectern lectern){ super(288, 180); this.lectern = lectern; this.currentSpell = lectern.currentSpell; this.setTextureSize(512, 512); } @Override public Spell getSpell(){ return currentSpell; } @Override public ResourceLocation getTexture(){ return TEXTURE; } @Override public SortType getSortType(){ return sortType; } @Override public boolean isSortDescending(){ return sortDescending; } private int getPageCount(){ return MathHelper.ceil((float)matchingSpells.size() / SPELL_BUTTON_COUNT); } private Spell getSpellForButton(GuiButtonSpell button){ return matchingSpells.get(currentPage * SPELL_BUTTON_COUNT + button.index); } @Override public void initGui(){ super.initGui(); final int left = this.width / 2 - this.xSize / 2; final int top = this.height / 2 - this.ySize / 2; int buttonID = 0; // Page buttons this.buttonList.add(nextPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH, top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE, TEXTURE, textureWidth, textureHeight)); this.buttonList.add(prevPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X, top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE, TEXTURE, textureWidth, textureHeight)); this.buttonList.add(lastPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - PAGE_BUTTON_SPACING, top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION, TEXTURE, textureWidth, textureHeight)); this.buttonList.add(firstPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X + PAGE_BUTTON_SPACING, top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION, TEXTURE, textureWidth, textureHeight)); this.buttonList.add(indexButton = new GuiButtonTurnPage(buttonID++, left + xSize/2 - 23, top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS, TEXTURE, textureWidth, textureHeight)); this.buttonList.add(locateButton = new GuiButtonLocateBook(buttonID++, left + xSize/2 - 34, top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT)); // Sort buttons for(SortType sortType : SortType.values()){ this.buttonList.add(sortButtons[sortType.ordinal()] = new GuiButtonSpellSort(buttonID++, left + SORT_BUTTON_INSET_X + SORT_BUTTON_SPACING * sortType.ordinal(), top + SORT_BUTTON_INSET_Y, sortType, this, this)); } // Spell buttons for(int i = 0; i < SPELL_BUTTON_COUNT; i++){ int row = i % SPELL_COLUMNS; int column = (i / SPELL_COLUMNS) % SPELL_ROWS; int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING : xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING; int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING; this.buttonList.add(spellButtons[i] = new GuiButtonSpell(buttonID++, left + x, top + y, i)); } this.searchField = new GuiTextField(0, this.fontRenderer, left + 157, top + 21, 106, this.fontRenderer.FONT_HEIGHT); this.searchField.setMaxStringLength(50); this.searchField.setEnableBackgroundDrawing(false); this.searchField.setVisible(true); this.searchField.setTextColor(16777215); this.searchField.setCanLoseFocus(false); this.searchField.setFocused(true); refreshAvailableSpells(); // Must be done last } @Override public void onGuiClosed(){ WizardryPacketHandler.net.sendToServer(new PacketLectern.Message(lectern.getPos(), currentSpell)); super.onGuiClosed(); } @Override protected void drawBackgroundLayer(int left, int top, int mouseX, int mouseY){ if(currentSpell == Spells.none){ drawIndexPage(left, top); }else{ super.drawBackgroundLayer(left, top, mouseX, mouseY); } } @Override protected void drawForegroundLayer(int left, int top, int mouseX, int mouseY){ if(currentSpell == Spells.none){ this.fontRenderer.drawString(I18n.format("gui." + Wizardry.MODID + ":lectern.title"), left + 20, top + SORT_BUTTON_INSET_Y, 0); }else{ super.drawForegroundLayer(left, top, mouseX, mouseY); } this.buttonList.forEach(b -> b.drawButtonForegroundLayer(mouseX, mouseY)); } private void drawIndexPage(int left, int top){ for(int i = 0; i < SPELL_BUTTON_COUNT; i++){ int index = currentPage * SPELL_BUTTON_COUNT + i; Spell spell = index < matchingSpells.size() ? matchingSpells.get(index) : Spells.none; boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(spell, null); Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); int row = i % SPELL_COLUMNS; int column = (i / SPELL_COLUMNS) % SPELL_ROWS; int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING : xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING; int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING; DrawingUtils.drawTexturedRect(left + x + 1, top + y + 1, 0, 0, 32, 32, 32, 32); } mc.renderEngine.bindTexture(getTexture()); DrawingUtils.drawTexturedRect(left, top, 0, 256, xSize, ySize, textureWidth, textureHeight); this.searchField.drawTextBox(); GlStateManager.color(1, 1, 1, 1); mc.renderEngine.bindTexture(getTexture()); } @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { super.mouseClicked(mouseX, mouseY, mouseButton); searchNeedsClearing = true; } @Override protected void actionPerformed(GuiButton button){ int lastPage = getPageCount() - 1; if(button == indexButton){ currentSpell = Spells.none; currentPage = 0; }else if(button == locateButton){ // Close the GUI this.mc.player.connection.sendPacket(new CPacketCloseWindow(this.mc.player.openContainer.windowId)); this.mc.displayGuiScreen(null); // Find the location(s) of the current spell's book(s) and highlight them for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){ for(int i = 0; i < bookshelf.getSizeInventory(); i++){ ItemStack stack = bookshelf.getStackInSlot(i); if(stack.getItem() instanceof ItemSpellBook){ Spell spell = Spell.byMetadata(stack.getMetadata()); if(spell == this.currentSpell){ BlockPos pos = (((TileEntity)bookshelf).getPos()); for(EnumFacing side : EnumFacing.VALUES){ ParticleBuilder.create(ParticleBuilder.Type.BLOCK_HIGHLIGHT).pos( GeometryUtils.getFaceCentre(pos, side) .add(new Vec3d(side.getDirectionVec()) .scale(GeometryUtils.ANTI_Z_FIGHTING_OFFSET))) .face(side).clr(0.9f, 0.5f, 0.8f).fade(0.7f, 0, 1).spawn(mc.world); } mc.world.playSound(pos, WizardrySounds.BLOCK_LECTERN_LOCATE_SPELL, SoundCategory.BLOCKS, 1, 0.7f, false); break; // This bookshelf has the spell, skip to the next bookshelf } } } } }else if(button == nextPageButton){ if(currentPage < lastPage) currentPage++; }else if(button == prevPageButton){ if(currentPage > 0) currentPage--; }else if(button == lastPageButton){ currentPage = lastPage; }else if(button == firstPageButton){ currentPage = 0; }else if(button instanceof GuiButtonSpell){ currentSpell = getSpellForButton((GuiButtonSpell)button); }else if(button instanceof GuiButtonSpellSort){ SortType sortType = (((GuiButtonSpellSort)button).sortType); if(this.sortType == sortType){ this.sortDescending = !this.sortDescending; }else{ this.sortType = sortType; this.sortDescending = false; } updateMatchingSpells(); } updateButtonVisiblity(); } @Override protected void keyTyped(char typedChar, int keyCode) throws IOException { if(this.searchNeedsClearing){ this.searchNeedsClearing = false; this.searchField.setText(""); this.currentPage = 0; } if(this.searchField.textboxKeyTyped(typedChar, keyCode)){ this.currentPage = 0; updateMatchingSpells(); updateButtonVisiblity(); }else{ super.keyTyped(typedChar, keyCode); } } private void updateButtonVisiblity(){ if(currentSpell == Spells.none){ this.searchField.setVisible(true); int lastPage = getPageCount() - 1; prevPageButton.visible = currentPage > 0; firstPageButton.visible = currentPage > 0; nextPageButton.visible = currentPage < lastPage; lastPageButton.visible = currentPage < lastPage; indexButton.visible = false; locateButton.visible = false; for(GuiButton button : sortButtons) button.visible = true; for(GuiButtonSpell button : spellButtons){ button.visible = currentPage * SPELL_BUTTON_COUNT + button.index < matchingSpells.size(); } }else{ this.searchField.setVisible(false); this.buttonList.forEach(b -> b.visible = false); // Hide all buttons... indexButton.visible = true; // ... except the index button and locate button locateButton.visible = true; } } private void updateMatchingSpells(){ matchingSpells = availableSpells.stream() .filter(s -> s.matches(searchField.getText().toLowerCase(Locale.ROOT))) .sorted(sortDescending ? sortType.comparator.reversed() : sortType.comparator) .collect(Collectors.toList()); } // TODO: Config option to always display all spells when in creative /** Called on initialisation and whenever a bookshelf is added or removed, to update the list of spells. */ public void refreshAvailableSpells(){ availableSpells.clear(); // No need to exclude the lectern TE because it's not an IInventory! for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){ for(int i=0; i= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; GlStateManager.color(1, 1, 1, 1); minecraft.getTextureManager().bindTexture(TEXTURE); DrawingUtils.drawTexturedRect(this.x, this.y, flag ? width : 0, 184, width, height, textureWidth, textureHeight); } } } private class GuiButtonSpell extends GuiButtonInvisible { private static final int WIDTH = 34, HEIGHT = 34; private final int index; public GuiButtonSpell(int id, int x, int y, int index){ super(id, x, y, WIDTH, HEIGHT); this.index = index; } @Override public void playPressSound(SoundHandler soundHandler){ soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1)); } @Override public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){ if(visible){ super.drawButton(minecraft, mouseX, mouseY, partialTicks); if(hovered){ mc.renderEngine.bindTexture(getTexture()); DrawingUtils.drawTexturedRect(x, y, 40, 180, width, height, textureWidth, textureHeight); } } } @Override public void drawButtonForegroundLayer(int mouseX, int mouseY){ if(visible && hovered){ Spell spell = getSpellForButton(this); if(Wizardry.proxy.shouldDisplayDiscovered(spell, null)){ drawHoveringText(Collections.singletonList(spell.getDisplayName()), mouseX, mouseY, fontRenderer); }else{ drawHoveringText(Collections.singletonList(SpellGlyphData.getGlyphName(spell, mc.world)), mouseX, mouseY, mc.standardGalacticFontRenderer); } } } } }