Phase A: Black Wax economy — resource layer, digger harvester, pressing plant, amp stack, vault, seeded maps, cash HUD, placeholder sprites

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-20 01:30:20 +10:00
parent 030b3f2698
commit 86ca71b9a2
20 changed files with 317 additions and 1 deletions

View File

@ -4,3 +4,15 @@ Container@PLAYER_WIDGETS:
LogicTicker@SIDEBAR_TICKER:
MenuButton@OPTIONS_BUTTON:
Key: escape
Container@CASH_COUNTER:
Logic: IngameCashCounterLogic
X: PARENT_WIDTH - WIDTH - 10
Y: 10
Width: 120
Height: 23
Children:
LabelWithTooltip@CASH:
Width: PARENT_WIDTH
Height: 23
Align: Right
Font: TinyBold

View File

@ -2,3 +2,13 @@ Cursors:
cursors/assets/default.png:
default:
Start: 0
harvest:
Start: 0
enter:
Start: 0
enter-blocked:
Start: 0
move:
Start: 0
move-blocked:
Start: 0

View File

@ -8,3 +8,16 @@ faction-vinylgod =
.name = Vinylgod
.description = Vinylgod Faction
Provide your own faction description here.
## economy.yaml
actor-digger =
.name = Crate Digger
actor-pressplant =
.name = Pressing Plant
actor-ampstack =
.name = Amp Stack
actor-vault =
.name = Record Vault
## world.yaml (resources)
resource-wax = Black Wax

Binary file not shown.

View File

@ -28,3 +28,12 @@ Players:
Faction: Random
Actors:
Actor1: pressplant
Owner: Creeps
Location: 12,12
Actor2: digger
Owner: Creeps
Location: 15,14
Actor3: ampstack
Owner: Creeps
Location: 10,14

Binary file not shown.

View File

@ -12,6 +12,7 @@ Notifications:
Rules:
vinylgod|rules/vinylgod.yaml
vinylgod|rules/economy.yaml
vinylgod|rules/mpspawn.yaml
vinylgod|rules/palettes.yaml
vinylgod|rules/player.yaml
@ -19,6 +20,7 @@ Rules:
Sequences:
vinylgod|sequences/vinylgod.yaml
vinylgod|sequences/economy.yaml
vinylgod|sequences/mapeditor.yaml
vinylgod|sequences/mpspawn.yaml

View File

@ -0,0 +1,100 @@
^EconSprite:
BodyOrientation:
QuantizeFacingsFromSequence:
RenderSprites:
WithSpriteBody:
SelectionDecorations:
AppearsOnRadar:
HitShape:
HiddenUnderFog:
digger:
Inherits: ^EconSprite
Tooltip:
Name: actor-digger.name
Selectable:
Bounds: 1024, 1024
Health:
HP: 60000
Armor:
Type: Heavy
Mobile:
Locomotor: wheeled
Speed: 72
Harvester:
Resources: Wax
BaleUnloadDelay: 1
SearchFromProcRadius: 15
SearchFromHarvesterRadius: 8
StoresResources:
Capacity: 20
Resources: Wax
DockClientManager:
RevealsShroud:
Range: 5c0
MapEditorData:
Categories: Units
^EconBuilding:
Inherits: ^EconSprite
Tooltip:
Health:
HP: 90000
Armor:
Type: Wood
RevealsShroud:
Range: 5c0
MapEditorData:
Categories: Buildings
pressplant:
Inherits: ^EconBuilding
Tooltip:
Name: actor-pressplant.name
Selectable:
Bounds: 2048, 2048
Building:
Footprint: xx ==
Dimensions: 2,2
TerrainTypes: Clear
Refinery:
DockHost:
Type: Unload
DockOffset: 0, 512, 0
StoresPlayerResources:
Capacity: 2000
Power:
Amount: -30
FreeActor:
Actor: digger
SpawnOffset: 0, 2
ampstack:
Inherits: ^EconBuilding
Tooltip:
Name: actor-ampstack.name
Selectable:
Bounds: 1024, 1024
Health:
HP: 40000
Building:
Footprint: x
Dimensions: 1,1
TerrainTypes: Clear
Power:
Amount: 100
vault:
Inherits: ^EconBuilding
Tooltip:
Name: actor-vault.name
Selectable:
Bounds: 1024, 1024
Health:
HP: 30000
Building:
Footprint: x
Dimensions: 1,1
TerrainTypes: Clear
StoresPlayerResources:
Capacity: 3000

