@@ -86,30 +86,60 @@ class Spider { |
| 86 | 86 | // Check ceiling |
| 87 | 87 | if (this.pos.y <= this.radius) { |
| 88 | 88 | this.pos.y = this.radius; |
| 89 | | - this.vel.y *= -0.5; |
| 89 | + this.vel.y *= -0.5; // Bounce off ceiling, don't land |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | 92 | // Check home branch collision (one-way platform) |
| 93 | | - if (window.homeBranch && this.vel.y > 0) { // Only when falling |
| 93 | + if (window.homeBranch && this.isAirborne && this.vel.y > 0.1) { // Only when actually falling (not just tiny velocity) |
| 94 | 94 | let branch = window.homeBranch; |
| 95 | | - // Collision should be right at the visual surface |
| 96 | | - let branchTop = branch.y - 5; // Much closer to actual visual surface |
| 97 | 95 | |
| 98 | 96 | // Check if spider is within branch X range |
| 99 | | - let inXRange = false; |
| 100 | | - if (branch.side === 'left') { |
| 101 | | - inXRange = this.pos.x >= 0 && this.pos.x <= branch.endX + 20; |
| 102 | | - } else { |
| 103 | | - inXRange = this.pos.x >= branch.endX - 20 && this.pos.x <= width; |
| 104 | | - } |
| 97 | + let branchStart = Math.min(branch.startX, branch.endX); |
| 98 | + let branchEnd = Math.max(branch.startX, branch.endX); |
| 105 | 99 | |
| 106 | | - // One-way collision: only collide when falling from above |
| 107 | | - if (inXRange && |
| 108 | | - this.pos.y - this.radius <= branchTop && |
| 109 | | - this.pos.y + this.radius >= branchTop && |
| 110 | | - this.pos.y - this.radius < branchTop) { |
| 111 | | - this.pos.y = branchTop - this.radius; |
| 112 | | - this.land(); |
| 100 | + if (this.pos.x >= branchStart - 20 && this.pos.x <= branchEnd + 20) { |
| 101 | + // Calculate position along branch (0 to 1) |
| 102 | + let t = (this.pos.x - branchStart) / (branchEnd - branchStart); |
| 103 | + t = constrain(t, 0, 1); |
| 104 | + |
| 105 | + // Find the appropriate knob for this position |
| 106 | + let knobEffect = 0; |
| 107 | + let yShift = 0; |
| 108 | + if (branch.knobs) { |
| 109 | + for (let knob of branch.knobs) { |
| 110 | + let knobDist = abs(t - knob.t); |
| 111 | + if (knobDist < 0.15) { // Within influence range of knob |
| 112 | + let influence = 1 - (knobDist / 0.15); |
| 113 | + knobEffect = Math.max(knobEffect, knob.topBump * influence); |
| 114 | + yShift += knob.yShift * influence; |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + // Base thickness tapers from full at start to 30% at end |
| 120 | + let baseThickness = lerp(branch.thickness * 1.2, branch.thickness * 0.3, t); |
| 121 | + |
| 122 | + // Add knob effect to thickness |
| 123 | + let actualThickness = baseThickness + knobEffect; |
| 124 | + |
| 125 | + // Account for branch angle |
| 126 | + let angleOffset = (this.pos.x - branchStart) * Math.tan(branch.angle); |
| 127 | + |
| 128 | + // The visual top of the branch (where spider should land) |
| 129 | + let branchTopY = branch.y - actualThickness + angleOffset + yShift; |
| 130 | + |
| 131 | + // Only trigger collision if spider is actually above and close to the branch |
| 132 | + // Previous position check to ensure we're coming from above |
| 133 | + let prevY = this.pos.y - this.vel.y; |
| 134 | + |
| 135 | + if (prevY <= branchTopY && // Was above the branch |
| 136 | + this.pos.y + this.radius >= branchTopY && // Now at or below surface |
| 137 | + this.pos.y - this.radius <= branchTopY + 20 && // But not too far below |
| 138 | + this.vel.y > 0.1) { // Actually moving downward with some speed |
| 139 | + |
| 140 | + this.pos.y = branchTopY - this.radius; |
| 141 | + this.land(); |
| 142 | + } |
| 113 | 143 | } |
| 114 | 144 | } |
| 115 | 145 | |