1// Behavior registry for active human sites. 2 3var humanSiteBehaviors = {}; 4 5function registerHumanSiteBehavior(kind, behavior) { 6 if (!kind || !behavior) return; 7 humanSiteBehaviors[kind] = behavior; 8} 9 10function getHumanSiteBehavior(siteOrKind) { 11 const kind = typeof siteOrKind === 'string' 12 ? siteOrKind 13 : siteOrKind && siteOrKind.userData && siteOrKind.userData.siteKind; 14 return kind ? humanSiteBehaviors[kind] || null : null; 15} 16 17function attachHumanSiteBehavior(site, seed) { 18 const behavior = getHumanSiteBehavior(site); 19 if (behavior && typeof behavior.attach === 'function') { 20 behavior.attach(site, seed); 21 } 22} 23 24function notifyHumanSiteAssimilated(site) { 25 const behavior = getHumanSiteBehavior(site); 26 if (behavior && typeof behavior.assimilate === 'function') { 27 behavior.assimilate(site); 28 } 29} 30 31function updateHumanSiteBehaviors(t, dt) { 32 if (!Array.isArray(humanSites)) return; 33 for (const site of humanSites) { 34 const behavior = getHumanSiteBehavior(site); 35 if (behavior && typeof behavior.update === 'function') { 36 behavior.update(site, t, dt); 37 } 38 } 39} 40 41// Drone gantry behavior. 42 43var HUMAN_GANTRY_REBUILD_TIME = 120; 44var HUMAN_GANTRY_LAUNCH_DELAY = 5.5; 45var HUMAN_ROVER_DEPLOY_DELAY = 6.0; 46var HUMAN_ROVER_REDEPLOY_TIME = 42.0; 47var HUMAN_ROVER_MAX_ACTIVE = 1; 48var HUMAN_ROVER_SCOUT_MIN_MISSION_TIME = 86.0; 49var HUMAN_POST_TUTORIAL_PASSIVE_TIME = 45.0; 50 51function isHumanGantryCombatUnlocked() { 52 return typeof window === 'undefined' 53 || typeof window.isFarColonyHumanCombatUnlocked !== 'function' 54 || window.isFarColonyHumanCombatUnlocked(); 55} 56 57function getHumanGantryPostTutorialPassiveRemaining() { 58 if (typeof window === 'undefined' 59 || typeof window.getFarColonyHumanPostTutorialPassiveRemaining !== 'function') { 60 return 0; 61 } 62 const remaining = Number(window.getFarColonyHumanPostTutorialPassiveRemaining()); 63 return Number.isFinite(remaining) ? Math.max(0, remaining) : 0; 64} 65 66function isHumanGantryBurnDroneAllowed() { 67 return isHumanGantryCombatUnlocked() && getHumanGantryPostTutorialPassiveRemaining() <= 0; 68} 69 70function isHumanGantryRoverAllowed() { 71 return isHumanGantryCombatUnlocked(); 72} 73 74function isHumanGantryRoverPassiveMode() { 75 return isHumanGantryCombatUnlocked() && getHumanGantryPostTutorialPassiveRemaining() > 0; 76} 77 78function getHumanGantryDormantDelay(gantry) { 79 if (!gantry || !gantry.site || !gantry.site.userData) return 0; 80 const delay = Number(gantry.site.userData.dormantDelay); 81 return Number.isFinite(delay) ? Math.max(0, delay) : 0; 82} 83 84function getHumanGantryDormantRemaining(gantry) { 85 const delay = getHumanGantryDormantDelay(gantry); 86 if (delay <= 0 || !gantry || gantry.activated) return 0; 87 if (!isHumanGantryCombatUnlocked() || getHumanGantryPostTutorialPassiveRemaining() > 0) return Infinity; 88 const now = typeof getGameplayTime === 'function' ? getGameplayTime() : 0; 89 if (Number.isFinite(gantry.activationTime)) return Math.max(0, gantry.activationTime - now); 90 return delay; 91} 92 93function isHumanGantryDormant(gantry) { 94 return getHumanGantryDormantRemaining(gantry) > 0; 95} 96 97function setHumanGantryDormantHold(gantry) { 98 if (!gantry || gantry.site.userData.assimilated) return; 99 shutdownHumanGantryDrone(gantry); 100 gantry.state = 'dormant'; 101 gantry.timer = 0; 102} 103 104function updateHumanGantryDormancy(gantry, t, activationClockUnlocked) { 105 const delay = getHumanGantryDormantDelay(gantry); 106 if (!gantry || delay <= 0 || gantry.activated) return false; 107 if (!activationClockUnlocked) return false; 108 if (!Number.isFinite(gantry.activationTime)) gantry.activationTime = t + delay; 109 if (t < gantry.activationTime) return false; 110 gantry.activated = true; 111 gantry.activationTime = t; 112 return true; 113} 114 115function getHumanGantryRoverMaxActive() { 116 const alert = typeof getHumanAlertLevel === 'function' ? getHumanAlertLevel() : HUMAN_ALERT_START_LEVEL; 117 return HUMAN_ROVER_MAX_ACTIVE + (alert >= 4 ? 1 : 0); 118} 119 120function getHumanGantryRoverRedeployTime(gantry) { 121 const alert = typeof getHumanAlertLevel === 'function' ? getHumanAlertLevel() : HUMAN_ALERT_START_LEVEL; 122 const pressureScale = Math.max(0, alert - 1) * 3.2; 123 return Math.max(24.0, HUMAN_ROVER_REDEPLOY_TIME - pressureScale) 124 + hash(gantry.seed, 2100 + gantry.roverSerial) * 10.0; 125} 126 127function getHumanGantryWorldLocal(gantry, key, fallbackX, fallbackY, fallbackZ) { 128 const local = new THREE.Vector3(fallbackX, fallbackY, fallbackZ); 129 const custom = gantry && gantry[key]; 130 if (custom 131 && Number.isFinite(custom.x) 132 && Number.isFinite(custom.y) 133 && Number.isFinite(custom.z)) { 134 local.copy(custom); 135 } 136 gantry.site.localToWorld(local); 137 return local; 138} 139 140function getHumanGantryDockPosition(gantry) { 141 return getHumanGantryWorldLocal(gantry, 'droneDockLocal', 0.08, 1.16, 0.04); 142} 143 144function getHumanGantryRoverStart(gantry) { 145 const start = getHumanGantryWorldLocal(gantry, 'roverStartLocal', -1.66, 0, 1.32); 146 start.y = terrainHeight(start.x, start.z); 147 return start; 148} 149 150function getHumanGantryRoverPatrolPoints(gantry) { 151 return getHumanGantryRoverScoutPoints(gantry, getHumanGantryRoverStart(gantry), gantry.roverSerial + 1); 152} 153 154function clampHumanScoutPoint(point) { 155 const half = typeof FOW_HALF === 'number' ? FOW_HALF : 40; 156 const margin = 3.5; 157 return { 158 x: Math.max(-half + margin, Math.min(half - margin, point.x)), 159 z: Math.max(-half + margin, Math.min(half - margin, point.z)) 160 }; 161} 162 163function getHumanGantryPassiveRoverFocus(start) { 164 const playerFocus = typeof START_POS !== 'undefined' ? START_POS : { x: 0, z: 0 }; 165 const dx = start.x - playerFocus.x; 166 const dz = start.z - playerFocus.z; 167 const d = Math.max(0.001, Math.sqrt(dx * dx + dz * dz)); 168 const push = Math.min(24.0, Math.max(12.0, d * 0.42)); 169 return clampHumanScoutPoint({ 170 x: start.x + dx / d * push, 171 z: start.z + dz / d * push 172 }); 173} 174 175function getHumanGantryRoverScoutPoints(gantry, start, serial) { 176 const points = []; 177 const passiveRoute = isHumanGantryRoverPassiveMode(); 178 const focus = passiveRoute 179 ? getHumanGantryPassiveRoverFocus(start) 180 : (typeof START_POS !== 'undefined' ? START_POS : { x: 0, z: 0 }); 181 const alert = typeof getHumanAlertLevel === 'function' ? getHumanAlertLevel() : HUMAN_ALERT_START_LEVEL;
182 const dx = focus.x - start.x; 183 const dz = focus.z - start.z; 184 const baseAngle = Math.atan2(dz, dx); 185 const sweepRange = passiveRoute ? 0.72 : (alert >= 4 ? 0.58 : 0.92); 186 const sweep = (hash(gantry.seed, 3200 + serial * 17) - 0.5) * sweepRange; 187 const angle = baseAngle + sweep; 188 const alongX = Math.cos(angle); 189 const alongZ = Math.sin(angle); 190 const sideX = -alongZ; 191 const sideZ = alongX; 192 const directDistance = Math.sqrt(dx * dx + dz * dz); 193 const count = passiveRoute ? 5 : (alert >= 5 ? 7 : 6); 194 const maxTravel = passiveRoute 195 ? Math.min(directDistance + 4.0, 24.0) 196 : Math.min(directDistance + 8.0 + alert * 1.5, 36.0); 197 198 for (let i = 0; i < count; i++) { 199 const p = (i + 1) / count; 200 const travel = Math.max(5.2, maxTravel * p); 201 const alternating = i % 2 === 0 ? 1 : -1; 202 const lateralBase = passiveRoute ? 1.5 + p * 4.2 : 2.2 + p * 6.6; 203 const lateral = alternating * lateralBase * (0.55 + hash(gantry.seed, 3310 + serial * 29 + i) * 0.90); 204 const jitter = (hash(gantry.seed, 3440 + serial * 31 + i) - 0.5) * 2.1; 205 points.push(clampHumanScoutPoint({ 206 x: start.x + alongX * (travel + jitter) + sideX * lateral, 207 z: start.z + alongZ * (travel + jitter) + sideZ * lateral 208 })); 209 } 210 211 const focusOffsetAngle = baseAngle + (hash(gantry.seed, 3570 + serial) - 0.5) * 1.65; 212 points.push(clampHumanScoutPoint({ 213 x: focus.x + Math.cos(focusOffsetAngle) * (5.2 + hash(gantry.seed, 3610 + serial) * 5.6), 214 z: focus.z + Math.sin(focusOffsetAngle) * (5.2 + hash(gantry.seed, 3650 + serial) * 5.6) 215 })); 216 return points; 217} 218 219function clearHumanGantryDockedDrone(gantry) { 220 if (!gantry || !gantry.dockedDrone) return; 221 disposeHumanDroneActor(gantry.dockedDrone, false); 222 gantry.dockedDrone = null; 223} 224 225function shutdownHumanGantryDrone(gantry) { 226 if (!gantry) return; 227 clearHumanGantryDockedDrone(gantry); 228 if (gantry.deployedDrone) { 229 despawnHumanBurnDrone(gantry.deployedDrone); 230 gantry.deployedDrone = null; 231 } 232} 233 234function shutdownHumanGantryRovers(gantry) { 235 if (!gantry) return; 236 for (const actor of humanRoverActors.slice()) { 237 if (actor && actor.homeSite === gantry.site) despawnHumanScoutRover(actor); 238 } 239 if (Array.isArray(gantry.roverActors)) gantry.roverActors.length = 0; 240} 241 242function setHumanGantryLaunchPrep(gantry, delay) { 243 if (!gantry) return; 244 shutdownHumanGantryDrone(gantry); 245 gantry.state = 'launch-prep'; 246 gantry.timer = Math.max(0.1, Number.isFinite(delay) ? delay : HUMAN_GANTRY_LAUNCH_DELAY); 247 gantry.previewBob = 0; 248 if (!humanDroneDefinition || typeof humanDroneDefinition.create !== 'function') return; 249 250 const dock = getHumanGantryDockPosition(gantry); 251 const hoverHeight = Math.max(0.45, dock.y - terrainHeight(dock.x, dock.z)); 252 const actor = humanDroneDefinition.create(getHumanDroneBuildApi(), dock.x, dock.z, gantry.seed + 6000, { 253 previewMode: true, 254 hoverHeight, 255 pathLength: 0, 256 pathAngle: gantry.patrolAngle, 257 phase: 0 258 }); 259 if (!actor || !actor.root) return; 260 actor.root.position.copy(dock); 261 actor.root.rotation.set(0, gantry.droneHeading, 0); 262 actor.root.userData.tooltipRequiresSight = true; 263 gantry.dockedDrone = actor; 264} 265 266function startHumanGantryRebuild(gantry, duration) { 267 if (!gantry || gantry.site.userData.assimilated) return; 268 shutdownHumanGantryDrone(gantry); 269 gantry.state = 'rebuilding'; 270 gantry.timer = Math.max(1, Number.isFinite(duration) ? duration : HUMAN_GANTRY_REBUILD_TIME); 271} 272 273function setHumanGantryLaunchHold(gantry) { 274 if (!gantry || gantry.site.userData.assimilated) return; 275 shutdownHumanGantryDrone(gantry); 276 gantry.state = 'holding'; 277 gantry.timer = 0; 278} 279 280function deployHumanGantryDrone(gantry) { 281 if (!gantry || gantry.site.userData.assimilated || isHumanGantryDormant(gantry) || !isHumanGantryBurnDroneAllowed()) return null; 282 clearHumanGantryDockedDrone(gantry); 283 const patrolOrigin = getHumanGantryWorldLocal(gantry, 'patrolOriginLocal', 0.40, 0, 0.02); 284 const actor = spawnHumanBurnDrone(patrolOrigin.x, patrolOrigin.z, gantry.seed + 4000, { 285 pathAngle: gantry.patrolAngle, 286 pathLength: Number.isFinite(gantry.pathLength) ? gantry.pathLength : 4.6, 287 hoverHeight: Number.isFinite(gantry.hoverHeight) ? gantry.hoverHeight : 4.2, 288 phase: hash(gantry.seed, 991) * Math.PI * 2 289 }); 290 if (!actor) return null; 291 actor.root.userData.gantrySite = gantry.site; 292 gantry.deployedDrone = actor; 293 gantry.state = 'deployed'; 294 gantry.timer = 0; 295 if (typeof window !== 'undefined' && typeof window.FAR_COLONY_PLAY_SOUND === 'function') { 296 window.FAR_COLONY_PLAY_SOUND('human-drone'); 297 } 298 return actor; 299} 300 301function deployHumanGantryRover(gantry) { 302 if (!gantry || gantry.site.userData.assimilated || isHumanGantryDormant(gantry) || !isHumanGantryRoverAllowed()) return null; 303 const start = getHumanGantryRoverStart(gantry); 304 const serial = ++gantry.roverSerial; 305 const route = getHumanGantryRoverScoutPoints(gantry, start, serial); 306 const routeLength = route.reduce((sum, point, index) => { 307 const previous = index === 0 ? start : route[index - 1]; 308 const dx = point.x - previous.x; 309 const dz = point.z - previous.z; 310 return sum + Math.sqrt(dx * dx + dz * dz); 311 }, 0); 312 const actor = spawnHumanScoutRover(start.x, start.z, gantry.seed + 8200 + serial * 37, { 313 startX: start.x, 314 startZ: start.z, 315 homeSite: gantry.site, 316 patrolPoints: route, 317 missionDuration: Math.max(HUMAN_ROVER_SCOUT_MIN_MISSION_TIME, routeLength * 1.45), 318 phase: hash(gantry.seed, 1840 + serial) * Math.PI * 2 319 }); 320 if (!actor) return null; 321 actor.root.userData.gantrySite = gantry.site; 322 gantry.roverActors.push(actor); 323 if (typeof window !== 'undefined' && typeof window.FAR_COLONY_PLAY_SOUND === 'function') { 324 window.FAR_COLONY_PLAY_SOUND('human-rover'); 325 } 326 return actor; 327} 328 329function updateHumanGantryRoverSpawner(gantry, stepDt, assimilated) { 330 if (!gantry || assimilated || !humanRoverDefinition || typeof humanRoverDefinition.create !== 'function') return; 331 if (isHumanGantryDormant(gantry)) { 332 gantry.roverTimer = HUMAN_ROVER_DEPLOY_DELAY; 333 return; 334 } 335 if (!isHumanGantryRoverAllowed()) { 336 gantry.roverTimer = HUMAN_ROVER_DEPLOY_DELAY; 337 return; 338 } 339 gantry.roverActors = gantry.roverActors.filter(actor => actor && actor.root && actor.root.parent && !actor.expired); 340 if (gantry.roverActors.length >= getHumanGantryRoverMaxActive()) return; 341 gantry.roverTimer = Math.max(0, (gantry.roverTimer || 0) - stepDt); 342 if (gantry.roverTimer > 0) return; 343 const actor = deployHumanGantryRover(gantry); 344 gantry.roverTimer = getHumanGantryRoverRedeployTime(gantry); 345 if (!actor) gantry.roverTimer = 6.0; 346} 347 348function attachHumanGantrySite(site, seed) { 349 const gantry = site && site.userData && site.userData.gantryActor; 350 if (!gantry) return; 351 if (getHumanGantryDormantDelay(gantry) > 0 && !gantry.activated) { 352 setHumanGantryDormantHold(gantry); 353 return; 354 } 355 if (isHumanGantryCombatUnlocked()) { 356 const passiveRemaining = getHumanGantryPostTutorialPassiveRemaining(); 357 setHumanGantryLaunchPrep(gantry, passiveRemaining > 0 ? passiveRemaining : HUMAN_GANTRY_LAUNCH_DELAY); 358 } 359 else setHumanGantryLaunchHold(gantry); 360} 361
362function assimilateHumanGantrySite(site) { 363 const gantry = site && site.userData && site.userData.gantryActor; 364 if (!gantry) return; 365 shutdownHumanGantryDrone(gantry); 366 shutdownHumanGantryRovers(gantry); 367} 368 369function updateHumanGantrySite(site, t, dt) { 370 const stepDt = Math.min(0.1, Math.max(0.0001, dt || 0.016)); 371 const gantry = site && site.userData && site.userData.gantryActor; 372 if (!gantry) return; 373 374 const assimilated = !!site.userData.assimilated; 375 const combatUnlocked = isHumanGantryCombatUnlocked(); 376 const passiveRemaining = getHumanGantryPostTutorialPassiveRemaining(); 377 const burnDroneAllowed = combatUnlocked && passiveRemaining <= 0; 378 const activatedNow = !assimilated && updateHumanGantryDormancy(gantry, t, burnDroneAllowed); 379 const dormant = !assimilated && isHumanGantryDormant(gantry); 380 if (activatedNow && site.userData.fringeOutpost && typeof showSignal === 'function') { 381 showSignal('HUMAN FRINGE GANTRY ONLINE', true, 2200); 382 if (typeof window !== 'undefined' && typeof window.FAR_COLONY_PLAY_SOUND === 'function') { 383 window.FAR_COLONY_PLAY_SOUND('human-online'); 384 } 385 } 386 if (!assimilated && !combatUnlocked) { 387 if (gantry.state !== 'holding' && gantry.state !== 'dormant') { 388 if (getHumanGantryDormantDelay(gantry) > 0) setHumanGantryDormantHold(gantry); 389 else setHumanGantryLaunchHold(gantry); 390 } 391 } else if (!assimilated && dormant) { 392 if (gantry.state !== 'dormant') setHumanGantryDormantHold(gantry); 393 gantry.timer = getHumanGantryDormantRemaining(gantry); 394 } else if (!assimilated && passiveRemaining > 0) { 395 if (gantry.state !== 'launch-prep' || !gantry.dockedDrone) { 396 setHumanGantryLaunchPrep(gantry, passiveRemaining); 397 } else { 398 gantry.timer = Math.max(0.1, passiveRemaining); 399 } 400 } else if (!assimilated && (gantry.state === 'holding' || gantry.state === 'dormant')) { 401 setHumanGantryLaunchPrep(gantry, HUMAN_GANTRY_LAUNCH_DELAY); 402 } else if (!assimilated && gantry.state === 'deployed') { 403 const active = gantry.deployedDrone; 404 if (!active || !active.root || !active.root.parent) { 405 gantry.deployedDrone = null; 406 startHumanGantryRebuild(gantry, HUMAN_GANTRY_REBUILD_TIME); 407 } 408 } else if (!assimilated && (gantry.state === 'launch-prep' || gantry.state === 'rebuilding')) { 409 gantry.timer = Math.max(0, gantry.timer - stepDt); 410 if (gantry.timer <= 0) { 411 if (gantry.state === 'rebuilding') setHumanGantryLaunchPrep(gantry, 3.5); 412 else deployHumanGantryDrone(gantry); 413 } 414 } 415 416 updateHumanGantryRoverSpawner(gantry, stepDt, assimilated); 417 418 const dormantVisual = !assimilated && gantry.state === 'dormant'; 419 const activePulse = assimilated 420 ? 0.18 421 : dormantVisual 422 ? 0.16 + Math.sin(t * 1.6 + gantry.seed) * 0.035 423 : 0.56 + Math.sin(t * 4.6 + gantry.seed) * 0.12; 424 gantry.emitterGlow.scale.setScalar(gantry.state === 'rebuilding' ? 1.15 + Math.sin(t * 7.5 + gantry.seed) * 0.18 : dormantVisual ? 0.56 : 0.8); 425 gantry.emitterGlow.visible = !assimilated; 426 gantry.emitterGlow.material.opacity = assimilated 427 ? 0.08 428 : dormantVisual 429 ? 0.08 + Math.max(0, activePulse) * 0.20 430 : gantry.state === 'rebuilding' 431 ? activePulse + 0.18 432 : activePulse * 0.65; 433 434 gantry.guideGlowBars.forEach((bar, index) => { 435 bar.material.opacity = assimilated 436 ? 0.08 437 : dormantVisual 438 ? 0.10 + Math.sin(t * 1.2 + index) * 0.025 439 : gantry.state === 'rebuilding' 440 ? 0.62 + Math.sin(t * 6.2 + index * 0.9) * 0.16 441 : gantry.state === 'launch-prep' 442 ? 0.48 + Math.sin(t * 4.2 + index) * 0.10 443 : 0.34 + Math.sin(t * 2.8 + index) * 0.06; 444 }); 445 446 gantry.berthMarkerLights.forEach((marker, index) => { 447 marker.material.opacity = assimilated 448 ? 0.07 449 : dormantVisual 450 ? 0.09 + Math.sin(t * 1.3 + index * 0.6) * 0.025 451 : gantry.state === 'rebuilding' 452 ? 0.70 + Math.sin(t * 7.0 + index * 0.6) * 0.18 453 : gantry.state === 'launch-prep' 454 ? 0.56 + Math.sin(t * 5.2 + index * 0.7) * 0.12 455 : 0.30 + Math.sin(t * 2.4 + index) * 0.06; 456 marker.scale.y = assimilated ? 0.65 : dormantVisual ? 0.72 : 1.0 + Math.sin(t * 4.8 + index) * 0.08; 457 }); 458
459 gantry.dockGlowPlates.forEach((plate, index) => { 460 plate.material.opacity = assimilated 461 ? 0.06 462 : dormantVisual 463 ? 0.05 464 : gantry.state === 'launch-prep' 465 ? 0.42 + Math.sin(t * 3.8 + index) * 0.08 466 : gantry.state === 'rebuilding' 467 ? 0.60 + Math.sin(t * 6.4 + index) * 0.12 468 : 0.22; 469 }); 470 471 gantry.lensPlanes.forEach((mesh, index) => { 472 mesh.position.y = (mesh.userData.baseY || mesh.position.y) 473 + Math.sin(t * (2.0 + index) + gantry.seed) * 0.02; 474 }); 475 476 gantry.armPivot.rotation.z = 0; 477 gantry.armFore.rotation.z = 0; 478 gantry.armHead.rotation.x = assimilated ? 0 : gantry.state === 'rebuilding' ? Math.sin(t * 9.0 + gantry.seed) * 0.16 : 0.06; 479 480 if (gantry.dockedDrone && gantry.dockedDrone.root) { 481 const dock = getHumanGantryDockPosition(gantry); 482 const bob = gantry.state === 'launch-prep' ? 0.04 + Math.sin(t * 3.8 + gantry.seed) * 0.02 : 0.02; 483 gantry.dockedDrone.root.position.set(dock.x, dock.y + bob, dock.z); 484 gantry.dockedDrone.root.rotation.set(0, gantry.droneHeading, 0); 485 } 486} 487 488function updateHumanRimMegastructureVisuals(site, t) { 489 const actor = site && site.userData && site.userData.rimMegastructureActor; 490 if (!actor) return; 491 const assimilated = !!site.userData.assimilated; 492 const active = !assimilated; 493 const pulse = 0.5 + Math.sin(t * 3.1 + actor.seed * 0.013) * 0.5; 494 495 if (actor.drillHead) { 496 actor.drillHead.rotation.y = active ? t * 1.35 : actor.drillHead.rotation.y; 497 actor.drillHead.position.y = active ? 0.82 + Math.sin(t * 1.8 + actor.seed) * 0.035 : 0.80; 498 } 499 if (actor.drillCable) { 500 actor.drillCable.scale.y = active ? 1.0 + Math.sin(t * 1.8 + actor.seed) * 0.035 : 0.94; 501 } 502 if (actor.topDish) { 503 actor.topDish.rotation.z = Math.sin(t * 0.86 + actor.seed) * (active ? 0.055 : 0.015); 504 } 505 if (Array.isArray(actor.statusLights)) { 506 actor.statusLights.forEach((light, index) => { 507 if (!light || !light.material) return; 508 const localPulse = 0.5 + Math.sin(t * (3.6 + index * 0.18) + (light.userData.phase || 0)) * 0.5; 509 light.material.opacity = active ? 0.34 + localPulse * 0.34 : 0.08; 510 light.scale.y = active ? 0.82 + localPulse * 0.34 : 0.55; 511 }); 512 } 513 if (Array.isArray(actor.drillFramePosts)) { 514 actor.drillFramePosts.forEach((post, index) => { 515 post.position.y = 1.88 + (active ? Math.sin(t * 1.1 + index * 0.8) * 0.008 : 0); 516 }); 517 } 518 if (actor.lightMat) actor.lightMat.opacity = active ? 0.24 + pulse * 0.24 : 0.06; 519} 520 521function updateHumanRimMegastructureSite(site, t, dt) { 522 updateHumanGantrySite(site, t, dt); 523 updateHumanRimMegastructureVisuals(site, t); 524} 525 526function assimilateHumanRimMegastructureSite(site) { 527 assimilateHumanGantrySite(site); 528 updateHumanRimMegastructureVisuals(site, 0); 529} 530 531function updateHumanFieldRelaySite(site, t, dt) { 532 const actor = site && site.userData && site.userData.fieldRelayActor; 533 if (!actor) return; 534 const assimilated = !!site.userData.assimilated; 535 if (assimilated) { 536 const assimilatedAt = Number.isFinite(site.userData.assimilatedAt) ? site.userData.assimilatedAt : t; 537 const linger = typeof HUMAN_FIELD_RELAY_ASSIMILATED_LINGER_TIME === 'number' 538 ? HUMAN_FIELD_RELAY_ASSIMILATED_LINGER_TIME 539 : 15.0; 540 const age = Math.max(0, t - assimilatedAt); 541 if (age >= linger) { 542 if (typeof removeHumanSite === 'function') removeHumanSite(site); 543 return; 544 } 545 if (!Number.isFinite(site.userData.relayAssimilatedBaseY)) site.userData.relayAssimilatedBaseY = site.position.y; 546 const vanishT = Math.max(0, Math.min(1, (age - linger * 0.55) / Math.max(0.001, linger * 0.45))); 547 const settle = 1.0 - vanishT * 0.18; 548 site.scale.setScalar(settle); 549 site.position.y = site.userData.relayAssimilatedBaseY - vanishT * 0.10; 550 } 551 const visible = typeof isWorldVisible !== 'function' || isWorldVisible(site.position.x, site.position.z); 552 const active = !assimilated; 553 const pulse = 0.5 + Math.sin(t * 2.8 + actor.seed * 0.017) * 0.5; 554 555 if (actor.radiusRing && actor.radiusRing.material) { 556 actor.radiusRing.visible = active && visible; 557 actor.radiusRing.material.opacity = actor.radiusRing.visible ? 0.055 + pulse * 0.055 : 0; 558 actor.radiusRing.rotation.z = t * 0.18; 559 actor.radiusRing.scale.setScalar(1.0 + pulse * 0.012); 560 } 561 if (actor.lightMat) actor.lightMat.opacity = active ? 0.44 + pulse * 0.34 : 0.10; 562 if (actor.pulseLight) actor.pulseLight.scale.setScalar(active ? 0.86 + pulse * 0.38 : 0.55); 563 if (actor.head) actor.head.rotation.y = -0.22 + Math.sin(t * 0.72 + actor.seed) * 0.07; 564 if (actor.dish) actor.dish.rotation.z = Math.sin(t * 1.1 + actor.seed) * 0.025; 565 if (actor.status) actor.status.position.y = 0.62 + Math.sin(t * 4.1 + actor.seed) * (active ? 0.018 : 0.006); 566} 567
568function assimilateHumanFieldRelaySite(site) { 569 const actor = site && site.userData && site.userData.fieldRelayActor; 570 if (!actor) return; 571 if (!Number.isFinite(site.userData.assimilatedAt)) { 572 site.userData.assimilatedAt = typeof getGameplayTime === 'function' ? getGameplayTime() : 0; 573 } 574 site.userData.relayAssimilatedBaseY = site.position.y; 575 if (actor.radiusRing) actor.radiusRing.visible = false; 576 if (actor.lightMat) actor.lightMat.opacity = 0.10; 577} 578 579registerHumanSiteBehavior('drone-gantry', { 580 attach: attachHumanGantrySite, 581 assimilate: assimilateHumanGantrySite, 582 update: updateHumanGantrySite 583}); 584 585registerHumanSiteBehavior('rim-megastructure', { 586 attach: attachHumanGantrySite, 587 assimilate: assimilateHumanRimMegastructureSite, 588 update: updateHumanRimMegastructureSite 589}); 590 591registerHumanSiteBehavior('field-relay', { 592 assimilate: assimilateHumanFieldRelaySite, 593 update: updateHumanFieldRelaySite 594});
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.