@@ -53,7 +53,7 @@ function setup() { |
| 53 | 53 | |
| 54 | 54 | // Generate twigs with FIXED positions |
| 55 | 55 | let twigs = []; |
| 56 | | - let numTwigs = 5; |
| 56 | + let numTwigs = 7; |
| 57 | 57 | for (let i = 0; i < numTwigs; i++) { |
| 58 | 58 | let t = 0.2 + (0.6 * i / (numTwigs - 1)); // Evenly distributed |
| 59 | 59 | let x = lerp(branchStartX, branchEndX, t); // Calculate actual X position |
@@ -62,29 +62,30 @@ function setup() { |
| 62 | 62 | length: 20 + (i * 4), // Vary length slightly |
| 63 | 63 | angle: (-PI/4 + (i * PI/20)) * (homeBranchSide === 'right' ? -1 : 1), |
| 64 | 64 | subTwigs: [ |
| 65 | | - { pos: 0.7, length: 5, angle: -5 }, |
| 66 | | - { pos: 0.5, length: 4, angle: 4 } |
| 65 | + { pos: 0.65, length: 6, angle: -6 }, |
| 66 | + { pos: 0.45, length: 5, angle: 5 }, |
| 67 | + { pos: 0.8, length: 4, angle: -3 } |
| 67 | 68 | ] |
| 68 | 69 | }); |
| 69 | 70 | } |
| 70 | 71 | |
| 71 | 72 | // Generate leaves with FIXED positions |
| 72 | 73 | let leaves = []; |
| 73 | | - for (let i = 0; i < 3; i++) { |
| 74 | | - let t = 0.3 + (0.4 * i / 2); // Distribute between 0.3 and 0.7 |
| 74 | + for (let i = 0; i < 5; i++) { |
| 75 | + let t = 0.3 + (0.4 * i / 4); // Distribute between 0.3 and 0.7 |
| 75 | 76 | let x = lerp(branchStartX, branchEndX, t); |
| 76 | 77 | leaves.push({ |
| 77 | 78 | x: x, // Store actual position |
| 78 | | - yOffset: -homeBranchThickness - (i * 10), |
| 79 | | - rotation: -PI/6 + (i * PI/6), |
| 80 | | - width: 15, |
| 81 | | - height: 8 |
| 79 | + yOffset: -homeBranchThickness - (i * 10) + (i % 2 === 0 ? -4 : 3), |
| 80 | + rotation: (-PI/7 + (i * PI/8)) * (homeBranchSide === 'right' ? -1 : 1), |
| 81 | + width: 16 + (i % 2 ? 2 : -1), |
| 82 | + height: 8 + (i % 2 ? 1 : 0) |
| 82 | 83 | }); |
| 83 | 84 | } |
| 84 | 85 | |
| 85 | 86 | // Generate bark textures with FIXED positions |
| 86 | 87 | let barkTextures = []; |
| 87 | | - for (let x = Math.min(branchStartX, branchEndX); x < Math.max(branchStartX, branchEndX); x += 20) { |
| 88 | + for (let x = Math.min(branchStartX, branchEndX); x < Math.max(branchStartX, branchEndX); x += 16) { |
| 88 | 89 | barkTextures.push({ |
| 89 | 90 | x: x, |
| 90 | 91 | yOff: -5 + (x % 10), // Deterministic offset based on position |
@@ -153,6 +154,20 @@ function setup() { |
| 153 | 154 | break; |
| 154 | 155 | } |
| 155 | 156 | } |
| 157 | + // Avoid overlapping the home branch body (transform point into branch frame) |
| 158 | + if (valid) { |
| 159 | + const ca = Math.cos(window.homeBranch.angle); |
| 160 | + const sa = Math.sin(window.homeBranch.angle); |
| 161 | + const relY = y - window.homeBranch.y; // translate to branch's local origin |
| 162 | + const xr = x * ca + relY * sa; // rotate into branch frame |
| 163 | + const yr = -x * sa + relY * ca; |
| 164 | + const minX = Math.min(window.homeBranch.startX, window.homeBranch.endX) - radius - 8; |
| 165 | + const maxX = Math.max(window.homeBranch.startX, window.homeBranch.endX) + radius + 8; |
| 166 | + const halfThickness = window.homeBranch.thickness + radius + 6; |
| 167 | + if (xr >= minX && xr <= maxX && Math.abs(yr) <= halfThickness) { |
| 168 | + valid = false; // too close to the branch hull |
| 169 | + } |
| 170 | + } |
| 156 | 171 | |
| 157 | 172 | if (valid) { |
| 158 | 173 | obstacles.push(new Obstacle(x, y, radius, type)); |
@@ -361,110 +376,139 @@ function drawSkyGradient() { |
| 361 | 376 | endShape(CLOSE); |
| 362 | 377 | pop(); |
| 363 | 378 | |
| 364 | | - // Main branch with organic shape and taper |
| 379 | + // Main branch with organic shape and taper (gnarlier) |
| 365 | 380 | push(); |
| 366 | 381 | translate(0, branch.y); |
| 367 | 382 | rotate(branch.angle); |
| 368 | | - |
| 369 | | - // Dark brown base |
| 383 | + |
| 384 | + // Base color varies by phase |
| 370 | 385 | noStroke(); |
| 371 | 386 | if (gamePhase === 'NIGHT') { |
| 372 | 387 | fill(30, 15, 5); |
| 373 | 388 | } else { |
| 374 | 389 | fill(92, 51, 23); |
| 375 | 390 | } |
| 376 | | - |
| 377 | | - // Create tapered, organic branch shape |
| 391 | + |
| 392 | + // Build an irregular, tapered hull using noise to perturb the top/bottom edges |
| 393 | + let segs = 24; // more segments for detail |
| 394 | + let topPts = []; |
| 395 | + let botPts = []; |
| 396 | + let len = branch.endX - branch.startX; |
| 397 | + for (let i = 0; i <= segs; i++) { |
| 398 | + let t = i / segs; |
| 399 | + let x = branch.startX + len * t; |
| 400 | + // base radius tapers along the branch |
| 401 | + let r = lerp(branch.thickness, branch.thickness * 0.35, t); |
| 402 | + // add subtle bumpiness using noise keyed by x so it stays stable |
| 403 | + let n = noise(x * 0.02, 3.1) - 0.5; // [-0.5, 0.5] |
| 404 | + let bump = n * (4 + 6 * (1 - t)); // larger bumps near base |
| 405 | + // slight sine undulation so it feels woody |
| 406 | + let und = sin(t * TWO_PI * 1.5) * 2 * (1 - t); |
| 407 | + let yTop = -(r + bump + und); |
| 408 | + let yBot = (r + bump * 0.6 + und * 0.4); |
| 409 | + topPts.push({x, y: yTop}); |
| 410 | + botPts.push({x, y: yBot}); |
| 411 | + } |
| 412 | + |
| 378 | 413 | beginShape(); |
| 379 | | - // Top edge with bumps and curves |
| 380 | | - vertex(branch.startX, -branch.thickness); |
| 381 | | - bezierVertex( |
| 382 | | - branch.startX + (branch.endX - branch.startX) * 0.2, -branch.thickness + 3, |
| 383 | | - branch.startX + (branch.endX - branch.startX) * 0.4, -branch.thickness * 0.8 + 2, |
| 384 | | - branch.startX + (branch.endX - branch.startX) * 0.6, -branch.thickness * 0.6 |
| 385 | | - ); |
| 386 | | - bezierVertex( |
| 387 | | - branch.startX + (branch.endX - branch.startX) * 0.8, -branch.thickness * 0.4, |
| 388 | | - branch.endX - 20, -branch.thickness * 0.3, |
| 389 | | - branch.endX, -branch.thickness * 0.2 |
| 390 | | - ); |
| 391 | | - |
| 392 | | - // Bottom edge with natural irregularities |
| 393 | | - vertex(branch.endX, branch.thickness * 0.2); |
| 394 | | - bezierVertex( |
| 395 | | - branch.endX - 20, branch.thickness * 0.3, |
| 396 | | - branch.startX + (branch.endX - branch.startX) * 0.8, branch.thickness * 0.4 + 2, |
| 397 | | - branch.startX + (branch.endX - branch.startX) * 0.6, branch.thickness * 0.6 |
| 398 | | - ); |
| 399 | | - bezierVertex( |
| 400 | | - branch.startX + (branch.endX - branch.startX) * 0.4, branch.thickness * 0.8, |
| 401 | | - branch.startX + (branch.endX - branch.startX) * 0.2, branch.thickness - 3, |
| 402 | | - branch.startX, branch.thickness |
| 403 | | - ); |
| 414 | + // top edge forward |
| 415 | + for (let p of topPts) { |
| 416 | + vertex(p.x, p.y); |
| 417 | + } |
| 418 | + // bottom edge back |
| 419 | + for (let i = botPts.length - 1; i >= 0; i--) { |
| 420 | + vertex(botPts[i].x, botPts[i].y); |
| 421 | + } |
| 404 | 422 | endShape(CLOSE); |
| 405 | | - |
| 406 | | - // Add lighter brown highlights |
| 423 | + |
| 424 | + // Add a secondary offshoot (fork) around 60% along the branch |
| 425 | + let forkT = 0.6; |
| 426 | + let forkX = branch.startX + len * forkT; |
| 427 | + let forkLen = min(70, len * 0.18); |
| 428 | + let forkAngle = (branch.side === 'right' ? -1 : 1) * (-PI/6 + noise(7.7) * PI/12); |
| 429 | + |
| 430 | + // draw the fork as a tapered curved limb |
| 431 | + push(); |
| 432 | + translate(forkX, lerp(topPts[Math.floor(segs * forkT)].y, botPts[Math.floor(segs * forkT)].y, 0.15)); |
| 433 | + rotate(forkAngle); |
| 434 | + noStroke(); |
| 407 | 435 | if (gamePhase === 'NIGHT') { |
| 408 | | - fill(50, 25, 10, 150); |
| 436 | + fill(35, 18, 6); |
| 409 | 437 | } else { |
| 410 | | - fill(139, 90, 43, 150); |
| 438 | + fill(102, 58, 28); |
| 411 | 439 | } |
| 412 | | - |
| 413 | | - // Top highlight |
| 414 | 440 | beginShape(); |
| 415 | | - vertex(branch.startX + 10, -branch.thickness + 5); |
| 416 | | - bezierVertex( |
| 417 | | - branch.startX + (branch.endX - branch.startX) * 0.3, -branch.thickness * 0.7, |
| 418 | | - branch.startX + (branch.endX - branch.startX) * 0.6, -branch.thickness * 0.5, |
| 419 | | - branch.endX - 30, -branch.thickness * 0.2 + 2 |
| 420 | | - ); |
| 421 | | - vertex(branch.endX - 30, 0); |
| 422 | | - bezierVertex( |
| 423 | | - branch.startX + (branch.endX - branch.startX) * 0.6, -2, |
| 424 | | - branch.startX + (branch.endX - branch.startX) * 0.3, -5, |
| 425 | | - branch.startX + 10, -8 |
| 426 | | - ); |
| 441 | + vertex(0, -6); |
| 442 | + bezierVertex(forkLen * 0.25, -8, forkLen * 0.55, -4, forkLen, 0); |
| 443 | + vertex(forkLen, 3); |
| 444 | + bezierVertex(forkLen * 0.55, 0, forkLen * 0.25, 4, 0, 6); |
| 427 | 445 | endShape(CLOSE); |
| 428 | | - |
| 429 | | - // Bark texture with grooves |
| 430 | | - stroke(60, 30, 10, 100); |
| 446 | + // tiny side twig on the fork |
| 447 | + stroke(gamePhase === 'NIGHT' ? color(40, 20, 0) : color(101, 67, 33)); |
| 448 | + strokeWeight(3); |
| 449 | + line(forkLen * 0.4, 0, forkLen * 0.4 + 14, -10); |
| 450 | + pop(); |
| 451 | + |
| 452 | + // Lighter highlights along the crown ridge |
| 453 | + if (gamePhase === 'NIGHT') { |
| 454 | + fill(50, 25, 10, 140); |
| 455 | + } else { |
| 456 | + fill(139, 90, 43, 160); |
| 457 | + } |
| 458 | + beginShape(); |
| 459 | + for (let i = 2; i <= segs - 2; i++) { |
| 460 | + let p = topPts[i]; |
| 461 | + vertex(p.x, p.y + 3); |
| 462 | + } |
| 463 | + for (let i = segs - 2; i >= 2; i--) { |
| 464 | + let p = topPts[i]; |
| 465 | + vertex(p.x, p.y + 7); |
| 466 | + } |
| 467 | + endShape(CLOSE); |
| 468 | + |
| 469 | + // Bark grooves: short diagonal strokes with slight randomness |
| 470 | + stroke(60, 30, 10, 110); |
| 431 | 471 | strokeWeight(1); |
| 432 | | - for (let i = 0; i < branch.barkTextures.length; i += 2) { |
| 433 | | - let texture = branch.barkTextures[i]; |
| 434 | | - // Vertical grooves |
| 435 | | - line(texture.x, texture.yOff - 5, texture.x + 3, texture.yOff + 8); |
| 436 | | - // Horizontal texture |
| 437 | | - if (i % 4 === 0) { |
| 438 | | - line(texture.x - 5, texture.yOff, texture.x + 15, texture.yOff + 2); |
| 472 | + for (let i = 0; i < branch.barkTextures.length; i++) { |
| 473 | + let bx = branch.barkTextures[i].x; |
| 474 | + // only draw inside the branch span |
| 475 | + if (bx < min(branch.startX, branch.endX) || bx > max(branch.startX, branch.endX)) continue; |
| 476 | + let t = (bx - branch.startX) / (len || 1); |
| 477 | + let r = lerp(branch.thickness, branch.thickness * 0.35, t); |
| 478 | + let ny = (noise(bx * 0.03, 9.2) - 0.5) * 10; |
| 479 | + let y = ny; |
| 480 | + line(bx - 3, y - r * 0.2, bx + 4, y + r * 0.25); |
| 481 | + if (i % 3 === 0) { |
| 482 | + line(bx - 5, y + 1, bx + 9, y + 3); |
| 439 | 483 | } |
| 440 | 484 | } |
| 441 | | - |
| 442 | | - // Add knots and bumps |
| 485 | + |
| 486 | + // Knots |
| 443 | 487 | noStroke(); |
| 444 | 488 | if (gamePhase === 'NIGHT') { |
| 445 | 489 | fill(40, 20, 5); |
| 446 | 490 | } else { |
| 447 | 491 | fill(80, 40, 15); |
| 448 | 492 | } |
| 449 | | - |
| 450 | | - // A couple of knots |
| 451 | | - ellipse(branch.startX + (branch.endX - branch.startX) * 0.3, -3, 15, 12); |
| 452 | | - ellipse(branch.startX + (branch.endX - branch.startX) * 0.7, 2, 10, 8); |
| 493 | + ellipse(branch.startX + len * 0.28, -2, 14, 11); |
| 494 | + ellipse(branch.startX + len * 0.73, 3, 11, 9); |
| 495 | + |
| 496 | + pop(); |
| 453 | 497 | |
| 454 | 498 | // Small twigs with organic angles |
| 455 | 499 | stroke(gamePhase === 'NIGHT' ? color(40, 20, 0) : color(101, 67, 33)); |
| 456 | 500 | for (let twig of branch.twigs) { |
| 457 | 501 | push(); |
| 458 | 502 | translate(twig.x, 0); |
| 459 | | - |
| 460 | | - // Make twigs thicker at base |
| 503 | + |
| 504 | + // Make twigs thicker at base, drawn as a gentle curve |
| 461 | 505 | strokeWeight(4); |
| 462 | | - line(0, 0, twig.length * 0.3, twig.length * 0.1); |
| 506 | + bezier(0, 0, twig.length * 0.18, twig.length * 0.05, twig.length * 0.42, twig.length * 0.12, twig.length * 0.62, twig.length * 0.16); |
| 463 | 507 | strokeWeight(3); |
| 464 | | - line(twig.length * 0.3, twig.length * 0.1, twig.length * 0.6, twig.length * 0.15); |
| 508 | + bezier(twig.length * 0.62, twig.length * 0.16, twig.length * 0.74, twig.length * 0.18, twig.length * 0.86, twig.length * 0.2, twig.length, twig.length * 0.22); |
| 465 | 509 | strokeWeight(2); |
| 466 | | - line(twig.length * 0.6, twig.length * 0.15, twig.length, twig.length * 0.2); |
| 467 | | - |
| 510 | + line(twig.length * 0.82, twig.length * 0.2, twig.length * 1.05, twig.length * 0.28); |
| 511 | + |
| 468 | 512 | // Tiny sub-twigs |
| 469 | 513 | strokeWeight(1); |
| 470 | 514 | for (let subTwig of twig.subTwigs) { |