Full base-building map against a live Stream bot holding The Algorithm behind a defended base. Building the Mastering Studio triggers a scripted all-in counterattack; silencing The Algorithm wins. Tick scan throttled to 1Hz. SIDE A is now three missions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
125 lines
3.7 KiB
Python
125 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate campaign mission 03 — THE DROP (finale: full base vs a live Stream base)."""
|
|
import os, struct
|
|
from PIL import Image
|
|
|
|
SIZE = 64
|
|
W = H = SIZE + 2
|
|
MAP = os.path.join(os.path.dirname(__file__), '..', 'mods', 'vinylgod', 'maps', 'm03')
|
|
os.makedirs(MAP, exist_ok=True)
|
|
|
|
WAX = [(14, 50, 6), (24, 40, 5), (32, 32, 6), (40, 24, 5), (50, 14, 6), (12, 30, 4), (52, 34, 4)]
|
|
|
|
data = bytearray()
|
|
data += struct.pack('<BHH', 2, W, H)
|
|
data += struct.pack('<III', 17, 0, 3 * W * H + 17)
|
|
data += b'\xff\x00\x00' * (W * H)
|
|
res = bytearray(2 * W * H)
|
|
for cx, cy, r in WAX:
|
|
for x in range(cx - r, cx + r + 1):
|
|
for y in range(cy - r, cy + r + 1):
|
|
d2 = (x - cx) ** 2 + (y - cy) ** 2
|
|
if d2 <= r * r and 0 < x < W - 1 and 0 < y < H - 1:
|
|
o = 2 * (x * H + y)
|
|
res[o], res[o + 1] = 1, max(2, round(12 * (1 - (d2 ** 0.5) / (r + 1))))
|
|
data += res
|
|
open(os.path.join(MAP, 'map.bin'), 'wb').write(data)
|
|
|
|
ACTORS = [
|
|
('spawn0', 'mpspawn', 'Neutral', 10, 54),
|
|
# the Party
|
|
('TheVan', 'popupvan', 'Player', 10, 54),
|
|
('dig1', 'digger', 'Player', 13, 54),
|
|
('dig2', 'digger', 'Player', 12, 56),
|
|
('guard1', 'stacktank', 'Player', 15, 52),
|
|
('guard2', 'spinner', 'Player', 16, 55),
|
|
('guard3', 'djgrunt', 'Player', 14, 57),
|
|
('guard4', 'djgrunt', 'Player', 15, 57),
|
|
# the Algorithm's seat
|
|
('TheAlgorithm', 'algorithm', 'Enemy', 52, 8),
|
|
('ehub', 'streamhub', 'Enemy', 48, 10),
|
|
('eproc1', 'transcoder', 'Enemy', 44, 8),
|
|
('eproc2', 'transcoder', 'Enemy', 55, 14),
|
|
('enode1', 'datacentre', 'Enemy', 50, 13),
|
|
('enode2', 'datacentre', 'Enemy', 54, 11),
|
|
('epods', 'podfarm', 'Enemy', 45, 13),
|
|
('eprint', 'printfarm', 'Enemy', 49, 16),
|
|
('eport', 'droneport', 'Enemy', 56, 18),
|
|
('etur1', 'adblocker', 'Enemy', 46, 17),
|
|
('etur2', 'adblocker', 'Enemy', 52, 18),
|
|
('etur3', 'adblocker', 'Enemy', 43, 11),
|
|
('escrap1', 'scraper', 'Enemy', 47, 20),
|
|
('escrap2', 'scraper', 'Enemy', 50, 20),
|
|
('emob1', 'drmwalker', 'Enemy', 44, 20),
|
|
('emob2', 'drmwalker', 'Enemy', 53, 22),
|
|
('emob3', 'bufferbot', 'Enemy', 46, 22),
|
|
('emob4', 'bufferbot', 'Enemy', 47, 22),
|
|
('emob5', 'bufferbot', 'Enemy', 48, 22),
|
|
# forward picket in the middle
|
|
('mid1', 'adblocker', 'Enemy', 34, 28),
|
|
('mid2', 'bufferbot', 'Enemy', 33, 30),
|
|
('mid3', 'bufferbot', 'Enemy', 35, 30),
|
|
]
|
|
actors = '\n'.join(f'\t{n}: {t}\n\t\tOwner: {o}\n\t\tLocation: {x},{y}' for n, t, o, x, y in ACTORS)
|
|
|
|
open(os.path.join(MAP, 'map.yaml'), 'w').write(f"""MapFormat: 12
|
|
|
|
RequiresMod: vinylgod
|
|
|
|
Title: 03 - The Drop
|
|
|
|
Author: Monster Robot Party
|
|
|
|
Tileset: VINYLGOD
|
|
|
|
MapSize: {W},{H}
|
|
|
|
Bounds: 1,1,{SIZE},{SIZE}
|
|
|
|
Visibility: MissionSelector
|
|
|
|
Categories: Campaign
|
|
|
|
LockPreview: True
|
|
|
|
Players:
|
|
PlayerReference@Neutral:
|
|
Name: Neutral
|
|
OwnsWorld: True
|
|
NonCombatant: True
|
|
Faction: vinylgod
|
|
PlayerReference@Player:
|
|
Name: Player
|
|
Playable: True
|
|
Required: True
|
|
AllowBots: False
|
|
LockFaction: True
|
|
Faction: vinylgod
|
|
LockSpawn: True
|
|
LockTeam: True
|
|
Enemies: Enemy
|
|
PlayerReference@Enemy:
|
|
Name: Enemy
|
|
Faction: stream
|
|
Bot: wax
|
|
Enemies: Player
|
|
|
|
Actors:
|
|
{actors}
|
|
|
|
Rules: vinylgod|rules/campaign.yaml, rules.yaml
|
|
|
|
FluentMessages: vinylgod|fluent/lua.ftl
|
|
""")
|
|
|
|
im = Image.new('RGB', (W, H), (55, 50, 52))
|
|
for cx, cy, r in WAX:
|
|
for x in range(cx - r, cx + r + 1):
|
|
for y in range(cy - r, cy + r + 1):
|
|
if (x - cx) ** 2 + (y - cy) ** 2 <= r * r and 0 <= x < W and 0 <= y < H:
|
|
im.putpixel((x, y), (255, 45, 150))
|
|
for _, _, o, x, y in ACTORS:
|
|
im.putpixel((x, y), (255, 255, 255) if o == 'Player' else (80, 150, 255) if o == 'Enemy' else (240, 210, 90))
|
|
im.resize((W * 3, H * 3), Image.NEAREST).save(os.path.join(MAP, 'map.png'))
|
|
print('m03 written')
|