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 { 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) { // 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 { 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 { var invState = new InvState(); var rtn = new Map(); 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, 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; } }