ui and inventory corruptin stuff
43
src/App.css
|
@ -35,3 +35,46 @@ h1 {
|
|||
.InputError {
|
||||
background-color: #eeaaaa;
|
||||
}
|
||||
|
||||
.ItemSlot {
|
||||
margin: 4px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
|
||||
background-color: #333333;
|
||||
box-sizing: content-box;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ItemSlotBroken {
|
||||
background-color: #660000;
|
||||
}
|
||||
|
||||
.ItemSlotSave {
|
||||
background-color: #003300;
|
||||
}
|
||||
|
||||
.ItemSlotEquipped {
|
||||
background-color: #0088ff;
|
||||
}
|
||||
|
||||
.ItemImage {
|
||||
border: 1px solid #999999;
|
||||
box-sizing: border-box;
|
||||
margin:1px;
|
||||
width:62px;
|
||||
height:62px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.ItemImageSave {
|
||||
}
|
||||
|
||||
.ItemCount {
|
||||
font-size: 10pt;
|
||||
position:relative;
|
||||
top:-22px;
|
||||
left:8px;
|
||||
color:#eeeeee;
|
||||
float: left;
|
||||
}
|
103
src/App.tsx
|
@ -7,32 +7,36 @@ import { CommandItem } from "./components/CommandItem";
|
|||
|
||||
import { DisplayPane } from "surfaces/DisplayPane";
|
||||
import { Item } from "core/Item";
|
||||
import { deserialzeCommands, serializeCommands } from "core/serialize";
|
||||
import { saveAs } from "data/FileSaver";
|
||||
import { parseCommand } from "core/Parser";
|
||||
|
||||
const getDefaultCommands = (): Command[]=>{
|
||||
const encoded = localStorage.getItem("HDS.CurrentCommands");
|
||||
const encoded = localStorage.getItem("HDS.CurrentCommandsText");
|
||||
if(encoded){
|
||||
return deserialzeCommands(encoded);
|
||||
const lines = encoded.split("\n");
|
||||
return lines.map(l=>parseCommand(l)).filter(c=>c) as Command[];
|
||||
}
|
||||
return [
|
||||
new CommandInitialize([
|
||||
{
|
||||
item: Item.Diamond,
|
||||
count: 5,
|
||||
equipped:false,
|
||||
},
|
||||
{
|
||||
item: Item.Slate,
|
||||
count: 1,
|
||||
equipped:false,
|
||||
},
|
||||
{
|
||||
item: Item.Glider,
|
||||
count: 1,
|
||||
equipped:false,
|
||||
},
|
||||
{
|
||||
item: Item.SpiritOrb,
|
||||
count: 4,
|
||||
equipped:false,
|
||||
}
|
||||
]),
|
||||
new CommandBreakSlots(4),
|
||||
|
@ -44,6 +48,7 @@ const getDefaultCommands = (): Command[]=>{
|
|||
};
|
||||
|
||||
export const App: React.FC = () => {
|
||||
const [overlaySave, setOverlaySave] = useState<boolean>(false);
|
||||
const [commands, setCommands] = useState<Command[]>(getDefaultCommands());
|
||||
const [displayIndex, setDisplayIndex] = useState<number>(0);
|
||||
const [contextMenuX, setContextMenuX] = useState<number>(0);
|
||||
|
@ -56,7 +61,7 @@ export const App: React.FC = () => {
|
|||
const inv = new Inventory();
|
||||
commands.forEach(c=>{
|
||||
c.execute(inv);
|
||||
inventories.push(inv.clone());
|
||||
inventories.push(inv.deepClone());
|
||||
});
|
||||
return inventories;
|
||||
}, [commands]);
|
||||
|
@ -72,8 +77,12 @@ export const App: React.FC = () => {
|
|||
}, [commands, displayIndex]);
|
||||
|
||||
useEffect(()=>{
|
||||
const encoded = serializeCommands(commands);
|
||||
localStorage.setItem("HDS.CurrentCommands", encoded);
|
||||
// const encoded = serializeCommands(commands);
|
||||
// localStorage.setItem("HDS.CurrentCommands", encoded);
|
||||
const lines = commands.map(c=>c.getDisplayString());
|
||||
const text = lines.join("\n");
|
||||
localStorage.setItem("HDS.CurrentCommandsText", text);
|
||||
|
||||
}, [commands]);
|
||||
|
||||
const uploadRef = useRef<HTMLInputElement>(null);
|
||||
|
@ -132,23 +141,22 @@ export const App: React.FC = () => {
|
|||
arrCopy.push(new CommandNothing());
|
||||
setCommands(arrCopy);
|
||||
}}>(new)</CommandItem>
|
||||
<CommandItem onClick={()=>{
|
||||
if(uploadRef.current){
|
||||
uploadRef.current.click();
|
||||
}
|
||||
}}>(import)</CommandItem>
|
||||
<CommandItem onClick={()=>{
|
||||
const lines = commands.map(c=>c.getDisplayString());
|
||||
const text = lines.join("\n");
|
||||
saveAs(text, "dupe.txt");
|
||||
}}>(export)</CommandItem>
|
||||
<CommandItem onClick={(x,y)=>{
|
||||
setContextIndex(-1);
|
||||
setContextMenuX(x);
|
||||
setContextMenuY(y);
|
||||
setContextMenuShowing(true);
|
||||
}}>(options)</CommandItem>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
{displayIndex >= 0 && displayIndex < commands.length &&
|
||||
<DisplayPane
|
||||
overlaySave={overlaySave}
|
||||
displayIndex={displayIndex}
|
||||
command={commands[displayIndex].getDisplayString()}
|
||||
stacks={inventories[displayIndex].getSlots()}
|
||||
slots={inventories[displayIndex].getSlots()}
|
||||
savedSlots={inventories[displayIndex].getSavedSlots()}
|
||||
numBroken={inventories[displayIndex].getNumBroken()}
|
||||
editCommand={(c)=>{
|
||||
const arrCopy = [...commands];
|
||||
|
@ -186,6 +194,7 @@ export const App: React.FC = () => {
|
|||
listStyleType: "none",
|
||||
paddingInlineStart: 0
|
||||
}}>
|
||||
{contextIndex >= 0 ? <>
|
||||
<CommandItem onClick={()=>{
|
||||
const arrCopy = [...commands];
|
||||
arrCopy.splice(contextIndex, 0, new CommandNothing());
|
||||
|
@ -202,7 +211,65 @@ export const App: React.FC = () => {
|
|||
setContextMenuShowing(false);
|
||||
setContextIndex(-1);
|
||||
}
|
||||
}}>Delete</CommandItem>
|
||||
}}>Delete</CommandItem></> :
|
||||
<>
|
||||
<CommandItem onClick={()=>{
|
||||
setOverlaySave(!overlaySave);
|
||||
}}>Toggle Save Overlay</CommandItem>
|
||||
<CommandItem onClick={()=>{
|
||||
if(uploadRef.current){
|
||||
uploadRef.current.click();
|
||||
}
|
||||
}}>Import</CommandItem>
|
||||
<CommandItem onClick={()=>{
|
||||
const lines = commands.map(c=>c.getDisplayString());
|
||||
const text = lines.join("\n");
|
||||
saveAs(text, "dupe.txt");
|
||||
}}>Export</CommandItem>
|
||||
<CommandItem onClick={()=>{
|
||||
alert(`Available Commands:
|
||||
Initialize X Item1 Y Item2 Z Item3 ...
|
||||
Break X Slots - add X broken slots
|
||||
Save
|
||||
Reload
|
||||
Sort Key/Material - sort key items or material
|
||||
Get/Add/Cook/Pickup X ITEM
|
||||
Remove/Drop/Sell X ITEM From Slot Y
|
||||
Remove/Sell/Eat MEAL From Slot X
|
||||
|
||||
Limitations:
|
||||
Inventory corruption is not implemented yet
|
||||
|
||||
`);
|
||||
alert(`Available Items:
|
||||
Slate
|
||||
Glider
|
||||
SpiritOrb
|
||||
SpeedFood
|
||||
Lotus
|
||||
SilentPrincess
|
||||
Honey
|
||||
Acorn
|
||||
FaroshScale
|
||||
FaroshClaw
|
||||
FaroshHorn
|
||||
HeartyBass
|
||||
Beetle
|
||||
Opal
|
||||
Diamond
|
||||
Tail
|
||||
Spring
|
||||
Shaft
|
||||
Core
|
||||
Wood
|
||||
Weapon
|
||||
`);
|
||||
}}>Reference</CommandItem>
|
||||
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
BIN
src/assets/img/Acorn.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
src/assets/img/AncientArrow.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/img/Axe.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/img/Beetle.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
src/assets/img/BombArrow.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/Core.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
src/assets/img/Diamond.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/FaroshClaw.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/img/FaroshHorn.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/img/FaroshScale.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/FireArrow.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
src/assets/img/ForestDwellerBow.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/img/Glider.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
src/assets/img/HeartyBass.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/Honey.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/IceArrow.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/img/Lotus.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
src/assets/img/MasterSword.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
src/assets/img/NormalArrow.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/Opal.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/PotLid.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
src/assets/img/Shaft.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/img/ShockArrow.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/img/SilentPrincess.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/Slate.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
src/assets/img/SpeedFood.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/img/SpiritOrb.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/img/Spring.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/img/Tail.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/Wood.png
Normal file
After Width: | Height: | Size: 12 KiB |
|
@ -5,14 +5,16 @@ type CommandItemProps = PropsWithChildren<{
|
|||
isSelected?: boolean,
|
||||
isContextSelected?: boolean,
|
||||
error?: boolean,
|
||||
onClick: ()=>void,
|
||||
onClick: (x: number, y: number)=>void,
|
||||
onContextMenu?: (x: number, y: number)=>void
|
||||
}>;
|
||||
|
||||
export const CommandItem: React.FC<CommandItemProps> = ({isSelected, isContextSelected,error, children, onClick, onContextMenu}) => {
|
||||
return <li
|
||||
className={clsx("CommandItem", isSelected && "CommandItemSelected", isContextSelected&& "CommandItemContextSelected",error && "CommandItemError")}
|
||||
onClick={onClick}
|
||||
onClick={(e)=>{
|
||||
onClick(e.clientX, e.clientY);
|
||||
}}
|
||||
onContextMenu={(e)=>{
|
||||
if(onContextMenu){
|
||||
onContextMenu(e.clientX,e.clientY);
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
import { ItemStack } from "./ItemStack";
|
||||
import { ItemSlot } from "./ItemSlot";
|
||||
|
||||
export type ItemListItemProps = {
|
||||
image: string,
|
||||
count: number,
|
||||
isEquipped: boolean,
|
||||
}
|
||||
|
||||
export type ItemListProps = {
|
||||
items: {name: string, count: number}[],
|
||||
isSave: boolean,
|
||||
items: ItemListItemProps[],
|
||||
numBroken: number
|
||||
}
|
||||
|
||||
export const ItemList: React.FC<ItemListProps> = ({items, numBroken}) => {
|
||||
export const ItemList: React.FC<ItemListProps> = ({items, numBroken, isSave}) => {
|
||||
return <>
|
||||
{
|
||||
items.map(({name, count}, i)=>{
|
||||
items.map(({image, count, isEquipped}, i)=>{
|
||||
const broken = i+numBroken >= items.length;
|
||||
return <ItemStack key={i} name={name} count={count} isBroken={broken}/>;
|
||||
return <ItemSlot key={i} image={image} count={count} isBroken={broken} isSave={isSave} isEquipped={isEquipped}/>;
|
||||
})
|
||||
}
|
||||
</>;
|
||||
|
|
30
src/components/ItemSlot.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import clsx from "clsx";
|
||||
type ItemSlotProps = {
|
||||
image: string,
|
||||
count: number,
|
||||
isBroken: boolean,
|
||||
isSave: boolean,
|
||||
isEquipped: boolean,
|
||||
};
|
||||
|
||||
export const ItemSlot: React.FC<ItemSlotProps> = ({image, count, isBroken, isSave, isEquipped})=>{
|
||||
return (
|
||||
<span className={clsx("ItemSlot", isBroken && "ItemSlotBroken", isSave && "ItemSlotSave", isEquipped && "ItemSlotEquipped")}>
|
||||
<img className={clsx("ItemImage", isSave && "ItemImageSave")}src={image} />
|
||||
{
|
||||
count > 0 && <span className={"ItemCount"}>
|
||||
x{count}
|
||||
</span>
|
||||
}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const DoubleItemSlot: React.FC<{first?: ItemSlotProps, second?: ItemSlotProps}> = ({first, second})=>{
|
||||
return (
|
||||
<span style={{display: "inline-block", width: 72, height: 144, verticalAlign:"top"}}>
|
||||
{first ? <ItemSlot {...first}/> : <div style={{height: 72}}/>}
|
||||
{second ? <ItemSlot {...second}/> : <div style={{height: 72}}/> }
|
||||
</span>
|
||||
)
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
type ItemStackProps = {
|
||||
name: string,
|
||||
count: number,
|
||||
isBroken: boolean
|
||||
};
|
||||
|
||||
export const ItemStack: React.FC<ItemStackProps> = ({name, count, isBroken})=>{
|
||||
return <span>[{name} x{count}]{isBroken && " (broken)"} <br/></span>;
|
||||
};
|
|
@ -1,28 +1,18 @@
|
|||
import { Inventory } from "./Inventory";
|
||||
import { Item, ItemIds } from "./Item";
|
||||
import { ItemStack } from "./ItemStack";
|
||||
import { idToItemData, Item, ItemStack, itemToArrowType, itemToItemData } from "./Item";
|
||||
|
||||
const Buffer = require("buffer/").Buffer; /* eslint-disable-line @typescript-eslint/no-var-requires*/
|
||||
|
||||
export interface Command {
|
||||
execute(inv: Inventory): void,
|
||||
getDisplayString(): string,
|
||||
fromBuffer(buf: Buffer): number,
|
||||
toBuffer(): Buffer,
|
||||
// fromBuffer(buf: Buffer): number,
|
||||
// toBuffer(): Buffer,
|
||||
}
|
||||
|
||||
export class CommandNothing implements Command {
|
||||
static Op = 0x0;
|
||||
|
||||
fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
toBuffer(): Buffer {
|
||||
const buf: Buffer = Buffer.alloc(1);
|
||||
buf.writeInt8(CommandNothing.Op);
|
||||
return buf;
|
||||
}
|
||||
|
||||
execute(_inv: Inventory): void {
|
||||
// nothing
|
||||
}
|
||||
|
@ -38,40 +28,36 @@ export class CommandInitialize implements Command {
|
|||
constructor(stacks: ItemStack[]){
|
||||
this.stacks = stacks;
|
||||
}
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
let read = 0;
|
||||
const size = buf.readUInt16LE();
|
||||
read+=2;
|
||||
const stacks: ItemStack[] = [];
|
||||
for(let i=0;i<size;i++){
|
||||
const count = buf.readInt16LE(read);
|
||||
read+=2;
|
||||
const id = buf.readInt8(read);
|
||||
read++;
|
||||
for(const item in ItemIds){
|
||||
if(ItemIds[item as Item] === id){
|
||||
stacks.push({item: item as Item, count});
|
||||
}
|
||||
}
|
||||
}
|
||||
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(ItemIds[item], write);
|
||||
write++;
|
||||
});
|
||||
return buf;
|
||||
}
|
||||
// public fromBuffer(buf: Buffer): number {
|
||||
// let read = 0;
|
||||
// const size = buf.readUInt16LE();
|
||||
// read+=2;
|
||||
// const stacks: ItemStack[] = [];
|
||||
// for(let i=0;i<size;i++){
|
||||
// const count = buf.readInt16LE(read);
|
||||
// read+=2;
|
||||
// const id = buf.readInt8(read);
|
||||
// read++;
|
||||
// stacks.push({item: idToItemData(id).item, count, equipped: false});
|
||||
// }
|
||||
// this.stacks = stacks;
|
||||
// return read;
|
||||
// }
|
||||
// public toBuffer(): Buffer {
|
||||
// const buf: Buffer = Buffer.alloc(3*this.stacks.length+3);
|
||||
// let write = 0;
|
||||
// buf.writeInt8(CommandInitialize.Op);
|
||||
// write++;
|
||||
// buf.writeInt16LE(this.stacks.length, write);
|
||||
// write+=2;
|
||||
// this.stacks.forEach(({item,count})=>{
|
||||
// buf.writeInt16LE(count&0xffff, write);
|
||||
// write+=2;
|
||||
// buf.writeInt8(itemToItemData(item).id, write);
|
||||
// write++;
|
||||
// });
|
||||
// return buf;
|
||||
// }
|
||||
|
||||
public execute(inv: Inventory): void {
|
||||
inv.init(this.stacks);
|
||||
|
@ -93,16 +79,16 @@ export class CommandBreakSlots implements Command {
|
|||
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 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);
|
||||
}
|
||||
|
@ -113,14 +99,14 @@ export class CommandBreakSlots implements Command {
|
|||
|
||||
export class CommandSave implements Command {
|
||||
static Op = 0x3;
|
||||
public fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
public toBuffer(): Buffer {
|
||||
const buf: Buffer = Buffer.alloc(1);
|
||||
buf.writeInt8(CommandSave.Op);
|
||||
return buf;
|
||||
}
|
||||
// public fromBuffer(_buf: Buffer): number {
|
||||
// return 0;
|
||||
// }
|
||||
// public toBuffer(): Buffer {
|
||||
// const buf: Buffer = Buffer.alloc(1);
|
||||
// buf.writeInt8(CommandSave.Op);
|
||||
// return buf;
|
||||
// }
|
||||
public execute(inv: Inventory): void {
|
||||
inv.save();
|
||||
}
|
||||
|
@ -131,14 +117,14 @@ export class CommandSave implements Command {
|
|||
|
||||
export class CommandReload implements Command {
|
||||
static Op = 0x4;
|
||||
public fromBuffer(_buf: Buffer): number {
|
||||
return 0;
|
||||
}
|
||||
public toBuffer(): Buffer {
|
||||
const buf: Buffer = Buffer.alloc(1);
|
||||
buf.writeInt8(CommandReload.Op);
|
||||
return buf;
|
||||
}
|
||||
// public 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();
|
||||
}
|
||||
|
@ -149,14 +135,14 @@ export class CommandReload implements Command {
|
|||
|
||||
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 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();
|
||||
}
|
||||
|
@ -167,14 +153,14 @@ export class CommandSortKey implements Command {
|
|||
|
||||
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 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();
|
||||
}
|
||||
|
@ -201,48 +187,20 @@ export class CommandRemoveMaterial implements Command {
|
|||
private count: number;
|
||||
private item: Item;
|
||||
private slot: number;
|
||||
constructor(verb: string, count: number, item: Item, 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;
|
||||
}
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
let read = 0;
|
||||
this.count = buf.readInt16LE(read);
|
||||
read+=2;
|
||||
const id = buf.readInt8(read);
|
||||
read+=1;
|
||||
for(const item in ItemIds){
|
||||
if(ItemIds[item as Item] === id){
|
||||
this.item = item as Item;
|
||||
}
|
||||
}
|
||||
this.slot = buf.readInt16LE(read);
|
||||
read+=2;
|
||||
this.verb = buf.readInt8(read);
|
||||
read++;
|
||||
return read;
|
||||
}
|
||||
public toBuffer(): Buffer {
|
||||
const buf: Buffer = Buffer.alloc(1+2+1+2+1);
|
||||
let write = 0;
|
||||
buf.writeInt8(CommandRemoveMaterial.Op);
|
||||
write++;
|
||||
buf.writeInt16LE(this.count, write);
|
||||
write+=2;
|
||||
buf.writeInt8(ItemIds[this.item], write);
|
||||
write++;
|
||||
buf.writeInt16LE(this.slot, write);
|
||||
write+=2;
|
||||
buf.writeInt8(this.verb, write);
|
||||
return buf;
|
||||
this.noSlot = noSlot;
|
||||
}
|
||||
public execute(inv: Inventory): void {
|
||||
inv.remove(this.item, this.count, this.slot);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return `${Verbs[this.verb]} ${this.count} ${this.item} From Slot ${this.slot+1}`;
|
||||
const slotString = this.noSlot ? "" : ` From Slot ${this.slot+1}`
|
||||
return `${Verbs[this.verb]} ${this.count} ${this.item}${slotString}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,43 +209,19 @@ export class CommandRemoveUnstackableMaterial implements Command {
|
|||
private verb: number;
|
||||
private item: Item;
|
||||
private slot: number;
|
||||
constructor(verb: string,item: Item, 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;
|
||||
}
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
let read = 0;
|
||||
const id = buf.readInt8(read);
|
||||
read+=1;
|
||||
for(const item in ItemIds){
|
||||
if(ItemIds[item as Item] === id){
|
||||
this.item = item as Item;
|
||||
}
|
||||
}
|
||||
this.slot = 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(CommandRemoveUnstackableMaterial.Op);
|
||||
write++;
|
||||
buf.writeInt8(ItemIds[this.item], write);
|
||||
write++;
|
||||
buf.writeInt16LE(this.slot, write);
|
||||
write+=2;
|
||||
buf.writeInt8(this.verb, write);
|
||||
return buf;
|
||||
this.noSlot = noSlot;
|
||||
}
|
||||
public execute(inv: Inventory): void {
|
||||
inv.remove(this.item, 1, this.slot);
|
||||
}
|
||||
public getDisplayString(): string {
|
||||
return `${Verbs[this.verb]} ${this.item} From Slot ${this.slot+1}`;
|
||||
const slotString = this.noSlot ? "" : ` From Slot ${this.slot+1}`
|
||||
return `${Verbs[this.verb]} ${this.item}${slotString}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -301,33 +235,30 @@ export class CommandAddMaterial implements Command {
|
|||
this.count = count;
|
||||
this.item = item;
|
||||
}
|
||||
public fromBuffer(buf: Buffer): number {
|
||||
let read = 0;
|
||||
const id = buf.readInt8(read);
|
||||
read+=1;
|
||||
for(const item in ItemIds){
|
||||
if(ItemIds[item as Item] === id){
|
||||
this.item = item as 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(ItemIds[this.item], write);
|
||||
write++;
|
||||
buf.writeInt16LE(this.count, write);
|
||||
write+=2;
|
||||
buf.writeInt8(this.verb, write);
|
||||
return buf;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
@ -335,3 +266,60 @@ export class CommandAddMaterial implements Command {
|
|||
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}`;
|
||||
}
|
||||
}
|
|
@ -1,30 +1,34 @@
|
|||
import { itemToType, getKeyItemSortOrder, shouldIgnoreOnReload, getMaterialSortOrder, Item, isStackable } from "./Item";
|
||||
import { ItemStack, ItemType } from "./ItemStack";
|
||||
import { Item, ItemStack, itemToItemData, ItemType, ItemTypes } from "./Item";
|
||||
import { Slots } from "./Slots";
|
||||
|
||||
|
||||
|
||||
export class Inventory {
|
||||
private slots: ItemStack[] = [];
|
||||
private savedSlots: ItemStack[] = [];
|
||||
private slots: Slots = new Slots([]);
|
||||
private savedSlots: Slots = new Slots([]);
|
||||
private numBroken = 0;
|
||||
private isInitialSort = false;
|
||||
private isAltered = true;
|
||||
private isSaveAltered = true;
|
||||
private inaccurate = false;
|
||||
public clone(): Inventory {
|
||||
public deepClone(): Inventory {
|
||||
const other = new Inventory();
|
||||
other.slots = [...this.slots.map(stack=>({...stack}))];
|
||||
other.savedSlots = [...this.savedSlots.map(stack=>({...stack}))];
|
||||
other.slots = this.slots.deepClone();
|
||||
other.savedSlots = this.savedSlots.deepClone();
|
||||
other.numBroken = this.numBroken;
|
||||
other.isInitialSort = this.isInitialSort;
|
||||
other.isAltered = this.isAltered;
|
||||
other.isSaveAltered = this.isSaveAltered;
|
||||
other.inaccurate = this.inaccurate;
|
||||
return other;
|
||||
}
|
||||
|
||||
public getSlots(): ItemStack[] {
|
||||
public getSlots(): Slots {
|
||||
return this.slots;
|
||||
}
|
||||
|
||||
public getSavedSlots(): Slots {
|
||||
return this.savedSlots;
|
||||
}
|
||||
|
||||
public getNumBroken(): number {
|
||||
return this.numBroken;
|
||||
}
|
||||
|
@ -34,12 +38,13 @@ export class Inventory {
|
|||
}
|
||||
|
||||
public init(stacks: ItemStack[]) {
|
||||
this.savedSlots = [...stacks.map((stack)=>({...stack}))];
|
||||
this.slots = [...stacks.map((stack)=>({...stack}))];
|
||||
this.slots = new Slots([]);
|
||||
stacks.forEach(s=>{
|
||||
this.slots.add(s.item, s.count)
|
||||
});
|
||||
this.numBroken = 0;
|
||||
this.isInitialSort = false;
|
||||
this.isAltered = true;
|
||||
this.isSaveAltered = true;
|
||||
this.inaccurate = false;
|
||||
}
|
||||
|
||||
|
@ -48,130 +53,127 @@ export class Inventory {
|
|||
}
|
||||
|
||||
public save() {
|
||||
this.isSaveAltered = this.isAltered;
|
||||
this.savedSlots = [...this.slots];
|
||||
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() {
|
||||
if(!this.isSaveAltered){
|
||||
this.inaccurate = true;
|
||||
}
|
||||
|
||||
// get things to dupe
|
||||
const dupeMap: {[k in ItemType]: ItemStack[]} = {
|
||||
[ItemType.Material]: [],
|
||||
[ItemType.Meal]: [],
|
||||
[ItemType.Key]: []
|
||||
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[i];
|
||||
if(!shouldIgnoreOnReload(stack.item)){
|
||||
dupeMap[itemToType(stack.item)].push(stack);
|
||||
}
|
||||
const stack = this.slots.get(i);
|
||||
const itemData = itemToItemData(stack.item);
|
||||
dupeMap[itemData.type].addStackCopy(stack);
|
||||
}
|
||||
// get materials, food, and key items
|
||||
const materials = this.savedSlots.filter(stack=>itemToType(stack.item)===ItemType.Material);
|
||||
const meals = this.savedSlots.filter(stack=>itemToType(stack.item)===ItemType.Meal);
|
||||
const keyItems = this.savedSlots.filter(stack=>itemToType(stack.item)===ItemType.Key);
|
||||
// apply dupe
|
||||
this.slots = [];
|
||||
// duped materials go to the left
|
||||
this.slots.push(...dupeMap[ItemType.Material].map(stack=>({...stack})));
|
||||
this.slots.push(...materials.map(stack=>({...stack})));
|
||||
this.slots.push(...dupeMap[ItemType.Meal].map(stack=>({...stack})));
|
||||
this.slots.push(...meals.map(stack=>({...stack})));
|
||||
// key items to the right
|
||||
this.slots.push(...keyItems.map(stack=>({...stack})));
|
||||
this.slots.push(...dupeMap[ItemType.Key].map(stack=>({...stack})));
|
||||
//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;
|
||||
this.isSaveAltered = false;
|
||||
}
|
||||
|
||||
public sortKey() {
|
||||
const nonKeyItems = this.slots.filter(stack=>itemToType(stack.item)!==ItemType.Key);
|
||||
const keyItems = this.slots.filter(stack=>itemToType(stack.item)===ItemType.Key);
|
||||
keyItems.sort((a,b)=>{
|
||||
return getKeyItemSortOrder(a.item) - getKeyItemSortOrder(b.item);
|
||||
});
|
||||
this.slots = [...nonKeyItems, ...keyItems];
|
||||
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 nonMaterial = this.slots.filter(stack=>itemToType(stack.item)!==ItemType.Material);
|
||||
const materials = this.slots.filter(stack=>itemToType(stack.item)===ItemType.Material);
|
||||
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 - nonMaterial.length);
|
||||
const sortPart = materials.splice(0, materials.length-brokenSlots);
|
||||
sortPart.sort((a,b)=>{
|
||||
return getMaterialSortOrder(a.item) - getMaterialSortOrder(b.item);
|
||||
});
|
||||
this.slots = [...sortPart, ...materials, ...nonMaterial];
|
||||
this.isInitialSort = false;
|
||||
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((a,b)=>{
|
||||
return getMaterialSortOrder(a.item) - getMaterialSortOrder(b.item);
|
||||
});
|
||||
this.slots = [...materials, ...nonMaterial];
|
||||
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) {
|
||||
let s = 0;
|
||||
for(let i = 0; i<this.slots.length;i++){
|
||||
if(this.slots[i].item === item){
|
||||
if(s<slot){
|
||||
s++;
|
||||
}else{
|
||||
this.slots[i].count-=count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.slots = this.slots.filter(({count})=>count>0);
|
||||
this.slots.remove(item, count, slot);
|
||||
this.isAltered=true;
|
||||
}
|
||||
|
||||
public add(item: Item, count: number) {
|
||||
let added = false;
|
||||
if(isStackable(item)){
|
||||
for(let i = 0; i<this.slots.length;i++){
|
||||
if(this.slots[i].item === item){
|
||||
this.slots[i].count+=count;
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!added){
|
||||
// add to the correct type
|
||||
switch(itemToType(item)){
|
||||
case ItemType.Material: {
|
||||
const materials = this.slots.filter(stack=>itemToType(stack.item)===ItemType.Material);
|
||||
this.slots.splice(materials.length, 0, {
|
||||
item, count
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ItemType.Meal: {
|
||||
const keyItems = this.slots.filter(stack=>itemToType(stack.item)===ItemType.Key);
|
||||
this.slots.splice(-keyItems.length, 0, {
|
||||
item, count
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ItemType.Key: {
|
||||
this.slots.push({
|
||||
item, count
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
238
src/core/Item.ts
|
@ -1,4 +1,58 @@
|
|||
import { ItemType } from "./ItemStack";
|
||||
import ImageSlate from "assets/img/Slate.png";
|
||||
import ImageGlider from "assets/img/Glider.png";
|
||||
import ImageSpiritOrb from "assets/img/SpiritOrb.png";
|
||||
import ImageLotus from "assets/img/Lotus.png";
|
||||
import ImageSilentPrincess from "assets/img/SilentPrincess.png";
|
||||
import ImageHoney from "assets/img/Lotus.png";
|
||||
import ImageAcorn from "assets/img/SilentPrincess.png";
|
||||
import ImageFaroshScale from "assets/img/FaroshScale.png";
|
||||
import ImageFaroshClaw from "assets/img/FaroshClaw.png";
|
||||
import ImageFaroshHorn from "assets/img/FaroshHorn.png";
|
||||
import ImageHeartyBass from "assets/img/HeartyBass.png";
|
||||
import ImageBeetle from "assets/img/Beetle.png";
|
||||
import ImageOpal from "assets/img/Opal.png";
|
||||
import ImageDiamond from "assets/img/Diamond.png";
|
||||
import ImageTail from "assets/img/Tail.png";
|
||||
import ImageSpring from "assets/img/Spring.png";
|
||||
import ImageShaft from "assets/img/Shaft.png";
|
||||
import ImageCore from "assets/img/Core.png";
|
||||
import ImageWood from "assets/img/Wood.png";
|
||||
import ImageSpeedFood from "assets/img/SpeedFood.png";
|
||||
import ImageAxe from "assets/img/Axe.png";
|
||||
import ImageBow from "assets/img/ForestDwellerBow.png";
|
||||
import ImageArrow from "assets/img/NormalArrow.png";
|
||||
import ImageFireArrow from "assets/img/FireArrow.png";
|
||||
import ImageIceArrow from "assets/img/IceArrow.png";
|
||||
import ImageShockArrow from "assets/img/ShockArrow.png";
|
||||
import ImageBombArrow from "assets/img/BombArrow.png";
|
||||
import ImageAncientArrow from "assets/img/AncientArrow.png";
|
||||
import ImageShield from "assets/img/PotLid.png";
|
||||
|
||||
export enum ItemType {
|
||||
Weapon = 0,
|
||||
Bow = 1,
|
||||
Arrow = 2,
|
||||
Shield = 3,
|
||||
Material = 4,
|
||||
Meal = 5,
|
||||
Key = 6
|
||||
}
|
||||
|
||||
export const ItemTypes = [
|
||||
ItemType.Weapon,
|
||||
ItemType.Bow,
|
||||
ItemType.Arrow,
|
||||
ItemType.Shield,
|
||||
ItemType.Material,
|
||||
ItemType.Meal,
|
||||
ItemType.Key
|
||||
]
|
||||
|
||||
export type ItemStack = {
|
||||
item: Item,
|
||||
count: number,
|
||||
equipped: boolean
|
||||
}
|
||||
|
||||
export enum Item {
|
||||
Slate = "Slate",
|
||||
|
@ -22,90 +76,110 @@ export enum Item {
|
|||
Core = "Core",
|
||||
Wood = "Wood",
|
||||
|
||||
SpeedFood = "SpeedFood"
|
||||
SpeedFood = "SpeedFood",
|
||||
Weapon = "Weapon",
|
||||
Bow = "Bow",
|
||||
NormalArrow = "NormalArrow",
|
||||
FireArrow = "FireArrow",
|
||||
IceArrow = "IceArrow",
|
||||
ShockArrow = "ShockArrow",
|
||||
BombArrow = "BombArrow",
|
||||
AncientArrow = "AncientArrow",
|
||||
Shield = "Shield"
|
||||
}
|
||||
|
||||
export const ItemIds = {
|
||||
/* Do not change the ID once created. Otherwise you would break existing codes */
|
||||
[Item.Slate]: 0x00,
|
||||
[Item.Glider]: 0x01,
|
||||
[Item.SpiritOrb]: 0x02,
|
||||
type ItemData = {
|
||||
item: Item,
|
||||
image: string,
|
||||
id: number,
|
||||
type: ItemType,
|
||||
repeatable: boolean,
|
||||
stackable: boolean,
|
||||
sortOrder: number,
|
||||
}
|
||||
|
||||
[Item.Diamond]: 0x10,
|
||||
[Item.Lotus]: 0x11,
|
||||
[Item.SilentPrincess]: 0x12,
|
||||
[Item.Honey]: 0x13,
|
||||
[Item.Acorn]: 0x14,
|
||||
[Item.FaroshScale]: 0x15,
|
||||
[Item.FaroshClaw]: 0x16,
|
||||
[Item.FaroshHorn]: 0x17,
|
||||
[Item.HeartyBass]: 0x18,
|
||||
[Item.Beetle]: 0x19,
|
||||
[Item.Opal]: 0x1a,
|
||||
[Item.Tail]: 0x1b,
|
||||
[Item.Spring]: 0x1c,
|
||||
[Item.Shaft]: 0x1d,
|
||||
[Item.Core]: 0x1e,
|
||||
[Item.Wood]: 0x1f,
|
||||
|
||||
[Item.SpeedFood]: 0x40,
|
||||
const IdToData: {[id: number]: ItemData} = {};
|
||||
const ItemToData: {[k in Item]?: ItemData} = {};
|
||||
const TypeToCount = {
|
||||
[ItemType.Weapon]: 0,
|
||||
[ItemType.Bow]: 0,
|
||||
[ItemType.Arrow]: 0,
|
||||
[ItemType.Shield]: 0,
|
||||
[ItemType.Material]: 0,
|
||||
[ItemType.Key]: 0,
|
||||
[ItemType.Meal]: 0,
|
||||
};
|
||||
const register = (id: number, item: Item, type: ItemType, image: string, options?: Partial<ItemData>) => {
|
||||
const sortOrder = TypeToCount[type];
|
||||
TypeToCount[type]++;
|
||||
const data: ItemData = {
|
||||
item,
|
||||
image,
|
||||
id,
|
||||
type,
|
||||
repeatable: true,
|
||||
stackable: true,
|
||||
sortOrder,
|
||||
...(options||{})
|
||||
};
|
||||
IdToData[id] = data;
|
||||
ItemToData[item] = data;
|
||||
}
|
||||
/* Do not change the ID once created. Otherwise you would break existing codes */
|
||||
register(0x00, Item.Slate, ItemType.Key, ImageSlate, {
|
||||
repeatable: false,
|
||||
stackable: false
|
||||
});
|
||||
register(0x01, Item.Glider, ItemType.Key, ImageGlider, {
|
||||
repeatable: false,
|
||||
stackable: false
|
||||
});
|
||||
register(0x02, Item.SpiritOrb, ItemType.Key, ImageSpiritOrb);
|
||||
|
||||
export const itemToType = (item: Item): ItemType => {
|
||||
if (item === Item.Slate || item === Item.Glider || item === Item.SpiritOrb){
|
||||
return ItemType.Key;
|
||||
register(0x11, Item.Lotus, ItemType.Material, ImageLotus);
|
||||
register(0x12, Item.SilentPrincess, ItemType.Material, ImageSilentPrincess);
|
||||
register(0x13, Item.Honey, ItemType.Material, ImageHoney);
|
||||
register(0x14, Item.Acorn, ItemType.Material, ImageAcorn);
|
||||
register(0x15, Item.FaroshScale, ItemType.Material, ImageFaroshScale);
|
||||
register(0x16, Item.FaroshClaw, ItemType.Material, ImageFaroshClaw);
|
||||
register(0x17, Item.FaroshHorn, ItemType.Material, ImageFaroshHorn);
|
||||
register(0x18, Item.HeartyBass, ItemType.Material, ImageHeartyBass);
|
||||
register(0x19, Item.Beetle, ItemType.Material, ImageBeetle);
|
||||
register(0x1a, Item.Opal, ItemType.Material, ImageOpal);
|
||||
register(0x10, Item.Diamond, ItemType.Material, ImageDiamond);
|
||||
register(0x1b, Item.Tail, ItemType.Material, ImageTail);
|
||||
register(0x1c, Item.Spring, ItemType.Material, ImageSpring);
|
||||
register(0x1d, Item.Shaft, ItemType.Material, ImageShaft);
|
||||
register(0x1e, Item.Core, ItemType.Material, ImageCore);
|
||||
register(0x1f, Item.Wood, ItemType.Material, ImageWood);
|
||||
|
||||
register(0x40, Item.SpeedFood, ItemType.Meal, ImageSpeedFood, {
|
||||
stackable: false
|
||||
});
|
||||
|
||||
register(0x50, Item.Weapon, ItemType.Weapon, ImageAxe, {
|
||||
stackable: false
|
||||
});
|
||||
|
||||
register(0x60, Item.Bow, ItemType.Bow, ImageBow, {
|
||||
stackable: false
|
||||
});
|
||||
register(0x70, Item.NormalArrow, ItemType.Arrow, ImageArrow);
|
||||
register(0x71, Item.FireArrow, ItemType.Arrow, ImageFireArrow);
|
||||
register(0x72, Item.IceArrow, ItemType.Arrow, ImageIceArrow);
|
||||
register(0x73, Item.ShockArrow, ItemType.Arrow, ImageShockArrow);
|
||||
register(0x74, Item.BombArrow, ItemType.Arrow, ImageBombArrow);
|
||||
register(0x75, Item.AncientArrow, ItemType.Arrow, ImageAncientArrow);
|
||||
register(0x60, Item.Shield, ItemType.Shield, ImageShield, {
|
||||
stackable: false
|
||||
});
|
||||
|
||||
export const idToItemData = (id: number): ItemData => IdToData[id];
|
||||
export const itemToItemData = (item: Item): ItemData => ItemToData[item] as ItemData;
|
||||
export const itemToArrowType = (item: Item): string => {
|
||||
if(itemToItemData(item).type === ItemType.Arrow){
|
||||
const str = `${item}`;
|
||||
return str.substring(0,str.length-5);
|
||||
}
|
||||
if (item === Item.SpeedFood) {
|
||||
return ItemType.Meal;
|
||||
}
|
||||
return ItemType.Material;
|
||||
};
|
||||
|
||||
export const shouldIgnoreOnReload = (item: Item): boolean => {
|
||||
return item === Item.Slate || item === Item.Glider;
|
||||
};
|
||||
|
||||
export const isStackable = (item: Item): boolean => {
|
||||
return item !==Item.Slate && item !== Item.Glider && item !== Item.SpeedFood;
|
||||
};
|
||||
|
||||
const KeyItemSortOrderMap = (()=>{
|
||||
const map: {[k in Item]?: number} = {};
|
||||
[
|
||||
Item.Slate,
|
||||
Item.Glider,
|
||||
Item.SpiritOrb
|
||||
].forEach((item, i)=>map[item] = i);
|
||||
return map;
|
||||
})();
|
||||
|
||||
export const getKeyItemSortOrder = (item: Item): number => {
|
||||
return KeyItemSortOrderMap[item] || -1;
|
||||
};
|
||||
|
||||
const MaterialSortOrderMap = (()=>{
|
||||
const map: {[k in Item]?: number} = {};
|
||||
[
|
||||
Item.Lotus,
|
||||
Item.SilentPrincess,
|
||||
Item.Honey,
|
||||
Item.Acorn,
|
||||
Item.FaroshScale,
|
||||
Item.FaroshClaw,
|
||||
Item.FaroshHorn,
|
||||
Item.HeartyBass,
|
||||
Item.Beetle,
|
||||
Item.Opal,
|
||||
Item.Diamond,
|
||||
Item.Tail,
|
||||
Item.Spring,
|
||||
Item.Shaft,
|
||||
Item.Core,
|
||||
Item.Wood,
|
||||
].forEach((item, i)=>map[item] = i);
|
||||
return map;
|
||||
})();
|
||||
|
||||
export const getMaterialSortOrder = (item: Item): number => {
|
||||
return MaterialSortOrderMap[item] || -1;
|
||||
};
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { Item } from "./Item";
|
||||
|
||||
export enum ItemType {
|
||||
Material,
|
||||
Meal,
|
||||
Key
|
||||
}
|
||||
|
||||
export type ItemStack = {
|
||||
item: Item,
|
||||
count: number,
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import { Command, CommandAddMaterial, CommandBreakSlots, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSortKey, CommandSortMaterial } from "./Command";
|
||||
import { Item } from "./Item";
|
||||
import { ItemStack } from "./ItemStack";
|
||||
import { Command, CommandAddMaterial, CommandBreakSlots, CommandEquip, CommandEquipArrow, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSortKey, CommandSortMaterial, CommandUnequip } from "./Command";
|
||||
import { Item, ItemStack } from "./Item";
|
||||
|
||||
export const parseCommand = (cmdString: string): Command | undefined => {
|
||||
const tokens = cmdString.split(" ").filter(i=>i);
|
||||
|
@ -18,7 +17,7 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
|||
const item = tokens[i+1];
|
||||
if (item in Item){
|
||||
stacks.push({
|
||||
item: Item[item as keyof typeof Item], count
|
||||
item: Item[item as keyof typeof Item], count, equipped:false
|
||||
});
|
||||
}else{
|
||||
return undefined;
|
||||
|
@ -54,7 +53,15 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
|||
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);
|
||||
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")){
|
||||
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;
|
||||
}
|
||||
|
@ -63,7 +70,14 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
|||
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);
|
||||
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;
|
||||
}
|
||||
|
@ -76,6 +90,54 @@ export const parseCommand = (cmdString: string): Command | undefined => {
|
|||
}
|
||||
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;
|
||||
};
|
||||
|
|
190
src/core/Slots.ts
Normal file
|
@ -0,0 +1,190 @@
|
|||
import { Item, ItemStack, itemToItemData, ItemType } from "./Item";
|
||||
|
||||
export class Slots {
|
||||
private internalSlots: ItemStack[] = [];
|
||||
constructor(slots: ItemStack[]) {
|
||||
this.internalSlots = slots;
|
||||
}
|
||||
public getSlotsRef(): ItemStack[] {
|
||||
return this.internalSlots;
|
||||
}
|
||||
public deepClone(): Slots {
|
||||
return new Slots(this.internalSlots.map(s=>({...s})));
|
||||
}
|
||||
public get length(): number {
|
||||
return this.internalSlots.length;
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
if(s<slot){
|
||||
s++;
|
||||
}else{
|
||||
this.internalSlots[i].count-=count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.internalSlots = this.internalSlots.filter(({count})=>count>0);
|
||||
}
|
||||
|
||||
public add(item: Item, count: number) {
|
||||
let added = false;
|
||||
const data = itemToItemData(item);
|
||||
if(data.stackable){
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
this.internalSlots[i].count+=count;
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!added){
|
||||
const after = this.removeFromEnd(this.getAfterType(data.type).length);
|
||||
if(data.stackable){
|
||||
if(data.type===ItemType.Arrow){
|
||||
// if currently equipped arrow == 0. new arrows are equiped
|
||||
const shouldEquipNew = this.internalSlots.filter(s=>{
|
||||
const sData = itemToItemData(s.item);
|
||||
return sData.type === data.type && s.equipped && s.count > 0;
|
||||
}).length === 0;
|
||||
this.addStack({item,count,equipped:shouldEquipNew});
|
||||
}else{
|
||||
this.addStack({item,count,equipped:false});
|
||||
}
|
||||
|
||||
}else{
|
||||
if(data.type===ItemType.Weapon || data.type===ItemType.Bow || data.type===ItemType.Shield){
|
||||
//Check equip
|
||||
const shouldEquipNew = this.internalSlots.filter(s=>{
|
||||
const sData = itemToItemData(s.item);
|
||||
return sData.type === data.type && s.equipped;
|
||||
}).length === 0;
|
||||
this.addStack({item,count:1,equipped: shouldEquipNew});
|
||||
for(let i=1;i<count;i++){
|
||||
this.addStack({item,count:1,equipped: false});
|
||||
|
||||
}
|
||||
}else{
|
||||
for(let i=0;i<count;i++){
|
||||
this.addStack({item,count:1,equipped: false});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.addSlotsToEnd(after);
|
||||
}
|
||||
}
|
||||
// this is for both equipments and arrows
|
||||
public equip(item: Item, slot: number) {
|
||||
let s = 0;
|
||||
const type = itemToItemData(item).type;
|
||||
const filtered = this.internalSlots.filter(s=>itemToItemData(s.item).type === type);
|
||||
for(let i = 0; i<filtered.length;i++){
|
||||
filtered[i].equipped=false;
|
||||
if(filtered[i].item === item){
|
||||
if (s===slot){
|
||||
filtered[i].equipped=true;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
public unequip(item:Item, slot: number) {
|
||||
let s = 0;
|
||||
const type = itemToItemData(item).type;
|
||||
if (type===ItemType.Arrow){
|
||||
return; // cannot unequip arrow
|
||||
}
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
if(slot < 0){
|
||||
if(this.internalSlots[i].equipped){
|
||||
this.internalSlots[i].equipped=false;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
if(s<slot){
|
||||
s++;
|
||||
}else{
|
||||
this.internalSlots[i].equipped=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Difference between shoot and remove:
|
||||
// 1. can only be from first (leftmost) slot
|
||||
// 2. empty slots not removed
|
||||
public shoot(item: Item, count: number) {
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].item === item){
|
||||
this.internalSlots[i].count-=count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sortArrows() {
|
||||
const after = this.removeFromEnd(this.getAfterType(ItemType.Arrow).length);
|
||||
const arrows = this.removeFromEnd(this.getByType(ItemType.Arrow).length);
|
||||
arrows.sort();
|
||||
this.addSlotsToEnd(arrows);
|
||||
this.addSlotsToEnd(after);
|
||||
}
|
||||
|
||||
public getFirstEquippedSlotIndex(type: ItemType): number {
|
||||
for(let i = 0; i<this.internalSlots.length;i++){
|
||||
if(this.internalSlots[i].equipped){
|
||||
const data = itemToItemData(this.internalSlots[i].item);
|
||||
if(data.type === type){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -1,65 +1,66 @@
|
|||
import { Command, CommandAddMaterial, CommandBreakSlots, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSortKey, CommandSortMaterial } from "./Command";
|
||||
import { Item } from "./Item";
|
||||
export {};
|
||||
// import { Command, CommandAddMaterial, CommandBreakSlots, CommandInitialize, CommandNothing, CommandReload, CommandRemoveMaterial, CommandRemoveUnstackableMaterial, CommandSave, CommandSortKey, CommandSortMaterial } from "./Command";
|
||||
// import { Item } from "./Item";
|
||||
|
||||
const Buffer = require("buffer/").Buffer; /* eslint-disable-line @typescript-eslint/no-var-requires*/
|
||||
// const Buffer = require("buffer/").Buffer; /* eslint-disable-line @typescript-eslint/no-var-requires*/
|
||||
|
||||
export const serializeCommands = (commands: Command[]): string => {
|
||||
const sizeBuf: Buffer = Buffer.alloc(4);
|
||||
sizeBuf.writeInt32LE(commands.length);
|
||||
// export const serializeCommands = (commands: Command[]): string => {
|
||||
// const sizeBuf: Buffer = Buffer.alloc(4);
|
||||
// sizeBuf.writeInt32LE(commands.length);
|
||||
|
||||
const commandBuffers = commands.map(c=>c.toBuffer());
|
||||
const allBufs = Buffer.concat([sizeBuf, ...commandBuffers]);
|
||||
// const commandBuffers = commands.map(c=>c.toBuffer());
|
||||
// const allBufs = Buffer.concat([sizeBuf, ...commandBuffers]);
|
||||
|
||||
return allBufs.toString("base64");
|
||||
};
|
||||
// return allBufs.toString("base64");
|
||||
// };
|
||||
|
||||
export const deserialzeCommands = (base64str: string): Command[] => {
|
||||
const buf: Buffer = Buffer.from(base64str, "base64");
|
||||
const size = buf.readInt32LE();
|
||||
let off = 4;
|
||||
const commands: Command[] = [];
|
||||
for(let i=0;i<size;i++){
|
||||
const op = buf.readUInt8(off);
|
||||
off++;
|
||||
let command: Command | undefined = undefined;
|
||||
switch(op){
|
||||
case CommandNothing.Op:
|
||||
command = new CommandNothing();
|
||||
break;
|
||||
case CommandInitialize.Op:
|
||||
command = new CommandInitialize([]);
|
||||
break;
|
||||
case CommandBreakSlots.Op:
|
||||
command = new CommandBreakSlots(0);
|
||||
break;
|
||||
case CommandSave.Op:
|
||||
command = new CommandSave();
|
||||
break;
|
||||
case CommandReload.Op:
|
||||
command = new CommandReload();
|
||||
break;
|
||||
case CommandSortKey.Op:
|
||||
command = new CommandSortKey();
|
||||
break;
|
||||
case CommandSortMaterial.Op:
|
||||
command = new CommandSortMaterial();
|
||||
break;
|
||||
case CommandRemoveMaterial.Op:
|
||||
command = new CommandRemoveMaterial("",0,Item.Slate,0);
|
||||
break;
|
||||
case CommandRemoveUnstackableMaterial.Op:
|
||||
command = new CommandRemoveUnstackableMaterial("", Item.Slate, 0);
|
||||
break;
|
||||
case CommandAddMaterial.Op:
|
||||
command = new CommandAddMaterial("",0,Item.Slate);
|
||||
break;
|
||||
}
|
||||
if(command){
|
||||
off += command.fromBuffer(buf.slice(off));
|
||||
commands.push(command);
|
||||
}else{
|
||||
console.error("invalid opcode: "+op);
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
};
|
||||
// export const deserialzeCommands = (base64str: string): Command[] => {
|
||||
// const buf: Buffer = Buffer.from(base64str, "base64");
|
||||
// const size = buf.readInt32LE();
|
||||
// let off = 4;
|
||||
// const commands: Command[] = [];
|
||||
// for(let i=0;i<size;i++){
|
||||
// const op = buf.readUInt8(off);
|
||||
// off++;
|
||||
// let command: Command | undefined = undefined;
|
||||
// switch(op){
|
||||
// case CommandNothing.Op:
|
||||
// command = new CommandNothing();
|
||||
// break;
|
||||
// case CommandInitialize.Op:
|
||||
// command = new CommandInitialize([]);
|
||||
// break;
|
||||
// case CommandBreakSlots.Op:
|
||||
// command = new CommandBreakSlots(0);
|
||||
// break;
|
||||
// case CommandSave.Op:
|
||||
// command = new CommandSave();
|
||||
// break;
|
||||
// case CommandReload.Op:
|
||||
// command = new CommandReload();
|
||||
// break;
|
||||
// case CommandSortKey.Op:
|
||||
// command = new CommandSortKey();
|
||||
// break;
|
||||
// case CommandSortMaterial.Op:
|
||||
// command = new CommandSortMaterial();
|
||||
// break;
|
||||
// case CommandRemoveMaterial.Op:
|
||||
// command = new CommandRemoveMaterial("",0,Item.Slate,0);
|
||||
// break;
|
||||
// case CommandRemoveUnstackableMaterial.Op:
|
||||
// command = new CommandRemoveUnstackableMaterial("", Item.Slate, 0);
|
||||
// break;
|
||||
// case CommandAddMaterial.Op:
|
||||
// command = new CommandAddMaterial("",0,Item.Slate);
|
||||
// break;
|
||||
// }
|
||||
// if(command){
|
||||
// off += command.fromBuffer(buf.slice(off));
|
||||
// commands.push(command);
|
||||
// }else{
|
||||
// console.error("invalid opcode: "+op);
|
||||
// }
|
||||
// }
|
||||
// return commands;
|
||||
// };
|
||||
|
|
|
@ -1,47 +1,45 @@
|
|||
import clsx from "clsx";
|
||||
import { ItemList, ItemListProps } from "components/ItemList";
|
||||
import { ItemList, ItemListItemProps, ItemListProps } from "components/ItemList";
|
||||
import { DoubleItemSlot } from "components/ItemSlot";
|
||||
import { Command } from "core/Command";
|
||||
import { itemToType } from "core/Item";
|
||||
import { ItemStack, ItemType } from "core/ItemStack";
|
||||
import { ItemStack, itemToItemData } from "core/Item";
|
||||
import { parseCommand } from "core/Parser";
|
||||
import { Slots } from "core/Slots";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
type DisplayPaneProps = {
|
||||
command: string,
|
||||
displayIndex: number,
|
||||
stacks: ItemStack[],
|
||||
slots: Slots,
|
||||
savedSlots: Slots,
|
||||
numBroken: number,
|
||||
overlaySave: boolean,
|
||||
editCommand: (c: Command)=>void
|
||||
}
|
||||
|
||||
const stacksToItemListProps = (stacks: ItemStack[], numBroken: number): [ItemListProps, ItemListProps, ItemListProps] => {
|
||||
const materials = stacks.filter(stack=>itemToType(stack.item)==ItemType.Material);
|
||||
const meals = stacks.filter(stack=>itemToType(stack.item)==ItemType.Meal);
|
||||
const keyItems = stacks.filter(stack=>itemToType(stack.item)==ItemType.Key);
|
||||
return [
|
||||
{
|
||||
items: stacksToNameAndCount(materials),
|
||||
numBroken: Math.max(0, numBroken - keyItems.length - meals.length )
|
||||
},
|
||||
{
|
||||
items: stacksToNameAndCount(meals),
|
||||
numBroken: Math.max(0, numBroken - keyItems.length)
|
||||
},{
|
||||
items: stacksToNameAndCount(keyItems),
|
||||
numBroken
|
||||
},
|
||||
];
|
||||
const stacksToItemListProps = (slots: Slots, numBroken: number, isSave: boolean): ItemListProps => {
|
||||
return {
|
||||
items: stacksToItemProps(slots.getSlotsRef()),
|
||||
numBroken,
|
||||
isSave,
|
||||
};
|
||||
};
|
||||
|
||||
const stacksToNameAndCount = (stacks: ItemStack[]): ItemListProps["items"] => {
|
||||
return stacks.map(({item, count})=>({name: item, count}));
|
||||
const stacksToItemProps = (stacks: ItemStack[]): ItemListItemProps[] => {
|
||||
return stacks.map(stackToItemProps);
|
||||
};
|
||||
|
||||
export const DisplayPane: React.FC<DisplayPaneProps> = ({command,editCommand,displayIndex, stacks, numBroken})=>{
|
||||
const stackToItemProps = ({item, count, equipped}: ItemStack): ItemListItemProps => {
|
||||
const data = itemToItemData(item);
|
||||
return {image: data.image, count: data.stackable ? count : 0, isEquipped:equipped};
|
||||
}
|
||||
|
||||
export const DisplayPane: React.FC<DisplayPaneProps> = ({command,editCommand,displayIndex, slots, savedSlots, numBroken, overlaySave})=>{
|
||||
const [commandString, setCommandString] = useState<string>("");
|
||||
const [hasError, setHasError] = useState<boolean>(false);
|
||||
const [materialListProps, mealListProps, keyItemListProps] = stacksToItemListProps(stacks, numBroken);
|
||||
const listProps = stacksToItemListProps(slots, numBroken, false);
|
||||
const listSaveProps = stacksToItemListProps(savedSlots, 0, true);
|
||||
useEffect(()=>{
|
||||
if(commandString!==command){
|
||||
setCommandString(command);
|
||||
|
@ -80,51 +78,70 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,editCommand,dis
|
|||
setHasError(true);
|
||||
}
|
||||
}}></input>
|
||||
<button onClick={()=>{
|
||||
alert(`Available Commands:
|
||||
Initialize X Item1 Y Item2 Z Item3 ...
|
||||
Break X Slots - add X broken slots
|
||||
Save
|
||||
Reload
|
||||
Sort Key/Material - sort key items or material
|
||||
Get/Add/Cook/Pickup X ITEM
|
||||
Remove/Drop/Sell X ITEM From Slot Y
|
||||
Remove/Sell/Eat MEAL From Slot X
|
||||
|
||||
Limitations:
|
||||
When you reload without altering inventory, things become weird. It won't be handled correctly and the commands will become red
|
||||
`);
|
||||
}}>Reference</button>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
{overlaySave ?
|
||||
<div style={{
|
||||
borderTop: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
height: "calc( ( 99vh - 60px ))",
|
||||
overflowY: "auto"
|
||||
} }>
|
||||
<div>Save / Current</div>
|
||||
<div>
|
||||
{
|
||||
(()=>{
|
||||
const doubleSlots: JSX.Element[] = [];
|
||||
for(let i=0;i<savedSlots.length && i<slots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot
|
||||
first={{...stackToItemProps(savedSlots.get(i)), isBroken:false, isSave:true}}
|
||||
second={{...stackToItemProps(slots.get(i)), isBroken:i>=slots.length-numBroken, isSave:false}}
|
||||
/>);
|
||||
}
|
||||
if(savedSlots.length>slots.length){
|
||||
for(let i=slots.length;i<savedSlots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot
|
||||
first={{...stackToItemProps(savedSlots.get(i)), isBroken:false, isSave:true}}
|
||||
/>);
|
||||
}
|
||||
}else if(slots.length > savedSlots.length){
|
||||
for(let i=savedSlots.length;i<slots.length;i++){
|
||||
doubleSlots.push(<DoubleItemSlot
|
||||
second={{...stackToItemProps(slots.get(i)), isBroken:i>=slots.length-numBroken, isSave:false}}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
return doubleSlots;
|
||||
})()
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
:<>
|
||||
|
||||
<div style={{
|
||||
borderTop: "1px solid black",
|
||||
borderBottom: "1px solid black",
|
||||
marginBottom: 2,
|
||||
boxSizing: "content-box",
|
||||
height: "calc( ( 99vh - 60px ) / 3)",
|
||||
height: "calc( ( 99vh - 60px ) / 2)",
|
||||
overflowY: "auto"
|
||||
} }>
|
||||
<ItemList {...materialListProps}/>
|
||||
<div>Inventory of (Hard) Save</div>
|
||||
<ItemList {...listSaveProps}/>
|
||||
</div>
|
||||
<div style={{
|
||||
borderTop: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
borderBottom: "1px solid black",
|
||||
marginBottom: 2,
|
||||
height: "calc( ( 99vh - 60px ) / 3)",
|
||||
height: "calc( ( 99vh - 60px ) / 2)",
|
||||
overflowY: "auto"
|
||||
} }>
|
||||
<ItemList {...mealListProps}/>
|
||||
</div>
|
||||
<div style={{
|
||||
borderTop: "1px solid black",
|
||||
boxSizing: "content-box",
|
||||
height: "calc( ( 99vh - 60px ) / 3)",
|
||||
overflowY: "auto"
|
||||
} }>
|
||||
<ItemList {...keyItemListProps}/>
|
||||
<div>Current Inventory</div>
|
||||
<ItemList {...listProps}/>
|
||||
</div>
|
||||
</>}
|
||||
|
||||
|
||||
</div>;
|
||||
};
|
||||
|
|