diff --git a/src/App.tsx b/src/App.tsx index c9eadc0..69618a5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,19 +1,16 @@ import { Command, CommandNothing } from "core/Command"; -import { Inventory } from "core/Inventory"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import "./App.css"; import { CommandItem } from "./components/CommandItem"; import { DisplayPane } from "surfaces/DisplayPane"; -import { Item } from "core/Item"; import { saveAs } from "data/FileSaver"; import { parseCommand } from "core/Parser"; import { ItemList } from "components/ItemList"; import { TitledList } from "components/TitledList"; import { createSimulationState, SimulationState } from "core/SimulationState"; import { ReferencePage } from "surfaces/ReferencePage"; -import { GameData } from "core/GameData"; const getDefaultCommands = (): Command[]=>{ const encoded = localStorage.getItem("HDS.CurrentCommandsText"); @@ -28,10 +25,9 @@ const getDefaultCommands = (): Command[]=>{ parseCommand("Reload"), parseCommand("Save"), parseCommand("Reload"), - ] as Command[];; + ] as Command[]; }; - export const App: React.FC = () => { const [page, setPageInState] = useState("#simulation"); const [overlaySave, setOverlaySave] = useState(false); @@ -120,12 +116,12 @@ export const App: React.FC = () => { height: 40 }}> - +
{ { !!simulationStates[displayIndex].getManualSave() && { - setSelectedSaveName(""); - }} - comment={false} - isSelected={selectedSaveName===""} + onClick={()=>{ + setSelectedSaveName(""); + }} + comment={false} + isSelected={selectedSaveName===""} - > + > Manual Save - + } - { - Object.entries(simulationStates[displayIndex].getNamedSaves()).map(([name, _gamedata])=>( - { - setSelectedSaveName(name); - }} - comment={false} - isSelected={selectedSaveName===name} - > - {name} - - )) - } - + { + Object.entries(simulationStates[displayIndex].getNamedSaves()).map(([name, _gamedata])=> + { + setSelectedSaveName(name); + }} + comment={false} + isSelected={selectedSaveName===name} + > + {name} + + ) + } + } @@ -185,55 +181,55 @@ export const App: React.FC = () => { }}> -
    - { - commands.map((c,i)=> - { - setDisplayIndex(i); - const inputField = document.getElementById("CommandInputField"); - if(inputField){ - inputField.focus(); - } - }} - onContextMenu={(x,y)=>{ - setContextIndex(i); - setContextMenuX(x); - setContextMenuY(y); - setContextMenuShowing(true); - }} - key={i} - isSelected={displayIndex===i} - isContextSelected={contextIndex===i} - comment={c.getDisplayString().startsWith("#")} - > - {c.getDisplayString()} - - ) - } - { - const arrCopy = [...commands]; - arrCopy.push(new CommandNothing()); - setCommands(arrCopy); - }} onContextMenu={()=>{ - const arrCopy = [...commands]; - arrCopy.push(new CommandNothing()); - setCommands(arrCopy); - }}>(new) - { - setContextIndex(-1); - setContextMenuX(x); - setContextMenuY(y); - setContextMenuShowing(true); - }} onContextMenu={(x,y)=>{ - setContextIndex(-1); - setContextMenuX(x); - setContextMenuY(y); - setContextMenuShowing(true); - }}>(options) +
      + { + commands.map((c,i)=> + { + setDisplayIndex(i); + const inputField = document.getElementById("CommandInputField"); + if(inputField){ + inputField.focus(); + } + }} + onContextMenu={(x,y)=>{ + setContextIndex(i); + setContextMenuX(x); + setContextMenuY(y); + setContextMenuShowing(true); + }} + key={i} + isSelected={displayIndex===i} + isContextSelected={contextIndex===i} + comment={c.getDisplayString().startsWith("#")} + > + {c.getDisplayString()} + + ) + } + { + const arrCopy = [...commands]; + arrCopy.push(new CommandNothing()); + setCommands(arrCopy); + }} onContextMenu={()=>{ + const arrCopy = [...commands]; + arrCopy.push(new CommandNothing()); + setCommands(arrCopy); + }}>(new) + { + setContextIndex(-1); + setContextMenuX(x); + setContextMenuY(y); + setContextMenuShowing(true); + }} onContextMenu={(x,y)=>{ + setContextIndex(-1); + setContextMenuX(x); + setContextMenuY(y); + setContextMenuShowing(true); + }}>(options) -
    +
