zeroed-some/cob / 5fa41f1

Browse files

make it quirky (more creative obstacles)

Authored by espadonne
SHA
5fa41f170bc27c2a334db22437aca4c397bc60e4
Parents
9d78b64
Tree
f4fc13f

2 changed files

StatusFile+-
M js/entities.js 397 84
M js/game.js 103 59
js/entities.jsmodified
@@ -559,14 +559,46 @@ class Fly {
559559
 
560560
 class Obstacle {
561561
   constructor (x, y, radius, type) {
562
+    // Store original position for drift tracking
563
+    this.originalX = x
564
+    this.originalY = y
562565
     this.x = x
563566
     this.y = y
564567
     this.radius = radius
565
-    this.type = type || (random() < 0.5 ? 'branch' : 'leaf')
568
+    this.type = type || 'leaf'
566569
     this.rotation = random(TWO_PI)
567570
     this.leafPoints = []
568571
     
569
-    if (this.type === 'leaf') {
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
570602
       let numPoints = 8
571603
       for (let i = 0; i < numPoints; i++) {
572604
         let angle = (TWO_PI / numPoints) * i
@@ -574,44 +606,292 @@ class Obstacle {
574606
         if (i === 0 || i === numPoints / 2) r = radius * 1.3
575607
         this.leafPoints.push({ angle: angle, radius: r })
576608
       }
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
+      }
577741
     }
578742
   }
579743
 
580744
   display () {
581745
     push()
582746
     translate(this.x, this.y)
583
-    rotate(this.rotation)
584747
     
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)
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)
762
+      }
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)
591782
       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)
592817
       }
593
-      strokeWeight(3)
818
+      // Antennae
819
+      line(-1, -1, -3, -3)
820
+      line(1, -1, 3, -3)
821
+      
822
+      pop()
594823
       
824
+    } else if (this.type === 'beetle') {
825
+      // Big beetle!
595826
       push()
596
-      strokeWeight(this.radius / 3)
597
-      line(-this.radius, 0, this.radius, 0)
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
+      }
598844
       
845
+      // Main beetle body
846
+      fill(red(this.beetleColor), green(this.beetleColor), blue(this.beetleColor))
847
+      stroke(0)
599848
       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)
849
+      ellipse(0, 0, this.radius * 1.6, this.radius * 2)
603850
       
604
-      stroke(80, 50, 20, 100)
851
+      // Shell split line
852
+      stroke(0)
605853
       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)
608876
       }
609
-      pop()
610877
       
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)
611885
       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
