Compare commits
7 Commits
9d6e8a366b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
c665401fd8
|
|||
|
2799a0be3d
|
|||
|
e527dd5b6a
|
|||
|
fe17b4fd67
|
|||
|
90b4015ba1
|
|||
|
2dd85c2b26
|
|||
|
1d60e13792
|
@@ -1,5 +1,6 @@
|
||||
package bin;
|
||||
|
||||
import kernel.turtle.Types.ToolSide;
|
||||
import lib.turtle.Helper;
|
||||
import kernel.turtle.TurtleMutex;
|
||||
import kernel.turtle.Turtle;
|
||||
@@ -116,6 +117,28 @@ class TurtleCtl extends CLIAppBase {
|
||||
var len = args.getInt("length");
|
||||
return asynPerform(Helper.combine.bind([Turtle.digEmpty.bind(Front), Turtle.forward, Turtle.digEmpty.bind(Up)]), len);
|
||||
}, [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> {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package kernel.gps;
|
||||
|
||||
import lib.SinglePromise;
|
||||
import kernel.log.Log;
|
||||
import kernel.turtle.Turtle;
|
||||
import lib.WorldPos;
|
||||
|
||||
@@ -71,6 +70,9 @@ class INS {
|
||||
}
|
||||
|
||||
private static function move(dir:Null<WorldPos>) {
|
||||
if (alignPromise.isRunning()) {
|
||||
return;
|
||||
}
|
||||
var pos = GPS.getPosition();
|
||||
if (pos == null || dir == null)
|
||||
return;
|
||||
@@ -86,53 +88,45 @@ class INS {
|
||||
return alignPromise.request();
|
||||
}
|
||||
|
||||
public static function startAlign():Promise<Noise> {
|
||||
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;
|
||||
}
|
||||
public static function startAlign():Void {
|
||||
if (Turtle.getFuelLevel() < 2) {
|
||||
alignPromise.reject(new Error("Not enough fuel to align"));
|
||||
return;
|
||||
}
|
||||
|
||||
GPS.locate().handle((result) -> {
|
||||
switch result {
|
||||
case Failure(err):
|
||||
Log.warn("GPS not available for 1st position");
|
||||
reject(new Error("GPS not available"));
|
||||
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();
|
||||
|
||||
if (moved == -1) {
|
||||
alignPromise.reject(new Error("Can't move"));
|
||||
return;
|
||||
case Success(pos1):
|
||||
var moved = tryMoving();
|
||||
}
|
||||
|
||||
if (moved == -1) {
|
||||
Log.warn("Can't move");
|
||||
reject(new Error("Can't move"));
|
||||
return;
|
||||
}
|
||||
|
||||
GPS.locate().handle((result) -> {
|
||||
switch result {
|
||||
case Failure(err):
|
||||
Log.warn("GPS not available for 2nd position");
|
||||
reject(new Error("GPS not available for 2nd position"));
|
||||
GPS.locate().handle((result2) -> {
|
||||
switch result2 {
|
||||
case Failure(err):
|
||||
alignPromise.reject(new Error("GPS not available for 2nd position: " + err));
|
||||
return;
|
||||
case Success(pos2):
|
||||
var cHeading = calcHeading(pos1, pos2, moved);
|
||||
if (cHeading == null) {
|
||||
alignPromise.reject(new Error("Can't calculate heading"));
|
||||
return;
|
||||
case Success(pos2):
|
||||
var cHeading = calcHeading(pos1, pos2, moved);
|
||||
if (cHeading == null) {
|
||||
Log.error("Can't calculate heading");
|
||||
reject(new Error("Can't calculate heading"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
heading = cHeading;
|
||||
moveBack(moved);
|
||||
GPS.setINSPosition(pos1);
|
||||
heading = cHeading;
|
||||
moveBack(moved);
|
||||
GPS.setINSPosition(pos1);
|
||||
|
||||
resolve(Noise);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return null;
|
||||
alignPromise.resolve(Noise);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -172,20 +166,14 @@ class INS {
|
||||
private static function moveBack(moved:Int) {
|
||||
if (moved == 0) {
|
||||
Turtle.forward();
|
||||
// cc.Turtle.forward();
|
||||
} else if (moved == 1) {
|
||||
Turtle.back();
|
||||
// cc.Turtle.back();
|
||||
} else if (moved == 2) {
|
||||
Turtle.back();
|
||||
// cc.Turtle.back();
|
||||
Turtle.turnRight();
|
||||
// cc.Turtle.turnRight();
|
||||
} else if (moved == 3) {
|
||||
Turtle.forward();
|
||||
// cc.Turtle.forward();
|
||||
Turtle.turnRight();
|
||||
// cc.Turtle.turnRight();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
76
src/kernel/peripherals/GeoScanner.hx
Normal file
76
src/kernel/peripherals/GeoScanner.hx
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package kernel.peripherals;
|
||||
|
||||
import kernel.log.Log;
|
||||
import lib.Debug;
|
||||
import cc.Peripheral;
|
||||
import lib.Tags;
|
||||
import lib.Item;
|
||||
import cc.periphs.ItemStorage.ReducedItemInfo;
|
||||
import lua.Table;
|
||||
@@ -22,7 +26,7 @@ class ItemInfo {
|
||||
class DetailedItemInfo extends ItemInfo {
|
||||
public final displayName:String;
|
||||
public final maxCount:Int;
|
||||
public final tags:Array<String>;
|
||||
public final tags:Tags;
|
||||
public final durability:Null<Float>;
|
||||
public final damage:Null<Int>;
|
||||
public final maxDamage:Null<Int>;
|
||||
@@ -32,10 +36,7 @@ class DetailedItemInfo extends ItemInfo {
|
||||
private function new(from:cc.periphs.ItemStorage.DetailedItemInfo) {
|
||||
super(from);
|
||||
|
||||
this.tags = [];
|
||||
for (k => _ in Table.toMap(from.tags)) {
|
||||
this.tags.push(k);
|
||||
}
|
||||
this.tags = Tags.fromBoolTable(from.tags);
|
||||
|
||||
this.displayName = from.displayName;
|
||||
this.maxCount = from.maxCount;
|
||||
@@ -55,11 +56,9 @@ class Inventory implements IPeripheral {
|
||||
public static inline final TYPE_NAME:String = "inventory";
|
||||
|
||||
private final addr:String;
|
||||
private final native:cc.periphs.ItemStorage;
|
||||
|
||||
public function new(addr:String) {
|
||||
this.addr = addr;
|
||||
this.native = cc.Peripheral.wrap(addr);
|
||||
}
|
||||
|
||||
public function getAddr():String {
|
||||
@@ -74,18 +73,18 @@ class Inventory implements IPeripheral {
|
||||
Get the size of this inventory.
|
||||
**/
|
||||
public function size():Int {
|
||||
return this.native.size();
|
||||
return Peripheral.call(this.addr, "size");
|
||||
}
|
||||
|
||||
/**
|
||||
List all items in this inventory.
|
||||
**/
|
||||
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();
|
||||
|
||||
for (k => v in list) {
|
||||
rtn.set(k, new ItemInfo(v));
|
||||
rtn.set(k - 1, new ItemInfo(v));
|
||||
}
|
||||
|
||||
return rtn;
|
||||
@@ -95,7 +94,7 @@ class Inventory implements IPeripheral {
|
||||
Get detailed information about an item.
|
||||
**/
|
||||
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.
|
||||
**/
|
||||
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 {
|
||||
return this.native.pushItems(to, toSlot, limit, toSlot);
|
||||
public function pushItems(to:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int {
|
||||
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 {
|
||||
return this.native.pullItems(from, fromSlot, limit, toSlot);
|
||||
return Peripheral.call(this.addr, "pullItems", from, fromSlot + 1, limit, toSlot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package kernel.peripherals;
|
||||
|
||||
import kernel.log.Log;
|
||||
import kernel.peripherals.Modem;
|
||||
import kernel.peripherals.Screen;
|
||||
|
||||
@@ -83,6 +82,8 @@ class Peripheral {
|
||||
return getRedstone(addr);
|
||||
case Inventory.TYPE_NAME:
|
||||
return getInventory(addr);
|
||||
case GeoScanner.TYPE_NAME:
|
||||
return getGeoScanner(addr);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -180,4 +181,15 @@ class Peripheral {
|
||||
public static function getAllInventorys():Array<Inventory> {
|
||||
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)];
|
||||
}
|
||||
}
|
||||
|
||||
12
src/lib/Block.hx
Normal file
12
src/lib/Block.hx
Normal 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
114
src/lib/BlockBlockMap.hx
Normal 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
48
src/lib/BlockMap.hx
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package lib;
|
||||
|
||||
import lib.Vec.Vec2;
|
||||
import lib.Vec.Vec3;
|
||||
|
||||
/**
|
||||
@@ -51,6 +52,16 @@ abstract BlockPos(Vec3<Int>) from Vec3<Int> to Vec3<Int> {
|
||||
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.
|
||||
**/
|
||||
|
||||
49
src/lib/Tags.hx
Normal file
49
src/lib/Tags.hx
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package lib;
|
||||
|
||||
import lib.Vec.Vec2;
|
||||
import lib.Vec.Vec3;
|
||||
|
||||
/**
|
||||
@@ -65,6 +66,16 @@ abstract WorldPos(Vec3<Float>) from Vec3<Float> to Vec3<Float> {
|
||||
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),
|
||||
|
||||
Reference in New Issue
Block a user