237 lines
5.4 KiB
Haxe
237 lines
5.4 KiB
Haxe
package lib.turtle;
|
|
|
|
import kernel.turtle.Types.ToolSide;
|
|
import kernel.log.Log;
|
|
import kernel.turtle.TurtleSlot;
|
|
import kernel.turtle.Types.InteractDirections;
|
|
import kernel.turtle.Turtle;
|
|
|
|
using Lambda;
|
|
using tink.CoreApi;
|
|
|
|
/**
|
|
A wrapper for the native turtle inventory functions. Adds usefullt helper functions.
|
|
**/
|
|
class InvManager {
|
|
public static function place(item:Item, dir:InteractDirections):Outcome<Noise, String> {
|
|
var invState = new InvState();
|
|
var slot = invState.getSlotWithMinCountForItem(item);
|
|
if (slot == null) {
|
|
return Failure("Item not in inventory");
|
|
}
|
|
|
|
Turtle.selectSlot(slot);
|
|
return Turtle.place(dir);
|
|
}
|
|
|
|
/**
|
|
Cleans up turtle inventory. Moves items together.
|
|
This should be run in a turtle thread.
|
|
**/
|
|
public static function defrag() {
|
|
var invState = new InvState();
|
|
var items = invState.getSlotsForItems();
|
|
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function equipTool(item:Item, side:ToolSide):Outcome<TurtleSlot, String> {
|
|
var invState = new InvState();
|
|
var toolSlot = invState.findItemSlot(item);
|
|
|
|
if (toolSlot == null) {
|
|
return Failure("Item not found");
|
|
}
|
|
|
|
Turtle.selectSlot(toolSlot);
|
|
var result = Turtle.equip(side);
|
|
|
|
switch result {
|
|
case Success(_):
|
|
return Success(toolSlot);
|
|
case Failure(failure):
|
|
return Failure(failure);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Check what items in the inventory can be used as fuel.
|
|
**/
|
|
public static function getCombustableItems():Array<Item> {
|
|
var invState = new InvState();
|
|
var rtn = new Map<Item, Noise>();
|
|
|
|
for (slot => v in invState) {
|
|
if (rtn.exists(v.name)) {
|
|
continue;
|
|
}
|
|
|
|
Turtle.selectSlot(slot);
|
|
var result = Turtle.canRefultWithSlot();
|
|
|
|
if (result) {
|
|
rtn.set(v.name, true); // How do i set a Noise type???
|
|
}
|
|
}
|
|
|
|
return [for (k => _ in rtn) k];
|
|
}
|
|
|
|
/**
|
|
Refule to a specific value.
|
|
Use array items first (in order).
|
|
Set `useAll` to false to don't use items not listes in priority.
|
|
**/
|
|
public static function refuel(to:Int, priority:Array<Item>, useAll:Bool = true) {
|
|
var invState = new InvState();
|
|
var fuelTo = MathI.min(to, Turtle.getFuelLimit());
|
|
|
|
if (Turtle.getFuelLevel() >= fuelTo) {
|
|
return;
|
|
}
|
|
|
|
var items = invState.getSlotsForItems();
|
|
|
|
for (item in priority) {
|
|
if (!items.exists(item)) {
|
|
continue;
|
|
}
|
|
|
|
var allSlotsWithItemOrderd = items.get(item);
|
|
allSlotsWithItemOrderd.sort((a, b) -> {
|
|
return invState.get(a).count - invState.get(b).count;
|
|
});
|
|
|
|
var fuelPerItem = 0;
|
|
|
|
for (slot in allSlotsWithItemOrderd) {
|
|
var slotInfo = invState.get(slot);
|
|
Turtle.selectSlot(slot);
|
|
|
|
if (fuelPerItem == 0) {
|
|
fuelPerItem = refuelFromSelectedSlot(slotInfo.count, fuelTo);
|
|
if (fuelPerItem == 0) {
|
|
Log.warn('Item ${slotInfo.name} is not combustable');
|
|
continue;
|
|
}
|
|
} else {
|
|
var itemsToUse = MathI.min(Math.ceil((fuelTo - (Turtle.getFuelLevel())) / fuelPerItem), slotInfo.count);
|
|
Turtle.refuel(itemsToUse);
|
|
}
|
|
|
|
if (Turtle.getFuelLevel() >= fuelTo) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If not reached our goal yet, check all other items to use as fuel.
|
|
if (!useAll) {
|
|
return;
|
|
}
|
|
|
|
for (slot => slotInfo in invState) {
|
|
// Skip items that we already checked
|
|
// Also the invstate might be invalid for these slots
|
|
if (priority.contains(slotInfo.name)) {
|
|
continue;
|
|
}
|
|
|
|
Turtle.selectSlot(slot);
|
|
refuelFromSelectedSlot(slotInfo.count, fuelTo);
|
|
|
|
if (Turtle.getFuelLevel() >= fuelTo) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Refuel from selected slot. The ammount of items in the slot must be given.
|
|
The `to` arg should be the desired fuel level.
|
|
Returns the ammount of fuel on item of this kind refuels.
|
|
Returns 0 of item is not combustable.
|
|
**/
|
|
private static function refuelFromSelectedSlot(count:Int, to:Int):Int {
|
|
var fuelLevel = Turtle.getFuelLevel();
|
|
|
|
if (!Turtle.refuel(1).isSuccess()) {
|
|
return 0;
|
|
}
|
|
|
|
var fuelPerItem = Turtle.getFuelLevel() - fuelLevel;
|
|
var itemsToUse = MathI.min(Math.ceil((to - (fuelLevel + fuelPerItem)) / fuelPerItem), count - 1);
|
|
|
|
Turtle.refuel(itemsToUse);
|
|
|
|
return fuelPerItem;
|
|
}
|
|
}
|