S3.7: isometric office assets: animated monitors, coffee machine with steam, toilets, sinks, real-time wall clock, decorative window weather; explanatory on-screen notes and the hint bubble removed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
23c7d55b1d
commit
202418f81c
17 files changed
+265
-94
No files matched your search
@@ -48,8 +48,6 @@ test("the library manages documents with versions and knowledge with history", a
|
||||
expect(orgId).toBeTruthy();
|
||||
|
||||
await page.getByRole("button", { name: "Library", exact: true }).click();
|
||||
// Each page says which object drives work: only a task runs.
|
||||
await expect(page.getByText("Nothing here starts work; a task does.", { exact: false })).toBeVisible();
|
||||
await page.getByRole("listitem").filter({ hasText: "Pricing memo" }).click();
|
||||
await page.getByRole("button", { name: "Edit", exact: true }).click();
|
||||
await page.getByLabel("Content").fill("Price is 12.");
|
||||
@@ -60,9 +58,7 @@ test("the library manages documents with versions and knowledge with history", a
|
||||
await expect(page.getByText("Price is 10.")).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "Knowledge", exact: true }).click();
|
||||
await expect(page.getByText("Nothing here starts work; a task does.", { exact: false })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Work", exact: true }).click();
|
||||
await expect(page.getByText("Only a task runs: an agent picks it up and does the work.", { exact: false })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Library", exact: true }).click();
|
||||
await page.getByRole("button", { name: "Knowledge", exact: true }).click();
|
||||
await page.getByRole("button", { name: "New", exact: true }).click();
|
||||
|
||||
@@ -91,9 +91,8 @@ test("the office floor opens agent details on click", async () => {
|
||||
}
|
||||
await expect(canvas).toBeVisible();
|
||||
|
||||
// The office offers no pretend controls over work, and says what a position means.
|
||||
// The office offers no pretend controls over work.
|
||||
await expect(page.getByRole("button", { name: "Whip" })).toHaveCount(0);
|
||||
await expect(page.getByText("Agents with work stay at their desks. Only idle agents walk to coffee, snacks or the meeting table.")).toBeVisible();
|
||||
|
||||
// Hired agents take open-floor desks in hire order and are clickable too.
|
||||
await hireAgent(page, "Ava Patel", "Designer", "Design");
|
||||
|
||||
@@ -60,15 +60,13 @@ test("text uses the bundled pixel font, and the palette follows the day part, se
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(dialog).toHaveCount(0);
|
||||
|
||||
// Progress is an XP bar announced as a value; hints are spoken by the guide in a bubble.
|
||||
// Progress is an XP bar announced as a value.
|
||||
await page.getByRole("button", { name: "Home" }).click();
|
||||
await expect(page.getByRole("progressbar", { name: "Goal progress" })).toHaveAttribute("aria-valuenow", "0");
|
||||
// On the roster the only agent holds the largest budget, and the bar says it compares budgets, not spend.
|
||||
await page.getByRole("button", { name: "Team", exact: true }).click();
|
||||
await expect(page.getByRole("progressbar", { name: "Budget compared with the largest agent budget" })).toHaveAttribute("aria-valuenow", "100");
|
||||
await page.getByRole("button", { name: "Work", exact: true }).click();
|
||||
const hint = page.getByRole("note").filter({ hasText: "Only a task runs" });
|
||||
await expect(hint.locator(".hint-speaker svg.pixel-icon")).toHaveCount(1);
|
||||
// Tabs are raised pixel tabs; fields are sunken pixel boxes.
|
||||
const views = page.getByRole("radiogroup", { name: "View" });
|
||||
expect(await views.locator("[aria-checked=true]").evaluate((tab) => getComputedStyle(tab).borderBottomColor === getComputedStyle(tab).backgroundColor)).toBe(true);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { depthOf, fromIso, isoFrame, isoPixelSize, pickAt, toIso } from "../../src/renderer/src/pages/office/iso";
|
||||
import { blockDrawable } from "../../src/renderer/src/pages/office/scene-renderer";
|
||||
import { blockDrawable, sceneDrawables } from "../../src/renderer/src/pages/office/scene-renderer";
|
||||
import { buildFloorPlan } from "../../src/renderer/src/pages/office/floor-plan";
|
||||
|
||||
const frame = isoFrame({ w: 1216, h: 600 });
|
||||
|
||||
@@ -70,11 +71,26 @@ describe("office depth order", () => {
|
||||
const resting = blockDrawable(frame, cup, 12, colors, 16, counter);
|
||||
expect(loose.depth).toBeLessThan(underneath.depth);
|
||||
expect(resting.depth).toBeGreaterThan(underneath.depth);
|
||||
expect(() => blockDrawable(frame, { x: 10, y: 10, w: 4, h: 4 }, 4, colors, 16, counter)).toThrow("A resting block must sit over its support.");
|
||||
// The coffee machine straddles the pieces at 720..752 and 752..784: it sorts after both.
|
||||
const machine = blockDrawable(frame, { x: 744, y: 44, w: 14, h: 14 }, 12, colors, 16, counter);
|
||||
for (const x of [720, 752]) expect(machine.depth).toBeGreaterThan(blockDrawable(frame, { x, y: 40, w: 32, h: 28 }, 16, colors).depth);
|
||||
// Off the support, or straddling two of its pieces, a resting block has no single surface to sort after: refused.
|
||||
expect(() => blockDrawable(frame, { x: 10, y: 10, w: 4, h: 4 }, 4, colors, 16, counter)).toThrow("A resting block must touch exactly one piece of its support.");
|
||||
expect(() => blockDrawable(frame, { x: 744, y: 44, w: 14, h: 14 }, 12, colors, 16, counter)).toThrow("A resting block must touch exactly one piece of its support.");
|
||||
// Overhanging the support's outer edge, like a plant's leaves past its pot, still sorts after that one piece.
|
||||
const overhang = blockDrawable(frame, { x: 726, y: 64, w: 14, h: 14 }, 12, colors, 16, counter);
|
||||
expect(overhang.depth).toBeGreaterThan(underneath.depth);
|
||||
// A person standing in front of the counter still draws over what rests on it.
|
||||
expect(depthOf({ x: 704, y: 92 })).toBeGreaterThan(resting.depth);
|
||||
});
|
||||
});
|
||||
|
||||
test("every piece of the scene sorts by a footprint no longer than 32 on either side, so a person beside a long block is never painted over", () => {
|
||||
const plan = buildFloorPlan(12);
|
||||
const items = sceneDrawables(isoFrame(plan.world), plan, "winter", new Set(), 0);
|
||||
const oversized = items.filter((item) => item.foot.w > 32 || item.foot.h > 32).map((item) => item.foot);
|
||||
expect(oversized).toEqual([]);
|
||||
// A person at the left basin sorts after both the counter piece and the basin in front of them.
|
||||
const atBasin = depthOf({ x: 944, y: 72 });
|
||||
for (const foot of [{ x: 928, y: 32 }, { x: 930, y: 40 }]) {
|
||||
const item = items.find((candidate) => candidate.foot.x === foot.x && candidate.foot.y === foot.y)!;
|
||||
expect(atBasin).toBeGreaterThan(item.depth);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { weatherFor } from "../../src/renderer/src/pages/office/sky";
|
||||
import { clockHands } from "../../src/renderer/src/pages/office/props";
|
||||
|
||||
const day = (month: number, date: number, hour = 12) => new Date(2026, month - 1, date, hour, 0, 0);
|
||||
|
||||
describe("office decor", () => {
|
||||
test("the view outside keeps one weather all day and fits the season", () => {
|
||||
expect(weatherFor(day(1, 10, 8))).toBe(weatherFor(day(1, 10, 22)));
|
||||
const seen = (month: number) => new Set(Array.from({ length: 28 }, (_, index) => weatherFor(day(month, index + 1))));
|
||||
expect([...seen(1)].sort()).toEqual(["clear", "snow"]);
|
||||
expect([...seen(4)].sort()).toEqual(["clear", "rain"]);
|
||||
expect(seen(7).has("snow")).toBe(false);
|
||||
expect(seen(7).has("leaves")).toBe(false);
|
||||
expect(seen(10).has("leaves")).toBe(true);
|
||||
expect(seen(10).has("snow")).toBe(false);
|
||||
});
|
||||
|
||||
test("the wall clock shows the local time: hands measured clockwise from twelve", () => {
|
||||
const at = (hour: number, minute: number) => clockHands(new Date(2026, 0, 1, hour, minute));
|
||||
expect(at(0, 0)).toEqual({ hour: 0, minute: 0 });
|
||||
expect(at(3, 0).hour).toBeCloseTo(Math.PI / 2);
|
||||
expect(at(15, 0).hour).toBeCloseTo(Math.PI / 2);
|
||||
expect(at(6, 30).hour).toBeCloseTo((6.5 / 12) * 2 * Math.PI);
|
||||
expect(at(6, 30).minute).toBeCloseTo(Math.PI);
|
||||
expect(at(9, 45).minute).toBeCloseTo(1.5 * Math.PI);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user