zeroed-some/cob / 827e77f

Browse files

webs drift with obstacles

Authored by espadonne
SHA
827e77f157f73f51e9ddd169c8b3f7b999e7a581
Parents
b588850
Tree
1a9a35e

3 changed files

StatusFile+-
M js/entities.js 149 130
M js/game.js 69 30
M js/physics.js 9 0
js/entities.jsmodified
@@ -350,126 +350,133 @@ class Spider {
350
     this.land()
350
     this.land()
351
   }
351
   }
352
 
352
 
353
-  land () {
353
+land() {
354
-    this.vel.mult(0)
354
+  this.vel.mult(0);
355
-    this.isAirborne = false
355
+  this.isAirborne = false;
356
-    this.canJump = true
356
+  this.canJump = true;
357
+
358
+  // FIX: Check if we're actually landing on something valid
359
+  let landedOnSomething = false;
360
+  let landingPoint = null; // Store where we're landing for anchor
361
+  let landingObstacle = null; // NEW: Track which obstacle we're landing on
362
+
363
+  // Check if on ground
364
+  if (this.pos.y >= height - this.radius - 5) {
365
+    landedOnSomething = true;
366
+    landingPoint = createVector(this.pos.x, height);
367
+  }
357
 
368
 
358
-    // FIX: Check if we're actually landing on something valid
369
+  // Check if on an obstacle
359
-    let landedOnSomething = false
370
+  if (!landedOnSomething) {
360
-    let landingPoint = null // Store where we're landing for anchor
371
+    for (let obstacle of obstacles) {
361
-
372
+      if (this.checkObstacleCollision(obstacle)) {
362
-    // Check if on ground
373
+        landedOnSomething = true;
363
-    if (this.pos.y >= height - this.radius - 5) {
374
+        landingObstacle = obstacle; // NEW: Store the obstacle
364
-      landedOnSomething = true
375
+        // Calculate edge point for anchor
365
-      landingPoint = createVector(this.pos.x, height)
376
+        let angle = atan2(this.pos.y - obstacle.y, this.pos.x - obstacle.x);
366
-    }
377
+        landingPoint = createVector(
367
-
378
+          obstacle.x + cos(angle) * obstacle.radius,
368
-    // Check if on an obstacle
379
+          obstacle.y + sin(angle) * obstacle.radius
369
-    if (!landedOnSomething) {
380
+        );
370
-      for (let obstacle of obstacles) {
381
+        break;
371
-        if (this.checkObstacleCollision(obstacle)) {
372
-          landedOnSomething = true
373
-          // Calculate edge point for anchor
374
-          let angle = atan2(this.pos.y - obstacle.y, this.pos.x - obstacle.x)
375
-          landingPoint = createVector(
376
-            obstacle.x + cos(angle) * obstacle.radius,
377
-            obstacle.y + sin(angle) * obstacle.radius
378
-          )
379
-          break
380
-        }
381
       }
382
       }
382
     }
383
     }
384
+  }
383
 
385
 
384
-    // Check if on a web strand
386
+  // Check if on a web strand
385
-    if (!landedOnSomething) {
387
+  if (!landedOnSomething) {
386
-      for (let strand of webStrands) {
388
+    for (let strand of webStrands) {
387
-        if (
389
+      if (strand !== currentStrand && !strand.broken && this.checkStrandCollision(strand)) {
388
-          strand !== currentStrand &&
390
+        landedOnSomething = true;
389
-          !strand.broken &&
391
+        // For web strands, use spider position as anchor
390
-          this.checkStrandCollision(strand)
392
+        landingPoint = this.pos.copy();
391
-        ) {
393
+        break;
392
-          landedOnSomething = true
393
-          // For web strands, use spider position as anchor
394
-          landingPoint = this.pos.copy()
395
-          break
396
-        }
397
       }
394
       }
398
     }
395
     }
396
+  }
399
 
397
 
400
-    // Check if on home branch
398
+  // Check if on home branch
401
-    if (!landedOnSomething && window.homeBranch) {
399
+  if (!landedOnSomething && window.homeBranch) {
402
-      let branch = window.homeBranch
400
+    let branch = window.homeBranch;
403
-      let branchStart = Math.min(branch.startX, branch.endX)
401
+    let branchStart = Math.min(branch.startX, branch.endX);
404
-      let branchEnd = Math.max(branch.startX, branch.endX)
402
+    let branchEnd = Math.max(branch.startX, branch.endX);
405
-
403
+
406
-      if (this.pos.x >= branchStart - 10 && this.pos.x <= branchEnd + 10) {
404
+    if (this.pos.x >= branchStart - 10 && this.pos.x <= branchEnd + 10) {
407
-        let t = (this.pos.x - branchStart) / (branchEnd - branchStart)
405
+      let t = (this.pos.x - branchStart) / (branchEnd - branchStart);
408
-        t = constrain(t, 0, 1)
406
+      t = constrain(t, 0, 1);
409
-        let branchTopThickness = lerp(
407
+      let branchTopThickness = lerp(branch.thickness * 0.9, branch.thickness * 0.35, t);
410
-          branch.thickness * 0.9,
408
+      let branchSurfaceY = branch.y - branchTopThickness;
411
-          branch.thickness * 0.35,
409
+      let angleCorrection = (this.pos.x - branchStart) * branch.angle;
412
-          t
410
+      branchSurfaceY += angleCorrection;
413
-        )
411
+
414
-        let branchSurfaceY = branch.y - branchTopThickness
412
+      if (abs(this.pos.y - branchSurfaceY) < this.radius + 10) {
415
-        let angleCorrection = (this.pos.x - branchStart) * branch.angle
413
+        landedOnSomething = true;
416
-        branchSurfaceY += angleCorrection
414
+        landingPoint = createVector(this.pos.x, branchSurfaceY);
417
-
418
-        if (abs(this.pos.y - branchSurfaceY) < this.radius + 10) {
419
-          landedOnSomething = true
420
-          landingPoint = createVector(this.pos.x, branchSurfaceY)
421
-        }
422
       }
415
       }
423
     }
416
     }
417
+  }
424
 
418
 
425
-    // FIX: If we're deploying web but didn't land on anything valid, destroy the web
419
+  // FIX: If we're deploying web but didn't land on anything valid, destroy the web
426
-    if (currentStrand && isDeployingWeb && (spacePressed || touchHolding)) {
420
+  if (currentStrand && isDeployingWeb && (spacePressed || touchHolding)) {
427
-      if (landedOnSomething && landingPoint) {
421
+    if (landedOnSomething && landingPoint) {
428
-        // Valid landing - finalize the web at the landing point
422
+      // Valid landing - finalize the web at the landing point
429
-        currentStrand.end = landingPoint.copy() // Use edge point, not spider center
423
+      currentStrand.end = landingPoint.copy(); // Use edge point, not spider center
430
-        if (!currentStrand.path || currentStrand.path.length === 0) {
424
+      
431
-          currentStrand.path = [landingPoint.copy()]
425
+      // NEW: Track obstacle attachment for the end point
432
-        } else {
426
+      if (landingObstacle) {
433
-          currentStrand.path.push(landingPoint.copy())
427
+        currentStrand.endObstacle = landingObstacle;
434
-        }
428
+        currentStrand.endAngle = atan2(
435
-        webNodes.push(new WebNode(landingPoint.x, landingPoint.y))
429
+          landingPoint.y - landingObstacle.y,
436
-
430
+          landingPoint.x - landingObstacle.x
437
-        // Update last anchor for next web
431
+        );
438
-        this.lastAnchorPoint = landingPoint.copy()
432
+      }
433
+      
434
+      if (!currentStrand.path || currentStrand.path.length === 0) {
435
+        currentStrand.path = [landingPoint.copy()];
439
       } else {
436
       } else {
440
-        // Invalid landing in mid-air - destroy the web!
437
+        currentStrand.path.push(landingPoint.copy());
441
-        if (
438
+      }
442
-          webStrands.length > 0 &&
439
+      
443
-          webStrands[webStrands.length - 1] === currentStrand
440
+      let newNode = new WebNode(landingPoint.x, landingPoint.y);
444
-        ) {
441
+      // NEW: Track node attachment
445
-          webStrands.pop() // Remove the invalid strand
442
+      if (landingObstacle) {
443
+        newNode.attachedObstacle = landingObstacle;
444
+        newNode.attachmentAngle = currentStrand.endAngle;
445
+      }
446
+      webNodes.push(newNode);
446
 
447
 
447
-          // Create poof particles
448
+      // Update last anchor for next web
448
-          for (let i = 0; i < 8; i++) {
449
+      this.lastAnchorPoint = landingPoint.copy();
449
-            let p = new Particle(this.pos.x, this.pos.y)
450
+    } else {
450
-            p.color = color(255, 255, 255, 150)
451
+      // Invalid landing in mid-air - destroy the web!
451
-            p.vel = createVector(random(-3, 3), random(-3, 3))
452
+      if (webStrands.length > 0 && webStrands[webStrands.length - 1] === currentStrand) {
452
-            p.size = 4
453
+        webStrands.pop(); // Remove the invalid strand
453
-            particles.push(p)
454
-          }
455
 
454
 
456
-          // Notification
455
+        // Create poof particles
457
-          if (notifications.length < 3) {
456
+        for (let i = 0; i < 8; i++) {
458
-            notifications.push(
457
+          let p = new Particle(this.pos.x, this.pos.y);
459
-              new Notification('Web needs anchor point!', color(255, 150, 150))
458
+          p.color = color(255, 255, 255, 150);
460
-            )
459
+          p.vel = createVector(random(-3, 3), random(-3, 3));
461
-          }
460
+          p.size = 4;
461
+          particles.push(p);
462
+        }
463
+
464
+        // Notification
465
+        if (notifications.length < 3) {
466
+          notifications.push(new Notification("Web needs anchor point!", color(255, 150, 150)));
462
         }
467
         }
463
       }
468
       }
464
-    } else if (landedOnSomething && landingPoint) {
465
-      // Update last anchor point even when not deploying web
466
-      this.lastAnchorPoint = landingPoint.copy()
467
     }
469
     }
468
-
470
+  } else if (landedOnSomething && landingPoint) {
469
-    currentStrand = null
471
+    // Update last anchor point even when not deploying web
470
-    isDeployingWeb = false
472
+    this.lastAnchorPoint = landingPoint.copy();
471
   }
473
   }
472
 
474
 
475
+  currentStrand = null;
476
+  isDeployingWeb = false;
477
+}
478
+
479
+
473
   display () {
480
   display () {
474
     push()
481
     push()
475
     translate(this.pos.x, this.pos.y)
482
     translate(this.pos.x, this.pos.y)
@@ -904,38 +911,50 @@ class Obstacle {
904
     }
911
     }
905
   }
