Cleanup, relocate and split up GuiReplayOverlay

Merge gui resources into one texture
This commit is contained in:
johni0702
2015-05-26 01:07:30 +02:00
parent e031d32d7d
commit 9f3a97f65c
21 changed files with 1002 additions and 845 deletions

View File

@@ -3,6 +3,7 @@ package eu.crushedpixel.replaymod.gui;
import eu.crushedpixel.replaymod.ReplayMod;
import eu.crushedpixel.replaymod.gui.elements.GuiEntryList;
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay;
import eu.crushedpixel.replaymod.holders.Keyframe;
import eu.crushedpixel.replaymod.holders.KeyframeSet;
import eu.crushedpixel.replaymod.replay.ReplayHandler;
@@ -18,7 +19,7 @@ import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;
public class GuiKeyframeRepository extends GuiScreen {
public class GuiKeyframeRepository extends GuiScreen implements GuiReplayOverlay.NoOverlay {
private boolean initialized = false;

View File

@@ -1,6 +1,7 @@
package eu.crushedpixel.replaymod.gui;
import eu.crushedpixel.replaymod.ReplayMod;
import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.settings.KeyBinding;
@@ -9,6 +10,27 @@ import java.io.IOException;
public class GuiMouseInput extends GuiScreen {
private final GuiReplayOverlay overlay;
public GuiMouseInput(GuiReplayOverlay overlay) {
this.overlay = overlay;
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
overlay.mouseClicked(mouseX, mouseY);
}
@Override
protected void mouseReleased(int mouseX, int mouseY, int state) {
overlay.mouseReleased(mouseX, mouseY);
}
@Override
protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
overlay.mouseDrag(mouseX, mouseY);
}
@Override
protected void keyTyped(char typedChar, int keyCode) throws IOException {
for(KeyBinding kb : Minecraft.getMinecraft().gameSettings.keyBindings)

View File

@@ -2,6 +2,7 @@ package eu.crushedpixel.replaymod.gui;
import com.mojang.realmsclient.util.Pair;
import eu.crushedpixel.replaymod.ReplayMod;
import eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay;
import eu.crushedpixel.replaymod.holders.PlayerVisibility;
import eu.crushedpixel.replaymod.registry.PlayerHandler;
import eu.crushedpixel.replaymod.replay.ReplayHandler;
@@ -30,7 +31,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class GuiPlayerOverview extends GuiScreen {
public class GuiPlayerOverview extends GuiScreen implements GuiReplayOverlay.NoOverlay {
public static boolean defaultSave = false;
@@ -289,7 +290,7 @@ public class GuiPlayerOverview extends GuiScreen {
}
//this is necessary to reset the GL parameters for further GUI rendering
drawRect(0, 0, 0, 0, Color.LIGHT_GRAY.getRGB());
GlStateManager.enableBlend();
}
private PlayerVisibility getVisibilityInstance() {

View File

@@ -26,11 +26,6 @@ public class GuiReplaySpeedSlider extends GuiButton {
this.valueStep = 1;
this.displayString = displayKey + ": 1x";
this.displayKey = displayKey;
Minecraft.getMinecraft().getTextureManager().bindTexture(buttonTextures);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
this.drawTexturedModalRect(this.xPosition + (int) (sliderValue * (float) (this.width - 8)), this.yPosition, 0, 66, 4, 20);
this.drawTexturedModalRect(this.xPosition + (int) (sliderValue * (float) (this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20);
}
public static float convertScaleRet(float value) {
@@ -151,7 +146,7 @@ public class GuiReplaySpeedSlider extends GuiButton {
float min = 0 - valueMin;
float max = valueMax + min;
return Math.round(value * (max) - min);
return Math.round(value * max - min);
}
public boolean mousePressed(Minecraft mc, int mouseX, int mouseY) {

View File

@@ -0,0 +1,125 @@
package eu.crushedpixel.replaymod.gui.elements;
import eu.crushedpixel.replaymod.gui.GuiEditKeyframe;
import eu.crushedpixel.replaymod.holders.Keyframe;
import eu.crushedpixel.replaymod.holders.PositionKeyframe;
import eu.crushedpixel.replaymod.holders.TimeKeyframe;
import eu.crushedpixel.replaymod.replay.ReplayHandler;
import net.minecraft.client.Minecraft;
public class GuiKeyframeTimeline extends GuiTimeline {
private static final int KEYFRAME_PLACE_X = 74;
private static final int KEYFRAME_PLACE_Y = 20;
private static final int KEYFRAME_TIME_X = 74;
private static final int KEYFRAME_TIME_Y = 25;
private Keyframe clickedKeyFrame;
private long clickTime;
private boolean dragging;
public GuiKeyframeTimeline(int positionX, int positionY, int width) {
super(positionX, positionY, width);
showMarkers = true;
}
public void mouseClicked(Minecraft mc, int mouseX, int mouseY) {
long time = getTimeAt(mouseX, mouseY);
if (time == -1) {
return;
}
int tolerance = (int) (2 * Math.round(zoom * timelineLength / width));
Keyframe closest;
if (mouseY >= positionY + BORDER_TOP + 5) {
closest = ReplayHandler.getClosestTimeKeyframeForRealTime((int) time, tolerance);
} else if (mouseY >= positionY + BORDER_TOP) {
closest = ReplayHandler.getClosestPlaceKeyframeForRealTime((int) time, tolerance);
} else {
closest = null;
}
ReplayHandler.selectKeyframe(closest); //can be null, deselects keyframe
// If we clicked on a key frame, then continue monitoring the mouse for movements
long currentTime = System.currentTimeMillis();
if (closest != null) {
if (currentTime - clickTime < 500) { // if double clicked then open GUI instead
mc.displayGuiScreen(new GuiEditKeyframe(closest));
this.clickedKeyFrame = null;
} else {
this.clickedKeyFrame = closest;
this.dragging = false;
}
} else { // If we didn't then just update the cursor
ReplayHandler.setRealTimelineCursor((int) time);
this.dragging = true;
}
this.clickTime = currentTime;
}
public void mouseDrag(int mouseX, int mouseY) {
long time = getTimeAt(mouseX, mouseY);
if (time != -1) {
if (clickedKeyFrame != null) {
int tolerance = (int) (2 * Math.round(zoom * timelineLength / width));
if (dragging || Math.abs(clickedKeyFrame.getRealTimestamp() - time) > tolerance) {
clickedKeyFrame.setRealTimestamp((int) time);
ReplayHandler.sortKeyframes();
dragging = true;
}
} else if (dragging) {
ReplayHandler.setRealTimelineCursor((int) time);
}
}
}
public void mouseRelease(int mouseX, int mouseY) {
mouseDrag(mouseX, mouseY);
clickedKeyFrame = null;
dragging = false;
}
@Override
public void draw(Minecraft mc, int mouseX, int mouseY) {
super.draw(mc, mouseX, mouseY);
int bodyWidth = width - BORDER_LEFT - BORDER_RIGHT;
long leftTime = Math.round(timeStart * timelineLength);
long rightTime = Math.round((timeStart + zoom) * timelineLength);
double segmentLength = timelineLength * zoom;
//Draw Keyframe logos
for(Keyframe kf : ReplayHandler.getKeyframes()) {
if (kf.getRealTimestamp() <= rightTime && kf.getRealTimestamp() >= leftTime) {
int textureX;
int textureY;
int y = positionY;
if (kf instanceof PositionKeyframe) {
textureX = KEYFRAME_PLACE_X;
textureY = KEYFRAME_PLACE_Y;
y += 0;
} else if (kf instanceof TimeKeyframe) {
textureX = KEYFRAME_TIME_X;
textureY = KEYFRAME_TIME_Y;
y += 5;
} else {
throw new UnsupportedOperationException("Unknown keyframe type: " + kf.getClass());
}
if (ReplayHandler.isSelected(kf)) {
textureX += 5;
}
long positionInSegment = kf.getRealTimestamp() - leftTime;
double fractionOfSegment = positionInSegment / segmentLength;
int x = (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth);
rect(x - 2, y + BORDER_TOP, textureX, textureY, 5, 5);
}
}
}
}

View File

@@ -0,0 +1,142 @@
package eu.crushedpixel.replaymod.gui.elements;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.GlStateManager;
import static eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay.TEXTURE_SIZE;
import static eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay.replay_gui;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.glEnable;
public class GuiScrollbar extends Gui {
protected static final int BORDER_LEFT = 1;
protected static final int BORDER_RIGHT = 2;
protected static final int BORDER_TOP = 1;
protected static final int BACKGROUND_WIDTH = 64;
protected static final int BACKGROUND_HEIGHT = 9;
protected static final int BACKGROUND_BODY_WIDTH = BACKGROUND_WIDTH - BORDER_LEFT - BORDER_RIGHT;
protected static final int BACKGROUND_TEXTURE_X = 64;
protected static final int BACKGROUND_TEXTURE_Y = 97;
protected static final int SLIDER_WIDTH = 62;
protected static final int SLIDER_HEIGHT = 7;
protected static final int SLIDER_TEXTURE_X = 64;
protected static final int SLIDER_TEXTURE_Y = 90;
protected static final int SLIDER_BORDER_LEFT = 1;
protected static final int SLIDER_BORDER_RIGHT = 2;
protected static final int SLIDER_BODY_WIDTH = SLIDER_WIDTH - SLIDER_BORDER_LEFT - SLIDER_BORDER_RIGHT;
/**
* Current position of the left end of the slider. Must be between 0 and 1 - {@link #size} (inclusive).
*/
public double sliderPosition;
/**
* Size of the slider. Should be between 0 (exclusive) and 1 (inclusive)
*/
public double size = 1;
protected final int positionX;
protected final int positionY;
protected final int width;
private int draggingStart;
private double draggingStartPosition;
public GuiScrollbar(int positionX, int positionY, int width) {
this.positionX = positionX;
this.positionY = positionY;
this.width = width;
}
private int getSliderOffsetX() {
return (int) Math.round((width - BORDER_LEFT - BORDER_RIGHT) * sliderPosition) + BORDER_LEFT;
}
public boolean startDragging(int mouseX, int mouseY) {
int offsetX = getSliderOffsetX();
int minX = positionX + offsetX;
int maxX = positionX + offsetX + (int) (width * size);
int minY = positionY + BORDER_TOP;
int maxY = minY + SLIDER_HEIGHT;
if (mouseX >= minX && mouseY >= minY && mouseX < maxX && mouseY < maxY) {
draggingStart = mouseX;
draggingStartPosition = sliderPosition;
return true;
} else {
return false;
}
}
public void doDragging(int mouseX) {
if (draggingStart != -1) {
double delta = (double) (mouseX - draggingStart) / (width - BORDER_LEFT - BORDER_RIGHT);
sliderPosition = Math.max(0, Math.min(1 - size, draggingStartPosition + delta));
}
}
public void endDragging(int mouseX) {
doDragging(mouseX);
draggingStart = -1;
}
public void draw(Minecraft mc) {
GlStateManager.resetColor();
mc.renderEngine.bindTexture(replay_gui);
glEnable(GL_BLEND);
// Background
{
// We have to increase the border size as there is one pixel row which is part of the border while drawing
// but isn't during position calculations
int BORDER_LEFT = GuiScrollbar.BORDER_LEFT + 1;
int BACKGROUND_BODY_WIDTH = GuiScrollbar.BACKGROUND_BODY_WIDTH - 1;
int bodyLeft = positionX + BORDER_LEFT;
int bodyRight = positionX + width - BORDER_RIGHT;
// Left border
rect(positionX, positionY, BACKGROUND_TEXTURE_X, BACKGROUND_TEXTURE_Y, BORDER_LEFT, BACKGROUND_HEIGHT);
// Body
for (int i = bodyLeft; i < bodyRight; i += BACKGROUND_BODY_WIDTH) {
rect(i, positionY, BACKGROUND_TEXTURE_X + BORDER_LEFT, BACKGROUND_TEXTURE_Y,
Math.min(BACKGROUND_BODY_WIDTH, bodyRight - i), BACKGROUND_HEIGHT);
}
// Right border
rect(bodyRight, positionY, BACKGROUND_TEXTURE_X + BACKGROUND_WIDTH - BORDER_RIGHT,
BACKGROUND_TEXTURE_Y, BORDER_RIGHT, BACKGROUND_HEIGHT);
}
// The slider itself
{
int positionX = this.positionX + getSliderOffsetX();
int positionY = this.positionY + BORDER_TOP;
int backgroundBodyWidth = width - BORDER_LEFT - BORDER_RIGHT;
int bodyLeft = positionX + SLIDER_BORDER_LEFT;
int bodyRight = positionX + (int) Math.round(backgroundBodyWidth * size) - SLIDER_BORDER_RIGHT;
// Left border
rect(positionX, positionY, SLIDER_TEXTURE_X, SLIDER_TEXTURE_Y, SLIDER_BORDER_LEFT, SLIDER_HEIGHT);
// Body
for (int i = bodyLeft; i < bodyRight; i += SLIDER_BODY_WIDTH) {
rect(i, positionY, SLIDER_TEXTURE_X + SLIDER_BORDER_LEFT, SLIDER_TEXTURE_Y,
Math.min(SLIDER_BODY_WIDTH, bodyRight - i), SLIDER_HEIGHT);
}
// Right border
rect(bodyRight, positionY, SLIDER_TEXTURE_X + SLIDER_WIDTH - SLIDER_BORDER_RIGHT,
SLIDER_TEXTURE_Y, SLIDER_BORDER_RIGHT, SLIDER_HEIGHT);
}
}
protected void rect(int x, int y, int u, int v, int width, int height) {
GlStateManager.resetColor();
Minecraft.getMinecraft().renderEngine.bindTexture(replay_gui);
glEnable(GL_BLEND);
drawModalRectWithCustomSizedTexture(x, y, u, v, width, height, TEXTURE_SIZE, TEXTURE_SIZE);
}
}

View File

@@ -0,0 +1,38 @@
package eu.crushedpixel.replaymod.gui.elements;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ResourceLocation;
public class GuiTexturedButton extends GuiButton {
private final ResourceLocation texture;
private final int u, v;
private final int textureWidth, textureHeight;
public GuiTexturedButton(int buttonId, int x, int y, int width, int height, ResourceLocation texture,
int u, int v, int textureWidth, int textureHeight) {
super(buttonId, x, y, width, height, "");
this.texture = texture;
this.u = u;
this.v = v;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
}
@Override
public void drawButton(Minecraft mc, int mouseX, int mouseY) {
if (visible) {
hovered = mouseX >= xPosition
&& mouseY >= yPosition
&& mouseX < xPosition + width
&& mouseY < yPosition + height;
mc.renderEngine.bindTexture(texture);
GlStateManager.color(1, 1, 1);
int u = this.u + (hovered ? width : 0);
Gui.drawModalRectWithCustomSizedTexture(xPosition, yPosition, u, v, width, height, textureWidth, textureHeight);
}
}
}

View File

@@ -0,0 +1,194 @@
package eu.crushedpixel.replaymod.gui.elements;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import static eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay.TEXTURE_SIZE;
import static eu.crushedpixel.replaymod.gui.overlay.GuiReplayOverlay.replay_gui;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.glEnable;
public class GuiTimeline extends Gui {
protected static final int TEXTURE_WIDTH = 64;
protected static final int BORDER_TOP = 4;
protected static final int BORDER_BOTTOM = 3;
protected static final int HEIGHT = 22;
protected static final int TEXTURE_X = 64;
protected static final int TEXTURE_Y = 106;
protected static final int BORDER_LEFT = 4;
protected static final int BORDER_RIGHT = 4;
protected static final int BODY_WIDTH = TEXTURE_WIDTH - BORDER_LEFT - BORDER_RIGHT;
/**
* Current position of the cursor. Should normally be between 0 and {@link #timelineLength}.
*/
public int cursorPosition;
/**
* Total length of the timeline.
*/
public int timelineLength;
/**
* The time at the left border of this timeline (fraction of the total length).
*/
public double timeStart;
/**
* Zoom of this timeline. 1/10 allows the user to see 1/10 of the total length.
*/
public double zoom = 1;
/**
* Whether to draw markers on the timeline in regular time intervals.
* This draws the time at big markers above the timeline. Therefore extra space in negative y direction
* should be kept empty if markers are desired.
*/
public boolean showMarkers;
protected final int positionX;
protected final int positionY;
protected final int width;
public GuiTimeline(int positionX, int positionY, int width) {
this.positionX = positionX;
this.positionY = positionY;
this.width = width;
}
/**
* Returns the time which the mouse is at.
* @param mouseX X coordinate of the mouse
* @param mouseY Y coordinate of the mouse
* @return The time or -1 if the mouse isn't on the timeline
*/
public long getTimeAt(int mouseX, int mouseY) {
int left = positionX + BORDER_LEFT;
int right = positionX + width - BORDER_RIGHT;
int bodyWidth = width - BORDER_LEFT - BORDER_RIGHT;
if (mouseX >= left && mouseX <= right && mouseY >= positionY && mouseY <= positionY + HEIGHT) {
double segmentLength = timelineLength * zoom;
double segmentTime = segmentLength * (mouseX - left) / bodyWidth;
return Math.round(timeStart * timelineLength + segmentTime);
} else {
return -1;
}
}
public void draw(Minecraft mc, int mouseX, int mouseY) {
int bodyLeft = positionX + BORDER_LEFT;
int bodyRight = positionX + width - BORDER_RIGHT;
int bodyWidth = width - BORDER_LEFT - BORDER_RIGHT;
{
// We have to increase the border size as there is one pixel row which is part of the border while drawing
// but isn't during position calculations
int BORDER_LEFT = GuiTimeline.BORDER_LEFT + 1;
int BODY_WIDTH = GuiTimeline.BODY_WIDTH - 1;
// Left border
rect(positionX, positionY, TEXTURE_X, TEXTURE_Y, BORDER_LEFT, HEIGHT);
// Body
for (int i = bodyLeft; i < bodyRight; i += BODY_WIDTH) {
rect(i, positionY, TEXTURE_X + BORDER_LEFT, TEXTURE_Y, Math.min(BODY_WIDTH, bodyRight - i), HEIGHT);
}
// Right border
rect(bodyRight, positionY, TEXTURE_X + BORDER_LEFT + BODY_WIDTH, TEXTURE_Y, BORDER_RIGHT, HEIGHT);
}
long leftTime = Math.round(timeStart * timelineLength);
long rightTime = Math.round((timeStart + zoom) * timelineLength);
// Draw markers
if (showMarkers) {
int markerY = positionY + HEIGHT - BORDER_BOTTOM;
MarkerType mt = MarkerType.getMarkerType(zoom, timelineLength);
// Small markers
for (int s = 0; s <= timelineLength; s += mt.smallDistance) {
if (s <= rightTime && s >= leftTime) {
long positionInSegment = s - leftTime;
double fractionOfSegment = positionInSegment / (zoom * timelineLength);
int markerX = (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth);
drawVerticalLine(markerX, markerY - 3, markerY, 0xffffffff);
}
}
// Big markers
for (int s = 0; s <= timelineLength; s += mt.distance) {
if (s <= rightTime && s >= leftTime) {
long positionInSegment = s - leftTime;
double fractionOfSegment = positionInSegment / (zoom * timelineLength);
int markerX = (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth);
drawVerticalLine(markerX, markerY - 7, markerY, 0xffc0c0c0); // Light gray
// Write time above the timeline
long sec = Math.round(s / 1000.0);
String timestamp = String.format("%02d:%02ds", sec / 60, sec % 60);
drawCenteredString(mc.fontRendererObj, timestamp, markerX, positionY - 8, 0xffffffff);
}
}
}
// Draw the cursor if it's on the current timeline segment
if (cursorPosition >= leftTime && cursorPosition <= rightTime) {
long positionInSegment = cursorPosition - leftTime;
double fractionOfSegment = positionInSegment / (zoom * timelineLength);
int cursorX = (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth);
rect(cursorX - 2, positionY + BORDER_TOP, 84, 20, 5, 16);
}
// Draw time under cursor if the mouse is on the timeline
long mouseTime = getTimeAt(mouseX, mouseY);
if (mouseTime != -1) {
long sec = mouseTime / 1000;
String timestamp = String.format("%02d:%02ds", sec / 60, sec % 60);
drawCenteredString(mc.fontRendererObj, timestamp, mouseX, mouseY + 5, 0xffffffff);
}
}
protected void rect(int x, int y, int u, int v, int width, int height) {
Minecraft.getMinecraft().renderEngine.bindTexture(replay_gui);
glEnable(GL_BLEND);
drawModalRectWithCustomSizedTexture(x, y, u, v, width, height, TEXTURE_SIZE, TEXTURE_SIZE);
}
private enum MarkerType {
ONE_S(1000, 100),
FIVE_S(5 * 1000, 1000),
QUARTER_M(15 * 1000, 3 * 1000),
HALF_M(30 * 1000, 5 * 1000),
ONE_M(60 * 1000, 10 * 1000),
FIVE_M(5 * 60 * 1000, 50 * 1000);
int distance;
int smallDistance;
int maximum = 10;
MarkerType(int minimum, int smallDistance) {
this.distance = minimum;
this.smallDistance = smallDistance;
}
public static MarkerType getMarkerType(double scale, long totalLength) {
long seconds = Math.round(totalLength * scale);
for(MarkerType mt : values()) {
if(seconds / mt.distance <= 10) {
return mt;
}
}
return FIVE_M;
}
}
}

View File

@@ -0,0 +1,37 @@
package eu.crushedpixel.replaymod.gui.overlay;
import eu.crushedpixel.replaymod.ReplayMod;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
/**
* Renders overlay during recording.
*/
public class GuiRecordingOverlay {
private final Minecraft mc;
public GuiRecordingOverlay(Minecraft mc) {
this.mc = mc;
}
/**
* Render the recording icon and text in the top left corner of the screen.
* @param event Rendered post game overlay
*/
@SubscribeEvent
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
if(ReplayMod.replaySettings.showRecordingIndicator()) {
FontRenderer fontRenderer = mc.fontRendererObj;
fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);
mc.renderEngine.bindTexture(GuiReplayOverlay.replay_gui);
GlStateManager.resetColor();
GlStateManager.enableAlpha();
Gui.drawModalRectWithCustomSizedTexture(10, 10, 40, 21, 16, 16, 64, 64);
}
}
}

View File

@@ -0,0 +1,411 @@
package eu.crushedpixel.replaymod.gui.overlay;
import eu.crushedpixel.replaymod.ReplayMod;
import eu.crushedpixel.replaymod.entities.CameraEntity;
import eu.crushedpixel.replaymod.gui.GuiMouseInput;
import eu.crushedpixel.replaymod.gui.GuiRenderSettings;
import eu.crushedpixel.replaymod.gui.GuiReplaySpeedSlider;
import eu.crushedpixel.replaymod.gui.elements.GuiKeyframeTimeline;
import eu.crushedpixel.replaymod.gui.elements.GuiScrollbar;
import eu.crushedpixel.replaymod.gui.elements.GuiTexturedButton;
import eu.crushedpixel.replaymod.gui.elements.GuiTimeline;
import eu.crushedpixel.replaymod.holders.Keyframe;
import eu.crushedpixel.replaymod.holders.Position;
import eu.crushedpixel.replaymod.holders.PositionKeyframe;
import eu.crushedpixel.replaymod.holders.TimeKeyframe;
import eu.crushedpixel.replaymod.registry.ReplayGuiRegistry;
import eu.crushedpixel.replaymod.replay.ReplayHandler;
import eu.crushedpixel.replaymod.utils.MouseUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiChat;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.inventory.GuiInventory;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.util.Point;
import java.io.IOException;
import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
public class GuiReplayOverlay extends Gui {
private static final Minecraft mc = Minecraft.getMinecraft();
public static final ResourceLocation replay_gui = new ResourceLocation("replaymod", "replay_gui.png");
public static final int TEXTURE_SIZE = 128;
private static final float ZOOM_STEPS = 0.05f;
private static GuiTexturedButton texturedButton(int x, int y, int u, int v, int size) {
return new GuiTexturedButton(0, x, y, size, size, replay_gui, u, v, TEXTURE_SIZE, TEXTURE_SIZE);
}
private final int displayWidth = mc.displayWidth;
private final int displayHeight = mc.displayHeight;
private final Point screenDimensions = MouseUtils.getScaledDimensions();
private final int WIDTH = screenDimensions.getX();
private final int HEIGHT = screenDimensions.getY();
// Top row
private final int TOP_ROW = 10;
private final int BUTTON_PLAY_PAUSE_X = 10;
private final int SPEED_X = 35;
private final int SPEED_WIDTH = 100;
private final int TIMELINE_X = SPEED_X + SPEED_WIDTH + 5;
// Bottom row
private final int BOTTOM_ROW = TOP_ROW + 33;
private final int BUTTON_PLAY_PATH_X = 10;
private final int BUTTON_EXPORT_X = BUTTON_PLAY_PATH_X + 25;
private final int BUTTON_PLACE_X = BUTTON_EXPORT_X + 25;
private final int BUTTON_TIME_X = BUTTON_PLACE_X + 25;
private final int TIMELINE_REAL_X = BUTTON_TIME_X + 25;
private final int TIMELINE_REAL_WIDTH = WIDTH - 14 - 11 - TIMELINE_REAL_X;
private final GuiButton buttonPlay = texturedButton(BUTTON_PLAY_PAUSE_X, TOP_ROW, 0, 0, 20);
private final GuiButton buttonPause = texturedButton(BUTTON_PLAY_PAUSE_X, TOP_ROW, 0, 20, 20);
private final GuiButton buttonExport = texturedButton(BUTTON_EXPORT_X, BOTTOM_ROW, 40, 0, 20);
private final GuiButton buttonPlayPath = texturedButton(BUTTON_PLAY_PATH_X, BOTTOM_ROW, 0, 0, 20);
private final GuiButton buttonPlace = texturedButton(BUTTON_PLACE_X, BOTTOM_ROW, 0, 40, 20);
private final GuiButton buttonPlaceSelected = texturedButton(BUTTON_PLACE_X, BOTTOM_ROW, 0, 60, 20);
private final GuiButton buttonTime = texturedButton(BUTTON_TIME_X, BOTTOM_ROW, 0, 80, 20);
private final GuiButton buttonTimeSelected = texturedButton(BUTTON_TIME_X, BOTTOM_ROW, 0, 100, 20);
private final GuiButton buttonZoomIn = texturedButton(WIDTH - 14 - 9, BOTTOM_ROW, 40, 20, 9);
private final GuiButton buttonZoomOut = texturedButton(WIDTH - 14 - 9, BOTTOM_ROW + 11, 40, 30, 9);
private final GuiTimeline timeline = new GuiTimeline(TIMELINE_X, TOP_ROW - 1, WIDTH - 14 - TIMELINE_X);
private final GuiKeyframeTimeline timelineReal = new GuiKeyframeTimeline(TIMELINE_REAL_X, BOTTOM_ROW - 1, TIMELINE_REAL_WIDTH);
{
timelineReal.timelineLength = 10 * 60 * 1000;
}
private final GuiScrollbar scrollbar = new GuiScrollbar(TIMELINE_REAL_X, BOTTOM_ROW + 22, TIMELINE_REAL_WIDTH);
private GuiReplaySpeedSlider speedSlider = new GuiReplaySpeedSlider(1, SPEED_X, TOP_ROW, I18n.format("replaymod.gui.speed"));
private float zoom_scale = 0.1f; //can see 1/10th of the timeline
private double pos_left = 0f; //left border of timeline is at 0%
public boolean isVisible() {
return ReplayHandler.isInReplay();
}
/**
* Resets the UI.
* @param slider {@code true} if the speed-slider should be reset as well
*/
public void resetUI(boolean slider) {
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
mc.displayGuiScreen(null);
}
ReplayHandler.setRealTimelineCursor(0);
if(slider)
speedSlider = new GuiReplaySpeedSlider(1, SPEED_X, TOP_ROW, I18n.format("replaymod.gui.speed"));
}
public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
}
public void unregister() {
FMLCommonHandler.instance().bus().unregister(this);
MinecraftForge.EVENT_BUS.unregister(this);
}
private void checkResize() {
if (displayWidth != mc.displayWidth || displayHeight != mc.displayHeight) {
GuiReplayOverlay other = new GuiReplayOverlay();
other.zoom_scale = this.zoom_scale;
other.pos_left = this.pos_left;
other.speedSlider = this.speedSlider;
this.unregister();
other.register();
ReplayMod.overlay = other;
}
}
@SubscribeEvent
public void onRenderTabList(RenderGameOverlayEvent.Pre event) { //cancelling tab list rendering and rendering help instead
if (!isVisible()) return;
if (event.type == RenderGameOverlayEvent.ElementType.PLAYER_LIST) {
event.setCanceled(true);
}
}
private void tick() {
if (mc.inGameHasFocus) { // TODO Check if this is necessary
Point scaled = MouseUtils.getScaledDimensions();
Mouse.setCursorPosition(scaled.getX() / 2, scaled.getY() / 2);
}
if (FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
Entity player = ReplayHandler.getCameraEntity();
if(player != null) {
player.setVelocity(0, 0, 0);
}
}
checkResize();
}
public void mouseDrag(int mouseX, int mouseY) {
speedSlider.mousePressed(mc, mouseX, mouseY);
scrollbar.doDragging(mouseX);
pos_left = scrollbar.sliderPosition;
timelineReal.mouseDrag(mouseX, mouseY);
}
public void mouseReleased(int mouseX, int mouseY) {
speedSlider.mouseReleased(mouseX, mouseY);
scrollbar.endDragging(mouseX);
pos_left = scrollbar.sliderPosition;
timelineReal.mouseRelease(mouseX, mouseY);
}
public void mouseClicked(int mouseX, int mouseY) {
if (buttonPlayPath.mousePressed(mc, mouseX, mouseY)) {
if (ReplayHandler.isInPath()) {
ReplayHandler.interruptReplay();
} else {
ReplayHandler.startPath(null);
}
}
if (ReplayHandler.isInPath()) {
return; // Only allow clicking of cancel button during path replay
}
if (ReplayMod.replaySender.paused()) {
if (buttonPlay.mousePressed(mc, mouseX, mouseY)) {
ReplayMod.replaySender.setReplaySpeed(speedSlider.getSliderValue());
}
} else {
if (buttonPause.mousePressed(mc, mouseX, mouseY)) {
ReplayMod.replaySender.setReplaySpeed(0);
}
}
speedSlider.mousePressed(mc, mouseX, mouseY);
scrollbar.startDragging(mouseX, mouseY);
timelineReal.mouseClicked(mc, mouseX, mouseY);
if (buttonExport.mousePressed(mc, mouseX, mouseY)) {
mc.displayGuiScreen(new GuiRenderSettings());
}
Keyframe keyframe = ReplayHandler.getSelectedKeyframe();
if (buttonPlace.mousePressed(mc, mouseX, mouseY)) {
if (keyframe instanceof PositionKeyframe) {
ReplayHandler.removeKeyframe(keyframe);
} else {
Entity cam = mc.getRenderViewEntity();
if(cam != null) {
Position position = new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch,
cam.rotationYaw % 360, ReplayHandler.getCameraTilt());
ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), position));
}
}
}
if (buttonTime.mousePressed(mc, mouseX, mouseY)) {
if (keyframe instanceof TimeKeyframe) {
ReplayHandler.removeKeyframe(keyframe);
} else {
ReplayHandler.addKeyframe(new TimeKeyframe(ReplayHandler.getRealTimelineCursor(), ReplayMod.replaySender.currentTimeStamp()));
}
}
if (buttonZoomIn.mousePressed(mc, mouseX, mouseY)) {
zoom_scale = Math.max(0.025f, zoom_scale - ZOOM_STEPS);
}
if (buttonZoomOut.mousePressed(mc, mouseX, mouseY)) {
zoom_scale = Math.min(1f, zoom_scale + ZOOM_STEPS);
pos_left = Math.min(pos_left, 1f - zoom_scale);
}
long timelineTime = timeline.getTimeAt(mouseX, mouseY);
if (timelineTime != -1) { // Click on timeline
//When hurrying, no Timeline jumping etc. is possible
if(!ReplayMod.replaySender.isHurrying()) {
if(timelineTime < ReplayMod.replaySender.currentTimeStamp()) {
mc.displayGuiScreen(null);
}
CameraEntity cam = ReplayHandler.getCameraEntity();
if(cam != null) {
ReplayHandler.setLastPosition(new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch, cam.rotationYaw));
} else {
ReplayHandler.setLastPosition(null);
}
long diff = timelineTime - ReplayMod.replaySender.getDesiredTimestamp();
if(diff != 0) {
if (diff > 0 && diff < 5000) { // Small difference and no time travel
ReplayMod.replaySender.jumpToTime((int) timelineTime);
} else { // We either have to restart the replay or send a significant amount of packets
// Render our please-wait-screen
GuiScreen guiScreen = new GuiScreen() {
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
drawBackground(0);
drawCenteredString(fontRendererObj, I18n.format("replaymod.gui.pleasewait"),
width / 2, height / 2, 0xffffffff);
}
};
// Make sure that the replaysender changes into sync mode
ReplayMod.replaySender.setSyncModeAndWait();
// Perform the rendering using OpenGL
pushMatrix();
clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
enableTexture2D();
mc.getFramebuffer().bindFramebuffer(true);
mc.entityRenderer.setupOverlayRendering();
guiScreen.setWorldAndResolution(mc, WIDTH, HEIGHT);
guiScreen.drawScreen(0, 0, 0);
mc.getFramebuffer().unbindFramebuffer();
popMatrix();
pushMatrix();
mc.getFramebuffer().framebufferRender(mc.displayWidth, mc.displayHeight);
popMatrix();
Display.update();
// Send the packets
ReplayMod.replaySender.sendPacketsTill((int) timelineTime);
ReplayMod.replaySender.setAsyncMode(true);
ReplayMod.replaySender.setReplaySpeed(0);
// Tick twice to process all packets and position interpolation
try {
mc.runTick();
mc.runTick();
} catch (IOException e) {
e.printStackTrace(); // This should never be thrown but whatever
}
// No need to remove our please-wait-screen. It'll vanish with the next
// render pass as it's never been a real GuiScreen in the first place.
}
}
}
}
}
@SubscribeEvent
public void onRenderGui(RenderGameOverlayEvent.Post event) throws IllegalArgumentException, IllegalAccessException {
if (!isVisible()) return;
FMLClientHandler fml = FMLClientHandler.instance();
tick();
// Do not draw if GUI doesn't want us to
if(mc.currentScreen instanceof NoOverlay) {
return;
}
// If we are not currently spectating someone
if (ReplayHandler.isCamera()) {
ReplayGuiRegistry.hide(); // hide all normal UI
} else {
ReplayGuiRegistry.show(); // otherwise show them
}
// Replace chat and inventory GUI (opened by pressing the respective hotkey) with a dummy input GUI
if(fml.isGUIOpen(GuiChat.class) || fml.isGUIOpen(GuiInventory.class)) {
mc.displayGuiScreen(new GuiMouseInput(this));
}
GlStateManager.resetColor();
Keyframe keyframe = ReplayHandler.getSelectedKeyframe();
Point mousePoint = MouseUtils.getMousePos();
int mouseX = mousePoint.getX();
int mouseY = mousePoint.getY();
if (!(mc.currentScreen instanceof GuiMouseInput)) {
// We only react to the mouse if our gui screen is opened.
// Otherwise we just move the mouse away so it doesn't activate any buttons
mouseX = mouseY = -1000;
}
// Draw speed slider
speedSlider.drawButton(mc, mouseX, mouseY);
// Draw buttons
buttonExport.drawButton(mc, mouseX, mouseY);
// Draw play/pause button
if (ReplayMod.replaySender.paused()) {
buttonPlay.drawButton(mc, mouseX, mouseY);
} else {
buttonPause.drawButton(mc, mouseX, mouseY);
}
buttonPlayPath.drawButton(mc, mouseX, mouseY);
// Keyframe buttons
if (keyframe instanceof PositionKeyframe) {
buttonPlaceSelected.drawButton(mc, mouseX, mouseY);
} else {
buttonPlace.drawButton(mc, mouseX, mouseY);
}
if (keyframe instanceof TimeKeyframe) {
buttonTimeSelected.drawButton(mc, mouseX, mouseY);
} else {
buttonTime.drawButton(mc, mouseX, mouseY);
}
buttonZoomIn.drawButton(mc, mouseX, mouseY);
buttonZoomOut.drawButton(mc, mouseX, mouseY);
// Draw scrollbar for real timeline
scrollbar.size = zoom_scale;
scrollbar.sliderPosition = pos_left;
scrollbar.draw(mc);
// Finally draw timelines so that no other GUI elements overlap the mouse-position strings
timeline.cursorPosition = ReplayMod.replaySender.currentTimeStamp();
timeline.timelineLength = ReplayMod.replaySender.replayLength();
timeline.draw(mc, mouseX, mouseY);
timelineReal.cursorPosition = ReplayHandler.getRealTimelineCursor();
timelineReal.zoom = zoom_scale;
timelineReal.timeStart = pos_left;
timelineReal.draw(mc, mouseX, mouseY);
GlStateManager.enableBlend();
}
/**
* Dummy interface for GUI on which this replay overlay shall not be rendered.
*/
public static interface NoOverlay {
}
}

View File

@@ -24,6 +24,7 @@ import net.minecraft.util.Util;
import org.apache.commons.io.FilenameUtils;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Point;
import javax.imageio.ImageIO;
import java.awt.*;