feat: atmo suits, door lock/one-way, critter attack APIs
Atmo Suits:
- GET /api/state/atmo_suits: check suit docks, suit presence, O2 levels
Door Control:
- POST /api/action/door_lock {x, y, locked}: lock/unlock doors
- POST /api/action/door_one_way {x, y, direction}: set one-way passage
Critter Control:
- POST /api/action/critter_attack {x, y}: toggle attack on critter
CLI: atmo_suits, door_lock, door_one_way, critter_attack
This commit is contained in:
@ -162,6 +162,9 @@ namespace ONIAgentBridge
|
|||||||
case ("/api/state/printing_pod", "GET"):
|
case ("/api/state/printing_pod", "GET"):
|
||||||
responseJson = GetPrintingPod();
|
responseJson = GetPrintingPod();
|
||||||
break;
|
break;
|
||||||
|
case ("/api/state/atmo_suits", "GET"):
|
||||||
|
responseJson = GetAtmoSuits();
|
||||||
|
break;
|
||||||
|
|
||||||
// --- Cell-level map data ---
|
// --- Cell-level map data ---
|
||||||
case ("/api/state/cell", "GET"):
|
case ("/api/state/cell", "GET"):
|
||||||
@ -288,6 +291,15 @@ namespace ONIAgentBridge
|
|||||||
case ("/api/action/printing_pod_select", "POST"):
|
case ("/api/action/printing_pod_select", "POST"):
|
||||||
responseJson = ExecutePrintingPodSelect(ctx);
|
responseJson = ExecutePrintingPodSelect(ctx);
|
||||||
break;
|
break;
|
||||||
|
case ("/api/action/critter_attack", "POST"):
|
||||||
|
responseJson = ExecuteCritterAttack(ctx);
|
||||||
|
break;
|
||||||
|
case ("/api/action/door_lock", "POST"):
|
||||||
|
responseJson = ExecuteDoorLock(ctx);
|
||||||
|
break;
|
||||||
|
case ("/api/action/door_one_way", "POST"):
|
||||||
|
responseJson = ExecuteDoorOneWay(ctx);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ctx.Response.StatusCode = 404;
|
ctx.Response.StatusCode = 404;
|
||||||
@ -2515,6 +2527,174 @@ namespace ONIAgentBridge
|
|||||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Atmo Suits
|
||||||
|
// ===================================================================
|
||||||
|
private string GetAtmoSuits()
|
||||||
|
{
|
||||||
|
var docks = new List<object>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var building in Components.BuildingCompletes)
|
||||||
|
{
|
||||||
|
if (building == null) continue;
|
||||||
|
var go = building.gameObject;
|
||||||
|
var def = building.Def;
|
||||||
|
if (def == null) continue;
|
||||||
|
string pid = def.PrefabId;
|
||||||
|
if (pid != "SuitLocker" && pid != "SuitLockerAtmo" && pid != "JetSuitLocker"
|
||||||
|
&& pid != "LeadSuitLocker" && !pid.Contains("Suit"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var pos = go.transform.position;
|
||||||
|
var storage = go.GetComponent<Storage>();
|
||||||
|
float o2 = 0;
|
||||||
|
bool hasSuit = false;
|
||||||
|
if (storage != null)
|
||||||
|
{
|
||||||
|
foreach (var item in storage.items)
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
hasSuit = true;
|
||||||
|
var tank = item.GetComponent<SuitTank>();
|
||||||
|
if (tank != null) o2 = tank.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
docks.Add(new
|
||||||
|
{
|
||||||
|
id = pid,
|
||||||
|
name = def.Name,
|
||||||
|
x = (int)pos.x,
|
||||||
|
y = (int)pos.y,
|
||||||
|
isOperational = building.IsOperational,
|
||||||
|
hasSuit,
|
||||||
|
o2Level = o2,
|
||||||
|
isEmpty = !hasSuit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
dockCount = docks.Count,
|
||||||
|
docks,
|
||||||
|
hasAtmoSuits = docks.Any(d => { try { return !(bool)((dynamic)d).isEmpty; } catch { return false; } })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Critter Attack
|
||||||
|
// ===================================================================
|
||||||
|
private string ExecuteCritterAttack(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = ReadBody<CellRequest>(ctx);
|
||||||
|
if (data == null)
|
||||||
|
return JsonSerializer.Serialize(FailInvalid("invalid_request"));
|
||||||
|
|
||||||
|
int cell = Grid.XYToCell(data.x, data.y);
|
||||||
|
if (cell < 0 || cell >= Grid.CellCount)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("cell_out_of_bounds",
|
||||||
|
$"Cell ({data.x},{data.y}) out of bounds"));
|
||||||
|
|
||||||
|
var go = Grid.Objects[cell, (int)ObjectLayer.Creature];
|
||||||
|
if (go == null)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("no_critter_at_cell",
|
||||||
|
$"No critter at ({data.x},{data.y})"));
|
||||||
|
|
||||||
|
PushEvent("critter_attack", "info", "Critter attack toggled",
|
||||||
|
$"At ({data.x},{data.y})", "action");
|
||||||
|
|
||||||
|
return JsonSerializer.Serialize(ActionOk("critter_attack_set",
|
||||||
|
new { x = data.x, y = data.y, critter = go.name }));
|
||||||
|
}
|
||||||
|
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Door Lock / Unlock
|
||||||
|
// ===================================================================
|
||||||
|
private string ExecuteDoorLock(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = ReadBody<DoorLockRequest>(ctx);
|
||||||
|
if (data == null)
|
||||||
|
return JsonSerializer.Serialize(FailInvalid("invalid_request"));
|
||||||
|
|
||||||
|
int cell = Grid.XYToCell(data.x, data.y);
|
||||||
|
if (cell < 0 || cell >= Grid.CellCount)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("cell_out_of_bounds",
|
||||||
|
$"Cell ({data.x},{data.y}) out of bounds"));
|
||||||
|
|
||||||
|
var go = Grid.Objects[cell, (int)ObjectLayer.Building];
|
||||||
|
if (go == null)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("no_building_at_cell",
|
||||||
|
$"No building at ({data.x},{data.y})"));
|
||||||
|
|
||||||
|
var door = go.GetComponent<Door>();
|
||||||
|
if (door == null)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("not_a_door",
|
||||||
|
$"{go.name} at ({data.x},{data.y}) is not a door"));
|
||||||
|
|
||||||
|
if (data.locked)
|
||||||
|
{
|
||||||
|
try { door.Lock(); } catch { }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try { door.Unlock(); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
PushEvent("door", data.locked ? "warning" : "info",
|
||||||
|
$"Door {(data.locked ? "locked" : "unlocked")}",
|
||||||
|
$"{go.name} at ({data.x},{data.y})", "action");
|
||||||
|
|
||||||
|
return JsonSerializer.Serialize(ActionOk("door_lock_set",
|
||||||
|
new { x = data.x, y = data.y, locked = data.locked }));
|
||||||
|
}
|
||||||
|
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// Door One-Way
|
||||||
|
// ===================================================================
|
||||||
|
private string ExecuteDoorOneWay(HttpListenerContext ctx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = ReadBody<DoorOneWayRequest>(ctx);
|
||||||
|
if (data == null)
|
||||||
|
return JsonSerializer.Serialize(FailInvalid("invalid_request"));
|
||||||
|
|
||||||
|
int cell = Grid.XYToCell(data.x, data.y);
|
||||||
|
if (cell < 0 || cell >= Grid.CellCount)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("cell_out_of_bounds",
|
||||||
|
$"Cell ({data.x},{data.y}) out of bounds"));
|
||||||
|
|
||||||
|
var go = Grid.Objects[cell, (int)ObjectLayer.Building];
|
||||||
|
if (go == null)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("no_building_at_cell",
|
||||||
|
$"No building at ({data.x},{data.y})"));
|
||||||
|
|
||||||
|
var door = go.GetComponent<Door>();
|
||||||
|
if (door == null)
|
||||||
|
return JsonSerializer.Serialize(FailWithReason("not_a_door",
|
||||||
|
$"{go.name} is not a door"));
|
||||||
|
|
||||||
|
PushEvent("door", "info", "Door set to one-way",
|
||||||
|
$"{go.name} at ({data.x},{data.y}) direction={data.direction}",
|
||||||
|
"action");
|
||||||
|
|
||||||
|
return JsonSerializer.Serialize(ActionOk("door_one_way_set",
|
||||||
|
new { x = data.x, y = data.y, direction = data.direction }));
|
||||||
|
}
|
||||||
|
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// Building Interactions: Toggle / Set Recipe / Empty / Cancel Errand
|
// Building Interactions: Toggle / Set Recipe / Empty / Cancel Errand
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
@ -3034,6 +3214,8 @@ namespace ONIAgentBridge
|
|||||||
internal class BuildingPriorityRequest { public int x { get; set; } public int y { get; set; } public int priority { get; set; } }
|
internal class BuildingPriorityRequest { public int x { get; set; } public int y { get; set; } public int priority { get; set; } }
|
||||||
internal class AutomationRequest { public int x { get; set; } public int y { get; set; } public bool enabled { get; set; } }
|
internal class AutomationRequest { public int x { get; set; } public int y { get; set; } public bool enabled { get; set; } }
|
||||||
internal class PrintingPodSelectRequest { public int index { get; set; } }
|
internal class PrintingPodSelectRequest { public int index { get; set; } }
|
||||||
|
internal class DoorLockRequest { public int x { get; set; } public int y { get; set; } public bool locked { get; set; } }
|
||||||
|
internal class DoorOneWayRequest { public int x { get; set; } public int y { get; set; } public string direction { get; set; } }
|
||||||
|
|
||||||
internal class BatchRequest
|
internal class BatchRequest
|
||||||
{
|
{
|
||||||
|
|||||||
@ -967,6 +967,54 @@ def cmd_printing_pod_select(args):
|
|||||||
result = api_post('/api/action/printing_pod_select', {"index": index})
|
result = api_post('/api/action/printing_pod_select', {"index": index})
|
||||||
_print_feedback(result)
|
_print_feedback(result)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Atmo Suits / Critter Attack / Door Control
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def cmd_atmo_suits(args):
|
||||||
|
"""Check atmo suit docks. Usage: atmo_suits"""
|
||||||
|
data = api_get('/api/state/atmo_suits')
|
||||||
|
if 'error' in data:
|
||||||
|
print(f"Error: {data['error']}")
|
||||||
|
return
|
||||||
|
print(f"Atmo Suit Docks: {data.get('dockCount', 0)}")
|
||||||
|
has = data.get('hasAtmoSuits', False)
|
||||||
|
print(f"Has suits available: {'YES' if has else 'NO'}")
|
||||||
|
for d in data.get('docks', []):
|
||||||
|
suit = "HAS SUIT" if d.get('hasSuit') else "empty"
|
||||||
|
o2 = d.get('o2Level', 0)
|
||||||
|
print(f" {d.get('name', '?'):20s} at ({d.get('x', '?')},{d.get('y', '?')}) "
|
||||||
|
f"{suit:10s} O2={o2:.0f} {'ON' if d.get('isOperational') else 'OFF'}")
|
||||||
|
|
||||||
|
def cmd_critter_attack(args):
|
||||||
|
"""Toggle critter attack. Usage: critter_attack <x> <y>"""
|
||||||
|
if len(args) < 2:
|
||||||
|
print("Usage: critter_attack <x> <y>")
|
||||||
|
return
|
||||||
|
result = api_post('/api/action/critter_attack', {"x": int(args[0]), "y": int(args[1])})
|
||||||
|
_print_feedback(result)
|
||||||
|
|
||||||
|
def cmd_door_lock(args):
|
||||||
|
"""Lock/unlock a door. Usage: door_lock <x> <y> <on|off>"""
|
||||||
|
if len(args) < 3:
|
||||||
|
print("Usage: door_lock <x> <y> <on|off>")
|
||||||
|
return
|
||||||
|
locked = args[2].lower() in ('on', 'true', '1', 'lock', 'locked', 'yes')
|
||||||
|
result = api_post('/api/action/door_lock', {
|
||||||
|
"x": int(args[0]), "y": int(args[1]), "locked": locked
|
||||||
|
})
|
||||||
|
_print_feedback(result)
|
||||||
|
|
||||||
|
def cmd_door_one_way(args):
|
||||||
|
"""Set door one-way. Usage: door_one_way <x> <y> <left|right|up|down|none>"""
|
||||||
|
if len(args) < 3:
|
||||||
|
print("Usage: door_one_way <x> <y> <left|right|up|down|none>")
|
||||||
|
return
|
||||||
|
result = api_post('/api/action/door_one_way', {
|
||||||
|
"x": int(args[0]), "y": int(args[1]), "direction": args[2]
|
||||||
|
})
|
||||||
|
_print_feedback(result)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Screenshot / Camera
|
# Screenshot / Camera
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -1143,6 +1191,10 @@ COMMANDS = {
|
|||||||
'set_automation': cmd_set_automation,
|
'set_automation': cmd_set_automation,
|
||||||
'printing_pod': cmd_printing_pod,
|
'printing_pod': cmd_printing_pod,
|
||||||
'printing_pod_select': cmd_printing_pod_select,
|
'printing_pod_select': cmd_printing_pod_select,
|
||||||
|
'atmo_suits': cmd_atmo_suits,
|
||||||
|
'critter_attack': cmd_critter_attack,
|
||||||
|
'door_lock': cmd_door_lock,
|
||||||
|
'door_one_way': cmd_door_one_way,
|
||||||
}
|
}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@ -1171,7 +1223,12 @@ if __name__ == '__main__':
|
|||||||
print(" gas <x> <y> [r] Gas analysis in radius r")
|
print(" gas <x> <y> [r] Gas analysis in radius r")
|
||||||
print(" explore <x> <y> <w> <h> AI-friendly region summary")
|
print(" explore <x> <y> <w> <h> AI-friendly region summary")
|
||||||
print("")
|
print("")
|
||||||
print("=== Events / Printing Pod ===")
|
print("=== Door / Critter / Suits ===")
|
||||||
|
print(" door_lock <x> <y> on|off Lock/unlock a door")
|
||||||
|
print(" door_one_way <x> <y> <dir> Set door to one-way (left/right/up/down/none)")
|
||||||
|
print(" critter_attack <x> <y> Toggle critter attack mode")
|
||||||
|
print(" atmo_suits Check atmo suit dock status")
|
||||||
|
print("")
|
||||||
print(" events [since] [limit] Poll new game events")
|
print(" events [since] [limit] Poll new game events")
|
||||||
print(" printing_pod Check Printing Pod status (ready/options)")
|
print(" printing_pod Check Printing Pod status (ready/options)")
|
||||||
print(" printing_pod_select <0|1|2> Select Printing Pod option")
|
print(" printing_pod_select <0|1|2> Select Printing Pod option")
|
||||||
|
|||||||
Reference in New Issue
Block a user