Compare commits

...

20 Commits

Author SHA1 Message Date
c665401fd8 use .call instead of native in Inventory peripheral 2025-05-23 15:39:44 +02:00
2799a0be3d added GeoScanner peripheral 2024-10-27 16:08:38 +01:00
e527dd5b6a added simple 3D navigation 2024-10-27 16:07:23 +01:00
fe17b4fd67 added inspect command to turtleCtl 2024-10-20 23:23:58 +02:00
90b4015ba1 get chunk from position 2024-10-20 22:20:33 +02:00
2dd85c2b26 added Tags class for list of classes 2024-10-20 22:18:23 +02:00
1d60e13792 idfk 2024-10-18 16:48:38 +02:00
9d6e8a366b fixed SinglePromise reset order 2024-10-18 16:37:46 +02:00
5167533c6d Ignore GPS messages from other dimension
refs #2
2024-10-17 00:14:39 +02:00
aac527ae89 added a timeout and improvements to GPS and INS
closes #6
2024-10-16 23:45:53 +02:00
af6a4c840b added SinglePromise and SingleTimeoutPromise 2024-10-16 23:42:21 +02:00
afbd1dfd68 improved Vector stuff 2024-10-16 23:39:07 +02:00
b305594ea4 improved networking 2024-10-15 01:19:36 +02:00
df7991763d added PaN 2024-10-14 21:42:52 +02:00
e0f8d274e7 renamed Pos and Pos3 2024-10-14 21:40:26 +02:00
08f41ccb0b constrained vec to only accept numbers 2024-10-14 12:11:14 +02:00
28ec48cc85 added PriorityQueue for later use 2024-10-12 20:58:10 +02:00
4f2b6e7c53 fixed uninitialized children array in RootElement 2024-08-17 01:11:38 +02:00
2e0bda7a6e renamed Perf to Peri 2024-08-17 00:58:01 +02:00
87c93f3ae0 TurtleCtl times parameter optional 2024-08-17 00:57:03 +02:00
44 changed files with 1106 additions and 400 deletions

View File

