Add common pathing code and SimplePathing module
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package com.replaymod.simplepathing;
|
||||
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.pathing.PathingRegistry;
|
||||
import com.replaymod.pathing.impl.TimelineImpl;
|
||||
import com.replaymod.pathing.interpolation.AbstractInterpolator;
|
||||
import com.replaymod.pathing.interpolation.Interpolator;
|
||||
import com.replaymod.pathing.interpolation.LinearInterpolator;
|
||||
import com.replaymod.pathing.path.Keyframe;
|
||||
import com.replaymod.pathing.path.Timeline;
|
||||
import com.replaymod.pathing.properties.CameraProperties;
|
||||
import com.replaymod.pathing.properties.TimestampProperty;
|
||||
import com.replaymod.replay.events.ReplayOpenEvent;
|
||||
import com.replaymod.simplepathing.gui.GuiPathing;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Mod(modid = ReplayModSimplePathing.MOD_ID, useMetadata = true)
|
||||
public class ReplayModSimplePathing implements PathingRegistry {
|
||||
public static final String MOD_ID = "replaymod-simplepathing";
|
||||
|
||||
@Mod.Instance(ReplayMod.MOD_ID)
|
||||
private static ReplayMod core;
|
||||
|
||||
private Logger logger;
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
logger = event.getModLog();
|
||||
|
||||
core.getSettingsRegistry().register(Setting.class);
|
||||
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void postReplayOpen(ReplayOpenEvent.Post event) {
|
||||
new GuiPathing(core, this, event.getReplayHandler());
|
||||
}
|
||||
|
||||
private Timeline currentTimeline = createTimeline(); { currentTimeline.createPath(); }
|
||||
private Keyframe selectedKeyframe;
|
||||
|
||||
public Keyframe getSelectedKeyframe() {
|
||||
return selectedKeyframe;
|
||||
}
|
||||
|
||||
public void setSelectedKeyframe(Keyframe selectedKeyframe) {
|
||||
this.selectedKeyframe = selectedKeyframe;
|
||||
}
|
||||
|
||||
public void setCurrentTimeline(Timeline currentTimeline) {
|
||||
this.currentTimeline = currentTimeline;
|
||||
}
|
||||
|
||||
public Timeline getCurrentTimeline() {
|
||||
return currentTimeline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timeline createTimeline() {
|
||||
Timeline timeline = new TimelineImpl();
|
||||
|
||||
timeline.registerProperty(TimestampProperty.PROPERTY);
|
||||
timeline.registerProperty(CameraProperties.POSITION);
|
||||
timeline.registerProperty(CameraProperties.ROTATION);
|
||||
|
||||
return timeline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeInterpolator(JsonWriter writer, Interpolator interpolator) throws IOException {
|
||||
if (interpolator instanceof LinearInterpolator) {
|
||||
writer.value("linear");
|
||||
} else {
|
||||
throw new IOException("Unknown interpolator type: " + interpolator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interpolator deserializeInterpolator(JsonReader reader) throws IOException {
|
||||
AbstractInterpolator interpolator;
|
||||
String type = reader.nextString();
|
||||
switch (type) {
|
||||
case "linear":
|
||||
interpolator = new LinearInterpolator();
|
||||
interpolator.registerProperty(TimestampProperty.PROPERTY);
|
||||
interpolator.registerProperty(CameraProperties.POSITION);
|
||||
interpolator.registerProperty(CameraProperties.ROTATION);
|
||||
return interpolator;
|
||||
default:
|
||||
throw new IOException("Unknown interpolation type: " + type);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/main/java/com/replaymod/simplepathing/Setting.java
Normal file
17
src/main/java/com/replaymod/simplepathing/Setting.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.replaymod.simplepathing;
|
||||
|
||||
import com.replaymod.core.SettingsRegistry;
|
||||
|
||||
public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
|
||||
// public static final Setting<Boolean> RECORD_SINGLEPLAYER = make("recordSingleplayer", "recordsingleplayer", true);
|
||||
// public static final Setting<Boolean> RECORD_SERVER = make("recordServer", "recordserver", true);
|
||||
// public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true);
|
||||
|
||||
private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
|
||||
return new Setting<>(key, displayName, defaultValue);
|
||||
}
|
||||
|
||||
public Setting(String key, String displayString, T defaultValue) {
|
||||
super("simplepathing", key, "replaymod.gui.settings." + displayString, defaultValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.replaymod.simplepathing.gui;
|
||||
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.pathing.path.Keyframe;
|
||||
import com.replaymod.pathing.properties.CameraProperties;
|
||||
import com.replaymod.pathing.properties.TimestampProperty;
|
||||
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
||||
import de.johni0702.minecraft.gui.GuiRenderer;
|
||||
import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
|
||||
public class GuiKeyframeTimeline extends AbstractGuiTimeline<GuiKeyframeTimeline> {
|
||||
protected static final int KEYFRAME_SIZE = 5;
|
||||
protected static final int KEYFRAME_TEXTURE_X = 74;
|
||||
protected static final int KEYFRAME_TEXTURE_Y = 20;
|
||||
|
||||
private final GuiPathing gui;
|
||||
|
||||
public GuiKeyframeTimeline(GuiPathing gui) {
|
||||
this.gui = gui;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawTimelineCursor(GuiRenderer renderer, ReadableDimension size) {
|
||||
ReplayModSimplePathing mod = gui.getMod();
|
||||
|
||||
int width = size.getWidth();
|
||||
int visibleWidth = width - BORDER_LEFT - BORDER_RIGHT;
|
||||
int startTime = getOffset();
|
||||
int visibleTime = (int) (getZoom() * getLength());
|
||||
int endTime = getOffset() + visibleTime;
|
||||
|
||||
renderer.bindTexture(ReplayMod.TEXTURE);
|
||||
|
||||
for (Keyframe keyframe : mod.getCurrentTimeline().getPaths().get(0).getKeyframes()) {
|
||||
if (keyframe.getTime() >= startTime && keyframe.getTime() <= endTime) {
|
||||
double relativeTime = keyframe.getTime() - startTime;
|
||||
int positonX = BORDER_LEFT + (int) (relativeTime / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2;
|
||||
int u = KEYFRAME_TEXTURE_X + (mod.getSelectedKeyframe() == keyframe ? KEYFRAME_SIZE : 0);
|
||||
int v = KEYFRAME_TEXTURE_Y;
|
||||
if (keyframe.getValue(CameraProperties.POSITION).isPresent()) {
|
||||
renderer.drawTexturedRect(positonX, BORDER_TOP, u, v, KEYFRAME_SIZE, KEYFRAME_SIZE);
|
||||
}
|
||||
if (keyframe.getValue(TimestampProperty.PROPERTY).isPresent()) {
|
||||
v += KEYFRAME_SIZE;
|
||||
renderer.drawTexturedRect(positonX, BORDER_TOP + KEYFRAME_SIZE, u, v, KEYFRAME_SIZE, KEYFRAME_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.drawTimelineCursor(renderer, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuiKeyframeTimeline getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
346
src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java
Normal file
346
src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java
Normal file
@@ -0,0 +1,346 @@
|
||||
package com.replaymod.simplepathing.gui;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.pathing.change.AddKeyframe;
|
||||
import com.replaymod.pathing.change.CombinedChange;
|
||||
import com.replaymod.pathing.change.UpdateKeyframeProperties;
|
||||
import com.replaymod.pathing.gui.GuiKeyframeRepository;
|
||||
import com.replaymod.pathing.interpolation.LinearInterpolator;
|
||||
import com.replaymod.pathing.path.Keyframe;
|
||||
import com.replaymod.pathing.path.Path;
|
||||
import com.replaymod.pathing.path.PathSegment;
|
||||
import com.replaymod.pathing.path.Timeline;
|
||||
import com.replaymod.pathing.player.RealtimeTimelinePlayer;
|
||||
import com.replaymod.pathing.properties.CameraProperties;
|
||||
import com.replaymod.pathing.properties.TimestampProperty;
|
||||
import com.replaymod.replay.ReplayHandler;
|
||||
import com.replaymod.replay.camera.CameraEntity;
|
||||
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
|
||||
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
||||
import de.johni0702.minecraft.gui.GuiRenderer;
|
||||
import de.johni0702.minecraft.gui.RenderInfo;
|
||||
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||
import de.johni0702.minecraft.gui.element.*;
|
||||
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.HorizontalLayout;
|
||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.util.Dimension;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
import org.lwjgl.util.ReadablePoint;
|
||||
import org.lwjgl.util.WritablePoint;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Gui plug-in to the GuiReplayOverlay for simple pathing.
|
||||
*/
|
||||
public class GuiPathing {
|
||||
public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20)
|
||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
||||
|
||||
public final GuiTexturedButton positionKeyframeButton = new GuiTexturedButton().setSize(20, 20)
|
||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
||||
|
||||
public final GuiTexturedButton timeKeyframeButton = new GuiTexturedButton().setSize(20, 20)
|
||||
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE);
|
||||
|
||||
public final GuiKeyframeTimeline timeline = new GuiKeyframeTimeline(this){
|
||||
@Override
|
||||
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
|
||||
if (player.isActive()) {
|
||||
setCursorPosition((int) player.getTimePassed());
|
||||
}
|
||||
super.draw(renderer, size, renderInfo);
|
||||
}
|
||||
}.onClick(new IGuiTimeline.OnClick() {
|
||||
@Override
|
||||
public void run(int time) {
|
||||
timeline.setCursorPosition(time);
|
||||
// TODO: Keyframe selection
|
||||
mod.setSelectedKeyframe(null);
|
||||
}
|
||||
}).setSize(Integer.MAX_VALUE, 20).setLength(30 * 60 * 1000).setMarkers();
|
||||
|
||||
public final GuiHorizontalScrollbar scrollbar = new GuiHorizontalScrollbar().setSize(Integer.MAX_VALUE, 9);
|
||||
{scrollbar.onValueChanged(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
timeline.setOffset((int) (scrollbar.getPosition() * timeline.getLength()));
|
||||
timeline.setZoom(scrollbar.getZoom());
|
||||
}
|
||||
}).setZoom(0.1);}
|
||||
|
||||
public final GuiTimelineTime<GuiKeyframeTimeline> timelineTime = new GuiTimelineTime<GuiKeyframeTimeline>()
|
||||
.setTimeline(timeline);
|
||||
|
||||
public final GuiTexturedButton zoomInButton = new GuiTexturedButton().setSize(9, 9).onClick(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
zoomTimeline(2d / 3d);
|
||||
}
|
||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20);
|
||||
|
||||
public final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9).onClick(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
zoomTimeline(3d / 2d);
|
||||
}
|
||||
}).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30);
|
||||
|
||||
public final GuiPanel zoomButtonPanel = new GuiPanel()
|
||||
.setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2))
|
||||
.addElements(null, zoomInButton, zoomOutButton);
|
||||
|
||||
public final GuiPanel timelinePanel = new GuiPanel().setSize(Integer.MAX_VALUE, 40)
|
||||
.setLayout(new CustomLayout<GuiPanel>() {
|
||||
@Override
|
||||
protected void layout(GuiPanel container, int width, int height) {
|
||||
pos(zoomButtonPanel, width - width(zoomButtonPanel), 10);
|
||||
pos(timelineTime, 0, 2);
|
||||
size(timelineTime, x(zoomButtonPanel), 8);
|
||||
pos(timeline, 0, y(timelineTime) + height(timelineTime));
|
||||
size(timeline, x(zoomButtonPanel) - 2, 20);
|
||||
pos(scrollbar, 0, y(timeline) + height(timeline) + 1);
|
||||
size(scrollbar, x(zoomButtonPanel) - 2, 9);
|
||||
}
|
||||
}).addElements(null, timelineTime, timeline, scrollbar, zoomButtonPanel);
|
||||
|
||||
public final GuiPanel panel = new GuiPanel()
|
||||
.setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(5))
|
||||
.addElements(new HorizontalLayout.Data(0.5), playPauseButton, positionKeyframeButton, timeKeyframeButton,
|
||||
timelinePanel);
|
||||
|
||||
/**
|
||||
* IGuiClickable dummy component that is inserted at a high level.
|
||||
* During path playback, this catches all click events and forwards them to the
|
||||
* abort path playback button.
|
||||
* Dragging does not have to be intercepted as every GUI element should only
|
||||
* respond to dragging events after it has received and handled a click event.
|
||||
*/
|
||||
private final IGuiClickable clickCatcher = new AbstractGuiClickable() {
|
||||
@Override
|
||||
protected AbstractGuiElement getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReadableDimension calcMinSize() {
|
||||
return new Dimension(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClick(ReadablePoint position, int button) {
|
||||
if (player.isActive()) {
|
||||
playPauseButton.mouseClick(position, button);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayer() {
|
||||
return player.isActive() ? 10 : 0;
|
||||
}
|
||||
};
|
||||
|
||||
private final ReplayModSimplePathing mod;
|
||||
private final ReplayHandler replayHandler;
|
||||
private final RealtimeTimelinePlayer player;
|
||||
|
||||
public GuiPathing(final ReplayMod core, final ReplayModSimplePathing mod, final ReplayHandler replayHandler) {
|
||||
this.mod = mod;
|
||||
this.replayHandler = replayHandler;
|
||||
this.player = new RealtimeTimelinePlayer(replayHandler);
|
||||
final GuiReplayOverlay overlay = replayHandler.getOverlay();
|
||||
|
||||
playPauseButton.setTexturePosH(new ReadablePoint() {
|
||||
@Override
|
||||
public int getX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
return player.isActive() ? 20 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocation(WritablePoint dest) {
|
||||
dest.setLocation(getX(), getY());
|
||||
}
|
||||
}).onClick(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (player.isActive()) {
|
||||
player.getFuture().cancel(false);
|
||||
} else {
|
||||
Timeline timeline = mod.getCurrentTimeline();
|
||||
Path path = timeline.getPaths().get(0);
|
||||
|
||||
// TODO Change interpolator via Change class
|
||||
LinearInterpolator interpolator = new LinearInterpolator();
|
||||
interpolator.registerProperty(TimestampProperty.PROPERTY);
|
||||
interpolator.registerProperty(CameraProperties.POSITION);
|
||||
interpolator.registerProperty(CameraProperties.ROTATION);
|
||||
for (PathSegment segment : path.getSegments()) {
|
||||
segment.setInterpolator(interpolator);
|
||||
}
|
||||
path.updateAll();
|
||||
|
||||
player.start(timeline);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
positionKeyframeButton.setTexturePosH(new ReadablePoint() {
|
||||
@Override
|
||||
public int getX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
Keyframe keyframe = mod.getSelectedKeyframe();
|
||||
boolean present = keyframe != null
|
||||
&& keyframe.getValue(CameraProperties.POSITION).isPresent();
|
||||
return present ? 60 : 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocation(WritablePoint dest) {
|
||||
dest.setLocation(getX(), getY());
|
||||
}
|
||||
}).onClick(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateKeyframe(false);
|
||||
}
|
||||
});
|
||||
|
||||
timeKeyframeButton.setTexturePosH(new ReadablePoint() {
|
||||
@Override
|
||||
public int getX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY() {
|
||||
Keyframe keyframe = mod.getSelectedKeyframe();
|
||||
boolean present = keyframe != null
|
||||
&& keyframe.getValue(TimestampProperty.PROPERTY).isPresent();
|
||||
return present ? 100 : 80;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocation(WritablePoint dest) {
|
||||
dest.setLocation(getX(), getY());
|
||||
}
|
||||
}).onClick(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateKeyframe(true);
|
||||
}
|
||||
});
|
||||
|
||||
overlay.addElements(null, panel, clickCatcher);
|
||||
overlay.setLayout(new CustomLayout<GuiReplayOverlay>(overlay.getLayout()) {
|
||||
@Override
|
||||
protected void layout(GuiReplayOverlay container, int width, int height) {
|
||||
pos(panel, 10, y(overlay.topPanel) + height(overlay.topPanel) + 3);
|
||||
size(panel, width - 20, 40);
|
||||
size(clickCatcher, 0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.keyframerepository", Keyboard.KEY_X, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!overlay.isVisible()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
GuiKeyframeRepository gui = new GuiKeyframeRepository(
|
||||
mod, replayHandler.getReplayFile(), mod.getCurrentTimeline());
|
||||
Futures.addCallback(gui.getFuture(), new FutureCallback<Timeline>() {
|
||||
@Override
|
||||
public void onSuccess(Timeline result) {
|
||||
if (result != null) {
|
||||
mod.setCurrentTimeline(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
t.printStackTrace();
|
||||
core.printWarningToChat("Error loading timeline: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
gui.display();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
core.printWarningToChat("Error loading timeline: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void zoomTimeline(double factor) {
|
||||
scrollbar.setZoom(scrollbar.getZoom() * factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when either one of the property buttons is pressed.
|
||||
* @param isTime {@code true} for the time property button, {@code false} for the place property button
|
||||
*/
|
||||
private void updateKeyframe(final boolean isTime) {
|
||||
int time = timeline.getCursorPosition();
|
||||
Timeline timeline = mod.getCurrentTimeline();
|
||||
Path path = timeline.getPaths().get(0);
|
||||
|
||||
AddKeyframe addChange = null;
|
||||
Keyframe keyframe = path.getKeyframe(time);
|
||||
if (keyframe == null) {
|
||||
addChange = AddKeyframe.create(path, time);
|
||||
addChange.apply(timeline);
|
||||
keyframe = path.getKeyframe(time);
|
||||
}
|
||||
|
||||
UpdateKeyframeProperties.Builder builder = UpdateKeyframeProperties.create(path, keyframe);
|
||||
if (isTime) {
|
||||
if (keyframe.getValue(TimestampProperty.PROPERTY).isPresent()) {
|
||||
builder.removeProperty(TimestampProperty.PROPERTY);
|
||||
} else {
|
||||
builder.setValue(TimestampProperty.PROPERTY, replayHandler.getReplaySender().currentTimeStamp());
|
||||
}
|
||||
} else {
|
||||
if (keyframe.getValue(CameraProperties.POSITION).isPresent()) {
|
||||
builder.removeProperty(CameraProperties.POSITION);
|
||||
builder.removeProperty(CameraProperties.ROTATION);
|
||||
} else {
|
||||
CameraEntity camera = replayHandler.getCameraEntity();
|
||||
builder.setValue(CameraProperties.POSITION, Triple.of(camera.posX, camera.posY, camera.posZ));
|
||||
builder.setValue(CameraProperties.ROTATION, Triple.of(camera.rotationYaw, camera.rotationPitch, camera.roll));
|
||||
}
|
||||
}
|
||||
UpdateKeyframeProperties updateChange = builder.done();
|
||||
updateChange.apply(timeline);
|
||||
if (addChange == null) {
|
||||
timeline.pushChange(updateChange);
|
||||
} else {
|
||||
timeline.pushChange(CombinedChange.createFromApplied(addChange, updateChange));
|
||||
}
|
||||
|
||||
mod.setSelectedKeyframe(keyframe);
|
||||
}
|
||||
|
||||
public ReplayModSimplePathing getMod() {
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user