zeroed-some/cob / b588850

Browse files

fixes

Authored by espadonne
SHA
b588850ac8e624d1147acbd89775acd59d369a99
Parents
59d53dc
Tree
055dccf

2 changed files

StatusFile+-
M js/entities.js 138 56
M js/game.js 75 7
js/entities.jsmodified
@@ -23,13 +23,15 @@ class Spider {
23
     // DAWN PHASE: Check and consume stamina
23
     // DAWN PHASE: Check and consume stamina
24
     if (gamePhase === 'DAWN') {
24
     if (gamePhase === 'DAWN') {
25
       if (jumpStamina < jumpCost) {
25
       if (jumpStamina < jumpCost) {
26
-        // Not enough stamina to jump
27
         isExhausted = true
26
         isExhausted = true
28
-        return // Can't jump
27
+        return
29
       }
28
       }
30
-      // Consume stamina for jump
31
       jumpStamina -= jumpCost
29
       jumpStamina -= jumpCost
32
       stats.totalJumps++
30
       stats.totalJumps++
31
+      // Delay stamina regen after each jump during DAWN
32
+      if (gamePhase === 'DAWN') {
33
+        staminaRegenCooldown = 60 // 1s at 60fps
34
+      }
33
     }
35
     }
34
 
36
 
35
     // PHASE 4B: Track wind jumps
37
     // PHASE 4B: Track wind jumps
@@ -60,7 +62,12 @@ class Spider {
60
     this.vel = direction
62
     this.vel = direction
61
     this.isAirborne = true
63
     this.isAirborne = true
62
     this.canJump = false
64
     this.canJump = false
63
-    this.lastAnchorPoint = this.pos.copy()
65
+
66
+    // FIX: Ensure lastAnchorPoint is set to edge, not center
67
+    if (!this.lastAnchorPoint) {
68
+      // If no anchor point set yet, use current position
69
+      this.lastAnchorPoint = this.pos.copy()
70
+    }
64
     // Record jump time for touch debounce
71
     // Record jump time for touch debounce
65
     if (typeof window !== 'undefined') {
72
     if (typeof window !== 'undefined') {
66
       window.lastJumpTime = millis()
73
       window.lastJumpTime = millis()
@@ -301,10 +308,20 @@ class Spider {
301
     // Only land if we're actually airborne
308
     // Only land if we're actually airborne
302
     if (!this.isAirborne) return
309
     if (!this.isAirborne) return
303
 
310
 
311
+    // Calculate angle from obstacle center to spider
304
     let angle = atan2(this.pos.y - obstacle.y, this.pos.x - obstacle.x)
312
     let angle = atan2(this.pos.y - obstacle.y, this.pos.x - obstacle.x)
313
+
314
+    // Place spider on the edge of the circular collision boundary
305
     this.pos.x = obstacle.x + cos(angle) * (obstacle.radius + this.radius)
315
     this.pos.x = obstacle.x + cos(angle) * (obstacle.radius + this.radius)
306
     this.pos.y = obstacle.y + sin(angle) * (obstacle.radius + this.radius)
316
     this.pos.y = obstacle.y + sin(angle) * (obstacle.radius + this.radius)
307
-    this.attachedObstacle = obstacle // Track which obstacle we're on
317
+
318
+    // FIX: Set anchor point at the edge, not center
319
+    this.lastAnchorPoint = createVector(
320
+      obstacle.x + cos(angle) * obstacle.radius,
321
+      obstacle.y + sin(angle) * obstacle.radius
322
+    )
323
+
324
+    this.attachedObstacle = obstacle
308
     this.land()
325
     this.land()
309
   }
326
   }
310
 
327
 
@@ -340,34 +357,48 @@ class Spider {
340
 
357
 
341
     // FIX: Check if we're actually landing on something valid
358
     // FIX: Check if we're actually landing on something valid
342
     let landedOnSomething = false
359
     let landedOnSomething = false
360
+    let landingPoint = null // Store where we're landing for anchor
343
 
361
 
344
     // Check if on ground
362
     // Check if on ground
345
     if (this.pos.y >= height - this.radius - 5) {
363
     if (this.pos.y >= height - this.radius - 5) {
346
       landedOnSomething = true
364
       landedOnSomething = true
365
+      landingPoint = createVector(this.pos.x, height)
347
     }
366
     }
348
 
367
 
349
     // Check if on an obstacle
368
     // Check if on an obstacle
350
-    for (let obstacle of obstacles) {
369
+    if (!landedOnSomething) {
351
-      if (this.checkObstacleCollision(obstacle)) {
370
+      for (let obstacle of obstacles) {
352
-        landedOnSomething = true
371
+        if (this.checkObstacleCollision(obstacle)) {
353
-        break
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
+        }
354
       }
381
       }
355
     }
382
     }
356
 
383
 
357
     // Check if on a web strand
384
     // Check if on a web strand
358
-    for (let strand of webStrands) {
385
+    if (!landedOnSomething) {
359
-      if (
386
+      for (let strand of webStrands) {
360
-        strand !== currentStrand &&
387
+        if (
361
-        !strand.broken &&
388
+          strand !== currentStrand &&
362
-        this.checkStrandCollision(strand)
389
+          !strand.broken &&
363
-      ) {
390
+          this.checkStrandCollision(strand)
364
-        landedOnSomething = true
391
+        ) {
365
-        break
392
+          landedOnSomething = true
393
+          // For web strands, use spider position as anchor
394
+          landingPoint = this.pos.copy()
395
+          break
396
+        }
366
       }
397
       }
367
     }
398
     }
368
 
399
 
369
     // Check if on home branch
400
     // Check if on home branch
370
-    if (window.homeBranch) {
401
+    if (!landedOnSomething && window.homeBranch) {
371
       let branch = window.homeBranch
402
       let branch = window.homeBranch
372
       let branchStart = Math.min(branch.startX, branch.endX)
403
       let branchStart = Math.min(branch.startX, branch.endX)
373
       let branchEnd = Math.max(branch.startX, branch.endX)
404
       let branchEnd = Math.max(branch.startX, branch.endX)
@@ -386,21 +417,25 @@ class Spider {
386
 
417
 
387
         if (abs(this.pos.y - branchSurfaceY) < this.radius + 10) {
418
         if (abs(this.pos.y - branchSurfaceY) < this.radius + 10) {
388
           landedOnSomething = true
419
           landedOnSomething = true
420
+          landingPoint = createVector(this.pos.x, branchSurfaceY)
389
         }
421
         }
390
       }
422
       }
391
     }
423
     }
392
 
424
 
393
     // FIX: If we're deploying web but didn't land on anything valid, destroy the web
425
     // FIX: If we're deploying web but didn't land on anything valid, destroy the web
394
     if (currentStrand && isDeployingWeb && (spacePressed || touchHolding)) {
426
     if (currentStrand && isDeployingWeb && (spacePressed || touchHolding)) {
395
-      if (landedOnSomething) {
427
+      if (landedOnSomething && landingPoint) {
396
-        // Valid landing - finalize the web
428
+        // Valid landing - finalize the web at the landing point
397
-        currentStrand.end = this.pos.copy()
429
+        currentStrand.end = landingPoint.copy() // Use edge point, not spider center
398
         if (!currentStrand.path || currentStrand.path.length === 0) {
430
         if (!currentStrand.path || currentStrand.path.length === 0) {
399
-          currentStrand.path = [this.pos.copy()]
431
+          currentStrand.path = [landingPoint.copy()]
400
         } else {
432
         } else {
401
-          currentStrand.path.push(this.pos.copy())
433
+          currentStrand.path.push(landingPoint.copy())
402
         }
434
         }
403
-        webNodes.push(new WebNode(this.pos.x, this.pos.y))
435
+        webNodes.push(new WebNode(landingPoint.x, landingPoint.y))
436
+
437
+        // Update last anchor for next web
438
+        this.lastAnchorPoint = landingPoint.copy()
404
       } else {
439
       } else {
405
         // Invalid landing in mid-air - destroy the web!
440
         // Invalid landing in mid-air - destroy the web!
406
         if (
441
         if (
@@ -426,6 +461,9 @@ class Spider {
426
           }
461
           }
427
         }
462
         }
428
       }
463
       }
464
+    } else if (landedOnSomething && landingPoint) {
465
+      // Update last anchor point even when not deploying web
466
+      this.lastAnchorPoint = landingPoint.copy()
429
     }
467
     }
430
 
468
 
431
     currentStrand = null
469
     currentStrand = null
@@ -869,42 +907,50 @@ class Obstacle {
869
   updateAttachedStrands () {
907
   updateAttachedStrands () {
870
     // Update web strands that are connected to this obstacle
908
     // Update web strands that are connected to this obstacle
871
     for (let strand of webStrands) {
909
     for (let strand of webStrands) {
872
-      // Check if strand starts at this obstacle
910
+      // Check if strand starts near this obstacle's edge
873
-      if (
911
+      let startDist = dist(strand.start.x, strand.start.y, this.x, this.y)
874
-        dist(strand.start.x, strand.start.y, this.x, this.y) <
912
+      if (startDist >= this.radius - 5 && startDist <= this.radius + 15) {
875
-        this.radius + 10
913
+        // Strand is attached to edge - update to maintain edge connection
876
-      ) {
914
+        let angle = atan2(strand.start.y - this.y, strand.start.x - this.x)
877
-        strand.start.x = this.x
915
+        strand.start.x = this.x + cos(angle) * this.radius
878
-        strand.start.y = this.y
916
+        strand.start.y = this.y + sin(angle) * this.radius
879
         if (strand.path && strand.path.length > 0) {
917
         if (strand.path && strand.path.length > 0) {
880
-          strand.path[0].x = this.x
918
+          strand.path[0].x = strand.start.x
881
-          strand.path[0].y = this.y
919
+          strand.path[0].y = strand.start.y
882
         }
920
         }
883
       }
921
       }
884
 
922
 
885
-      // Check if strand ends at this obstacle
923
+      // Check if strand ends near this obstacle's edge
886
-      if (
924
+      if (strand.end) {
887
-        strand.end &&
925
+        let endDist = dist(strand.end.x, strand.end.y, this.x, this.y)
888
-        dist(strand.end.x, strand.end.y, this.x, this.y) < this.radius + 10
926
+        if (endDist >= this.radius - 5 && endDist <= this.radius + 15) {
889
-      ) {
927
+          // Strand is attached to edge - update to maintain edge connection
890
-        strand.end.x = this.x
928
+          let angle = atan2(strand.end.y - this.y, strand.end.x - this.x)
891
-        strand.end.y = this.y
929
+          strand.end.x = this.x + cos(angle) * this.radius
892
-        if (strand.path && strand.path.length > 0) {
930
+          strand.end.y = this.y + sin(angle) * this.radius
893
-          strand.path[strand.path.length - 1].x = this.x
931
+          if (strand.path && strand.path.length > 0) {
894
-          strand.path[strand.path.length - 1].y = this.y
932
+            strand.path[strand.path.length - 1].x = strand.end.x
933
+            strand.path[strand.path.length - 1].y = strand.end.y
934
+          }
895
         }
935
         }
896
       }
936
       }
897
     }
937
     }
898
   }
938
   }
899
 
939
 
900
   breakAttachedStrands () {
940
   breakAttachedStrands () {
901
-    // Break any strands attached to this beetle that has drifted too far
941
+    // Check for strands attached to this obstacle's edge
902
     for (let strand of webStrands) {
942
     for (let strand of webStrands) {
943
+      // Check if attached to edge (not center)
944
+      let startDist = dist(strand.start.x, strand.start.y, this.x, this.y)
903
       let attachedToStart =
945
       let attachedToStart =
904
-        dist(strand.start.x, strand.start.y, this.x, this.y) < this.radius + 10
946
+        startDist >= this.radius - 5 && startDist <= this.radius + 15
905
-      let attachedToEnd =
947
+
906
-        strand.end &&
948
+      let attachedToEnd = false
907
-        dist(strand.end.x, strand.end.y, this.x, this.y) < this.radius + 10
949
+      if (strand.end) {
950
+        let endDist = dist(strand.end.x, strand.end.y, this.x, this.y)
951
+        attachedToEnd =
952
+          endDist >= this.radius - 5 && endDist <= this.radius + 15
953
+      }
908
 
954
 
909
       if (attachedToStart || attachedToEnd) {
955
       if (attachedToStart || attachedToEnd) {
910
         // Mark strand as broken
956
         // Mark strand as broken
@@ -1056,7 +1102,11 @@ class Obstacle {
1056
       // Matte fabric shading (subtle, non-glossy)
1102
       // Matte fabric shading (subtle, non-glossy)
1057
       noStroke()
1103
       noStroke()
1058
       // Soft radial shading toward top-left to imply ambient light without specular shine
1104
       // 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) {
1105
+      for (
1106
+        let r = this.radius * 1.2;
1107
+        r > this.radius * 0.2;
1108
+        r -= this.radius * 0.15
1109
+      ) {
1060
         fill(255, 255, 255, 8) // very low alpha
1110
         fill(255, 255, 255, 8) // very low alpha
1061
         ellipse(-this.radius * 0.25, -this.radius * 0.35, r * 0.25, r * 0.18)
1111
         ellipse(-this.radius * 0.25, -this.radius * 0.35, r * 0.25, r * 0.18)
1062
       }
1112
       }
@@ -1622,19 +1672,51 @@ class Bird {
1622
       // Update target position while diving
1672
       // Update target position while diving
1623
       if (frameCount % 8 === 0) {
1673
       if (frameCount % 8 === 0) {
1624
         this.targetX = spider.pos.x
1674
         this.targetX = spider.pos.x
1625
-        this.targetY = spider.pos.y
1675
+        // Keep steering intent downward; don't let target sit above our per-dive floor
1676
+        this.targetY =
1677
+          typeof this.pullUpY === 'number'
1678
+            ? Math.min(spider.pos.y, this.pullUpY - 4)
1679
+            : spider.pos.y
1626
       }
1680
       }
1627
 
1681
 
1628
       // Only consider bailing out after a minimum number of dive frames
1682
       // Only consider bailing out after a minimum number of dive frames
1629
-      let canBailOut = this.diveFrames > 15
1683
+      let canBailOut = this.diveFrames > 22
1630
       // Use pullUpY for stable bailout check
1684
       // Use pullUpY for stable bailout check
1631
-      let reachedPullUpY = (typeof this.pullUpY === 'number') ? (this.y > this.pullUpY) : false
1685
+      let reachedPullUpY =
1686
+        typeof this.pullUpY === 'number' ? this.y > this.pullUpY : false
1632
       let reachedBottom = this.y > height - 20 // Go almost to canvas bottom
1687
       let reachedBottom = this.y > height - 20 // Go almost to canvas bottom
1633
-      let closeToSpider = dist(this.x, this.y, spider.pos.x, spider.pos.y) < 50
1688
+      const hitCollision =
1634
-
1689
+        dist(this.x, this.y, spider.pos.x, spider.pos.y) <=
1635
-      if (canBailOut && (reachedPullUpY || reachedBottom || closeToSpider)) {
1690
+        this.size * 0.5 + spider.radius
1691
+      const nearButNotHit =
1692
+        !hitCollision &&
1693
+        abs(this.x - spider.pos.x) < 30 &&
1694
+        abs(this.y - spider.pos.y) < 24
1695
+
1696
+      if (canBailOut && (hitCollision || reachedPullUpY || reachedBottom)) {
1697
+        // Convert near-miss near the floor into a sweep instead of an early bail
1698
+        if (
1699
+          !hitCollision &&
1700
+          reachedPullUpY &&
1701
+          spider.pos.y > height - 30 &&
1702
+          !this.sweeping
1703
+        ) {
1704
+          this.sweeping = true
1705
+          this.y = spider.pos.y // lock to spider height
1706
+          this.vy = 0
1707
+          const sweepDirection = spider.pos.x > this.x ? 1 : -1
1708
+          this.vx = sweepDirection * 8
1709
+          setTimeout(() => {
1710
+            this.sweeping = false
1711
+            this.state = 'retreating'
1712
+            this.attacking = false
1713
+            this.diveFrames = 0
1714
+            this.pullUpY = null
1715
+          }, 500)
1716
+          return // skip normal bailout path
1717
+        }
1636
         // If spider is at bottom and we haven't hit it yet, do a horizontal sweep
1718
         // If spider is at bottom and we haven't hit it yet, do a horizontal sweep
1637
-        if (spider.pos.y > height - 30 && !closeToSpider && !this.sweeping) {
1719
+        if (spider.pos.y > height - 30 && !hitCollision && !this.sweeping) {
1638
           this.sweeping = true
1720
           this.sweeping = true
1639
           this.y = spider.pos.y // Match spider height
1721
           this.y = spider.pos.y // Match spider height
1640
           this.vy = 0 // Stop vertical movement
1722
           this.vy = 0 // Stop vertical movement
js/game.jsmodified
@@ -67,6 +67,7 @@ let staminaRegenRate = 0.2
67
 let isExhausted = false
67
 let isExhausted = false
68
 let fliesMunchedLastNight = 0
68
 let fliesMunchedLastNight = 0
69
 let birds = []
69
 let birds = []
70
+let staminaRegenCooldown = 0
70
 
71
 
71
 // PHASE 4B: Wind System
72
 // PHASE 4B: Wind System
72
 let windActive = false
73
 let windActive = false
@@ -1037,16 +1038,83 @@ function draw () {
1037
     pop()
1038
     pop()
1038
   }
1039
   }
1039
 
1040
 
1041
+  // Threat cue when regen is fully suppressed
1042
+  if (gamePhase === 'DAWN') {
1043
+    let showThreatCue = false
1044
+    for (let b of birds) {
1045
+      if (
1046
+        b &&
1047
+        b.state === 'diving' &&
1048
+        dist(b.x, b.y, spider.pos.x, spider.pos.y) < 180
1049
+      ) {
1050
+        showThreatCue = true
1051
+        break
1052
+      }
1053
+    }
1054
+    if (showThreatCue) {
1055
+      push()
1056
+      textAlign(CENTER)
1057
+      textSize(14)
1058
+      fill(255, 80, 80, 210)
1059
+      stroke(0)
1060
+      strokeWeight(2)
1061
+      text(
1062
+        'UNDER ATTACK! stamina regen halted',
1063
+        spider.pos.x,
1064
+        spider.pos.y - 48
1065
+      )
1066
+      pop()
1067
+    }
1068
+  }
1069
+
1040
   // PHASE 4: Update and display birds during dawn
1070
   // PHASE 4: Update and display birds during dawn
1041
   if (gamePhase === 'DAWN') {
1071
   if (gamePhase === 'DAWN') {
1042
-    // Update stamina
1072
+    // Update stamina (suppressed during bird attack sequences)
1043
-    if (!spider.isAirborne && spider.vel.mag() < 0.1) {
1073
+    // Threat scan (expose flags for cooldown logic)
1044
-      // Resting - faster regen
1074
+    let anyBirdActive = false
1045
-      jumpStamina += staminaRegenRate * 2.5
1075
+    let nearDivingBird = false
1046
-    } else {
1076
+    for (let b of birds) {
1047
-      // Moving - normal regen
1077
+      if (!b) continue
1048
-      jumpStamina += staminaRegenRate
1078
+      // any bird on screen during DAWN counts as pressure
1079
+      if (b.state !== 'retreating') anyBirdActive = true
1080
+      // hard suppression if a diving bird is close
1081
+      if (b.state === 'diving') {
1082
+        const d = dist(b.x, b.y, spider.pos.x, spider.pos.y)
1083
+        if (d < 180) nearDivingBird = true
1084
+      }
1085
+    }
1086
+    const birdThreatMultiplier = nearDivingBird
1087
+      ? 0.0
1088
+      : anyBirdActive
1089
+      ? 0.4
1090
+      : 1.0
1091
+
1092
+    // Cooldown: delay regen start after jumps and while threats persist
1093
+    if (staminaRegenCooldown > 0) {
1094
+      staminaRegenCooldown--
1095
+    }
1096
+    if (nearDivingBird) {
1097
+      staminaRegenCooldown = Math.max(staminaRegenCooldown, 90) // +1.5s after a near dive
1098
+    } else if (anyBirdActive) {
1099
+      staminaRegenCooldown = Math.max(staminaRegenCooldown, 45) // +0.75s while birds hunt
1100
+    }
1101
+    // Light movement also nudges the cooldown so regen only starts when resting
1102
+    if (spider.vel.mag() >= 0.3) {
1103
+      staminaRegenCooldown = Math.max(staminaRegenCooldown, 15)
1049
     }
1104
     }
1105
+
1106
+    // Base regen depends on motion
1107
+    let regen =
1108
+      staminaRegenRate *
1109
+      (spider.isAirborne || spider.vel.mag() >= 0.1 ? 1.0 : 2.0)
1110
+    // Apply threat suppression
1111
+    regen *= birdThreatMultiplier
1112
+    // Apply cooldown suppression
1113
+    if (staminaRegenCooldown > 0) {
1114
+      regen = 0
1115
+    }
1116
+
1117
+    jumpStamina += regen
1050
     jumpStamina = min(jumpStamina, maxJumpStamina)
1118
     jumpStamina = min(jumpStamina, maxJumpStamina)
1051
     isExhausted = jumpStamina < jumpCost
1119
     isExhausted = jumpStamina < jumpCost
1052
 
1120