{"version":3,"sources":["game/utils/inputHandler.ts","game/effects/StarsEffect.ts","game/effects/AbstractEffect.ts","game/Enemy.ts","game/Projectile.ts","game/Player.ts","game/effects/CrtEffect.ts","game/EnemyGroup.ts","game/Game.ts","components/GameView.tsx","components/Overlay.tsx","components/PauseOverlay.tsx","utils/hooks/useKeypress.tsx","components/Button.tsx","components/StartScreen.tsx","components/Controls.tsx","components/ComputerMonitor.tsx","components/Score.tsx","components/Nav.tsx","components/Footer.tsx","App.tsx","serviceWorker.ts","index.tsx"],"names":["KeyCodes","StarsEffect","scene","stars","init","state","this","forEach","star","i","position","z","geometry","THREE","material","color","sphere","x","Math","random","y","scale","add","push","enemyMesh","loadEnemyMesh","a","loader","GLTFLoader","loadAsync","asset","enemy","getObjectByName","setX","setY","setZ","rotation","console","error","BasicEnemy","mesh","speed","acceleration","update","clone","useOrientation","undefined","requestOrientation","DeviceOrientationEvent","requestPermission","then","response","window","catch","err","waitForOrientationRequest","Promise","resolve","reject","awaitResolve","setTimeout","createInputHandler","orientationHandler","instance","onDeviceOrientationChange","pressedKeys","Map","onKeyUp","e","delete","keyCode","handler","keySingleHandlers","get","onKeyDown","set","run","val","key","k","keyHandlers","destroy","document","onkeydown","onkeyup","removeEventListener","addEventListener","materialShader","uniforms","uColor","vertexShader","fragmentShader","Projectile","origin","velocity","initialVelocity","deleted","min","maxVelocity","Game","globalOptions","far","remove","baseGeometry","loadMesh","playerMesh","Player","minX","maxX","canShoot","projectiles","dampening","fireTimeout","stepSize","shoot","p","move","direction","diff","newValue","max","posX","shader","tDiffuse","value","opacity","curve","scanLines","CrtPass","ShaderPass","EnemyGroup","moveDirectionX","round","moveOffsetX","isEmpty","group","entities","movementSpeedX","movementSpeedZ","size","width","height","removeEntity","index","indexOf","splice","length","enemyStepX","checkPlaneCollision","plane","setFromObject","intersectsPlane","getCollidingEntity","pos","adjustedPos","find","abs","createGrid","rows","cols","options","row","col","Enemy","spacing","enemyOptions","pixelRatio","el","delegate","started","paused","renderer","composer","camera","effectsPipeline","inputHandler","updateSize","setSize","setPixelRatio","fov","aspect","near","handleDeviceOrientationChange","gamma","log","player","setupGameInputHandling","leftArrow","rightArrow","space","escape","resume","pause","keyL","startLevel","onStartGame","resetGameState","render","enemies","g","drawDeadline","linewidth","dashSize","gapSize","points","vertices","line","computeLineDistances","setupGameEffects","EffectComposer","renderPass","RenderPass","addPass","bloomPass","UnrealBloomPass","crtPass","setupGameState","score","finishPlane","enemyMovementSpeedX","enemymovementSpeedZ","onPaused","onResumed","requestAnimationFrame","effect","filter","proj","j","onEnemyKilled","gameOver","spawnNextWave","initialHealth","onGameOver","updateScore","amount","oldScore","onScoreChanged","setPath","all","setupGame","antialias","setClearColor","directionalLight","normalize","appendChild","domElement","DeadlineText","styled","div","getGameSize","getElementById","bounds","getBoundingClientRect","innerWidth","StyledOverlay","PauseOverlay","useKeypress","action","useEffect","onKeyup","StyledButton","button","StyledGameOver","MDLogo","img","StartScreen","isGameOver","onStart","src","alt","onClick","KeyBox","span","KeyBlock","ControlsContainer","props","embed","Controls","StyledMonitorContainer","css","StyledMonitor","StyledInnerScreen","ScreenButtonRow","PowerIndicator","playing","PowerButton","ComputerMonitor","children","onPowerClick","id","StyledScore","Score","toLocaleString","minimumIntegerDigits","useGrouping","highScore","StyledNav","nav","StyledAnchor","Nav","href","StyledFooter","footer","Footer","AppContainer","StyledGame","container","useRef","game","current","windowResizeHandler","newSize","target","devicePixelRatio","createGameInstance","className","ref","getHighScore","localStorage","getItem","parseInt","App","useState","queryString","location","search","URLSearchParams","isEmbeded","setPaused","setHighScore","setStarted","setScore","isNewHighScore","setNewHighScore","setGameOver","gameDelegate","onComplete","alert","newHighScoreSet","newScore","setItem","persistHighScore","isNewHigh","Boolean","hostname","match","ReactDOM","StrictMode","navigator","serviceWorker","ready","registration","unregister","message"],"mappings":"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","file":"static/js/main.47ebf0d5.chunk.js","sourcesContent":["let useOrientation: boolean | undefined = undefined;\n\nexport const requestOrientation = () => {\n if (typeof DeviceOrientationEvent === undefined) {\n useOrientation = false;\n return;\n }\n\n\n if (typeof (DeviceOrientationEvent as any).requestPermission !== \"function\") {\n useOrientation = false;\n return\n }\n\n (DeviceOrientationEvent as any)\n .requestPermission()\n .then((response: string) => {\n if (response === \"granted\" && window.DeviceOrientationEvent) {\n useOrientation = true;\n return\n }\n useOrientation = false;\n\n }).catch((err: any) => {\n useOrientation = false;\n });\n\n useOrientation = true;\n return;\n};\n\nexport const waitForOrientationRequest = () => {\n const awaitResolve = () => {\n if (useOrientation !== undefined) {\n return true;\n }\n // console.log(\"waiting:\", useOrientation);\n setTimeout(awaitResolve, 200);\n };\n\n return new Promise((resolve, reject) => {\n if (awaitResolve()) {\n resolve(true);\n } else {\n reject()\n }\n });\n};\n\nconst createInputHandler = (\n orientationHandler?: (e: DeviceOrientationEvent) => void\n) => {\n const instance = {\n onDeviceOrientationChange: orientationHandler,\n pressedKeys: new Map(),\n onKeyUp: (e: KeyboardEvent) => {\n instance.pressedKeys.delete(e.keyCode);\n\n // handle single key press handlers\n const handler = instance.keySingleHandlers.get(e.keyCode);\n if (handler) {\n handler();\n }\n },\n onKeyDown: (e: KeyboardEvent) => {\n instance.pressedKeys.set(e.keyCode, true);\n },\n run: () => {\n instance.pressedKeys.forEach((val, key) => {\n if (!val) {\n return;\n }\n const k = key as KeyCodes;\n const handler = instance.keyHandlers.get(k);\n if (!handler) {\n return;\n }\n handler();\n });\n },\n destroy: () => {\n document.onkeydown = null;\n document.onkeyup = null;\n if (instance.onDeviceOrientationChange) {\n window.removeEventListener(\n \"deviceorientation\",\n instance.onDeviceOrientationChange\n );\n }\n },\n keyHandlers: new Map void>(),\n keySingleHandlers: new Map void>(),\n };\n\n if (instance.onDeviceOrientationChange && useOrientation) {\n window.addEventListener(\n \"deviceorientation\",\n instance.onDeviceOrientationChange\n );\n }\n\n document.onkeydown = instance.onKeyDown;\n document.onkeyup = instance.onKeyUp;\n return instance;\n};\n\nexport enum KeyCodes {\n backspace = 8,\n tab = 9,\n enter = 13,\n shift = 16,\n ctrl = 17,\n alt = 18,\n pause = 19,\n capsLock = 20,\n escape = 27,\n space = 32,\n pageUp = 33,\n pageDown = 34,\n end = 35,\n home = 36,\n leftArrow = 37,\n upArrow = 38,\n rightArrow = 39,\n downArrow = 40,\n insert = 45,\n delete = 46,\n key_0 = 48,\n key_1 = 49,\n key_2 = 50,\n key_3 = 51,\n key_4 = 52,\n key_5 = 53,\n key_6 = 54,\n key_7 = 55,\n key_8 = 56,\n key_9 = 57,\n keyA = 65,\n keyB = 66,\n keyC = 67,\n keyD = 68,\n keyE = 69,\n keyF = 70,\n keyG = 71,\n keyH = 72,\n keyI = 73,\n keyJ = 74,\n keyK = 75,\n keyL = 76,\n keyM = 77,\n keyN = 78,\n keyO = 79,\n keyP = 80,\n keyQ = 81,\n keyR = 82,\n keyS = 83,\n keyT = 84,\n keyU = 85,\n keyV = 86,\n keyW = 87,\n keyX = 88,\n keyY = 89,\n keyZ = 90,\n leftMeta = 91,\n rightMeta = 92,\n select = 93,\n numpad_0 = 96,\n numpad_1 = 97,\n numpad_2 = 98,\n numpad_3 = 99,\n numpad_4 = 100,\n numpad_5 = 101,\n numpad_6 = 102,\n numpad_7 = 103,\n numpad_8 = 104,\n numpad_9 = 105,\n multiply = 106,\n add = 107,\n subtract = 109,\n decimal = 110,\n divide = 111,\n f1 = 112,\n f2 = 113,\n f3 = 114,\n f4 = 115,\n f5 = 116,\n f6 = 117,\n f7 = 118,\n f8 = 119,\n f9 = 120,\n f10 = 121,\n f11 = 122,\n f12 = 123,\n numLock = 144,\n scrollLock = 145,\n semicolon = 186,\n equals = 187,\n comma = 188,\n dash = 189,\n period = 190,\n forwardSlash = 191,\n graveAccent = 192,\n openBracket = 219,\n backSlash = 220,\n closeBracket = 221,\n singleQuote = 222,\n}\n\nexport default createInputHandler;\n","import * as THREE from \"three\";\n\nimport AbstractEffect from \"./AbstractEffect\";\nimport { IGameState } from \"../Game\";\n\nclass StarsEffect extends AbstractEffect {\n private stars: THREE.Mesh[] = [];\n\n constructor(scene: THREE.Scene) {\n super(scene);\n this.init(scene);\n }\n\n update(state: IGameState) {\n // loop through each star\n this.stars.forEach((star, i) => {\n star = this.stars[i];\n\n // and move it forward dependent on the mouseY position.\n star.position.z += i / 10;\n\n // if the particle is too close move it to the back\n if (star.position.z > 1000) star.position.z -= 2000;\n });\n }\n\n private init(scene: THREE.Scene) {\n const stars: THREE.Mesh[] = [];\n // The loop will move from z position of -1000 to z position 1000, adding a random particle at each position.\n for (var z = -1000; z < 1000; z += 20) {\n // Make a sphere (exactly the same as before).\n var geometry = new THREE.SphereGeometry(0.5, 32, 32);\n var material = new THREE.MeshBasicMaterial({ color: 0xffffff });\n var sphere = new THREE.Mesh(geometry, material);\n\n // This time we give the sphere random x and y positions between -500 and 500\n sphere.position.x = Math.random() * 1000 - 500;\n sphere.position.y = Math.random() * 1000 - 500;\n\n // Then set the z position to where it is in the loop (distance of camera)\n sphere.position.z = z;\n\n // scale it up a bit\n sphere.scale.x = sphere.scale.y = 2;\n\n //add the sphere to the scene\n scene.add(sphere);\n\n //finally push it to the stars array\n stars.push(sphere);\n }\n\n this.stars = stars;\n }\n}\n\nexport default StarsEffect;\n","import * as THREE from \"three\";\n\nimport { IGameState } from \"../Game\";\n\nexport interface IGameEffect {\n update: (state: IGameState) => void;\n}\n\nexport default abstract class {\n constructor(private scene: THREE.Scene) {}\n\n update(state: IGameState) {}\n}\n","import * as THREE from \"three\";\nimport { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader\";\n\nexport interface IEnemy {\n mesh: THREE.Mesh;\n health: number;\n canShoot: boolean;\n}\n\nexport interface IEnemyOptions {\n size: number;\n initialHealth: number;\n}\n\nlet enemyMesh: THREE.Mesh | null = null;\n\nexport const loadEnemyMesh = async () => {\n const loader = new GLTFLoader();\n try {\n const asset = await loader.loadAsync(\"enemy-2.gltf\");\n if (!asset) {\n return;\n }\n\n const enemy = asset.scene.getObjectByName(\"Magento\") as THREE.Mesh;\n\n enemy.material = new THREE.MeshPhongMaterial({\n color: 0xf15c22,\n });\n enemy.scale.setX(303);\n enemy.scale.setY(303);\n enemy.scale.setZ(303);\n enemy.rotation.z = 3.15;\n enemy.rotation.x = 3.15;\n enemyMesh = enemy;\n } catch (e) {\n console.error(e);\n }\n};\n\nexport default class BasicEnemy {\n public mesh: THREE.Mesh;\n\n // speed multiplier\n public speed: number = 1;\n\n // movement amount which will fall every tick\n public acceleration: number = 0;\n\n constructor(\n options: IEnemyOptions = {\n size: 5,\n initialHealth: 10,\n }\n ) {\n this.mesh = enemyMesh!.clone();\n }\n\n public update = () => {\n this.mesh.rotation.z += 0.02;\n };\n}\n","import * as THREE from \"three\";\nimport Game from \"./Game\";\n\nexport interface IProjectile {}\n\nexport const createProjectile = (scene: THREE.Scene, origin: THREE.Vector3) => {\n // const materials = new THREE.MeshPhongMaterial({ color: 0xddd500 });\n // const boxGeometry = new THREE.SphereGeometry(1);\n\n // const projectile = new THREE.Mesh(boxGeometry, materials);\n let curve = new THREE.LineCurve3(\n new THREE.Vector3(0, 0, 0),\n new THREE.Vector3(0, 0, -1)\n );\n let baseGeometry = new THREE.TubeBufferGeometry(curve, 25, 1, 8, false);\n let material = new THREE.MeshBasicMaterial({ color: 0x545454 });\n const projectile = new THREE.Mesh(baseGeometry, material);\n\n projectile.position.set(origin.x, origin.y, origin.z);\n scene.add(projectile);\n return projectile;\n};\n\nconst materialShader: THREE.Shader = {\n uniforms: {\n uColor: new THREE.Uniform(new THREE.Color(\"0xdddddd\")),\n },\n vertexShader: `\n varying vec2 texCoords;\n void main() {\n texCoords = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.);\n }`,\n fragmentShader: `\n uniform vec3 uColor; \n varying vec2 texCoords;\n\n void main() {\n vec3 color = vec3(uColor * texCoords.y * 0.5 );\n gl_FragColor = vec4(color,0.1);\n }\n `,\n};\n\nclass Projectile {\n static initialVelocity = 0.6;\n static maxVelocity = 12;\n\n public mesh: THREE.Mesh;\n public velocity: number = Projectile.initialVelocity;\n public deleted: Boolean = false;\n\n constructor(origin: THREE.Vector3, private scene: THREE.Scene) {\n const baseGeometry = new THREE.SphereGeometry(1);\n // let baseGeometry = new THREE.TubeBufferGeometry(curve, 25, 1, 8, false);\n let material = new THREE.ShaderMaterial({ ...materialShader });\n this.mesh = new THREE.Mesh(baseGeometry, material);\n this.mesh.position.x = origin.x;\n this.mesh.position.y = -5;\n }\n\n public update = () => {\n this.velocity = Math.min(\n this.velocity * (1 + this.velocity),\n Projectile.maxVelocity\n );\n this.mesh.position.z -= this.velocity;\n\n // if projectile is out of visible bounds remove it1\n if (this.mesh.position.z >= Game.globalOptions.far) {\n this.delete();\n }\n };\n\n public delete = () => {\n this.scene.remove(this.mesh);\n this.deleted = true;\n };\n}\n\nexport default Projectile;\n","import * as THREE from \"three\";\nimport { GLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader\";\nimport Projectile from \"./Projectile\";\n\nexport const loadMesh = async () => {\n const loader = new GLTFLoader();\n try {\n const asset = await loader.loadAsync(\"player.gltf\");\n if (!asset) {\n return;\n }\n\n const playerMesh = asset.scene.getObjectByName(\"Player\") as THREE.Mesh;\n\n playerMesh.material = new THREE.MeshPhongMaterial({\n color: 0x3498db,\n });\n playerMesh.scale.setX(300);\n playerMesh.scale.setY(300);\n playerMesh.scale.setZ(300);\n playerMesh.rotation.x = 0;\n Player.mesh = playerMesh;\n } catch (e) {\n console.error(e);\n }\n};\n\nexport default class Player {\n static mesh: THREE.Mesh;\n public canShoot: Boolean = true;\n public mesh: THREE.Mesh;\n public projectiles: Projectile[] = [];\n\n // speed multiplier\n public speed: number = 1;\n // movement amount which will fall every tick\n public acceleration: number = 0;\n // public accelerationZ: number = 0;\n\n public dampening = 0.35;\n public fireTimeout = 250;\n public stepSize = 0.7;\n\n constructor(private scene: THREE.Scene, public minX = -90, public maxX = 90) {\n const mesh = Player.mesh.clone();\n mesh.position.z = 20;\n this.mesh = mesh;\n }\n\n public shoot = () => {\n if (!this.canShoot) {\n return;\n }\n const p = new Projectile(this.mesh.position, this.scene);\n this.projectiles.push(p);\n this.scene.add(p.mesh);\n this.canShoot = false;\n // this.accelerationZ += Math.min(0.2, 6);\n setTimeout(() => {\n this.canShoot = true;\n }, this.fireTimeout);\n };\n\n public move = (direction: number, diff: number = this.stepSize) => {\n const newValue = this.acceleration + diff * direction;\n\n this.acceleration =\n newValue < 0 ? Math.max(newValue, -3) : Math.min(newValue, 3);\n };\n\n public update = () => {\n let posX = this.mesh.position.x + this.acceleration * this.speed;\n this.mesh.position.x = Math.min(Math.max(posX, this.minX), this.maxX);\n\n if (this.acceleration) {\n this.mesh.rotation.z = this.acceleration * -0.2;\n this.acceleration =\n this.acceleration < 0\n ? Math.min(0, this.acceleration + this.dampening)\n : Math.max(0, this.acceleration - this.dampening);\n return;\n }\n // if (this.accelerationZ) {\n // this.mesh.position.z = -this.accelerationZ;\n // this.accelerationZ = Math.max(this.accelerationZ - this.dampening, 0);\n // }\n };\n}\n","/**\n * Full-screen textured quad shader\n */\n\nimport { ShaderPass } from \"three/examples/jsm/postprocessing/ShaderPass\";\n\nconst shader = {\n uniforms: {\n tDiffuse: { value: null },\n opacity: { value: 1 },\n curve: { value: 2.25 },\n scanLines: { value: 640 },\n },\n\n vertexShader: `\n varying vec2 vUv;\n uniform float curve;\n\n void main() {\n vUv = uv;\n vec3 pos = position * 1.25;\n gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );\n }\n `,\n\n fragmentShader: `\n uniform float opacity;\n uniform sampler2D tDiffuse;\n uniform float curve;\n uniform float scanLines;\n varying vec2 vUv;\n\n void main() {\n vec2 textCoords = vec2(vUv.x, vUv.y);\n\n // compute distance to center\n vec2 dCenter = abs(0.5 - textCoords);\n\n dCenter *= dCenter;\n\n // add curve \n textCoords -= 0.5;\n textCoords *= 1.0 + dCenter * curve;\n textCoords += 0.5;\n\n vec4 texel = texture2D( tDiffuse, textCoords);\n texel.rgb += sin(textCoords.y * scanLines) * 0.04;\n\n\n \tgl_FragColor = opacity * texel;\n }\n `,\n};\n\nclass CrtPass extends ShaderPass {\n constructor() {\n super(shader);\n }\n}\n\nexport default CrtPass;\n","import * as THREE from \"three\";\nimport Enemy, { IEnemyOptions } from \"./Enemy\";\n\nexport interface IEnemyGridSpawnOptions {\n spacing: THREE.Vector3;\n origin: THREE.Vector3;\n enemyOptions: IEnemyOptions;\n movementSpeedX: number;\n movementSpeedZ: number;\n}\n\nconst targetMovement = 25;\n\nexport default class EnemyGroup {\n /**\n * create a grid of enemies and spawn them\n * @param scene THREE js rendering scene\n * @param rows\n * @param cols\n * @param options\n */\n public static createGrid = (\n scene: THREE.Scene,\n rows: number,\n cols: number,\n options: IEnemyGridSpawnOptions\n ): EnemyGroup => {\n // compute enemy size\n const group = new EnemyGroup(scene);\n group.movementSpeedX = options.movementSpeedX;\n group.movementSpeedZ = options.movementSpeedZ;\n\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n // create enemy\n const enemy = new Enemy();\n\n // set enemy position in grid\n enemy.mesh.position.x =\n options.origin.x +\n col * (options.spacing.x + options.enemyOptions.size);\n enemy.mesh.position.y = options.origin.y + options.spacing.y;\n enemy.mesh.position.z =\n options.origin.z +\n row * (options.spacing.z + options.enemyOptions.size);\n\n group.add(enemy);\n }\n }\n return group;\n };\n\n private moveDirectionX = Math.round(Math.random()) === 0 ? -1 : 1;\n private moveOffsetX = 0;\n public isEmpty: boolean = true;\n\n private group: THREE.Group;\n private entities: Enemy[] = [];\n private movementSpeedX = 0.4;\n private movementSpeedZ = 0.2;\n\n // onscreen bounding box size of enemy group including all entities\n public size: { width: number; height: number } = { width: 0, height: 0 };\n\n constructor(private scene: THREE.Scene) {\n this.group = new THREE.Group();\n this.scene.add(this.group);\n }\n\n public add = (enemy: Enemy) => {\n this.group.add(enemy.mesh);\n this.entities.push(enemy);\n this.isEmpty = false;\n };\n\n public removeEntity = (enemy: Enemy) => {\n this.group.remove(enemy.mesh);\n const index = this.entities.indexOf(enemy);\n\n if (index !== -1) {\n this.entities.splice(index, 1);\n }\n\n this.isEmpty = this.entities.length === 0;\n };\n\n public remove = () => {\n this.scene.remove(this.group);\n };\n\n public update = () => {\n if (this.group.position.x < -targetMovement) {\n this.moveDirectionX = +1;\n }\n if (this.group.position.x > targetMovement) {\n this.moveDirectionX = -1;\n }\n let enemyStepX = this.movementSpeedX * this.moveDirectionX;\n\n this.group.position.z += this.movementSpeedZ;\n this.group.position.x += enemyStepX;\n\n this.entities.forEach((e) => e.update());\n };\n\n public checkPlaneCollision = (plane: THREE.Plane) => {\n const box = new THREE.Box3().setFromObject(this.group);\n return box.intersectsPlane(plane);\n };\n\n public getCollidingEntity = (pos: THREE.Vector3): Enemy | undefined => {\n const adjustedPos = new THREE.Vector3(\n pos.x - this.group.position.x,\n 0,\n pos.z - this.group.position.z\n );\n return this.entities.find(\n (e) =>\n Math.abs(e.mesh.position.z - adjustedPos.z) <= 10 &&\n Math.abs(e.mesh.position.x - adjustedPos.x) <= 10\n );\n };\n}\n","import * as THREE from \"three\";\nimport StarsEffect from \"./effects/StarsEffect\";\nimport BasicEnemy, { loadEnemyMesh } from \"./Enemy\";\nimport createInputHandler, {\n KeyCodes,\n waitForOrientationRequest,\n} from \"./utils/inputHandler\";\nimport Player, { loadMesh } from \"./Player\";\nimport AbstractEffect from \"./effects/AbstractEffect\";\nimport ControlDelegate from \"./ControlDelegate\";\nimport { EffectComposer } from \"three/examples/jsm/postprocessing/EffectComposer.js\";\nimport { RenderPass } from \"three/examples/jsm/postprocessing/RenderPass.js\";\nimport { UnrealBloomPass } from \"three/examples/jsm/postprocessing/UnrealBloomPass.js\";\nimport CrtPass from \"./effects/CrtEffect\";\nimport EnemyGroup from \"./EnemyGroup\";\n\nexport interface IGameState {\n score: number;\n\n player: Player;\n enemies: EnemyGroup[];\n finishPlane: THREE.Plane;\n enemyMovementSpeedX: number;\n enemymovementSpeedZ: number;\n}\n\nclass Game {\n static globalOptions = {\n fov: 60,\n near: 0.1,\n far: 500,\n };\n\n public delegate?: ControlDelegate;\n\n private started: boolean = false;\n private paused: boolean = false;\n private loader = new THREE.TextureLoader();\n private renderer!: THREE.WebGLRenderer;\n private composer!: any;\n private state!: IGameState;\n private scene!: THREE.Scene;\n private camera!: THREE.Camera;\n private effectsPipeline: AbstractEffect[] = [];\n private inputHandler: ReturnType | undefined;\n\n constructor(\n private width: number,\n private height: number,\n private pixelRatio: number,\n private el: HTMLElement\n ) {\n // setup texture loader\n this.loader.setPath(\"textures/\");\n\n // setup all assets\n Promise.all([loadEnemyMesh(), loadMesh()])\n .then(() => {\n this.setupGame();\n })\n .catch((e) => console.error(\"Failed to setup\", e));\n }\n\n // update camera and renderer for a new resolution\n // this may also move the camera to fit the viewport\n public updateSize = (\n width: number,\n height: number,\n pixelRatio: number = 1\n ) => {\n this.renderer.setSize(width, height);\n this.renderer.setPixelRatio(pixelRatio);\n\n let fov = Game.globalOptions.fov;\n if (width < 768) {\n fov = 90;\n }\n\n const aspect = width / height;\n const camera = new THREE.PerspectiveCamera(\n fov,\n aspect,\n Game.globalOptions.near,\n Game.globalOptions.far\n );\n camera.position.z = 80;\n camera.position.y = 130;\n // camera.position.x = 100;\n camera.rotation.set(-0.75, 0, 0);\n this.scene.add(camera);\n if (this.camera) {\n this.scene.remove(this.camera);\n }\n this.camera = camera;\n this.width = width;\n this.height = height;\n this.pixelRatio = pixelRatio;\n };\n\n public handleDeviceOrientationChange = (e: DeviceOrientationEvent) => {\n if (!e.gamma) {\n return;\n }\n console.log(\"Gamma\", e.gamma);\n const gamma = Math.min(Math.max(e.gamma, -40), +40) / 80;\n this.state.player.mesh.position.x = gamma * 140;\n };\n\n public setupGameInputHandling = (player: Player) => {\n // remove old key handlers if present\n if (this.inputHandler) {\n this.el.removeEventListener(\"touchstart\", player.shoot);\n this.inputHandler.destroy();\n }\n\n const inputHandler = createInputHandler(this.handleDeviceOrientationChange);\n this.el.addEventListener(\"touchstart\", player.shoot);\n inputHandler.keyHandlers = new Map void>([\n [\n KeyCodes.leftArrow,\n () => {\n player.move(-1);\n },\n ],\n [\n KeyCodes.rightArrow,\n () => {\n player.move(1);\n },\n ],\n [KeyCodes.space, player.shoot],\n ]);\n\n // setup handlers that should only be triggered on key up\n inputHandler.keySingleHandlers = new Map void>([\n [\n KeyCodes.escape,\n () => {\n console.log(\"Escape pressed\");\n this.paused ? this.resume() : this.pause();\n },\n ],\n [\n KeyCodes.keyL,\n () => {\n this.startLevel();\n },\n ],\n ]);\n\n this.inputHandler = inputHandler;\n };\n\n public onStartGame = async () => {\n // wait till the orientation event was either allowed or not\n try {\n await waitForOrientationRequest();\n } catch (e) { }\n\n if (this.delegate && this.delegate.onStartGame) {\n this.delegate.onStartGame();\n }\n\n // setup initial game state\n this.resetGameState();\n\n // setup input handler\n this.setupGameInputHandling(this.state.player);\n\n this.started = true;\n this.paused = true;\n this.startLevel();\n\n // start render loop\n this.render();\n this.paused = false;\n };\n\n public startLevel = () => {\n // remove all enemies\n this.state.enemies.forEach((g) => g.remove());\n this.state.enemies = [];\n\n // remove all projectiles\n this.state.player.projectiles.forEach((p) => this.scene.remove(p.mesh));\n };\n\n private setupGame() {\n this.renderer = new THREE.WebGLRenderer({\n antialias: true,\n });\n\n this.renderer.setClearColor(0x000000, 1);\n\n const scene = new THREE.Scene();\n this.scene = scene;\n\n // Lights\n scene.add(new THREE.AmbientLight(0x111111));\n\n var directionalLight = new THREE.DirectionalLight(0xffffff, 1);\n directionalLight.position.set(1, 1, 1).normalize();\n scene.add(directionalLight);\n\n this.updateSize(this.width, this.height, this.pixelRatio);\n\n this.el.appendChild(this.renderer.domElement);\n // this.state = this.setupGameState();\n this.drawDeadline();\n this.setupGameEffects();\n }\n\n private drawDeadline = () => {\n //create a blue LineBasicMaterial\n const material = new THREE.LineDashedMaterial({\n color: 0xeeeeee,\n linewidth: 100,\n scale: 2,\n dashSize: 10,\n gapSize: 10,\n });\n const points = [];\n points.push(new THREE.Vector3(-1000, 0, 0));\n points.push(new THREE.Vector3(1000, 0, 0));\n\n const geometry = new THREE.Geometry();\n geometry.vertices = points;\n\n const line = new THREE.Line(geometry, material);\n line.computeLineDistances();\n this.scene.add(line);\n };\n\n private setupGameEffects = () => {\n this.effectsPipeline.push(new StarsEffect(this.scene));\n\n const composer = new EffectComposer(this.renderer);\n\n const renderPass = new RenderPass(this.scene, this.camera);\n composer.addPass(renderPass);\n\n const bloomPass = new (UnrealBloomPass as any)();\n composer.addPass(bloomPass);\n\n const crtPass = new CrtPass();\n composer.addPass(crtPass);\n\n this.composer = composer;\n };\n\n /**\n * clears old game state if present\n */\n private resetGameState = () => {\n if (!this.state) {\n this.state = this.setupGameState();\n return;\n }\n\n // remove everything\n this.state.player.projectiles.forEach((p) => p.delete());\n this.scene.remove(this.state.player.mesh);\n this.state.enemies.forEach((e) => e.remove());\n\n this.state = this.setupGameState();\n };\n\n private setupGameState = (): IGameState => {\n const player = new Player(this.scene);\n this.scene.add(player.mesh);\n return {\n score: 0,\n player: player,\n enemies: [],\n finishPlane: new THREE.Plane(new THREE.Vector3(0, 0, 1)),\n enemyMovementSpeedX: 0.3,\n enemymovementSpeedZ: 0.2,\n };\n };\n\n public pause = () => {\n this.paused = true;\n if (this.delegate?.onPaused) {\n this.delegate!.onPaused();\n }\n };\n\n public resume = () => {\n this.paused = false;\n if (this.delegate?.onResumed) {\n this.delegate!.onResumed();\n }\n // this.render();\n };\n\n private render = () => {\n if (!this.started) {\n return;\n }\n this.inputHandler!.run();\n\n requestAnimationFrame(this.render);\n\n if (this.paused) {\n return;\n }\n\n // update game logic\n this.update();\n\n // render scene and effects\n this.composer.render(this.scene, this.camera);\n\n this.effectsPipeline.forEach((effect) => effect.update(this.state));\n // animateBgEffect(this.stars);\n };\n\n /**\n * update game state\n */\n // TODO: bind to external timer not the framerate\n private update = () => {\n // update player\n this.state.player.update();\n\n this.state.player.projectiles.forEach((p) => p.update());\n\n // filter out unused projectiles\n this.state.player.projectiles = this.state.player.projectiles.filter(\n (p) => !p.deleted\n );\n\n this.state.enemies = this.state.enemies.filter((e) => !e.isEmpty);\n this.state.enemies.forEach((group) => {\n group.update();\n\n // check for collision\n this.state.player.projectiles.forEach((proj, j) => {\n const enemy = group.getCollidingEntity(proj.mesh.position);\n if (!enemy) {\n return;\n }\n\n group.removeEntity(enemy);\n this.onEnemyKilled(enemy);\n this.state.player.projectiles.splice(j, 1);\n this.scene.remove(proj.mesh);\n });\n\n // check if enemy collides with finish\n if (group.checkPlaneCollision(this.state.finishPlane)) {\n this.gameOver();\n }\n });\n\n // if no more enemies spawn new ones\n if (this.state.enemies.length === 0) {\n this.spawnNextWave();\n }\n };\n\n private spawnNextWave = () => {\n this.state.enemies.push(\n EnemyGroup.createGrid(this.scene, 5, 5, {\n origin: new THREE.Vector3(-30, 0, -250),\n spacing: new THREE.Vector3(20, 0, 20),\n enemyOptions: { size: 5, initialHealth: 10 },\n movementSpeedX: this.state.enemyMovementSpeedX,\n movementSpeedZ: this.state.enemymovementSpeedZ,\n })\n );\n\n this.state.enemyMovementSpeedX += 0.1;\n this.state.enemymovementSpeedZ += 0.05;\n this.state.player.fireTimeout = Math.max(\n 0.01,\n this.state.player.fireTimeout - 0.09\n );\n this.state.player.stepSize = Math.min(10, this.state.player.stepSize + 0.1);\n };\n\n private gameOver = () => {\n this.started = false;\n if (this.delegate?.onGameOver) {\n this.delegate.onGameOver(this.state.score, \"You are just bad!\");\n }\n };\n\n private onEnemyKilled = (enemy: BasicEnemy) => {\n this.updateScore(50);\n };\n\n private updateScore = (amount: number) => {\n const oldScore = this.state.score;\n this.state.score += amount;\n\n if (this.delegate?.onScoreChanged) {\n this.delegate?.onScoreChanged(this.state.score, oldScore);\n }\n };\n}\n\nexport default Game;\n","import React, { useRef, useEffect } from \"react\";\nimport styled from \"styled-components\";\nimport ControlDelegate from \"../game/ControlDelegate\";\nimport Game from \"../game/Game\";\n\nexport interface CanvasProps {\n delegate: ControlDelegate;\n started: boolean;\n gameOver: boolean;\n paused: boolean;\n className?: string;\n}\n\nconst DeadlineText = styled.div`\n display: none;\n\n @media screen and (min-width: 768px) {\n display: block;\n }\n position: absolute;\n right: 3vmin;\n bottom: 15vmin;\n color: rgba(255, 0, 0, 0.3);\n`;\n\nconst getGameSize = () => {\n const el = document.getElementById(\"inner-screen\");\n if (el) {\n const bounds = el.getBoundingClientRect();\n return { width: bounds.width, height: bounds.height };\n }\n\n // fallback if element not present\n const width = Math.min(window.innerWidth, 1920) * 0.58;\n const height = width * 0.78;\n\n return { width, height };\n};\n\nconst createGameInstance = (target: HTMLElement) => {\n const size = getGameSize();\n return new Game(\n size.width,\n size.height,\n window.devicePixelRatio ?? 1,\n target\n );\n};\n\nexport default (props: CanvasProps) => {\n const container = useRef();\n const game = useRef();\n\n useEffect(() => {\n if (!props.gameOver && props.started) {\n game.current?.onStartGame();\n }\n }, [props.started, props.gameOver]);\n\n useEffect(() => {\n if (!game.current) {\n return;\n }\n if (props.paused) {\n game.current!.pause();\n } else {\n game.current!.resume();\n }\n }, [game, props.paused]);\n\n // setup resize handler\n useEffect(() => {\n const windowResizeHandler = (e: Event) => {\n if (!game.current) {\n return;\n }\n const newSize = getGameSize();\n game.current.updateSize(newSize.width, newSize.height);\n };\n\n window.addEventListener(\"resize\", windowResizeHandler);\n return () => window.removeEventListener(\"resize\", windowResizeHandler);\n }, [game]);\n\n // setup gl and canvas when it is setup\n useEffect(() => {\n if (!container.current) {\n return;\n }\n\n game.current = createGameInstance(container.current);\n }, [container]);\n\n useEffect(() => {\n if (game.current) {\n game.current.delegate = props.delegate;\n }\n }, [props.delegate, game]);\n\n return (\n <>\n
\n {props.started && DEADLINE}\n \n );\n};\n","import styled from \"styled-components\";\n\nconst StyledOverlay = styled.div`\n position: absolute;\n left: 0;\n top: 0;\n right: 0;\n bottom: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: rgba(0, 0, 0, 0.8);\n color: #fff;\n\n > div {\n max-width: 90%;\n }\n`;\n\nexport default StyledOverlay;\n","import React from \"react\";\nimport StyledOverlay from \"./Overlay\";\n\nconst PauseOverlay = () => {\n return (\n \n
\n

