Workers P2: Regie 1 (Agenten, Snapshot ohne Animation, Posen, Leerlauf)

Dazu drawSideBubble in sprites.js: Ottos seitliche Blase braucht das Raster des Kontexts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-18 02:53:59 +00:00
1 parent 1aabbef3ab
commit e69c4355bd
5 files changed
+619 -1

No files matched your search

+39 -1
View File
@@ -253,6 +253,9 @@ function deviceRect(ctx, x, y, w, h, color) {
ctx.fillRect(x, y, w, h)
}
// Breite der Zeichenfläche in Geräte-Pixeln (ohne Canvas 960, wie die Welt bei Raster 3)
const canvasWidth = (ctx) => (ctx.canvas && ctx.canvas.width > 0 ? ctx.canvas.width : 960)
/* Sprechblase über einem Worker: (x, y) = Spitze der Blase (logisch). Maße wachsen mit dem Raster; die Blase
wird in Geräte-Pixeln in die Breite von ctx.canvas geklemmt (Rand 2 bei k = 3, ohne Canvas 960). */
export function drawBubble(ctx, x, y, text) {
@@ -270,7 +273,7 @@ export function drawBubble(ctx, x, y, text) {
const bh = u(22)
const tipX = Math.round(x * k + ox)
const by = Math.round(y * k + oy) - bh - u(6)
const maxW = ctx.canvas && ctx.canvas.width > 0 ? ctx.canvas.width : 960
const maxW = canvasWidth(ctx)
const bx = Math.max(edge, Math.min(maxW - bw - edge, Math.round(tipX - bw / 2)))
// Die Spitze bleibt am Kasten, auch wenn der Kasten wegen des Rands verschoben wurde
const tailW = u(6)
@@ -282,3 +285,38 @@ export function drawBubble(ctx, x, y, text) {
ctx.fillStyle = PAL.ink
ctx.fillText(str, bx + pad, by + u(5))
}
/* Sprechblase neben einem Worker, für Otto an der Oberkante der Welt, wo über dem Kopf kein Platz ist (Vorlage: sideBubble).
leftX/rightX = linke und rechte Körperkante (logisch), y = Oberkante der Blase. Die Blase steht links vom Körper und ihre
Spitze zeigt nach rechts auf leftX; fehlt links der Platz (Rand 4), steht sie rechts davon mit der Spitze auf rightX,
in die Breite von ctx.canvas geklemmt. Maße und Schrift wachsen mit dem Raster. */
export function drawSideBubble(ctx, text, { leftX, rightX, y }) {
const str = String(text ?? '')
if (str === '') return
const { k, ox, oy } = gridOf(ctx)
const f = k / 3
const u = (n) => Math.max(1, Math.round(n * f))
ctx.font = fontOf(500, 12 * f)
ctx.textBaseline = 'top'
ctx.textAlign = 'left'
const pad = u(7)
const edge = u(2)
const gap = u(9) // Blasenrand bis Körperkante: Rahmen, Spitze (8) und ein Pixel Luft
const bw = Math.ceil(ctx.measureText(str).width) + 2 * pad
const bh = u(22)
const by = Math.round(y * k + oy)
let bx = Math.round(leftX * k + ox) - gap - bw
const onLeft = bx >= u(4)
if (!onLeft) bx = Math.max(edge, Math.min(canvasWidth(ctx) - bw - edge, Math.round(rightX * k + ox) + gap))
deviceRect(ctx, bx - edge, by - edge, bw + 2 * edge, bh + 2 * edge, PAL.o)
deviceRect(ctx, bx, by, bw, bh, PAL.w)
if (onLeft) {
deviceRect(ctx, bx + bw + edge, by + u(7), u(4), u(4), PAL.o)
deviceRect(ctx, bx + bw + u(6), by + u(8), u(2), u(2), PAL.o)
} else {
deviceRect(ctx, bx - u(6), by + u(7), u(4), u(4), PAL.o)
deviceRect(ctx, bx - u(8), by + u(8), u(2), u(2), PAL.o)
}
ctx.fillStyle = PAL.ink
ctx.fillText(str, bx + pad, by + u(5))
}