PageSourceSearch

https://georgluif.at/assets/hero-portal.js?v=a3d8ea80

js georgluif.at collected 2026-10-09 21:25:23 UTC 5,539 bytes, 2 lines download raw bytes

1/*! © 2011–2026 Georg Luif · georgluif.at · All rights reserved. */
2import*as et from"./three.module.min.js";import{createTownPortal as ot}from"./town-portal.js";import{createEffect as nt}from"./particles.js";import{FX as rt}from"./fx-recipes.js";import{whereWords as it,areaIn as j}from"./presence-census.js";import{HOME_AREA as y,homeSlots as at,homeSpot as st,arrivalAt as D,insidePortal as ft,nearHome as lt,openPortals as ct,portalWire as C,readPortalWire as ut,COOLDOWN_MS as Y,REARM_R as dt,spawnOfGraph as pt}from"./portal-math.js";const I="ga_portal_v1",M=.5,P=12,mt=.1,ht={color:"#2f6bff",rim:"#a8e4ff"},yt={color:"#3b8cff",rim:"#d8f2ff"};let i=gt(),G=-1/0;function gt(){try{const r=JSON.parse(sessionStorage.getItem(I));return r?ut(r):null}catch{return null}}function J(){try{i?sessionStorage.setItem(I,JSON.stringify(C(i))):sessionStorage.removeItem(I)}catch{}}export{pt as spawnOfGraph};export function createHomePortal(r){const u=r.root,m=r.scene,E=r.camera,g=r.runtime,a=r.avatar||null,w=r.area||y,X=r.selfId||null,d=r.hud||null,l=r.toast||null,R=r.sfx||null,x=r.stats||null,_=r.portals||null,b=r.hasPerk||function(){return!1},H=r.busy||function(){return!1},B=!!r.reducedMotion,N=it(r.words),k=r.homeSpawn||{x:0,z:38};let F=null;const s=new Map;let z=!1,A=0,K=!0,S=!1;const c=[];function U(){const t=ct(F,X,i),e=at(t.map(function(n){return n.id})),o=[];for(const n of t){const f=st(k,e.get(n.id)||0);if(n.area===w&&o.push({key:"f:"+n.id,kind:"field",ownerId:n.id,own:n.own,x:n.x,y:n.y,z:n.z,to:{area:y,at:D(f.x,f.z,k)},toArea:y}),w===y){const p=D(n.x,n.z);o.push({key:"h:"+n.id,kind:"home",ownerId:n.id,own:n.own,x:f.x,y:V(f.x,f.z),z:f.z,to:{area:n.area,at:p,stand:n.y+M},toArea:n.area})}}return o}function V(t,e){return g.groundHeight?g.groundHeight(t,e):0}function q(t){return t.kind==="field"?(t.own?"Your portal":"A portal")+" · home to the meadow":(t.own?"Your portal":"A bird’s portal")+" · back "+(j(N,t.toArea)||"somewhere")}function Q(t){const e=document.createElement("div");return e.className="hero-portal-tag",e.textContent=t,e.style.visibility="hidden",u.appendChild(e),e}function v(){if(z)return;const t=U(),e=new Set;for(const o of t){e.add(o.key);let n=s.get(o.key);if(n&&(Math.abs(n.x-o.x)>.05||Math.abs(n.z-o.z)>.05||n.to.area!==o.to.area)&&(O(n),n=null),n)n.to=o.to,n.own=o.own;else{const f=o.own?yt:ht,p=ot({color:f.color,rim:f.rim,width:1.4,height:2.3});p.object3d.position.set(o.x,o.y,o.z),p.setNear(!0),m.add(p.object3d),n=Object.assign({},o,{tp:p,tag:Q(q(o)),armed:!1}),s.set(o.key,n),K||L(o.x,o.y,o.z)}}s.forEach(function(o,n){e.has(n)||(L(o.x,o.y,o.z),O(o))}),K=!1,d&&d.setPortal(b()&&a?{open:!!i,title:i?"Home portal — open "+(j(N,i.area)||"")+" (T opens it here instead)":"Home portal (T)"}:!1)}function O(t){t.tp.dispose(),t.tag.remove(),s.delete(t.key)}
2function L(t,e,o){if(B||!m)return;const n=nt(rt.magicPoof(),{seed:Math.random()*9973|0});n.points.position.set(t,e+1.1,o),n.points.traverse(function(f){f.raycast=function(){}}),m.add(n.points),n.burst(),c.push({e:n,t:0})}function T(){if(z||!a||S||!b())return!1;const t=performance.now();if(t-G<Y)return l&&l("the portal is still gathering"),!1;if(H())return l&&l("not while riding"),!1;const e=a.position;return w===y&&lt(k,e.x,e.z)?(l&&l("you’re home already"),!1):(G=t,i={area:w,x:+e.x.toFixed(2),y:+(e.y-M).toFixed(2),z:+e.z.toFixed(2)},J(),v(),d&&d.portalCooldown(Y),!g.playEventDefault("portal")&&R&&R.chirp(1.4,.5),a.gesture&&a.gesture("ruffle"),x&&x.record("homeportal",{open:!0}),l&&l("a portal home — walk into it"),!0)}function W(t){if(t.code!=="KeyT"||t.repeat||t.metaKey||t.ctrlKey||t.altKey)return;const e=t.target;if(e&&(e.tagName==="INPUT"||e.tagName==="TEXTAREA"||e.isContentEditable)||u.hasAttribute("data-hero-veiled"))return;const o=u.getBoundingClientRect();o.bottom>0&&o.top<window.innerHeight&&T()}a&&(window.addEventListener("keydown",W),d&&d.onPortal(T));function Z(t){if(S||!_)return;S=!0,t.own&&t.kind==="home"&&(i=null,J()),g.playEventDefault("portal"),x&&x.record("portal",{to:String(t.to.area),home:!0,own:!!t.own});const e=t.to.at;_.goTo(t.to.area,t.to.stand!=null?[e.x,e.z,t.to.stand]:[e.x,e.z])}function $(){if(!a)return;const t=a.position;s.forEach(function(e){const o=Math.hypot(t.x-e.x,t.z-e.z);if(!e.armed){o>dt&&(e.armed=!0);return}ft(t.x,t.y-M,t.z,e.x,e.y,e.z)&&!H()&&Z(e)})}const h=new et.Vector3;function tt(){const t=a?a.position:E.position;s.forEach(function(e){const o=Math.hypot(t.x-e.x,t.z-e.z);if(o>P){e.tag.style.visibility!=="hidden"&&(e.tag.style.visibility="hidden");return}if(h.set(e.x,e.y+2.75,e.z).project(E),h.z>1||h.z<-1){e.tag.style.visibility="hidden";return}e.tag.style.visibility="",e.tag.style.left=((h.x*.5+.5)*u.clientWidth).toFixed(1)+"px",e.tag.style.top=((-h.y*.5+.5)*u.clientHeight).toFixed(1)+"px",e.tag.style.opacity=o<P-3?"1":((P-o)/3).toFixed(2)})}return v(),{wire:function(){return i&&b()?C(i):null},onCensus:function(t){F=t||null,v()},refresh:v,cast:T,update:function(t){if(z)return!1;const e=t>0?t:.016;s.forEach(function(o){o.tp.update(e,E)});for(let o=c.length-1;o>=0;o--){const n=c[o];n.t+=e,n.e.update(e),n.t>1.6&&(m.remove(n.e.points),n.e.dispose(),c.splice(o,1))}return A+=e,A>=mt&&(A=0,$()),tt(),s.size>0||c.length>0},probe:function(){return{mine:i?C(i):null,shown:Array.from(s.keys()),armed:Array.from(s.values()).filter(function(t){return t.armed}).map(function(t){return t.key})}},dispose:function(){z=!0,a&&window.removeEventListener("keydown",W),s.forEach(function(t){O(t)});for(const t of c)m.remove(t.e.points),t.e.dispose();c.length=0}}}

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.