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 {
|
.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 {
|
.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 {
|
.ItemSlot {
|
||||||
|
@ -42,7 +61,7 @@ h1 {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
|
|
||||||
background-color: #333333;
|
background-color: #333333bb;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +70,7 @@ h1 {
|
||||||
background-color: #660000;
|
background-color: #660000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ItemSlotSave {
|
.GameDataBackground {
|
||||||
background-color: #003300;
|
background-color: #003300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,3 +100,21 @@ h3.ListHeader {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px;
|
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 { 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 "./App.css";
|
||||||
import { CommandItem } from "./components/CommandItem";
|
import { CommandItem } from "./components/CommandItem";
|
||||||
|
|
||||||
import { DisplayPane, stacksToItemListProps } from "surfaces/DisplayPane";
|
import { DisplayPane } from "surfaces/DisplayPane";
|
||||||
import { Item } from "core/Item";
|
import { Item } from "core/Item";
|
||||||
import { saveAs } from "data/FileSaver";
|
import { saveAs } from "data/FileSaver";
|
||||||
import { parseCommand } from "core/Parser";
|
import { parseCommand } from "core/Parser";
|
||||||
import { ItemList } from "components/ItemList";
|
import { ItemList } from "components/ItemList";
|
||||||
import { TitledList } from "components/TitledList";
|
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 getDefaultCommands = (): Command[]=>{
|
||||||
const encoded = localStorage.getItem("HDS.CurrentCommandsText");
|
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 lines.map(l=>parseCommand(l)).filter(c=>c) as Command[];
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
new CommandInitialize([
|
parseCommand("Get 5 Diamond 1 Slate 1 Glider 4 SpiritOrb"),
|
||||||
{
|
parseCommand("Save"),
|
||||||
item: Item.Diamond,
|
parseCommand("Break 4 Slots"),
|
||||||
count: 5,
|
parseCommand("Reload"),
|
||||||
equipped:false,
|
parseCommand("Save"),
|
||||||
},
|
parseCommand("Reload"),
|
||||||
{
|
] as Command[];;
|
||||||
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()
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = () => {
|
export const App: React.FC = () => {
|
||||||
|
const [page, setPageInState] = useState<string>("#simulation");
|
||||||
const [overlaySave, setOverlaySave] = useState<boolean>(false);
|
const [overlaySave, setOverlaySave] = useState<boolean>(false);
|
||||||
const [commands, setCommands] = useState<Command[]>(getDefaultCommands());
|
const [commands, setCommands] = useState<Command[]>(getDefaultCommands());
|
||||||
|
const [selectedSaveName, setSelectedSaveName] = useState<string>("");
|
||||||
const [displayIndex, setDisplayIndex] = useState<number>(0);
|
const [displayIndex, setDisplayIndex] = useState<number>(0);
|
||||||
const [contextMenuX, setContextMenuX] = useState<number>(0);
|
const [contextMenuX, setContextMenuX] = useState<number>(0);
|
||||||
const [contextMenuY, setContextMenuY] = useState<number>(0);
|
const [contextMenuY, setContextMenuY] = useState<number>(0);
|
||||||
|
@ -86,20 +46,36 @@ export const App: React.FC = () => {
|
||||||
const uploadRef = useRef<HTMLInputElement>(null);
|
const uploadRef = useRef<HTMLInputElement>(null);
|
||||||
const contextMenuRef = useRef<HTMLDivElement>(null);
|
const contextMenuRef = useRef<HTMLDivElement>(null);
|
||||||
// compute props
|
// compute props
|
||||||
const inventories = useMemo(()=>{
|
const simulationStates = useMemo(()=>{
|
||||||
const inventories: Inventory[] = [];
|
const simulationStates: SimulationState[] = [];
|
||||||
const inv = new Inventory();
|
const state = createSimulationState();
|
||||||
commands.forEach(c=>{
|
commands.forEach(c=>{
|
||||||
c.execute(inv);
|
c.execute(state);
|
||||||
inventories.push(inv.deepClone());
|
simulationStates.push(state.deepClone());
|
||||||
});
|
});
|
||||||
return inventories;
|
return simulationStates;
|
||||||
}, [commands]);
|
}, [commands]);
|
||||||
|
|
||||||
|
const setPage = useCallback((hash: string)=>{
|
||||||
|
window.location.hash = hash;
|
||||||
|
setPageInState(hash);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
setPage(window.location.hash || "#simulation");
|
||||||
|
}, [window.location.hash]);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
window.onkeydown=(e)=>{
|
window.onkeydown=(e)=>{
|
||||||
if(e.code==="ArrowDown"){
|
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));
|
setDisplayIndex(Math.min(commands.length-1, displayIndex+1));
|
||||||
|
}
|
||||||
}else if(e.code==="ArrowUp"){
|
}else if(e.code==="ArrowUp"){
|
||||||
setDisplayIndex(Math.max(0, displayIndex-1));
|
setDisplayIndex(Math.max(0, displayIndex-1));
|
||||||
}
|
}
|
||||||
|
@ -143,8 +119,12 @@ export const App: React.FC = () => {
|
||||||
<div id="NavBar" style={{
|
<div id="NavBar" style={{
|
||||||
height: 40
|
height: 40
|
||||||
}}>
|
}}>
|
||||||
<button>Simulation</button>
|
<button onClick={()=>{
|
||||||
<button>Reference</button>
|
setPage("#simulation")
|
||||||
|
}}>Simulation</button>
|
||||||
|
<button onClick={()=>{
|
||||||
|
setPage("#reference")
|
||||||
|
}}>Reference</button>
|
||||||
<button>Options</button>
|
<button>Options</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -156,56 +136,51 @@ export const App: React.FC = () => {
|
||||||
maxHeight: 220,
|
maxHeight: 220,
|
||||||
height: "30vh",
|
height: "30vh",
|
||||||
border: "1px solid black",
|
border: "1px solid black",
|
||||||
boxSizing: "content-box",
|
boxSizing: "border-box",
|
||||||
overflowY: "hidden"
|
overflowY: "hidden",
|
||||||
|
|
||||||
}}>
|
}}>
|
||||||
<TitledList title="Saves">
|
<TitledList title="Saves">
|
||||||
|
{
|
||||||
|
displayIndex >=0 && displayIndex < simulationStates.length &&
|
||||||
<ol>
|
<ol>
|
||||||
|
{
|
||||||
|
!!simulationStates[displayIndex].getManualSave() &&
|
||||||
<CommandItem
|
<CommandItem
|
||||||
onClick={()=>{}}
|
onClick={()=>{
|
||||||
|
setSelectedSaveName("");
|
||||||
|
}}
|
||||||
comment={false}
|
comment={false}
|
||||||
|
isSelected={selectedSaveName===""}
|
||||||
|
|
||||||
>
|
>
|
||||||
Manual Save
|
Manual Save
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Object.entries(simulationStates[displayIndex].getNamedSaves()).map(([name, _gamedata])=>(
|
||||||
<CommandItem
|
<CommandItem
|
||||||
onClick={()=>{}}
|
onClick={()=>{
|
||||||
|
setSelectedSaveName(name);
|
||||||
|
}}
|
||||||
comment={false}
|
comment={false}
|
||||||
|
isSelected={selectedSaveName===name}
|
||||||
>
|
>
|
||||||
Auto Save 1
|
{name}
|
||||||
</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
|
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
))
|
||||||
|
}
|
||||||
</ol>
|
</ol>
|
||||||
|
}
|
||||||
|
|
||||||
</TitledList>
|
</TitledList>
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
minHeight: "calc( 70vh - 45px )",
|
minHeight: "calc( 70vh - 40px )",
|
||||||
height: "calc( 100vh - 45px - 220px )",
|
height: "calc( 100vh - 40px - 220px )",
|
||||||
border: "1px solid black",
|
border: "1px solid black",
|
||||||
boxSizing: "content-box",
|
boxSizing: "border-box",
|
||||||
overflowY: "hidden"
|
overflowY: "hidden"
|
||||||
|
|
||||||
}}>
|
}}>
|
||||||
|
@ -215,7 +190,13 @@ export const App: React.FC = () => {
|
||||||
{
|
{
|
||||||
commands.map((c,i)=>
|
commands.map((c,i)=>
|
||||||
<CommandItem
|
<CommandItem
|
||||||
onClick={()=>setDisplayIndex(i)}
|
onClick={()=>{
|
||||||
|
setDisplayIndex(i);
|
||||||
|
const inputField = document.getElementById("CommandInputField");
|
||||||
|
if(inputField){
|
||||||
|
inputField.focus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
onContextMenu={(x,y)=>{
|
onContextMenu={(x,y)=>{
|
||||||
setContextIndex(i);
|
setContextIndex(i);
|
||||||
setContextMenuX(x);
|
setContextMenuX(x);
|
||||||
|
@ -259,39 +240,73 @@ export const App: React.FC = () => {
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div id="MainPane" style={{
|
<div id="MainPane" style={{
|
||||||
width: "calc ( 100% - 300px )"
|
position: "absolute",
|
||||||
|
top: 40,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 300,
|
||||||
|
backgroundColor: "#262626"
|
||||||
}}>
|
}}>
|
||||||
|
{
|
||||||
|
page === "#simulation" && <>
|
||||||
<div style={{
|
<div style={{
|
||||||
maxHeight: 220,
|
maxHeight: 220,
|
||||||
height: "30vh",
|
height: "30vh",
|
||||||
border: "1px solid black",
|
border: "1px solid black",
|
||||||
boxSizing: "content-box",
|
boxSizing: "border-box",
|
||||||
overflowY: "hidden"
|
overflowY: "hidden",
|
||||||
|
color: "white",
|
||||||
|
backgroundColor: "#262626"
|
||||||
} }>
|
} }>
|
||||||
|
{
|
||||||
|
(displayIndex >= 0 && displayIndex < commands.length) ?
|
||||||
<TitledList title="Save Data">
|
<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>
|
||||||
|
:
|
||||||
|
<TitledList title="Select an instruction on the left to view it">
|
||||||
|
|
||||||
|
|
||||||
|
</TitledList>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</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 &&
|
{displayIndex >= 0 && displayIndex < commands.length &&
|
||||||
<DisplayPane
|
<DisplayPane
|
||||||
overlaySave={overlaySave}
|
overlaySave={overlaySave}
|
||||||
displayIndex={displayIndex}
|
displayIndex={displayIndex}
|
||||||
command={commands[displayIndex].getDisplayString()}
|
command={commands[displayIndex].getDisplayString()}
|
||||||
orbs={inventories[displayIndex].getTurnedInOrbs()}
|
simulationState={simulationStates[displayIndex]}
|
||||||
slots={inventories[displayIndex].getSlots()}
|
|
||||||
savedSlots={inventories[displayIndex].getSavedSlots()}
|
|
||||||
numBroken={inventories[displayIndex].getNumBroken()}
|
|
||||||
editCommand={(c)=>{
|
editCommand={(c)=>{
|
||||||
const arrCopy = [...commands];
|
const arrCopy = [...commands];
|
||||||
arrCopy[displayIndex] = c;
|
arrCopy[displayIndex] = c;
|
||||||
setCommands(arrCopy);
|
setCommands(arrCopy);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
page === "#reference" && <ReferencePage />
|
||||||
|
}
|
||||||
</div>
|
</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";
|
import { ItemSlot } from "./ItemSlot";
|
||||||
|
|
||||||
export type ItemListItemProps = {
|
|
||||||
image: string,
|
|
||||||
count: number,
|
|
||||||
isEquipped: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ItemListProps = {
|
export type ItemListProps = {
|
||||||
isSave: boolean,
|
slots: DisplayableSlot[]
|
||||||
items: ItemListItemProps[],
|
|
||||||
numBroken: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ItemList: React.FC<ItemListProps> = ({items, numBroken, isSave}) => {
|
export const ItemList: React.FC<ItemListProps> = ({slots}) => {
|
||||||
return <>
|
return <>
|
||||||
{
|
{
|
||||||
items.map(({image, count, isEquipped}, i)=>{
|
slots.map((slot, i)=>{
|
||||||
const broken = i+numBroken >= items.length;
|
return <ItemSlot key={i} slot={slot}/>;
|
||||||
return <ItemSlot key={i} image={image} count={count} isBroken={broken} isSave={isSave} isEquipped={isEquipped}/>;
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</>;
|
</>;
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import { DisplayableSlot } from "core/DisplayableInventory";
|
||||||
|
import Background from "assets/Background.png";
|
||||||
|
|
||||||
|
|
||||||
type ItemSlotProps = {
|
type ItemSlotProps = {
|
||||||
image: string,
|
slot: DisplayableSlot
|
||||||
count: number,
|
|
||||||
isBroken: boolean,
|
|
||||||
isSave: boolean,
|
|
||||||
isEquipped: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ItemSlot: React.FC<ItemSlotProps> = ({image, count, isBroken, isSave, isEquipped})=>{
|
export const ItemSlot: React.FC<ItemSlotProps> = ({slot: {image, count, isBrokenSlot, isEquipped, displayCount}})=>{
|
||||||
return (
|
return (
|
||||||
<span className={clsx("ItemSlot", isBroken && "ItemSlotBroken", isSave && "ItemSlotSave", isEquipped && "ItemSlotEquipped")}>
|
<span className={clsx("ItemSlot", isBrokenSlot && "ItemSlotBroken", isEquipped && "ItemSlotEquipped")}>
|
||||||
<img className={clsx("ItemImage", isSave && "ItemImageSave")}src={image} />
|
<img className={clsx("ItemImage")} src={image} />
|
||||||
{
|
{
|
||||||
count > 0 && <span className={"ItemCount"}>
|
displayCount && <span className={"ItemCount"}>
|
||||||
x{count}
|
x{count}
|
||||||
</span>
|
</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})=>{
|
export const DoubleItemSlot: React.FC<{first?: ItemSlotProps, second?: ItemSlotProps}> = ({first, second})=>{
|
||||||
return (
|
return (
|
||||||
<span style={{display: "inline-block", width: 72, height: 144, verticalAlign:"top"}}>
|
<span style={{display: "inline-block", width: 72, height: 144, verticalAlign:"top"}}>
|
||||||
{first ? <ItemSlot {...first}/> : <div style={{height: 72}}/>}
|
<div style={{height: 72, background: `url(${Background})`}} >
|
||||||
{second ? <ItemSlot {...second}/> : <div style={{height: 72}}/> }
|
{first && <ItemSlot {...first}/>}
|
||||||
|
</div>
|
||||||
|
<div style={{height: 72}}>
|
||||||
|
{second && <ItemSlot {...second}/>}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -8,9 +8,9 @@ export const TitledList: React.FC<TitledListProps> = ({title, children}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3 className="ListHeader" style={{
|
<h3 className="ListHeader" style={{
|
||||||
height: 20,
|
height: 40,
|
||||||
border: "1px solid red",
|
borderBottom: "2px solid",
|
||||||
boxSizing: "content-box"
|
boxSizing: "border-box",
|
||||||
}}>
|
}}>
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
import { Inventory } from "./Inventory";
|
import { Inventory } from "./Inventory";
|
||||||
import { Item, ItemStack, itemToArrowType } from "./Item";
|
import { Item, ItemStack, itemToArrowType } from "./Item";
|
||||||
|
import { SimulationState } from "./SimulationState";
|
||||||
|
|
||||||
export interface Command {
|
export interface Command {
|
||||||
execute(inv: Inventory): void,
|
execute(state: SimulationState): void,
|
||||||
getDisplayString(): string,
|
getDisplayString(): string,
|
||||||
// fromBuffer(buf: Buffer): number,
|
|
||||||
// toBuffer(): Buffer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandNothing implements Command {
|
export class CommandNothing implements Command {
|
||||||
static Op = 0x0;
|
|
||||||
|
|
||||||
execute(_inv: Inventory): void {
|
execute(_state: Inventory): void {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
getDisplayString(): string {
|
getDisplayString(): string {
|
||||||
|
@ -21,44 +19,14 @@ export class CommandNothing implements Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandInitialize implements Command {
|
export class CommandInitialize implements Command {
|
||||||
static Op = 0x1;
|
|
||||||
private stacks: ItemStack[];
|
private stacks: ItemStack[];
|
||||||
constructor(stacks: ItemStack[]){
|
constructor(stacks: ItemStack[]){
|
||||||
this.stacks = stacks;
|
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 {
|
public execute(state: SimulationState): void {
|
||||||
inv.init(this.stacks);
|
state.initialize(this.stacks);
|
||||||
}
|
}
|
||||||
public getDisplayString(): string {
|
public getDisplayString(): string {
|
||||||
const parts = ["Initialize"];
|
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 {
|
export class CommandSave implements Command {
|
||||||
static Op = 0x3;
|
|
||||||
// public fromBuffer(_buf: Buffer): number {
|
public execute(state: SimulationState): void {
|
||||||
// return 0;
|
state.save();
|
||||||
// }
|
|
||||||
// public toBuffer(): Buffer {
|
|
||||||
// const buf: Buffer = Buffer.alloc(1);
|
|
||||||
// buf.writeInt8(CommandSave.Op);
|
|
||||||
// return buf;
|
|
||||||
// }
|
|
||||||
public execute(inv: Inventory): void {
|
|
||||||
inv.save();
|
|
||||||
}
|
}
|
||||||
public getDisplayString(): string {
|
public getDisplayString(): string {
|
||||||
return "Save";
|
return "Save";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandReload implements Command {
|
export class CommandSaveAs 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 {
|
|
||||||
private name: string;
|
private name: string;
|
||||||
constructor(name: string){
|
constructor(name: string){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
public execute(inv: Inventory): void {
|
public execute(state: SimulationState): void {
|
||||||
inv.save();
|
state.save(this.name);
|
||||||
inv.setTag(this.name);
|
|
||||||
}
|
}
|
||||||
public getDisplayString(): string {
|
public getDisplayString(): string {
|
||||||
return `Save As ${this.name}`;
|
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;
|
private name: string;
|
||||||
constructor(name: string){
|
constructor(name: string){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
public execute(inv: Inventory): void {
|
public execute(state: SimulationState): void {
|
||||||
inv.applyTag(this.name);
|
state.useSaveForNextReload(this.name);
|
||||||
}
|
}
|
||||||
public getDisplayString(): string {
|
public getDisplayString(): string {
|
||||||
return `Use ${this.name}`;
|
return `Use ${this.name}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandCloseGame implements Command {
|
export class CommandBreakSlots implements Command {
|
||||||
public execute(inv: Inventory): void {
|
|
||||||
inv.closeGame();
|
private numToBreak: number;
|
||||||
|
constructor(numToBreak: number){
|
||||||
|
this.numToBreak = numToBreak;
|
||||||
|
}
|
||||||
|
|
||||||
|
public execute(state: SimulationState): void {
|
||||||
|
state.breakSlots(this.numToBreak);
|
||||||
}
|
}
|
||||||
public getDisplayString(): string {
|
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 {
|
export class CommandComment implements Command {
|
||||||
private name: string;
|
private name: string;
|
||||||
constructor(name: string){
|
constructor(name: string){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
public execute(_inv: Inventory): void {
|
public execute(_state: SimulationState): void {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
public getDisplayString(): string {
|
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";
|
import { Slots } from "./Slots";
|
||||||
|
|
||||||
export class Inventory {
|
export class Inventory {
|
||||||
private slots: Slots = new Slots([]);
|
// private slots: Slots = new Slots([]);
|
||||||
private savedSlots: Slots = new Slots([]);
|
// private savedSlots: Slots = new Slots([]);
|
||||||
private namedSlots: {[name: string]: Slots} = {};
|
// private namedSlots: {[name: string]: Slots} = {};
|
||||||
private numBroken = 0;
|
// private numBroken = 0;
|
||||||
private isInitialSort = false;
|
// private isInitialSort = false;
|
||||||
private isAltered = true;
|
// private isAltered = true;
|
||||||
private inaccurate = false;
|
// private inaccurate = false;
|
||||||
private turnedInOrbs = 0;
|
// private turnedInOrbs = 0;
|
||||||
public deepClone(): Inventory {
|
// public deepClone(): Inventory {
|
||||||
const other = new Inventory();
|
// const other = new Inventory();
|
||||||
other.slots = this.slots.deepClone();
|
// other.slots = this.slots.deepClone();
|
||||||
other.savedSlots = this.savedSlots.deepClone();
|
// other.savedSlots = this.savedSlots.deepClone();
|
||||||
other.numBroken = this.numBroken;
|
// other.numBroken = this.numBroken;
|
||||||
other.isInitialSort = this.isInitialSort;
|
// other.isInitialSort = this.isInitialSort;
|
||||||
other.isAltered = this.isAltered;
|
// other.isAltered = this.isAltered;
|
||||||
other.inaccurate = this.inaccurate;
|
// other.inaccurate = this.inaccurate;
|
||||||
other.turnedInOrbs = this.turnedInOrbs;
|
// other.turnedInOrbs = this.turnedInOrbs;
|
||||||
other.namedSlots = {};
|
// other.namedSlots = {};
|
||||||
for(const name in this.namedSlots){
|
// for(const name in this.namedSlots){
|
||||||
other.namedSlots[name] = this.namedSlots[name].deepClone();
|
// other.namedSlots[name] = this.namedSlots[name].deepClone();
|
||||||
}
|
// }
|
||||||
return other;
|
// return other;
|
||||||
}
|
|
||||||
|
|
||||||
public getSlots(): Slots {
|
|
||||||
return this.slots;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSavedSlots(): Slots {
|
|
||||||
return this.savedSlots;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getNumBroken(): number {
|
|
||||||
return this.numBroken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isInaccurate(): boolean {
|
|
||||||
return this.inaccurate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getTurnedInOrbs(): number {
|
|
||||||
return this.turnedInOrbs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public rawInit(stacks: ItemStack[]) {
|
|
||||||
this.slots = new Slots(stacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(stacks: ItemStack[]) {
|
|
||||||
this.slots = new Slots([]);
|
|
||||||
stacks.forEach(s=>{
|
|
||||||
this.slots.add(s.item, s.count);
|
|
||||||
});
|
|
||||||
this.numBroken = 0;
|
|
||||||
this.isInitialSort = false;
|
|
||||||
this.isAltered = true;
|
|
||||||
this.inaccurate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public closeGame() {
|
|
||||||
this.numBroken = 0;
|
|
||||||
this.isInitialSort = false;
|
|
||||||
this.isAltered = true;
|
|
||||||
this.inaccurate = false;
|
|
||||||
this.slots = new Slots([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public addBrokenSlots(num: number) {
|
|
||||||
this.numBroken+=num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setTag(name: string){
|
|
||||||
this.namedSlots[name] = this.savedSlots.deepClone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public applyTag(name: string){
|
|
||||||
if(name in this.namedSlots){
|
|
||||||
this.savedSlots = this.namedSlots[name].deepClone();
|
|
||||||
}else{
|
|
||||||
this.savedSlots = new Slots([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public save() {
|
|
||||||
if(this.isAltered){
|
|
||||||
this.savedSlots = this.slots.deepClone();
|
|
||||||
}
|
|
||||||
// Inventory Corruption
|
|
||||||
// get durability transfer slots
|
|
||||||
const durabilityTransferSlots: number[] = [];
|
|
||||||
const equippedWeapon = this.slots.getFirstEquippedSlotIndex(ItemType.Weapon);
|
|
||||||
if(equippedWeapon>=0){
|
|
||||||
durabilityTransferSlots.push(equippedWeapon);
|
|
||||||
}
|
|
||||||
const equippedBow = this.slots.getFirstEquippedSlotIndex(ItemType.Bow);
|
|
||||||
if(equippedBow>=0){
|
|
||||||
durabilityTransferSlots.push(equippedBow);
|
|
||||||
}
|
|
||||||
const equippedShield = this.slots.getFirstEquippedSlotIndex(ItemType.Shield);
|
|
||||||
if(equippedShield>=0){
|
|
||||||
durabilityTransferSlots.push(equippedShield);
|
|
||||||
}
|
|
||||||
durabilityTransferSlots.forEach(s=>{
|
|
||||||
if(s<this.savedSlots.length){
|
|
||||||
// We ignore the case where durability transfer happens from equipment to equipment
|
|
||||||
|
|
||||||
if(itemToItemData(this.savedSlots.get(s).item).stackable){
|
|
||||||
this.savedSlots.get(s).count = 999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public reload() {
|
|
||||||
|
|
||||||
// get things to dupe
|
|
||||||
const dupeMap: {[k in ItemType]: Slots} = {
|
|
||||||
[ItemType.Weapon]: new Slots([]),
|
|
||||||
[ItemType.Bow]: new Slots([]),
|
|
||||||
[ItemType.Arrow]: new Slots([]),
|
|
||||||
[ItemType.Shield]: new Slots([]),
|
|
||||||
[ItemType.Material]: new Slots([]),
|
|
||||||
[ItemType.Meal]: new Slots([]),
|
|
||||||
[ItemType.Key]: new Slots([])
|
|
||||||
};
|
|
||||||
for(let i=Math.max(0, this.slots.length-this.numBroken);i<this.slots.length;i++){
|
|
||||||
const stack = this.slots.get(i);
|
|
||||||
const itemData = itemToItemData(stack.item);
|
|
||||||
dupeMap[itemData.type].addStackCopy(stack);
|
|
||||||
}
|
|
||||||
// apply dupe
|
|
||||||
//console.log(dupeMap);
|
|
||||||
this.slots = new Slots([]);
|
|
||||||
// const dupeType = (type: ItemType) => {
|
|
||||||
|
|
||||||
// }
|
// }
|
||||||
ItemTypes.forEach(type=>{
|
|
||||||
this.slots.addSlotsToEnd(dupeMap[type]);
|
|
||||||
this.slots.addSlotsToEnd(this.savedSlots.getByType(type).deepClone());
|
|
||||||
});
|
|
||||||
|
|
||||||
this.slots.sortArrows();
|
// public getSlots(): Slots {
|
||||||
this.isInitialSort = true;
|
// return this.slots;
|
||||||
this.isAltered = false;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
public sortKey() {
|
// public getSavedSlots(): Slots {
|
||||||
const nonKeyItems = this.slots.getBeforeType(ItemType.Key);
|
// return this.savedSlots;
|
||||||
const keyItems = this.slots.getByType(ItemType.Key);
|
// }
|
||||||
keyItems.sort();
|
|
||||||
nonKeyItems.addSlotsToEnd(keyItems);
|
|
||||||
this.slots = nonKeyItems;
|
|
||||||
this.isAltered=true;
|
|
||||||
this.isInitialSort=false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sortMaterial() {
|
// public getNumBroken(): number {
|
||||||
const beforeMaterial = this.slots.getBeforeType(ItemType.Material);
|
// return this.numBroken;
|
||||||
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;
|
// public isInaccurate(): boolean {
|
||||||
this.isInitialSort = false;
|
// return this.inaccurate;
|
||||||
this.isAltered=true;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
public remove(item: Item, count: number, slot: number) {
|
// public getTurnedInOrbs(): number {
|
||||||
this.slots.remove(item, count, slot);
|
// return this.turnedInOrbs;
|
||||||
if(item===Item.SpiritOrb){
|
// }
|
||||||
this.turnedInOrbs+=count;
|
|
||||||
}
|
|
||||||
this.isAltered=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public add(item: Item, count: number) {
|
// public rawInit(stacks: ItemStack[]) {
|
||||||
this.slots.add(item, count);
|
// this.slots = new Slots(stacks);
|
||||||
if(itemToItemData(item).type===ItemType.Arrow){
|
// }
|
||||||
this.slots.sortArrows();
|
|
||||||
}
|
|
||||||
this.isAltered=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public equipEquipmentOrArrow(item: Item, slot: number) {
|
// public init(stacks: ItemStack[]) {
|
||||||
this.slots.equip(item, slot);
|
// this.slots = new Slots([]);
|
||||||
this.isAltered=true;
|
// 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){
|
// public closeGame() {
|
||||||
this.slots.unequip(item, slot);
|
// this.numBroken = 0;
|
||||||
this.isAltered=true;
|
// this.isInitialSort = false;
|
||||||
}
|
// this.isAltered = true;
|
||||||
|
// this.inaccurate = false;
|
||||||
|
// this.slots = new Slots([]);
|
||||||
|
// }
|
||||||
|
|
||||||
public shootArrow(item: Item, count: number){
|
// public addBrokenSlots(num: number) {
|
||||||
this.slots.shoot(item, count);
|
// this.numBroken+=num;
|
||||||
this.isAltered=true;
|
// }
|
||||||
}
|
|
||||||
|
// 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,
|
Bow = 1,
|
||||||
Arrow = 2,
|
Arrow = 2,
|
||||||
Shield = 3,
|
Shield = 3,
|
||||||
Material = 4,
|
Armor = 4,
|
||||||
Meal = 5,
|
Material = 5,
|
||||||
Key = 6
|
Meal = 6,
|
||||||
|
Key = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ItemTypes = [
|
export const ItemTypes = [
|
||||||
|
@ -73,6 +74,8 @@ export enum Item {
|
||||||
HeartyRadish = "HeartyRadish",
|
HeartyRadish = "HeartyRadish",
|
||||||
BigHeartyRadish = "BigHeartyRadish",
|
BigHeartyRadish = "BigHeartyRadish",
|
||||||
Fairy = "Fairy",
|
Fairy = "Fairy",
|
||||||
|
|
||||||
|
MasterSword = "MasterSword",
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemData = {
|
type ItemData = {
|
||||||
|
@ -90,6 +93,7 @@ const TypeToCount = {
|
||||||
[ItemType.Bow]: 0,
|
[ItemType.Bow]: 0,
|
||||||
[ItemType.Arrow]: 0,
|
[ItemType.Arrow]: 0,
|
||||||
[ItemType.Shield]: 0,
|
[ItemType.Shield]: 0,
|
||||||
|
[ItemType.Armor]: 0,
|
||||||
[ItemType.Material]: 0,
|
[ItemType.Material]: 0,
|
||||||
[ItemType.Key]: 0,
|
[ItemType.Key]: 0,
|
||||||
[ItemType.Meal]: 0,
|
[ItemType.Meal]: 0,
|
||||||
|
@ -161,6 +165,9 @@ register(0x50, Item.Weapon, ItemType.Weapon, {
|
||||||
image: Images.Axe,
|
image: Images.Axe,
|
||||||
stackable: false
|
stackable: false
|
||||||
});
|
});
|
||||||
|
register(0, Item.MasterSword, ItemType.Weapon, {
|
||||||
|
stackable: false,
|
||||||
|
})
|
||||||
|
|
||||||
register(0x60, Item.Bow, ItemType.Bow, {
|
register(0x60, Item.Bow, ItemType.Bow, {
|
||||||
image: Images.ForestDwellerBow,
|
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";
|
import { Item, ItemStack } from "./Item";
|
||||||
|
|
||||||
export const parseCommand = (cmdString: string): Command | undefined => {
|
export const parseCommand = (cmdString: string): Command | undefined => {
|
||||||
|
|
||||||
if(cmdString.startsWith("# ")){
|
if(cmdString.startsWith("# ")){
|
||||||
return new CommandComment(cmdString.substring(2));
|
return new CommandComment(cmdString.substring(2));
|
||||||
}
|
}
|
||||||
|
@ -28,22 +39,24 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
||||||
}
|
}
|
||||||
return new CommandInitialize(stacks);
|
return new CommandInitialize(stacks);
|
||||||
}
|
}
|
||||||
// no var
|
// Save/Reload
|
||||||
if(tokens.length===1 && tokens[0] === "Save"){
|
if(tokens.length===1 && tokens[0] === "Save"){
|
||||||
return new CommandSave();
|
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"){
|
if(tokens.length===1 && tokens[0] === "Reload"){
|
||||||
return new CommandReload();
|
return new CommandReload();
|
||||||
}
|
}
|
||||||
|
if(tokens.length===2 && tokens[0] === "Reload"){
|
||||||
if(tokens.length===2 && tokens[0] === "Sort" && tokens[1] === "Key"){
|
return new CommandReload(tokens[1]);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
// break
|
// break
|
||||||
if (tokens.length > 2 && tokens[0] === "Break" && tokens[2]=== "Slots" ){
|
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)){
|
if(Number.isInteger(slots)){
|
||||||
return new CommandBreakSlots(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;
|
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";
|
import { Item, ItemStack, itemToItemData, ItemType } from "./Item";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the data model common to GameData and VisibleInventory
|
||||||
|
*/
|
||||||
export class Slots {
|
export class Slots {
|
||||||
private internalSlots: ItemStack[] = [];
|
private internalSlots: ItemStack[] = [];
|
||||||
constructor(slots: ItemStack[]) {
|
constructor(slots: ItemStack[]) {
|
||||||
|
@ -14,184 +20,301 @@ export class Slots {
|
||||||
public get length(): number {
|
public get length(): number {
|
||||||
return this.internalSlots.length;
|
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 {
|
if(mCount <= 1){
|
||||||
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){
|
|
||||||
return;
|
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;
|
||||||
}
|
}
|
||||||
}
|
return aData.type - bData.type;
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
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{
|
public clearFirst(count: number) {
|
||||||
this.internalSlots[i].count-=count;
|
this.internalSlots.splice(0, count);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// 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;
|
||||||
|
|
||||||
}
|
// }else{
|
||||||
}
|
// this.internalSlots[i].count-=count;
|
||||||
}
|
// break;
|
||||||
this.internalSlots = this.internalSlots.filter(({count})=>count>0);
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
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){
|
if(data.stackable){
|
||||||
for(let i = 0; i<this.internalSlots.length;i++){
|
for(let i = 0; i<this.internalSlots.length;i++){
|
||||||
if(this.internalSlots[i].item === item){
|
if(this.internalSlots[i].item === item){
|
||||||
this.internalSlots[i].count+=count;
|
if(reloading){
|
||||||
added = true;
|
if(this.internalSlots[i].count + count > 999){
|
||||||
break;
|
// 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){
|
// Need to add new slot
|
||||||
const after = this.removeFromEnd(this.getAfterType(data.type).length);
|
// 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.stackable){
|
||||||
if(data.type===ItemType.Arrow){
|
if(data.type===ItemType.Arrow){
|
||||||
// if currently equipped arrow == 0. new arrows are equiped
|
// 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 shouldEquipNew = this.internalSlots.filter(s=>{
|
||||||
const sData = itemToItemData(s.item);
|
const sData = itemToItemData(s.item);
|
||||||
return sData.type === data.type && s.equipped && s.count > 0;
|
return sData.type === data.type && s.equipped && s.count > 0;
|
||||||
}).length === 0;
|
}).length === 0;
|
||||||
this.addStack({item,count,equipped:shouldEquipNew});
|
this.addSlot({item,count,equipped:shouldEquipNew}, mCount+1);
|
||||||
}else{
|
}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){
|
if(data.type===ItemType.Weapon || data.type===ItemType.Bow || data.type===ItemType.Shield){
|
||||||
//Check equip
|
//Check equip
|
||||||
const shouldEquipNew = this.internalSlots.filter(s=>{
|
const shouldEquipNew = !reloading && this.internalSlots.filter(s=>{
|
||||||
const sData = itemToItemData(s.item);
|
const sData = itemToItemData(s.item);
|
||||||
return sData.type === data.type && s.equipped;
|
return sData.type === data.type && s.equipped;
|
||||||
}).length === 0;
|
}).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++){
|
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{
|
}else{
|
||||||
for(let i=0;i<count;i++){
|
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);
|
// return how many slots are added
|
||||||
}
|
// public add(item: Item, count: number, equipped: boolean, isReloading: boolean, mCount: number): number {
|
||||||
}
|
// let added = false;
|
||||||
// this is for both equipments and arrows
|
// const data = itemToItemData(item);
|
||||||
public equip(item: Item, slot: number) {
|
// if(data.stackable){
|
||||||
let s = 0;
|
// for(let i = 0; i<this.internalSlots.length;i++){
|
||||||
const type = itemToItemData(item).type;
|
// if(this.internalSlots[i].item === item){
|
||||||
const filtered = this.internalSlots.filter(s=>itemToItemData(s.item).type === type);
|
// if(isReloading){
|
||||||
for(let i = 0; i<filtered.length;i++){
|
// if(mCount > 0 && this.internalSlots[i].count + count > 999){
|
||||||
filtered[i].equipped=false;
|
// return 0; // Skip, do not add new stack at all
|
||||||
if(filtered[i].item === item){
|
// }
|
||||||
if (s===slot){
|
// // Otherwise push the entire new stack
|
||||||
filtered[i].equipped=true;
|
// this.internalSlots.push({item, count, equipped});
|
||||||
}
|
// }else{
|
||||||
s++;
|
// this.internalSlots[i].count = Math.min(999, this.internalSlots[i].count+count);
|
||||||
}
|
// added = true;
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
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:
|
// break;
|
||||||
// 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(!added){
|
||||||
if(this.internalSlots[i].item === item){
|
// const after = this.removeFromEnd(this.getAfterType(data.type).length);
|
||||||
this.internalSlots[i].count-=count;
|
// 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() {
|
// }else{
|
||||||
const after = this.removeFromEnd(this.getAfterType(ItemType.Arrow).length);
|
// if(data.type===ItemType.Weapon || data.type===ItemType.Bow || data.type===ItemType.Shield){
|
||||||
const arrows = this.removeFromEnd(this.getByType(ItemType.Arrow).length);
|
// //Check equip
|
||||||
arrows.sort();
|
// const shouldEquipNew = this.internalSlots.filter(s=>{
|
||||||
this.addSlotsToEnd(arrows);
|
// const sData = itemToItemData(s.item);
|
||||||
this.addSlotsToEnd(after);
|
// 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++){
|
// }else{
|
||||||
if(this.internalSlots[i].equipped){
|
// for(let i=0;i<count;i++){
|
||||||
const data = itemToItemData(this.internalSlots[i].item);
|
// this.addStack({item,count:1,equipped: false});
|
||||||
if(data.type === type){
|
// }
|
||||||
return i;
|
// }
|
||||||
}
|
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
return -1;
|
// 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 clsx from "clsx";
|
||||||
import { ItemList, ItemListItemProps, ItemListProps } from "components/ItemList";
|
import { ItemList, ItemListProps } from "components/ItemList";
|
||||||
import { DoubleItemSlot } from "components/ItemSlot";
|
import { DoubleItemSlot } from "components/ItemSlot";
|
||||||
|
import { TitledList } from "components/TitledList";
|
||||||
import { Command } from "core/Command";
|
import { Command } from "core/Command";
|
||||||
import { ItemStack, itemToItemData } from "core/Item";
|
import { ItemStack, itemToItemData } from "core/Item";
|
||||||
import { parseCommand } from "core/Parser";
|
import { parseCommand } from "core/Parser";
|
||||||
|
import { SimulationState } from "core/SimulationState";
|
||||||
import { Slots } from "core/Slots";
|
import { Slots } from "core/Slots";
|
||||||
|
import Background from "assets/Background.png";
|
||||||
|
import InGameBackground from "assets/InGame.png";
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
type DisplayPaneProps = {
|
type DisplayPaneProps = {
|
||||||
command: string,
|
command: string,
|
||||||
orbs: number,
|
|
||||||
displayIndex: number,
|
displayIndex: number,
|
||||||
slots: Slots,
|
simulationState: SimulationState,
|
||||||
savedSlots: Slots,
|
|
||||||
numBroken: number,
|
|
||||||
overlaySave: boolean,
|
overlaySave: boolean,
|
||||||
editCommand: (c: Command)=>void
|
editCommand: (c: Command)=>void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stacksToItemListProps = (slots: Slots, numBroken: number, isSave: boolean): ItemListProps => {
|
// export const stacksToItemListProps = (slots: Slots, numBroken: number, isSave: boolean): ItemListProps => {
|
||||||
return {
|
// return {
|
||||||
items: stacksToItemProps(slots.getSlotsRef()),
|
// items: stacksToItemProps(slots.getSlotsRef()),
|
||||||
numBroken,
|
// numBroken,
|
||||||
isSave,
|
// isSave,
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
export const stacksToItemProps = (stacks: ItemStack[]): ItemListItemProps[] => {
|
// export const stacksToItemProps = (stacks: ItemStack[]): ItemListItemProps[] => {
|
||||||
return stacks.map(stackToItemProps);
|
// return stacks.map(stackToItemProps);
|
||||||
};
|
// };
|
||||||
|
|
||||||
export const stackToItemProps = ({item, count, equipped}: ItemStack): ItemListItemProps => {
|
// export const stackToItemProps = ({item, count, equipped}: ItemStack): ItemListItemProps => {
|
||||||
const data = itemToItemData(item);
|
// const data = itemToItemData(item);
|
||||||
return {image: data.image, count: data.stackable ? count : 0, isEquipped:equipped};
|
// 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 [commandString, setCommandString] = useState<string>("");
|
||||||
const [hasError, setHasError] = useState<boolean>(false);
|
const [hasError, setHasError] = useState<boolean>(false);
|
||||||
const listProps = stacksToItemListProps(slots, numBroken, false);
|
//const listProps = stacksToItemListProps(slots, numBroken, false);
|
||||||
const listSaveProps = stacksToItemListProps(savedSlots, 0, true);
|
//const listSaveProps = stacksToItemListProps(savedSlots, 0, true);
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if(commandString!==command){
|
if(commandString!==command){
|
||||||
setCommandString(command);
|
setCommandString(command);
|
||||||
|
@ -50,22 +51,25 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,orbs,editComman
|
||||||
}, [command, displayIndex]);
|
}, [command, displayIndex]);
|
||||||
|
|
||||||
return <div id="DisplayPane" style={{
|
return <div id="DisplayPane" style={{
|
||||||
width: "calc( 100% - 300px - 5px )",
|
height: "100%",
|
||||||
float: "right",
|
// width: "calc( 100% - 300px - 5px )",
|
||||||
border: "1px solid black",
|
// float: "right",
|
||||||
boxSizing: "content-box"
|
// border: "1px solid black",
|
||||||
|
// boxSizing: "content-box"
|
||||||
} }>
|
} }>
|
||||||
<div style={{
|
<div style={{
|
||||||
marginBottom: 2,
|
boxSizing: "border-box",
|
||||||
boxSizing: "content-box",
|
height: "40px"
|
||||||
height: "50px"
|
|
||||||
} }>
|
} }>
|
||||||
<input className={clsx("Calamity", hasError && "InputError")} style={{
|
<input id="CommandInputField" className={clsx("Calamity", "CommandInput", hasError && "InputError")} style={{
|
||||||
marginTop: 2,
|
background: `url(${Background})`,
|
||||||
width: "80%",
|
width: "100%",
|
||||||
height: "40px",
|
height: "40px",
|
||||||
fontSize: "20pt",
|
paddingLeft: 10,
|
||||||
|
margin: 0,
|
||||||
|
boxSizing: "border-box",
|
||||||
|
fontSize: "16pt",
|
||||||
|
outline: "none",
|
||||||
}}value={commandString}
|
}}value={commandString}
|
||||||
placeholder="Type command here..."
|
placeholder="Type command here..."
|
||||||
onChange={(e)=>{
|
onChange={(e)=>{
|
||||||
|
@ -79,45 +83,55 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,orbs,editComman
|
||||||
setHasError(true);
|
setHasError(true);
|
||||||
}
|
}
|
||||||
}}></input>
|
}}></input>
|
||||||
<span>
|
|
||||||
Orbs: {orbs}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{
|
||||||
|
height: "calc( 100% - 40px )"
|
||||||
|
}}>
|
||||||
{overlaySave ?
|
{overlaySave ?
|
||||||
<div style={{
|
<div style={{
|
||||||
borderTop: "1px solid black",
|
borderTop: "1px solid black",
|
||||||
boxSizing: "content-box",
|
boxSizing: "border-box",
|
||||||
height: "calc( ( 99vh - 60px ))",
|
height: "100%",
|
||||||
overflowY: "auto"
|
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[] = [];
|
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}
|
doubleSlots.push(<DoubleItemSlot key={i}
|
||||||
first={{...stackToItemProps(savedSlots.get(i)), isBroken:false, isSave:true}}
|
first={{slot: gameDataSlots[i]}}
|
||||||
second={{...stackToItemProps(slots.get(i)), isBroken:i>=slots.length-numBroken, isSave:false}}
|
second={{slot: inventorySlots[i]}}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
if(savedSlots.length>slots.length){
|
if(inventorySlots.length>gameDataSlots.length){
|
||||||
for(let i=slots.length;i<savedSlots.length;i++){
|
for(let i=inventorySlots.length;i<gameDataSlots.length;i++){
|
||||||
doubleSlots.push(<DoubleItemSlot key={i+slots.length}
|
doubleSlots.push(<DoubleItemSlot key={i+inventorySlots.length}
|
||||||
first={{...stackToItemProps(savedSlots.get(i)), isBroken:false, isSave:true}}
|
first={{slot: gameDataSlots[i]}}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
}else if(slots.length > savedSlots.length){
|
}else if(inventorySlots.length > gameDataSlots.length){
|
||||||
for(let i=savedSlots.length;i<slots.length;i++){
|
for(let i=gameDataSlots.length;i<inventorySlots.length;i++){
|
||||||
doubleSlots.push(<DoubleItemSlot key={i + savedSlots.length}
|
doubleSlots.push(<DoubleItemSlot key={i + gameDataSlots.length}
|
||||||
second={{...stackToItemProps(slots.get(i)), isBroken:i>=slots.length-numBroken, isSave:false}}
|
second={{slot: inventorySlots[i]}}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return doubleSlots;
|
return doubleSlots;
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
</div>
|
</TitledList>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -125,25 +139,37 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,orbs,editComman
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
borderTop: "1px solid black",
|
borderTop: "1px solid black",
|
||||||
|
background: `url(${Background})`,
|
||||||
|
color: "white",
|
||||||
borderBottom: "1px solid black",
|
borderBottom: "1px solid black",
|
||||||
marginBottom: 2,
|
boxSizing: "border-box",
|
||||||
boxSizing: "content-box",
|
height: "50%",
|
||||||
height: "calc( ( 99vh - 60px ) / 2)",
|
|
||||||
overflowY: "auto"
|
overflowY: "auto"
|
||||||
} }>
|
} }>
|
||||||
<div>Inventory of Save</div>
|
<TitledList title="Game Data">
|
||||||
<ItemList {...listSaveProps}/>
|
<ItemList slots={simulationState.displayableGameData.getDisplayedSlots()}/>
|
||||||
|
</TitledList>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
borderTop: "1px solid black",
|
borderTop: "1px solid black",
|
||||||
boxSizing: "content-box",
|
background: `url(${InGameBackground})`,
|
||||||
height: "calc( ( 99vh - 60px ) / 2)",
|
backgroundPosition: "center",
|
||||||
overflowY: "auto"
|
backgroundSize: "100%",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
height: "50%",
|
||||||
|
overflowY: "auto",
|
||||||
|
color: "white"
|
||||||
} }>
|
} }>
|
||||||
<div>Current Inventory</div>
|
<TitledList title={`Visible Inventory (Count=${simulationState.inventoryMCount})`}>
|
||||||
<ItemList {...listProps}/>
|
<ItemList slots={simulationState.displayablePouch.getDisplayedSlots()}/>
|
||||||
|
</TitledList>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</>}
|
</>}
|
||||||
|
</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