912
   }
906
 
913
 
907
-  updateAttachedStrands () {
914
+updateAttachedStrands() {
908
-    // Update web strands that are connected to this obstacle
915
+  // Update web strands that are connected to this obstacle
909
-    for (let strand of webStrands) {
916
+  for (let strand of webStrands) {
910
-      // Check if strand starts near this obstacle's edge
917
+    if (!strand || strand.broken) continue;
911
-      let startDist = dist(strand.start.x, strand.start.y, this.x, this.y)
918
+    
912
-      if (startDist >= this.radius - 5 && startDist <= this.radius + 15) {
919
+    // Check if strand starts at this obstacle
913
-        // Strand is attached to edge - update to maintain edge connection
920
+    if (strand.startObstacle === this) {
914
-        let angle = atan2(strand.start.y - this.y, strand.start.x - this.x)
921
+      // Update the start position to maintain the attachment
915
-        strand.start.x = this.x + cos(angle) * this.radius
922
+      let angle = strand.startAngle; // Use stored angle
916
-        strand.start.y = this.y + sin(angle) * this.radius
923
+      strand.start.x = this.x + cos(angle) * this.radius;
917
-        if (strand.path && strand.path.length > 0) {
924
+      strand.start.y = this.y + sin(angle) * this.radius;
918
-          strand.path[0].x = strand.start.x
925
+      
919
-          strand.path[0].y = strand.start.y
926
+      // Update path if it exists
920
-        }
927
+      if (strand.path && strand.path.length > 0) {
928
+        strand.path[0].x = strand.start.x;
929
+        strand.path[0].y = strand.start.y;
921
       }
930
       }
922
-
931
+    }
923
-      // Check if strand ends near this obstacle's edge
932
+    
924
-      if (strand.end) {
933
+    // Check if strand ends at this obstacle
925
-        let endDist = dist(strand.end.x, strand.end.y, this.x, this.y)
934
+    if (strand.endObstacle === this) {
926
-        if (endDist >= this.radius - 5 && endDist <= this.radius + 15) {
935
+      // Update the end position to maintain the attachment
927
-          // Strand is attached to edge - update to maintain edge connection
936
+      let angle = strand.endAngle; // Use stored angle
928
-          let angle = atan2(strand.end.y - this.y, strand.end.x - this.x)
937
+      strand.end.x = this.x + cos(angle) * this.radius;
929
-          strand.end.x = this.x + cos(angle) * this.radius
938
+      strand.end.y = this.y + sin(angle) * this.radius;
930
-          strand.end.y = this.y + sin(angle) * this.radius
939
+      
931
-          if (strand.path && strand.path.length > 0) {
940
+      // Update path if it exists
932
-            strand.path[strand.path.length - 1].x = strand.end.x
941
+      if (strand.path && strand.path.length > 0) {
933
-            strand.path[strand.path.length - 1].y = strand.end.y
942
+        strand.path[strand.path.length - 1].x = strand.end.x;
934
-          }
943
+        strand.path[strand.path.length - 1].y = strand.end.y;
935
-        }
936
       }
944
       }
937
     }
945
     }
938
   }
946
   }
947
+  
948
+  // Also update web nodes attached to this obstacle
949
+  for (let node of webNodes) {
950
+    if (node.attachedObstacle === this) {
951
+      let angle = node.attachmentAngle;
952
+      node.x = this.x + cos(angle) * this.radius;
953
+      node.y = this.y + sin(angle) * this.radius;
954
+    }
955
+  }
956
+}
957
+
939
 
