diff --git a/Justfile b/Justfile index 54b9248..8621159 100644 --- a/Justfile +++ b/Justfile @@ -2,3 +2,6 @@ deploy: rm -rf docs/ npm run build mv -T build docs + git add . + git commit -m "Local Build Deployment" + git push diff --git a/src/App.css b/src/App.css index ab0651e..9b85622 100644 --- a/src/App.css +++ b/src/App.css @@ -19,8 +19,9 @@ h1 { background-color: #eeaaaa; } -.CommandItemError{ - color: red; +.CommandItemComment{ + color: green; + font-size: 10pt; } .CommandItem { @@ -29,7 +30,7 @@ h1 { } .CommandItem:hover { - border: 1px solid black + background-color: #eeeeee; } .InputError { @@ -77,4 +78,4 @@ h1 { left:8px; color:#eeeeee; float: left; -} \ No newline at end of file +} diff --git a/src/App.tsx b/src/App.tsx index f99d461..70ac03c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -55,6 +55,9 @@ export const App: React.FC = () => { const [contextMenuY, setContextMenuY] = useState(0); const [contextMenuShowing, setContextMenuShowing] = useState(false); const [contextIndex, setContextIndex] = useState(-1); + + const uploadRef = useRef(null); + const contextMenuRef = useRef(null); // compute props const inventories = useMemo(()=>{ const inventories: Inventory[] = []; @@ -85,7 +88,16 @@ export const App: React.FC = () => { }, [commands]); - const uploadRef = useRef(null); + useEffect(()=>{ + if(contextMenuRef.current && contextMenuShowing){ + const rect = contextMenuRef.current.getBoundingClientRect(); + if (rect.bottom > window.innerHeight){ + setContextMenuY(contextMenuY-rect.height); + } + } + }, [contextMenuRef, contextMenuShowing]) + + return (
{ key={i} isSelected={displayIndex===i} isContextSelected={contextIndex===i} - error={inventories[i].isInaccurate()} + comment={c.getDisplayString().startsWith("#")} > {c.getDisplayString()} @@ -140,12 +152,21 @@ export const App: React.FC = () => { 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) @@ -154,7 +175,8 @@ export const App: React.FC = () => { { setContextIndex(-1); e.preventDefault(); }}> -
{ setContextMenuShowing(false); 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); + setContextMenuShowing(false); + setContextIndex(-1); + } + + + }}>Move Up + { if(confirm("Delete?")){ setCommands(commands.filter((_,i)=>i!==contextIndex)); if(displayIndex >= commands.length){ diff --git a/src/assets/img/HyruleBass.png b/src/assets/img/HyruleBass.png new file mode 100644 index 0000000..6775618 Binary files /dev/null and b/src/assets/img/HyruleBass.png differ diff --git a/src/assets/img/LizalfosHorn.png b/src/assets/img/LizalfosHorn.png new file mode 100644 index 0000000..da8dbaf Binary files /dev/null and b/src/assets/img/LizalfosHorn.png differ diff --git a/src/assets/img/LizalfosTalon.png b/src/assets/img/LizalfosTalon.png new file mode 100644 index 0000000..a6c7f6f Binary files /dev/null and b/src/assets/img/LizalfosTalon.png differ diff --git a/src/assets/img/Rushroom.png b/src/assets/img/Rushroom.png new file mode 100644 index 0000000..a73e6fa Binary files /dev/null and b/src/assets/img/Rushroom.png differ diff --git a/src/assets/img/Screw.png b/src/assets/img/Screw.png new file mode 100644 index 0000000..08d7f16 Binary files /dev/null and b/src/assets/img/Screw.png differ diff --git a/src/components/CommandItem.tsx b/src/components/CommandItem.tsx index 4ac4921..daeee45 100644 --- a/src/components/CommandItem.tsx +++ b/src/components/CommandItem.tsx @@ -3,15 +3,15 @@ import { PropsWithChildren } from "react"; type CommandItemProps = PropsWithChildren<{ isSelected?: boolean, + comment?:boolean, isContextSelected?: boolean, - error?: boolean, onClick: (x: number, y: number)=>void, onContextMenu?: (x: number, y: number)=>void }>; -export const CommandItem: React.FC = ({isSelected, isContextSelected,error, children, onClick, onContextMenu}) => { +export const CommandItem: React.FC = ({isSelected, isContextSelected, comment,children, onClick, onContextMenu}) => { return
  • { onClick(e.clientX, e.clientY); }} diff --git a/src/core/Command.ts b/src/core/Command.ts index 24f0911..146b5ee 100644 --- a/src/core/Command.ts +++ b/src/core/Command.ts @@ -322,4 +322,53 @@ export class CommandUnequip implements Command { const slotString = this.noSlot ? "" : ` In Slot ${this.slot+1}` return `Unequip ${this.item}${slotString}`; } -} \ No newline at end of file +} + +export class CommandSetTag implements Command { + private name: string; + constructor(name: string){ + this.name = name; + } + public execute(inv: Inventory): void { + inv.save(); + inv.setTag(this.name); + } + public getDisplayString(): string { + return `Save As ${this.name}` + } +} + +export class CommandApplyTag implements Command { + private name: string; + constructor(name: string){ + this.name = name; + } + public execute(inv: Inventory): void { + inv.applyTag(this.name); + } + public getDisplayString(): string { + return `Use ${this.name}` + } +} + +export class CommandCloseGame implements Command { + public execute(inv: Inventory): void { + inv.closeGame(); + } + public getDisplayString(): string { + return `Close Game`; + } +} + +export class CommandComment implements Command { + private name: string; + constructor(name: string){ + this.name = name; + } + public execute(inv: Inventory): void { + + } + public getDisplayString(): string { + return `# ${this.name}`; + } +} diff --git a/src/core/Inventory.ts b/src/core/Inventory.ts index 7d4a004..33c5803 100644 --- a/src/core/Inventory.ts +++ b/src/core/Inventory.ts @@ -6,10 +6,12 @@ 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(); @@ -18,6 +20,11 @@ export class Inventory { 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; } @@ -37,6 +44,10 @@ export class Inventory { return this.inaccurate; } + public getTurnedInOrbs(): number { + return this.turnedInOrbs; + } + public init(stacks: ItemStack[]) { this.slots = new Slots([]); stacks.forEach(s=>{ @@ -48,10 +59,30 @@ export class Inventory { 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(); @@ -150,6 +181,9 @@ export class Inventory { public remove(item: Item, count: number, slot: number) { this.slots.remove(item, count, slot); + if(item===Item.SpiritOrb){ + this.turnedInOrbs+=count; + } this.isAltered=true; } diff --git a/src/core/Item.ts b/src/core/Item.ts index 83ecfaf..13a970f 100644 --- a/src/core/Item.ts +++ b/src/core/Item.ts @@ -3,8 +3,8 @@ import ImageGlider from "assets/img/Glider.png"; import ImageSpiritOrb from "assets/img/SpiritOrb.png"; import ImageLotus from "assets/img/Lotus.png"; import ImageSilentPrincess from "assets/img/SilentPrincess.png"; -import ImageHoney from "assets/img/Lotus.png"; -import ImageAcorn from "assets/img/SilentPrincess.png"; +import ImageHoney from "assets/img/Honey.png"; +import ImageAcorn from "assets/img/Acorn.png"; import ImageFaroshScale from "assets/img/FaroshScale.png"; import ImageFaroshClaw from "assets/img/FaroshClaw.png"; import ImageFaroshHorn from "assets/img/FaroshHorn.png"; @@ -27,6 +27,11 @@ import ImageShockArrow from "assets/img/ShockArrow.png"; import ImageBombArrow from "assets/img/BombArrow.png"; import ImageAncientArrow from "assets/img/AncientArrow.png"; import ImageShield from "assets/img/PotLid.png"; +import ImageRushroom from "assets/img/Rushroom.png"; +import ImageScrew from "assets/img/Screw.png"; +import ImageHyruleBass from "assets/img/HyruleBass.png"; +import ImageLizalfosHorn from "assets/img/LizalfosHorn.png"; +import ImageLizalfosTalon from "assets/img/LizalfosTalon.png"; export enum ItemType { Weapon = 0, @@ -76,6 +81,12 @@ export enum Item { Core = "Core", Wood = "Wood", + Rushroom = "Rushroom", + Screw = "Screw", + HyruleBass = "HyruleBass", + LizalfosHorn = "LizalfosHorn", + LizalfosTalon = "LizalfosTalon", + SpeedFood = "SpeedFood", Weapon = "Weapon", Bow = "Bow", @@ -122,6 +133,9 @@ const register = (id: number, item: Item, type: ItemType, image: string, options sortOrder, ...(options||{}) }; + if(id in IdToData){ + console.error("Multiple items registered to the same id: "+id+", ("+item+")"); + } IdToData[id] = data; ItemToData[item] = data; } @@ -137,6 +151,7 @@ register(0x01, Item.Glider, ItemType.Key, ImageGlider, { register(0x02, Item.SpiritOrb, ItemType.Key, ImageSpiritOrb); register(0x11, Item.Lotus, ItemType.Material, ImageLotus); +register(0x20, Item.Rushroom, ItemType.Material, ImageRushroom); register(0x12, Item.SilentPrincess, ItemType.Material, ImageSilentPrincess); register(0x13, Item.Honey, ItemType.Material, ImageHoney); register(0x14, Item.Acorn, ItemType.Material, ImageAcorn); @@ -144,10 +159,14 @@ register(0x15, Item.FaroshScale, ItemType.Material, ImageFaroshScale); register(0x16, Item.FaroshClaw, ItemType.Material, ImageFaroshClaw); register(0x17, Item.FaroshHorn, ItemType.Material, ImageFaroshHorn); register(0x18, Item.HeartyBass, ItemType.Material, ImageHeartyBass); +register(0x21, Item.HyruleBass, ItemType.Material, ImageHyruleBass); register(0x19, Item.Beetle, ItemType.Material, ImageBeetle); register(0x1a, Item.Opal, ItemType.Material, ImageOpal); register(0x10, Item.Diamond, ItemType.Material, ImageDiamond); +register(0x23, Item.LizalfosHorn, ItemType.Material, ImageLizalfosHorn); +register(0x24, Item.LizalfosTalon, ItemType.Material, ImageLizalfosTalon); register(0x1b, Item.Tail, ItemType.Material, ImageTail); +register(0x22, Item.Screw, ItemType.Material, ImageScrew); register(0x1c, Item.Spring, ItemType.Material, ImageSpring); register(0x1d, Item.Shaft, ItemType.Material, ImageShaft); register(0x1e, Item.Core, ItemType.Material, ImageCore); @@ -170,7 +189,7 @@ register(0x72, Item.IceArrow, ItemType.Arrow, ImageIceArrow); register(0x73, Item.ShockArrow, ItemType.Arrow, ImageShockArrow); register(0x74, Item.BombArrow, ItemType.Arrow, ImageBombArrow); register(0x75, Item.AncientArrow, ItemType.Arrow, ImageAncientArrow); -register(0x60, Item.Shield, ItemType.Shield, ImageShield, { +register(0x80, Item.Shield, ItemType.Shield, ImageShield, { stackable: false }); diff --git a/src/core/Parser.ts b/src/core/Parser.ts index d74f4a0..3d570eb 100644 --- a/src/core/Parser.ts +++ b/src/core/Parser.ts @@ -1,7 +1,10 @@ -import { Command, CommandAddMaterial, CommandBreakSlots, CommandEquip, CommandEquipArrow, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSortKey, CommandSortMaterial, CommandUnequip } from "./Command"; +import { Command, CommandAddMaterial, CommandApplyTag, CommandBreakSlots, CommandCloseGame, CommandComment, CommandEquip, CommandEquipArrow, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSetTag, CommandSortKey, CommandSortMaterial, CommandUnequip } from "./Command"; import { Item, ItemStack } from "./Item"; export const parseCommand = (cmdString: string): Command | undefined => { + if(cmdString.startsWith("# ")){ + return new CommandComment(cmdString.substring(2)); + } const tokens = cmdString.split(" ").filter(i=>i); if(tokens.length===0){ return new CommandNothing(); @@ -39,6 +42,9 @@ export const parseCommand = (cmdString: string): Command | undefined => { 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(); + } // break if (tokens.length > 2 && tokens[0] === "Break" && tokens[2]=== "Slots" ){ const slots = parseInt(tokens[1]); @@ -138,6 +144,14 @@ export const parseCommand = (cmdString: string): Command | undefined => { } return undefined; } - + // Multi Save + if (tokens.length === 3 && tokens[0] === "Save" && tokens[1] === "As"){ + const name = tokens[2]; + return new CommandSetTag(name); + } + if (tokens.length === 2 && tokens[0] === "Use"){ + const name = tokens[1]; + return new CommandApplyTag(name); + } return undefined; }; diff --git a/src/core/Slots.ts b/src/core/Slots.ts index b5f52fb..87acff9 100644 --- a/src/core/Slots.ts +++ b/src/core/Slots.ts @@ -55,13 +55,20 @@ export class Slots { } public remove(item: Item, count: number, slot: number) { let s = 0; - for(let i = 0; i 0;i++){ if(this.internalSlots[i].item === item){ if(s = ({command,editCommand,displayIndex, slots, savedSlots, numBroken, overlaySave})=>{ +export const DisplayPane: React.FC = ({command,orbs,editCommand,displayIndex, slots, savedSlots, numBroken, overlaySave})=>{ const [commandString, setCommandString] = useState(""); const [hasError, setHasError] = useState(false); const listProps = stacksToItemListProps(slots, numBroken, false); @@ -78,7 +79,9 @@ export const DisplayPane: React.FC = ({command,editCommand,dis setHasError(true); } }}> - + + Orbs: {orbs} +
  • {overlaySave ?
    = ({command,editCommand,dis (()=>{ const doubleSlots: JSX.Element[] = []; for(let i=0;i=slots.length-numBroken, isSave:false}} />); } if(savedSlots.length>slots.length){ for(let i=slots.length;i); } }else if(slots.length > savedSlots.length){ for(let i=savedSlots.length;i=slots.length-numBroken, isSave:false}} />); } @@ -128,7 +131,7 @@ export const DisplayPane: React.FC = ({command,editCommand,dis height: "calc( ( 99vh - 60px ) / 2)", overflowY: "auto" } }> -
    Inventory of (Hard) Save
    +
    Inventory of Save