App 2.45.1: Halloween's song flies across the sky on a bat's banner
Instead of a line at the foot of the hill, a bat tows a parchment banner with the title and artist through the sky above the sign. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
@@ -27,7 +27,7 @@ import java.util.Locale;
|
||||
* of power, rising with it, in orange or, where it regenerated, green. Unread values show "—" and
|
||||
* light nothing. With music playing, the pumpkin's glow swells and its carving brightens on the
|
||||
* beat ({@link MusicTap}); colours and the embers keep their meaning, and with no music it burns as
|
||||
* before. What plays is carved into the foot of the hill ({@link NowPlaying}).
|
||||
* before. What plays flies across the sky on a banner a bat tows ({@link NowPlaying}).
|
||||
*
|
||||
* The night sky is drawn once into a bitmap per size; only the rest goes over it. Bats, bubbles and
|
||||
* the ghost move (about 30 frames a second) only while the car moves and the view is on screen;
|
||||
@@ -96,10 +96,10 @@ final class HalloweenView extends ClusterCanvasView {
|
||||
if (sky == null || skyW != w || skyH != h) buildSky(w, h, u);
|
||||
c.drawBitmap(sky, 0, 0, image);
|
||||
drawBats(c, w, u);
|
||||
boolean song = drawSong(c, w, u);
|
||||
drawMoon(c, w, u);
|
||||
drawGhost(c, w, u);
|
||||
drawHill(c, w, h, u);
|
||||
drawSong(c, w, h, u);
|
||||
drawEmbers(c, w, u);
|
||||
float pulse = music == null ? 0f : music.pulse();
|
||||
drawPumpkin(c, w, u, pulse);
|
||||
@@ -107,7 +107,7 @@ final class HalloweenView extends ClusterCanvasView {
|
||||
drawFlask(c, u);
|
||||
drawTombstone(c, w, u);
|
||||
drawGears(c, w, u);
|
||||
keepAnimating(moving || pulse > 0f, 33);
|
||||
keepAnimating(moving || pulse > 0f || song, 33);
|
||||
}
|
||||
|
||||
private void label(Canvas c, String s, float x, float y, float size, int colour, Typeface tf, Paint.Align a) {
|
||||
@@ -277,17 +277,51 @@ final class HalloweenView extends ClusterCanvasView {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- What plays, carved into the hill ----------
|
||||
// ---------- What plays: a bat tows it across the sky on a banner ----------
|
||||
|
||||
private void drawSong(Canvas c, int w, int h, float u) {
|
||||
if (v.songTitle == null) return;
|
||||
text.setTypeface(thin);
|
||||
text.setTextSize(20f * u);
|
||||
float room = w * 0.44f;
|
||||
String artist = v.songArtist == null ? "" : " — " + cut(v.songArtist, room / 2f);
|
||||
/**
|
||||
* A bat pulls a parchment banner with the song from left to right, high in the sky, behind the
|
||||
* sign and the moon, again and again while something plays. Returns whether it is drawn, so
|
||||
* the view keeps animating for it.
|
||||
*/
|
||||
private boolean drawSong(Canvas c, int w, float u) {
|
||||
if (v.songTitle == null) return false;
|
||||
text.setTypeface(face);
|
||||
text.setTextSize(19f * u);
|
||||
float room = Math.min(560f * u, w * 0.45f);
|
||||
String artist = v.songArtist == null ? "" : " — " + cut(v.songArtist, room / 2.5f);
|
||||
// A long title gives way, so the artist stays.
|
||||
String s = "♪ " + cut(v.songTitle, room - text.measureText("♪ " + artist)) + artist;
|
||||
label(c, s, w / 2f, h - 12f * u, 20f * u, AccentColor.withAlpha(BONE, 0xC0), thin, Paint.Align.CENTER);
|
||||
float t = SystemClock.uptimeMillis() / 1000f;
|
||||
float bw = text.measureText(s) + 40f * u, bh = 30f * u, tow = 56f * u;
|
||||
// One pass takes about half a minute; then it comes round again from the left.
|
||||
float travel = w + bw + tow + 80f * u;
|
||||
float bx = (t * 70f * u) % travel - bw - tow - 40f * u;
|
||||
// In the strip of sky above the sign, clear of it.
|
||||
float yc = 34f * u + (float) Math.sin(t * 1.3f) * 4f * u;
|
||||
float top = yc - bh / 2f, bottom = yc + bh / 2f, right = bx + bw, notch = 12f * u;
|
||||
// The ribbon, its trailing end cut as a swallowtail.
|
||||
path.reset();
|
||||
path.moveTo(bx, top);
|
||||
path.lineTo(right, top);
|
||||
path.lineTo(right, bottom);
|
||||
path.lineTo(bx, bottom);
|
||||
path.lineTo(bx + notch, yc);
|
||||
path.close();
|
||||
fill.setColor(BONE);
|
||||
c.drawPath(path, fill);
|
||||
line.setColor(0xFF8A6A3A);
|
||||
line.setStrokeWidth(2f * u);
|
||||
c.drawPath(path, line);
|
||||
label(c, s, bx + notch + (bw - notch) / 2f, yc + 7f * u, 19f * u, INK, face, Paint.Align.CENTER);
|
||||
// The tow lines, and the bat at their end.
|
||||
float batX = right + tow, batY = yc - 10f * u;
|
||||
line.setColor(BAT_EDGE);
|
||||
line.setStrokeWidth(1.5f * u);
|
||||
c.drawLine(right, top + 3f * u, batX, batY + 6f * u, line);
|
||||
c.drawLine(right, bottom - 3f * u, batX, batY + 6f * u, line);
|
||||
bat(c, batX, batY, 26f * u, (float) Math.sin(t * 12f), u);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@code s} cut with "…" to fit {@code room} in the text paint as set. */
|
||||
|
||||
Reference in New Issue
Block a user