958
 
940
   breakAttachedStrands () {
959
   breakAttachedStrands () {
941
     // Check for strands attached to this obstacle's edge
960
     // Check for strands attached to this obstacle's edge
js/game.jsmodified
@@ -2509,29 +2509,50 @@ function updateResources () {
2509
   }
2509
   }
2510
 }
2510
 }
2511
 
2511
 
2512
-function handleWebDeployment () {
2512
+function handleWebDeployment() {
2513
   // Handle keyboard-based web deployment
2513
   // Handle keyboard-based web deployment
2514
   if (spacePressed && spider.isAirborne && !isDeployingWeb && webSilk > 10) {
2514
   if (spacePressed && spider.isAirborne && !isDeployingWeb && webSilk > 10) {
2515
-    isDeployingWeb = true
2515
+    isDeployingWeb = true;
2516
-    currentStrand = new WebStrand(spider.lastAnchorPoint.copy(), null)
2516
+    currentStrand = new WebStrand(spider.lastAnchorPoint.copy(), null);
2517
-    currentStrand.path = [spider.lastAnchorPoint.copy()]
2517
+    currentStrand.path = [spider.lastAnchorPoint.copy()];
2518
-    webStrands.push(currentStrand)
2518
+    
2519
-    webNodes.push(
2519
+    // NEW: Check if starting from an obstacle
2520
-      new WebNode(spider.lastAnchorPoint.x, spider.lastAnchorPoint.y)
2520
+    for (let obstacle of obstacles) {
2521
-    )
2521
+      let d = dist(spider.lastAnchorPoint.x, spider.lastAnchorPoint.y, obstacle.x, obstacle.y);
2522
+      // Check if anchor is on obstacle edge (within tolerance)
2523
+      if (abs(d - obstacle.radius) < 10) {
2524
+        currentStrand.startObstacle = obstacle;
2525
+        currentStrand.startAngle = atan2(
2526
+          spider.lastAnchorPoint.y - obstacle.y,
2527
+          spider.lastAnchorPoint.x - obstacle.x
2528
+        );
2529
+        break;
2530
+      }
2531
+    }
2532
+    
2533
+    webStrands.push(currentStrand);
2534
+    
2535
+    let newNode = new WebNode(spider.lastAnchorPoint.x, spider.lastAnchorPoint.y);
2536
+    // NEW: Track node attachment if on obstacle
2537
+    if (currentStrand.startObstacle) {
2538
+      newNode.attachedObstacle = currentStrand.startObstacle;
2539
+      newNode.attachmentAngle = currentStrand.startAngle;
2540
+    }
2541
+    webNodes.push(newNode);
2522
   }
2542
   }
2523
 
2543
 
2524
   // Update web for keyboard controls
2544
   // Update web for keyboard controls
2525
   if (currentStrand && isDeployingWeb && spider.isAirborne && spacePressed) {
2545
   if (currentStrand && isDeployingWeb && spider.isAirborne && spacePressed) {
2526
-    currentStrand.end = spider.pos.copy()
2546
+    currentStrand.end = spider.pos.copy();
2527
     if (frameCount % 2 === 0) {
2547
     if (frameCount % 2 === 0) {
2528
-      currentStrand.path.push(spider.pos.copy())
2548
+      currentStrand.path.push(spider.pos.copy());
2529
     }
2549
     }
2530
   }
2550
   }
2531
 
2551
 
2532
   // Touch-based web deployment is handled in touchMoved()
2552
   // Touch-based web deployment is handled in touchMoved()
2533
 }
2553
 }
