Decree two: construction buildup anims, Boombox Crates, real faction flags, character portraits

* All 17 buildings now RISE out of the ground behind a pink scan line when
  built (tools/make_anims.py generates 7-frame make sequences; WithMakeAnimation
  on the building template).
* Boombox Crates: neutral pickups spawn on a timer (max 4). 500 records usually,
  1500 jackpot sometimes, bass_ring feedback trap rarely. Locomotors crush
  crates to collect, classic style.
* Lobby faction flags are real now: MRP record, Stream play button, split '?'.
* MC 8-TRACK and ASSISTANT portraits generated for briefings/press kit.
* Balance ledger updated: first aura-era match went to Stream (runaway flag);
  full ledger M M M | S S | M | S — boundary variance, no knee-jerk tune.

Smoke-tested PASS on arena and m01 with everything live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-26 19:53:36 +10:00
parent 8945a6a06b
commit 8e4d13b72b
31 changed files with 182 additions and 7 deletions

View File

@ -198,8 +198,11 @@ faction, or it reads as clip-art soup. Fix the Blender camera rig once, reuse.
Six instrumented arena matches so far: 3x MRP blowouts (structural AI bugs,
fixed), 2x Stream blowouts (buff overshoot: scraper 18 / drmwalker 750),
then 1x close 12-min MRP win with zero verdict flags after the half-step
walk-back (scraper 16, drmwalker 820, bufferbot 5000 kept). The matchup now
flips with small knob changes = we are near the boundary. Acceptance test for
walk-back (scraper 16, drmwalker 820, bufferbot 5000 kept), then 1x Stream win
(runaway-flagged) in the first aura-era match. Full ledger: M M M | S S | M | S.
The matchup now flips match to match = near the boundary with real variance.
If Stream trends ahead over a series, first knobs: In The Mix range 5c0->6c0
or Low Latency 130->120. Acceptance test for
any future balance change: a 5-match arena series via balance_report.py, not
a single game. Human playtesting is the real referee from here.

View File

@ -35,3 +35,7 @@ All images generated locally on MODELBEAST (`flux_local`) via `tools/mbgen.py`.
| renders/curator.png | 2026-07-25 | glossy seamless white tech aesthetic, soft blue ring lights, smooth rounded minimal design, hovering white sphere ringed with many camera lenses, a single blue eye at the front, faint halo, isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text |
| renders/launchramp.png | 2026-07-25 | rusty junkyard robot aesthetic, rust metal with hot pink trim, analog dials and speaker parts, scrap metal launch ramp pad with painted pink chevrons, floodlights and a parked flying vinyl disc, isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text |
| renders/droneport.png | 2026-07-25 | glossy seamless white tech aesthetic, soft blue ring lights, smooth rounded minimal design, white circular drone landing pad building with recessed blue guide lights and folded launch arms, isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text |
| renders/boombox.png | 2026-07-26 | rusty boombox ghetto blaster with hot pink speaker cones and a cassette deck, tiny video game pickup crate sprite |
| portrait_mc8track.png | 2026-07-26 | character portrait of MC 8-TRACK, warm friendly rusty robot radio announcer, cassette-deck chest, glowing pink CRT smile |
| portrait_assistant.png | 2026-07-26 | character portrait of ASSISTANT, eerily polite seamless white smart-speaker robot, single calm blue ring light face |
| keyart_raw.png | 2026-07-26 | wide cinematic key art, monster robot holding vinyl aloft on hill of smashed speakers, white obelisk city, vinyl sun |

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

View File

@ -117,12 +117,12 @@ strategic:
player_owned: 160, 142, 22, 22
flags:
Inherits: ^Glyphs
Image: chrome/assets/flags.png
Regions:
vinylgod: 226, 241, 30, 15
stream: 226, 241, 30, 15
Random: 226, 241, 30, 15
spectator: 226, 241, 30, 15
vinylgod: 0, 0, 30, 15
stream: 0, 16, 30, 15
Random: 0, 32, 30, 15
spectator: 0, 32, 30, 15
music:
Inherits: ^Glyphs

View File

@ -109,3 +109,5 @@ actor-waxseed =
.name = Wax Seep
actor-lacquer =
.name = Master Lacquer
actor-boombox =
.name = Boombox Crate

