feat: power grid, pipes, CO2, temp zones, save/load, skills, diseases, storage APIs
New endpoints: - Save/Load: /api/action/save, /api/action/save_as, /api/action/load, /api/state/saves - Power grid: /api/state/power (circuits, load, overload detection) - Pipe contents: /api/state/pipes?type=gas|liquid (debug plumbing) - CO2 tracking: /api/state/co2 (find CO2 pockets) - Temp zones: /api/state/temperature/zones (hot/cold spots) - Morale: /api/state/morale - Diseases: /api/state/diseases (dupe infection + environmental germs) - Storage: /api/state/storage (building contents) - Skills: /api/state/duplicants/skills (attributes + skill trees) - Assign job: /api/action/assign_job New CLI commands: save, save_as, load, saves, power, pipes, co2, temp_zones, morale, diseases, storage, skills, assign_job CLI help reorganized with clear categories: Save/Load, Grid Analysis, Colony Management
This commit is contained in:
231
tools/oni_api.py
231
tools/oni_api.py
@ -498,6 +498,205 @@ def cmd_speed(args):
|
||||
result = api_post('/api/action/speed', {"speed": speed})
|
||||
_print_feedback(result)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Save / Load
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_saves(args):
|
||||
"""List saves. Usage: saves"""
|
||||
data = api_get('/api/state/saves')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
for s in data.get('saves', []):
|
||||
print(f" {s.get('name', '?'):40s} {s.get('size', 0)/1024:.0f} KB")
|
||||
|
||||
def cmd_save(args):
|
||||
"""Save game. Usage: save [name]"""
|
||||
name = ' '.join(args) if args else None
|
||||
result = api_post('/api/action/save', {"name": name} if name else {})
|
||||
_print_feedback(result)
|
||||
|
||||
def cmd_save_as(args):
|
||||
"""Save with specific name. Usage: save_as <name>"""
|
||||
if not args:
|
||||
print("Usage: save_as <name>")
|
||||
return
|
||||
result = api_post('/api/action/save_as', {"name": ' '.join(args)})
|
||||
_print_feedback(result)
|
||||
|
||||
def cmd_load(args):
|
||||
"""Load a save. Usage: load <name>"""
|
||||
if not args:
|
||||
print("Usage: load <name>")
|
||||
return
|
||||
name = ' '.join(args)
|
||||
print(f"[!] Loading save '{name}' — game will restart!")
|
||||
result = api_post('/api/action/load', {"name": name})
|
||||
_print_feedback(result)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Power Grid
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_power(args):
|
||||
"""Analyze power grid. Usage: power"""
|
||||
data = api_get('/api/state/power')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
print(f"Power Grid — {data.get('circuitCount', 0)} circuits")
|
||||
print()
|
||||
for c in data.get('circuits', []):
|
||||
overload = " *** OVERLOAD ***" if c.get('isOverloaded') else ""
|
||||
print(f" Circuit {c.get('id', '?')}: {c.get('wattsUsed', 0):.0f}W / {c.get('maxWatts', 0):.0f}W used{overload}")
|
||||
print()
|
||||
print(f"Generators ({len(data.get('generators', []))}):")
|
||||
for g in data.get('generators', []):
|
||||
status = 'ON' if g.get('isActive') else 'OFF'
|
||||
print(f" {g.get('name', '?'):25s} {g.get('watts', 0):.0f}W [{status}]")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pipes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_pipes(args):
|
||||
"""Inspect pipe contents. Usage: pipes [gas|liquid]"""
|
||||
ptype = args[0] if args else 'all'
|
||||
if ptype not in ('gas', 'liquid', 'all'):
|
||||
print("Usage: pipes [gas|liquid|all]")
|
||||
return
|
||||
data = api_get(f"/api/state/pipes?type={ptype}")
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
print(f"Pipe Contents ({data.get('pipeType', '?')}): {data.get('segmentCount', 0)} segments")
|
||||
for s in data.get('segments', [])[:20]:
|
||||
t = s.get('type', '?')
|
||||
el = s.get('element', '?')
|
||||
m = s.get('mass', 0)
|
||||
temp = s.get('temperature', 0)
|
||||
print(f" [{t}] {el:20s} {m:8.1f} kg {temp:6.1f} K")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CO2
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_co2(args):
|
||||
"""Find CO2 pockets. Usage: co2"""
|
||||
data = api_get('/api/state/co2')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
print(f"CO2 Pockets: {data.get('pocketCount', 0)} Total: {data.get('totalMassKg', 0):.0f} kg")
|
||||
for p in data.get('pockets', [])[:10]:
|
||||
print(f" ({p.get('x', '?')},{p.get('y', '?')}) {p.get('mass', 0):.1f} kg {p.get('temp', 0):.0f}°C")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Temperature
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_temp_zones(args):
|
||||
"""Analyze temperature zones. Usage: temp_zones"""
|
||||
data = api_get('/api/state/temperature/zones')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
print(f"Temperature Analysis:")
|
||||
print(f" Average: {data.get('averageC', 0):.0f}°C")
|
||||
print(f" Min: {data.get('minC', 0):.0f}°C")
|
||||
print(f" Max: {data.get('maxC', 0):.0f}°C")
|
||||
print(f" Samples: {data.get('sampleCount', 0)}")
|
||||
print()
|
||||
hotspots = data.get('hotSpots', [])
|
||||
if hotspots:
|
||||
print(f"Hot spots (>50°C):")
|
||||
for h in hotspots:
|
||||
print(f" ({h.get('x', '?')},{h.get('y', '?')}) {h.get('tempC', 0):.0f}°C")
|
||||
coldspots = data.get('coldSpots', [])
|
||||
if coldspots:
|
||||
print(f"Cold spots (<5°C):")
|
||||
for c in coldspots:
|
||||
print(f" ({c.get('x', '?')},{c.get('y', '?')}) {c.get('tempC', 0):.0f}°C")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Morale
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_morale(args):
|
||||
"""Check morale. Usage: morale"""
|
||||
data = api_get('/api/state/morale')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
for m in data if isinstance(data, list) else []:
|
||||
print(f" {m.get('name', '?'):12s} morale={m.get('morale', 0):.0f} qol={m.get('qualityOfLife', 0):.0f}")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Diseases
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_diseases(args):
|
||||
"""Check disease status. Usage: diseases"""
|
||||
data = api_get('/api/state/diseases')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
infected = data.get('infectedDuplicants', [])
|
||||
if infected:
|
||||
print(f"Infected dupes: {len(infected)}")
|
||||
for d in infected:
|
||||
print(f" {d.get('duplicant', '?')} - {d.get('disease', '?')} ({d.get('severity', '?')})")
|
||||
else:
|
||||
print("No infected duplicants.")
|
||||
germs = data.get('environmentGerms', {})
|
||||
if germs:
|
||||
print(f"Environmental germs:")
|
||||
for name, count in sorted(germs.items(), key=lambda x: -x[1])[:5]:
|
||||
print(f" {name}: {count:.0f}")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Storage
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_storage(args):
|
||||
"""Check storage. Usage: storage"""
|
||||
data = api_get('/api/state/storage')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
print(f"Storage buildings: {data.get('storageCount', 0)}")
|
||||
for s in data.get('storages', [])[:10]:
|
||||
print(f" {s.get('building', '?'):25s} ({s.get('x', '?')},{s.get('y', '?')}) "
|
||||
f"{s.get('totalMass', 0):.0f}/{s.get('capacity', 0):.0f} kg")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Skills / Job
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_skills(args):
|
||||
"""Show duplicant skills. Usage: skills"""
|
||||
data = api_get('/api/state/duplicants/skills')
|
||||
if 'error' in data:
|
||||
print(f"Error: {data['error']}")
|
||||
return
|
||||
for d in data if isinstance(data, list) else []:
|
||||
print(f" {d.get('name', '?'):12s} skill_pts={d.get('skillPoints', 0)} total_pts={d.get('totalSkillPointsGained', 0)}")
|
||||
for a in d.get('attributes', [])[:5]:
|
||||
print(f" {a.get('name', '?'):15s} = {a.get('value', 0)}")
|
||||
|
||||
def cmd_assign_job(args):
|
||||
"""Assign dupe to a job. Usage: assign_job <dupe_name> <chore_group>"""
|
||||
if len(args) < 2:
|
||||
print("Usage: assign_job <dupe_name> <chore_group>")
|
||||
print(" chore groups: Build, Dig, Cook, Farm, Ranch, Operate, Research, Store, Tidy, LifeSupport")
|
||||
return
|
||||
result = api_post('/api/action/assign_job', {
|
||||
"duplicantId": args[0],
|
||||
"choreGroup": args[1]
|
||||
})
|
||||
_print_feedback(result)
|
||||
|
||||
def _print_feedback(result):
|
||||
"""Pretty-print action feedback."""
|
||||
if result.get('success'):
|
||||
@ -611,6 +810,19 @@ COMMANDS = {
|
||||
'pause': cmd_pause,
|
||||
'unpause': cmd_unpause,
|
||||
'speed': cmd_speed,
|
||||
'save': cmd_save,
|
||||
'save_as': cmd_save_as,
|
||||
'load': cmd_load,
|
||||
'saves': cmd_saves,
|
||||
'power': cmd_power,
|
||||
'pipes': cmd_pipes,
|
||||
'co2': cmd_co2,
|
||||
'temp_zones': cmd_temp_zones,
|
||||
'morale': cmd_morale,
|
||||
'diseases': cmd_diseases,
|
||||
'storage': cmd_storage,
|
||||
'skills': cmd_skills,
|
||||
'assign_job': cmd_assign_job,
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -663,6 +875,25 @@ if __name__ == '__main__':
|
||||
print(" unpause [speed] Unpause the game at 1x/2x/3x speed")
|
||||
print(" speed <1|2|3> Set game speed while running")
|
||||
print("")
|
||||
print("=== Save / Load ===")
|
||||
print(" save [name] Save game (AI undo button)")
|
||||
print(" save_as <name> Save with custom name")
|
||||
print(" saves List save files")
|
||||
print(" load <name> Load a save (rollback)")
|
||||
print("")
|
||||
print("=== Grid Analysis ===")
|
||||
print(" power Power grid (circuits, load, overloads)")
|
||||
print(" pipes [gas|liquid|all] Pipe contents (debug plumbing)")
|
||||
print(" co2 Find CO2 pockets (colony killer!)")
|
||||
print(" temp_zones Temperature hot/cold spots")
|
||||
print("")
|
||||
print("=== Colony Management ===")
|
||||
print(" storages Storage building contents")
|
||||
print(" diseases Disease/infection overview")
|
||||
print(" skills Duplicant skills and attributes")
|
||||
print(" assign_job <name> <job> Assign dupe to a chore group")
|
||||
print(" morale Duplicant morale levels")
|
||||
print("")
|
||||
print("=== Batch / Priority (Advanced) ===")
|
||||
print(" batch <json_file> Execute batch plan")
|
||||
print(" priority_global <t> <p> Set global default priority")
|
||||
|
||||
Reference in New Issue
Block a user