1
0
Fork 0

ui and inventory corruptin stuff

This commit is contained in:
iTNTPiston 2022-06-20 20:28:09 -07:00
parent 3a92f0fc1d
commit b8ac12086c
44 changed files with 994 additions and 532 deletions

View file

@ -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;
}

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/img/Axe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
src/assets/img/Beetle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/img/Core.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
src/assets/img/Diamond.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/assets/img/Glider.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/img/Honey.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/img/IceArrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/assets/img/Lotus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/img/Opal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/img/PotLid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
src/assets/img/Shaft.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/img/Slate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/img/Spring.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/assets/img/Tail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/img/Wood.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -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);

View file

@ -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}/>;
})
}
</>;

View 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>
)
}

View file

@ -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)"}&nbsp;<br/></span>;
};

View file

@ -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}`;
}
}

View file

@ -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;
}
}

View file

@ -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 "";
}

View file

@ -1,12 +0,0 @@
import { Item } from "./Item";
export enum ItemType {
Material,
Meal,
Key
}
export type ItemStack = {
item: Item,
count: number,
}

View file

@ -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
View 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;
}
}

View file

@ -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;
// };

View file

@ -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>;
};