GAME PAUSED

\n

Press ESC to resume

\n
\n
\n );\n};\n\nexport default PauseOverlay;\n","import { useEffect } from \"react\";\nimport { KeyCodes } from \"../../game/utils/inputHandler\";\n/**\n * useKeyPress\n * @param {string} key - the name of the key to respond to, compared against event.key\n * @param {function} action - the action to perform on key press\n */\nconst useKeypress = (key: KeyCodes, action: (e: KeyboardEvent) => void) => {\n useEffect(() => {\n const onKeyup = (e: KeyboardEvent) => {\n if (e.keyCode === key) {\n action(e);\n }\n };\n window.addEventListener(\"keyup\", onKeyup);\n return () => window.removeEventListener(\"keyup\", onKeyup);\n // eslint-disable-next-line\n }, []);\n};\n\nexport default useKeypress;\n","import styled from \"styled-components\";\n\nconst StyledButton = styled.button`\n display: inline-block;\n background-color: transparent;\n outline: 0;\n border-radius: 0;\n border: 4px solid #fff;\n color: #fff;\n padding: 0.9rem;\n font-size: 1.5rem;\n cursor: pointer;\n\n &:hover {\n border-color: #ccc;\n color: #ccc;\n }\n`;\n\nexport default StyledButton;\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport { KeyCodes, requestOrientation } from \"../game/utils/inputHandler\";\nimport useKeypress from \"../utils/hooks/useKeypress\";\nimport StyledButton from \"./Button\";\nimport StyledOverlay from \"./Overlay\";\n\ninterface StartScreenProps {\n onStart: () => void;\n isGameOver: boolean;\n isNewHigh: boolean;\n score: number;\n}\n\nconst StyledGameOver = styled.div`\n font-size: 2rem;\n @media screen and (min-width: 768px) {\n font-size: 3rem;\n }\n margin-bottom: 2rem;\n`;\n\nconst MDLogo = styled.img`\n margin-bottom: 2rem;\n`;\n\nconst StartScreen: React.FC = ({\n isGameOver,\n onStart,\n score,\n}) => {\n useKeypress(KeyCodes.space, () => {\n requestOrientation();\n onStart();\n });\n\n return (\n \n
\n \n {!isGameOver && (\n \n Magento
Destroyers\n
\n )}\n {isGameOver && (\n <>\n GAME OVER\n

Magento has ruined your life...

\n
\n
\n
\n \n )}\n

