155 lines
5.6 KiB
C#
155 lines
5.6 KiB
C#
namespace SpaceGame.Simulation.Api.Simulation;
|
|
|
|
// ─── Planning State ────────────────────────────────────────────────────────────
|
|
|
|
public sealed class ShipPlanningState
|
|
{
|
|
public string ShipKind { get; set; } = string.Empty;
|
|
public bool HasMiningCapability { get; set; }
|
|
public bool FactionWantsOre { get; set; }
|
|
public bool FactionWantsExpansion { get; set; }
|
|
public string? CurrentObjective { get; set; }
|
|
|
|
public ShipPlanningState Clone() => (ShipPlanningState)MemberwiseClone();
|
|
}
|
|
|
|
// ─── Goals ─────────────────────────────────────────────────────────────────────
|
|
|
|
// A ship should always have an assigned objective. The planner picks the best one.
|
|
public sealed class AssignObjectiveGoal : GoapGoal<ShipPlanningState>
|
|
{
|
|
public override string Name => "assign-objective";
|
|
|
|
public override bool IsSatisfied(ShipPlanningState state) => state.CurrentObjective is not null;
|
|
|
|
public override float ComputePriority(ShipPlanningState state, SimulationWorld world, CommanderRuntime commander) =>
|
|
100f;
|
|
}
|
|
|
|
// ─── Actions ───────────────────────────────────────────────────────────────────
|
|
|
|
public sealed class SetMiningObjectiveAction : GoapAction<ShipPlanningState>
|
|
{
|
|
public override string Name => "set-mining-objective";
|
|
public override float Cost => 1f;
|
|
|
|
public override bool CheckPreconditions(ShipPlanningState state) =>
|
|
state.HasMiningCapability && state.FactionWantsOre;
|
|
|
|
public override ShipPlanningState ApplyEffects(ShipPlanningState state)
|
|
{
|
|
state.CurrentObjective = "auto-mine";
|
|
return state;
|
|
}
|
|
|
|
public override void Execute(SimulationEngine engine, SimulationWorld world, CommanderRuntime commander)
|
|
{
|
|
var ship = world.Ships.FirstOrDefault(s => s.Id == commander.ControlledEntityId);
|
|
if (ship is null || string.Equals(ship.DefaultBehavior.Kind, "auto-mine", StringComparison.Ordinal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ship.DefaultBehavior.Kind = "auto-mine";
|
|
ship.DefaultBehavior.Phase = null;
|
|
ship.DefaultBehavior.NodeId = null;
|
|
}
|
|
}
|
|
|
|
public sealed class SetPatrolObjectiveAction : GoapAction<ShipPlanningState>
|
|
{
|
|
public override string Name => "set-patrol-objective";
|
|
public override float Cost => 2f;
|
|
|
|
public override bool CheckPreconditions(ShipPlanningState state) =>
|
|
string.Equals(state.ShipKind, "military", StringComparison.Ordinal);
|
|
|
|
public override ShipPlanningState ApplyEffects(ShipPlanningState state)
|
|
{
|
|
state.CurrentObjective = "patrol";
|
|
return state;
|
|
}
|
|
|
|
public override void Execute(SimulationEngine engine, SimulationWorld world, CommanderRuntime commander)
|
|
{
|
|
var ship = world.Ships.FirstOrDefault(s => s.Id == commander.ControlledEntityId);
|
|
if (ship is null || string.Equals(ship.DefaultBehavior.Kind, "patrol", StringComparison.Ordinal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ship.DefaultBehavior.PatrolPoints.Count == 0)
|
|
{
|
|
var station = world.Stations.FirstOrDefault(s =>
|
|
s.FactionId == ship.FactionId &&
|
|
string.Equals(s.SystemId, ship.SystemId, StringComparison.Ordinal));
|
|
|
|
if (station is not null)
|
|
{
|
|
var radius = station.Radius + 90f;
|
|
ship.DefaultBehavior.PatrolPoints.AddRange(
|
|
[
|
|
new Vector3(station.Position.X + radius, station.Position.Y, station.Position.Z),
|
|
new Vector3(station.Position.X, station.Position.Y, station.Position.Z + radius),
|
|
new Vector3(station.Position.X - radius, station.Position.Y, station.Position.Z),
|
|
new Vector3(station.Position.X, station.Position.Y, station.Position.Z - radius),
|
|
]);
|
|
}
|
|
}
|
|
|
|
ship.DefaultBehavior.Kind = "patrol";
|
|
}
|
|
}
|
|
|
|
public sealed class SetConstructionObjectiveAction : GoapAction<ShipPlanningState>
|
|
{
|
|
public override string Name => "set-construction-objective";
|
|
public override float Cost => 1f;
|
|
|
|
public override bool CheckPreconditions(ShipPlanningState state) =>
|
|
string.Equals(state.ShipKind, "construction", StringComparison.Ordinal) && state.FactionWantsExpansion;
|
|
|
|
public override ShipPlanningState ApplyEffects(ShipPlanningState state)
|
|
{
|
|
state.CurrentObjective = "construct-station";
|
|
return state;
|
|
}
|
|
|
|
public override void Execute(SimulationEngine engine, SimulationWorld world, CommanderRuntime commander)
|
|
{
|
|
var ship = world.Ships.FirstOrDefault(s => s.Id == commander.ControlledEntityId);
|
|
if (ship is null || string.Equals(ship.DefaultBehavior.Kind, "construct-station", StringComparison.Ordinal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ship.DefaultBehavior.Kind = "construct-station";
|
|
ship.DefaultBehavior.Phase = null;
|
|
}
|
|
}
|
|
|
|
public sealed class SetIdleObjectiveAction : GoapAction<ShipPlanningState>
|
|
{
|
|
public override string Name => "set-idle-objective";
|
|
public override float Cost => 10f;
|
|
|
|
public override bool CheckPreconditions(ShipPlanningState state) => true;
|
|
|
|
public override ShipPlanningState ApplyEffects(ShipPlanningState state)
|
|
{
|
|
state.CurrentObjective = "idle";
|
|
return state;
|
|
}
|
|
|
|
public override void Execute(SimulationEngine engine, SimulationWorld world, CommanderRuntime commander)
|
|
{
|
|
var ship = world.Ships.FirstOrDefault(s => s.Id == commander.ControlledEntityId);
|
|
if (ship is null || string.Equals(ship.DefaultBehavior.Kind, "idle", StringComparison.Ordinal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ship.DefaultBehavior.Kind = "idle";
|
|
}
|
|
}
|