View File

@ -1,6 +1,8 @@
^palettes:
PaletteFromGrayscale@greyscale:
Name: greyscale
PaletteFromGrayscale@terrain:
Name: terrain
PlayerColorPalette@player:
BasePalette: greyscale
PlayerColorShift:

View File

@ -5,6 +5,11 @@
player:
Inherits: ^baseplayer
DeveloperMode:
PlayerResources:
DefaultCash: 5000
ResourceValues:
Wax: 25
PowerManager:
editorplayer:
Inherits: ^baseplayer

View File

@ -29,12 +29,47 @@ world:
SpawnMapActors:
SpawnStartingUnits:
StartingUnits@Vinylgod:
BaseActor: vinylgod
BaseActor: pressplant
SupportActors: ampstack
Factions: vinylgod
ClassName: options-starting-units.unlabeled
BuildingInfluence:
PathFinder:
Locomotor@WHEELED:
Name: wheeled
TerrainSpeeds:
Clear: 100
Wax: 80
ResourceLayer:
RecalculateResourceDensity: true
ResourceTypes:
Wax:
ResourceIndex: 1
TerrainType: Wax
AllowedTerrainTypes: Clear
MaxDensity: 12
ResourceRenderer:
ResourceTypes:
Wax:
Sequences: wax01
Name: resource-wax
ResourceClaimLayer:
editorworld:
Inherits: ^baseworld
EditorResourceLayer:
RecalculateResourceDensity: true
ResourceTypes:
Wax:
ResourceIndex: 1
TerrainType: Wax
AllowedTerrainTypes: Clear
MaxDensity: 12
ResourceRenderer:
ResourceTypes:
Wax:
Sequences: wax01
Name: resource-wax
BuildableTerrainOverlay:
AllowedTerrainTypes:
Palette:

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

View File

@ -0,0 +1,19 @@
resources:
wax01:
Filename: sequences/assets/wax.png
digger:
idle:
Filename: sequences/assets/digger.png
pressplant:
idle:
Filename: sequences/assets/pressplant.png
ampstack:
idle:
Filename: sequences/assets/ampstack.png
vault:
idle:
Filename: sequences/assets/vault.png

View File

@ -7,6 +7,8 @@ General:
Terrain:
TerrainType@Clear:
Type: Clear
TerrainType@Wax:
Type: Wax
Templates:
Template@255:

77
tools/gen_placeholders.py Normal file
View File