\n {isGameOver ? \"Press space to try again\" : \"Press space to start\"}\n

\n
\n
\n
\n {\n // request for gestures\n requestOrientation();\n onStart();\n }}\n >\n START GAME\n \n
\n
\n
\n );\n};\n\nexport default StartScreen;\n","import React from \"react\";\nimport styled from \"styled-components\";\n\nconst KeyBox = styled.span`\n display: inline-block;\n border: 2px solid #eee;\n font-size: 1.4rem;\n padding: 0.5rem 0.9rem;\n`;\n\nconst KeyBlock = styled.span`\n display: inline-block;\n white-space: nowrap;\n`;\n\nconst ControlsContainer = styled.div<{ embed?: boolean }>`\n margin-top: 2rem;\n margin-left: 2rem;\n margin-right: 2rem;\n color: #eee;\n padding: 0.5rem;\n text-align: left;\n transform: scale(${(props) => (props.embed ? 0.7 : 1)});\n`;\n\nconst Controls: React.FC<{ embed?: boolean }> = (props) => {\n return (\n \n CONTROLS: \n \n = move left,\n \n \n = move right,\n \n \n SPACE = shoot,\n \n \n ESC = pase\n \n \n );\n};\n\nexport default Controls;\n","import React from \"react\";\nimport styled, { css } from \"styled-components\";\nimport Controls from \"./Controls\";\n\nconst StyledMonitorContainer = styled.div<{ embed?: boolean }>`\n user-select: none;\n position: relative;\n width: 100%;\n height: 100vh;\n\n ${(props) =>\n !props.embed &&\n css`\n @media screen and (min-width: 768px) {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n }\n `}\n`;\n\nconst StyledMonitor = styled.div<{ embed?: boolean }>`\n width: 100%;\n height: 100vh;\n @media screen and (min-width: 768px) {\n ${(props) =>\n props.embed\n ? css`\n padding: 5%;\n width: 90%;\n height: 73%;\n `\n : css`\n padding: 5vmin;\n width: 80vmin;\n height: 65vmin;\n `}\n border-radius: 1rem;\n position: relative;\n background: rgb(193, 193, 193);\n background: linear-gradient(\n 125deg,\n rgba(193, 193, 193, 1) 0%,\n rgba(144, 144, 144, 1) 100%\n );\n }\n`;\n\nconst StyledInnerScreen = styled.div`\n position: relative;\n height: 95%;\n\n @media screen and (min-width: 768px) {\n height: 92%;\n background-color: #000;\n border-radius: 3rem;\n overflow: hidden;\n border: 0.5rem solid #777;\n }\n`;\n\nconst ScreenButtonRow = styled.div`\n display: none;\n @media screen and (min-width: 768px) {\n display: flex;\n }\n position: absolute;\n right: 5vmin;\n bottom: 3vmin;\n padding-right: 2vmin;\n align-items: center;\n`;\n\nconst PowerIndicator = styled.div<{ playing: boolean }>`\n width: 1vmin;\n height: 1vmin;\n border-radius: 50%;\n background-color: ${(props) => (props.playing ? \"#218c74\" : \"red\")};\n border: 0.25vmin solid #aaa;\n`;\n\nconst PowerButton = styled.button`\n display: inline-block;\n padding: 1.2vmin;\n outline: 0;\n border: 0.25vmin solid #aaa;\n border-radius: 5%;\n background: #909090;\n color: #444;\n margin-right: 2vmin;\n cursor: pointer;\n\n &:hover {\n background: #808080;\n }\n\n &:active {\n background: #666363;\n }\n`;\n\nconst ComputerMonitor: React.FC<{\n playing: boolean;\n embed?: boolean;\n onPowerClick?: () => void;\n}> = ({ children, playing, onPowerClick, embed }) => {\n return (\n \n \n {children}\n \n POWER\n \n \n \n\n \n \n );\n};\n\nexport default ComputerMonitor;\n","import React from \"react\";\nimport styled from \"styled-components\";\n\nconst StyledScore = styled.div`\n position: absolute;\n left: 15px;\n top: 50px;\n @media screen and (min-width: 768px) {\n left: 5%;\n top: 5%;\n }\n color: #fff;\n`;\n\nconst Score = (props: { score: number; highScore?: number }) => {\n return (\n \n

