diff --git a/package.json b/package.json index 70cf4b6..fa62111 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "botw-ist", - "version": "3.1.5", + "version": "3.1.6", "homepage": "https://ist.itntpiston.app/", "private": true, "dependencies": { diff --git a/src/core/SimulationState.ts b/src/core/SimulationState.ts index 22798c0..9500833 100644 --- a/src/core/SimulationState.ts +++ b/src/core/SimulationState.ts @@ -314,4 +314,9 @@ export class SimulationState { this.pouch.swap(i,j); } + public inaccuratelySortMaterials() { + this.pouch.inaccuratelySortMaterials(); + this.syncGameDataWithPouch(); + } + } diff --git a/src/core/command/ast/ast.generated.ts b/src/core/command/ast/ast.generated.ts index 8f2b9d9..38f39f4 100644 --- a/src/core/command/ast/ast.generated.ts +++ b/src/core/command/ast/ast.generated.ts @@ -110,7 +110,7 @@ const parseCommand: ParseFunction = (tokens) => { return ParseResultFail; }; export type ASTCommand = ASTCommandInitGameData | ASTCommandInitialize | ASTCommandCook | ASTCommandCookCrit | ASTCommandAdd | ASTCommandPickUp | ASTCommandRemoveAll | ASTCommandRemove | ASTCommandDrop | ASTCommandEat | ASTCommandDnp | ASTCommandEquip | ASTCommandUnequipAll | ASTCommandUnequip | ASTCommandShoot | ASTCommandEnterTrial | ASTCommandExitTrial | ASTCommandWriteMetadata | ASTCommandSave | ASTCommandReload | ASTCommandBreakSlots | ASTCommandCloseGame | ASTCommandSyncGameData | ASTCommandHas; -// (derivation union) SuperCommand => SuperCommandAddSlot | SuperCommandRemoveSlot | SuperCommandSwap | SuperCommandForCommand +// (derivation union) SuperCommand => SuperCommandAddSlot | SuperCommandRemoveSlot | SuperCommandSwap | SuperCommandSortMaterial | SuperCommandForCommand const parseSuperCommand: ParseFunction = (tokens) => { let result: ASTSuperCommand | undefined; result = parseSuperCommandAddSlot(tokens); @@ -119,11 +119,13 @@ const parseSuperCommand: ParseFunction = (tokens) => { if(result !== ParseResultFail) return result; result = parseSuperCommandSwap(tokens); if(result !== ParseResultFail) return result; + result = parseSuperCommandSortMaterial(tokens); + if(result !== ParseResultFail) return result; result = parseSuperCommandForCommand(tokens); if(result !== ParseResultFail) return result; return ParseResultFail; }; -export type ASTSuperCommand = ASTSuperCommandAddSlot | ASTSuperCommandRemoveSlot | ASTSuperCommandSwap | ASTSuperCommandForCommand; +export type ASTSuperCommand = ASTSuperCommandAddSlot | ASTSuperCommandRemoveSlot | ASTSuperCommandSwap | ASTSuperCommandSortMaterial | ASTSuperCommandForCommand; // (derivation) CommandInitGameData => LiteralInitialize ZeroOrMoreItems export const isCommandInitGameData = (node: T | ASTCommandInitGameData | null): node is ASTCommandInitGameData => Boolean(node && node.type === "ASTCommandInitGameData"); export type ASTCommandInitGameData = { @@ -1089,6 +1091,45 @@ const parseSuperCommandSwap: ParseFunction = (tokens) => { mInteger3, }; }; +// (derivation) SuperCommandSortMaterial => LiteralMaterial +export const isSuperCommandSortMaterial = (node: T | ASTSuperCommandSortMaterial | null): node is ASTSuperCommandSortMaterial => Boolean(node && node.type === "ASTSuperCommandSortMaterial"); +export type ASTSuperCommandSortMaterial = { + readonly type: "ASTSuperCommandSortMaterial", + readonly literal0: readonly [number, number], + readonly literal1: readonly [number, number], + readonly mLiteralMaterial2: ASTLiteralMaterial, +}; +const parseSuperCommandSortMaterial: ParseFunction = (tokens) => { + let rangeTokens: Token[]; + tokens.push(); + rangeTokens = []; + if(tokens.consume(rangeTokens) !== "!") { + tokens.restore(); + tokens.pop(); + return ParseResultFail; + } + const literal0 = [rangeTokens[0].start, rangeTokens[rangeTokens.length-1].end] as const; + rangeTokens = []; + if(tokens.consume(rangeTokens) !== "sort") { + tokens.restore(); + tokens.pop(); + return ParseResultFail; + } + const literal1 = [rangeTokens[0].start, rangeTokens[rangeTokens.length-1].end] as const; + const mLiteralMaterial2 = parseLiteralMaterial(tokens); + if(mLiteralMaterial2 === ParseResultFail) { + tokens.restore(); + tokens.pop(); + return ParseResultFail; + } + tokens.pop(); + return { + type: "ASTSuperCommandSortMaterial", + literal0, + literal1, + mLiteralMaterial2, + }; +}; // (literal union) LiteralInitialize => | export const isLiteralInitialize = (node: T | ASTLiteralInitialize | null): node is ASTLiteralInitialize => Boolean(node && node.type === "ASTLiteralInitialize"); export type ASTLiteralInitialize = { diff --git a/src/core/command/ast/grammar.txt b/src/core/command/ast/grammar.txt index f7d30dd..0b9c95c 100644 --- a/src/core/command/ast/grammar.txt +++ b/src/core/command/ast/grammar.txt @@ -59,6 +59,7 @@ SuperCommand => SuperCommandAddSlot # ! add slot ... [from slot X] | SuperCommandRemoveSlot # ! remove slot X | SuperCommandSwap # ! swap i j + | SuperCommandSortMaterial # ! sort material | SuperCommandForCommand --- @@ -92,6 +93,7 @@ CommandHas => LiteralMaybeNot ValueValue OneOrMoreIdentifiers SuperCommandAddSlot => LiteralSlot ArgumentOneOrMoreItemsMaybeFromSlot SuperCommandRemoveSlot => LiteralSlot Integer SuperCommandSwap => Integer Integer +SuperCommandSortMaterial => LiteralMaterial ## Literals LiteralInitialize => | diff --git a/src/core/command/parse.cmd.super.test.ts b/src/core/command/parse.cmd.super.test.ts index 52b5189..f42e91e 100644 --- a/src/core/command/parse.cmd.super.test.ts +++ b/src/core/command/parse.cmd.super.test.ts @@ -1,6 +1,6 @@ import { createMockItems, createMockItemSearch } from "data/test"; import { ItemStackArg } from "./ItemStackArg"; -import { SuperCommandAddSlot, SuperCommandSwap } from "./parse.cmd.super"; +import { SuperCommandAddSlot, SuperCommandSortMaterial, SuperCommandSwap } from "./parse.cmd.super"; describe("core/command/parse.super !swap", ()=>{ it("parses", ()=>{ @@ -9,6 +9,13 @@ describe("core/command/parse.super !swap", ()=>{ }); +describe("core/command/parse.super !sort material", ()=>{ + it("parses", ()=>{ + expect("!sort material").toParseIntoCommand(undefined, new SuperCommandSortMaterial([])); + }); + +}); + describe("core/command/parse.super !add slot", ()=>{ it("parses", ()=>{ const MockItems = createMockItems([ diff --git a/src/core/command/parse.cmd.super.ts b/src/core/command/parse.cmd.super.ts index be9ca27..ff33507 100644 --- a/src/core/command/parse.cmd.super.ts +++ b/src/core/command/parse.cmd.super.ts @@ -1,7 +1,7 @@ import { SimulationState } from "core/SimulationState"; import { arrayEqual } from "data/util"; import { getSlotsToAdd, ItemStackArg } from "./ItemStackArg"; -import { ASTSuperCommandAddSlot, ASTSuperCommandSwap } from "./ast"; +import { ASTSuperCommandAddSlot, ASTSuperCommandSortMaterial, ASTSuperCommandSwap } from "./ast"; import { AbstractProperCommand, Command } from "./command"; import { parseASTInteger } from "./parse.basis"; import { parseASTArgumentOneOrMoreItemsAllowAllMaybeFromSlot } from "./parse.clause.with.fromslot"; @@ -69,3 +69,25 @@ export const parseASTSuperCommandAddSlot: ParserItem = (ast) => { + const codeBlocks: CodeBlockTree = [ + codeBlockFromRange(ast.literal0, "keyword.super"), + codeBlockFromRange(ast.literal1, "keyword.super"), + ]; + + return [new SuperCommandSortMaterial(codeBlocks), codeBlocks]; +}; diff --git a/src/core/command/parsev2.ts b/src/core/command/parsev2.ts index 0137411..fa0023a 100644 --- a/src/core/command/parsev2.ts +++ b/src/core/command/parsev2.ts @@ -26,6 +26,7 @@ import { isCommandWriteMetadata, isSuperCommandAddSlot, isSuperCommandForCommand, + isSuperCommandSortMaterial, isSuperCommandSwap } from "./ast"; import { CmdErr, Command, CommandHint, CommandNop, ErrorCommand } from "./command"; @@ -40,7 +41,7 @@ import { parseASTCommandReload } from "./parse.cmd.reload"; import { parseASTCommandDnp, parseASTCommandDrop, parseASTCommandEat, parseASTCommandRemove, parseASTCommandRemoveAll } from "./parse.cmd.remove"; import { parseASTCommandSave } from "./parse.cmd.save"; import { parseASTCommandShoot } from "./parse.cmd.shoot"; -import { parseASTSuperCommandAddSlot, parseASTSuperCommandSwap } from "./parse.cmd.super"; +import { parseASTSuperCommandAddSlot, parseASTSuperCommandSortMaterial, parseASTSuperCommandSwap } from "./parse.cmd.super"; import { parseASTCommandSyncGameData } from "./parse.cmd.sync"; import { parseASTCommandEnterTrial, parseASTCommandExitTrial } from "./parse.cmd.trial"; import { parseASTCommandWriteMetadata } from "./parse.cmd.write"; @@ -271,6 +272,9 @@ const parseASTTarget: ParserItem = (ast, search) => { if(isSuperCommandSwap(ast)){ return withNoError(parseASTSuperCommandSwap(ast)); } + if(isSuperCommandSortMaterial(ast)){ + return withNoError(parseASTSuperCommandSortMaterial(ast)); + } if(isSuperCommandForCommand(ast)){ const codeBlocks = [codeBlockFromRange(ast.literal0, "keyword.super")]; diff --git a/src/core/inventory/Slots.ts b/src/core/inventory/Slots.ts index 1071517..e68e4d1 100644 --- a/src/core/inventory/Slots.ts +++ b/src/core/inventory/Slots.ts @@ -177,4 +177,8 @@ export class Slots { this.core.swap(i, j); } + public inaccuratelySortMaterials() { + this.core.sortMaterials(); + } + } diff --git a/src/core/inventory/SlotsCore.ts b/src/core/inventory/SlotsCore.ts index d2f55d7..6c7caf8 100644 --- a/src/core/inventory/SlotsCore.ts +++ b/src/core/inventory/SlotsCore.ts @@ -41,6 +41,17 @@ export class SlotsCore { this.internalSlots[j] = temp; } + public sortMaterials() { + stableSort(this.internalSlots, (a,b)=>{ + const itemA = a.get().item; + const itemB = b.get().item; + if(itemA.type === ItemType.Material && itemB.type === ItemType.Material){ + return itemA.sortOrder - itemB.sortOrder; + } + return 0; + }); + } + // Used to decide if game data is synced with inventory // Two Slots are equal if the ItemStacks equal, including metadata equality public equals(other: SlotsCore): boolean { diff --git a/src/core/inventory/VisibleInventory.ts b/src/core/inventory/VisibleInventory.ts index d30e945..5e82db8 100644 --- a/src/core/inventory/VisibleInventory.ts +++ b/src/core/inventory/VisibleInventory.ts @@ -217,6 +217,10 @@ export class VisibleInventory implements DisplayableInventory{ this.slots.swap(i, j); } + public inaccuratelySortMaterials() { + this.slots.inaccuratelySortMaterials(); + } + // public countItems(type: ItemType, countAnyWeapon: boolean): number { // // [confirmed] iTNTPiston: when mcount === 0, nothing is checked (only when =0) // const mcount = this.getMCount();