@@ -7,11 +7,51 @@ class WebStrand { |
| 7 | 7 | this.strength = 1; |
| 8 | 8 | this.vibration = 0; |
| 9 | 9 | this.path = []; |
| 10 | + this.segments = []; // For physics simulation |
| 11 | + this.maxLength = 200; // Maximum strand length before it breaks |
| 12 | + this.tension = 0; |
| 13 | + this.broken = false; |
| 10 | 14 | } |
| 11 | 15 | |
| 12 | 16 | update() { |
| 13 | 17 | this.vibration *= 0.95; |
| 14 | 18 | |
| 19 | + // Calculate strand length and tension |
| 20 | + if (this.end) { |
| 21 | + let length = dist(this.start.x, this.start.y, this.end.x, this.end.y); |
| 22 | + this.tension = length / this.maxLength; |
| 23 | + |
| 24 | + // Break if overstretched or unsupported arc |
| 25 | + if (this.tension > 1.5 || this.checkUnsupportedArc()) { |
| 26 | + this.broken = true; |
| 27 | + } |
| 28 | + } |
| 29 | + |
| 30 | + // Apply gravity to path points for realistic sagging |
| 31 | + if (this.path && this.path.length > 2 && !this.broken) { |
| 32 | + for (let i = 1; i < this.path.length - 1; i++) { |
| 33 | + // Don't move the anchor points |
| 34 | + let point = this.path[i]; |
| 35 | + |
| 36 | + // Check if this point is supported by anything |
| 37 | + let supported = false; |
| 38 | + for (let obstacle of obstacles) { |
| 39 | + if (dist(point.x, point.y, obstacle.x, obstacle.y) < obstacle.radius + 5) { |
| 40 | + supported = true; |
| 41 | + break; |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + // Apply gravity if not supported |
| 46 | + if (!supported) { |
| 47 | + point.y += 0.3; // Gravity effect |
| 48 | + |
| 49 | + // Add slight pendulum motion |
| 50 | + point.x += sin(frameCount * 0.02 + i) * 0.1; |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 15 | 55 | for (let node of webNodes) { |
| 16 | 56 | if (dist(node.x, node.y, this.start.x, this.start.y) < 5 || |
| 17 | 57 | dist(node.x, node.y, this.end.x, this.end.y) < 5) { |
@@ -19,18 +59,57 @@ class WebStrand { |
| 19 | 59 | } |
| 20 | 60 | } |
| 21 | 61 | } |
| 62 | + |
| 63 | + checkUnsupportedArc() { |
| 64 | + if (!this.path || this.path.length < 3) return false; |
| 65 | + |
| 66 | + // Check if the web forms an unsupported arc (both ends lower than middle) |
| 67 | + let startY = this.start.y; |
| 68 | + let endY = this.end ? this.end.y : this.path[this.path.length - 1].y; |
| 69 | + let lowestPoint = startY; |
| 70 | + let highestPoint = startY; |
| 71 | + |
| 72 | + for (let point of this.path) { |
| 73 | + if (point.y > lowestPoint) lowestPoint = point.y; |
| 74 | + if (point.y < highestPoint) highestPoint = point.y; |
| 75 | + } |
| 76 | + |
| 77 | + // If the arc goes up significantly and both ends are near bottom, it's unsupported |
| 78 | + let arcHeight = lowestPoint - highestPoint; |
| 79 | + let bothEndsLow = startY > height - 200 && endY > height - 200; |
| 80 | + let significantArc = arcHeight > 100; |
| 81 | + |
| 82 | + // Check if there's any support in the middle |
| 83 | + let hasMiddleSupport = false; |
| 84 | + for (let i = Math.floor(this.path.length * 0.3); i < Math.floor(this.path.length * 0.7); i++) { |
| 85 | + let point = this.path[i]; |
| 86 | + for (let obstacle of obstacles) { |
| 87 | + if (dist(point.x, point.y, obstacle.x, obstacle.y) < obstacle.radius + 10) { |
| 88 | + hasMiddleSupport = true; |
| 89 | + break; |
| 90 | + } |
| 91 | + } |
| 92 | + if (hasMiddleSupport) break; |
| 93 | + } |
| 94 | + |
| 95 | + return bothEndsLow && significantArc && !hasMiddleSupport; |
| 96 | + } |
| 22 | 97 | |
| 23 | 98 | display() { |
| 99 | + if (this.broken) return; // Don't display broken strands |
| 100 | + |
| 24 | 101 | push(); |
| 25 | 102 | |
| 26 | | - if (gamePhase === 'NIGHT') { |
| 103 | + // Change color based on tension |
| 104 | + if (this.tension > 0.8) { |
| 105 | + stroke(255, 200, 200, 200); // Reddish when strained |
| 106 | + } else if (gamePhase === 'NIGHT') { |
| 27 | 107 | stroke(255, 255, 255, 250); |
| 28 | | - strokeWeight(2); |
| 29 | 108 | } else { |
| 30 | 109 | stroke(255, 255, 255, 200); |
| 31 | | - strokeWeight(1.5); |
| 32 | 110 | } |
| 33 | 111 | |
| 112 | + strokeWeight(gamePhase === 'NIGHT' ? 2 : 1.5); |
| 34 | 113 | noFill(); |
| 35 | 114 | |
| 36 | 115 | if (this.path && this.path.length > 2) { |
@@ -60,6 +139,11 @@ class WebStrand { |
| 60 | 139 | let midX = (this.start.x + this.end.x) / 2; |
| 61 | 140 | let midY = (this.start.y + this.end.y) / 2 + this.vibration * sin(frameCount * 0.3); |
| 62 | 141 | |
| 142 | + // Add sag based on horizontal distance |
| 143 | + let horizontalDist = abs(this.end.x - this.start.x); |
| 144 | + let sag = horizontalDist * 0.1; // More sag for longer horizontal spans |
| 145 | + midY += sag; |
| 146 | + |
| 63 | 147 | beginShape(); |
| 64 | 148 | curveVertex(this.start.x, this.start.y); |
| 65 | 149 | curveVertex(this.start.x, this.start.y); |
@@ -87,6 +171,7 @@ class WebStrand { |
| 87 | 171 | } |
| 88 | 172 | } |
| 89 | 173 | |
| 174 | + |
| 90 | 175 | class WebNode { |
| 91 | 176 | constructor(x, y) { |
| 92 | 177 | this.x = x; |