package com.teamwizardry.wizardry.client.gui.worktable; import com.teamwizardry.librarianlib.core.LibrarianLib; import com.teamwizardry.librarianlib.features.animator.Easing; import com.teamwizardry.librarianlib.features.animator.animations.BasicAnimation; import com.teamwizardry.librarianlib.features.animator.animations.Keyframe; import com.teamwizardry.librarianlib.features.animator.animations.KeyframeAnimation; import com.teamwizardry.librarianlib.features.animator.animations.ScheduledEventAnimation; import com.teamwizardry.librarianlib.features.gui.GuiBase; import com.teamwizardry.librarianlib.features.gui.component.GuiComponent; import com.teamwizardry.librarianlib.features.gui.component.GuiComponentEvents; import com.teamwizardry.librarianlib.features.gui.components.ComponentRect; import com.teamwizardry.librarianlib.features.gui.components.ComponentSprite; import com.teamwizardry.librarianlib.features.gui.components.ComponentText; import com.teamwizardry.librarianlib.features.gui.components.ComponentVoid; import com.teamwizardry.librarianlib.features.gui.mixin.DragMixin; import com.teamwizardry.librarianlib.features.helpers.NBTHelper; import com.teamwizardry.librarianlib.features.math.Vec2d; import com.teamwizardry.librarianlib.features.network.PacketHandler; import com.teamwizardry.librarianlib.features.sprite.Sprite; import com.teamwizardry.wizardry.Wizardry; import com.teamwizardry.wizardry.api.spell.CommonWorktableModule; import com.teamwizardry.wizardry.api.spell.SpellBuilder; import com.teamwizardry.wizardry.api.spell.SpellRing; import com.teamwizardry.wizardry.api.spell.SpellUtils; import com.teamwizardry.wizardry.api.spell.module.ModuleInstance; import com.teamwizardry.wizardry.api.spell.module.ModuleInstanceModifier; import com.teamwizardry.wizardry.api.spell.module.ModuleRegistry; import com.teamwizardry.wizardry.api.spell.module.ModuleType; import com.teamwizardry.wizardry.api.util.RandUtil; import com.teamwizardry.wizardry.common.network.PacketSendSpellToBook; import com.teamwizardry.wizardry.common.network.PacketSyncWorktable; import com.teamwizardry.wizardry.common.tile.TileMagiciansWorktable; import com.teamwizardry.wizardry.init.ModItems; import com.teamwizardry.wizardry.init.ModSounds; import kotlin.Pair; import net.minecraft.client.Minecraft; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.awt.*; import java.util.List; import java.util.*; import java.util.function.Function; /** * Created by Demoniaque on 6/17/2016. */ public class WorktableGui extends GuiBase { static final Sprite TABLE_SPRITE = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/table.png")); static final Sprite SIDE_BAR_SHORT = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/sidebar.png")); static final Sprite SIDE_BAR_LONG = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/sidebar_long.png")); static final Sprite BUTTON_NORMAL = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/button.png")); static final Sprite BUTTON_HIGHLIGHTED = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/button_highlighted.png")); static final Sprite BUTTON_PRESSED = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/button_pressed.png")); static final Sprite BUTTON_SHORT_NORMAL = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/button_short.png")); static final Sprite BUTTON_SHORT_HIGHLIGHTED = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/button_short_highlighted.png")); static final Sprite BUTTON_SHORT_PRESSED = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/button_short_pressed.png")); static final Sprite PLATE = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/plate.png")); static final Sprite PLATE_HIGHLIGHTED = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/plate_highlighted.png")); static final Sprite PLATE_HIGHLIGHTED_ERROR = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/plate_highlighted_error.png")); static final Sprite STREAK = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/streak.png"), 16, 16); static final Sprite BOOK_ICON = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/items/book.png")); static final Sprite SAVE_ICON = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/save.png")); static final Sprite BROOM_ICON = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/broom.png")); static final Sprite BOOK_COVER_ICON = new Sprite(new ResourceLocation(Wizardry.MODID, "textures/gui/worktable/book_cover.png")); final ComponentModifiers modifiers; final ComponentVoid paper; final ComponentText toast; @Nullable public TableModule selectedModule = null; public float backgroundAlpha = 0f; public boolean animationPlaying = false; private ComponentSprite tableComponent; private boolean hadBook = false, bookWarnRevised = false; protected Set<CommonWorktableModule> commonModules = new HashSet<>(); private List<List<ModuleInstance>> chains = new ArrayList<>(); private boolean canBeSaved = false; @NotNull private final BlockPos pos; public WorktableGui(@Nonnull BlockPos pos) { super(480, 224); this.pos = pos; // GRAY OUT BACKGROUND ComponentRect grayBackground = new ComponentRect(0, 0, 40000, 40000); grayBackground.getColor().setValue(new Color(0.05f, 0.05f, 0.05f, 0.8f)); grayBackground.getTransform().setTranslateZ(-20); getFullscreenComponents().add(grayBackground); // TABLE tableComponent = new ComponentSprite(TABLE_SPRITE, 0, 0, 480, 224); getMainComponents().add(tableComponent); // PAPER PLATE paper = new ComponentVoid(181, 22, 180, 184); tableComponent.add(paper); // --- TOAST BOX --- // toast = new ComponentText(384, 56, ComponentText.TextAlignH.LEFT, ComponentText.TextAlignV.TOP); toast.setSize(new Vec2d(80, 63).mul(2)); toast.clipping.setClipToBounds(true); toast.getTransform().setScale(0.5f); toast.getWrap().setValue(160); tableComponent.add(toast); setCodexToastMessage(); // --- TOAST BOX --- // toast.BUS.hook(GuiComponentEvents.ComponentTickEvent.class, event -> { if (!bookWarnRevised && !hadBook && Minecraft.getMinecraft().player.inventory.hasItemStack(new ItemStack(ModItems.BOOK))) { toast.getColor().setValue(Color.GREEN); toast.getText().setValue(LibrarianLib.PROXY.translate("wizardry.table.codex_found")); bookWarnRevised = true; } }); // --- SIDEBAR MODULES --- // ComponentSprite shapes = new ComponentSprite(SIDE_BAR_SHORT, 29, 31, 48, 80); addModules(shapes, ModuleType.SHAPE); tableComponent.add(shapes); ComponentSprite effects = new ComponentSprite(SIDE_BAR_LONG, 93, 37, 48, 160); addModules(effects, ModuleType.EFFECT); tableComponent.add(effects); ComponentSprite events = new ComponentSprite(SIDE_BAR_SHORT, 29, 123, 48, 80); addModules(events, ModuleType.EVENT); tableComponent.add(events); // --- SIDEBAR MODULES --- // modifiers = new ComponentModifiers(this); tableComponent.add(modifiers); int menuX = 384; int menuY = 20; int menuWidth = 80; int buttonWidth = 20; int buttonHeight = 16; int buttonX = 390; int buttonY = 25; int spacing = 5; int iconSize = 12; int iconSizeHovered = 16; // --- SAVE BUTTON --- // { ComponentSprite save = new ComponentSprite(BUTTON_SHORT_NORMAL, menuX + (menuWidth / 2) - buttonWidth - (buttonWidth / 2) - spacing, spacing + menuY, buttonWidth, buttonHeight); ComponentSprite sprite = new ComponentSprite(SAVE_ICON, (buttonWidth / 2) - (iconSize / 2), (buttonHeight / 2) - (iconSize / 2), iconSize, iconSize); save.add(sprite); save.render.getTooltip().func((Function<GuiComponent, List<String>>) t -> { List<String> txt = new ArrayList<>(); if (!animationPlaying) { txt.add(TextFormatting.GOLD + LibrarianLib.PROXY.translate("wizardry.table.save")); txt.add(TextFormatting.GRAY + LibrarianLib.PROXY.translate("wizardry.table.save_desc")); if (!Minecraft.getMinecraft().player.inventory.hasItemStack(new ItemStack(ModItems.BOOK))) { txt.add(TextFormatting.RED + LibrarianLib.PROXY.translate("wizardry.table.save_error")); } if (!canBeSaved) { txt.add(TextFormatting.RED + "Invalid Spell!"); } } return txt; }); save.BUS.hook(GuiComponentEvents.ComponentTickEvent.class, event -> { if (!canBeSaved || animationPlaying || !Minecraft.getMinecraft().player.inventory.hasItemStack(new ItemStack(ModItems.BOOK))) { save.setSprite(BUTTON_SHORT_PRESSED); } else { if (event.component.getMouseOver()) save.setSprite(BUTTON_SHORT_HIGHLIGHTED); else save.setSprite(BUTTON_SHORT_NORMAL); } }); save.BUS.hook(GuiComponentEvents.MouseDownEvent.class, event -> { if (!animationPlaying && event.component.getMouseOver()) Minecraft.getMinecraft().player.playSound(ModSounds.BUTTON_CLICK_IN, 1f, 1f); }); save.BUS.hook(GuiComponentEvents.MouseUpEvent.class, event -> { if (!animationPlaying && event.component.getMouseOver()) Minecraft.getMinecraft().player.playSound(ModSounds.BUTTON_CLICK_OUT, 1f, 1f); }); paper.BUS.hook(TableModule.ModuleUpdateEvent.class, event -> { commonModules.clear(); chains.clear(); for (TableModule head : getSpellHeads()) { List<ModuleInstance> chain = new ArrayList<>(); CommonWorktableModule lastCommonModule = new CommonWorktableModule(head.hashCode(), head.getModule(), head.getPos(), null, new HashMap<>()); commonModules.add(lastCommonModule); chain.add(head.getModule()); // Add head modifiers for (ModuleInstance module : ModuleRegistry.INSTANCE.getModules(ModuleType.MODIFIER)) { if (!(module instanceof ModuleInstanceModifier)) continue; if (!head.hasData(Integer.class, module.getNBTKey())) continue; int count = head.getData(Integer.class, module.getNBTKey()); lastCommonModule.addModifier((ModuleInstanceModifier) module, count); for (int i = 0; i < count; i++) { chain.add(module); } } TableModule linksTo = head.getLinksTo(); while (linksTo != null) { if (linksTo.isInvalid()) continue; chain.add(linksTo.getModule()); CommonWorktableModule commonModule = new CommonWorktableModule(linksTo.hashCode(), linksTo.getModule(), linksTo.getPos(), null, new HashMap<>()); lastCommonModule.setLinksTo(commonModule); lastCommonModule = commonModule; for (ModuleInstance module : ModuleRegistry.INSTANCE.getModules(ModuleType.MODIFIER)) { if (!(module instanceof ModuleInstanceModifier)) continue; if (!linksTo.hasData(Integer.class, module.getNBTKey())) continue; int count = linksTo.getData(Integer.class, module.getNBTKey()); for (int i = 0; i < count; i++) { chain.add(module); } lastCommonModule.addModifier((ModuleInstanceModifier) module, count); } linksTo = linksTo.getLinksTo(); } chains.add(chain); } canBeSaved = true; primary: for (GuiComponent paperComponent : paper.getChildren()) { if (!(paperComponent instanceof TableModule)) continue; TableModule module = (TableModule) paperComponent; if (module.isInvalid()) continue; if (module.getLinksTo() == null) { for (GuiComponent subPaperComponent : paper.getChildren()) { if (!(subPaperComponent instanceof TableModule)) continue; TableModule subModule = (TableModule) subPaperComponent; if (subModule.isInvalid()) continue; if (subModule == module) continue; if (subModule.getLinksTo() == module) { continue primary; } } module.setErrored(true); TableModule.select(module); canBeSaved = false; } } if (!canBeSaved) { setToastMessage("Component is not linked to anything! Link it to something to make it function properly.", Color.RED); return; } primary: for (GuiComponent paperComponent : paper.getChildren()) { if (!(paperComponent instanceof TableModule)) continue; TableModule module = (TableModule) paperComponent; if (module.isInvalid()) continue; if (module.getModule().getModuleType() != ModuleType.SHAPE) { for (GuiComponent subPaperComponent : paper.getChildren()) { if (!(subPaperComponent instanceof TableModule)) continue; TableModule subModule = (TableModule) subPaperComponent; if (subModule.isInvalid()) continue; if (subModule == module) continue; if (subModule.getLinksTo() == module) continue primary; } module.setErrored(true); TableModule.select(module); canBeSaved = false; } } if (!canBeSaved) { setToastMessage("Spell chain starts without a Shape component! Your spell needs to start with a shape to run properly.", Color.RED); return; } boolean shapeLinkedFromNothing = false; for (CommonWorktableModule module : commonModules) { CommonWorktableModule lastModule = module; while (lastModule != null) { if (lastModule.module.getModuleType() == ModuleType.SHAPE) { if (lastModule.linksTo == null) { canBeSaved = false; setToastMessage("Shape component is not linked to anything! It needs to link to something so you can run your spell properly.", Color.RED); } else { boolean isLinkedFrom = false; for (CommonWorktableModule linkedFrom : commonModules) { if (linkedFrom.linksTo == lastModule) { isLinkedFrom = true; break; } } if (!isLinkedFrom) shapeLinkedFromNothing = true; } } else if (lastModule.module.getModuleType() == ModuleType.EVENT) { if (lastModule.linksTo == null) { canBeSaved = false; setToastMessage("Event is linked to nothing. Link it to an effect to use it properly.", Color.RED); } } lastModule = lastModule.linksTo; } } if (!canBeSaved) return; if (!shapeLinkedFromNothing) { canBeSaved = false; setToastMessage("No spell starting with a shape found. Start a spell with a shape to be able to run it properly.", Color.RED); } if (canBeSaved) { setToastMessage("Spell is valid!", Color.GREEN); } }); save.BUS.hook(GuiComponentEvents.MouseClickEvent.class, (event) -> { if (!canBeSaved || animationPlaying || !Minecraft.getMinecraft().player.inventory.hasItemStack(new ItemStack(ModItems.BOOK))) return; NBTTagList commonList = new NBTTagList(); for (CommonWorktableModule commonModule : commonModules) { commonList.appendTag(commonModule.serializeNBT()); } // for (ItemStack stack : Minecraft.getMinecraft().player.inventory.mainInventory) { // if (stack.getItem() == ModItems.BOOK) { // NBTHelper.setList(stack, "common_modules", commonList); // } // } PacketHandler.NETWORK.sendToServer(new PacketSendSpellToBook(chains, commonModules)); playSaveAnimation(null); }); getMainComponents().add(save); } // --- SAVE BUTTON --- // // --- LOAD BUTTON --- // { ComponentSprite load = new ComponentSprite(BUTTON_SHORT_NORMAL, menuX + (menuWidth / 2) - (buttonWidth / 2), spacing + menuY, buttonWidth, buttonHeight); String saveStr = LibrarianLib.PROXY.translate("wizardry.table.load"); int stringWidth = Minecraft.getMinecraft().fontRenderer.getStringWidth(saveStr); int fitWidth = load.getSize().getXi() - 16; ComponentText textSave = new ComponentText(16 + (int) (fitWidth / 2.0 - stringWidth / 2.0), (load.getSize().getYi() / 2), ComponentText.TextAlignH.LEFT, ComponentText.TextAlignV.MIDDLE); textSave.getText().setValue(saveStr); // load.add(textSave); ComponentSprite sprite = new ComponentSprite(BOOK_COVER_ICON, (buttonWidth / 2) - (iconSize / 2), (buttonHeight / 2) - (iconSize / 2), iconSize, iconSize); load.add(sprite); load.render.getTooltip().func((Function<GuiComponent, List<String>>) t -> { List<String> txt = new ArrayList<>(); if (!animationPlaying) { txt.add(TextFormatting.GOLD + LibrarianLib.PROXY.translate("wizardry.table.load")); txt.add(TextFormatting.GRAY + LibrarianLib.PROXY.translate("wizardry.table.load_desc")); } return txt; }); load.BUS.hook(GuiComponentEvents.ComponentTickEvent.class, event -> { if (animationPlaying) { load.setSprite(BUTTON_SHORT_PRESSED); } else { if (event.component.getMouseOver()) load.setSprite(BUTTON_SHORT_HIGHLIGHTED); else load.setSprite(BUTTON_SHORT_NORMAL); } }); load.BUS.hook(GuiComponentEvents.MouseDownEvent.class, event -> { if (!animationPlaying && event.component.getMouseOver()) Minecraft.getMinecraft().player.playSound(ModSounds.BUTTON_CLICK_IN, 1f, 1f); }); load.BUS.hook(GuiComponentEvents.MouseUpEvent.class, event -> { if (!animationPlaying && event.component.getMouseOver()) Minecraft.getMinecraft().player.playSound(ModSounds.BUTTON_CLICK_OUT, 1f, 1f); }); load.BUS.hook(GuiComponentEvents.MouseClickEvent.class, (event) -> { if (animationPlaying) return; NBTTagList commonList = null; for (ItemStack stack : Minecraft.getMinecraft().player.inventory.mainInventory) { if (stack.getItem() == ModItems.BOOK) { NBTTagList list = NBTHelper.getList(stack, "common_modules", NBTTagCompound.class); if (list == null || list.isEmpty()) continue; commonList = list; } } if (commonList == null) return; Set<CommonWorktableModule> commonModules = new HashSet<>(); for (NBTBase base : commonList) { if (base instanceof NBTTagCompound) { NBTTagCompound compound = (NBTTagCompound) base; CommonWorktableModule commonModule = CommonWorktableModule.deserailize(compound); commonModules.add(commonModule); } } boolean isEmpty = true; for (GuiComponent child : paper.getChildren()) { if (child instanceof TableModule) { isEmpty = false; break; } } if (isEmpty) { playLoadAnimation(commonModules, () -> { syncToServer(); animationPlaying = false; }); } else playClearAnimation(() -> playLoadAnimation(commonModules, () -> { syncToServer(); animationPlaying = false; })); setToastMessageNoHeader(LibrarianLib.PROXY.translate("wizardry.table.spell_loaded"), Color.GREEN); }); getMainComponents().add(load); } // --- LOAD BUTTON --- // // --- CLEAR BUTTON --- // { ComponentSprite clear = new ComponentSprite(BUTTON_SHORT_NORMAL, menuX + (menuWidth / 2) + (buttonWidth / 2) + spacing, spacing + menuY, buttonWidth, buttonHeight); String saveStr = LibrarianLib.PROXY.translate("wizardry.table.clear"); int stringWidth = Minecraft.getMinecraft().fontRenderer.getStringWidth(saveStr); int fitWidth = clear.getSize().getXi() - 16; ComponentText textSave = new ComponentText(16 + (int) (fitWidth / 2.0 - stringWidth / 2.0), (clear.getSize().getYi() / 2), ComponentText.TextAlignH.LEFT, ComponentText.TextAlignV.MIDDLE); textSave.getText().setValue(saveStr); // clear.add(textSave); ComponentSprite sprite = new ComponentSprite(BROOM_ICON, (buttonWidth / 2) - (iconSize / 2), (buttonHeight / 2) - (iconSize / 2), iconSize, iconSize); clear.add(sprite); clear.render.getTooltip().func((Function<GuiComponent, List<String>>) t -> { List<String> txt = new ArrayList<>(); if (!animationPlaying) { txt.add(TextFormatting.GOLD + LibrarianLib.PROXY.translate("wizardry.table.clear")); txt.add(TextFormatting.GRAY + LibrarianLib.PROXY.translate("wizardry.table.clear_desc")); } return txt; }); clear.BUS.hook(GuiComponentEvents.ComponentTickEvent.class, event -> { if (animationPlaying) { clear.setSprite(BUTTON_SHORT_PRESSED); } else { if (event.component.getMouseOver()) clear.setSprite(BUTTON_SHORT_HIGHLIGHTED); else clear.setSprite(BUTTON_SHORT_NORMAL); } }); clear.BUS.hook(GuiComponentEvents.MouseDownEvent.class, event -> { if (!animationPlaying && event.component.getMouseOver()) Minecraft.getMinecraft().player.playSound(ModSounds.BUTTON_CLICK_IN, 1f, 1f); }); clear.BUS.hook(GuiComponentEvents.MouseUpEvent.class, event -> { if (!animationPlaying && event.component.getMouseOver()) Minecraft.getMinecraft().player.playSound(ModSounds.BUTTON_CLICK_OUT, 1f, 1f); }); clear.BUS.hook(GuiComponentEvents.MouseClickEvent.class, (event) -> { if (animationPlaying) return; playClearAnimation(() -> { syncToServer(); animationPlaying = false; }); setToastMessageNoHeader(LibrarianLib.PROXY.translate("wizardry.table.paper_cleared"), Color.GREEN); }); getMainComponents().add(clear); } // --- CLEAR BUTTON --- // load(); paper.BUS.fire(new TableModule.ModuleUpdateEvent()); } public void load() { TileEntity tile = Minecraft.getMinecraft().world.getTileEntity(pos); if (tile instanceof TileMagiciansWorktable) { if (((TileMagiciansWorktable) tile).commonModules != null) { Set<CommonWorktableModule> commonModules = ((TileMagiciansWorktable) tile).getCommonModules(); for (CommonWorktableModule commonHead : commonModules) { if(commonHead != null) { // ADDED HERE CommonWorktableModule commonModule = commonHead; TableModule lastModule = new TableModule(this, commonHead.module, true, false); lastModule.setPos(commonHead.pos); while (commonModule != null) { lastModule.radius = 10; lastModule.textRadius = 0; paper.add(lastModule); DragMixin drag = new DragMixin(lastModule, vec2d -> vec2d); drag.setDragOffset(new Vec2d(6, 6)); for (ModuleInstanceModifier modifier : commonModule.modifiers.keySet()) { lastModule.setData(Integer.class, modifier.getNBTKey(), commonModule.modifiers.get(modifier)); } if (commonModule.linksTo != null) { if(commonModule.linksTo.module != null) { TableModule childModule = new TableModule(this, commonModule.linksTo.module, true, false); childModule.setPos(commonModule.linksTo.pos); lastModule.setLinksTo(childModule); lastModule = childModule; } else { // If a module was removed from the mod, try and clear the table. // This doesn't clear the whole table, but it's good enough. ((TileMagiciansWorktable) tile).setCommonModules(new NBTTagList()); syncToServer(); Minecraft.getMinecraft().player.sendMessage(new TextComponentString(TextFormatting.RED + "wizardry.table.no_component_error")); return; } } commonModule = commonModule.linksTo; } } } } } } public void syncToServer() { Set<CommonWorktableModule> commonModules = new HashSet<>(); for (TableModule head : getSpellHeads()) { TableModule lastModule = head; CommonWorktableModule lastCommonModule = new CommonWorktableModule(lastModule.hashCode(), lastModule.getModule(), lastModule.getPos(), null, new HashMap<>()); commonModules.add(lastCommonModule); while (lastModule != null) { if (lastModule != head) { CommonWorktableModule commonModule = new CommonWorktableModule(lastModule.hashCode(), lastModule.getModule(), lastModule.getPos(), null, new HashMap<>()); lastCommonModule.setLinksTo(commonModule); lastCommonModule = commonModule; } for (ModuleInstance module : ModuleRegistry.INSTANCE.getModules(ModuleType.MODIFIER)) { if (!(module instanceof ModuleInstanceModifier)) continue; if (!lastModule.hasData(Integer.class, module.getNBTKey())) continue; int count = lastModule.getData(Integer.class, module.getNBTKey()); lastCommonModule.addModifier((ModuleInstanceModifier) module, count); } lastModule = lastModule.getLinksTo(); } } int dim = Minecraft.getMinecraft().world.provider.getDimension(); PacketHandler.NETWORK.sendToServer(new PacketSyncWorktable(dim, pos, commonModules)); } public void setCodexToastMessage() { if (!Minecraft.getMinecraft().player.inventory.hasItemStack(new ItemStack(ModItems.BOOK))) { hadBook = false; toast.getColor().setValue(Color.RED); toast.getText().setValue(LibrarianLib.PROXY.translate("wizardry.table.no_codex_found")); } else { hadBook = true; toast.getColor().setValue(Color.GREEN); toast.getText().setValue(LibrarianLib.PROXY.translate("wizardry.table.codex_found")); } } public String getToastHeader() { return TextFormatting.YELLOW.toString() + getSpellName() + TextFormatting.RESET + "\n\n"; } public String getSpellName() { List<List<ModuleInstance>> chains = new ArrayList<>(); for (TableModule head : getSpellHeads()) { List<ModuleInstance> chain = new ArrayList<>(); TableModule lastModule = head; while (lastModule != null) { chain.add(lastModule.getModule()); for (ModuleInstance module : ModuleRegistry.INSTANCE.getModules(ModuleType.MODIFIER)) { if (!(module instanceof ModuleInstanceModifier)) continue; if (!lastModule.hasData(Integer.class, module.getNBTKey())) continue; int count = lastModule.getData(Integer.class, module.getNBTKey()); for (int i = 0; i < count; i++) { chain.add(module); } } lastModule = lastModule.getLinksTo(); } chains.add(chain); } SpellBuilder builder = new SpellBuilder(SpellUtils.getSpellItems(chains)); StringBuilder spellNameBuilder = new StringBuilder(); SpellRing lastRing = null; for (SpellRing ring : builder.getSpell()) { if (lastRing == null) lastRing = ring; if (ring != null) { if (ring != lastRing) spellNameBuilder.append(TextFormatting.GRAY).append(" | "); SpellRing tmpRing = ring; while (tmpRing != null) { spellNameBuilder .append(TextFormatting.YELLOW) .append(tmpRing.getModuleReadableName()) .append(TextFormatting.GRAY) .append("(") .append(TextFormatting.BLUE) .append(Math.round(tmpRing.getManaDrain(null) * tmpRing.getManaMultiplier())) .append(TextFormatting.GRAY) .append("/") .append(TextFormatting.RED) .append(Math.round(tmpRing.getBurnoutFill(null) * tmpRing.getBurnoutMultiplier())) .append(TextFormatting.GRAY) .append(")"); tmpRing = tmpRing.getChildRing(); if (tmpRing != null) { spellNameBuilder.append(TextFormatting.GRAY).append(" > "); } } } } return spellNameBuilder.toString(); } public Pair<String, Color> getToastMessage() { String text = toast.getText().getValue(toast); Color color = toast.getColor().getValue(toast); return new Pair<>(text, color); } public void setToastMessage(String text, Color color) { toast.getText().setValue(getToastHeader() + text); toast.getColor().setValue(color); } public void setToastMessageNoHeader(String text, Color color) { toast.getText().setValue(text); toast.getColor().setValue(color); } private Set<TableModule> getSpellHeads() { Set<TableModule> set = new HashSet<>(); for (GuiComponent child : paper.getChildren()) { if (!(child instanceof TableModule)) continue; TableModule childModule = (TableModule) child; if (childModule.isInvalid()) continue; boolean linkedToSomehow = false; for (GuiComponent subChild : paper.getChildren()) { if (subChild.isInvalid()) continue; if (!(subChild instanceof TableModule)) continue; TableModule subChildModule = (TableModule) subChild; if (subChildModule == childModule) continue; if (subChildModule.getLinksTo() == childModule) { linkedToSomehow = true; break; } } if (!linkedToSomehow) { set.add(childModule); } } return set; } private void addModules(ComponentSprite parent, ModuleType type) { int column = 0, row = 0; for (ModuleInstance module : ModuleRegistry.INSTANCE.getModules(type)) { TableModule tableModule = new TableModule(this, module, false, false); tableModule.setPos(new Vec2d(row * 16, column * 16)); parent.add(tableModule); if (++row >= 3) { row = 0; column++; } } } public void playSaveAnimation(@Nullable Runnable finish) { animationPlaying = true; resetSelectedModule(); Runnable runnable = () -> { ComponentVoid fakePaper = new ComponentVoid(180, 19, 180, 188); fakePaper.getTransform().setTranslateZ(100); getMainComponents().add(fakePaper); ComponentVoid bookIconMask = new ComponentVoid(0, -100, 180, 100); fakePaper.add(bookIconMask); // GRAY BACKGROUND { ComponentRect grayBackground = new ComponentRect(0, 0, tableComponent.getSize().getXi(), tableComponent.getSize().getYi()); grayBackground.getColor().setValue(new Color(0.05f, 0.05f, 0.05f, 0f)); grayBackground.getTransform().setTranslateZ(200); grayBackground.BUS.hook(GuiComponentEvents.ComponentTickEvent.class, event -> { grayBackground.getColor().setValue(new Color(0.05f, 0.05f, 0.05f, backgroundAlpha)); }); tableComponent.add(grayBackground); KeyframeAnimation<WorktableGui> anim = new KeyframeAnimation<>(this, "backgroundAlpha"); anim.setDuration(100); anim.setKeyframes(new Keyframe[]{ new Keyframe(0, 0f, Easing.easeInOutQuint), new Keyframe(0.2f, 0.65f, Easing.easeInOutQuint), new Keyframe(0.7f, 0.65f, Easing.easeInOutQuint), new Keyframe(1f, 0f, Easing.easeInOutQuint) }); anim.setCompletion(grayBackground::invalidate); getMainComponents().add(anim); } // BOOK PEAK ANIMATION { ComponentSprite bookIcon = new ComponentSprite(BOOK_ICON, (int) ((bookIconMask.getSize().getX() / 2.0) - 16), (int) (bookIconMask.getSize().getY() + 50), 32, 32); bookIconMask.add(bookIcon); bookIcon.getTransform().setTranslateZ(200); bookIconMask.clipping.setClipToBounds(true); bookIconMask.getTransform().setTranslateZ(250); final Vec2d originalPos = bookIcon.getPos(); KeyframeAnimation<ComponentSprite> anim = new KeyframeAnimation<>(bookIcon, "pos.y"); anim.setDuration(120); anim.setKeyframes(new Keyframe[]{ new Keyframe(0, originalPos.getY(), Easing.linear), new Keyframe(0.4f, (bookIconMask.getSize().getY() / 2.0) - 25, Easing.easeInBack), new Keyframe(0.5f, (bookIconMask.getSize().getY() / 2.0) - 10, Easing.easeOutBack), new Keyframe(0.8f, (bookIconMask.getSize().getY() / 2.0) - 10, Easing.easeInBack), new Keyframe(1f, originalPos.getY(), Easing.easeInBack) }); anim.setCompletion(() -> { fakePaper.invalidate(); if (finish == null) animationPlaying = false; else { finish.run(); } }); ScheduledEventAnimation animSound = new ScheduledEventAnimation(120 * 0.5f, () -> Minecraft.getMinecraft().player.playSound(ModSounds.SCRIBBLING, 1f, 1f)); bookIcon.add(anim, animSound); } // PAPER ITEMS ANIMATION { HashMap<TableModule, UUID> links = new HashMap<>(); for (GuiComponent component : paper.getChildren()) { if (!(component instanceof TableModule)) continue; TableModule tableModule = (TableModule) component; TableModule fakeModule = new TableModule(this, tableModule.getModule(), false, true); fakeModule.setPos(tableModule.getPos()); for (Object tag : tableModule.getTagList()) fakeModule.addTag(tag); fakeModule.getTransform().setTranslateZ(230); fakePaper.add(fakeModule); for (ModuleInstance module : ModuleRegistry.INSTANCE.getModules(ModuleType.MODIFIER)) { if (tableModule.hasData(Integer.class, module.getNBTKey())) { fakeModule.setData(Integer.class, module.getNBTKey(), tableModule.getData(Integer.class, module.getNBTKey())); } } UUID uuid = tableModule.getData(UUID.class, "uuid"); if (uuid != null) fakeModule.setData(UUID.class, "uuid", uuid); TableModule linkedModule = tableModule.getLinksTo(); if (linkedModule == null) continue; UUID linkTo = linkedModule.getData(UUID.class, "uuid"); if (linkTo != null) links.put(fakeModule, linkTo); } for (TableModule module : links.keySet()) { if (!links.containsKey(module)) continue; UUID linkTo = links.get(module); if (linkTo == null) continue; for (GuiComponent child : fakePaper.getChildren()) { UUID uuid = child.getData(UUID.class, "uuid"); if (uuid == null) continue; if (!linkTo.equals(uuid)) continue; if (!(child instanceof TableModule)) continue; TableModule reverseUUIDLink = (TableModule) child; module.setLinksTo(reverseUUIDLink); break; } } for (GuiComponent component : fakePaper.getChildren()) { if (!(component instanceof TableModule)) continue; TableModule fakeModule = (TableModule) component; Vec2d random = fakeModule.getPos().add(RandUtil.nextDouble(-20, 20), RandUtil.nextDouble(-20, 20)); float delay = RandUtil.nextFloat(0.2f, 0.3f); float dur = RandUtil.nextFloat(70, 100); ScheduledEventAnimation animSound1 = new ScheduledEventAnimation(dur * delay, () -> Minecraft.getMinecraft().player.playSound(ModSounds.POP, 1f, 1f)); ScheduledEventAnimation animSound2 = new ScheduledEventAnimation(dur * 0.75f, () -> Minecraft.getMinecraft().player.playSound(ModSounds.WHOOSH, 1f, 1f)); KeyframeAnimation<TableModule> animX = new KeyframeAnimation<>(fakeModule, "pos.x"); animX.setDuration(dur); animX.setKeyframes(new Keyframe[]{ new Keyframe(delay, fakeModule.getPos().getX(), Easing.easeOutQuint), new Keyframe(0.45f, random.getX(), Easing.easeOutQuint), new Keyframe(0.6f, random.getX(), Easing.easeOutQuint), new Keyframe(1f, (bookIconMask.getSize().getX() / 2.0) - 8, Easing.easeInOutQuint) }); KeyframeAnimation<TableModule> animY = new KeyframeAnimation<>(fakeModule, "pos.y"); animY.setDuration(dur); animY.setKeyframes(new Keyframe[]{ new Keyframe(delay, fakeModule.getPos().getY(), Easing.easeOutQuint), new Keyframe(0.45f, random.getY(), Easing.easeOutQuint), new Keyframe(0.6f, random.getY(), Easing.easeOutQuint), new Keyframe(1f, -(bookIconMask.getSize().getY() / 2.0) - 4, Easing.easeInOutQuint) }); BasicAnimation<TableModule> animRadius = new BasicAnimation<>(fakeModule, "radius"); animRadius.setDuration(20); animRadius.setEasing(Easing.easeOutCubic); animRadius.setTo(0); BasicAnimation<TableModule> animText = new BasicAnimation<>(fakeModule, "textRadius"); animText.setDuration(40); animText.setEasing(Easing.easeOutCubic); animText.setTo(0); animY.setCompletion(fakeModule::invalidate); fakeModule.add(animX, animY, animSound1, animSound2, animRadius, animText); } } }; if (selectedModule != null) { selectedModule = null; getMainComponents().add(new ScheduledEventAnimation(5, runnable)); } else runnable.run(); } private void resetSelectedModule() { if (selectedModule != null) { Vec2d toSize = new Vec2d(16, 16); BasicAnimation<TableModule> animSize = new BasicAnimation<>(selectedModule, "size"); animSize.setDuration(5); animSize.setEasing(Easing.easeOutCubic); animSize.setTo(toSize); selectedModule.add(animSize); BasicAnimation<TableModule> animPos = new BasicAnimation<>(selectedModule, "pos"); animPos.setDuration(5); animPos.setEasing(Easing.easeOutCubic); animPos.setTo(selectedModule.getPos().add((selectedModule.getSize().sub(toSize)).mul(0.5f))); selectedModule.add(animPos); } } public void playClearAnimation(@Nullable Runnable finish) { animationPlaying = true; resetSelectedModule(); Runnable runnable = () -> { // PAPER ITEMS ANIMATION { ScheduledEventAnimation animFinish = new ScheduledEventAnimation(110, () -> { if (finish == null) { animationPlaying = false; syncToServer(); } else finish.run(); }); paper.add(animFinish); for (GuiComponent component : paper.getChildren()) { if (!(component instanceof TableModule)) continue; TableModule module = (TableModule) component; Vec2d random = module.getPos().add(RandUtil.nextDouble(-20, 20), RandUtil.nextDouble(-20, 20)); float delay = RandUtil.nextFloat(0.2f, 0.3f); float dur = RandUtil.nextFloat(70, 100); BasicAnimation<TableModule> animRadius = new BasicAnimation<>(module, "radius"); animRadius.setDuration(20); animRadius.setEasing(Easing.easeOutCubic); animRadius.setTo(0); BasicAnimation<TableModule> animText = new BasicAnimation<>(module, "textRadius"); animText.setDuration(40); animText.setEasing(Easing.easeOutCubic); animText.setTo(0); ScheduledEventAnimation animSound1 = new ScheduledEventAnimation(dur * delay, () -> Minecraft.getMinecraft().player.playSound(ModSounds.POP, 1f, 1f)); KeyframeAnimation<TableModule> animX = new KeyframeAnimation<>(module, "pos.x"); animX.setDuration(dur); animX.setKeyframes(new Keyframe[]{ new Keyframe(delay, module.getPos().getX(), Easing.easeOutQuint), new Keyframe(0.45f, random.getX(), Easing.easeOutQuint), new Keyframe(0.6f, random.getX(), Easing.easeOutQuint), new Keyframe(1f, random.getX() + RandUtil.nextDouble(-10, 10), Easing.easeInOutQuint) }); KeyframeAnimation<TableModule> animY = new KeyframeAnimation<>(module, "pos.y"); animY.setDuration(dur); animY.setKeyframes(new Keyframe[]{ new Keyframe(delay, module.getPos().getY(), Easing.easeOutQuint), new Keyframe(0.45f, random.getY(), Easing.easeOutQuint), new Keyframe(0.6f, random.getY(), Easing.easeOutQuint), new Keyframe(1f, height + 100, Easing.easeInOutQuint) }); animY.setCompletion(() -> { module.invalidate(); Minecraft.getMinecraft().player.playSound(ModSounds.ZOOM, 1f, 1f); }); module.add(animX, animY, animSound1, animRadius, animText); } } }; if (selectedModule != null) { selectedModule = null; getMainComponents().add(new ScheduledEventAnimation(5, runnable)); } else runnable.run(); } public void playLoadAnimation(Set<CommonWorktableModule> commonModules, @Nullable Runnable finish) { animationPlaying = true; resetSelectedModule(); ComponentVoid bookIconMask = new ComponentVoid(0, -100, 180, 100); paper.add(bookIconMask); Vec2d bookOrigin = new Vec2d((bookIconMask.getSize().getX() / 2.0) - 8, -(bookIconMask.getSize().getY() / 2.0) - 4); Runnable itemsRunnable = () -> { // PAPER ITEMS ANIMATION { for (CommonWorktableModule commonHead : commonModules) { CommonWorktableModule commonModule = commonHead; TableModule lastModule = new TableModule(this, commonHead.module, true, false); lastModule.setPos(commonHead.pos); while (commonModule != null) { paper.add(lastModule); DragMixin drag = new DragMixin(lastModule, vec2d -> vec2d); drag.setDragOffset(new Vec2d(6, 6)); lastModule.setData(Vec2d.class, "true_pos", commonModule.pos); for (ModuleInstanceModifier modifier : commonModule.modifiers.keySet()) { lastModule.setData(Integer.class, modifier.getNBTKey(), commonModule.modifiers.get(modifier)); } lastModule.radius = 0; lastModule.textRadius = 0; if (commonModule.linksTo != null) { TableModule childModule = new TableModule(this, commonModule.linksTo.module, true, false); childModule.setPos(bookOrigin); lastModule.setLinksTo(childModule); lastModule = childModule; } commonModule = commonModule.linksTo; } } for (GuiComponent component : paper.getChildren()) { if (!(component instanceof TableModule)) continue; TableModule module = (TableModule) component; Vec2d target = module.getData(Vec2d.class, "true_pos"); if (target == null) { module.invalidate(); continue; } Vec2d randGen = new Vec2d(RandUtil.nextDouble(-100, 100), RandUtil.nextDouble(-100, 100)); Vec2d random = bookOrigin.add(randGen); float delay = RandUtil.nextFloat(0.2f, 0.3f); float dur = RandUtil.nextFloat(70, 100); ScheduledEventAnimation animSound1 = new ScheduledEventAnimation(dur * delay, () -> Minecraft.getMinecraft().player.playSound(ModSounds.WHOOSH, 1f, 1f)); KeyframeAnimation<TableModule> animX = new KeyframeAnimation<>(module, "pos.x"); animX.setDuration(dur); animX.setKeyframes(new Keyframe[]{ new Keyframe(delay, bookOrigin.getX(), Easing.easeOutQuint), new Keyframe(0.5f, random.getX(), Easing.easeOutQuint), new Keyframe(0.6f, random.getX(), Easing.easeOutQuint), new Keyframe(1f, target.getX(), Easing.easeOutQuint) }); KeyframeAnimation<TableModule> animY = new KeyframeAnimation<>(module, "pos.y"); animY.setDuration(dur); animY.setKeyframes(new Keyframe[]{ new Keyframe(delay, bookOrigin.getY(), Easing.easeOutQuint), new Keyframe(0.5f, random.getY(), Easing.easeOutQuint), new Keyframe(0.6f, random.getY(), Easing.easeOutQuint), new Keyframe(1f, target.getY(), Easing.easeOutQuint) }); animY.setCompletion(() -> { BasicAnimation<TableModule> animRadius = new BasicAnimation<>(module, "radius"); animRadius.setDuration(20); animRadius.setEasing(Easing.easeOutCubic); animRadius.setTo(10); module.add(animRadius); BasicAnimation<TableModule> animText = new BasicAnimation<>(module, "textRadius"); animText.setDuration(40); animText.setEasing(Easing.easeOutCubic); animText.setTo(0); module.add(animText); }); module.add(animX, animY, animSound1); } } }; Runnable runnable = () -> { // GRAY BACKGROUND { ComponentRect grayBackground = new ComponentRect(0, 0, tableComponent.getSize().getXi(), tableComponent.getSize().getYi()); grayBackground.getColor().setValue(new Color(0.05f, 0.05f, 0.05f, 0f)); grayBackground.getTransform().setTranslateZ(200); grayBackground.BUS.hook(GuiComponentEvents.ComponentTickEvent.class, event -> { grayBackground.getColor().setValue(new Color(0.05f, 0.05f, 0.05f, backgroundAlpha)); }); // tableComponent.add(grayBackground); KeyframeAnimation<WorktableGui> anim = new KeyframeAnimation<>(this, "backgroundAlpha"); anim.setDuration(100); anim.setKeyframes(new Keyframe[]{ new Keyframe(0, 0f, Easing.easeInOutQuint), new Keyframe(0.2f, 0.65f, Easing.easeInOutQuint), new Keyframe(0.7f, 0.65f, Easing.easeInOutQuint), new Keyframe(1f, 0f, Easing.easeInOutQuint) }); anim.setCompletion(grayBackground::invalidate); // getMainComponents().add(anim); } // BOOK PEAK ANIMATION { ComponentSprite bookIcon = new ComponentSprite(BOOK_ICON, (int) ((bookIconMask.getSize().getX() / 2.0) - 16), (int) (bookIconMask.getSize().getY() + 50), 32, 32); bookIconMask.add(bookIcon); bookIcon.getTransform().setTranslateZ(200); bookIconMask.clipping.setClipToBounds(true); bookIconMask.getTransform().setTranslateZ(250); final Vec2d originalPos = bookIcon.getPos(); KeyframeAnimation<ComponentSprite> anim = new KeyframeAnimation<>(bookIcon, "pos.y"); anim.setDuration(120); anim.setKeyframes(new Keyframe[]{ new Keyframe(0, originalPos.getY(), Easing.linear), new Keyframe(0.4f, (bookIconMask.getSize().getY() / 2.0) - 25, Easing.easeInBack), new Keyframe(0.5f, (bookIconMask.getSize().getY() / 2.0) - 10, Easing.easeOutBack), new Keyframe(0.8f, (bookIconMask.getSize().getY() / 2.0) - 10, Easing.easeInBack), new Keyframe(1f, originalPos.getY(), Easing.easeInBack) }); ScheduledEventAnimation animSound = new ScheduledEventAnimation(120 * 0.4f, () -> { Minecraft.getMinecraft().player.playSound(ModSounds.WHOOSH, 1f, 1f); itemsRunnable.run(); ScheduledEventAnimation animFinish = new ScheduledEventAnimation(100, () -> { bookIcon.invalidate(); if (finish == null) animationPlaying = false; else { finish.run(); } }); getMainComponents().add(animFinish); }); bookIcon.add(anim, animSound); } }; if (selectedModule != null) { selectedModule = null; getMainComponents().add(new ScheduledEventAnimation(5, runnable)); } else runnable.run(); } @Override public boolean doesGuiPauseGame() { return false; } }