Allow dragging of keyframes in simplepathing
This commit is contained in:
@@ -6,23 +6,73 @@ import com.replaymod.core.ReplayMod;
|
|||||||
import com.replaymod.pathing.properties.CameraProperties;
|
import com.replaymod.pathing.properties.CameraProperties;
|
||||||
import com.replaymod.pathing.properties.SpectatorProperty;
|
import com.replaymod.pathing.properties.SpectatorProperty;
|
||||||
import com.replaymod.pathing.properties.TimestampProperty;
|
import com.replaymod.pathing.properties.TimestampProperty;
|
||||||
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
|
import com.replaymod.replaystudio.pathing.change.Change;
|
||||||
import com.replaymod.replaystudio.pathing.path.Keyframe;
|
import com.replaymod.replaystudio.pathing.path.Keyframe;
|
||||||
import com.replaymod.replaystudio.pathing.path.Path;
|
import com.replaymod.replaystudio.pathing.path.Path;
|
||||||
import com.replaymod.replaystudio.pathing.path.PathSegment;
|
import com.replaymod.replaystudio.pathing.path.PathSegment;
|
||||||
|
import com.replaymod.replaystudio.pathing.path.Timeline;
|
||||||
|
import com.replaymod.replaystudio.pathing.property.Property;
|
||||||
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
||||||
import de.johni0702.minecraft.gui.GuiRenderer;
|
import de.johni0702.minecraft.gui.GuiRenderer;
|
||||||
import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline;
|
import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline;
|
||||||
|
import de.johni0702.minecraft.gui.function.Draggable;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.lwjgl.util.Point;
|
||||||
import org.lwjgl.util.ReadableDimension;
|
import org.lwjgl.util.ReadableDimension;
|
||||||
|
import org.lwjgl.util.ReadablePoint;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline> {
|
public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline> implements Draggable {
|
||||||
protected static final int KEYFRAME_SIZE = 5;
|
protected static final int KEYFRAME_SIZE = 5;
|
||||||
protected static final int KEYFRAME_TEXTURE_X = 74;
|
protected static final int KEYFRAME_TEXTURE_X = 74;
|
||||||
protected static final int KEYFRAME_TEXTURE_Y = 20;
|
protected static final int KEYFRAME_TEXTURE_Y = 20;
|
||||||
|
private static final int DOUBLE_CLICK_INTERVAL = 250;
|
||||||
|
private static final int DRAGGING_THRESHOLD = KEYFRAME_SIZE;
|
||||||
|
|
||||||
private final GuiPathing gui;
|
private final GuiPathing gui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The keyframe that was last clicked on using the left mouse button.
|
||||||
|
*/
|
||||||
|
private Keyframe lastClickedKeyframe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id of the path of {@link #lastClickedKeyframe}.
|
||||||
|
*/
|
||||||
|
private int lastClickedPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time at which {@link #lastClickedKeyframe} was updated.
|
||||||
|
* According to {@link Minecraft#getSystemTime()}.
|
||||||
|
*/
|
||||||
|
private long lastClickedTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to handle dragging events.
|
||||||
|
*/
|
||||||
|
private boolean dragging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we have surpassed the initial threshold and are actually dragging the keyframe.
|
||||||
|
*/
|
||||||
|
private boolean actuallyDragging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where the mouse was when {@link #dragging} started.
|
||||||
|
*/
|
||||||
|
private int draggingStartX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change caused by dragging. Whenever the user moves the keyframe further, the previous change is undone
|
||||||
|
* and a new one is created. This way when the mouse is released, only one change is in the undo history.
|
||||||
|
*/
|
||||||
|
private Change draggingChange;
|
||||||
|
|
||||||
public GuiKeyframeTimeline(GuiPathing gui) {
|
public GuiKeyframeTimeline(GuiPathing gui) {
|
||||||
this.gui = gui;
|
this.gui = gui;
|
||||||
}
|
}
|
||||||
@@ -91,6 +141,163 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline
|
|||||||
super.drawTimelineCursor(renderer, size);
|
super.drawTimelineCursor(renderer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the keyframe at the specified position.
|
||||||
|
* @param position The raw position
|
||||||
|
* @return Pair of path id and keyframe or null when no keyframe was clicked
|
||||||
|
*/
|
||||||
|
private Pair<Integer, Keyframe> getKeyframe(ReadablePoint position) {
|
||||||
|
int time = getTimeAt(position.getX(), position.getY());
|
||||||
|
if (time != -1) {
|
||||||
|
Point mouse = new Point(position);
|
||||||
|
getContainer().convertFor(this, mouse);
|
||||||
|
int mouseY = mouse.getY();
|
||||||
|
if (mouseY > BORDER_TOP && mouseY < BORDER_TOP + 2 * KEYFRAME_SIZE) {
|
||||||
|
Timeline timeline = gui.getMod().getCurrentTimeline();
|
||||||
|
int path;
|
||||||
|
if (mouseY <= BORDER_TOP + KEYFRAME_SIZE) {
|
||||||
|
// Position keyframe
|
||||||
|
path = GuiPathing.POSITION_PATH;
|
||||||
|
} else {
|
||||||
|
// Time keyframe
|
||||||
|
path = GuiPathing.TIME_PATH;
|
||||||
|
}
|
||||||
|
int visibleTime = (int) (getZoom() * getLength());
|
||||||
|
int tolerance = visibleTime * KEYFRAME_SIZE / (size.getWidth() - BORDER_LEFT - BORDER_RIGHT) / 2;
|
||||||
|
Optional<Keyframe> keyframe = timeline.getPaths().get(path).getKeyframes().stream()
|
||||||
|
.filter(k -> Math.abs(k.getTime() - time) <= tolerance)
|
||||||
|
.sorted(Comparator.comparing(k -> Math.abs(k.getTime() - time)))
|
||||||
|
.findFirst();
|
||||||
|
if (keyframe.isPresent()) {
|
||||||
|
return Pair.of(path, keyframe.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseClick(ReadablePoint position, int button) {
|
||||||
|
int time = getTimeAt(position.getX(), position.getY());
|
||||||
|
Pair<Integer, Keyframe> pathKeyframePair = getKeyframe(position);
|
||||||
|
if (pathKeyframePair != null) {
|
||||||
|
// Clicked on keyframe
|
||||||
|
Keyframe keyframe = pathKeyframePair.getRight();
|
||||||
|
if (button == 0) { // Left click
|
||||||
|
long now = Minecraft.getSystemTime();
|
||||||
|
if (lastClickedKeyframe == keyframe) {
|
||||||
|
// Clicked the same keyframe again, potentially a double click
|
||||||
|
if (now - lastClickedTime < DOUBLE_CLICK_INTERVAL) {
|
||||||
|
// Yup, double click, open the edit keyframe gui
|
||||||
|
gui.openEditKeyframePopup(keyframe);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Not a double click, just update the click time and selection
|
||||||
|
lastClickedTime = now;
|
||||||
|
lastClickedKeyframe = keyframe;
|
||||||
|
lastClickedPath = pathKeyframePair.getLeft();
|
||||||
|
selectKeyframe(lastClickedPath, lastClickedKeyframe);
|
||||||
|
// We might be dragging
|
||||||
|
draggingStartX = position.getX();
|
||||||
|
dragging = true;
|
||||||
|
} else if (button == 1) { // Right click
|
||||||
|
for (Property property : keyframe.getProperties()) {
|
||||||
|
applyPropertyToGame(property, keyframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (time != -1) {
|
||||||
|
// Clicked on timeline but not on any keyframe
|
||||||
|
setCursorPosition(time);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Missed timeline
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method because generics cannot be defined on blocks
|
||||||
|
private <T> void applyPropertyToGame(Property<T> property, Keyframe keyframe) {
|
||||||
|
Optional<T> value = keyframe.getValue(property);
|
||||||
|
if (value.isPresent()) {
|
||||||
|
property.applyToGame(value.get(), ReplayModReplay.instance.getReplayHandler());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectKeyframe(int path, Keyframe keyframe) {
|
||||||
|
if (path == GuiPathing.POSITION_PATH) {
|
||||||
|
gui.getMod().setSelectedPositionKeyframe(keyframe);
|
||||||
|
gui.getMod().setSelectedTimeKeyframe(null);
|
||||||
|
} else {
|
||||||
|
gui.getMod().setSelectedPositionKeyframe(null);
|
||||||
|
gui.getMod().setSelectedTimeKeyframe(keyframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseDrag(ReadablePoint position, int button, long timeSinceLastCall) {
|
||||||
|
if (!dragging) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!actuallyDragging) {
|
||||||
|
// Check if threshold has been passed by now
|
||||||
|
if (Math.abs(position.getX() - draggingStartX) >= DRAGGING_THRESHOLD) {
|
||||||
|
actuallyDragging = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (actuallyDragging) {
|
||||||
|
// Threshold passed
|
||||||
|
Path path = gui.getMod().getCurrentTimeline().getPaths().get(lastClickedPath);
|
||||||
|
Point mouse = new Point(position);
|
||||||
|
getContainer().convertFor(this, mouse);
|
||||||
|
int mouseX = mouse.getX();
|
||||||
|
int width = size.getWidth();
|
||||||
|
int bodyWidth = width - BORDER_LEFT - BORDER_RIGHT;
|
||||||
|
double segmentLength = getLength() * getZoom();
|
||||||
|
double segmentTime = segmentLength * (mouseX - BORDER_LEFT) / bodyWidth;
|
||||||
|
int newTime = Math.min(Math.max((int) Math.round(getOffset() + segmentTime), 0), getLength());
|
||||||
|
if (newTime < 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there already is a keyframe at the target time, then increase the time by one until there is none
|
||||||
|
while (path.getKeyframe(newTime) != null) {
|
||||||
|
newTime++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First undo any previous changes
|
||||||
|
if (draggingChange != null) {
|
||||||
|
draggingChange.undo(gui.getMod().getCurrentTimeline());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move keyframe to new position and
|
||||||
|
// store change for later undoing / pushing to history
|
||||||
|
draggingChange = gui.moveKeyframe(lastClickedPath, lastClickedKeyframe, newTime);
|
||||||
|
|
||||||
|
// Selected keyframe has been replaced
|
||||||
|
selectKeyframe(lastClickedPath, path.getKeyframe(newTime));
|
||||||
|
|
||||||
|
// Path has been changed
|
||||||
|
path.updateAll();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseRelease(ReadablePoint position, int button) {
|
||||||
|
if (dragging) {
|
||||||
|
if (actuallyDragging) {
|
||||||
|
gui.getMod().getCurrentTimeline().pushChange(draggingChange);
|
||||||
|
draggingChange = null;
|
||||||
|
actuallyDragging = false;
|
||||||
|
}
|
||||||
|
dragging = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected GuiKeyframeTimeline getThis() {
|
protected GuiKeyframeTimeline getThis() {
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.replaymod.replaystudio.pathing.path.Keyframe;
|
|||||||
import com.replaymod.replaystudio.pathing.path.Path;
|
import com.replaymod.replaystudio.pathing.path.Path;
|
||||||
import com.replaymod.replaystudio.pathing.path.PathSegment;
|
import com.replaymod.replaystudio.pathing.path.PathSegment;
|
||||||
import com.replaymod.replaystudio.pathing.path.Timeline;
|
import com.replaymod.replaystudio.pathing.path.Timeline;
|
||||||
|
import com.replaymod.replaystudio.pathing.property.Property;
|
||||||
import com.replaymod.replaystudio.util.EntityPositionTracker;
|
import com.replaymod.replaystudio.util.EntityPositionTracker;
|
||||||
import com.replaymod.replaystudio.util.Location;
|
import com.replaymod.replaystudio.util.Location;
|
||||||
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
||||||
@@ -32,7 +33,6 @@ import de.johni0702.minecraft.gui.container.GuiPanel;
|
|||||||
import de.johni0702.minecraft.gui.element.*;
|
import de.johni0702.minecraft.gui.element.*;
|
||||||
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
|
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
|
||||||
import de.johni0702.minecraft.gui.element.advanced.GuiTimelineTime;
|
import de.johni0702.minecraft.gui.element.advanced.GuiTimelineTime;
|
||||||
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;
|
||||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
@@ -91,15 +91,7 @@ public class GuiPathing {
|
|||||||
}
|
}
|
||||||
super.draw(renderer, size, renderInfo);
|
super.draw(renderer, size, renderInfo);
|
||||||
}
|
}
|
||||||
}.onClick(new IGuiTimeline.OnClick() {
|
}.setSize(Integer.MAX_VALUE, 20).setLength(30 * 60 * 1000).setMarkers();
|
||||||
@Override
|
|
||||||
public void run(int time) {
|
|
||||||
timeline.setCursorPosition(time);
|
|
||||||
// TODO: Keyframe selection
|
|
||||||
mod.setSelectedPositionKeyframe(null);
|
|
||||||
mod.setSelectedTimeKeyframe(null);
|
|
||||||
}
|
|
||||||
}).setSize(Integer.MAX_VALUE, 20).setLength(30 * 60 * 1000).setMarkers();
|
|
||||||
|
|
||||||
public final GuiHorizontalScrollbar scrollbar = new GuiHorizontalScrollbar().setSize(Integer.MAX_VALUE, 9);
|
public final GuiHorizontalScrollbar scrollbar = new GuiHorizontalScrollbar().setSize(Integer.MAX_VALUE, 9);
|
||||||
{scrollbar.onValueChanged(new Runnable() {
|
{scrollbar.onValueChanged(new Runnable() {
|
||||||
@@ -399,7 +391,7 @@ public class GuiPathing {
|
|||||||
Timeline timeline = mod.getCurrentTimeline();
|
Timeline timeline = mod.getCurrentTimeline();
|
||||||
Path path = timeline.getPaths().get(isTime ? TIME_PATH : POSITION_PATH);
|
Path path = timeline.getPaths().get(isTime ? TIME_PATH : POSITION_PATH);
|
||||||
|
|
||||||
Keyframe keyframe = path.getKeyframe(time);
|
Keyframe keyframe = isTime ? mod.getSelectedTimeKeyframe() : mod.getSelectedPositionKeyframe();
|
||||||
Change change;
|
Change change;
|
||||||
if (keyframe == null) {
|
if (keyframe == null) {
|
||||||
change = AddKeyframe.create(path, time);
|
change = AddKeyframe.create(path, time);
|
||||||
@@ -442,33 +434,9 @@ public class GuiPathing {
|
|||||||
// Update interpolators for spectator keyframes
|
// Update interpolators for spectator keyframes
|
||||||
// while this is overkill, it is far simpler than updating differently for every possible case
|
// while this is overkill, it is far simpler than updating differently for every possible case
|
||||||
if (!isTime) {
|
if (!isTime) {
|
||||||
Interpolator interpolator = null;
|
Change interpolators = updateInterpolators();
|
||||||
boolean isSpectatorInterpolator = false;
|
interpolators.apply(timeline);
|
||||||
for (PathSegment segment : path.getSegments()) {
|
change = CombinedChange.createFromApplied(change, interpolators);
|
||||||
if (segment.getStartKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()
|
|
||||||
&& segment.getEndKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()) {
|
|
||||||
// Spectator segment
|
|
||||||
if (!isSpectatorInterpolator) {
|
|
||||||
isSpectatorInterpolator = true;
|
|
||||||
interpolator = new LinearInterpolator();
|
|
||||||
interpolator.registerProperty(SpectatorProperty.PROPERTY);
|
|
||||||
}
|
|
||||||
SetInterpolator setInterpolator = SetInterpolator.create(segment, interpolator);
|
|
||||||
setInterpolator.apply(timeline);
|
|
||||||
change = CombinedChange.createFromApplied(change, setInterpolator);
|
|
||||||
} else {
|
|
||||||
// Normal segment
|
|
||||||
if (isSpectatorInterpolator || interpolator == null) {
|
|
||||||
isSpectatorInterpolator = false;
|
|
||||||
interpolator = new CubicSplineInterpolator();
|
|
||||||
interpolator.registerProperty(CameraProperties.POSITION);
|
|
||||||
interpolator.registerProperty(CameraProperties.ROTATION);
|
|
||||||
}
|
|
||||||
SetInterpolator setInterpolator = SetInterpolator.create(segment, interpolator);
|
|
||||||
setInterpolator.apply(timeline);
|
|
||||||
change = CombinedChange.createFromApplied(change, setInterpolator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Change specPosUpdate = updateSpectatorPositions();
|
Change specPosUpdate = updateSpectatorPositions();
|
||||||
@@ -484,7 +452,35 @@ public class GuiPathing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Change updateSpectatorPositions() {
|
public Change updateInterpolators() {
|
||||||
|
List<Change> changes = new ArrayList<>();
|
||||||
|
Interpolator interpolator = null;
|
||||||
|
boolean isSpectatorInterpolator = false;
|
||||||
|
for (PathSegment segment : mod.getCurrentTimeline().getPaths().get(POSITION_PATH).getSegments()) {
|
||||||
|
if (segment.getStartKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()
|
||||||
|
&& segment.getEndKeyframe().getValue(SpectatorProperty.PROPERTY).isPresent()) {
|
||||||
|
// Spectator segment
|
||||||
|
if (!isSpectatorInterpolator) {
|
||||||
|
isSpectatorInterpolator = true;
|
||||||
|
interpolator = new LinearInterpolator();
|
||||||
|
interpolator.registerProperty(SpectatorProperty.PROPERTY);
|
||||||
|
}
|
||||||
|
changes.add(SetInterpolator.create(segment, interpolator));
|
||||||
|
} else {
|
||||||
|
// Normal segment
|
||||||
|
if (isSpectatorInterpolator || interpolator == null) {
|
||||||
|
isSpectatorInterpolator = false;
|
||||||
|
interpolator = new CubicSplineInterpolator();
|
||||||
|
interpolator.registerProperty(CameraProperties.POSITION);
|
||||||
|
interpolator.registerProperty(CameraProperties.ROTATION);
|
||||||
|
}
|
||||||
|
changes.add(SetInterpolator.create(segment, interpolator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CombinedChange.create(changes.toArray(new Change[changes.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Change updateSpectatorPositions() {
|
||||||
List<Change> changes = new ArrayList<>();
|
List<Change> changes = new ArrayList<>();
|
||||||
Path positionPath = mod.getCurrentTimeline().getPaths().get(POSITION_PATH);
|
Path positionPath = mod.getCurrentTimeline().getPaths().get(POSITION_PATH);
|
||||||
Path timePath = mod.getCurrentTimeline().getPaths().get(TIME_PATH);
|
Path timePath = mod.getCurrentTimeline().getPaths().get(TIME_PATH);
|
||||||
@@ -514,6 +510,56 @@ public class GuiPathing {
|
|||||||
return CombinedChange.create(changes.toArray(new Change[changes.size()]));
|
return CombinedChange.create(changes.toArray(new Change[changes.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Change moveKeyframe(int pathId, Keyframe keyframe, long newTime) {
|
||||||
|
Timeline timeline = mod.getCurrentTimeline();
|
||||||
|
Path path = timeline.getPaths().get(pathId);
|
||||||
|
// Interpolator might be required later (only if path is the time path)
|
||||||
|
Optional<Interpolator> interpolator =
|
||||||
|
path.getSegments().stream().findFirst().map(PathSegment::getInterpolator);
|
||||||
|
|
||||||
|
// First remove the old keyframe
|
||||||
|
Change removeChange = RemoveKeyframe.create(path, keyframe);
|
||||||
|
removeChange.apply(timeline);
|
||||||
|
|
||||||
|
// and add a new one at the correct time
|
||||||
|
Change addChange = AddKeyframe.create(path, newTime);
|
||||||
|
addChange.apply(timeline);
|
||||||
|
path.getKeyframe(newTime);
|
||||||
|
|
||||||
|
// Then copy over all properties
|
||||||
|
UpdateKeyframeProperties.Builder builder = UpdateKeyframeProperties.create(path, path.getKeyframe(newTime));
|
||||||
|
for (Property property : keyframe.getProperties()) {
|
||||||
|
copyProperty(property, keyframe, builder);
|
||||||
|
}
|
||||||
|
Change propertyChange = builder.done();
|
||||||
|
propertyChange.apply(timeline);
|
||||||
|
|
||||||
|
// Finally set the interpolators
|
||||||
|
Change interpolatorChange;
|
||||||
|
if (pathId == GuiPathing.POSITION_PATH) {
|
||||||
|
// Position / Spectator keyframes need special handling
|
||||||
|
interpolatorChange = updateInterpolators();
|
||||||
|
} else {
|
||||||
|
// Time keyframes only need updating when only one segment of them exists
|
||||||
|
if (path.getSegments().size() == 1) {
|
||||||
|
interpolatorChange = SetInterpolator.create(path.getSegments().iterator().next(), interpolator.get());
|
||||||
|
} else {
|
||||||
|
interpolatorChange = CombinedChange.create(); // Noop change
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interpolatorChange.apply(timeline);
|
||||||
|
// and update spectator positions
|
||||||
|
Change spectatorChange = updateSpectatorPositions();
|
||||||
|
spectatorChange.apply(timeline);
|
||||||
|
|
||||||
|
return CombinedChange.createFromApplied(removeChange, addChange, propertyChange, interpolatorChange, spectatorChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method because generics cannot be defined on blocks
|
||||||
|
private <T> void copyProperty(Property<T> property, Keyframe from, UpdateKeyframeProperties.Builder to) {
|
||||||
|
from.getValue(property).ifPresent(value -> to.setValue(property, value));
|
||||||
|
}
|
||||||
|
|
||||||
public ReplayModSimplePathing getMod() {
|
public ReplayModSimplePathing getMod() {
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
@@ -522,6 +568,10 @@ public class GuiPathing {
|
|||||||
return entityTracker;
|
return entityTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void openEditKeyframePopup(Keyframe keyframe) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private class LoadEntityTrackerPopup extends AbstractGuiPopup<LoadEntityTrackerPopup> {
|
private class LoadEntityTrackerPopup extends AbstractGuiPopup<LoadEntityTrackerPopup> {
|
||||||
private final GuiProgressBar progressBar = new GuiProgressBar(popup).setSize(300, 20)
|
private final GuiProgressBar progressBar = new GuiProgressBar(popup).setSize(300, 20)
|
||||||
.setI18nLabel("replaymod.gui.loadentitytracker");
|
.setI18nLabel("replaymod.gui.loadentitytracker");
|
||||||
|
|||||||
Reference in New Issue
Block a user