]> git.localhorst.tv Git - alttp.git/blob - resources/js/components/tracker/ToggleIcon.js
separate bottle tracking
[alttp.git] / resources / js / components / tracker / ToggleIcon.js
1 import PropTypes from 'prop-types';
2 import React from 'react';
3
4 import ZeldaIcon from '../common/ZeldaIcon';
5 import {
6         addDungeonCheck,
7         decrement,
8         getDungeonBoss,
9         getDungeonRemainingItems,
10         getDungeonPrize,
11         hasDungeonBoss,
12         hasDungeonPrize,
13         highestActive,
14         increment,
15         removeDungeonCheck,
16         toggleBoolean,
17         toggleBossDefeated,
18 } from '../../helpers/tracker';
19 import { useTracker } from '../../hooks/tracker';
20
21 const ToggleIcon = ({ controller, className, icons }) => {
22         const { setManualState, state } = useTracker();
23         const activeController = controller || ToggleIcon.nullController;
24         const active = activeController.getActive(state, icons);
25         const defaultIcon = activeController.getDefault(state, icons);
26         const classNames = ['toggle-icon'];
27         if (active) {
28                 classNames.push('active');
29         } else {
30                 classNames.push('inactive');
31         }
32         if (className) {
33                 classNames.push(className);
34         }
35         return <span
36                 className={classNames.join(' ')}
37                 onClick={(e) => {
38                         activeController.handlePrimary(state, setManualState, icons);
39                         e.preventDefault();
40                         e.stopPropagation();
41                 }}
42                 onContextMenu={(e) => {
43                         activeController.handleSecondary(state, setManualState, icons);
44                         e.preventDefault();
45                         e.stopPropagation();
46                 }}
47         >
48                 <ZeldaIcon name={active || defaultIcon || icons[0]} />
49         </span>;
50 };
51
52 const doNothing = () => { };
53
54 const firstIcon = (state, icons) => icons[0];
55
56 const nextIcon = (state, setState, icons) => {
57         const highest = highestActive(state, icons);
58         const highestIndex = highest ? icons.indexOf(highest) : -1;
59         if (highestIndex + 1 < icons.length) {
60                 setState(toggleBoolean(icons[highestIndex + 1]));
61         } else {
62                 const changes = {};
63                 icons.forEach(icon => {
64                         changes[icon] = false;
65                 });
66                 setState(s => ({ ...s, ...changes }));
67         }
68 };
69
70 const previousIcon = (state, setState, icons) => {
71         const highest = highestActive(state, icons);
72         const highestIndex = highest ? icons.indexOf(highest) : -1;
73         if (highestIndex >= 0) {
74                 setState(toggleBoolean(icons[highestIndex]));
75         } else {
76                 const changes = {};
77                 icons.forEach(icon => {
78                         changes[icon] = true;
79                 });
80                 setState(s => ({ ...s, ...changes }));
81         }
82 };
83
84 const nextString = property => (state, setState, icons) => {
85         const current = state[property] || icons[0];
86         const currentIndex = icons.indexOf(current);
87         const nextIndex = (currentIndex + 1) % icons.length;
88         const next = icons[nextIndex];
89         setState(s => ({ ...s, [property]: next }));
90 };
91
92 const previousString = property => (state, setState, icons) => {
93         const current = state[property] || icons[0];
94         const currentIndex = icons.indexOf(current);
95         const previousIndex = (currentIndex + icons.length - 1) % icons.length;
96         const previous = icons[previousIndex];
97         setState(s => ({ ...s, [property]: previous }));
98 };
99
100 ToggleIcon.bottleController = ctrl => ({
101         getActive: (state, icons) => state[ctrl] ? icons[state[ctrl] - 1] : null,
102         getDefault: () => 'bottle',
103         handlePrimary: (state, setState, icons) => {
104                 if (state[ctrl] === 0) {
105                         // skip over mushroom
106                         setState(s => ({ ...s, [ctrl]: 2 }));
107                 } else {
108                         setState(increment(ctrl, icons.length));
109                 }
110         },
111         handleSecondary: (state, setState, icons) => {
112                 if (state[ctrl] === 2) {
113                         // skip over mushroom
114                         setState(s => ({ ...s, [ctrl]: 0 }));
115                 } else {
116                         setState(decrement(ctrl, icons.length));
117                 }
118         },
119 });
120
121 ToggleIcon.countController = max => ({
122         getActive: highestActive,
123         getDefault: firstIcon,
124         handlePrimary: (state, setState, icons) => {
125                 setState(increment(icons[0], max));
126         },
127         handleSecondary: (state, setState, icons) => {
128                 setState(decrement(icons[0], max));
129         },
130 });
131
132 ToggleIcon.dungeonBossController = (dungeon) => ({
133         getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
134         getDefault: (state) => getDungeonBoss(state, dungeon),
135         handlePrimary: dungeon.bosses.length > 1
136                 ? nextString(`${dungeon.id}-boss`)
137                 : (state, setState) => {
138                         setState(toggleBossDefeated(dungeon));
139                 },
140         handleSecondary: dungeon.bosses.length > 1 ?
141                 previousString(`${dungeon.id}-boss`)
142                 : (state, setState) => {
143                         setState(toggleBossDefeated(dungeon));
144                 },
145 });
146
147 ToggleIcon.dungeonCheckController = (dungeon) => ({
148         getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
149         getDefault: firstIcon,
150         handlePrimary: (state, setState) => {
151                 setState(addDungeonCheck(dungeon));
152         },
153         handleSecondary: (state, setState) => {
154                 setState(removeDungeonCheck(dungeon));
155         },
156 });
157
158 ToggleIcon.dungeonController = dungeon => ({
159         getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
160         getDefault: firstIcon,
161         handlePrimary: (state, setState, icons) => {
162                 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
163         },
164         handleSecondary: (state, setState, icons) => {
165                 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
166         },
167 });
168
169 ToggleIcon.dungeonCountController = (dungeon, max) => ({
170         getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
171         getDefault: firstIcon,
172         handlePrimary: (state, setState, icons) => {
173                 setState(increment(`${dungeon.id}-${icons[0]}`, max));
174         },
175         handleSecondary: (state, setState, icons) => {
176                 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
177         },
178 });
179
180 ToggleIcon.dungeonPrizeController = (dungeon) => ({
181         getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
182         getDefault: (state) => getDungeonPrize(state, dungeon),
183         handlePrimary: nextString(`${dungeon.id}-prize`),
184         handleSecondary: previousString(`${dungeon.id}-prize`),
185 });
186
187 ToggleIcon.medallionController = {
188         getActive: highestActive,
189         getDefault: firstIcon,
190         handlePrimary: nextIcon,
191         handleSecondary: (state, setState, icons) => {
192                 const mm = state['mm-medallion'];
193                 const tr = state['tr-medallion'];
194                 const isMM = mm === icons[0];
195                 const isTR = tr === icons[0];
196                 console.log({ mm, isMM, tr, isTR });
197                 if (!isMM && !isTR) {
198                         // empty: set as MM if mire is unset, else set as TR if TR is unset
199                         if (!mm) {
200                                 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
201                         } else if (!tr) {
202                                 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
203                         }
204                 } else if (isMM && !isTR) {
205                         // MM: if TR is free, switch to TR, otherwise remove MM
206                         if (!tr) {
207                                 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
208                         } else {
209                                 setState(s => ({ ...s, 'mm-medallion': null }));
210                         }
211                 } else if (!isMM && isTR) {
212                         // TR: if MM is free, switch to both, otherwise remove TR
213                         if (!mm) {
214                                 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
215                         } else {
216                                 setState(s => ({ ...s, 'tr-medallion': null }));
217                         }
218                 } else {
219                         // both: remove both
220                         setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
221                 }
222         },
223 };
224
225 ToggleIcon.modulusController = ctrl => ({
226         getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
227         getDefault: firstIcon,
228         handlePrimary: (state, setState, icons) => {
229                 setState(increment(ctrl, icons.length));
230         },
231         handleSecondary: (state, setState, icons) => {
232                 setState(decrement(ctrl, icons.length));
233         },
234 });
235
236 ToggleIcon.nullController = {
237         getActive: () => null,
238         getDefault: firstIcon,
239         handlePrimary: doNothing,
240         handleSecondary: doNothing,
241 };
242
243 ToggleIcon.simpleController = {
244         getActive: highestActive,
245         getDefault: firstIcon,
246         handlePrimary: nextIcon,
247         handleSecondary: previousIcon,
248 };
249
250 ToggleIcon.progressiveController = (master, min, max) => ({
251         getActive: (state, icons) => {
252                 const count = Math.max(min, Math.min(max, state[master] || 0));
253                 return count ? icons[count - 1] : null;
254         },
255         getDefault: firstIcon,
256         handlePrimary: (state, setState) => {
257                 setState(increment(master, max, min));
258         },
259         handleSecondary: (state, setState) => {
260                 setState(decrement(master, max, min));
261         },
262 });
263
264 ToggleIcon.propTypes = {
265         active: PropTypes.string,
266         className: PropTypes.string,
267         controller: PropTypes.shape({
268                 handlePrimary: PropTypes.func,
269                 handleSecondary: PropTypes.func,
270         }),
271         icons: PropTypes.arrayOf(PropTypes.string),
272 };
273
274 export default ToggleIcon;