Merge branch '1.8' into 1.9.4
0db1b9eFix opening a replay multiple times by quickly double clicking (fixes #25)9ff767bFix spectated player having the arm model of the camera playerd82cef3Make sure video folder exists before rendering (fixes #24)cc65b26Replace custom update system with forge update checkerc8b71dcAdd tooltips to ingame buttonsb240f8aFix camera path always playing from the beginning regardless of ctrl key4805e5eFix crash when setting two keyframes to the same time via the edit keyframe gui18f8303Fix crash when moving the last keyframefcd4c07Add error popup when trying to play path with reversed time keyframes Update jGui9c605baUpdate jGui Fix handling of ESC key in popups (fixes #17)721fdc4Allow moving the keyframe timeline cursor by dragging (fixes #18)1851e97Allow deletion of selected keyframes by pressing the DELETE key (fixes #19)d547098Fix markers not being removedb2c7faaFix marker not being deselected when clicking anywhere else4e6b387Update jGui Fix popups being drawn outside of the actually visible element (fixes #21)21bbeb2Fix bitrate field always being active regardless of selected preset (fixes #20)b5a6d8bFix camera player head being visible while in third persondd0537bCenter star between resolution fields in render settings guie35bfcaIncrease spacing between rows in the render settings gui6d0cdfbUpdate jGui (fixes #15)
This commit is contained in:
Submodule ReplayStudio updated: 99955dd564...6509ab2106
2
jGui
2
jGui
Submodule jGui updated: 46e47874a4...7849fb3bb7
@@ -34,6 +34,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Mod(modid = ReplayMod.MOD_ID,
|
@Mod(modid = ReplayMod.MOD_ID,
|
||||||
useMetadata = true,
|
useMetadata = true,
|
||||||
|
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
|
||||||
guiFactory = "com.replaymod.core.gui.GuiFactory")
|
guiFactory = "com.replaymod.core.gui.GuiFactory")
|
||||||
public class ReplayMod {
|
public class ReplayMod {
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ public class ReplayModExtras {
|
|||||||
FullBrightness.class,
|
FullBrightness.class,
|
||||||
HotkeyButtons.class,
|
HotkeyButtons.class,
|
||||||
LocalizationExtra.class,
|
LocalizationExtra.class,
|
||||||
OpenEyeExtra.class,
|
OpenEyeExtra.class
|
||||||
VersionChecker.class
|
|
||||||
);
|
);
|
||||||
|
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package com.replaymod.extras;
|
|
||||||
|
|
||||||
import com.replaymod.core.ReplayMod;
|
|
||||||
import com.replaymod.online.ReplayModOnline;
|
|
||||||
import net.minecraft.client.gui.Gui;
|
|
||||||
import net.minecraft.client.gui.GuiMainMenu;
|
|
||||||
import net.minecraft.client.resources.I18n;
|
|
||||||
import net.minecraftforge.client.event.GuiScreenEvent;
|
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
public class VersionChecker implements Extra {
|
|
||||||
@Mod.Instance(ReplayModOnline.MOD_ID)
|
|
||||||
private static ReplayModOnline module;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void register(ReplayMod mod) throws Exception {
|
|
||||||
final String currentVersion = mod.getVersion();
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
boolean upToDate = module.getApiClient().isVersionUpToDate(currentVersion);
|
|
||||||
if (!upToDate) {
|
|
||||||
MinecraftForge.EVENT_BUS.register(VersionChecker.this);
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "ReplayMod-VersionChecker").start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public void onDrawScreen(GuiScreenEvent.DrawScreenEvent.Post event) {
|
|
||||||
if (!(event.getGui() instanceof GuiMainMenu)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = Math.max(100, event.getGui().width / 2 - 100 - 10);
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") List<String> lines =
|
|
||||||
event.getGui().mc.fontRendererObj.listFormattedStringToWidth(I18n.format("replaymod.gui.outdated"), width);
|
|
||||||
|
|
||||||
int maxLineWidth = 0;
|
|
||||||
for(String line : lines) {
|
|
||||||
int lineWidth = event.getGui().mc.fontRendererObj.getStringWidth(line);
|
|
||||||
if(lineWidth > maxLineWidth) {
|
|
||||||
maxLineWidth = lineWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gui.drawRect(2, 77, 5 + maxLineWidth + 3, 80 + (lines.size() * 10), 0x80FF0000);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for(String line : lines) {
|
|
||||||
event.getGui().mc.fontRendererObj.drawStringWithShadow(line, 5, 80 + (i * 10), Color.WHITE.getRGB());
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,15 +11,18 @@ import de.johni0702.minecraft.gui.element.GuiLabel;
|
|||||||
import de.johni0702.minecraft.gui.element.GuiTextField;
|
import de.johni0702.minecraft.gui.element.GuiTextField;
|
||||||
import de.johni0702.minecraft.gui.element.GuiToggleButton;
|
import de.johni0702.minecraft.gui.element.GuiToggleButton;
|
||||||
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
|
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
|
||||||
|
import de.johni0702.minecraft.gui.function.Typeable;
|
||||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
import net.minecraft.client.resources.I18n;
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import org.lwjgl.input.Keyboard;
|
||||||
|
import org.lwjgl.util.ReadablePoint;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class GuiReplayCenterSearch extends AbstractGuiPopup<GuiReplayCenterSearch> {
|
public class GuiReplayCenterSearch extends AbstractGuiPopup<GuiReplayCenterSearch> implements Typeable {
|
||||||
private final GuiReplayCenter replayCenter;
|
private final GuiReplayCenter replayCenter;
|
||||||
private final ApiClient apiClient;
|
private final ApiClient apiClient;
|
||||||
public final GuiLabel title = new GuiLabel().setI18nText("replaymod.gui.center.search.filters").setColor(Colors.BLACK);
|
public final GuiLabel title = new GuiLabel().setI18nText("replaymod.gui.center.search.filters").setColor(Colors.BLACK);
|
||||||
@@ -100,4 +103,13 @@ public class GuiReplayCenterSearch extends AbstractGuiPopup<GuiReplayCenterSearc
|
|||||||
protected GuiReplayCenterSearch getThis() {
|
protected GuiReplayCenterSearch getThis() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) {
|
||||||
|
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||||
|
cancelButton.onClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public abstract class AbstractTimelinePlayer {
|
|||||||
private final Minecraft mc = Minecraft.getMinecraft();
|
private final Minecraft mc = Minecraft.getMinecraft();
|
||||||
private final ReplayHandler replayHandler;
|
private final ReplayHandler replayHandler;
|
||||||
private Timeline timeline;
|
private Timeline timeline;
|
||||||
|
protected long startOffset;
|
||||||
private long lastTime;
|
private long lastTime;
|
||||||
private long lastTimestamp;
|
private long lastTimestamp;
|
||||||
private ListenableFuture<Void> future;
|
private ListenableFuture<Void> future;
|
||||||
@@ -33,6 +34,11 @@ public abstract class AbstractTimelinePlayer {
|
|||||||
this.replayHandler = replayHandler;
|
this.replayHandler = replayHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ListenableFuture<Void> start(Timeline timeline, long from) {
|
||||||
|
startOffset = from;
|
||||||
|
return start(timeline);
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<Void> start(Timeline timeline) {
|
public ListenableFuture<Void> start(Timeline timeline) {
|
||||||
this.timeline = timeline;
|
this.timeline = timeline;
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,6 @@ public class RealtimeTimelinePlayer extends AbstractTimelinePlayer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getTimePassed() {
|
public long getTimePassed() {
|
||||||
return firstFrame ? 0 : System.currentTimeMillis() - startTime;
|
return startOffset + (firstFrame ? 0 : System.currentTimeMillis() - startTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import net.minecraft.client.Minecraft;
|
|||||||
import net.minecraft.crash.CrashReport;
|
import net.minecraft.crash.CrashReport;
|
||||||
import net.minecraft.crash.CrashReportCategory;
|
import net.minecraft.crash.CrashReportCategory;
|
||||||
import org.apache.commons.exec.CommandLine;
|
import org.apache.commons.exec.CommandLine;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.io.output.TeeOutputStream;
|
import org.apache.commons.io.output.TeeOutputStream;
|
||||||
import org.lwjgl.util.ReadableDimension;
|
import org.lwjgl.util.ReadableDimension;
|
||||||
@@ -34,6 +35,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
|
||||||
File outputFolder = settings.getOutputFile().getParentFile();
|
File outputFolder = settings.getOutputFile().getParentFile();
|
||||||
|
FileUtils.forceMkdir(outputFolder);
|
||||||
String fileName = settings.getOutputFile().getName();
|
String fileName = settings.getOutputFile().getName();
|
||||||
|
|
||||||
commandArgs = settings.getExportArguments()
|
commandArgs = settings.getExportArguments()
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
|||||||
}).setSize(122, 20).setSteps(110);
|
}).setSize(122, 20).setSteps(110);
|
||||||
public final GuiPanel videoResolutionPanel = new GuiPanel()
|
public final GuiPanel videoResolutionPanel = new GuiPanel()
|
||||||
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(2))
|
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(2))
|
||||||
.addElements(null, videoWidth, new GuiLabel().setText("*"), videoHeight);
|
.addElements(new HorizontalLayout.Data(0.5), videoWidth, new GuiLabel().setText("*"), videoHeight);
|
||||||
|
|
||||||
public final GuiNumberField bitRateField = new GuiNumberField().setValue(10).setSize(50, 20);
|
public final GuiNumberField bitRateField = new GuiNumberField().setValue(10).setSize(50, 20);
|
||||||
public final GuiDropdownMenu<String> bitRateUnit = new GuiDropdownMenu<String>()
|
public final GuiDropdownMenu<String> bitRateUnit = new GuiDropdownMenu<String>()
|
||||||
@@ -130,7 +130,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
|||||||
new GuiPanel().addElements(null, bitRateField, bitRateUnit).setLayout(new HorizontalLayout()),
|
new GuiPanel().addElements(null, bitRateField, bitRateUnit).setLayout(new HorizontalLayout()),
|
||||||
frameRateSlider).setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(3)),
|
frameRateSlider).setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(3)),
|
||||||
new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton)
|
new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton)
|
||||||
.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(3));
|
.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(5));
|
||||||
|
|
||||||
public final GuiCheckbox nametagCheckbox = new GuiCheckbox()
|
public final GuiCheckbox nametagCheckbox = new GuiCheckbox()
|
||||||
.setI18nLabel("replaymod.gui.rendersettings.nametags");
|
.setI18nLabel("replaymod.gui.rendersettings.nametags");
|
||||||
@@ -284,6 +284,15 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
|||||||
videoHeight.setTextColor(Colors.RED);
|
videoHeight.setTextColor(Colors.RED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable/Disable bitrate input field and dropdown
|
||||||
|
if (encodingPresetDropdown.getSelectedValue().hasBitrateSetting()) {
|
||||||
|
bitRateField.setEnabled();
|
||||||
|
bitRateUnit.setEnabled();
|
||||||
|
} else {
|
||||||
|
bitRateField.setDisabled();
|
||||||
|
bitRateUnit.setDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
// Enable/Disable camera stabilization checkboxes
|
// Enable/Disable camera stabilization checkboxes
|
||||||
switch (renderMethodDropdown.getSelectedValue()) {
|
switch (renderMethodDropdown.getSelectedValue()) {
|
||||||
case CUBIC:
|
case CUBIC:
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class ReplayHandler {
|
|||||||
*/
|
*/
|
||||||
private boolean suppressCameraMovements;
|
private boolean suppressCameraMovements;
|
||||||
|
|
||||||
private final Set<Marker> markers;
|
private final List<Marker> markers;
|
||||||
|
|
||||||
private final GuiReplayOverlay overlay;
|
private final GuiReplayOverlay overlay;
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ public class ReplayHandler {
|
|||||||
|
|
||||||
MinecraftForge.EVENT_BUS.post(new ReplayOpenEvent.Pre(this));
|
MinecraftForge.EVENT_BUS.post(new ReplayOpenEvent.Pre(this));
|
||||||
|
|
||||||
markers = new HashSet<>(replayFile.getMarkers().or(Collections.<Marker>emptySet()));
|
markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet()));
|
||||||
|
|
||||||
replaySender = new ReplaySender(this, replayFile, asyncMode);
|
replaySender = new ReplaySender(this, replayFile, asyncMode);
|
||||||
|
|
||||||
@@ -188,9 +188,9 @@ public class ReplayHandler {
|
|||||||
/**
|
/**
|
||||||
* Returns all markers.
|
* Returns all markers.
|
||||||
* When changed, {@link #saveMarkers()} should be called to save the changes.
|
* When changed, {@link #saveMarkers()} should be called to save the changes.
|
||||||
* @return Set of markers
|
* @return Collection of markers in no particular order
|
||||||
*/
|
*/
|
||||||
public Set<Marker> getMarkers() {
|
public Collection<Marker> getMarkers() {
|
||||||
return markers;
|
return markers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ public class ReplayHandler {
|
|||||||
*/
|
*/
|
||||||
public void saveMarkers() {
|
public void saveMarkers() {
|
||||||
try {
|
try {
|
||||||
replayFile.writeMarkers(markers);
|
replayFile.writeMarkers(new HashSet<>(markers));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.minecraft.block.material.Material;
|
import net.minecraft.block.material.Material;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.entity.AbstractClientPlayer;
|
||||||
import net.minecraft.client.entity.EntityPlayerSP;
|
import net.minecraft.client.entity.EntityPlayerSP;
|
||||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||||
import net.minecraft.client.settings.KeyBinding;
|
import net.minecraft.client.settings.KeyBinding;
|
||||||
@@ -237,6 +238,14 @@ public class CameraEntity extends EntityPlayerSP {
|
|||||||
return ReplayModReplay.instance.getReplayHandler().isCameraView(); // Make sure we're treated as spectator
|
return ReplayModReplay.instance.getReplayHandler().isCameraView(); // Make sure we're treated as spectator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRenderInPass(int pass) {
|
||||||
|
// Never render the camera
|
||||||
|
// This is necessary to hide the player head in third person mode and to not
|
||||||
|
// cause any unwanted shadows when rendering with shaders.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceLocation getLocationSkin() {
|
public ResourceLocation getLocationSkin() {
|
||||||
Entity view = mc.getRenderViewEntity();
|
Entity view = mc.getRenderViewEntity();
|
||||||
@@ -246,6 +255,15 @@ public class CameraEntity extends EntityPlayerSP {
|
|||||||
return super.getLocationSkin();
|
return super.getLocationSkin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSkinType() {
|
||||||
|
Entity view = mc.getRenderViewEntity();
|
||||||
|
if (view != this && view instanceof AbstractClientPlayer) {
|
||||||
|
return ((AbstractClientPlayer) view).getSkinType();
|
||||||
|
}
|
||||||
|
return super.getSkinType();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getSwingProgress(float renderPartialTicks) {
|
public float getSwingProgress(float renderPartialTicks) {
|
||||||
Entity view = mc.getRenderViewEntity();
|
Entity view = mc.getRenderViewEntity();
|
||||||
|
|||||||
@@ -5,14 +5,17 @@ import com.replaymod.replay.ReplayHandler;
|
|||||||
import de.johni0702.minecraft.gui.container.GuiContainer;
|
import de.johni0702.minecraft.gui.container.GuiContainer;
|
||||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||||
import de.johni0702.minecraft.gui.element.*;
|
import de.johni0702.minecraft.gui.element.*;
|
||||||
|
import de.johni0702.minecraft.gui.function.Typeable;
|
||||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
import com.replaymod.replaystudio.data.Marker;
|
import com.replaymod.replaystudio.data.Marker;
|
||||||
|
import org.lwjgl.input.Keyboard;
|
||||||
|
import org.lwjgl.util.ReadablePoint;
|
||||||
|
|
||||||
public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> {
|
public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> implements Typeable {
|
||||||
private final ReplayHandler replayHandler;
|
private final ReplayHandler replayHandler;
|
||||||
private final Marker marker;
|
private final Marker marker;
|
||||||
|
|
||||||
@@ -107,4 +110,13 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> {
|
|||||||
protected GuiEditMarkerPopup getThis() {
|
protected GuiEditMarkerPopup getThis() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) {
|
||||||
|
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||||
|
cancelButton.onClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,8 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline<GuiMarkerTimeline> im
|
|||||||
replayHandler.doJump(marker.getTime(), false);
|
replayHandler.doJump(marker.getTime(), false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
selectedMarker = null;
|
||||||
}
|
}
|
||||||
return super.mouseClick(position, button);
|
return super.mouseClick(position, button);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import de.johni0702.minecraft.gui.GuiRenderer;
|
|||||||
import de.johni0702.minecraft.gui.RenderInfo;
|
import de.johni0702.minecraft.gui.RenderInfo;
|
||||||
import de.johni0702.minecraft.gui.container.AbstractGuiOverlay;
|
import de.johni0702.minecraft.gui.container.AbstractGuiOverlay;
|
||||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||||
|
import de.johni0702.minecraft.gui.element.GuiElement;
|
||||||
import de.johni0702.minecraft.gui.element.GuiSlider;
|
import de.johni0702.minecraft.gui.element.GuiSlider;
|
||||||
import de.johni0702.minecraft.gui.element.GuiTexturedButton;
|
import de.johni0702.minecraft.gui.element.GuiTexturedButton;
|
||||||
|
import de.johni0702.minecraft.gui.element.GuiTooltip;
|
||||||
import de.johni0702.minecraft.gui.element.advanced.IGuiTimeline;
|
import de.johni0702.minecraft.gui.element.advanced.IGuiTimeline;
|
||||||
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
@@ -27,8 +29,20 @@ public class GuiReplayOverlay extends AbstractGuiOverlay<GuiReplayOverlay> {
|
|||||||
|
|
||||||
public final GuiPanel topPanel = new GuiPanel(this)
|
public final GuiPanel topPanel = new GuiPanel(this)
|
||||||
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.LEFT).setSpacing(5));
|
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.LEFT).setSpacing(5));
|
||||||
public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20)
|
public final GuiTexturedButton playPauseButton = new GuiTexturedButton() {
|
||||||
.setTexture(ReplayMod.TEXTURE, TEXTURE_SIZE);
|
@Override
|
||||||
|
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||||
|
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||||
|
if (tooltip != null) {
|
||||||
|
if (getTextureNormal().getY() == 0) { // Play button
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.unpause");
|
||||||
|
} else { // Pause button
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.pause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||||
public final GuiSlider speedSlider = new GuiSlider().setSize(100, 20).setSteps(37); // 0.0 is not included
|
public final GuiSlider speedSlider = new GuiSlider().setSize(100, 20).setSteps(37); // 0.0 is not included
|
||||||
public final GuiMarkerTimeline timeline;
|
public final GuiMarkerTimeline timeline;
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
}).onSelectionDoubleClicked(() -> {
|
}).onSelectionDoubleClicked(() -> {
|
||||||
if (this.loadButton.isEnabled()) {
|
if (this.loadButton.isEnabled()) {
|
||||||
this.loadButton.onClick();
|
this.loadButton.onClick();
|
||||||
|
// Disable load button to prevent the player from opening the replay twice at the same time
|
||||||
|
this.loadButton.setDisabled();
|
||||||
}
|
}
|
||||||
}).setDrawShadow(true).setDrawSlider(true);
|
}).setDrawShadow(true).setDrawSlider(true);
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,20 @@ import de.johni0702.minecraft.gui.element.GuiButton;
|
|||||||
import de.johni0702.minecraft.gui.element.GuiLabel;
|
import de.johni0702.minecraft.gui.element.GuiLabel;
|
||||||
import de.johni0702.minecraft.gui.element.GuiNumberField;
|
import de.johni0702.minecraft.gui.element.GuiNumberField;
|
||||||
import de.johni0702.minecraft.gui.element.IGuiLabel;
|
import de.johni0702.minecraft.gui.element.IGuiLabel;
|
||||||
|
import de.johni0702.minecraft.gui.function.Typeable;
|
||||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
|
import de.johni0702.minecraft.gui.utils.Consumer;
|
||||||
import org.apache.commons.lang3.tuple.Triple;
|
import org.apache.commons.lang3.tuple.Triple;
|
||||||
|
import org.lwjgl.input.Keyboard;
|
||||||
|
import org.lwjgl.util.ReadablePoint;
|
||||||
|
|
||||||
import static de.johni0702.minecraft.gui.utils.Utils.link;
|
import static de.johni0702.minecraft.gui.utils.Utils.link;
|
||||||
|
|
||||||
public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends AbstractGuiPopup<T> {
|
public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends AbstractGuiPopup<T> implements Typeable {
|
||||||
private static GuiNumberField newGuiNumberField() {
|
private static GuiNumberField newGuiNumberField() {
|
||||||
return new GuiNumberField().setPrecision(0).setValidateOnFocusChange(true);
|
return new GuiNumberField().setPrecision(0).setValidateOnFocusChange(true);
|
||||||
}
|
}
|
||||||
@@ -68,9 +72,10 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
|
|
||||||
long time = keyframe.getTime();
|
long time = keyframe.getTime();
|
||||||
timeMinField.setValue(time / 1000 / 60);
|
Consumer<String> updateSaveButtonState = s -> saveButton.setEnabled(canSave());
|
||||||
timeSecField.setValue(time / 1000 % 60);
|
timeMinField.setValue(time / 1000 / 60).onTextChanged(updateSaveButtonState);
|
||||||
timeMSecField.setValue(time % 1000);
|
timeSecField.setValue(time / 1000 % 60).onTextChanged(updateSaveButtonState);
|
||||||
|
timeMSecField.setValue(time % 1000).onTextChanged(updateSaveButtonState);
|
||||||
|
|
||||||
title.setI18nText("replaymod.gui.editkeyframe.title." + type);
|
title.setI18nText("replaymod.gui.editkeyframe.title." + type);
|
||||||
saveButton.onClick(() -> {
|
saveButton.onClick(() -> {
|
||||||
@@ -84,6 +89,23 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canSave() {
|
||||||
|
long newTime = (timeMinField.getInteger() * 60 + timeSecField.getInteger()) * 1000 + timeMSecField.getInteger();
|
||||||
|
if (newTime != keyframe.getTime() && path.getKeyframe(newTime) != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) {
|
||||||
|
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||||
|
cancelButton.onClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open() {
|
public void open() {
|
||||||
super.open();
|
super.open();
|
||||||
|
|||||||
@@ -121,24 +121,42 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline
|
|||||||
|| !segment.getInterpolator().getKeyframeProperties().contains(SpectatorProperty.PROPERTY)) {
|
|| !segment.getInterpolator().getKeyframeProperties().contains(SpectatorProperty.PROPERTY)) {
|
||||||
continue; // Not a spectator segment
|
continue; // Not a spectator segment
|
||||||
}
|
}
|
||||||
long startFrameTime = segment.getStartKeyframe().getTime();
|
drawQuadOnSegment(renderer, visibleWidth, segment, BORDER_TOP + 1, 0xFF0088FF);
|
||||||
long endFrameTime = segment.getEndKeyframe().getTime();
|
}
|
||||||
if (startFrameTime >= endTime || endFrameTime <= startTime) {
|
|
||||||
continue; // Segment out of display range
|
|
||||||
}
|
|
||||||
|
|
||||||
double relativeStart = startFrameTime - startTime;
|
// Draw red quads on time path segments that would require time going backwards
|
||||||
double relativeEnd = endFrameTime - startTime;
|
for (PathSegment segment : mod.getCurrentTimeline().getPaths().get(GuiPathing.TIME_PATH).getSegments()) {
|
||||||
int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + KEYFRAME_SIZE / 2 + 1);
|
long startTimestamp = segment.getStartKeyframe().getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
||||||
int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2);
|
long endTimestamp = segment.getEndKeyframe().getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
||||||
if (startX < endX) {
|
if (endTimestamp >= startTimestamp) {
|
||||||
renderer.drawRect(startX + 1, BORDER_TOP + 1, endX - startX - 2, KEYFRAME_SIZE - 2, 0xFF0088FF);
|
continue; // All is fine, time is not moving backwards
|
||||||
}
|
}
|
||||||
|
drawQuadOnSegment(renderer, visibleWidth, segment, BORDER_TOP + KEYFRAME_SIZE + 1, 0xFFFF0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.drawTimelineCursor(renderer, size);
|
super.drawTimelineCursor(renderer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void drawQuadOnSegment(GuiRenderer renderer, int visibleWidth, PathSegment segment, int y, int color) {
|
||||||
|
int startTime = getOffset();
|
||||||
|
int visibleTime = (int) (getZoom() * getLength());
|
||||||
|
int endTime = getOffset() + visibleTime;
|
||||||
|
|
||||||
|
long startFrameTime = segment.getStartKeyframe().getTime();
|
||||||
|
long endFrameTime = segment.getEndKeyframe().getTime();
|
||||||
|
if (startFrameTime >= endTime || endFrameTime <= startTime) {
|
||||||
|
return; // Segment out of display range
|
||||||
|
}
|
||||||
|
|
||||||
|
double relativeStart = startFrameTime - startTime;
|
||||||
|
double relativeEnd = endFrameTime - startTime;
|
||||||
|
int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + KEYFRAME_SIZE / 2 + 1);
|
||||||
|
int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2);
|
||||||
|
if (startX < endX) {
|
||||||
|
renderer.drawRect(startX + 1, y, endX - startX - 2, KEYFRAME_SIZE - 2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the keyframe at the specified position.
|
* Returns the keyframe at the specified position.
|
||||||
* @param position The raw position
|
* @param position The raw position
|
||||||
@@ -243,6 +261,15 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline
|
|||||||
@Override
|
@Override
|
||||||
public boolean mouseDrag(ReadablePoint position, int button, long timeSinceLastCall) {
|
public boolean mouseDrag(ReadablePoint position, int button, long timeSinceLastCall) {
|
||||||
if (!dragging) {
|
if (!dragging) {
|
||||||
|
if (button == 0) {
|
||||||
|
// Left click, the user might try to move the cursor by clicking and holding
|
||||||
|
int time = getTimeAt(position.getX(), position.getY());
|
||||||
|
if (time != -1) {
|
||||||
|
// and they are still on the timeline, so update the time appropriately
|
||||||
|
setCursorPosition(time);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
|
|||||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||||
|
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
||||||
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
@@ -69,22 +70,69 @@ public class GuiPathing {
|
|||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger();
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20)
|
public final GuiTexturedButton playPauseButton = new GuiTexturedButton() {
|
||||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
@Override
|
||||||
|
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||||
|
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||||
|
if (tooltip != null) {
|
||||||
|
if (player.isActive()) {
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.pausepath");
|
||||||
|
} else if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) {
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.playpathfromstart");
|
||||||
|
} else {
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.playpath");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||||
|
|
||||||
public final GuiTexturedButton renderButton = new GuiTexturedButton().onClick(new Runnable() {
|
public final GuiTexturedButton renderButton = new GuiTexturedButton().onClick(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
preparePathsForPlayback();
|
if (!preparePathsForPlayback()) return;
|
||||||
new GuiRenderSettings(replayHandler, mod.getCurrentTimeline()).display();
|
new GuiRenderSettings(replayHandler, mod.getCurrentTimeline()).display();
|
||||||
}
|
}
|
||||||
}).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0);
|
}).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0)
|
||||||
|
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.renderpath"));
|
||||||
|
|
||||||
public final GuiTexturedButton positionKeyframeButton = new GuiTexturedButton().setSize(20, 20)
|
public final GuiTexturedButton positionKeyframeButton = new GuiTexturedButton() {
|
||||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
@Override
|
||||||
|
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||||
|
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||||
|
if (tooltip != null) {
|
||||||
|
if (getTextureNormal().getY() == 40) { // Add keyframe
|
||||||
|
if (getTextureNormal().getX() == 0) { // Position
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.addposkeyframe");
|
||||||
|
} else { // Spectator
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.addspeckeyframe");
|
||||||
|
}
|
||||||
|
} else { // Remove keyframe
|
||||||
|
if (getTextureNormal().getX() == 0) { // Position
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.removeposkeyframe");
|
||||||
|
} else { // Spectator
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.removespeckeyframe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||||
|
|
||||||
public final GuiTexturedButton timeKeyframeButton = new GuiTexturedButton().setSize(20, 20)
|
public final GuiTexturedButton timeKeyframeButton = new GuiTexturedButton() {
|
||||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
@Override
|
||||||
|
public GuiElement getTooltip(RenderInfo renderInfo) {
|
||||||
|
GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo);
|
||||||
|
if (tooltip != null) {
|
||||||
|
if (getTextureNormal().getY() == 80) { // Add time keyframe
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.addtimekeyframe");
|
||||||
|
} else { // Remove time keyframe
|
||||||
|
tooltip.setI18nText("replaymod.gui.ingame.menu.removetimekeyframe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
}.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip());
|
||||||
|
|
||||||
public final GuiKeyframeTimeline timeline = new GuiKeyframeTimeline(this){
|
public final GuiKeyframeTimeline timeline = new GuiKeyframeTimeline(this){
|
||||||
@Override
|
@Override
|
||||||
@@ -113,14 +161,16 @@ public class GuiPathing {
|
|||||||
public void run() {
|
public void run() {
|
||||||
zoomTimeline(2d / 3d);
|
zoomTimeline(2d / 3d);
|
||||||
}
|
}
|
||||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20);
|
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20)
|
||||||
|
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomin"));
|
||||||
|
|
||||||
public final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9).onClick(new Runnable() {
|
public final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9).onClick(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
zoomTimeline(3d / 2d);
|
zoomTimeline(3d / 2d);
|
||||||
}
|
}
|
||||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30);
|
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30)
|
||||||
|
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomout"));
|
||||||
|
|
||||||
public final GuiPanel zoomButtonPanel = new GuiPanel()
|
public final GuiPanel zoomButtonPanel = new GuiPanel()
|
||||||
.setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2))
|
.setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2))
|
||||||
@@ -231,10 +281,12 @@ public class GuiPathing {
|
|||||||
Timeline timeline = mod.getCurrentTimeline();
|
Timeline timeline = mod.getCurrentTimeline();
|
||||||
Path timePath = timeline.getPaths().get(TIME_PATH);
|
Path timePath = timeline.getPaths().get(TIME_PATH);
|
||||||
|
|
||||||
preparePathsForPlayback();
|
if (!preparePathsForPlayback()) return;
|
||||||
|
|
||||||
timePath.setActive(!Keyboard.isKeyDown(Keyboard.KEY_LSHIFT));
|
timePath.setActive(!Keyboard.isKeyDown(Keyboard.KEY_LSHIFT));
|
||||||
ListenableFuture<Void> future = player.start(timeline);
|
// Start from cursor time unless the control key is pressed (then start from beginning)
|
||||||
|
int startTime = Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)? 0 : GuiPathing.this.timeline.getCursorPosition();
|
||||||
|
ListenableFuture<Void> future = player.start(timeline, startTime);
|
||||||
overlay.setCloseable(false);
|
overlay.setCloseable(false);
|
||||||
overlay.setMouseVisible(true);
|
overlay.setMouseVisible(true);
|
||||||
Futures.addCallback(future, new FutureCallback<Void>() {
|
Futures.addCallback(future, new FutureCallback<Void>() {
|
||||||
@@ -403,6 +455,15 @@ public class GuiPathing {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> {
|
||||||
|
if (!overlay.isVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mod.getSelectedKeyframe() != null) {
|
||||||
|
updateKeyframe(mod.getSelectedKeyframe().getValue(TimestampProperty.PROPERTY).isPresent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Start loading entity tracker
|
// Start loading entity tracker
|
||||||
entityTrackerFuture = SettableFuture.create();
|
entityTrackerFuture = SettableFuture.create();
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
@@ -429,10 +490,27 @@ public class GuiPathing {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void preparePathsForPlayback() {
|
private boolean preparePathsForPlayback() {
|
||||||
Timeline timeline = mod.getCurrentTimeline();
|
Timeline timeline = mod.getCurrentTimeline();
|
||||||
timeline.getPaths().get(TIME_PATH).updateAll();
|
timeline.getPaths().get(TIME_PATH).updateAll();
|
||||||
timeline.getPaths().get(POSITION_PATH).updateAll();
|
timeline.getPaths().get(POSITION_PATH).updateAll();
|
||||||
|
|
||||||
|
// Make sure time keyframes's values are monotonically increasing
|
||||||
|
int lastTime = 0;
|
||||||
|
for (Keyframe keyframe : timeline.getPaths().get(TIME_PATH).getKeyframes()) {
|
||||||
|
int time = keyframe.getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
||||||
|
if (time < lastTime) {
|
||||||
|
// We are going backwards in time
|
||||||
|
GuiInfoPopup.open(replayHandler.getOverlay(),
|
||||||
|
"replaymod.error.negativetime1",
|
||||||
|
"replaymod.error.negativetime2",
|
||||||
|
"replaymod.error.negativetime3");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lastTime = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void zoomTimeline(double factor) {
|
public void zoomTimeline(double factor) {
|
||||||
|
|||||||
@@ -485,6 +485,9 @@ replaymod.gui.objects=Custom Objects
|
|||||||
#Errors
|
#Errors
|
||||||
replaymod.error.unknownrestriction1=This replay cannot be played with your current version.
|
replaymod.error.unknownrestriction1=This replay cannot be played with your current version.
|
||||||
replaymod.error.unknownrestriction2=It tried to enforce %s which is unknown.
|
replaymod.error.unknownrestriction2=It tried to enforce %s which is unknown.
|
||||||
|
replaymod.error.negativetime1=Some of your time keyframes are out of order.
|
||||||
|
replaymod.error.negativetime2=Going backwards in time is not supported.
|
||||||
|
replaymod.error.negativetime3=The invalid parts are marked in red.
|
||||||
|
|
||||||
#Replay Mod Incompatibility Warning
|
#Replay Mod Incompatibility Warning
|
||||||
replaymod.gui.modwarning.title=Incompatibility detected
|
replaymod.gui.modwarning.title=Incompatibility detected
|
||||||
|
|||||||
Reference in New Issue
Block a user