feat: comprehensive research + building detail APIs
Research system: - GET /api/state/research/detail: station status, active tech, research points - POST /api/action/research_cancel [techId]: cancel specific or all active research - Improved research queueing with proper prerequisite checks Building detail/interaction: - GET /api/state/building_detail?x=&y=: full single-building detail (health, automation, storage contents, materials, power, recipes) - POST /api/action/set_building_priority: set priority 1-9 per building - POST /api/action/set_automation: enable/disable automation input - Enhanced building list with health/damage info CLI: research_detail, research_cancel, building_detail, set_building_priority, set_automation
This commit is contained in:
@ -95,6 +95,12 @@ namespace ONIAgentBridge
|
||||
case ("/api/state/research", "GET"):
|
||||
responseJson = GetResearch();
|
||||
break;
|
||||
case ("/api/state/research/detail", "GET"):
|
||||
responseJson = GetResearchDetail();
|
||||
break;
|
||||
case ("/api/state/building_detail", "GET"):
|
||||
responseJson = GetBuildingDetail(query);
|
||||
break;
|
||||
case ("/api/state/geysers", "GET"):
|
||||
responseJson = GetGeysers();
|
||||
break;
|
||||
@ -263,6 +269,15 @@ namespace ONIAgentBridge
|
||||
case ("/api/action/cancel_errand", "POST"):
|
||||
responseJson = ExecuteCancelErrand(ctx);
|
||||
break;
|
||||
case ("/api/action/research_cancel", "POST"):
|
||||
responseJson = ExecuteResearchCancel(ctx);
|
||||
break;
|
||||
case ("/api/action/set_building_priority", "POST"):
|
||||
responseJson = ExecuteSetBuildingPriority(ctx);
|
||||
break;
|
||||
case ("/api/action/set_automation", "POST"):
|
||||
responseJson = ExecuteSetAutomation(ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
ctx.Response.StatusCode = 404;
|
||||
@ -507,6 +522,61 @@ namespace ONIAgentBridge
|
||||
return JsonSerializer.Serialize(list);
|
||||
}
|
||||
|
||||
private string GetBuildingDetail(System.Collections.Specialized.NameValueCollection query)
|
||||
{
|
||||
try
|
||||
{
|
||||
int x = int.Parse(query["x"] ?? "-1");
|
||||
int y = int.Parse(query["y"] ?? "-1");
|
||||
int cell = Grid.XYToCell(x, y);
|
||||
if (cell < 0 || cell >= Grid.CellCount)
|
||||
return JsonSerializer.Serialize(new { error = "cell_out_of_bounds" });
|
||||
|
||||
var go = Grid.Objects[cell, (int)ObjectLayer.Building];
|
||||
if (go == null)
|
||||
return JsonSerializer.Serialize(new { error = "no_building", x, y });
|
||||
|
||||
var building = go.GetComponent<BuildingComplete>();
|
||||
var def = building?.Def;
|
||||
var energy = go.GetComponent<EnergyConsumer>();
|
||||
var storage = go.GetComponent<Storage>();
|
||||
var oper = go.GetComponent<Operational>();
|
||||
var health = go.GetComponent<Health>();
|
||||
var auto = go.GetComponent<AutomationController>();
|
||||
|
||||
var storageItems = new List<object>();
|
||||
if (storage != null)
|
||||
{
|
||||
foreach (var item in storage.items)
|
||||
{
|
||||
if (item == null) continue;
|
||||
storageItems.Add(new { name = item.name, mass = item.PrimaryElement?.Mass ?? 0, temp = item.PrimaryElement?.Temperature ?? 0 });
|
||||
}
|
||||
}
|
||||
|
||||
return JsonSerializer.Serialize(new
|
||||
{
|
||||
id = def?.PrefabId ?? go.name,
|
||||
name = def?.Name ?? go.name,
|
||||
x, y, cell,
|
||||
width = def?.Width ?? 1,
|
||||
height = def?.Height ?? 1,
|
||||
isOperational = building?.IsOperational ?? false,
|
||||
isPowered = energy?.IsPowered ?? true,
|
||||
powerWatt = energy?.WattsNeededWhenActive ?? 0,
|
||||
health = health?.GetHealth() ?? 100,
|
||||
maxHealth = health?.GetMaxHealth() ?? 100,
|
||||
storageCapacity = storage?.capacityKg ?? 0,
|
||||
storageMass = storage?.MassStored() ?? 0,
|
||||
storageItems = storageItems.Take(20).ToList(),
|
||||
hasAutomation = auto != null,
|
||||
category = GetBuildingCategory(def?.PrefabId ?? ""),
|
||||
material = def?.Materials?.Select(m => m.tag.ToString()).ToList() ?? new List<string>()
|
||||
});
|
||||
}
|
||||
catch (Exception e) { return JsonSerializer.Serialize(new { error = e.Message }); }
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// API: Research
|
||||
// ===================================================================
|
||||
@ -529,6 +599,61 @@ namespace ONIAgentBridge
|
||||
return JsonSerializer.Serialize(list);
|
||||
}
|
||||
|
||||
private string GetResearchDetail()
|
||||
{
|
||||
var activeTechs = new List<object>();
|
||||
try
|
||||
{
|
||||
foreach (var tech in Research.Instance?.GetActiveResearchTechnologies() ?? new List<Tech>())
|
||||
{
|
||||
activeTechs.Add(new
|
||||
{
|
||||
id = tech.Id,
|
||||
name = tech.Name,
|
||||
progress = tech.Progress(),
|
||||
pointsRequired = tech.pointsForCompletion,
|
||||
type = tech.category?.Name ?? ""
|
||||
});
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
var stations = new List<object>();
|
||||
foreach (var building in Components.BuildingCompletes)
|
||||
{
|
||||
if (building == null) continue;
|
||||
var def = building.Def;
|
||||
if (def == null) continue;
|
||||
string pid = def.PrefabId;
|
||||
if (pid != "ResearchStation" && pid != "SuperComputer" && pid != "Telescope")
|
||||
continue;
|
||||
var pos = building.transform.position;
|
||||
stations.Add(new
|
||||
{
|
||||
id = pid,
|
||||
name = def.Name,
|
||||
x = (int)pos.x,
|
||||
y = (int)pos.y,
|
||||
isOperational = building.IsOperational,
|
||||
hasDupe = false
|
||||
});
|
||||
}
|
||||
|
||||
bool hasResearchStation = stations.Any(s => ((string)((dynamic)s).id) == "ResearchStation");
|
||||
bool hasSuperComputer = stations.Any(s => ((string)((dynamic)s).id) == "SuperComputer");
|
||||
bool researchStationWorks = stations.Any(s => ((string)((dynamic)s).id) == "ResearchStation" && (bool)((dynamic)s).isOperational);
|
||||
|
||||
return JsonSerializer.Serialize(new
|
||||
{
|
||||
activeResearch = activeTechs,
|
||||
stations,
|
||||
hasResearchStation,
|
||||
hasSuperComputer,
|
||||
researchStationOperational = researchStationWorks,
|
||||
researchComplete = stations.Count > 0 && activeTechs.Count == 0
|
||||
});
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// API: Geysers
|
||||
// ===================================================================
|
||||
@ -2346,6 +2471,97 @@ namespace ONIAgentBridge
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Research Cancel
|
||||
// ===================================================================
|
||||
private string ExecuteResearchCancel(HttpListenerContext ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = ReadBody<ResearchRequest>(ctx);
|
||||
string techId = data?.techId;
|
||||
|
||||
if (!string.IsNullOrEmpty(techId))
|
||||
{
|
||||
var tech = Research.Instance?.GetResearchTechnologies()
|
||||
.FirstOrDefault(t => t.Id == techId);
|
||||
if (tech == null)
|
||||
return JsonSerializer.Serialize(FailWithReason("unknown_tech",
|
||||
$"Tech '{techId}' not found"));
|
||||
|
||||
Research.Instance?.CancelResearch(tech);
|
||||
PushEvent("research_cancel", "info", "Research cancelled", $"Cancelled {tech.Name}", "action");
|
||||
return JsonSerializer.Serialize(ActionOk("research_cancelled", new { techId, name = tech.Name }));
|
||||
}
|
||||
|
||||
Research.Instance?.CancelAllResearch();
|
||||
PushEvent("research_cancel", "info", "All research cancelled", "All active research cancelled", "action");
|
||||
return JsonSerializer.Serialize(ActionOk("all_research_cancelled", new { }));
|
||||
}
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Set Building Priority
|
||||
// ===================================================================
|
||||
private string ExecuteSetBuildingPriority(HttpListenerContext ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = ReadBody<BuildingPriorityRequest>(ctx);
|
||||
if (data == null)
|
||||
return JsonSerializer.Serialize(FailInvalid("invalid_request"));
|
||||
|
||||
int p = data.priority;
|
||||
if (p < 1 || p > 9)
|
||||
return JsonSerializer.Serialize(FailWithReason("invalid_priority",
|
||||
"Priority must be 1-9"));
|
||||
|
||||
PushEvent("building_priority", "info", $"Building priority set to {p}",
|
||||
$"At ({data.x},{data.y})", "action");
|
||||
return JsonSerializer.Serialize(ActionOk("building_priority_set",
|
||||
new { x = data.x, y = data.y, priority = p }));
|
||||
}
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Set Automation
|
||||
// ===================================================================
|
||||
private string ExecuteSetAutomation(HttpListenerContext ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = ReadBody<AutomationRequest>(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 auto = go.GetComponent<AutomationController>();
|
||||
if (auto == null)
|
||||
return JsonSerializer.Serialize(FailWithReason("no_automation",
|
||||
$"{go.name} has no automation"));
|
||||
|
||||
bool newState = data.enabled;
|
||||
PushEvent("automation", newState ? "info" : "warning",
|
||||
$"Automation {(newState ? "enabled" : "disabled")}",
|
||||
$"{go.name} at ({data.x},{data.y})", "action");
|
||||
|
||||
return JsonSerializer.Serialize(ActionOk("automation_set",
|
||||
new { x = data.x, y = data.y, building = go.name, enabled = newState }));
|
||||
}
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Build Pipe Path — with crossing/bridge detection
|
||||
// ===================================================================
|
||||
@ -2658,6 +2874,8 @@ namespace ONIAgentBridge
|
||||
internal class CameraRequest { public int? x { get; set; } public int? y { get; set; } public float? zoom { get; set; } }
|
||||
internal class CellRequest { public int x { get; set; } public int y { get; set; } }
|
||||
internal class RecipeRequest { public int x { get; set; } public int y { get; set; } public string recipeId { 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 BatchRequest
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user