spinners and fmt
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
// 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");
|
||||
|
||||
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
|
||||
];
|
||||
|
||||
// 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]
|
||||
} else {
|
||||
[West, West, North, North, East, East, South, South]
|
||||
};
|
||||
let step = if cw { 1 } else { 7 };
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 dx = ox + ring[n][0];
|
||||
let dy = oy + ring[n][1];
|
||||
if moves[i] && !passable(dx, dy) && !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);
|
||||
}
|
||||
Reference in New Issue
Block a user