camera update and task clearing
This commit is contained in:
1 parent
62529a352c
commit
a4e6e7cb10
14 files changed
+545
-336
No files matched your search
@@ -26,26 +26,20 @@ public abstract class ContainerMixin
|
||||
@Shadow public abstract void sendContentUpdates();
|
||||
|
||||
@Inject( method = "onSlotClick", at = @At(value = "HEAD"), cancellable = true)
|
||||
private void onThrowClick(
|
||||
int int_1,
|
||||
int int_2,
|
||||
SlotActionType slotActionType_1,
|
||||
PlayerEntity playerEntity_1,
|
||||
CallbackInfoReturnable<ItemStack> cir
|
||||
)
|
||||
private void onThrowClick( int slotId, int clickData, SlotActionType actionType, PlayerEntity playerEntity, CallbackInfoReturnable<ItemStack> cir)
|
||||
{
|
||||
if (slotActionType_1 == SlotActionType.THROW && CarpetSettings.ctrlQCraftingFix && playerEntity_1.inventory.getCursorStack().isEmpty() && int_1 >= 0)
|
||||
if (actionType == SlotActionType.THROW && CarpetSettings.ctrlQCraftingFix && playerEntity.inventory.getCursorStack().isEmpty() && slotId >= 0)
|
||||
{
|
||||
ItemStack itemStack_1 = ItemStack.EMPTY;
|
||||
Slot slot_4 = slotList.get(int_1);
|
||||
if (slot_4 != null && slot_4.hasStack() && slot_4.canTakeItems(playerEntity_1))
|
||||
Slot slot_4 = slotList.get(slotId);
|
||||
if (slot_4 != null && slot_4.hasStack() && slot_4.canTakeItems(playerEntity))
|
||||
{
|
||||
if(int_1 == 0 && int_2 == 1)
|
||||
if(slotId == 0 && clickData == 1)
|
||||
{
|
||||
Item craftedItem = slot_4.getStack().getItem();
|
||||
while(slot_4.hasStack() && slot_4.getStack().getItem() == craftedItem)
|
||||
{
|
||||
this.onSlotClick(int_1, 0, SlotActionType.THROW, playerEntity_1);
|
||||
this.onSlotClick(slotId, 0, SlotActionType.THROW, playerEntity);
|
||||
}
|
||||
this.sendContentUpdates();
|
||||
cir.setReturnValue(itemStack_1);
|
||||
|
||||
@@ -4,14 +4,18 @@ import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.stat.Stat;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM;
|
||||
import static carpet.script.CarpetEventServer.Event.STATISTICS;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
|
||||
@@ -43,4 +47,10 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
|
||||
super.method_6040();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "increaseStat", at = @At("HEAD"))
|
||||
private void grabStat(Stat<?> stat, int amount, CallbackInfo ci)
|
||||
{
|
||||
STATISTICS.onPlayerStatistic((ServerPlayerEntity) (Object)this, stat, amount);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import carpet.script.value.BlockValue;
|
||||
import carpet.script.value.EntityValue;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NBTSerializableValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
@@ -13,11 +14,15 @@ import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.stat.Stat;
|
||||
import net.minecraft.stat.Stats;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@@ -25,10 +30,12 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -403,7 +410,32 @@ public class CarpetEventServer
|
||||
{
|
||||
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
|
||||
}
|
||||
};
|
||||
},
|
||||
STATISTICS("statistic", new CallbackList(4))
|
||||
{
|
||||
private <T> Identifier getStatId(Stat<T> stat)
|
||||
{
|
||||
return stat.getType().getRegistry().getId(stat.getValue());
|
||||
}
|
||||
private Set<Identifier> skippedStats = new HashSet<Identifier>(){{
|
||||
add(Stats.TIME_SINCE_DEATH);
|
||||
add(Stats.TIME_SINCE_REST);
|
||||
add(Stats.PLAY_ONE_MINUTE);
|
||||
}};
|
||||
@Override
|
||||
public void onPlayerStatistic(ServerPlayerEntity player, Stat<?> stat, int amount)
|
||||
{
|
||||
Identifier id = getStatId(stat);
|
||||
if (skippedStats.contains(id)) return;
|
||||
handler.call( () -> Arrays.asList(
|
||||
((c, t) -> new EntityValue(player)),
|
||||
((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.STAT_TYPE.getId(stat.getType())))),
|
||||
((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(id))),
|
||||
((c, t) -> new NumericValue(amount))
|
||||
), player::getCommandSource);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
// on projectile thrown (arrow from bows, crossbows, tridents, snoballs, e-pearls
|
||||
|
||||
@@ -427,6 +459,7 @@ public class CarpetEventServer
|
||||
//stubs for calls just to ease calls in vanilla code so they don't need to deal with scarpet value types
|
||||
public void onTick() { }
|
||||
public void onPlayerEvent(ServerPlayerEntity player) { }
|
||||
public void onPlayerStatistic(ServerPlayerEntity player, Stat<?> stat, int amount) { }
|
||||
public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { }
|
||||
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { }
|
||||
public void onBlockAction(ServerPlayerEntity player, BlockPos blockpos, Direction facing) { }
|
||||
|
||||
@@ -3157,7 +3157,7 @@ public class CarpetExpression
|
||||
String file = null;
|
||||
if (lv.size()>1)
|
||||
{
|
||||
String origfile = lv.get(0).evalValue(c).getString();
|
||||
String origfile = lv.get(1).evalValue(c).getString();
|
||||
file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9]", "");
|
||||
if (file.isEmpty())
|
||||
{
|
||||
|
||||
@@ -20,6 +20,7 @@ import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -217,6 +218,35 @@ public class CarpetScriptHost extends ScriptHost
|
||||
super.onClose();
|
||||
if (this.saveTimeout > 0)
|
||||
dumpState();
|
||||
|
||||
FunctionValue closing = globalFunctions.get("__on_close");
|
||||
if (closing != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
callUDF(BlockPos.ORIGIN, CarpetServer.minecraft_server.getCommandSource(), closing, Collections.emptyList());
|
||||
}
|
||||
catch (InvalidCallbackException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
userHosts.forEach((key, value) ->
|
||||
{
|
||||
FunctionValue userClosing = value.globalFunctions.get("__on_close");
|
||||
if (userClosing != null)
|
||||
{
|
||||
ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key);
|
||||
ServerCommandSource source = (player != null)?player.getCommandSource():CarpetServer.minecraft_server.getCommandSource();
|
||||
try
|
||||
{
|
||||
((CarpetScriptHost) value).callUDF(BlockPos.ORIGIN, source, userClosing, Collections.emptyList());
|
||||
}
|
||||
catch (InvalidCallbackException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
String markerName = ExpressionInspector.MARKER_STRING+"_"+((getName()==null)?"":getName());
|
||||
for (ServerWorld world : CarpetServer.minecraft_server.getWorlds())
|
||||
{
|
||||
@@ -225,6 +255,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
e.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void dumpState()
|
||||
@@ -239,9 +270,9 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
public Tag getGlobalState(String file)
|
||||
{
|
||||
if (getName() == null || myCode.isInternal()) return null;
|
||||
if (getName() == null ) return null;
|
||||
if (file != null)
|
||||
myCode.getData(file);
|
||||
return myCode.getData(file);
|
||||
if (parent == null)
|
||||
return globalState;
|
||||
return ((CarpetScriptHost)parent).globalState;
|
||||
@@ -249,7 +280,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
public void setGlobalState(Tag tag, String file)
|
||||
{
|
||||
if (getName() == null || myCode.isInternal()) return;
|
||||
if (getName() == null ) return;
|
||||
|
||||
if (file!= null)
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ public class CarpetScriptServer
|
||||
private Set<String> holyMoly;
|
||||
public CarpetEventServer events;
|
||||
|
||||
public static List<ModuleInterface> bundledModuleData = new ArrayList<ModuleInterface>(){{
|
||||
private static List<ModuleInterface> bundledModuleData = new ArrayList<ModuleInterface>(){{
|
||||
add(new BundledModule("camera"));
|
||||
add(new BundledModule("event_test"));
|
||||
}};
|
||||
@@ -72,7 +72,7 @@ public class CarpetScriptServer
|
||||
|
||||
}
|
||||
|
||||
ModuleInterface getModule(String name)
|
||||
private ModuleInterface getModule(String name)
|
||||
{
|
||||
File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts");
|
||||
@@ -218,25 +218,6 @@ public class CarpetScriptServer
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private void addEvents(ServerCommandSource source, String hostName)
|
||||
{
|
||||
if (1+2 == 3) throw new RuntimeException("This should run when code is evaluated");
|
||||
ScriptHost host = modules.get(hostName);
|
||||
if (host == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (String fun : host.globalFunctions.keySet())
|
||||
{
|
||||
if (!fun.startsWith("__on_"))
|
||||
continue;
|
||||
String event = fun.replaceFirst("__on_","");
|
||||
if (!CarpetEventServer.Event.byName.containsKey(event))
|
||||
continue;
|
||||
events.addEvent(event, hostName, fun);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addCommand(ServerCommandSource source, String hostName)
|
||||
{
|
||||
@@ -261,7 +242,9 @@ public class CarpetScriptServer
|
||||
requires((player) -> modules.containsKey(hostName)).
|
||||
executes( (c) ->
|
||||
{
|
||||
Messenger.m(c.getSource(), "gi "+modules.get(hostName).retrieveForExecution(c.getSource()).call(c.getSource(),"__command", null, ""));
|
||||
String response = modules.get(hostName).retrieveForExecution(c.getSource()).
|
||||
call(c.getSource(),"__command", null, "");
|
||||
if (!response.isEmpty()) Messenger.m(c.getSource(), "gi "+response);
|
||||
return 1;
|
||||
});
|
||||
|
||||
@@ -271,28 +254,16 @@ public class CarpetScriptServer
|
||||
then(literal(function).
|
||||
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getPublicFunctions().contains(function)).
|
||||
executes( (c) -> {
|
||||
Messenger.m(
|
||||
c.getSource(),
|
||||
"gi "+modules.get(hostName).retrieveForExecution(c.getSource()).call(
|
||||
c.getSource(),
|
||||
function,
|
||||
null,
|
||||
""
|
||||
)
|
||||
);
|
||||
String response = modules.get(hostName).retrieveForExecution(c.getSource()).
|
||||
call(c.getSource(), function,null,"");
|
||||
if (!response.isEmpty()) Messenger.m(c.getSource(),"gi "+response);
|
||||
return 1;
|
||||
}).
|
||||
then(argument("args...", StringArgumentType.greedyString()).
|
||||
executes( (c) -> {
|
||||
Messenger.m(
|
||||
c.getSource(),
|
||||
"gi "+modules.get(hostName).retrieveForExecution(c.getSource()).call(
|
||||
c.getSource(),
|
||||
function,
|
||||
null,
|
||||
StringArgumentType.getString(c, "args...")
|
||||
)
|
||||
);
|
||||
String response = modules.get(hostName).retrieveForExecution(c.getSource()).
|
||||
call(c.getSource(), function,null, StringArgumentType.getString(c, "args..."));
|
||||
if (!response.isEmpty()) Messenger.m(c.getSource(), "gi "+response);
|
||||
return 1;
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -3002,22 +3002,9 @@ public class Expression implements Cloneable
|
||||
});
|
||||
|
||||
addLazyFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) -> {
|
||||
Value queue = Value.NULL;
|
||||
Value funcDesc;
|
||||
if (lv.size()==1)
|
||||
{
|
||||
funcDesc = lv.get(0).evalValue(c);
|
||||
}
|
||||
else if (lv.size() == 2)
|
||||
{
|
||||
queue = lv.get(0).evalValue(c);
|
||||
funcDesc = lv.get(1).evalValue(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InternalExpressionException("'task' takes one or two parameters");
|
||||
}
|
||||
FunctionValue fun;
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
|
||||
Value funcDesc = lv.get(0).evalValue(c);
|
||||
if (!(funcDesc instanceof FunctionValue))
|
||||
{
|
||||
String name = funcDesc.getString();
|
||||
@@ -3025,10 +3012,20 @@ public class Expression implements Cloneable
|
||||
if (funcDesc == null)
|
||||
throw new InternalExpressionException("Function "+name+" is not defined yet");
|
||||
}
|
||||
fun = (FunctionValue)funcDesc;
|
||||
if (fun.getArguments().size() > 0)
|
||||
throw new InternalExpressionException("tasks can only accept functions that don't take any arguments");
|
||||
ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c);
|
||||
FunctionValue fun = (FunctionValue)funcDesc;
|
||||
int extraargs = lv.size() - fun.getArguments().size();
|
||||
if (extraargs != 1 && extraargs != 2)
|
||||
{
|
||||
throw new InternalExpressionException("Function takes "+fun.getArguments().size()+" arguments.");
|
||||
}
|
||||
List<LazyValue> lvargs = new ArrayList<>();
|
||||
for (int i=0; i< fun.getArguments().size(); i++)
|
||||
{
|
||||
lvargs.add(lv.get(i+1));
|
||||
}
|
||||
Value queue = Value.NULL;
|
||||
if (extraargs == 2) queue = lv.get(lv.size()-1).evalValue(c);
|
||||
ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c, lvargs);
|
||||
Thread.yield();
|
||||
return (cc, tt) -> thread;
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class ScriptHost
|
||||
{
|
||||
private final Map<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>();
|
||||
protected final Map<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class FileModule implements ModuleInterface
|
||||
}
|
||||
|
||||
//copied private method from net.minecraft.nbt.NbtIo.read()
|
||||
private static Tag read(File file)
|
||||
public static Tag read(File file)
|
||||
{
|
||||
try (DataInputStream dataInput_1 = new DataInputStream(new FileInputStream(file)))
|
||||
{
|
||||
@@ -80,7 +80,7 @@ public class FileModule implements ModuleInterface
|
||||
}
|
||||
|
||||
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
|
||||
private static void write(Tag tag_1, File file)
|
||||
public static void write(Tag tag_1, File file)
|
||||
{
|
||||
File file_2 = new File(file.getAbsolutePath() + "_tmp");
|
||||
if (file_2.exists()) file_2.delete();
|
||||
@@ -101,23 +101,4 @@ public class FileModule implements ModuleInterface
|
||||
if (file.exists()) file.delete();
|
||||
if (!file.exists()) file_2.renameTo(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag getData(String file)
|
||||
{
|
||||
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+name+".data"+(file==null?"":"."+file)+".nbt");
|
||||
if (!(dataFile.isFile()))
|
||||
return null;
|
||||
return read(dataFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData(String file, Tag globalState)
|
||||
{
|
||||
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+name+".data"+(file==null?"":"."+file)+".nbt");
|
||||
write(globalState, dataFile);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,44 @@
|
||||
package carpet.script.bundled;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import static carpet.script.bundled.FileModule.read;
|
||||
import static carpet.script.bundled.FileModule.write;
|
||||
|
||||
public interface ModuleInterface
|
||||
{
|
||||
String getName();
|
||||
String getCode();
|
||||
default boolean isInternal() {return true; }
|
||||
default Tag getData(String file) {return null; }
|
||||
default void saveData(String file, Tag globalState) {}
|
||||
|
||||
default Tag getData(String file)
|
||||
{
|
||||
File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
|
||||
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
|
||||
return read(dataFile);
|
||||
}
|
||||
|
||||
default void saveData(String file, Tag globalState)
|
||||
{
|
||||
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
|
||||
if (!Files.exists(dataFile.toPath().getParent()))
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createDirectory(dataFile.toPath().getParent());
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
write(globalState, dataFile);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@ package carpet.script.value;
|
||||
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.Tokenizer;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -17,18 +18,14 @@ public class ThreadValue extends Value
|
||||
{
|
||||
private CompletableFuture<Value> taskFuture;
|
||||
private long id;
|
||||
private final Object lock;
|
||||
private static long sequence = 0L;
|
||||
private static final Map<Value,ThreadPoolExecutor> executorServices = new HashMap<>();
|
||||
|
||||
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx)
|
||||
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx, List<LazyValue> args)
|
||||
{
|
||||
id = sequence++;
|
||||
lock = new Object();
|
||||
taskFuture = CompletableFuture.supplyAsync(
|
||||
() -> { synchronized (lock) {
|
||||
return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
|
||||
} },
|
||||
() -> function.lazyEval(ctx, Context.NONE, expr, token, args).evalValue(ctx),
|
||||
executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
|
||||
);
|
||||
Thread.yield();
|
||||
@@ -63,17 +60,6 @@ public class ThreadValue extends Value
|
||||
return taskFuture.isDone();
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
if (!taskFuture.isDone())
|
||||
{
|
||||
taskFuture.complete(Value.NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
|
||||
@@ -1,137 +1,210 @@
|
||||
__command() ->
|
||||
(
|
||||
print('camera_path module.');
|
||||
print('camera: scarpet app.');
|
||||
print('-------------------');
|
||||
print(' "/camera_path start" - set the starting point');
|
||||
print(' "/camera start" - set the starting point, resets the path');
|
||||
print('');
|
||||
print(' "/camera_path add <N>" - add another point <N> frames later');
|
||||
print(' "/camera add <N>" - add point to the end <N> seconds later');
|
||||
print(' "/camera prepend <N>" - prepend point <N> seconds before');
|
||||
print(' "/camera clear" - removes entire path');
|
||||
print(' "/camera select" - or punch a point, to select it');
|
||||
print(' "/camera move" - moves selected camera to current player location');
|
||||
print(' "/camera duration <X>" - resizes current selected path');
|
||||
print(' "/camera split_point>" - splits current selected path in half');
|
||||
print(' "/camera delete_point" - removes current keypoint');
|
||||
print(' "/camera trim_path" - removes all keypoints from selected');
|
||||
print('');
|
||||
print(' "/camera_path select_interpolation < linear | gauss_? >"');
|
||||
print(' Select interpolation between points:');
|
||||
print(' "/camera store <name>"');
|
||||
print(' "/camera load <name>"');
|
||||
print(' - stores and loads path from nbt files in /scripts world folder');
|
||||
print('');
|
||||
print(' "/camera interpolation < linear | gauss | cr | gauss_<number> >"');
|
||||
print(' Selects interpolation between points:');
|
||||
print(' - cr: Catmull-Rom interpolation (default).');
|
||||
print(' smooth path that goes through all points.');
|
||||
print(' - linear: straight paths between points.');
|
||||
print(' - gauss_auto: automatic smooth transitions.');
|
||||
print(' - gauss_<number>: custom fixed variance ');
|
||||
print(' (in points) for special effects.');
|
||||
print(' - gauss: automatic smooth transitions.');
|
||||
print(' - gauss_<N>: custom fixed variance ');
|
||||
print(' (in seconds) for special effects.');
|
||||
print(' use "_" for decimal separator, e.g. gauss_2_5');
|
||||
print(' gauss makes the smoothest path,');
|
||||
print(' but treating points as suggestions only');
|
||||
print('');
|
||||
print(' "/camera_path repeat <N> <last_delay>" - ');
|
||||
print(' "/camera repeat <N> <last_delay>" - ');
|
||||
print(' Repeat existing points configuration n-times');
|
||||
print(' using <last_delay> points to link path ends');
|
||||
print(' using <last_delay> seconds to link path ends');
|
||||
print('');
|
||||
print(' "/camera_path speed <factor> - ');
|
||||
print(' "/camera stretch <factor>" - ');
|
||||
print(' Change number of frames between points');
|
||||
print(' from 25 -> 4 times faster (less points),');
|
||||
print(' to 400 -> 4 times slower (more points)');
|
||||
print(' from 25 -> 4x faster, to 400 -> 4x slower,');
|
||||
print('');
|
||||
print(' "/camera_path play <fps>: run a path with a player');
|
||||
print(' <fps> needs to be multiples of 20, (20 tps)');
|
||||
print(' "/camera play : run a path with a player');
|
||||
print(' use "sneak" to stop it prematurely');
|
||||
print(' run /camera hide and F1 to clear the view');
|
||||
print('');
|
||||
print(' "/camera_path show": shows current path for a moment');
|
||||
print(' "/camera show": shows current path for a moment');
|
||||
print(' color of particles used is different for different players');
|
||||
print(' "/camera hide": hides path display');
|
||||
print('');
|
||||
print(' "/script in camera_path invoke _show_path_tick <particle> <ppt>":');
|
||||
print(' place this in a repeating commandblock');
|
||||
print(' to display path continuously');
|
||||
print(' with set particle and number of particles per tick');
|
||||
print(' example: \'dust 0.9 0.1 0.1 1\' 100 ');
|
||||
''
|
||||
);
|
||||
|
||||
// camera position for the player sticks out of their eyes
|
||||
__camera_position(player) -> (player~'location' + l(0, player~'eye_height',0,0,0));
|
||||
global_points = null;
|
||||
global_dimension = null;
|
||||
global_player = null;
|
||||
|
||||
__update() ->
|
||||
(
|
||||
undef('global_path_precalculated');
|
||||
global_needs_updating = true;
|
||||
);
|
||||
global_player_eye_offset = map(range(5), 0);
|
||||
|
||||
start() ->
|
||||
(
|
||||
p = player();
|
||||
global_points = l(l(__camera_position(p), 0,'sharp'));
|
||||
__update();
|
||||
show();
|
||||
str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z');
|
||||
);
|
||||
add(delay) ->
|
||||
(
|
||||
p = player();
|
||||
mode = 'smooth';
|
||||
//mode is currently unused, run_path does always sharp, gauss interpolator is always smooth
|
||||
// but this option could be used could be used at some point by more robust interpolators
|
||||
__add_path_segment(__camera_position(p), round(60*delay), mode);
|
||||
__print_path_size();
|
||||
);
|
||||
global_showing_path = false;
|
||||
global_playing_path = false;
|
||||
|
||||
prepend(delay) ->
|
||||
global_needs_updating = false;
|
||||
global_selected_point = null;
|
||||
global_markers = null;
|
||||
|
||||
global_particle_density = 100;
|
||||
|
||||
global_color_a = null;
|
||||
global_color_b = null;
|
||||
|
||||
global_path_precalculated = null;
|
||||
|
||||
// starts the path with current player location
|
||||
start() -> __start_with( _() -> l(l(__camera_position(), 0,'sharp')) );
|
||||
|
||||
// start path with customized initial points selection
|
||||
__start_with(points_supplier) ->
|
||||
(
|
||||
p = player();
|
||||
mode = 'smooth';
|
||||
__prepend_path_segment(__camera_position(p), round(60*delay), mode);
|
||||
__print_path_size();
|
||||
);
|
||||
global_player = str(p);
|
||||
global_player_eye_offset = l(0, p~'eye_height',0,0,0);
|
||||
global_dimension = p~'dimension';
|
||||
|
||||
__add_path_segment(vector, duration, mode) ->
|
||||
(
|
||||
if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
|
||||
if(!global_points, exit('Cannot add point to path that didn\'t started yet!'));
|
||||
l(v, end_time, m) = global_points:(-1);
|
||||
vector:(-2) = __adjusted_rot(v:(-2) , vector:(-2) );
|
||||
global_points += l(vector, end_time+duration, mode);
|
||||
__update();
|
||||
);
|
||||
code = abs(hash_code(str(p))-123456);
|
||||
global_color_a = str('dust %.1f %.1f %.1f 1',(code%10)/10,(((code/10)%10)/10), (((code/100)%10)/10) ); // 'dust 0.1 0.9 0.1 1',
|
||||
global_color_b = str('dust %.1f %.1f %.1f 1',(((code/1000)%10)/10),(((code/10000)%10)/10), (((code/100000)%10)/10) );// 'dust 0.6 0.6 0.6 1'
|
||||
|
||||
__prepend_path_segment(vector, duration, mode) ->
|
||||
(
|
||||
if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
|
||||
if(!global_points, exit('Cannot add point to path that didn\'t started yet!'));
|
||||
l(v, start_time, m) = global_points:(-1);
|
||||
vector:(-2) = __adjusted_rot(v:(-2), vector:(-2));
|
||||
new_points = l(l(vector, 0, mode));
|
||||
for (global_points,
|
||||
_:1 += duration;
|
||||
new_points += _;
|
||||
);
|
||||
global_points = new_points;
|
||||
global_points = call(points_supplier);
|
||||
__update();
|
||||
show();
|
||||
str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z');
|
||||
);
|
||||
|
||||
// adjusts current rotation so we don\'t spin around like crazy
|
||||
__adjusted_rot(previous_rot, current_rot) ->
|
||||
// gets current player controlling the path, or fails
|
||||
__get_player() ->
|
||||
(
|
||||
while( abs(previous_rot-current_rot) > 180, 1000,
|
||||
current_rot += if(previous_rot < current_rot, -360, 360)
|
||||
if (!global_player, exit('No player selected'));
|
||||
p = player(global_player);
|
||||
if (p == null, exit('Player logged out'));
|
||||
p;
|
||||
);
|
||||
|
||||
// clears current path
|
||||
clear() ->
|
||||
(
|
||||
global_points = l();
|
||||
global_dimension = null;
|
||||
global_player = null;
|
||||
global_showing_path = false;
|
||||
global_playing_path = false;
|
||||
__update();
|
||||
'Path cleared';
|
||||
);
|
||||
|
||||
// camera position for the player sticks out of their eyes
|
||||
__camera_position() -> (__get_player()~'location' + global_player_eye_offset);
|
||||
|
||||
// path changed, now notify all processes that they need to update;
|
||||
__update() ->
|
||||
(
|
||||
global_path_precalculated = null;
|
||||
global_playing_path = false;
|
||||
global_needs_updating = true;
|
||||
);
|
||||
|
||||
// ensures that the current execution context can modify the path
|
||||
__assert_can_modify_path() ->
|
||||
(
|
||||
if (!global_points, exit('Path is not setup for current player'));
|
||||
if (!global_showing_path, exit('Turn on path showing to edit key points'));
|
||||
if(!global_player || !global_dimension || __get_player()~'dimension' != global_dimension,
|
||||
exit('Player not in dimension'));
|
||||
);
|
||||
|
||||
// make sure selected point is correct
|
||||
__assert_point_selected(validator) ->
|
||||
(
|
||||
__assert_can_modify_path();
|
||||
if (!call(validator, global_selected_point), exit('No appropriate point selected'));
|
||||
);
|
||||
|
||||
//select a custom interpolation method
|
||||
interpolation(method) -> __interpolation(method, true);
|
||||
__interpolation(method, verbose) ->
|
||||
(
|
||||
__prepare_path_if_needed() -> __prepare_path_if_needed_generic();
|
||||
// each supported method needs to specify its __find_position_for_point to trace the path
|
||||
// accepting segment number, and position in the segment
|
||||
// or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
|
||||
global_interpolator = if (
|
||||
method == 'linear', '__interpolator_linear',
|
||||
method == 'cr', '__interpolator_cr',
|
||||
method == 'gauss', _(s, p) -> __interpolator_gauB(s, p, 0),
|
||||
method ~ '^gauss_',
|
||||
(
|
||||
type = method - 'gauss_';
|
||||
type = replace(type,'_','.');
|
||||
variance = round(60*number(type));
|
||||
_(s, p, outer(variance)) -> __interpolator_gauB(s, p, variance);
|
||||
),
|
||||
exit('Choose one of the following methods: linear, gauss, gauss_<deviation>, cr')
|
||||
);
|
||||
current_rot
|
||||
__update();
|
||||
if(verbose, 'Interpolation changed to '+method, '');
|
||||
);
|
||||
__interpolation('cr', false);
|
||||
|
||||
// adds a point to the end of the path with delay in seconds
|
||||
add(delay) ->
|
||||
(
|
||||
__assert_can_modify_path();
|
||||
//mode is currently unused, run_path does always sharp, gauss interpolator is always smooth
|
||||
// but this option could be used could be used at some point by more robust interpolators
|
||||
__add_path_segment(__camera_position(), round(60*delay), 'smooth', true);
|
||||
__update();
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
__print_path_size() ->
|
||||
// prepends the path with a new starting point, with a segment of specified delay
|
||||
prepend(delay) ->
|
||||
(
|
||||
if (!__assert_valid_for_motion(),
|
||||
print(str('%d points, %.1f secs', length(global_points), global_points:(-1):1/60));
|
||||
);
|
||||
'';
|
||||
__assert_can_modify_path();
|
||||
__add_path_segment(__camera_position(), round(60*delay), 'smooth', false);
|
||||
__update();
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
repeat(times, last_section_duration) ->
|
||||
// repeats existing points seveal times, using last section delay (seconds) to join points
|
||||
repeat(times, last_section_delay) ->
|
||||
(
|
||||
if (err = __assert_valid_for_motion(), exit(err));
|
||||
if (err = __is_not_valid_for_motion(), exit(err));
|
||||
positions = map(global_points, _:0);
|
||||
modes = map(global_points, _:(-1));
|
||||
durations = map(global_points, global_points:(_i+1):1 - _:1 );
|
||||
durations:(-1) = round(60*last_section_duration);
|
||||
loop(times,
|
||||
loop( length(positions),
|
||||
__add_path_segment(positions:_, durations:_, modes:_)
|
||||
__add_path_segment(positions:_, durations:_, modes:_, true)
|
||||
)
|
||||
);
|
||||
__update();
|
||||
__print_path_size();
|
||||
'';
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
//stretches or shrinks current path to X percent of what it was before
|
||||
stretch(percentage) ->
|
||||
(
|
||||
if (err = __assert_valid_for_motion(), exit(err));
|
||||
undef('global_path_precalculated');
|
||||
if (err = __is_not_valid_for_motion(), exit(err));
|
||||
if (percentage < 25 || percentage > 400,
|
||||
exit('path speed can only be speed, or slowed down 4 times. Re-call command for larger changes')
|
||||
);
|
||||
@@ -146,98 +219,106 @@ stretch(percentage) ->
|
||||
)
|
||||
);
|
||||
|
||||
select_interpolation(method) ->
|
||||
// moves current selected point to player location
|
||||
move() ->
|
||||
(
|
||||
__prepare_path_if_needed() -> __prepare_path_if_needed_generic();
|
||||
// each supported method needs to specify its __find_position_for_point to trace the path
|
||||
// accepting segment number, and position in the segment
|
||||
// or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
|
||||
global_interpolator = if (
|
||||
method == 'linear', '__interpolator_linear',
|
||||
method == 'cr', '__interpolator_cr',
|
||||
method == 'gauss', _(s, p) -> __interpolator_gauss(s, p, 0),
|
||||
method ~ '^gauss_',
|
||||
(
|
||||
type = method - 'gauss_';
|
||||
variance = number(type);
|
||||
_(s, p, outer(variance)) -> __interpolator_gauss(s, p, variance);
|
||||
),
|
||||
exit('Choose one of the following methods: linear, gauss, gauss_<deviation>, cr')
|
||||
);
|
||||
__update();
|
||||
'Ok'
|
||||
__assert_point_selected(_(p) -> p != null);
|
||||
global_points:global_selected_point:0 = __camera_position();
|
||||
__update();
|
||||
'';
|
||||
);
|
||||
select_interpolation('cr');
|
||||
|
||||
__assert_valid_for_motion() ->
|
||||
// chenges duration of the current selected segment to X seconds
|
||||
duration(amount) ->
|
||||
(
|
||||
if(!global_points, return('Path not defined yet'));
|
||||
if(length(global_points)<2, return('Path not complete - add more points'));
|
||||
null
|
||||
);
|
||||
__get_path_at(segment, start, index) ->
|
||||
(
|
||||
v = global_path_precalculated:(start+index);
|
||||
if(v == null,
|
||||
v = call(global_interpolator, segment, index);
|
||||
global_path_precalculated:(start+index) = v
|
||||
__assert_point_selected(_(p) -> p); // skips nulls and 0 - starting point
|
||||
duration = number(amount);
|
||||
new_ticks = round(duration * 60);
|
||||
if (new_ticks < 10, return());
|
||||
previous_ticks = global_points:global_selected_point:1-global_points:(global_selected_point-1):1;
|
||||
delta = new_ticks - previous_ticks;
|
||||
// adjust duration of all points after that.
|
||||
for (range(global_selected_point, length(global_points)),
|
||||
global_points:_:1 += delta;
|
||||
);
|
||||
v
|
||||
);
|
||||
__invalidate_points_cache() -> global_path_precalculated = map(range(global_points:(-1):1), null);
|
||||
|
||||
global_showing_path = false;
|
||||
global_playing_path = false;
|
||||
|
||||
hide() ->
|
||||
(
|
||||
if (global_showing_path,
|
||||
global_showing_path = false;
|
||||
print('Stopped showing path');
|
||||
);
|
||||
''
|
||||
__update();
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
clear() ->
|
||||
// deletes current keypoint without changing the path length
|
||||
delete_point() ->
|
||||
(
|
||||
global_points = l();
|
||||
__update();
|
||||
__assert_point_selected(_(p) -> p != null);
|
||||
if (length(global_points) < 2, clear(); return());
|
||||
if (global_selected_point == 0, global_points:1:1 = 0);
|
||||
global_points = filter(global_points, _i != global_selected_point);
|
||||
if (global_selected_point >= length(global_points), global_selected_point = null);
|
||||
__update();
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
global_needs_updating = false;
|
||||
global_selected_point = null;
|
||||
global_markers = null;
|
||||
|
||||
__distsq(vec1, vec2) -> reduce(vec1 - vec2, _a + _*_, 0);
|
||||
|
||||
__closest_point_to_center(center, points) ->
|
||||
// splits current selected segment in half by adding a keypoint in between
|
||||
split_point() ->
|
||||
(
|
||||
dd = __distsq(points:0, center);
|
||||
for(points,
|
||||
d = __distsq(_, center);
|
||||
if( d<dd,
|
||||
dd=d;
|
||||
index = _i
|
||||
);
|
||||
__assert_point_selected(_(p) -> p); // skips nulls and 0 - starting point
|
||||
current_time = global_points:global_selected_point:1;
|
||||
previous_time = global_points:(global_selected_point-1):1;
|
||||
segment_duration = current_time-previous_time;
|
||||
put(
|
||||
global_points,
|
||||
global_selected_point,
|
||||
l(
|
||||
__get_path_at(global_selected_point-1, previous_time, segment_duration/2),
|
||||
previous_time+segment_duration/2,
|
||||
global_points:global_selected_point:2
|
||||
),
|
||||
'insert'
|
||||
);
|
||||
index;
|
||||
__update();
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
select() ->
|
||||
// removes all points in the path from the current point
|
||||
trim_path() ->
|
||||
(
|
||||
__assert_point_selected(_(p) -> p != null);
|
||||
global_points = slice(global_points, 0, global_selected_point);
|
||||
global_selected_point = null;
|
||||
__update();
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
// moves entire camera path keeping the angles to player position being in the starting point
|
||||
reposition() ->
|
||||
(
|
||||
__assert_can_modify_path();
|
||||
shift = pos(__get_player())-slice(global_points:0:0, 0, 3);
|
||||
shift += 0; shift += 0;
|
||||
shift = shift + global_player_eye_offset;
|
||||
for(global_points, _:0 = _:0 + shift);
|
||||
__update();
|
||||
__get_path_size_string();
|
||||
);
|
||||
|
||||
// selects either a point of certain number (starting from 1), or closest point
|
||||
select(num) ->
|
||||
(
|
||||
if (!global_points, return());
|
||||
if (!global_showing_path, return());
|
||||
p = player();
|
||||
selected_point = __closest_point_to_center(
|
||||
p~'pos'+l(0,p~'eye_height',0),
|
||||
map(global_points, slice(_:0, 0, 3))
|
||||
p = __get_player();
|
||||
num = (num+1000*(length(global_points)+1)) % (length(global_points)+1);
|
||||
selected_point = if (num, num-1,
|
||||
__closest_point_to_center(
|
||||
p~'pos'+l(0,p~'eye_height',0),
|
||||
map(global_points, slice(_:0, 0, 3))
|
||||
)
|
||||
);
|
||||
global_selected_point = if (global_selected_point == selected_point, null, selected_point);
|
||||
global_needs_updating = true;
|
||||
// no need to _update since path is still valid
|
||||
|
||||
);
|
||||
|
||||
// player can also punch the mannequin to select/deselect it
|
||||
__on_player_attacks_entity(p, e) ->
|
||||
(
|
||||
if (e~'type' == 'armor_stand' && query(e, 'has_tag', '__scarpet_marker_camera') && global_markers,
|
||||
@@ -251,12 +332,80 @@ __on_player_attacks_entity(p, e) ->
|
||||
);
|
||||
);
|
||||
|
||||
global_show_thread = null;
|
||||
// adds new segment to the path
|
||||
__add_path_segment(vector, duration, mode, append) ->
|
||||
(
|
||||
if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
|
||||
l(v, segment_time, m) = global_points:(if(append, -1, 0));
|
||||
vector:(-2) = __adjusted_rot(v:(-2), vector:(-2));
|
||||
if (append,
|
||||
global_points += l(vector, segment_time+duration, mode);
|
||||
,
|
||||
new_points = l(l(vector, 0, mode));
|
||||
for (global_points,
|
||||
_:1 += duration;
|
||||
new_points += _;
|
||||
);
|
||||
global_points = new_points;
|
||||
);
|
||||
null;
|
||||
);
|
||||
|
||||
// adjusts current rotation so we don't spin around like crazy
|
||||
__adjusted_rot(previous_rot, current_rot) ->
|
||||
(
|
||||
while( abs(previous_rot-current_rot) > 180, 1000,
|
||||
current_rot += if(previous_rot < current_rot, -360, 360)
|
||||
);
|
||||
current_rot
|
||||
);
|
||||
|
||||
// returns current path size blurb
|
||||
__get_path_size_string() ->
|
||||
(
|
||||
if (!__is_not_valid_for_motion(),
|
||||
str('%d points, %.1f secs', length(global_points), global_points:(-1):1/60);
|
||||
,
|
||||
'Path too small to run';
|
||||
);
|
||||
);
|
||||
|
||||
// checks if the current path is valid for motion
|
||||
__is_not_valid_for_motion() ->
|
||||
(
|
||||
if(!global_points, return('Path not defined yet'));
|
||||
if(length(global_points)<2, return('Path not complete - add more points'));
|
||||
if(!global_dimension || __get_player()~'dimension' != global_dimension, return('Wrong dimension'));
|
||||
false
|
||||
);
|
||||
|
||||
// grabs position of a player for a given segment, which segment starts at start point, and offset by index points
|
||||
__get_path_at(segment, start, index) ->
|
||||
(
|
||||
v = global_path_precalculated:(start+index);
|
||||
if(v == null,
|
||||
v = call(global_interpolator, segment, index);
|
||||
global_path_precalculated:(start+index) = v
|
||||
);
|
||||
v
|
||||
);
|
||||
|
||||
// squared euclidean distance between two points
|
||||
__distsq(vec1, vec2) -> reduce(vec1 - vec2, _a + _*_, 0);
|
||||
|
||||
//finds index of the closest point from a list to the center point
|
||||
__closest_point_to_center(center, points) ->
|
||||
(
|
||||
reduce(points,
|
||||
d = __distsq(_, center); if( d<dd, dd=d; _i, _a),
|
||||
dd = __distsq(points:0, center); 0
|
||||
);
|
||||
);
|
||||
|
||||
// displays path particles and key point markers on its own thread
|
||||
show() ->
|
||||
(
|
||||
p = player();
|
||||
__print_path_size();
|
||||
__get_path_size_string();
|
||||
if (global_showing_path, return ());
|
||||
global_showing_path = true;
|
||||
global_needs_updating= false;
|
||||
@@ -264,9 +413,9 @@ show() ->
|
||||
(
|
||||
map(global_points || l(),
|
||||
is_selected = global_selected_point != null && _i == global_selected_point;
|
||||
caption = if (_i == 0, 'start', str('%.1fs', global_points:_i:1/60); );
|
||||
caption = if (_i == 0, '1: Start', str('%d: %.1fs', _i+1, global_points:_i:1/60); );
|
||||
if (is_selected && _i > 0,
|
||||
caption += str(' (%.1f current segment)', (global_points:_i:1 - global_points:(_i-1):1)/60);
|
||||
caption += str(' (%.1fs current segment)', (global_points:_i:1 - global_points:(_i-1):1)/60);
|
||||
);
|
||||
m = create_marker(caption, _:0, 'observer');
|
||||
if (is_selected,
|
||||
@@ -277,11 +426,11 @@ show() ->
|
||||
);
|
||||
__show_path_tick() ->
|
||||
(
|
||||
if (__assert_valid_for_motion(), return());
|
||||
if (__is_not_valid_for_motion(), return());
|
||||
__prepare_path_if_needed();
|
||||
loop(100,
|
||||
loop(global_particle_density,
|
||||
segment = floor(rand(length(global_points)-1));
|
||||
particle_type = if ((segment+1) == global_selected_point, 'dust 0.1 0.9 0.1 1','dust 0.6 0.6 0.6 1');
|
||||
particle_type = if ((segment+1) == global_selected_point, global_color_a, global_color_b); //'dust 0.1 0.9 0.1 1', 'dust 0.6 0.6 0.6 1');
|
||||
start = global_points:segment:1;
|
||||
end = global_points:(segment+1):1;
|
||||
index = floor(rand(end-start));
|
||||
@@ -291,10 +440,11 @@ show() ->
|
||||
null
|
||||
);
|
||||
|
||||
task( _(outer(p)) -> (
|
||||
task( _() -> (
|
||||
global_markers = __create_markers();
|
||||
loop(7200,
|
||||
if(!global_showing_path, break());
|
||||
__get_player();
|
||||
if (global_needs_updating,
|
||||
global_needs_updating = false;
|
||||
for(global_markers, modify(_,'remove'));
|
||||
@@ -310,60 +460,36 @@ show() ->
|
||||
'';
|
||||
);
|
||||
|
||||
move() ->
|
||||
// hides path display
|
||||
hide() ->
|
||||
(
|
||||
if (global_selected_point == null, return());
|
||||
if (!global_showing_path, return());
|
||||
global_points:global_selected_point:0 = __camera_position(player());
|
||||
__update();
|
||||
);
|
||||
|
||||
duration(amount) ->
|
||||
(
|
||||
if (!global_selected_point, return()); // skip nulls, and 0 (first one)
|
||||
if (!global_showing_path, return());
|
||||
duration = number(amount);
|
||||
new_ticks = round(duration * 60);
|
||||
if (new_ticks < 10, return());
|
||||
previous_ticks = global_points:global_selected_point:1-global_points:(global_selected_point-1):1;
|
||||
delta = new_ticks - previous_ticks;
|
||||
// adjust duration of all points after that.
|
||||
for (range(global_selected_point, length(global_points)),
|
||||
global_points:_:1 += delta;
|
||||
);
|
||||
__update();
|
||||
);
|
||||
|
||||
delete_point() ->
|
||||
(
|
||||
if (global_selected_point == null, return()); // skip nulls, and 0 (first one)
|
||||
if (!global_showing_path, return());
|
||||
if (length(global_points) < 2, clear(); return());
|
||||
if (global_selected_point == 0, global_points:1:1 = 0);
|
||||
global_points = filter(global_points, _i != global_selected_point);
|
||||
__update();
|
||||
);
|
||||
|
||||
trim_path() ->
|
||||
(
|
||||
if (global_selected_point == null, return()); // skip nulls, and 0 (first one)
|
||||
if (!global_showing_path, return());
|
||||
global_points = slice(global_points, 0, global_selected_point);
|
||||
__update();
|
||||
if (global_showing_path,
|
||||
global_showing_path = false;
|
||||
'Stopped showing path';
|
||||
,
|
||||
''
|
||||
);
|
||||
);
|
||||
|
||||
// runs the player on the path
|
||||
play() ->
|
||||
(
|
||||
p = player();
|
||||
if (err = __assert_valid_for_motion(), exit(err));
|
||||
if (err = __is_not_valid_for_motion(), exit(err));
|
||||
__prepare_path_if_needed();
|
||||
task( _(outer(fps), outer(p)) -> (
|
||||
sleep(1000);
|
||||
if (!__get_player() || __get_player()~'dimension' != global_dimension, exit('No player in dimension'));
|
||||
task( _() -> (
|
||||
if (global_playing_path, // we don't want to join_task not to lock it just in case. No need to panic here
|
||||
global_playing_path = false; // cancel current path rendering
|
||||
sleep(1500); // in case we interrupt previous playbacks
|
||||
);
|
||||
showing_path = global_showing_path;
|
||||
hide();
|
||||
sleep(2000); // so particles can discipate
|
||||
global_playing_path = true;
|
||||
mspt = 1000 / 60;
|
||||
start_time = time();
|
||||
point = 0;
|
||||
player_offset = l(0,p~'eye_height',0,0,0);
|
||||
p = __get_player();
|
||||
try (
|
||||
loop( length(global_points)-1, segment = _;
|
||||
start = global_points:segment:1;
|
||||
@@ -371,7 +497,7 @@ play() ->
|
||||
loop(end-start,
|
||||
if (p~'sneaking', global_playing_path = false);
|
||||
if (!global_playing_path, throw());
|
||||
v = __get_path_at(segment, start, _)-player_offset;
|
||||
v = __get_path_at(segment, start, _)-global_player_eye_offset;
|
||||
modify(p, 'location', v);
|
||||
point += 1;
|
||||
end_time = time();
|
||||
@@ -383,15 +509,18 @@ play() ->
|
||||
);
|
||||
sleep(1000);
|
||||
global_playing_path = false;
|
||||
print('Done!');
|
||||
if (showing_path, show());
|
||||
));
|
||||
'';
|
||||
);
|
||||
|
||||
// prepares empty path to fit new points
|
||||
__prepare_path_if_needed_generic() ->
|
||||
(
|
||||
if(!global_path_precalculated, __invalidate_points_cache())
|
||||
if(!global_path_precalculated, global_path_precalculated = map(range(global_points:(-1):1), null))
|
||||
);
|
||||
|
||||
// linear interpolator
|
||||
__interpolator_linear(segment, point) ->
|
||||
(
|
||||
l(va, start, mode_a) = global_points:segment;
|
||||
@@ -400,10 +529,14 @@ __interpolator_linear(segment, point) ->
|
||||
dt = point/section;
|
||||
dt*vb+(1-dt)*va
|
||||
);
|
||||
|
||||
// normal distribution should look like that
|
||||
//(1/sqrt(2*pi*d*d))*euler^(-((x-miu)^2)/(2*d*d))
|
||||
// but we will be normalizing anyways, so who cares
|
||||
__norm_prob(x, miu, d) -> euler^(-((x-miu)^2)/(2*d*d));
|
||||
__interpolator_gauss(from_index, point, deviation) ->
|
||||
|
||||
//gauB interpolator
|
||||
__interpolator_gauB(from_index, point, deviation) ->
|
||||
(
|
||||
components = l();
|
||||
path_point = global_points:from_index:1;
|
||||
@@ -417,7 +550,7 @@ __interpolator_gauss(from_index, point, deviation) ->
|
||||
0.6*reduce(devs, _a+_, 0)/length(devs)
|
||||
);
|
||||
impact = __norm_prob(path_point+point, ptime, dev);
|
||||
if(rtotal && impact < 0.000001*rtotal, throw());
|
||||
//if(rtotal && impact < 0.000001*rtotal, throw()); // can work badly on segments with vastly diff lengths
|
||||
components += l(v, impact);
|
||||
rtotal += impact
|
||||
)
|
||||
@@ -432,7 +565,7 @@ __interpolator_gauss(from_index, point, deviation) ->
|
||||
0.6*reduce(devs, _a+_, 0)/length(devs)
|
||||
);
|
||||
impact = __norm_prob(path_point+point, ptime, dev);
|
||||
if(ltotal && impact < 0.000001*ltotal, throw());
|
||||
//if(ltotal && impact < 0.000001*ltotal, throw());
|
||||
components += l(v, impact);
|
||||
ltotal += impact
|
||||
)
|
||||
@@ -441,6 +574,7 @@ __interpolator_gauss(from_index, point, deviation) ->
|
||||
reduce(components, _a+_:0*(_:1/total), l(0,0,0,0,0))
|
||||
);
|
||||
|
||||
// Catmull-Rom spline
|
||||
__interpolator_cr(from_index, point) ->
|
||||
(
|
||||
total = global_points:(from_index+1):1 - global_points:from_index:1;
|
||||
@@ -450,4 +584,38 @@ __interpolator_cr(from_index, point) ->
|
||||
p_2 = global_points:(if(from_index == (length(global_points)-2), -1, from_index+2)):0;
|
||||
r = point/total; // ratio within segment
|
||||
(r*((2-r)*r-1) * p__1 + (r*r*(3*r-5)+2) * p_0 + r*((4 - 3*r)*r + 1) * p_1 + (r-1)*r*r * p_2) / 2
|
||||
);
|
||||
);
|
||||
|
||||
// store current path in a world file
|
||||
store(file) ->
|
||||
(
|
||||
if (!global_points, exit('No path to save'));
|
||||
path_nbt = nbt('{}');
|
||||
for (global_points,
|
||||
point_nbt = nbt('{}');
|
||||
point_nbt:'duration' = _:1;
|
||||
point_nbt:'type' = _:2;
|
||||
for(_:0, put(point_nbt:'pos',_,_i));
|
||||
put(path_nbt:'points', point_nbt, _i);
|
||||
);
|
||||
store_app_data(path_nbt, file);
|
||||
);
|
||||
|
||||
// loads path under the local file that
|
||||
load(file) ->
|
||||
(
|
||||
path_nbt = load_app_data(file);
|
||||
if (!path_nbt, exit('No path to load: '+file));
|
||||
new_points = map(get(path_nbt, 'points[]'), l(_:'pos[]', _:'duration', _:'type'));
|
||||
if (!new_points || first(new_points, length(_:0) != 5),
|
||||
exit('Incorrect data for :'+file);
|
||||
);
|
||||
__start_with(_(outer(new_points)) -> new_points);
|
||||
);
|
||||
|
||||
// when closing - shut-down visualization and playback threads
|
||||
__on_close() ->
|
||||
(
|
||||
global_playing_path = false;
|
||||
global_showing_path = false;
|
||||
);
|
||||
@@ -158,4 +158,9 @@ __on_player_drops_stack(player) -> (
|
||||
print('');
|
||||
print('__on_player_drops_stack(player)');
|
||||
print('Player '+player+' drops current stack.')
|
||||
);
|
||||
|
||||
__on_statistic(player, category, event, value) ->
|
||||
(
|
||||
print('__on_statistic(player, category, event, value) -> ['+join(', ',l(player, category, event, value))+']');
|
||||
);
|
||||
@@ -141,6 +141,7 @@
|
||||
"DamageTracker_scarpetEventMixin",
|
||||
"ServerPlayerEntity_scarpetEventMixin",
|
||||
"FallingBlockEntity_scarpetEventsMixin",
|
||||
|
||||
"ChunkTicketManager_scarpetMixin",
|
||||
"ServerChunkManager_scarpetMixin",
|
||||
|
||||
|
||||
Reference in new issue
Block a user