]> git.localhorst.tv Git - alttp.git/blob - resources/js/helpers/alttp-ram.js
d5bfa1bf2ef8f581c78907a09814b456664286ee
[alttp.git] / resources / js / helpers / alttp-ram.js
1 export const RAM_ADDR = {
2         PRIZE_MAP: 0x1209B,
3         FREE_ITEM_MENU: 0x180045,
4         CRYSTAL_MAP: 0x180050,
5         GT_CRYSTALS: 0x18019A,
6         GANON_TYPE: 0x1801A8,
7         SEED_TYPE: 0x180210,
8         INIT_SRAM: 0x183000,
9 };
10
11 export const SRAM_ADDR = {
12         ROOM_DATA_START: 0x000,
13         ROOM_DATA_END: 0x250,
14         OW_DATA_START: 0x280,
15         PYRAMID_SCREEN: 0x2DB,
16         OW_DATA_END: 0x300,
17         INV_START: 0x340,
18         INV_END: 0x4EF,
19 };
20
21 export const WRAM_ADDR = {
22         GAME_MODE: 0x10,
23         CURRENT_DUNGEON: 0x10E,
24         SAVE_DATA: 0xF000,
25 };
26
27 export const INV_ADDR = {
28         BOW: 0x00,
29         BOOM: 0x01,
30         HOOK: 0x02,
31         BOMB: 0x03,
32         POWDER: 0x04,
33         FROD: 0x05,
34         IROD: 0x06,
35         BOMBOS: 0x07,
36         ETHER: 0x08,
37         QUAKE: 0x09,
38         LAMP: 0x0A,
39         HAMMER: 0x0B,
40         FLUTE: 0x0C,
41         BUGNET: 0x0D,
42         BOOK: 0x0E,
43         BOTTLE: 0x0F,
44         SOMARIA: 0x10,
45         BYRNA: 0x11,
46         CAPE: 0x12,
47         MIRROR: 0x13,
48         GLOVE: 0x14,
49         BOOTS: 0x15,
50         FLIPPERS: 0x16,
51         MOONPEARL: 0x17,
52         SWORD: 0x19,
53         SHIELD: 0x1A,
54         ARMOR: 0x1B,
55         BOTTLE_1: 0x1C,
56         BOTTLE_2: 0x1D,
57         BOTTLE_3: 0x1E,
58         BOTTLE_4: 0x1F,
59         WALLET: 0x20,
60         RUPEES: 0x22,
61         COMPASS: 0x24,
62         BIG_KEY: 0x26,
63         MAP: 0x28,
64         HEART_PIECE: 0x2B,
65         HEALTH: 0x2C,
66         MAGIC: 0x2E,
67         KEYS: 0x2F,
68         PENDANTS: 0x34,
69         ARROWS: 0x37,
70         ABILITIES: 0x39,
71         CRYSTALS: 0x3A,
72         MAGIC_USE: 0x3B,
73         SMALL_KEY_START: 0x3C,
74         SMALL_KEY_END: 0x4C,
75         RANDO_BOOM: 0x4C,
76         RANDO_POWDER: 0x4C,
77         RANDO_FLUTE: 0x4C,
78         RANDO_BOW: 0x4E,
79         RANDO_KEY_START: 0x1A0,
80         RANDO_KEY_END: 0x1AF,
81 };
82
83 export const DUNGEON_IDS = {
84         SEWERS: 0,
85         HC: 1,
86         EP: 2,
87         DP: 3,
88         CT: 4,
89         SP: 5,
90         PD: 6,
91         MM: 7,
92         SW: 8,
93         IP: 9,
94         TH: 10,
95         TT: 11,
96         TR: 12,
97         GT: 13,
98 };
99
100 export const DUNGEON_MASKS = {
101         SEWERS: 0x0080,
102         HC: 0x0040,
103         EP: 0x0020,
104         DP: 0x0010,
105         CT: 0x0008,
106         SP: 0x0004,
107         PD: 0x0002,
108         MM: 0x0001,
109         SW: 0x8000,
110         IP: 0x4000,
111         TH: 0x2000,
112         TT: 0x1000,
113         TR: 0x0800,
114         GT: 0x0400,
115 };
116
117 export const ABILITY_MASKS = {
118         SWIM: 0x02,
119         DASH: 0x04,
120         PULL: 0x08,
121         TALK: 0x20,
122         READ: 0x40,
123 };
124
125 export const IN_GAME_MODES = [
126         0x05, // loading game
127         0x06, // entering dungeon
128         0x07, // dungeon
129         0x08, // entering overworld
130         0x09, // overworld
131         0x0A, // entering special overworld
132         0x0B, // special overworld
133         0x0E, // text/menu/map
134         0x0F, // closing spot
135         0x10, // opening spot
136         0x11, // falling
137         0x12, // dying
138         0x13, // fanfare
139         0x15, // mirror
140         0x16, // refill
141         0x17, // S&Q
142         0x18, // aga 2 cutscene
143         0x19, // triforce room
144         0x1B, // spawn select
145 ];
146
147 export const getShort = (data, offset) => (data[offset] * 256) + data[offset + 1];
148
149 export const buildPrizeMap = (prizes, crystals) => {
150         const map = {};
151         Object.entries(DUNGEON_IDS).forEach(([, id]) => {
152                 const isCrystal = !!(crystals && crystals[id]);
153                 const mask = (prizes && prizes[id]) || 0;
154                 map[id] = { isCrystal, mask };
155         });
156         return map;
157 };
158
159 export const isBossDefeated = (data, room) => {
160         return !!(data && (data[(2 * room) + 1] & 0x08));
161 };
162
163 export const isChestOpen = (data, room, chest) => {
164         if (chest < 4) {
165                 return !!(data && (data[2 * room] & Math.pow(2, chest + 4)));
166         } else {
167                 return !!(data && (data[(2 * room) + 1] & Math.pow(2, chest - 4)));
168         }
169 };
170
171 export const hasVisitedQuadrant = (data, room, quadrant) => {
172         return !!(data && (data[2 * room] & Math.pow(2, quadrant - 1)));
173 };
174
175 export const GT_BASEMENT_CHECKS = [
176         'iceM',
177         'iceL',
178         'iceR',
179         'dmUL',
180         'dmUR',
181         'dmBL',
182         'dmBR',
183         'randoUL',
184         'randoUR',
185         'randoBL',
186         'randoBR',
187         'fireSnake',
188         'mapChest',
189         'hopeL',
190         'hopeR',
191         'bobsChest',
192         'torchSeen',
193         'tileRoom',
194         'compassUL',
195         'compassUR',
196         'compassBL',
197         'compassBR',
198 ];
199
200 export const GT_BASEMENT_ALL = [
201         'iceM',
202         'iceL',
203         'iceR',
204         'dmUL',
205         'dmUR',
206         'dmBL',
207         'dmBR',
208         'randoUL',
209         'randoUR',
210         'randoBL',
211         'randoBR',
212         'fireSnake',
213         'mapChest',
214         'hopeL',
215         'hopeR',
216         'bobsChest',
217         'torch',
218         'torchSeen',
219         'tileRoom',
220         'compassUL',
221         'compassUR',
222         'compassBL',
223         'compassBR',
224 ];
225
226 export const getGTBasementState = (data) => ({
227         iceM: isChestOpen(data, 0x1C, 0),
228         iceL: isChestOpen(data, 0x1C, 1),
229         iceR: isChestOpen(data, 0x1C, 2),
230         dmUL: isChestOpen(data, 0x7B, 0),
231         dmUR: isChestOpen(data, 0x7B, 1),
232         dmBL: isChestOpen(data, 0x7B, 2),
233         dmBR: isChestOpen(data, 0x7B, 3),
234         randoUL: isChestOpen(data, 0x7C, 0),
235         randoUR: isChestOpen(data, 0x7C, 1),
236         randoBL: isChestOpen(data, 0x7C, 2),
237         randoBR: isChestOpen(data, 0x7C, 3),
238         fireSnake: isChestOpen(data, 0x7D, 0),
239         mapChest: isChestOpen(data, 0x8B, 0),
240         hopeL: isChestOpen(data, 0x8C, 1),
241         hopeR: isChestOpen(data, 0x8C, 2),
242         bobsChest: isChestOpen(data, 0x8C, 3),
243         torchSeen: hasVisitedQuadrant(data, 0x8C, 4),
244         torch: isChestOpen(data, 0x8C, 6),
245         tileRoom: isChestOpen(data, 0x8D, 0),
246         compassUL: isChestOpen(data, 0x9D, 0),
247         compassUR: isChestOpen(data, 0x9D, 1),
248         compassBL: isChestOpen(data, 0x9D, 2),
249         compassBR: isChestOpen(data, 0x9D, 3),
250 });
251
252 export const countGTBasementState = (state) =>
253         GT_BASEMENT_CHECKS.reduce((acc, cur) => state[cur] ? acc + 1 : acc, 0);
254
255 export const compareGTBasementState = (prev, cur) => {
256         for (let i = 0; i < GT_BASEMENT_ALL.length; ++i) {
257                 if (prev[GT_BASEMENT_ALL[i]] !== cur[GT_BASEMENT_ALL[i]]) {
258                         return GT_BASEMENT_ALL[i];
259                 }
260         }
261         return '';
262 };