Source: fs-init.js

/**
 * Initializes the flight simulator game
 */
async function init() {

  // config for three.js
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  renderer = new THREE.WebGLRenderer();
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap

  // resize the renderer when the window is resized
  window.addEventListener("resize", () => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
  });
  renderer.setSize(window.innerWidth, window.innerHeight);

  // add a clock
  clock = new THREE.Clock();
  deltaTime = 0;

  // add the renderer to the dom
  camera.position.set(4, 8, 17);

  initStats();
  initDevControls();

  placeTorusObjects();
  placeObstaclesObjects();

  // add a point light to the top of the scene
  const pointLight = new THREE.PointLight(0xffffff, 1, 1000);
  pointLight.position.set(0, torusSpawnRadius, -100);
  scene.add(pointLight);
  scene.add(new THREE.HemisphereLight(0xddeeff, 0x0f0e0d, 0.5))

  // init ocean and sky
  await initOceanAndSky();

  // add the plane
  await initFlying();
  startTime = new Date().getTime();
  checkForPlaneCollision = false;

  // add event listener on mouse click for a boost
  document.addEventListener("click", () => { speed += 10 });

  // check if the controls should be inverted
  if (localStorage.getItem("invertedControls") === "true") invertedControls = true;
  showInvertedControlsDiv();

  // add the canvas and remove the loading div
  document.body.appendChild(renderer.domElement);
  document.body.removeChild(document.getElementById("loading"));

}


/**
 * Initializes the ocean and sky
 * !!! This code and the textures are directly from three.js !!!
 */
async function initOceanAndSky() {

  // water
  const waterGeometry = new THREE.PlaneGeometry(10000, 10000);
  water = new THREE.Water(
    waterGeometry,
    {
      textureWidth: 512,
      textureHeight: 512,
      waterNormals: new THREE.TextureLoader().load('../textures/waternormals.jpg', function (texture) { texture.wrapS = texture.wrapT = THREE.RepeatWrapping; }),
      sunDirection: new THREE.Vector3(),
      sunColor: 0xffffff,
      waterColor: 0x001e0f,
      distortionScale: 3.7,
      fog: scene.fog !== undefined
    }
  );
  water.rotation.x = - Math.PI / 2;
  scene.add(water);

  // sky
  const sky = new THREE.Sky();
  sky.scale.setScalar(10000);
  scene.add(sky);
  const parameters = {
    elevation: 0.4,
    azimuth: 180
  };
  const pmremGenerator = new THREE.PMREMGenerator(renderer);
  let renderTarget;

  // sun
  const phi = THREE.MathUtils.degToRad(90 - parameters.elevation);
  const theta = THREE.MathUtils.degToRad(parameters.azimuth);
  sun = new THREE.Vector3();
  sun.setFromSphericalCoords(1, phi, theta);
  sky.material.uniforms['sunPosition'].value.copy(sun);
  water.material.uniforms['sunDirection'].value.copy(sun).normalize();

  // environment
  if (renderTarget !== undefined) renderTarget.dispose();
  renderTarget = pmremGenerator.fromScene(sky);
  scene.environment = renderTarget.texture;
}


/**
 * Initialize the FPS stats
 */
function initStats() {
  stats = new Stats();
  stats.setMode(0);
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.left = '10px';
  stats.domElement.style.top = '80px';
  stats.domElement.id = "stats";
  stats.domElement.style.display = "none";
  document.body.appendChild(stats.domElement);
}


/**
 * Quits the game and shows a game over message
 */
function gameOver() {

  // remove the plane
  scene.remove(sceneObjects.modelPlane);

  // stop the game
  isFlying = false;
  isGameOver = true;

  // set cursor to default
  document.body.style.cursor = "pointer";

  // show game over message and score in the middle of the screen
  const gameOverDiv = document.getElementById("gameOverDiv");
  const gameOverScreen = document.getElementById("gameOverScreen");
  gameOverScreen.style.display = "flex";
  gameOverDiv.innerHTML = "Game over! </br> Your score is: " + torusScore;

  // show restart button
  const restartButton = document.getElementById("restartButton");
  restartButton.onclick = () => {
    location.reload();
  }

  // show a exit button
  const exitButton = document.getElementById("exitButton");
  exitButton.onclick = () => {
    location.href = "/?redirect-from=flight-simulator";
  }
}


/**
 * Inverts the controls and saves the setting in the local storage
 */
function invertControls() {
  invertedControls = !invertedControls;
  localStorage.setItem("invertedControls", invertedControls);
  showInvertedControlsDiv();
}


/**
 * Shows a alert with if the controls are inverted or not for 3 seconds
 */
function showInvertedControlsDiv() {
  const textIfTrue = "Inverted Controls: On <br/> The airplane will follow your mouse cursor. <br/> This setting is saved in your browser and can be toggled by pressing 'I'.";
  const textIfFalse = "Inverted Controls: Off <br/> The control direction is realisitc like in a real airplane. <br/> This setting is saved in your browser and can be toggled by pressing 'I'.";
  document.getElementById("invertedControls").innerHTML = invertedControls ? textIfTrue : textIfFalse;
  document.getElementById("invertedControls").style.display = "block";
  if (invertedControlsDivTimeout != null) {
    clearTimeout(invertedControlsDivTimeout);
    invertedControlsDivTimeout = null;
  }
  invertedControlsDivTimeout = setTimeout(() => {
    document.getElementById("invertedControls").style.display = "none";
  }, 3000);
}


/**
 * Initialize developer controls / keyboard shortcuts and experimental features
 */
function initDevControls() {

  window.addEventListener("keydown", event => {
    switch (event.key) {

      case "j":
      case "J":

        // toggle stats visibility
        stats.domElement.style.display = stats.domElement.style.display === "none" ? "block" : "none";

        break;

      case "k":
      case "K":

        // toogle vector visibility
        showFlightVectors = !showFlightVectors;

        break;

      case "f":
      case "F":

        // go back to flight school
        location.href = "/?redirect-from=flight-simulator";

        break;

      case "i":
      case "I":

        invertControls();

        break;

      case "p":
      case "P":

        // pause the game
        if (isGameOver) break;
        if (isFlying) {
          isFlying = false;
          document.getElementById("time").innerHTML = "Paused";
        } else {
          isFlying = true;
        }

        break;

    }
  });

}