PageSourceSearch

https://ntb-chi.vercel.app/js/portal.js

js ntb-chi.vercel.app collected 2026-10-03 07:24:48 UTC 15,541 bytes, 366 lines download raw bytes

1/* ------------------ PORTAL SYSTEM ------------------ */
2
3// Portal variables
4const PORTAL = {
5    startPortalBox: null,
6    exitPortalBox: null,
7    metaversePortalBox: null,
8    exitPortalParticles: null,
9    startPortalParticles: null,
10    metaversePortalParticles: null
11  };
12  
13  function setupPortals() {
14    // Only create start portal if portal parameter is in URL
15    if (new URLSearchParams(window.location.search).get('portal')) {
16      createStartPortal();
17    }
18  
19    // Create exit portal (always present)
20    createExitPortal();
21    
22    // Create metaverse portal
23    createMetaversePortal();
24  }
25  
26  function createStartPortal() {
27    // Create start portal
28    const startPortalGroup = new THREE.Group();
29    startPortalGroup.position.set(CONFIG.SPAWN_POINT_X, CONFIG.SPAWN_POINT_Y, CONFIG.SPAWN_POINT_Z);
30    startPortalGroup.rotation.x = 0.35;
31    startPortalGroup.rotation.y = 0;
32  
33    // Portal components
34    const startPortalGeometry = new THREE.TorusGeometry(15, 2, 16, 100);
35    const startPortalMaterial = new THREE.MeshPhongMaterial({
36      color: 0xff0000,
37      emissive: 0xff0000,
38      transparent: true,
39      opacity: 0.8
40    });
41    const startPortal = new THREE.Mesh(startPortalGeometry, startPortalMaterial);
42    startPortalGroup.add(startPortal);
43  
44    const startPortalInnerGeometry = new THREE.CircleGeometry(13, 32);
45    const startPortalInnerMaterial = new THREE.MeshBasicMaterial({
46      color: 0xff0000,
47      transparent: true,
48      opacity: 0.5,
49      side: THREE.DoubleSide
50    });
51    const startPortalInner = new THREE.Mesh(startPortalInnerGeometry, startPortalInnerMaterial);
52    startPortalGroup.add(startPortalInner);
53  
54    // Create particle system for portal effect
55    const startPortalParticleCount = 500;
56    PORTAL.startPortalParticles = new THREE.BufferGeometry();
57    const startPortalPositions = new Float32Array(startPortalParticleCount * 3);
58    const startPortalColors = new Float32Array(startPortalParticleCount * 3);
59  
60    for (let i = 0; i < startPortalParticleCount * 3; i += 3) {
61      // Create particles in a ring around the portal
62      const angle = Math.random() * Math.PI * 2;
63      const radius = 15 + (Math.random() - 0.5) * 4;
64      startPortalPositions[i] = Math.cos(angle) * radius;
65      startPortalPositions[i + 1] = Math.sin(angle) * radius;
66      startPortalPositions[i + 2] = (Math.random() - 0.5) * 4;
67  
68      // Red color with slight variation
69      startPortalColors[i] = 0.8 + Math.random() * 0.2;
70      startPortalColors[i + 1] = 0;
71      startPortalColors[i + 2] = 0;
72    }
73  
74    PORTAL.startPortalParticles.setAttribute('position', new THREE.BufferAttribute(startPortalPositions, 3));
75    PORTAL.startPortalParticles.setAttribute('color', new THREE.BufferAttribute(startPortalColors, 3));
76  
77    const startPortalParticleMaterial = new THREE.PointsMaterial({
78      size: 0.2,
79      vertexColors: true,
80      transparent: true,
81      opacity: 0.6
82    });
83  
84    const startPortalParticleSystem = new THREE.Points(PORTAL.startPortalParticles, startPortalParticleMaterial);
85    startPortalGroup.add(startPortalParticleSystem);
86  
87    RENDERER.scene.add(startPortalGroup);
88    PORTAL.startPortalBox = new THREE.Box3().setFromObject(startPortalGroup);
89  }
90  
91  function createExitPortal() {
92    const exitPortalGroup = new THREE.Group();
93    exitPortalGroup.position.set(CONFIG.WORLD_SIZE / 2 - 30, CONFIG.SPAWN_POINT_Y, -CONFIG.WORLD_SIZE / 2 + 30);
94    exitPortalGroup.rotation.x = 0;
95    exitPortalGroup.rotation.y = 0;
96  
97    // Create portal effect with same structure as start portal but green
98    const exitPortalGeometry = new THREE.TorusGeometry(15, 2, 16, 100);
99    const exitPortalMaterial = new THREE.MeshPhongMaterial({
100      color: 0x00ff00,
101      emissive: 0x00ff00,
102      transparent: true,
103      opacity: 0.8
104    });
105    const exitPortal = new THREE.Mesh(exitPortalGeometry, exitPortalMaterial);
106    exitPortalGroup.add(exitPortal);
107  
108    const exitPortalInnerGeometry = new THREE.CircleGeometry(13, 32);
109    const exitPortalInnerMaterial = new THREE.MeshBasicMaterial({
110      color: 0x00ff00,
111      transparent: true,
112      opacity: 0.5,
113      side: THREE.DoubleSide
114    });
115    const exitPortalInner = new THREE.Mesh(exitPortalInnerGeometry, exitPortalInnerMaterial);
116    exitPortalGroup.add(exitPortalInner);
117  
118    // Add portal label
119    const canvas = document.createElement('canvas');
120    const context = canvas.getContext('2d');
121    canvas.width = 512;
122    canvas.height = 64;
123    context.fillStyle = '#00ff00';
124    context.font = 'bold 32px Arial';
125    context.textAlign = 'center';
126    context.fillText('VIBEVERSE PORTAL', canvas.width / 2, canvas.height / 2);
127    const texture = new THREE.CanvasTexture(canvas);
128    const labelGeometry = new THREE.PlaneGeometry(30, 5);
129    const labelMaterial = new THREE.MeshBasicMaterial({
130      map: texture,
131      transparent: true,
132      side: THREE.DoubleSide
133    });
134    const label = new THREE.Mesh(labelGeometry, labelMaterial);
135    label.position.y = 20;
136    exitPortalGroup.add(label);
137  
138    // Create particle system for portal effect
139    const exitPortalParticleCount = 500;
140    PORTAL.exitPortalParticles = new THREE.BufferGeometry();
141    const exitPortalPositions = new Float32Array(exitPortalParticleCount * 3);
142    const exitPortalColors = new Float32Array(exitPortalParticleCount * 3);
143  
144    for (let i = 0; i < exitPortalParticleCount * 3; i += 3) {
145      const angle = Math.random() * Math.PI * 2;
146      const radius = 15 + (Math.random() - 0.5) * 4;
147      exitPortalPositions[i] = Math.cos(angle) * radius;
148      exitPortalPositions[i + 1] = Math.sin(angle) * radius;
149      exitPortalPositions[i + 2] = (Math.random() - 0.5) * 4;
150  
151      // Green color with slight variation
152      exitPortalColors[i] = 0;
153      exitPortalColors[i + 1] = 0.8 + Math.random() * 0.2;
154      exitPortalColors[i + 2] = 0;
155    }
156  
157    PORTAL.exitPortalParticles.setAttribute('position', new THREE.BufferAttribute(exitPortalPositions, 3));
158    PORTAL.exitPortalParticles.setAttribute('color', new THREE.BufferAttribute(exitPortalColors, 3));
159  
160    const exitPortalParticleMaterial = new THREE.PointsMaterial({
161      size: 0.2,
162      vertexColors: true,
163      transparent: true,
164      opacity: 0.6
165    });
166  
167    const exitPortalParticleSystem = new THREE.Points(PORTAL.exitPortalParticles, exitPortalParticleMaterial);
168    exitPortalGroup.add(exitPortalParticleSystem);
169  
170    RENDERER.scene.add(exitPortalGroup);
171    PORTAL.exitPortalBox = new THREE.Box3().setFromObject(exitPortalGroup);
172  }
173  
174  function createMetaversePortal() {
175    const metaversePortalGroup = new THREE.Group();
176    metaversePortalGroup.position.set(-CONFIG.WORLD_SIZE / 2 + 30, CONFIG.SPAWN_POINT_Y, -CONFIG.WORLD_SIZE / 2 + 30);
177    metaversePortalGroup.rotation.x = 0;
178    metaversePortalGroup.rotation.y = 0;
179  
180    // Create portal effect with Facebook blue color (#1877F2)
181    const metaversePortalGeometry = new THREE.TorusGeometry(15, 2, 16, 100);
182    const metaversePortalMaterial = new THREE.MeshPhongMaterial({
183      color: 0x1877F2,
184      emissive: 0x1877F2,
185      transparent: true,
186      opacity: 0.8
187    });
188    const metaversePortal = new THREE.Mesh(metaversePortalGeometry, metaversePortalMaterial);
189    metaversePortalGroup.add(metaversePortal);
190  
191    const metaversePortalInnerGeometry = new THREE.CircleGeometry(13, 32);
192    const metaversePortalInnerMaterial = new THREE.MeshBasicMaterial({
193      color: 0x1877F2,
194      transparent: true,
195      opacity: 0.5,
196      side: THREE.DoubleSide
197    });
198    const metaversePortalInner = new THREE.Mesh(metaversePortalInnerGeometry, metaversePortalInnerMaterial);
199    metaversePortalGroup.add(metaversePortalInner);
200  
201    // METAVERSE label
202    const metaverseCanvas = document.createElement('canvas');
203    const metaverseContext = metaverseCanvas.getContext('2d');
204    metaverseCanvas.width = 512;
205    metaverseCanvas.height = 64;
206    metaverseContext.fillStyle = '#1877F2';
207    metaverseContext.font = 'bold 32px Arial';
208    metaverseContext.textAlign = 'center';
209    metaverseContext.fillText('METAVERSE', metaverseCanvas.width / 2, metaverseCanvas.height / 2);
210    const metaverseTexture = new THREE.CanvasTexture(metaverseCanvas);
211    const metaverseLabelGeometry = new THREE.PlaneGeometry(25, 5);
212    const metaverseLabelMaterial = new THREE.MeshBasicMaterial({
213      map: metaverseTexture,
214      transparent: true,
215      side: THREE.DoubleSide
216    });
217    const metaverseLabel = new THREE.Mesh(metaverseLabelGeometry, metaverseLabelMaterial);
218    metaverseLabel.position.y = 20;
219    metaversePortalGroup.add(metaverseLabel);
220  
221    // Create particle system for portal effect
222    const metaversePortalParticleCount = 500;
223    PORTAL.metaversePortalParticles = new THREE.BufferGeometry();
224    const metaversePortalPositions = new Float32Array(metaversePortalParticleCount * 3);
225    const metaversePortalColors = new Float32Array(metaversePortalParticleCount * 3);
226  
227    for (let i = 0; i < metaversePortalParticleCount * 3; i += 3) {
228      const angle = Math.random() * Math.PI * 2;
229      const radius = 15 + (Math.random() - 0.5) * 4;
230      metaversePortalPositions[i] = Math.cos(angle) * radius;
231      metaversePortalPositions[i + 1] = Math.sin(angle) * radius;
232      metaversePortalPositions[i + 2] = (Math.random() - 0.5) * 4;
233  
234      // Facebook blue color with slight variation
235      metaversePortalColors[i] = 0.09;                     // Red component (24/255)
236      metaversePortalColors[i + 1] = 0.47;                 // Green component (119/255)
237      metaversePortalColors[i + 2] = 0.95 + Math.random() * 0.05; // Blue component (242/255)
238    }
239  
240    PORTAL.metaversePortalParticles.setAttribute('position', new THREE.BufferAttribute(metaversePortalPositions, 3));
241    PORTAL.metaversePortalParticles.setAttribute('color', new THREE.BufferAttribute(metaversePortalColors, 3));
242  
243    const metaversePortalParticleMaterial = new THREE.PointsMaterial({
244      size: 0.2,
245      vertexColors: true,
246      transparent: true,
247      opacity: 0.6
248    });
249  
250    const metaversePortalParticleSystem = new THREE.Points(PORTAL.metaversePortalParticles, metaversePortalParticleMaterial);
251    metaversePortalGroup.add(metaversePortalParticleSystem);
252  
253    RENDERER.scene.add(metaversePortalGroup);
254    PORTAL.metaversePortalBox = new THREE.Box3().setFromObject(metaversePortalGroup);
255  }
256  
257  function updatePortals(delta, elapsedTime) {
258    // Update start portal particles if they exist
259    if (PORTAL.startPortalParticles && PORTAL.startPortalParticles.attributes && PORTAL.startPortalParticles.attributes.position) {
260      const positions = PORTAL.startPortalParticles.attributes.position.array;
261      for (let i = 0; i < positions.length; i += 3) {
262        positions[i + 1] += 0.05 * Math.sin(elapsedTime + i);
263      }
264      PORTAL.startPortalParticles.attributes.position.needsUpdate = true;
265    }
266  
267    // Update exit portal particles
268    if (PORTAL.exitPortalParticles && PORTAL.exitPortalParticles.attributes && PORTAL.exitPortalParticles.attributes.position) {
269      const positions = PORTAL.exitPortalParticles.attributes.position.array;
270      for (let i = 0; i < positions.length; i += 3) {
271        positions[i + 1] += 0.05 * Math.sin(elapsedTime + i);
272      }
273      PORTAL.exitPortalParticles.attributes.position.needsUpdate = true;
274    }
275  
276    // Check for start portal collision if it exists
277    if (new URLSearchParams(window.location.search).get('portal') && PORTAL.startPortalBox) {
278      const playerBox = new THREE.Box3().setFromObject(PLAYER.bike);
279      const portalDistance = playerBox.getCenter(new THREE.Vector3()).distanceTo(PORTAL.startPortalBox.getCenter(new THREE.Vector3()));
280  
281      if (portalDistance < 50) {
282        // Get ref from URL params
283        const urlParams = new URLSearchParams(window.location.search);
284        const refUrl = urlParams.get('ref');
285        if (refUrl) {
286          // Add https if not present and include query params
287          let url = refUrl;
288          if (!url.startsWith('http://') && !url.startsWith('https://')) {
289            url = 'https://' + url;
290          }
291          const currentParams = new URLSearchParams(window.location.search);
292          const newParams = new URLSearchParams();
293          for (const [key, value] of currentParams) {
294            if (key !== 'ref') {
295              newParams.append(key, value);
296            }
297          }
298          const paramString = newParams.toString();
299          window.location.href = url + (paramString ? '?' + paramString : '');
300        }
301      }
302    }
303  
304    // Check for exit portal collision
305    if (PORTAL.exitPortalBox) {
306      const playerBox = new THREE.Box3().setFromObject(PLAYER.bike);
307      const portalDistance = playerBox.getCenter(new THREE.Vector3()).distanceTo(PORTAL.exitPortalBox.getCenter(new THREE.Vector3()));
308  
309      if (portalDistance < 50) {
310        // Start loading the next page in the background
311        const currentParams = new URLSearchParams(window.location.search);
312        const newParams = new URLSearchParams();
313        newParams.append('portal', 'true');
314  
315        // Use player's progress as parameters
316        newParams.append('score', PLAYER.score);
317        newParams.append('color', RENDERER.effectsConfig.playerColor);
318  
319        for (const [key, value] of currentParams) {
320          if (!['portal', 'score', 'color'].includes(key)) {
321            newParams.append(key, value);
322          }
323        }
324  
325        const paramString = newParams.toString();
326        const nextPage = 'https://portal.pieter.com' + (paramString ? '?' + paramString : '');
327  
328        // Create hidden iframe to preload next page
329        if (!document.getElementById('preloadFrame')) {
330          const iframe = document.createElement('iframe');
331          iframe.id = 'preloadFrame';
332          iframe.style.display = 'none';
333          iframe.src = nextPage;
334          document.body.appendChild(iframe);
335        }
336  
337        // Only redirect once actually in the portal (very close)
338        if (portalDistance < 15) {
339          window.location.href = nextPage;
340        }
341      }
342    }
343  
344    // Update Metaverse portal particles
345    if (PORTAL.metaversePortalParticles && PORTAL.metaversePortalParticles.attributes && PORTAL.metaversePortalParticles.attributes.position) {
346      const positions = PORTAL.metaversePortalParticles.attributes.position.array;
347      for (let i = 0; i < positions.length; i += 3) {
348        positions[i + 1] += 0.05 * Math.sin(elapsedTime + i);
349      }
350      PORTAL.metaversePortalParticles.attributes.position.needsUpdate = true;
351    }
352  
353    // Check for Metaverse portal collision
354    if (PORTAL.metaversePortalBox) {
355      const playerBox = new THREE.Box3().setFromObject(PLAYER.bike);
356      const portalDistance = playerBox.getCenter(new THREE.Vector3()).distanceTo(PORTAL.metaversePortalBox.getCenter(new THREE.Vector3()));
357  
358      if (portalDistance < 50) {
359        // Only redirect once actually in the portal (very close)
360        if (portalDistance < 15) {
361          // Redirect to the metaverse URL
362          window.location.href = "https://metaverse-delta.vercel.app/";
363        }
364      }
365    }
366  }

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.