PageSourceSearch

https://playsout.com/games/Flappy%20Parrot/js/world.js

js playsout.com collected 2026-09-24 10:51:19 UTC 5,522 bytes, 225 lines download raw bytes

1export const PHYSICS = {
2  gravity: 640,
3  flapVy: -196,
4  maxFall: 268,
5  crashBounce: -70,
6  birdX: 66,
7  birdW: 22,
8  birdH: 20,
9  groundY: 194,
10  columnW: 22,
11  columnGap: 112,
12  hitbox: { x: 5, y: 6, w: 12, h: 8 },
13  startY: 82,
14  viewW: 256,
15  viewH: 224,
16  gapMinY: 24,
17  gapBottomPad: 30,
18  gapWander: 44,
19  firstColumnPad: 60,
20  speed0: 60,
21  speed20: 94,
22  gap0: 72,
23  gap20: 56,
24  scoreRamp: 20,
25};
26
27export const POINTS = {
28  base: 80,
29  step: 10,
30};
31
32export function awardPoints(n) {
33  return POINTS.base + (n - 1) * POINTS.step;
34}
35
36function clamp01(t) {
37  return Math.min(1, Math.max(0, t));
38}
39
40function lerp(a, b, t) {
41  return a + (b - a) * clamp01(t);
42}
43
44export function scrollSpeed(score) {
45  return lerp(PHYSICS.speed0, PHYSICS.speed20, score / PHYSICS.scoreRamp);
46}
47
48export function gapHeight(score) {
49  return Math.round(lerp(PHYSICS.gap0, PHYSICS.gap20, score / PHYSICS.scoreRamp));
50}
51
52function makeRng(seed) {
53  let s = seed >>> 0;
54  return () => {
55    s += 1831565813;
56    let t = s;
57    t = Math.imul(t ^ (t >>> 15), t | 1);
58    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
59    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
60  };
61}
62
63export function createWorld({ seed = 1 } = {}) {
64  return {
65    phase: "ready",
66    rng: makeRng(seed),
67    elapsed: 0,
68    t: 0,
69    y: PHYSICS.startY,
70    vy: 0,
71    columns: [],
72    score: 0,
73    clears: 0,
74    events: [],
75  };
76}
77
78export function birdHitbox(world) {
79  const { x, y, w, h } = PHYSICS.hitbox;
80  return {
81    x: PHYSICS.birdX + x,
82    y: world.y + y,
83    w,
84    h,
85  };
86}
87
88export function columnBoxes(column) {
89  const gapBottom = column.gapY + column.gap;
90  return {
91    top: { x: column.x, y: 0, w: PHYSICS.columnW, h: column.gapY },
92    bottom: {
93      x: column.x,
94      y: gapBottom,
95      w: PHYSICS.columnW,
96      h: PHYSICS.groundY - gapBottom,
97    },
98  };
99}
100
101function aabb(a, b) {
102  return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
103}
104
105function pickGapY(world, gap) {
106  const minCenter = PHYSICS.gapMinY + gap / 2;
107  const maxCenter = PHYSICS.groundY - gap - PHYSICS.gapBottomPad + gap / 2;
108  const last = world.columns[world.columns.length - 1];
109  const prevCenter = last
110    ? last.gapY + last.gap / 2
111    : (minCenter + maxCenter) / 2;
112  const wander = (world.rng() * 2 - 1) * PHYSICS.gapWander;
113  const center = Math.min(maxCenter, Math.max(minCenter, prevCenter + wander));
114  return Math.round(center - gap / 2);
115}
116
117function spawnColumn(world, x) {
118  const gap = gapHeight(world.clears);
119  world.columns.push({
120    x,
121    gap,
122    gapY: pickGapY(world, gap),
123    passed: false,
124  });
125}
126
127export function flap(world) {
128  if (world.phase === "falling" || world.phase === "dead") return false;
129  if (world.phase === "ready") {
130    world.phase = "playing";
131    world.t = 0;
132  }
133  world.vy = PHYSICS.flapVy;
134  world.events.push({ type: "flap" });
135  return true;
136}
137
138function crashIntoColumn(world, hitY) {
139  world.phase = "falling";
140  world.t = 0;
141  world.vy = PHYSICS.crashBounce;
142  world.events.push({
143    type: "crash",
144    x: PHYSICS.birdX + PHYSICS.birdW / 2,
145    y: hitY,
146  });
147}
148
149export function updateWorld(world, dt, viewport) {
150  world.t += dt;
151  if (world.phase === "dead" || world.phase === "ready") return;
152
153  world.elapsed += dt;
154  world.vy = Math.min(PHYSICS.maxFall, world.vy + PHYSICS.gravity * dt);
155  world.y += world.vy * dt;
156  if (world.y < 0) {
157    world.y = 0;
158    world.vy = 0;
159  }
160
161  if (world.phase === "falling") {
162    if (world.y + PHYSICS.birdH >= PHYSICS.groundY) {
163      world.y = PHYSICS.groundY - PHYSICS.birdH;
164      world.vy = 0;
165      world.phase = "dead";
166      world.t = 0;
167      world.events.push({
168        type: "land",
169        x: PHYSICS.birdX + PHYSICS.birdW / 2,
170        y: PHYSICS.groundY,
171      });
172    }
173    return;
174  }
175
176  const speed = scrollSpeed(world.clears);
177  for (const column of world.columns) column.x -= speed * dt;
178  if (world.columns.length > 0 && world.columns[0].x + PHYSICS.columnW < 0) {
179    world.columns.shift();
180  }
181  if (world.columns.length === 0) {
182    spawnColumn(world, viewport.width + PHYSICS.firstColumnPad);
183  }
184  while (world.columns[world.columns.length - 1].x < viewport.width) {
185    spawnColumn(
186      world,
187      world.columns[world.columns.length - 1].x + PHYSICS.columnGap,
188    );
189  }
190
191  if (world.y + PHYSICS.birdH >= PHYSICS.groundY) {
192    world.y = PHYSICS.groundY - PHYSICS.birdH;
193    world.vy = 0;
194    world.phase = "dead";
195    world.t = 0;
196    const x = PHYSICS.birdX + PHYSICS.birdW / 2;
197    world.events.push({ type: "crash", x, y: PHYSICS.groundY });
198    world.events.push({ type: "land", x, y: PHYSICS.groundY });
199    return;
200  }
201
202  const box = birdHitbox(world);
203  for (const column of world.columns) {
204    const { top, bottom } = columnBoxes(column);
205    if (aabb(box, top) || aabb(box, bottom)) {
206      crashIntoColumn(world, box.y + box.h / 2);
207      return;
208    }
209  }
210
211  const birdCenterX = PHYSICS.birdX + PHYSICS.birdW / 2;
212  for (const column of world.columns) {
213    if (column.passed || column.x + PHYSICS.columnW > birdCenterX) continue;
214    column.passed = true;
215    world.clears += 1;
216    const gained = awardPoints(world.clears);
217    world.score += gained;
218    world.events.push({
219      type: "score",
220      score: world.score,
221      gained,
222      clears: world.clears,
223    });
224  }
225}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.