2534
 
2554
 
2555
+
2535
 function updateUI () {
2556
 function updateUI () {
2536
   // Update control instructions based on device
2557
   // Update control instructions based on device
2537
   let isMobile =
2558
   let isMobile =
@@ -3002,40 +3023,58 @@ function recycleNearbyWeb () {
3002
   }
3023
   }
3003
 }
3024
 }
3004
 
3025
 
3005
-function touchStarted () {
3026
+function touchStarted() {
3006
   if (touches.length > 0) {
3027
   if (touches.length > 0) {
3007
-    touchStartTime = millis()
3028
+    touchStartTime = millis();
3008
-    touchStartX = touches[0].x
3029
+    touchStartX = touches[0].x;
3009
-    touchStartY = touches[0].y
3030
+    touchStartY = touches[0].y;
3010
 
3031
 
3011
     // Check for double tap on spider to munch
3032
     // Check for double tap on spider to munch
3012
-    let touchOnSpider =
3033
+    let touchOnSpider = dist(touches[0].x, touches[0].y, spider.pos.x, spider.pos.y) < 30;
3013
-      dist(touches[0].x, touches[0].y, spider.pos.x, spider.pos.y) < 30
3014
 
3034
 
3015
     if (touchOnSpider && millis() - lastTapTime < 300) {
3035
     if (touchOnSpider && millis() - lastTapTime < 300) {
3016
       // Double tap detected on spider - MUNCH!
3036
       // Double tap detected on spider - MUNCH!
3017
-      spider.munch()
3037
+      spider.munch();
3018
-      lastTapTime = 0 // Reset to prevent triple tap
3038
+      lastTapTime = 0; // Reset to prevent triple tap
3019
     } else if (!spider.isAirborne) {
3039
     } else if (!spider.isAirborne) {
3020
       // Single tap while on ground - jump
3040
       // Single tap while on ground - jump
3021
-      spider.jump(touches[0].x, touches[0].y)
3041
+      spider.jump(touches[0].x, touches[0].y);
3022
-      lastTapTime = millis()
3042
+      lastTapTime = millis();
3023
     } else if (spider.isAirborne && webSilk > 10 && !isDeployingWeb) {
3043
     } else if (spider.isAirborne && webSilk > 10 && !isDeployingWeb) {
3024
       // Start web deployment if airborne (only if not already deploying)
3044
       // Start web deployment if airborne (only if not already deploying)
3025
-      touchHolding = true
3045
+      touchHolding = true;
3026
-      isDeployingWeb = true
3046
+      isDeployingWeb = true;
3027
-      currentStrand = new WebStrand(spider.lastAnchorPoint.copy(), null)
3047
+      currentStrand = new WebStrand(spider.lastAnchorPoint.copy(), null);
3028
-      currentStrand.path = [spider.lastAnchorPoint.copy()]
3048
+      currentStrand.path = [spider.lastAnchorPoint.copy()];
3029
-      webStrands.push(currentStrand)
3049
+      
3030
-      webNodes.push(
3050
+      for (let obstacle of obstacles) {
3031
-        new WebNode(spider.lastAnchorPoint.x, spider.lastAnchorPoint.y)
3051
+        let d = dist(spider.lastAnchorPoint.x, spider.lastAnchorPoint.y, obstacle.x, obstacle.y);
3032
-      )
3052
+        // Check if anchor is on obstacle edge (within tolerance)
3053
+        if (abs(d - obstacle.radius) < 10) {
3054
+          currentStrand.startObstacle = obstacle;
3055
+          currentStrand.startAngle = atan2(
3056
+            spider.lastAnchorPoint.y - obstacle.y,
3057
+            spider.lastAnchorPoint.x - obstacle.x
3058
+          );
3059
+          break;
3060
+        }
3061
+      }
3062
+      
3063
+      webStrands.push(currentStrand);
3064
+      
3065
+      let newNode = new WebNode(spider.lastAnchorPoint.x, spider.lastAnchorPoint.y);
3066
+      // NEW: Track node attachment if on obstacle
3067
+      if (currentStrand.startObstacle) {
3068
+        newNode.attachedObstacle = currentStrand.startObstacle;
3069
+        newNode.attachmentAngle = currentStrand.startAngle;
3070
+      }
3071
+      webNodes.push(newNode);
3033
     } else if (spider.isAirborne && isDeployingWeb) {
3072
     } else if (spider.isAirborne && isDeployingWeb) {
3034
       // If already deploying and user taps again, just continue (don't create new strand)
3073
       // If already deploying and user taps again, just continue (don't create new strand)
3035
-      touchHolding = true
3074
+      touchHolding = true;
3036
     }
3075
     }
3037
   }
3076
   }
3038
-  return false // Prevent default
3077
+  return false; // Prevent default
3039
 }
3078
 }
