/* * This file is part of the Illarion project. * * Copyright © 2015 - Illarion e.V. * * Illarion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Illarion is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ package illarion.client.gui.controller.game; import de.lessvoid.nifty.EndNotify; import de.lessvoid.nifty.Nifty; import de.lessvoid.nifty.builder.EffectBuilder; import de.lessvoid.nifty.builder.ElementBuilder; import de.lessvoid.nifty.builder.PanelBuilder; import de.lessvoid.nifty.controls.label.builder.LabelBuilder; import de.lessvoid.nifty.elements.Element; import de.lessvoid.nifty.screen.Screen; import de.lessvoid.nifty.screen.ScreenController; import de.lessvoid.nifty.tools.Color; import de.lessvoid.nifty.tools.SizeValue; import illarion.client.graphics.FontLoader; import illarion.client.gui.InformGui; import illarion.client.util.UpdateTask; import illarion.client.world.MapDimensions; import illarion.client.world.World; import org.illarion.engine.GameContainer; import org.illarion.engine.graphic.Font; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Arrays; /** * This handler is used to show and hide all the temporary inform messages on the screen. It provides the required * facilities to create and remove the elements. * * @author Martin Karing >nitram@illarion.org< */ public final class InformHandler implements InformGui, ScreenController { /** * This task is created as storage for the creation on the information display. */ private final class InformBuildTask implements UpdateTask { /** * The element builder that is executed to create the inform message. */ @Nonnull private final ElementBuilder builder; /** * The element that will be parent to the elements created by the builder. */ @Nonnull private final Element parent; /** * The element that needs to get a new layout once the inform is displayed. */ @Nonnull private final Element layoutParent; /** * The optional action that is executed before the inform is build. */ @Nullable private final Runnable prepareAction; /** * Create a new instance of this build task that stores all the information needed to create the inform * message. * * @param informBuilder the element builder that creates the message * @param parentElement the parent element that will store the elements created by the element builder * @param layoutParent the element that needs to get a new layout once the inform is displayed * @param prepareAction the action executed directly before the task is build */ InformBuildTask(@Nonnull ElementBuilder informBuilder, @Nonnull Element parentElement, @Nonnull Element layoutParent, @Nullable Runnable prepareAction) { builder = informBuilder; parent = parentElement; this.layoutParent = layoutParent; this.prepareAction = prepareAction; } @Override public void onUpdateGame(@Nonnull GameContainer container, int delta) { if (!parent.isVisible()) { parent.showWithoutEffects(); } if (prepareAction != null) { prepareAction.run(); } Element msg = builder.build(parentNifty, parentScreen, parent); msg.showWithoutEffects(); layoutParent.layoutElements(); msg.hide(new RemoveEndNotify(msg)); } } /** * This utility class is a end notification that will trigger the removal of a target element. This is needed to * remove the server messages again from the screen. */ private static final class RemoveEndNotify implements EndNotify { /** * The target element. */ private final Element target; /** * The constructor of this class. * * @param element the element to remove */ RemoveEndNotify(Element element) { target = element; } @Override public void perform() { target.markForRemoval(new LayoutElementsEndNotify(target.getParent())); } } /** * This utility class is a end notification that will trigger the calculation of the layout of a target element. * This is needed to put the parent container of the server informs back into shape. */ private static final class LayoutElementsEndNotify implements EndNotify { /** * The target element. */ private final Element target; /** * The constructor of this class. * * @param element the element to layout */ LayoutElementsEndNotify(Element element) { target = element; } @Override public void perform() { target.layoutElements(); if (target.getChildren().isEmpty()) { target.hide(); } } } /** * The logger that is used for the logging output of this class. */ @Nonnull private static final Logger log = LoggerFactory.getLogger(InformHandler.class); /** * The instance of the Nifty-GUI this handler is bound to. */ private Nifty parentNifty; /** * The instance of the screen this handler is operating on. */ private Screen parentScreen; /** * This is the panel that will be parent to all broadcast messages. */ @Nullable private Element broadcastParentPanel; /** * This is the panel that will be parent to all server messages. */ @Nullable private Element serverParentPanel; /** * This is the panel that will be parent to all text to messages. */ @Nullable private Element textToParentPanel; /** * This is the panel that will be parent to all text-to messages. */ private Element scriptParentPanel; @Override public void bind(@Nonnull Nifty nifty, @Nonnull Screen screen) { parentNifty = nifty; parentScreen = screen; broadcastParentPanel = screen.findElementById("broadcastMsgPanel"); serverParentPanel = screen.findElementById("serverMsgPanel"); textToParentPanel = screen.findElementById("textToMsgPanel"); scriptParentPanel = screen.findElementById("scriptMessagePanel"); } @Override public void onEndScreen() { Arrays.asList(broadcastParentPanel, serverParentPanel, textToParentPanel, scriptParentPanel).stream() .filter(panel -> panel != null).forEach(panel -> panel.getChildren().forEach(Element::markForRemoval)); } @Override public void onStartScreen() { // nothing to do } @Override public void showBroadcastInform(@Nonnull String message) { if (broadcastParentPanel == null) { log.warn("Received server inform before the GUI became ready."); return; } LabelBuilder labelBuilder = new LabelBuilder(); labelBuilder.label(message); labelBuilder.font(FontLoader.BUBBLE_FONT); labelBuilder.invisibleToMouse(); EffectBuilder effectBuilder = new EffectBuilder("hide"); effectBuilder.startDelay(10000 + (message.length() * 50)); labelBuilder.onHideEffect(effectBuilder); showInform(labelBuilder, broadcastParentPanel, broadcastParentPanel.getParent()); } /** * Show a inform on the screen. * * @param informBuilder the builder that is meant to create the inform message * @param parent the parent element that stores the inform message * @param layoutParent the element that needs to get its layout recalculated */ public void showInform(@Nonnull ElementBuilder informBuilder, @Nonnull Element parent, @Nonnull Element layoutParent) { showInform(informBuilder, parent, layoutParent, null); } /** * Show a inform on the screen. * * @param informBuilder the builder that is meant to create the inform message * @param parent the parent element that stores the inform message * @param layoutParent the element that needs to get its layout recalculated * @param prepareAction the action executed right before the element is build */ public void showInform(@Nonnull ElementBuilder informBuilder, @Nonnull Element parent, @Nonnull Element layoutParent, @Nullable Runnable prepareAction) { World.getUpdateTaskManager().addTask(new InformBuildTask(informBuilder, parent, layoutParent, prepareAction)); } /** * Show a script inform message on the screen. * * @param priority the priority of the message * @param message the message */ @Override public void showScriptInform(int priority, @Nonnull String message) { if (scriptParentPanel == null) { log.warn("Received script inform before the GUI became ready. Priority: {} Message: {}", priority, message); return; } PanelBuilder panelBuilder = new PanelBuilder(); panelBuilder.childLayoutHorizontal(); panelBuilder.width(SizeValue.percent(75)); panelBuilder.alignCenter(); LabelBuilder labelBuilder = new LabelBuilder(); panelBuilder.control(labelBuilder); labelBuilder.label(message); labelBuilder.font(FontLoader.BUBBLE_FONT); switch (priority) { case 1: labelBuilder.color(Color.WHITE); break; case 2: labelBuilder.color(new Color(1.0f, 0.5f, 0.5f, 1.0f)); break; default: labelBuilder.color(new Color(0.9f, 0.9f, 0.9f, 1.0f)); } labelBuilder.invisibleToMouse(); labelBuilder.valignCenter(); labelBuilder.alignCenter(); labelBuilder.width(SizeValue.percent(100)); labelBuilder.textHAlignCenter(); labelBuilder.parameter("wrap", "true"); int moveUpTime = getScriptInformDisplayTime(message, priority); EffectBuilder moveEffectBuilder = new EffectBuilder("move"); moveEffectBuilder.length(moveUpTime); moveEffectBuilder.startDelay(0); moveEffectBuilder.effectParameter("mode", "toOffset"); moveEffectBuilder.effectParameter("direction", "bottom"); moveEffectBuilder.effectParameter("offsetY", "-80"); labelBuilder.onHideEffect(moveEffectBuilder); int fadeOutTime = moveUpTime / 4; EffectBuilder fadeOutBuilder = new EffectBuilder("fade"); fadeOutBuilder.length(fadeOutTime); fadeOutBuilder.startDelay(moveUpTime - fadeOutTime); fadeOutBuilder.effectParameter("start", "FF"); fadeOutBuilder.effectParameter("end", "00"); labelBuilder.onHideEffect(fadeOutBuilder); panelBuilder.onHideEffect(new EffectBuilder("hide")); showInform(panelBuilder, scriptParentPanel, scriptParentPanel.getParent()); } private static int getScriptInformDisplayTime(@Nonnull CharSequence text, int priority) { if (priority == 0) { return 5000;// + (text.length() * 50); } return 8000;// + (text.length() * 50); } @Override public void showServerInform(@Nonnull String message) { if (serverParentPanel == null) { log.warn("Received server inform before the GUI became ready."); return; } PanelBuilder panelBuilder = new PanelBuilder(); panelBuilder.childLayoutHorizontal(); String text = "Server> " + message; LabelBuilder labelBuilder = new LabelBuilder(); panelBuilder.control(labelBuilder); labelBuilder.label(text); labelBuilder.font(FontLoader.CONSOLE_FONT); labelBuilder.invisibleToMouse(); labelBuilder.wrap(true); labelBuilder.alignLeft(); EffectBuilder effectBuilder = new EffectBuilder("hide"); effectBuilder.startDelay(10000 + (message.length() * 50)); panelBuilder.onHideEffect(effectBuilder); showInform(panelBuilder, serverParentPanel, serverParentPanel.getParent(), () -> { Font font = FontLoader.getInstance().getFont(FontLoader.CONSOLE_FONT); int width = font.getWidth(text); int maxWidth = MapDimensions.getInstance().getOnScreenWidth() - 10; if (width > maxWidth) { width = maxWidth; } panelBuilder.width(SizeValue.px(width)); }); } @Override public void showTextToInform(@Nonnull String message) { if (textToParentPanel == null) { log.warn("Received server inform before the GUI became ready."); return; } LabelBuilder labelBuilder = new LabelBuilder(); labelBuilder.label(message); labelBuilder.font(FontLoader.BUBBLE_FONT); labelBuilder.invisibleToMouse(); EffectBuilder effectBuilder = new EffectBuilder("hide"); effectBuilder.startDelay(10000 + (message.length() * 50)); labelBuilder.onHideEffect(effectBuilder); showInform(labelBuilder, textToParentPanel, textToParentPanel.getParent()); } }