184 lines
4.0 KiB
Haxe
184 lines
4.0 KiB
Haxe
package lib.turtle;
|
|
|
|
import kernel.log.Log;
|
|
import kernel.turtle.Types.TurtleSlot;
|
|
import kernel.turtle.Types.InteractDirections;
|
|
import kernel.turtle.Turtle;
|
|
|
|
using Lambda;
|
|
using tink.CoreApi;
|
|
|
|
typedef InvState = Map<TurtleSlot, {count:Int, name:Item, max:Int}>;
|
|
|
|
/**
|
|
A wrapper for the native turtle inventory functions. Adds usefullt helper functions.
|
|
**/
|
|
class InvManager {
|
|
public function new() {}
|
|
|
|
private static function getInvState() {
|
|
var invState:InvState = new Map();
|
|
for (i in 0...Turtle.MAX_SLOTS) {
|
|
var detail = Turtle.getItemDetail(i);
|
|
var spaceLeft = Turtle.getItemSpace(i);
|
|
|
|
switch detail {
|
|
case Some(v):
|
|
invState.set(i, {
|
|
count: v.count,
|
|
name: v.name,
|
|
max: spaceLeft + v.count
|
|
});
|
|
case None:
|
|
invState.remove(i);
|
|
}
|
|
}
|
|
return invState;
|
|
}
|
|
|
|
public static function getItemCountInfo():Map<Item, Int> {
|
|
var invState = getInvState();
|
|
var rtn:Map<Item, Int> = new Map();
|
|
|
|
for (slot in invState) {
|
|
if (!rtn.exists(slot.name)) {
|
|
rtn.set(slot.name, slot.count);
|
|
continue;
|
|
}
|
|
|
|
var count = rtn.get(slot.name);
|
|
rtn.set(slot.name, count + slot.count);
|
|
}
|
|
|
|
return rtn;
|
|
}
|
|
|
|
private static function getSlotWithMinCountForItem(item:Item, invState:InvState):Null<TurtleSlot> {
|
|
var min:Int = 99; // TODO: is there something like MAX_INT ???
|
|
var minSlot:TurtleSlot = -1;
|
|
|
|
for (k => slot in invState) {
|
|
if (slot.name != item) {
|
|
continue;
|
|
}
|
|
|
|
if (slot.count < min) {
|
|
min = slot.count;
|
|
minSlot = k;
|
|
}
|
|
};
|
|
|
|
return minSlot == -1 ? null : minSlot;
|
|
}
|
|
|
|
public static function place(item:Item, dir:InteractDirections):Outcome<Noise, String> {
|
|
var invState = getInvState();
|
|
var slot = getSlotWithMinCountForItem(item, invState);
|
|
if (slot == null) {
|
|
return Failure("Item not in inventory");
|
|
}
|
|
|
|
return placeSlot(slot, dir);
|
|
}
|
|
|
|
private static function placeSlot(slot:TurtleSlot, dir:InteractDirections):Outcome<Noise, String> {
|
|
Turtle.selectSlot(slot); // TODO: handle error
|
|
return Turtle.place(dir);
|
|
}
|
|
|
|
private static function getSlotsForItems(invState:InvState):Map<Item, Array<TurtleSlot>> {
|
|
var rtn:Map<Item, Array<TurtleSlot>> = new Map();
|
|
|
|
for (k => slot in invState) {
|
|
if (!rtn.exists(slot.name)) {
|
|
rtn.set(slot.name, [k]);
|
|
continue;
|
|
}
|
|
|
|
var value = rtn.get(slot.name);
|
|
value.push(k);
|
|
rtn.set(slot.name, value);
|
|
}
|
|
|
|
return rtn;
|
|
}
|
|
|
|
/**
|
|
Cleans up turtle inventory. Moves items together.
|
|
This should be run in a turtle thread.
|
|
**/
|
|
public static function defrag() {
|
|
var invState = getInvState();
|
|
var items = getSlotsForItems(invState);
|
|
|
|
for (item in items) {
|
|
defragItem(invState, item);
|
|
}
|
|
}
|
|
|
|
private static function defragItem(invState:InvState, slots:Array<TurtleSlot>) {
|
|
// Sort slots by items inside.
|
|
slots.sort((a, b) -> {
|
|
return invState.get(a).count - invState.get(b).count;
|
|
});
|
|
|
|
var maxInSlot = invState.get(slots[0]).max;
|
|
|
|
// Loop from both sides. Move the slots with the lowest count into the fullest.
|
|
var topIndex = slots.length - 1;
|
|
|
|
for (k => slot in slots) {
|
|
if (topIndex < k) {
|
|
return;
|
|
}
|
|
|
|
var count = invState.get(slot).count;
|
|
|
|
if (count == maxInSlot) {
|
|
return;
|
|
}
|
|
|
|
var loopProtect = 20;
|
|
|
|
while (loopProtect > 0) {
|
|
loopProtect--;
|
|
|
|
if (topIndex < k) {
|
|
return;
|
|
}
|
|
|
|
var topCount = invState.get(slots[topIndex]).count;
|
|
var spaceInTop = maxInSlot - topCount;
|
|
|
|
if (spaceInTop == 0) {
|
|
topIndex--;
|
|
continue;
|
|
}
|
|
|
|
var transferCount = MathI.min(count, spaceInTop);
|
|
if (!Turtle.transfer(slot, slots[topIndex], transferCount).isSuccess()) {
|
|
Log.error("Failed to transfer items");
|
|
}
|
|
|
|
var topState = invState.get(slots[topIndex]);
|
|
topState.count += transferCount;
|
|
invState.set(slots[topIndex], topState);
|
|
|
|
var botState = invState.get(slot);
|
|
botState.count -= transferCount;
|
|
invState.set(slot, botState);
|
|
|
|
count -= transferCount;
|
|
|
|
if (count <= 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (loopProtect < 0) {
|
|
Log.silly("Loopprotection triggerd");
|
|
}
|
|
}
|
|
}
|
|
}
|