@@ -110,7 +110,21 @@ function setup() { |
| 110 | 110 | let spiderStartX = homeBranchSide === 'left' ? |
| 111 | 111 | homeBranchLength * 0.8 : |
| 112 | 112 | width - homeBranchLength * 0.8; |
| 113 | | - spider = new Spider(spiderStartX, homeBranchY - 15); |
| 113 | + |
| 114 | + // Calculate the visual top of the branch at spider's position |
| 115 | + let branchStart = Math.min(branchStartX, branchEndX); |
| 116 | + let branchEnd = Math.max(branchStartX, branchEndX); |
| 117 | + let t = (spiderStartX - branchStart) / (branchEnd - branchStart); |
| 118 | + t = constrain(t, 0, 1); |
| 119 | + |
| 120 | + // Branch thickness tapers from full to 35% |
| 121 | + let branchTopThickness = lerp(homeBranchThickness, homeBranchThickness * 0.35, t); |
| 122 | + |
| 123 | + // Account for branch angle |
| 124 | + let angleOffset = (spiderStartX - branchStart) * Math.tan(homeBranchSide === 'left' ? 0.05 : -0.05); |
| 125 | + |
| 126 | + // Place spider on top of the visual branch |
| 127 | + spider = new Spider(spiderStartX, homeBranchY - branchTopThickness + angleOffset); |
| 114 | 128 | |
| 115 | 129 | // Add invisible obstacles along the branch for web anchor points |
| 116 | 130 | let numBranchAnchors = 3; |
@@ -376,113 +390,90 @@ function drawSkyGradient() { |
| 376 | 390 | endShape(CLOSE); |
| 377 | 391 | pop(); |
| 378 | 392 | |
| 379 | | - // Main branch with organic shape and taper (gnarlier) |
| 393 | + // Main branch with organic shape and taper |
| 380 | 394 | push(); |
| 381 | 395 | translate(0, branch.y); |
| 382 | 396 | rotate(branch.angle); |
| 383 | | - |
| 384 | | - // Base color varies by phase |
| 397 | + |
| 385 | 398 | noStroke(); |
| 399 | + |
| 400 | + // Base color |
| 386 | 401 | if (gamePhase === 'NIGHT') { |
| 387 | 402 | fill(30, 15, 5); |
| 388 | 403 | } else { |
| 389 | 404 | fill(92, 51, 23); |
| 390 | 405 | } |
| 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 | | - |
| 406 | + |
| 407 | + // Branch body with taper |
| 413 | 408 | beginShape(); |
| 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 | | - } |
| 409 | + vertex(branch.startX, -branch.thickness); |
| 410 | + bezierVertex( |
| 411 | + branch.startX + (branch.endX - branch.startX) * 0.3, -branch.thickness * 0.9, |
| 412 | + branch.startX + (branch.endX - branch.startX) * 0.7, -branch.thickness * 0.6, |
| 413 | + branch.endX, -branch.thickness * 0.35 |
| 414 | + ); |
| 415 | + vertex(branch.endX, branch.thickness * 0.35); |
| 416 | + bezierVertex( |
| 417 | + branch.startX + (branch.endX - branch.startX) * 0.7, branch.thickness * 0.6, |
| 418 | + branch.startX + (branch.endX - branch.startX) * 0.3, branch.thickness * 0.9, |
| 419 | + branch.startX, branch.thickness |
| 420 | + ); |
| 422 | 421 | endShape(CLOSE); |
| 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 |
| 422 | + |
| 423 | + // Add a fork around 70% down the branch |
| 431 | 424 | push(); |
| 432 | | - translate(forkX, lerp(topPts[Math.floor(segs * forkT)].y, botPts[Math.floor(segs * forkT)].y, 0.15)); |
| 433 | | - rotate(forkAngle); |
| 434 | | - noStroke(); |
| 425 | + let forkX = branch.startX + (branch.endX - branch.startX) * 0.7; |
| 426 | + let forkY = 0; |
| 427 | + translate(forkX, forkY); |
| 428 | + rotate((branch.side === 'right' ? -1 : 1) * PI/6); |
| 429 | + |
| 430 | + // Fork branch |
| 435 | 431 | if (gamePhase === 'NIGHT') { |
| 436 | 432 | fill(35, 18, 6); |
| 437 | 433 | } else { |
| 438 | 434 | fill(102, 58, 28); |
| 439 | 435 | } |
| 436 | + |
| 440 | 437 | beginShape(); |
| 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); |
| 438 | + vertex(0, -8); |
| 439 | + bezierVertex(20, -7, 35, -5, 50, -3); |
| 440 | + vertex(50, 3); |
| 441 | + bezierVertex(35, 5, 20, 7, 0, 8); |
| 445 | 442 | endShape(CLOSE); |
| 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 | 443 | pop(); |
| 451 | | - |
| 452 | | - // Lighter highlights along the crown ridge |
| 444 | + |
| 445 | + // Add lighter highlights |
| 453 | 446 | if (gamePhase === 'NIGHT') { |
| 454 | | - fill(50, 25, 10, 140); |
| 447 | + fill(50, 25, 10, 150); |
| 455 | 448 | } else { |
| 456 | | - fill(139, 90, 43, 160); |
| 449 | + fill(139, 90, 43, 180); |
| 457 | 450 | } |
| 451 | + |
| 452 | + // Highlight on top ridge |
| 458 | 453 | 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 | | - } |
| 454 | + vertex(branch.startX + 20, -branch.thickness * 0.8); |
| 455 | + bezierVertex( |
| 456 | + branch.startX + (branch.endX - branch.startX) * 0.4, -branch.thickness * 0.7, |
| 457 | + branch.startX + (branch.endX - branch.startX) * 0.6, -branch.thickness * 0.5, |
| 458 | + branch.endX - 20, -branch.thickness * 0.25 |
| 459 | + ); |
| 460 | + vertex(branch.endX - 20, -branch.thickness * 0.15); |
| 461 | + bezierVertex( |
| 462 | + branch.startX + (branch.endX - branch.startX) * 0.6, -branch.thickness * 0.4, |
| 463 | + branch.startX + (branch.endX - branch.startX) * 0.4, -branch.thickness * 0.6, |
| 464 | + branch.startX + 20, -branch.thickness * 0.7 |
| 465 | + ); |
| 467 | 466 | endShape(CLOSE); |
| 468 | | - |
| 469 | | - // Bark grooves: short diagonal strokes with slight randomness |
| 470 | | - stroke(60, 30, 10, 110); |
| 467 | + |
| 468 | + // Bark texture lines |
| 469 | + stroke(60, 30, 10, 100); |
| 471 | 470 | strokeWeight(1); |
| 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); |
| 471 | + for (let texture of branch.barkTextures) { |
| 472 | + if (texture.x % 20 < 10) { |
| 473 | + line(texture.x, texture.yOff, texture.x + 3, texture.endYOff); |
| 483 | 474 | } |
| 484 | 475 | } |
| 485 | | - |
| 476 | + |
| 486 | 477 | // Knots |
| 487 | 478 | noStroke(); |
| 488 | 479 | if (gamePhase === 'NIGHT') { |
@@ -490,39 +481,38 @@ function drawSkyGradient() { |
| 490 | 481 | } else { |
| 491 | 482 | fill(80, 40, 15); |
| 492 | 483 | } |
| 493 | | - ellipse(branch.startX + len * 0.28, -2, 14, 11); |
| 494 | | - ellipse(branch.startX + len * 0.73, 3, 11, 9); |
| 495 | | - |
| 484 | + ellipse(branch.startX + (branch.endX - branch.startX) * 0.3, -5, 12, 8); |
| 485 | + ellipse(branch.startX + (branch.endX - branch.startX) * 0.65, 3, 8, 10); |
| 486 | + |
| 496 | 487 | pop(); |
| 497 | 488 | |
| 498 | | - // Small twigs with organic angles |
| 489 | + // Small twigs |
| 499 | 490 | stroke(gamePhase === 'NIGHT' ? color(40, 20, 0) : color(101, 67, 33)); |
| 500 | 491 | for (let twig of branch.twigs) { |
| 501 | 492 | push(); |
| 502 | | - translate(twig.x, 0); |
| 503 | | - |
| 504 | | - // Make twigs thicker at base, drawn as a gentle curve |
| 505 | | - strokeWeight(4); |
| 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); |
| 493 | + translate(twig.x, branch.y); |
| 494 | + rotate(twig.angle); |
| 495 | + |
| 496 | + // Main twig |
| 507 | 497 | strokeWeight(3); |
| 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); |
| 509 | | - strokeWeight(2); |
| 510 | | - line(twig.length * 0.82, twig.length * 0.2, twig.length * 1.05, twig.length * 0.28); |
| 511 | | - |
| 512 | | - // Tiny sub-twigs |
| 498 | + line(0, 0, twig.length, 0); |
| 499 | + |
| 500 | + // Sub twigs |
| 513 | 501 | strokeWeight(1); |
| 514 | 502 | for (let subTwig of twig.subTwigs) { |
| 515 | | - line(twig.length * subTwig.pos, twig.length * 0.15, |
| 516 | | - twig.length * subTwig.pos + subTwig.length, |
| 517 | | - twig.length * 0.15 + subTwig.angle); |
| 503 | + push(); |
| 504 | + translate(twig.length * subTwig.pos, 0); |
| 505 | + rotate(subTwig.angle * 0.1); |
| 506 | + line(0, 0, subTwig.length, -subTwig.angle); |
| 507 | + pop(); |
| 518 | 508 | } |
| 519 | 509 | pop(); |
| 520 | 510 | } |
| 521 | 511 | |
| 522 | | - // Add leaves with more natural placement |
| 512 | + // Add leaves |
| 523 | 513 | for (let leaf of branch.leaves) { |
| 524 | 514 | push(); |
| 525 | | - translate(leaf.x, leaf.yOffset); |
| 515 | + translate(leaf.x, branch.y + leaf.yOffset); |
| 526 | 516 | rotate(leaf.rotation); |
| 527 | 517 | |
| 528 | 518 | // Leaf shadow |
@@ -546,7 +536,6 @@ function drawSkyGradient() { |
| 546 | 536 | } |
| 547 | 537 | |
| 548 | 538 | pop(); |
| 549 | | - pop(); |
| 550 | 539 | } |
| 551 | 540 | } |
| 552 | 541 | |