@@ -2,7 +2,7 @@ package bin;
import lib.CLIAppBase; import lib.CLIAppBase;
import kernel.gps.INS; import kernel.gps.INS;
import lib.Pos3; import lib.WorldPos;
import lib.Vec.Vec3; import lib.Vec.Vec3;
using tink.CoreApi; using tink.CoreApi;
@@ -15,7 +15,7 @@ class GPS extends CLIAppBase {
var y:Float = args.getFloat("y"); var y:Float = args.getFloat("y");
var z:Float = args.getFloat("z"); var z:Float = args.getFloat("z");
var pos:Pos3 = new Vec3<Float>(x, y, z); var pos:WorldPos = new Vec3<Float>(x, y, z);
kernel.gps.GPS.setManualPosition(pos); kernel.gps.GPS.setManualPosition(pos);
@@ -51,11 +51,12 @@ class GPS extends CLIAppBase {
}); });
registerAsyncSubcommand("locate", (args) -> { registerAsyncSubcommand("locate", (args) -> {
return kernel.gps.GPS.locate().map((pos) -> { return kernel.gps.GPS.locate().map((result) -> {
if (pos != null) { switch result {
case Success(pos):
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}'); handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
} else { case Failure(err):
handle.writeLine("Position not available"); handle.writeLine("Position not available: " + err);
} }
return true; return true;
}); });

View File

@@ -4,7 +4,7 @@ import kernel.peripherals.Peripherals.Peripheral;
import lib.CLIAppBase; import lib.CLIAppBase;
@:build(macros.Binstore.includeBin("Peripheral", ["ph", "peripheral"])) @:build(macros.Binstore.includeBin("Peripheral", ["ph", "peripheral"]))
class Perf extends CLIAppBase { class Peri extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("inspect", (args) -> { registerSyncSubcommand("inspect", (args) -> {
var addr = args.getString("addr"); var addr = args.getString("addr");

View File

@@ -1,5 +1,6 @@
package bin; package bin;
import kernel.turtle.Types.ToolSide;
import lib.turtle.Helper; import lib.turtle.Helper;
import kernel.turtle.TurtleMutex; import kernel.turtle.TurtleMutex;
import kernel.turtle.Turtle; import kernel.turtle.Turtle;
@@ -12,28 +13,28 @@ using tink.CoreApi;
class TurtleCtl extends CLIAppBase { class TurtleCtl extends CLIAppBase {
public function new() { public function new() {
registerAsyncSubcommand("f", (args) -> { registerAsyncSubcommand("f", (args) -> {
return asynPerform(Turtle.forward, args.getInt("times")); return asynPerform(Turtle.forward, args.getInt("times") ?? 1);
}, [Int("times")]); }, [Optional(Int("times"))]);
registerAsyncSubcommand("b", (args) -> { registerAsyncSubcommand("b", (args) -> {
return asynPerform(Turtle.back, args.getInt("times")); return asynPerform(Turtle.back, args.getInt("times") ?? 1);
}, [Int("times")]); }, [Optional(Int("times"))]);
registerAsyncSubcommand("l", (args) -> { registerAsyncSubcommand("l", (args) -> {
return asynPerform(Turtle.turnLeft, args.getInt("times")); return asynPerform(Turtle.turnLeft, args.getInt("times") ?? 1);
}, [Int("times")]); }, [Optional(Int("times"))]);
registerAsyncSubcommand("r", (args) -> { registerAsyncSubcommand("r", (args) -> {
return asynPerform(Turtle.turnRight, args.getInt("times")); return asynPerform(Turtle.turnRight, args.getInt("times") ?? 1);
}, [Int("times")]); }, [Optional(Int("times"))]);
registerAsyncSubcommand("u", (args) -> { registerAsyncSubcommand("u", (args) -> {
return asynPerform(Turtle.up, args.getInt("times")); return asynPerform(Turtle.up, args.getInt("times") ?? 1);
}, [Int("times")]); }, [Optional(Int("times"))]);
registerAsyncSubcommand("d", (args) -> { registerAsyncSubcommand("d", (args) -> {
return asynPerform(Turtle.down, args.getInt("times")); return asynPerform(Turtle.down, args.getInt("times") ?? 1);
}, [Int("times")]); }, [Optional(Int("times"))]);
registerAsyncSubcommand("defrag", (args) -> { registerAsyncSubcommand("defrag", (args) -> {
return asynPerform(() -> { return asynPerform(() -> {
@@ -116,6 +117,28 @@ class TurtleCtl extends CLIAppBase {
var len = args.getInt("length"); var len = args.getInt("length");
return asynPerform(Helper.combine.bind([Turtle.digEmpty.bind(Front), Turtle.forward, Turtle.digEmpty.bind(Up)]), len); return asynPerform(Helper.combine.bind([Turtle.digEmpty.bind(Front), Turtle.forward, Turtle.digEmpty.bind(Up)]), len);
}, [Int("length")]); }, [Int("length")]);
registerSyncSubcommand("inspect", (args) -> {
var res = Turtle.inspect(Front);
switch res {
case Failure(err):
handle.writeLine("Failed: " + err);
return false;
case Success(data):
handle.writeLine('Name: ${data.name}');
handle.writeLine("Tags:");
for (tag in data.tags.sortByName()) {
handle.writeLine(' ${tag}');
}
handle.writeLine("State:");
handle.writeLine(data.state);
}
return true;
}, []);
} }
private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> { private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> {

View File

@@ -1,6 +1,6 @@
package bin.pathfinder; package bin.pathfinder;
import lib.Pos3; import lib.WorldPos;
import lib.ui.elements.IUIElement; import lib.ui.elements.IUIElement;
import lib.ui.elements.TextElement; import lib.ui.elements.TextElement;
import lib.ui.elements.RootElement; import lib.ui.elements.RootElement;
@@ -36,7 +36,7 @@ class PFClient implements IProcess {
private function render() { private function render() {
var acc = kernel.gps.GPS.getAccuracy(); var acc = kernel.gps.GPS.getAccuracy();
var pos:Pos3 = kernel.gps.GPS.getPosition() ?? {x: 0, y: 0, z: 0}; var pos:WorldPos = kernel.gps.GPS.getPosition() ?? {x: 0, y: 0, z: 0};
var childre:Array<IUIElement> = [ var childre:Array<IUIElement> = [
new TextElement('Acc: ${acc}'), new TextElement('Acc: ${acc}'),

View File

@@ -4,7 +4,7 @@ import kernel.turtle.TurtleMutex;
import kernel.turtle.Turtle; import kernel.turtle.Turtle;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
import kernel.log.Log; import kernel.log.Log;
import lib.Pos; import lib.ScreenPos;
import cc.HTTP.HTTPResponse; import cc.HTTP.HTTPResponse;
import lua.TableTools; import lua.TableTools;
import lua.Coroutine; import lua.Coroutine;
@@ -38,11 +38,11 @@ class KernelEvents {
distance:Null<Float> distance:Null<Float>
}>; }>;
public static var onMonitorResize(default, null):Signal<String>; public static var onMonitorResize(default, null):Signal<String>;
public static var onMonitorTouch(default, null):Signal<{addr:String, pos:Pos}>; public static var onMonitorTouch(default, null):Signal<{addr:String, pos:ScreenPos}>;
public static var onMouseClick(default, null):Signal<{button:ButtonType, pos:Pos}>; public static var onMouseClick(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onMouseDrag(default, null):Signal<{button:ButtonType, pos:Pos}>; public static var onMouseDrag(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onMouseScroll(default, null):Signal<{dir:Int, pos:Pos}>; public static var onMouseScroll(default, null):Signal<{dir:Int, pos:ScreenPos}>;
public static var onMouseUp(default, null):Signal<{button:ButtonType, pos:Pos}>; public static var onMouseUp(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onPaste(default, null):Signal<String>; public static var onPaste(default, null):Signal<String>;
public static var onPeripheral(default, null):Signal<String>; public static var onPeripheral(default, null):Signal<String>;
public static var onPeripheralDetach(default, null):Signal<String>; public static var onPeripheralDetach(default, null):Signal<String>;
@@ -75,11 +75,11 @@ class KernelEvents {
distance:Null<Float> distance:Null<Float>
}> = Signal.trigger(); }> = Signal.trigger();
private static final onMonitorResizeTrigger:SignalTrigger<String> = Signal.trigger(); private static final onMonitorResizeTrigger:SignalTrigger<String> = Signal.trigger();
private static final onMonitorTouchTrigger:SignalTrigger<{addr:String, pos:Pos}> = Signal.trigger(); private static final onMonitorTouchTrigger:SignalTrigger<{addr:String, pos:ScreenPos}> = Signal.trigger();
private static final onMouseClickTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger(); private static final onMouseClickTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger(); private static final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:Pos}> = Signal.trigger(); private static final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:ScreenPos}> = Signal.trigger();
private static final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger(); private static final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onPasteTrigger:SignalTrigger<String> = Signal.trigger(); private static final onPasteTrigger:SignalTrigger<String> = Signal.trigger();
private static final onPeripheralTrigger:SignalTrigger<String> = Signal.trigger(); private static final onPeripheralTrigger:SignalTrigger<String> = Signal.trigger();
private static final onPeripheralDetachTrigger:SignalTrigger<String> = Signal.trigger(); private static final onPeripheralDetachTrigger:SignalTrigger<String> = Signal.trigger();

View File

@@ -1,6 +1,6 @@
package kernel; package kernel;
import lib.Pos; import lib.ScreenPos;
import kernel.ui.ITermWriteable; import kernel.ui.ITermWriteable;
import cc.Term; import cc.Term;
import lib.Vec.Vec2; import lib.Vec.Vec2;
@@ -39,7 +39,7 @@ class MainTerm implements ITermWriteable {
Term.scroll(y); Term.scroll(y);
} }
public function getCursorPos():Pos { public function getCursorPos():ScreenPos {
var rtn = Term.getCursorPos(); var rtn = Term.getCursorPos();
return { return {
x: rtn.x - 1, x: rtn.x - 1,
@@ -60,7 +60,7 @@ class MainTerm implements ITermWriteable {
Term.setCursorBlink(blink); Term.setCursorBlink(blink);
} }
public function getSize():Pos { public function getSize():ScreenPos {
var rtn = Term.getSize(); var rtn = Term.getSize();
return { return {
x: rtn.width, x: rtn.width,

View File

@@ -1,11 +1,12 @@
package kernel.gps; package kernel.gps;
import lib.SingleTimeoutPromise;
import kernel.log.Log; import kernel.log.Log;
import lib.KVStore; import lib.KVStore;
import kernel.net.Net; import kernel.net.Net;
import kernel.net.INetworkInterface; import kernel.net.INetworkInterface;
import kernel.net.Package; import kernel.net.Package;
import lib.Pos3; import lib.WorldPos;
using tink.CoreApi; using tink.CoreApi;
@@ -15,20 +16,22 @@ using tink.CoreApi;
You need at least 3 computers that know their position to determine the position of the computer. You need at least 3 computers that know their position to determine the position of the computer.
**/ **/
class GPS { class GPS {
private static inline final TIMEOUT:Int = 1;
private static var shouldRespond = true; private static var shouldRespond = true;
private static var shouldDoWholeNumberCheck = true; private static var shouldDoWholeNumberCheck = true;
private static var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed) private static var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed)
private static var cachedPosition:Pos3; private static var cachedPosition:WorldPos;
private static var lastPositionResponse:Array<{pos:Pos3, dist:Float}> = []; private static var lastPositionResponse:Array<{pos:WorldPos, dist:Float}> = [];
private static var futureResolve:(pos:Null<Pos3>) -> Void = null; private static final locatePromise:SingleTimeoutPromise<WorldPos> = new SingleTimeoutPromise(TIMEOUT, brodcastPositionRequest);
@:allow(kernel.Init) @:allow(kernel.Init)
private static function init() { private static function init() {
loadCachedPosition(); loadCachedPosition();
} }
public static function setManualPosition(pos:Pos3) { public static function setManualPosition(pos:WorldPos) {
var kvstore = new KVStore("gps"); var kvstore = new KVStore("gps");
kvstore.set("mpos", pos); kvstore.set("mpos", pos);
kvstore.save(); kvstore.save();
@@ -40,12 +43,12 @@ class GPS {
} }
@:allow(kernel.gps.INS) @:allow(kernel.gps.INS)
private static function setINSPosition(pos:Pos3) { private static function setINSPosition(pos:WorldPos) {
cachedPosition = pos; cachedPosition = pos;
posAccuracy = 1; posAccuracy = 1;
} }
public static function getPosition():Null<Pos3> { public static function getPosition():Null<WorldPos> {
return cachedPosition; return cachedPosition;
} }
@@ -58,18 +61,8 @@ class GPS {
posAccuracy = 0; posAccuracy = 0;
} }
public static function locate():Future<Null<Pos3>> { public static function locate():Promise<WorldPos> {
// TODO: implenet a timeout return locatePromise.request();
// TODO: dont send a request twice if the last one is still pending or we moved
return new Future<Null<Pos3>>((resolve) -> {
futureResolve = resolve;
sendPositionRequest();
return null;
});
}
private static function resolveFuture(pos:Null<Pos3>) {
futureResolve(pos);
} }
private static function persistCachedPositon() { private static function persistCachedPositon() {
@@ -85,8 +78,8 @@ class GPS {
var kvstore = new KVStore("gps"); var kvstore = new KVStore("gps");
kvstore.load(); kvstore.load();
var mPos:Null<Pos3> = kvstore.get("mpos"); // Manual set position var mPos:Null<WorldPos> = kvstore.get("mpos"); // Manual set position
var cPos:Null<Pos3> = kvstore.get("cpos"); // Cached position var cPos:Null<WorldPos> = kvstore.get("cpos"); // Cached position
if (mPos != null && cPos != null && mPos == cPos) { if (mPos != null && cPos != null && mPos == cPos) {
// Both are the same, so we can use the cached position // Both are the same, so we can use the cached position
@@ -117,12 +110,17 @@ class GPS {
} }
} }
private static function sendPositionRequest() { private static function brodcastPositionRequest() {
Net.brodcastGPSRequest(); Net.brodcastGPSRequest();
} }
@:allow(kernel.net.Net) @:allow(kernel.net.Net)
private static function handlePackage(pack:Package<Noise>, dist:Float, iface:INetworkInterface) { private static function handlePackage(pack:Package<Noise>, dist:Null<Float>, iface:INetworkInterface) {
if (dist == null) {
// Message comes from another dimension and has no distance.
return;
}
switch (pack.type) { switch (pack.type) {
case GPSRequest: case GPSRequest:
if (!shouldRespond) if (!shouldRespond)
@@ -132,13 +130,13 @@ class GPS {
if (cachedPosition == null) if (cachedPosition == null)
return; return;
var response = new Package(Net.networkID, pack.fromID, pack.msgID, GPSResponse(cachedPosition), null, 0); var response = new Package(Net.networkID, pack.fromID, pack.msgID, GPSResponse(cachedPosition.x, cachedPosition.y, cachedPosition.z,), null, 0);
iface.send(pack.fromID, Net.networkID, response); iface.send(pack.fromID, Net.networkID, response);
case GPSResponse(pos): case GPSResponse(x, y, z):
if (lastPositionResponse.contains({pos: pos, dist: dist})) if (lastPositionResponse.contains({pos: {x: x, y: y, z: z}, dist: dist}))
return; // Ignore duplicate responses return; // Ignore duplicate responses
lastPositionResponse.push({pos: pos, dist: dist}); lastPositionResponse.push({pos: {x: x, y: y, z: z}, dist: dist});
// TODO: wait for a few seconds before calculating the position, so we can get more responses // TODO: wait for a few seconds before calculating the position, so we can get more responses
@@ -158,12 +156,12 @@ class GPS {
cachedPosition = calculatedPosition; cachedPosition = calculatedPosition;
posAccuracy = 3; posAccuracy = 3;
resolveFuture(calculatedPosition); locatePromise.resolve(calculatedPosition);
default: default:
} }
} }
private static function calculatePosition():Null<Pos3> { private static function calculatePosition():Null<WorldPos> {
if (lastPositionResponse.length < 3) if (lastPositionResponse.length < 3)
return null; return null;
@@ -219,7 +217,7 @@ class GPS {
/** /**
Determines the position(s) of a point given 3 other points and the distance to each of them. Determines the position(s) of a point given 3 other points and the distance to each of them.
**/ **/
private static function trilateration(p1:Pos3, p2:Pos3, p3:Pos3, r1:Float, r2:Float, r3:Float):Pair<Pos3, Pos3> { private static function trilateration(p1:WorldPos, p2:WorldPos, p3:WorldPos, r1:Float, r2:Float, r3:Float):Pair<WorldPos, WorldPos> {
var a2b = p2 - p1; var a2b = p2 - p1;
var a2c = p3 - p1; var a2c = p3 - p1;
@@ -228,7 +226,7 @@ class GPS {
var i = ex.dot(a2c); var i = ex.dot(a2c);
var ey = (a2c - ex * i).normalize(); var ey = (a2c - ex * i).normalize();
var j = ey.dot(a2c); var j = ey.dot(a2c);
var ez = ex.cross(ey); var ez:WorldPos = ex.cross(ey);
var x = (r1 * r1 - r2 * r2 + d * d) / (2 * d); var x = (r1 * r1 - r2 * r2 + d * d) / (2 * d);
var y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j); var y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j);

View File

@@ -1,14 +1,15 @@
package kernel.gps; package kernel.gps;
import kernel.log.Log; import lib.SinglePromise;
import kernel.turtle.Turtle; import kernel.turtle.Turtle;
import lib.Pos3; import lib.WorldPos;
using tink.CoreApi; using tink.CoreApi;
class INS { class INS {
private static var heading:Null<Pos3> = null; private static var heading:Null<WorldPos> = null;
private static var alingment:Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned private static var alingment:Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned
private static final alignPromise:SinglePromise<Noise> = new SinglePromise(startAlign);
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private static function moveForward() { private static function moveForward() {
@@ -68,7 +69,10 @@ class INS {
} }
} }
private static function move(dir:Null<Pos3>) { private static function move(dir:Null<WorldPos>) {
if (alignPromise.isRunning()) {
return;
}
var pos = GPS.getPosition(); var pos = GPS.getPosition();
if (pos == null || dir == null) if (pos == null || dir == null)
return; return;
@@ -76,47 +80,42 @@ class INS {
GPS.setINSPosition(newPos); GPS.setINSPosition(newPos);
} }
public static function getHeading():Null<Pos3> { public static function getHeading():Null<WorldPos> {
return heading; return heading;
} }
public static function align():Promise<Noise> { public static function align():Promise<Noise> {
Log.info("Aligning INS"); return alignPromise.request();
return new Promise<Noise>((resolve, reject) -> {
if (Turtle.getFuelLevel() < 2) {
Log.warn("Not enough fuel to align");
reject(new Error("Not enough fuel to align"));
return null;
} }
GPS.locate().handle((pos1) -> { public static function startAlign():Void {
Log.debug('pos1: $pos1'); if (Turtle.getFuelLevel() < 2) {
if (pos1 == null) { alignPromise.reject(new Error("Not enough fuel to align"));
Log.warn("GPS not available for 1st position");
reject(new Error("GPS not available"));
return; return;
} }
GPS.locate().handle((result) -> {
switch result {
case Failure(err):
alignPromise.reject(new Error("Failed to locate 1st positon: " + err));
return;
case Success(pos1):
var moved = tryMoving(); var moved = tryMoving();
if (moved == -1) { if (moved == -1) {
Log.warn("Can't move"); alignPromise.reject(new Error("Can't move"));
reject(new Error("Can't move"));
return; return;
} }
GPS.locate().handle((pos2) -> { GPS.locate().handle((result2) -> {
Log.debug('pos2: $pos2'); switch result2 {
if (pos2 == null) { case Failure(err):
Log.warn("GPS not available for 2nd position"); alignPromise.reject(new Error("GPS not available for 2nd position: " + err));
reject(new Error("GPS not available for 2nd position"));
return; return;
} case Success(pos2):
var cHeading = calcHeading(pos1, pos2, moved); var cHeading = calcHeading(pos1, pos2, moved);
if (cHeading == null) { if (cHeading == null) {
Log.error("Can't calculate heading"); alignPromise.reject(new Error("Can't calculate heading"));
reject(new Error("Can't calculate heading"));
return; return;
} }
@@ -124,10 +123,10 @@ class INS {
moveBack(moved); moveBack(moved);
GPS.setINSPosition(pos1); GPS.setINSPosition(pos1);
resolve(Noise); alignPromise.resolve(Noise);
}
}); });
}); }
return null;
}); });
} }
@@ -150,7 +149,7 @@ class INS {
} }
} }
private static function calcHeading(pos1:Pos3, pos2:Pos3, moved:Int):Null<Pos3> { private static function calcHeading(pos1:WorldPos, pos2:WorldPos, moved:Int):Null<WorldPos> {
if (moved == 0) { if (moved == 0) {
return pos1 - pos2; return pos1 - pos2;
} else if (moved == 1) { } else if (moved == 1) {
@@ -167,24 +166,18 @@ class INS {
private static function moveBack(moved:Int) { private static function moveBack(moved:Int) {
if (moved == 0) { if (moved == 0) {
Turtle.forward(); Turtle.forward();
// cc.Turtle.forward();
} else if (moved == 1) { } else if (moved == 1) {
Turtle.back(); Turtle.back();
// cc.Turtle.back();
} else if (moved == 2) { } else if (moved == 2) {
Turtle.back(); Turtle.back();
// cc.Turtle.back();
Turtle.turnRight(); Turtle.turnRight();
// cc.Turtle.turnRight();
} else if (moved == 3) { } else if (moved == 3) {
Turtle.forward(); Turtle.forward();
// cc.Turtle.forward();
Turtle.turnRight(); Turtle.turnRight();
// cc.Turtle.turnRight();
} }
} }
private static function rotatePos3ToRight(pos:Pos3):Pos3 { private static function rotatePos3ToRight(pos:WorldPos):WorldPos {
if (pos.x == 0 && pos.z == -1) { if (pos.x == 0 && pos.z == -1) {
return {x: 1, y: 0, z: 0}; return {x: 1, y: 0, z: 0};
} else if (pos.x == -1 && pos.z == 0) { } else if (pos.x == -1 && pos.z == 0) {
@@ -198,7 +191,7 @@ class INS {
} }
} }
private static function rotatePos3ToLeft(pos3:Pos3):Pos3 { private static function rotatePos3ToLeft(pos3:WorldPos):WorldPos {
return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3))); return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3)));
} }
} }

View File

@@ -39,6 +39,35 @@ class Net {
setupInterf(interf); setupInterf(interf);
} }
// Handle when a modem has been removed
KernelEvents.onPeripheralDetach.handle((addr) -> {
var idx = interfaces.findIndex((i) -> {
return i.name() == addr;
});
if (idx == -1) {
return;
}
Log.debug('Removed modem on addr: $addr');
interfaces.splice(idx, 1);
Routing.removeInterface(addr);
});
// Handle when a new modem is connected
KernelEvents.onPeripheral.handle((addr) -> {
if (!Peripheral.getTypes(addr).contains("modem")) {
return;
}
Log.debug('Added modem on addr: $addr');
var modem = Peripheral.getModem(addr);
interfaces.push(modem);
setupInterf(modem);
Routing.addInterface(modem);
});
setupPingHandle(); setupPingHandle();
Routing.setup(); Routing.setup();
@@ -62,6 +91,9 @@ class Net {
}); });
} }
/**
Enables the interface to receive messages.
**/
private static function setupInterf(interf:INetworkInterface) { private static function setupInterf(interf:INetworkInterface) {
interf.onMessage.handle(e -> handle(e.pack, interf, e.dist)); interf.onMessage.handle(e -> handle(e.pack, interf, e.dist));
interf.listen(networkID); interf.listen(networkID);
@@ -135,12 +167,13 @@ class Net {
if (!sendRaw(pack)) { if (!sendRaw(pack)) {
// Cant forward // Cant forward
Log.silly('Received a message that could not be forwarded to ${pack.toID}');
} }
} }
public static function respondTo(pack:GenericPackage, data:Dynamic) { public static function respondTo(pack:GenericPackage, data:Dynamic) {
if (pack.type.match(DataNoResponse(_))) { if (!pack.type.match(Data(_))) {
Log.warn("Responed to a no response package. Ignoring"); Log.warn("Responding to a package that does require responding. Ignoring.");
return; return;
} }
@@ -212,6 +245,8 @@ class Net {
if (timeout != null) { if (timeout != null) {
timeout.cancle(); timeout.cancle();
} }
responseBus.remove(pack.msgID);
}); });
timeout = new Timer(MESSAGE_TIMEOUT, () -> { timeout = new Timer(MESSAGE_TIMEOUT, () -> {

View File

@@ -1,7 +1,5 @@
package kernel.net; package kernel.net;
import lib.Pos3;
typedef NetworkID = Int; typedef NetworkID = Int;
typedef GenericPackage = Package<Dynamic>; typedef GenericPackage = Package<Dynamic>;
@@ -12,7 +10,7 @@ enum PackageTypes {
RouteDiscover(reachableIDs:Array<{id:NetworkID, cost:Int}>); RouteDiscover(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverResponse(reachableIDs:Array<{id:NetworkID, cost:Int}>); RouteDiscoverResponse(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverUpdate(reachableIDs:Array<{id:NetworkID, cost:Int}>); RouteDiscoverUpdate(reachableIDs:Array<{id:NetworkID, cost:Int}>);
GPSResponse(pos:Pos3); GPSResponse(x:Float, y:Float, z:Float);
GPSRequest(); GPSRequest();
} }

View File

@@ -74,30 +74,17 @@ class Routing {
private static function handleRoutePackage(pack:Package<Noise>, interf:INetworkInterface):Void { private static function handleRoutePackage(pack:Package<Noise>, interf:INetworkInterface):Void {
addPossibleRoute(pack.fromID, interf, 0, pack.fromID); addPossibleRoute(pack.fromID, interf, 0, pack.fromID);
var shouldRespond:Bool = switch pack.type { switch pack.type {
case RouteDiscoverResponse(routes): case RouteDiscoverResponse(routes):
// A request for routes has been answerd
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
false;
case RouteDiscover(routes): case RouteDiscover(routes):
// Received a request for available routes
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
true;
case RouteDiscoverUpdate(routes):
for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
}
false;
default:
Log.error("Expected package to be a Route discover package");
false;
};
if (!shouldRespond) {
return;
}
// Respond to peer // Respond to peer
var response:Package<Noise> = { var response:Package<Noise> = {
@@ -110,6 +97,14 @@ class Routing {
} }
interf.send(response.toID, Net.networkID, response); interf.send(response.toID, Net.networkID, response);
case RouteDiscoverUpdate(routes):
// Received an update of routes
for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
}
default:
Log.silly("Expected package to be a Route discover package");
};
} }
/** /**
@@ -126,6 +121,9 @@ class Routing {
return routes; return routes;
} }
/**
Create a new packate to brodcast our routes.
**/
private static function newRoutDiscoverPackage():Package<Noise> { private static function newRoutDiscoverPackage():Package<Noise> {
var pack:Package<Noise> = { var pack:Package<Noise> = {
type: RouteDiscover(genRouteList()), type: RouteDiscover(genRouteList()),
@@ -179,4 +177,28 @@ class Routing {
public static function getRouteTable():Map<NetworkID, Route> { public static function getRouteTable():Map<NetworkID, Route> {
return routingTable; return routingTable;
} }
/**
Call when an interface is no longer available.
Will rebrodcast routing requests.
**/
@:allow(kernel.net.Net)
private static function removeInterface(addr:String) {
// Remove the interface from the routing table
for (to => modem in routingTable) {
if (modem.interf.name() == addr) {
routingTable.remove(to);
}
}
// Since some routes to some host could be lost
// we rebrodcast on all interfaces again
brodcastRoutingTable();
}
@:allow(kernel.net.Net)
private static function addInterface(iface:INetworkInterface) {
var pack = newRoutDiscoverPackage();
iface.send(Net.BRODCAST_PORT, Net.networkID, pack);
}
} }

View File

@@ -0,0 +1,76 @@
package kernel.peripherals;
import lib.BlockPos;
import lib.Tags;
import cc.Peripheral;
using tink.CoreApi;
using lua.Table;
@:structInit typedef ScanBlock = {
name:String,
tags:Tags,
pos:BlockPos
}
class GeoScanner implements IPeripheral {
public static inline final TYPE_NAME:String = "geoScanner";
private final addr:String;
public function new(addr:String) {
this.addr = addr;
}
public function getAddr():String {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
/**
Returns the current time remaining until then next scan() can be ran.
**/
public function getScanCooldown():Int {
return Peripheral.call(this.addr, "getScanCooldown");
}
/**
Returns the cost in FE for a scan with the given radius.
Returns null of scan of this radius is not posible.
**/
public function cost(radius:Int):Null<Int> {
return Peripheral.call(this.addr, "cost", radius);
}
/**
Returns a list of data about all blocks in the radius.
X,Y,Z are relative to the geoscanner block.
Air blocks are not included.
List is unsorted.
**/
public function scan(radius:Int):Outcome<Array<ScanBlock>, String> {
// TODO: Handel fail state
var result:lua.Table<Int, {
x:Int,
y:Int,
z:Int,
name:String,
tags:lua.Table<Int, String>
}> = Peripheral.call(this.addr, "scan", radius);
return Success(result.toArray().map((e) -> {
name: e.name,
tags: Tags.fromIntTable(e.tags),
pos: new BlockPos(e.x, e.y, e.z)
}));
}
public function chunkAnalyze():Outcome<Map<String, Int>, String> {
var result:lua.Table<String, Int> = Peripheral.call(this.addr, "chunkAnalyze");
return Success(result.toMap());
}
}

View File

@@ -1,5 +1,9 @@
package kernel.peripherals; package kernel.peripherals;
import kernel.log.Log;
import lib.Debug;
import cc.Peripheral;
import lib.Tags;
import lib.Item; import lib.Item;
import cc.periphs.ItemStorage.ReducedItemInfo; import cc.periphs.ItemStorage.ReducedItemInfo;
import lua.Table; import lua.Table;
@@ -22,7 +26,7 @@ class ItemInfo {
class DetailedItemInfo extends ItemInfo { class DetailedItemInfo extends ItemInfo {
public final displayName:String; public final displayName:String;
public final maxCount:Int; public final maxCount:Int;
public final tags:Array<String>; public final tags:Tags;
public final durability:Null<Float>; public final durability:Null<Float>;
public final damage:Null<Int>; public final damage:Null<Int>;
public final maxDamage:Null<Int>; public final maxDamage:Null<Int>;
@@ -32,10 +36,7 @@ class DetailedItemInfo extends ItemInfo {
private function new(from:cc.periphs.ItemStorage.DetailedItemInfo) { private function new(from:cc.periphs.ItemStorage.DetailedItemInfo) {
super(from); super(from);
this.tags = []; this.tags = Tags.fromBoolTable(from.tags);
for (k => _ in Table.toMap(from.tags)) {
this.tags.push(k);
}
this.displayName = from.displayName; this.displayName = from.displayName;
this.maxCount = from.maxCount; this.maxCount = from.maxCount;
@@ -55,11 +56,9 @@ class Inventory implements IPeripheral {
public static inline final TYPE_NAME:String = "inventory"; public static inline final TYPE_NAME:String = "inventory";
private final addr:String; private final addr:String;
private final native:cc.periphs.ItemStorage;
public function new(addr:String) { public function new(addr:String) {
this.addr = addr; this.addr = addr;
this.native = cc.Peripheral.wrap(addr);
} }
public function getAddr():String { public function getAddr():String {
@@ -74,18 +73,18 @@ class Inventory implements IPeripheral {
Get the size of this inventory. Get the size of this inventory.
**/ **/
public function size():Int { public function size():Int {
return this.native.size(); return Peripheral.call(this.addr, "size");
} }
/** /**
List all items in this inventory. List all items in this inventory.
**/ **/
public function list():Map<Int, ItemInfo> { public function list():Map<Int, ItemInfo> {
var list = Table.toArray(this.native.list()); var list:Map<Int, ReducedItemInfo> = Table.toMap(Peripheral.call(this.addr, "list"));
var rtn:Map<Int, ItemInfo> = new Map(); var rtn:Map<Int, ItemInfo> = new Map();
for (k => v in list) { for (k => v in list) {
rtn.set(k, new ItemInfo(v)); rtn.set(k - 1, new ItemInfo(v));
} }
return rtn; return rtn;
@@ -95,7 +94,7 @@ class Inventory implements IPeripheral {
Get detailed information about an item. Get detailed information about an item.
**/ **/
public function getItemDetail(slot:Int):DetailedItemInfo { public function getItemDetail(slot:Int):DetailedItemInfo {
return new DetailedItemInfo(this.native.getItemDetail(slot)); return new DetailedItemInfo(Peripheral.call(this.addr, "getItemDetail", slot + 1));
} }
/** /**
@@ -105,20 +104,20 @@ class Inventory implements IPeripheral {
Keep in mind that this does not depend on that item stored in this slot. Keep in mind that this does not depend on that item stored in this slot.
**/ **/
public function getItemLimit(slot:Int):Int { public function getItemLimit(slot:Int):Int {
return this.native.getItemLimit(slot); return Peripheral.call(this.addr, "getItemLimit", slot + 1);
} }
/** /**
Push items from one inventory to another connected one. Push items from one inventory to another connected one. Needs to be on the same wired network.
**/ **/
public function pushItems(toSlot:Int, to:String, ?limit:Int, ?toSlot:Int):Int { public function pushItems(to:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int {
return this.native.pushItems(to, toSlot, limit, toSlot); return Peripheral.call(this.addr, "pushItems", to, fromSlot + 1, limit, toSlot);
} }
/** /**
Pull items from a connected inventory into this one. Pull items from a connected inventory into this one. Needs to be on the same wired network.
**/ **/
public function pullItems(from:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int { public function pullItems(from:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int {
return this.native.pullItems(from, fromSlot, limit, toSlot); return Peripheral.call(this.addr, "pullItems", from, fromSlot + 1, limit, toSlot);
} }
} }

View File

@@ -1,6 +1,5 @@
package kernel.peripherals; package kernel.peripherals;
import kernel.log.Log;
import kernel.peripherals.Modem; import kernel.peripherals.Modem;
import kernel.peripherals.Screen; import kernel.peripherals.Screen;
@@ -83,6 +82,8 @@ class Peripheral {
return getRedstone(addr); return getRedstone(addr);
case Inventory.TYPE_NAME: case Inventory.TYPE_NAME:
return getInventory(addr); return getInventory(addr);
case GeoScanner.TYPE_NAME:
return getGeoScanner(addr);
} }
return null; return null;
@@ -180,4 +181,15 @@ class Peripheral {
public static function getAllInventorys():Array<Inventory> { public static function getAllInventorys():Array<Inventory> {
return [for (addr in findAddrByType(Inventory.TYPE_NAME)) new Inventory(addr)]; return [for (addr in findAddrByType(Inventory.TYPE_NAME)) new Inventory(addr)];
} }
public static function getGeoScanner(addr:String):Null<GeoScanner> {
var addr = safeGetAddr(addr, GeoScanner.TYPE_NAME);
if (addr == null)
return null;
return new GeoScanner(addr);
}
public static function getAllGeoScanners():Array<GeoScanner> {
return [for (addr in findAddrByType(GeoScanner.TYPE_NAME)) new GeoScanner(addr)];
}
} }

View File

@@ -2,7 +2,7 @@ package kernel.peripherals;
import cc.Peripheral; import cc.Peripheral;
import lib.Rect; import lib.Rect;
import lib.Pos; import lib.ScreenPos;
class Printer implements IPeripheral { class Printer implements IPeripheral {
public static inline final TYPE_NAME:String = "printer"; public static inline final TYPE_NAME:String = "printer";
@@ -25,11 +25,11 @@ class Printer implements IPeripheral {
public function write(text:String) {} public function write(text:String) {}
public function getCurserPos():Pos { public function getCurserPos():ScreenPos {
return new Pos({x: 0, y: 0}); return new ScreenPos({x: 0, y: 0});
} }
public function setCurserPos(pos:Pos) { public function setCurserPos(pos:ScreenPos) {
this.native.setCursorPos(pos.x, pos.y); this.native.setCursorPos(pos.x, pos.y);
} }

View File

@@ -1,7 +1,7 @@
package kernel.peripherals; package kernel.peripherals;
import cc.Peripheral; import cc.Peripheral;
import lib.Pos; import lib.ScreenPos;
import cc.Term.TerminalSize; import cc.Term.TerminalSize;
import kernel.ui.ITermWriteable; import kernel.ui.ITermWriteable;
import lib.Vec.Vec2; import lib.Vec.Vec2;
@@ -59,7 +59,7 @@ class Screen implements ITermWriteable implements IPeripheral {
nativ.scroll(y); nativ.scroll(y);
} }
public function getCursorPos():Pos { public function getCursorPos():ScreenPos {
var rtn = nativ.getCursorPos(); var rtn = nativ.getCursorPos();
return { return {
x: rtn.x - 1, x: rtn.x - 1,

View File

@@ -1,6 +1,6 @@
package kernel.ui; package kernel.ui;
import lib.Pos; import lib.ScreenPos;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
import kernel.ui.ITermWriteable; import kernel.ui.ITermWriteable;
@@ -86,7 +86,7 @@ class BufferedVirtualTermWriter implements IVirtualTermWriter extends TermBuffer
super.scroll(y); super.scroll(y);
} }
public override function getCursorPos():Pos { public override function getCursorPos():ScreenPos {
if (isEnabled()) { if (isEnabled()) {
return target.getCursorPos(); return target.getCursorPos();
} else { } else {

View File

@@ -1,6 +1,6 @@
package kernel.ui; package kernel.ui;
import lib.Pos; import lib.ScreenPos;
import lib.Color; import lib.Color;
import lib.Vec.Vec2; import lib.Vec.Vec2;
@@ -18,7 +18,7 @@ interface ITermWriteable {
/** /**
Even though CC is 1 based we use a 0 based index. Even though CC is 1 based we use a 0 based index.
**/ **/
public function getCursorPos():Pos; public function getCursorPos():ScreenPos;
/** /**
Even though CC is 1 based we use a 0 based index. Even though CC is 1 based we use a 0 based index.

View File

@@ -1,7 +1,7 @@
package kernel.ui; package kernel.ui;
import kernel.log.Log; import kernel.log.Log;
import lib.Pos; import lib.ScreenPos;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
@@ -110,8 +110,8 @@ class StatelessVirtualTermWriter implements IVirtualTermWriter {
target.scroll(y); target.scroll(y);
} }
public inline function getCursorPos():Pos { public inline function getCursorPos():ScreenPos {
return enabled ? target.getCursorPos() : new Pos({x: 0, y: 0}); return enabled ? target.getCursorPos() : new ScreenPos({x: 0, y: 0});
} }
public inline function setCursorPos(x:Int, y:Int) { public inline function setCursorPos(x:Int, y:Int) {

View File

@@ -1,6 +1,6 @@
package kernel.ui; package kernel.ui;
import lib.Pos; import lib.ScreenPos;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
import kernel.ui.ITermWriteable; import kernel.ui.ITermWriteable;
@@ -18,7 +18,7 @@ class TermBuffer implements ITermWriteable {
**/ **/
private var screenBuffer:Array<Array<Pixel>>; private var screenBuffer:Array<Array<Pixel>>;
private var cursorPos:Pos = {x: 0, y: 0}; private var cursorPos:ScreenPos = {x: 0, y: 0};
private var currentTextColor:Color = White; private var currentTextColor:Color = White;
private var currentBgColor:Color = Black; private var currentBgColor:Color = Black;
private var cursorBlink:Bool = false; private var cursorBlink:Bool = false;
@@ -91,7 +91,7 @@ class TermBuffer implements ITermWriteable {
target.setCursorBlink(cursorBlink); target.setCursorBlink(cursorBlink);
} }
private function safeWriteScreenBuffer(pos:Pos, char:String) { private function safeWriteScreenBuffer(pos:ScreenPos, char:String) {
if (screenBuffer.length > pos.y && screenBuffer[pos.y].length > pos.x) { if (screenBuffer.length > pos.y && screenBuffer[pos.y].length > pos.x) {
screenBuffer[pos.y][pos.x].char = char; screenBuffer[pos.y][pos.x].char = char;
screenBuffer[pos.y][pos.x].bg = currentBgColor; screenBuffer[pos.y][pos.x].bg = currentBgColor;
@@ -121,7 +121,7 @@ class TermBuffer implements ITermWriteable {
]); ]);
} }
public function getCursorPos():Pos { public function getCursorPos():ScreenPos {
return cursorPos; return cursorPos;
} }

View File

@@ -1,7 +1,7 @@
package kernel.ui; package kernel.ui;
import lib.ui.rendere.IUIEventDelegate; import lib.ui.rendere.IUIEventDelegate;
import lib.Pos; import lib.ScreenPos;
import lib.Color; import lib.Color;
import kernel.ButtonType; import kernel.ButtonType;
import lib.Vec.Vec2; import lib.Vec.Vec2;
@@ -17,21 +17,21 @@ class WindowContext implements ITermWriteable {
@:allow(kernel.ui.WindowManager) private var eventDelegate:Null<IUIEventDelegate>; @:allow(kernel.ui.WindowManager) private var eventDelegate:Null<IUIEventDelegate>;
public var onClick(default, null):Signal<{button:ButtonType, pos:Pos}>; public var onClick(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>; public var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>;
public var onKeyUp(default, null):Signal<Int>; public var onKeyUp(default, null):Signal<Int>;
public var onMouseDrag(default, null):Signal<{button:ButtonType, pos:Pos}>; public var onMouseDrag(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public var onMouseScroll(default, null):Signal<{dir:Int, pos:Pos}>; public var onMouseScroll(default, null):Signal<{dir:Int, pos:ScreenPos}>;
public var onMouseUp(default, null):Signal<{button:ButtonType, pos:Pos}>; public var onMouseUp(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public var onPaste(default, null):Signal<String>; public var onPaste(default, null):Signal<String>;
public var onChar(default, null):Signal<String>; public var onChar(default, null):Signal<String>;
@:allow(kernel.ui.WindowManager) private final onClickTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onClickTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}>; @:allow(kernel.ui.WindowManager) private final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}>;
@:allow(kernel.ui.WindowManager) private final onKeyUpTrigger:SignalTrigger<Int>; @:allow(kernel.ui.WindowManager) private final onKeyUpTrigger:SignalTrigger<Int>;
@:allow(kernel.ui.WindowManager) private final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onPasteTrigger:SignalTrigger<String>; @:allow(kernel.ui.WindowManager) private final onPasteTrigger:SignalTrigger<String>;
@:allow(kernel.ui.WindowManager) private final onCharTrigger:SignalTrigger<String>; @:allow(kernel.ui.WindowManager) private final onCharTrigger:SignalTrigger<String>;
@@ -89,7 +89,7 @@ class WindowContext implements ITermWriteable {
writer.scroll(y); writer.scroll(y);
} }
public inline function getCursorPos():Pos { public inline function getCursorPos():ScreenPos {
return writer.getCursorPos(); return writer.getCursorPos();
} }

12
src/lib/Block.hx Normal file
View File

@@ -0,0 +1,12 @@
package lib;
@:structInit
class Block {
public final name:PaN;
public final tags:Tags;
public function new(name:String, tags:Tags) {
this.name = name;
this.tags = tags;
}
}

114
src/lib/BlockBlockMap.hx Normal file
View File

@@ -0,0 +1,114 @@
package lib;
import kernel.peripherals.GeoScanner.ScanBlock;
import lib.BlockMap;
@:forward
abstract BlockBlockMap(BlockMap<Block>) {
public inline function new() {
this = new BlockMap<Block>();
}
public static function fromScan(scan:Array<ScanBlock>):BlockBlockMap {
var map:BlockBlockMap = new BlockBlockMap();
for (block in scan) {
map.set(block.pos, new Block(block.name, block.tags));
}
return map;
}
/**
Navigate from one point into another.
Returns the path to take.
Based on A*
**/
public function nav(from:BlockPos, to:BlockPos):Array<BlockPos> {
if (!canPass(to)) {
trace("End is blocked");
return [];
}
var openSet = new PriorityQueue();
openSet.insert(from, 0);
var cameFrom:BlockMap<BlockPos> = new BlockMap();
var gCost:BlockMap<Int> = new BlockMap(); // G cost is the distance from start
gCost.set(from, 0);
var fCost:BlockMap<Float> = new BlockMap(); // F cost is the combines cost of moving to this pos (G cost) + the distnace to the end
fCost.set(from, from.distance(to));
while (!openSet.isEmpty()) {
var current = openSet.extractMin(); // Check the pos with the lowest F cost
if (current.equals(to)) {
var totalPath = [];
var currentPathPos = to;
while (cameFrom.exists(currentPathPos)) {
totalPath.unshift(currentPathPos);
currentPathPos = cameFrom.get(currentPathPos);
}
totalPath.unshift(from);
return totalPath;
}
for (neighbor in current.neighbors()) {
if (!canPass(neighbor)) {
continue;
}
var newGCost = gCost.get(current) + 1; // Movment cost to neighbor is always 1 as we can't move diagonally
if (newGCost < (gCost.get(neighbor) ?? 9999)) {
cameFrom.set(neighbor, current);
gCost.set(neighbor, newGCost);
fCost.set(neighbor, newGCost + neighbor.distance(to));
if (!openSet.containsElement(neighbor)) {
openSet.insert(neighbor, fCost.get(neighbor));
}
}
}
}
return [];
}
/**
Some block can be passed by the turtle.
Some blocks get destroyed by it.
**/
public function canPass(k:BlockPos):Bool {
var block = this.get(k);
if (block == null) {
return true;
}
switch block.name {
case "minecraft:water":
return true;
case "minecaft:lava":
case "mimecraft:grass":
case "minecraft:hanging_roots":
case "minecraft:warped_roots":
case "minecraft:dead_bush":
case "minecraft:fern":
case "minecraft:vine":
case "minecraft:glow_lichen":
case "minecraft:seagrass":
case "minecraft:crimson_roots":
case "minecraft:nether_sprouts":
case "minecraft:snow":
case "minecraft:rose_bush":
case "minecraft:fire":
return true;
}
return false;
}
}

48
src/lib/BlockMap.hx Normal file
View File

@@ -0,0 +1,48 @@
package lib;
import haxe.ds.IntMap;
/**
Map values to positions in 3D space. For representing blocks from a scan see `BlockBlockMap`.
The only reason this exsists is that the abstract class for BlockPos does not
satisfy the constraint of HashMap. The hashCode function needs to be on a real class and not on
an abstract class. Thanks Obama.
**/
@:forward(iterator, clear)
abstract BlockMap<T>(IntMap<T>) {
/**
Creates a new HashMap.
**/
public inline function new() {
this = new IntMap();
}
/**
See `Map.set`
**/
@:arrayAccess public inline function set(k:BlockPos, v:T) {
this.set(k.hashCode(), v);
}
/**
See `Map.get`
**/
@:arrayAccess public inline function get(k:BlockPos) {
return this.get(k.hashCode());
}
/**
See `Map.exists`
**/
public inline function exists(k:BlockPos) {
return this.exists(k.hashCode());
}
/**
See `Map.remove`
**/
public inline function remove(k:BlockPos) {
return this.remove(k.hashCode());
}
}

78
src/lib/BlockPos.hx Normal file
View File

@@ -0,0 +1,78 @@
package lib;
import lib.Vec.Vec2;
import lib.Vec.Vec3;
/**
Represents a Point in a 3D `Int` System.
Basicly a wrapper for Vec3<Int> with some extra functions.
`Y` represents the height of the point.
**/
@:forward
abstract BlockPos(Vec3<Int>) from Vec3<Int> to Vec3<Int> {
public inline function new(x:Int, y:Int, z:Int) {
this = new Vec3(x, y, z);
}
@:op(A + B)
public inline function add(rhs:BlockPos):BlockPos {
return this.add(rhs);
}
@:op(A - B)
public inline function sub(rhs:BlockPos):BlockPos {
return this.sub(rhs);
}
@:op(A * B)
public inline function multiplyScalar(rhs:Int):BlockPos {
return this.multiplyScalar(rhs);
}
@:op(-A)
public inline function negate():BlockPos {
return this.negate();
}
@:op(A == B)
public function equals(rhs:BlockPos):Bool {
return this.x == rhs.x && this.y == rhs.y && this.z == rhs.z;
}
@:op(A != B)
public function notEquals(rhs:BlockPos):Bool {
return !equals(rhs);
}
public function hashCode():Int {
return (this.x * 73856093) ^ (this.y * 19349663) ^ (this.z * 83492791);
}
public function toString():String {
return 'BlockPos(${this.x}, ${this.y}, ${this.z})';
}
/**
Returns the chunk the position is in.
**/
public function chunk():Vec2<Int> {
var x = Math.floor(this.x / 16);
var z = Math.floor(this.z / 16);
return new Vec2(x, z);
}
/**
Returns a list of positions neighboring the block. No Diagonal.
**/
public function neighbors():Array<BlockPos> {
return [
new BlockPos(this.x + 1, this.y + 0, this.z + 0), // Front
new BlockPos(this.x - 1, this.y + 0, this.z + 0), // Back
new BlockPos(this.x + 0, this.y + 0, this.z - 1), // Left
new BlockPos(this.x + 0, this.y + 0, this.z + 1), // Right
new BlockPos(this.x + 0, this.y - 1, this.z + 0), // Bot
new BlockPos(this.x + 0, this.y + 1, this.z + 0), // Top
];
}
}

View File

@@ -3,7 +3,8 @@ package lib;
/** /**
Represents an item in the game. Represents an item in the game.
**/ **/
enum abstract Item(String) to String { @:forward
enum abstract Item(PaN) to String {
public inline function new(name:String) { public inline function new(name:String) {
// Check if the name is valid. in the format `mod:item_name` e.g. `minecraft:apple` // Check if the name is valid. in the format `mod:item_name` e.g. `minecraft:apple`
// TODO: implement // TODO: implement
@@ -16,10 +17,6 @@ enum abstract Item(String) to String {
return new Item(s); return new Item(s);
} }
function getBase():String {
return this.split(":")[0];
}
var Coal = "minecraft:coal"; var Coal = "minecraft:coal";
var Charcoal = "minecraft:charcoal"; var Charcoal = "minecraft:charcoal";
} }

18
src/lib/PaN.hx Normal file
View File

@@ -0,0 +1,18 @@
package lib;
/**
Provider and name. e.g. "minecraft:dirt", "mod:item", "minecraft:overworld"
**/
abstract PaN(String) to String from String {
public inline function new(pan:String) {
this = pan;
}
public function getProvider():String {
return this.split(":")[0];
}
public function getName():String {
return this.split(":")[1];
}
}

View File

@@ -1,46 +0,0 @@
package lib;
import lib.Vec.Vec2;
/**
Reporesents a Point in a 2D `Int` System.
Basicly a wrapper for Vec2<Int> with some extra functions.
**/
@:forward(x, y)
abstract Pos(Vec2<Int>) from Vec2<Int> to Vec2<Int> {
inline public function new(i:Vec2<Int>) {
this = i;
}
@:op(A + B)
public function add(rhs:Vec2<Int>):Pos {
return new Pos({
y: this.y + rhs.y,
x: this.x + rhs.x,
});
}
@:op(A - B)
public function sub(rhs:Vec2<Int>):Pos {
return new Pos({
y: this.y - rhs.y,
x: this.x - rhs.x,
});
}
@:op(A * B)
public function multiply(rhs:Vec2<Int>):Pos {
return new Pos({
y: this.y * rhs.y,
x: this.x * rhs.x,
});
}
@:op(-A)
public function negate():Pos {
return new Pos({
y: -this.y,
x: -this.x,
});
}
}

View File

@@ -1,111 +0,0 @@
package lib;
import lib.Vec.Vec3;
/**
Reporesents a Point in a 3D `Float` System.
Basicly a wrapper for Vec3<Float> with some extra functions.
`Y` represents the height of the point.
**/
@:forward(x, y, z)
abstract Pos3(Vec3<Float>) from Vec3<Float> to Vec3<Float> {
inline public function new(i:Vec3<Float>) {
this = i;
}
@:op(A + B)
public function add(rhs:Vec3<Float>):Pos3 {
return new Pos3({
y: this.y + rhs.y,
x: this.x + rhs.x,
z: this.z + rhs.z
});
}
@:op(A - B)
public function sub(rhs:Vec3<Float>):Pos3 {
return new Pos3({
y: this.y - rhs.y,
x: this.x - rhs.x,
z: this.z - rhs.z
});
}
@:op(A * B)
public function multiplyScalar(rhs:Float):Pos3 {
return new Pos3({
y: this.y * rhs,
x: this.x * rhs,
z: this.z * rhs
});
}
@:op(A / B)
public function divideScalar(rhs:Float):Pos3 {
return new Pos3({
y: this.y / rhs,
x: this.x / rhs,
z: this.z / rhs
});
}
@:op(-A)
public function negate():Pos3 {
return new Pos3({
y: -this.y,
x: -this.x,
z: -this.z
});
}
public function dot(rhs:Vec3<Float>):Float {
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
}
public function cross(rhs:Vec3<Float>):Pos3 {
return new Pos3({
x: this.y * rhs.z - this.z * rhs.y,
y: this.z * rhs.x - this.x * rhs.z,
z: this.x * rhs.y - this.y * rhs.x
});
}
public function normalize():Pos3 {
var l = length();
return multiplyScalar(1 / l);
}
public function length():Float {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
@:op(A == B)
public function equals(rhs:Pos3):Bool {
return close(rhs);
}
@:op(A != B)
public function notEquals(rhs:Pos3):Bool {
return !close(rhs);
}
public function close(rhs:Pos3, epsilon:Float = 0.001):Bool {
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
}
public function toString():String {
return 'Pos3(${this.x}, ${this.y}, ${this.z})';
}
public function round():Pos3 {
return new Pos3({
x: Math.fround(this.x),
y: Math.fround(this.y),
z: Math.fround(this.z)
});
}
public function distance(rhs:Pos3):Float {
return Math.sqrt(Math.pow(this.x - rhs.x, 2) + Math.pow(this.y - rhs.y, 2) + Math.pow(this.z - rhs.z, 2));
}
}

69
src/lib/PriorityQueue.hx Normal file
View File

@@ -0,0 +1,69 @@
package lib;
using Lambda;
abstract PriorityQueue<T>(Array<{prio:Float, val:T}>) {
public inline function new() {
this = [];
}
public function containsElement(element:T):Bool {
return this.exists((e) -> e.val == element);
}
public function isEmpty():Bool {
return this.length == 0;
}
public function insert(e:T, prio:Float) {
this.push({prio: prio, val: e});
bubbleUp(this.length - 1);
}
public function extractMin():Null<T> {
if (this.length == 0) {
return null;
}
swap(0, this.length - 1);
var minElement = this.pop();
bubbleDown(0);
return minElement.val;
}
private function bubbleUp(index:Int) {
var parentIndex:Int = Math.floor((index - 1) / 2);
if (index > 0 && this[index].prio < this[parentIndex].prio) {
swap(index, parentIndex);
bubbleUp(parentIndex);
}
}
private function bubbleDown(index:Int) {
var leftIndex = 2 * index + 1;
var rightIndex = 2 * index + 2;
var smallest = index;
if (leftIndex < this.length && this[leftIndex].prio < this[smallest].prio) {
smallest = leftIndex;
}
if (rightIndex < this.length && this[rightIndex].prio < this[smallest].prio) {
smallest = rightIndex;
}
if (smallest != index) {
swap(index, smallest);
bubbleDown(smallest);
}
}
private function swap(i:Int, j:Int) {
var tmp = this[i];
this[i] = this[j];
this[j] = tmp;
}
}

View File

@@ -1,10 +1,12 @@
package lib; package lib;
class Rect { import kernel.peripherals.Screen;
private final tl:Pos;
private final br:Pos;
public function new(p1:Pos, p2:Pos) { class Rect {
public final tl:ScreenPos;
public final br:ScreenPos;
public function new(p1:ScreenPos, p2:ScreenPos) {
this.tl = { this.tl = {
x: MathI.min(p1.x, p2.x), x: MathI.min(p1.x, p2.x),
y: MathI.min(p1.y, p2.y) y: MathI.min(p1.y, p2.y)
@@ -20,11 +22,11 @@ class Rect {
return getWidth() * getHight(); return getWidth() * getHight();
} }
public function isInside(p:Pos):Bool { public function isInside(p:ScreenPos):Bool {
return (p.x >= tl.x && p.x <= br.x) && (p.y >= tl.y && p.y <= br.y); return (p.x >= tl.x && p.x <= br.x) && (p.y >= tl.y && p.y <= br.y);
} }
public function isOutside(p:Pos):Bool { public function isOutside(p:ScreenPos):Bool {
return !this.isInside(p); return !this.isInside(p);
} }
@@ -36,7 +38,7 @@ class Rect {
return br.x - tl.x; return br.x - tl.x;
} }
public function offset(pos:Pos) { public function offset(pos:ScreenPos) {
tl.x += pos.x; tl.x += pos.x;
tl.y += pos.y; tl.y += pos.y;
br.x += pos.x; br.x += pos.x;

34
src/lib/ScreenPos.hx Normal file
View File

@@ -0,0 +1,34 @@
package lib;
import lib.Vec.Vec2;
/**
Reporesents a Point in a 2D `Int` System.
Basicly a wrapper for Vec2<Int> with some extra functions.
**/
@:forward(x, y)
abstract ScreenPos(Vec2<Int>) from Vec2<Int> to Vec2<Int> {
inline public function new(i:Vec2<Int>) {
this = i;
}
@:op(A + B)
public inline function add(rhs:Vec2<Int>):ScreenPos {
return this.add(rhs);
}
@:op(A - B)
public inline function sub(rhs:Vec2<Int>):ScreenPos {
return this.sub(rhs);
}
@:op(A * B)
public inline function multiply(rhs:Vec2<Int>):ScreenPos {
return this.multiply(rhs);
}
@:op(-A)
public inline function negate():ScreenPos {
return this.negate();
}
}

55
src/lib/SinglePromise.hx Normal file
View File

@@ -0,0 +1,55 @@
package lib;
import kernel.EndOfLoop;
using tink.CoreApi;
class SinglePromise<T> {
private var inProgress:Bool = false;
private var interalPromise:Promise<T> = null;
private var interalResolve:T->Void = null;
private var interalReject:(Error->Void) = null;
private final activate:Void->Void;
public function new(activate:Void->Void) {
this.activate = activate;
}
public function request():Promise<T> {
if (this.inProgress) {
trace("Is progress");
return this.interalPromise;
}
this.inProgress = true;
this.interalPromise = new Promise((resolve, reject) -> {
this.interalResolve = resolve;
this.interalReject = reject;
this.activate();
return null;
});
return this.interalPromise;
}
public function isRunning():Bool {
return this.inProgress;
}
public function resolve(val:T) {
if (this.inProgress) {
this.inProgress = false;
this.interalResolve(val);
}
}
public function reject(err:Error) {
if (this.inProgress) {
this.inProgress = false;
this.interalReject(err);
}
}
}

View File

@@ -0,0 +1,64 @@
package lib;
import kernel.Timer;
using tink.CoreApi;
class SingleTimeoutPromise<T> {
private var inProgress:Bool = false;
private var interalPromise:Promise<T> = null;
private var interalResolve:T->Void = null;
private var interalReject:(Error->Void) = null;
private var timer:Timer = null;
private final activate:Void->Void;
private final timeout:Int;
public function new(timeout:Int, activate:Void->Void) {
this.activate = activate;
this.timeout = timeout;
}
public function request():Promise<T> {
if (this.inProgress) {
return this.interalPromise;
}
this.inProgress = true;
this.interalPromise = new Promise((resolve, reject) -> {
this.interalResolve = resolve;
this.interalReject = reject;
this.activate();
this.timer = new Timer(this.timeout, () -> {
this.reject(new Error("Timeout"));
});
return null;
});
return this.interalPromise;
}
public function isRunning():Bool {
return this.inProgress;
}
public function resolve(val:T) {
if (this.inProgress) {
this.inProgress = false;
this.timer.cancle();
this.interalResolve(val);
}
}
public function reject(err:Error) {
if (this.inProgress) {
this.inProgress = false;
this.timer.cancle();
this.interalReject(err);
}
}
}

49
src/lib/Tags.hx Normal file
View File

@@ -0,0 +1,49 @@
package lib;
import lib.PaN;
using Lambda;
using lua.Table;
/**
Represents a list a minecraft tags.
Needed because tags are SOMETIMES retuned not as arrays.
**/
@:forward
enum abstract Tags(Array<PaN>) from Array<PaN> to Array<PaN> {
inline public function new(arr:Array<String>) {
this = arr;
}
/**
From values that are object with the tag as the filed and the value set to true.
**/
@:from
public static function fromBoolTable(from:Table<String, Bool>) {
var rtn = [];
for (k => _ in Table.toMap(from)) {
rtn.push(k);
}
return new Tags(rtn);
}
/**
From values that are lua arrays.
**/
@:from
public static function fromIntTable(from:Table<Int, String>) {
return new Tags(from.toArray());
}
public function sortByName():Tags {
var copy = this.copy();
copy.sort((a:String, b:String) -> {
return if (a < b) -1 else 1;
});
return copy;
}
}

View File

@@ -1,6 +1,6 @@
package lib; package lib;
@:structInit class Vec2<T> { @:structInit class Vec2<T:Float> {
public var x:T; public var x:T;
public var y:T; public var y:T;
@@ -8,16 +8,96 @@ package lib;
this.x = x; this.x = x;
this.y = y; this.y = y;
} }
public function add(rhs:Vec2<T>):Vec2<T> {
return {
y: this.y + rhs.y,
x: this.x + rhs.x,
};
} }
@:structInit class Vec3<T> { public function sub(rhs:Vec2<T>):Vec2<T> {
public var x:T; return {
public var y:T; y: this.y - rhs.y,
public var z:T; x: this.x - rhs.x,
};
}
public function multiply(rhs:Vec2<T>):Vec2<T> {
return {
y: this.y * rhs.y,
x: this.x * rhs.x,
};
}
public function negate():Vec2<T> {
return {
y: -this.y,
x: -this.x,
};
}
}
@:structInit class Vec3<T:Float> {
public final x:T;
public final y:T;
public final z:T;
public function new(x:T, y:T, z:T) { public function new(x:T, y:T, z:T) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z; this.z = z;
} }
public function add(rhs:Vec3<T>):Vec3<T> {
return {
x: this.x + rhs.x,
y: this.y + rhs.y,
z: this.z + rhs.z
};
}
public function sub(rhs:Vec3<T>):Vec3<T> {
return {
x: this.x - rhs.x,
y: this.y - rhs.y,
z: this.z - rhs.z
};
}
public function multiplyScalar(rhs:T):Vec3<T> {
return {
x: this.x * rhs,
y: this.y * rhs,
z: this.z * rhs
};
}
public function negate():Vec3<T> {
return {
x: -this.x,
y: -this.y,
z: -this.z
};
}
public function dot(rhs:Vec3<T>):Float {
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
}
public function cross(rhs:Vec3<T>):Vec3<T> {
return {
x: this.y * rhs.z - this.z * rhs.y,
y: this.z * rhs.x - this.x * rhs.z,
z: this.x * rhs.y - this.y * rhs.x
};
}
public function length():Float {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
public function distance(rhs:Vec3<T>):Float {
return Math.sqrt(Math.pow(this.x - rhs.x, 2) + Math.pow(this.y - rhs.y, 2) + Math.pow(this.z - rhs.z, 2));
}
} }

86
src/lib/WorldPos.hx Normal file
View File

@@ -0,0 +1,86 @@
package lib;
import lib.Vec.Vec2;
import lib.Vec.Vec3;
/**
Reporesents a Point in a 3D `Float` System.
Basicly a wrapper for Vec3<Float> with some extra functions.
`Y` represents the height of the point.
**/
@:forward
abstract WorldPos(Vec3<Float>) from Vec3<Float> to Vec3<Float> {
public inline function new(v:Vec3<Float>) {
this = v;
}
@:op(A + B)
public inline function add(rhs:WorldPos):WorldPos {
return this.add(rhs);
}
@:op(A - B)
public inline function sub(rhs:Vec3<Float>):WorldPos {
return this.sub(rhs);
}
@:op(A * B)
public inline function multiplyScalar(rhs:Float):WorldPos {
return this.multiplyScalar(rhs);
}
@:op(A / B)
public function divideScalar(rhs:Float):WorldPos {
return {
y: this.y / rhs,
x: this.x / rhs,
z: this.z / rhs
};
}
@:op(-A)
public function negate():WorldPos {
return this.negate();
}
public function normalize():WorldPos {
var l = this.length();
return multiplyScalar(1 / l);
}
@:op(A == B)
public function equals(rhs:WorldPos):Bool {
return close(rhs);
}
@:op(A != B)
public function notEquals(rhs:WorldPos):Bool {
return !close(rhs);
}
public function close(rhs:WorldPos, epsilon:Float = 0.001):Bool {
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
}
public function toString():String {
return 'WorldPos(${this.x}, ${this.y}, ${this.z})';
}
/**
Returns the chunk the position is in.
**/
public function chunk():Vec2<Int> {
var x = Math.floor(this.x / 16);
var z = Math.floor(this.z / 16);
return new Vec2(x, z);
}
public function round():WorldPos {
return new WorldPos({
x: Math.fround(this.x),
y: Math.fround(this.y),
z: Math.fround(this.z)
});
}
}

View File

@@ -1,7 +1,7 @@
package lib.ui; package lib.ui;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
import lib.Pos; import lib.ScreenPos;
import lib.Rect; import lib.Rect;
import kernel.ui.Pixel; import kernel.ui.Pixel;
@@ -10,7 +10,7 @@ abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pix
this = [[]]; this = [[]];
} }
public inline function set(i:Pos, pixel:Pixel) { public inline function set(i:ScreenPos, pixel:Pixel) {
if (this[i.y] == null) { if (this[i.y] == null) {
this[i.y] = []; this[i.y] = [];
} }
@@ -18,7 +18,7 @@ abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pix
this[i.y][i.x] = pixel; this[i.y][i.x] = pixel;
} }
public inline function get(i:Pos):Null<Pixel> { public inline function get(i:ScreenPos):Null<Pixel> {
if (this[i.y] == null) { if (this[i.y] == null) {
return null; return null;
} }
@@ -26,11 +26,11 @@ abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pix
return this[i.y][i.x]; return this[i.y][i.x];
} }
public function keyValueIterator():KeyValueIterator<Pos, Pixel> { public function keyValueIterator():KeyValueIterator<ScreenPos, Pixel> {
return new CanvasKeyValueIterator(this); return new CanvasKeyValueIterator(this);
} }
public function combine(other:Canvas, offset:Pos) { public function combine(other:Canvas, offset:ScreenPos) {
for (key => value in other) { for (key => value in other) {
if (value == null) { if (value == null) {
continue; continue;
@@ -127,8 +127,8 @@ abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pix
class CanvasKeyValueIterator { class CanvasKeyValueIterator {
private final canvas:Array<Array<Pixel>>; private final canvas:Array<Array<Pixel>>;
private var index:Null<Pos> = {x: 0, y: 0}; private var index:Null<ScreenPos> = {x: 0, y: 0};
private var nextIndex:Null<Pos> = null; private var nextIndex:Null<ScreenPos> = null;
@:allow(lib.ui.Canvas) @:allow(lib.ui.Canvas)
private function new(canvas:Array<Array<Pixel>>) { private function new(canvas:Array<Array<Pixel>>) {
@@ -141,7 +141,7 @@ class CanvasKeyValueIterator {
this.nextIndex = nextValidPixel(); this.nextIndex = nextValidPixel();
} }
private function isValidPos(pos:Pos):Bool { private function isValidPos(pos:ScreenPos):Bool {
if (this.canvas[pos.y] == null) { if (this.canvas[pos.y] == null) {
return false; return false;
} }
@@ -157,7 +157,7 @@ class CanvasKeyValueIterator {
return this.index != null; return this.index != null;
} }
private function nextValidPixel():Null<Pos> { private function nextValidPixel():Null<ScreenPos> {
if (this.index == null) { if (this.index == null) {
return null; return null;
} }
@@ -182,7 +182,7 @@ class CanvasKeyValueIterator {
return null; return null;
} }
public function next():{key:Pos, value:Pixel} { public function next():{key:ScreenPos, value:Pixel} {
var rtn = { var rtn = {
key: this.index, key: this.index,
value: this.canvas[this.index.y][this.index.x] value: this.canvas[this.index.y][this.index.x]

View File

@@ -1,17 +1,17 @@
package lib.ui; package lib.ui;
import lib.Pos; import lib.ScreenPos;
import kernel.ButtonType; import kernel.ButtonType;
using tink.CoreApi; using tink.CoreApi;
typedef UIEvents = { typedef UIEvents = {
public var ?onClick:Callback<{button:ButtonType, pos:Pos}>; public var ?onClick:Callback<{button:ButtonType, pos:ScreenPos}>;
public var ?onKey:Callback<{keyCode:Int, isHeld:Bool}>; public var ?onKey:Callback<{keyCode:Int, isHeld:Bool}>;
public var ?onKeyUp:Callback<Int>; public var ?onKeyUp:Callback<Int>;
public var ?onMouseDrag:Callback<{button:ButtonType, pos:Pos}>; public var ?onMouseDrag:Callback<{button:ButtonType, pos:ScreenPos}>;
public var ?onMouseScroll:Callback<{dir:Int, pos:Pos}>; public var ?onMouseScroll:Callback<{dir:Int, pos:ScreenPos}>;
public var ?onMouseUp:Callback<{button:ButtonType, pos:Pos}>; public var ?onMouseUp:Callback<{button:ButtonType, pos:ScreenPos}>;
public var ?onPaste:Callback<String>; public var ?onPaste:Callback<String>;
public var ?onChar:Callback<String>; public var ?onChar:Callback<String>;
} }

View File

@@ -15,7 +15,7 @@ abstract EventMap(Array<{bound:Rect, delegate:IUIEventDelegate}>) {
} }
} }
public function findResponseableDelegate(pos:Pos):IUIEventDelegate { public function findResponseableDelegate(pos:ScreenPos):IUIEventDelegate {
for (i in 0...this.length) { for (i in 0...this.length) {
var newi = (this.length - 1) - i; var newi = (this.length - 1) - i;
var element = this[newi]; var element = this[newi];

View File

@@ -3,5 +3,5 @@ package lib.ui.elements;
import lib.ui.rendere.IUIEventDelegate; import lib.ui.rendere.IUIEventDelegate;
interface IUIElement extends IUIEventDelegate { interface IUIElement extends IUIEventDelegate {
public function render(bounds:Pos):Canvas; public function render(bounds:ScreenPos):Canvas;
} }

View File

@@ -1,7 +1,7 @@
package lib.ui.elements; package lib.ui.elements;
class RootElement implements IUIElement { class RootElement implements IUIElement {
private var children:Array<IUIElement>; private var children:Array<IUIElement> = [];
private final eventManager:UIEventManager = new UIEventManager(); private final eventManager:UIEventManager = new UIEventManager();
private var title:String = ""; private var title:String = "";
@@ -21,15 +21,15 @@ class RootElement implements IUIElement {
this.children = children; this.children = children;
} }
public function render(bounds:Pos):Canvas { public function render(bounds:ScreenPos):Canvas {
var canvas = new Canvas(); var canvas = new Canvas();
var offset = new Pos({x: 0, y: 0}); var offset = new ScreenPos({x: 0, y: 0});
if (hasTitle()) { if (hasTitle()) {
var title = new TextElement(this.title); var title = new TextElement(this.title);
var halfWidth = Math.floor(bounds.x / 2) - Math.floor(this.title.length / 2); var halfWidth = Math.floor(bounds.x / 2) - Math.floor(this.title.length / 2);
canvas.combine(title.render(bounds), {x: halfWidth, y: offset.y}); canvas.combine(title.render(bounds), {x: halfWidth, y: offset.y});
offset = new Pos({x: 0, y: 1}); offset = new ScreenPos({x: 0, y: 1});
} }
this.eventManager.clearMap(); this.eventManager.clearMap();
@@ -42,7 +42,7 @@ class RootElement implements IUIElement {
this.eventManager.addMapElement(child, bounds); this.eventManager.addMapElement(child, bounds);
canvas.combine(childCanvas, offset); canvas.combine(childCanvas, offset);
offset = new Pos({x: 0, y: offset.y + bounds.getHight() + 1}); offset = new ScreenPos({x: 0, y: offset.y + bounds.getHight() + 1});
} }
return canvas; return canvas;

View File

@@ -26,7 +26,7 @@ class TextElement implements IUIElement {
return uiEvents; return uiEvents;
} }
public function render(bounds:Pos):Canvas { public function render(bounds:ScreenPos):Canvas {
var canvas = new Canvas(); var canvas = new Canvas();
var x = 0; var x = 0;

View File

@@ -30,7 +30,7 @@ class UIEventManager implements IUIEventDelegate {
}; };
} }
private function handleClickEvent(params:{button:ButtonType, pos:Pos}) { private function handleClickEvent(params:{button:ButtonType, pos:ScreenPos}) {
var element = this.map.findResponseableDelegate(params.pos); var element = this.map.findResponseableDelegate(params.pos);
if (element == null) { if (element == null) {
return; return;
@@ -48,11 +48,11 @@ class UIEventManager implements IUIEventDelegate {
private function handlePasteEvent(text:String) {} private function handlePasteEvent(text:String) {}
private function handleMouseUpEvent(params:{button:ButtonType, pos:Pos}) {} private function handleMouseUpEvent(params:{button:ButtonType, pos:ScreenPos}) {}
private function handleMouseScrollEvent(params:{dir:Int, pos:Pos}) {} private function handleMouseScrollEvent(params:{dir:Int, pos:ScreenPos}) {}
private function handleMouseDragEvent(params:{button:ButtonType, pos:Pos}) {} private function handleMouseDragEvent(params:{button:ButtonType, pos:ScreenPos}) {}
private function handleKeyEvent(params:{keyCode:Int, isHeld:Bool}) {} private function handleKeyEvent(params:{keyCode:Int, isHeld:Bool}) {}