diff --git a/settings/quickshell.nix b/settings/quickshell.nix index c16ee1b..7427e92 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -935,16 +935,9 @@ in property bool loose: false // shoved off, ballistic property real vx: 0 property real vy: 0 + property real spin: 0 // rad/tick while airborne } - // Turn around: the two ends swap roles, so the face leads in - // the new direction instead of the worm moonwalking. - function reverse() { - let f = worm.frontD; - worm.frontD = worm.backD; - worm.backD = f; - worm.step = -worm.step; - } // Every segment's position, rebuilt each tick. One array // instead of per-segment bindings: the gait and the ballistic @@ -1005,11 +998,17 @@ in } } if (worm.loose) { - // Flying, he keeps whatever shape he was - // caught in and simply travels — a curled-up - // worm mid-air, not a swimming one. - let dx = hx - body[0].x, dy = hy - body[0].y; - body = body.map(p => Qt.point(p.x + dx, p.y + dy)); + // Airborne he keeps the shape he was caught + // in and tumbles about his own head: map() + // reads the old array throughout, so body[0] + // stays the pivot and the head lands exactly + // on the new position. + let c = Math.cos(worm.spin), s = Math.sin(worm.spin); + let px = body[0].x, py = body[0].y; + body = body.map(p => { + let ox = p.x - px, oy = p.y - py; + return Qt.point(hx + ox * c - oy * s, hy + ox * s + oy * c); + }); } else { body = railBody(); } @@ -1071,46 +1070,47 @@ in // direction off of. property var pushers: [] property var prevRects: [] - readonly property real shoveSpeed: 18 + readonly property real shoveSpeed: 26 + // Ticks before a panel may launch him again. Without it he can + // land back inside the same rect with his head over the edge + // and get booted forever. + property int shoveCool: 0 + + function launch(p) { + worm.loose = true; + worm.vx = p.push.x * shoveSpeed; + worm.vy = p.push.y * shoveSpeed - 8; // -8: sent up, not slid along + worm.spin = (0.04 + Math.random() * 0.05) * (p.push.x >= 0 ? 1 : -1); + shoveCool = 45; + } function inRect(p, r) { return Math.abs(p.x - r.x) <= r.z + 6 && Math.abs(p.y - r.y) <= r.w + 6; } function checkShove() { - let head = body[0], tail = body[segments - 1]; + if (shoveCool > 0) { shoveCool--; return; } + let tail = body[segments - 1]; for (let i = 0; i < pushers.length; i++) { let r = pushers[i].rect, q = prevRects[i]; if (!r || r.z < 0.5) continue; - // Any part of him in the way is enough to be swept; - // only the head decides a turn-around. + // Any part of him in the way is enough to be caught. let touched = false; for (let s = 0; s < body.length && !touched; s++) touched = inRect(body[s], r); if (!touched) continue; - // Either it was not there a tick ago — the pop-out, - // and the usual case given how fast the morph is — or - // an edge has swept outward over him since. Both mean - // the panel arrived where he was standing. - let appeared = !q || q.z < 0.5; - let grew = !appeared && ( - (r.x + r.z) - (q.x + q.z) > 1 || (q.x - q.z) - (r.x - r.z) > 1 - || (r.y + r.w) - (q.y + q.w) > 1 || (q.y - q.w) - (r.y - r.w) > 1); - if (appeared || grew) { - worm.loose = true; - worm.vx = pushers[i].push.x * shoveSpeed; - worm.vy = pushers[i].push.y * shoveSpeed - 3; // -3: a little air - return; - } - // Sitting still, it is simply solid: he walks into the - // panel and turns around rather than crawling through - // it, which is what made an open dropdown feel like it - // was not there at all. Only turn him when he is - // ENTERING — head in, tail still out. Whole body - // inside (he landed there) means walking on is the way - // out, and reversing on that would just buzz him in - // place against both edges. - if (inRect(head, r) && !inRect(tail, r)) reverse(); + // A panel popping out on top of him and him crawling + // into one already open both end the same way now: + // launched. Only the first is the moment you asked + // for, but a dropdown sits open for minutes while he + // crawls at well under a pixel a tick, so contact is + // the event you actually see — and having that merely + // turn him round is what read as never being knocked. + // + // The exception is his whole body being inside, which + // means he landed in there: walking out is the way + // out, and kicking him again would only pin him. + if (!inRect(tail, r)) launch(pushers[i]); return; } }