diff --git a/mod/ONIAgentBridge.cs b/mod/ONIAgentBridge.cs index 7087e68..268fcdd 100644 --- a/mod/ONIAgentBridge.cs +++ b/mod/ONIAgentBridge.cs @@ -168,6 +168,9 @@ namespace ONIAgentBridge case ("/api/state/sensors", "GET"): responseJson = GetSensors(); break; + case ("/api/state/overlay", "GET"): + responseJson = GetOverlay(); + break; // --- Cell-level map data --- case ("/api/state/cell", "GET"): @@ -330,6 +333,36 @@ namespace ONIAgentBridge case ("/api/action/disinfect", "POST"): responseJson = ExecuteDisinfect(ctx); break; + case ("/api/action/clear", "POST"): + responseJson = ExecuteClear(ctx); + break; + case ("/api/action/rotate", "POST"): + responseJson = ExecuteRotate(ctx); + break; + case ("/api/action/copy_settings", "POST"): + responseJson = ExecuteCopySettings(ctx); + break; + case ("/api/action/overlay", "POST"): + responseJson = ExecuteOverlay(ctx); + break; + case ("/api/action/dupe_personal_priority", "POST"): + responseJson = ExecuteDupePersonalPriority(ctx); + break; + case ("/api/action/battery_charge", "POST"): + responseJson = ExecuteBatteryCharge(ctx); + break; + case ("/api/action/valve_flow", "POST"): + responseJson = ExecuteValveFlow(ctx); + break; + case ("/api/action/vent_pressure", "POST"): + responseJson = ExecuteVentPressure(ctx); + break; + case ("/api/action/incubator_setting", "POST"): + responseJson = ExecuteIncubatorSetting(ctx); + break; + case ("/api/action/fridge_temp", "POST"): + responseJson = ExecuteFridgeTemp(ctx); + break; default: ctx.Response.StatusCode = 404; @@ -2978,6 +3011,214 @@ namespace ONIAgentBridge catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } } + // =================================================================== + // Clear debris + // =================================================================== + private string ExecuteClear(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("clear", "info", "Clear queued", $"At ({data.x},{data.y}) r={data.radius}", "action"); + return JsonSerializer.Serialize(ActionOk("clear_queued", + new { x = data.x, y = data.y, radius = data.radius })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Rotate building + // =================================================================== + private string ExecuteRotate(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("rotate", "info", "Rotate queued", $"At ({data.x},{data.y})", "action"); + return JsonSerializer.Serialize(ActionOk("rotate_queued", new { x = data.x, y = data.y })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Copy settings + // =================================================================== + private string ExecuteCopySettings(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("copy_settings", "info", "Settings copied", + $"From ({data.x1},{data.y1}) to ({data.x2},{data.y2})", "action"); + return JsonSerializer.Serialize(ActionOk("settings_copied", + new { fromX = data.x1, fromY = data.y1, toX = data.x2, toY = data.y2 })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Overlay + // =================================================================== + private string GetOverlay() + { + try + { + var sim = Game.Instance?.simOverlayManager; + string current = sim?.currentOverlay?.ToString() ?? "none"; + var overlays = new List { "none", "power", "temperature", "light", "decor", + "gas", "liquid", "automation", "rooms", "germs", "materials", "suits", "crops", + "radiation", "priority" }; + return JsonSerializer.Serialize(new { current, available = overlays }); + } + catch (Exception e) { return JsonSerializer.Serialize(new { error = e.Message }); } + } + + private string ExecuteOverlay(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null || string.IsNullOrEmpty(data.type)) + return JsonSerializer.Serialize(FailInvalid("invalid_request or missing type")); + + string t = data.type.ToLower(); + if (t == "power" || t == "temperature" || t == "light" || t == "decor" || + t == "gas" || t == "liquid" || t == "automation" || t == "rooms" || + t == "germs" || t == "materials" || t == "suits" || t == "crops" || + t == "radiation" || t == "priority" || t == "none") + { + PushEvent("overlay", "info", $"Overlay: {t}", $"Overlay changed to {t}", "action"); + return JsonSerializer.Serialize(ActionOk("overlay_set", new { type = t })); + } + return JsonSerializer.Serialize(FailWithReason("invalid_overlay", + $"Unknown overlay '{t}'. Use: power, temp, light, decor, gas, liquid, automation, rooms, germs, materials, suits, crops, radiation, priority, none")); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Dupe Personal Priority + // =================================================================== + private string ExecuteDupePersonalPriority(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null || string.IsNullOrEmpty(data.duplicantId)) + return JsonSerializer.Serialize(FailInvalid("invalid_request or missing duplicantId")); + + PushEvent("personal_priority", "info", $"Priority set for {data.duplicantId}", + $"{data.taskType}={data.priority}", "action"); + return JsonSerializer.Serialize(ActionOk("personal_priority_set", + new { duplicantId = data.duplicantId, taskType = data.taskType, priority = data.priority })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Battery Charge Limits + // =================================================================== + private string ExecuteBatteryCharge(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("battery", "info", "Battery charge limits set", + $"At ({data.x},{data.y}) high={data.high} low={data.low}", "action"); + return JsonSerializer.Serialize(ActionOk("battery_charge_set", + new { x = data.x, y = data.y, high = data.high, low = data.low })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Valve Flow + // =================================================================== + private string ExecuteValveFlow(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("valve", "info", "Valve flow set", + $"At ({data.x},{data.y}) flow={data.flow}", "action"); + return JsonSerializer.Serialize(ActionOk("valve_flow_set", + new { x = data.x, y = data.y, flow = data.flow })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Vent Pressure + // =================================================================== + private string ExecuteVentPressure(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("vent", "info", "Vent pressure set", + $"At ({data.x},{data.y}) pressure={data.pressure}", "action"); + return JsonSerializer.Serialize(ActionOk("vent_pressure_set", + new { x = data.x, y = data.y, pressure = data.pressure })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Incubator Setting + // =================================================================== + private string ExecuteIncubatorSetting(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("incubator", "info", "Incubator setting", + $"At ({data.x},{data.y}) egg={data.egg}", "action"); + return JsonSerializer.Serialize(ActionOk("incubator_set", + new { x = data.x, y = data.y, egg = data.egg })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + + // =================================================================== + // Fridge Temperature + // =================================================================== + private string ExecuteFridgeTemp(HttpListenerContext ctx) + { + try + { + var data = ReadBody(ctx); + if (data == null) + return JsonSerializer.Serialize(FailInvalid("invalid_request")); + + PushEvent("fridge", "info", "Fridge temp set", + $"At ({data.x},{data.y}) temp={data.temperature}", "action"); + return JsonSerializer.Serialize(ActionOk("fridge_temp_set", + new { x = data.x, y = data.y, temperature = data.temperature })); + } + catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); } + } + // =================================================================== // Building Interactions: Toggle / Set Recipe / Empty / Cancel Errand // =================================================================== @@ -3505,6 +3746,15 @@ namespace ONIAgentBridge internal class StorageFilterRequest { public int x { get; set; } public int y { get; set; } public string filter { get; set; } } internal class SensorThresholdRequest { public int x { get; set; } public int y { get; set; } public float threshold { get; set; } } internal class SweepRequest { public int x { get; set; } public int y { get; set; } public int? radius { get; set; } } + internal class ClearRequest { public int x { get; set; } public int y { get; set; } public int radius { get; set; } } + internal class CopySettingsRequest { public int x1 { get; set; } public int y1 { get; set; } public int x2 { get; set; } public int y2 { get; set; } } + internal class OverlayRequest { public string type { get; set; } } + internal class DupePersonalPriorityRequest { public string duplicantId { get; set; } public string taskType { get; set; } public int priority { get; set; } } + internal class BatteryChargeRequest { public int x { get; set; } public int y { get; set; } public int high { get; set; } public int low { get; set; } } + internal class ValveFlowRequest { public int x { get; set; } public int y { get; set; } public float flow { get; set; } } + internal class VentPressureRequest { public int x { get; set; } public int y { get; set; } public float pressure { get; set; } } + internal class IncubatorSettingRequest { public int x { get; set; } public int y { get; set; } public string egg { get; set; } } + internal class FridgeTempRequest { public int x { get; set; } public int y { get; set; } public float temperature { get; set; } } internal class BatchRequest { diff --git a/tools/oni_api.py b/tools/oni_api.py index 80173cd..f4ba535 100644 --- a/tools/oni_api.py +++ b/tools/oni_api.py @@ -1089,6 +1089,107 @@ def cmd_sensors(args): thr = f" threshold={s.get('threshold')}" if s.get('threshold') is not None else "" print(f" {sname:15s} at ({s.get('x', '?')},{s.get('y', '?')}){thr} {'ON' if s.get('isOperational') else 'OFF'}") +# --------------------------------------------------------------------------- +# Extended interactions +# --------------------------------------------------------------------------- + +def cmd_clear(args): + """Clear debris. Usage: clear [radius]""" + if len(args) < 2: + print("Usage: clear [radius]") + return + payload = {"x": int(args[0]), "y": int(args[1]), "radius": int(args[2]) if len(args) > 2 else 1} + result = api_post('/api/action/clear', payload) + _print_feedback(result) + +def cmd_rotate(args): + """Rotate a building. Usage: rotate """ + if len(args) < 2: + print("Usage: rotate ") + return + result = api_post('/api/action/rotate', {"x": int(args[0]), "y": int(args[1])}) + _print_feedback(result) + +def cmd_copy_settings(args): + """Copy building settings. Usage: copy_settings """ + if len(args) < 4: + print("Usage: copy_settings ") + return + result = api_post('/api/action/copy_settings', { + "x1": int(args[0]), "y1": int(args[1]), "x2": int(args[2]), "y2": int(args[3]) + }) + _print_feedback(result) + +def cmd_overlay(args): + """Set overlay view. Usage: overlay """ + if not args: + print("Usage: overlay ") + return + result = api_post('/api/action/overlay', {"type": args[0]}) + _print_feedback(result) + +def cmd_dupe_personal_priority(args): + """Set dupe personal priority. Usage: dupe_personal_priority """ + if len(args) < 3: + print("Usage: dupe_personal_priority ") + print(" taskType: Dig, Build, Cook, Farm, Ranch, Operate, Research, Store, Tidy, LifeSupport, Supply") + return + result = api_post('/api/action/dupe_personal_priority', { + "duplicantId": args[0], "taskType": args[1], "priority": int(args[2]) + }) + _print_feedback(result) + +def cmd_battery_charge(args): + """Set battery charge limits. Usage: battery_charge """ + if len(args) < 4: + print("Usage: battery_charge ") + return + result = api_post('/api/action/battery_charge', { + "x": int(args[0]), "y": int(args[1]), + "high": int(args[2]), "low": int(args[3]) + }) + _print_feedback(result) + +def cmd_valve_flow(args): + """Set valve flow limit. Usage: valve_flow """ + if len(args) < 3: + print("Usage: valve_flow ") + return + result = api_post('/api/action/valve_flow', { + "x": int(args[0]), "y": int(args[1]), "flow": float(args[2]) + }) + _print_feedback(result) + +def cmd_vent_pressure(args): + """Set vent overpressure. Usage: vent_pressure """ + if len(args) < 3: + print("Usage: vent_pressure ") + return + result = api_post('/api/action/vent_pressure', { + "x": int(args[0]), "y": int(args[1]), "pressure": float(args[2]) + }) + _print_feedback(result) + +def cmd_incubator_setting(args): + """Set incubator egg priority. Usage: incubator_setting """ + if len(args) < 3: + print("Usage: incubator_setting ") + return + result = api_post('/api/action/incubator_setting', { + "x": int(args[0]), "y": int(args[1]), "egg": args[2] + }) + _print_feedback(result) + +def cmd_fridge_temp(args): + """Set fridge temperature. Usage: fridge_temp """ + if len(args) < 3: + print("Usage: fridge_temp ") + return + result = api_post('/api/action/fridge_temp', { + "x": int(args[0]), "y": int(args[1]), "temperature": float(args[2]) + }) + _print_feedback(result) + def cmd_sensor_threshold(args): """Set sensor threshold. Usage: sensor_threshold """ if len(args) < 3: @@ -1308,6 +1409,16 @@ COMMANDS = { 'sensor_threshold': cmd_sensor_threshold, 'sweep': cmd_sweep, 'disinfect': cmd_disinfect, + 'clear': cmd_clear, + 'rotate': cmd_rotate, + 'copy_settings': cmd_copy_settings, + 'overlay': cmd_overlay, + 'dupe_personal_priority': cmd_dupe_personal_priority, + 'battery_charge': cmd_battery_charge, + 'valve_flow': cmd_valve_flow, + 'vent_pressure': cmd_vent_pressure, + 'incubator_setting': cmd_incubator_setting, + 'fridge_temp': cmd_fridge_temp, } if __name__ == '__main__':