+      
614891
     } else if (this.type === 'leaf') {
892
+      // Original leaf code
893
+      rotate(this.rotation)
894
+      
615895
       if (gamePhase === 'NIGHT') {
616896
         fill(20, 40, 20)
617897
         stroke(10, 20, 10)
@@ -646,6 +926,39 @@ class Obstacle {
646926
       line(0, 0, this.radius / 2, -this.radius / 2)
647927
       line(0, 0, -this.radius / 2, this.radius / 2)
648928
       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)
649962
     }
650963
 
651964
     pop()
js/game.jsmodified
@@ -114,77 +114,120 @@ function setup() {
114114
             homeBranchLength * t : 
115115
             width - homeBranchLength * t;
116116
         let y = homeBranchY + sin(t * PI) * 10; // Slight curve
117
-        obstacles.push(new Obstacle(x, y, 20, 'branch'));
117
+        obstacles.push(new Obstacle(x, y, 20, 'leaf')); // Use leaf as invisible anchor
118118
     }
119119
     
120
-    // Create obstacles with better distribution
121
-    let numObstacles = Math.floor((width * height) / 60000);
122
-    numObstacles = constrain(numObstacles, 10, 25);
120
+    // Create fewer, bigger, quirkier obstacles
121
+    let numObstacles = Math.floor((width * height) / 120000); // Much fewer
122
+    numObstacles = constrain(numObstacles, 8, 15);
123123
     
124
-    // Divide screen into zones for better distribution
125
-    let zones = [
126
-        { minY: 50, maxY: height * 0.3 },        // Top zone
127
-        { minY: height * 0.3, maxY: height * 0.6 }, // Middle zone
128
-        { minY: height * 0.6, maxY: height - 100 }  // Bottom zone
129
-    ];
124
+    // Create ant balloons (2-3)
125
+    let numBalloons = Math.floor(random(2, 4));
126
+    for (let i = 0; i < numBalloons; i++) {
127
+        let attempts = 0;
128
+        let placed = false;
130129
         
131
-    let obstaclesPerZone = Math.ceil(numObstacles / 3);
130
+        while (!placed && attempts < 30) {
131
+            let x = random(120, width - 120);
132
+            let y = random(80, height * 0.5); // Balloons float in upper half
133
+            let radius = random(40, 55); // Bigger
132134
             
133
-    for (let zone of zones) {
134
-        for (let i = 0; i < obstaclesPerZone; i++) {
135
+            let valid = true;
136
+            // Check distance from other obstacles
137
+            for (let obstacle of obstacles) {
138
+                if (dist(x, y, obstacle.x, obstacle.y) < radius + obstacle.radius + 80) {
139
+                    valid = false;
140
+                    break;
141
+                }
142
+            }
143
+            
144
+            // Check distance from home branch
145
+            if (valid && window.homeBranch) {
146
+                let branchY = window.homeBranch.y;
147
+                if (Math.abs(y - branchY) < radius + 50) {
148
+                    valid = false;
149
+                }
150
+            }
151
+            
152
+            if (valid) {
153
+                obstacles.push(new Obstacle(x, y, radius, 'balloon'));
154
+                placed = true;
155
+            }
156
+            attempts++;
157
+        }
158
+    }
159
+    
160
+    // Create beetles (2-3)
161
+    let numBeetles = Math.floor(random(2, 4));
162
+    for (let i = 0; i < numBeetles; i++) {
135163
         let attempts = 0;
136164
         let placed = false;
137165
         
138
-            while (!placed && attempts < 20) {
139
-                let x = random(80, width - 80);
140
-                let y = random(zone.minY, zone.maxY);
141
-                let radius = random(25, 45);
142
-                let type = random() < 0.6 ? 'branch' : 'leaf';
166
+        while (!placed && attempts < 30) {
167
+            let x = random(100, width - 100);
168
+            let y = random(height * 0.3, height * 0.7); // Middle areas
169
+            let radius = random(35, 50);
143170
             
144171
             let valid = true;
145172
             for (let obstacle of obstacles) {
146
-                    if (dist(x, y, obstacle.x, obstacle.y) < radius + obstacle.radius + 40) {
173
+                if (dist(x, y, obstacle.x, obstacle.y) < radius + obstacle.radius + 70) {
147174
                     valid = false;
148175
                     break;
149176
                 }
150177
             }
151
-                // Avoid overlapping the home branch body (transform point into branch frame)
152
-                if (valid) {
153
-                    const ca = Math.cos(window.homeBranch.angle);
154
-                    const sa = Math.sin(window.homeBranch.angle);
155
-                    const relY = y - window.homeBranch.y; // translate to branch's local origin
156
-                    const xr = x * ca + relY * sa;        // rotate into branch frame
157
-                    const yr = -x * sa + relY * ca;
158
-                    const minX = Math.min(window.homeBranch.startX, window.homeBranch.endX) - radius - 8;
159
-                    const maxX = Math.max(window.homeBranch.startX, window.homeBranch.endX) + radius + 8;
160
-                    const halfThickness = window.homeBranch.thickness + radius + 6;
161
-                    if (xr >= minX && xr <= maxX && Math.abs(yr) <= halfThickness) {
162
-                        valid = false; // too close to the branch hull
178
+            
179
+            // Check distance from home branch
180
+            if (valid && window.homeBranch) {
181
+                let branchY = window.homeBranch.y;
182
+                if (Math.abs(y - branchY) < radius + 40) {
183
+                    valid = false;
163184
                 }
164185
             }
165186
             
166187
             if (valid) {
167
-                    obstacles.push(new Obstacle(x, y, radius, type));
188
+                obstacles.push(new Obstacle(x, y, radius, 'beetle'));
168189
                 placed = true;
169190
             }
170191
             attempts++;
171192
         }
172193
     }
194
+    
195
+    // Create leaves (3-4) for more stable anchor points
196
+    let numLeaves = Math.floor(random(3, 5));
197
+    for (let i = 0; i < numLeaves; i++) {
198
+        let attempts = 0;
199
+        let placed = false;
200
+        
201
+        while (!placed && attempts < 30) {
202
+            let x = random(80, width - 80);
203
+            let y = random(100, height - 150);
204
+            let radius = random(30, 40);
205
+            
206
+            let valid = true;
207
+            for (let obstacle of obstacles) {
208
+                if (dist(x, y, obstacle.x, obstacle.y) < radius + obstacle.radius + 60) {
209
+                    valid = false;
210
+                    break;
211
+                }
173212
             }
174213
             
175
-    // Add guaranteed anchor points with better bottom coverage
176
-    obstacles.push(new Obstacle(50, height/2, 35, 'branch'));
177
-    obstacles.push(new Obstacle(width - 50, height/2, 35, 'branch'));
178
-    obstacles.push(new Obstacle(width/2, 50, 40, 'leaf'));
214
+            if (valid) {
215
+                obstacles.push(new Obstacle(x, y, radius, 'leaf'));
216
+                placed = true;
217
+            }
218
+            attempts++;
219
+        }
220
+    }
179221
     
180
-    // More bottom anchors for reachability
181
-    obstacles.push(new Obstacle(width/4, height - 120, 35, 'leaf'));
182
-    obstacles.push(new Obstacle(3*width/4, height - 120, 35, 'branch'));
183
-    obstacles.push(new Obstacle(width/2, height - 150, 30, 'branch'));
222
+    // Add guaranteed edge anchor points (smaller, stable leaves)
223
+    obstacles.push(new Obstacle(50, height/2, 25, 'leaf'));
224
+    obstacles.push(new Obstacle(width - 50, height/2, 25, 'leaf'));
225
+    obstacles.push(new Obstacle(width/2, 60, 25, 'leaf'));
184226
     
185
-    if (width > 1200) {
186
-        obstacles.push(new Obstacle(width/3, height/3, 35, 'leaf'));
187
-        obstacles.push(new Obstacle(2*width/3, height/3, 35, 'branch'));
227
+    // Bottom anchors for better coverage
228
+    if (width > 1000) {
229
+        obstacles.push(new Obstacle(width/3, height - 130, 25, 'leaf'));
230
+        obstacles.push(new Obstacle(2*width/3, height - 130, 25, 'leaf'));
188231
     }
189232
     
190233
     // Spawn initial food boxes
@@ -224,8 +267,9 @@ function draw() {
224267
         drawMoon();
225268
     }
226269
     
227
-    // Display game objects
270
+    // Update and display game objects
228271
     for (let obstacle of obstacles) {
272
+        obstacle.update(); // Update movement and animations
229273
         obstacle.display();
230274
     }
231275