98 lines
4.7 KiB
Java
98 lines
4.7 KiB
Java
package localmods.mtkviewpoint;
|
|
|
|
import java.util.*;
|
|
import me.zed_0xff.zombie_buddy.Exposer.LuaClass;
|
|
import se.krka.kahlua.vm.*;
|
|
import viewpoint.core.Frame;
|
|
import viewpoint.render.SceneData;
|
|
import zombie.Lua.Event;
|
|
import zombie.Lua.LuaEventManager;
|
|
|
|
/** Reads MTK's real projectiles on the game thread; publishes immutable render snapshots. */
|
|
@LuaClass(name = "MTKViewpointProjectiles")
|
|
public final class ProjectileBridge {
|
|
static final int LIMIT = 256;
|
|
static final double LEVEL = 2.4494895935058594;
|
|
private static KahluaTable physics, visuals;
|
|
private static final Map<SceneData, List<TracerMesh.Segment>> frames =
|
|
Collections.synchronizedMap(new WeakHashMap<>());
|
|
|
|
private ProjectileBridge() {}
|
|
|
|
public static LuaClosure legacyRenderer() {
|
|
var events = new ArrayList<Event>();
|
|
LuaEventManager.getEvents(events, new HashMap<>());
|
|
LuaClosure found = null;
|
|
for (var event : events) {
|
|
if (!"OnPreUIDraw".equals(event.name)) continue;
|
|
for (var callback : event.callbacks) {
|
|
var constants = Arrays.asList(callback.prototype.constants);
|
|
if (constants.containsAll(List.of("renderline","VisualProjectilePC","currentDistance","startHeight","isCovered"))) {
|
|
if (found != null) return null; // ambiguous: leave the dependency untouched
|
|
found = callback;
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
public static boolean bind(Object removeById, KahluaTable visualList) {
|
|
// In installed MTK 2.0.0 removeByID closes over exactly one value:
|
|
// projectilesByID. Read that reference; never replace or modify it.
|
|
if (!(removeById instanceof LuaClosure fn) || fn.prototype.numParams != 2 || fn.upvalues.length != 1
|
|
|| !Arrays.asList(fn.prototype.constants).containsAll(List.of("id","list","table","remove"))
|
|
|| fn.upvalues[0] == null || !(fn.upvalues[0].getValue() instanceof KahluaTable table)
|
|
|| visualList == null) return false;
|
|
physics = table;
|
|
visuals = visualList;
|
|
frames.clear();
|
|
return true;
|
|
}
|
|
|
|
public static void capture(Frame frame) {
|
|
if (frame == null) return;
|
|
if (physics == null || visuals == null) { frames.remove(frame.scene); return; }
|
|
var result = new ArrayList<TracerMesh.Segment>();
|
|
for (int i = 1, count = Math.min(visuals.len(), LIMIT); i <= count; i++) {
|
|
if (!(visuals.rawget(i) instanceof KahluaTable visual)) continue;
|
|
if (!(physics.rawget(visual.rawget("id")) instanceof KahluaTable shot)) continue;
|
|
double distance = num(shot, "distance"), travelled = num(shot, "currentDistance");
|
|
double x = num(shot,"startX"), y = num(shot,"startY"), z = num(shot,"startHeight");
|
|
double ex = num(shot,"endX"), ey = num(shot,"endY"), ez = num(shot,"endHeight");
|
|
if (!finite(distance,travelled,x,y,z,ex,ey,ez) || distance <= 0 || travelled <= 0 || travelled >= distance) continue;
|
|
double scale = num(visual,"scale");
|
|
if (!Double.isFinite(scale)) scale = 0.1;
|
|
double length = Math.max(0.45, Math.min(2.5, distance * Math.max(0.1,scale) * 0.08));
|
|
double head = Math.min(1,travelled/distance), tail = Math.max(0,(travelled-length)/distance);
|
|
double hx=x+(ex-x)*head, hy=y+(ey-y)*head, hz=z+(ez-z)*head;
|
|
double tx=x+(ex-x)*tail, ty=y+(ey-y)*tail, tz=z+(ez-z)*tail;
|
|
float[] h=local(frame.scene,hx,hy,hz), t=local(frame.scene,tx,ty,tz);
|
|
KahluaTable color = visual.rawget("color") instanceof KahluaTable c ? c : null;
|
|
result.add(new TracerMesh.Segment(h[0],h[1],h[2],t[0],t[1],t[2],
|
|
color(color,1,1),color(color,2,0.72f),color(color,3,0.14f),1));
|
|
}
|
|
frames.put(frame.scene,List.copyOf(result));
|
|
}
|
|
|
|
static List<TracerMesh.Segment> segments(SceneData scene) {
|
|
return frames.getOrDefault(scene,List.of());
|
|
}
|
|
|
|
static float[] local(SceneData scene, double x, double y, double z) {
|
|
double lift=scene.relief == null ? 0 : scene.relief.scene(x,y);
|
|
return new float[]{(float)(-x-scene.originX),(float)(z*LEVEL+lift-scene.originY),(float)(-y-scene.originZ)};
|
|
}
|
|
|
|
private static double num(KahluaTable t,String key) {
|
|
return t.rawget(key) instanceof Number n ? n.doubleValue() : Double.NaN;
|
|
}
|
|
private static float color(KahluaTable t,int key,float fallback) {
|
|
return t != null && t.rawget(key) instanceof Number n && Float.isFinite(n.floatValue())
|
|
? Math.max(0,Math.min(1,n.floatValue())) : fallback;
|
|
}
|
|
private static boolean finite(double... values) {
|
|
for (double v:values) if (!Double.isFinite(v)) return false;
|
|
return true;
|
|
}
|
|
}
|