diff --git a/src/App.tsx b/src/App.tsx index 65d3467..9614a4d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,6 @@ import "./App.css"; import { CommandItem } from "./components/CommandItem"; import { DisplayPane } from "surfaces/DisplayPane"; -import { saveAs } from "data/FileSaver"; import { parseCommand } from "core/Parser"; import { ItemList } from "components/ItemList"; import { TitledList } from "components/TitledList"; @@ -69,16 +68,25 @@ export const App: React.FC = () => { useEffect(()=>{ window.onkeydown=(e)=>{ if(e.code==="ArrowDown"){ - if(displayIndex===commands.length-1){ + let nextCommandIndex = displayIndex+1; + while(nextCommandIndex=0 && !commands[nextCommandIndex].isValid()){ + nextCommandIndex--; + } + setDisplayIndex(Math.max(0, nextCommandIndex)); } }; }, [commands, displayIndex]); @@ -219,7 +227,6 @@ export const App: React.FC = () => { setCommands(arrCopy); }}>(new) - @@ -306,7 +313,7 @@ export const App: React.FC = () => { commandText={commandText} setCommandText={(value)=>{ if(value !== commandText){ - const commands = value.split("\n").map(parseCommand) + const commands = value.split("\n").map(parseCommand); setCommands(commands); } }} @@ -341,32 +348,32 @@ export const App: React.FC = () => { paddingInlineStart: 0 }}> - { + { + const arrCopy = [...commands]; + arrCopy.splice(contextIndex, 0, new CommandNop("")); + setCommands(arrCopy); + setContextIndex(-1); + }}>Insert Above + { + if(contextIndex > 0){ const arrCopy = [...commands]; - arrCopy.splice(contextIndex, 0, new CommandNop("")); + const temp = arrCopy[contextIndex]; + arrCopy[contextIndex] = arrCopy[contextIndex-1]; + arrCopy[contextIndex-1] = temp; setCommands(arrCopy); setContextIndex(-1); - }}>Insert Above - { - if(contextIndex > 0){ - const arrCopy = [...commands]; - const temp = arrCopy[contextIndex]; - arrCopy[contextIndex] = arrCopy[contextIndex-1]; - arrCopy[contextIndex-1] = temp; - setCommands(arrCopy); - setContextIndex(-1); - } + } - }}>Move Up - { - if(confirm("Delete?")){ - setCommands(commands.filter((_,i)=>i!==contextIndex)); - if(displayIndex >= commands.length){ - setDisplayIndex(commands.length-1); - } - setContextIndex(-1); + }}>Move Up + { + if(confirm("Delete?")){ + setCommands(commands.filter((_,i)=>i!==contextIndex)); + if(displayIndex >= commands.length){ + setDisplayIndex(commands.length-1); } - }}>Delete + setContextIndex(-1); + } + }}>Delete diff --git a/src/components/CommandItem.tsx b/src/components/CommandItem.tsx index dc7366e..dea8f28 100644 --- a/src/components/CommandItem.tsx +++ b/src/components/CommandItem.tsx @@ -30,7 +30,7 @@ export const CommandItem: React.FC = ({ ); const clickHandler = useCallback((e: React.MouseEvent)=>{ - onClick(e.clientX, e.clientY) + onClick(e.clientX, e.clientY); }, [onClick]); const contextMenuHandler = useCallback((e: React.MouseEvent)=>{ if(onContextMenu){ diff --git a/src/core/Command.ts b/src/core/Command.ts index a9ba2db..167df5a 100644 --- a/src/core/Command.ts +++ b/src/core/Command.ts @@ -232,20 +232,21 @@ const joinItemStackString = (initial: string, stacks: ItemStack[]): string => { }; export class CommandDaP extends CommandImpl { - private count: number; - private item: Item; + private stacks: ItemStack[]; - constructor(count: number, item: Item){ + constructor(stacks: ItemStack[]){ super(); - this.count = count; - this.item = item; + this.stacks = stacks; } public execute(state: SimulationState): void { - state.remove(this.item, this.count, 0); - state.obtain(this.item, this.count); + this.stacks.forEach(({item,count})=>{ + state.remove(item, count, 0); + state.obtain(item, count); + }); + } public getDisplayString(): string { - return `D&P ${this.count} ${this.item}`; + return joinItemStackString("D&P", this.stacks); } } @@ -328,6 +329,21 @@ export class CommandSync extends CommandImpl { } } +export class CommandEventide extends CommandImpl { + private enter: boolean; + constructor(enter: boolean){ + super(); + this.enter = enter; + } + + public execute(state: SimulationState): void { + state.setEventide(this.enter); + } + public getDisplayString(): string { + return `${this.enter? "Enter":"Exit"} Eventide`; + } +} + export class CommandNop extends CommandImpl { private text: string; constructor(text: string){ diff --git a/src/core/Parser.ts b/src/core/Parser.ts index f546b2b..de74340 100644 --- a/src/core/Parser.ts +++ b/src/core/Parser.ts @@ -7,6 +7,7 @@ import { CommandCloseGame, CommandDaP, CommandEquip, + CommandEventide, CommandInitialize, CommandNop, CommandReload, @@ -124,11 +125,10 @@ export const parseCommand = (cmdString: string): Command => { } } //Shortcut for drop and pick up - if (tokens.length === 3 && tokens[0] === "D&P" ){ - const count = parseInt(tokens[1]); - const item = tokens[2]; - if(Number.isInteger(count) && item in Item){ - return new CommandDaP(count, Item[item as keyof typeof Item]); + if (tokens.length >2 && tokens[0] === "D&P" ){ + const stacks = parseItemStacks(tokens, 1); + if(stacks){ + return new CommandDaP(stacks); } } @@ -182,6 +182,9 @@ export const parseCommand = (cmdString: string): Command => { if(tokens.length===2 && tokens[0] === "Sync" && tokens[1] === "GameData"){ return new CommandSync("Sync GameData"); } + if(tokens.length===2 && (tokens[0] === "Enter" || tokens[0] === "Exit") && tokens[1] === "Eventide"){ + return new CommandEventide(tokens[0] === "Enter"); + } return new CommandNop(cmdString); }; diff --git a/src/core/SimulationState.ts b/src/core/SimulationState.ts index 47e4ea4..6a1061e 100644 --- a/src/core/SimulationState.ts +++ b/src/core/SimulationState.ts @@ -85,6 +85,7 @@ export class SimulationState { this.pouch.clearForReload(); this.gameData.addAllToPouchOnReload(this.pouch); this.pouch.updateEquipmentDurability(this.gameData); + this.isOnEventide = false; } public useSaveForNextReload(name: string){ @@ -126,6 +127,22 @@ export class SimulationState { this.isOnEventide = false; } + public setEventide(onEventide: boolean){ + if(this.isOnEventide !== onEventide){ + if(onEventide){ + // clear everything except for key items + this.pouch.clearForEventide(); + // game data is not updated (?) + + }else{ + // reload pouch from gamedata as if reloading a save + this.reloadFrom(this.gameData); + } + this.isOnEventide = onEventide; + } + + } + public syncGameDataWithPouch() { if(!this.isOnEventide){ this.gameData.syncWith(this.pouch); diff --git a/src/core/Slots.ts b/src/core/Slots.ts index 9ca7ac1..c72219e 100644 --- a/src/core/Slots.ts +++ b/src/core/Slots.ts @@ -264,4 +264,12 @@ export class Slots { } + // return how many slots are removed + public clearAllButKeyItems(): number { + const newslots = this.internalSlots.filter(stack=>itemToItemData(stack.item).type === ItemType.Key); + const removedCount = this.internalSlots.length - newslots.length; + this.internalSlots = newslots; + return removedCount; + } + } diff --git a/src/core/VisibleInventory.ts b/src/core/VisibleInventory.ts index f2adda5..f3b0e47 100644 --- a/src/core/VisibleInventory.ts +++ b/src/core/VisibleInventory.ts @@ -105,4 +105,8 @@ export class VisibleInventory implements DisplayableInventory{ public resetCount(): void { this.count = this.slots.length; } + + public clearForEventide(): void { + this.count-=this.slots.clearAllButKeyItems(); + } } diff --git a/src/surfaces/DisplayPane.tsx b/src/surfaces/DisplayPane.tsx index dd77df7..7c3c1c7 100644 --- a/src/surfaces/DisplayPane.tsx +++ b/src/surfaces/DisplayPane.tsx @@ -58,12 +58,8 @@ export const DisplayPane: React.FC = ({command,editCommand,dis const cmdString = e.target.value; setCommandString(cmdString); const parsedCommand = parseCommand(cmdString); - if(parsedCommand){ - editCommand(parsedCommand); - setHasError(false); - }else{ - setHasError(true); - } + editCommand(parsedCommand); + setHasError(cmdString!=="" &&!cmdString.startsWith("#") && !parsedCommand.isValid()); }}> diff --git a/src/surfaces/OptionPage.tsx b/src/surfaces/OptionPage.tsx index fdccd61..b422d99 100644 --- a/src/surfaces/OptionPage.tsx +++ b/src/surfaces/OptionPage.tsx @@ -1,107 +1,106 @@ -import { TitledList } from "components/TitledList" -import { parseCommand } from "core/Parser"; -import { saveAs } from "data/FileSaver"; -import { useRef, useState } from "react"; - -type OptionPageProps = { - interlaceInventory: boolean, - setInterlaceInventory: (value: boolean)=>void, - commandText: string, - setCommandText: (value: string)=>void, -} - -export const OptionPage: React.FC = ({ - interlaceInventory, - setInterlaceInventory, - commandText, - setCommandText -}) => { - const [currentText, setCurrentText] = useState(commandText); - const [fileName, setFileName] = useState(""); - const uploadRef = useRef(null); - - return ( -
- - { - const files = e.target.files; - if(files?.length && files[0]){ - const file = files[0]; - const fileName = file.name.endsWith(".txt") ? file.name.substring(0, file.name.length-4) : file.name; - setFileName(fileName); - file.text().then(text=>{ - setCurrentText(text); - setCommandText(text); - }); - } - }}/> - - -
- -

- Interlace Inventory with GameData - -

-

- Toggle whether Visible Inventory should be displayed separetely from Game Data or interlaced. -

- -

Import / Export

-

- You can also directly copy, paste, or edit the commands here -

-

- - - { - setFileName(e.target.value); - }} - placeholder="File name" - /> -