/**
* Copyright (c) Lambda Innovation, 2013-2016
* This file is part of the AcademyCraft mod.
* https://github.com/LambdaInnovation/AcademyCraft
* Licensed under GPLv3, see project root for more information.
*/
package cn.academy.terminal.client;
import cn.academy.core.ModuleCoreClient;
import cn.academy.core.Resources;
import cn.academy.core.client.sound.ACSounds;
import cn.academy.core.registry.RegACKeyHandler;
import cn.academy.terminal.App;
import cn.academy.terminal.AppEnvironment;
import cn.academy.terminal.TerminalData;
import cn.academy.terminal.event.AppInstalledEvent;
import cn.lambdalib.annoreg.core.Registrant;
import cn.lambdalib.annoreg.mc.RegInitCallback;
import cn.lambdalib.cgui.gui.CGui;
import cn.lambdalib.cgui.gui.Widget;
import cn.lambdalib.cgui.gui.WidgetContainer;
import cn.lambdalib.cgui.gui.component.Component;
import cn.lambdalib.cgui.gui.component.DrawTexture;
import cn.lambdalib.cgui.gui.component.TextBox;
import cn.lambdalib.cgui.gui.event.FrameEvent;
import cn.lambdalib.cgui.xml.CGUIDocument;
import cn.lambdalib.util.client.HudUtils;
import cn.lambdalib.util.client.RenderUtils;
import cn.lambdalib.util.client.auxgui.AuxGui;
import cn.lambdalib.util.client.auxgui.AuxGuiHandler;
import cn.lambdalib.util.generic.MathUtils;
import cn.lambdalib.util.helper.GameTimer;
import cn.lambdalib.util.key.KeyHandler;
import cn.lambdalib.util.key.KeyManager;
import cn.lambdalib.util.mc.ControlOverrider;
import cn.lambdalib.util.mc.SideHelper;
import com.google.common.base.Preconditions;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.*;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
import java.util.ArrayList;
import java.util.List;
/**
* @author WeAthFolD
*/
@Registrant
@SideOnly(Side.CLIENT)
public class TerminalUI extends AuxGui {
private static final String OVERRIDE_GROUP = "AC_Terminal";
private static AuxGui current = null;
private static final double BALANCE_SPEED = 3; //pixel/ms
public static final int MAX_MX = 605, MAX_MY = 740;
static final ResourceLocation APP_BACK = tex("app_back"), APP_BACK_HDR = tex("app_back_highlight"), CURSOR = tex("cursor");
final double SENSITIVITY = 0.7;
private static WidgetContainer loaded;
@RegInitCallback
private static void __init() {
loaded = CGUIDocument.panicRead(new ResourceLocation("academy:guis/terminal.xml"));
}
CGui gui;
Widget root;
TerminalMouseHelper helper;
MouseHelper oldHelper;
LeftClickHandler clickHandler;
double mouseX, mouseY;
double buffX, buffY; //Used for rotation judging. Will balance to mouseX and mouseY at the rate of BALANCE_SPEED.
long createTime;
long lastFrameTime;
int selection = 0;
int scroll = 0;
List<Widget> apps = new ArrayList<>();
public TerminalUI() {
gui = new CGui();
gui.addWidget(root = loaded.getWidget("back").copy());
buffX = buffY = mouseX = mouseY = 150;
MinecraftForge.EVENT_BUS.register(this);
initGui();
}
@Override
public void onAdded() {
Minecraft mc = Minecraft.getMinecraft();
oldHelper = mc.mouseHelper;
mc.mouseHelper = helper = new TerminalMouseHelper();
ModuleCoreClient.dynKeyManager.addKeyHandler("terminal_click", KeyManager.MOUSE_LEFT, clickHandler = new LeftClickHandler());
ControlOverrider.override(OVERRIDE_GROUP, KeyManager.MOUSE_LEFT);
}
@Override
public void onDisposed() {
Minecraft mc = Minecraft.getMinecraft();
mc.mouseHelper = oldHelper;
ModuleCoreClient.dynKeyManager.removeKeyHandler("terminal_click");
ControlOverrider.endOverride(OVERRIDE_GROUP);
}
@Override
public boolean isForeground() {
return false;
}
@Override
public boolean isConsistent() {
return false;
}
@Override
public void draw(ScaledResolution sr) {
//Frame update
selection = (int)((mouseY - 0.01) / MAX_MY * 3) * 3 + (int)((mouseX - 0.01) / MAX_MX * 3);
if(mouseY == 0) {
mouseY = 1;
if(scroll > 0) scroll--;
}
if(mouseY == MAX_MY) {
mouseY -= 1;
if(scroll < getMaxScroll()) scroll++;
}
//Draw
Minecraft mc = Minecraft.getMinecraft();
long time = GameTimer.getTime();
if(lastFrameTime == 0) lastFrameTime = time;
long dt = time - lastFrameTime;
mouseX += helper.dx * SENSITIVITY;
mouseY -= helper.dy * SENSITIVITY;
mouseX = Math.max(0, Math.min(MAX_MX, mouseX));
mouseY = Math.max(0, Math.min(MAX_MY, mouseY));
buffX = balance(dt, buffX, mouseX);
buffY = balance(dt, buffY, mouseY);
helper.dx = helper.dy = 0;
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix();
GL11.glLoadIdentity();
float aspect = (float)mc.displayWidth / mc.displayHeight;
GLU.gluPerspective(50,
aspect,
1f, 100);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPushMatrix();
GL11.glLoadIdentity();
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_ALPHA_TEST);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4d(1, 1, 1, 1);
double scale = 1.0 / 310;
GL11.glTranslated(.35 * aspect, 1.2, -4);
GL11.glTranslated(1, -1.8, 0);
GL11.glRotated(-1.6, 0, 0, 1);
GL11.glRotated(-18 - 4 * (buffX / MAX_MX - 0.5) + 1 * Math.sin(time / 1000.0), 0, 1, 0);
GL11.glRotated(7 + 4 * (buffY / MAX_MY - 0.5), 1, 0, 0);
//DEBUG CODE
// GL11.glPointSize(20);
// GL11.glColor4d(1, 1, 1, 1);
// GL11.glDisable(GL11.GL_TEXTURE_2D);
// GL11.glBegin(GL11.GL_POINTS);
// GL11.glVertex3f(0, 0, 0);
// GL11.glEnd();
// GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glTranslated(-1, 1.8, 0);
GL11.glScaled(scale, -scale, scale);
gui.draw(mouseX, mouseY);
{
GL11.glPushMatrix();
double csize = (getSelectedApp() == null ? 1 : 1.3) * (20 + Math.sin(time / 300.0) * 2);
RenderUtils.loadTexture(CURSOR);
GL11.glColor4d(1, 1, 1, .4);
GL11.glTranslated(0, 0, -2);
GL11.glDisable(GL11.GL_ALPHA_TEST);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
HudUtils.rect(-csize/2 + buffX, -csize/2 + buffY + 120, csize, csize);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glPopMatrix();
}
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glCullFace(GL11.GL_BACK);
}
@SubscribeEvent
public void _onAppInstalled(AppInstalledEvent evt) {
if (SideHelper.isClient()) {
updateAppList(TerminalData.get(Minecraft.getMinecraft().thePlayer));
}
}
private double balance(long dt, double from, double to) {
double d = to - from;
return from + Math.min(BALANCE_SPEED * dt, Math.abs(d)) * Math.signum(d);
}
private void initGui() {
EntityPlayer player = Minecraft.getMinecraft().thePlayer;
final TerminalData data = TerminalData.get(player);
createTime = GameTimer.getTime();
{
Widget widget = root.getWidget("text_appcount");
TextBox textBox = widget.getComponent(TextBox.class);
widget.listen(FrameEvent.class, (w, e) -> {
int currentTime = (int) (player.worldObj.getWorldTime() % 24000);
int hour = currentTime / 1000;
int minutes = (currentTime % 1000) * 60 / 1000;
String countText = StatCollector.translateToLocalFormatted("ac.gui.terminal.appcount", apps.size());
String timeText = wrapTime(hour) + ":" + wrapTime(minutes);
textBox.content = countText + ", " + timeText;
});
}
TextBox.get(root.getWidget("text_username")).content = player.getCommandSenderName();
// Obsolete stuff
root.removeWidget("text_loading");
root.removeWidget("icon_loading");
updateAppList(data);
createTime = GameTimer.getTime();
root.getWidget("arrow_up").listen(FrameEvent.class,
(w, e) -> {
DrawTexture.get(w).enabled = scroll > 0;
});
root.getWidget("arrow_up").listen(FrameEvent.class,
(w, e) -> {
DrawTexture.get(w).enabled = scroll > 0;
});
root.getWidget("arrow_down").listen(FrameEvent.class,
(w, e) -> {
DrawTexture.get(w).enabled = scroll < getMaxScroll();
});
root.getWidget("icon_loading").listen(FrameEvent.class,
(w, e) -> {
DrawTexture.get(w).color.a = 0.1 + 0.45 * (1 + MathHelper.sin(GameTimer.getTime() / 200.0f));
});
root.getWidget("text_loading").listen(FrameEvent.class,
(w, e) -> {
TextBox.get(w).option.color.a = 0.1 + 0.45 * (1 + MathHelper.sin(GameTimer.getTime() / 200.0f));
});
}
private String wrapTime(int val) {
assert val >= 0 && val < 100;
return val < 10 ? ("0" + val) : (String.valueOf(val));
}
private void updateAppList(TerminalData data) {
for(Widget w : apps)
w.dispose();
apps.clear();
for(App app : data.getInstalledApps()) {
Widget w = createAppWidget(apps.size(), app);
root.addWidget(w);
apps.add(w);
}
TextBox.get(root.getWidget("text_appcount")).content =
StatCollector.translateToLocalFormatted("ac.gui.terminal.appcount", apps.size());
updatePosition();
}
private void updatePosition() {
final double START_X = 65, START_Y = 155, STEP_X = 180, STEP_Y = 180;
// Check if scroll is viable
int max = getMaxScroll();
if(scroll > max) scroll = max;
for(Widget w : apps) {
w.transform.doesDraw = false;
}
for(int i = scroll * 3; i < scroll * 3 + 9 && i < apps.size(); ++i) {
int order = i - scroll * 3;
Widget app = apps.get(i);
app.transform.doesDraw = true;
app.transform.x = START_X + STEP_X * (order % 3);
app.transform.y = START_Y + STEP_Y * (order / 3);
app.dirty = true;
}
}
private int getMaxScroll() {
int r;
if(apps.size() % 3 == 0)
r = apps.size() / 3;
else r = apps.size() / 3 + 1;
return Math.max(0, r - 3);
}
private Widget getSelectedApp() {
int lookup = scroll + selection;
return apps.size() <= lookup ? null : apps.get(lookup);
}
private long getLifetime() {
return GameTimer.getTime() - createTime;
}
private Widget createAppWidget(int id, App app) {
Widget ret = root.getWidget("app_template").copy();
ret.transform.doesDraw = true;
DrawTexture.get(ret.getWidget("icon")).texture = app.getIcon();
TextBox.get(ret.getWidget("text")).content = app.getDisplayName();
ret.addComponent(new AppHandler(id, app));
return ret;
}
private static ResourceLocation tex(String name) {
return Resources.getTexture("guis/data_terminal/" + name);
}
public static void passOn(AuxGui newGui) {
Preconditions.checkNotNull(current);
current.dispose();
current = newGui;
AuxGuiHandler.register(current);
}
@RegACKeyHandler(name = "open_data_terminal", defaultKey = Keyboard.KEY_LMENU)
public static KeyHandler keyHandler = new KeyHandler() {
@Override
public void onKeyUp() {
EntityPlayer player = getPlayer();
TerminalData tData = TerminalData.get(player);
if(tData.isTerminalInstalled()) {
if(current == null || current .isDisposed()) {
current = new TerminalUI();
AuxGuiHandler.register(current);
} else if (current instanceof TerminalUI) {
current.dispose();
current = null;
}
} else {
player.addChatComponentMessage(new ChatComponentTranslation("ac.terminal.notinstalled"));
}
}
};
private class AppHandler extends Component {
final int id;
final App app;
DrawTexture drawer;
TextBox text;
DrawTexture icon;
boolean lastSelected = true;
public AppHandler(int _id, App _app) {
super("AppHandler");
id = _id;
app = _app;
listen(FrameEvent.class, (w, e) -> {
double mAlpha = MathUtils.clampd(0.0, 1.0, (getLifetime() - ((id + 1) * 100)) / 400.0);
boolean selected = getSelectedApp() == w;
if(selected) {
if(!lastSelected) {
ACSounds.playClient(Minecraft.getMinecraft().thePlayer, "terminal.select", 0.2f);
}
drawer.texture = APP_BACK_HDR;
drawer.zLevel = text.zLevel = icon.zLevel = 40;
drawer.color.a = mAlpha;
icon.color.a = 0.8 * mAlpha;
text.option.color.a = 0.1 + 0.72 * mAlpha;
} else {
drawer.texture = APP_BACK;
drawer.zLevel = text.zLevel = icon.zLevel = 10;
drawer.color.a = mAlpha;
icon.color.a = 0.6 * mAlpha;
text.option.color.a = 0.10 + 0.1 * mAlpha;
}
lastSelected = selected;
});
}
@Override
public void onAdded() {
super.onAdded();
drawer = DrawTexture.get(widget);
text = TextBox.get(widget.getWidget("text"));
icon = DrawTexture.get(widget.getWidget("icon"));
drawer.color.a = icon.color.a = 0;
text.option.color.a = 0.1;
}
}
static AppHandler getHandler(Widget w) {
return w.getComponent("AppHandler");
}
private class LeftClickHandler extends KeyHandler {
@Override
public void onKeyUp() {
Widget app = getSelectedApp();
if(app != null) {
AppHandler handler = getHandler(app);
AppEnvironment env = handler.app.createEnvironment();
TerminalData data = TerminalData.get(getPlayer());
env.app = handler.app;
env.terminal = TerminalUI.this;
//ACSounds.playClient(getPlayer(), "terminal.confirm", 0.5f);
env.onStart();
}
}
}
}