v2.1.0 Complete rewrite: proper Mod API, Python toolchain, auto-camera, comprehensive SKILL

This commit is contained in:
JianFeeeee
2026-05-30 11:58:45 +08:00
parent 89c6707aa8
commit 3d70338087
10 changed files with 2479 additions and 3507 deletions

View File

@ -1,163 +1,185 @@
import json
import sys
from oni_api import api_post
#!/usr/bin/env python3
"""
ONI Agent — Blueprint Builder
==============================
Pre-built building modules for one-click deployment.
Usage:
python oni_builder.py list List all blueprints
python oni_builder.py show <name> Show blueprint details
python oni_builder.py build <name> <x> <y> Build blueprint at anchor point
"""
import json, sys
from oni_api import api_post, api_get
BLUEPRINTS = {
'spom': {
'name': 'SPOM (Self-Powered Oxygen Module)',
'description': 'Standard Rodriguez SPOM: electrolyzer + hydrogen generators',
'size': {'width': 8, 'height': 6},
'dig': {'x': -1, 'y': -1, 'width': 10, 'height': 8},
'buildings': [
{'id': 'Electrolyzer', 'x': 3, 'y': 2},
{'id': 'GasPump', 'x': 1, 'y': 2},
{'id': 'GasPump', 'x': 5, 'y': 2},
{'id': 'HydrogenGenerator', 'x': 1, 'y': 0},
{'id': 'HydrogenGenerator', 'x': 4, 'y': 0},
{'id': 'GasFilter', 'x': 3, 'y': 0},
'desc': 'Standard Rodriguez SPOM: electrolyzer + H2 generators + gas pumps',
'size': {'w': 8, 'h': 6},
'dig': {'dx': -1, 'dy': -1, 'w': 10, 'h': 8},
'steps': [
{'type': 'build', 'id': 'Electrolyzer', 'x': 3, 'y': 2},
{'type': 'build', 'id': 'GasPump', 'x': 1, 'y': 2},
{'type': 'build', 'id': 'GasPump', 'x': 5, 'y': 2},
{'type': 'build', 'id': 'HydrogenGenerator', 'x': 1, 'y': 0},
{'type': 'build', 'id': 'HydrogenGenerator', 'x': 4, 'y': 0},
],
},
'spom_mini': {
'name': 'Mini SPOM',
'description': 'Compact electrolyzer + 1 hydrogen generator for early game',
'size': {'width': 5, 'height': 4},
'dig': {'x': -1, 'y': -1, 'width': 7, 'height': 6},
'buildings': [
{'id': 'Electrolyzer', 'x': 2, 'y': 1},
{'id': 'GasPump', 'x': 1, 'y': 1},
{'id': 'HydrogenGenerator', 'x': 2, 'y': 0},
'desc': 'Compact electrolyzer + 1 hydrogen generator for early game',
'size': {'w': 5, 'h': 4},
'dig': {'dx': -1, 'dy': -1, 'w': 7, 'h': 6},
'steps': [
{'type': 'build', 'id': 'Electrolyzer', 'x': 2, 'y': 1},
{'type': 'build', 'id': 'GasPump', 'x': 1, 'y': 1},
{'type': 'build', 'id': 'HydrogenGenerator', 'x': 2, 'y': 0},
],
},
'toilet_loop': {
'name': 'Bathroom Water Loop',
'description': 'Lavatory -> Water Purifier -> Lavatory closed loop',
'size': {'width': 8, 'height': 4},
'dig': {'x': -1, 'y': -1, 'width': 10, 'height': 6},
'buildings': [
{'id': 'Lavatory', 'x': 1, 'y': 1},
{'id': 'Lavatory', 'x': 3, 'y': 1},
{'id': 'WaterPurifier', 'x': 6, 'y': 1},
{'id': 'LiquidPump', 'x': 6, 'y': 2},
],
},
'ranch_hatch': {
'name': 'Hatch Ranch',
'description': 'Standard hatch ranching module with feeder and incubator',
'size': {'width': 10, 'height': 6},
'dig': {'x': -1, 'y': -1, 'width': 12, 'height': 8},
'buildings': [
{'id': 'RanchStation', 'x': 1, 'y': 1},
{'id': 'Incubator', 'x': 4, 'y': 1},
{'id': 'StorageLocker', 'x': 8, 'y': 1},
'desc': '2 Lavatories -> Water Purifier closed loop',
'size': {'w': 8, 'h': 4},
'dig': {'dx': -1, 'dy': -1, 'w': 10, 'h': 6},
'steps': [
{'type': 'build', 'id': 'Lavatory', 'x': 1, 'y': 1},
{'type': 'build', 'id': 'Lavatory', 'x': 3, 'y': 1},
{'type': 'build', 'id': 'WaterPurifier', 'x': 6, 'y': 1},
],
},
'farm_mealwood': {
'name': 'Mealwood Farm',
'description': 'Basic mealwood farm: 5 planter boxes + storage',
'size': {'width': 6, 'height': 4},
'dig': {'x': -1, 'y': -1, 'width': 8, 'height': 6},
'buildings': [
{'id': 'PlanterBox', 'x': 1, 'y': 1},
{'id': 'PlanterBox', 'x': 3, 'y': 1},
{'id': 'PlanterBox', 'x': 5, 'y': 1},
{'id': 'PlanterBox', 'x': 1, 'y': 3},
{'id': 'PlanterBox', 'x': 3, 'y': 3},
{'id': 'StorageLocker', 'x': 5, 'y': 3},
'desc': '5 PlanterBox + 1 StorageLocker',
'size': {'w': 6, 'h': 4},
'dig': {'dx': -1, 'dy': -1, 'w': 8, 'h': 6},
'steps': [
{'type': 'build', 'id': 'PlanterBox', 'x': 1, 'y': 1},
{'type': 'build', 'id': 'PlanterBox', 'x': 3, 'y': 1},
{'type': 'build', 'id': 'PlanterBox', 'x': 5, 'y': 1},
{'type': 'build', 'id': 'PlanterBox', 'x': 1, 'y': 3},
{'type': 'build', 'id': 'PlanterBox', 'x': 3, 'y': 3},
{'type': 'build', 'id': 'StorageLocker', 'x': 5, 'y': 3},
],
},
'bedroom': {
'name': 'Standard Bedroom',
'desc': '4 Cots + decor for bedroom room bonus',
'size': {'w': 8, 'h': 4},
'dig': {'dx': -1, 'dy': -1, 'w': 10, 'h': 6},
'steps': [
{'type': 'build', 'id': 'Cot', 'x': 1, 'y': 1},
{'type': 'build', 'id': 'Cot', 'x': 3, 'y': 1},
{'type': 'build', 'id': 'Cot', 'x': 5, 'y': 1},
{'type': 'build', 'id': 'Cot', 'x': 7, 'y': 1},
],
},
'cooling': {
'name': 'Steam Turbine Cooler',
'description': 'Liquid cooling loop with steam turbine + aquatuner',
'size': {'width': 8, 'height': 6},
'dig': {'x': -1, 'y': -1, 'width': 10, 'height': 8},
'buildings': [
{'id': 'SteamTurbine', 'x': 1, 'y': 4},
{'id': 'SteamTurbine', 'x': 5, 'y': 4},
{'id': 'Aquatuner', 'x': 2, 'y': 1},
{'id': 'LiquidPump', 'x': 5, 'y': 1},
'desc': 'Aquatuner + Steam Turbine cooling loop',
'size': {'w': 8, 'h': 6},
'dig': {'dx': -1, 'dy': -1, 'w': 10, 'h': 8},
'steps': [
{'type': 'build', 'id': 'SteamTurbine', 'x': 1, 'y': 4},
{'type': 'build', 'id': 'SteamTurbine', 'x': 5, 'y': 4},
{'type': 'build', 'id': 'Aquatuner', 'x': 2, 'y': 1},
],
},
'bedroom': {
'name': 'Barracks / Bedroom',
'description': 'Basic bedroom module with cots and decorations',
'size': {'width': 8, 'height': 4},
'dig': {'x': -1, 'y': -1, 'width': 10, 'height': 6},
'buildings': [
{'id': 'Bed', 'x': 1, 'y': 1},
{'id': 'Bed', 'x': 3, 'y': 1},
{'id': 'Bed', 'x': 5, 'y': 1},
{'id': 'LadderBed', 'x': 1, 'y': 3},
{'id': 'LadderBed', 'x': 3, 'y': 3},
{'id': 'FlowerVase', 'x': 6, 'y': 1},
'ranch_hatch': {
'name': 'Hatch Ranch',
'desc': 'RanchStation + Incubator + StorageLocker',
'size': {'w': 10, 'h': 6},
'dig': {'dx': -1, 'dy': -1, 'w': 12, 'h': 8},
'steps': [
{'type': 'build', 'id': 'RanchStation', 'x': 1, 'y': 1},
{'type': 'build', 'id': 'Incubator', 'x': 4, 'y': 1},
{'type': 'build', 'id': 'StorageLocker', 'x': 8, 'y': 1},
],
},
}
def list_blueprints():
print(f"Available Blueprints ({len(BLUEPRINTS)}):")
print("=" * 60)
for key, bp in BLUEPRINTS.items():
print(f" {key:16s} {bp['name']:28s} {bp['size']['width']}x{bp['size']['height']}")
print(f" {'':16s} {bp['description']}")
def cmd_list(args):
print(f"Available blueprints ({len(BLUEPRINTS)}):")
for name, bp in sorted(BLUEPRINTS.items()):
print(f" {name:15s} {bp['name']}")
print(f" {'':15s} {bp['desc']}")
print(f" {'':15s} Size: {bp['size']['w']}x{bp['size']['h']} | {len(bp['steps'])} buildings")
print()
def cmd_show(args):
if not args:
print("Usage: show <blueprint_name>", file=sys.stderr)
return
name = args[0]
if name not in BLUEPRINTS:
print(f"Unknown blueprint: {name}", file=sys.stderr)
return
bp = BLUEPRINTS[name]
print(f"Blueprint: {bp['name']}")
print(f" {bp['desc']}")
print(f" Size: {bp['size']['w']}x{bp['size']['h']}")
if bp.get('dig'):
d = bp['dig']
print(f" Dig area: offset ({d['dx']},{d['dy']}) {d['w']}x{d['h']}")
print(f" Buildings ({len(bp['steps'])}):")
for s in bp['steps']:
print(f" {s['id']:25s} at anchor+({s['x']},{s['y']})")
def apply_blueprint(name, origin_x, origin_y):
bp = BLUEPRINTS.get(name)
if not bp:
print(f"[!] Blueprint '{name}' not found")
print(f" Use 'list' to see available blueprints")
return False
def cmd_build(args):
if len(args) < 3:
print("Usage: build <blueprint_name> <anchor_x> <anchor_y>", file=sys.stderr)
return
name, ax, ay = args[0], int(args[1]), int(args[2])
if name not in BLUEPRINTS:
print(f"Unknown blueprint: {name}", file=sys.stderr)
return
bp = BLUEPRINTS[name]
print(f"Applying blueprint: {bp['name']}")
print(f" Origin: ({origin_x}, {origin_y})")
print(f" Size: {bp['size']['width']} x {bp['size']['height']}")
print()
# 1. Dig the area first
if bp.get('dig'):
d = bp['dig']
dx, dy = ax + d['dx'], ay + d['dy']
print(f"Digging area: ({dx},{dy}) {d['w']}x{d['h']}...")
r = api_post('/api/action/dig', {"x": dx, "y": dy, "width": d['w'], "height": d['h']})
if r.get("success"):
info = r.get("data", {})
print(f" Queued {info.get('count', '?')} dig orders")
else:
print(f" Dig warning: {r.get('errorMessage', r.get('error', 'unknown'))}")
# 2. Build each structure
results = []
for step in bp['steps']:
bx, by = ax + step['x'], ay + step['y']
print(f"Building {step['id']} at ({bx},{by})...")
r = api_post('/api/action/build', {"buildingId": step['id'], "x": bx, "y": by})
if r.get("success"):
info = r.get("data", {})
has_mat = info.get('hasAllMaterials', False)
results.append(f"{step['id']} at ({bx},{by}) {'(may wait for materials)' if not has_mat else ''}")
else:
results.append(f"{step['id']} at ({bx},{by}): {r.get('errorMessage', r.get('error', 'unknown'))}")
dig = bp.get('dig', {'x': -1, 'y': -1, 'width': bp['size']['width'] + 2, 'height': bp['size']['height'] + 2})
dig_result = api_post('/api/action/dig', {
'x': origin_x + dig['x'],
'y': origin_y + dig['y'],
'width': dig['width'],
'height': dig['height'],
})
status = 'OK' if 'error' not in dig_result else dig_result.get('error', 'fail')
print(f" [Dig] area ({dig['width']}x{dig['height']}): {status}")
print(f"\n--- {bp['name']} build results ---")
for r in results:
print(r)
for b in bp['buildings']:
x = origin_x + b['x']
y = origin_y + b['y']
result = api_post('/api/action/build', {
'buildingId': b['id'],
'x': x,
'y': y,
})
ok = 'error' not in result
results.append({'building': b['id'], 'x': x, 'y': y, 'ok': ok})
status = 'OK' if ok else result.get('error', 'fail')
print(f" [Build] {b['id']:20s} at ({x:3d}, {y:3d}): {status}")
ok_count = sum(1 for r in results if r['ok'])
print()
print(f" Result: {ok_count}/{len(results)} buildings placed")
return ok_count == len(results)
if __name__ == '__main__':
cmd = sys.argv[1] if len(sys.argv) > 1 else 'list'
if cmd == 'list':
list_blueprints()
elif cmd == 'build':
if len(sys.argv) < 4:
print("Usage: python oni_builder.py build <blueprint_name> <origin_x> <origin_y>")
print()
list_blueprints()
sys.exit(1)
success = apply_blueprint(sys.argv[2], int(sys.argv[3]), int(sys.argv[4]))
sys.exit(0 if success else 1)
def main():
if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help", "help"):
print(__doc__)
return
cmd = sys.argv[1]
args = sys.argv[2:]
if cmd == "list":
cmd_list(args)
elif cmd == "show":
cmd_show(args)
elif cmd == "build":
cmd_build(args)
else:
print("Usage: python oni_builder.py <list|build>")
print(f"Unknown: {cmd}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()