diff --git a/public/tide-pool.js b/public/tide-pool.js
index 06beb28..4607721 100644
--- a/public/tide-pool.js
+++ b/public/tide-pool.js
@@ -1214,7 +1214,12 @@ async function initialize() {
const WEIGHT = { collapse: 9, fall: 6, form: 5, found: 5, debut: 4, erect: 3.5, complete: 3, birth: 3, arrive: 2.5, repair: 2 };
function candidates() {
const light = daylight(world), picks = [];
- const add = (x, z, score, zoom) => { if (Number.isFinite(x + z) && onShelf(world, x, z)) picks.push({ x, z, score, zoom }); };
+ const add = (x, z, score, zoom) => {
+ if (!Number.isFinite(x + z) || !onShelf(world, x, z)) return null;
+ const pick = { x, z, score, zoom };
+ picks.push(pick);
+ return pick;
+ };
for (const e of world.events) {
const age = world.time - e.time;
if (age > 45 || e.x == null || !WEIGHT[e.type]) continue;
@@ -1229,16 +1234,32 @@ async function initialize() {
}
add(t.x, t.z, 1 + t.n / 70 + lights * (.4 + dusk), clamp(3 + t.reach, 3.4, 6));
}
- const fish = world.creatures.filter(c => c.sp === 'tab');
- if (fish.length > 4) add(fish.reduce((a, c) => a + c.x, 0) / fish.length, fish.reduce((a, c) => a + c.z, 0) / fish.length, 1.4, 3.4);
+ // The school, where the most fish swim together; at night its glowing wake is the best thing on the shelf.
+ const school = schoolAt();
+ const followed = school && school.n > 3 && add(school.x, school.z, 1 + school.n * .1 + dark * 3, 3.2);
+ if (followed) followed.school = true;
+ // In a storm, the largest structure taking the waves.
+ const storm = stormAt(world);
+ if (storm > .1 && world.structures.length) { const t = [...world.structures].sort((a, b) => b.n - a.n)[0]; add(t.x, t.z, 4 + storm * 4, 4.2); }
return picks;
}
+ function schoolAt() {
+ const fish = world.creatures.filter(c => c.sp === 'tab');
+ let best = null;
+ for (const a of fish) {
+ const near = fish.filter(b => Math.hypot(a.x - b.x, a.z - b.z) < 2);
+ if (!best || near.length > best.length) best = near;
+ }
+ return best && { x: best.reduce((s, c) => s + c.x, 0) / best.length, z: best.reduce((s, c) => s + c.z, 0) / best.length, n: best.length };
+ }
function direct(dt) {
const now = performance.now(), last = director.shot;
const urgent = last && world.events.some(e => (e.type === 'collapse' || e.type === 'fall') && world.time - e.time < 2 && e.x != null &&
Math.hypot(e.x - last.x, e.z - last.z) > 1);
if (!last || now > director.until || urgent) {
- const picks = candidates().map(p => ({ ...p, score: p.score + Math.random() * .8 - (last && Math.hypot(p.x - last.x, p.z - last.z) < 2 ? 1.5 : 0) }));
+ // Something else next time: not the same place, and not the school twice running.
+ const picks = candidates().map(p => ({ ...p, score: p.score + Math.random() * .8 - (last && Math.hypot(p.x - last.x, p.z - last.z) < 2 ? 1.5 : 0) -
+ (last?.school && p.school ? 3 : 0) }));
picks.sort((a, b) => b.score - a.score);
// Now and then a wide shot of the whole shelf between the close ones.
director.wide = !urgent && !director.wide && Math.random() < .3;
@@ -1247,6 +1268,11 @@ async function initialize() {
director.until = now + (director.wide ? 14000 : 20000 + Math.random() * 12000);
}
director.phase += dt;
+ // A school swims on, so a shot of one keeps it in view.
+ if (director.shot.school) {
+ const school = schoolAt();
+ if (school) { director.shot.x += (school.x - director.shot.x) * Math.min(1, dt * .4); director.shot.z += (school.z - director.shot.z) * Math.min(1, dt * .4); }
+ }
// Never quite still: a slow circle around the subject, and a slow breath in and out.
const shot = director.shot, t = director.phase;
goal.x = shot.x + Math.cos(t * .07) * .7; goal.z = shot.z + Math.sin(t * .053) * .45;
diff --git a/src/components/tide-pool.tsx b/src/components/tide-pool.tsx
index 5878bcd..64350a5 100644
--- a/src/components/tide-pool.tsx
+++ b/src/components/tide-pool.tsx
@@ -141,7 +141,7 @@ export function TidePoolContent() {
{/* Lets the three.js add-ons resolve the same pinned module the page already uses. */}
-
+
);
}