zeroed-some/cob / aa1f028

Browse files

balloon redesign, obstacle distribution

Authored by espadonne
SHA
aa1f028f6b8310b9198278c2cd014a91d6488990
Parents
5bc00e6
Tree
9a9ae05

2 changed files

StatusFile+-
M js/entities.js 297 275
M js/game.js 182 112
js/entities.jsmodified
@@ -333,92 +333,104 @@ class Spider {
333333
     this.land()
334334
   }
335335
 
336
-  land() {
337
-        this.vel.mult(0);
338
-        this.isAirborne = false;
339
-        this.canJump = true;
340
-
341
-        // FIX: Check if we're actually landing on something valid
342
-        let landedOnSomething = false;
343
-        
344
-        // Check if on ground
345
-        if (this.pos.y >= height - this.radius - 5) {
346
-            landedOnSomething = true;
347
-        }
348
-        
349
-        // Check if on an obstacle
350
-        for (let obstacle of obstacles) {
351
-            if (this.checkObstacleCollision(obstacle)) {
352
-                landedOnSomething = true;
353
-                break;
354
-            }
355
-        }
356
-        
357
-        // Check if on a web strand
358
-        for (let strand of webStrands) {
359
-            if (strand !== currentStrand && !strand.broken && this.checkStrandCollision(strand)) {
360
-                landedOnSomething = true;
361
-                break;
362
-            }
363
-        }
364
-        
365
-        // Check if on home branch
366
-        if (window.homeBranch) {
367
-            let branch = window.homeBranch;
368
-            let branchStart = Math.min(branch.startX, branch.endX);
369
-            let branchEnd = Math.max(branch.startX, branch.endX);
370
-            
371
-            if (this.pos.x >= branchStart - 10 && this.pos.x <= branchEnd + 10) {
372
-                let t = (this.pos.x - branchStart) / (branchEnd - branchStart);
373
-                t = constrain(t, 0, 1);
374
-                let branchTopThickness = lerp(branch.thickness * 0.9, branch.thickness * 0.35, t);
375
-                let branchSurfaceY = branch.y - branchTopThickness;
376
-                let angleCorrection = (this.pos.x - branchStart) * branch.angle;
377
-                branchSurfaceY += angleCorrection;
378
-                
379
-                if (abs(this.pos.y - branchSurfaceY) < this.radius + 10) {
380
-                    landedOnSomething = true;
381
-                }
382
-            }
336
+  land () {
337
+    this.vel.mult(0)
338
+    this.isAirborne = false
339
+    this.canJump = true
340
+
341
+    // FIX: Check if we're actually landing on something valid
342
+    let landedOnSomething = false
343
+
344
+    // Check if on ground
345
+    if (this.pos.y >= height - this.radius - 5) {
346
+      landedOnSomething = true
347
+    }
348
+
349
+    // Check if on an obstacle
350
+    for (let obstacle of obstacles) {
351
+      if (this.checkObstacleCollision(obstacle)) {
352
+        landedOnSomething = true
353
+        break
354
+      }
355
+    }
356
+
357
+    // Check if on a web strand
358
+    for (let strand of webStrands) {
359
+      if (
360
+        strand !== currentStrand &&
361
+        !strand.broken &&
362
+        this.checkStrandCollision(strand)
363
+      ) {
364
+        landedOnSomething = true
365
+        break
366
+      }
367
+    }
368
+
369
+    // Check if on home branch
370
+    if (window.homeBranch) {
371
+      let branch = window.homeBranch
372
+      let branchStart = Math.min(branch.startX, branch.endX)
373
+      let branchEnd = Math.max(branch.startX, branch.endX)
374
+
375
+      if (this.pos.x >= branchStart - 10 && this.pos.x <= branchEnd + 10) {
376
+        let t = (this.pos.x - branchStart) / (branchEnd - branchStart)
377
+        t = constrain(t, 0, 1)
378
+        let branchTopThickness = lerp(
379
+          branch.thickness * 0.9,
380
+          branch.thickness * 0.35,
381
+          t
382
+        )
383
+        let branchSurfaceY = branch.y - branchTopThickness
384
+        let angleCorrection = (this.pos.x - branchStart) * branch.angle
385
+        branchSurfaceY += angleCorrection
386
+
387
+        if (abs(this.pos.y - branchSurfaceY) < this.radius + 10) {
388
+          landedOnSomething = true
383389
         }
384
-        
385
-        // FIX: If we're deploying web but didn't land on anything valid, destroy the web
386
-        if (currentStrand && isDeployingWeb && (spacePressed || touchHolding)) {
387
-            if (landedOnSomething) {
388
-                // Valid landing - finalize the web
389
-                currentStrand.end = this.pos.copy();
390
-                if (!currentStrand.path || currentStrand.path.length === 0) {
391
-                    currentStrand.path = [this.pos.copy()];
392
-                } else {
393
-                    currentStrand.path.push(this.pos.copy());
394
-                }
395
-                webNodes.push(new WebNode(this.pos.x, this.pos.y));
396
-            } else {
397
-                // Invalid landing in mid-air - destroy the web!
398
-                if (webStrands.length > 0 && webStrands[webStrands.length - 1] === currentStrand) {
399
-                    webStrands.pop(); // Remove the invalid strand
400
-                    
401
-                    // Create poof particles
402
-                    for (let i = 0; i < 8; i++) {
403
-                        let p = new Particle(this.pos.x, this.pos.y);
404
-                        p.color = color(255, 255, 255, 150);
405
-                        p.vel = createVector(random(-3, 3), random(-3, 3));
406
-                        p.size = 4;
407
-                        particles.push(p);
408
-                    }
409
-                    
410
-                    // Notification
411
-                    if (notifications.length < 3) {
412
-                        notifications.push(new Notification("Web needs anchor point!", color(255, 150, 150)));
413
-                    }
414
-                }
415
-            }
390
+      }
391
+    }
392
+
393
+    // FIX: If we're deploying web but didn't land on anything valid, destroy the web
394
+    if (currentStrand && isDeployingWeb && (spacePressed || touchHolding)) {
395
+      if (landedOnSomething) {
396
+        // Valid landing - finalize the web
397
+        currentStrand.end = this.pos.copy()
398
+        if (!currentStrand.path || currentStrand.path.length === 0) {
399
+          currentStrand.path = [this.pos.copy()]
400
+        } else {
401
+          currentStrand.path.push(this.pos.copy())
416402
         }
403
+        webNodes.push(new WebNode(this.pos.x, this.pos.y))
404
+      } else {
405
+        // Invalid landing in mid-air - destroy the web!
406
+        if (
407
+          webStrands.length > 0 &&
408
+          webStrands[webStrands.length - 1] === currentStrand
409
+        ) {
410
+          webStrands.pop() // Remove the invalid strand
417411
 
418
-        currentStrand = null;
419
-        isDeployingWeb = false;
412
+          // Create poof particles
413
+          for (let i = 0; i < 8; i++) {
414
+            let p = new Particle(this.pos.x, this.pos.y)
415
+            p.color = color(255, 255, 255, 150)
416
+            p.vel = createVector(random(-3, 3), random(-3, 3))
417
+            p.size = 4
418
+            particles.push(p)
419
+          }
420
+
421
+          // Notification
422
+          if (notifications.length < 3) {
423
+            notifications.push(
424
+              new Notification('Web needs anchor point!', color(255, 150, 150))
425
+            )
426
+          }
427
+        }
428
+      }
420429
     }
421430
 
431
+    currentStrand = null
432
+    isDeployingWeb = false
433
+  }
422434
 
423435
   display () {
424436
     push()
@@ -763,8 +775,7 @@ class Obstacle {
763775
         color(255, 200, 100) // Yellow
764776
       ]
765777
       this.balloonColor = random(this.balloonColors)
766
-      this.stringWave = 0
767
-      this.antLegPhase = random(TWO_PI)
778
+      // Remove complex properties - we don't need them for simple balloon
768779
     } else if (this.type === 'beetle') {
769780
       this.bobAmount = 4
770781
       this.driftSpeed = random(0.15, 0.35)
@@ -785,9 +796,6 @@ class Obstacle {
785796
         if (i === 0 || i === numPoints / 2) r = radius * 1.3
786797
         this.leafPoints.push({ angle: angle, radius: r })
787798
       }
788
-    } else if (this.type === 'branch') {
789
-      // Keep for backwards compatibility
790
-      this.bobAmount = 0
791799
     }
792800
   }
793801
 
@@ -847,13 +855,8 @@ class Obstacle {
847855
       if (this.driftDistance > 100) {
848856
         this.breakAttachedStrands()
849857
       }
850
-    }
851858
 
852
-    // Update animation phases
853
-    if (this.type === 'balloon') {
854
-      this.stringWave = sin(frameCount * 0.05 + this.bobOffset) * 0.1
855
-      this.antLegPhase += 0.1
856
-    } else if (this.type === 'beetle') {
859
+      // Update wing animation
857860
       this.wingPhase += 0.15
858861
     }
859862
 
@@ -981,212 +984,265 @@ class Obstacle {
981984
     translate(this.x, this.y)
982985
 
983986
     if (this.type === 'balloon') {
984
-      // Hot air balloon with canvas texture!
987
+      // ============================================
988
+      // HOT AIR BALLOON WITH CANVAS TEXTURE
989
+      // ============================================
985990
       push()
986991
 
987
-      // String/rope first (behind balloon)
988
-      stroke(80, 60, 40)
989
-      strokeWeight(1.5)
990
-      noFill()
991
-      beginShape()
992
-      for (let i = 0; i <= 10; i++) {
993
-        let t = i / 10
994
-        let stringX = sin(t * PI * 2 + this.stringWave) * 3
995
-        let stringY = t * 40 + this.radius
996
-        curveVertex(stringX, stringY)
997
-      }
998
-      endShape()
999
-
1000992
       // Balloon shadow
1001993
       noStroke()
1002994
       fill(0, 0, 0, 30)
1003
-      ellipse(5, 5, this.radius * 2.2, this.radius * 2.5)
995
+      ellipse(5, 5, this.radius * 2.1)
1004996
 
1005
-      // Main balloon with canvas panels
1006
-      push()
1007
-      // Draw vertical panels for that classic hot air balloon look
997
+      // Main balloon with canvas panel texture
998
+      // Draw vertical panels like a real hot air balloon
1008999
       let numPanels = 8
10091000
       for (let i = 0; i < numPanels; i++) {
1010
-        let angle1 = (TWO_PI / numPanels) * i
1011
-        let angle2 = (TWO_PI / numPanels) * (i + 1)
1001
+        push()
1002
+
1003
+        // Rotate for each panel
1004
+        rotate((TWO_PI / numPanels) * i)
10121005
 
1013
-        // Alternate panel colors for striped effect
1006
+        // Alternate panel colors for classic hot air balloon look
10141007
         if (i % 2 === 0) {
10151008
           fill(
10161009
             red(this.balloonColor),
10171010
             green(this.balloonColor),
1018
-            blue(this.balloonColor),
1019
-            200
1011
+            blue(this.balloonColor)
10201012
           )
10211013
         } else {
1014
+          // Slightly darker alternate panels
10221015
           fill(
1023
-            red(this.balloonColor) - 30,
1024
-            green(this.balloonColor) - 30,
1025
-            blue(this.balloonColor) - 30,
1026
-            200
1016
+            red(this.balloonColor) * 0.9,
1017
+            green(this.balloonColor) * 0.9,
1018
+            blue(this.balloonColor) * 0.9
10271019
           )
10281020
         }
10291021
 
1030
-        // Draw tapered panel (wider at middle, narrow at top/bottom)
1031
-        beginShape()
1032
-        // Top point
1033
-        vertex(0, -this.radius * 1.2)
1034
-        // Upper curve
1035
-        bezierVertex(
1036
-          cos(angle1) * this.radius * 0.3,
1037
-          -this.radius * 0.9,
1038
-          cos(angle1) * this.radius * 0.8,
1039
-          -this.radius * 0.3,
1040
-          cos(angle1) * this.radius * 1.1,
1041
-          0
1042
-        )
1043
-        // Lower curve to bottom
1044
-        bezierVertex(
1045
-          cos(angle1) * this.radius * 0.9,
1046
-          this.radius * 0.5,
1047
-          cos(angle1) * this.radius * 0.4,
1048
-          this.radius * 0.9,
1022
+        // Draw panel as pie slice
1023
+        noStroke()
1024
+        arc(
10491025
           0,
1050
-          this.radius * 1.1
1051
-        )
1052
-        // Back up the other side
1053
-        bezierVertex(
1054
-          cos(angle2) * this.radius * 0.4,
1055
-          this.radius * 0.9,
1056
-          cos(angle2) * this.radius * 0.9,
1057
-          this.radius * 0.5,
1058
-          cos(angle2) * this.radius * 1.1,
1059
-          0
1060
-        )
1061
-        bezierVertex(
1062
-          cos(angle2) * this.radius * 0.8,
1063
-          -this.radius * 0.3,
1064
-          cos(angle2) * this.radius * 0.3,
1065
-          -this.radius * 0.9,
10661026
           0,
1067
-          -this.radius * 1.2
1027
+          this.radius * 2,
1028
+          this.radius * 2,
1029
+          -PI / numPanels,
1030
+          PI / numPanels,
1031
+          PIE
10681032
         )
1069
-        endShape(CLOSE)
1033
+
1034
+        pop()
10701035
       }
10711036
 
1072
-      // Panel seams/ropes
1073
-      stroke(60, 40, 20, 100)
1074
-      strokeWeight(0.5)
1037
+      // Add panel seams (the ropes/stitching between panels)
1038
+      stroke(60, 40, 20, 110)
1039
+      strokeWeight(1)
10751040
       for (let i = 0; i < numPanels; i++) {
10761041
         let angle = (TWO_PI / numPanels) * i
1077
-        // Vertical seam lines
1078
-        beginShape()
1079
-        noFill()
1080
-        vertex(0, -this.radius * 1.2)
1081
-        bezierVertex(
1082
-          cos(angle) * this.radius * 0.3,
1083
-          -this.radius * 0.9,
1084
-          cos(angle) * this.radius * 0.8,
1085
-          -this.radius * 0.3,
1086
-          cos(angle) * this.radius * 1.1,
1087
-          0
1088
-        )
1089
-        bezierVertex(
1090
-          cos(angle) * this.radius * 0.9,
1091
-          this.radius * 0.5,
1092
-          cos(angle) * this.radius * 0.4,
1093
-          this.radius * 0.9,
1094
-          0,
1095
-          this.radius * 1.1
1096
-        )
1097
-        endShape()
1042
+        let x1 = cos(angle) * this.radius * 0.2
1043
+        let y1 = sin(angle) * this.radius * 0.2
1044
+        let x2 = cos(angle) * this.radius * 0.95
1045
+        let y2 = sin(angle) * this.radius * 0.95
1046
+        line(x1, y1, x2, y2)
10981047
       }
10991048
 
1100
-      // Highlight on balloon
1049
+      // Add circular reinforcement bands
1050
+      noFill()
1051
+      stroke(80, 50, 30, 80)
1052
+      strokeWeight(1.5)
1053
+      ellipse(0, 0, this.radius * 1.4)
1054
+      ellipse(0, 0, this.radius * 0.8)
1055
+
1056
+      // Matte fabric shading (subtle, non-glossy)
11011057
       noStroke()
1102
-      fill(255, 255, 255, 80)
1103
-      ellipse(
1104
-        -this.radius * 0.3,
1105
-        -this.radius * 0.5,
1106
-        this.radius * 0.6,
1107
-        this.radius * 0.7
1108
-      )
1109
-      pop()
1058
+      // Soft radial shading toward top-left to imply ambient light without specular shine
1059
+      for (let r = this.radius * 1.2; r > this.radius * 0.2; r -= this.radius * 0.15) {
1060
+        fill(255, 255, 255, 8) // very low alpha
1061
+        ellipse(-this.radius * 0.25, -this.radius * 0.35, r * 0.25, r * 0.18)
1062
+      }
1063
+      // Global matte overlay to reduce plastic look
1064
+      noStroke()
1065
+      fill(230, 210, 190, 18)
1066
+      ellipse(0, 0, this.radius * 2, this.radius * 2)
11101067
 
1111
-      // FLAME EFFECT!
1068
+      // Bottom opening of balloon (where flame goes)
1069
+      fill(40, 20, 10)
1070
+      ellipse(0, this.radius * 0.9, this.radius * 0.4, this.radius * 0.15)
1071
+
1072
+      // Support ropes from balloon to basket
1073
+      stroke(80, 60, 40)
1074
+      strokeWeight(2)
1075
+      // Four support ropes
1076
+      line(-this.radius * 0.3, this.radius * 0.85, -8, this.radius + 20)
1077
+      line(this.radius * 0.3, this.radius * 0.85, 8, this.radius + 20)
1078
+      line(-this.radius * 0.15, this.radius * 0.9, -4, this.radius + 20)
1079
+      line(this.radius * 0.15, this.radius * 0.9, 4, this.radius + 20)
1080
+
1081
+      // FLAME EFFECT (between balloon and basket)
11121082
       push()
1113
-      translate(0, this.radius - 5)
1083
+      translate(0, this.radius + 10)
1084
+
11141085
       // Flame glow
11151086
       noStroke()
1116
-      fill(255, 200, 0, 40 + sin(frameCount * 0.3) * 20)
1117
-      ellipse(0, 0, 25, 25)
1118
-      fill(255, 150, 0, 60 + sin(frameCount * 0.4) * 30)
1119
-      ellipse(0, 0, 15, 18)
1120
-      // Flame itself
1121
-      fill(255, 200, 0)
1087
+      fill(255, 200, 0, 30 + sin(frameCount * 0.2) * 20)
1088
+      ellipse(0, 0, 30, 30)
1089
+      fill(255, 150, 0, 50 + sin(frameCount * 0.3) * 30)
1090
+      ellipse(0, 0, 20, 25)
1091
+
1092
+      // Animated flame
11221093
       push()
1123
-      let flameHeight = 8 + sin(frameCount * 0.5) * 3
1124
-      translate(0, -2)
1094
+      let flameHeight = 12 + sin(frameCount * 0.4) * 4
1095
+      let flameWave = sin(frameCount * 0.3) * 2
1096
+
1097
+      // Outer flame (orange)
1098
+      fill(255, 150, 0)
11251099
       beginShape()
1126
-      vertex(-3, 0)
1100
+      vertex(-5, 5)
1101
+      bezierVertex(
1102
+        -5 + flameWave,
1103
+        -flameHeight * 0.5,
1104
+        -2 + flameWave,
1105
+        -flameHeight * 0.8,
1106
+        flameWave,
1107
+        -flameHeight
1108
+      )
11271109
       bezierVertex(
1128
-        -3,
1129
-        -flameHeight * 0.7,
1130
-        -1,
1131
-        -flameHeight,
1132
-        0,
1133
-        -flameHeight * 1.2
1110
+        2 + flameWave,
1111
+        -flameHeight * 0.8,
1112
+        5 + flameWave,
1113
+        -flameHeight * 0.5,
1114
+        5,
1115
+        5
11341116
       )
1135
-      bezierVertex(1, -flameHeight, 3, -flameHeight * 0.7, 3, 0)
11361117
       endShape(CLOSE)
1137
-      fill(255, 255, 200)
1138
-      ellipse(0, -flameHeight * 0.5, 3, 4)
1118
+
1119
+      // Inner flame (yellow/white)
1120
+      fill(255, 255, 150)
1121
+      beginShape()
1122
+      vertex(-2, 5)
1123
+      bezierVertex(
1124
+        -2 + flameWave * 0.5,
1125
+        -flameHeight * 0.3,
1126
+        -1 + flameWave * 0.5,
1127
+        -flameHeight * 0.5,
1128
+        flameWave * 0.5,
1129
+        -flameHeight * 0.7
1130
+      )
1131
+      bezierVertex(
1132
+        1 + flameWave * 0.5,
1133
+        -flameHeight * 0.5,
1134
+        2 + flameWave * 0.5,
1135
+        -flameHeight * 0.3,
1136
+        2,
1137
+        5
1138
+      )
1139
+      endShape(CLOSE)
1140
+
1141
+      // Flame tip (bright white)
1142
+      fill(255, 255, 255)
1143
+      ellipse(flameWave * 0.5, -flameHeight * 0.5, 3, 5)
11391144
       pop()
1145
+
11401146
       pop()
11411147
 
1142
-      // Basket
1148
+      // BIGGER, MORE DETAILED BASKET
11431149
       push()
1144
-      translate(0, this.radius + 10)
1150
+      translate(0, this.radius + 25)
1151
+
1152
+      // Basket shadow
1153
+      noStroke()
1154
+      fill(0, 0, 0, 20)
1155
+      rect(-11, 2, 22, 15, 2)
1156
+
1157
+      // Main basket - bigger to see ant better
11451158
       fill(101, 67, 33)
11461159
       stroke(80, 50, 20)
1147
-      strokeWeight(1)
1148
-      // Woven basket shape
1149
-      beginShape()
1150
-      vertex(-8, 0)
1151
-      vertex(8, 0)
1152
-      vertex(6, 10)
1153
-      vertex(-6, 10)
1154
-      endShape(CLOSE)
1155
-      // Basket weave pattern
1160
+      strokeWeight(1.5)
1161
+      rect(-10, 0, 20, 14, 2) // Bigger basket
1162
+
1163
+      // Woven basket texture
11561164
       stroke(80, 50, 20, 150)
1157
-      for (let i = -6; i < 6; i += 2) {
1158
-        line(i, 1, i, 9)
1165
+      strokeWeight(1)
1166
+      // Vertical weaves
1167
+      for (let i = -8; i < 8; i += 3) {
1168
+        line(i, 1, i, 13)
11591169
       }
1160
-      for (let i = 2; i < 9; i += 2) {
1161
-        line(-6, i, 6, i)
1170
+      // Horizontal weaves
1171
+      for (let i = 3; i < 12; i += 3) {
1172
+        line(-9, i, 9, i)
11621173
       }
1163
-      // Basket rim
1174
+
1175
+      // Basket rim (thicker, more pronounced)
11641176
       stroke(60, 40, 20)
1165
-      strokeWeight(1.5)
1166
-      line(-8, 0, 8, 0)
1177
+      strokeWeight(2)
1178
+      line(-10, 0, 10, 0)
1179
+
1180
+      // Corner reinforcements
1181
+      fill(80, 50, 20)
1182
+      noStroke()
1183
+      ellipse(-9, 0, 3)
1184
+      ellipse(9, 0, 3)
1185
+
11671186
       pop()
11681187
 
1169
-      // Ant in basket (peeking over edge)
1188
+      // DETAILED ANT PILOT (bigger, more visible)
11701189
       push()
1171
-      translate(0, this.radius + 12)
1190
+      translate(0, this.radius + 28)
1191
+
1192
+      // Ant body
11721193
       fill(20)
11731194
       noStroke()
1174
-      // Just ant head and antennae visible
1175
-      ellipse(0, -2, 6, 4) // Head peeking up
1195
+      ellipse(0, 0, 8, 5) // Thorax
1196
+      ellipse(0, -3, 6, 5) // Head
1197
+      ellipse(0, 3, 7, 6) // Abdomen
1198
+
1199
+      // Ant eyes
1200
+      fill(255, 100, 100)
1201
+      ellipse(-2, -3, 2)
1202
+      ellipse(2, -3, 2)
1203
+
11761204
       // Antennae
11771205
       stroke(20)
1178
-      strokeWeight(0.5)
1179
-      line(-1, -3, -3, -6)
1180
-      line(1, -3, 3, -6)
1181
-      // Tiny ant arms gripping basket edge
11821206
       strokeWeight(1)
1183
-      line(-3, 0, -4, 2)
1184
-      line(3, 0, 4, 2)
1207
+      line(-1, -5, -3, -8)
1208
+      line(1, -5, 3, -8)
1209
+
1210
+      // Little ant arms holding basket edge
1211
+      strokeWeight(1.5)
1212
+      line(-3, 0, -6, -3)
1213
+      line(3, 0, 6, -3)
1214
+
1215
+      // Ant legs visible over basket edge
1216
+      line(-2, 2, -4, 5)
1217
+      line(2, 2, 4, 5)
1218
+
1219
+      // Optional: Tiny pilot goggles
1220
+      stroke(100, 50, 0)
1221
+      strokeWeight(1)
1222
+      noFill()
1223
+      ellipse(-2, -3, 3)
1224
+      ellipse(2, -3, 3)
1225
+      line(-0.5, -3, 0.5, -3)
1226
+
1227
+      pop()
1228
+
1229
+      // Sandbags hanging from basket (optional detail)
1230
+      push()
1231
+      translate(0, this.radius + 25)
1232
+      fill(80, 60, 40)
1233
+      noStroke()
1234
+      ellipse(-12, 10, 4, 5)
1235
+      ellipse(12, 10, 4, 5)
1236
+      // Sandbag ropes
1237
+      stroke(60, 40, 20)
1238
+      strokeWeight(0.5)
1239
+      line(-10, 7, -12, 10)
1240
+      line(10, 7, 12, 10)
11851241
       pop()
11861242
 
11871243
       pop()
11881244
     } else if (this.type === 'beetle') {
1189
-      // Big floating beetle!
1245
+      // Big floating beetle! (KEEP EXISTING CODE)
11901246
       push()
11911247
       rotate(this.rotation)
11921248
 
@@ -1264,11 +1320,9 @@ class Obstacle {
12641320
       ellipse(this.radius * 0.2, this.radius * 0.4, this.radius * 0.35)
12651321
       ellipse(-this.radius * 0.25, this.radius * 0.3, this.radius * 0.25)
12661322
 
1267
-      // No legs - they're flying!
1268
-      // Just small leg stubs tucked under the body
1323
+      // Tiny tucked legs
12691324
       stroke(0)
12701325
       strokeWeight(1)
1271
-      // Tiny tucked legs
12721326
       line(
12731327
         -this.radius * 0.5,
12741328
         -this.radius * 0.2,
@@ -1299,7 +1353,7 @@ class Obstacle {
12991353
       line(-3, -this.radius * 1.1, -8, -this.radius * 1.4)
13001354
       line(3, -this.radius * 1.1, 8, -this.radius * 1.4)
13011355
 
1302
-      // Eyes (bigger and more prominent)
1356
+      // Eyes
13031357
       fill(255, 0, 0)
13041358
       noStroke()
13051359
       ellipse(-5, -this.radius * 0.7, 5)
@@ -1311,7 +1365,7 @@ class Obstacle {
13111365
 
13121366
       pop()
13131367
     } else if (this.type === 'leaf') {
1314
-      // Original leaf code
1368
+      // Original leaf code (KEEP EXISTING)
13151369
       rotate(this.rotation)
13161370
 
13171371
       if (gamePhase === 'NIGHT') {
@@ -1348,38 +1402,6 @@ class Obstacle {
13481402
       line(0, 0, this.radius / 2, -this.radius / 2)
13491403
       line(0, 0, -this.radius / 2, this.radius / 2)
13501404
       line(0, 0, this.radius / 2, this.radius / 2)
1351
-    } else if (this.type === 'branch') {
1352
-      // Keep old branch code for backwards compatibility
1353
-      rotate(this.rotation)
1354
-
1355
-      if (gamePhase === 'NIGHT') {
1356
-        stroke(40, 20, 0)
1357
-        fill(50, 25, 5)
1358
-      } else {
1359
-        stroke(101, 67, 33)
1360
-        fill(139, 90, 43)
1361
-      }
1362
-      strokeWeight(3)
1363
-
1364
-      push()
1365
-      strokeWeight(this.radius / 3)
1366
-      line(-this.radius, 0, this.radius, 0)
1367
-
1368
-      strokeWeight(2)
1369
-      line(-this.radius / 2, 0, -this.radius / 2 - 10, -10)
1370
-      line(this.radius / 3, 0, this.radius / 3 + 8, -8)
1371
-      line(0, 0, 5, -15)
1372
-
1373
-      stroke(80, 50, 20, 100)
1374
-      strokeWeight(1)
1375
-      for (let i = -this.radius; i < this.radius; i += 5) {
1376
-        line(i, -2, i + 2, 2)
1377
-      }
1378
-      pop()
1379
-
1380
-      noStroke()
1381
-      fill(255, 255, 255, 30)
1382
-      ellipse(0, 0, this.radius * 2)
13831405
     }
13841406
 
13851407
     pop()
js/game.jsmodified
@@ -473,23 +473,27 @@ function setup () {
473473
   let numObstacles = Math.floor((width * height) / 60000) // More obstacles
474474
   numObstacles = constrain(numObstacles, 15, 25)
475475
 
476
-  // Create ant balloons (4-6)
477
-  let numBalloons = Math.floor(random(4, 7))
476
+  // Create ant balloons
477
+  let numBalloons = Math.floor(random(11, 15))
478478
   for (let i = 0; i < numBalloons; i++) {
479479
     let attempts = 0
480480
     let placed = false
481481
 
482482
     while (!placed && attempts < 30) {
483
-      let x = random(80, width - 80)
484
-      let y = random(60, height * 0.55) // Balloons float in upper half
485
-      let radius = random(35, 45) // Good size for hopping
483
+      // Distribute balloons more evenly across the screen
484
+      let gridX = (i % 3) * (width / 3) + random(50, width / 3 - 50)
485
+      let gridY = Math.floor(i / 3) * (height / 4) + random(40, height / 4)
486
+
487
+      let x = constrain(gridX, 80, width - 80)
488
+      let y = constrain(gridY, 60, height * 0.6) // Balloons in upper 60% of screen
489
+      let radius = random(35, 50) // Varied sizes for visual interest
486490
 
487491
       let valid = true
488492
       // Check distance from other obstacles
489493
       for (let obstacle of obstacles) {
490494
         if (
491495
           dist(x, y, obstacle.x, obstacle.y) <
492
-          radius + obstacle.radius + 50
496
+          radius + obstacle.radius + 40
493497
         ) {
494498
           valid = false
495499
           break
@@ -499,7 +503,7 @@ function setup () {
499503
       // Check distance from home branch
500504
       if (valid && window.homeBranch) {
501505
         let branchY = window.homeBranch.y
502
-        if (Math.abs(y - branchY) < radius + 35) {
506
+        if (Math.abs(y - branchY) < radius + 40) {
503507
           valid = false
504508
         }
505509
       }
@@ -512,22 +516,27 @@ function setup () {
512516
     }
513517
   }
514518
 
515
-  // Create beetles (3-5)
516
-  let numBeetles = Math.floor(random(3, 6))
519
+  // Create beetles
520
+  let numBeetles = Math.floor(random(8, 11))
517521
   for (let i = 0; i < numBeetles; i++) {
518522
     let attempts = 0
519523
     let placed = false
520524
 
521525
     while (!placed && attempts < 30) {
522
-      let x = random(70, width - 70)
523
-      let y = random(height * 0.2, height * 0.8) // Spread throughout middle/lower
524
-      let radius = random(30, 40)
526
+      // Beetles spread throughout middle and lower areas
527
+      let gridX = (i % 3) * (width / 3) + random(60, width / 3 - 60)
528
+      let gridY =
529
+        height * 0.3 + Math.floor(i / 3) * (height * 0.25) + random(-30, 30)
530
+
531
+      let x = constrain(gridX, 70, width - 70)
532
+      let y = constrain(gridY, height * 0.2, height * 0.85)
533
+      let radius = random(28, 42) // Varied beetle sizes
525534
 
526535
       let valid = true
527536
       for (let obstacle of obstacles) {
528537
         if (
529538
           dist(x, y, obstacle.x, obstacle.y) <
530
-          radius + obstacle.radius + 45
539
+          radius + obstacle.radius + 35
531540
         ) {
532541
           valid = false
533542
           break
@@ -550,25 +559,34 @@ function setup () {
550559
     }
551560
   }
552561
 
553
-  // Create LOTS of leaves (8-12) for excellent hopping coverage
554
-  let numLeaves = Math.floor(random(8, 13))
562
+  // Create LESS leaves. they're unrealistic!
563
+  let numLeaves = Math.floor(random(4, 7))
555564
   for (let i = 0; i < numLeaves; i++) {
556565
     let attempts = 0
557566
     let placed = false
558567
 
559568
     while (!placed && attempts < 30) {
560
-      // Distribute leaves more evenly across the screen
561
-      let gridX = (i % 4) * (width / 4) + random(50, width / 4 - 50)
562
-      let gridY = Math.floor(i / 4) * (height / 3) + random(50, height / 3 - 50)
563
-      let x = constrain(gridX, 50, width - 50)
564
-      let y = constrain(gridY, 60, height - 100)
565
-      let radius = random(20, 30)
569
+      // Place leaves strategically to fill gaps
570
+      let x, y
571
+
572
+      // Try to place leaves in areas not covered by balloons/beetles
573
+      if (i < 2) {
574
+        // Place some near edges for web anchoring
575
+        x = random() < 0.5 ? random(30, 100) : random(width - 100, width - 30)
576
+        y = random(height * 0.3, height * 0.7)
577
+      } else {
578
+        // Fill gaps in the middle
579
+        x = random(100, width - 100)
580
+        y = random(height * 0.4, height - 100)
581
+      }
582
+
583
+      let radius = random(22, 32) // Leaves stay relatively small
566584
 
567585
       let valid = true
568586
       for (let obstacle of obstacles) {
569587
         if (
570588
           dist(x, y, obstacle.x, obstacle.y) <
571
-          radius + obstacle.radius + 35
589
+          radius + obstacle.radius + 30
572590
         ) {
573591
           valid = false
574592
           break
@@ -583,45 +601,73 @@ function setup () {
583601
     }
584602
   }
585603
 
586
-  // Add even more guaranteed coverage points (smaller leaves)
587
-  // Corners
588
-  obstacles.push(new Obstacle(50, 80, 18, 'leaf'))
589
-  obstacles.push(new Obstacle(width - 50, 80, 18, 'leaf'))
590
-  obstacles.push(new Obstacle(50, height - 100, 18, 'leaf'))
591
-  obstacles.push(new Obstacle(width - 50, height - 100, 18, 'leaf'))
592
-
593
-  // Edge midpoints
594
-  obstacles.push(new Obstacle(35, height / 3, 18, 'leaf'))
595
-  obstacles.push(new Obstacle(35, (2 * height) / 3, 18, 'leaf'))
596
-  obstacles.push(new Obstacle(width - 35, height / 3, 18, 'leaf'))
597
-  obstacles.push(new Obstacle(width - 35, (2 * height) / 3, 18, 'leaf'))
598
-  obstacles.push(new Obstacle(width / 3, 45, 18, 'leaf'))
599
-  obstacles.push(new Obstacle((2 * width) / 3, 45, 18, 'leaf'))
600
-  obstacles.push(new Obstacle(width / 3, height - 90, 18, 'leaf'))
601
-  obstacles.push(new Obstacle((2 * width) / 3, height - 90, 18, 'leaf'))
602
-
603
-  // Fill any remaining gaps for smooth hopping
604
-  if (width > 600) {
605
-    // Create a grid of small leaves to ensure no dead zones
606
-    for (let x = width / 5; x < width; x += width / 5) {
607
-      for (let y = height / 4; y < height - 100; y += height / 4) {
608
-        // Check if there's already an obstacle nearby
609
-        let needsLeaf = true
610
-        for (let obstacle of obstacles) {
611
-          if (dist(x, y, obstacle.x, obstacle.y) < 80) {
612
-            needsLeaf = false
613
-            break
614
-          }
615
-        }
616
-        if (needsLeaf) {
617
-          obstacles.push(
618
-            new Obstacle(x + random(-20, 20), y + random(-20, 20), 15, 'leaf')
619
-          )
604
+  let anchorPoints = [
605
+    { x: 50, y: height * 0.25 },
606
+    { x: width - 50, y: height * 0.25 },
607
+    { x: 50, y: height * 0.75 },
608
+    { x: width - 50, y: height * 0.75 },
609
+    { x: width * 0.5, y: 50 },
610
+    { x: width * 0.5, y: height - 80 }
611
+  ]
612
+
613
+  for (let point of anchorPoints) {
614
+    // Check if there's already an obstacle nearby
615
+    let needsAnchor = true
616
+    for (let obstacle of obstacles) {
617
+      if (dist(point.x, point.y, obstacle.x, obstacle.y) < 60) {
618
+        needsAnchor = false
619
+        break
620
+      }
621
+    }
622
+
623
+    if (needsAnchor) {
624
+      obstacles.push(
625
+        new Obstacle(
626
+          point.x + random(-15, 15),
627
+          point.y + random(-15, 15),
628
+          18,
629
+          'leaf'
630
+        )
631
+      )
632
+    }
633
+  }
634
+
635
+  if (random() < 0.5) {
636
+    let attempts = 0
637
+    let placed = false
638
+
639
+    while (!placed && attempts < 20) {
640
+      let x = random(width * 0.3, width * 0.7)
641
+      let y = random(height * 0.2, height * 0.4)
642
+      let radius = random(55, 65) // Extra large balloon
643
+
644
+      let valid = true
645
+      for (let obstacle of obstacles) {
646
+        if (
647
+          dist(x, y, obstacle.x, obstacle.y) <
648
+          radius + obstacle.radius + 60
649
+        ) {
650
+          valid = false
651
+          break
620652
         }
621653
       }
654
+
655
+      if (valid) {
656
+        obstacles.push(new Obstacle(x, y, radius, 'balloon'))
657
+        placed = true
658
+      }
659
+      attempts++
622660
     }
623661
   }
624662
 
663
+  // Debug: Log obstacle distribution
664
+  let balloonCount = obstacles.filter(o => o.type === 'balloon').length
665
+  let beetleCount = obstacles.filter(o => o.type === 'beetle').length
666
+  let leafCount = obstacles.filter(o => o.type === 'leaf').length
667
+  console.log(
668
+    `Obstacles created - Balloons: ${balloonCount}, Beetles: ${beetleCount}, Leaves: ${leafCount}`
669
+  )
670
+
625671
   // Spawn initial food boxes
626672
   let numBoxes = Math.max(3, Math.floor(width / 400))
627673
   for (let i = 0; i < numBoxes; i++) {
@@ -2764,32 +2810,35 @@ function keyPressed () {
27642810
   }
27652811
 }
27662812
 
2767
-function keyReleased() {
2768
-    if (key === ' ') {
2769
-        spacePressed = false;
2770
-        
2771
-        // FIX: Check if web is floating when released
2772
-        if (isDeployingWeb && currentStrand && spider.isAirborne) {
2773
-            // Spider is still airborne - this would create a floating web
2774
-            // Remove the incomplete strand
2775
-            if (webStrands.length > 0 && webStrands[webStrands.length - 1] === currentStrand) {
2776
-                webStrands.pop();
2777
-                
2778
-                // Poof effect
2779
-                for (let i = 0; i < 5; i++) {
2780
-                    let p = new Particle(spider.pos.x, spider.pos.y);
2781
-                    p.color = color(255, 200, 200, 100);
2782
-                    p.vel = createVector(random(-2, 2), random(-2, 2));
2783
-                    p.size = 3;
2784
-                    particles.push(p);
2785
-                }
2786
-            }
2813
+function keyReleased () {
2814
+  if (key === ' ') {
2815
+    spacePressed = false
2816
+
2817
+    // FIX: Check if web is floating when released
2818
+    if (isDeployingWeb && currentStrand && spider.isAirborne) {
2819
+      // Spider is still airborne - this would create a floating web
2820
+      // Remove the incomplete strand
2821
+      if (
2822
+        webStrands.length > 0 &&
2823
+        webStrands[webStrands.length - 1] === currentStrand
2824
+      ) {
2825
+        webStrands.pop()
2826
+
2827
+        // Poof effect
2828
+        for (let i = 0; i < 5; i++) {
2829
+          let p = new Particle(spider.pos.x, spider.pos.y)
2830
+          p.color = color(255, 200, 200, 100)
2831
+          p.vel = createVector(random(-2, 2), random(-2, 2))
2832
+          p.size = 3
2833
+          particles.push(p)
27872834
         }
2788
-        
2789
-        isDeployingWeb = false;
2790
-        currentStrand = null;
2791
-        return false;
2835
+      }
27922836
     }
2837
+
2838
+    isDeployingWeb = false
2839
+    currentStrand = null
2840
+    return false
2841
+  }
27932842
 }
27942843
 
27952844
 function mousePressed () {
@@ -2939,39 +2988,60 @@ function touchMoved () {
29392988
   return false // Prevent default
29402989
 }
29412990
 
2942
-function touchEnded() {
2943
-    touchHolding = false;
2944
-    touchProcessing = false;
2945
-    
2946
-    // Power jump handling...
2947
-    if (chargingJump && !spider.isAirborne) {
2948
-        // ... existing power jump code ...
2949
-    }
2950
-    chargingJump = false;
2951
-    jumpChargeTime = 0;
2952
-    
2953
-    // FIX: Check if web is floating when touch released
2954
-    if (isDeployingWeb && currentStrand && spider.isAirborne) {
2955
-        // Spider is still airborne - this would create a floating web
2956
-        // Remove the incomplete strand
2957
-        if (webStrands.length > 0 && webStrands[webStrands.length - 1] === currentStrand) {
2958
-            webStrands.pop();
2959
-            
2960
-            // Poof effect
2961
-            for (let i = 0; i < 5; i++) {
2962
-                let p = new Particle(spider.pos.x, spider.pos.y);
2963
-                p.color = color(255, 200, 200, 100);
2964
-                p.vel = createVector(random(-2, 2), random(-2, 2));
2965
-                p.size = 3;
2966
-                particles.push(p);
2967
-            }
2968
-        }
2991
+function touchEnded () {
2992
+  touchHolding = false
2993
+  touchProcessing = false
2994
+
2995
+  // PHASE 3: Power Jump - release charged jump
2996
+  if (chargingJump && !spider.isAirborne) {
2997
+    let chargeRatio = min(jumpChargeTime / maxJumpCharge, 1)
2998
+    let chargeMultiplier = 1 + chargeRatio // 1x to 2x multiplier
2999
+    spider.jumpChargeVisual = 0
3000
+
3001
+    // Use touch position if available, otherwise use last known position
3002
+    let targetX = touches.length > 0 ? touches[0].x : touchStartX
3003
+    let targetY = touches.length > 0 ? touches[0].y : touchStartY
3004
+    spider.jump(targetX, targetY, chargeMultiplier)
3005
+
3006
+    // Create charge release particles
3007
+    if (chargeRatio > 0.5) {
3008
+      for (let i = 0; i < 10; i++) {
3009
+        let p = new Particle(spider.pos.x, spider.pos.y)
3010
+        p.color = color(255, 255, 100)
3011
+        p.vel = createVector(random(-3, 3), random(-1, 2))
3012
+        p.size = 5
3013
+        particles.push(p)
3014
+      }
29693015
     }
2970
-    
2971
-    isDeployingWeb = false;
2972
-    currentStrand = null;
2973
-    
2974
-    return false;
3016
+  }
3017
+  chargingJump = false
3018
+  jumpChargeTime = 0
3019
+
3020
+  // FIX: Check if web is floating when touch released
3021
+  if (isDeployingWeb && currentStrand && spider.isAirborne) {
3022
+    // Spider is still airborne - this would create a floating web
3023
+    // Remove the incomplete strand
3024
+    if (
3025
+      webStrands.length > 0 &&
3026
+      webStrands[webStrands.length - 1] === currentStrand
3027
+    ) {
3028
+      webStrands.pop()
3029
+
3030
+      // Poof effect
3031
+      for (let i = 0; i < 5; i++) {
3032
+        let p = new Particle(spider.pos.x, spider.pos.y)
3033
+        p.color = color(255, 200, 200, 100)
3034
+        p.vel = createVector(random(-2, 2), random(-2, 2))
3035
+        p.size = 3
3036
+        particles.push(p)
3037
+      }
3038
+    }
3039
+  }
3040
+
3041
+  isDeployingWeb = false
3042
+  currentStrand = null
3043
+
3044
+  return false
29753045
 }
29763046
 
29773047
 function windowResized () {