@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,735 +0,0 @@
|
|
|
1
|
-
// Arrows' rules, with no React: build a run with `createGame`, give it a layout, `start` it and `tick`
|
|
2
|
-
// it every frame. The board is a 12 x 16 grid tiled by bent arrows. Tap an arrow whose way out (the
|
|
3
|
-
// straight line ahead of its head, to the board edge) is clear and it flies off for 500; tap a blocked
|
|
4
|
-
// one and it shakes, and the arrow in its way pulses. Clear the board to win (with a time bonus); run
|
|
5
|
-
// out of time and it's over with no score. Boards are generated exactly as in the original version.
|
|
6
|
-
import { type Camera, type Entity, World } from '@nayan-ui/engine';
|
|
7
|
-
import { calculateTimeBasedScore } from '../../services/ScoringService';
|
|
8
|
-
import type { GameTheme } from '../../services/ThemeService';
|
|
9
|
-
import { setBackdropTheme } from '../shared/EngineBackdrop';
|
|
10
|
-
import { introPlayed } from '../shared/EngineUtils';
|
|
11
|
-
import {
|
|
12
|
-
DIR_DC,
|
|
13
|
-
DIR_DR,
|
|
14
|
-
Direction,
|
|
15
|
-
DRAW_IN_STAGGER,
|
|
16
|
-
DRAW_IN_TIME,
|
|
17
|
-
FLY_TIME,
|
|
18
|
-
GRID_COLS,
|
|
19
|
-
GRID_ROWS,
|
|
20
|
-
type Layout,
|
|
21
|
-
POINTS_PER_ARROW,
|
|
22
|
-
TIME_LIMIT,
|
|
23
|
-
TOTAL_CELLS
|
|
24
|
-
} from './ArrowsConstants';
|
|
25
|
-
import {
|
|
26
|
-
arrowColor,
|
|
27
|
-
type ArrowView,
|
|
28
|
-
buildScenery,
|
|
29
|
-
flashBlocked,
|
|
30
|
-
type Flight,
|
|
31
|
-
pulseBlocker,
|
|
32
|
-
pulseBoard,
|
|
33
|
-
type Scenery,
|
|
34
|
-
setArrowColor,
|
|
35
|
-
spawnArrow,
|
|
36
|
-
spawnDot,
|
|
37
|
-
spawnFlight,
|
|
38
|
-
stepFlight
|
|
39
|
-
} from './ArrowsScene';
|
|
40
|
-
|
|
41
|
-
export type Phase = 'ready' | 'playing' | 'over';
|
|
42
|
-
|
|
43
|
-
/** A cell on an arrow's path. */
|
|
44
|
-
export interface Waypoint {
|
|
45
|
-
row: number;
|
|
46
|
-
col: number;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** A bent arrow: the cells it runs through, tail first, and the way its head points off the board. */
|
|
50
|
-
export interface Arrow {
|
|
51
|
-
id: number;
|
|
52
|
-
waypoints: Waypoint[];
|
|
53
|
-
exitDir: Direction;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ── Board generation ──────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
const idx = (row: number, col: number) => row * GRID_COLS + col;
|
|
59
|
-
|
|
60
|
-
// Shuffle an array in-place (Fisher-Yates)
|
|
61
|
-
const shuffle = <T>(arr: T[]): T[] => {
|
|
62
|
-
for (let i = arr.length - 1; i > 0; i--) {
|
|
63
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
64
|
-
[arr[i], arr[j]] = [arr[j]!, arr[i]!];
|
|
65
|
-
}
|
|
66
|
-
return arr;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// Check if the exit ray from the last waypoint of an arrow is clear
|
|
70
|
-
// (no other arrow's cell lies on the straight ray to the board edge).
|
|
71
|
-
const isExitClear = (cellToArrowId: (number | null)[], arrow: Arrow): boolean => {
|
|
72
|
-
const last = arrow.waypoints[arrow.waypoints.length - 1]!;
|
|
73
|
-
const dr = DIR_DR[arrow.exitDir]!;
|
|
74
|
-
const dc = DIR_DC[arrow.exitDir]!;
|
|
75
|
-
let r = last.row + dr;
|
|
76
|
-
let c = last.col + dc;
|
|
77
|
-
while (r >= 0 && r < GRID_ROWS && c >= 0 && c < GRID_COLS) {
|
|
78
|
-
const occupant = cellToArrowId[idx(r, c)];
|
|
79
|
-
if (occupant !== null && occupant !== arrow.id) return false;
|
|
80
|
-
r += dr;
|
|
81
|
-
c += dc;
|
|
82
|
-
}
|
|
83
|
-
return true;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Get the direction from cell A to adjacent cell B
|
|
87
|
-
const dirBetween = (a: Waypoint, b: Waypoint): Direction => {
|
|
88
|
-
const dr = b.row - a.row;
|
|
89
|
-
const dc = b.col - a.col;
|
|
90
|
-
if (dr === -1) return Direction.UP;
|
|
91
|
-
if (dr === 1) return Direction.DOWN;
|
|
92
|
-
if (dc === 1) return Direction.RIGHT;
|
|
93
|
-
return Direction.LEFT;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Get the direction from flat cell index a to adjacent flat cell index b
|
|
97
|
-
const dirBetweenCells = (a: number, b: number): Direction => {
|
|
98
|
-
const d = b - a;
|
|
99
|
-
if (d === -GRID_COLS) return Direction.UP;
|
|
100
|
-
if (d === GRID_COLS) return Direction.DOWN;
|
|
101
|
-
if (d === 1) return Direction.RIGHT;
|
|
102
|
-
return Direction.LEFT;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const toWaypoint = (ci: number): Waypoint => ({ row: Math.floor(ci / GRID_COLS), col: ci % GRID_COLS });
|
|
106
|
-
|
|
107
|
-
// --- Board Generation ---
|
|
108
|
-
// Creates multi-segment arrows that fill the entire grid.
|
|
109
|
-
// Algorithm: reverse peeling with random walks.
|
|
110
|
-
// 1. Start with all cells unassigned.
|
|
111
|
-
// 2. Repeatedly pick an unassigned cell, do a random walk through unassigned
|
|
112
|
-
// neighbors until we reach a cell that has a clear exit ray to the board edge
|
|
113
|
-
// (through already-assigned/removed cells).
|
|
114
|
-
// 3. Mark all cells in the walk as one arrow.
|
|
115
|
-
// This guarantees solvability: arrows are created in removal order.
|
|
116
|
-
const generateBoardOnce = (): { arrows: Arrow[]; cellToArrowId: (number | null)[]; count: number } => {
|
|
117
|
-
const assigned = new Uint8Array(TOTAL_CELLS); // 1 = cell belongs to an arrow already removed
|
|
118
|
-
const cellToArrowId: (number | null)[] = new Array(TOTAL_CELLS).fill(null);
|
|
119
|
-
const arrows: Arrow[] = [];
|
|
120
|
-
let arrowId = 0;
|
|
121
|
-
let remaining = TOTAL_CELLS;
|
|
122
|
-
|
|
123
|
-
// Check if a ray from (row, col) in dir reaches the board edge through assigned cells
|
|
124
|
-
const rayClearOfUnassigned = (row: number, col: number, dir: Direction): boolean => {
|
|
125
|
-
const dr = DIR_DR[dir]!;
|
|
126
|
-
const dc = DIR_DC[dir]!;
|
|
127
|
-
let r = row + dr;
|
|
128
|
-
let c = col + dc;
|
|
129
|
-
while (r >= 0 && r < GRID_ROWS && c >= 0 && c < GRID_COLS) {
|
|
130
|
-
if (!assigned[idx(r, c)]) return false;
|
|
131
|
-
r += dr;
|
|
132
|
-
c += dc;
|
|
133
|
-
}
|
|
134
|
-
return true;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// Min/max path length for visual variety (3 to 14 cells)
|
|
138
|
-
const MIN_PATH = 3;
|
|
139
|
-
const MAX_PATH = 14;
|
|
140
|
-
|
|
141
|
-
// Walk scratch buffers, reused for every walk instead of allocating a Set + arrays per try.
|
|
142
|
-
// walkStamp[ci] === walkId marks cells already on the current walk.
|
|
143
|
-
const walkStamp = new Int32Array(TOTAL_CELLS);
|
|
144
|
-
let walkId = 0;
|
|
145
|
-
const path = new Int16Array(MAX_PATH);
|
|
146
|
-
const neighbors = new Int16Array(4);
|
|
147
|
-
|
|
148
|
-
// True if the ray from head in dir passes through the current walk — rejects confusing
|
|
149
|
-
// U-shaped arrows whose arrowhead points back into their own body.
|
|
150
|
-
const exitRayHitsWalk = (head: number, dir: Direction): boolean => {
|
|
151
|
-
const dr = DIR_DR[dir]!;
|
|
152
|
-
const dc = DIR_DC[dir]!;
|
|
153
|
-
let r = Math.floor(head / GRID_COLS) + dr;
|
|
154
|
-
let c = (head % GRID_COLS) + dc;
|
|
155
|
-
while (r >= 0 && r < GRID_ROWS && c >= 0 && c < GRID_COLS) {
|
|
156
|
-
if (walkStamp[idx(r, c)] === walkId) return true;
|
|
157
|
-
r += dr;
|
|
158
|
-
c += dc;
|
|
159
|
-
}
|
|
160
|
-
return false;
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Unassigned cells (order is irrelevant: start cells are drawn from it uniformly at random).
|
|
164
|
-
// Kept up to date on each new arrow instead of rebuilding + reshuffling all 192 cells per arrow.
|
|
165
|
-
const unassignedList = new Int16Array(TOTAL_CELLS);
|
|
166
|
-
const unassignedPos = new Int16Array(TOTAL_CELLS);
|
|
167
|
-
let unassignedCount = 0;
|
|
168
|
-
const rebuildUnassigned = () => {
|
|
169
|
-
unassignedCount = 0;
|
|
170
|
-
for (let ci = 0; ci < TOTAL_CELLS; ci++) {
|
|
171
|
-
if (!assigned[ci]) {
|
|
172
|
-
unassignedPos[ci] = unassignedCount;
|
|
173
|
-
unassignedList[unassignedCount++] = ci;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
const swapUnassigned = (i: number, j: number) => {
|
|
178
|
-
const a = unassignedList[i]!;
|
|
179
|
-
const b = unassignedList[j]!;
|
|
180
|
-
unassignedList[i] = b;
|
|
181
|
-
unassignedList[j] = a;
|
|
182
|
-
unassignedPos[b] = i;
|
|
183
|
-
unassignedPos[a] = j;
|
|
184
|
-
};
|
|
185
|
-
rebuildUnassigned();
|
|
186
|
-
|
|
187
|
-
while (remaining > 0) {
|
|
188
|
-
if (unassignedCount === 0) break;
|
|
189
|
-
|
|
190
|
-
// Try to build a path starting from a random unassigned cell. Drawing each next start
|
|
191
|
-
// with a lazy Fisher-Yates step gives the same uniformly random try order as shuffling
|
|
192
|
-
// the whole candidate list up front, but only pays for the starts actually tried.
|
|
193
|
-
let built = false;
|
|
194
|
-
|
|
195
|
-
for (let k = 0; k < unassignedCount; k++) {
|
|
196
|
-
swapUnassigned(k, k + Math.floor(Math.random() * (unassignedCount - k)));
|
|
197
|
-
const start = unassignedList[k]!;
|
|
198
|
-
|
|
199
|
-
// Random walk: grow a path from `start`
|
|
200
|
-
walkId++;
|
|
201
|
-
path[0] = start;
|
|
202
|
-
walkStamp[start] = walkId;
|
|
203
|
-
let len = 1;
|
|
204
|
-
|
|
205
|
-
// Try to extend the path
|
|
206
|
-
let current = start;
|
|
207
|
-
const targetLen = MIN_PATH + Math.floor(Math.random() * (MAX_PATH - MIN_PATH + 1));
|
|
208
|
-
|
|
209
|
-
while (len < targetLen) {
|
|
210
|
-
const row = Math.floor(current / GRID_COLS);
|
|
211
|
-
const col = current % GRID_COLS;
|
|
212
|
-
let n = 0;
|
|
213
|
-
for (let d = 0; d < 4; d++) {
|
|
214
|
-
const dr = DIR_DR[d]!;
|
|
215
|
-
const dc = DIR_DC[d]!;
|
|
216
|
-
const nr = row + dr;
|
|
217
|
-
const nc = col + dc;
|
|
218
|
-
if (nr < 0 || nr >= GRID_ROWS || nc < 0 || nc >= GRID_COLS) continue;
|
|
219
|
-
const ni = idx(nr, nc);
|
|
220
|
-
if (!assigned[ni] && walkStamp[ni] !== walkId) neighbors[n++] = ni;
|
|
221
|
-
}
|
|
222
|
-
if (n === 0) break;
|
|
223
|
-
const next = neighbors[Math.floor(Math.random() * n)]!;
|
|
224
|
-
path[len++] = next;
|
|
225
|
-
walkStamp[next] = walkId;
|
|
226
|
-
current = next;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Only accept a STRAIGHT exit: the arrowhead must continue the direction of
|
|
230
|
-
// the last segment (no turn at the last cell). Try the forward orientation
|
|
231
|
-
// first, then the reversed one. Require at least 2 cells; otherwise try another
|
|
232
|
-
// start. Single-cell leftovers are absorbed into a neighbour below.
|
|
233
|
-
if (len < 2) continue;
|
|
234
|
-
const lastCell = path[len - 1]!;
|
|
235
|
-
const firstCell = path[0]!;
|
|
236
|
-
let exitDir: Direction | null = null;
|
|
237
|
-
let reversed = false;
|
|
238
|
-
|
|
239
|
-
// Forward: exit continues (prev -> last) straight off the board
|
|
240
|
-
const contDir = dirBetweenCells(path[len - 2]!, lastCell);
|
|
241
|
-
if (rayClearOfUnassigned(Math.floor(lastCell / GRID_COLS), lastCell % GRID_COLS, contDir) && !exitRayHitsWalk(lastCell, contDir)) {
|
|
242
|
-
exitDir = contDir;
|
|
243
|
-
} else {
|
|
244
|
-
// Reversed: exit continues (path[1] -> first) straight off the board
|
|
245
|
-
const contDirRev = dirBetweenCells(path[1]!, firstCell);
|
|
246
|
-
if (rayClearOfUnassigned(Math.floor(firstCell / GRID_COLS), firstCell % GRID_COLS, contDirRev) && !exitRayHitsWalk(firstCell, contDirRev)) {
|
|
247
|
-
exitDir = contDirRev;
|
|
248
|
-
reversed = true;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (exitDir === null) continue;
|
|
253
|
-
|
|
254
|
-
// Create the arrow and mark all cells as assigned
|
|
255
|
-
const waypoints: Waypoint[] = new Array(len);
|
|
256
|
-
for (let i = 0; i < len; i++) {
|
|
257
|
-
const ci = path[reversed ? len - 1 - i : i]!;
|
|
258
|
-
waypoints[i] = toWaypoint(ci);
|
|
259
|
-
assigned[ci] = 1;
|
|
260
|
-
cellToArrowId[ci] = arrowId;
|
|
261
|
-
remaining--;
|
|
262
|
-
// Swap-remove from the unassigned list
|
|
263
|
-
swapUnassigned(unassignedPos[ci]!, unassignedCount - 1);
|
|
264
|
-
unassignedCount--;
|
|
265
|
-
}
|
|
266
|
-
arrows.push({ id: arrowId, waypoints, exitDir });
|
|
267
|
-
arrowId++;
|
|
268
|
-
built = true;
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// No >=2 path could be built this pass: absorb every remaining cell into an
|
|
273
|
-
// adjacent existing arrow so that no single-cell arrow is left.
|
|
274
|
-
// Strategy 1: prepend onto an adjacent arrow's TAIL (keeps head exit unchanged).
|
|
275
|
-
// Strategy 2: append onto an adjacent arrow's HEAD and recompute exit dir.
|
|
276
|
-
// Both are solvability-safe since the leftover was unassigned when the host
|
|
277
|
-
// arrow was created, so its exit ray was validated without this cell.
|
|
278
|
-
if (!built) {
|
|
279
|
-
for (let r = 0; r < GRID_ROWS; r++) {
|
|
280
|
-
for (let c = 0; c < GRID_COLS; c++) {
|
|
281
|
-
const ci = idx(r, c);
|
|
282
|
-
if (assigned[ci]) continue;
|
|
283
|
-
|
|
284
|
-
let merged = false;
|
|
285
|
-
|
|
286
|
-
// Strategy 1: prepend onto adjacent arrow's tail
|
|
287
|
-
for (const d of shuffle([0, 1, 2, 3])) {
|
|
288
|
-
const dr = DIR_DR[d]!;
|
|
289
|
-
const dc = DIR_DC[d]!;
|
|
290
|
-
const nr = r + dr;
|
|
291
|
-
const nc = c + dc;
|
|
292
|
-
if (nr < 0 || nr >= GRID_ROWS || nc < 0 || nc >= GRID_COLS) continue;
|
|
293
|
-
const nid = cellToArrowId[idx(nr, nc)];
|
|
294
|
-
if (nid === null || nid === undefined) continue;
|
|
295
|
-
const host = arrows[nid]!;
|
|
296
|
-
const tail = host.waypoints[0]!;
|
|
297
|
-
if (tail.row === nr && tail.col === nc) {
|
|
298
|
-
host.waypoints.unshift({ row: r, col: c });
|
|
299
|
-
assigned[ci] = 1;
|
|
300
|
-
cellToArrowId[ci] = host.id;
|
|
301
|
-
remaining--;
|
|
302
|
-
merged = true;
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Strategy 2: append onto adjacent arrow's head and recompute exit dir
|
|
308
|
-
if (!merged) {
|
|
309
|
-
for (const d of shuffle([0, 1, 2, 3])) {
|
|
310
|
-
const dr = DIR_DR[d]!;
|
|
311
|
-
const dc = DIR_DC[d]!;
|
|
312
|
-
const nr = r + dr;
|
|
313
|
-
const nc = c + dc;
|
|
314
|
-
if (nr < 0 || nr >= GRID_ROWS || nc < 0 || nc >= GRID_COLS) continue;
|
|
315
|
-
const nid = cellToArrowId[idx(nr, nc)];
|
|
316
|
-
if (nid === null || nid === undefined) continue;
|
|
317
|
-
const host = arrows[nid]!;
|
|
318
|
-
const head = host.waypoints[host.waypoints.length - 1]!;
|
|
319
|
-
if (head.row !== nr || head.col !== nc) continue;
|
|
320
|
-
// New exit must be the straight continuation from old head → new cell
|
|
321
|
-
const newExitDir = dirBetween(head, { row: r, col: c });
|
|
322
|
-
if (rayClearOfUnassigned(r, c, newExitDir)) {
|
|
323
|
-
host.waypoints.push({ row: r, col: c });
|
|
324
|
-
host.exitDir = newExitDir;
|
|
325
|
-
assigned[ci] = 1;
|
|
326
|
-
cellToArrowId[ci] = host.id;
|
|
327
|
-
remaining--;
|
|
328
|
-
merged = true;
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (!merged) {
|
|
335
|
-
// Strategy 3: pair with an adjacent unassigned cell to make a 2-cell arrow
|
|
336
|
-
let created = false;
|
|
337
|
-
for (const d of shuffle([0, 1, 2, 3])) {
|
|
338
|
-
const dr = DIR_DR[d]!;
|
|
339
|
-
const dc = DIR_DC[d]!;
|
|
340
|
-
const nr = r + dr;
|
|
341
|
-
const nc = c + dc;
|
|
342
|
-
if (nr < 0 || nr >= GRID_ROWS || nc < 0 || nc >= GRID_COLS) continue;
|
|
343
|
-
if (assigned[idx(nr, nc)]) continue;
|
|
344
|
-
const exitD = dirBetween({ row: r, col: c }, { row: nr, col: nc });
|
|
345
|
-
if (rayClearOfUnassigned(nr, nc, exitD)) {
|
|
346
|
-
const wp: Waypoint[] = [
|
|
347
|
-
{ row: r, col: c },
|
|
348
|
-
{ row: nr, col: nc }
|
|
349
|
-
];
|
|
350
|
-
arrows.push({ id: arrowId, waypoints: wp, exitDir: exitD });
|
|
351
|
-
assigned[ci] = 1;
|
|
352
|
-
cellToArrowId[ci] = arrowId;
|
|
353
|
-
const nci = idx(nr, nc);
|
|
354
|
-
assigned[nci] = 1;
|
|
355
|
-
cellToArrowId[nci] = arrowId;
|
|
356
|
-
remaining -= 2;
|
|
357
|
-
arrowId++;
|
|
358
|
-
created = true;
|
|
359
|
-
break;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
if (!created) {
|
|
363
|
-
// Strategy 4: force-merge at tail or head of ANY adjacent arrow.
|
|
364
|
-
// Only tail (prepend) and head (append) are safe — mid-path splice
|
|
365
|
-
// would create non-adjacent segments that render as diagonals.
|
|
366
|
-
for (const d of shuffle([0, 1, 2, 3])) {
|
|
367
|
-
const dr = DIR_DR[d]!;
|
|
368
|
-
const dc = DIR_DC[d]!;
|
|
369
|
-
const nr = r + dr;
|
|
370
|
-
const nc = c + dc;
|
|
371
|
-
if (nr < 0 || nr >= GRID_ROWS || nc < 0 || nc >= GRID_COLS) continue;
|
|
372
|
-
const nid = cellToArrowId[idx(nr, nc)];
|
|
373
|
-
if (nid === null || nid === undefined) continue;
|
|
374
|
-
const host = arrows[nid]!;
|
|
375
|
-
const tail = host.waypoints[0]!;
|
|
376
|
-
const head = host.waypoints[host.waypoints.length - 1]!;
|
|
377
|
-
if (tail.row === nr && tail.col === nc) {
|
|
378
|
-
// Prepend at tail — keeps head exit unchanged
|
|
379
|
-
host.waypoints.unshift({ row: r, col: c });
|
|
380
|
-
assigned[ci] = 1;
|
|
381
|
-
cellToArrowId[ci] = host.id;
|
|
382
|
-
remaining--;
|
|
383
|
-
created = true;
|
|
384
|
-
break;
|
|
385
|
-
}
|
|
386
|
-
if (head.row === nr && head.col === nc) {
|
|
387
|
-
// Append at head — recompute exit direction
|
|
388
|
-
const newExitDir = dirBetween(head, { row: r, col: c });
|
|
389
|
-
host.waypoints.push({ row: r, col: c });
|
|
390
|
-
host.exitDir = newExitDir;
|
|
391
|
-
assigned[ci] = 1;
|
|
392
|
-
cellToArrowId[ci] = host.id;
|
|
393
|
-
remaining--;
|
|
394
|
-
created = true;
|
|
395
|
-
break;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (!created) {
|
|
400
|
-
// Absolute last resort (should never happen on a connected grid)
|
|
401
|
-
assigned[ci] = 1;
|
|
402
|
-
remaining--;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
rebuildUnassigned();
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return { arrows, cellToArrowId, count: arrows.length };
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
// Simulate solving: repeatedly find an arrow whose exit ray is clear, remove it.
|
|
415
|
-
// If all arrows can be removed in some order the board is solvable.
|
|
416
|
-
// (Removing an arrow never blocks another, so the sweep order doesn't affect the result.)
|
|
417
|
-
const isBoardSolvable = (arrows: Arrow[], cellToArrowId: (number | null)[]): boolean => {
|
|
418
|
-
const map = cellToArrowId.slice(); // working copy
|
|
419
|
-
let pending = arrows.slice();
|
|
420
|
-
while (pending.length > 0) {
|
|
421
|
-
const stillBlocked: Arrow[] = [];
|
|
422
|
-
for (const arrow of pending) {
|
|
423
|
-
if (isExitClear(map, arrow)) {
|
|
424
|
-
for (const wp of arrow.waypoints) {
|
|
425
|
-
map[idx(wp.row, wp.col)] = null;
|
|
426
|
-
}
|
|
427
|
-
} else {
|
|
428
|
-
stillBlocked.push(arrow);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
if (stillBlocked.length === pending.length) return false;
|
|
432
|
-
pending = stillBlocked;
|
|
433
|
-
}
|
|
434
|
-
return true;
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
// Check that no arrow's exit ray passes through its own waypoints (confusing visually).
|
|
438
|
-
const noSelfFacing = (arrows: Arrow[]): boolean => {
|
|
439
|
-
// stamp[ci] === arrow index + 1 marks that arrow's cells (no per-arrow Set)
|
|
440
|
-
const stamp = new Int32Array(TOTAL_CELLS);
|
|
441
|
-
for (let a = 0; a < arrows.length; a++) {
|
|
442
|
-
const arrow = arrows[a]!;
|
|
443
|
-
const mark = a + 1;
|
|
444
|
-
for (const wp of arrow.waypoints) stamp[idx(wp.row, wp.col)] = mark;
|
|
445
|
-
const head = arrow.waypoints[arrow.waypoints.length - 1]!;
|
|
446
|
-
const dr = DIR_DR[arrow.exitDir]!;
|
|
447
|
-
const dc = DIR_DC[arrow.exitDir]!;
|
|
448
|
-
let r = head.row + dr;
|
|
449
|
-
let c = head.col + dc;
|
|
450
|
-
while (r >= 0 && r < GRID_ROWS && c >= 0 && c < GRID_COLS) {
|
|
451
|
-
if (stamp[idx(r, c)] === mark) return false;
|
|
452
|
-
r += dr;
|
|
453
|
-
c += dc;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return true;
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
// Cells the generator's last-resort branch skipped have no arrow and render as empty dots.
|
|
460
|
-
const countEmptyCells = (cellToArrowId: (number | null)[]): number => {
|
|
461
|
-
let empty = 0;
|
|
462
|
-
for (const id of cellToArrowId) if (id === null) empty++;
|
|
463
|
-
return empty;
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
// Generate a solvable board with no self-facing arrows, no deadlocks and every cell covered by an arrow.
|
|
467
|
-
// About a third of raw boards are fully covered, so this usually succeeds in ~5 attempts (<1ms).
|
|
468
|
-
const MIN_ARROW_LEN = 2;
|
|
469
|
-
const MAX_BOARD_ATTEMPTS = 200;
|
|
470
|
-
const generateBoard = (): { arrows: Arrow[]; cellToArrowId: (number | null)[]; count: number } => {
|
|
471
|
-
let fallback: ReturnType<typeof generateBoardOnce> | null = null;
|
|
472
|
-
let fallbackEmpty = Infinity;
|
|
473
|
-
for (let attempt = 0; attempt < MAX_BOARD_ATTEMPTS; attempt++) {
|
|
474
|
-
const board = generateBoardOnce();
|
|
475
|
-
if (
|
|
476
|
-
!board.arrows.every((a) => a.waypoints.length >= MIN_ARROW_LEN) ||
|
|
477
|
-
!noSelfFacing(board.arrows) ||
|
|
478
|
-
!isBoardSolvable(board.arrows, board.cellToArrowId)
|
|
479
|
-
) {
|
|
480
|
-
continue;
|
|
481
|
-
}
|
|
482
|
-
const empty = countEmptyCells(board.cellToArrowId);
|
|
483
|
-
if (empty === 0) return board;
|
|
484
|
-
if (empty < fallbackEmpty) {
|
|
485
|
-
fallback = board;
|
|
486
|
-
fallbackEmpty = empty;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// Practically unreachable: keep the valid board with the fewest empty cells
|
|
490
|
-
return fallback ?? generateBoardOnce();
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
// ── Run ───────────────────────────────────────────────────────────────
|
|
494
|
-
|
|
495
|
-
const WORLD_CAPACITY = 1600; // arrow parts (~2 per cell plus heads), 192 dots, flights, scenery
|
|
496
|
-
|
|
497
|
-
export function createGame(theme: GameTheme) {
|
|
498
|
-
const world = new World(WORLD_CAPACITY);
|
|
499
|
-
const camera: Camera = { eye: [0, 0, 400], target: [0, 0, 0], fov: 1, ortho: 1 };
|
|
500
|
-
const board = generateBoard();
|
|
501
|
-
const g = {
|
|
502
|
-
world,
|
|
503
|
-
camera,
|
|
504
|
-
theme,
|
|
505
|
-
color: arrowColor(theme),
|
|
506
|
-
layout: null as Layout | null,
|
|
507
|
-
scenery: null as Scenery | null,
|
|
508
|
-
phase: 'ready' as Phase,
|
|
509
|
-
/** Score and the countdown live in the engine (Rust), advanced by world.update. */
|
|
510
|
-
get score() {
|
|
511
|
-
return world.scoreboard.score;
|
|
512
|
-
},
|
|
513
|
-
get timeLeft() {
|
|
514
|
-
return world.scoreboard.shownSeconds;
|
|
515
|
-
},
|
|
516
|
-
won: false,
|
|
517
|
-
/** By id; null once flown. */
|
|
518
|
-
arrows: board.arrows as (Arrow | null)[],
|
|
519
|
-
cellToArrowId: board.cellToArrowId,
|
|
520
|
-
remaining: board.count,
|
|
521
|
-
views: [] as (ArrowView | null)[],
|
|
522
|
-
dots: [] as Entity[],
|
|
523
|
-
flights: [] as Flight[],
|
|
524
|
-
/** The next board to appear draws itself in. */
|
|
525
|
-
intro: true,
|
|
526
|
-
/** Game seconds the current intro has been playing (-1 until its board spawns). */
|
|
527
|
-
introAge: -1,
|
|
528
|
-
// UI hooks, set by the screen.
|
|
529
|
-
onPhase: (_p: Phase) => {},
|
|
530
|
-
onScore: (_s: number) => {},
|
|
531
|
-
onTime: (_t: number) => {},
|
|
532
|
-
onRemaining: (_n: number) => {},
|
|
533
|
-
onClear: () => {},
|
|
534
|
-
onBlocked: () => {},
|
|
535
|
-
onWin: () => {},
|
|
536
|
-
onLose: () => {}
|
|
537
|
-
};
|
|
538
|
-
world.scoreboard.reset(0, TIME_LIMIT);
|
|
539
|
-
world.scoreboard.handlers = {
|
|
540
|
-
onSecond: (t) => g.onTime(t),
|
|
541
|
-
onTimeUp: () => g.phase === 'playing' && end(g, false)
|
|
542
|
-
};
|
|
543
|
-
return g;
|
|
544
|
-
}
|
|
545
|
-
export type Game = ReturnType<typeof createGame>;
|
|
546
|
-
|
|
547
|
-
/** Lays the scene out for the view (first layout, header height, rotation): rebuilt from the run's state. */
|
|
548
|
-
export function setLayout(g: Game, l: Layout) {
|
|
549
|
-
const w = g.world;
|
|
550
|
-
if (g.scenery) {
|
|
551
|
-
w.despawn(g.scenery.backdrop);
|
|
552
|
-
w.despawn(g.scenery.board); // its arrows, dots and flights go with it
|
|
553
|
-
}
|
|
554
|
-
g.flights = [];
|
|
555
|
-
g.layout = l;
|
|
556
|
-
g.camera.ortho = l.height / 2;
|
|
557
|
-
g.scenery = buildScenery(w, l, g.theme, g.intro);
|
|
558
|
-
spawnBoard(g);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/** A new theme: the background is repainted and the arrows recolored; the run carries on. */
|
|
562
|
-
export function setTheme(g: Game, theme: GameTheme) {
|
|
563
|
-
g.theme = theme;
|
|
564
|
-
g.color = arrowColor(theme);
|
|
565
|
-
if (!g.scenery || g.world.isDisposed) return;
|
|
566
|
-
setBackdropTheme(g.world, g.scenery.backdrop, theme);
|
|
567
|
-
for (const v of g.views) if (v) setArrowColor(g.world, v, g.color);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
/** (Re)creates the arrows and empty-cell dots from the run's state; a new board draws itself in. */
|
|
571
|
-
function spawnBoard(g: Game) {
|
|
572
|
-
const l = g.layout;
|
|
573
|
-
const s = g.scenery;
|
|
574
|
-
if (!l || !s) return;
|
|
575
|
-
const intro = g.intro;
|
|
576
|
-
if (intro) g.introAge = 0;
|
|
577
|
-
g.views = g.arrows.map((a) => (a ? spawnArrow(g.world, l, s.board, a, g.color, intro ? introDelay(a) : null) : null));
|
|
578
|
-
g.dots = [];
|
|
579
|
-
for (let ci = 0; ci < TOTAL_CELLS; ci++) {
|
|
580
|
-
if (g.cellToArrowId[ci] === null) g.dots.push(spawnDot(g.world, l, s.board, Math.floor(ci / GRID_COLS), ci % GRID_COLS));
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/** Arrow starts are spread over the first part of the draw-in. */
|
|
585
|
-
const introDelay = (a: Arrow) => ((a.id % 12) / 12) * DRAW_IN_STAGGER * DRAW_IN_TIME;
|
|
586
|
-
|
|
587
|
-
function clearBoard(g: Game) {
|
|
588
|
-
const w = g.world;
|
|
589
|
-
for (const v of g.views) if (v) w.despawn(v.root);
|
|
590
|
-
for (const d of g.dots) w.despawn(d);
|
|
591
|
-
for (const f of g.flights) w.despawn(f.root);
|
|
592
|
-
g.views = [];
|
|
593
|
-
g.dots = [];
|
|
594
|
-
g.flights = [];
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
function newBoard(g: Game) {
|
|
598
|
-
clearBoard(g);
|
|
599
|
-
const board = generateBoard();
|
|
600
|
-
g.arrows = board.arrows;
|
|
601
|
-
g.cellToArrowId = board.cellToArrowId;
|
|
602
|
-
g.remaining = board.count;
|
|
603
|
-
g.intro = true;
|
|
604
|
-
spawnBoard(g);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
export function start(g: Game) {
|
|
608
|
-
if (g.phase !== 'ready') return;
|
|
609
|
-
if (g.remaining <= 0) {
|
|
610
|
-
newBoard(g);
|
|
611
|
-
g.onRemaining(g.remaining);
|
|
612
|
-
}
|
|
613
|
-
g.phase = 'playing';
|
|
614
|
-
g.world.scoreboard.startCountdown(TIME_LIMIT);
|
|
615
|
-
g.onPhase('playing');
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/** A new board and timer (Stop, Play Again), drawing itself in. The world and its GPU view are kept. */
|
|
619
|
-
export function reset(g: Game) {
|
|
620
|
-
newBoard(g);
|
|
621
|
-
g.world.scoreboard.reset(0, TIME_LIMIT);
|
|
622
|
-
g.won = false;
|
|
623
|
-
g.phase = 'ready';
|
|
624
|
-
g.onScore(0);
|
|
625
|
-
g.onTime(TIME_LIMIT);
|
|
626
|
-
g.onRemaining(g.remaining);
|
|
627
|
-
g.onPhase('ready');
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
export function dispose(g: Game) {
|
|
631
|
-
g.flights = [];
|
|
632
|
-
g.world.dispose();
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
function end(g: Game, won: boolean) {
|
|
636
|
-
g.world.scoreboard.stop();
|
|
637
|
-
g.won = won;
|
|
638
|
-
g.phase = 'over';
|
|
639
|
-
if (won) {
|
|
640
|
-
g.onWin();
|
|
641
|
-
} else {
|
|
642
|
-
// Time up: in-flight arrows are wiped and the run scores nothing (the last arrow of a win flies on).
|
|
643
|
-
for (const f of g.flights) g.world.despawn(f.root);
|
|
644
|
-
g.flights = [];
|
|
645
|
-
g.world.scoreboard.set(0);
|
|
646
|
-
g.onScore(0);
|
|
647
|
-
g.onLose();
|
|
648
|
-
}
|
|
649
|
-
g.onPhase('over');
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
export function tick(g: Game, dt: number) {
|
|
653
|
-
if (g.world.isDisposed) return;
|
|
654
|
-
introPlayed(g, dt);
|
|
655
|
-
const l = g.layout;
|
|
656
|
-
if (l && g.flights.length) {
|
|
657
|
-
// Clamped so a resume (or a long frame) finishes a flight rather than skipping it
|
|
658
|
-
const step = Math.min(dt, 0.05);
|
|
659
|
-
let kept = 0;
|
|
660
|
-
for (const flight of g.flights) if (stepFlight(g.world, l, flight, step, FLY_TIME)) g.flights[kept++] = flight;
|
|
661
|
-
g.flights.length = kept;
|
|
662
|
-
}
|
|
663
|
-
g.world.update(dt);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// ── Tapping ───────────────────────────────────────────────────────────
|
|
667
|
-
|
|
668
|
-
/** The first arrow on `arrow`'s way out: the one that blocks it. */
|
|
669
|
-
function findBlocker(g: Game, arrow: Arrow): Arrow | null {
|
|
670
|
-
const head = arrow.waypoints[arrow.waypoints.length - 1]!;
|
|
671
|
-
const dr = DIR_DR[arrow.exitDir]!;
|
|
672
|
-
const dc = DIR_DC[arrow.exitDir]!;
|
|
673
|
-
let r = head.row + dr;
|
|
674
|
-
let c = head.col + dc;
|
|
675
|
-
while (r >= 0 && r < GRID_ROWS && c >= 0 && c < GRID_COLS) {
|
|
676
|
-
const occupant = g.cellToArrowId[idx(r, c)];
|
|
677
|
-
if (occupant !== null && occupant !== undefined && occupant !== arrow.id) return g.arrows[occupant] ?? null;
|
|
678
|
-
r += dr;
|
|
679
|
-
c += dc;
|
|
680
|
-
}
|
|
681
|
-
return null;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
/** A tap: the arrow under it flies off if its way out is clear, or shakes if it's blocked. */
|
|
685
|
-
export function tap(g: Game, x: number, y: number) {
|
|
686
|
-
const l = g.layout;
|
|
687
|
-
const s = g.scenery;
|
|
688
|
-
if (!l || !s || g.phase !== 'playing') return;
|
|
689
|
-
// Cells own half the gap around them, plus a little, as on the original board
|
|
690
|
-
const lx = x - l.boardLeft + l.hitMargin;
|
|
691
|
-
const ly = y - l.boardTop + l.hitMargin;
|
|
692
|
-
if (lx < 0 || ly < 0) return;
|
|
693
|
-
const col = Math.floor(lx / l.stride);
|
|
694
|
-
const row = Math.floor(ly / l.stride);
|
|
695
|
-
if (row >= GRID_ROWS || col >= GRID_COLS) return;
|
|
696
|
-
const id = g.cellToArrowId[idx(row, col)];
|
|
697
|
-
if (id === null || id === undefined) return;
|
|
698
|
-
const arrow = g.arrows[id];
|
|
699
|
-
if (!arrow) return;
|
|
700
|
-
|
|
701
|
-
const w = g.world;
|
|
702
|
-
if (!isExitClear(g.cellToArrowId, arrow)) {
|
|
703
|
-
const view = g.views[id];
|
|
704
|
-
if (view) flashBlocked(w, view, g.color);
|
|
705
|
-
const blocker = findBlocker(g, arrow);
|
|
706
|
-
const blockerView = blocker && g.views[blocker.id];
|
|
707
|
-
if (blockerView) pulseBlocker(w, blockerView, g.color);
|
|
708
|
-
g.onBlocked();
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// Off it goes: its cells empty (each shows a dot once the tail has passed) and it flies.
|
|
713
|
-
for (const wp of arrow.waypoints) {
|
|
714
|
-
g.cellToArrowId[idx(wp.row, wp.col)] = null;
|
|
715
|
-
g.dots.push(spawnDot(w, l, s.board, wp.row, wp.col));
|
|
716
|
-
}
|
|
717
|
-
g.arrows[id] = null;
|
|
718
|
-
const view = g.views[id];
|
|
719
|
-
if (view) w.despawn(view.root);
|
|
720
|
-
g.views[id] = null;
|
|
721
|
-
g.flights.push(spawnFlight(w, l, s.board, arrow));
|
|
722
|
-
|
|
723
|
-
g.remaining -= 1;
|
|
724
|
-
g.world.scoreboard.add(POINTS_PER_ARROW);
|
|
725
|
-
g.onRemaining(g.remaining);
|
|
726
|
-
g.onClear();
|
|
727
|
-
if (g.remaining <= 0) {
|
|
728
|
-
g.world.scoreboard.set(calculateTimeBasedScore(TIME_LIMIT - g.timeLeft, TIME_LIMIT, g.score));
|
|
729
|
-
g.onScore(g.score);
|
|
730
|
-
pulseBoard(w, s.board);
|
|
731
|
-
end(g, true);
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
g.onScore(g.score);
|
|
735
|
-
}
|