import PropTypes from 'prop-types';
import React from 'react';
+import { Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import Icon from '../common/Icon';
compareGTBasementState,
countGTBasementState,
getGTBasementState,
+ IN_GAME_MODES,
+ INV_ADDR,
+ RAM_ADDR,
+ SRAM_ADDR,
+ WRAM_ADDR,
} from '../../helpers/alttp-ram';
import { useSNES } from '../../hooks/snes';
-const IN_GAME_MODES = [
- 0x05, // loading game
- 0x06, // entering dungeon
- 0x07, // dungeon
- 0x08, // entering overworld
- 0x09, // overworld
- 0x0A, // entering special overworld
- 0x0B, // special overworld
- 0x0E, // text/menu/map
- 0x0F, // closing spot
- 0x10, // opening spot
- 0x11, // falling
- 0x12, // dying
- 0x13, // fanfare
- 0x15, // mirror
- 0x16, // refill
- 0x17, // S&Q
- 0x18, // aga 2 cutscene
- 0x19, // triforce room
- 0x1A, // credits
- 0x1B, // spawn select
-];
-
const GT_TYPES = [
0x02, // all dungeons
0x03, // defeat ganon
0x0B, // completionist
];
-const FREE_ITEM_MENU = 0x180045;
-const GT_CRYSTALS = 0x18019A;
-const GANON_TYPE = 0x1801A8;
-const SEED_TYPE = 0x180210;
-const INIT_SRAM = 0x183000;
-
-const GAME_MODE = 0x10;
-const CURRENT_DUNGEON = 0x10E;
-const SAVE_WRAM = 0xF000;
-const ROOM_DATA_START = 0x000;
-const ROOM_DATA_END = 0x140;
-const PYRAMID_SCREEN = 0x2DB;
-const BIG_KEYS_1 = 0x366;
-const OWNED_CRYSTALS = 0x37A;
-
const GT_ENTRANCE_ID = 55;
const GuessingGameAutoTracking = ({ onSolve, onStart, onStop }) => {
const {
disable: disableSNES,
enable: enableSNES,
+ openSettings,
sock,
status,
} = useSNES();
React.useEffect(() => {
if (enabled && !status.error && status.connected && status.device) {
const checkInGame = () => {
- sock.current.readWRAM(GAME_MODE, 1, (data) => {
+ sock.current.readWRAM(WRAM_ADDR.GAME_MODE, 1, (data) => {
setInGame(IN_GAME_MODES.includes(data[0]));
});
};
// refresh static game information
React.useEffect(() => {
if (!inGame) return;
- sock.current.readBytes(SEED_TYPE, 1, (data) => {
+ sock.current.readBytes(RAM_ADDR.SEED_TYPE, 1, (data) => {
setSeedType(data[0]);
});
- sock.current.readBytes(GT_CRYSTALS, 1, (data) => {
+ sock.current.readBytes(RAM_ADDR.GT_CRYSTALS, 1, (data) => {
setGTCrystals(data[0]);
});
- sock.current.readBytes(GANON_TYPE, 1, (data) => {
+ sock.current.readBytes(RAM_ADDR.GANON_TYPE, 1, (data) => {
setGanonType(data[0]);
});
- sock.current.readBytes(FREE_ITEM_MENU, 1, (data) => {
+ sock.current.readBytes(RAM_ADDR.FREE_ITEM_MENU, 1, (data) => {
setFreeItemMenu(data[0]);
});
- sock.current.readBytes(INIT_SRAM + PYRAMID_SCREEN, 1, (data) => {
+ sock.current.readBytes(RAM_ADDR.INIT_SRAM + SRAM_ADDR.PYRAMID_SCREEN, 1, (data) => {
setPyramidOpen(!!(data[0] & 0x20));
});
}, [inGame, sock]);
React.useEffect(() => {
if (!applicable || !inGame || hasBigKey) return;
const updateCrystals = () => {
- sock.current.readWRAM(SAVE_WRAM + OWNED_CRYSTALS, 1, (data) => {
+ const crAddress = WRAM_ADDR.SAVE_DATA + SRAM_ADDR.INV_START + INV_ADDR.CRYSTALS;
+ sock.current.readWRAM(crAddress, 1, (data) => {
let owned = 0;
for (let i = 0; i < 7; ++i) {
if (data[0] & Math.pow(2, i)) {
if (!applicable || hasBigKey || ownedCrystals !== gtCrystals || hasEntered) return;
controls.current.onStart();
const updateDungeon = () => {
- sock.current.readWRAM(CURRENT_DUNGEON, 2, (data) => {
+ sock.current.readWRAM(WRAM_ADDR.CURRENT_DUNGEON, 2, (data) => {
setLastEntrance(data[0] + (data[1] * 256));
});
};
React.useEffect(() => {
if (!applicable || !hasEntered || hasBigKey) return;
const updateGTState = () => {
- const roomDataSize = ROOM_DATA_END - ROOM_DATA_START;
- sock.current.readWRAM(SAVE_WRAM + ROOM_DATA_START, roomDataSize, (data) => {
+ const roomDataStart = WRAM_ADDR.SAVE_DATA + SRAM_ADDR.ROOM_DATA_START;
+ const roomDataSize = SRAM_ADDR.ROOM_DATA_END - SRAM_ADDR.ROOM_DATA_START;
+ sock.current.readWRAM(roomDataStart, roomDataSize, (data) => {
const gtState = getGTBasementState(data);
const gtCount = countGTBasementState(gtState);
setBasement(old => {
const solution = basement.last === 'torch' ? basement.torch : basement.count;
controls.current.onSolve(solution);
} else {
- sock.current.readWRAM(SAVE_WRAM + BIG_KEYS_1, 1, (data) => {
+ const bkAddr = WRAM_ADDR.SAVE_DATA + SRAM_ADDR.INV_START + INV_ADDR.BIG_KEY;
+ sock.current.readWRAM(bkAddr, 1, (data) => {
setHasBigKey(!!(data[0] & 0x04));
});
}
title={t(`autoTracking.statusMsg.${statusMsg}`, { device: status.device })}
/>
: null}
+ <Button
+ className="me-2"
+ onClick={openSettings}
+ size="sm"
+ title={t('snes.settings')}
+ variant="outline-secondary"
+ >
+ <Icon.SETTINGS title="" />
+ </Button>
<ToggleSwitch
onChange={toggle}
title={t('autoTracking.heading')}