| 10366 | } |
| 10367 | |
| 10368 | function possess(b,host){ |
| 10369 | if(POV.bird)return; |
| 10370 | handAbort(); setHover(null); |
| 10371 | POV.bird=b; POV.host=host; POV.t0=clock.getElapsedTime(); POV.bank=0; |
| 10372 | POV.lookX=POV.lookY=POV.tLookX=POV.tLookY=0; |
| 10373 | /* Remember exactly where it was standing so putting it back is free. The bird |
| 10374 | stays parented where it is and the tour is written into its parent's space, |
| 10375 | which keeps it inside everything that already owns and disposes it. */ |
| 10376 | POV.home=b.position.clone(); |
| 10377 | POV.route=tourRoute(); |
| 10378 | /* Sightseeing pace. The old orbit was ambient motion meant to be glanced at |
| 10379 | and this is travel, but travel you cannot look out of is a rollercoaster — |
| 10380 | about four seconds a district, which is long enough to read one. */ |
| 10381 | POV.speed=OPEN?9:16; |
| 10382 | POV.manual=false; POV.keys.clear(); POV.yaw=0; POV.vel.set(0,0,0); |
| 10383 | b.getWorldPosition(POV.prev); POV.pos.copy(POV.prev); |
| 10384 | POV.dir.set(0,0,1); |
| 10385 | /* Start the lap at whatever point of the route it is already nearest, so the |
| 10386 | ride begins where the bird is instead of teleporting it across the realm. */ |
| 10387 | POV.u=0; |
| 10388 | if(POV.route){ |
| 10389 | let bestD=1e9; |
| 10390 | for(let i=0;i<=120;i++){ |
| 10391 | const u=i/120, d=POV.route.curve.getPointAt(u).distanceToSquared(POV.prev); |
| 10392 | if(d<bestD){ bestD=d; POV.u=u; } |
| 10393 | } |
| 10394 | } |
| 10395 | setView(camFP); |
| 10396 | rootEl.classList.add('pov'); |
| 10397 | emit({riding:{name:(host&&(host.niche?nameOf(host) |
| 10398 | :host.realm.name))||'—',manual:false}}); |
| 10399 | panning=rotating=false; |
| 10400 | flash(); |
| 10401 | } |
| 10402 | function unpossess(){ |
| 10403 | setHover(null); POV.aim=null; |
| 10404 | if(!POV.bird)return; |