@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,477 +0,0 @@
|
|
|
1
|
-
// Candy Crush's rules, with no React: build a run with `createGame`, give it a layout, `start` it and
|
|
2
|
-
// `tick` it every frame. Swipe a candy into a neighbour; the swap stays if it lines up 3 or more of a
|
|
3
|
-
// color (in a row or column), else they bump and go back. Matches clear for 50 a candy, the candies
|
|
4
|
-
// above fall into the gaps, new ones drop in, and any matches that makes cascade at 1.25x per step.
|
|
5
|
-
// Lining up 5 leaves a color bomb: swap it with any candy to clear every candy of that color.
|
|
6
|
-
// Score as much as you can before the timer runs out (every finished run counts as a win).
|
|
7
|
-
import { type Camera, type Entity, type Vec3, World } from '@nayan-ui/engine';
|
|
8
|
-
import type { GameTheme } from '../../services/ThemeService';
|
|
9
|
-
import { introPlayed } from '../shared/EngineUtils';
|
|
10
|
-
import {
|
|
11
|
-
BOMB,
|
|
12
|
-
BOMB_MATCH,
|
|
13
|
-
BOMB_MULTIPLIER,
|
|
14
|
-
CANDY_TYPES,
|
|
15
|
-
CELL_GAP,
|
|
16
|
-
CLEAR_RIPPLE,
|
|
17
|
-
COMBO_MULTIPLIER,
|
|
18
|
-
FALL_SPRING,
|
|
19
|
-
FALL_TIME,
|
|
20
|
-
GRID_SIZE,
|
|
21
|
-
INTRO_COL_STAGGER,
|
|
22
|
-
INTRO_ROW_STAGGER,
|
|
23
|
-
type Layout,
|
|
24
|
-
LIFT_SCALE,
|
|
25
|
-
LIFT_SPRING,
|
|
26
|
-
MATCH_TIME,
|
|
27
|
-
MIN_MATCH,
|
|
28
|
-
NUDGE_DISTANCE,
|
|
29
|
-
NUDGE_SPRING,
|
|
30
|
-
NUDGE_TIME,
|
|
31
|
-
POINTS_PER_CANDY,
|
|
32
|
-
RETURN_SPRING,
|
|
33
|
-
SWAP_TIME,
|
|
34
|
-
TIME_LIMIT,
|
|
35
|
-
WARNING_TIMES,
|
|
36
|
-
ZAP_TIME
|
|
37
|
-
} from './CandyCrushConstants';
|
|
38
|
-
import {
|
|
39
|
-
buildScenery,
|
|
40
|
-
candyPosition,
|
|
41
|
-
crushCandy,
|
|
42
|
-
dragZ,
|
|
43
|
-
popInBomb,
|
|
44
|
-
removeScenery,
|
|
45
|
-
type Scenery,
|
|
46
|
-
setSceneryTheme,
|
|
47
|
-
spawnCandy,
|
|
48
|
-
zap
|
|
49
|
-
} from './CandyCrushScene';
|
|
50
|
-
|
|
51
|
-
export type Phase = 'ready' | 'playing' | 'over';
|
|
52
|
-
export type Direction = 'up' | 'down' | 'left' | 'right';
|
|
53
|
-
|
|
54
|
-
type Candy = { type: number; row: number; col: number; root: Entity | null };
|
|
55
|
-
type Grid = (Candy | null)[];
|
|
56
|
-
|
|
57
|
-
const WORLD_CAPACITY = 1000; // 49 candies (a color bomb is 7 parts), candies on their way out, zaps, scenery, bursts
|
|
58
|
-
|
|
59
|
-
const at = (row: number, col: number) => row * GRID_SIZE + col;
|
|
60
|
-
const randomType = () => Math.floor(Math.random() * CANDY_TYPES);
|
|
61
|
-
|
|
62
|
-
/** Would a candy of `type` at (row, col) line up 3 with what's already placed? */
|
|
63
|
-
function wouldMatch(grid: Grid, row: number, col: number, type: number) {
|
|
64
|
-
const run = (dr: number, dc: number) => {
|
|
65
|
-
let n = 0;
|
|
66
|
-
for (let r = row + dr, c = col + dc; r >= 0 && r < GRID_SIZE && c >= 0 && c < GRID_SIZE; r += dr, c += dc) {
|
|
67
|
-
if (grid[at(r, c)]?.type !== type) break;
|
|
68
|
-
n++;
|
|
69
|
-
}
|
|
70
|
-
return n;
|
|
71
|
-
};
|
|
72
|
-
return 1 + run(0, -1) + run(0, 1) >= MIN_MATCH || 1 + run(-1, 0) + run(1, 0) >= MIN_MATCH;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** A random board with (almost always) no matches to start: each cell gets 10 tries at a safe color. */
|
|
76
|
-
function generateGrid(): Grid {
|
|
77
|
-
const grid: Grid = new Array(GRID_SIZE * GRID_SIZE).fill(null);
|
|
78
|
-
for (let row = 0; row < GRID_SIZE; row++) {
|
|
79
|
-
for (let col = 0; col < GRID_SIZE; col++) {
|
|
80
|
-
let type = randomType();
|
|
81
|
-
for (let tries = 1; tries < 10 && wouldMatch(grid, row, col, type); tries++) type = randomType();
|
|
82
|
-
grid[at(row, col)] = { type, row, col, root: null };
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return grid;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Every candy in a straight run of 3+ of one color (across rows, then down columns), and the runs of 5+ (each leaves a color bomb). */
|
|
89
|
-
function findMatches(grid: Grid): { matched: Candy[]; fives: Candy[][] } {
|
|
90
|
-
const found = new Set<Candy>();
|
|
91
|
-
const fives: Candy[][] = [];
|
|
92
|
-
const scan = (cell: (line: number, i: number) => Candy | null) => {
|
|
93
|
-
for (let line = 0; line < GRID_SIZE; line++) {
|
|
94
|
-
let i = 0;
|
|
95
|
-
while (i < GRID_SIZE) {
|
|
96
|
-
const first = cell(line, i);
|
|
97
|
-
let end = i + 1;
|
|
98
|
-
while (first && first.type !== BOMB && end < GRID_SIZE && cell(line, end)?.type === first.type) end++;
|
|
99
|
-
if (first && first.type !== BOMB && end - i >= MIN_MATCH) {
|
|
100
|
-
for (let k = i; k < end; k++) found.add(cell(line, k)!);
|
|
101
|
-
if (end - i >= BOMB_MATCH) fives.push(Array.from({ length: end - i }, (_, k) => cell(line, i + k)!));
|
|
102
|
-
}
|
|
103
|
-
i = end;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
scan((row, col) => grid[at(row, col)] ?? null);
|
|
108
|
-
scan((col, row) => grid[at(row, col)] ?? null);
|
|
109
|
-
return { matched: [...found], fives };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function createGame(theme: GameTheme) {
|
|
113
|
-
const world = new World(WORLD_CAPACITY);
|
|
114
|
-
const camera: Camera = { eye: [0, 0, 400], target: [0, 0, 0], fov: 1, ortho: 1 };
|
|
115
|
-
const g = {
|
|
116
|
-
world,
|
|
117
|
-
camera,
|
|
118
|
-
theme,
|
|
119
|
-
layout: null as Layout | null,
|
|
120
|
-
scenery: null as Scenery | null,
|
|
121
|
-
phase: 'ready' as Phase,
|
|
122
|
-
/** Score and the countdown live in the engine (Rust), advanced by world.update. */
|
|
123
|
-
get score() {
|
|
124
|
-
return world.scoreboard.score;
|
|
125
|
-
},
|
|
126
|
-
get timeLeft() {
|
|
127
|
-
return world.scoreboard.shownSeconds;
|
|
128
|
-
},
|
|
129
|
-
/** Game clock, seconds: drives the delayed steps (they pause with the frame loop). */
|
|
130
|
-
time: 0,
|
|
131
|
-
grid: generateGrid(),
|
|
132
|
-
/** A swap is resolving (matches, falls, cascades): no new moves until it settles. */
|
|
133
|
-
busy: false,
|
|
134
|
-
combo: 0,
|
|
135
|
-
/** The candy under the finger. */
|
|
136
|
-
dragging: null as Candy | null,
|
|
137
|
-
/** The next board to appear rains in. */
|
|
138
|
-
intro: true,
|
|
139
|
-
/** Game seconds the current intro has been playing (-1 until its board spawns). */
|
|
140
|
-
introAge: -1,
|
|
141
|
-
// UI hooks, set by the screen.
|
|
142
|
-
onPhase: (_p: Phase) => {},
|
|
143
|
-
onScore: (_s: number) => {},
|
|
144
|
-
onTime: (_t: number) => {},
|
|
145
|
-
onSelect: () => {},
|
|
146
|
-
onSwap: () => {},
|
|
147
|
-
onInvalid: () => {},
|
|
148
|
-
onMatch: (_combo: number) => {},
|
|
149
|
-
/** A color bomb was made. */
|
|
150
|
-
onBomb: () => {},
|
|
151
|
-
/** A color bomb went off. */
|
|
152
|
-
onBlast: () => {},
|
|
153
|
-
onWarning: (_secondsLeft: number) => {},
|
|
154
|
-
onTimeUp: () => {}
|
|
155
|
-
};
|
|
156
|
-
world.scoreboard.reset(0, TIME_LIMIT);
|
|
157
|
-
world.scoreboard.handlers = {
|
|
158
|
-
onSecond: (t) => {
|
|
159
|
-
g.onTime(t);
|
|
160
|
-
if (t > 0 && (WARNING_TIMES as readonly number[]).includes(t)) g.onWarning(t);
|
|
161
|
-
},
|
|
162
|
-
onTimeUp: () => g.phase === 'playing' && end(g)
|
|
163
|
-
};
|
|
164
|
-
return g;
|
|
165
|
-
}
|
|
166
|
-
export type Game = ReturnType<typeof createGame>;
|
|
167
|
-
|
|
168
|
-
const after = (g: Game, seconds: number, run: () => void) => g.world.after(seconds, run);
|
|
169
|
-
|
|
170
|
-
// ── Scene ─────────────────────────────────────────────────────────────
|
|
171
|
-
|
|
172
|
-
/** Lays the scene out for the view (first layout, header height, rotation): rebuilt from the run's state. */
|
|
173
|
-
export function setLayout(g: Game, l: Layout) {
|
|
174
|
-
if (g.scenery) removeScenery(g.world, g.scenery);
|
|
175
|
-
g.layout = l;
|
|
176
|
-
g.camera.ortho = l.height / 2;
|
|
177
|
-
g.scenery = buildScenery(g.world, l, g.theme);
|
|
178
|
-
g.dragging = null;
|
|
179
|
-
spawnAll(g);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** A new theme: the background and board are repainted; the candies and the run carry on. */
|
|
183
|
-
export function setTheme(g: Game, theme: GameTheme) {
|
|
184
|
-
g.theme = theme;
|
|
185
|
-
if (g.layout && g.scenery && !g.world.isDisposed) setSceneryTheme(g.world, g.layout, g.scenery, theme);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function despawnCandy(g: Game, c: Candy) {
|
|
189
|
-
if (c.root != null) g.world.despawn(c.root); // its parts go with it
|
|
190
|
-
c.root = null;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/** (Re)creates every candy at its cell; a new board rains in, column by column, bottom rows first. */
|
|
194
|
-
function spawnAll(g: Game) {
|
|
195
|
-
const l = g.layout;
|
|
196
|
-
if (!l) return;
|
|
197
|
-
const intro = g.intro;
|
|
198
|
-
if (intro) g.introAge = 0;
|
|
199
|
-
for (const c of g.grid) {
|
|
200
|
-
if (!c) continue;
|
|
201
|
-
despawnCandy(g, c);
|
|
202
|
-
if (!intro) {
|
|
203
|
-
c.root = spawnCandy(g.world, l, c.type, candyPosition(l, c.row, c.col), 1);
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
c.root = spawnCandy(g.world, l, c.type, candyPosition(l, c.row - 2, c.col), 0);
|
|
207
|
-
const delay = c.col * INTRO_COL_STAGGER + (GRID_SIZE - 1 - c.row) * INTRO_ROW_STAGGER;
|
|
208
|
-
g.world.animate(c.root, { position: candyPosition(l, c.row, c.col) }, { spring: FALL_SPRING, delay });
|
|
209
|
-
g.world.animate(c.root, { scale: 1 }, { duration: FALL_TIME * 0.6, delay, easing: 'back' });
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// ── Run ───────────────────────────────────────────────────────────────
|
|
214
|
-
|
|
215
|
-
export function start(g: Game) {
|
|
216
|
-
if (g.phase !== 'ready') return;
|
|
217
|
-
g.phase = 'playing';
|
|
218
|
-
g.world.scoreboard.startCountdown(TIME_LIMIT);
|
|
219
|
-
g.onPhase('playing');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** A new board and timer (Stop, Play Again). The world, and the GPU view drawing it, are kept. */
|
|
223
|
-
export function reset(g: Game) {
|
|
224
|
-
g.world.cancelTimers(); // a cascade in progress stops here, so it can't touch the new board
|
|
225
|
-
for (const c of g.grid) if (c) despawnCandy(g, c);
|
|
226
|
-
g.grid = generateGrid();
|
|
227
|
-
g.busy = false;
|
|
228
|
-
g.combo = 0;
|
|
229
|
-
g.dragging = null;
|
|
230
|
-
g.intro = true;
|
|
231
|
-
spawnAll(g);
|
|
232
|
-
g.world.scoreboard.reset(0, TIME_LIMIT);
|
|
233
|
-
g.phase = 'ready';
|
|
234
|
-
g.onScore(0);
|
|
235
|
-
g.onTime(TIME_LIMIT);
|
|
236
|
-
g.onPhase('ready');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export function dispose(g: Game) {
|
|
240
|
-
g.world.cancelTimers();
|
|
241
|
-
g.world.dispose();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/** Time up. A cascade still resolving plays out on screen, but scores no more. */
|
|
245
|
-
function end(g: Game) {
|
|
246
|
-
g.world.scoreboard.stop();
|
|
247
|
-
if (g.dragging) release(g);
|
|
248
|
-
g.phase = 'over';
|
|
249
|
-
g.onTimeUp();
|
|
250
|
-
g.onPhase('over');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export function tick(g: Game, dt: number) {
|
|
254
|
-
if (g.world.isDisposed) return;
|
|
255
|
-
introPlayed(g, dt);
|
|
256
|
-
g.time += dt;
|
|
257
|
-
g.world.update(dt);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// ── Touch ─────────────────────────────────────────────────────────────
|
|
261
|
-
|
|
262
|
-
/** The board cell under a view point, or null (cells own the gap after them, as on the original board). */
|
|
263
|
-
function cellAt(l: Layout, x: number, y: number): [number, number] | null {
|
|
264
|
-
const col = Math.floor((x - l.boardLeft - CELL_GAP) / l.step);
|
|
265
|
-
const row = Math.floor((y - l.boardTop - CELL_GAP) / l.step);
|
|
266
|
-
return row < 0 || row >= GRID_SIZE || col < 0 || col >= GRID_SIZE ? null : [row, col];
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/** Finger down: the candy under it lifts and follows the finger. */
|
|
270
|
-
export function touchStart(g: Game, x: number, y: number) {
|
|
271
|
-
const l = g.layout;
|
|
272
|
-
if (!l || g.phase !== 'playing' || g.busy) return;
|
|
273
|
-
const cell = cellAt(l, x, y);
|
|
274
|
-
const candy = cell ? g.grid[at(cell[0], cell[1])] : null;
|
|
275
|
-
if (!candy || candy.root == null) return;
|
|
276
|
-
g.dragging = candy;
|
|
277
|
-
g.onSelect();
|
|
278
|
-
g.world.stopAnimation(candy.root);
|
|
279
|
-
g.world.set(candy.root, { position: candyPosition(l, candy.row, candy.col, dragZ(l)) });
|
|
280
|
-
g.world.animate(candy.root, { scale: LIFT_SCALE }, { spring: LIFT_SPRING });
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export function touchMove(g: Game, dx: number, dy: number) {
|
|
284
|
-
const l = g.layout;
|
|
285
|
-
const c = g.dragging;
|
|
286
|
-
if (!l || !c || c.root == null) return;
|
|
287
|
-
const home = candyPosition(l, c.row, c.col, dragZ(l));
|
|
288
|
-
g.world.set(c.root, { position: [home[0] + dx, home[1] - dy, home[2]] });
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/** Finger up: a long enough drag swaps toward its main direction; otherwise the candy settles back. */
|
|
292
|
-
export function touchEnd(g: Game, dx: number, dy: number, cancelled = false) {
|
|
293
|
-
const c = g.dragging;
|
|
294
|
-
const l = g.layout;
|
|
295
|
-
if (!c || !l) return;
|
|
296
|
-
g.dragging = null;
|
|
297
|
-
const ax = Math.abs(dx);
|
|
298
|
-
const ay = Math.abs(dy);
|
|
299
|
-
if (!cancelled && g.phase === 'playing' && Math.max(ax, ay) > l.swipeThreshold) {
|
|
300
|
-
swap(g, c, ax > ay ? (dx > 0 ? 'right' : 'left') : dy > 0 ? 'down' : 'up');
|
|
301
|
-
} else {
|
|
302
|
-
settle(g, c);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function release(g: Game) {
|
|
307
|
-
const c = g.dragging;
|
|
308
|
-
g.dragging = null;
|
|
309
|
-
if (c) settle(g, c);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/** Springs a candy back to its cell at rest size. */
|
|
313
|
-
function settle(g: Game, c: Candy) {
|
|
314
|
-
const l = g.layout;
|
|
315
|
-
if (!l || c.root == null) return;
|
|
316
|
-
g.world.animate(c.root, { position: candyPosition(l, c.row, c.col) }, { spring: RETURN_SPRING });
|
|
317
|
-
g.world.animate(c.root, { scale: 1 }, { spring: RETURN_SPRING });
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const STEP: Record<Direction, [number, number]> = { up: [-1, 0], down: [1, 0], left: [0, -1], right: [0, 1] };
|
|
321
|
-
|
|
322
|
-
function place(g: Game, c: Candy, row: number, col: number) {
|
|
323
|
-
c.row = row;
|
|
324
|
-
c.col = col;
|
|
325
|
-
g.grid[at(row, col)] = c;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function swap(g: Game, a: Candy, dir: Direction) {
|
|
329
|
-
const l = g.layout!;
|
|
330
|
-
const w = g.world;
|
|
331
|
-
const [dr, dc] = STEP[dir];
|
|
332
|
-
const row = a.row + dr;
|
|
333
|
-
const col = a.col + dc;
|
|
334
|
-
const b = row >= 0 && row < GRID_SIZE && col >= 0 && col < GRID_SIZE ? g.grid[at(row, col)] : null;
|
|
335
|
-
if (!b || g.busy) {
|
|
336
|
-
if (!b && !g.busy) g.onInvalid();
|
|
337
|
-
settle(g, a);
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
const [ar, ac] = [a.row, a.col];
|
|
341
|
-
place(g, a, row, col);
|
|
342
|
-
place(g, b, ar, ac);
|
|
343
|
-
const bomb = a.type === BOMB ? a : b.type === BOMB ? b : null;
|
|
344
|
-
|
|
345
|
-
if (bomb || findMatches(g.grid).matched.length) {
|
|
346
|
-
// The two glide past each other (the dragged one from under the finger), then the board resolves.
|
|
347
|
-
g.busy = true;
|
|
348
|
-
g.combo = 0;
|
|
349
|
-
if (a.root != null) {
|
|
350
|
-
w.animate(a.root, { position: candyPosition(l, a.row, a.col) }, { duration: SWAP_TIME, easing: 'easeOut' });
|
|
351
|
-
w.animate(a.root, { scale: 1 }, { spring: RETURN_SPRING });
|
|
352
|
-
}
|
|
353
|
-
if (b.root != null) w.animate(b.root, { position: candyPosition(l, b.row, b.col) }, { duration: SWAP_TIME, easing: 'easeOut' });
|
|
354
|
-
g.onSwap();
|
|
355
|
-
after(g, SWAP_TIME, () => (bomb ? blastColor(g, bomb, bomb === a ? b : a) : resolve(g, [a, b])));
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// No match: undo it. The dragged candy springs home and the other bumps toward it and back.
|
|
360
|
-
place(g, b, row, col);
|
|
361
|
-
place(g, a, ar, ac);
|
|
362
|
-
settle(g, a);
|
|
363
|
-
const root = b.root;
|
|
364
|
-
if (root != null) {
|
|
365
|
-
const home = candyPosition(l, b.row, b.col);
|
|
366
|
-
const bump: Vec3 = [home[0] - dc * NUDGE_DISTANCE * l.step, home[1] + dr * NUDGE_DISTANCE * l.step, home[2]];
|
|
367
|
-
w.animate(root, { position: bump }, { duration: NUDGE_TIME, easing: 'easeOut' }).then(() => {
|
|
368
|
-
if (b.root === root && g.dragging !== b) w.animate(root, { position: home }, { spring: NUDGE_SPRING });
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
g.onInvalid();
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// ── Resolving ─────────────────────────────────────────────────────────
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Clears the board's matches, scores them, then drops candies in; repeats until nothing matches. Each
|
|
378
|
-
* run of 5+ leaves a color bomb: where the swapped candy (`moved`) landed, else in the run's middle.
|
|
379
|
-
*/
|
|
380
|
-
function resolve(g: Game, moved: readonly Candy[] = []) {
|
|
381
|
-
const l = g.layout;
|
|
382
|
-
const { matched, fives } = findMatches(g.grid);
|
|
383
|
-
if (!l || !matched.length) {
|
|
384
|
-
g.busy = false;
|
|
385
|
-
g.combo = 0;
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
g.combo++;
|
|
389
|
-
if (g.phase === 'playing') {
|
|
390
|
-
g.onScore(g.world.scoreboard.add(Math.floor(matched.length * POINTS_PER_CANDY * Math.pow(COMBO_MULTIPLIER, g.combo - 1))));
|
|
391
|
-
}
|
|
392
|
-
g.onMatch(g.combo);
|
|
393
|
-
|
|
394
|
-
const bombs = new Set(fives.map((run) => run.find((c) => moved.includes(c)) ?? run[Math.floor(run.length / 2)]!));
|
|
395
|
-
const w = g.world;
|
|
396
|
-
// Clears ripple out from where the swap landed (a cascade's, from its first match), as in Block Blast
|
|
397
|
-
const origin = moved.find((c) => matched.includes(c)) ?? matched[0]!;
|
|
398
|
-
for (const c of matched) {
|
|
399
|
-
if (bombs.has(c)) continue;
|
|
400
|
-
g.grid[at(c.row, c.col)] = null;
|
|
401
|
-
crush(g, l, c, Math.hypot(c.row - origin.row, c.col - origin.col) * CLEAR_RIPPLE);
|
|
402
|
-
}
|
|
403
|
-
for (const c of bombs) {
|
|
404
|
-
if (c.root != null) w.despawn(c.root);
|
|
405
|
-
c.type = BOMB;
|
|
406
|
-
c.root = spawnCandy(w, l, BOMB, candyPosition(l, c.row, c.col), 1);
|
|
407
|
-
popInBomb(w, c.root);
|
|
408
|
-
}
|
|
409
|
-
if (bombs.size) g.onBomb();
|
|
410
|
-
const ripple = Math.max(...matched.map((c) => Math.hypot(c.row - origin.row, c.col - origin.col))) * CLEAR_RIPPLE;
|
|
411
|
-
after(g, MATCH_TIME + ripple, () => {
|
|
412
|
-
drop(g);
|
|
413
|
-
after(g, FALL_TIME, () => resolve(g));
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/** A candy leaves the board: it crushes (after `delay`) and is despawned once gone. */
|
|
418
|
-
function crush(g: Game, l: Layout, c: Candy, delay: number) {
|
|
419
|
-
const root = c.root;
|
|
420
|
-
c.root = null;
|
|
421
|
-
if (root == null) return;
|
|
422
|
-
crushCandy(g.world, l, root, c.type, candyPosition(l, c.row, c.col), MATCH_TIME * 0.95, delay).then(() => {
|
|
423
|
-
g.world.despawn(root);
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* A color bomb swapped with a candy: every candy of that color is zapped and crushed with the bomb
|
|
429
|
-
* (another bomb: every candy on the board), at double points; then the board falls and resolves.
|
|
430
|
-
*/
|
|
431
|
-
function blastColor(g: Game, bomb: Candy, other: Candy) {
|
|
432
|
-
const l = g.layout;
|
|
433
|
-
if (!l) return;
|
|
434
|
-
const all = other.type === BOMB;
|
|
435
|
-
const targets = g.grid.filter((c): c is Candy => !!c && c !== bomb && (all || c.type === other.type));
|
|
436
|
-
g.combo = 1;
|
|
437
|
-
if (g.phase === 'playing') g.onScore(g.world.scoreboard.add((targets.length + 1) * POINTS_PER_CANDY * BOMB_MULTIPLIER));
|
|
438
|
-
g.onBlast();
|
|
439
|
-
const from = candyPosition(l, bomb.row, bomb.col);
|
|
440
|
-
const stagger = Math.min(0.012, 0.3 / Math.max(1, targets.length));
|
|
441
|
-
targets.forEach((c, i) => {
|
|
442
|
-
zap(g.world, l, from, candyPosition(l, c.row, c.col), all ? BOMB : c.type, i * stagger);
|
|
443
|
-
g.grid[at(c.row, c.col)] = null;
|
|
444
|
-
crush(g, l, c, ZAP_TIME + i * stagger);
|
|
445
|
-
});
|
|
446
|
-
g.grid[at(bomb.row, bomb.col)] = null;
|
|
447
|
-
crush(g, l, bomb, ZAP_TIME);
|
|
448
|
-
after(g, ZAP_TIME + targets.length * stagger + MATCH_TIME, () => {
|
|
449
|
-
drop(g);
|
|
450
|
-
after(g, FALL_TIME, () => resolve(g));
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/** Gravity per column: the candies left fall to the bottom, new ones grow in as they drop into the top. */
|
|
455
|
-
function drop(g: Game) {
|
|
456
|
-
const l = g.layout;
|
|
457
|
-
if (!l) return;
|
|
458
|
-
const w = g.world;
|
|
459
|
-
for (let col = 0; col < GRID_SIZE; col++) {
|
|
460
|
-
let row = GRID_SIZE - 1;
|
|
461
|
-
for (let r = GRID_SIZE - 1; r >= 0; r--) {
|
|
462
|
-
const c = g.grid[at(r, col)];
|
|
463
|
-
if (!c) continue;
|
|
464
|
-
g.grid[at(r, col)] = null;
|
|
465
|
-
place(g, c, row, col);
|
|
466
|
-
if (r !== row && c.root != null) w.animate(c.root, { position: candyPosition(l, row, col) }, { spring: FALL_SPRING });
|
|
467
|
-
row--;
|
|
468
|
-
}
|
|
469
|
-
for (; row >= 0; row--) {
|
|
470
|
-
const c: Candy = { type: randomType(), row, col, root: null };
|
|
471
|
-
g.grid[at(row, col)] = c;
|
|
472
|
-
c.root = spawnCandy(w, l, c.type, candyPosition(l, row - 1, col), 0);
|
|
473
|
-
w.animate(c.root, { position: candyPosition(l, row, col) }, { spring: FALL_SPRING });
|
|
474
|
-
w.animate(c.root, { scale: 1 }, { duration: FALL_TIME * 0.6, easing: 'back' });
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
// Everything Candy Crush draws. Orthographic camera, one world unit per point, looking down -z: x right
|
|
2
|
-
// and y up from the view's centre. Depth (toward the camera): the game background, the board panel and
|
|
3
|
-
// its empty cells, the candies, a dragged candy. Each candy is an invisible root at its cell's centre
|
|
4
|
-
// carrying a matte raised block (shared/EngineBlocks, as Block Blast's, without its gloss or shadow),
|
|
5
|
-
// so moves, swells and shrinks animate the root alone. Every part is a shared mesh: mesh ids are
|
|
6
|
-
// app-wide and limited. The engine doesn't clip, so new candies grow in as they drop rather
|
|
7
|
-
// than fall from behind the board's top edge.
|
|
8
|
-
import { type Color, type Entity, extrude, loft, type Model, Path, quat, type Vec3, type World } from '@nayan-ui/engine';
|
|
9
|
-
import type { GameTheme } from '../../services/ThemeService';
|
|
10
|
-
import { setBackdropTheme, spawnBackdrop } from '../shared/EngineBackdrop';
|
|
11
|
-
import { popAndBurst } from '../shared/EngineBlasts';
|
|
12
|
-
import { spawnRaisedBlock } from '../shared/EngineBlocks';
|
|
13
|
-
import { cacheGameAsset, createGameMesh as createShape } from '../shared/EngineMeshes';
|
|
14
|
-
import { toColor } from '../shared/EngineUtils';
|
|
15
|
-
import {
|
|
16
|
-
BOMB,
|
|
17
|
-
CANDY_COLORS,
|
|
18
|
-
CELL_GAP,
|
|
19
|
-
cellCenter,
|
|
20
|
-
CLASSIC_BOARD,
|
|
21
|
-
CLASSIC_BORDER,
|
|
22
|
-
CLASSIC_CELL,
|
|
23
|
-
CLASSIC_RADIUS,
|
|
24
|
-
GRID_SIZE,
|
|
25
|
-
type Layout,
|
|
26
|
-
THEMED_RADIUS
|
|
27
|
-
} from './CandyCrushConstants';
|
|
28
|
-
|
|
29
|
-
const Z_BACKDROP = -20;
|
|
30
|
-
const Z_BOARD = 0;
|
|
31
|
-
const Z_CELLS = 0.2;
|
|
32
|
-
const Z_RIM = 0.4;
|
|
33
|
-
|
|
34
|
-
/** A view point (from the top-left) at depth `z`, in world space. */
|
|
35
|
-
export const toWorld = (l: Layout, x: number, y: number, z = 0): Vec3 => [x - l.width / 2, l.height / 2 - y, z];
|
|
36
|
-
|
|
37
|
-
const candyDepth = (l: Layout) => l.cell * 0.4;
|
|
38
|
-
const candyZ = (l: Layout) => Z_RIM + candyDepth(l) / 2 + 1;
|
|
39
|
-
export const dragZ = (l: Layout) => candyZ(l) + candyDepth(l) * 2;
|
|
40
|
-
|
|
41
|
-
/** Where the candy at (row, col) rests (row may be negative: above the board). */
|
|
42
|
-
export const candyPosition = (l: Layout, row: number, col: number, z = candyZ(l)): Vec3 => {
|
|
43
|
-
const [x, y] = cellCenter(l, row, col);
|
|
44
|
-
return toWorld(l, x, y, z);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// ── Meshes (registered once per size: mesh ids are app-wide and limited) ──
|
|
48
|
-
|
|
49
|
-
const meshes = new Map<string, Model>();
|
|
50
|
-
const mesh = (key: string, build: () => Model) => {
|
|
51
|
-
let m = meshes.get(key);
|
|
52
|
-
if (!m) {
|
|
53
|
-
m = cacheGameAsset('candy-crush/CandyCrushScene:' + key, build);
|
|
54
|
-
meshes.set(key, m);
|
|
55
|
-
}
|
|
56
|
-
return m;
|
|
57
|
-
};
|
|
58
|
-
/** A flat rounded square from its top-left; with `rim`, only its outline. */
|
|
59
|
-
const panelMesh = (size: number, radius: number, rim = 0) =>
|
|
60
|
-
mesh(`panel:${size}:${radius}:${rim}`, () => {
|
|
61
|
-
const path = new Path().addRoundedRect(0, 0, size, size, radius);
|
|
62
|
-
if (rim > 0) path.addRoundedRect(rim, rim, size - rim * 2, size - rim * 2, radius - rim);
|
|
63
|
-
return createShape(extrude(path, { depth: 0, fillRule: 'evenodd' }), { center: false });
|
|
64
|
-
});
|
|
65
|
-
/** All 49 empty cells in one flat mesh, from the board's top-left. */
|
|
66
|
-
const cellsMesh = (l: Layout) =>
|
|
67
|
-
mesh(`candy-cells:${l.cell}`, () => {
|
|
68
|
-
const path = new Path();
|
|
69
|
-
for (let r = 0; r < GRID_SIZE; r++)
|
|
70
|
-
for (let c = 0; c < GRID_SIZE; c++) path.addRoundedRect(CELL_GAP + c * l.step, CELL_GAP + r * l.step, l.cell, l.cell, l.cell * 0.2);
|
|
71
|
-
return createShape(extrude(path, { depth: 0 }), { center: false });
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// ── Board ─────────────────────────────────────────────────────────────
|
|
75
|
-
|
|
76
|
-
export type Scenery = { backdrop: Entity; parts: Entity[] };
|
|
77
|
-
|
|
78
|
-
/** The board panel, its empty cells and its border, for a theme. */
|
|
79
|
-
function spawnBoard(world: World, l: Layout, theme: GameTheme): Entity[] {
|
|
80
|
-
const classic = theme.mode === 'classic';
|
|
81
|
-
const radius = classic ? CLASSIC_RADIUS : THEMED_RADIUS;
|
|
82
|
-
const origin = (z: number) => toWorld(l, l.boardLeft, l.boardTop, z);
|
|
83
|
-
const flat = (m: Model, z: number, color: string) => world.spawn({ mesh: m, position: origin(z), color: toColor(color), pickable: false });
|
|
84
|
-
return [
|
|
85
|
-
flat(panelMesh(l.boardSize, radius), Z_BOARD, classic ? CLASSIC_BOARD : theme.BOARD.SURFACE),
|
|
86
|
-
flat(cellsMesh(l), Z_CELLS, classic ? CLASSIC_CELL : theme.BOARD.CELL_EMPTY),
|
|
87
|
-
flat(panelMesh(l.boardSize, radius, 1.5), Z_RIM, classic ? CLASSIC_BORDER : theme.BOARD.BORDER)
|
|
88
|
-
];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function buildScenery(world: World, l: Layout, theme: GameTheme): Scenery {
|
|
92
|
-
return { backdrop: spawnBackdrop(world, l.width, l.height, Z_BACKDROP, theme), parts: spawnBoard(world, l, theme) };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function removeScenery(world: World, s: Scenery) {
|
|
96
|
-
world.despawn(s.backdrop);
|
|
97
|
-
for (const e of s.parts) world.despawn(e);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** A new theme: the background is repainted and the board rebuilt in its colors. */
|
|
101
|
-
export function setSceneryTheme(world: World, l: Layout, s: Scenery, theme: GameTheme) {
|
|
102
|
-
setBackdropTheme(world, s.backdrop, theme);
|
|
103
|
-
for (const e of s.parts) world.despawn(e);
|
|
104
|
-
s.parts = spawnBoard(world, l, theme);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// ── Candies ───────────────────────────────────────────────────────────
|
|
108
|
-
|
|
109
|
-
const BODY = CANDY_COLORS.map((c) => toColor(c));
|
|
110
|
-
const WHITE: Color = [1, 1, 1, 1];
|
|
111
|
-
const BOMB_BURST: Color = [1, 0.85, 0.4, 1];
|
|
112
|
-
const WHITE_HOT: Color = [2, 2, 2, 1]; // above 1 glows, as the shared pop's flash
|
|
113
|
-
/** Each candy's block (by its root), which flashes as the candy pops. */
|
|
114
|
-
const blocks = new Map<Entity, Entity>();
|
|
115
|
-
export const candyColor = (type: number) => (type === BOMB ? BOMB_BURST : BODY[type % BODY.length]!);
|
|
116
|
-
|
|
117
|
-
let bomb: Model | null = null;
|
|
118
|
-
/**
|
|
119
|
-
* The color bomb's block: the same bevelled block as the others, its colors baked in, blending smoothly
|
|
120
|
-
* from one block color at each corner (top-left, top-right, bottom-right, bottom-left) to the next.
|
|
121
|
-
*/
|
|
122
|
-
function bombMesh(): Model {
|
|
123
|
-
if (bomb) return bomb;
|
|
124
|
-
const corner = (i: number) => BODY[i % BODY.length]!;
|
|
125
|
-
const [tl, tr, br, bl] = [corner(0), corner(1), corner(2), corner(3)];
|
|
126
|
-
const lerp = (a: Color, b: Color, t: number): Color => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t, 1];
|
|
127
|
-
const blend = ([x, y]: Vec3): Color => {
|
|
128
|
-
const u = Math.max(0, Math.min(1, x + 0.5));
|
|
129
|
-
const v = Math.max(0, Math.min(1, y + 0.5));
|
|
130
|
-
return lerp(lerp(bl, br, u), lerp(tl, tr, u), v);
|
|
131
|
-
};
|
|
132
|
-
bomb = createShape(
|
|
133
|
-
loft(
|
|
134
|
-
[
|
|
135
|
-
{ z: -0.5, bottom: -0.5, top: 0.5, width: 0.5, square: 7 },
|
|
136
|
-
{ z: 0.2, bottom: -0.5, top: 0.5, width: 0.5, square: 7 },
|
|
137
|
-
{ z: 0.4, bottom: -0.465, top: 0.465, width: 0.465, square: 6.5 },
|
|
138
|
-
{ z: 0.5, bottom: -0.39, top: 0.39, width: 0.39, square: 5.5 }
|
|
139
|
-
],
|
|
140
|
-
{ around: 64, smoothness: 3, color: blend }
|
|
141
|
-
),
|
|
142
|
-
{ center: false }
|
|
143
|
-
);
|
|
144
|
-
return bomb;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* A candy at view-world `position`, `scale` of full size. A color bomb is a block blending all the
|
|
149
|
-
* colors, gently pulsing so it stands out.
|
|
150
|
-
*/
|
|
151
|
-
export function spawnCandy(world: World, l: Layout, type: number, position: Vec3, scale: number): Entity {
|
|
152
|
-
const size = l.cell - 4; // the original body's 2 point inset
|
|
153
|
-
const depth = candyDepth(l);
|
|
154
|
-
const root = world.spawn({ mesh: 'none', position, scale, pickable: false });
|
|
155
|
-
if (type !== BOMB) {
|
|
156
|
-
blocks.set(
|
|
157
|
-
root,
|
|
158
|
-
spawnRaisedBlock(world, { parent: root, position: [0, 0, 0], size: [size, size, depth], color: candyColor(type), gloss: false, shadow: false })
|
|
159
|
-
);
|
|
160
|
-
return root;
|
|
161
|
-
}
|
|
162
|
-
const block = world.spawn({ mesh: bombMesh(), parent: root, scale: [size, size, depth], color: WHITE, pickable: false });
|
|
163
|
-
blocks.set(root, block);
|
|
164
|
-
world.animate(block, { scale: [size * 1.07, size * 1.07, depth] }, { duration: 0.5, repeat: 'forever', yoyo: true, easing: 'easeInOut' });
|
|
165
|
-
return root;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** A color bomb just made: it grows in where its match was, with a little overshoot. */
|
|
169
|
-
export function popInBomb(world: World, root: Entity) {
|
|
170
|
-
world.animate(root, [{ scale: 0.3 }, { scale: 1.25, at: 0.6 }, { scale: 1 }], { duration: 0.35, easing: 'easeOut' });
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/** A thin zap from a color bomb to a block it clears, fading. */
|
|
174
|
-
export function zap(world: World, l: Layout, from: Vec3, to: Vec3, type: number, delay: number) {
|
|
175
|
-
const dx = to[0] - from[0];
|
|
176
|
-
const dy = to[1] - from[1];
|
|
177
|
-
const angle = Math.atan2(dy, dx);
|
|
178
|
-
const length = Math.hypot(dx, dy);
|
|
179
|
-
const [r, g, b] = type === BOMB ? [1, 1, 1] : candyColor(type);
|
|
180
|
-
const z = from[2] + candyDepth(l);
|
|
181
|
-
world.after(delay, () => {
|
|
182
|
-
const bar = world.spawn({
|
|
183
|
-
mesh: 'cube',
|
|
184
|
-
position: [(from[0] + to[0]) / 2, (from[1] + to[1]) / 2, z],
|
|
185
|
-
rotation: quat.z(angle),
|
|
186
|
-
scale: [length, l.cell * 0.08, 1],
|
|
187
|
-
color: [r, g, b, 0.9],
|
|
188
|
-
pickable: false
|
|
189
|
-
});
|
|
190
|
-
world.animate(bar, { scale: [length, 0.01, 1], color: [r, g, b, 0] }, { duration: 0.3 }).then(() => {
|
|
191
|
-
if (world.has(bar)) world.despawn(bar);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** A matched candy pops as Block Blast's blocks do (shared/EngineBlasts): it swells, flashes white, settles and shrinks away, bursting in its color over a fading glow. Resolves when it's gone. */
|
|
197
|
-
export function crushCandy(world: World, l: Layout, root: Entity, type: number, at: Vec3, duration: number, delay = 0): Promise<void> {
|
|
198
|
-
const color = type === BOMB ? BOMB_BURST : candyColor(type);
|
|
199
|
-
const block = blocks.get(root);
|
|
200
|
-
blocks.delete(root);
|
|
201
|
-
if (block != null)
|
|
202
|
-
world.animate(block, [{ color: WHITE_HOT, at: 0.33 }, { color: type === BOMB ? WHITE : color }], { duration: duration * 0.3, delay });
|
|
203
|
-
return popAndBurst(
|
|
204
|
-
world,
|
|
205
|
-
root,
|
|
206
|
-
{ scale: 1, delay, duration },
|
|
207
|
-
{ position: [at[0], at[1], at[2] + candyDepth(l)], size: l.cell, color, glowZ: Z_RIM + 0.2 }
|
|
208
|
-
);
|
|
209
|
-
}
|