@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Generate placeholder sprite PNGs for Phase A (stdlib only).
OpenRA PngSheet slices multi-frame sheets via an embedded tEXt chunk
"FrameSize: w,h" (see engine PngSheetLoader.cs). Real art replaces these in
Phase D keep the filenames.
"""
import os, struct, zlib
OUT = os.path.join(os.path.dirname(__file__), '..', 'mods', 'vinylgod', 'sequences', 'assets')
PINK = (255, 45, 150, 255)
DARK = (30, 26, 32, 255)
GREY = (90, 85, 95, 255)
CLEAR = (0, 0, 0, 0)
def chunk(tag, data):
return struct.pack('>I', len(data)) + tag + data + struct.pack('>I', zlib.crc32(tag + data))
def write_png(path, w, h, px, text=None):
raw = b''.join(b'\x00' + b''.join(bytes(px[y * w + x]) for x in range(w)) for y in range(h))
out = b'\x89PNG\r\n\x1a\n' + chunk(b'IHDR', struct.pack('>IIBBBBB', w, h, 8, 6, 0, 0, 0))
for k, v in (text or {}).items():
out += chunk(b'tEXt', k.encode() + b'\x00' + v.encode())
out += chunk(b'IDAT', zlib.compress(raw)) + chunk(b'IEND', b'')
open(path, 'wb').write(out)
print(f'wrote {path} {w}x{h}')
def canvas(w, h, fill=CLEAR):
return [fill] * (w * h)
def rect(px, w, x0, y0, x1, y1, c):
for y in range(y0, y1):
for x in range(x0, x1):
px[y * w + x] = c
def disc(px, w, cx, cy, r, c):
for y in range(int(cy - r), int(cy + r) + 1):
for x in range(int(cx - r), int(cx + r) + 1):
if (x - cx) ** 2 + (y - cy) ** 2 <= r * r:
px[y * w + x] = c
def box_sprite(path, size, body, accent):
px = canvas(size, size)
m = size // 8
rect(px, size, m, m, size - m, size - m, body)
rect(px, size, m, m, size - m, m + 2, accent) # top trim
disc(px, size, size / 2, size / 2, size // 5, accent) # centre disc
write_png(path, size, size, px)
os.makedirs(OUT, exist_ok=True)
# wax: 12 density frames, 32x32, horizontal strip
W, H, N = 32, 32, 12
px = canvas(W * N, H)
blob = [(9, 9), (22, 12), (14, 21), (24, 24), (7, 24), (17, 7), (26, 17), (11, 15),
(20, 19), (15, 27), (5, 16), (27, 8)]
for f in range(N):
for i in range(f + 1):
cx, cy = blob[i]
for dx in range(W): # draw into frame f's slice
pass
gx = f * W + cx
disc(px, W * N, gx, cy, 3, PINK if i % 3 == 0 else DARK)
write_png(os.path.join(OUT, 'wax.png'), W * N, H, px, text={'FrameSize': '32,32'})
box_sprite(os.path.join(OUT, 'digger.png'), 24, PINK, DARK)
box_sprite(os.path.join(OUT, 'pressplant.png'), 64, DARK, PINK)
box_sprite(os.path.join(OUT, 'ampstack.png'), 32, GREY, PINK)
box_sprite(os.path.join(OUT, 'vault.png'), 32, DARK, GREY)

30
tools/seed_wax.py Normal file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""Seed Wax resource cells into an OpenRA map.bin (format 2, no height data).
Layout (engine Map.cs SaveBinaryData): u8 fmt, u16 w, u16 h, u32 tilesOff(17),
u32 heightsOff(0), u32 resOff; tiles = w*h*(u16,u8) column-major (i*h+j);
resources = w*h*(u8 type, u8 density), same order.
Usage: seed_wax.py <map.bin> cx,cy,r [cx,cy,r ...] (circular blobs, density
scales down toward each blob's edge; type byte 1 = Wax ResourceIndex)
"""
import struct, sys
path = sys.argv[1]
data = bytearray(open(path, 'rb').read())
fmt, w, h = data[0], *struct.unpack_from('<HH', data, 1)
tiles_off, heights_off, res_off = struct.unpack_from('<III', data, 5)
assert fmt == 2 and heights_off == 0 and res_off == 3 * w * h + 17, 'unexpected map format'
for spec in sys.argv[2:]:
cx, cy, r = map(int, spec.split(','))
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 and 0 <= y < h:
density = max(2, round(12 * (1 - (d2 ** 0.5) / (r + 1))))
o = res_off + 2 * (x * h + y)
data[o], data[o + 1] = 1, density
open(path, 'wb').write(data)
print(f'{path}: seeded {len(sys.argv) - 2} blob(s) on {w}x{h} map')