more ui updates and core logic
This commit is contained in:
parent
0cf48a91aa
commit
8567bec9f9
20 changed files with 1502 additions and 983 deletions
45
src/App.css
45
src/App.css
|
@ -30,11 +30,30 @@ h1 {
|
|||
}
|
||||
|
||||
.CommandItem:hover {
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.CommandInput {
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-bottom: 2px solid #0074d9;
|
||||
color: #b7f1ff;
|
||||
text-shadow: 0 0 5px #3aa0ff,0 0 5px #3aa0ff,0 0 5px #3aa0ff;
|
||||
}
|
||||
|
||||
.CommandInput:focus-visible {
|
||||
border-color: #b7f1ff;
|
||||
}
|
||||
|
||||
.InputError {
|
||||
background-color: #eeaaaa;
|
||||
color: #ff3333;
|
||||
text-shadow: 0 0 5px #ee7777,0 0 5px #ee7777,0 0 5px #ee7777;
|
||||
border-bottom: 2px solid #dd0000;
|
||||
}
|
||||
|
||||
.InputError:focus-visible {
|
||||
border-bottom: 2px solid #ff3333;
|
||||
}
|
||||
|
||||
.ItemSlot {
|
||||
|
@ -42,7 +61,7 @@ h1 {
|
|||
width: 64px;
|
||||
height: 64px;
|
||||
|
||||
background-color: #333333;
|
||||
background-color: #333333bb;
|
||||
box-sizing: content-box;
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -51,7 +70,7 @@ h1 {
|
|||
background-color: #660000;
|
||||
}
|
||||
|
||||
.ItemSlotSave {
|
||||
.GameDataBackground {
|
||||
background-color: #003300;
|
||||
}
|
||||
|
||||
|
@ -81,3 +100,21 @@ h3.ListHeader {
|
|||
margin: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h4.Reference {
|
||||
margin: 0;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
p.Reference {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
p.Example {
|
||||
color:#eeee00;
|
||||
}
|
||||
|
||||
h3.Reference {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
|
225
src/App.tsx
225
src/App.tsx
|
@ -1,16 +1,19 @@
|
|||
import { Command, CommandBreakSlots, CommandInitialize, CommandNothing, CommandReload, CommandSave, CommandSortKey } from "core/Command";
|
||||
import { Command, CommandNothing } from "core/Command";
|
||||
import { Inventory } from "core/Inventory";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import "./App.css";
|
||||
import { CommandItem } from "./components/CommandItem";
|
||||
|
||||
import { DisplayPane, stacksToItemListProps } from "surfaces/DisplayPane";
|
||||
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");
|
||||
|
@ -19,64 +22,21 @@ const getDefaultCommands = (): Command[]=>{
|
|||
return lines.map(l=>parseCommand(l)).filter(c=>c) as Command[];
|
||||
}
|
||||
return [
|
||||
new CommandInitialize([
|
||||
{
|
||||
item: Item.Diamond,
|
||||
count: 5,
|
||||
equipped:false,
|
||||
},
|
||||
{
|
||||
item: Item.Slate,
|
||||
count: 1,
|
||||
equipped:false,
|
||||
},
|
||||
{
|
||||
item: Item.Glider,
|
||||
count: 1,
|
||||
equipped:false,
|
||||
},
|
||||
{
|
||||
item: Item.SpiritOrb,
|
||||
count: 4,
|
||||
equipped:false,
|
||||
}
|
||||
]),
|
||||
new CommandBreakSlots(4),
|
||||
new CommandReload(),
|
||||
new CommandSortKey(),
|
||||
new CommandSave(),
|
||||
new CommandReload()
|
||||
];
|
||||
parseCommand("Get 5 Diamond 1 Slate 1 Glider 4 SpiritOrb"),
|
||||
parseCommand("Save"),
|
||||
parseCommand("Break 4 Slots"),
|
||||
parseCommand("Reload"),
|
||||
parseCommand("Save"),
|
||||
parseCommand("Reload"),
|
||||
] as Command[];;
|
||||
};
|
||||
|
||||
const tempSaveInventory = new Inventory();
|
||||
tempSaveInventory.rawInit([
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
{item: Item.Acorn, count: 55, equipped: false},
|
||||
])
|
||||
const listProps = stacksToItemListProps(tempSaveInventory.getSlots(), 0, true);
|
||||
|
||||
|
||||
export const App: React.FC = () => {
|
||||
const [page, setPageInState] = useState<string>("#simulation");
|
||||
const [overlaySave, setOverlaySave] = 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);
|
||||
|
@ -86,20 +46,36 @@ export const App: React.FC = () => {
|
|||
const uploadRef = useRef<HTMLInputElement>(null);
|
||||
const contextMenuRef = useRef<HTMLDivElement>(null);
|
||||
// compute props
|
||||
const inventories = useMemo(()=>{
|
||||
const inventories: Inventory[] = [];
|
||||
const inv = new Inventory();
|
||||
const simulationStates = useMemo(()=>{
|
||||
const simulationStates: SimulationState[] = [];
|
||||
const state = createSimulationState();
|
||||
commands.forEach(c=>{
|
||||
c.execute(inv);
|
||||
inventories.push(inv.deepClone());
|
||||
c.execute(state);
|
||||
simulationStates.push(state.deepClone());
|
||||
});
|
||||
return inventories;
|
||||
return simulationStates;
|
||||
}, [commands]);
|
||||
|
||||
const setPage = useCallback((hash: string)=>{
|
||||
window.location.hash = hash;
|
||||
setPageInState(hash);
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
setPage(window.location.hash || "#simulation");
|
||||
}, [window.location.hash]);
|
||||
|
||||
useEffect(()=>{
|
||||
window.onkeydown=(e)=>{
|
||||
if(e.code==="ArrowDown"){
|
||||
if(displayIndex===commands.length-1){
|
||||
const arrCopy = [...commands];
|
||||
arrCopy.push(new CommandNothing());
|
||||
setCommands(arrCopy);
|
||||
setDisplayIndex(arrCopy.length-1);
|
||||
}else{
|
||||
setDisplayIndex(Math.min(commands.length-1, displayIndex+1));
|
||||
}
|
||||
}else if(e.code==="ArrowUp"){
|
||||
setDisplayIndex(Math.max(0, displayIndex-1));
|
||||
}
|
||||
|
@ -143,8 +119,12 @@ export const App: React.FC = () => {
|
|||
<div id="NavBar" style={{
|
||||
height: 40
|
||||
}}>
|
||||
<button>Simulation</button>
|
||||
<button>Reference</button>
|
||||
<button onClick={()=>{
|
||||
setPage("#simulation")
|
||||
}}>Simulation</button>
|
||||
<button onClick={()=>{
|
||||
setPage("#reference")
|
||||
}}>Reference</button>
|
||||
<button>Options</button>
|
||||
</div>
|
||||
|
||||
|
@ -156,56 +136,51 @@ export const App: React.FC = () => {
|
|||
maxHeight: 220,
|
||||
height: "30vh",
|
||||
border: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
overflowY: "hidden"
|
||||
boxSizing: "border-box",
|
||||
overflowY: "hidden",
|
||||
|
||||
}}>
|
||||
<TitledList title="Saves">
|
||||
{
|
||||
displayIndex >=0 && displayIndex < simulationStates.length &&
|
||||
<ol>
|
||||
{
|
||||
!!simulationStates[displayIndex].getManualSave() &&
|
||||
<CommandItem
|
||||
onClick={()=>{}}
|
||||
onClick={()=>{
|
||||
setSelectedSaveName("");
|
||||
}}
|
||||
comment={false}
|
||||
isSelected={selectedSaveName===""}
|
||||
|
||||
>
|
||||
Manual Save
|
||||
</CommandItem>
|
||||
}
|
||||
|
||||
{
|
||||
Object.entries(simulationStates[displayIndex].getNamedSaves()).map(([name, _gamedata])=>(
|
||||
<CommandItem
|
||||
onClick={()=>{}}
|
||||
onClick={()=>{
|
||||
setSelectedSaveName(name);
|
||||
}}
|
||||
comment={false}
|
||||
isSelected={selectedSaveName===name}
|
||||
>
|
||||
Auto Save 1
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onClick={()=>{}}
|
||||
comment={false}
|
||||
>
|
||||
Auto Save 2
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onClick={()=>{}}
|
||||
comment={false}
|
||||
>
|
||||
Auto Save 3
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onClick={()=>{}}
|
||||
comment={false}
|
||||
>
|
||||
Auto Save 4
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onClick={()=>{}}
|
||||
comment={false}
|
||||
>
|
||||
Auto Save 5
|
||||
{name}
|
||||
</CommandItem>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
}
|
||||
|
||||
</TitledList>
|
||||
</div>
|
||||
<div style={{
|
||||
minHeight: "calc( 70vh - 45px )",
|
||||
height: "calc( 100vh - 45px - 220px )",
|
||||
minHeight: "calc( 70vh - 40px )",
|
||||
height: "calc( 100vh - 40px - 220px )",
|
||||
border: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
boxSizing: "border-box",
|
||||
overflowY: "hidden"
|
||||
|
||||
}}>
|
||||
|
@ -215,7 +190,13 @@ export const App: React.FC = () => {
|
|||
{
|
||||
commands.map((c,i)=>
|
||||
<CommandItem
|
||||
onClick={()=>setDisplayIndex(i)}
|
||||
onClick={()=>{
|
||||
setDisplayIndex(i);
|
||||
const inputField = document.getElementById("CommandInputField");
|
||||
if(inputField){
|
||||
inputField.focus();
|
||||
}
|
||||
}}
|
||||
onContextMenu={(x,y)=>{
|
||||
setContextIndex(i);
|
||||
setContextMenuX(x);
|
||||
|
@ -259,39 +240,73 @@ export const App: React.FC = () => {
|
|||
|
||||
</div>
|
||||
<div id="MainPane" style={{
|
||||
width: "calc ( 100% - 300px )"
|
||||
position: "absolute",
|
||||
top: 40,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 300,
|
||||
backgroundColor: "#262626"
|
||||
}}>
|
||||
{
|
||||
page === "#simulation" && <>
|
||||
<div style={{
|
||||
maxHeight: 220,
|
||||
height: "30vh",
|
||||
border: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
overflowY: "hidden"
|
||||
boxSizing: "border-box",
|
||||
overflowY: "hidden",
|
||||
color: "white",
|
||||
backgroundColor: "#262626"
|
||||
} }>
|
||||
{
|
||||
(displayIndex >= 0 && displayIndex < commands.length) ?
|
||||
<TitledList title="Save Data">
|
||||
<ItemList {...listProps}/>
|
||||
{
|
||||
selectedSaveName === "" && !!simulationStates[displayIndex].getManualSave() &&
|
||||
<ItemList slots={(simulationStates[displayIndex].getManualSave() as GameData).getDisplayedSlots()}/>
|
||||
}
|
||||
{
|
||||
selectedSaveName !== "" && !!simulationStates[displayIndex].getNamedSaves()[selectedSaveName] &&
|
||||
<ItemList slots={(simulationStates[displayIndex].getNamedSaves()[selectedSaveName] as GameData).getDisplayedSlots()}/>
|
||||
}
|
||||
</TitledList>
|
||||
:
|
||||
<TitledList title="Select an instruction on the left to view it">
|
||||
|
||||
|
||||
</TitledList>
|
||||
}
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div >
|
||||
<div style={{
|
||||
minHeight: "calc( 70vh - 40px )",
|
||||
height: "calc( 100vh - 40px - 220px )",
|
||||
border: "1px solid black",
|
||||
boxSizing: "border-box",
|
||||
overflowY: "hidden"
|
||||
}}>
|
||||
{displayIndex >= 0 && displayIndex < commands.length &&
|
||||
<DisplayPane
|
||||
overlaySave={overlaySave}
|
||||
displayIndex={displayIndex}
|
||||
command={commands[displayIndex].getDisplayString()}
|
||||
orbs={inventories[displayIndex].getTurnedInOrbs()}
|
||||
slots={inventories[displayIndex].getSlots()}
|
||||
savedSlots={inventories[displayIndex].getSavedSlots()}
|
||||
numBroken={inventories[displayIndex].getNumBroken()}
|
||||
simulationState={simulationStates[displayIndex]}
|
||||
editCommand={(c)=>{
|
||||
const arrCopy = [...commands];
|
||||
arrCopy[displayIndex] = c;
|
||||
setCommands(arrCopy);
|
||||
}}
|
||||
/>
|
||||
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{
|
||||
page === "#reference" && <ReferencePage />
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
|
|
BIN
src/assets/Background.png
Normal file
BIN
src/assets/Background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
src/assets/Crash.png
Normal file
BIN
src/assets/Crash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
BIN
src/assets/InGame.png
Normal file
BIN
src/assets/InGame.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 MiB |
|
@ -1,23 +1,15 @@
|
|||
import { DisplayableSlot } from "core/DisplayableInventory";
|
||||
import { ItemSlot } from "./ItemSlot";
|
||||
|
||||
export type ItemListItemProps = {
|
||||
image: string,
|
||||
count: number,
|
||||
isEquipped: boolean,
|
||||
}
|
||||
|
||||
export type ItemListProps = {
|
||||
isSave: boolean,
|
||||
items: ItemListItemProps[],
|
||||
numBroken: number
|
||||
slots: DisplayableSlot[]
|
||||
}
|
||||
|
||||
export const ItemList: React.FC<ItemListProps> = ({items, numBroken, isSave}) => {
|
||||
export const ItemList: React.FC<ItemListProps> = ({slots}) => {
|
||||
return <>
|
||||
{
|
||||
items.map(({image, count, isEquipped}, i)=>{
|
||||
const broken = i+numBroken >= items.length;
|
||||
return <ItemSlot key={i} image={image} count={count} isBroken={broken} isSave={isSave} isEquipped={isEquipped}/>;
|
||||
slots.map((slot, i)=>{
|
||||
return <ItemSlot key={i} slot={slot}/>;
|
||||
})
|
||||
}
|
||||
</>;
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import clsx from "clsx";
|
||||
import { DisplayableSlot } from "core/DisplayableInventory";
|
||||
import Background from "assets/Background.png";
|
||||
|
||||
|
||||
type ItemSlotProps = {
|
||||
image: string,
|
||||
count: number,
|
||||
isBroken: boolean,
|
||||
isSave: boolean,
|
||||
isEquipped: boolean,
|
||||
slot: DisplayableSlot
|
||||
};
|
||||
|
||||
export const ItemSlot: React.FC<ItemSlotProps> = ({image, count, isBroken, isSave, isEquipped})=>{
|
||||
export const ItemSlot: React.FC<ItemSlotProps> = ({slot: {image, count, isBrokenSlot, isEquipped, displayCount}})=>{
|
||||
return (
|
||||
<span className={clsx("ItemSlot", isBroken && "ItemSlotBroken", isSave && "ItemSlotSave", isEquipped && "ItemSlotEquipped")}>
|
||||
<img className={clsx("ItemImage", isSave && "ItemImageSave")}src={image} />
|
||||
<span className={clsx("ItemSlot", isBrokenSlot && "ItemSlotBroken", isEquipped && "ItemSlotEquipped")}>
|
||||
<img className={clsx("ItemImage")} src={image} />
|
||||
{
|
||||
count > 0 && <span className={"ItemCount"}>
|
||||
displayCount && <span className={"ItemCount"}>
|
||||
x{count}
|
||||
</span>
|
||||
}
|
||||
|
@ -23,8 +23,12 @@ export const ItemSlot: React.FC<ItemSlotProps> = ({image, count, isBroken, isSav
|
|||
export const DoubleItemSlot: React.FC<{first?: ItemSlotProps, second?: ItemSlotProps}> = ({first, second})=>{
|
||||
return (
|
||||
<span style={{display: "inline-block", width: 72, height: 144, verticalAlign:"top"}}>
|
||||
{first ? <ItemSlot {...first}/> : <div style={{height: 72}}/>}
|
||||
{second ? <ItemSlot {...second}/> : <div style={{height: 72}}/> }
|
||||
<div style={{height: 72, background: `url(${Background})`}} >
|
||||
{first && <ItemSlot {...first}/>}
|
||||
</div>
|
||||
<div style={{height: 72}}>
|
||||
{second && <ItemSlot {...second}/>}
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
};
|
|
@ -8,9 +8,9 @@ export const TitledList: React.FC<TitledListProps> = ({title, children}) => {
|
|||
return (
|
||||
<>
|
||||
<h3 className="ListHeader" style={{
|
||||
height: 20,
|
||||
border: "1px solid red",
|
||||
boxSizing: "content-box"
|
||||
height: 40,
|
||||
borderBottom: "2px solid",
|
||||
boxSizing: "border-box",
|
||||
}}>
|
||||
{title}
|
||||
</h3>
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import { Inventory } from "./Inventory";
|
||||
import { Item, ItemStack, itemToArrowType } from "./Item";
|
||||
import { SimulationState } from "./SimulationState";
|
||||
|
||||
export interface Command {
|
||||
execute(inv: Inventory): void,
|
||||
execute(state: SimulationState): void,
|
||||
getDisplayString(): string,
|
||||
// fromBuffer(buf: Buffer): number,
|
||||
// toBuffer(): Buffer,
|
||||
}
|
||||
|
||||
export class CommandNothing implements Command {
|
||||
static Op = 0x0;
|
||||
|
||||
execute(_inv: Inventory): void {
|
||||
execute(_state: Inventory): void {
|
||||
// nothing
|
||||
}
|
||||
getDisplayString(): string {
|
||||
|
@ -21,44 +19,14 @@ export class CommandNothing implements Command {
|
|||
}
|
||||
|
||||
export class CommandInitialize implements Command {
|
||||
static Op = 0x1;
|
||||
|
||||
private stacks: ItemStack[];
|
||||
constructor(stacks: ItemStack[]){
|
||||
this.stacks = stacks;
|
||||
}
|
||||
// public fromBuffer(buf: Buffer): number {
|
||||
// let read = 0;
|
||||
// const size = buf.readUInt16LE();
|
||||
// read+=2;
|
||||
// const stacks: ItemStack[] = [];
|
||||
// for(let i=0;i<size;i++){
|
||||
// const count = buf.readInt16LE(read);
|
||||
// read+=2;
|
||||
// const id = buf.readInt8(read);
|
||||
// read++;
|
||||
// stacks.push({item: idToItemData(id).item, count, equipped: false});
|
||||
// }
|
||||
// this.stacks = stacks;
|
||||
// return read;
|
||||
// }
|
||||
// public toBuffer(): Buffer {
|
||||
// const buf: Buffer = Buffer.alloc(3*this.stacks.length+3);
|
||||
// let write = 0;
|
||||
// buf.writeInt8(CommandInitialize.Op);
|
||||
// write++;
|
||||
// buf.writeInt16LE(this.stacks.length, write);
|
||||
// write+=2;
|
||||
// this.stacks.forEach(({item,count})=>{
|
||||
// buf.writeInt16LE(count&0xffff, write);
|
||||
// write+=2;
|
||||
// buf.writeInt8(itemToItemData(item).id, write);
|
||||
// write++;
|
||||
// });
|
||||
// return buf;
|
||||
// }
|
||||
|
||||
public execute(inv: Inventory): void {
|
||||
inv.init(this.stacks);
|
||||
public execute(state: SimulationState): void {
|
||||
state.initialize(this.stacks);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
const parts = ["Initialize"];
|
||||
|
@ -71,299 +39,281 @@ export class CommandInitialize implements Command {
|
|||
|
||||
}
|
||||
|
||||
export class CommandBreakSlots implements Command {
|
||||
static Op = 0x2;
|
||||
private numToBreak: number;
|
||||
constructor(numToBreak: number){
|
||||
this.numToBreak = numToBreak;
|
||||
}
|
||||
// public fromBuffer(buf: Buffer): number {
|
||||
// this.numToBreak = buf.readInt16LE();
|
||||
// return 2;
|
||||
// }
|
||||
// public toBuffer(): Buffer {
|
||||
// const buf: Buffer = Buffer.alloc(3);
|
||||
// buf.writeUInt8(CommandBreakSlots.Op);
|
||||
// buf.writeInt16LE(this.numToBreak, 1);
|
||||
// return buf;
|
||||
// }
|
||||
public execute(inv: Inventory): void {
|
||||
inv.addBrokenSlots(this.numToBreak);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return `Break ${this.numToBreak} Slots`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandSave implements Command {
|
||||
static Op = 0x3;
|
||||
// public fromBuffer(_buf: Buffer): number {
|
||||
// return 0;
|
||||
// }
|
||||
// public toBuffer(): Buffer {
|
||||
// const buf: Buffer = Buffer.alloc(1);
|
||||
// buf.writeInt8(CommandSave.Op);
|
||||
// return buf;
|
||||
// }
|
||||
public execute(inv: Inventory): void {
|
||||
inv.save();
|
||||
|
||||
public execute(state: SimulationState): void {
|
||||
state.save();
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return "Save";
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandReload implements Command {
|
||||
static Op = 0x4;
|
||||
// public fromBuffer(_buf: Buffer): number {
|
||||
// return 0;
|
||||
// }
|
||||
// public toBuffer(): Buffer {
|
||||
// const buf: Buffer = Buffer.alloc(1);
|
||||
// buf.writeInt8(CommandReload.Op);
|
||||
// return buf;
|
||||
// }
|
||||
public execute(inv: Inventory): void {
|
||||
inv.reload();
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return "Reload";
|
||||
}
|
||||
}
|
||||
|
||||
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 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";
|
||||
}
|
||||
}
|
||||
|
||||
const Verbs = ["?", "Remove", "Drop", "Sell", "Eat", "Cook", "Get", "Add", "Pickup"];
|
||||
const VerbToId = {
|
||||
"Remove" : 1,
|
||||
"Drop": 2,
|
||||
"Sell": 3,
|
||||
"Eat": 4,
|
||||
"Cook": 5,
|
||||
"Get": 6,
|
||||
"Add": 7,
|
||||
"Pickup": 8
|
||||
};
|
||||
|
||||
export class CommandRemoveMaterial implements Command {
|
||||
static Op = 0x7;
|
||||
private verb: number;
|
||||
private count: number;
|
||||
private item: Item;
|
||||
private slot: number;
|
||||
private noSlot: boolean;
|
||||
constructor(verb: string, count: number, item: Item, slot: number, noSlot: boolean){
|
||||
this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;
|
||||
this.count = count;
|
||||
this.item = item;
|
||||
this.slot = slot;
|
||||
this.noSlot = noSlot;
|
||||
}
|
||||
public execute(inv: Inventory): void {
|
||||
inv.remove(this.item, this.count, this.slot);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
const slotString = this.noSlot ? "" : ` From Slot ${this.slot+1}`;
|
||||
return `${Verbs[this.verb]} ${this.count} ${this.item}${slotString}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandRemoveUnstackableMaterial implements Command {
|
||||
static Op = 0x8;
|
||||
private verb: number;
|
||||
private item: Item;
|
||||
private slot: number;
|
||||
private noSlot: boolean;
|
||||
constructor(verb: string,item: Item, slot: number, noSlot: boolean){
|
||||
this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;
|
||||
this.item = item;
|
||||
this.slot = slot;
|
||||
this.noSlot = noSlot;
|
||||
}
|
||||
public execute(inv: Inventory): void {
|
||||
inv.remove(this.item, 1, this.slot);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
const slotString = this.noSlot ? "" : ` From Slot ${this.slot+1}`;
|
||||
return `${Verbs[this.verb]} ${this.item}${slotString}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandAddMaterial implements Command {
|
||||
static Op = 0x9;
|
||||
private verb: number;
|
||||
private count: number;
|
||||
private item: Item;
|
||||
constructor(verb: string, count: number, item: Item){
|
||||
this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;
|
||||
this.count = count;
|
||||
this.item = item;
|
||||
}
|
||||
// public fromBuffer(buf: Buffer): number {
|
||||
// let read = 0;
|
||||
// const id = buf.readInt8(read);
|
||||
// read+=1;
|
||||
// this.item = idToItemData(id).item;
|
||||
|
||||
// this.count = buf.readInt16LE(read);
|
||||
// read+=2;
|
||||
// this.verb = buf.readInt8(read);
|
||||
// read++;
|
||||
// return read;
|
||||
// }
|
||||
// public toBuffer(): Buffer {
|
||||
// const buf: Buffer = Buffer.alloc(1+1+2+1);
|
||||
// let write = 0;
|
||||
// buf.writeInt8(CommandAddMaterial.Op);
|
||||
// write++;
|
||||
// buf.writeInt8(itemToItemData(this.item).id, write);
|
||||
// write++;
|
||||
// buf.writeInt16LE(this.count, write);
|
||||
// write+=2;
|
||||
// buf.writeInt8(this.verb, write);
|
||||
// return buf;
|
||||
// }
|
||||
public execute(inv: Inventory): void {
|
||||
inv.add(this.item, this.count);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return `${Verbs[this.verb]} ${this.count} ${this.item}`;
|
||||
}
|
||||
}
|
||||
|
||||
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 CommandEquip 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 ${this.item}${slotString}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandUnequip 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.unequipEquipment(this.item, this.slot);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
const slotString = this.noSlot ? "" : ` In Slot ${this.slot+1}`;
|
||||
return `Unequip ${this.item}${slotString}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandSetTag implements Command {
|
||||
export class CommandSaveAs implements Command {
|
||||
private name: string;
|
||||
constructor(name: string){
|
||||
this.name = name;
|
||||
}
|
||||
public execute(inv: Inventory): void {
|
||||
inv.save();
|
||||
inv.setTag(this.name);
|
||||
public execute(state: SimulationState): void {
|
||||
state.save(this.name);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return `Save As ${this.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandApplyTag implements Command {
|
||||
export class CommandReload implements Command {
|
||||
private name?: string;
|
||||
constructor(name?: string){
|
||||
this.name = name;
|
||||
}
|
||||
public execute(state: SimulationState): void {
|
||||
state.reload(this.name);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return `Reload${this.name?` ${this.name}`:""}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class CommandUse implements Command {
|
||||
private name: string;
|
||||
constructor(name: string){
|
||||
this.name = name;
|
||||
}
|
||||
public execute(inv: Inventory): void {
|
||||
inv.applyTag(this.name);
|
||||
public execute(state: SimulationState): void {
|
||||
state.useSaveForNextReload(this.name);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return `Use ${this.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandCloseGame implements Command {
|
||||
public execute(inv: Inventory): void {
|
||||
inv.closeGame();
|
||||
export class CommandBreakSlots implements Command {
|
||||
|
||||
private numToBreak: number;
|
||||
constructor(numToBreak: number){
|
||||
this.numToBreak = numToBreak;
|
||||
}
|
||||
|
||||
public execute(state: SimulationState): void {
|
||||
state.breakSlots(this.numToBreak);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return "Close Game";
|
||||
return `Break ${this.numToBreak} Slots`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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 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";
|
||||
// }
|
||||
// }
|
||||
|
||||
// const Verbs = ["?", "Remove", "Drop", "Sell", "Eat", "Cook", "Get", "Add", "Pickup"];
|
||||
// const VerbToId = {
|
||||
// "Remove" : 1,
|
||||
// "Drop": 2,
|
||||
// "Sell": 3,
|
||||
// "Eat": 4,
|
||||
// "Cook": 5,
|
||||
// "Get": 6,
|
||||
// "Add": 7,
|
||||
// "Pickup": 8
|
||||
// };
|
||||
|
||||
// export class CommandRemoveMaterial implements Command {
|
||||
// static Op = 0x7;
|
||||
// private verb: number;
|
||||
// private count: number;
|
||||
// private item: Item;
|
||||
// private slot: number;
|
||||
// private noSlot: boolean;
|
||||
// constructor(verb: string, count: number, item: Item, slot: number, noSlot: boolean){
|
||||
// this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;
|
||||
// this.count = count;
|
||||
// this.item = item;
|
||||
// this.slot = slot;
|
||||
// this.noSlot = noSlot;
|
||||
// }
|
||||
// public execute(inv: Inventory): void {
|
||||
// inv.remove(this.item, this.count, this.slot);
|
||||
// }
|
||||
// public getDisplayString(): string {
|
||||
// const slotString = this.noSlot ? "" : ` From Slot ${this.slot+1}`;
|
||||
// return `${Verbs[this.verb]} ${this.count} ${this.item}${slotString}`;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export class CommandRemoveUnstackableMaterial implements Command {
|
||||
// static Op = 0x8;
|
||||
// private verb: number;
|
||||
// private item: Item;
|
||||
// private slot: number;
|
||||
// private noSlot: boolean;
|
||||
// constructor(verb: string,item: Item, slot: number, noSlot: boolean){
|
||||
// this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;
|
||||
// this.item = item;
|
||||
// this.slot = slot;
|
||||
// this.noSlot = noSlot;
|
||||
// }
|
||||
// public execute(inv: Inventory): void {
|
||||
// inv.remove(this.item, 1, this.slot);
|
||||
// }
|
||||
// public getDisplayString(): string {
|
||||
// const slotString = this.noSlot ? "" : ` From Slot ${this.slot+1}`;
|
||||
// return `${Verbs[this.verb]} ${this.item}${slotString}`;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export class CommandAddMaterial implements Command {
|
||||
// static Op = 0x9;
|
||||
// private verb: number;
|
||||
// private count: number;
|
||||
// private item: Item;
|
||||
// constructor(verb: string, count: number, item: Item){
|
||||
// this.verb = VerbToId[verb as keyof typeof VerbToId] || 0;
|
||||
// this.count = count;
|
||||
// this.item = item;
|
||||
// }
|
||||
// // public fromBuffer(buf: Buffer): number {
|
||||
// // let read = 0;
|
||||
// // const id = buf.readInt8(read);
|
||||
// // read+=1;
|
||||
// // this.item = idToItemData(id).item;
|
||||
|
||||
// // this.count = buf.readInt16LE(read);
|
||||
// // read+=2;
|
||||
// // this.verb = buf.readInt8(read);
|
||||
// // read++;
|
||||
// // return read;
|
||||
// // }
|
||||
// // public toBuffer(): Buffer {
|
||||
// // const buf: Buffer = Buffer.alloc(1+1+2+1);
|
||||
// // let write = 0;
|
||||
// // buf.writeInt8(CommandAddMaterial.Op);
|
||||
// // write++;
|
||||
// // buf.writeInt8(itemToItemData(this.item).id, write);
|
||||
// // write++;
|
||||
// // buf.writeInt16LE(this.count, write);
|
||||
// // write+=2;
|
||||
// // buf.writeInt8(this.verb, write);
|
||||
// // return buf;
|
||||
// // }
|
||||
// public execute(inv: Inventory): void {
|
||||
// inv.add(this.item, this.count);
|
||||
// }
|
||||
// public getDisplayString(): string {
|
||||
// return `${Verbs[this.verb]} ${this.count} ${this.item}`;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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 CommandEquip 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 ${this.item}${slotString}`;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export class CommandUnequip 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.unequipEquipment(this.item, this.slot);
|
||||
// }
|
||||
// public getDisplayString(): string {
|
||||
// const slotString = this.noSlot ? "" : ` In Slot ${this.slot+1}`;
|
||||
// return `Unequip ${this.item}${slotString}`;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// 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 execute(_state: SimulationState): void {
|
||||
// nothing
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
|
|
24
src/core/DisplayableInventory.ts
Normal file
24
src/core/DisplayableInventory.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Item, ItemStack, itemToItemData, ItemType } from "./Item"
|
||||
|
||||
export type DisplayableSlot = {
|
||||
image: string,
|
||||
count: number,
|
||||
displayCount: boolean,
|
||||
isEquipped: boolean,
|
||||
isBrokenSlot: boolean,
|
||||
}
|
||||
|
||||
export interface DisplayableInventory {
|
||||
getDisplayedSlots: ()=>DisplayableSlot[]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
30
src/core/GameData.ts
Normal file
30
src/core/GameData.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { DisplayableInventory, DisplayableSlot, itemStackToDisplayableSlot } from "./DisplayableInventory";
|
||||
import { Slots } from "./Slots";
|
||||
import { VisibleInventory } from "./VisibleInventory";
|
||||
|
||||
/*
|
||||
* Implementation of GameData in botw
|
||||
*/
|
||||
export class GameData implements DisplayableInventory {
|
||||
|
||||
private slots: Slots = new Slots([]);
|
||||
constructor(slots: Slots){
|
||||
this.slots = slots;
|
||||
}
|
||||
|
||||
public deepClone(): GameData {
|
||||
return new GameData(this.slots.deepClone());
|
||||
}
|
||||
|
||||
public syncWith(pouch: VisibleInventory) {
|
||||
this.slots = pouch.getSlots().deepClone();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -2,214 +2,214 @@ 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) => {
|
||||
|
||||
// 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;
|
||||
// }
|
||||
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 getSlots(): Slots {
|
||||
// return this.slots;
|
||||
// }
|
||||
|
||||
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 getSavedSlots(): Slots {
|
||||
// return this.savedSlots;
|
||||
// }
|
||||
|
||||
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);
|
||||
}
|
||||
// public getNumBroken(): number {
|
||||
// return this.numBroken;
|
||||
// }
|
||||
|
||||
this.slots = beforeMaterial;
|
||||
this.isInitialSort = false;
|
||||
this.isAltered=true;
|
||||
}
|
||||
// public isInaccurate(): boolean {
|
||||
// return this.inaccurate;
|
||||
// }
|
||||
|
||||
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 getTurnedInOrbs(): number {
|
||||
// return this.turnedInOrbs;
|
||||
// }
|
||||
|
||||
public add(item: Item, count: number) {
|
||||
this.slots.add(item, count);
|
||||
if(itemToItemData(item).type===ItemType.Arrow){
|
||||
this.slots.sortArrows();
|
||||
}
|
||||
this.isAltered=true;
|
||||
}
|
||||
// public rawInit(stacks: ItemStack[]) {
|
||||
// this.slots = new Slots(stacks);
|
||||
// }
|
||||
|
||||
public equipEquipmentOrArrow(item: Item, slot: number) {
|
||||
this.slots.equip(item, slot);
|
||||
this.isAltered=true;
|
||||
}
|
||||
// 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 unequipEquipment(item: Item, slot: number){
|
||||
this.slots.unequip(item, slot);
|
||||
this.isAltered=true;
|
||||
}
|
||||
// public closeGame() {
|
||||
// this.numBroken = 0;
|
||||
// this.isInitialSort = false;
|
||||
// this.isAltered = true;
|
||||
// this.inaccurate = false;
|
||||
// this.slots = new Slots([]);
|
||||
// }
|
||||
|
||||
public shootArrow(item: Item, count: number){
|
||||
this.slots.shoot(item, count);
|
||||
this.isAltered=true;
|
||||
}
|
||||
// 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;
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@ export enum ItemType {
|
|||
Bow = 1,
|
||||
Arrow = 2,
|
||||
Shield = 3,
|
||||
Material = 4,
|
||||
Meal = 5,
|
||||
Key = 6
|
||||
Armor = 4,
|
||||
Material = 5,
|
||||
Meal = 6,
|
||||
Key = 7
|
||||
}
|
||||
|
||||
export const ItemTypes = [
|
||||
|
@ -73,6 +74,8 @@ export enum Item {
|
|||
HeartyRadish = "HeartyRadish",
|
||||
BigHeartyRadish = "BigHeartyRadish",
|
||||
Fairy = "Fairy",
|
||||
|
||||
MasterSword = "MasterSword",
|
||||
}
|
||||
|
||||
type ItemData = {
|
||||
|
@ -90,6 +93,7 @@ const TypeToCount = {
|
|||
[ItemType.Bow]: 0,
|
||||
[ItemType.Arrow]: 0,
|
||||
[ItemType.Shield]: 0,
|
||||
[ItemType.Armor]: 0,
|
||||
[ItemType.Material]: 0,
|
||||
[ItemType.Key]: 0,
|
||||
[ItemType.Meal]: 0,
|
||||
|
@ -161,6 +165,9 @@ register(0x50, Item.Weapon, ItemType.Weapon, {
|
|||
image: Images.Axe,
|
||||
stackable: false
|
||||
});
|
||||
register(0, Item.MasterSword, ItemType.Weapon, {
|
||||
stackable: false,
|
||||
})
|
||||
|
||||
register(0x60, Item.Bow, ItemType.Bow, {
|
||||
image: Images.ForestDwellerBow,
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
import { Command, CommandAddMaterial, CommandApplyTag, CommandBreakSlots, CommandCloseGame, CommandComment, CommandEquip, CommandEquipArrow, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSetTag, CommandSortKey, CommandSortMaterial, CommandUnequip } from "./Command";
|
||||
import {
|
||||
Command,
|
||||
CommandBreakSlots,
|
||||
CommandComment,
|
||||
CommandInitialize,
|
||||
CommandNothing,
|
||||
CommandReload,
|
||||
CommandSave,
|
||||
CommandSaveAs,
|
||||
CommandUse
|
||||
} from "./Command";
|
||||
import { Item, ItemStack } from "./Item";
|
||||
|
||||
export const parseCommand = (cmdString: string): Command | undefined => {
|
||||
|
||||
if(cmdString.startsWith("# ")){
|
||||
return new CommandComment(cmdString.substring(2));
|
||||
}
|
||||
|
@ -28,22 +39,24 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
|||
}
|
||||
return new CommandInitialize(stacks);
|
||||
}
|
||||
// no var
|
||||
// Save/Reload
|
||||
if(tokens.length===1 && tokens[0] === "Save"){
|
||||
return new CommandSave();
|
||||
}
|
||||
// // Multi Save
|
||||
if (tokens.length === 3 && tokens[0] === "Save" && tokens[1] === "As"){
|
||||
const name = tokens[2];
|
||||
return new CommandSaveAs(name);
|
||||
}
|
||||
if (tokens.length === 2 && tokens[0] === "Use"){
|
||||
const name = tokens[1];
|
||||
return new CommandUse(name);
|
||||
}
|
||||
if(tokens.length===1 && tokens[0] === "Reload"){
|
||||
return new CommandReload();
|
||||
}
|
||||
|
||||
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] === "Reload"){
|
||||
return new CommandReload(tokens[1]);
|
||||
}
|
||||
// break
|
||||
if (tokens.length > 2 && tokens[0] === "Break" && tokens[2]=== "Slots" ){
|
||||
|
@ -51,107 +64,110 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
|||
if(Number.isInteger(slots)){
|
||||
return new CommandBreakSlots(slots);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// remove material
|
||||
if (tokens.length === 6 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Drop"|| tokens[0] === "Eat") && tokens[3] === "From" && tokens[4] ==="Slot" ){
|
||||
const count = parseInt(tokens[1]);
|
||||
const item = tokens[2];
|
||||
const slot = parseInt(tokens[5]);
|
||||
if(Number.isInteger(count) && Number.isInteger(slot) && item in Item){
|
||||
return new CommandRemoveMaterial(tokens[0], count, Item[item as keyof typeof Item], slot-1, false);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (tokens.length === 3 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Drop"|| tokens[0] === "Eat")){
|
||||
const count = parseInt(tokens[1]);
|
||||
const item = tokens[2];
|
||||
if(Number.isInteger(count) && item in Item){
|
||||
return new CommandRemoveMaterial(tokens[0], count, Item[item as keyof typeof Item], 0, true);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// remove 1 material
|
||||
if (tokens.length === 5 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Eat") && tokens[2] === "From" && tokens[3] ==="Slot" ){
|
||||
const item = tokens[1];
|
||||
const slot = parseInt(tokens[4]);
|
||||
if(Number.isInteger(slot) && item in Item){
|
||||
return new CommandRemoveUnstackableMaterial(tokens[0], Item[item as keyof typeof Item], slot-1, false);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (tokens.length === 2 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Eat")){
|
||||
const item = tokens[1];
|
||||
if(item in Item){
|
||||
return new CommandRemoveUnstackableMaterial(tokens[0], Item[item as keyof typeof Item], 0, true);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// add material
|
||||
if (tokens.length === 3 && (tokens[0] === "Get" || tokens[0] === "Cook" || tokens[0] === "Add" || tokens[0] === "Pickup")){
|
||||
const count = parseInt(tokens[1]);
|
||||
const item = tokens[2];
|
||||
if(Number.isInteger(count) && item in Item){
|
||||
return new CommandAddMaterial(tokens[0], count, Item[item as keyof typeof Item]);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// Equip Equipment
|
||||
if (tokens.length === 5 && tokens[0] === "Equip" && tokens[2] === "In" && tokens[3] ==="Slot" ){
|
||||
const item = tokens[1];
|
||||
const slot = parseInt(tokens[4]);
|
||||
if( Number.isInteger(slot) && item in Item){
|
||||
return new CommandEquip(Item[item as keyof typeof Item], slot-1, false);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (tokens.length === 2 && tokens[0] === "Equip"){
|
||||
const item = tokens[1];
|
||||
if( item in Item){
|
||||
return new CommandEquip(Item[item as keyof typeof Item], 0, true);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// Unequip Equipment
|
||||
if (tokens.length === 5 && tokens[0] === "Unequip" && tokens[2] === "In" && tokens[3] ==="Slot" ){
|
||||
const item = tokens[1];
|
||||
const slot = parseInt(tokens[4]);
|
||||
if( Number.isInteger(slot) && item in Item){
|
||||
return new CommandUnequip(Item[item as keyof typeof Item], slot-1, false);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (tokens.length === 2 && tokens[0] === "Unequip"){
|
||||
const item = tokens[1];
|
||||
if( item in Item){
|
||||
return new CommandUnequip(Item[item as keyof typeof Item], -1, true);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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
|
||||
// if (tokens.length === 6 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Drop"|| tokens[0] === "Eat") && tokens[3] === "From" && tokens[4] ==="Slot" ){
|
||||
// const count = parseInt(tokens[1]);
|
||||
// const item = tokens[2];
|
||||
// const slot = parseInt(tokens[5]);
|
||||
// if(Number.isInteger(count) && Number.isInteger(slot) && item in Item){
|
||||
// return new CommandRemoveMaterial(tokens[0], count, Item[item as keyof typeof Item], slot-1, false);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// if (tokens.length === 3 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Drop"|| tokens[0] === "Eat")){
|
||||
// const count = parseInt(tokens[1]);
|
||||
// const item = tokens[2];
|
||||
// if(Number.isInteger(count) && item in Item){
|
||||
// return new CommandRemoveMaterial(tokens[0], count, Item[item as keyof typeof Item], 0, true);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// // remove 1 material
|
||||
// if (tokens.length === 5 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Eat") && tokens[2] === "From" && tokens[3] ==="Slot" ){
|
||||
// const item = tokens[1];
|
||||
// const slot = parseInt(tokens[4]);
|
||||
// if(Number.isInteger(slot) && item in Item){
|
||||
// return new CommandRemoveUnstackableMaterial(tokens[0], Item[item as keyof typeof Item], slot-1, false);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// if (tokens.length === 2 && (tokens[0] === "Remove" || tokens[0] === "Sell" || tokens[0] === "Eat")){
|
||||
// const item = tokens[1];
|
||||
// if(item in Item){
|
||||
// return new CommandRemoveUnstackableMaterial(tokens[0], Item[item as keyof typeof Item], 0, true);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// // add material
|
||||
// if (tokens.length === 3 && (tokens[0] === "Get" || tokens[0] === "Cook" || tokens[0] === "Add" || tokens[0] === "Pickup")){
|
||||
// const count = parseInt(tokens[1]);
|
||||
// const item = tokens[2];
|
||||
// if(Number.isInteger(count) && item in Item){
|
||||
// return new CommandAddMaterial(tokens[0], count, Item[item as keyof typeof Item]);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// // Equip Equipment
|
||||
// if (tokens.length === 5 && tokens[0] === "Equip" && tokens[2] === "In" && tokens[3] ==="Slot" ){
|
||||
// const item = tokens[1];
|
||||
// const slot = parseInt(tokens[4]);
|
||||
// if( Number.isInteger(slot) && item in Item){
|
||||
// return new CommandEquip(Item[item as keyof typeof Item], slot-1, false);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// if (tokens.length === 2 && tokens[0] === "Equip"){
|
||||
// const item = tokens[1];
|
||||
// if( item in Item){
|
||||
// return new CommandEquip(Item[item as keyof typeof Item], 0, true);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// // Unequip Equipment
|
||||
// if (tokens.length === 5 && tokens[0] === "Unequip" && tokens[2] === "In" && tokens[3] ==="Slot" ){
|
||||
// const item = tokens[1];
|
||||
// const slot = parseInt(tokens[4]);
|
||||
// if( Number.isInteger(slot) && item in Item){
|
||||
// return new CommandUnequip(Item[item as keyof typeof Item], slot-1, false);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// if (tokens.length === 2 && tokens[0] === "Unequip"){
|
||||
// const item = tokens[1];
|
||||
// if( item in Item){
|
||||
// return new CommandUnequip(Item[item as keyof typeof Item], -1, true);
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
// // 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;
|
||||
// }
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
|
136
src/core/SimulationState.ts
Normal file
136
src/core/SimulationState.ts
Normal file
|
@ -0,0 +1,136 @@
|
|||
import { DisplayableInventory } from "./DisplayableInventory";
|
||||
import { GameData } from "./GameData";
|
||||
import { ItemStack } from "./Item";
|
||||
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)
|
||||
);
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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();
|
||||
}
|
||||
return new SimulationState(
|
||||
this.gameData.deepClone(),
|
||||
this.manualSave ? this.manualSave.deepClone() : null,
|
||||
copyNamedSaves,
|
||||
this.pouch.deepClone()
|
||||
);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
public useSaveForNextReload(name: string){
|
||||
this.nextReloadName = name;
|
||||
}
|
||||
|
||||
public breakSlots(n: number) {
|
||||
this.pouch.modifyCount(-n);
|
||||
}
|
||||
|
||||
public get displayableGameData(): DisplayableInventory {
|
||||
return this.gameData;
|
||||
}
|
||||
|
||||
public get displayablePouch(): DisplayableInventory {
|
||||
return this.pouch;
|
||||
}
|
||||
|
||||
public get inventoryMCount(): number {
|
||||
return this.pouch.getCount();
|
||||
}
|
||||
|
||||
public getManualSave(): GameData | null {
|
||||
return this.manualSave;
|
||||
}
|
||||
|
||||
public getNamedSaves(): {[name: string]: GameData} {
|
||||
return this.namedSaves;
|
||||
}
|
||||
|
||||
// public get displayableGameData(): DisplayableInventory {
|
||||
// return this.gameData;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Save - save to hard save slot
|
||||
// Save As <name> - save to a auto save slot
|
||||
// Reload - reload hard save
|
||||
// Reload <name> - reload a named auto save
|
||||
// Use <name> - no effect, but next Reload reloads the named auto save
|
||||
// Break X Slots
|
||||
// Sort Key (In Tab X)
|
||||
// Sort Material (In Tab X)
|
||||
// Get/Add/Cook/Pickup X <item>, X can be omitted and default to 1
|
||||
// Get/Add/Cook/Pickup X <item> Y <item2> ...
|
||||
// Remove/Drop/Sell/Eat X <item> From Slot Y, X can be omitted and default to 1
|
||||
// Remove/Drop/Sell/Eat X <item1> Y <item2> ...
|
||||
// D&P X <item>, drop and pick up (to sort)
|
||||
// Equip <item> (In Slot X)
|
||||
// Unequip <item> (In Slot X), without slot, it unequipps the first equipped
|
||||
// Shoot X Arrow, x can be ommited and default to 1
|
||||
// Close Game
|
||||
// Close Inventory, same as Resync GameData
|
|
@ -1,5 +1,11 @@
|
|||
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
|
||||
*/
|
||||
export class Slots {
|
||||
private internalSlots: ItemStack[] = [];
|
||||
constructor(slots: ItemStack[]) {
|
||||
|
@ -14,184 +20,301 @@ export class Slots {
|
|||
public get length(): number {
|
||||
return this.internalSlots.length;
|
||||
}
|
||||
public get(i: number): ItemStack{
|
||||
return this.internalSlots[i];
|
||||
|
||||
// Sort the item types as they appear in game. Arrows are also sorted amongst each other
|
||||
// input mCount = null will skip the optimization. Otherwise if mCount <= 1, do nothing
|
||||
public sortItemType(mCount: number | null) {
|
||||
if(mCount === null){
|
||||
mCount = this.internalSlots.length;
|
||||
}
|
||||
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).type<type));
|
||||
}
|
||||
public getAfterType(type: ItemType): Slots {
|
||||
return new Slots(this.internalSlots.filter(s=>itemToItemData(s.item).type>type));
|
||||
}
|
||||
public addSlotsToEnd(slots: Slots) {
|
||||
slots.internalSlots.forEach(s=>this.addStack(s));
|
||||
}
|
||||
public addStack(stack: ItemStack) {
|
||||
// Scan non-repeatables
|
||||
const data = itemToItemData(stack.item);
|
||||
if(!data.repeatable){
|
||||
for(let i=0;i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].item===stack.item){
|
||||
if(mCount <= 1){
|
||||
return;
|
||||
}
|
||||
stableSort(this.internalSlots, (a,b)=>{
|
||||
const aData = itemToItemData(a.item);
|
||||
const bData = itemToItemData(b.item);
|
||||
if(aData.type === ItemType.Arrow && bData.type === ItemType.Arrow){
|
||||
return aData.sortOrder - bData.sortOrder;
|
||||
}
|
||||
}
|
||||
this.internalSlots.push(stack);
|
||||
}
|
||||
public addStackCopy(stack: ItemStack) {
|
||||
this.addStack({...stack});
|
||||
}
|
||||
public sort() {
|
||||
this.internalSlots.sort((a,b)=>{
|
||||
return itemToItemData(a.item).sortOrder - itemToItemData(b.item).sortOrder;
|
||||
return aData.type - bData.type;
|
||||
});
|
||||
}
|
||||
public removeFromEnd(count: number): Slots {
|
||||
const end = this.internalSlots.splice(-count, count);
|
||||
return new Slots(end);
|
||||
}
|
||||
public remove(item: Item, count: number, slot: number) {
|
||||
let s = 0;
|
||||
for(let i = 0; i<this.internalSlots.length && count > 0;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
if(s<slot){
|
||||
s++;
|
||||
}else{
|
||||
if(this.internalSlots[i].count<count){
|
||||
count-=this.internalSlots[i].count;
|
||||
this.internalSlots[i].count=0;
|
||||
|
||||
}else{
|
||||
this.internalSlots[i].count-=count;
|
||||
break;
|
||||
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).type<type));
|
||||
// }
|
||||
// public getAfterType(type: ItemType): Slots {
|
||||
// return new Slots(this.internalSlots.filter(s=>itemToItemData(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){
|
||||
this.internalSlots.push(stack);
|
||||
return 1;
|
||||
}
|
||||
for(let i=0;i<stack.count;i++){
|
||||
this.internalSlots.push({...stack, count: 1});
|
||||
}
|
||||
return stack.count;
|
||||
}
|
||||
public addSlot(stack: ItemStack, mCount: number | null) {
|
||||
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);
|
||||
// }
|
||||
// public remove(item: Item, count: number, slot: number) {
|
||||
// let s = 0;
|
||||
// for(let i = 0; i<this.internalSlots.length && count > 0;i++){
|
||||
// if(this.internalSlots[i].item === item){
|
||||
// if(s<slot){
|
||||
// s++;
|
||||
// }else{
|
||||
// if(this.internalSlots[i].count<count){
|
||||
// count-=this.internalSlots[i].count;
|
||||
// this.internalSlots[i].count=0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
this.internalSlots = this.internalSlots.filter(({count})=>count>0);
|
||||
}
|
||||
// }else{
|
||||
// this.internalSlots[i].count-=count;
|
||||
// break;
|
||||
// }
|
||||
|
||||
public add(item: Item, count: number) {
|
||||
let added = false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// this.internalSlots = this.internalSlots.filter(({count})=>count>0);
|
||||
// }
|
||||
|
||||
// Add something to inventory in game
|
||||
// returns number of slots added
|
||||
public add(item: Item, count: number, equippedDuringReload: boolean, reloading: boolean, mCount: number | null): number {
|
||||
if(mCount === null){
|
||||
mCount = this.internalSlots.length;
|
||||
}
|
||||
//let added = false;
|
||||
const data = itemToItemData(item);
|
||||
// If item is stackable (arrow, material, spirit orbs)
|
||||
// Check if there's already a slot, if so, add it to that and cap it at 999
|
||||
if(data.stackable){
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
this.internalSlots[i].count+=count;
|
||||
added = true;
|
||||
break;
|
||||
if(reloading){
|
||||
if(this.internalSlots[i].count + count > 999){
|
||||
// do not add new stack during loading save, if it would exceed 999
|
||||
return 0;
|
||||
}
|
||||
// Otherwise add the stack directly
|
||||
this.addSlot({item, count, equipped: equippedDuringReload}, mCount+1);
|
||||
return 1;
|
||||
}
|
||||
this.internalSlots[i].count = Math.min(999, this.internalSlots[i].count+count);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!added){
|
||||
const after = this.removeFromEnd(this.getAfterType(data.type).length);
|
||||
// Need to add new slot
|
||||
// Key item check: if the key item or master sword already exists in the first tab, do not add
|
||||
if(mCount != 0){
|
||||
if(data.type === ItemType.Key || item === Item.MasterSword) {
|
||||
let i=0;
|
||||
while(i<this.internalSlots.length && itemToItemData(this.internalSlots[i].item).type < data.type){
|
||||
i++;
|
||||
}
|
||||
for(;i<this.internalSlots.length && itemToItemData(this.internalSlots[i].item).type === data.type;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
// Found the key item/master sword, do not add
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks finish, do add new slot
|
||||
if(data.stackable){
|
||||
if(data.type===ItemType.Arrow){
|
||||
// if currently equipped arrow == 0. new arrows are equiped
|
||||
// TODO: botw needs more testing on how arrows are handled in various cases
|
||||
const shouldEquipNew = this.internalSlots.filter(s=>{
|
||||
const sData = itemToItemData(s.item);
|
||||
return sData.type === data.type && s.equipped && s.count > 0;
|
||||
}).length === 0;
|
||||
this.addStack({item,count,equipped:shouldEquipNew});
|
||||
this.addSlot({item,count,equipped:shouldEquipNew}, mCount+1);
|
||||
}else{
|
||||
this.addStack({item,count,equipped:false});
|
||||
this.addSlot({item,count,equipped:false}, mCount+1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
}else{
|
||||
if(data.type===ItemType.Weapon || data.type===ItemType.Bow || data.type===ItemType.Shield){
|
||||
//Check equip
|
||||
const shouldEquipNew = this.internalSlots.filter(s=>{
|
||||
const shouldEquipNew = !reloading && this.internalSlots.filter(s=>{
|
||||
const sData = itemToItemData(s.item);
|
||||
return sData.type === data.type && s.equipped;
|
||||
}).length === 0;
|
||||
this.addStack({item,count:1,equipped: shouldEquipNew});
|
||||
this.addSlot({item,count:1,equipped: shouldEquipNew}, mCount+1);
|
||||
for(let i=1;i<count;i++){
|
||||
this.addStack({item,count:1,equipped: false});
|
||||
this.addSlot({item,count:1,equipped: false}, mCount+i+1);
|
||||
|
||||
}
|
||||
}else{
|
||||
for(let i=0;i<count;i++){
|
||||
this.addStack({item,count:1,equipped: false});
|
||||
this.addSlot({item,count:1,equipped: false}, mCount+i+1);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
this.addSlotsToEnd(after);
|
||||
}
|
||||
}
|
||||
// this is for both equipments and arrows
|
||||
public equip(item: Item, slot: number) {
|
||||
let s = 0;
|
||||
const type = itemToItemData(item).type;
|
||||
const filtered = this.internalSlots.filter(s=>itemToItemData(s.item).type === type);
|
||||
for(let i = 0; i<filtered.length;i++){
|
||||
filtered[i].equipped=false;
|
||||
if(filtered[i].item === item){
|
||||
if (s===slot){
|
||||
filtered[i].equipped=true;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
public unequip(item:Item, slot: number) {
|
||||
let s = 0;
|
||||
const type = itemToItemData(item).type;
|
||||
if (type===ItemType.Arrow){
|
||||
return; // cannot unequip arrow
|
||||
}
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
if(slot < 0){
|
||||
if(this.internalSlots[i].equipped){
|
||||
this.internalSlots[i].equipped=false;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
if(s<slot){
|
||||
s++;
|
||||
}else{
|
||||
this.internalSlots[i].equipped=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// return how many slots are added
|
||||
// public add(item: Item, count: number, equipped: boolean, isReloading: boolean, mCount: number): number {
|
||||
// let added = false;
|
||||
// const data = itemToItemData(item);
|
||||
// if(data.stackable){
|
||||
// for(let i = 0; i<this.internalSlots.length;i++){
|
||||
// if(this.internalSlots[i].item === item){
|
||||
// if(isReloading){
|
||||
// if(mCount > 0 && this.internalSlots[i].count + count > 999){
|
||||
// return 0; // Skip, do not add new stack at all
|
||||
// }
|
||||
// // Otherwise push the entire new stack
|
||||
// this.internalSlots.push({item, count, equipped});
|
||||
// }else{
|
||||
// this.internalSlots[i].count = Math.min(999, this.internalSlots[i].count+count);
|
||||
// added = true;
|
||||
// }
|
||||
|
||||
// Difference between shoot and remove:
|
||||
// 1. can only be from first (leftmost) slot
|
||||
// 2. empty slots not removed
|
||||
public shoot(item: Item, count: number) {
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
this.internalSlots[i].count-=count;
|
||||
}
|
||||
}
|
||||
}
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if(!added){
|
||||
// const after = this.removeFromEnd(this.getAfterType(data.type).length);
|
||||
// if(data.stackable){
|
||||
// if(data.type===ItemType.Arrow){
|
||||
// // if currently equipped arrow == 0. new arrows are equiped
|
||||
// const shouldEquipNew = this.internalSlots.filter(s=>{
|
||||
// const sData = itemToItemData(s.item);
|
||||
// return sData.type === data.type && s.equipped && s.count > 0;
|
||||
// }).length === 0;
|
||||
// this.addStack({item,count,equipped:shouldEquipNew});
|
||||
// }else{
|
||||
// this.addStack({item,count,equipped:false});
|
||||
// }
|
||||
|
||||
public sortArrows() {
|
||||
const after = this.removeFromEnd(this.getAfterType(ItemType.Arrow).length);
|
||||
const arrows = this.removeFromEnd(this.getByType(ItemType.Arrow).length);
|
||||
arrows.sort();
|
||||
this.addSlotsToEnd(arrows);
|
||||
this.addSlotsToEnd(after);
|
||||
}
|
||||
// }else{
|
||||
// if(data.type===ItemType.Weapon || data.type===ItemType.Bow || data.type===ItemType.Shield){
|
||||
// //Check equip
|
||||
// const shouldEquipNew = this.internalSlots.filter(s=>{
|
||||
// const sData = itemToItemData(s.item);
|
||||
// return sData.type === data.type && s.equipped;
|
||||
// }).length === 0;
|
||||
// this.addStack({item,count:1,equipped: shouldEquipNew});
|
||||
// for(let i=1;i<count;i++){
|
||||
// this.addStack({item,count:1,equipped: false});
|
||||
|
||||
public getFirstEquippedSlotIndex(type: ItemType): number {
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].equipped){
|
||||
const data = itemToItemData(this.internalSlots[i].item);
|
||||
if(data.type === type){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
// }
|
||||
// }else{
|
||||
// for(let i=0;i<count;i++){
|
||||
// this.addStack({item,count:1,equipped: false});
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// this.addSlotsToEnd(after);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // this is for both equipments and arrows
|
||||
// public equip(item: Item, slot: number) {
|
||||
// let s = 0;
|
||||
// const type = itemToItemData(item).type;
|
||||
// const filtered = this.internalSlots.filter(s=>itemToItemData(s.item).type === type);
|
||||
// for(let i = 0; i<filtered.length;i++){
|
||||
// filtered[i].equipped=false;
|
||||
// if(filtered[i].item === item){
|
||||
// if (s===slot){
|
||||
// filtered[i].equipped=true;
|
||||
// }
|
||||
// s++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// public unequip(item:Item, slot: number) {
|
||||
// let s = 0;
|
||||
// const type = itemToItemData(item).type;
|
||||
// if (type===ItemType.Arrow){
|
||||
// return; // cannot unequip arrow
|
||||
// }
|
||||
// for(let i = 0; i<this.internalSlots.length;i++){
|
||||
// if(this.internalSlots[i].item === item){
|
||||
// if(slot < 0){
|
||||
// if(this.internalSlots[i].equipped){
|
||||
// this.internalSlots[i].equipped=false;
|
||||
// break;
|
||||
// }
|
||||
// }else{
|
||||
// if(s<slot){
|
||||
// s++;
|
||||
// }else{
|
||||
// this.internalSlots[i].equipped=false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Difference between shoot and remove:
|
||||
// // 1. can only be from first (leftmost) slot
|
||||
// // 2. empty slots not removed
|
||||
// public shoot(item: Item, count: number) {
|
||||
// for(let i = 0; i<this.internalSlots.length;i++){
|
||||
// if(this.internalSlots[i].item === item){
|
||||
// this.internalSlots[i].count-=count;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// public sortArrows() {
|
||||
// const after = this.removeFromEnd(this.getAfterType(ItemType.Arrow).length);
|
||||
// const arrows = this.removeFromEnd(this.getByType(ItemType.Arrow).length);
|
||||
// arrows.sort();
|
||||
// this.addSlotsToEnd(arrows);
|
||||
// this.addSlotsToEnd(after);
|
||||
// }
|
||||
|
||||
// public getFirstEquippedSlotIndex(type: ItemType): number {
|
||||
// for(let i = 0; i<this.internalSlots.length;i++){
|
||||
// if(this.internalSlots[i].equipped){
|
||||
// const data = itemToItemData(this.internalSlots[i].item);
|
||||
// if(data.type === type){
|
||||
// return i;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return -1;
|
||||
// }
|
||||
}
|
||||
|
|
59
src/core/VisibleInventory.ts
Normal file
59
src/core/VisibleInventory.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { DisplayableInventory, DisplayableSlot, itemStackToDisplayableSlot } from "./DisplayableInventory";
|
||||
import { Item, ItemStack } from "./Item";
|
||||
import { Slots } from "./Slots";
|
||||
|
||||
/*
|
||||
* Implementation of Visible Inventory (PauseMenuDataMgr) in botw
|
||||
*/
|
||||
export class VisibleInventory implements DisplayableInventory{
|
||||
private slots: Slots = new Slots([]);
|
||||
/* Implementation of mCount in botw */
|
||||
private count: number = 0;
|
||||
constructor(slots: Slots, count: number){
|
||||
this.slots = slots;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public deepClone(): VisibleInventory {
|
||||
return new VisibleInventory(this.slots.deepClone(), this.count);
|
||||
}
|
||||
|
||||
public getDisplayedSlots(): DisplayableSlot[] {
|
||||
return this.slots.getSlotsRef().map((stack, i)=>itemStackToDisplayableSlot(stack, i>=this.count));
|
||||
}
|
||||
|
||||
public getSlots(): Slots {
|
||||
return this.slots;
|
||||
}
|
||||
|
||||
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 addInGame
|
||||
|
||||
// Only clears first this.count
|
||||
public clearForReload() {
|
||||
if(this.count > 0){
|
||||
this.slots.clearFirst(this.count);
|
||||
this.count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public getCount(): number {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
public modifyCount(delta: number): void {
|
||||
this.count+=delta;
|
||||
}
|
||||
|
||||
public resetCount(): void {
|
||||
this.count = this.slots.length;
|
||||
}
|
||||
}
|
14
src/data/mergeSort.ts
Normal file
14
src/data/mergeSort.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
//https://medium.com/@fsufitch/is-javascript-array-sort-stable-46b90822543f
|
||||
export const stableSort = <T>(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);
|
||||
|
||||
for (let i=0; i<array.length; i++) {
|
||||
array[i] = stabilizedThis[i][0];
|
||||
}
|
||||
}
|
|
@ -1,46 +1,47 @@
|
|||
import clsx from "clsx";
|
||||
import { ItemList, ItemListItemProps, ItemListProps } from "components/ItemList";
|
||||
import { ItemList, ItemListProps } from "components/ItemList";
|
||||
import { DoubleItemSlot } from "components/ItemSlot";
|
||||
import { TitledList } from "components/TitledList";
|
||||
import { Command } from "core/Command";
|
||||
import { ItemStack, itemToItemData } from "core/Item";
|
||||
import { parseCommand } from "core/Parser";
|
||||
import { SimulationState } from "core/SimulationState";
|
||||
import { Slots } from "core/Slots";
|
||||
import Background from "assets/Background.png";
|
||||
import InGameBackground from "assets/InGame.png";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
type DisplayPaneProps = {
|
||||
command: string,
|
||||
orbs: number,
|
||||
displayIndex: number,
|
||||
slots: Slots,
|
||||
savedSlots: Slots,
|
||||
numBroken: number,
|
||||
simulationState: SimulationState,
|
||||
overlaySave: boolean,
|
||||
editCommand: (c: Command)=>void
|
||||
}
|
||||
|
||||
export const stacksToItemListProps = (slots: Slots, numBroken: number, isSave: boolean): ItemListProps => {
|
||||
return {
|
||||
items: stacksToItemProps(slots.getSlotsRef()),
|
||||
numBroken,
|
||||
isSave,
|
||||
};
|
||||
};
|
||||
// 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 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 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<DisplayPaneProps> = ({command,orbs,editCommand,displayIndex, slots, savedSlots, numBroken, overlaySave})=>{
|
||||
export const DisplayPane: React.FC<DisplayPaneProps> = ({command,editCommand,displayIndex,simulationState, overlaySave})=>{
|
||||
const [commandString, setCommandString] = useState<string>("");
|
||||
const [hasError, setHasError] = useState<boolean>(false);
|
||||
const listProps = stacksToItemListProps(slots, numBroken, false);
|
||||
const listSaveProps = stacksToItemListProps(savedSlots, 0, true);
|
||||
//const listProps = stacksToItemListProps(slots, numBroken, false);
|
||||
//const listSaveProps = stacksToItemListProps(savedSlots, 0, true);
|
||||
useEffect(()=>{
|
||||
if(commandString!==command){
|
||||
setCommandString(command);
|
||||
|
@ -50,22 +51,25 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,orbs,editComman
|
|||
}, [command, displayIndex]);
|
||||
|
||||
return <div id="DisplayPane" style={{
|
||||
width: "calc( 100% - 300px - 5px )",
|
||||
float: "right",
|
||||
border: "1px solid black",
|
||||
boxSizing: "content-box"
|
||||
height: "100%",
|
||||
// width: "calc( 100% - 300px - 5px )",
|
||||
// float: "right",
|
||||
// border: "1px solid black",
|
||||
// boxSizing: "content-box"
|
||||
} }>
|
||||
<div style={{
|
||||
marginBottom: 2,
|
||||
boxSizing: "content-box",
|
||||
height: "50px"
|
||||
boxSizing: "border-box",
|
||||
height: "40px"
|
||||
} }>
|
||||
<input className={clsx("Calamity", hasError && "InputError")} style={{
|
||||
marginTop: 2,
|
||||
width: "80%",
|
||||
<input id="CommandInputField" className={clsx("Calamity", "CommandInput", hasError && "InputError")} style={{
|
||||
background: `url(${Background})`,
|
||||
width: "100%",
|
||||
height: "40px",
|
||||
fontSize: "20pt",
|
||||
|
||||
paddingLeft: 10,
|
||||
margin: 0,
|
||||
boxSizing: "border-box",
|
||||
fontSize: "16pt",
|
||||
outline: "none",
|
||||
}}value={commandString}
|
||||
placeholder="Type command here..."
|
||||
onChange={(e)=>{
|
||||
|
@ -79,45 +83,55 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,orbs,editComman
|
|||
setHasError(true);
|
||||
}
|
||||
}}></input>
|
||||
<span>
|
||||
Orbs: {orbs}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div style={{
|
||||
height: "calc( 100% - 40px )"
|
||||
}}>
|
||||
{overlaySave ?
|
||||
<div style={{
|
||||
borderTop: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
height: "calc( ( 99vh - 60px ))",
|
||||
overflowY: "auto"
|
||||
boxSizing: "border-box",
|
||||
height: "100%",
|
||||
overflowY: "auto",
|
||||
background: `url(${InGameBackground})`,
|
||||
backgroundPosition: "center",
|
||||
backgroundSize: "auto 100%",
|
||||
color: "white",
|
||||
} }>
|
||||
<div>Save / Current</div>
|
||||
<div>
|
||||
|
||||
<TitledList title={`Game Data / Visible Inventory (Count=${simulationState.inventoryMCount})`}>
|
||||
{
|
||||
(()=>{
|
||||
const doubleSlots: JSX.Element[] = [];
|
||||
for(let i=0;i<savedSlots.length && i<slots.length;i++){
|
||||
const gameDataSlots = simulationState.displayableGameData.getDisplayedSlots();
|
||||
const inventorySlots = simulationState.displayablePouch.getDisplayedSlots();
|
||||
console.log(inventorySlots);
|
||||
for(let i=0;i<gameDataSlots.length && i<inventorySlots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot key={i}
|
||||
first={{...stackToItemProps(savedSlots.get(i)), isBroken:false, isSave:true}}
|
||||
second={{...stackToItemProps(slots.get(i)), isBroken:i>=slots.length-numBroken, isSave:false}}
|
||||
first={{slot: gameDataSlots[i]}}
|
||||
second={{slot: inventorySlots[i]}}
|
||||
/>);
|
||||
}
|
||||
if(savedSlots.length>slots.length){
|
||||
for(let i=slots.length;i<savedSlots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot key={i+slots.length}
|
||||
first={{...stackToItemProps(savedSlots.get(i)), isBroken:false, isSave:true}}
|
||||
if(inventorySlots.length>gameDataSlots.length){
|
||||
for(let i=inventorySlots.length;i<gameDataSlots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot key={i+inventorySlots.length}
|
||||
first={{slot: gameDataSlots[i]}}
|
||||
/>);
|
||||
}
|
||||
}else if(slots.length > savedSlots.length){
|
||||
for(let i=savedSlots.length;i<slots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot key={i + savedSlots.length}
|
||||
second={{...stackToItemProps(slots.get(i)), isBroken:i>=slots.length-numBroken, isSave:false}}
|
||||
}else if(inventorySlots.length > gameDataSlots.length){
|
||||
for(let i=gameDataSlots.length;i<inventorySlots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot key={i + gameDataSlots.length}
|
||||
second={{slot: inventorySlots[i]}}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
return doubleSlots;
|
||||
})()
|
||||
}
|
||||
</div>
|
||||
</TitledList>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -125,25 +139,37 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,orbs,editComman
|
|||
|
||||
<div style={{
|
||||
borderTop: "1px solid black",
|
||||
background: `url(${Background})`,
|
||||
color: "white",
|
||||
borderBottom: "1px solid black",
|
||||
marginBottom: 2,
|
||||
boxSizing: "content-box",
|
||||
height: "calc( ( 99vh - 60px ) / 2)",
|
||||
boxSizing: "border-box",
|
||||
height: "50%",
|
||||
overflowY: "auto"
|
||||
} }>
|
||||
<div>Inventory of Save</div>
|
||||
<ItemList {...listSaveProps}/>
|
||||
<TitledList title="Game Data">
|
||||
<ItemList slots={simulationState.displayableGameData.getDisplayedSlots()}/>
|
||||
</TitledList>
|
||||
|
||||
</div>
|
||||
<div style={{
|
||||
borderTop: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
height: "calc( ( 99vh - 60px ) / 2)",
|
||||
overflowY: "auto"
|
||||
background: `url(${InGameBackground})`,
|
||||
backgroundPosition: "center",
|
||||
backgroundSize: "100%",
|
||||
boxSizing: "border-box",
|
||||
height: "50%",
|
||||
overflowY: "auto",
|
||||
color: "white"
|
||||
} }>
|
||||
<div>Current Inventory</div>
|
||||
<ItemList {...listProps}/>
|
||||
<TitledList title={`Visible Inventory (Count=${simulationState.inventoryMCount})`}>
|
||||
<ItemList slots={simulationState.displayablePouch.getDisplayedSlots()}/>
|
||||
</TitledList>
|
||||
|
||||
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
|
||||
|
||||
</div>;
|
||||
};
|
||||
|
|
86
src/surfaces/ReferencePage.tsx
Normal file
86
src/surfaces/ReferencePage.tsx
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { ItemList } from "components/ItemList";
|
||||
import { TitledList } from "components/TitledList";
|
||||
import React from "react";
|
||||
|
||||
export const ReferencePage: React.FC = React.memo(()=>{
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div style={{height: "100%", width: "100%", color: "white"}}>
|
||||
<TitledList title="Reference">
|
||||
<div style={{padding: 10}}>
|
||||
<h2>Commands</h2>
|
||||
<h3 className="Reference">Initialize X item1 Y item2 Z item3 ...</h3>
|
||||
<h4 className="Reference">Used for initializing inventory before simulation</h4>
|
||||
<p className="Reference">
|
||||
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
|
||||
</p>
|
||||
<p className="Reference">
|
||||
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
|
||||
</p>
|
||||
<p className="Reference">
|
||||
Note that this will not clear saves. You can use this command to initialize multiple saves
|
||||
</p>
|
||||
<p className="Reference Example">Example: Initialize 1 Apple 2 Axe 3 Slate 4 SpiritOrb</p>
|
||||
|
||||
<h3 className="Reference">Save / Save As NAME</h3>
|
||||
<h4 className="Reference">Simulates a hard save or auto save action</h4>
|
||||
<p className="Reference">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p className="Reference Example">Example 1: Save</p>
|
||||
<p className="Reference Example">Example 2: Save As MySave</p>
|
||||
<p className="Reference">
|
||||
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
|
||||
</p>
|
||||
|
||||
<h3 className="Reference">Reload (NAME)</h3>
|
||||
<h4 className="Reference">Simulates reloading a save</h4>
|
||||
<p className="Reference">
|
||||
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
|
||||
</p>
|
||||
<p className="Reference">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p className="Reference Example">Example 1: Reload</p>
|
||||
<p className="Reference Example">Example 2: Reload MySave</p>
|
||||
|
||||
<h3 className="Reference">Use NAME</h3>
|
||||
<h4 className="Reference">(Deprecated) Specify which save to load on the subsequent reload</h4>
|
||||
<p className="Reference Example">
|
||||
This command is only for backward compatibility. Use "Reload" instead
|
||||
</p>
|
||||
<p className="Reference">
|
||||
Specify the save named NAME to be reloaded on the next "Reload" command
|
||||
</p>
|
||||
|
||||
<p className="Reference Example">Example: Use MySave</p>
|
||||
|
||||
<h3 className="Reference">Break X Slots</h3>
|
||||
<h4 className="Reference">Simulate making X broken slots with hold smuggle glitch</h4>
|
||||
<p className="Reference">
|
||||
Decrease inventory Count by X
|
||||
</p>
|
||||
<p className="Reference">
|
||||
This command does not automatically simulate the hold smuggle and sell process.
|
||||
It just changes count (i.e. make broken slots) with magic.
|
||||
</p>
|
||||
|
||||
<p className="Reference Example">Example: Break 4 Slots</p>
|
||||
</div>
|
||||
|
||||
</TitledList>
|
||||
</div>
|
||||
)
|
||||
});
|
Reference in a new issue