File size: 1,925 Bytes
71eae78
 
 
 
 
 
 
 
 
 
2428d78
 
 
 
 
71eae78
 
 
 
 
 
 
 
 
 
2428d78
 
 
 
 
 
 
 
 
 
 
 
71eae78
2428d78
71eae78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Follows the prince horizontally with a dead zone across the middle third.
// No vertical movement. No easing, no overshoot. Spec 2.2: "It should be
// boring."
//
// This is the only place world coordinates become screen coordinates.

import { TUNING } from './tuning.js';

export function createCamera(viewportW, worldMin, worldMax) {
  const halfDead = (viewportW * TUNING.CAMERA_DEAD_ZONE) / 2;
  // Mutable, because the village changes them exactly once. On the road the
  // camera follows forever; at the mouth it stops following and starts being
  // bounded, and that one switch IS the transition between the two modes.
  let minX = worldMin;
  let maxX = Math.max(worldMin, worldMax - viewportW);

  return {
    x: worldMin, prevX: worldMin,

    snapshot() { this.prevX = this.x; },

    reset(targetX) {
      this.x = this.prevX = this.clamp(targetX - viewportW / 2);
    },

    /**
     * Re-bound the view. Takes CAMERA positions, not world edges — the mouth of
     * the village fixes where the view may go, not where the world stops, and
     * passing world edges through the viewport subtraction twice is exactly the
     * off-by-a-half-screen this file has already produced once.
     */
    setBounds(minCamX, maxCamX) {
      minX = minCamX;
      maxX = Math.max(minCamX, maxCamX);
      this.x = this.clamp(this.x);
    },

    clamp(x) {
      return Math.min(maxX, Math.max(minX, x));
    },

    /** Move only when the target leaves the middle third. */
    follow(targetX) {
      const centre = this.x + viewportW / 2;
      const offset = targetX - centre;
      if (offset > halfDead) this.x += offset - halfDead;
      else if (offset < -halfDead) this.x += offset + halfDead;
      this.x = this.clamp(this.x);
    },

    /** World x -> screen x at a given parallax rate. 1.0 is the actor layer. */
    toScreen(worldX, rate = 1) {
      return worldX - this.x * rate;
    },
  };
}