Round 5a: heroes (The Selector, The Curator), air (45 Flyer, Compression Drone) with pads + Aircraft queue/tab, scouts (Boom Bike, Crawler), 5 weapons, wax seep regrowth on all maps
Wax seeps fix the economy flatline the arena telemetry exposed: both bots hit cash=0/stored=0 from tick 6250 with no way to recover. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83
LORE.md
@ -95,6 +95,89 @@ replaced by a receipt.
|
||||
| `datacentre` | Edge Node | power/build-radius creep | humming white monolith with a single blue status LED |
|
||||
| `transcoder` | Transcoder | refinery | white cube that swallows records whole; only receipts come out |
|
||||
|
||||
## Heroes
|
||||
|
||||
Each side fields exactly one hero. They cost a fortune, they cannot be replaced
|
||||
while alive, and losing one is a story beat, not a statistic.
|
||||
|
||||
### THE SELECTOR (MRP, `selector`)
|
||||
|
||||
Nobody built the Selector. It assembled itself in the sub-basement of a flooded
|
||||
pressing plant out of a coat rack, a broken mastering lathe, and the contents of
|
||||
a collection somebody spent forty years building and one afternoon losing.
|
||||
|
||||
It carries a crate of **one-of-one dubplates** — acetates cut before the
|
||||
Compression, of songs that exist nowhere else. Each one it throws is gone
|
||||
forever. That is the Selector's whole tragedy and its whole tactic: it wins
|
||||
fights by destroying the last copies of the things it is fighting for.
|
||||
|
||||
It does not speak. It nods, and something in the distance falls over.
|
||||
|
||||
- Slow, absurdly tough for infantry, huge splash damage, long reload.
|
||||
- Voice lines are just the needle-drop crackle of a record starting.
|
||||
|
||||
### THE CURATOR (Stream, `curator`)
|
||||
|
||||
The Algorithm's only physical opinion. A white sphere ringed with lenses that
|
||||
hovers three feet off the ground and *watches*, and the watching is the weapon —
|
||||
everything it looks at is being scored, ranked, and found wanting.
|
||||
|
||||
It is unfailingly polite. It refers to units it is destroying as "underperforming
|
||||
content." It refers to the Selector as "a legacy account."
|
||||
|
||||
- Fast, fragile for a hero, continuous beam that melts armour.
|
||||
- Cannot capture or hold ground. It only ever passes through, sampling.
|
||||
|
||||
## Air
|
||||
|
||||
Late-war escalation. Both sides looked up at the same time.
|
||||
|
||||
- **45 Flyer** (`flyer45`, MRP): a 7-inch single with tonearm rotor blades and a
|
||||
scavenged engine pod bolted through the spindle hole. Loud, fast, made of
|
||||
rubbish, held together by belief.
|
||||
- **Compression Drone** (`compressor`, Stream): from the original Stream roster —
|
||||
projects a shrinking blue field; things inside get smaller and worse.
|
||||
- Launch pads: **Launch Ramp** (`launchramp`) and **Drone Port** (`droneport`).
|
||||
|
||||
## Scouts
|
||||
|
||||
- **Boom Bike** (`boombike`, MRP): a trike with two speaker cones where the
|
||||
pillion should be. Nothing outruns it. Nothing wants to be near it either.
|
||||
- **Crawler** (`crawler`, Stream): a flat white rover that maps your base and
|
||||
files a report. Unarmed, silent, already gone by the time you shoot at it.
|
||||
|
||||
## Wax seeps
|
||||
|
||||
Wax fields are not deposits, they are **wounds**. Each field grows from a
|
||||
**seep point** — a crack over a buried pressing plant, a landfill vent, a
|
||||
flooded archive — and as long as the seep is open, the field regrows. Kill the
|
||||
economy of a map and it will heal; the Black Wax has all the time in the world.
|
||||
|
||||
## The campaign: SIDE A
|
||||
|
||||
Three missions. The MRP origin story, told from the landfill up.
|
||||
|
||||
### 1. FIRST PRESSING
|
||||
The Party wakes up. A pop-up van and a single digger in a wax field at the edge
|
||||
of the old industrial estate. Get a pressing plant running and press the first
|
||||
records of the new world — before the Stream's survey drones finish counting
|
||||
what's down here.
|
||||
*Objectives: build a Pressing Plant, bank 2000 records, survive the first probe.*
|
||||
|
||||
### 2. CRATE DIGGERS
|
||||
A Stream transcoder outpost has found something under the hill: a **master
|
||||
lacquer**, the original of an original. In six minutes it will be a receipt.
|
||||
Take the outpost apart and take back the master.
|
||||
*Objectives: destroy the Transcoder before the timer, keep the Selector alive.*
|
||||
|
||||
### 3. THE DROP
|
||||
The Algorithm has surfaced. Build the Mastering Studio, charge the orbital
|
||||
tonearm, and put the needle through it.
|
||||
*Objectives: build a Mastering Studio, fire THE DROP at the Algorithm, survive
|
||||
the counterattack.*
|
||||
|
||||
*(SIDE B — the Stream campaign, in which you are the polite one — is unwritten.)*
|
||||
|
||||
## Tone
|
||||
|
||||
Saturday-morning-cartoon apocalypse. The stakes are real (music is being deleted
|
||||
|
||||
@ -31,3 +31,7 @@ All images generated locally on MODELBEAST (`flux_local`) via `tools/mbgen.py`.
|
||||
| renders/scraper.png | 2026-07-20 | glossy seamless white tech aesthetic, soft blue ring lights, smooth rounded minimal design, autonomous white street sweeper vehicle inhaling black vinyl through a glowing blue intake, isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text |
|
||||
| renders/bufferbot.png | 2026-07-20 | glossy seamless white tech aesthetic, soft blue ring lights, smooth rounded minimal design, knee-high white sphere robot with a loading spinner face, isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text |
|
||||
| renders/drmwalker.png | 2026-07-20 | glossy seamless white tech aesthetic, soft blue ring lights, smooth rounded minimal design, tall faceless white mech with one blue ring-light eye and a glowing paywall screen chest, isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text |
|
||||
| renders/selector.png | 2026-07-25 | rusty junkyard robot aesthetic, rust metal with hot pink trim, analog dials and speaker parts, legendary hero DJ robot in a long coat with a crate of glowing gold dubplates under one arm and a tonearm staff, isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text |
|
||||
| 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 |
|
||||
|
||||
BIN
assets_src/renders/curator.png
Normal file
|
After Width: | Height: | Size: 928 KiB |
BIN
assets_src/renders/curator_raw.png
Normal file
|
After Width: | Height: | Size: 819 KiB |
BIN
assets_src/renders/droneport.png
Normal file
|
After Width: | Height: | Size: 935 KiB |
BIN
assets_src/renders/droneport_raw.png
Normal file
|
After Width: | Height: | Size: 832 KiB |
BIN
assets_src/renders/launchramp.png
Normal file
|
After Width: | Height: | Size: 895 KiB |
BIN
assets_src/renders/launchramp_raw.png
Normal file
|
After Width: | Height: | Size: 772 KiB |
BIN
assets_src/renders/selector.png
Normal file
|
After Width: | Height: | Size: 937 KiB |
BIN
assets_src/renders/selector_raw.png
Normal file
|
After Width: | Height: | Size: 805 KiB |
BIN
assets_src/renders_topdown/boombike.png
Normal file
|
After Width: | Height: | Size: 547 KiB |
BIN
assets_src/renders_topdown/boombike_raw.png
Normal file
|
After Width: | Height: | Size: 471 KiB |
BIN
assets_src/renders_topdown/compressor.png
Normal file
|
After Width: | Height: | Size: 969 KiB |
BIN
assets_src/renders_topdown/compressor_raw.png
Normal file
|
After Width: | Height: | Size: 850 KiB |
BIN
assets_src/renders_topdown/crawler.png
Normal file
|
After Width: | Height: | Size: 813 KiB |
BIN
assets_src/renders_topdown/crawler_raw.png
Normal file
|
After Width: | Height: | Size: 720 KiB |
BIN
assets_src/renders_topdown/flyer45.png
Normal file
|
After Width: | Height: | Size: 553 KiB |
BIN
assets_src/renders_topdown/flyer45_raw.png
Normal file
|
After Width: | Height: | Size: 484 KiB |
BIN
assets_src/renders_topdown/waxseed.png
Normal file
|
After Width: | Height: | Size: 774 KiB |
BIN
assets_src/renders_topdown/waxseed_raw.png
Normal file
|
After Width: | Height: | Size: 687 KiB |
|
Before Width: | Height: | Size: 305 B After Width: | Height: | Size: 527 B |
@ -403,3 +403,6 @@ production-icons:
|
||||
vehicle: 0, 32, 16, 16
|
||||
vehicle-disabled: 16, 32, 16, 16
|
||||
vehicle-alert: 32, 32, 16, 16
|
||||
aircraft: 0, 48, 16, 16
|
||||
aircraft-disabled: 16, 48, 16, 16
|
||||
aircraft-alert: 32, 48, 16, 16
|
||||
|
||||
@ -93,3 +93,14 @@ Container@PLAYER_WIDGETS:
|
||||
X: 7
|
||||
Y: 7
|
||||
ImageCollection: production-icons
|
||||
ProductionTypeButton@AIRCRAFT:
|
||||
Y: 99
|
||||
Width: 30
|
||||
Height: 30
|
||||
Background: button
|
||||
ProductionGroup: Aircraft
|
||||
Children:
|
||||
Image@ICON:
|
||||
X: 7
|
||||
Y: 7
|
||||
ImageCollection: production-icons
|
||||
|
||||
@ -87,3 +87,23 @@ power-thedrop =
|
||||
power-theshuffle =
|
||||
.name = THE SHUFFLE
|
||||
.description = Reality-compression strike. Terms of service apply.
|
||||
|
||||
## round 5: heroes, scouts, air
|
||||
actor-selector =
|
||||
.name = The Selector
|
||||
actor-curator =
|
||||
.name = The Curator
|
||||
actor-boombike =
|
||||
.name = Boom Bike
|
||||
actor-crawler =
|
||||
.name = Crawler
|
||||
actor-flyer45 =
|
||||
.name = 45 Flyer
|
||||
actor-compressor =
|
||||
.name = Compression Drone
|
||||
actor-launchramp =
|
||||
.name = Launch Ramp
|
||||
actor-droneport =
|
||||
.name = Drone Port
|
||||
actor-waxseed =
|
||||
.name = Wax Seep
|
||||
|
||||
|
Before Width: | Height: | Size: 86 B After Width: | Height: | Size: 86 B |
@ -58,5 +58,11 @@ Actors:
|
||||
Actor4: scraper
|
||||
Owner: BotB
|
||||
Location: 24,25
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 8,20
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 18,10
|
||||
|
||||
Rules: rules.yaml
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 113 B |
@ -76,3 +76,30 @@ Actors:
|
||||
Actor5: mpspawn
|
||||
Owner: Multi5
|
||||
Location: 45,84
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 12,45
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 20,20
|
||||
waxseed2: waxseed
|
||||
Owner: Neutral
|
||||
Location: 20,71
|
||||
waxseed3: waxseed
|
||||
Owner: Neutral
|
||||
Location: 45,45
|
||||
waxseed4: waxseed
|
||||
Owner: Neutral
|
||||
Location: 45,25
|
||||
waxseed5: waxseed
|
||||
Owner: Neutral
|
||||
Location: 45,66
|
||||
waxseed6: waxseed
|
||||
Owner: Neutral
|
||||
Location: 71,20
|
||||
waxseed7: waxseed
|
||||
Owner: Neutral
|
||||
Location: 71,71
|
||||
waxseed8: waxseed
|
||||
Owner: Neutral
|
||||
Location: 79,45
|
||||
|
||||
|
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 99 B |
@ -60,3 +60,30 @@ Actors:
|
||||
Actor3: mpspawn
|
||||
Owner: Multi3
|
||||
Location: 57,57
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 10,32
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 16,16
|
||||
waxseed2: waxseed
|
||||
Owner: Neutral
|
||||
Location: 16,49
|
||||
waxseed3: waxseed
|
||||
Owner: Neutral
|
||||
Location: 32,32
|
||||
waxseed4: waxseed
|
||||
Owner: Neutral
|
||||
Location: 32,10
|
||||
waxseed5: waxseed
|
||||
Owner: Neutral
|
||||
Location: 32,55
|
||||
waxseed6: waxseed
|
||||
Owner: Neutral
|
||||
Location: 49,16
|
||||
waxseed7: waxseed
|
||||
Owner: Neutral
|
||||
Location: 49,49
|
||||
waxseed8: waxseed
|
||||
Owner: Neutral
|
||||
Location: 55,32
|
||||
|
||||
|
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 89 B |
@ -44,3 +44,18 @@ Actors:
|
||||
Actor1: mpspawn
|
||||
Owner: Multi1
|
||||
Location: 34,20
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 7,7
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 7,34
|
||||
waxseed2: waxseed
|
||||
Owner: Neutral
|
||||
Location: 20,20
|
||||
waxseed3: waxseed
|
||||
Owner: Neutral
|
||||
Location: 34,7
|
||||
waxseed4: waxseed
|
||||
Owner: Neutral
|
||||
Location: 34,34
|
||||
|
||||
|
Before Width: | Height: | Size: 86 B After Width: | Height: | Size: 86 B |
@ -52,3 +52,9 @@ Actors:
|
||||
Actor8: adblocker
|
||||
Owner: Creeps
|
||||
Location: 25,22
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 8,20
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 18,10
|
||||
|
||||
|
Before Width: | Height: | Size: 86 B After Width: | Height: | Size: 86 B |
@ -36,3 +36,9 @@ Actors:
|
||||
Actor0: mpspawn
|
||||
Owner: Multi0
|
||||
Location: 16,16
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 10,10
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 24,23
|
||||
|
||||
@ -215,3 +215,152 @@ algorithm:
|
||||
DisplayRadarPing: true
|
||||
SupportPowerPaletteOrder: 10
|
||||
SupportPowerChargeBar:
|
||||
|
||||
# --- round 5: MRP hero + scout ---
|
||||
|
||||
selector:
|
||||
Inherits: ^ArmedUnit
|
||||
Voiced:
|
||||
VoiceSet: MrpVoice
|
||||
Buildable:
|
||||
Queue: Infantry
|
||||
BuildPaletteOrder: 30
|
||||
Prerequisites: mastering
|
||||
BuildLimit: 1
|
||||
Valued:
|
||||
Cost: 2000
|
||||
Tooltip:
|
||||
Name: actor-selector.name
|
||||
Health:
|
||||
HP: 40000
|
||||
Armor:
|
||||
Type: None
|
||||
Mobile:
|
||||
Locomotor: foot
|
||||
Speed: 42
|
||||
Armament:
|
||||
Weapon: dubplate
|
||||
RevealsShroud:
|
||||
Range: 7c0
|
||||
SelectionDecorations:
|
||||
|
||||
boombike:
|
||||
Inherits: ^ArmedUnit
|
||||
Voiced:
|
||||
VoiceSet: MrpVoice
|
||||
-WithSpriteBody:
|
||||
WithFacingSpriteBody:
|
||||
Buildable:
|
||||
Queue: Vehicle
|
||||
BuildPaletteOrder: 35
|
||||
Prerequisites: garage
|
||||
Valued:
|
||||
Cost: 400
|
||||
Tooltip:
|
||||
Name: actor-boombike.name
|
||||
Health:
|
||||
HP: 15000
|
||||
Armor:
|
||||
Type: Light
|
||||
Mobile:
|
||||
Locomotor: wheeled
|
||||
Speed: 140
|
||||
Armament:
|
||||
Weapon: bike_horn
|
||||
RevealsShroud:
|
||||
Range: 8c0
|
||||
|
||||
# --- round 5: air ---
|
||||
# ponytail: aircraft are Targetable as GroundActor too, so existing weapons can
|
||||
# hit them. Upgrade path: dedicated AA armaments + AirborneActor-only targeting.
|
||||
|
||||
^AirUnit:
|
||||
BodyOrientation:
|
||||
QuantizeFacingsFromSequence:
|
||||
RenderSprites:
|
||||
WithFacingSpriteBody:
|
||||
SelectionDecorations:
|
||||
AppearsOnRadar:
|
||||
UseLocation: true
|
||||
HitShape:
|
||||
HiddenUnderFog:
|
||||
Type: GroundPosition
|
||||
Selectable:
|
||||
Bounds: 1024, 1024
|
||||
Targetable:
|
||||
TargetTypes: GroundActor, AirborneActor
|
||||
MustBeDestroyed:
|
||||
FireWarheadsOnDeath:
|
||||
Weapon: UnitExplode
|
||||
Aircraft:
|
||||
CanHover: True
|
||||
CanSlide: True
|
||||
VTOL: True
|
||||
IdleBehavior: Land
|
||||
LandableTerrainTypes: Clear, Wax
|
||||
AirborneCondition: airborne
|
||||
CruisingCondition: cruising
|
||||
Hovers@CRUISING:
|
||||
RequiresCondition: cruising
|
||||
AttackMove:
|
||||
Guard:
|
||||
Guardable:
|
||||
AutoTarget:
|
||||
AutoTargetPriority@DEFAULT:
|
||||
ValidTargets: GroundActor, Structure
|
||||
RevealsShroud:
|
||||
Range: 7c0
|
||||
Type: CenterPosition
|
||||
MapEditorData:
|
||||
Categories: Aircraft
|
||||
|
||||
flyer45:
|
||||
Inherits: ^AirUnit
|
||||
Voiced:
|
||||
VoiceSet: MrpVoice
|
||||
Buildable:
|
||||
Queue: Aircraft
|
||||
BuildPaletteOrder: 10
|
||||
Prerequisites: launchramp
|
||||
Valued:
|
||||
Cost: 900
|
||||
Tooltip:
|
||||
Name: actor-flyer45.name
|
||||
Health:
|
||||
HP: 22000
|
||||
Armor:
|
||||
Type: Light
|
||||
Aircraft:
|
||||
Speed: 149
|
||||
Armament:
|
||||
Weapon: rotor_blade
|
||||
PauseOnCondition: !ammo
|
||||
AttackAircraft:
|
||||
AttackType: Hover
|
||||
FacingTolerance: 90
|
||||
AmmoPool:
|
||||
Ammo: 12
|
||||
ReloadDelay: 24
|
||||
AmmoCondition: ammo
|
||||
|
||||
launchramp:
|
||||
Inherits: ^EconBuilding
|
||||
Inherits@shape: ^Buildable2x2
|
||||
Buildable:
|
||||
Queue: Building
|
||||
BuildPaletteOrder: 55
|
||||
Prerequisites: garage
|
||||
Valued:
|
||||
Cost: 800
|
||||
Tooltip:
|
||||
Name: actor-launchramp.name
|
||||
Health:
|
||||
HP: 70000
|
||||
Production:
|
||||
Produces: Aircraft
|
||||
Exit@1:
|
||||
SpawnOffset: 0,-256,0
|
||||
ExitCell: 0,0
|
||||
Reservable:
|
||||
Power:
|
||||
Amount: -20
|
||||
|
||||
@ -263,3 +263,27 @@ garage:
|
||||
ExitCell: 0, 2
|
||||
Power:
|
||||
Amount: -20
|
||||
|
||||
# --- round 5: wax seep points (regrow the fields; see LORE.md "Wax seeps") ---
|
||||
|
||||
waxseed:
|
||||
BodyOrientation:
|
||||
RenderSprites:
|
||||
WithSpriteBody:
|
||||
Interactable:
|
||||
HiddenUnderShroud:
|
||||
Building:
|
||||
Footprint: x
|
||||
Dimensions: 1,1
|
||||
TerrainTypes: Clear, Wax
|
||||
AppearsOnRadar:
|
||||
RadarColorFromTerrain:
|
||||
Terrain: Wax
|
||||
AppearsOnMapPreview:
|
||||
Terrain: Wax
|
||||
SeedsResource:
|
||||
ResourceType: Wax
|
||||
Interval: 90
|
||||
MaxRange: 60
|
||||
MapEditorData:
|
||||
Categories: Resource spawn
|
||||
|
||||
@ -26,6 +26,10 @@ player:
|
||||
Type: Infantry
|
||||
Group: Infantry
|
||||
ReadyAudio: UnitReady
|
||||
ClassicProductionQueue@Aircraft:
|
||||
Type: Aircraft
|
||||
Group: Aircraft
|
||||
ReadyAudio: UnitReady
|
||||
ConquestVictoryConditions:
|
||||
MissionObjectives:
|
||||
WinNotification: Win
|
||||
|
||||
@ -202,3 +202,108 @@ drmwalker:
|
||||
Speed: 42
|
||||
Armament:
|
||||
Weapon: feedback_burst
|
||||
|
||||
# --- round 5: Stream hero + scout ---
|
||||
|
||||
curator:
|
||||
Inherits: ^ArmedUnit
|
||||
Voiced:
|
||||
VoiceSet: StreamVoice
|
||||
Buildable:
|
||||
Queue: Infantry
|
||||
BuildPaletteOrder: 30
|
||||
Prerequisites: algorithm
|
||||
BuildLimit: 1
|
||||
Valued:
|
||||
Cost: 2000
|
||||
Tooltip:
|
||||
Name: actor-curator.name
|
||||
Health:
|
||||
HP: 25000
|
||||
Armor:
|
||||
Type: Light
|
||||
Mobile:
|
||||
Locomotor: foot
|
||||
Speed: 95
|
||||
Armament:
|
||||
Weapon: curator_beam
|
||||
RevealsShroud:
|
||||
Range: 8c0
|
||||
SelectionDecorations:
|
||||
|
||||
crawler:
|
||||
Inherits: ^Unit
|
||||
Voiced:
|
||||
VoiceSet: StreamVoice
|
||||
-WithSpriteBody:
|
||||
WithFacingSpriteBody:
|
||||
Buildable:
|
||||
Queue: Vehicle
|
||||
BuildPaletteOrder: 15
|
||||
Prerequisites: transcoder
|
||||
Valued:
|
||||
Cost: 300
|
||||
Tooltip:
|
||||
Name: actor-crawler.name
|
||||
Health:
|
||||
HP: 10000
|
||||
Armor:
|
||||
Type: Light
|
||||
Mobile:
|
||||
Locomotor: wheeled
|
||||
Speed: 155
|
||||
RevealsShroud:
|
||||
Range: 10c0
|
||||
|
||||
# --- round 5: air ---
|
||||
|
||||
compressor:
|
||||
Inherits: ^AirUnit
|
||||
Voiced:
|
||||
VoiceSet: StreamVoice
|
||||
Buildable:
|
||||
Queue: Aircraft
|
||||
BuildPaletteOrder: 11
|
||||
Prerequisites: droneport
|
||||
Valued:
|
||||
Cost: 700
|
||||
Tooltip:
|
||||
Name: actor-compressor.name
|
||||
Health:
|
||||
HP: 16000
|
||||
Armor:
|
||||
Type: Light
|
||||
Aircraft:
|
||||
Speed: 170
|
||||
Armament:
|
||||
Weapon: compress_field
|
||||
PauseOnCondition: !ammo
|
||||
AttackAircraft:
|
||||
AttackType: Hover
|
||||
FacingTolerance: 90
|
||||
AmmoPool:
|
||||
Ammo: 10
|
||||
ReloadDelay: 20
|
||||
AmmoCondition: ammo
|
||||
|
||||
droneport:
|
||||
Inherits: ^EconBuilding
|
||||
Inherits@shape: ^Buildable2x2
|
||||
Buildable:
|
||||
Queue: Building
|
||||
BuildPaletteOrder: 56
|
||||
Prerequisites: printfarm
|
||||
Valued:
|
||||
Cost: 800
|
||||
Tooltip:
|
||||
Name: actor-droneport.name
|
||||
Health:
|
||||
HP: 60000
|
||||
Production:
|
||||
Produces: Aircraft
|
||||
Exit@1:
|
||||
SpawnOffset: 0,-256,0
|
||||
ExitCell: 0,0
|
||||
Reservable:
|
||||
Power:
|
||||
Amount: -20
|
||||
|
||||
BIN
mods/vinylgod/sequences/assets/boombike.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
mods/vinylgod/sequences/assets/compressor.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
mods/vinylgod/sequences/assets/crawler.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
mods/vinylgod/sequences/assets/curator.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
mods/vinylgod/sequences/assets/droneport.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
mods/vinylgod/sequences/assets/flyer45.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
mods/vinylgod/sequences/assets/icon_curator.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
mods/vinylgod/sequences/assets/icon_droneport.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
mods/vinylgod/sequences/assets/icon_launchramp.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
mods/vinylgod/sequences/assets/icon_selector.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
mods/vinylgod/sequences/assets/launchramp.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
mods/vinylgod/sequences/assets/selector.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
mods/vinylgod/sequences/assets/waxseed.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
@ -213,3 +213,59 @@ smoke_m:
|
||||
Filename: sequences/assets/smoke.png
|
||||
Start: 6
|
||||
Length: 2
|
||||
|
||||
selector:
|
||||
idle:
|
||||
Filename: sequences/assets/selector.png
|
||||
icon:
|
||||
Filename: sequences/assets/icon_selector.png
|
||||
|
||||
curator:
|
||||
idle:
|
||||
Filename: sequences/assets/curator.png
|
||||
icon:
|
||||
Filename: sequences/assets/icon_curator.png
|
||||
|
||||
launchramp:
|
||||
idle:
|
||||
Filename: sequences/assets/launchramp.png
|
||||
icon:
|
||||
Filename: sequences/assets/icon_launchramp.png
|
||||
|
||||
droneport:
|
||||
idle:
|
||||
Filename: sequences/assets/droneport.png
|
||||
icon:
|
||||
Filename: sequences/assets/icon_droneport.png
|
||||
|
||||
boombike:
|
||||
idle:
|
||||
Filename: sequences/assets/boombike.png
|
||||
Facings: 16
|
||||
icon:
|
||||
Filename: sequences/assets/icon_boombike.png
|
||||
|
||||
crawler:
|
||||
idle:
|
||||
Filename: sequences/assets/crawler.png
|
||||
Facings: 16
|
||||
icon:
|
||||
Filename: sequences/assets/icon_crawler.png
|
||||
|
||||
flyer45:
|
||||
idle:
|
||||
Filename: sequences/assets/flyer45.png
|
||||
Facings: 16
|
||||
icon:
|
||||
Filename: sequences/assets/icon_flyer45.png
|
||||
|
||||
compressor:
|
||||
idle:
|
||||
Filename: sequences/assets/compressor.png
|
||||
Facings: 16
|
||||
icon:
|
||||
Filename: sequences/assets/icon_compressor.png
|
||||
|
||||
waxseed:
|
||||
idle:
|
||||
Filename: sequences/assets/waxseed.png
|
||||
|
||||
@ -153,3 +153,104 @@ theshuffle_impact:
|
||||
Explosions: bigboom
|
||||
ImpactSounds: audio/sfx_boom.wav
|
||||
ValidTargets: Ground, GroundActor
|
||||
|
||||
# --- round 5: heroes, scouts, air ---
|
||||
|
||||
dubplate:
|
||||
Inherits: ^GroundWeapon
|
||||
Report: audio/sfx_throw.wav
|
||||
ReloadDelay: 90
|
||||
Range: 5c512
|
||||
Projectile: Bullet
|
||||
Speed: 448
|
||||
LaunchAngle: 40
|
||||
Image: seven_inch_proj
|
||||
Sequences: idle
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 426
|
||||
Damage: 9000
|
||||
ValidTargets: GroundActor
|
||||
Versus:
|
||||
None: 100
|
||||
Wood: 90
|
||||
Light: 85
|
||||
Heavy: 65
|
||||
Warhead@2Eff: CreateEffect
|
||||
Explosions: bigboom
|
||||
ImpactSounds: audio/sfx_boom.wav
|
||||
ValidTargets: Ground, GroundActor
|
||||
|
||||
curator_beam:
|
||||
Inherits: ^GroundWeapon
|
||||
Report: audio/sfx_zap.wav
|
||||
ReloadDelay: 8
|
||||
Range: 4c512
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 42
|
||||
Damage: 1100
|
||||
ValidTargets: GroundActor
|
||||
Versus:
|
||||
None: 70
|
||||
Wood: 80
|
||||
Light: 110
|
||||
Heavy: 120
|
||||
Warhead@2Eff: CreateEffect
|
||||
Explosions: pop
|
||||
ValidTargets: Ground, GroundActor
|
||||
|
||||
bike_horn:
|
||||
Inherits: ^GroundWeapon
|
||||
Report: audio/sfx_bass.wav
|
||||
ReloadDelay: 35
|
||||
Range: 2c512
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 128
|
||||
Damage: 1400
|
||||
ValidTargets: GroundActor
|
||||
Versus:
|
||||
None: 120
|
||||
Wood: 30
|
||||
Light: 40
|
||||
Heavy: 10
|
||||
Warhead@2Eff: CreateEffect
|
||||
Explosions: pop
|
||||
ImpactSounds: audio/sfx_pop.wav
|
||||
ValidTargets: Ground, GroundActor
|
||||
|
||||
rotor_blade:
|
||||
Inherits: ^GroundWeapon
|
||||
Report: audio/sfx_throw.wav
|
||||
ReloadDelay: 22
|
||||
Range: 3c512
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 96
|
||||
Damage: 2600
|
||||
ValidTargets: GroundActor
|
||||
Versus:
|
||||
None: 95
|
||||
Wood: 60
|
||||
Light: 90
|
||||
Heavy: 55
|
||||
Warhead@2Eff: CreateEffect
|
||||
Explosions: pop
|
||||
ImpactSounds: audio/sfx_pop.wav
|
||||
ValidTargets: Ground, GroundActor
|
||||
|
||||
compress_field:
|
||||
Inherits: ^GroundWeapon
|
||||
Report: audio/sfx_zap.wav
|
||||
ReloadDelay: 28
|
||||
Range: 3c256
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 213
|
||||
Damage: 1900
|
||||
ValidTargets: GroundActor
|
||||
Versus:
|
||||
None: 110
|
||||
Wood: 70
|
||||
Light: 80
|
||||
Heavy: 45
|
||||
Warhead@2Eff: CreateEffect
|
||||
Explosions: pop
|
||||
ImpactSounds: audio/sfx_pop.wav
|
||||
ValidTargets: Ground, GroundActor
|
||||
|
||||
@ -30,6 +30,11 @@ VEHICLES = [
|
||||
('streamvan', 26, STR + 'sleek white autonomous delivery van with blue play-button roof logo'),
|
||||
('scraper', 24, STR + 'white autonomous street sweeper vehicle with glowing blue front intake'),
|
||||
('drmwalker', 30, STR + 'white quad-legged mech with one blue ring light, seen from above'),
|
||||
# round 5: scouts + aircraft
|
||||
('boombike', 20, MRP + 'small fast motorbike trike with two loudspeaker cones mounted on the back'),
|
||||
('crawler', 18, STR + 'small flat white six-wheeled scout rover with a blue sensor dome'),
|
||||
('flyer45', 22, MRP + 'flying machine built from a black vinyl record with spinning tonearm rotor blades and a small rusty engine pod'),
|
||||
('compressor', 22, STR + 'white quadcopter drone with four rotors and a glowing blue lens underneath'),
|
||||
]
|
||||
|
||||
|
||||
@ -47,7 +52,12 @@ def sheet16(im, size, out):
|
||||
print(f'[facings] {out}', flush=True)
|
||||
|
||||
|
||||
only = [a for a in sys.argv[1:] if not a.startswith('-')]
|
||||
skip_world = '--units-only' in sys.argv
|
||||
|
||||
for name, size, prompt in VEHICLES:
|
||||
if only and name not in only:
|
||||
continue
|
||||
raw = os.path.join(RENDERS, f'{name}_raw.png')
|
||||
cut = os.path.join(RENDERS, f'{name}.png')
|
||||
if not os.path.exists(cut):
|
||||
@ -55,6 +65,10 @@ for name, size, prompt in VEHICLES:
|
||||
run('bg_remove_local', {}, cut, asset_id=upload(raw))
|
||||
sheet16(Image.open(cut).convert('RGBA'), size, os.path.join(ASSETS, f'{name}.png'))
|
||||
|
||||
if skip_world or only:
|
||||
print('ALL DONE (units only)')
|
||||
sys.exit(0)
|
||||
|
||||
# terrain tile: seamless ground texture -> 32x32
|
||||
traw = os.path.join(RENDERS, 'terrain_raw.png')
|
||||
if not os.path.exists(traw):
|
||||
|
||||
@ -43,6 +43,11 @@ ACTORS = [
|
||||
('scraper', 22, STR + 'autonomous white street sweeper vehicle inhaling black vinyl through a glowing blue intake'),
|
||||
('bufferbot', 14, STR + 'knee-high white sphere robot with a loading spinner face'),
|
||||
('drmwalker', 28, STR + 'tall faceless white mech with one blue ring-light eye and a glowing paywall screen chest'),
|
||||
# round 5: heroes + air bases
|
||||
('selector', 22, MRP + 'legendary hero DJ robot in a long coat with a crate of glowing gold dubplates under one arm and a tonearm staff'),
|
||||
('curator', 22, STR + 'hovering white sphere ringed with many camera lenses, a single blue eye at the front, faint halo'),
|
||||
('launchramp', 64, MRP + 'scrap metal launch ramp pad with painted pink chevrons, floodlights and a parked flying vinyl disc'),
|
||||
('droneport', 64, STR + 'white circular drone landing pad building with recessed blue guide lights and folded launch arms'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
68
tools/place_seeds.py
Normal file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Place a neutral `waxseed` actor at the centre of every wax blob on every map.
|
||||
|
||||
Reads the resource plane straight out of each map.bin, finds blob centroids by
|
||||
flood fill, and appends the actors to map.yaml. Idempotent: strips previously
|
||||
placed waxseed entries first.
|
||||
"""
|
||||
import os, re, struct, glob
|
||||
|
||||
MAPS = glob.glob(os.path.join(os.path.dirname(__file__), '..', 'mods', 'vinylgod', 'maps', '*'))
|
||||
|
||||
|
||||
def resource_cells(path):
|
||||
d = open(path, 'rb').read()
|
||||
fmt, w, h = d[0], *struct.unpack_from('<HH', d, 1)
|
||||
tiles_off, heights_off, res_off = struct.unpack_from('<III', d, 5)
|
||||
assert fmt == 2 and heights_off == 0, path
|
||||
cells = set()
|
||||
for x in range(w):
|
||||
for y in range(h):
|
||||
if d[res_off + 2 * (x * h + y)] != 0:
|
||||
cells.add((x, y))
|
||||
return cells
|
||||
|
||||
|
||||
def blobs(cells):
|
||||
"""Flood-fill into connected blobs, return list of (cx, cy)."""
|
||||
seen, out = set(), []
|
||||
for start in sorted(cells):
|
||||
if start in seen:
|
||||
continue
|
||||
stack, blob = [start], []
|
||||
seen.add(start)
|
||||
while stack:
|
||||
x, y = stack.pop()
|
||||
blob.append((x, y))
|
||||
for n in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
|
||||
if n in cells and n not in seen:
|
||||
seen.add(n)
|
||||
stack.append(n)
|
||||
if len(blob) >= 6: # ignore specks
|
||||
out.append((round(sum(p[0] for p in blob) / len(blob)),
|
||||
round(sum(p[1] for p in blob) / len(blob))))
|
||||
return out
|
||||
|
||||
|
||||
for mapdir in sorted(MAPS):
|
||||
my, mb = os.path.join(mapdir, 'map.yaml'), os.path.join(mapdir, 'map.bin')
|
||||
if not (os.path.exists(my) and os.path.exists(mb)):
|
||||
continue
|
||||
centres = blobs(resource_cells(mb))
|
||||
s = open(my).read()
|
||||
s = re.sub(r'\n\twaxseed\d+: waxseed\n\t\tOwner: Neutral\n\t\tLocation: \d+,\d+', '', s)
|
||||
if 'Actors:' not in s:
|
||||
s = s.rstrip() + '\n\nActors:\n'
|
||||
block = ''.join(f'\n\twaxseed{i}: waxseed\n\t\tOwner: Neutral\n\t\tLocation: {x},{y}'
|
||||
for i, (x, y) in enumerate(centres))
|
||||
# append to the Actors section (it is the last block in our generated maps)
|
||||
head, _, tail = s.partition('Actors:')
|
||||
lines = tail.split('\n')
|
||||
cut = len(lines)
|
||||
for i, ln in enumerate(lines):
|
||||
if ln and not ln.startswith('\t') and i > 0: # next top-level key
|
||||
cut = i
|
||||
break
|
||||
tail = '\n'.join(lines[:cut]).rstrip() + block + '\n' + '\n'.join(lines[cut:])
|
||||
open(my, 'w').write(head + 'Actors:' + tail)
|
||||
print(f'{os.path.basename(mapdir)}: {len(centres)} seep(s) at {centres}')
|
||||