View File

@ -116,6 +116,7 @@ djgrunt:
^EconBuilding:
Inherits: ^EconSprite
WithMakeAnimation:
ShakeOnDeath:
Duration: 12
Intensity: 2
@ -328,3 +329,29 @@ lacquer:
HP: 1000
MapEditorData:
Categories: Props
# neutral pickup: the Boombox Crate. Cash mostly, jackpot sometimes, feedback trap rarely.
boombox:
Interactable:
HiddenUnderFog:
BodyOrientation:
QuantizeFacingsFromSequence:
RenderSprites:
Tooltip:
Name: actor-boombox.name
Crate:
Duration: 6000
TerrainTypes: Clear, Wax
WithCrateBody:
LandSequence: idle
GiveCashCrateAction:
Amount: 500
SelectionShares: 20
UseCashTick: true
GiveCashCrateAction@jackpot:
Amount: 1500
SelectionShares: 5
UseCashTick: true
ExplodeCrateAction@trap:
Weapon: bass_ring
SelectionShares: 5

View File

@ -50,12 +50,14 @@ world:
PanelName: SKIRMISH_STATS
Locomotor@WHEELED:
Name: wheeled
Crushes: crate
TerrainSpeeds:
Clear: 100
Wax: 80
Locomotor@FOOT:
Name: foot
SharesCell: true
Crushes: crate
TerrainSpeeds:
Clear: 100
Wax: 70
@ -73,6 +75,10 @@ world:
Sequences: wax01
Name: resource-wax
ResourceClaimLayer:
CrateSpawner:
Maximum: 4
SpawnInterval: 3000
CrateActors: boombox
ScreenShaker:
SmudgeLayer@SCORCH:
Type: Scorch

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -27,6 +27,10 @@ djgrunt:
Filename: sequences/assets/icon_djgrunt.png
popupshop:
make:
Filename: sequences/assets/popupshop_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/popupshop.png
Length: 6
@ -35,6 +39,10 @@ popupshop:
Filename: sequences/assets/icon_popupshop.png
pressplant:
make:
Filename: sequences/assets/pressplant_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/pressplant.png
Length: 6
@ -43,6 +51,10 @@ pressplant:
Filename: sequences/assets/icon_pressplant.png
ampstack:
make:
Filename: sequences/assets/ampstack_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/ampstack.png
Length: 6
@ -51,6 +63,10 @@ ampstack:
Filename: sequences/assets/icon_ampstack.png
vault:
make:
Filename: sequences/assets/vault_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/vault.png
Length: 6
@ -59,6 +75,10 @@ vault:
Filename: sequences/assets/icon_vault.png
merchtable:
make:
Filename: sequences/assets/merchtable_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/merchtable.png
Length: 6
@ -67,6 +87,10 @@ merchtable:
Filename: sequences/assets/icon_merchtable.png
garage:
make:
Filename: sequences/assets/garage_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/garage.png
Length: 6
@ -102,6 +126,10 @@ roadie:
Filename: sequences/assets/icon_roadie.png
mastering:
make:
Filename: sequences/assets/mastering_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/mastering.png
Length: 6
@ -117,6 +145,10 @@ streamvan:
Filename: sequences/assets/icon_streamvan.png
streamhub:
make:
Filename: sequences/assets/streamhub_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/streamhub.png
Length: 6
@ -125,6 +157,10 @@ streamhub:
Filename: sequences/assets/icon_streamhub.png
datacentre:
make:
Filename: sequences/assets/datacentre_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/datacentre.png
Length: 6
@ -133,6 +169,10 @@ datacentre:
Filename: sequences/assets/icon_datacentre.png
transcoder:
make:
Filename: sequences/assets/transcoder_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/transcoder.png
Length: 6
@ -141,6 +181,10 @@ transcoder:
Filename: sequences/assets/icon_transcoder.png
podfarm:
make:
Filename: sequences/assets/podfarm_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/podfarm.png
Length: 6
@ -149,6 +193,10 @@ podfarm:
Filename: sequences/assets/icon_podfarm.png
printfarm:
make:
Filename: sequences/assets/printfarm_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/printfarm.png
Length: 6
@ -190,6 +238,10 @@ explosion:
Length: 12
hornpit:
make:
Filename: sequences/assets/hornpit_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/hornpit.png
Length: 6
@ -198,6 +250,10 @@ hornpit:
Filename: sequences/assets/icon_hornpit.png
adblocker:
make:
Filename: sequences/assets/adblocker_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/adblocker.png
Length: 6
@ -224,6 +280,10 @@ icon:
Filename: sequences/assets/icon_theshuffle.png
algorithm:
make:
Filename: sequences/assets/algorithm_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/algorithm.png
Length: 6
@ -257,6 +317,10 @@ curator:
Filename: sequences/assets/icon_curator.png
launchramp:
make:
Filename: sequences/assets/launchramp_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/launchramp.png
Length: 6
@ -265,6 +329,10 @@ launchramp:
Filename: sequences/assets/icon_launchramp.png
droneport:
make:
Filename: sequences/assets/droneport_make.png
Length: 7
Tick: 70
idle:
Filename: sequences/assets/droneport.png
Length: 6
@ -327,3 +395,7 @@ auras:
Filename: sequences/assets/aura_mix.png
latency:
Filename: sequences/assets/aura_latency.png
boombox:
idle:
Filename: sequences/assets/boombox.png

