This commit is contained in:
2026-06-21 22:04:10 -05:00
parent 475534f7c4
commit 87979ac610
7 changed files with 134 additions and 255 deletions
+20 -70
View File
@@ -16,30 +16,30 @@ fn tick(dt) {
// Direction from the builtin tag; clockwise unless explicitly counter-clockwise.
let cw = !Me.has_tag("BUILTIN_spinner_ccw");
let ox = Me.x;
let oy = Me.y;
// The 8 neighbour offsets in clockwise order, starting at North.
let ring = [
[ 0, -1], // N
[ 1, -1], // NE
[ 1, 0], // E
[ 1, 1], // SE
[ 0, 1], // S
[-1, 1], // SW
[-1, 0], // W
[-1, -1], // NW
let cw_ring = [
[Me.x, Me.y-1], // N
[Me.x+1, Me.y-1], // NE
[Me.x+1, Me.y], // E
[Me.x+1, Me.y+1], // SE
[Me.x, Me.y+1], // S
[Me.x-1, Me.y+1], // SW
[Me.x-1, Me.y], // W
[Me.x-1, Me.y-1], // NW
];
// The cardinal step that carries each ring cell to the *next* slot in the spin
// direction, and the index offset to that slot (+1 clockwise, -1 == +7 counter).
// CW: N->NE is East, NE->E is South, ... CCW: N->NW is West, NE->N is West, ...
let dirs = if cw {
[East, South, South, West, West, North, North, East]
// For CCW, it's the same list in reverse
let ccw_ring = [];
for neighbor in cw_ring {
ccw_ring.insert(0, neighbor);
}
// Rotate the neighbor cells:
if cw {
shift(cw_ring);
} else {
[West, West, North, North, East, East, South, South]
};
let step = if cw { 1 } else { 7 };
shift(ccw_ring);
}
// Animate the glyph one frame per rotation (changing only the character): the
// line spins '/'-'\'-, slash-swapped for counter-clockwise. Frame state lives in
@@ -50,55 +50,5 @@ fn tick(dt) {
set_tile(frames[f % 4]);
Registry.set(fkey, (f + 1) % 4);
// moves[i]: will ring cell i rotate into its destination slot? Seed with
// can_shift, which is true only when cell i holds a pushable whose immediate
// next cell isn't a wall (so it could move *if* that next cell ends up free).
let moves = [];
for i in 0..8 {
let sx = ox + ring[i][0];
let sy = oy + ring[i][1];
moves.push(can_shift(sx, sy, dirs[i]));
}
// Cascade: a cell may only move if its destination is a hole (`passable`), or
// the occupant of that destination also moves. Demote until stable. A broken
// arc collapses back from the jam; a full pushable cycle never demotes (every
// destination's occupant moves) and rotates whole via swap's cycle handling.
loop {
let changed = false;
for i in 0..8 {
let n = (i + step) % 8;
let pn = (i + step - 1) % 8;
let dx = ox + ring[n][0];
let dy = oy + ring[n][1];
let sx = ox + ring[pn][0];
let sy = oy + ring[pn][1];
let blocked = !passable(dx, dy) && !combinable(sx, sy, dx, dy);
if moves[i] && blocked && !moves[n] {
moves[i] = false;
changed = true;
}
}
if !changed { break; }
}
// Emit the surviving rotations as one simultaneous batch, then pace to twice
// a second. Coordinates are pulled into locals first to keep each expression
// simple (Rhai caps expression complexity).
let batch = [];
for i in 0..8 {
if moves[i] {
let n = (i + step) % 8;
let sx = ox + ring[i][0];
let sy = oy + ring[i][1];
let dx = ox + ring[n][0];
let dy = oy + ring[n][1];
batch.push([sx, sy, dx, dy]);
}
}
swap(batch);
delay(0.5);
}