add eventide; dnp allows multiple; update docs
This commit is contained in:
parent
d9d7fb6a7e
commit
15715e2a11
10 changed files with 246 additions and 159 deletions
19
src/App.tsx
19
src/App.tsx
|
@ -5,7 +5,6 @@ import "./App.css";
|
||||||
import { CommandItem } from "./components/CommandItem";
|
import { CommandItem } from "./components/CommandItem";
|
||||||
|
|
||||||
import { DisplayPane } from "surfaces/DisplayPane";
|
import { DisplayPane } from "surfaces/DisplayPane";
|
||||||
import { saveAs } from "data/FileSaver";
|
|
||||||
import { parseCommand } from "core/Parser";
|
import { parseCommand } from "core/Parser";
|
||||||
import { ItemList } from "components/ItemList";
|
import { ItemList } from "components/ItemList";
|
||||||
import { TitledList } from "components/TitledList";
|
import { TitledList } from "components/TitledList";
|
||||||
|
@ -69,16 +68,25 @@ export const App: React.FC = () => {
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
window.onkeydown=(e)=>{
|
window.onkeydown=(e)=>{
|
||||||
if(e.code==="ArrowDown"){
|
if(e.code==="ArrowDown"){
|
||||||
if(displayIndex===commands.length-1){
|
let nextCommandIndex = displayIndex+1;
|
||||||
|
while(nextCommandIndex<commands.length && !commands[nextCommandIndex].isValid()){
|
||||||
|
nextCommandIndex++;
|
||||||
|
}
|
||||||
|
if(nextCommandIndex===commands.length-1){
|
||||||
const arrCopy = [...commands];
|
const arrCopy = [...commands];
|
||||||
arrCopy.push(new CommandNop(""));
|
arrCopy.push(new CommandNop(""));
|
||||||
setCommands(arrCopy);
|
setCommands(arrCopy);
|
||||||
setDisplayIndex(arrCopy.length-1);
|
setDisplayIndex(arrCopy.length-1);
|
||||||
}else{
|
}else{
|
||||||
setDisplayIndex(Math.min(commands.length-1, displayIndex+1));
|
|
||||||
|
setDisplayIndex(Math.min(commands.length-1, nextCommandIndex));
|
||||||
}
|
}
|
||||||
}else if(e.code==="ArrowUp"){
|
}else if(e.code==="ArrowUp"){
|
||||||
setDisplayIndex(Math.max(0, displayIndex-1));
|
let nextCommandIndex = displayIndex-1;
|
||||||
|
while(nextCommandIndex>=0 && !commands[nextCommandIndex].isValid()){
|
||||||
|
nextCommandIndex--;
|
||||||
|
}
|
||||||
|
setDisplayIndex(Math.max(0, nextCommandIndex));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [commands, displayIndex]);
|
}, [commands, displayIndex]);
|
||||||
|
@ -219,7 +227,6 @@ export const App: React.FC = () => {
|
||||||
setCommands(arrCopy);
|
setCommands(arrCopy);
|
||||||
}}>(new)</CommandItem>
|
}}>(new)</CommandItem>
|
||||||
|
|
||||||
|
|
||||||
</ol>
|
</ol>
|
||||||
</TitledList>
|
</TitledList>
|
||||||
|
|
||||||
|
@ -306,7 +313,7 @@ export const App: React.FC = () => {
|
||||||
commandText={commandText}
|
commandText={commandText}
|
||||||
setCommandText={(value)=>{
|
setCommandText={(value)=>{
|
||||||
if(value !== commandText){
|
if(value !== commandText){
|
||||||
const commands = value.split("\n").map(parseCommand)
|
const commands = value.split("\n").map(parseCommand);
|
||||||
setCommands(commands);
|
setCommands(commands);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -30,7 +30,7 @@ export const CommandItem: React.FC<CommandItemProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
const clickHandler = useCallback((e: React.MouseEvent)=>{
|
const clickHandler = useCallback((e: React.MouseEvent)=>{
|
||||||
onClick(e.clientX, e.clientY)
|
onClick(e.clientX, e.clientY);
|
||||||
}, [onClick]);
|
}, [onClick]);
|
||||||
const contextMenuHandler = useCallback((e: React.MouseEvent)=>{
|
const contextMenuHandler = useCallback((e: React.MouseEvent)=>{
|
||||||
if(onContextMenu){
|
if(onContextMenu){
|
||||||
|
|
|
@ -232,20 +232,21 @@ const joinItemStackString = (initial: string, stacks: ItemStack[]): string => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CommandDaP extends CommandImpl {
|
export class CommandDaP extends CommandImpl {
|
||||||
private count: number;
|
private stacks: ItemStack[];
|
||||||
private item: Item;
|
|
||||||
|
|
||||||
constructor(count: number, item: Item){
|
constructor(stacks: ItemStack[]){
|
||||||
super();
|
super();
|
||||||
this.count = count;
|
this.stacks = stacks;
|
||||||
this.item = item;
|
|
||||||
}
|
}
|
||||||
public execute(state: SimulationState): void {
|
public execute(state: SimulationState): void {
|
||||||
state.remove(this.item, this.count, 0);
|
this.stacks.forEach(({item,count})=>{
|
||||||
state.obtain(this.item, this.count);
|
state.remove(item, count, 0);
|
||||||
|
state.obtain(item, count);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
public getDisplayString(): string {
|
public getDisplayString(): string {
|
||||||
return `D&P ${this.count} ${this.item}`;
|
return joinItemStackString("D&P", this.stacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,6 +329,21 @@ export class CommandSync extends CommandImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CommandEventide extends CommandImpl {
|
||||||
|
private enter: boolean;
|
||||||
|
constructor(enter: boolean){
|
||||||
|
super();
|
||||||
|
this.enter = enter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public execute(state: SimulationState): void {
|
||||||
|
state.setEventide(this.enter);
|
||||||
|
}
|
||||||
|
public getDisplayString(): string {
|
||||||
|
return `${this.enter? "Enter":"Exit"} Eventide`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class CommandNop extends CommandImpl {
|
export class CommandNop extends CommandImpl {
|
||||||
private text: string;
|
private text: string;
|
||||||
constructor(text: string){
|
constructor(text: string){
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
CommandCloseGame,
|
CommandCloseGame,
|
||||||
CommandDaP,
|
CommandDaP,
|
||||||
CommandEquip,
|
CommandEquip,
|
||||||
|
CommandEventide,
|
||||||
CommandInitialize,
|
CommandInitialize,
|
||||||
CommandNop,
|
CommandNop,
|
||||||
CommandReload,
|
CommandReload,
|
||||||
|
@ -124,11 +125,10 @@ export const parseCommand = (cmdString: string): Command => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Shortcut for drop and pick up
|
//Shortcut for drop and pick up
|
||||||
if (tokens.length === 3 && tokens[0] === "D&P" ){
|
if (tokens.length >2 && tokens[0] === "D&P" ){
|
||||||
const count = parseInt(tokens[1]);
|
const stacks = parseItemStacks(tokens, 1);
|
||||||
const item = tokens[2];
|
if(stacks){
|
||||||
if(Number.isInteger(count) && item in Item){
|
return new CommandDaP(stacks);
|
||||||
return new CommandDaP(count, Item[item as keyof typeof Item]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +182,9 @@ export const parseCommand = (cmdString: string): Command => {
|
||||||
if(tokens.length===2 && tokens[0] === "Sync" && tokens[1] === "GameData"){
|
if(tokens.length===2 && tokens[0] === "Sync" && tokens[1] === "GameData"){
|
||||||
return new CommandSync("Sync GameData");
|
return new CommandSync("Sync GameData");
|
||||||
}
|
}
|
||||||
|
if(tokens.length===2 && (tokens[0] === "Enter" || tokens[0] === "Exit") && tokens[1] === "Eventide"){
|
||||||
|
return new CommandEventide(tokens[0] === "Enter");
|
||||||
|
}
|
||||||
|
|
||||||
return new CommandNop(cmdString);
|
return new CommandNop(cmdString);
|
||||||
};
|
};
|
||||||
|
|
|
@ -85,6 +85,7 @@ export class SimulationState {
|
||||||
this.pouch.clearForReload();
|
this.pouch.clearForReload();
|
||||||
this.gameData.addAllToPouchOnReload(this.pouch);
|
this.gameData.addAllToPouchOnReload(this.pouch);
|
||||||
this.pouch.updateEquipmentDurability(this.gameData);
|
this.pouch.updateEquipmentDurability(this.gameData);
|
||||||
|
this.isOnEventide = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public useSaveForNextReload(name: string){
|
public useSaveForNextReload(name: string){
|
||||||
|
@ -126,6 +127,22 @@ export class SimulationState {
|
||||||
this.isOnEventide = false;
|
this.isOnEventide = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setEventide(onEventide: boolean){
|
||||||
|
if(this.isOnEventide !== onEventide){
|
||||||
|
if(onEventide){
|
||||||
|
// clear everything except for key items
|
||||||
|
this.pouch.clearForEventide();
|
||||||
|
// game data is not updated (?)
|
||||||
|
|
||||||
|
}else{
|
||||||
|
// reload pouch from gamedata as if reloading a save
|
||||||
|
this.reloadFrom(this.gameData);
|
||||||
|
}
|
||||||
|
this.isOnEventide = onEventide;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public syncGameDataWithPouch() {
|
public syncGameDataWithPouch() {
|
||||||
if(!this.isOnEventide){
|
if(!this.isOnEventide){
|
||||||
this.gameData.syncWith(this.pouch);
|
this.gameData.syncWith(this.pouch);
|
||||||
|
|
|
@ -264,4 +264,12 @@ export class Slots {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return how many slots are removed
|
||||||
|
public clearAllButKeyItems(): number {
|
||||||
|
const newslots = this.internalSlots.filter(stack=>itemToItemData(stack.item).type === ItemType.Key);
|
||||||
|
const removedCount = this.internalSlots.length - newslots.length;
|
||||||
|
this.internalSlots = newslots;
|
||||||
|
return removedCount;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,4 +105,8 @@ export class VisibleInventory implements DisplayableInventory{
|
||||||
public resetCount(): void {
|
public resetCount(): void {
|
||||||
this.count = this.slots.length;
|
this.count = this.slots.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clearForEventide(): void {
|
||||||
|
this.count-=this.slots.clearAllButKeyItems();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,12 +58,8 @@ export const DisplayPane: React.FC<DisplayPaneProps> = ({command,editCommand,dis
|
||||||
const cmdString = e.target.value;
|
const cmdString = e.target.value;
|
||||||
setCommandString(cmdString);
|
setCommandString(cmdString);
|
||||||
const parsedCommand = parseCommand(cmdString);
|
const parsedCommand = parseCommand(cmdString);
|
||||||
if(parsedCommand){
|
|
||||||
editCommand(parsedCommand);
|
editCommand(parsedCommand);
|
||||||
setHasError(false);
|
setHasError(cmdString!=="" &&!cmdString.startsWith("#") && !parsedCommand.isValid());
|
||||||
}else{
|
|
||||||
setHasError(true);
|
|
||||||
}
|
|
||||||
}}></input>
|
}}></input>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { TitledList } from "components/TitledList"
|
import { TitledList } from "components/TitledList";
|
||||||
import { parseCommand } from "core/Parser";
|
|
||||||
import { saveAs } from "data/FileSaver";
|
import { saveAs } from "data/FileSaver";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
|
@ -103,5 +102,5 @@ export const OptionPage: React.FC<OptionPageProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</TitledList>
|
</TitledList>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
|
@ -13,6 +13,9 @@ export const ReferencePage: React.FC = React.memo(()=>{
|
||||||
getAllItems().map((item, i)=><h4 key={i} className="Reference">{item}</h4>)
|
getAllItems().map((item, i)=><h4 key={i} className="Reference">{item}</h4>)
|
||||||
}
|
}
|
||||||
<h2>Commands</h2>
|
<h2>Commands</h2>
|
||||||
|
<p className="Reference">
|
||||||
|
This is a list of available commands. All commands and items are case-sensitive
|
||||||
|
</p>
|
||||||
<h3 className="Reference">Initialize X item1 Y item2 Z item3 ...</h3>
|
<h3 className="Reference">Initialize X item1 Y item2 Z item3 ...</h3>
|
||||||
<h4 className="Reference">Used for initializing inventory before simulation</h4>
|
<h4 className="Reference">Used for initializing inventory before simulation</h4>
|
||||||
<p className="Reference">
|
<p className="Reference">
|
||||||
|
@ -29,12 +32,17 @@ export const ReferencePage: React.FC = React.memo(()=>{
|
||||||
</p>
|
</p>
|
||||||
<p className="Reference Example">Example: Initialize 1 Apple 2 Axe 3 Slate 4 SpiritOrb</p>
|
<p className="Reference Example">Example: Initialize 1 Apple 2 Axe 3 Slate 4 SpiritOrb</p>
|
||||||
|
|
||||||
<h3 className="Reference">Save / Save As NAME</h3>
|
<h3 className="Reference">Save</h3>
|
||||||
|
<h3 className="Reference2">Save As NAME</h3>
|
||||||
|
|
||||||
<h4 className="Reference">Simulates a hard save or auto save action</h4>
|
<h4 className="Reference">Simulates a hard save or auto save action</h4>
|
||||||
<p className="Reference">
|
<p className="Reference">
|
||||||
Writes Game Data to the corresponding save slot. The auto saves are specified by NAME.
|
Writes Game Data to the corresponding save slot. The auto saves are specified by NAME.
|
||||||
You can have as many auto saves as you want in the simulator.
|
You can have as many auto saves as you want in the simulator.
|
||||||
</p>
|
</p>
|
||||||
|
<p className="Reference">
|
||||||
|
You cannot save on Eventide/ToTS. However, the simulator does not enforce that.
|
||||||
|
</p>
|
||||||
|
|
||||||
<p className="Reference Example">Example 1: Save</p>
|
<p className="Reference Example">Example 1: Save</p>
|
||||||
<p className="Reference Example">Example 2: Save As MySave</p>
|
<p className="Reference Example">Example 2: Save As MySave</p>
|
||||||
|
@ -128,12 +136,18 @@ export const ReferencePage: React.FC = React.memo(()=>{
|
||||||
<p className="Reference Example">Example 3: Sell 10 Apple 5 Diamond</p>
|
<p className="Reference Example">Example 3: Sell 10 Apple 5 Diamond</p>
|
||||||
<p className="Reference Example">Example 4: Sell 5 Apple From Slot 3</p>
|
<p className="Reference Example">Example 4: Sell 5 Apple From Slot 3</p>
|
||||||
|
|
||||||
<h3 className="Reference">D&P X item</h3>
|
<h3 className="Reference">D&P X item1 Y item2 Z item3 ...</h3>
|
||||||
<h4 className="Reference">Shortcut for drop and pick up, for sorting inventory</h4>
|
<h4 className="Reference">Shortcut for drop and pick up, for sorting inventory</h4>
|
||||||
<p className="Reference">
|
<p className="Reference">
|
||||||
This command drops X item from the first slot, then pick them up
|
This command drops and pick up each item stack in the specified order.
|
||||||
|
You can also repeat items if you are combining more than 2 slots.
|
||||||
</p>
|
</p>
|
||||||
<p className="Reference Example">Example: D&P 5 Diamond</p>
|
<p className="Reference">
|
||||||
|
You can only drop from slot 1 with this shortcut.
|
||||||
|
</p>
|
||||||
|
<p className="Reference Example">Example 1: D&P 5 Diamond</p>
|
||||||
|
<p className="Reference Example">Example 2: D&P 20 Shaft 5 Diamond</p>
|
||||||
|
<p className="Reference Example">Example 3: D&P 5 Diamond 10 Diamond</p>
|
||||||
|
|
||||||
<h3 className="Reference">Equip item</h3>
|
<h3 className="Reference">Equip item</h3>
|
||||||
<h3 className="Reference2">Equip item In Slot X</h3>
|
<h3 className="Reference2">Equip item In Slot X</h3>
|
||||||
|
@ -183,6 +197,29 @@ export const ReferencePage: React.FC = React.memo(()=>{
|
||||||
This command is currently broken
|
This command is currently broken
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 className="Reference">Shoot X Arrow</h3>
|
||||||
|
<h4 className="Reference">Simulates shooting arrow without opening inventory</h4>
|
||||||
|
<p className="Reference">
|
||||||
|
When reloading a save with desynced game data, the equipped weapon/bow/shield are automatically corrupted, but not the arrows.
|
||||||
|
To corrupt the equipped arrow slot, you need to shoot an arrow.
|
||||||
|
</p>
|
||||||
|
<p className="Reference">
|
||||||
|
This command does not let you select which arrow to shoot.
|
||||||
|
When you reload a save, Link should have the last equipped arrow slot equipped in the overworld.
|
||||||
|
<span className="Example">[needs confirmation]</span>
|
||||||
|
</p>
|
||||||
|
<p className="Reference Example">Example: Shoot 1 Arrow</p>
|
||||||
|
|
||||||
|
<h3 className="Reference">Enter/Exit Eventide</h3>
|
||||||
|
<h4 className="Reference">Simulates entering/exiting Eventide or Trial of the Sword</h4>
|
||||||
|
<p className="Reference">
|
||||||
|
When entering Eventide or TotS, the entire inventory is cleared except for key items regardless of inventory count.
|
||||||
|
While the challenge is active, none of the inventory changes are synced to game data.
|
||||||
|
</p>
|
||||||
|
<p className="Reference">
|
||||||
|
When exiting the challenge, the game reloads the game data as if reloading a save
|
||||||
|
</p>
|
||||||
|
<p className="Reference Example">Example: Enter Eventide</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</TitledList>
|
</TitledList>
|
||||||
|
|
Reference in a new issue