@@ -16,64 +16,64 @@ class Spider { |
| 16 | 16 | this.munchCooldown = 0 |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | | -jump(targetX, targetY) { |
| 20 | | - if (!this.canJump) return; |
| 21 | | - |
| 22 | | - let direction = createVector(targetX - this.pos.x, targetY - this.pos.y); |
| 23 | | - let clickDistance = direction.mag(); |
| 24 | | - direction.normalize(); |
| 25 | | - |
| 26 | | - // Scale jump power based on click distance (closer clicks = smaller jumps) |
| 27 | | - let actualJumpPower = map(clickDistance, 0, 200, 3, this.jumpPower); |
| 28 | | - actualJumpPower = constrain(actualJumpPower, 3, this.jumpPower); |
| 29 | | - direction.mult(actualJumpPower); |
| 30 | | - |
| 31 | | - this.vel = direction; |
| 32 | | - this.isAirborne = true; |
| 33 | | - this.canJump = false; |
| 34 | | - this.lastAnchorPoint = this.pos.copy(); |
| 19 | + jump(targetX, targetY) { |
| 20 | + if (!this.canJump) return; |
| 21 | + |
| 22 | + let direction = createVector(targetX - this.pos.x, targetY - this.pos.y); |
| 23 | + let clickDistance = direction.mag(); |
| 24 | + direction.normalize(); |
| 25 | + |
| 26 | + // Scale jump power based on click distance (closer clicks = smaller jumps) |
| 27 | + let actualJumpPower = map(clickDistance, 0, 200, 3, this.jumpPower); |
| 28 | + actualJumpPower = constrain(actualJumpPower, 3, this.jumpPower); |
| 29 | + direction.mult(actualJumpPower); |
| 30 | + |
| 31 | + this.vel = direction; |
| 32 | + this.isAirborne = true; |
| 33 | + this.canJump = false; |
| 34 | + this.lastAnchorPoint = this.pos.copy(); |
| 35 | + |
| 36 | + // Check if we're jumping off a web strand |
| 37 | + for (let strand of webStrands) { |
| 38 | + if (strand === currentStrand) continue; |
| 39 | + |
| 40 | + if (this.checkStrandCollision(strand)) { |
| 41 | + // Much simpler shimmy detection based on actual jump power used |
| 42 | + let isShimmy = actualJumpPower < 6; // If we used less than half power, it's a shimmy |
| 35 | 43 | |
| 36 | | - // Check if we're jumping off a web strand |
| 37 | | - for (let strand of webStrands) { |
| 38 | | - if (strand === currentStrand) continue; |
| 39 | | - |
| 40 | | - if (this.checkStrandCollision(strand)) { |
| 41 | | - // Much simpler shimmy detection based on actual jump power used |
| 42 | | - let isShimmy = actualJumpPower < 6; // If we used less than half power, it's a shimmy |
| 43 | | - |
| 44 | | - // Apply appropriate recoil based on movement type |
| 45 | | - if (isShimmy) { |
| 46 | | - // Trigger shimmy visual effect |
| 47 | | - this.shimmyEffect = 20; |
| 48 | | - |
| 49 | | - // NO recoil at all for shimmying - just tiny vibration |
| 50 | | - strand.vibrate(0.3); |
| 51 | | - |
| 52 | | - // Tiny yellow particles |
| 53 | | - let p = new Particle(this.pos.x, this.pos.y); |
| 54 | | - p.color = color(255, 255, 100, 80); |
| 55 | | - p.vel = createVector(random(-0.3, 0.3), random(-0.3, 0.3)); |
| 56 | | - p.size = 2; |
| 57 | | - particles.push(p); |
| 58 | | - } else { |
| 59 | | - // Scale recoil based on actual jump power |
| 60 | | - let recoilForce = -(actualJumpPower / this.jumpPower) * 0.08; // Scale by power ratio |
| 61 | | - strand.applyRecoil(recoilForce); |
| 62 | | - |
| 63 | | - // Create particles only for real jumps |
| 64 | | - for (let i = 0; i < 2; i++) { |
| 65 | | - let p = new Particle(this.pos.x, this.pos.y); |
| 66 | | - p.color = color(255, 255, 255, 120); |
| 67 | | - p.vel = createVector(random(-0.8, 0.8), random(1, 2)); |
| 68 | | - p.size = 3; |
| 69 | | - particles.push(p); |
| 70 | | - } |
| 71 | | - } |
| 72 | | - |
| 73 | | - break; |
| 74 | | - } |
| 44 | + // Apply appropriate recoil based on movement type |
| 45 | + if (isShimmy) { |
| 46 | + // Trigger shimmy visual effect |
| 47 | + this.shimmyEffect = 20; |
| 48 | + |
| 49 | + // NO recoil at all for shimmying - just tiny vibration |
| 50 | + strand.vibrate(0.3); |
| 51 | + |
| 52 | + // Tiny yellow particles |
| 53 | + let p = new Particle(this.pos.x, this.pos.y); |
| 54 | + p.color = color(255, 255, 100, 80); |
| 55 | + p.vel = createVector(random(-0.3, 0.3), random(-0.3, 0.3)); |
| 56 | + p.size = 2; |
| 57 | + particles.push(p); |
| 58 | + } else { |
| 59 | + // Scale recoil based on actual jump power |
| 60 | + let recoilForce = -(actualJumpPower / this.jumpPower) * 0.08; // Scale by power ratio |
| 61 | + strand.applyRecoil(recoilForce); |
| 62 | + |
| 63 | + // Create particles only for real jumps |
| 64 | + for (let i = 0; i < 2; i++) { |
| 65 | + let p = new Particle(this.pos.x, this.pos.y); |
| 66 | + p.color = color(255, 255, 255, 120); |
| 67 | + p.vel = createVector(random(-0.8, 0.8), random(1, 2)); |
| 68 | + p.size = 3; |
| 69 | + particles.push(p); |
| 70 | + } |
| 75 | 71 | } |
| 72 | + |
| 73 | + break; |
| 74 | + } |
| 76 | 75 | } |
| 76 | + } |
| 77 | 77 | |
| 78 | 78 | munch () { |
| 79 | 79 | if (this.munchCooldown > 0) return |
@@ -559,14 +559,46 @@ class Fly { |
| 559 | 559 | |
| 560 | 560 | class Obstacle { |
| 561 | 561 | constructor (x, y, radius, type) { |
| 562 | + // Store original position for drift tracking |
| 563 | + this.originalX = x |
| 564 | + this.originalY = y |
| 562 | 565 | this.x = x |
| 563 | 566 | this.y = y |
| 564 | 567 | this.radius = radius |
| 565 | | - this.type = type || (random() < 0.5 ? 'branch' : 'leaf') |
| 568 | + this.type = type || 'leaf' |
| 566 | 569 | this.rotation = random(TWO_PI) |
| 567 | 570 | this.leafPoints = [] |
| 568 | | - |
| 569 | | - if (this.type === 'leaf') { |
| 571 | + |
| 572 | + // Movement properties for all types |
| 573 | + this.bobOffset = random(TWO_PI) |
| 574 | + this.bobSpeed = random(0.02, 0.04) |
| 575 | + this.bobAmount = 0 |
| 576 | + |
| 577 | + // Type-specific initialization |
| 578 | + if (this.type === 'balloon') { |
| 579 | + this.bobAmount = 8 // Balloons bob more |
| 580 | + this.balloonColors = [ |
| 581 | + color(255, 100, 100), // Red |
| 582 | + color(100, 200, 255), // Blue |
| 583 | + color(255, 200, 100) // Yellow |
| 584 | + ] |
| 585 | + this.balloonColor = random(this.balloonColors) |
| 586 | + this.stringWave = 0 |
| 587 | + this.antLegPhase = random(TWO_PI) |
| 588 | + |
| 589 | + } else if (this.type === 'beetle') { |
| 590 | + this.bobAmount = 4 |
| 591 | + this.driftSpeed = random(0.15, 0.35) |
| 592 | + this.driftAngle = random(TWO_PI) |
| 593 | + this.driftChangeRate = random(0.005, 0.015) |
| 594 | + this.wingPhase = random(TWO_PI) |
| 595 | + this.beetleColor = random() < 0.5 ? |
| 596 | + color(20, 60, 20) : // Dark green |
| 597 | + color(40, 20, 60) // Purple |
| 598 | + this.driftDistance = 0 // Track total drift |
| 599 | + |
| 600 | + } else if (this.type === 'leaf') { |
| 601 | + this.bobAmount = 2 // Leaves bob slightly |
| 570 | 602 | let numPoints = 8 |
| 571 | 603 | for (let i = 0; i < numPoints; i++) { |
| 572 | 604 | let angle = (TWO_PI / numPoints) * i |
@@ -574,44 +606,292 @@ class Obstacle { |
| 574 | 606 | if (i === 0 || i === numPoints / 2) r = radius * 1.3 |
| 575 | 607 | this.leafPoints.push({ angle: angle, radius: r }) |
| 576 | 608 | } |
| 609 | + } else if (this.type === 'branch') { |
| 610 | + // Keep for backwards compatibility |
| 611 | + this.bobAmount = 0 |
| 612 | + } |
| 613 | + } |
| 614 | + |
| 615 | + update() { |
| 616 | + // Bobbing motion for all types |
| 617 | + let bob = sin(frameCount * this.bobSpeed + this.bobOffset) * this.bobAmount |
| 618 | + this.y = this.originalY + bob |
| 619 | + |
| 620 | + // Beetle-specific drift |
| 621 | + if (this.type === 'beetle') { |
| 622 | + // Store initial position if not set |
| 623 | + if (!this.initialX) { |
| 624 | + this.initialX = this.x |
| 625 | + this.initialY = this.y |
| 626 | + } |
| 627 | + |
| 628 | + // Slowly change drift direction using Perlin noise |
| 629 | + this.driftAngle += (noise(frameCount * this.driftChangeRate, this.originalX * 0.01) - 0.5) * 0.1 |
| 630 | + |
| 631 | + // Apply drift to original position |
| 632 | + this.originalX += cos(this.driftAngle) * this.driftSpeed |
| 633 | + this.originalY += sin(this.driftAngle) * this.driftSpeed * 0.5 |
| 634 | + |
| 635 | + // Calculate total drift distance from initial position |
| 636 | + this.driftDistance = dist(this.originalX, this.originalY, this.initialX, this.initialY) |
| 637 | + |
| 638 | + // Keep beetles on screen with soft boundaries |
| 639 | + if (this.originalX < 80) { |
| 640 | + this.driftAngle = random(-PI/4, PI/4) |
| 641 | + this.originalX = 80 |
| 642 | + } |
| 643 | + if (this.originalX > width - 80) { |
| 644 | + this.driftAngle = random(3*PI/4, 5*PI/4) |
| 645 | + this.originalX = width - 80 |
| 646 | + } |
| 647 | + if (this.originalY < 80) { |
| 648 | + this.driftAngle = random(-3*PI/4, -PI/4) |
| 649 | + this.originalY = 80 |
| 650 | + } |
| 651 | + if (this.originalY > height - 150) { |
| 652 | + this.driftAngle = random(PI/4, 3*PI/4) |
| 653 | + this.originalY = height - 150 |
| 654 | + } |
| 655 | + |
| 656 | + // Update actual position (with bob already applied to y) |
| 657 | + this.x = this.originalX |
| 658 | + |
| 659 | + // Check if beetle has drifted too far and break attached strands |
| 660 | + if (this.driftDistance > 100) { |
| 661 | + this.breakAttachedStrands() |
| 662 | + } |
| 663 | + } |
| 664 | + |
| 665 | + // Update animation phases |
| 666 | + if (this.type === 'balloon') { |
| 667 | + this.stringWave = sin(frameCount * 0.05 + this.bobOffset) * 0.1 |
| 668 | + this.antLegPhase += 0.1 |
| 669 | + } else if (this.type === 'beetle') { |
| 670 | + this.wingPhase += 0.15 |
| 671 | + } |
| 672 | + |
| 673 | + // For all moving obstacles, update any attached web strands |
| 674 | + if (this.bobAmount > 0 || this.type === 'beetle') { |
| 675 | + this.updateAttachedStrands() |
| 676 | + } |
| 677 | + } |
| 678 | + |
| 679 | + updateAttachedStrands() { |
| 680 | + // Update web strands that are connected to this obstacle |
| 681 | + for (let strand of webStrands) { |
| 682 | + // Check if strand starts at this obstacle |
| 683 | + if (dist(strand.start.x, strand.start.y, this.x, this.y) < this.radius + 10) { |
| 684 | + strand.start.x = this.x |
| 685 | + strand.start.y = this.y |
| 686 | + if (strand.path && strand.path.length > 0) { |
| 687 | + strand.path[0].x = this.x |
| 688 | + strand.path[0].y = this.y |
| 689 | + } |
| 690 | + } |
| 691 | + |
| 692 | + // Check if strand ends at this obstacle |
| 693 | + if (strand.end && dist(strand.end.x, strand.end.y, this.x, this.y) < this.radius + 10) { |
| 694 | + strand.end.x = this.x |
| 695 | + strand.end.y = this.y |
| 696 | + if (strand.path && strand.path.length > 0) { |
| 697 | + strand.path[strand.path.length - 1].x = this.x |
| 698 | + strand.path[strand.path.length - 1].y = this.y |
| 699 | + } |
| 700 | + } |
| 701 | + } |
| 702 | + } |
| 703 | + |
| 704 | + breakAttachedStrands() { |
| 705 | + // Break any strands attached to this beetle that has drifted too far |
| 706 | + for (let strand of webStrands) { |
| 707 | + let attachedToStart = dist(strand.start.x, strand.start.y, this.x, this.y) < this.radius + 10 |
| 708 | + let attachedToEnd = strand.end && dist(strand.end.x, strand.end.y, this.x, this.y) < this.radius + 10 |
| 709 | + |
| 710 | + if (attachedToStart || attachedToEnd) { |
| 711 | + // Mark strand as broken |
| 712 | + strand.broken = true |
| 713 | + |
| 714 | + // Create dramatic snap particles |
| 715 | + let snapX = attachedToStart ? strand.start.x : strand.end.x |
| 716 | + let snapY = attachedToStart ? strand.start.y : strand.end.y |
| 717 | + |
| 718 | + // Red/pink particles for the snap |
| 719 | + for (let i = 0; i < 8; i++) { |
| 720 | + let p = new Particle(snapX, snapY) |
| 721 | + p.color = color(255, random(100, 200), random(100, 150)) |
| 722 | + p.vel = createVector(random(-5, 5), random(-5, 2)) |
| 723 | + p.size = random(4, 8) |
| 724 | + particles.push(p) |
| 725 | + } |
| 726 | + |
| 727 | + // White strand particles |
| 728 | + for (let i = 0; i < 4; i++) { |
| 729 | + let p = new Particle(snapX, snapY) |
| 730 | + p.color = color(255, 255, 255) |
| 731 | + p.vel = createVector(random(-3, 3), random(-3, 0)) |
| 732 | + p.size = 3 |
| 733 | + particles.push(p) |
| 734 | + } |
| 735 | + |
| 736 | + // Reset beetle drift after breaking strands |
| 737 | + this.initialX = this.x |
| 738 | + this.initialY = this.y |
| 739 | + this.driftDistance = 0 |
| 740 | + } |
| 577 | 741 | } |
| 578 | 742 | } |
| 579 | 743 | |
| 580 | 744 | display () { |
| 581 | 745 | push() |
| 582 | 746 | translate(this.x, this.y) |
| 583 | | - rotate(this.rotation) |
| 584 | | - |
| 585 | | - if (this.type === 'branch') { |
| 586 | | - if (gamePhase === 'NIGHT') { |
| 587 | | - stroke(40, 20, 0) |
| 588 | | - fill(50, 25, 5) |
| 589 | | - } else { |
| 590 | | - stroke(101, 67, 33) |
| 591 | | - fill(139, 90, 43) |
| 747 | + |
| 748 | + if (this.type === 'balloon') { |
| 749 | + // Balloon with ant in basket! |
| 750 | + push() |
| 751 | + |
| 752 | + // String first (behind balloon) |
| 753 | + stroke(80, 60, 40) |
| 754 | + strokeWeight(1) |
| 755 | + noFill() |
| 756 | + beginShape() |
| 757 | + for (let i = 0; i <= 10; i++) { |
| 758 | + let t = i / 10 |
| 759 | + let stringX = sin(t * PI * 2 + this.stringWave) * 3 |
| 760 | + let stringY = t * 40 + this.radius |
| 761 | + curveVertex(stringX, stringY) |
| 592 | 762 | } |
| 593 | | - strokeWeight(3) |
| 594 | | - |
| 763 | + endShape() |
| 764 | + |
| 765 | + // Balloon shadow |
| 766 | + noStroke() |
| 767 | + fill(0, 0, 0, 30) |
| 768 | + ellipse(5, 5, this.radius * 2.2, this.radius * 2.5) |
| 769 | + |
| 770 | + // Main balloon |
| 771 | + noStroke() |
| 772 | + fill(red(this.balloonColor), green(this.balloonColor), blue(this.balloonColor), 150) |
| 773 | + ellipse(0, 0, this.radius * 2.2, this.radius * 2.5) |
| 774 | + fill(red(this.balloonColor) + 30, green(this.balloonColor) + 30, blue(this.balloonColor) + 30, 200) |
| 775 | + ellipse(-this.radius * 0.3, -this.radius * 0.3, this.radius * 1.2, this.radius * 1.4) |
| 776 | + // Highlight |
| 777 | + fill(255, 255, 255, 120) |
| 778 | + ellipse(-this.radius * 0.4, -this.radius * 0.5, this.radius * 0.5, this.radius * 0.6) |
| 779 | + |
| 780 | + // Basket |
| 781 | + translate(0, this.radius + 10) |
| 782 | + fill(139, 90, 43) |
| 783 | + stroke(100, 60, 20) |
| 784 | + strokeWeight(1) |
| 785 | + // Trapezoid basket |
| 786 | + beginShape() |
| 787 | + vertex(-8, 0) |
| 788 | + vertex(8, 0) |
| 789 | + vertex(6, 10) |
| 790 | + vertex(-6, 10) |
| 791 | + endShape(CLOSE) |
| 792 | + // Basket weave pattern |
| 793 | + stroke(100, 60, 20, 100) |
| 794 | + for (let i = -6; i < 6; i += 3) { |
| 795 | + line(i, 2, i, 8) |
| 796 | + } |
| 797 | + for (let i = 2; i < 8; i += 3) { |
| 798 | + line(-6, i, 6, i) |
| 799 | + } |
| 800 | + |
| 801 | + // Ant in basket |
| 802 | + translate(0, 5) |
| 803 | + fill(20) |
| 804 | + noStroke() |
| 805 | + // Ant body |
| 806 | + ellipse(0, 0, 6, 4) // Head |
| 807 | + ellipse(0, 3, 5, 6) // Thorax |
| 808 | + ellipse(0, 7, 7, 9) // Abdomen |
| 809 | + // Ant legs (animated) |
| 810 | + stroke(20) |
| 811 | + strokeWeight(0.5) |
| 812 | + for (let i = 0; i < 3; i++) { |
| 813 | + let legAngle = this.antLegPhase + i * 0.5 |
| 814 | + let legSpread = 4 + sin(legAngle) * 2 |
| 815 | + line(-2, 3 + i * 2, -legSpread, 3 + i * 2) |
| 816 | + line(2, 3 + i * 2, legSpread, 3 + i * 2) |
| 817 | + } |
| 818 | + // Antennae |
| 819 | + line(-1, -1, -3, -3) |
| 820 | + line(1, -1, 3, -3) |
| 821 | + |
| 822 | + pop() |
| 823 | + |
| 824 | + } else if (this.type === 'beetle') { |
| 825 | + // Big beetle! |
| 595 | 826 | push() |
| 596 | | - strokeWeight(this.radius / 3) |
| 597 | | - line(-this.radius, 0, this.radius, 0) |
| 598 | | - |
| 827 | + rotate(this.rotation) |
| 828 | + |
| 829 | + // Shadow |
| 830 | + noStroke() |
| 831 | + fill(0, 0, 0, 40) |
| 832 | + ellipse(3, 3, this.radius * 1.8, this.radius * 2.2) |
| 833 | + |
| 834 | + // Wings (if flying at night) |
| 835 | + if (gamePhase === 'NIGHT') { |
| 836 | + push() |
| 837 | + fill(255, 255, 255, 100 + sin(this.wingPhase) * 50) |
| 838 | + noStroke() |
| 839 | + let wingSpread = sin(this.wingPhase) * 15 |
| 840 | + ellipse(-wingSpread, 0, 20, 12) |
| 841 | + ellipse(wingSpread, 0, 20, 12) |
| 842 | + pop() |
| 843 | + } |
| 844 | + |
| 845 | + // Main beetle body |
| 846 | + fill(red(this.beetleColor), green(this.beetleColor), blue(this.beetleColor)) |
| 847 | + stroke(0) |
| 599 | 848 | strokeWeight(2) |
| 600 | | - line(-this.radius / 2, 0, -this.radius / 2 - 10, -10) |
| 601 | | - line(this.radius / 3, 0, this.radius / 3 + 8, -8) |
| 602 | | - line(0, 0, 5, -15) |
| 603 | | - |
| 604 | | - stroke(80, 50, 20, 100) |
| 849 | + ellipse(0, 0, this.radius * 1.6, this.radius * 2) |
| 850 | + |
| 851 | + // Shell split line |
| 852 | + stroke(0) |
| 605 | 853 | strokeWeight(1) |
| 606 | | - for (let i = -this.radius; i < this.radius; i += 5) { |
| 607 | | - line(i, -2, i + 2, 2) |
| 854 | + line(0, -this.radius, 0, this.radius) |
| 855 | + |
| 856 | + // Head |
| 857 | + fill(10) |
| 858 | + ellipse(0, -this.radius * 0.8, this.radius * 0.8, this.radius * 0.6) |
| 859 | + |
| 860 | + // Spots/pattern |
| 861 | + noStroke() |
| 862 | + fill(0, 0, 0, 80) |
| 863 | + ellipse(-this.radius * 0.3, 0, this.radius * 0.4) |
| 864 | + ellipse(this.radius * 0.3, -this.radius * 0.2, this.radius * 0.3) |
| 865 | + ellipse(this.radius * 0.2, this.radius * 0.4, this.radius * 0.35) |
| 866 | + ellipse(-this.radius * 0.25, this.radius * 0.3, this.radius * 0.25) |
| 867 | + |
| 868 | + // Legs |
| 869 | + stroke(0) |
| 870 | + strokeWeight(2) |
| 871 | + for (let i = 0; i < 3; i++) { |
| 872 | + let legY = -this.radius * 0.3 + i * this.radius * 0.3 |
| 873 | + let legMove = sin(this.wingPhase * 2 + i) * 2 |
| 874 | + line(-this.radius * 0.8, legY, -this.radius * 1.2 + legMove, legY + 5) |
| 875 | + line(this.radius * 0.8, legY, this.radius * 1.2 - legMove, legY + 5) |
| 608 | 876 | } |
| 609 | | - pop() |
| 610 | | - |
| 877 | + |
| 878 | + // Antennae |
| 879 | + strokeWeight(1) |
| 880 | + line(-3, -this.radius * 1.1, -8, -this.radius * 1.4) |
| 881 | + line(3, -this.radius * 1.1, 8, -this.radius * 1.4) |
| 882 | + |
| 883 | + // Eyes |
| 884 | + fill(255, 0, 0) |
| 611 | 885 | noStroke() |
| 612 | | - fill(255, 255, 255, 30) |
| 613 | | - ellipse(0, 0, this.radius * 2) |
| 886 | + ellipse(-5, -this.radius * 0.7, 4) |
| 887 | + ellipse(5, -this.radius * 0.7, 4) |
| 888 | + |
| 889 | + pop() |
| 890 | + |
| 614 | 891 | } else if (this.type === 'leaf') { |
| 892 | + // Original leaf code |
| 893 | + rotate(this.rotation) |
| 894 | + |
| 615 | 895 | if (gamePhase === 'NIGHT') { |
| 616 | 896 | fill(20, 40, 20) |
| 617 | 897 | stroke(10, 20, 10) |
@@ -646,6 +926,39 @@ class Obstacle { |
| 646 | 926 | line(0, 0, this.radius / 2, -this.radius / 2) |
| 647 | 927 | line(0, 0, -this.radius / 2, this.radius / 2) |
| 648 | 928 | line(0, 0, this.radius / 2, this.radius / 2) |
| 929 | + |
| 930 | + } else if (this.type === 'branch') { |
| 931 | + // Keep old branch code for backwards compatibility |
| 932 | + rotate(this.rotation) |
| 933 | + |
| 934 | + if (gamePhase === 'NIGHT') { |
| 935 | + stroke(40, 20, 0) |
| 936 | + fill(50, 25, 5) |
| 937 | + } else { |
| 938 | + stroke(101, 67, 33) |
| 939 | + fill(139, 90, 43) |
| 940 | + } |
| 941 | + strokeWeight(3) |
| 942 | + |
| 943 | + push() |
| 944 | + strokeWeight(this.radius / 3) |
| 945 | + line(-this.radius, 0, this.radius, 0) |
| 946 | + |
| 947 | + strokeWeight(2) |
| 948 | + line(-this.radius / 2, 0, -this.radius / 2 - 10, -10) |
| 949 | + line(this.radius / 3, 0, this.radius / 3 + 8, -8) |
| 950 | + line(0, 0, 5, -15) |
| 951 | + |
| 952 | + stroke(80, 50, 20, 100) |
| 953 | + strokeWeight(1) |
| 954 | + for (let i = -this.radius; i < this.radius; i += 5) { |
| 955 | + line(i, -2, i + 2, 2) |
| 956 | + } |
| 957 | + pop() |
| 958 | + |
| 959 | + noStroke() |
| 960 | + fill(255, 255, 255, 30) |
| 961 | + ellipse(0, 0, this.radius * 2) |
| 649 | 962 | } |
| 650 | 963 | |
| 651 | 964 | pop() |
@@ -734,4 +1047,4 @@ class Particle { |
| 734 | 1047 | isDead () { |
| 735 | 1048 | return this.lifetime <= 0 |
| 736 | 1049 | } |
| 737 | | -} |
| 1050 | +} |