1/** 2 * âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 3 * SCENE SETTINGS â central config loaded from Firestore 4 * Admin panel writes to site_config/scene_settings 5 * Game reads on startup and merges with these hard-coded defaults. 6 * âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 7 */ 8 9import { db } from './firebase-config.js'; 10import { doc, getDoc } from 'https://www.gstatic.com/firebasejs/10.12.0/firebase-firestore.js'; 11import { JOURNEY_DEFAULTS } from './JourneyGuide.js'; 12 13// ââ Hard-coded defaults (never mutated) ââââââââââââââââââââââ 14export const DEFAULTS = Object.freeze({ 15 ...JOURNEY_DEFAULTS, 16 17 // ââ Audio volumes (0 â 1) âââââââââââââââââââââââââââ 18 snd_catMeow: 0.05, 19 snd_jumpMeow: 0.15, 20 snd_jumpMeowEnabled: true, 21 snd_jumpMeowPerTen: 2, 22 snd_crystalPop: 0.1, 23 snd_birdChirp: 0.01, 24 snd_wind: 0.10, 25 snd_portalEnter: 0.05, 26 snd_portalAmbient: 0.1, 27 snd_catInBush: 0.5, 28 snd_crystalAttach: 0.55, 29 snd_portalOpening: 0.6, 30 snd_confetti: 0.55, 31 32 // ââ Audio distances âââââââââââââââââââââââââââââââââ 33 snd_birdHearRange: 20, 34 snd_birdLoudRange: 3, 35 snd_portalHearRange: 25, 36 snd_portalLoudRange: 4, 37 snd_bushRadius: 3, 38 39 // ââ Camera ââââââââââââââââââââââââââââââââââââââââââ 40 cam_distance: 12, 41 cam_height: 5, 42 cam_sensitivity: 0.002, 43 cam_fov: 55, 44 45 // ââ Character âââââââââââââââââââââââââââââââââââââââ 46 char_speed: 12, 47 char_sprintMultiplier: 2, 48 char_jumpForce: 14, 49 char_gravity: 35, 50 51 // ââ Emoji reactions âââââââââââââââââââââââââââââââââ 52 emoji_available: ['ð','ð¥','ð¯','ð','â¨','ð','â¤ï¸','ð¤©','ð¡','ð¨','ð','â'], 53 emoji_maxPerPortal: 10, 54 emoji_minDistance: 5, 55 emoji_maxDistance: 8, 56 emoji_floatHeight: 0.9, 57 emoji_bobSpeed: 1.2, 58 emoji_bobAmount: 0.15, 59 emoji_portalProximity: 4, 60 61 // ââ Particles âââââââââââââââââââââââââââââââââââââââ 62 ptcl_fireflyCount: 50, 63 ptcl_fireflySize: 0.25, 64 ptcl_fireflySpeed: 0.5, 65 ptcl_leafCount: 100, 66 ptcl_leafSize: 0.15, 67 ptcl_leafSpeed: 1.0, 68 ptcl_pollenCount: 400, 69 ptcl_pollenSize: 0.06, 70 ptcl_pollenSpeed: 1.0, 71 72 // ââ Visual / Atmosphere âââââââââââââââââââââââââââââ 73 vis_fogNear: 60, 74 vis_fogFar: 700, 75 vis_exposure: 1.4, 76 vis_ambientIntensity: 0.65, 77 vis_sunIntensity: 1.2, 78 79 // ââ Birds âââââââââââââââââââââââââââââââââââââââââââ 80 bird_count: 10, 81 bird_fleeDistance: 8, 82 bird_fleeSpeed: 22, 83 bird_fleeHeight: 25, 84 bird_autoflyMinTime: 6, 85 bird_autoflyMaxTime: 10, 86 bird_autoflyMinDist: 30, 87 bird_autoflyMaxDist: 60, 88 bird_autoflyHeight: 18, 89 bird_landSpeed: 6, 90 bird_peckMinTime: 1.5, 91 bird_peckMaxTime: 3.0, 92 bird_peckSpeed: 8, 93 bird_lookMinTime: 1.0, 94 bird_lookMaxTime: 2.5, 95 bird_lookSpeed: 1.5, 96 bird_hopChance: 0.3, 97 bird_hopDistance: 0.3, 98 bird_hopHeight: 0.12, 99 100 // ââ Fish ââââââââââââââââââââââââââââââââââââââââââââ 101 fish_count: 6, 102 fish_swimSpeedMin: 2, 103 fish_swimSpeedMax: 4, 104 fish_swimDepthMin: -0.4, 105 fish_swimDepthMax: -1.2, 106 fish_turnSpeed: 2.0, 107 fish_swimAnimSpeed: 1.4, 108 fish_wanderRadius: 50, 109 fish_jumpChance: 0.02, 110 fish_jumpHeight: 2.5, 111 fish_catScareRadius: 4,
112 fish_catScareJumpChance: 0.4, 113 fish_catScareFleeDistance: 8, 114 fish_catScareSpeedMult: 3.5, 115 116 // ââ Bubbles âââââââââââââââââââââââââââââââââââââââââ 117 bubble_maxCount: 200, 118 bubble_size: 0.12, 119 bubble_swimRate: 8, 120 bubble_swimSpread: 0.8, 121 bubble_enterBurst: 30, 122 bubble_exitBurst: 15, 123 bubble_riseSpeed: 0.6, 124 bubble_driftSpeed: 0.15, 125 126 // ââ Crystals ââââââââââââââââââââââââââââââââââââââââ 127 // NOTE: crystal_total is legacy. Runtime uses portals.length * 3 when story mode is active. 128 crystal_total: 19, 129 crystal_size: 0.4, 130 crystal_collectDistance: 2.5, 131 crystal_floatSpeed: 2, 132 crystal_floatAmount: 0.2, 133 crystal_glowRadius: 0.6, 134 crystal_lightIntensity: 0.5, 135 crystal_lightRange: 5, 136 137 // ââ Crystal Messages ââââââââââââââââââââââââââââââââ 138 crystal_messages: [], 139 crystal_msgDuration: 8, 140 crystal_meowEnabled: true, 141 142 // ââ Cat Thoughts ââââââââââââââââââââââââââââââââââââ 143 thought_enabled: true, 144 thought_globalCooldown: 15, 145 thought_meowVolume: 0.15, 146 thought_msgDuration: 6, 147 thought_aiEnabled: false, 148 thought_chatEnabled: true, 149 thought_chatLimit: 10, 150 151 // Birds 152 thought_birdNearbyEnabled: true, 153 thought_birdNearbyCooldown: 25, 154 thought_birdNearbyChance: 0.03, 155 thought_birdNearbyMessages: [], 156 thought_birdNearbyMessagesWhiteCatHere: [], 157 thought_birdNearbyMessagesNearWhiteCat: [], 158 159 thought_birdVeryCloseEnabled: true, 160 thought_birdVeryCloseCooldown: 20, 161 thought_birdVeryCloseChance: 0.1, 162 thought_birdVeryCloseMessages: [], 163 thought_birdVeryCloseMessagesWhiteCatHere: [], 164 thought_birdVeryCloseMessagesNearWhiteCat: [], 165 166 thought_birdsFleeingEnabled: true, 167 thought_birdsFleeingCooldown: 20, 168 thought_birdsFleeingChance: 0.5, 169 thought_birdsFleeingMessages: [], 170 thought_birdsFleeingMessagesWhiteCatHere: [], 171 thought_birdsFleeingMessagesNearWhiteCat: [], 172 173 thought_birdsReturningEnabled: true, 174 thought_birdsReturningCooldown: 30, 175 thought_birdsReturningChance: 0.05, 176 thought_birdsReturningMessages: [], 177 thought_birdsReturningMessagesWhiteCatHere: [], 178 thought_birdsReturningMessagesNearWhiteCat: [], 179 180 // Fish 181 thought_fishVisibleEnabled: true, 182 thought_fishVisibleCooldown: 30, 183 thought_fishVisibleChance: 0.02, 184 thought_fishVisibleMessages: [], 185 thought_fishVisibleMessagesWhiteCatHere: [], 186 thought_fishVisibleMessagesNearWhiteCat: [], 187 188 thought_fishFleeingEnabled: true, 189 thought_fishFleeingCooldown: 20, 190 thought_fishFleeingChance: 0.3, 191 thought_fishFleeingMessages: [], 192 thought_fishFleeingMessagesWhiteCatHere: [], 193 thought_fishFleeingMessagesNearWhiteCat: [], 194 195 thought_fishJumpingEnabled: true, 196 thought_fishJumpingCooldown: 25, 197 thought_fishJumpingChance: 0.2, 198 thought_fishJumpingMessages: [], 199 thought_fishJumpingMessagesWhiteCatHere: [], 200 thought_fishJumpingMessagesNearWhiteCat: [], 201 202 // Water 203 thought_swimmingEnabled: true, 204 thought_swimmingCooldown: 25, 205 thought_swimmingChance: 0.02, 206 thought_swimmingMessages: [], 207 thought_swimmingMessagesWhiteCatHere: [], 208 thought_swimmingMessagesNearWhiteCat: [], 209 210 thought_pondEdgeEnabled: true, 211 thought_pondEdgeCooldown: 25, 212 thought_pondEdgeChance: 0.02, 213 thought_pondEdgeMessages: [], 214 thought_pondEdgeMessagesWhiteCatHere: [], 215 thought_pondEdgeMessagesNearWhiteCat: [], 216 217 thought_enterWaterEnabled: true, 218 thought_enterWaterCooldown: 15, 219 thought_enterWaterChance: 0.6, 220 thought_enterWaterMessages: [], 221 thought_enterWaterMessagesWhiteCatHere: [], 222 thought_enterWaterMessagesNearWhiteCat: [], 223 224 thought_exitWaterEnabled: true, 225 thought_exitWaterCooldown: 15, 226 thought_exitWaterChance: 0.5, 227 thought_exitWaterMessages: [], 228 thought_exitWaterMessagesWhiteCatHere: [], 229 thought_exitWaterMessagesNearWhiteCat: [], 230 231 // Trees 232 thought_nearTreeEnabled: true, 233 thought_nearTreeCooldown: 30, 234 thought_nearTreeChance: 0.02, 235 thought_nearTreeMessages: [], 236 thought_nearTreeMessagesWhiteCatHere: [], 237 thought_nearTreeMessagesNearWhiteCat: [], 238 239 // Bushes 240 thought_inBushEnabled: true, 241 thought_inBushCooldown: 30, 242 thought_inBushChance: 0.02, 243 thought_inBushMessages: [], 244 thought_inBushMessagesWhiteCatHere: [], 245 thought_inBushMessagesNearWhiteCat: [], 246 247 // Rocks 248 thought_nearRockEnabled: true, 249 thought_nearRockCooldown: 30, 250 thought_nearRockChance: 0.02, 251 thought_nearRockMessages: [], 252 thought_nearRockMessagesWhiteCatHere: [], 253 thought_nearRockMessagesNearWhiteCat: [], 254 255 thought_onRockEnabled: true, 256 thought_onRockCooldown: 25, 257 thought_onRockChance: 0.04, 258 thought_onRockMessages: [], 259 thought_onRockMessagesWhiteCatHere: [], 260 thought_onRockMessagesNearWhiteCat: [], 261 262 // Huts 263 thought_nearHutEnabled: true, 264 thought_nearHutCooldown: 30, 265 thought_nearHutChance: 0.02, 266 thought_nearHutMessages: [], 267 thought_nearHutMessagesWhiteCatHere: [], 268 thought_nearHutMessagesNearWhiteCat: [], 269 270 thought_onRoofEnabled: true, 271 thought_onRoofCooldown: 25, 272 thought_onRoofChance: 0.04, 273 thought_onRoofMessages: [], 274 thought_onRoofMessagesWhiteCatHere: [], 275 thought_onRoofMessagesNearWhiteCat: [], 276 277 // Portals 278 thought_nearPortalEnabled: true, 279 thought_nearPortalCooldown: 25, 280 thought_nearPortalChance: 0.03, 281 thought_nearPortalMessages: [], 282 thought_nearPortalMessagesWhiteCatHere: [], 283 thought_nearPortalMessagesNearWhiteCat: [], 284 285 // Crystals 286 thought_nearCrystalEnabled: true, 287 thought_nearCrystalCooldown: 25, 288 thought_nearCrystalChance: 0.03, 289 thought_nearCrystalMessages: [], 290 thought_nearCrystalMessagesWhiteCatHere: [], 291 thought_nearCrystalMessagesNearWhiteCat: [], 292 293 // Fish Bones
294 thought_nearFishBoneEnabled: true, 295 thought_nearFishBoneCooldown: 30, 296 thought_nearFishBoneChance: 0.03, 297 thought_nearFishBoneMessages: [], 298 thought_nearFishBoneMessagesWhiteCatHere: [], 299 thought_nearFishBoneMessagesNearWhiteCat: [], 300 301 // Sky / Camera 302 thought_lookingUpEnabled: true, 303 thought_lookingUpCooldown: 30, 304 thought_lookingUpChance: 0.03, 305 thought_lookingUpMessages: [], 306 thought_lookingUpMessagesWhiteCatHere: [], 307 thought_lookingUpMessagesNearWhiteCat: [], 308 309 thought_facingCameraEnabled: true, 310 thought_facingCameraCooldown: 35, 311 thought_facingCameraChance: 0.02, 312 thought_facingCameraMessages: [], 313 thought_facingCameraMessagesWhiteCatHere: [], 314 thought_facingCameraMessagesNearWhiteCat: [], 315 316 // Movement 317 thought_jumpingEnabled: true, 318 thought_jumpingCooldown: 40, 319 thought_jumpingChance: 0.3, 320 thought_jumpingMessages: [], 321 thought_jumpingMessagesWhiteCatHere: [], 322 thought_jumpingMessagesNearWhiteCat: [], 323 324 thought_roamingEnabled: true, 325 thought_roamingCooldown: 45, 326 thought_roamingChance: 0.005, 327 thought_roamingMessages: [], 328 thought_roamingMessagesWhiteCatHere: [], 329 thought_roamingMessagesNearWhiteCat: [], 330 331 thought_idleEnabled: true, 332 thought_idleCooldown: 20, 333 thought_idleChance: 0.01, 334 thought_idleMessages: [], 335 thought_idleMessagesWhiteCatHere: [], 336 thought_idleMessagesNearWhiteCat: [], 337 338 thought_restingEnabled: true, 339 thought_restingCooldown: 20, 340 thought_restingChance: 0.02, 341 thought_restingMessages: [], 342 thought_restingMessagesWhiteCatHere: [], 343 thought_restingMessagesNearWhiteCat: [], 344 345 // ââ Character animation âââââââââââââââââââââââââââââ 346 char_furToggleEnabled: false, 347 char_swimSpeed: 5, 348 char_swimDepth: -1.5, 349 char_swimAnimSpeed: 1.0, 350 char_walkAnimSpeed: 3, 351 char_idleAnimSpeed: 1.0, 352 char_jumpAnimSpeed: 1.0, 353 char_restdownAnimSpeed: 3.0, 354 char_restAnimSpeed: 1.0, 355 356 // ââ Sky âââââââââââââââââââââââââââââââââââââââââââââ 357 sky_topColor: 0x9cc8e6, 358 sky_horizonColor: 0xe6f2fb, 359 sky_sunStrength: 0.35, 360 361 // ââ Clouds ââââââââââââââââââââââââââââââââââââââââââ 362 // Population & placement 363 cloud_count: 120, 364 cloud_minHeight: 100, 365 cloud_maxHeight: 276, 366 cloud_minDist: 80, 367 cloud_maxDist: 600, 368 // Type mix (relative weights) 369 cloud_cumulusWeight: 3, 370 cloud_puffWeight: 0, 371 // Cumulus sizing 372 cloud_cumulusWidthMin: 100, 373 cloud_cumulusWidthMax: 180, 374 cloud_cumulusHeightRatioMin: 0.57, 375 cloud_cumulusHeightRatioMax: 1.27, 376 cloud_cumulusOpacityMin: 0.43, 377 cloud_cumulusOpacityMax: 0.7, 378 // Puff sizing 379 cloud_puffWidthMin: 32, 380 cloud_puffWidthMax: 57, 381 cloud_puffHeightRatioMin: 0.65, 382 cloud_puffHeightRatioMax: 1.19, 383 cloud_puffOpacityMin: 0.43, 384 cloud_puffOpacityMax: 0.75, 385 // Texture & shape 386 cloud_textureSize: 448, 387 cloud_bodyRxMin: 70, 388 cloud_bodyRxMax: 109, 389 cloud_bodyRyRatioMin: 0.6, 390 cloud_bodyRyRatioMax: 1.24, 391 cloud_bumpCountMin: 6, 392 cloud_bumpCountMax: 15, 393 cloud_bumpRadiusMin: 0.65, 394 cloud_bumpRadiusMax: 1.12, 395 cloud_bumpDistanceMin: 0.4, 396 cloud_bumpDistanceMax: 1.16, 397 cloud_bumpTopBias: 0.13, 398 cloud_bumpAlphaMin: 0.33, 399 cloud_bumpAlphaMax: 0.68, 400 cloud_softenerCount: 1, 401 cloud_softenerAlphaMin: 0.35, 402 cloud_softenerAlphaMax: 0.78, 403 // Shading (3-layer stack) 404 cloud_topColor: 0xfff4e3, 405 cloud_sideColor: 0xffffff, 406 cloud_baseColor: 0xb9c2d4, 407 cloud_baseOpacityMul: 0.04, 408 cloud_topOpacityMul: 0.2, 409 cloud_layerYOffset: 0.05, 410 // Motion 411 cloud_windDirRad: 0.5655, 412 cloud_windSpeedMin: 0.6, 413 cloud_windSpeedMax: 1.4, 414 cloud_bobAmpMin: 0, 415 cloud_bobAmpMax: 0.3, 416 cloud_bobSpeedMin: 0.2, 417 cloud_bobSpeedMax: 0.5, 418 419 // ââ Pond ââââââââââââââââââââââââââââââââââââââââââââ 420 pond_waterColor: 0x2d98b5, 421 pond_waterOpacity: 0.82, 422 pond_maxDepth: 3.5, 423 pond_lilypadCount: 10, 424 425 // ââ Models / Scatter ââââââââââââââââââââââââââââââââ 426 mdl_treeCount: 60, 427 mdl_treeScaleMin: 3.5, 428 mdl_treeScaleMax: 4, 429 mdl_grassCount: 40000, 430 mdl_grassScaleMin: 12, 431 mdl_grassScaleMax: 16, 432 mdl_grassWindStrength: 0.8, 433 mdl_cloverCount: 1000, 434 mdl_cloverScaleMin: 0.5, 435 mdl_cloverScaleMax: 1, 436 mdl_rockCount: 4, 437 mdl_rockScaleMin: 2, 438 mdl_rockScaleMax: 2.5, 439 mdl_flatRockCount: 6, 440 mdl_flatRockScaleMin: 19, 441 mdl_flatRockScaleMax: 20, 442 mdl_bushCount: 200, 443 mdl_bushScaleMin: 2, 444 mdl_bushScaleMax: 3, 445 mdl_bushWindStrength: 0.2, 446
447 // ââ Story / Rescue ââââââââââââââââââââââââââââââââââ 448 story_depositMessages: [ 449 'Three crystals anchored!', 450 'The portal hums to life...', 451 'One step closer to finding her.', 452 ], 453 454 // ââ White Cat NPC â behaviour âââââââââââââââââââââââ 455 // 456 // The NPC runs a "commitment" state machine: each state (wander, follow, 457 // journey, chase, rest, etc.) runs to its own completion and only THEN 458 // falls back to `idle`, which pauses for a randomized duration before 459 // picking the next action. No mid-action interrupts â so movement looks 460 // natural (straight lines, deliberate pauses) instead of twitchy. 461 // 462 // Everything below is admin-tunable. Lower numbers = more frequent / 463 // more aggressive. Higher = calmer / more reserved. 464 // 465 npc_enabled: true, 466 npc_moveSpeed: 8, 467 npc_sprintSpeed: 14, 468 469 // Idle pauses â how long the cat stands still between decisions 470 npc_idleMinDuration: 2.0, // shortest idle pause (seconds) 471 npc_idleMaxDuration: 6.0, // longest idle pause 472 473 // Following the player 474 // NOTE: stand-off distances must exceed the cat-cat physical collision 475 // radius (~2.5 combined, see main.js _resolveCatCatCollision). Anything 476 // lower causes the NPC to keep pushing into the player's back and the 477 // cats visually clip into each other. 478 npc_followMinDistance: 3.5, // default stand-off distance when following 479 npc_followMaxDistance: 14, // won't start a follow if player is further 480 npc_followGiveUpDistance: 40, // mid-follow, gives up if player gets this far 481 npc_followChance: 0.6, // probability of entering follow from idle 482 npc_followSprintChance: 0.25, // 25% of follow sessions are at a run 483 npc_followCommitMin: 6.0, // shortest committed follow session 484 npc_followCommitMax: 14.0, // longest committed follow session 485 npc_closeFollowChance: 0.25, // 25% of follows are "close follow" 486 npc_closeFollowDistance: 2.8, // tight stand-off â just above the 2.5 collision minimum 487 488 // Wandering and journeys 489 npc_wanderRadius: 30, // max range of a short wander target 490 npc_wanderChance: 0.65, // probability of wandering vs idling 491 npc_journeyChance: 0.18, // chance of a long cross-map journey 492 npc_journeySprintChance: 0.35, // 35% of journeys have a sprint burst 493 npc_journeyCooldownMin: 20, // seconds between journeys (min) 494 npc_journeyCooldownMax: 50, // seconds between journeys (max) 495 496 // Long-range "smell" homing â she can smell the player from anywhere 497 npc_smellHomeChance: 0.08, // probability on any idle eval 498 npc_smellHomeMinDist: 45, // only fires when player is at least this far 499 npc_smellHomeArrive: 4, // stops when within this distance of player 500 501 // Guaranteed-reunion timer â if she's been far from the player for this 502 // long, she'll bolt in (sprint) regardless of distance, stop very close, 503 // then resume whatever she was going to do. 504 npc_forceReunionInterval: 40, // seconds away before forced reunion 505 npc_forceReunionCloseDistance: 6, // distance that counts as "with player" 506 507 // Rest (lying down / sleeping) 508 npc_restIntervalMean: 60, // avg seconds between rests 509 npc_restIntervalVar: 20, // ± variance on the interval 510 npc_restDuration: 20, // how long each rest lasts 511 512 // Jumping on rocks and hut roofs 513 npc_jumpOnRockChance: 0.4, // chance of seeking an elevated surface 514 npc_jumpCooldown: 6, // seconds before jumping again 515 npc_imitatePlayerChance: 0.5, // when player is on roof/rock, chance to follow up 516 517 // Birds 518 npc_chaseBirdChance: 0.35, // chance of pouncing when a bird is near 519 npc_chaseBirdRadius: 22, // starts chase if bird within this distance 520 npc_chaseBirdGiveUp: 30, // bird escapes if this far 521 522 // Fish 523 npc_chaseFishChance: 0.25, // chance of chasing a fish 524 npc_chaseFishRadius: 15, // dry-land chase-trigger distance 525 npc_chaseFishSwimRadius: 30, // in-water chase-trigger distance 526
527 // Fish bones 528 npc_sniffBoneChance: 0.5, // chance of sniffing when near a bone 529 npc_sniffRadius: 5, // only sniffs if within this close 530 npc_sniffDuration: 2.5, // how long the sniff pause lasts 531 532 // ââ NPC Dialogue â globals ââââââââââââââââââââââââââ 533 // Proximity gating: NPC bubbles + meows only fire when the player is 534 // within npcChat_proximityRadius. Out of range, she's silent. 535 npcChat_enabled: true, 536 npcChat_globalCooldown: 12, 537 npcChat_msgDuration: 6, 538 npcChat_proximityRadius: 18, 539 npcChat_meowVolumeScale: 1.0, 540 npcChat_meowEnabled: true, 541 542 // Each trigger below has the same schema: 543 // npcChat_${key}Enabled / Cooldown / Chance / Messages 544 545 npcChat_nearPlayerEnabled: true, npcChat_nearPlayerCooldown: 20, npcChat_nearPlayerChance: 0.2, npcChat_nearPlayerMessages: [], npcChat_nearPlayerReplies: [], 546 npcChat_swimmingEnabled: true, npcChat_swimmingCooldown: 25, npcChat_swimmingChance: 0.3, npcChat_swimmingMessages: [], 547 npcChat_onRoofEnabled: true, npcChat_onRoofCooldown: 30, npcChat_onRoofChance: 0.3, npcChat_onRoofMessages: [], 548 npcChat_onRockEnabled: true, npcChat_onRockCooldown: 30, npcChat_onRockChance: 0.3, npcChat_onRockMessages: [], 549 npcChat_inBushEnabled: true, npcChat_inBushCooldown: 25, npcChat_inBushChance: 0.3, npcChat_inBushMessages: [], 550 npcChat_nearTreeEnabled: true, npcChat_nearTreeCooldown: 35, npcChat_nearTreeChance: 0.15, npcChat_nearTreeMessages: [], 551 npcChat_nearHutEnabled: true, npcChat_nearHutCooldown: 35, npcChat_nearHutChance: 0.15, npcChat_nearHutMessages: [], 552 npcChat_nearRockEnabled: true, npcChat_nearRockCooldown: 35, npcChat_nearRockChance: 0.1, npcChat_nearRockMessages: [], 553 npcChat_nearPortalEnabled: true, npcChat_nearPortalCooldown: 35, npcChat_nearPortalChance: 0.15,npcChat_nearPortalMessages: [], 554 npcChat_nearCrystalEnabled: true, npcChat_nearCrystalCooldown: 30,npcChat_nearCrystalChance: 0.15,npcChat_nearCrystalMessages: [], 555 npcChat_nearFishBoneEnabled:true, npcChat_nearFishBoneCooldown: 40,npcChat_nearFishBoneChance: 0.3,npcChat_nearFishBoneMessages: [], 556 npcChat_pondEdgeEnabled: true, npcChat_pondEdgeCooldown: 30, npcChat_pondEdgeChance: 0.15, npcChat_pondEdgeMessages: [], 557 npcChat_birdNearbyEnabled: true, npcChat_birdNearbyCooldown: 25, npcChat_birdNearbyChance: 0.3, npcChat_birdNearbyMessages: [], 558 npcChat_fishVisibleEnabled: true, npcChat_fishVisibleCooldown: 25,npcChat_fishVisibleChance: 0.3,npcChat_fishVisibleMessages: [], 559 npcChat_jumpingEnabled: true, npcChat_jumpingCooldown: 18, npcChat_jumpingChance: 0.2, npcChat_jumpingMessages: [], 560 npcChat_lookingUpEnabled: true, npcChat_lookingUpCooldown: 30, npcChat_lookingUpChance: 0.15, npcChat_lookingUpMessages: [], 561 npcChat_facingCameraEnabled:true, npcChat_facingCameraCooldown: 35,npcChat_facingCameraChance:0.1,npcChat_facingCameraMessages: [], 562 npcChat_roamingEnabled: true, npcChat_roamingCooldown: 30, npcChat_roamingChance: 0.08, npcChat_roamingMessages: [], 563 npcChat_followingEnabled: true, npcChat_followingCooldown: 25, npcChat_followingChance: 0.1, npcChat_followingMessages: [], 564 npcChat_restingEnabled: true, npcChat_restingCooldown: 40, npcChat_restingChance: 0.3, npcChat_restingMessages: [], 565 npcChat_atPortalEnabled: true, npcChat_atPortalCooldown: 30, npcChat_atPortalChance: 0.3, npcChat_atPortalMessages: [], 566 npcChat_idleEnabled: true, npcChat_idleCooldown: 45, npcChat_idleChance: 0.1, npcChat_idleMessages: [], 567 568 // ââ Admin debug â UI buttons shown only to the logged-in admin ââ 569 // Gated by BOTH the auth check (must match the admin UID) AND this flag. 570 // Toggle from Admin â White Cat NPC (or wherever you place the control). 571 admin_showRescueDebugBtn: false, // shows the "replay rescue cinematic" button on the HUD 572 573 // ââ Cat Duet â scripted QâA conversations between black + white cats ââ 574 // Both cats can ask; the OTHER delivers the paired answer after a delay. 575 // Duets are gated by proximity (npcDuet_proximityRadius) and by which 576 // interaction-point trigger is currently active in the world. 577 npcDuet_enabled: true, 578 npcDuet_globalCooldown: 40, // default seconds between any two duets 579 npcDuet_proximityRadius: 6, // both cats must be within this of each other 580 npcDuet_pauseBefore: 4, // silent lead-in (sec) before the question appears 581 npcDuet_exchangeDelay: 2.5, // gap (seconds) between question and answer 582 npcDuet_muteThoughtsAfter: 4, // extra mute time after the answer (no thought bubbles) 583 584 // Per-trigger conversation pools. Each conversation entry is: 585 // { question: string, answer: string, asker: 'random'|'black'|'white' } 586 // Empty pools mean no duet for that context. 587 npcDuet_generalEnabled: true, npcDuet_generalCooldown: 40, npcDuet_generalChance: 0.4, npcDuet_generalConversations: [], 588 npcDuet_swimmingEnabled: true, npcDuet_swimmingCooldown: 50, npcDuet_swimmingChance: 0.5, npcDuet_swimmingConversations: [], 589 npcDuet_onRoofEnabled: true, npcDuet_onRoofCooldown: 50, npcDuet_onRoofChance: 0.5, npcDuet_onRoofConversations: [], 590 npcDuet_onRockEnabled: true, npcDuet_onRockCooldown: 50, npcDuet_onRockChance: 0.5, npcDuet_onRockConversations: [], 591 npcDuet_inBushEnabled: true, npcDuet_inBushCooldown: 60, npcDuet_inBushChance: 0.4, npcDuet_inBushConversations: [], 592 npcDuet_nearTreeEnabled: true, npcDuet_nearTreeCooldown: 60, npcDuet_nearTreeChance: 0.3, npcDuet_nearTreeConversations: [], 593 npcDuet_nearRockEnabled: true, npcDuet_nearRockCooldown: 60, npcDuet_nearRockChance: 0.3, npcDuet_nearRockConversations: [], 594 npcDuet_nearHutEnabled: true, npcDuet_nearHutCooldown: 60, npcDuet_nearHutChance: 0.3, npcDuet_nearHutConversations: [], 595 npcDuet_nearPortalEnabled: true, npcDuet_nearPortalCooldown: 45, npcDuet_nearPortalChance: 0.4, npcDuet_nearPortalConversations: [], 596 npcDuet_atPortalEnabled: true, npcDuet_atPortalCooldown: 45, npcDuet_atPortalChance: 0.5, npcDuet_atPortalConversations: [], 597 npcDuet_nearCrystalEnabled: true, npcDuet_nearCrystalCooldown: 45, npcDuet_nearCrystalChance: 0.4, npcDuet_nearCrystalConversations: [],
598 npcDuet_nearFishBoneEnabled: true, npcDuet_nearFishBoneCooldown: 60, npcDuet_nearFishBoneChance: 0.4, npcDuet_nearFishBoneConversations: [], 599 npcDuet_pondEdgeEnabled: true, npcDuet_pondEdgeCooldown: 60, npcDuet_pondEdgeChance: 0.3, npcDuet_pondEdgeConversations: [], 600 npcDuet_birdNearbyEnabled: true, npcDuet_birdNearbyCooldown: 45, npcDuet_birdNearbyChance: 0.4, npcDuet_birdNearbyConversations: [], 601 npcDuet_fishVisibleEnabled: true, npcDuet_fishVisibleCooldown: 45, npcDuet_fishVisibleChance: 0.4, npcDuet_fishVisibleConversations: [], 602 npcDuet_idleEnabled: true, npcDuet_idleCooldown: 50, npcDuet_idleChance: 0.3, npcDuet_idleConversations: [], 603 npcDuet_restingEnabled: true, npcDuet_restingCooldown: 60, npcDuet_restingChance: 0.3, npcDuet_restingConversations: [], 604 605 // ââ Memory Pods â Polaroids scattered in the world ââ 606 // Visual tuning for the in-world Polaroid + glow + particles. Content 607 // (image, caption, filter, font, count) lives in site_config/memories. 608 memory_enabled: true, 609 memory_count: 4, // how many pods to scatter 610 memory_proximityRadius: 5, // walk within this to get "E to read" hint 611 memory_spawnMinRadius: 20, // keep pods away from world center 612 memory_spawnMaxRadius: 95, // within playable area 613 memory_spawnMinSpacing: 18, // min distance between pods 614 memory_polaroidScale: 1.3, // ground Polaroid plane scale 615 memory_glowColor: '#ffe29a', // warm cream glow 616 memory_glowIntensity: 1.4, 617 memory_glowPulseSpeed: 1.6, 618 memory_hoverHeight: 1.2, // glow orb height above Polaroid 619 memory_particleCount: 14, // magic particles per pod 620 memory_particleRadius: 0.8, 621 memory_viewedDimFactor: 0.25, // 0 = dark, 1 = no dim. Admin-tunable. 622 memory_hintText: 'Press E to read', 623 memory_autoDismissDuration: 0, // 0 = manual close; >0 = auto-close after N sec 624}); 625 626// ââ Mutable runtime config (starts as copy of defaults) ââââââ 627export const SCENE = { ...DEFAULTS }; 628 629/** 630 * Fetch overrides from Firestore and merge into SCENE. 631 * Call this BEFORE creating any game entities. 632 */ 633export async function loadSceneSettings() { 634 try { 635 const snap = await getDoc(doc(db, 'site_config', 'scene_settings')); 636 if (snap.exists()) { 637 const data = snap.data(); 638 for (const key of Object.keys(DEFAULTS)) { 639 if (data[key] !== undefined) { 640 SCENE[key] = data[key]; 641 } 642 } 643 } 644 } catch (e) { 645 console.warn('Scene settings: using defaults', e); 646 } 647}
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.