| 10431 | } |
| 10432 | |
| 10433 | function possess(b,host){ |
| 10434 | if(POV.bird)return; |
| 10435 | handAbort(); setHover(null); |
| 10436 | POV.bird=b; POV.host=host; POV.t0=clock.getElapsedTime(); POV.bank=0; |
| 10437 | POV.lookX=POV.lookY=POV.tLookX=POV.tLookY=0; |
| 10438 | /* Remember exactly where it was standing so putting it back is free. The bird |
| 10439 | stays parented where it is and the tour is written into its parent's space, |
| 10440 | which keeps it inside everything that already owns and disposes it. */ |
| 10441 | POV.home=b.position.clone(); |
| 10442 | POV.route=tourRoute(); |
| 10443 | /* Sightseeing pace. The old orbit was ambient motion meant to be glanced at |
| 10444 | and this is travel, but travel you cannot look out of is a rollercoaster — |
| 10445 | about four seconds a district, which is long enough to read one. */ |
| 10446 | POV.speed=OPEN?9:16; |
| 10447 | POV.manual=false; POV.keys.clear(); POV.yaw=0; POV.vel.set(0,0,0); |
| 10448 | b.getWorldPosition(POV.prev); POV.pos.copy(POV.prev); |
| 10449 | POV.dir.set(0,0,1); |
| 10450 | /* Start the lap at whatever point of the route it is already nearest, so the |
| 10451 | ride begins where the bird is instead of teleporting it across the realm. */ |
| 10452 | POV.u=0; |
| 10453 | if(POV.route){ |
| 10454 | let bestD=1e9; |
| 10455 | for(let i=0;i<=120;i++){ |
| 10456 | const u=i/120, d=POV.route.curve.getPointAt(u).distanceToSquared(POV.prev); |
| 10457 | if(d<bestD){ bestD=d; POV.u=u; } |
| 10458 | } |
| 10459 | } |
| 10460 | setView(camFP); |
| 10461 | rootEl.classList.add('pov'); |
| 10462 | emit({riding:{name:(host&&(host.niche?nameOf(host) |
| 10463 | :host.realm.name))||'—',manual:false}}); |
| 10464 | panning=rotating=false; |
| 10465 | flash(); |
| 10466 | } |
| 10467 | function unpossess(){ |
| 10468 | setHover(null); POV.aim=null; |
| 10469 | if(!POV.bird)return; |