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.