1
0
Fork 0

options refactoring

This commit is contained in:
iTNTPiston 2022-06-24 14:58:33 -07:00
parent 155bc5be84
commit 7174332bf1
9 changed files with 360 additions and 375 deletions

View file

@ -24,6 +24,10 @@ h1 {
font-size: 10pt;
}
.CommandItemInvalid {
color: red;
}
.CommandItem {
padding: 2px 2px 2px 10px;
cursor: pointer;
@ -110,7 +114,7 @@ p.Reference {
padding-left: 20px;
}
p.Example {
.Example {
color:#eeee00;
}
@ -123,3 +127,65 @@ h3.Reference2 {
margin-top: 0;
margin-bottom: 0;
}
button.MainButton {
font-family: CalamitySans;
font-weight: bold;
display: inline-block;
padding: 5px;
width: 100px;
margin: 5px;
background-color: #00000099;
color: #ffffff;
border-radius: 0;
border: 2px solid #888888;
}
button.MainButton:hover {
border-color: #eeeeee;
box-shadow: 0 0 5px #eeeeee;
}
button.MainButton:active {
background-color: #888888;
}
.FullWidth {
width: 100% !important;
}
div.OtherPage {
height: 100%;
width: 100%;
color: #ffffff;
}
div.OtherPageContent{
padding: 10px;
}
.MainInput {
display: block;
width: 100%;
background-color: #00000099;
color: #ffffff;
border-radius: 0;
border: 2px solid #888888;
margin-bottom: 5px;
}
input.MainInput {
font-size: 14pt;
font-family: CalamitySans;
}
textarea {
resize: none;
height: 300px;
}
.MainInput:focus-visible {
outline: none;
border-color: #eeeeee;
box-shadow: 0 0 5px #eeeeee;
}

View file

@ -1,4 +1,4 @@
import { Command, CommandNothing } from "core/Command";
import { Command, CommandNop } from "core/Command";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import "./App.css";
@ -11,16 +11,18 @@ import { ItemList } from "components/ItemList";
import { TitledList } from "components/TitledList";
import { createSimulationState, SimulationState } from "core/SimulationState";
import { ReferencePage } from "surfaces/ReferencePage";
import { OptionPage } from "surfaces/OptionPage";
const getDefaultCommands = (): Command[]=>{
const encoded = localStorage.getItem("HDS.CurrentCommandsText");
if(encoded){
const lines = encoded.split("\n");
return lines.map(l=>parseCommand(l)).filter(c=>c) as Command[];
return lines.map(parseCommand);
}
return [
parseCommand("Get 5 Diamond 1 Slate 1 Glider 4 SpiritOrb"),
parseCommand("Save"),
parseCommand("# Magically break 4 slots"),
parseCommand("Break 4 Slots"),
parseCommand("Reload"),
parseCommand("Save"),
@ -30,16 +32,16 @@ const getDefaultCommands = (): Command[]=>{
export const App: React.FC = () => {
const [page, setPageInState] = useState<string>("#simulation");
const [overlaySave, setOverlaySave] = useState<boolean>(false);
// Option States
const [interlaceInventory, setInterlaceInventory] = useState<boolean>(false);
const [commands, setCommands] = useState<Command[]>(getDefaultCommands());
const [selectedSaveName, setSelectedSaveName] = useState<string>("");
const [displayIndex, setDisplayIndex] = useState<number>(0);
const [contextMenuX, setContextMenuX] = useState<number>(0);
const [contextMenuY, setContextMenuY] = useState<number>(0);
const [contextMenuShowing, setContextMenuShowing] = useState<boolean>(false);
const [contextIndex, setContextIndex] = useState<number>(-1);
const uploadRef = useRef<HTMLInputElement>(null);
const contextMenuRef = useRef<HTMLDivElement>(null);
// compute props
const simulationStates = useMemo(()=>{
@ -51,6 +53,9 @@ export const App: React.FC = () => {
});
return simulationStates;
}, [commands]);
const commandText = useMemo(()=>{
return commands.map(c=>c.getDisplayString()).join("\n");
}, [commands]);
const setPage = useCallback((hash: string)=>{
window.location.hash = hash;
@ -66,7 +71,7 @@ export const App: React.FC = () => {
if(e.code==="ArrowDown"){
if(displayIndex===commands.length-1){
const arrCopy = [...commands];
arrCopy.push(new CommandNothing());
arrCopy.push(new CommandNop(""));
setCommands(arrCopy);
setDisplayIndex(arrCopy.length-1);
}else{
@ -88,40 +93,32 @@ export const App: React.FC = () => {
}, [commands]);
useEffect(()=>{
if(contextMenuRef.current && contextMenuShowing){
if(contextIndex < 0 || contextIndex >= commands.length){
setContextIndex(-1);
}else if(contextMenuRef.current){
const rect = contextMenuRef.current.getBoundingClientRect();
if (rect.bottom > window.innerHeight){
setContextMenuY(contextMenuY-rect.height);
}
}
}, [contextMenuRef, contextMenuShowing]);
}, [contextMenuRef, contextIndex, commands]);
return (
<div className='Calamity'>
<input ref={uploadRef} id="Upload" type="File" hidden onChange={(e)=>{
const files = e.target.files;
if(files?.length && files[0]){
const file = files[0];
file.text().then(text=>{
const lines = text.split("\n");
const parsedCommands: Command[] = lines.map(l=>parseCommand(l)).filter(c=>c) as Command[];
setDisplayIndex(0);
setContextIndex(-1);
setContextMenuShowing(false);
setCommands(parsedCommands);
});
}
}}/>
<div id="NavBar" style={{
backgroundColor: "#262626",
height: 40
}}>
<button onClick={()=>{
<button className="MainButton" onClick={()=>{
setPage("#simulation");
}}>Simulation</button>
<button onClick={()=>{
<button className="MainButton" onClick={()=>{
setPage("#reference");
}}>Reference</button>
<button disabled>Options</button>
<button className="MainButton" onClick={()=>{
setPage("#options");
}}>Options</button>
</div>
<div id="SidePane" style={{
@ -145,8 +142,9 @@ export const App: React.FC = () => {
<CommandItem
onClick={()=>{
setSelectedSaveName("");
setPage("#simulation");
}}
comment={false}
useListItem
isSelected={selectedSaveName===""}
>
@ -159,9 +157,10 @@ export const App: React.FC = () => {
<CommandItem
onClick={()=>{
setSelectedSaveName(name);
setPage("#simulation");
}}
comment={false}
isSelected={selectedSaveName===name}
useListItem
>
{name}
</CommandItem>
@ -188,6 +187,7 @@ export const App: React.FC = () => {
<CommandItem
onClick={()=>{
setDisplayIndex(i);
setPage("#simulation");
const inputField = document.getElementById("CommandInputField");
if(inputField){
inputField.focus();
@ -197,12 +197,13 @@ export const App: React.FC = () => {
setContextIndex(i);
setContextMenuX(x);
setContextMenuY(y);
setContextMenuShowing(true);
}}
key={i}
isSelected={displayIndex===i}
isContextSelected={contextIndex===i}
comment={c.getDisplayString().startsWith("#")}
isComment={c.getDisplayString().startsWith("#")}
useListItem={!c.getDisplayString().startsWith("#")}
isInvalid={!c.isValid()}
>
{c.getDisplayString()}
</CommandItem>
@ -210,24 +211,14 @@ export const App: React.FC = () => {
}
<CommandItem onClick={()=>{
const arrCopy = [...commands];
arrCopy.push(new CommandNothing());
arrCopy.push(new CommandNop(""));
setCommands(arrCopy);
}} onContextMenu={()=>{
const arrCopy = [...commands];
arrCopy.push(new CommandNothing());
arrCopy.push(new CommandNop(""));
setCommands(arrCopy);
}}>(new)</CommandItem>
<CommandItem onClick={(x,y)=>{
setContextIndex(-1);
setContextMenuX(x);
setContextMenuY(y);
setContextMenuShowing(true);
}} onContextMenu={(x,y)=>{
setContextIndex(-1);
setContextMenuX(x);
setContextMenuY(y);
setContextMenuShowing(true);
}}>(options)</CommandItem>
</ol>
</TitledList>
@ -248,8 +239,6 @@ export const App: React.FC = () => {
<div style={{
maxHeight: 220,
height: "30vh",
border: "1px solid black",
boxSizing: "border-box",
overflowY: "hidden",
color: "white",
backgroundColor: "#262626"
@ -291,7 +280,7 @@ export const App: React.FC = () => {
}}>
{displayIndex >= 0 && displayIndex < commands.length &&
<DisplayPane
overlaySave={overlaySave}
overlaySave={interlaceInventory}
displayIndex={displayIndex}
command={commands[displayIndex].getDisplayString()}
simulationState={simulationStates[displayIndex]}
@ -309,20 +298,32 @@ export const App: React.FC = () => {
{
page === "#reference" && <ReferencePage />
}
{
page === "#options" &&
<OptionPage
interlaceInventory={interlaceInventory}
setInterlaceInventory={setInterlaceInventory}
commandText={commandText}
setCommandText={(value)=>{
if(value !== commandText){
const commands = value.split("\n").map(parseCommand)
setCommands(commands);
}
}}
/>
}
</div>
{
contextMenuShowing && <div style={{
contextIndex >= 0 && contextIndex < commands.length && <div style={{
position: "absolute",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
}} onClick={()=>{
setContextMenuShowing(false);
setContextIndex(-1);
}} onContextMenu={(e)=>{
setContextMenuShowing(false);
setContextIndex(-1);
e.preventDefault();
}}>
@ -339,12 +340,11 @@ export const App: React.FC = () => {
listStyleType: "none",
paddingInlineStart: 0
}}>
{contextIndex >= 0 ? <>
<CommandItem onClick={()=>{
const arrCopy = [...commands];
arrCopy.splice(contextIndex, 0, new CommandNothing());
arrCopy.splice(contextIndex, 0, new CommandNop(""));
setCommands(arrCopy);
setContextMenuShowing(false);
setContextIndex(-1);
}}>Insert Above</CommandItem>
<CommandItem onClick={()=>{
@ -354,7 +354,6 @@ export const App: React.FC = () => {
arrCopy[contextIndex] = arrCopy[contextIndex-1];
arrCopy[contextIndex-1] = temp;
setCommands(arrCopy);
setContextMenuShowing(false);
setContextIndex(-1);
}
@ -365,28 +364,9 @@ export const App: React.FC = () => {
if(displayIndex >= commands.length){
setDisplayIndex(commands.length-1);
}
setContextMenuShowing(false);
setContextIndex(-1);
}
}}>Delete</CommandItem></> :
<>
<CommandItem onClick={()=>{
setOverlaySave(!overlaySave);
}}>Toggle Save Overlay</CommandItem>
<CommandItem onClick={()=>{
if(uploadRef.current){
uploadRef.current.click();
}
}}>Import</CommandItem>
<CommandItem onClick={()=>{
const lines = commands.map(c=>c.getDisplayString());
const text = lines.join("\n");
saveAs(text, "dupe.txt");
}}>Export</CommandItem>
</>
}
}}>Delete</CommandItem>
</ul>
</div>

View file

@ -1,30 +1,55 @@
import clsx from "clsx";
import { PropsWithChildren } from "react";
import React, { PropsWithChildren, useCallback } from "react";
type CommandItemProps = PropsWithChildren<{
useListItem?: boolean,
isSelected?: boolean,
comment?:boolean,
isComment?: boolean,
isInvalid?: boolean,
isContextSelected?: boolean,
onClick: (x: number, y: number)=>void,
onContextMenu?: (x: number, y: number)=>void
}>;
export const CommandItem: React.FC<CommandItemProps> = ({isSelected, isContextSelected, comment,children, onClick, onContextMenu}) => {
if(comment){
return <div className={clsx("CommandItem", isSelected && "CommandItemSelected", isContextSelected&& "CommandItemContextSelected",comment && "CommandItemComment")}>{children}</div>;
}
return <li
className={clsx("CommandItem", isSelected && "CommandItemSelected", isContextSelected&& "CommandItemContextSelected",comment && "CommandItemComment")}
onClick={(e)=>{
onClick(e.clientX, e.clientY);
}}
onContextMenu={(e)=>{
if(onContextMenu){
onContextMenu(e.clientX,e.clientY);
e.preventDefault();
}
export const CommandItem: React.FC<CommandItemProps> = ({
useListItem,
isSelected,
isContextSelected,
isComment,
isInvalid,
onClick,
onContextMenu,
children
}) => {
const className = clsx(
"CommandItem",
isSelected && "CommandItemSelected",
isContextSelected && "CommandItemContextSelected",
isComment && "CommandItemComment",
isInvalid && !isComment && "CommandItemInvalid",
);
}}
>{children}&nbsp;</li>;
const clickHandler = useCallback((e: React.MouseEvent)=>{
onClick(e.clientX, e.clientY)
}, [onClick]);
const contextMenuHandler = useCallback((e: React.MouseEvent)=>{
if(onContextMenu){
onContextMenu(e.clientX,e.clientY);
e.preventDefault();
}
}, [onContextMenu]);
if(!useListItem){
return (
<div className={className} onClick={clickHandler} onContextMenu={contextMenuHandler}>
{children}&nbsp;
</div>
);
}
return (
<li className={className} onClick={clickHandler} onContextMenu={contextMenuHandler}>
{children}&nbsp;
</li>
);
};

View file

@ -1,27 +1,29 @@
import { Inventory } from "./Inventory";
import { Item, ItemStack } from "./Item";
import { SimulationState } from "./SimulationState";
export interface Command {
isValid(): boolean,
execute(state: SimulationState): void,
getDisplayString(): string,
}
export class CommandNothing implements Command {
execute(_state: Inventory): void {
class CommandImpl implements Command{
isValid(): boolean {
return true;
}
execute(_state: SimulationState): void {
// nothing
}
getDisplayString(): string {
return "";
throw new Error("Method not implemented.");
}
}
export class CommandInitialize implements Command {
export class CommandInitialize extends CommandImpl {
private stacks: ItemStack[];
constructor(stacks: ItemStack[]){
super();
this.stacks = stacks;
}
@ -34,7 +36,7 @@ export class CommandInitialize implements Command {
}
export class CommandSave implements Command {
export class CommandSave extends CommandImpl {
public execute(state: SimulationState): void {
state.save();
@ -44,9 +46,10 @@ export class CommandSave implements Command {
}
}
export class CommandSaveAs implements Command {
export class CommandSaveAs extends CommandImpl {
private name: string;
constructor(name: string){
super();
this.name = name;
}
public execute(state: SimulationState): void {
@ -57,9 +60,10 @@ export class CommandSaveAs implements Command {
}
}
export class CommandReload implements Command {
export class CommandReload extends CommandImpl {
private name?: string;
constructor(name?: string){
super();
this.name = name;
}
public execute(state: SimulationState): void {
@ -70,9 +74,10 @@ export class CommandReload implements Command {
}
}
export class CommandUse implements Command {
export class CommandUse extends CommandImpl{
private name: string;
constructor(name: string){
super();
this.name = name;
}
public execute(state: SimulationState): void {
@ -81,12 +86,16 @@ export class CommandUse implements Command {
public getDisplayString(): string {
return `Use ${this.name}`;
}
public isValid(): boolean {
return false; // this command is deprecated
}
}
export class CommandBreakSlots implements Command {
export class CommandBreakSlots extends CommandImpl {
private numToBreak: number;
constructor(numToBreak: number){
super();
this.numToBreak = numToBreak;
}
@ -98,11 +107,12 @@ export class CommandBreakSlots implements Command {
}
}
export class CommandAdd implements Command {
export class CommandAdd extends CommandImpl {
private verb: string;
private count: number;
private item: Item;
constructor(verb: string, count: number, item: Item){
super();
this.verb = verb;
this.count = count;
this.item = item;
@ -116,10 +126,11 @@ export class CommandAdd implements Command {
}
}
export class CommandAddWithoutCount implements Command {
export class CommandAddWithoutCount extends CommandImpl {
private verb: string;
private item: Item;
constructor(verb: string, item: Item){
super();
this.verb = verb;
this.item = item;
}
@ -132,10 +143,11 @@ export class CommandAddWithoutCount implements Command {
}
}
export class CommandAddMultiple implements Command {
export class CommandAddMultiple extends CommandImpl {
private verb: string;
private stacks: ItemStack[];
constructor(verb: string, stacks: ItemStack[]){
super();
this.verb = verb;
this.stacks = stacks;
}
@ -149,13 +161,14 @@ export class CommandAddMultiple implements Command {
}
}
export class CommandRemove implements Command {
export class CommandRemove extends CommandImpl {
private verb: string;
private count: number;
private item: Item;
private slot: number;
private noSlot: boolean;
constructor(verb: string, count: number, item: Item, slot: number, noSlot: boolean){
super();
this.verb = verb;
this.count = count;
this.item = item;
@ -171,12 +184,13 @@ export class CommandRemove implements Command {
}
}
export class CommandRemoveWithoutCount implements Command {
export class CommandRemoveWithoutCount extends CommandImpl {
private verb: string;
private item: Item;
private slot: number;
private noSlot: boolean;
constructor(verb: string, item: Item, slot: number, noSlot: boolean){
super();
this.verb = verb;
this.item = item;
this.slot = slot;
@ -191,10 +205,11 @@ export class CommandRemoveWithoutCount implements Command {
}
}
export class CommandRemoveMultiple implements Command {
export class CommandRemoveMultiple extends CommandImpl {
private verb: string;
private stacks: ItemStack[];
constructor(verb: string, stacks: ItemStack[]){
super();
this.verb = verb;
this.stacks = stacks;
}
@ -216,11 +231,12 @@ const joinItemStackString = (initial: string, stacks: ItemStack[]): string => {
return parts.join(" ");
};
export class CommandDaP implements Command {
export class CommandDaP extends CommandImpl {
private count: number;
private item: Item;
constructor(count: number, item: Item,){
constructor(count: number, item: Item){
super();
this.count = count;
this.item = item;
}
@ -233,11 +249,12 @@ export class CommandDaP implements Command {
}
}
export class CommandEquip implements Command {
export class CommandEquip extends CommandImpl {
private item: Item;
private slot: number;
private noSlot: boolean;
constructor(item: Item, slot: number, noSlot: boolean){
super();
this.item = item;
this.slot = slot;
this.noSlot = noSlot;
@ -252,11 +269,12 @@ export class CommandEquip implements Command {
}
}
export class CommandUnequip implements Command {
export class CommandUnequip extends CommandImpl {
private item: Item;
private slot: number;
private noSlot: boolean;
constructor(item: Item, slot: number, noSlot: boolean){
super();
this.item = item;
this.slot = slot;
this.noSlot = noSlot;
@ -271,9 +289,10 @@ export class CommandUnequip implements Command {
}
}
export class CommandShootArrow implements Command {
export class CommandShootArrow extends CommandImpl {
private count: number;
constructor(count: number){
super();
this.count = count;
}
@ -285,7 +304,7 @@ export class CommandShootArrow implements Command {
}
}
export class CommandCloseGame implements Command {
export class CommandCloseGame extends CommandImpl {
public execute(state: SimulationState): void {
state.closeGame();
}
@ -294,9 +313,10 @@ export class CommandCloseGame implements Command {
}
}
export class CommandSync implements Command {
export class CommandSync extends CommandImpl {
private actionString: string;
constructor(actionString: string){
super();
this.actionString = actionString;
}
@ -308,20 +328,24 @@ export class CommandSync implements Command {
}
}
export class CommandComment implements Command {
private name: string;
constructor(name: string){
this.name = name;
export class CommandNop extends CommandImpl {
private text: string;
constructor(text: string){
super();
this.text = text;
}
public isValid(): boolean {
return false;
}
public execute(_state: SimulationState): void {
// nothing
}
public getDisplayString(): string {
return `# ${this.name}`;
return this.text;
}
}
export class CommandSortKey implements Command {
export class CommandSortKey extends CommandImpl {
static Op = 0x5;
// public fromBuffer(_buf: Buffer): number {
// return 0;
@ -339,7 +363,7 @@ export class CommandSortKey implements Command {
}
}
export class CommandSortMaterial implements Command {
export class CommandSortMaterial extends CommandImpl {
static Op = 0x6;
// public fromBuffer(_buf: Buffer): number {
// return 0;

View file

@ -1,215 +0,0 @@
// import { Item, ItemStack, itemToItemData, ItemType, ItemTypes } from "./Item";
// import { Slots } from "./Slots";
export class Inventory {
// private slots: Slots = new Slots([]);
// private savedSlots: Slots = new Slots([]);
// private namedSlots: {[name: string]: Slots} = {};
// private numBroken = 0;
// private isInitialSort = false;
// private isAltered = true;
// private inaccurate = false;
// private turnedInOrbs = 0;
// public deepClone(): Inventory {
// const other = new Inventory();
// other.slots = this.slots.deepClone();
// other.savedSlots = this.savedSlots.deepClone();
// other.numBroken = this.numBroken;
// other.isInitialSort = this.isInitialSort;
// other.isAltered = this.isAltered;
// other.inaccurate = this.inaccurate;
// other.turnedInOrbs = this.turnedInOrbs;
// other.namedSlots = {};
// for(const name in this.namedSlots){
// other.namedSlots[name] = this.namedSlots[name].deepClone();
// }
// return other;
// }
// public getSlots(): Slots {
// return this.slots;
// }
// public getSavedSlots(): Slots {
// return this.savedSlots;
// }
// public getNumBroken(): number {
// return this.numBroken;
// }
// public isInaccurate(): boolean {
// return this.inaccurate;
// }
// public getTurnedInOrbs(): number {
// return this.turnedInOrbs;
// }
// public rawInit(stacks: ItemStack[]) {
// this.slots = new Slots(stacks);
// }
// public init(stacks: ItemStack[]) {
// this.slots = new Slots([]);
// stacks.forEach(s=>{
// this.slots.add(s.item, s.count);
// });
// this.numBroken = 0;
// this.isInitialSort = false;
// this.isAltered = true;
// this.inaccurate = false;
// }
// public closeGame() {
// this.numBroken = 0;
// this.isInitialSort = false;
// this.isAltered = true;
// this.inaccurate = false;
// this.slots = new Slots([]);
// }
// public addBrokenSlots(num: number) {
// this.numBroken+=num;
// }
// public setTag(name: string){
// this.namedSlots[name] = this.savedSlots.deepClone();
// }
// public applyTag(name: string){
// if(name in this.namedSlots){
// this.savedSlots = this.namedSlots[name].deepClone();
// }else{
// this.savedSlots = new Slots([]);
// }
// }
// public save() {
// if(this.isAltered){
// this.savedSlots = this.slots.deepClone();
// }
// // Inventory Corruption
// // get durability transfer slots
// const durabilityTransferSlots: number[] = [];
// const equippedWeapon = this.slots.getFirstEquippedSlotIndex(ItemType.Weapon);
// if(equippedWeapon>=0){
// durabilityTransferSlots.push(equippedWeapon);
// }
// const equippedBow = this.slots.getFirstEquippedSlotIndex(ItemType.Bow);
// if(equippedBow>=0){
// durabilityTransferSlots.push(equippedBow);
// }
// const equippedShield = this.slots.getFirstEquippedSlotIndex(ItemType.Shield);
// if(equippedShield>=0){
// durabilityTransferSlots.push(equippedShield);
// }
// durabilityTransferSlots.forEach(s=>{
// if(s<this.savedSlots.length){
// // We ignore the case where durability transfer happens from equipment to equipment
// if(itemToItemData(this.savedSlots.get(s).item).stackable){
// this.savedSlots.get(s).count = 999;
// }
// }
// });
// }
// public reload() {
// // get things to dupe
// const dupeMap: {[k in ItemType]: Slots} = {
// [ItemType.Weapon]: new Slots([]),
// [ItemType.Bow]: new Slots([]),
// [ItemType.Arrow]: new Slots([]),
// [ItemType.Shield]: new Slots([]),
// [ItemType.Material]: new Slots([]),
// [ItemType.Meal]: new Slots([]),
// [ItemType.Key]: new Slots([])
// };
// for(let i=Math.max(0, this.slots.length-this.numBroken);i<this.slots.length;i++){
// const stack = this.slots.get(i);
// const itemData = itemToItemData(stack.item);
// dupeMap[itemData.type].addStackCopy(stack);
// }
// // apply dupe
// //console.log(dupeMap);
// this.slots = new Slots([]);
// // const dupeType = (type: ItemType) => {
// // }
// ItemTypes.forEach(type=>{
// this.slots.addSlotsToEnd(dupeMap[type]);
// this.slots.addSlotsToEnd(this.savedSlots.getByType(type).deepClone());
// });
// this.slots.sortArrows();
// this.isInitialSort = true;
// this.isAltered = false;
// }
// public sortKey() {
// const nonKeyItems = this.slots.getBeforeType(ItemType.Key);
// const keyItems = this.slots.getByType(ItemType.Key);
// keyItems.sort();
// nonKeyItems.addSlotsToEnd(keyItems);
// this.slots = nonKeyItems;
// this.isAltered=true;
// this.isInitialSort=false;
// }
// public sortMaterial() {
// const beforeMaterial = this.slots.getBeforeType(ItemType.Material);
// const afterMaterial = this.slots.getAfterType(ItemType.Material);
// const materials = this.slots.getByType(ItemType.Material);
// if(this.isInitialSort){
// // the materials in broken slots are not sorted
// const brokenSlots = Math.max(0, this.numBroken - afterMaterial.length);
// const noSortPart = materials.removeFromEnd(brokenSlots);
// materials.sort();
// beforeMaterial.addSlotsToEnd(materials);
// beforeMaterial.addSlotsToEnd(noSortPart);
// beforeMaterial.addSlotsToEnd(afterMaterial);
// }else{
// materials.sort();
// beforeMaterial.addSlotsToEnd(materials);
// beforeMaterial.addSlotsToEnd(afterMaterial);
// }
// this.slots = beforeMaterial;
// this.isInitialSort = false;
// this.isAltered=true;
// }
// public remove(item: Item, count: number, slot: number) {
// this.slots.remove(item, count, slot);
// if(item===Item.SpiritOrb){
// this.turnedInOrbs+=count;
// }
// this.isAltered=true;
// }
// public add(item: Item, count: number) {
// this.slots.add(item, count);
// if(itemToItemData(item).type===ItemType.Arrow){
// this.slots.sortArrows();
// }
// this.isAltered=true;
// }
// public equipEquipmentOrArrow(item: Item, slot: number) {
// this.slots.equip(item, slot);
// this.isAltered=true;
// }
// public unequipEquipment(item: Item, slot: number){
// this.slots.unequip(item, slot);
// this.isAltered=true;
// }
// public shootArrow(item: Item, count: number){
// this.slots.shoot(item, count);
// this.isAltered=true;
// }
}

View file

@ -5,11 +5,10 @@ import {
CommandAddWithoutCount,
CommandBreakSlots,
CommandCloseGame,
CommandComment,
CommandDaP,
CommandEquip,
CommandInitialize,
CommandNothing,
CommandNop,
CommandReload,
CommandRemove,
CommandRemoveMultiple,
@ -25,25 +24,24 @@ import {
} from "./Command";
import { Item, ItemStack } from "./Item";
export const parseCommand = (cmdString: string): Command | undefined => {
export const parseCommand = (cmdString: string): Command => {
if(cmdString.startsWith("# ")){
return new CommandComment(cmdString.substring(2));
}
const tokens = cmdString.split(" ").filter(i=>i);
if(tokens.length===0){
return new CommandNothing();
return new CommandNop("");
}
// intialize
if(tokens.length>1 && tokens[0] === "Initialize"){
const stacks = parseItemStacks(tokens, 1);
return stacks ? new CommandInitialize(stacks) : undefined;
if(stacks){
return new CommandInitialize(stacks);
}
}
// Save/Reload
if(tokens.length===1 && tokens[0] === "Save"){
return new CommandSave();
}
// // Multi Save
// Multi Save
if (tokens.length === 3 && tokens[0] === "Save" && tokens[1] === "As"){
const name = tokens[2];
return new CommandSaveAs(name);
@ -73,18 +71,18 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if(Number.isInteger(count) && item in Item){
return new CommandAdd(tokens[0], count, Item[item as keyof typeof Item]);
}
return undefined;
}
if (tokens.length === 2 && isAddVerb(tokens[0])){
const item = tokens[1];
if(item in Item){
return new CommandAddWithoutCount(tokens[0], Item[item as keyof typeof Item]);
}
return undefined;
}
if(tokens.length>2 && isAddVerb(tokens[0])){
const stacks = parseItemStacks(tokens, 1);
return stacks ? new CommandAddMultiple(tokens[0], stacks) : undefined;
if(stacks){
return new CommandAddMultiple(tokens[0], stacks);
}
}
// remove X item From Slot Y
if (tokens.length === 6 && isRemoveVerb(tokens[0]) && tokens[3] === "From" && tokens[4] ==="Slot" ){
@ -94,7 +92,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if(Number.isInteger(count) && Number.isInteger(slot) && item in Item){
return new CommandRemove(tokens[0], count, Item[item as keyof typeof Item], slot-1, false);
}
return undefined;
}
// remove X item
if (tokens.length === 3 && isRemoveVerb(tokens[0]) ){
@ -103,7 +100,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if(Number.isInteger(count) && item in Item){
return new CommandRemove(tokens[0], count, Item[item as keyof typeof Item], 0, true);
}
return undefined;
}
// remove item From Slot Y
if (tokens.length === 5 && isRemoveVerb(tokens[0]) && tokens[2] === "From" && tokens[3] ==="Slot" ){
@ -112,7 +108,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if(Number.isInteger(slot) && item in Item){
return new CommandRemoveWithoutCount(tokens[0], Item[item as keyof typeof Item], slot-1, false);
}
return undefined;
}
// remove item
if (tokens.length === 2 && isRemoveVerb(tokens[0]) ){
@ -120,12 +115,13 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if(item in Item){
return new CommandRemoveWithoutCount(tokens[0], Item[item as keyof typeof Item], 0, true);
}
return undefined;
}
// remove multiple
if(tokens.length>2 && isRemoveVerb(tokens[0])){
const stacks = parseItemStacks(tokens, 1);
return stacks ? new CommandRemoveMultiple(tokens[0], stacks) : undefined;
if(stacks){
return new CommandRemoveMultiple(tokens[0], stacks);
}
}
//Shortcut for drop and pick up
if (tokens.length === 3 && tokens[0] === "D&P" ){
@ -134,7 +130,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if(Number.isInteger(count) && item in Item){
return new CommandDaP(count, Item[item as keyof typeof Item]);
}
return undefined;
}
// Equip item In Slot X
@ -144,7 +139,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if( Number.isInteger(slot) && item in Item){
return new CommandEquip(Item[item as keyof typeof Item], slot-1, false);
}
return undefined;
}
// Equip item
if (tokens.length === 2 && tokens[0] === "Equip"){
@ -152,7 +146,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if( item in Item){
return new CommandEquip(Item[item as keyof typeof Item], 0, true);
}
return undefined;
}
// Unequip item in slot X
if (tokens.length === 5 && tokens[0] === "Unequip" && tokens[2] === "In" && tokens[3] ==="Slot" ){
@ -161,7 +154,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if( Number.isInteger(slot) && item in Item){
return new CommandUnequip(Item[item as keyof typeof Item], slot-1, false);
}
return undefined;
}
// Unequip item
if (tokens.length === 2 && tokens[0] === "Unequip"){
@ -169,7 +161,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if( item in Item){
return new CommandUnequip(Item[item as keyof typeof Item], -1, true);
}
return undefined;
}
// Shoot X Arrow
if (tokens.length === 3 && tokens[0] === "Shoot" && tokens[2] === "Arrow"){
@ -177,7 +168,6 @@ export const parseCommand = (cmdString: string): Command | undefined => {
if( Number.isInteger(count) ){
return new CommandShootArrow(count);
}
return undefined;
}
if(tokens.length===2 && tokens[0] === "Sort" && tokens[1] === "Key"){
@ -193,15 +183,15 @@ export const parseCommand = (cmdString: string): Command | undefined => {
return new CommandSync("Sync GameData");
}
return undefined;
return new CommandNop(cmdString);
};
const isAddVerb = (token: string): boolean => {
return token === "Get" || token === "Cook" || token === "Add" || token === "Pickup";
return token === "Get" || token === "Cook" || token === "Add" || token === "Pickup" || token === "Buy";
};
const isRemoveVerb = (token: string): boolean => {
return token === "Remove" || token === "Sell" || token === "Eat" || token === "Drop";
return token === "Remove" || token === "Sell" || token === "Eat" || token === "Drop" || token === "With";
};
const parseItemStacks = (tokens: string[], from: number): ItemStack[] | undefined => {

View file

@ -53,6 +53,7 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,editCommand,dis
outline: "none",
}}value={commandString}
placeholder="Type command here..."
spellCheck={false}
onChange={(e)=>{
const cmdString = e.target.value;
setCommandString(cmdString);

107
src/surfaces/OptionPage.tsx Normal file
View file

@ -0,0 +1,107 @@
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<OptionPageProps> = ({
interlaceInventory,
setInterlaceInventory,
commandText,
setCommandText
}) => {
const [currentText, setCurrentText] = useState<string>(commandText);
const [fileName, setFileName] = useState<string>("");
const uploadRef = useRef<HTMLInputElement>(null);
return (
<div className="OtherPage">
<input ref={uploadRef} id="Upload" type="File" hidden onChange={(e)=>{
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);
});
}
}}/>
<TitledList title="Options">
<div className="OtherPageContent">
<h3 className="Reference">
Interlace Inventory with GameData
<button className="MainButton" onClick={()=>{
setInterlaceInventory(!interlaceInventory);
}}>
{interlaceInventory ? "ON" : "OFF"}
</button>
</h3>
<h4 className="Reference">
Toggle whether Visible Inventory should be displayed separetely from Game Data or interlaced.
</h4>
<h3 className="Reference">Import / Export</h3>
<h4 className="Reference">
You can also directly copy, paste, or edit the commands here
</h4>
<p className="Reference">
<button className="MainButton" onClick={()=>{
if(uploadRef.current){
uploadRef.current.click();
}
}}>
Import
</button>
<button className="MainButton" onClick={()=>{
saveAs(currentText, fileName+".txt" || "dupe.txt");
}}>
Export
</button>
<input
className="MainInput"
spellCheck={false}
value={fileName}
onChange={(e)=>{
setFileName(e.target.value);
}}
placeholder="File name"
/>
<textarea
className="MainInput"
spellCheck={false}
value={currentText}
onChange={(e)=>{
setCurrentText(e.target.value);
}}
/>
{
currentText !== commandText &&
<>
<button className="MainButton" onClick={()=>{
setCommandText(currentText);
}}>
Save
</button>
<span className="Example">Don't forget to save changes</span>
</>
}
</p>
</div>
</TitledList>
</div>
)
}

View file

@ -5,12 +5,12 @@ import React from "react";
export const ReferencePage: React.FC = React.memo(()=>{
return (
<div style={{height: "100%", width: "100%", color: "white"}}>
<div className="OtherPage">
<TitledList title="Reference">
<div style={{padding: 10}}>
<div className="OtherPageContent">
<h2>Items</h2>
{
getAllItems().map(item=><h4 className="Reference">{item}</h4>)
getAllItems().map((item, i)=><h4 key={i} className="Reference">{item}</h4>)
}
<h2>Commands</h2>
<h3 className="Reference">Initialize X item1 Y item2 Z item3 ...</h3>
@ -81,9 +81,9 @@ export const ReferencePage: React.FC = React.memo(()=>{
<p className="Reference Example">Example: Break 4 Slots</p>
<h3 className="Reference">Get/Add/Cook/Pickup item</h3>
<h3 className="Reference2">Get/Add/Cook/Pickup X item</h3>
<h3 className="Reference2">Get/Add/Cook/Pickup X item1 Y item2 Z item3 ...</h3>
<h3 className="Reference">Get/Add/Cook/Pickup/Buy item</h3>
<h3 className="Reference2">Get/Add/Cook/Pickup/Buy X item</h3>
<h3 className="Reference2">Get/Add/Cook/Pickup/Buy X item1 Y item2 Z item3 ...</h3>
<h4 className="Reference">Simulate obtaining items in game</h4>
<p className="Reference">
Add the item(s) to visible inventory. Sync with Game Data unless you are on Eventide or inside TOTS
@ -103,11 +103,11 @@ export const ReferencePage: React.FC = React.memo(()=>{
<p className="Reference Example">Example 2: Get 10 Apple</p>
<p className="Reference Example">Example 3: Pickup 10 Apple 5 Diamond 1 Slate 5 MasterSword</p>
<h3 className="Reference">Remove/Sell/Eat/Drop item</h3>
<h3 className="Reference2">Remove/Sell/Eat/Drop X item</h3>
<h3 className="Reference2">Remove/Sell/Eat/Drop item From Slot Y</h3>
<h3 className="Reference2">Remove/Sell/Eat/Drop X item From Slot Y</h3>
<h3 className="Reference2">Remove/Sell/Eat/Drop X item1 Y item2 Z item3 ...</h3>
<h3 className="Reference">With/Remove/Sell/Eat/Drop item</h3>
<h3 className="Reference2">With/Remove/Sell/Eat/Drop X item</h3>
<h3 className="Reference2">With/Remove/Sell/Eat/Drop item From Slot Y</h3>
<h3 className="Reference2">With/Remove/Sell/Eat/Drop X item From Slot Y</h3>
<h3 className="Reference2">With/Remove/Sell/Eat/Drop X item1 Y item2 Z item3 ...</h3>
<h4 className="Reference">Simulate removing items in game</h4>
<p className="Reference">
Remove the item(s) to visible inventory. Sync with Game Data unless you are on Eventide or inside TOTS
@ -170,6 +170,13 @@ export const ReferencePage: React.FC = React.memo(()=>{
</p>
<p className="Reference Example">Example: Close Game</p>
<h3 className="Reference">Sync GameData</h3>
<h4 className="Reference">Copy Visible Inventory to Game Data</h4>
<p className="Reference">
Usually done in game by opening and closing inventory.
</p>
<p className="Reference Example">Example: Sync GameData</p>
<h3 className="Reference">Sort Key/Material</h3>
<h4 className="Reference">Simulates press Y to sort tab</h4>
<p className="Reference Example">