@@ -249,45 +245,51 @@ export const App: React.FC = () => { }}> { page === "#simulation" && <> -
- { - (displayIndex >= 0 && displayIndex < commands.length) ? - +
{ - selectedSaveName === "" && !!simulationStates[displayIndex].getManualSave() && - + displayIndex >= 0 && displayIndex < commands.length ? + + { + (()=>{ + if (selectedSaveName === ""){ + const manualSave = simulationStates[displayIndex].getManualSave(); + if(manualSave){ + return ; + } + }else if(selectedSaveName){ + const namedSaves = simulationStates[displayIndex].getNamedSaves(); + if(selectedSaveName in namedSaves){ + const save = namedSaves[selectedSaveName]; + return ; + } + } + return null; + })() + } + + : + + + } - { - selectedSaveName !== "" && !!simulationStates[displayIndex].getNamedSaves()[selectedSaveName] && - - } - - : - - - - } - - - -
-
- {displayIndex >= 0 && displayIndex < commands.length && +
+
+ {displayIndex >= 0 && displayIndex < commands.length && { }} /> - } -
- + } +
+ } { page === "#reference" && } - - - - {/*
- - -
-
*/} - {/*
- - - -
*/} - - - {/*
*/} - - { contextMenuShowing &&
{ const text = lines.join("\n"); saveAs(text, "dupe.txt"); }}>Export - { - alert(`Available Commands: -Initialize X Item1 Y Item2 Z Item3 ... -Break X Slots - add X broken slots -Save -Reload -Sort Key/Material - sort key items or material -Get/Add/Cook/Pickup X ITEM -Remove/Drop/Sell X ITEM From Slot Y -Remove/Sell/Eat MEAL From Slot X - -Limitations: -Inventory corruption is not implemented yet - -`); - alert(`Available Items: -Slate -Glider -SpiritOrb -SpeedFood -Lotus -SilentPrincess -Honey -Acorn -FaroshScale -FaroshClaw -FaroshHorn -HeartyBass -Beetle -Opal -Diamond -Tail -Spring -Shaft -Core -Wood -Weapon - `); - }}>Reference diff --git a/src/assets/img/2017022420411100_F1C11A22FAEE3B82F21B330E1B786A39.0.webp b/src/assets/img/2017022420411100_F1C11A22FAEE3B82F21B330E1B786A39.0.webp deleted file mode 100644 index 2022f5d..0000000 Binary files a/src/assets/img/2017022420411100_F1C11A22FAEE3B82F21B330E1B786A39.0.webp and /dev/null differ diff --git a/src/assets/img/ZoraArmor.png b/src/assets/img/ZoraArmor.png new file mode 100644 index 0000000..3f33f43 Binary files /dev/null and b/src/assets/img/ZoraArmor.png differ diff --git a/src/components/CommandItem.tsx b/src/components/CommandItem.tsx index c8de161..6c29e07 100644 --- a/src/components/CommandItem.tsx +++ b/src/components/CommandItem.tsx @@ -11,7 +11,7 @@ type CommandItemProps = PropsWithChildren<{ export const CommandItem: React.FC = ({isSelected, isContextSelected, comment,children, onClick, onContextMenu}) => { if(comment){ - return
{children}
+ return
{children}
; } return
  • - -export const TitledList: React.FC = ({title, children}) => { - return ( - <> -

    - {title} -

    -
    - {children} -
    - - ); -} +import { PropsWithChildren } from "react"; + +type TitledListProps = PropsWithChildren<{ + title: string +}> + +export const TitledList: React.FC = ({title, children}) => { + return ( + <> +

    + {title} +

    +
    + {children} +
    + + ); +}; diff --git a/src/core/Command.ts b/src/core/Command.ts index e807557..7ad4c18 100644 --- a/src/core/Command.ts +++ b/src/core/Command.ts @@ -1,5 +1,5 @@ import { Inventory } from "./Inventory"; -import { Item, ItemStack, itemToArrowType } from "./Item"; +import { Item, ItemStack } from "./Item"; import { SimulationState } from "./SimulationState"; export interface Command { @@ -70,7 +70,6 @@ export class CommandReload implements Command { } } - export class CommandUse implements Command { private name: string; constructor(name: string){ @@ -215,7 +214,7 @@ const joinItemStackString = (initial: string, stacks: ItemStack[]): string => { parts.push(item); }); return parts.join(" "); -} +}; export class CommandDaP implements Command { private count: number; @@ -273,7 +272,7 @@ export class CommandUnequip implements Command { } export class CommandShootArrow implements Command { - private count: number + private count: number; constructor(count: number){ this.count = count; } @@ -286,39 +285,28 @@ export class CommandShootArrow implements Command { } } +export class CommandCloseGame implements Command { + public execute(state: SimulationState): void { + state.closeGame(); + } + public getDisplayString(): string { + return "Close Game"; + } +} -// export class CommandEquipArrow implements Command { -// private item: Item; -// private slot: number; -// private noSlot: boolean; -// constructor(item: Item, slot: number, noSlot: boolean){ -// this.item = item; -// this.slot = slot; -// this.noSlot = noSlot; -// } - -// public execute(inv: Inventory): void { -// inv.equipEquipmentOrArrow(this.item, this.slot); -// } -// public getDisplayString(): string { -// const slotString = this.noSlot ? "" : ` In Slot ${this.slot+1}`; -// return `Equip ${itemToArrowType(this.item)} Arrow${slotString}`; -// } -// } +export class CommandSync implements Command { + private actionString: string; + constructor(actionString: string){ + this.actionString = actionString; + } - - - - - -// export class CommandCloseGame implements Command { -// public execute(inv: Inventory): void { -// inv.closeGame(); -// } -// public getDisplayString(): string { -// return "Close Game"; -// } -// } + public execute(state: SimulationState): void { + state.syncGameDataWithPouch(); + } + public getDisplayString(): string { + return this.actionString; + } +} export class CommandComment implements Command { private name: string; @@ -333,38 +321,38 @@ export class CommandComment implements Command { } } -// export class CommandSortKey implements Command { -// static Op = 0x5; -// // public fromBuffer(_buf: Buffer): number { -// // return 0; -// // } -// // public toBuffer(): Buffer { -// // const buf: Buffer = Buffer.alloc(1); -// // buf.writeInt8(CommandSortKey.Op); -// // return buf; -// // } -// public execute(inv: Inventory): void { -// inv.sortKey(); -// } -// public getDisplayString(): string { -// return "Sort Key"; -// } -// } +export class CommandSortKey implements Command { + static Op = 0x5; + // public fromBuffer(_buf: Buffer): number { + // return 0; + // } + // public toBuffer(): Buffer { + // const buf: Buffer = Buffer.alloc(1); + // buf.writeInt8(CommandSortKey.Op); + // return buf; + // } + public execute(_state: SimulationState): void { + // wip + } + public getDisplayString(): string { + return "Sort Key"; + } +} -// export class CommandSortMaterial implements Command { -// static Op = 0x6; -// // public fromBuffer(_buf: Buffer): number { -// // return 0; -// // } -// // public toBuffer(): Buffer { -// // const buf: Buffer = Buffer.alloc(1); -// // buf.writeInt8(CommandSortMaterial.Op); -// // return buf; -// // } -// public execute(inv: Inventory): void { -// inv.sortMaterial(); -// } -// public getDisplayString(): string { -// return "Sort Material"; -// } -// } +export class CommandSortMaterial implements Command { + static Op = 0x6; + // public fromBuffer(_buf: Buffer): number { + // return 0; + // } + // public toBuffer(): Buffer { + // const buf: Buffer = Buffer.alloc(1); + // buf.writeInt8(CommandSortMaterial.Op); + // return buf; + // } + public execute(_state: SimulationState): void { + // wip + } + public getDisplayString(): string { + return "Sort Material"; + } +} diff --git a/src/core/DisplayableInventory.ts b/src/core/DisplayableInventory.ts index 41fd330..09d4c88 100644 --- a/src/core/DisplayableInventory.ts +++ b/src/core/DisplayableInventory.ts @@ -1,4 +1,4 @@ -import { Item, ItemStack, itemToItemData, ItemType } from "./Item" +import { ItemStack, itemToItemData, ItemType } from "./Item"; export type DisplayableSlot = { image: string, @@ -13,12 +13,13 @@ export interface DisplayableInventory { } export const itemStackToDisplayableSlot = ({item, count, equipped}: ItemStack, isBrokenSlot: boolean): DisplayableSlot => { - const data = itemToItemData(item); - return { - image: data.image, - displayCount: data.stackable && (data.type === ItemType.Arrow || count > 0), - count, - isEquipped: equipped, - isBrokenSlot - } -} + const data = itemToItemData(item); + return { + image: data.image, + // for unstackable items (meal/key items) display count if count > 1, even if it's unstackable + displayCount: data.stackable ? data.type === ItemType.Arrow || count > 0 : count > 1, + count, + isEquipped: equipped, + isBrokenSlot + }; +}; diff --git a/src/core/GameData.ts b/src/core/GameData.ts index 545d36e..dbb9180 100644 --- a/src/core/GameData.ts +++ b/src/core/GameData.ts @@ -1,5 +1,4 @@ import { DisplayableInventory, DisplayableSlot, itemStackToDisplayableSlot } from "./DisplayableInventory"; -import { Item, itemToItemData } from "./Item"; import { Slots } from "./Slots"; import { VisibleInventory } from "./VisibleInventory"; @@ -8,28 +7,28 @@ import { VisibleInventory } from "./VisibleInventory"; */ export class GameData implements DisplayableInventory { - private slots: Slots = new Slots([]); - constructor(slots: Slots){ - this.slots = slots; - } + private slots: Slots = new Slots([]); + constructor(slots: Slots){ + this.slots = slots; + } - public deepClone(): GameData { - return new GameData(this.slots.deepClone()); - } + public deepClone(): GameData { + return new GameData(this.slots.deepClone()); + } - public syncWith(pouch: VisibleInventory) { - this.slots = pouch.getSlots().deepClone(); - } + public syncWith(pouch: VisibleInventory) { + this.slots = pouch.getSlots().deepClone(); + } - public updateDurability(durability: number, slot: number){ - this.slots.corrupt(durability, slot); - } + public updateDurability(durability: number, slot: number){ + this.slots.corrupt(durability, slot); + } - public addAllToPouchOnReload(pouch: VisibleInventory) { - this.slots.getSlotsRef().forEach(stack=>pouch.addWhenReload(stack.item, stack.count, stack.equipped)); - } + public addAllToPouchOnReload(pouch: VisibleInventory) { + this.slots.getSlotsRef().forEach(stack=>pouch.addWhenReload(stack.item, stack.count, stack.equipped)); + } - public getDisplayedSlots(): DisplayableSlot[] { - return this.slots.getSlotsRef().map(stack=>itemStackToDisplayableSlot(stack, false)); - } + public getDisplayedSlots(): DisplayableSlot[] { + return this.slots.getSlotsRef().map(stack=>itemStackToDisplayableSlot(stack, false)); + } } diff --git a/src/core/Inventory.ts b/src/core/Inventory.ts index 6f262fc..4bc5e26 100644 --- a/src/core/Inventory.ts +++ b/src/core/Inventory.ts @@ -1,5 +1,5 @@ -import { Item, ItemStack, itemToItemData, ItemType, ItemTypes } from "./Item"; -import { Slots } from "./Slots"; +// import { Item, ItemStack, itemToItemData, ItemType, ItemTypes } from "./Item"; +// import { Slots } from "./Slots"; export class Inventory { // private slots: Slots = new Slots([]); diff --git a/src/core/Item.ts b/src/core/Item.ts index d928ecb..c2ca4c3 100644 --- a/src/core/Item.ts +++ b/src/core/Item.ts @@ -76,6 +76,7 @@ export enum Item { Fairy = "Fairy", MasterSword = "MasterSword", + ZoraArmor = "ZoraArmor", } type ItemData = { @@ -167,7 +168,7 @@ register(0x50, Item.Weapon, ItemType.Weapon, { }); register(0, Item.MasterSword, ItemType.Weapon, { stackable: false, -}) +}); register(0x60, Item.Bow, ItemType.Bow, { image: Images.ForestDwellerBow, @@ -184,6 +185,10 @@ register(0x80, Item.Shield, ItemType.Shield, { stackable: false }); +register(9, Item.ZoraArmor, ItemType.Armor, { + stackable: false +}); + //export const idToItemData = (id: number): ItemData => IdToData[id]; export const itemToItemData = (item: Item): ItemData => ItemToData[item] as ItemData; export const itemToArrowType = (item: Item): string => { @@ -193,3 +198,5 @@ export const itemToArrowType = (item: Item): string => { } return ""; }; + +export const getAllItems = (): string[] => Object.keys(ItemToData); diff --git a/src/core/Parser.ts b/src/core/Parser.ts index 4b7e4b9..12003de 100644 --- a/src/core/Parser.ts +++ b/src/core/Parser.ts @@ -4,6 +4,7 @@ import { CommandAddMultiple, CommandAddWithoutCount, CommandBreakSlots, + CommandCloseGame, CommandComment, CommandDaP, CommandEquip, @@ -16,6 +17,9 @@ import { CommandSave, CommandSaveAs, CommandShootArrow, + CommandSortKey, + CommandSortMaterial, + CommandSync, CommandUnequip, CommandUse } from "./Command"; @@ -54,7 +58,7 @@ export const parseCommand = (cmdString: string): Command | undefined => { if(tokens.length===2 && tokens[0] === "Reload"){ return new CommandReload(tokens[1]); } - // break + // break if (tokens.length > 2 && tokens[0] === "Break" && tokens[2]=== "Slots" ){ const slots = parseInt(tokens[1]); if(Number.isInteger(slots)){ @@ -176,50 +180,29 @@ export const parseCommand = (cmdString: string): Command | undefined => { return undefined; } - // if(tokens.length===2 && tokens[0] === "Sort" && tokens[1] === "Key"){ - // return new CommandSortKey(); - // } - // if(tokens.length===2 && tokens[0] === "Sort" && tokens[1] === "Material"){ - // return new CommandSortMaterial(); - // } - // if(tokens.length===2 && tokens[0] === "Close" && tokens[1] === "Game"){ - // return new CommandCloseGame(); - // } - - // return undefined; - // } - // // remove material - - - - - // // Equip Arrow - // if (tokens.length === 6 && tokens[0] === "Equip" && tokens[2] === "Arrow" && tokens[3] === "In" && tokens[4] ==="Slot" ){ - // const item = tokens[1]+"Arrow"; - // const slot = parseInt(tokens[5]); - // if( Number.isInteger(slot) && item in Item){ - // return new CommandEquipArrow(Item[item as keyof typeof Item], slot-1, false); - // } - // return undefined; - // } - // if (tokens.length === 3 && tokens[0] === "Equip" && tokens[2] === "Arrow" ){ - // const item = tokens[1]+"Arrow"; - // if(item in Item){ - // return new CommandEquipArrow(Item[item as keyof typeof Item], 0, true); - // } - // return undefined; - // } + if(tokens.length===2 && tokens[0] === "Sort" && tokens[1] === "Key"){ + return new CommandSortKey(); + } + if(tokens.length===2 && tokens[0] === "Sort" && tokens[1] === "Material"){ + return new CommandSortMaterial(); + } + if(tokens.length===2 && tokens[0] === "Close" && tokens[1] === "Game"){ + return new CommandCloseGame(); + } + if(tokens.length===2 && tokens[0] === "Sync" && tokens[1] === "GameData"){ + return new CommandSync("Sync GameData"); + } return undefined; }; const isAddVerb = (token: string): boolean => { - return token === "Get" || token === "Cook" || token === "Add" || token === "Pickup" -} + return token === "Get" || token === "Cook" || token === "Add" || token === "Pickup"; +}; const isRemoveVerb = (token: string): boolean => { - return token === "Remove" || token === "Sell" || token === "Eat" || token === "Drop" -} + return token === "Remove" || token === "Sell" || token === "Eat" || token === "Drop"; +}; const parseItemStacks = (tokens: string[], from: number): ItemStack[] | undefined => { if((tokens.length-from)%2 !== 0){ @@ -242,4 +225,4 @@ const parseItemStacks = (tokens: string[], from: number): ItemStack[] | undefine } } return stacks; -} +}; diff --git a/src/core/SimulationState.ts b/src/core/SimulationState.ts index a31a382..47e4ea4 100644 --- a/src/core/SimulationState.ts +++ b/src/core/SimulationState.ts @@ -5,160 +5,161 @@ import { Slots } from "./Slots"; import { VisibleInventory } from "./VisibleInventory"; export const createSimulationState = (): SimulationState => { - return new SimulationState( - new GameData(new Slots([])), - null, - {}, - new VisibleInventory(new Slots([]), 0) - ); -} + return new SimulationState( + new GameData(new Slots([])), + null, + {}, + new VisibleInventory(new Slots([]), 0) + ); +}; /* * The state of simulation, including game data, visible inventory, and all save slots */ export class SimulationState { - private gameData: GameData; - private manualSave: GameData | null; - private namedSaves: {[name: string]: GameData} = {}; - private pouch: VisibleInventory; - private nextReloadName?: string; - private isOnEventide: boolean = false; + private gameData: GameData; + private manualSave: GameData | null; + private namedSaves: {[name: string]: GameData} = {}; + private pouch: VisibleInventory; + private nextReloadName?: string; + private isOnEventide = false; - constructor(gameData: GameData, manualSave: GameData | null, namedSaves: {[name: string]: GameData}, pouch: VisibleInventory){ - this.gameData = gameData; - this.manualSave = manualSave; - this.namedSaves = namedSaves; - this.pouch = pouch; - } + constructor(gameData: GameData, manualSave: GameData | null, namedSaves: {[name: string]: GameData}, pouch: VisibleInventory){ + this.gameData = gameData; + this.manualSave = manualSave; + this.namedSaves = namedSaves; + this.pouch = pouch; + } - public deepClone(): SimulationState { - const copyNamedSaves: {[name: string]: GameData} = {}; - for(const name in this.namedSaves){ - copyNamedSaves[name] = this.namedSaves[name].deepClone(); - } - const newState = new SimulationState( - this.gameData.deepClone(), - this.manualSave ? this.manualSave.deepClone() : null, - copyNamedSaves, - this.pouch.deepClone() - ); - newState.nextReloadName = this.nextReloadName; - newState.isOnEventide = this.isOnEventide; + public deepClone(): SimulationState { + const copyNamedSaves: {[name: string]: GameData} = {}; + for(const name in this.namedSaves){ + copyNamedSaves[name] = this.namedSaves[name].deepClone(); + } + const newState = new SimulationState( + this.gameData.deepClone(), + this.manualSave ? this.manualSave.deepClone() : null, + copyNamedSaves, + this.pouch.deepClone() + ); + newState.nextReloadName = this.nextReloadName; + newState.isOnEventide = this.isOnEventide; - return newState; - } + return newState; + } - public initialize(stacks: ItemStack[]) { - this.pouch = new VisibleInventory(new Slots([]), 0); - stacks.forEach((stack)=>this.pouch.addDirectly(stack)); - this.gameData.syncWith(this.pouch); - } + public initialize(stacks: ItemStack[]) { + this.pouch = new VisibleInventory(new Slots([]), 0); + stacks.forEach((stack)=>this.pouch.addDirectly(stack)); + this.gameData.syncWith(this.pouch); + } - public save(name?: string) { - if(name){ - this.namedSaves[name] = this.gameData.deepClone(); - }else{ - this.manualSave = this.gameData.deepClone(); - } - } + public save(name?: string) { + if(name){ + this.namedSaves[name] = this.gameData.deepClone(); + }else{ + this.manualSave = this.gameData.deepClone(); + } + } - public reload(name?: string) { - if(name){ - if(name in this.namedSaves){ - this.reloadFrom(this.namedSaves[name]); - } - }else{ - if(this.nextReloadName){ - if(this.nextReloadName in this.namedSaves){ - this.reloadFrom(this.namedSaves[this.nextReloadName]); - } - }else{ - const save = this.manualSave; - if(save){ - this.reloadFrom(save); - } - } - } - } + public reload(name?: string) { + if(name){ + if(name in this.namedSaves){ + this.reloadFrom(this.namedSaves[name]); + } + }else{ + if(this.nextReloadName){ + if(this.nextReloadName in this.namedSaves){ + this.reloadFrom(this.namedSaves[this.nextReloadName]); + } + }else{ + const save = this.manualSave; + if(save){ + this.reloadFrom(save); + } + } + } + } - private reloadFrom(data: GameData) { - this.gameData = data.deepClone(); - this.pouch.clearForReload(); - this.gameData.addAllToPouchOnReload(this.pouch); - this.pouch.updateEquipmentDurability(this.gameData); - } + private reloadFrom(data: GameData) { + this.gameData = data.deepClone(); + this.pouch.clearForReload(); + this.gameData.addAllToPouchOnReload(this.pouch); + this.pouch.updateEquipmentDurability(this.gameData); + } - public useSaveForNextReload(name: string){ - this.nextReloadName = name; - } + public useSaveForNextReload(name: string){ + this.nextReloadName = name; + } - public breakSlots(n: number) { - this.pouch.modifyCount(-n); - } + public breakSlots(n: number) { + this.pouch.modifyCount(-n); + } - public obtain(item: Item, count: number) { - this.pouch.addInGame(item, count); - this.syncGameDataWithPouch(); - } + public obtain(item: Item, count: number) { + this.pouch.addInGame(item, count); + this.syncGameDataWithPouch(); + } - public remove(item: Item, count: number, slot: number) { - this.pouch.remove(item, count, slot); - this.syncGameDataWithPouch(); - } + public remove(item: Item, count: number, slot: number) { + this.pouch.remove(item, count, slot); + this.syncGameDataWithPouch(); + } - public equip(item: Item, slot: number) { - this.pouch.equip(item, slot); - this.syncGameDataWithPouch(); - } + public equip(item: Item, slot: number) { + this.pouch.equip(item, slot); + this.syncGameDataWithPouch(); + } - public unequip(item: Item, slot: number){ - this.pouch.unequip(item, slot); - this.syncGameDataWithPouch(); - } + public unequip(item: Item, slot: number){ + this.pouch.unequip(item, slot); + this.syncGameDataWithPouch(); + } - public shootArrow(count: number){ - this.pouch.shootArrow(count, this.gameData); - // does not sync - } + public shootArrow(count: number){ + this.pouch.shootArrow(count, this.gameData); + // does not sync + } - public syncGameDataWithPouch() { - if(!this.isOnEventide){ - this.gameData.syncWith(this.pouch); - } - } + public closeGame() { + this.pouch = new VisibleInventory(new Slots([]), 0); + this.gameData = new GameData(new Slots([])); + this.isOnEventide = false; + } - public get displayableGameData(): DisplayableInventory { - return this.gameData; - } + public syncGameDataWithPouch() { + if(!this.isOnEventide){ + this.gameData.syncWith(this.pouch); + } + } - public get displayablePouch(): DisplayableInventory { - return this.pouch; - } + public get displayableGameData(): DisplayableInventory { + return this.gameData; + } - public get inventoryMCount(): number { - return this.pouch.getCount(); - } + public get displayablePouch(): DisplayableInventory { + return this.pouch; + } - public getManualSave(): GameData | null { - return this.manualSave; - } + public get inventoryMCount(): number { + return this.pouch.getCount(); + } - public getNamedSaves(): {[name: string]: GameData} { - return this.namedSaves; - } + public getManualSave(): GameData | null { + return this.manualSave; + } - // public get displayableGameData(): DisplayableInventory { - // return this.gameData; - // } + public getNamedSaves(): {[name: string]: GameData} { + return this.namedSaves; + } + // public get displayableGameData(): DisplayableInventory { + // return this.gameData; + // } } - - - // Shoot X Arrow, x can be ommited and default to 1 -// Close Game // Close Inventory, same as Resync GameData // Enter Eventide / Leave Eventide // Sort Key (In Tab X) - need more research on which tab is sorted. (might not be possible to select which tab to sort) diff --git a/src/core/Slots.ts b/src/core/Slots.ts index 874151c..9ca7ac1 100644 --- a/src/core/Slots.ts +++ b/src/core/Slots.ts @@ -1,8 +1,6 @@ -import { count } from "console"; import { stableSort } from "data/mergeSort"; import { Item, ItemStack, itemToItemData, ItemType } from "./Item"; - /* * This is the data model common to GameData and VisibleInventory */ @@ -43,21 +41,7 @@ export class Slots { public clearFirst(count: number) { this.internalSlots.splice(0, count); } - // public get(i: number): ItemStack{ - // return this.internalSlots[i]; - // } - // public getByType(type: ItemType): Slots { - // return new Slots(this.internalSlots.filter(s=>itemToItemData(s.item).type===type)); - // } - // public getBeforeType(type: ItemType): Slots { - // return new Slots(this.internalSlots.filter(s=>itemToItemData(s.item).typeitemToItemData(s.item).type>type)); - // } - // public addSlotsToEnd(slots: Slots) { - // slots.internalSlots.forEach(s=>this.addStack(s)); - // } + public addStackDirectly(stack: ItemStack): number { const data = itemToItemData(stack.item); if(data.stackable){ @@ -73,18 +57,6 @@ export class Slots { this.internalSlots.push({...stack}); this.sortItemType(mCount); } - // public addStackCopy(stack: ItemStack) { - // this.addStack({...stack}); - // } - // public sort() { - // this.internalSlots.sort((a,b)=>{ - // return itemToItemData(a.item).sortOrder - itemToItemData(b.item).sortOrder; - // }); - // } - // public removeFromEnd(count: number): Slots { - // const end = this.internalSlots.splice(-count, count); - // return new Slots(end); - // } // remove item(s) start from slot // return number of slots removed @@ -179,34 +151,29 @@ export class Slots { } if(reloading){ - for(let i=0;i{ + const sData = itemToItemData(s.item); + return sData.type === data.type && s.equipped; + }).length === 0; + this.addSlot({item,count:1,equipped: shouldEquipNew}, mCount+1); + for(let i=1;i{ - const sData = itemToItemData(s.item); - return sData.type === data.type && s.equipped; - }).length === 0; - this.addSlot({item,count:1,equipped: shouldEquipNew}, mCount+1); - for(let i=1;i= this.internalSlots.length){ return; } - const thisData = itemToItemData(this.internalSlots[slot].item); - if(thisData.stackable){ + const thisData = itemToItemData(this.internalSlots[slot].item); + // Currently only supports corrupting arrows, material, meal and key items as durability values are not simulated on equipments + if(thisData.type >= ItemType.Material || thisData.stackable){ this.internalSlots[slot].count = durability; } } @@ -268,20 +236,22 @@ export class Slots { // shoot count arrows. return the slot that was updated, or -1 public shootArrow(count: number): number { // first find equipped arrow, search entire inventory + // this is the last equipped arrow before armor let i=0; + let equippedArrow: Item | undefined = undefined; for(;i ItemType.Shield){ + break; + } + if(this.internalSlots[i].equipped && data.type === ItemType.Arrow){ + equippedArrow = data.item; } } if(i>=this.internalSlots.length){ //can't find equipped arrow return -1; } - const equippedArrow = this.internalSlots[i].item; // now find the first slot of that arrow and update for(let j=0;jitemStackToDisplayableSlot(stack, i>=this.count)); - } + public getDisplayedSlots(): DisplayableSlot[] { + return this.slots.getSlotsRef().map((stack, i)=>itemStackToDisplayableSlot(stack, i>=this.count)); + } - public getSlots(): Slots { - return this.slots; - } + public getSlots(): Slots { + return this.slots; + } - public addDirectly(stack: ItemStack){ - this.count+=this.slots.addStackDirectly(stack); - } + public addDirectly(stack: ItemStack){ + this.count+=this.slots.addStackDirectly(stack); + } - public addWhenReload(item: Item, count: number, equippedDuringReload: boolean) { - const slotsAdded = this.slots.add(item, count, equippedDuringReload, true, this.count); - this.count+=slotsAdded; - } + public addWhenReload(item: Item, count: number, equippedDuringReload: boolean) { + const slotsAdded = this.slots.add(item, count, equippedDuringReload, true, this.count); + this.count+=slotsAdded; + } - public addInGame(item: Item, count: number) { - const slotsAdded = this.slots.add(item, count, false, false, this.count); - this.count+=slotsAdded; - } + public addInGame(item: Item, count: number) { + const slotsAdded = this.slots.add(item, count, false, false, this.count); + this.count+=slotsAdded; + } - public remove(item: Item, count: number, slot: number) { - const slotsRemoved = this.slots.remove(item, count, slot); - this.count-=slotsRemoved; - } + public remove(item: Item, count: number, slot: number) { + const slotsRemoved = this.slots.remove(item, count, slot); + this.count-=slotsRemoved; + } - public equip(item: Item, slot: number) { - this.slots.equip(item, slot); - } + public equip(item: Item, slot: number) { + this.slots.equip(item, slot); + } - public unequip(item: Item, slot: number) { - this.slots.unequip(item, slot); - } + public unequip(item: Item, slot: number) { + this.slots.unequip(item, slot); + } - // Only clears first this.count - public clearForReload() { - if(this.count > 0){ - this.slots.clearFirst(this.count); - this.count = 0; - } - } + // Only clears first this.count + public clearForReload() { + if(this.count > 0){ + this.slots.clearFirst(this.count); + this.count = 0; + } + } - public updateEquipmentDurability(gameData: GameData) { - // find first weapon/bow/shield. this one searches entire inventory - let foundWeapon = false; - let foundBow = false; - let foundShield = false; - this.slots.getSlotsRef().forEach(({item, equipped}, i)=>{ - if(equipped){ - const type = itemToItemData(item).type; - if(type === ItemType.Weapon && !foundWeapon){ - gameData.updateDurability(999, i); - foundWeapon = true; - } - if(type === ItemType.Bow && !foundBow){ - gameData.updateDurability(999, i); - foundBow = true; - } - if(type === ItemType.Shield && !foundShield){ - gameData.updateDurability(999, i); - foundShield = true; - } - } - }) - } + public updateEquipmentDurability(gameData: GameData) { + // find first weapon/bow/shield. this one searches entire inventory + let foundWeapon = false; + let foundBow = false; + let foundShield = false; + this.slots.getSlotsRef().forEach(({item, equipped}, i)=>{ + if(equipped){ + const type = itemToItemData(item).type; + if(type === ItemType.Weapon && !foundWeapon){ + gameData.updateDurability(999, i); + foundWeapon = true; + } + if(type === ItemType.Bow && !foundBow){ + gameData.updateDurability(999, i); + foundBow = true; + } + if(type === ItemType.Shield && !foundShield){ + gameData.updateDurability(999, i); + foundShield = true; + } + } + }); + } - public shootArrow(count: number, gameData: GameData) { - const updatedSlot = this.slots.shootArrow(count); - if(updatedSlot>=0){ - const durability = this.slots.getSlotsRef()[updatedSlot].count; - gameData.updateDurability(durability, updatedSlot); - } - } + public shootArrow(count: number, gameData: GameData) { + const updatedSlot = this.slots.shootArrow(count); + if(updatedSlot>=0){ + const durability = this.slots.getSlotsRef()[updatedSlot].count; + gameData.updateDurability(durability, updatedSlot); + } + } - public getCount(): number { - return this.count; - } + public getCount(): number { + return this.count; + } - public modifyCount(delta: number): void { - this.count+=delta; - } + public modifyCount(delta: number): void { + this.count+=delta; + } - public resetCount(): void { - this.count = this.slots.length; - } + public resetCount(): void { + this.count = this.slots.length; + } } diff --git a/src/data/mergeSort.ts b/src/data/mergeSort.ts index 4fdea6a..c0b3e40 100644 --- a/src/data/mergeSort.ts +++ b/src/data/mergeSort.ts @@ -1,14 +1,14 @@ //https://medium.com/@fsufitch/is-javascript-array-sort-stable-46b90822543f export const stableSort = (array: T[], cmp: (a:T, b:T) => number): void => { - const stabilizedThis: [T, number][] = array.map((el, index) => [el, index]); - const stableCmp = (a: [T, number], b: [T, number]) => { - let order = cmp(a[0], b[0]); - if (order != 0) return order; - return a[1] - b[1]; - } - stabilizedThis.sort(stableCmp); + const stabilizedThis: [T, number][] = array.map((el, index) => [el, index]); + const stableCmp = (a: [T, number], b: [T, number]) => { + const order = cmp(a[0], b[0]); + if (order != 0) {return order;} + return a[1] - b[1]; + }; + stabilizedThis.sort(stableCmp); - for (let i=0; ivoid } -// export const stacksToItemListProps = (slots: Slots, numBroken: number, isSave: boolean): ItemListProps => { -// return { -// items: stacksToItemProps(slots.getSlotsRef()), -// numBroken, -// isSave, -// }; -// }; - -// export const stacksToItemProps = (stacks: ItemStack[]): ItemListItemProps[] => { -// return stacks.map(stackToItemProps); -// }; - -// export const stackToItemProps = ({item, count, equipped}: ItemStack): ItemListItemProps => { -// const data = itemToItemData(item); -// return {image: data.image, count: data.stackable ? count : 0, isEquipped:equipped}; -// }; - export const DisplayPane: React.FC = ({command,editCommand,displayIndex,simulationState, overlaySave})=>{ const [commandString, setCommandString] = useState(""); const [hasError, setHasError] = useState(false); @@ -88,88 +69,83 @@ export const DisplayPane: React.FC = ({command,editCommand,dis
    - {overlaySave ? -
    + {overlaySave ? +
    - { - (()=>{ - const doubleSlots: JSX.Element[] = []; - const gameDataSlots = simulationState.displayableGameData.getDisplayedSlots(); - const inventorySlots = simulationState.displayablePouch.getDisplayedSlots(); - console.log(inventorySlots); - for(let i=0;i); - } - if(inventorySlots.length>gameDataSlots.length){ - for(let i=inventorySlots.length;i{ + const doubleSlots: JSX.Element[] = []; + const gameDataSlots = simulationState.displayableGameData.getDisplayedSlots(); + const inventorySlots = simulationState.displayablePouch.getDisplayedSlots(); + for(let i=0;i); - } - }else if(inventorySlots.length > gameDataSlots.length){ - for(let i=gameDataSlots.length;i); } - } - return doubleSlots; - })() - } + if(gameDataSlots.length>inventorySlots.length){ + for(let i=inventorySlots.length;i); + } + }else if(inventorySlots.length > gameDataSlots.length){ + for(let i=gameDataSlots.length;i); + } + } + return doubleSlots; + })() + } - - -
    - - :<> - -
    - - - -
    -
    - - - + + :<> + +
    + + + +
    +
    + + + -
    - } +
    + }
    -
    ; }; diff --git a/src/surfaces/ReferencePage.tsx b/src/surfaces/ReferencePage.tsx index d653c60..5f7db8c 100644 --- a/src/surfaces/ReferencePage.tsx +++ b/src/surfaces/ReferencePage.tsx @@ -1,169 +1,184 @@ -import { ItemList } from "components/ItemList"; import { TitledList } from "components/TitledList"; +import { getAllItems } from "core/Item"; import React from "react"; export const ReferencePage: React.FC = React.memo(()=>{ - - - return ( -
    - -
    -

    Commands

    -

    Initialize X item1 Y item2 Z item3 ...

    -

    Used for initializing inventory before simulation

    -

    + return ( +

    + +
    +

    Items

    + { + getAllItems().map(item=>

    {item}

    ) + } +

    Commands

    +

    Initialize X item1 Y item2 Z item3 ...

    +

    Used for initializing inventory before simulation

    +

    Fully resets the inventory by clearing all items and set Count to 0, then forcefully write the item list to inventory. This would reset any broken slot you already have, and any in-game checks that happen when adding items are disabled. For example, the items will appear in the order you specify, not in the in-game tab order -

    -

    +

    +

    If you specify count > 1 for unstackable items like weapon or sheika slate, multiple of that item would be added. Game Data will be synced with Visible Inventory after the reset -

    -

    +

    +

    Note that this will not clear saves. You can use this command to initialize multiple saves -

    -

    Example: Initialize 1 Apple 2 Axe 3 Slate 4 SpiritOrb

    +

    +

    Example: Initialize 1 Apple 2 Axe 3 Slate 4 SpiritOrb

    -

    Save / Save As NAME

    -

    Simulates a hard save or auto save action

    -

    +

    Save / Save As NAME

    +

    Simulates a hard save or auto save action

    +

    Writes Game Data to the corresponding save slot. The auto saves are specified by NAME. You can have as many auto saves as you want in the simulator. -

    +

    -

    Example 1: Save

    -

    Example 2: Save As MySave

    -

    +

    Example 1: Save

    +

    Example 2: Save As MySave

    +

    Example 1 will save to the manual save slot, while example 2 will save to the slot named "MySave". There cannot be spaces in the name. If "MySave" doesn't exist, a new slot is created -

    +

    -

    Reload (NAME)

    -

    Simulates reloading a save

    -

    +

    Reload (NAME)

    +

    Simulates reloading a save

    +

    First, reads Game Data from the corresponding save slot. If NAME is not given, the manual save is used unless "Use" commands are used before this (see below). If NAME is given, the corresponding save slot with that name is used -

    -

    +

    +

    After that, the first Count items in the visible inventory is removed, and Count is decreased accordingly. Then, each item slot in the Game Data is added to the inventory. -

    +

    -

    Example 1: Reload

    -

    Example 2: Reload MySave

    +

    Example 1: Reload

    +

    Example 2: Reload MySave

    -

    Use NAME

    -

    (Deprecated) Specify which save to load on the subsequent reload

    -

    +

    Use NAME

    +

    (Deprecated) Specify which save to load on the subsequent reload

    +

    This command is only for backward compatibility. Use "Reload" instead -

    -

    +

    +

    Specify the save named NAME to be reloaded on the next "Reload" command -

    +

    -

    Example: Use MySave

    +

    Example: Use MySave

    -

    Break X Slots

    -

    Simulate making X broken slots with hold smuggle glitch

    -

    +

    Break X Slots

    +

    Simulate making X broken slots with hold smuggle glitch

    +

    Decrease inventory Count by X -

    -

    +

    +

    This command does not automatically simulate the hold smuggle and sell process. It just changes count (i.e. make broken slots) with magic. -

    +

    -

    Example: Break 4 Slots

    +

    Example: Break 4 Slots

    -

    Get/Add/Cook/Pickup item

    -

    Get/Add/Cook/Pickup X item

    -

    Get/Add/Cook/Pickup X item1 Y item2 Z item3 ...

    -

    Simulate obtaining items in game

    -

    +

    Get/Add/Cook/Pickup item

    +

    Get/Add/Cook/Pickup X item

    +

    Get/Add/Cook/Pickup X item1 Y item2 Z item3 ...

    +

    Simulate obtaining items in game

    +

    Add the item(s) to visible inventory. Sync with Game Data unless you are on Eventide or inside TOTS -

    -

    +

    +

    Like in game, you won't be able to obtain multiple unstackable key items, or multiple master sword in this way. If a stackable item is at 999 or more when you invoke this command, the count is set to 999 (not fully accurate since you won't be able to pick up more items in game). -

    -

    +

    +

    If you specify a count for unstackable items, they are added in different slots as if you pick them up in game, one after another. -

    -

    +

    +

    Note that you must not enter plural forms for the item name. -

    +

    -

    Example 1: Add Apple

    -

    Example 2: Get 10 Apple

    -

    Example 3: Pickup 10 Apple 5 Diamond 1 Slate 5 MasterSword

    +

    Example 1: Add Apple

    +

    Example 2: Get 10 Apple

    +

    Example 3: Pickup 10 Apple 5 Diamond 1 Slate 5 MasterSword

    -

    Remove/Sell/Eat/Drop item

    -

    Remove/Sell/Eat/Drop X item

    -

    Remove/Sell/Eat/Drop item From Slot Y

    -

    Remove/Sell/Eat/Drop X item From Slot Y

    -

    Remove/Sell/Eat/Drop X item1 Y item2 Z item3 ...

    -

    Simulate removing items in game

    -

    +

    Remove/Sell/Eat/Drop item

    +

    Remove/Sell/Eat/Drop X item

    +

    Remove/Sell/Eat/Drop item From Slot Y

    +

    Remove/Sell/Eat/Drop X item From Slot Y

    +

    Remove/Sell/Eat/Drop X item1 Y item2 Z item3 ...

    +

    Simulate removing items in game

    +

    Remove the item(s) to visible inventory. Sync with Game Data unless you are on Eventide or inside TOTS -

    -

    +

    +

    When number of item is not specified, it defaults to 1. Up to X items will be removed from inventory, even when they span multiple slots. If X > total number of items in inventory, all of them will be removed. -

    -

    +

    +

    When slot is specified, it starts removing from slot X (slot 1 is the leftmost slot with that item, slot 2 is the second leftmost slot with that item). -

    -

    +

    +

    Note that you must not enter plural forms for the item name. -

    +

    -

    Example 1: Remove Apple

    -

    Example 2: Drop 10 Diamond

    -

    Example 3: Sell 10 Apple 5 Diamond

    -

    Example 4: Sell 5 Apple From Slot 3

    +

    Example 1: Remove Apple

    +

    Example 2: Drop 10 Diamond

    +

    Example 3: Sell 10 Apple 5 Diamond

    +

    Example 4: Sell 5 Apple From Slot 3

    -

    D&P X item

    -

    Shortcut for drop and pick up, for sorting inventory

    -

    +

    D&P X item

    +

    Shortcut for drop and pick up, for sorting inventory

    +

    This command drops X item from the first slot, then pick them up -

    -

    Example: D&P 5 Diamond

    +

    +

    Example: D&P 5 Diamond

    -

    Equip item

    -

    Equip item In Slot X

    -

    Simulates equipping something

    -

    +

    Equip item

    +

    Equip item In Slot X

    +

    Simulates equipping something

    +

    When equipping an item, all other item of the same type in the first tab is unequipped, then the item selected is equipped. -

    -

    +

    +

    Slot can be used if you have multiple of the same item. When slot is not specified, the leftmost item will be equipped. Note that you can use this command to equip something that is already equipped, which is not possible in game. You can also equip unequippable items like materials, but it is not meaningful -

    -

    Example 1: Equip Weapon

    -

    Example 2: Equip Weapon In Slot 3

    +

    +

    Example 1: Equip Weapon

    +

    Example 2: Equip Weapon In Slot 3

    -

    Unequip item

    -

    Unequip item In Slot X

    -

    Simulates unequipping something

    -

    +

    Unequip item

    +

    Unequip item In Slot X

    +

    Simulates unequipping something

    +

    When unequipping an item, only the selected item is unequipped. -

    -

    +

    +

    Slot can be used if you have multiple of the same item. When slot is not specified, the leftmost equipped item will be unequipped. Note that you can use this command to unequip something that is already unequipped, which is useless. You cannot unequip arrows. -

    -

    Example 1: Unequip Shield

    -

    Example 2: Unequip Shield In Slot 5

    -
    +

    +

    Example 1: Unequip Shield

    +

    Example 2: Unequip Shield In Slot 5

    + +

    Close Game

    +

    Simulates closing the game and restarting

    +

    + When closing the game, Visible Inventory and Game Data are erased +

    +

    Example: Close Game

    + +

    Sort Key/Material

    +

    Simulates press Y to sort tab

    +

    + This command is currently broken +

    + +
    - - -
    - ) +
    +
    + ); });