61
tools/make_anims.py Normal file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Generate construction 'make' animations for every building.
7 frames per building: the structure rises out of the ground behind a hot pink
scan line, classic buildup feel without hand-drawn scaffolding. Source frames
are the static frame-0 backups maintained by animate_sprites.py.
Writes <name>_make.png sheets and appends `make:` sequences.
"""
import os
from PIL import Image, ImageDraw
from PIL.PngImagePlugin import PngInfo
ROOT = os.path.join(os.path.dirname(__file__), '..')
ASSETS = os.path.join(ROOT, 'mods', 'vinylgod', 'sequences', 'assets')
BACKUP = os.path.join(ROOT, 'assets_src', 'static_frames')
SEQ = os.path.join(ROOT, 'mods', 'vinylgod', 'sequences', 'economy.yaml')
FRAMES = 7
PINK = (255, 45, 150, 255)
BUILDINGS = ['popupshop', 'pressplant', 'ampstack', 'vault', 'merchtable', 'garage',
'mastering', 'hornpit', 'streamhub', 'datacentre', 'transcoder', 'podfarm',
'printfarm', 'adblocker', 'algorithm', 'launchramp', 'droneport']
seq = open(SEQ).read()
made = []
for name in BUILDINGS:
src = os.path.join(BACKUP, f'{name}.png')
if not os.path.exists(src):
continue
base = Image.open(src).convert('RGBA')
w, h = base.size
sheet = Image.new('RGBA', (w * FRAMES, h), (0, 0, 0, 0))
for i in range(FRAMES):
t = (i + 1) / FRAMES
reveal = int(h * t) # rows revealed, bottom-up
rise = int((1 - t) * h * 0.25) # slight rise from the ground
frame = Image.new('RGBA', (w, h), (0, 0, 0, 0))
part = base.crop((0, h - reveal, w, h))
frame.paste(part, (0, h - reveal + rise), part)
d = ImageDraw.Draw(frame)
if i < FRAMES - 1:
y = h - reveal + rise
if 0 <= y < h:
d.line([0, y, w, y], fill=PINK, width=2)
sheet.paste(frame, (i * w, 0))
meta = PngInfo()
meta.add_text('FrameSize', f'{w},{h}')
sheet.save(os.path.join(ASSETS, f'{name}_make.png'), pnginfo=meta)
block = f'{name}:\n\tidle:'
if block in seq and f'{name}:\n\tidle:' in seq and f'sequences/assets/{name}_make.png' not in seq:
seq = seq.replace(f'{name}:\n\tidle:',
f'{name}:\n\tmake:\n\t\tFilename: sequences/assets/{name}_make.png\n'
f'\t\tLength: {FRAMES}\n\t\tTick: 70\n\tidle:', 1)
made.append(name)
print(f'make anim: {name}')
open(SEQ, 'w').write(seq)
print(f'{len(made)} buildup animations written')