]> git.localhorst.tv Git - alttp.git/blob - resources/js/components/tracker/ToggleIcon.js
da1a18139e8f90bac57e1edb502b0f57e029caff
[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.countController = max => ({
101         getActive: highestActive,
102         getDefault: firstIcon,
103         handlePrimary: (state, setState, icons) => {
104                 setState(increment(icons[0], max));
105         },
106         handleSecondary: (state, setState, icons) => {
107                 setState(decrement(icons[0], max));
108         },
109 });
110
111 ToggleIcon.dungeonBossController = (dungeon) => ({
112         getActive: (state) => hasDungeonBoss(state, dungeon) ? getDungeonBoss(state, dungeon) : null,
113         getDefault: (state) => getDungeonBoss(state, dungeon),
114         handlePrimary: dungeon.bosses.length > 1
115                 ? nextString(`${dungeon.id}-boss`)
116                 : (state, setState) => {
117                         setState(toggleBossDefeated(dungeon));
118                 },
119         handleSecondary: dungeon.bosses.length > 1 ?
120                 previousString(`${dungeon.id}-boss`)
121                 : (state, setState) => {
122                         setState(toggleBossDefeated(dungeon));
123                 },
124 });
125
126 ToggleIcon.dungeonCheckController = (dungeon) => ({
127         getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
128         getDefault: firstIcon,
129         handlePrimary: (state, setState) => {
130                 setState(addDungeonCheck(dungeon));
131         },
132         handleSecondary: (state, setState) => {
133                 setState(removeDungeonCheck(dungeon));
134         },
135 });
136
137 ToggleIcon.dungeonController = dungeon => ({
138         getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
139         getDefault: firstIcon,
140         handlePrimary: (state, setState, icons) => {
141                 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
142         },
143         handleSecondary: (state, setState, icons) => {
144                 setState(toggleBoolean(`${dungeon.id}-${icons[0]}`));
145         },
146 });
147
148 ToggleIcon.dungeonCountController = (dungeon, max) => ({
149         getActive: (state, icons) => state[`${dungeon.id}-${icons[0]}`] ? icons[0] : null,
150         getDefault: firstIcon,
151         handlePrimary: (state, setState, icons) => {
152                 setState(increment(`${dungeon.id}-${icons[0]}`, max));
153         },
154         handleSecondary: (state, setState, icons) => {
155                 setState(decrement(`${dungeon.id}-${icons[0]}`, max));
156         },
157 });
158
159 ToggleIcon.dungeonPrizeController = (dungeon) => ({
160         getActive: (state) => hasDungeonPrize(state, dungeon) ? getDungeonPrize(state, dungeon) : null,
161         getDefault: (state) => getDungeonPrize(state, dungeon),
162         handlePrimary: nextString(`${dungeon.id}-prize`),
163         handleSecondary: previousString(`${dungeon.id}-prize`),
164 });
165
166 ToggleIcon.medallionController = {
167         getActive: highestActive,
168         getDefault: firstIcon,
169         handlePrimary: nextIcon,
170         handleSecondary: (state, setState, icons) => {
171                 const mm = state['mm-medallion'];
172                 const tr = state['tr-medallion'];
173                 const isMM = mm === icons[0];
174                 const isTR = tr === icons[0];
175                 console.log({ mm, isMM, tr, isTR });
176                 if (!isMM && !isTR) {
177                         // empty: set as MM if mire is unset, else set as TR if TR is unset
178                         if (!mm) {
179                                 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
180                         } else if (!tr) {
181                                 setState(s => ({ ...s, 'tr-medallion': icons[0] }));
182                         }
183                 } else if (isMM && !isTR) {
184                         // MM: if TR is free, switch to TR, otherwise remove MM
185                         if (!tr) {
186                                 setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': icons[0] }));
187                         } else {
188                                 setState(s => ({ ...s, 'mm-medallion': null }));
189                         }
190                 } else if (!isMM && isTR) {
191                         // TR: if MM is free, switch to both, otherwise remove TR
192                         if (!mm) {
193                                 setState(s => ({ ...s, 'mm-medallion': icons[0] }));
194                         } else {
195                                 setState(s => ({ ...s, 'tr-medallion': null }));
196                         }
197                 } else {
198                         // both: remove both
199                         setState(s => ({ ...s, 'mm-medallion': null, 'tr-medallion': null }));
200                 }
201         },
202 };
203
204 ToggleIcon.modulusController = ctrl => ({
205         getActive: (state, icons) => icons[(state[ctrl] || 0) % icons.length],
206         getDefault: firstIcon,
207         handlePrimary: (state, setState, icons) => {
208                 setState(increment(icons[0], icons.length));
209         },
210         handleSecondary: (state, setState, icons) => {
211                 setState(decrement(icons[0], icons.length));
212         },
213 });
214
215 ToggleIcon.nullController = {
216         getActive: () => null,
217         getDefault: firstIcon,
218         handlePrimary: doNothing,
219         handleSecondary: doNothing,
220 };
221
222 ToggleIcon.simpleController = {
223         getActive: highestActive,
224         getDefault: firstIcon,
225         handlePrimary: nextIcon,
226         handleSecondary: previousIcon,
227 };
228
229 ToggleIcon.progressiveController = (master, min, max) => ({
230         getActive: (state, icons) => {
231                 const count = Math.max(min, Math.min(max, state[master] || 0));
232                 return count ? icons[count - 1] : null;
233         },
234         getDefault: firstIcon,
235         handlePrimary: (state, setState) => {
236                 setState(increment(master, max, min));
237         },
238         handleSecondary: (state, setState) => {
239                 setState(decrement(master, max, min));
240         },
241 });
242
243 ToggleIcon.propTypes = {
244         active: PropTypes.string,
245         className: PropTypes.string,
246         controller: PropTypes.shape({
247                 handlePrimary: PropTypes.func,
248                 handleSecondary: PropTypes.func,
249         }),
250         icons: PropTypes.arrayOf(PropTypes.string),
251 };
252
253 export default ToggleIcon;