PageSourceSearch

https://ball-labyrinth-game.web.app/js/classes/GameState.js

js ball-labyrinth-game.web.app collected 2026-10-03 12:17:41 UTC 12,390 bytes, 383 lines download raw bytes

1class GameState {
2    constructor() {
3        this.mode = {
4            lastPlayed:null,
5            current: null
6        };
7
8        this.package = {
9            lastPlayed:0,
10            current: null
11        };
12
13        this.level = {
14            lastPlayed:0,
15            current: null
16        };
17
18        this.hints = 999;
19
20        this.progress = {};
21
22        this.uuid = uuid();
23
24        this.language = 'EN';
25        this.muted = false;
26        this.adfree = false;
27        this.unlockedPackages = []
28        this.inductions = {}
29
30        this.levelNumber = this.levelNumber.bind(this)
31        this.muteUnmute = this.muteUnmute.bind(this);
32        this.currentLevelBestMoves = this.currentLevelBestMoves.bind(this);
33        this.firstPlay = this.firstPlay.bind(this);
34        this.firstPackagePlay = this.firstPackagePlay.bind(this);
35        this.refreshLastPlayed = this.refreshLastPlayed.bind(this)
36        this.levelHasHint = this.levelHasHint.bind(this);
37        this.levelPlayedFirstTime = this.levelPlayedFirstTime.bind(this);
38        this.levelAwardState = this.levelAwardState.bind(this);
39        this.starsAndMovesAwardState = this.starsAndMovesAwardState.bind(this);
40        this.levelSolved = this.levelSolved.bind(this);
41        this.userStars = this.userStars.bind(this);
42        this.modeStars = this.modeStars.bind(this);
43        this.packStars = this.packStars.bind(this);
44        this.packAllStars = this.packAllStars.bind(this);
45        this.levelStars = this.levelStars.bind(this);
46        this.levelMoves = this.levelMoves.bind(this);
47        this.levelTargetMoves = this.levelTargetMoves.bind(this);
48        this.packageStats = this.packageStats.bind(this);
49        this.levelStats = this.levelStats.bind(this);
50        this.restore = this.restore.bind(this);
51        this.persist = this.persist.bind(this);
52
53        this.restore();
54
55        // apply ui stuff of mute
56        this.muteUnmute();
57        this.muteUnmute();
58
59        if (this.adfree) {
60            get('body').classList.add('noads')
61        }
62    }
63
64    levelNumber() {
65        return this.package.current * 100 + this.level.current
66    }
67
68    muteUnmute() {
69        this.muted = !this.muted;
70
71        if (this.muted) {
72            get('body').classList.add('muted');
73        } else {
74            get('body').classList.remove('muted');
75        }
76    }
77
78    currentLevelBestMoves() {
79        if (this.progress[this.mode.current]) {
80            if (this.progress[this.mode.current][this.package.current]) {
81                if (this.progress[this.mode.current][this.package.current][this.level.current]) {
82                    return this.progress[this.mode.current][this.package.current][this.level.current].moves;
83                }
84            }
85        }
86
87        return 0;
88    }
89
90    levelHasHint(level) {
91        if (this.progress[this.mode.current]) {
92            if (this.progress[this.mode.current][this.package.current]) {
93                if (this.progress[this.mode.current][this.package.current][+level]) {
94                    return false;
95                }
96            }
97        }
98
99        return (level + 1) % 10 === 0;
100    }
101
102    firstPlay() {
103        if (this.mode.current !== 'TEST' && !this.progress[this.mode.current]) {
104            return true;
105        } else {
106            return false;
107        }
108    }
109
110    firstPackagePlay() {
111        if (this.mode.current !== 'TEST' && (!this.progress[this.mode.current] || !this.progress[this.mode.current][this.package.current])) {
112            return true;
113        } else {
114            return false;
115        }
116    }
117
118    refreshLastPlayed() {
119        if (this.mode.current === 'TEST') return;
120
121        this.mode.lastPlayed = this.mode.current;
122        this.package.lastPlayed = this.package.current;
123        this.level.lastPlayed = this.level.current;
124    }
125
126    levelPlayedFirstTime(level, pack, mode) {
127        if (!pack) pack = this.package.current
128        if (!mode) mode = this.mode.current
129        if (this.progress[mode]) {
130            if (this.progress[mode][pack]) {
131                if (this.progress[mode][pack][+level]) {
132                    return false;
133                }
134            }
135        }
136
137        return true;
138    }
139
140    levelAwardState(lvlState) {
141        // not solved before
142            // now better than before
143                // 3 stars
144                    // more than target moves
145                        //--REDUNDANT_MOVES
146                    // equal to target moves
147                        //--EXACT_MOVES
148                    // less than target moves
149                        //--LESS_MOVES
150                // less than 3 stars
151                //--STARS_NEEDED
152        // solved before
153            // now better than before
154                // 3 stars
155                    // more than target moves
156                        //--REDUNDANT_MOVES
157                    // equal to target moves
158                        //--EXACT_MOVES
159                    // less than target moves
160                        //--LESS_MOVES
161            // now not better than before
162                // but it can be better
163                // it can't be better
164
165        const solvedBefore = !this.levelPlayedFirstTime(lvlState.level, lvlState.pack, lvlState.mode)
166        const currentSolution = this.starsAndMovesAwardState(lvlState.stars, lvlState.moves, lvlState.targetMoves)
167
168        if (!solvedBefore) {
169            return currentSolution
170        } else {
171            const oldLvlState = this.progress[lvlState.mode][lvlState.pack][+lvlState.level]
172            const oldSolution = this.starsAndMovesAwardState(oldLvlState.stars, oldLvlState.moves, oldLvlState.targetMoves)
173
174            if (currentSolution.number > oldSolution.number) {
175                return currentSolution
176            } else {
177                return AWARD_STATE.UNCHANGED
178            }
179        }
180    }
181
182    starsAndMovesAwardState(stars, moves, targetMoves) {
183        if (stars === LEVEL_STARS_AMOUNT) {
184            if (moves > targetMoves) {
185                return AWARD_STATE.REDUNDANT_MOVES
186            } else if (moves === targetMoves) {
187                return AWARD_STATE.EXACT_MOVES
188            } else {
189                return AWARD_STATE.LESS_MOVES
190            }
191        } else {
192            return AWARD_STATE.STARS_NEEDED
193        }
194    }
195
196    levelSolved(lvlState) {
197        // increment hints if required
198        let hintAdded = false;
199        let solvedBetter = false;
200
201        if (this.levelHasHint(lvlState.level)) {
202            this.hints++;
203            hintAdded = true;
204        }
205
206        if (!this.progress[lvlState.mode]) {
207            this.progress[lvlState.mode] = [];
208        }
209
210        if (!this.progress[lvlState.mode][lvlState.pack]) {
211            this.progress[lvlState.mode][lvlState.pack] = [];
212        }
213
214        var lvl = this.progress[lvlState.mode][lvlState.pack][+lvlState.level];
215
216        // first time solved
217        if (!lvl) {
218            // check if it was solved better that expected
219            let betterThanExpected =
220                lvlState.stars === 3
221                && lvlState.moves < levels[lvlState.mode][lvlState.pack].levels[lvlState.level].solution.length;
222
223            if (betterThanExpected) {
224                this.hints += HINTS_FOR_BETTER_SOLUTION;
225                solvedBetter = true;
226            }
227        }
228
229        this.progress[lvlState.mode][lvlState.pack][+lvlState.level] = lvlState.best(lvl);
230
231        this.persist()
232
233        return {userStars: this.userStars(), hintAdded: hintAdded, solvedBetter: solvedBetter};
234    }
235
236    userStars() {
237        return this.modeStars(STAR_MODE) + this.modeStars(MOOVE_MODE);
238    }
239
240    modeStars(md) {
241        if (!this.progress[md]) return 0;
242
243        var modeStars = 0;
244
245        for (var p=0; p<this.progress[md].length; p++) {
246            modeStars += this.packStars(md, p);
247        }
248
249        return modeStars;
250    }
251
252    packStars(md, pk) {
253        if (!this.progress[md]) return 0;
254        if (!this.progress[md][pk]) return 0;
255
256        var pkStars = 0;
257
258        for (var lvl=0; lvl<this.progress[md][pk].length; lvl++) {
259            pkStars += this.levelStars(md, pk, lvl);
260        }
261
262        return pkStars;
263    }
264
265    packAllStars(md, pk) {
266        return levels[md][pk].levels.length * LEVEL_STARS_AMOUNT;
267    }
268
269    packAwards(md, pk) {
270        if (!this.progress[md]) return 0;
271        if (!this.progress[md][pk]) return 0;
272
273        var pkAwards = 0;
274
275        for (var lvl=0; lvl<this.progress[md][pk].length; lvl++) {
276            pkAwards += this.levelAwards(md, pk, lvl);
277        }
278
279        return pkAwards;
280    }
281
282    levelStars(md, pk, lvl) {
283        if (!this.progress[md]) return 0;
284        if (!this.progress[md][pk]) return 0;
285
286        return this.progress[md][pk][lvl] ? this.progress[md][pk][lvl].stars : 0;
287    }
288
289    levelMoves(md, pk, lvl) {
290        if (!this.progress[md]) return 0;
291        if (!this.progress[md][pk]) return 0;
292
293        return this.progress[md][pk][lvl] ? this.progress[md][pk][lvl].moves : 0;
294    }
295
296    levelTargetMoves(md, pk, lvl) {
297        return levels[md][pk].levels[lvl].solution.length
298    }
299
300    levelAwards(md, pk, lvl) {
301        if (!this.progress[md]) return 0;
302        if (!this.progress[md][pk]) return 0;
303        if (!this.progress[md][pk][lvl]) return 0;
304
305        const levelMoves = this.levelMoves(md, pk, lvl)
306        const levelTargetMoves = this.levelTargetMoves(md, pk, lvl)
307
308        if (this.progress[md][pk][lvl].stars === LEVEL_STARS_AMOUNT && levelMoves <= levelTargetMoves) {
309            return levelMoves < levelTargetMoves ? 2 : 1
310        } else {
311            return 0
312        }
313    }
314
315    packageStats(mode, pack) {
316        const packStars = this.packStars(mode, pack)
317        const packAllStars = this.packAllStars(mode, pack)
318        const packAwards = this.packAwards(mode, pack)
319        const packLevelsAmount = levels[mode][pack].levels.length
320
321        //if (pack === 0) {
322            return new PackStats(true, packStars, packAllStars, packAwards, packLevelsAmount)
323        /*} else {
324            const previousPackState = this.packageStats(mode, pack-1);
325            let packageAvailable =
326                previousPackState.available &&
327                previousPackState.stars >= (previousPackState.allStars - ALLOWED_STAR_GAP)
328
329            return new PackStats(packageAvailable, packStars, packAllStars, packAwards, packLevelsAmount)
330        }*/
331    }
332
333    levelStats(mode, pack, lvl) {
334        let packageStats = this.packageStats(mode, pack)
335        const packageBought = this.unlockedPackages.includes(pack)
336
337        const levelStars = this.levelStars(mode, pack, lvl)
338        const levelMoves = this.levelMoves(mode, pack, lvl)
339        const levelTargetMoves = this.levelTargetMoves(mode, pack, lvl)
340
341        if (packageStats.available || packageBought) {
342            //if (lvl === 0) {
343                return new LevelStats(true, levelStars, levelMoves, levelTargetMoves)
344            /*} else {
345                let prevLvlState = this.levelStats(mode, pack, lvl-1);
346                let prevLvlMoves = this.levelMoves(mode, pack, lvl-1)
347
348                if (prevLvlState.available && (prevLvlState.stars > 0 || prevLvlMoves > 0)) {
349                    return new LevelStats(true, levelStars, levelMoves, levelTargetMoves)
350                } else {
351                    return new LevelStats()
352                }
353            }*/
354        } else {
355            if (lvl < ALLOWED_LEVELS_TO_TRY) {
356                if (lvl === 0) {
357                    return new LevelStats(true, levelStars, levelMoves, levelTargetMoves)
358                } else {
359                    let prevLvlStars = this.levelStars(mode, pack, lvl-1)
360                    let prevLvlMoves = this.levelMoves(mode, pack, lvl-1)
361
362                    return new LevelStats((prevLvlStars > 0 || prevLvlMoves > 0), levelStars, levelMoves, levelTargetMoves)
363                }
364            } else {
365                return new LevelStats()
366            }
367        }
368    }
369
370    restore() {
371        var stateString = storage.get(STORAGE_STATE_KEY);
372        if (stateString) {
373            Object.assign(this, JSON.parse(stateString));
374        }
375
376        this.mode.current = this.mode.current === 'TEST' ? STAR_MODE : this.mode.current;
377        this.mode.lastPlayed = this.mode.lastPlayed === 'TEST' ? STAR_MODE : this.mode.lastPlayed;
378    }
379
380    persist() {
381        storage.persist(STORAGE_STATE_KEY ,JSON.stringify(this));
382    }
383}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.