The active tilemap layer can be renamed from the painting section, and a Show setting fades or hides the other layers in the scene view while the tilemap is selected (#138)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bfbcbe604c
commit
f53279d065
10 files changed
+248
-13
No files matched your search
+107
-1
@@ -64,8 +64,31 @@ async function cellShows(t, x, y, quadrants) {
|
||||
return { ok: off.length === 0, detail: JSON.stringify({ read, want }) };
|
||||
}
|
||||
|
||||
/** `over` laid at `alpha` on `under`, both `#rrggbb` as `pixelAt` reads them. */
|
||||
function blend(over, under, alpha) {
|
||||
const channel = (hex, i) => parseInt(hex.slice(i, i + 2), 16);
|
||||
return `#${[1, 3, 5]
|
||||
.map((i) => Math.round(channel(over, i) * alpha + channel(under, i) * (1 - alpha)))
|
||||
.map((c) => c.toString(16).padStart(2, '0'))
|
||||
.join('')}`;
|
||||
}
|
||||
|
||||
export async function tilemap(t) {
|
||||
const { check, json, evaluate, mutate, click, clickText, drag, press, shot, settle, undo, select } = t;
|
||||
const {
|
||||
check,
|
||||
json,
|
||||
evaluate,
|
||||
mutate,
|
||||
click,
|
||||
clickText,
|
||||
drag,
|
||||
press,
|
||||
typeText,
|
||||
shot,
|
||||
settle,
|
||||
undo,
|
||||
select,
|
||||
} = t;
|
||||
const { saved, api, navigate, projectId } = t;
|
||||
|
||||
await press('Escape');
|
||||
@@ -283,6 +306,89 @@ export async function tilemap(t) {
|
||||
await undo();
|
||||
await settle();
|
||||
|
||||
// 40m. The painted layer is named in the field below the list, and Show singles it out in the
|
||||
// scene view (#138). It sits here, not at the end, because here both layers exist: the upper
|
||||
// one is painted on and holds the cell (4,0), the lower one the 3 × 3 block.
|
||||
const nameField = await inInspector(t, '__gm.tilemap.layerNameAt()');
|
||||
if (nameField) {
|
||||
await click(nameField.x, nameField.y);
|
||||
await press('a', { ctrl: true });
|
||||
await typeText('Roofs');
|
||||
await press('Enter');
|
||||
}
|
||||
await settle();
|
||||
layers = await json(`__gm.tilemap.layers(${JSON.stringify(made.id)})`);
|
||||
const renamedRow = await json('(__gm.tilemap.layerRowAt(0) || {}).id');
|
||||
const nameNow = await json('(__gm.tilemap.layerNameAt() || {}).value');
|
||||
check(
|
||||
'the name field renames the layer painted on, and the list shows the new name',
|
||||
layers[1].name === 'Roofs' &&
|
||||
layers[0].name === 'Layer 1' &&
|
||||
renamedRow === layers[1].id &&
|
||||
nameNow === 'Roofs',
|
||||
JSON.stringify({ layers, renamedRow, nameNow }),
|
||||
);
|
||||
|
||||
// Pixels again, so the animation is cut down to one step for the reading, as in 40e.
|
||||
await mutate("__gm.state.project.assets.find((a) => a.id === 'at-grass').pictures = ['at-floor-1']");
|
||||
await settle();
|
||||
const quarter = async (x, y) => (await cellQuarters(t, x, y))?.[0] ?? null;
|
||||
const lowerWhole = await quarter(1, 1);
|
||||
const emptyCell = await quarter(5, 3);
|
||||
const upperWhole = await quarter(4, 0);
|
||||
/** Chooses an entry of the Show dropdown: the topmost hit, which is the open menu. */
|
||||
const show = async (label) => {
|
||||
const button = await inInspector(t, '__gm.tilemap.focusAt()');
|
||||
if (button) await click(button.x, button.y);
|
||||
await settle();
|
||||
const entry = await t.locate(label, true);
|
||||
await click(entry.x, entry.y);
|
||||
await settle();
|
||||
};
|
||||
|
||||
await show('Others faded');
|
||||
const faded = await quarter(1, 1);
|
||||
// The lower layer is laid over the background at 30 %, so its colour lands in between.
|
||||
const mix = lowerWhole && emptyCell && blend(lowerWhole, emptyCell, 0.3);
|
||||
const focusLabel = await json('(__gm.tilemap.focusAt() || {}).label');
|
||||
check(
|
||||
'Others faded draws the other layers at 30 % and the painted one whole',
|
||||
Boolean(mix) &&
|
||||
colourDistance(faded, mix) <= 16 &&
|
||||
colourDistance(await quarter(4, 0), upperWhole) <= 12 &&
|
||||
focusLabel === 'Others faded',
|
||||
JSON.stringify({ faded, mix, lowerWhole, emptyCell, focusLabel }),
|
||||
);
|
||||
|
||||
await show('Active layer only');
|
||||
const solo = await quarter(1, 1);
|
||||
const soloInfo = await json(`__gm.tilemap.info(${JSON.stringify(MAP_OBJECT)})`);
|
||||
check(
|
||||
'Active layer only leaves the other layers out and keeps the painted one',
|
||||
colourDistance(solo, emptyCell) <= 12 &&
|
||||
colourDistance(await quarter(4, 0), upperWhole) <= 12 &&
|
||||
soloInfo.drawCalls === 1,
|
||||
JSON.stringify({ solo, emptyCell, soloInfo }),
|
||||
);
|
||||
|
||||
// Only the selected map is singled out; deselected it is drawn whole, and the model never changed.
|
||||
await evaluate('__gm.state.select(null)');
|
||||
await settle();
|
||||
const deselected = await quarter(1, 1);
|
||||
layers = await json(`__gm.tilemap.layers(${JSON.stringify(made.id)})`);
|
||||
check(
|
||||
'a deselected map is drawn whole again, and Show hid nothing in the model',
|
||||
colourDistance(deselected, lowerWhole) <= 12 && layers.every((l) => !l.hidden),
|
||||
JSON.stringify({ deselected, lowerWhole, layers }),
|
||||
);
|
||||
await select({ kind: 'object', holderId: 'scene1', objectIds: [MAP_OBJECT], objectId: MAP_OBJECT });
|
||||
await show('All layers');
|
||||
await mutate(
|
||||
"__gm.state.project.assets.find((a) => a.id === 'at-grass').pictures = ['at-floor-1', 'at-floor-2']",
|
||||
);
|
||||
await undo(); // the rename
|
||||
await settle();
|
||||
|
||||
// 40i. Back to Select: the map is moved again instead of painted on.
|
||||
await clickInInspector(t, '__gm.tilemap.toolAt("select")');
|
||||
await settle();
|
||||
|
||||
Reference in new issue
Block a user