3040
 
3079
 
3041
 function touchMoved () {
3080
 function touchMoved () {
js/physics.jsmodified
@@ -16,6 +16,12 @@ class WebStrand {
16
         this.damping = 0.75; // Damping factor for recoil (much faster damping to prevent accumulation)
16
         this.damping = 0.75; // Damping factor for recoil (much faster damping to prevent accumulation)
17
         this.springConstant = 0.04; // Spring stiffness (much softer spring)
17
         this.springConstant = 0.04; // Spring stiffness (much softer spring)
18
         this.flexibility = 1.0; // How much the web can be dragged by flies
18
         this.flexibility = 1.0; // How much the web can be dragged by flies
19
+        
20
+        // NEW: Track obstacle attachments
21
+        this.startObstacle = null;
22
+        this.startAngle = 0;
23
+        this.endObstacle = null;
24
+        this.endAngle = 0;
19
     }
25
     }
20
 
26
 
21
     update() {
27
     update() {
@@ -254,6 +260,9 @@ class WebNode {
254
         this.vx = 0;
260
         this.vx = 0;
255
         this.vy = 0;
261
         this.vy = 0;
256
         this.pinned = false;
262
         this.pinned = false;
263
+        
264
+        this.attachedObstacle = null;
265
+        this.attachmentAngle = 0;
257
     }
266
     }
258
 
267
 
259
     applyForce(fx, fy) {
268
     applyForce(fx, fy) {