\n SCORE:\n {props.score.toLocaleString(\"en\", {\n minimumIntegerDigits: 8,\n useGrouping: false,\n })}\n

\n {props.highScore && (\n
\n HIGH SCORE:\n {props.highScore.toLocaleString(\"en\", {\n minimumIntegerDigits: 8,\n useGrouping: false,\n })}\n
\n )}\n
\n );\n};\n\nexport default Score;\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport { Github } from \"@styled-icons/boxicons-logos/Github\";\n\nconst StyledNav = styled.nav`\n position: fixed;\n left: 0;\n right: 0;\n top: 2vmin;\n padding: 0.5rem;\n display: flex;\n justify-content: space-between;\n max-width: 1280px;\n margin-left: auto;\n margin-right: auto;\n color: #eee;\n z-index: 10;\n`;\n\nconst StyledAnchor = styled.a`\n opacity: 0.8;\n transition: 0.1s opacity ease-in;\n color: #fff;\n\n &::active,\n &::visited {\n color: #fff;\n }\n\n &:hover {\n opacity: 0.2;\n }\n`;\n\nconst Nav = (props: {}) => {\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default Nav;\n","import React from \"react\";\nimport styled from \"styled-components\";\n\nconst StyledFooter = styled.footer`\n @media screen and (min-width: 768px) {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0.5rem;\n }\n padding: 0.5rem;\n text-align: center;\n justify-content: space-between;\n max-width: 1280px;\n margin-left: auto;\n margin-right: auto;\n color: #222;\n z-index: 10;\n`;\n\nconst Footer = (props: {}) => {\n return (\n \n

\n The Magento name and its related logos are trademarks owned by Magento,\n Inc.\n

\n
\n );\n};\n\nexport default Footer;\n","import React, { useRef, useState } from \"react\";\nimport \"./App.css\";\nimport Game from \"./components/GameView\";\nimport styled from \"styled-components\";\nimport PauseOverlay from \"./components/PauseOverlay\";\nimport ControlDelegate from \"./game/ControlDelegate\";\nimport StartScreen from \"./components/StartScreen\";\nimport ComputerMonitor from \"./components/ComputerMonitor\";\nimport Score from \"./components/Score\";\nimport Nav from \"./components/Nav\";\nimport Footer from \"./components/Footer\";\n\nconst AppContainer = styled.div<{ embed?: boolean }>`\n position: relative;\n color: #000;\n font-family: \"Press Start 2P\", monospace;\n background-color: ${(props) => (props.embed ? \"transparent\" : \"#000\")};\n`;\n\nconst StyledGame = styled(Game)`\n user-select: none;\n`;\n\nconst STORAGE_KEY = \"highScore\";\n\nconst persistHighScore = (newScore: number) => {\n const oldScore = getHighScore();\n if (!oldScore) {\n localStorage.setItem(STORAGE_KEY, newScore + \"\");\n return true;\n }\n\n if (oldScore < newScore) {\n localStorage.setItem(STORAGE_KEY, newScore + \"\");\n return true;\n }\n return false;\n};\n\nconst getHighScore = () => {\n const score = localStorage.getItem(STORAGE_KEY);\n if (score) {\n return parseInt(score);\n }\n return undefined;\n};\n\nconst isEmbeded = () => {\n const queryString = window.location.search;\n const urlParams = new URLSearchParams(queryString);\n return !!urlParams.get(\"embed\");\n};\n\nconst App = () => {\n const [embed] = useState(isEmbeded());\n const [paused, setPaused] = useState(false);\n const [highScore, setHighScore] = useState(getHighScore());\n const [started, setStarted] = useState(false);\n const [score, setScore] = useState(0);\n const [isNewHighScore, setNewHighScore] = useState(false);\n const [isGameOver, setGameOver] = useState(false);\n\n const gameDelegate = useRef({\n onComplete: () => alert(\"you have completed the level\"),\n onResumed: () => setPaused(false),\n onPaused: () => setPaused(true),\n onScoreChanged: (value: number) => setScore(value),\n onStartGame: () => {\n setScore(0);\n setGameOver(false);\n setStarted(true);\n },\n onGameOver: (score) => {\n const newHighScoreSet = persistHighScore(score);\n setNewHighScore(newHighScoreSet);\n if (newHighScoreSet) {\n setHighScore(score);\n }\n setGameOver(true);\n },\n });\n\n return (\n
\n {!embed &&
\n );\n};\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\ntype Config = {\n onSuccess?: (registration: ServiceWorkerRegistration) => void;\n onUpdate?: (registration: ServiceWorkerRegistration) => void;\n};\n\nexport function register(config?: Config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(\n process.env.PUBLIC_URL,\n window.location.href\n );\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl: string, config?: Config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl: string, config?: Config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready\n .then(registration => {\n registration.unregister();\n })\n .catch(error => {\n console.error(error.message);\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(\n \n \n ,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}