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.