@nayan-ui/games 1.20.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/games/arrows/Arrows.js +1 -1
- package/lib/module/games/arrows/ArrowsScene.js +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlasterScene.js +1 -1
- package/lib/module/games/bike-racing/BikeMeshes.js +1 -1
- package/lib/module/games/bike-racing/BikeRacing.js +1 -1
- package/lib/module/games/bike-racing/BikeRacingConstants.js +1 -1
- package/lib/module/games/bike-racing/BikeRacingScene.js +1 -1
- package/lib/module/games/block-blast/BlockBlast.js +1 -1
- package/lib/module/games/block-blast/BlockBlastScene.js +1 -1
- package/lib/module/games/block-breaker/BlockBreaker.js +1 -1
- package/lib/module/games/block-breaker/BlockBreakerScene.js +1 -1
- package/lib/module/games/bluk/Bluk.js +1 -1
- package/lib/module/games/bluk/BlukScene.js +1 -1
- package/lib/module/games/bubble-shooter/BubbleShooter.js +1 -1
- package/lib/module/games/bubble-shooter/BubbleShooterScene.js +1 -1
- package/lib/module/games/candy-crush/CandyCrush.js +1 -1
- package/lib/module/games/candy-crush/CandyCrushScene.js +1 -1
- package/lib/module/games/car-racing/CarMeshes.js +1 -1
- package/lib/module/games/car-racing/CarRacing.js +1 -1
- package/lib/module/games/car-racing/CarRacingScene.js +1 -1
- package/lib/module/games/chameleon-run/ChameleonRun.js +1 -1
- package/lib/module/games/chameleon-run/ChameleonRunBackdrop.js +1 -1
- package/lib/module/games/chameleon-run/ChameleonRunMeshes.js +1 -1
- package/lib/module/games/checkers/Checkers.js +1 -1
- package/lib/module/games/checkers/CheckersScene.js +1 -1
- package/lib/module/games/color-switch/ColorSwitch.js +1 -1
- package/lib/module/games/color-switch/ColorSwitchScene.js +1 -1
- package/lib/module/games/colors-sort/ColorsSort.js +1 -1
- package/lib/module/games/colors-sort/ColorsSortScene.js +1 -1
- package/lib/module/games/connect-em-all/ConnectEmAll.js +1 -1
- package/lib/module/games/connect-em-all/ConnectEmAllScene.js +1 -1
- package/lib/module/games/crossy-road/CrossyRoad.js +1 -1
- package/lib/module/games/crossy-road/CrossyRoadMeshes.js +1 -1
- package/lib/module/games/crossy-road/CrossyRoadPerson.js +1 -1
- package/lib/module/games/dino-jump/DinoJump.js +1 -1
- package/lib/module/games/dino-jump/DinoJumpScene.js +1 -1
- package/lib/module/games/dots-and-boxes/DotsAndBoxes.js +1 -1
- package/lib/module/games/dots-and-boxes/DotsAndBoxesScene.js +1 -1
- package/lib/module/games/falling-sticks/FallingSticks.js +1 -1
- package/lib/module/games/falling-sticks/FallingSticksScene.js +1 -1
- package/lib/module/games/find-different-number/FindDifferentNumber.js +1 -1
- package/lib/module/games/find-different-number/FindDifferentNumberScene.js +1 -1
- package/lib/module/games/flappy-bird/FlappyBird.js +1 -1
- package/lib/module/games/flappy-bird/FlappyBirdScene.js +1 -1
- package/lib/module/games/fruit-merger/FruitMerger.js +1 -1
- package/lib/module/games/fruit-merger/FruitMergerScene.js +1 -1
- package/lib/module/games/fruit-ninja/FruitNinja.js +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaScene.js +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaWorld.js +1 -1
- package/lib/module/games/game-2048/Game2048.js +1 -1
- package/lib/module/games/game-2048/Game2048Scene.js +1 -1
- package/lib/module/games/knife-hit/KnifeHit.js +1 -1
- package/lib/module/games/knife-hit/KnifeHitScene.js +1 -1
- package/lib/module/games/ludo-king/LudoKing.js +1 -1
- package/lib/module/games/ludo-king/LudoKingScene.js +1 -1
- package/lib/module/games/maze-runner/MazeRunner.js +1 -1
- package/lib/module/games/maze-runner/MazeRunnerScene.js +1 -1
- package/lib/module/games/memory-match/MemoryMatch.js +1 -1
- package/lib/module/games/memory-match/MemoryMatchScene.js +1 -1
- package/lib/module/games/mine-sweeper/MineSweeper.js +1 -1
- package/lib/module/games/mine-sweeper/MineSweeperScene.js +1 -1
- package/lib/module/games/mob-control/MobControl.js +1 -1
- package/lib/module/games/mob-control/MobControlGame.js +1 -1
- package/lib/module/games/mob-control/MobControlMeshes.js +1 -1
- package/lib/module/games/mob-control/MobControlRenderer.js +1 -1
- package/lib/module/games/mob-control/MobControlRules.js +1 -1
- package/lib/module/games/mob-control/MobControlScene.js +1 -1
- package/lib/module/games/nuts-and-bolts/NutsAndBolts.js +1 -1
- package/lib/module/games/nuts-and-bolts/NutsAndBoltsScene.js +1 -1
- package/lib/module/games/pac-man/PacMan.js +1 -1
- package/lib/module/games/pac-man/PacManGame.js +1 -1
- package/lib/module/games/pac-man/PacManScene.js +1 -1
- package/lib/module/games/paper-io/PaperIo.js +1 -1
- package/lib/module/games/paper-io/PaperIoMeshes.js +1 -1
- package/lib/module/games/paper-io/PaperIoScene.js +1 -1
- package/lib/module/games/penguin-run/PenguinMeshes.js +1 -1
- package/lib/module/games/penguin-run/PenguinRun.js +1 -1
- package/lib/module/games/penguin-run/PenguinRunScene.js +1 -1
- package/lib/module/games/perfect-circle/PerfectCircle.js +1 -1
- package/lib/module/games/perfect-circle/PerfectCircleScene.js +1 -1
- package/lib/module/games/pipe-connect/PipeConnect.js +1 -1
- package/lib/module/games/pipe-connect/PipeConnectScene.js +1 -1
- package/lib/module/games/play-with-tua/PlayWithTua.js +1 -1
- package/lib/module/games/popit-fidget/PopitFidget.js +1 -1
- package/lib/module/games/popit-fidget/PopitFidgetScene.js +1 -1
- package/lib/module/games/reversi/Reversi.js +1 -1
- package/lib/module/games/reversi/ReversiScene.js +1 -1
- package/lib/module/games/search-grid/SearchGrid.js +1 -1
- package/lib/module/games/search-grid/SearchGridScene.js +1 -1
- package/lib/module/games/shared/BackdropCard.js +1 -1
- package/lib/module/games/shared/EngineBackdrop.js +1 -1
- package/lib/module/games/shared/EngineBlasts.js +1 -1
- package/lib/module/games/shared/EngineBlocks.js +1 -1
- package/lib/module/games/shared/EngineDice.js +1 -1
- package/lib/module/games/shared/EngineFruits.js +1 -1
- package/lib/module/games/shared/EngineGameLoader.js +1 -1
- package/lib/module/games/shared/EngineGameScreen.js +1 -1
- package/lib/module/games/shared/EngineGlyphs.js +1 -1
- package/lib/module/games/shared/EngineHud.js +1 -1
- package/lib/module/games/shared/EngineMeadow.js +1 -1
- package/lib/module/games/shared/EngineTouchSurface.js +1 -1
- package/lib/module/games/shared/GameActivity.js +1 -1
- package/lib/module/games/shared/GameLoading.js +1 -1
- package/lib/module/games/shared/GameLoadingTransition.js +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbersScene.js +1 -1
- package/lib/module/games/snake-3d/Snake3D.js +1 -1
- package/lib/module/games/snake-3d/Snake3DScene.js +1 -1
- package/lib/module/games/snakes-and-ladders/SnakesAndLadders.js +1 -1
- package/lib/module/games/snakes-and-ladders/SnakesAndLaddersScene.js +1 -1
- package/lib/module/games/space-fighter/SpaceFighter.js +1 -1
- package/lib/module/games/space-fighter/SpaceFighterScene.js +1 -1
- package/lib/module/games/spider-solitaire/SpiderSolitaire.js +1 -1
- package/lib/module/games/spider-solitaire/SpiderSolitaireScene.js +1 -1
- package/lib/module/games/stack-tower/StackTower.js +1 -1
- package/lib/module/games/stack-tower/StackTowerScene.js +1 -1
- package/lib/module/games/sudoku/Sudoku.js +1 -1
- package/lib/module/games/sudoku/SudokuScene.js +1 -1
- package/lib/module/games/tangram/Tangram.js +1 -1
- package/lib/module/games/tangram/TangramScene.js +1 -1
- package/lib/module/games/tank-1990/Tank1990.js +1 -1
- package/lib/module/games/tank-1990/Tank1990Scene.js +1 -1
- package/lib/module/games/tic-tac-toe/TicTacToe.js +1 -1
- package/lib/module/games/tic-tac-toe/TicTacToeScene.js +1 -1
- package/lib/module/games/tile-home/TileHome.js +1 -1
- package/lib/module/games/tile-home/TileHomeScene.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHunt.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHuntConstants.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHuntModel.js +1 -1
- package/lib/module/games/treasure-hunt/TreasureHuntScene.js +1 -1
- package/lib/module/games/whack-a-mole/WhackAMole.js +1 -1
- package/lib/module/games/whack-a-mole/WhackAMoleScene.js +1 -1
- package/lib/module/helpers/GameSettingsModal.js +1 -1
- package/lib/module/helpers/GameTheme.js +1 -1
- package/package.json +1 -2
- package/src/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/src/assets/models/explorer/explorer.glb +0 -0
- package/src/assets/models/explorer/params.json +0 -51
- package/src/assets/models/explorer/source.json +0 -222
- package/src/assets/models/play-with-tua/play-with-tua.glb +0 -0
- package/src/assets/models/play-with-tua/sources.json +0 -141
- package/src/assets/sfx/balloon_pop.wav +0 -0
- package/src/assets/sfx/blocked.wav +0 -0
- package/src/assets/sfx/boing.wav +0 -0
- package/src/assets/sfx/bonus.wav +0 -0
- package/src/assets/sfx/boom.wav +0 -0
- package/src/assets/sfx/bump.wav +0 -0
- package/src/assets/sfx/cannon.wav +0 -0
- package/src/assets/sfx/capture.wav +0 -0
- package/src/assets/sfx/card_flip.wav +0 -0
- package/src/assets/sfx/card_place.wav +0 -0
- package/src/assets/sfx/catch.wav +0 -0
- package/src/assets/sfx/chime.wav +0 -0
- package/src/assets/sfx/chomp.wav +0 -0
- package/src/assets/sfx/chomp_alt.wav +0 -0
- package/src/assets/sfx/clack.wav +0 -0
- package/src/assets/sfx/clang.wav +0 -0
- package/src/assets/sfx/clash.wav +0 -0
- package/src/assets/sfx/click.wav +0 -0
- package/src/assets/sfx/coin.wav +0 -0
- package/src/assets/sfx/combo.wav +0 -0
- package/src/assets/sfx/crash.wav +0 -0
- package/src/assets/sfx/crunch.wav +0 -0
- package/src/assets/sfx/dance.wav +0 -0
- package/src/assets/sfx/deselect.wav +0 -0
- package/src/assets/sfx/dice_roll.wav +0 -0
- package/src/assets/sfx/die.wav +0 -0
- package/src/assets/sfx/draw.wav +0 -0
- package/src/assets/sfx/drop.wav +0 -0
- package/src/assets/sfx/eat.wav +0 -0
- package/src/assets/sfx/engine.wav +0 -0
- package/src/assets/sfx/error.wav +0 -0
- package/src/assets/sfx/flag.wav +0 -0
- package/src/assets/sfx/flap.wav +0 -0
- package/src/assets/sfx/flip_disc.wav +0 -0
- package/src/assets/sfx/footstep_run.wav +0 -0
- package/src/assets/sfx/footstep_walk.wav +0 -0
- package/src/assets/sfx/game_over.wav +0 -0
- package/src/assets/sfx/gate.wav +0 -0
- package/src/assets/sfx/hit.wav +0 -0
- package/src/assets/sfx/hop.wav +0 -0
- package/src/assets/sfx/hurt.wav +0 -0
- package/src/assets/sfx/invalid.wav +0 -0
- package/src/assets/sfx/jump.wav +0 -0
- package/src/assets/sfx/ladder.wav +0 -0
- package/src/assets/sfx/land.wav +0 -0
- package/src/assets/sfx/laugh.wav +0 -0
- package/src/assets/sfx/lose.wav +0 -0
- package/src/assets/sfx/merge.wav +0 -0
- package/src/assets/sfx/miss.wav +0 -0
- package/src/assets/sfx/nitro.wav +0 -0
- package/src/assets/sfx/pew.wav +0 -0
- package/src/assets/sfx/place.wav +0 -0
- package/src/assets/sfx/pling.wav +0 -0
- package/src/assets/sfx/point.wav +0 -0
- package/src/assets/sfx/pop.wav +0 -0
- package/src/assets/sfx/pop_soft.wav +0 -0
- package/src/assets/sfx/power_up.wav +0 -0
- package/src/assets/sfx/reveal.wav +0 -0
- package/src/assets/sfx/robot.wav +0 -0
- package/src/assets/sfx/rotate.wav +0 -0
- package/src/assets/sfx/run_steps.wav +0 -0
- package/src/assets/sfx/select.wav +0 -0
- package/src/assets/sfx/shuffle.wav +0 -0
- package/src/assets/sfx/six.wav +0 -0
- package/src/assets/sfx/slice.wav +0 -0
- package/src/assets/sfx/slide.wav +0 -0
- package/src/assets/sfx/snake.wav +0 -0
- package/src/assets/sfx/snap.wav +0 -0
- package/src/assets/sfx/splash.wav +0 -0
- package/src/assets/sfx/star.wav +0 -0
- package/src/assets/sfx/start.wav +0 -0
- package/src/assets/sfx/swoosh.wav +0 -0
- package/src/assets/sfx/tap.wav +0 -0
- package/src/assets/sfx/thud.wav +0 -0
- package/src/assets/sfx/thunk.wav +0 -0
- package/src/assets/sfx/tick.wav +0 -0
- package/src/assets/sfx/time_up.wav +0 -0
- package/src/assets/sfx/tock.wav +0 -0
- package/src/assets/sfx/walk_steps.wav +0 -0
- package/src/assets/sfx/warning.wav +0 -0
- package/src/assets/sfx/whack.wav +0 -0
- package/src/assets/sfx/whoosh.wav +0 -0
- package/src/assets/sfx/win.wav +0 -0
- package/src/assets/sfx/zap.wav +0 -0
- package/src/assets/textures/ad_bolt.png +0 -0
- package/src/assets/textures/ad_bubbles.png +0 -0
- package/src/assets/textures/ad_chevrons.png +0 -0
- package/src/assets/textures/asphalt.png +0 -0
- package/src/assets/textures/bark.jpg +0 -0
- package/src/assets/textures/explorer-atlas.png +0 -0
- package/src/assets/textures/facade.png +0 -0
- package/src/assets/textures/grass.png +0 -0
- package/src/assets/textures/leaves.png +0 -0
- package/src/assets/textures/limestone.jpg +0 -0
- package/src/assets/textures/meadow.jpg +0 -0
- package/src/assets/textures/nature-sources.json +0 -87
- package/src/assets/textures/pine.png +0 -0
- package/src/assets/textures/river.jpg +0 -0
- package/src/assets/textures/soft_glow.png +0 -0
- package/src/games/arrows/Arrows.tsx +0 -69
- package/src/games/arrows/ArrowsConstants.ts +0 -107
- package/src/games/arrows/ArrowsGame.ts +0 -735
- package/src/games/arrows/ArrowsScene.ts +0 -395
- package/src/games/balloon-blaster/BalloonBlaster.tsx +0 -75
- package/src/games/balloon-blaster/BalloonBlasterConstants.ts +0 -79
- package/src/games/balloon-blaster/BalloonBlasterGame.ts +0 -287
- package/src/games/balloon-blaster/BalloonBlasterPhysics.ts +0 -95
- package/src/games/balloon-blaster/BalloonBlasterScene.ts +0 -225
- package/src/games/bike-racing/BikeMeshes.ts +0 -273
- package/src/games/bike-racing/BikeRacing.tsx +0 -211
- package/src/games/bike-racing/BikeRacingConstants.ts +0 -103
- package/src/games/bike-racing/BikeRacingGame.ts +0 -557
- package/src/games/bike-racing/BikeRacingScene.ts +0 -119
- package/src/games/block-blast/BlockBlast.tsx +0 -73
- package/src/games/block-blast/BlockBlastConstants.ts +0 -179
- package/src/games/block-blast/BlockBlastGame.ts +0 -471
- package/src/games/block-blast/BlockBlastScene.ts +0 -156
- package/src/games/block-breaker/BlockBreaker.tsx +0 -79
- package/src/games/block-breaker/BlockBreakerConstants.ts +0 -139
- package/src/games/block-breaker/BlockBreakerGame.ts +0 -459
- package/src/games/block-breaker/BlockBreakerScene.ts +0 -257
- package/src/games/bluk/Bluk.tsx +0 -82
- package/src/games/bluk/BlukConstants.ts +0 -121
- package/src/games/bluk/BlukGame.ts +0 -323
- package/src/games/bluk/BlukRules.ts +0 -154
- package/src/games/bluk/BlukScene.ts +0 -514
- package/src/games/bubble-shooter/BubbleShooter.tsx +0 -76
- package/src/games/bubble-shooter/BubbleShooterConstants.ts +0 -103
- package/src/games/bubble-shooter/BubbleShooterGame.ts +0 -459
- package/src/games/bubble-shooter/BubbleShooterScene.ts +0 -158
- package/src/games/candy-crush/CandyCrush.tsx +0 -77
- package/src/games/candy-crush/CandyCrushConstants.ts +0 -94
- package/src/games/candy-crush/CandyCrushGame.ts +0 -477
- package/src/games/candy-crush/CandyCrushScene.ts +0 -209
- package/src/games/car-racing/CarMeshes.ts +0 -440
- package/src/games/car-racing/CarRacing.tsx +0 -211
- package/src/games/car-racing/CarRacingConstants.ts +0 -167
- package/src/games/car-racing/CarRacingFacade.ts +0 -40
- package/src/games/car-racing/CarRacingGame.ts +0 -539
- package/src/games/car-racing/CarRacingScene.ts +0 -1026
- package/src/games/car-racing/CarRacingScenery.ts +0 -265
- package/src/games/chameleon-run/ChameleonRun.tsx +0 -96
- package/src/games/chameleon-run/ChameleonRunBackdrop.ts +0 -186
- package/src/games/chameleon-run/ChameleonRunConstants.ts +0 -47
- package/src/games/chameleon-run/ChameleonRunGame.ts +0 -309
- package/src/games/chameleon-run/ChameleonRunMeshes.ts +0 -305
- package/src/games/chameleon-run/ChameleonRunRules.ts +0 -252
- package/src/games/chameleon-run/ChameleonRunScene.ts +0 -310
- package/src/games/checkers/Checkers.tsx +0 -83
- package/src/games/checkers/CheckersAI.ts +0 -255
- package/src/games/checkers/CheckersConstants.ts +0 -68
- package/src/games/checkers/CheckersGame.ts +0 -322
- package/src/games/checkers/CheckersRules.ts +0 -167
- package/src/games/checkers/CheckersScene.ts +0 -308
- package/src/games/color-switch/ColorSwitch.tsx +0 -75
- package/src/games/color-switch/ColorSwitchConstants.ts +0 -61
- package/src/games/color-switch/ColorSwitchGame.ts +0 -329
- package/src/games/color-switch/ColorSwitchScene.ts +0 -220
- package/src/games/colors-sort/ColorsSort.tsx +0 -82
- package/src/games/colors-sort/ColorsSortConstants.ts +0 -119
- package/src/games/colors-sort/ColorsSortGame.ts +0 -246
- package/src/games/colors-sort/ColorsSortScene.ts +0 -315
- package/src/games/connect-em-all/ConnectEmAll.tsx +0 -70
- package/src/games/connect-em-all/ConnectEmAllConstants.ts +0 -77
- package/src/games/connect-em-all/ConnectEmAllGame.ts +0 -401
- package/src/games/connect-em-all/ConnectEmAllScene.ts +0 -235
- package/src/games/crossy-road/CrossyRoad.tsx +0 -74
- package/src/games/crossy-road/CrossyRoadCamera.ts +0 -59
- package/src/games/crossy-road/CrossyRoadConstants.ts +0 -132
- package/src/games/crossy-road/CrossyRoadGame.ts +0 -293
- package/src/games/crossy-road/CrossyRoadMeshes.ts +0 -195
- package/src/games/crossy-road/CrossyRoadPerson.ts +0 -198
- package/src/games/crossy-road/CrossyRoadRules.ts +0 -99
- package/src/games/crossy-road/CrossyRoadScene.ts +0 -355
- package/src/games/dino-jump/DinoJump.tsx +0 -77
- package/src/games/dino-jump/DinoJumpConstants.ts +0 -80
- package/src/games/dino-jump/DinoJumpGame.ts +0 -344
- package/src/games/dino-jump/DinoJumpScene.ts +0 -686
- package/src/games/dots-and-boxes/DotsAndBoxes.tsx +0 -83
- package/src/games/dots-and-boxes/DotsAndBoxesConstants.ts +0 -77
- package/src/games/dots-and-boxes/DotsAndBoxesGame.ts +0 -271
- package/src/games/dots-and-boxes/DotsAndBoxesScene.ts +0 -249
- package/src/games/falling-sticks/FallingSticks.tsx +0 -72
- package/src/games/falling-sticks/FallingSticksConstants.ts +0 -62
- package/src/games/falling-sticks/FallingSticksGame.ts +0 -220
- package/src/games/falling-sticks/FallingSticksRules.ts +0 -145
- package/src/games/falling-sticks/FallingSticksScene.ts +0 -245
- package/src/games/find-different-number/FindDifferentNumber.tsx +0 -74
- package/src/games/find-different-number/FindDifferentNumberConstants.ts +0 -74
- package/src/games/find-different-number/FindDifferentNumberGame.ts +0 -174
- package/src/games/find-different-number/FindDifferentNumberRules.ts +0 -43
- package/src/games/find-different-number/FindDifferentNumberScene.ts +0 -193
- package/src/games/flappy-bird/FlappyBird.tsx +0 -73
- package/src/games/flappy-bird/FlappyBirdConstants.ts +0 -95
- package/src/games/flappy-bird/FlappyBirdGame.ts +0 -375
- package/src/games/flappy-bird/FlappyBirdScene.ts +0 -630
- package/src/games/fruit-merger/FruitMerger.tsx +0 -73
- package/src/games/fruit-merger/FruitMergerConstants.ts +0 -102
- package/src/games/fruit-merger/FruitMergerGame.ts +0 -285
- package/src/games/fruit-merger/FruitMergerRules.ts +0 -286
- package/src/games/fruit-merger/FruitMergerScene.ts +0 -498
- package/src/games/fruit-ninja/FruitNinja.tsx +0 -129
- package/src/games/fruit-ninja/FruitNinjaConstants.ts +0 -107
- package/src/games/fruit-ninja/FruitNinjaGame.ts +0 -391
- package/src/games/fruit-ninja/FruitNinjaScene.ts +0 -319
- package/src/games/fruit-ninja/FruitNinjaWorld.ts +0 -17
- package/src/games/game-2048/Game2048.tsx +0 -84
- package/src/games/game-2048/Game2048Constants.ts +0 -68
- package/src/games/game-2048/Game2048Game.ts +0 -181
- package/src/games/game-2048/Game2048Rules.ts +0 -101
- package/src/games/game-2048/Game2048Scene.ts +0 -206
- package/src/games/knife-hit/KnifeHit.tsx +0 -78
- package/src/games/knife-hit/KnifeHitConstants.ts +0 -89
- package/src/games/knife-hit/KnifeHitGame.ts +0 -264
- package/src/games/knife-hit/KnifeHitScene.ts +0 -360
- package/src/games/ludo-king/LudoKing.tsx +0 -86
- package/src/games/ludo-king/LudoKingConstants.ts +0 -212
- package/src/games/ludo-king/LudoKingGame.ts +0 -326
- package/src/games/ludo-king/LudoKingRules.ts +0 -143
- package/src/games/ludo-king/LudoKingScene.ts +0 -397
- package/src/games/maze-runner/MazeRunner.tsx +0 -88
- package/src/games/maze-runner/MazeRunnerConstants.ts +0 -65
- package/src/games/maze-runner/MazeRunnerGame.ts +0 -176
- package/src/games/maze-runner/MazeRunnerRules.ts +0 -161
- package/src/games/maze-runner/MazeRunnerScene.ts +0 -245
- package/src/games/memory-match/MemoryMatch.tsx +0 -77
- package/src/games/memory-match/MemoryMatchConstants.ts +0 -105
- package/src/games/memory-match/MemoryMatchGame.ts +0 -222
- package/src/games/memory-match/MemoryMatchRules.ts +0 -38
- package/src/games/memory-match/MemoryMatchScene.ts +0 -268
- package/src/games/mine-sweeper/MineSweeper.tsx +0 -77
- package/src/games/mine-sweeper/MineSweeperConstants.ts +0 -94
- package/src/games/mine-sweeper/MineSweeperGame.ts +0 -234
- package/src/games/mine-sweeper/MineSweeperRules.ts +0 -67
- package/src/games/mine-sweeper/MineSweeperScene.ts +0 -262
- package/src/games/mob-control/MobControl.tsx +0 -73
- package/src/games/mob-control/MobControlConstants.ts +0 -106
- package/src/games/mob-control/MobControlGame.ts +0 -176
- package/src/games/mob-control/MobControlMeshes.ts +0 -85
- package/src/games/mob-control/MobControlRenderer.ts +0 -108
- package/src/games/mob-control/MobControlRules.ts +0 -522
- package/src/games/mob-control/MobControlScene.ts +0 -399
- package/src/games/number-search/NumberSearch.tsx +0 -18
- package/src/games/number-search/NumberSearchNumbers.ts +0 -623
- package/src/games/nuts-and-bolts/NutsAndBolts.tsx +0 -85
- package/src/games/nuts-and-bolts/NutsAndBoltsConstants.ts +0 -75
- package/src/games/nuts-and-bolts/NutsAndBoltsGame.ts +0 -298
- package/src/games/nuts-and-bolts/NutsAndBoltsRules.ts +0 -474
- package/src/games/nuts-and-bolts/NutsAndBoltsScene.ts +0 -498
- package/src/games/pac-man/PacMan.tsx +0 -86
- package/src/games/pac-man/PacManConstants.ts +0 -169
- package/src/games/pac-man/PacManGame.ts +0 -406
- package/src/games/pac-man/PacManRules.ts +0 -255
- package/src/games/pac-man/PacManScene.ts +0 -387
- package/src/games/paper-io/PaperIo.tsx +0 -91
- package/src/games/paper-io/PaperIoConstants.ts +0 -86
- package/src/games/paper-io/PaperIoGame.ts +0 -193
- package/src/games/paper-io/PaperIoMeshes.ts +0 -233
- package/src/games/paper-io/PaperIoRules.ts +0 -315
- package/src/games/paper-io/PaperIoScene.ts +0 -310
- package/src/games/penguin-run/PenguinMeshes.ts +0 -269
- package/src/games/penguin-run/PenguinModel.ts +0 -712
- package/src/games/penguin-run/PenguinRun.tsx +0 -77
- package/src/games/penguin-run/PenguinRunConstants.ts +0 -69
- package/src/games/penguin-run/PenguinRunGame.ts +0 -406
- package/src/games/penguin-run/PenguinRunScene.ts +0 -949
- package/src/games/perfect-circle/PerfectCircle.tsx +0 -74
- package/src/games/perfect-circle/PerfectCircleConstants.ts +0 -52
- package/src/games/perfect-circle/PerfectCircleGame.ts +0 -153
- package/src/games/perfect-circle/PerfectCircleRules.ts +0 -111
- package/src/games/perfect-circle/PerfectCircleScene.ts +0 -176
- package/src/games/pipe-connect/PipeConnect.tsx +0 -69
- package/src/games/pipe-connect/PipeConnectConstants.ts +0 -101
- package/src/games/pipe-connect/PipeConnectGame.ts +0 -179
- package/src/games/pipe-connect/PipeConnectRules.ts +0 -326
- package/src/games/pipe-connect/PipeConnectScene.ts +0 -271
- package/src/games/play-with-tua/PlayWithTua.tsx +0 -123
- package/src/games/play-with-tua/PlayWithTuaConstants.ts +0 -70
- package/src/games/play-with-tua/PlayWithTuaGame.ts +0 -167
- package/src/games/play-with-tua/PlayWithTuaModel.ts +0 -42
- package/src/games/play-with-tua/PlayWithTuaScene.ts +0 -36
- package/src/games/popit-fidget/PopitFidget.tsx +0 -72
- package/src/games/popit-fidget/PopitFidgetConstants.ts +0 -71
- package/src/games/popit-fidget/PopitFidgetGame.ts +0 -148
- package/src/games/popit-fidget/PopitFidgetScene.ts +0 -242
- package/src/games/reversi/Reversi.tsx +0 -94
- package/src/games/reversi/ReversiConstants.ts +0 -67
- package/src/games/reversi/ReversiGame.ts +0 -266
- package/src/games/reversi/ReversiRules.ts +0 -176
- package/src/games/reversi/ReversiScene.ts +0 -240
- package/src/games/search-grid/SearchGrid.tsx +0 -92
- package/src/games/search-grid/SearchGridConstants.ts +0 -178
- package/src/games/search-grid/SearchGridGame.ts +0 -209
- package/src/games/search-grid/SearchGridScene.ts +0 -301
- package/src/games/shared/BackdropCard.tsx +0 -54
- package/src/games/shared/EngineBackdrop.ts +0 -300
- package/src/games/shared/EngineBlasts.ts +0 -136
- package/src/games/shared/EngineBlocks.ts +0 -91
- package/src/games/shared/EngineControlText.ts +0 -17
- package/src/games/shared/EngineDice.ts +0 -250
- package/src/games/shared/EngineFootprint.ts +0 -35
- package/src/games/shared/EngineFruits.ts +0 -1435
- package/src/games/shared/EngineGameLoader.tsx +0 -88
- package/src/games/shared/EngineGameScreen.tsx +0 -325
- package/src/games/shared/EngineGameWidth.ts +0 -3
- package/src/games/shared/EngineGameWidth.web.ts +0 -3
- package/src/games/shared/EngineGlyphs.ts +0 -178
- package/src/games/shared/EngineHud.ts +0 -815
- package/src/games/shared/EngineLoading.ts +0 -477
- package/src/games/shared/EngineMeadow.ts +0 -725
- package/src/games/shared/EngineMeshCache.ts +0 -15
- package/src/games/shared/EngineMeshes.ts +0 -10
- package/src/games/shared/EngineRacingChase.ts +0 -21
- package/src/games/shared/EngineRacingHandling.ts +0 -48
- package/src/games/shared/EngineReadyContext.ts +0 -4
- package/src/games/shared/EngineTilt.ts +0 -44
- package/src/games/shared/EngineTouchSurface.tsx +0 -103
- package/src/games/shared/EngineUtils.ts +0 -52
- package/src/games/shared/GameActivity.ts +0 -13
- package/src/games/shared/GameLoading.tsx +0 -202
- package/src/games/shared/GameLoadingTransition.tsx +0 -61
- package/src/games/shared/ProceduralCacheVersion.ts +0 -2
- package/src/games/shared/useEngineGame.ts +0 -240
- package/src/games/shared/useGameResource.ts +0 -21
- package/src/games/shared/useLiveState.ts +0 -58
- package/src/games/sliding-numbers/SlidingNumbers.tsx +0 -71
- package/src/games/sliding-numbers/SlidingNumbersConstants.ts +0 -65
- package/src/games/sliding-numbers/SlidingNumbersGame.ts +0 -192
- package/src/games/sliding-numbers/SlidingNumbersRules.ts +0 -79
- package/src/games/sliding-numbers/SlidingNumbersScene.ts +0 -193
- package/src/games/snake-3d/Snake3D.tsx +0 -76
- package/src/games/snake-3d/Snake3DConstants.ts +0 -61
- package/src/games/snake-3d/Snake3DGame.ts +0 -192
- package/src/games/snake-3d/Snake3DMotion.ts +0 -22
- package/src/games/snake-3d/Snake3DRules.ts +0 -116
- package/src/games/snake-3d/Snake3DScene.ts +0 -322
- package/src/games/snakes-and-ladders/SnakesAndLadders.tsx +0 -87
- package/src/games/snakes-and-ladders/SnakesAndLaddersConstants.ts +0 -136
- package/src/games/snakes-and-ladders/SnakesAndLaddersGame.ts +0 -310
- package/src/games/snakes-and-ladders/SnakesAndLaddersScene.ts +0 -515
- package/src/games/space-fighter/SpaceFighter.tsx +0 -74
- package/src/games/space-fighter/SpaceFighterConstants.ts +0 -86
- package/src/games/space-fighter/SpaceFighterGame.ts +0 -292
- package/src/games/space-fighter/SpaceFighterScene.ts +0 -550
- package/src/games/spider-solitaire/SpiderSolitaire.tsx +0 -80
- package/src/games/spider-solitaire/SpiderSolitaireConstants.ts +0 -107
- package/src/games/spider-solitaire/SpiderSolitaireGame.ts +0 -387
- package/src/games/spider-solitaire/SpiderSolitaireRules.ts +0 -123
- package/src/games/spider-solitaire/SpiderSolitaireScene.ts +0 -352
- package/src/games/stack-tower/StackTower.tsx +0 -76
- package/src/games/stack-tower/StackTowerConstants.ts +0 -74
- package/src/games/stack-tower/StackTowerGame.ts +0 -236
- package/src/games/stack-tower/StackTowerScene.ts +0 -186
- package/src/games/sudoku/Sudoku.tsx +0 -73
- package/src/games/sudoku/SudokuConstants.ts +0 -127
- package/src/games/sudoku/SudokuGame.ts +0 -250
- package/src/games/sudoku/SudokuRules.ts +0 -125
- package/src/games/sudoku/SudokuScene.ts +0 -304
- package/src/games/tangram/Tangram.tsx +0 -79
- package/src/games/tangram/TangramConstants.ts +0 -64
- package/src/games/tangram/TangramGame.ts +0 -356
- package/src/games/tangram/TangramRules.ts +0 -409
- package/src/games/tangram/TangramScene.ts +0 -203
- package/src/games/tank-1990/Tank1990.tsx +0 -78
- package/src/games/tank-1990/Tank1990Constants.ts +0 -81
- package/src/games/tank-1990/Tank1990Game.ts +0 -331
- package/src/games/tank-1990/Tank1990Rules.ts +0 -390
- package/src/games/tank-1990/Tank1990Scene.ts +0 -434
- package/src/games/tic-tac-toe/TicTacToe.tsx +0 -98
- package/src/games/tic-tac-toe/TicTacToeConstants.ts +0 -69
- package/src/games/tic-tac-toe/TicTacToeGame.ts +0 -200
- package/src/games/tic-tac-toe/TicTacToeRules.ts +0 -103
- package/src/games/tic-tac-toe/TicTacToeScene.ts +0 -245
- package/src/games/tile-home/TileHome.tsx +0 -92
- package/src/games/tile-home/TileHomeConstants.ts +0 -139
- package/src/games/tile-home/TileHomeGame.ts +0 -435
- package/src/games/tile-home/TileHomeScene.ts +0 -154
- package/src/games/treasure-hunt/TreasureHunt.tsx +0 -206
- package/src/games/treasure-hunt/TreasureHuntConstants.ts +0 -266
- package/src/games/treasure-hunt/TreasureHuntControls.ts +0 -280
- package/src/games/treasure-hunt/TreasureHuntGame.ts +0 -402
- package/src/games/treasure-hunt/TreasureHuntMeshes.ts +0 -422
- package/src/games/treasure-hunt/TreasureHuntModel.ts +0 -506
- package/src/games/treasure-hunt/TreasureHuntRules.ts +0 -1210
- package/src/games/treasure-hunt/TreasureHuntScene.ts +0 -586
- package/src/games/whack-a-mole/WhackAMole.tsx +0 -79
- package/src/games/whack-a-mole/WhackAMoleConstants.ts +0 -94
- package/src/games/whack-a-mole/WhackAMoleGame.ts +0 -233
- package/src/games/whack-a-mole/WhackAMoleScene.ts +0 -349
- package/src/games/word-search/WordSearch.tsx +0 -18
- package/src/games/word-search/WordSearchWords.ts +0 -1642
- package/src/helpers/GamePatternData.ts +0 -120
- package/src/helpers/GameSettingsModal.tsx +0 -384
- package/src/helpers/GameTheme.tsx +0 -37
- package/src/helpers/IconPaths.ts +0 -6
- package/src/helpers/index.ts +0 -2
- package/src/index.tsx +0 -60
- package/src/services/GameAssetLoader.ts +0 -111
- package/src/services/GameAssets.ts +0 -149
- package/src/services/GameLoaders.ts +0 -93
- package/src/services/GamesConstants.ts +0 -193
- package/src/services/GamesMetadata.ts +0 -711
- package/src/services/GamesService.ts +0 -116
- package/src/services/HapticsService.ts +0 -48
- package/src/services/ScoringService.ts +0 -19
- package/src/services/SfxSources.ts +0 -81
- package/src/services/SoundsService.ts +0 -582
- package/src/services/ThemeService.ts +0 -281
- package/src/services/UtilsService.ts +0 -134
- package/src/services/index.ts +0 -6
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
// Raised, bevelled tiles with patterned backs, ivory faces and real 3D fruit.
|
|
2
|
-
// Each match gets a mint border and a check seal; all decoration shares cached meshes.
|
|
3
|
-
import {
|
|
4
|
-
type Color,
|
|
5
|
-
effects,
|
|
6
|
-
type Entity,
|
|
7
|
-
extrude,
|
|
8
|
-
merge,
|
|
9
|
-
type Model,
|
|
10
|
-
Path,
|
|
11
|
-
type Quat,
|
|
12
|
-
quat,
|
|
13
|
-
recolor,
|
|
14
|
-
type Vec3,
|
|
15
|
-
type World
|
|
16
|
-
} from '@nayan-ui/engine';
|
|
17
|
-
import type { GameTheme } from '../../services/ThemeService';
|
|
18
|
-
import { setBackdropTheme, spawnBackdrop } from '../shared/EngineBackdrop';
|
|
19
|
-
import { spawnRaisedBlock } from '../shared/EngineBlocks';
|
|
20
|
-
import { getFruit } from '../shared/EngineFruits';
|
|
21
|
-
import { cacheGameAsset, createGameMesh as createShape } from '../shared/EngineMeshes';
|
|
22
|
-
import { placeTiltedBackdrop } from '../shared/EngineTilt';
|
|
23
|
-
import { toColor } from '../shared/EngineUtils';
|
|
24
|
-
import { cardCentre, COLORS, FACE_SIZE, FLIP_TIME, FRUIT_DETAIL, FRUIT_SIZE, type Layout, THICKNESS } from './MemoryMatchConstants';
|
|
25
|
-
import { type Card, FRUIT_SYMBOLS } from './MemoryMatchRules';
|
|
26
|
-
|
|
27
|
-
const meshes = new Map<string, Model>();
|
|
28
|
-
const cached = (key: string, build: () => Model) => {
|
|
29
|
-
let m = meshes.get(key);
|
|
30
|
-
if (!m) {
|
|
31
|
-
m = cacheGameAsset('memory-match/MemoryMatchScene:' + key, build);
|
|
32
|
-
meshes.set(key, m);
|
|
33
|
-
}
|
|
34
|
-
return m;
|
|
35
|
-
};
|
|
36
|
-
const BACKDROP_DEPTH = 30;
|
|
37
|
-
const RADIUS = 0.16;
|
|
38
|
-
const FACE_UP: Quat = [0, 0, 0, 1];
|
|
39
|
-
const FACE_DOWN = quat.y(Math.PI);
|
|
40
|
-
const EDGE_ON = quat.y(Math.PI / 2);
|
|
41
|
-
|
|
42
|
-
const square = (inset: number, path = new Path()) =>
|
|
43
|
-
path.addRoundedRect(-0.5 + inset, -0.5 + inset, 1 - inset * 2, 1 - inset * 2, Math.max(0.01, RADIUS - inset));
|
|
44
|
-
|
|
45
|
-
const flat = (path: Path, z: number, color: Color) => ({
|
|
46
|
-
geometry: extrude(path, { depth: 0, fillRule: 'evenodd' as const }),
|
|
47
|
-
position: [0, 0, z] as Vec3,
|
|
48
|
-
color
|
|
49
|
-
});
|
|
50
|
-
const outline = (inset: number, width: number) => square(inset + width, square(inset));
|
|
51
|
-
|
|
52
|
-
/** Matte ink avoids the moving white glare of glossy cards under the scene light. */
|
|
53
|
-
const backMesh = () =>
|
|
54
|
-
cached('mm-back', () => {
|
|
55
|
-
const top = toColor(COLORS.BACK_TOP);
|
|
56
|
-
const bottom = toColor(COLORS.BACK_BOTTOM);
|
|
57
|
-
const gradient = recolor(extrude(square(0.012), { depth: 0 }), ({ position: [x, y] }) => {
|
|
58
|
-
const t = Math.max(0, Math.min(1, (x + 0.5 + (0.5 - y)) / 2));
|
|
59
|
-
return [top[0] + (bottom[0] - top[0]) * t, top[1] + (bottom[1] - top[1]) * t, top[2] + (bottom[2] - top[2]) * t, 1];
|
|
60
|
-
});
|
|
61
|
-
const mini = extrude(new Path().addRoundedRect(-0.12, -0.165, 0.24, 0.33, 0.04), { depth: 0.008 });
|
|
62
|
-
const dots = new Path();
|
|
63
|
-
for (const x of [-0.35, 0.35]) for (const y of [-0.35, 0.35]) dots.addCircle(x, y, 0.012);
|
|
64
|
-
const print = merge([
|
|
65
|
-
{ geometry: gradient },
|
|
66
|
-
flat(outline(0.065, 0.009), 0.002, toColor(COLORS.BACK_BORDER)),
|
|
67
|
-
flat(new Path().addCircle(0, 0, 0.275), 0.003, toColor(COLORS.BACK_HALO)),
|
|
68
|
-
flat(dots, 0.003, toColor(COLORS.BACK_BORDER)),
|
|
69
|
-
{ geometry: mini, position: [-0.065, 0.025, 0.009], rotation: quat.z(0.2), color: toColor(COLORS.BACK_PAIR) },
|
|
70
|
-
{ geometry: mini, position: [0.065, -0.025, 0.022], rotation: quat.z(-0.18), color: toColor(COLORS.BACK_MOTIF) },
|
|
71
|
-
flat(new Path().addCircle(0.065, 0.025, 0.035), 0.028, toColor(COLORS.BACK_TOP))
|
|
72
|
-
]);
|
|
73
|
-
return createShape(merge([{ geometry: print, rotation: FACE_DOWN, position: [0, 0, -THICKNESS / 2 - 0.002] }]), { center: false, shine: 0 });
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const frontMesh = () =>
|
|
77
|
-
cached('mm-front', () => createShape(extrude(outline(0.065, 0.01), { depth: 0, fillRule: 'evenodd' }), { center: false, shine: 0 }));
|
|
78
|
-
|
|
79
|
-
const sealMesh = () =>
|
|
80
|
-
cached('mm-seal', () =>
|
|
81
|
-
createShape(
|
|
82
|
-
merge([
|
|
83
|
-
flat(new Path().addCircle(0, 0, 0.075), 0, toColor(COLORS.MATCHED_BADGE)),
|
|
84
|
-
flat(
|
|
85
|
-
new Path().addPolygon([
|
|
86
|
-
[-0.037, 0],
|
|
87
|
-
[-0.012, 0.025],
|
|
88
|
-
[0.04, -0.027],
|
|
89
|
-
[0.029, -0.038],
|
|
90
|
-
[-0.012, 0.003],
|
|
91
|
-
[-0.026, -0.011]
|
|
92
|
-
]),
|
|
93
|
-
0.002,
|
|
94
|
-
[1, 1, 1, 1]
|
|
95
|
-
)
|
|
96
|
-
]),
|
|
97
|
-
{ center: false, shine: 0 }
|
|
98
|
-
)
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// ── Spawning ──────────────────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
/** A shared transform keeps the stock, printed faces, match seal and fruit together during a flip. */
|
|
104
|
-
type CardView = { root: Entity; body: Entity; backBody: Entity; back: Entity; front: Entity; seal: Entity; fruit: Entity; fruitScale: number };
|
|
105
|
-
export type Scene = { backdrop: Entity; cards: CardView[] };
|
|
106
|
-
|
|
107
|
-
const SURFACE = 0;
|
|
108
|
-
const restZ = (l: Layout) => SURFACE + (l.card * THICKNESS) / 2;
|
|
109
|
-
const frontColor = (state: Card['state']): Color => toColor(state === 'matched' ? COLORS.CARD_MATCHED : COLORS.CARD_FRONT);
|
|
110
|
-
const borderColor = (state: Card['state']): Color => toColor(state === 'matched' ? COLORS.MATCHED_BORDER : COLORS.FRONT_BORDER);
|
|
111
|
-
|
|
112
|
-
/** Center the fruit's projected bounds on the face, accounting for its height under the tilted camera. */
|
|
113
|
-
function fruitFit(symbol: number, tilt: number) {
|
|
114
|
-
const f = getFruit(FRUIT_SYMBOLS[symbol]!, FRUIT_DETAIL);
|
|
115
|
-
const lean = Math.tan(tilt);
|
|
116
|
-
const s = FRUIT_SIZE / Math.max(f.whole.size[0], f.whole.size[1] + f.whole.size[2] * lean);
|
|
117
|
-
const depth = f.whole.size[2] * s;
|
|
118
|
-
const lift = depth / 2 + 0.01;
|
|
119
|
-
return {
|
|
120
|
-
model: f.whole,
|
|
121
|
-
scale: s,
|
|
122
|
-
position: [-f.center[0] * s, -f.center[1] * s - lift * lean, THICKNESS / 2 + lift - f.center[2] * s] as Vec3
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function spawnCard(world: World, l: Layout, i: number, card: Card): CardView {
|
|
127
|
-
const [x, y] = cardCentre(l, i);
|
|
128
|
-
const up = card.state !== 'down';
|
|
129
|
-
const root = world.spawn({ mesh: 'none', position: [x, y, restZ(l)], rotation: up ? FACE_UP : FACE_DOWN, scale: l.card, pickable: false });
|
|
130
|
-
// Two shared Tile Home blocks meet at the middle, so either face has a rounded raised rim when flipped.
|
|
131
|
-
const body = spawnRaisedBlock(world, {
|
|
132
|
-
parent: root,
|
|
133
|
-
position: [0, 0, THICKNESS / 4],
|
|
134
|
-
size: [1, 1, THICKNESS / 2],
|
|
135
|
-
color: frontColor(card.state),
|
|
136
|
-
gloss: false,
|
|
137
|
-
shadow: false
|
|
138
|
-
});
|
|
139
|
-
const backBody = spawnRaisedBlock(world, {
|
|
140
|
-
parent: root,
|
|
141
|
-
position: [0, 0, -THICKNESS / 4],
|
|
142
|
-
size: [1, 1, THICKNESS / 2],
|
|
143
|
-
color: toColor(COLORS.BACK_BOTTOM),
|
|
144
|
-
gloss: false,
|
|
145
|
-
shadow: false
|
|
146
|
-
});
|
|
147
|
-
world.set(backBody, { rotation: FACE_DOWN });
|
|
148
|
-
const back = world.spawn({ mesh: backMesh(), parent: root, scale: [FACE_SIZE, FACE_SIZE, 1], pickable: false });
|
|
149
|
-
const front = world.spawn({
|
|
150
|
-
mesh: frontMesh(),
|
|
151
|
-
parent: root,
|
|
152
|
-
position: [0, 0, THICKNESS / 2 + 0.002],
|
|
153
|
-
scale: [FACE_SIZE, FACE_SIZE, 1],
|
|
154
|
-
color: borderColor(card.state),
|
|
155
|
-
pickable: false
|
|
156
|
-
});
|
|
157
|
-
const seal = world.spawn({
|
|
158
|
-
mesh: sealMesh(),
|
|
159
|
-
parent: root,
|
|
160
|
-
position: [0.34 * FACE_SIZE, 0.34 * FACE_SIZE, THICKNESS / 2 + 0.006],
|
|
161
|
-
scale: card.state === 'matched' ? FACE_SIZE : 0,
|
|
162
|
-
pickable: false
|
|
163
|
-
});
|
|
164
|
-
const f = fruitFit(card.symbol, l.tilt);
|
|
165
|
-
const fruit = world.spawn({ mesh: f.model, parent: root, position: f.position, scale: up ? f.scale : 0, pickable: false });
|
|
166
|
-
return { root, body, backBody, back, front, seal, fruit, fruitScale: f.scale };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function buildScene(world: World, l: Layout, theme: GameTheme, cards: Card[]): Scene {
|
|
170
|
-
const backdrop = spawnBackdrop(world, l.width, l.height, -BACKDROP_DEPTH, theme);
|
|
171
|
-
placeTiltedBackdrop(world, backdrop, l, BACKDROP_DEPTH);
|
|
172
|
-
return { backdrop, cards: cards.map((c, i) => spawnCard(world, l, i, c)) };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function removeScene(world: World, s: Scene) {
|
|
176
|
-
for (const e of [s.backdrop, ...s.cards.map((c) => c.root)]) world.despawn(e);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export function setSceneTheme(world: World, s: Scene, theme: GameTheme) {
|
|
180
|
-
setBackdropTheme(world, s.backdrop, theme);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ── Motion ────────────────────────────────────────────────────────────
|
|
184
|
-
|
|
185
|
-
/** A new deal: every card face down with its new fruit, popping in one after another. */
|
|
186
|
-
export function deal(world: World, l: Layout, s: Scene, cards: Card[]) {
|
|
187
|
-
cards.forEach((card, i) => {
|
|
188
|
-
const old = s.cards[i];
|
|
189
|
-
if (old) world.despawn(old.root);
|
|
190
|
-
const v = spawnCard(world, l, i, card);
|
|
191
|
-
s.cards[i] = v;
|
|
192
|
-
world.set(v.root, { scale: 0.01 });
|
|
193
|
-
world.animate(v.root, { scale: l.card }, { duration: 0.26, delay: i * 0.022, easing: 'back' });
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** A card turns over (face up or face down), lifting off the table as it turns; its fruit shows only face up. */
|
|
198
|
-
export function flip(world: World, l: Layout, s: Scene, i: number, up: boolean) {
|
|
199
|
-
const v = s.cards[i];
|
|
200
|
-
if (!v) return;
|
|
201
|
-
const [x, y] = cardCentre(l, i);
|
|
202
|
-
const z = restZ(l);
|
|
203
|
-
world.stopAnimation(v.root);
|
|
204
|
-
world.animate(
|
|
205
|
-
v.root,
|
|
206
|
-
[
|
|
207
|
-
{ rotation: EDGE_ON, position: [x, y, z + l.card * 0.28], at: 0.5 },
|
|
208
|
-
{ rotation: up ? FACE_UP : FACE_DOWN, position: [x, y, z] }
|
|
209
|
-
],
|
|
210
|
-
{ duration: FLIP_TIME, easing: 'easeInOut' }
|
|
211
|
-
);
|
|
212
|
-
world.stopAnimation(v.fruit);
|
|
213
|
-
if (up) {
|
|
214
|
-
world.set(v.fruit, { scale: 0 });
|
|
215
|
-
world.animate(v.fruit, { scale: v.fruitScale }, { duration: FLIP_TIME / 2, delay: FLIP_TIME / 2, easing: 'easeOut' });
|
|
216
|
-
} else world.animate(v.fruit, { scale: 0 }, { duration: FLIP_TIME / 2, easing: 'easeIn' });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** A pair found: both cards turn green and pulse. */
|
|
220
|
-
export function matched(world: World, l: Layout, s: Scene, i: number) {
|
|
221
|
-
const v = s.cards[i];
|
|
222
|
-
if (!v) return;
|
|
223
|
-
world.set(v.body, { color: frontColor('matched') });
|
|
224
|
-
world.set(v.front, { color: borderColor('matched') });
|
|
225
|
-
world.animate(v.seal, { scale: FACE_SIZE }, { duration: 0.18, delay: FLIP_TIME, easing: 'back' });
|
|
226
|
-
world.animate(v.root, [{ scale: l.card * 1.04, at: 0.4 }, { scale: l.card }], { duration: 0.32, delay: FLIP_TIME, easing: 'easeOut' });
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/** A miss: both cards shake. */
|
|
230
|
-
export function mismatch(world: World, l: Layout, s: Scene, i: number) {
|
|
231
|
-
const v = s.cards[i];
|
|
232
|
-
if (v) world.animate(v.root, { shake: l.card * 0.06 }, { duration: 0.26, delay: FLIP_TIME });
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/** Every pair found: a pulse wave across the board, and confetti. */
|
|
236
|
-
export function celebrate(world: World, l: Layout, s: Scene) {
|
|
237
|
-
world.animate(
|
|
238
|
-
s.cards.map((v) => v.root),
|
|
239
|
-
[{ scale: l.card * 1.05, at: 0.4 }, { scale: l.card }],
|
|
240
|
-
{
|
|
241
|
-
duration: 0.32,
|
|
242
|
-
delay: 0.25,
|
|
243
|
-
stagger: 0.035,
|
|
244
|
-
easing: 'easeOut'
|
|
245
|
-
}
|
|
246
|
-
);
|
|
247
|
-
effects.confetti(world, {
|
|
248
|
-
position: [0, 0, l.card],
|
|
249
|
-
count: 70,
|
|
250
|
-
size: l.card * 0.08,
|
|
251
|
-
speed: l.boardW * 1.4,
|
|
252
|
-
lifetime: 1.3,
|
|
253
|
-
gravity: -l.boardW * 2.4,
|
|
254
|
-
colors: ['#FFD700', '#4CAF50', '#5b7cfa', '#FF6B6B'].map((c) => toColor(c))
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/** Out of time: the cards not matched dim. */
|
|
259
|
-
export function timeUp(world: World, s: Scene, cards: Card[]) {
|
|
260
|
-
const grey: Color = [0.55, 0.55, 0.6, 1];
|
|
261
|
-
const targets: Entity[] = [];
|
|
262
|
-
cards.forEach((c, i) => {
|
|
263
|
-
const v = s.cards[i];
|
|
264
|
-
if (!v || c.state === 'matched') return;
|
|
265
|
-
targets.push(v.body, v.backBody, v.back);
|
|
266
|
-
});
|
|
267
|
-
world.animate(targets, { color: grey }, { duration: 0.4 });
|
|
268
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
// Mine Sweeper's screen, on the Nayan engine: the board, tiles, counts, flags and mines are one 3D scene
|
|
2
|
-
// (an orthographic camera leaning back over the board), under Playtura's scoreboard, controls and
|
|
3
|
-
// modals. The play is in MineSweeperGame.ts (the board's rules in MineSweeperRules.ts), what it draws
|
|
4
|
-
// in MineSweeperScene.ts.
|
|
5
|
-
//
|
|
6
|
-
// One world serves the whole visit: Start, Stop and Play Again reset it in place and a theme change
|
|
7
|
-
// repaints the board, so the GPU view is never rebuilt. Reveals, flags and explosions animate in the
|
|
8
|
-
// engine's Rust core; JS reads the finger and steps the clock and the world once a frame.
|
|
9
|
-
import { HapticType } from '../../services/HapticsService';
|
|
10
|
-
import { GAME_SOUNDS } from '../../services/SoundsService';
|
|
11
|
-
import { GAME_IDS, isTablet } from '../../services/UtilsService';
|
|
12
|
-
import { engineGame, EngineGameScreen } from '../shared/EngineGameScreen';
|
|
13
|
-
import { scoreColumn, textColumn, timerColumn } from '../shared/EngineHud';
|
|
14
|
-
import { useEngineGame } from '../shared/useEngineGame';
|
|
15
|
-
import { useLiveState } from '../shared/useLiveState';
|
|
16
|
-
import { boardFor, getLayout, LIGHT, TIME_LIMIT } from './MineSweeperConstants';
|
|
17
|
-
import { createGame, dispose, drag, type GameEvent, reset, setLayout, setTheme, start, tick } from './MineSweeperGame';
|
|
18
|
-
|
|
19
|
-
const SOUNDS = GAME_SOUNDS.MINE_SWEEPER;
|
|
20
|
-
// As the original game: the board's size is fixed for the device
|
|
21
|
-
const BOARD = boardFor(isTablet);
|
|
22
|
-
|
|
23
|
-
/** Each event's sound and haptic, as the original version played them. */
|
|
24
|
-
const FEEDBACK: Record<GameEvent, { sound: (typeof SOUNDS)[keyof typeof SOUNDS]; haptic: HapticType }> = {
|
|
25
|
-
reveal: { sound: SOUNDS.REVEAL, haptic: HapticType.LIGHT },
|
|
26
|
-
flag: { sound: SOUNDS.FLAG, haptic: HapticType.MEDIUM },
|
|
27
|
-
mine: { sound: SOUNDS.MINE_HIT, haptic: HapticType.ERROR }
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const MineSweeper = engineGame(GAME_IDS.MINE_SWEEPER, (props) => {
|
|
31
|
-
const [score, setScore] = useLiveState(0);
|
|
32
|
-
const [time, setTime] = useLiveState(TIME_LIMIT);
|
|
33
|
-
const [mines, setMines] = useLiveState(BOARD.mines);
|
|
34
|
-
|
|
35
|
-
// Win or lose when the round ends
|
|
36
|
-
const { theme, status, screen, gameOver } = useEngineGame(props, {
|
|
37
|
-
gameId: GAME_IDS.MINE_SWEEPER,
|
|
38
|
-
create: (t) => createGame(t, BOARD),
|
|
39
|
-
start,
|
|
40
|
-
reset,
|
|
41
|
-
dispose,
|
|
42
|
-
tick,
|
|
43
|
-
setTheme,
|
|
44
|
-
resize: (g, width, height, offset) => {
|
|
45
|
-
const l = getLayout(width, height, offset, BOARD);
|
|
46
|
-
setLayout(g, l);
|
|
47
|
-
return l.boardWidth;
|
|
48
|
-
},
|
|
49
|
-
drag,
|
|
50
|
-
sounds: SOUNDS,
|
|
51
|
-
onStart: (fx) => fx(SOUNDS.START, HapticType.SUCCESS),
|
|
52
|
-
onStop: (fx) => fx(null, HapticType.MEDIUM),
|
|
53
|
-
onOver: (fx, s) => fx(s === 'win' ? SOUNDS.WIN : SOUNDS.GAME_OVER, s === 'win' ? HapticType.SUCCESS : HapticType.ERROR),
|
|
54
|
-
wire: (g, fx) => {
|
|
55
|
-
g.onScore = setScore;
|
|
56
|
-
g.onTime = setTime;
|
|
57
|
-
g.onMines = setMines;
|
|
58
|
-
g.onEvent = (event) => fx(FEEDBACK[event].sound, FEEDBACK[event].haptic);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<EngineGameScreen
|
|
64
|
-
{...screen}
|
|
65
|
-
light={LIGHT}
|
|
66
|
-
columns={[scoreColumn(score), textColumn('MINES', mines), timerColumn(time, theme)]}
|
|
67
|
-
subtext="Reveal cells, flag mines!"
|
|
68
|
-
gameOver={gameOver({
|
|
69
|
-
title: status === 'win' ? 'YOU WON!' : 'BOOM!',
|
|
70
|
-
message: status === 'win' ? 'All mines cleared!' : 'You hit a mine!',
|
|
71
|
-
scoreLabel: 'Final Score',
|
|
72
|
-
score,
|
|
73
|
-
buttonText: 'Sweep Again!'
|
|
74
|
-
})}
|
|
75
|
-
/>
|
|
76
|
-
);
|
|
77
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// Mine Sweeper's rules, colors, timings and layout. Rules, board sizes and scoring match the original version
|
|
2
|
-
// (games/mine-sweeper), so scores carry over.
|
|
3
|
-
import { getScoreBoardTop } from '../shared/EngineHud';
|
|
4
|
-
import type { TiltView } from '../shared/EngineTilt';
|
|
5
|
-
import { frontLight, gameWidthFor } from '../shared/EngineUtils';
|
|
6
|
-
|
|
7
|
-
// ── Rules ─────────────────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
export type Board = { rows: number; cols: number; mines: number };
|
|
10
|
-
/** 9 x 10 on a phone, 12 x 14 on a tablet (as the original game sized it), with 5 mines on either. */
|
|
11
|
-
export const boardFor = (tablet: boolean): Board => (tablet ? { rows: 14, cols: 12, mines: 5 } : { rows: 10, cols: 9, mines: 5 });
|
|
12
|
-
|
|
13
|
-
export const TIME_LIMIT = 180; // seconds
|
|
14
|
-
export const POINTS_PER_REVEAL = 100;
|
|
15
|
-
export const POINTS_PER_FLAG = 50;
|
|
16
|
-
export const WIN_BONUS = 5000;
|
|
17
|
-
|
|
18
|
-
// ── Touch and feel ────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
export const FLAG_HOLD = 0.3; // seconds a finger rests on a cell to flag it (as the original long press)
|
|
21
|
-
export const TAP_SLOP = 10; // points a finger may wander and still tap or flag
|
|
22
|
-
export const TAP_MAX = 0.5; // seconds: longer and a lift isn't a tap (as the original tap)
|
|
23
|
-
export const GAME_OVER_DELAY = 0.4; // after a mine goes off
|
|
24
|
-
export const REVEAL_STEP = 0.025; // revealed cells ripple out from the tap, this much a cell
|
|
25
|
-
|
|
26
|
-
// ── Colors ────────────────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
export const NUMBER_COLORS = ['', '#2196F3', '#4CAF50', '#F44336', '#9C27B0', '#FF9800', '#00BCD4', '#795548', '#607D8B'];
|
|
29
|
-
export const COLORS = {
|
|
30
|
-
HIDDEN: '#3D7A4E',
|
|
31
|
-
HIDDEN_ALT: '#5DA86E',
|
|
32
|
-
REVEALED: '#F0E6D0',
|
|
33
|
-
REVEALED_ALT: '#E5DABE',
|
|
34
|
-
MINE: '#E74C3C',
|
|
35
|
-
CLASSIC_BOARD: 'rgba(255, 255, 255, 0.30)',
|
|
36
|
-
CLASSIC_GRID: 'rgba(255, 255, 255, 0.10)'
|
|
37
|
-
} as const;
|
|
38
|
-
|
|
39
|
-
// ── The scene ─────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
/** The camera leans back from straight down, so the raised tiles, flags and mines stand up. */
|
|
42
|
-
export const TILT = 0.34;
|
|
43
|
-
/** Lit from the upper left; surfaces facing up off the board (+z) keep their exact color. */
|
|
44
|
-
export const LIGHT = frontLight([-0.4, 0.5, 1], 0.45);
|
|
45
|
-
|
|
46
|
-
// ── Layout (view points) ──────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
const SCOREBOARD_HEIGHT = 82;
|
|
49
|
-
const CONTROLS_HEIGHT = 110;
|
|
50
|
-
export const SPACING = 3; // between cells
|
|
51
|
-
const PADDING = 6; // round the grid
|
|
52
|
-
|
|
53
|
-
export type Layout = TiltView &
|
|
54
|
-
Board & { cell: number; pitch: number; gridWidth: number; gridHeight: number; boardWidth: number; boardHeight: number };
|
|
55
|
-
|
|
56
|
-
/** Whole-point cells as wide as the game area allows (as the original board), smaller if the screen is short. */
|
|
57
|
-
export function getLayout(width: number, height: number, offset: number, b: Board): Layout {
|
|
58
|
-
const gameWidth = gameWidthFor(width);
|
|
59
|
-
const top = getScoreBoardTop(offset) + SCOREBOARD_HEIGHT;
|
|
60
|
-
const room = Math.max(200, height - top - CONTROLS_HEIGHT);
|
|
61
|
-
const fromWidth = (gameWidth - PADDING * 2 - SPACING * (b.cols + 1)) / b.cols;
|
|
62
|
-
const fromHeight = (room / Math.cos(TILT) - PADDING * 2 - SPACING * (b.rows + 1)) / b.rows;
|
|
63
|
-
const cell = Math.max(10, Math.floor(Math.min(fromWidth, fromHeight)));
|
|
64
|
-
const gridWidth = b.cols * (cell + SPACING) + SPACING;
|
|
65
|
-
const gridHeight = b.rows * (cell + SPACING) + SPACING;
|
|
66
|
-
return {
|
|
67
|
-
...b,
|
|
68
|
-
width,
|
|
69
|
-
height,
|
|
70
|
-
tilt: TILT,
|
|
71
|
-
centerY: top + room / 2,
|
|
72
|
-
cell,
|
|
73
|
-
pitch: cell + SPACING,
|
|
74
|
-
gridWidth,
|
|
75
|
-
gridHeight,
|
|
76
|
-
boardWidth: gridWidth + PADDING * 2,
|
|
77
|
-
boardHeight: gridHeight + PADDING * 2
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** A cell's centre on the board (row 0 along the top), in world units. */
|
|
82
|
-
export function cellCentre(l: Layout, row: number, col: number): [number, number] {
|
|
83
|
-
return [-l.gridWidth / 2 + SPACING + col * l.pitch + l.cell / 2, l.gridHeight / 2 - SPACING - row * l.pitch - l.cell / 2];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** The cell a board point is on (not in the gaps between cells), or null. */
|
|
87
|
-
export function cellAt(l: Layout, x: number, y: number): [number, number] | null {
|
|
88
|
-
const lx = x + l.gridWidth / 2 - SPACING;
|
|
89
|
-
const ly = l.gridHeight / 2 - SPACING - y;
|
|
90
|
-
const col = Math.floor(lx / l.pitch);
|
|
91
|
-
const row = Math.floor(ly / l.pitch);
|
|
92
|
-
if (row < 0 || row >= l.rows || col < 0 || col >= l.cols) return null;
|
|
93
|
-
return lx - col * l.pitch <= l.cell && ly - row * l.pitch <= l.cell ? [row, col] : null;
|
|
94
|
-
}
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
// Mine Sweeper's play, with no React: build a game with `createGame`, give it a layout, `start` it and
|
|
2
|
-
// `tick` it every frame. Tap a cell to reveal it (the mines are laid on the first reveal, never round
|
|
3
|
-
// it, and an empty cell opens its neighbours); rest a finger on one to flag or unflag it. Reveal every
|
|
4
|
-
// safe cell inside three minutes to win; a mine, or the clock running out, loses. The original service's and
|
|
5
|
-
// store's rules, ported as they were.
|
|
6
|
-
import { type Camera, type DragEvent, World } from '@nayan-ui/engine';
|
|
7
|
-
import { calculateTimeBasedScore } from '../../services/ScoringService';
|
|
8
|
-
import type { GameTheme } from '../../services/ThemeService';
|
|
9
|
-
import { aimTilted, unproject } from '../shared/EngineTilt';
|
|
10
|
-
import {
|
|
11
|
-
type Board,
|
|
12
|
-
cellAt,
|
|
13
|
-
FLAG_HOLD,
|
|
14
|
-
GAME_OVER_DELAY,
|
|
15
|
-
type Layout,
|
|
16
|
-
POINTS_PER_FLAG,
|
|
17
|
-
POINTS_PER_REVEAL,
|
|
18
|
-
TAP_MAX,
|
|
19
|
-
TAP_SLOP,
|
|
20
|
-
TIME_LIMIT,
|
|
21
|
-
WIN_BONUS
|
|
22
|
-
} from './MineSweeperConstants';
|
|
23
|
-
import { type Cell, CellState, emptyGrid, floodReveal, placeMines } from './MineSweeperRules';
|
|
24
|
-
import { buildScene, celebrate, explode, flag, removeScene, reveal, type Scene, setSceneTheme } from './MineSweeperScene';
|
|
25
|
-
|
|
26
|
-
export type Phase = 'ready' | 'playing' | 'over';
|
|
27
|
-
/** reveal: cells opened; flag: a flag planted; mine: one went off. */
|
|
28
|
-
export type GameEvent = 'reveal' | 'flag' | 'mine';
|
|
29
|
-
|
|
30
|
-
const WORLD_CAPACITY = 600; // up to 168 tiles and as many marks, the board, and the bursts
|
|
31
|
-
|
|
32
|
-
export function createGame(theme: GameTheme, board: Board) {
|
|
33
|
-
const world = new World(WORLD_CAPACITY);
|
|
34
|
-
const camera: Camera = { eye: [0, 0, 240], target: [0, 0, 0], fov: 1, ortho: 1 };
|
|
35
|
-
const g = {
|
|
36
|
-
world,
|
|
37
|
-
camera,
|
|
38
|
-
theme,
|
|
39
|
-
board,
|
|
40
|
-
layout: null as Layout | null,
|
|
41
|
-
scene: null as Scene | null,
|
|
42
|
-
phase: 'ready' as Phase,
|
|
43
|
-
grid: emptyGrid(board),
|
|
44
|
-
firstReveal: true,
|
|
45
|
-
/** Set the moment a mine goes off: nothing more counts in the beat before the round ends. */
|
|
46
|
-
mineHit: false,
|
|
47
|
-
/** Score and the countdown live in the engine (Rust), advanced by world.update. */
|
|
48
|
-
get score() {
|
|
49
|
-
return world.scoreboard.score;
|
|
50
|
-
},
|
|
51
|
-
get timeRemaining() {
|
|
52
|
-
return world.scoreboard.shownSeconds;
|
|
53
|
-
},
|
|
54
|
-
minesRemaining: board.mines,
|
|
55
|
-
revealed: 0,
|
|
56
|
-
won: false,
|
|
57
|
-
/** A finger down on the board: where, when, and whether it's already flagged (so the lift isn't a tap). */
|
|
58
|
-
press: null as { x: number; y: number; at: number; cell: [number, number] | null; flagged: boolean } | null,
|
|
59
|
-
/** Seconds on the game's clock, for telling taps from presses. */
|
|
60
|
-
clock: 0,
|
|
61
|
-
// UI hooks, set by the screen.
|
|
62
|
-
onPhase: (_p: Phase) => {},
|
|
63
|
-
onScore: (_s: number) => {},
|
|
64
|
-
onTime: (_t: number) => {},
|
|
65
|
-
onMines: (_n: number) => {},
|
|
66
|
-
onEvent: (_e: GameEvent) => {}
|
|
67
|
-
};
|
|
68
|
-
world.scoreboard.reset(0, TIME_LIMIT);
|
|
69
|
-
world.scoreboard.handlers = {
|
|
70
|
-
onSecond: (t) => g.onTime(t),
|
|
71
|
-
onTimeUp: () => g.phase === 'playing' && end(g, false)
|
|
72
|
-
};
|
|
73
|
-
return g;
|
|
74
|
-
}
|
|
75
|
-
export type Game = ReturnType<typeof createGame>;
|
|
76
|
-
|
|
77
|
-
const safeCells = (b: Board) => b.rows * b.cols - b.mines;
|
|
78
|
-
|
|
79
|
-
// ── Scene ─────────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
/** Lays the board out for the view (first layout, header height, rotation): rebuilt from the grid's state. */
|
|
82
|
-
export function setLayout(g: Game, l: Layout) {
|
|
83
|
-
if (g.scene) removeScene(g.world, g.scene);
|
|
84
|
-
g.layout = l;
|
|
85
|
-
aimTilted(g.camera, l);
|
|
86
|
-
g.scene = buildScene(g.world, l, g.theme, g.grid);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function setTheme(g: Game, theme: GameTheme) {
|
|
90
|
-
g.theme = theme;
|
|
91
|
-
if (g.layout && g.scene) setSceneTheme(g.world, g.layout, g.scene, theme);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ── Run ───────────────────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
export function start(g: Game) {
|
|
97
|
-
if (g.phase !== 'ready') return;
|
|
98
|
-
g.phase = 'playing';
|
|
99
|
-
g.world.scoreboard.reset();
|
|
100
|
-
g.world.scoreboard.startCountdown(TIME_LIMIT);
|
|
101
|
-
g.revealed = 0;
|
|
102
|
-
g.firstReveal = true;
|
|
103
|
-
g.mineHit = false;
|
|
104
|
-
g.onScore(0);
|
|
105
|
-
g.onTime(TIME_LIMIT);
|
|
106
|
-
g.onPhase('playing');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** A fresh, hidden board, not started (Stop, Play Again). The world, and the GPU view drawing it, are kept. */
|
|
110
|
-
export function reset(g: Game) {
|
|
111
|
-
g.world.cancelTimers();
|
|
112
|
-
g.grid = emptyGrid(g.board);
|
|
113
|
-
g.firstReveal = true;
|
|
114
|
-
g.mineHit = false;
|
|
115
|
-
g.world.scoreboard.reset(0, TIME_LIMIT);
|
|
116
|
-
g.minesRemaining = g.board.mines;
|
|
117
|
-
g.revealed = 0;
|
|
118
|
-
g.won = false;
|
|
119
|
-
g.press = null;
|
|
120
|
-
g.phase = 'ready';
|
|
121
|
-
if (g.layout) setLayout(g, g.layout);
|
|
122
|
-
g.onScore(0);
|
|
123
|
-
g.onTime(TIME_LIMIT);
|
|
124
|
-
g.onMines(g.minesRemaining);
|
|
125
|
-
g.onPhase('ready');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function dispose(g: Game) {
|
|
129
|
-
g.world.dispose();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function tick(g: Game, dt: number) {
|
|
133
|
-
if (g.world.isDisposed) return;
|
|
134
|
-
g.clock += Math.min(dt, 0.25);
|
|
135
|
-
// A finger resting on a cell long enough flags it
|
|
136
|
-
const p = g.press;
|
|
137
|
-
if (p && !p.flagged && p.cell && g.clock - p.at >= FLAG_HOLD) {
|
|
138
|
-
p.flagged = true;
|
|
139
|
-
toggleFlag(g, p.cell[0], p.cell[1]);
|
|
140
|
-
}
|
|
141
|
-
g.world.update(dt);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function end(g: Game, won: boolean) {
|
|
145
|
-
g.world.scoreboard.stop();
|
|
146
|
-
g.won = won;
|
|
147
|
-
g.phase = 'over';
|
|
148
|
-
g.press = null;
|
|
149
|
-
g.onPhase('over');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ── The finger ────────────────────────────────────────────────────────
|
|
153
|
-
|
|
154
|
-
/** The cell under a view point (on the raised tiles' tops), or null between cells or off the board. */
|
|
155
|
-
export function cellUnder(g: Game, x: number, y: number): [number, number] | null {
|
|
156
|
-
const l = g.layout;
|
|
157
|
-
if (!l) return null;
|
|
158
|
-
const [wx, wy] = unproject(l, x, y, l.cell * 0.32);
|
|
159
|
-
return cellAt(l, wx, wy);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Down on a cell starts a press; resting there flags it (in `tick`); a quick lift without wandering reveals it. */
|
|
163
|
-
export function drag(g: Game, { phase, x, y }: DragEvent) {
|
|
164
|
-
if (phase === 'start') {
|
|
165
|
-
g.press = { x, y, at: g.clock, cell: cellUnder(g, x, y), flagged: false };
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
const p = g.press;
|
|
169
|
-
if (!p) return;
|
|
170
|
-
if (Math.hypot(x - p.x, y - p.y) > TAP_SLOP) {
|
|
171
|
-
g.press = null; // a drag, not a tap or a press
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
if (phase !== 'end') return;
|
|
175
|
-
g.press = null;
|
|
176
|
-
if (!p.flagged && p.cell && g.clock - p.at < TAP_MAX) revealAt(g, p.cell[0], p.cell[1]);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// ── Moves ─────────────────────────────────────────────────────────────
|
|
180
|
-
|
|
181
|
-
/** Reveals a cell: a mine ends the round; otherwise it (and any empty run) opens, 100 points a cell. */
|
|
182
|
-
export function revealAt(g: Game, row: number, col: number) {
|
|
183
|
-
if (g.phase !== 'playing' || g.mineHit) return;
|
|
184
|
-
const cell = g.grid[row]?.[col];
|
|
185
|
-
if (!cell || cell.state !== CellState.HIDDEN) return;
|
|
186
|
-
if (g.firstReveal) {
|
|
187
|
-
g.firstReveal = false;
|
|
188
|
-
placeMines(g.grid, g.board, row, col);
|
|
189
|
-
}
|
|
190
|
-
const { layout: l, scene: s } = g;
|
|
191
|
-
if (cell.isMine) {
|
|
192
|
-
g.mineHit = true;
|
|
193
|
-
const mines = g.grid.flat().filter((c) => c.isMine && c.state !== CellState.REVEALED);
|
|
194
|
-
for (const m of mines) m.state = CellState.REVEALED;
|
|
195
|
-
if (l && s) explode(g.world, l, s, g.camera, cell, mines);
|
|
196
|
-
g.onEvent('mine');
|
|
197
|
-
// The clock stops with the blast: the round ends on the mine, not the timer
|
|
198
|
-
g.world.scoreboard.stop();
|
|
199
|
-
g.world.after(GAME_OVER_DELAY, () => end(g, false));
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const opened: Cell[] = floodReveal(g.grid, g.board, row, col);
|
|
203
|
-
if (!opened.length) return;
|
|
204
|
-
for (const c of opened) c.state = CellState.REVEALED;
|
|
205
|
-
g.revealed += opened.length;
|
|
206
|
-
g.world.scoreboard.add(opened.length * POINTS_PER_REVEAL);
|
|
207
|
-
if (l && s) reveal(g.world, l, s, opened, cell);
|
|
208
|
-
g.onEvent('reveal');
|
|
209
|
-
if (g.revealed >= safeCells(g.board)) {
|
|
210
|
-
g.world.scoreboard.set(calculateTimeBasedScore(TIME_LIMIT - g.timeRemaining, TIME_LIMIT, g.score + WIN_BONUS));
|
|
211
|
-
g.onScore(g.score);
|
|
212
|
-
if (l) celebrate(g.world, l);
|
|
213
|
-
end(g, true);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
g.onScore(g.score);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** Flags a hidden cell (50 points, one fewer mine to find) or takes its flag back off. */
|
|
220
|
-
export function toggleFlag(g: Game, row: number, col: number) {
|
|
221
|
-
if (g.phase !== 'playing' || g.mineHit) return;
|
|
222
|
-
const cell = g.grid[row]?.[col];
|
|
223
|
-
if (!cell || cell.state === CellState.REVEALED) return;
|
|
224
|
-
const planting = cell.state === CellState.HIDDEN;
|
|
225
|
-
cell.state = planting ? CellState.FLAGGED : CellState.HIDDEN;
|
|
226
|
-
g.minesRemaining += planting ? -1 : 1;
|
|
227
|
-
if (planting) g.world.scoreboard.add(POINTS_PER_FLAG);
|
|
228
|
-
if (g.layout && g.scene) flag(g.world, g.layout, g.scene, cell);
|
|
229
|
-
g.onMines(g.minesRemaining);
|
|
230
|
-
if (planting) {
|
|
231
|
-
g.onScore(g.score);
|
|
232
|
-
g.onEvent('flag');
|
|
233
|
-
}
|
|
234
|
-
}
|