// Rotates this object's 8 neighbours one step around a ring every 0.5s, shoving // each pushable into the next ring slot via a single simultaneous `swap`. The spin // direction comes from the `BUILTIN_spinner_*` tag the map loader attaches (it // defaults to clockwise when no tag is present). // // A neighbour may only rotate if its destination slot will actually be free: the // slot is a hole (`passable`), or that slot's own occupant also rotates out. We // can't decide that with `can_shift` alone — `can_shift(j) == false` is ambiguous // between "j is empty" and "j is a blocked solid" — so we seed with `can_shift` // and then cascade-demote using `passable` to spot the genuine holes. fn tick(dt) { // Only start a new rotation when the previous one (and its delay) has drained, // exactly like the built-in pusher's pacing. if Queue.length() != 0 { return; } // Direction from the builtin tag; clockwise unless explicitly counter-clockwise. let cw = !Me.has_tag("BUILTIN_spinner_ccw"); // The 8 neighbour offsets in clockwise order, starting at North. 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 ]; // 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 { 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 // the board Registry, keyed per spinner, since script scope resets each tick. let frames = if cw { [47, 0xc4, 92, 0xb3] } else { [92, 0xc4, 47, 0xb3] }; let fkey = `spin_${Me.id}`; let f = Registry.get_or(fkey, 0); set_tile(frames[f % 4]); Registry.set(fkey, (f + 1) % 4); delay(0.5); }