mirror of
https://github.com/SikongJueluo/cc-utils.git
synced 2025-12-20 13:37:49 +08:00
fix: accesscontrol toast; feature: autocraft basic; reconstruct: autocraft
fix: - accesscontrol send toast failed - advanced peripherals BlockDetailData wrong record type feature: - autocraft support multi package craft - autocraft more fast craft speed reconstruct: - CraftManager algorithm - autocraft logic
This commit is contained in:
@@ -124,9 +124,9 @@ function sendNotice(player: string, playerInfo?: PlayerInfo) {
|
|||||||
const noticeTargetPlayers = config.adminGroupConfig.groupUsers.concat(
|
const noticeTargetPlayers = config.adminGroupConfig.groupUsers.concat(
|
||||||
config.usersGroups
|
config.usersGroups
|
||||||
.filter((value) => value.isNotice)
|
.filter((value) => value.isNotice)
|
||||||
.map((value) => value.groupUsers ?? [])
|
.flatMap((value) => value.groupUsers ?? []),
|
||||||
.flat(),
|
|
||||||
);
|
);
|
||||||
|
logger.debug(`noticeTargetPlayers: ${noticeTargetPlayers.join(", ")}`);
|
||||||
|
|
||||||
for (const targetPlayer of noticeTargetPlayers) {
|
for (const targetPlayer of noticeTargetPlayers) {
|
||||||
if (!onlinePlayers.includes(targetPlayer)) continue;
|
if (!onlinePlayers.includes(targetPlayer)) continue;
|
||||||
@@ -134,6 +134,7 @@ function sendNotice(player: string, playerInfo?: PlayerInfo) {
|
|||||||
name: player,
|
name: player,
|
||||||
info: playerInfo,
|
info: playerInfo,
|
||||||
});
|
});
|
||||||
|
sleep(1);
|
||||||
}
|
}
|
||||||
releaser.release();
|
releaser.release();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
import { CraftManager } from "@/lib/CraftManager";
|
import {
|
||||||
import { CCLog } from "@/lib/ccLog";
|
CraftManager,
|
||||||
|
CraftRecipe,
|
||||||
|
CreatePackageTag,
|
||||||
|
} from "@/lib/CraftManager";
|
||||||
|
import { CCLog, LogLevel } from "@/lib/ccLog";
|
||||||
|
import { Queue } from "@/lib/datatype/Queue";
|
||||||
|
|
||||||
const logger = new CCLog("autocraft.log");
|
const logger = new CCLog("autocraft.log", { outputMinLevel: LogLevel.Info });
|
||||||
|
|
||||||
const peripheralsNames = {
|
const peripheralsNames = {
|
||||||
packagesContainer: "minecraft:chest_10",
|
// packsInventory: "minecraft:chest_14",
|
||||||
itemsContainer: "minecraft:chest_9",
|
// itemsInventory: "minecraft:chest_15",
|
||||||
packageExtractor: "create:packager_1",
|
// packageExtractor: "create:packager_3",
|
||||||
blockReader: "front",
|
blockReader: "bottom",
|
||||||
wiredModem: "back",
|
wiredModem: "right",
|
||||||
redstone: "front",
|
redstone: "left",
|
||||||
|
packsInventory: "minecraft:chest_1121",
|
||||||
|
itemsInventory: "minecraft:chest_1120",
|
||||||
|
packageExtractor: "create:packager_0",
|
||||||
};
|
};
|
||||||
|
|
||||||
const packagesContainer = peripheral.wrap(
|
const packsInventory = peripheral.wrap(
|
||||||
peripheralsNames.packagesContainer,
|
peripheralsNames.packsInventory,
|
||||||
) as InventoryPeripheral;
|
) as InventoryPeripheral;
|
||||||
const itemsContainer = peripheral.wrap(
|
const itemsInventory = peripheral.wrap(
|
||||||
peripheralsNames.itemsContainer,
|
peripheralsNames.itemsInventory,
|
||||||
) as InventoryPeripheral;
|
) as InventoryPeripheral;
|
||||||
const packageExtractor = peripheral.wrap(
|
const packageExtractor = peripheral.wrap(
|
||||||
peripheralsNames.packageExtractor,
|
peripheralsNames.packageExtractor,
|
||||||
@@ -31,87 +39,170 @@ const turtleLocalName = wiredModem.getNameLocal();
|
|||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
IDLE,
|
IDLE,
|
||||||
CHECK_PACK,
|
|
||||||
READ_RECIPE,
|
READ_RECIPE,
|
||||||
PULL_ITEMS,
|
|
||||||
CRAFT_OUTPUT,
|
CRAFT_OUTPUT,
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
const craftManager = new CraftManager(turtleLocalName);
|
const craftManager = new CraftManager(turtleLocalName, itemsInventory);
|
||||||
|
const recipesQueue = new Queue<CraftRecipe>();
|
||||||
|
const recipesWaitingMap = new Map<number, CraftRecipe[] | CraftRecipe>();
|
||||||
|
let currentState = State.IDLE;
|
||||||
|
let nextState = State.IDLE;
|
||||||
let hasPackage = redstone.getInput(peripheralsNames.redstone);
|
let hasPackage = redstone.getInput(peripheralsNames.redstone);
|
||||||
// let currentState = State.IDLE;
|
while (hasPackage) {
|
||||||
// let nextState = State.IDLE;
|
hasPackage = redstone.getInput(peripheralsNames.redstone);
|
||||||
|
logger.warn("redstone activated when init, please clear inventory");
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("AutoCraft init finished...");
|
logger.info("AutoCraft init finished...");
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// Switch state
|
||||||
|
switch (currentState) {
|
||||||
|
case State.IDLE: {
|
||||||
|
nextState = hasPackage ? State.READ_RECIPE : State.IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case State.READ_RECIPE: {
|
||||||
|
nextState = hasPackage ? State.READ_RECIPE : State.CRAFT_OUTPUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case State.CRAFT_OUTPUT: {
|
||||||
|
nextState =
|
||||||
|
recipesQueue.size() > 0
|
||||||
|
? State.CRAFT_OUTPUT
|
||||||
|
: hasPackage
|
||||||
|
? State.READ_RECIPE
|
||||||
|
: State.IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
logger.error(`Unknown state`);
|
||||||
|
nextState = hasPackage ? State.READ_RECIPE : State.IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// State logic
|
||||||
|
switch (currentState) {
|
||||||
|
case State.IDLE: {
|
||||||
if (!hasPackage) os.pullEvent("redstone");
|
if (!hasPackage) os.pullEvent("redstone");
|
||||||
hasPackage = redstone.getInput(peripheralsNames.redstone);
|
hasPackage = redstone.getInput(peripheralsNames.redstone);
|
||||||
if (!hasPackage) {
|
break;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
logger.info(`Package detected`);
|
|
||||||
|
|
||||||
const itemsInfo = packagesContainer.list();
|
case State.READ_RECIPE: {
|
||||||
for (const key in itemsInfo) {
|
logger.info(`Package detected`);
|
||||||
const slot = parseInt(key);
|
const packagesInfoRecord = packsInventory.list();
|
||||||
const item = itemsInfo[slot];
|
for (const key in packagesInfoRecord) {
|
||||||
logger.info(`${item.count}x ${item.name} in slot ${key}`);
|
const slotNum = parseInt(key);
|
||||||
|
packsInventory.pushItems(turtleLocalName, slotNum);
|
||||||
|
|
||||||
// Get package NBT
|
// Get package NBT
|
||||||
packagesContainer.pushItems(turtleLocalName, slot);
|
logger.debug(
|
||||||
const packageInfo = blockReader.getBlockData()!.Items[1];
|
`Turtle:\n${textutils.serialise(blockReader.getBlockData()!, { allow_repetitions: true })}`,
|
||||||
// log.info(textutils.serialise(packageInfo));
|
);
|
||||||
|
const packageDetailInfo = blockReader.getBlockData()?.Items[1];
|
||||||
// Get recipe
|
if (packageDetailInfo === undefined) {
|
||||||
const packageRecipes = CraftManager.getPackageRecipe(packageInfo);
|
logger.error(`Package detail info not found`);
|
||||||
|
|
||||||
// No recipe, just extract package
|
|
||||||
if (packageRecipes.isNone()) {
|
|
||||||
packageExtractor.pullItems(turtleLocalName, 1);
|
|
||||||
logger.info(`No recipe, just pass`);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract package
|
// Get OrderId and isFinal
|
||||||
// log.info(`Get recipe ${textutils.serialise(recipe)}`);
|
const packageOrderId = (packageDetailInfo.tag as CreatePackageTag)
|
||||||
|
.Fragment.OrderId;
|
||||||
|
const packageIsFinal =
|
||||||
|
(packageDetailInfo.tag as CreatePackageTag).Fragment.IsFinal > 0
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
|
||||||
|
// Get recipe
|
||||||
|
const packageRecipes =
|
||||||
|
CraftManager.getPackageRecipe(packageDetailInfo);
|
||||||
|
if (packageRecipes.isSome()) {
|
||||||
|
if (packageIsFinal) recipesQueue.enqueue(packageRecipes.value);
|
||||||
|
else recipesWaitingMap.set(packageOrderId, packageRecipes.value);
|
||||||
|
} else {
|
||||||
|
if (packageIsFinal && recipesWaitingMap.has(packageOrderId)) {
|
||||||
|
recipesQueue.enqueue(recipesWaitingMap.get(packageOrderId)!);
|
||||||
|
recipesWaitingMap.delete(packageOrderId);
|
||||||
|
} else {
|
||||||
|
logger.debug(`No recipe, just pass`);
|
||||||
|
}
|
||||||
|
}
|
||||||
packageExtractor.pullItems(turtleLocalName, 1);
|
packageExtractor.pullItems(turtleLocalName, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentState === State.READ_RECIPE &&
|
||||||
|
nextState === State.CRAFT_OUTPUT
|
||||||
|
) {
|
||||||
|
craftManager.initItemsMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case State.CRAFT_OUTPUT: {
|
||||||
|
// Check recipe
|
||||||
|
const recipe = recipesQueue.dequeue();
|
||||||
|
if (recipe === undefined) break;
|
||||||
|
|
||||||
// Pull and craft multi recipe
|
|
||||||
for (const recipe of packageRecipes.value) {
|
|
||||||
let craftOutputItem: BlockItemDetailData | undefined = undefined;
|
|
||||||
let restCraftCnt = recipe.Count;
|
let restCraftCnt = recipe.Count;
|
||||||
|
let maxSignleCraftCnt = restCraftCnt;
|
||||||
|
|
||||||
|
let craftItemDetail: ItemDetail | undefined = undefined;
|
||||||
do {
|
do {
|
||||||
// Clear workbench
|
// Clear workbench
|
||||||
craftManager.pushAll(itemsContainer);
|
craftManager.clearTurtle();
|
||||||
|
|
||||||
logger.info(`Pull items according to a recipe`);
|
logger.info(`Pull items according to a recipe`);
|
||||||
const craftCnt = craftManager
|
const craftCnt = craftManager
|
||||||
.pullItems(recipe, itemsContainer, restCraftCnt)
|
.pullItemsWithRecipe(recipe, maxSignleCraftCnt)
|
||||||
.unwrapOrElse((error) => {
|
.unwrapOrElse((error) => {
|
||||||
logger.error(error.message);
|
logger.error(error.message);
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (craftCnt == 0) break;
|
if (craftCnt == 0) break;
|
||||||
craftManager.craft();
|
if (craftCnt < maxSignleCraftCnt) maxSignleCraftCnt = craftCnt;
|
||||||
|
const craftRet = craftManager.craft(maxSignleCraftCnt);
|
||||||
|
craftItemDetail ??= craftRet;
|
||||||
logger.info(`Craft ${craftCnt} times`);
|
logger.info(`Craft ${craftCnt} times`);
|
||||||
restCraftCnt -= craftCnt;
|
restCraftCnt -= craftCnt;
|
||||||
|
|
||||||
// Get output item
|
|
||||||
craftOutputItem ??= blockReader.getBlockData()!.Items[1];
|
|
||||||
} while (restCraftCnt > 0);
|
} while (restCraftCnt > 0);
|
||||||
|
|
||||||
// Finally output
|
// Finally output
|
||||||
if (restCraftCnt > 0) {
|
if (restCraftCnt > 0) {
|
||||||
logger.warn(`Only craft ${recipe.Count - restCraftCnt} times`);
|
logger.warn(
|
||||||
|
`Only craft ${recipe.Count - restCraftCnt}x ${craftItemDetail?.name ?? "UnknownItem"}`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Finish craft ${recipe.Count}x ${craftOutputItem?.id}`);
|
logger.info(
|
||||||
|
`Finish craft ${recipe.Count}x ${craftItemDetail?.name ?? "UnknownItem"}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
craftManager.pushAll(itemsContainer);
|
|
||||||
|
// Clear workbench and inventory
|
||||||
|
const turtleItemSlots = Object.values(
|
||||||
|
blockReader.getBlockData()!.Items,
|
||||||
|
).map((val) => val.Slot + 1);
|
||||||
|
craftManager.clearTurtle(turtleItemSlots);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
sleep(1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check packages
|
||||||
|
hasPackage = redstone.getInput(peripheralsNames.redstone);
|
||||||
|
// State update
|
||||||
|
currentState = nextState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,23 +8,52 @@ import { Result, Ok, Err, Option, Some, None } from "./thirdparty/ts-result-es";
|
|||||||
// 13, 14, 15, 16
|
// 13, 14, 15, 16
|
||||||
|
|
||||||
const TURTLE_SIZE = 16;
|
const TURTLE_SIZE = 16;
|
||||||
|
const CRAFT_OUTPUT_SLOT = 4;
|
||||||
// const CRAFT_SLOT_CNT = 9;
|
// const CRAFT_SLOT_CNT = 9;
|
||||||
const CRAFT_SLOT_TABLE: number[] = [1, 2, 3, 5, 6, 7, 9, 10, 11];
|
const CRAFT_SLOT_TABLE: number[] = [1, 2, 3, 5, 6, 7, 9, 10, 11];
|
||||||
// const REST_SLOT_CNT = 7;
|
// const REST_SLOT_CNT = 7;
|
||||||
// const REST_SLOT_TABLE: number[] = [4, 8, 12, 13, 14, 15, 16];
|
// const REST_SLOT_TABLE: number[] = [4, 8, 12, 13, 14, 15, 16];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the NBT data of a Create mod package. This data is used for managing crafting and logistics,
|
||||||
|
* especially in the context of multi-step crafting orders.
|
||||||
|
* The structure is inspired by the logic in Create's own packaging and repackaging helpers.
|
||||||
|
* @see https://github.com/Creators-of-Create/Create/blob/mc1.21.1/dev/src/main/java/com/simibubi/create/content/logistics/packager/repackager/PackageRepackageHelper.java
|
||||||
|
*/
|
||||||
interface CreatePackageTag {
|
interface CreatePackageTag {
|
||||||
|
/**
|
||||||
|
* The items contained within this package.
|
||||||
|
*/
|
||||||
Items: {
|
Items: {
|
||||||
|
/**
|
||||||
|
* A list of the items stored in the package.
|
||||||
|
*/
|
||||||
Items: {
|
Items: {
|
||||||
id: string;
|
id: string;
|
||||||
Count: number;
|
Count: number;
|
||||||
Slot: number;
|
Slot: number;
|
||||||
}[];
|
}[];
|
||||||
|
/**
|
||||||
|
* The number of slots in the package's inventory.
|
||||||
|
*/
|
||||||
Size: number;
|
Size: number;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Information about this package's role as a fragment of a larger crafting order.
|
||||||
|
* This is used to track progress and manage dependencies in a distributed crafting system.
|
||||||
|
*/
|
||||||
Fragment: {
|
Fragment: {
|
||||||
|
/**
|
||||||
|
* The index of this fragment within the larger order.
|
||||||
|
*/
|
||||||
Index: number;
|
Index: number;
|
||||||
|
/**
|
||||||
|
* The context of the overall order this fragment belongs to.
|
||||||
|
*/
|
||||||
OrderContext: {
|
OrderContext: {
|
||||||
|
/**
|
||||||
|
* A list of crafting recipes required for the order.
|
||||||
|
*/
|
||||||
OrderedCrafts: {
|
OrderedCrafts: {
|
||||||
Pattern: {
|
Pattern: {
|
||||||
Entries: {
|
Entries: {
|
||||||
@@ -38,6 +67,9 @@ interface CreatePackageTag {
|
|||||||
};
|
};
|
||||||
Count: number;
|
Count: number;
|
||||||
}[];
|
}[];
|
||||||
|
/**
|
||||||
|
* A list of pre-existing item stacks required for the order.
|
||||||
|
*/
|
||||||
OrderedStacks: {
|
OrderedStacks: {
|
||||||
Entries: {
|
Entries: {
|
||||||
Item: {
|
Item: {
|
||||||
@@ -48,11 +80,26 @@ interface CreatePackageTag {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Whether this is the final fragment in the sequence for this specific part of the order.
|
||||||
|
*/
|
||||||
IsFinal: number;
|
IsFinal: number;
|
||||||
|
/**
|
||||||
|
* The unique identifier for the overall order.
|
||||||
|
*/
|
||||||
OrderId: number;
|
OrderId: number;
|
||||||
|
/**
|
||||||
|
* The index of this package in a linked list of packages for the same order.
|
||||||
|
*/
|
||||||
LinkIndex: number;
|
LinkIndex: number;
|
||||||
|
/**
|
||||||
|
* Whether this is the last package in the linked list.
|
||||||
|
*/
|
||||||
IsFinalLink: number;
|
IsFinalLink: number;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* The destination address for this package.
|
||||||
|
*/
|
||||||
Address: string;
|
Address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,17 +118,25 @@ interface CraftRecipe {
|
|||||||
|
|
||||||
interface InventorySlotInfo {
|
interface InventorySlotInfo {
|
||||||
name: string;
|
name: string;
|
||||||
count: number;
|
slotCountQueue: Queue<{
|
||||||
maxCount: number;
|
|
||||||
slotNum: number;
|
slotNum: number;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
|
maxCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CraftMode = "keep" | "keepProduct" | "keepIngredient";
|
type CraftMode = "keep" | "keepProduct" | "keepIngredient";
|
||||||
|
|
||||||
class CraftManager {
|
class CraftManager {
|
||||||
private localName: string;
|
private localName: string;
|
||||||
|
private inventory: InventoryPeripheral;
|
||||||
|
|
||||||
constructor(modem: WiredModemPeripheral | string) {
|
private inventoryItemsMap = new Map<string, InventorySlotInfo>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
modem: WiredModemPeripheral | string,
|
||||||
|
srcInventory: InventoryPeripheral,
|
||||||
|
) {
|
||||||
if (turtle == undefined) {
|
if (turtle == undefined) {
|
||||||
throw new Error("Script must be run in a turtle computer");
|
throw new Error("Script must be run in a turtle computer");
|
||||||
}
|
}
|
||||||
@@ -105,20 +160,9 @@ class CraftManager {
|
|||||||
}
|
}
|
||||||
this.localName = name;
|
this.localName = name;
|
||||||
// log.info(`Get turtle name : ${name}`);
|
// log.info(`Get turtle name : ${name}`);
|
||||||
}
|
|
||||||
|
|
||||||
public pushAll(outputInventory: InventoryPeripheral): void {
|
// Inventory
|
||||||
for (let i = 1; i <= TURTLE_SIZE; i++) {
|
this.inventory = srcInventory;
|
||||||
outputInventory.pullItems(this.localName, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public craft(dstInventory?: InventoryPeripheral, limit?: number): void {
|
|
||||||
turtle.craft(limit);
|
|
||||||
|
|
||||||
if (dstInventory != undefined) {
|
|
||||||
dstInventory.pullItems(this.localName, 1, limit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getPackageRecipe(
|
public static getPackageRecipe(
|
||||||
@@ -142,40 +186,152 @@ class CraftManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public pullItems(
|
public initItemsMap() {
|
||||||
recipe: CraftRecipe,
|
const ingredientList = this.inventory.list();
|
||||||
srcInventory: InventoryPeripheral,
|
|
||||||
craftCnt: number,
|
|
||||||
): Result<number> {
|
|
||||||
// Initialize hash map
|
|
||||||
const ingredientList = srcInventory.list();
|
|
||||||
const ingredientMap = new Map<string, Queue<InventorySlotInfo>>();
|
|
||||||
for (const key in ingredientList) {
|
for (const key in ingredientList) {
|
||||||
const slotNum = parseInt(key);
|
const slotNum = parseInt(key);
|
||||||
const item = srcInventory.getItemDetail(slotNum)!;
|
const item = this.inventory.getItemDetail(slotNum)!;
|
||||||
|
|
||||||
if (ingredientMap.has(item.name)) {
|
if (this.inventoryItemsMap.has(item.name)) {
|
||||||
ingredientMap.get(item.name)!.enqueue({
|
this.inventoryItemsMap.get(item.name)!.slotCountQueue.enqueue({
|
||||||
name: item.name,
|
|
||||||
slotNum: slotNum,
|
slotNum: slotNum,
|
||||||
count: item.count,
|
count: item.count,
|
||||||
maxCount: item.maxCount,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ingredientMap.set(
|
this.inventoryItemsMap.set(item.name, {
|
||||||
item.name,
|
|
||||||
new Queue<InventorySlotInfo>([
|
|
||||||
{
|
|
||||||
name: item.name,
|
name: item.name,
|
||||||
slotNum: slotNum,
|
|
||||||
count: item.count,
|
|
||||||
maxCount: item.maxCount,
|
maxCount: item.maxCount,
|
||||||
},
|
slotCountQueue: new Queue<{ slotNum: number; count: number }>([
|
||||||
|
{ slotNum: slotNum, count: item.count },
|
||||||
]),
|
]),
|
||||||
);
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public pullFromInventory(
|
||||||
|
itemId: string,
|
||||||
|
count?: number,
|
||||||
|
toSlot?: number,
|
||||||
|
): Result<number> {
|
||||||
|
const item = this.inventoryItemsMap.get(itemId);
|
||||||
|
if (item === undefined || item.slotCountQueue.size() === 0)
|
||||||
|
return new Err(Error(`No item match ${itemId}`));
|
||||||
|
|
||||||
|
if (count === undefined) {
|
||||||
|
const itemSlot = item.slotCountQueue.dequeue()!;
|
||||||
|
const pullItemsCnt = this.inventory.pushItems(
|
||||||
|
this.localName,
|
||||||
|
itemSlot.slotNum,
|
||||||
|
itemSlot.count,
|
||||||
|
toSlot,
|
||||||
|
);
|
||||||
|
return new Ok(pullItemsCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
let restCount = count;
|
||||||
|
while (restCount > 0 && item.slotCountQueue.size() > 0) {
|
||||||
|
const itemSlot = item.slotCountQueue.dequeue()!;
|
||||||
|
const pullItemsCnt = this.inventory.pushItems(
|
||||||
|
this.localName,
|
||||||
|
itemSlot.slotNum,
|
||||||
|
Math.min(restCount, itemSlot.count),
|
||||||
|
toSlot,
|
||||||
|
);
|
||||||
|
if (pullItemsCnt < itemSlot.count) {
|
||||||
|
item.slotCountQueue.enqueue({
|
||||||
|
slotNum: itemSlot.slotNum,
|
||||||
|
count: itemSlot.count - pullItemsCnt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
restCount -= pullItemsCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Ok(count - restCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public pushToInventoryEmpty(
|
||||||
|
fromSlot: number,
|
||||||
|
count?: number,
|
||||||
|
): Result<number> {
|
||||||
|
let emptySlot = 0;
|
||||||
|
for (let i = this.inventory.size(); i > 0; i--) {
|
||||||
|
const isEmpty = this.inventory.getItemDetail(i) === undefined;
|
||||||
|
if (isEmpty) {
|
||||||
|
emptySlot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emptySlot <= 0) return new Err(Error("No empty slot found"));
|
||||||
|
|
||||||
|
return new Ok(
|
||||||
|
this.inventory.pullItems(this.localName, fromSlot, count, emptySlot),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public pushToInventory(fromSlot: number): Result<number> {
|
||||||
|
const itemInfoDetail = turtle.getItemDetail(fromSlot) as
|
||||||
|
| SlotDetail
|
||||||
|
| undefined;
|
||||||
|
if (itemInfoDetail === undefined) return new Ok(0);
|
||||||
|
const inventoryItemInfo = this.inventoryItemsMap.get(itemInfoDetail.name);
|
||||||
|
|
||||||
|
if (inventoryItemInfo === undefined) {
|
||||||
|
return this.pushToInventoryEmpty(fromSlot, itemInfoDetail.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
let restItemsCount = itemInfoDetail.count;
|
||||||
|
for (const slotInfo of inventoryItemInfo.slotCountQueue) {
|
||||||
|
const pullItemsCount = inventoryItemInfo.maxCount - slotInfo.count;
|
||||||
|
if (pullItemsCount > 0) {
|
||||||
|
this.inventory.pullItems(
|
||||||
|
this.localName,
|
||||||
|
fromSlot,
|
||||||
|
pullItemsCount,
|
||||||
|
slotInfo.slotNum,
|
||||||
|
);
|
||||||
|
restItemsCount -= pullItemsCount;
|
||||||
|
if (restItemsCount <= 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restItemsCount > 0) {
|
||||||
|
const pushRet = this.pushToInventoryEmpty(fromSlot, restItemsCount);
|
||||||
|
if (pushRet.isErr()) return pushRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Ok(itemInfoDetail.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearTurtle(slots?: number[]): void {
|
||||||
|
if (slots !== undefined) {
|
||||||
|
for (const slotNum of slots) {
|
||||||
|
this.pushToInventory(slotNum);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i <= TURTLE_SIZE; i++) {
|
||||||
|
this.pushToInventory(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public craft(limit?: number, outputSlot = CRAFT_OUTPUT_SLOT): ItemDetail {
|
||||||
|
turtle.select(outputSlot);
|
||||||
|
turtle.craft(limit);
|
||||||
|
const craftItemDetail = turtle.getItemDetail(
|
||||||
|
outputSlot,
|
||||||
|
true,
|
||||||
|
) as ItemDetail;
|
||||||
|
|
||||||
|
return craftItemDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public pullItemsWithRecipe(
|
||||||
|
recipe: CraftRecipe,
|
||||||
|
craftCnt: number,
|
||||||
|
): Result<number> {
|
||||||
let maxCraftCnt = craftCnt;
|
let maxCraftCnt = craftCnt;
|
||||||
for (const index in recipe.PatternEntries) {
|
for (const index in recipe.PatternEntries) {
|
||||||
const entry = recipe.PatternEntries[index];
|
const entry = recipe.PatternEntries[index];
|
||||||
@@ -183,58 +339,24 @@ class CraftManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ingredientMap.has(entry.Item.id))
|
const ingredient = this.inventoryItemsMap.get(entry.Item.id);
|
||||||
|
if (ingredient === undefined)
|
||||||
return new Err(Error(`No ingredient match ${entry.Item.id}`));
|
return new Err(Error(`No ingredient match ${entry.Item.id}`));
|
||||||
|
|
||||||
const ingredient = ingredientMap.get(entry.Item.id)!;
|
|
||||||
let restCraftCnt = maxCraftCnt;
|
|
||||||
while (restCraftCnt > 0 && ingredient.size() > 0) {
|
|
||||||
const slotItem = ingredient.dequeue()!;
|
|
||||||
|
|
||||||
// Check item max stack count
|
// Check item max stack count
|
||||||
if (slotItem.maxCount < maxCraftCnt) {
|
if (ingredient.maxCount < maxCraftCnt) {
|
||||||
maxCraftCnt = slotItem.maxCount;
|
maxCraftCnt = ingredient.maxCount;
|
||||||
restCraftCnt = maxCraftCnt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slotItem.count >= restCraftCnt) {
|
// Pull items
|
||||||
const pushItemsCnt = srcInventory.pushItems(
|
const pullItemsCnt = this.pullFromInventory(
|
||||||
this.localName,
|
ingredient.name,
|
||||||
slotItem.slotNum,
|
maxCraftCnt,
|
||||||
restCraftCnt,
|
|
||||||
CRAFT_SLOT_TABLE[index],
|
CRAFT_SLOT_TABLE[index],
|
||||||
);
|
);
|
||||||
if (pushItemsCnt !== restCraftCnt)
|
if (pullItemsCnt.isErr()) return pullItemsCnt;
|
||||||
return new Err(
|
|
||||||
Error(
|
|
||||||
`Try to get items ${restCraftCnt}x "${slotItem.name}" from inventory, but only get ${pushItemsCnt}x`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (slotItem.count > restCraftCnt) {
|
|
||||||
ingredient.enqueue({
|
|
||||||
...slotItem,
|
|
||||||
count: slotItem.count - restCraftCnt,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
restCraftCnt = 0;
|
|
||||||
} else {
|
|
||||||
const pushItemsCnt = srcInventory.pushItems(
|
|
||||||
this.localName,
|
|
||||||
slotItem.slotNum,
|
|
||||||
slotItem.count,
|
|
||||||
CRAFT_SLOT_TABLE[index],
|
|
||||||
);
|
|
||||||
if (pushItemsCnt !== slotItem.count)
|
|
||||||
return new Err(
|
|
||||||
Error(
|
|
||||||
`Try to get items ${slotItem.count}x "${slotItem.name}" from inventory, but only get ${pushItemsCnt}x`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
restCraftCnt -= slotItem.count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restCraftCnt > 0)
|
if (pullItemsCnt.value < maxCraftCnt)
|
||||||
return new Err(Error("Not enough items in inventory"));
|
return new Err(Error("Not enough items in inventory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,14 @@ export class Queue<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enqueue(data: T): void {
|
public enqueue(data: T | T[]): void {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
for (const val of data) {
|
||||||
|
this.enqueue(val);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const node = new Node(data);
|
const node = new Node(data);
|
||||||
|
|
||||||
if (this._head === undefined) {
|
if (this._head === undefined) {
|
||||||
|
|||||||
2
types/advanced-peripherals/shared.d.ts
vendored
2
types/advanced-peripherals/shared.d.ts
vendored
@@ -6,7 +6,7 @@ declare interface BlockItemDetailData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare interface BlockDetailData {
|
declare interface BlockDetailData {
|
||||||
Items: Record<number, BlockItemDetailData>;
|
Items: Record<string, BlockItemDetailData>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user