Deepen faction economy and station planning

This commit is contained in:
2026-03-19 23:34:06 -04:00
parent 9a5040cf1f
commit cd1fe776a5
33 changed files with 3170 additions and 175 deletions

View File

@@ -19,6 +19,8 @@ internal sealed class ShipBehaviorStateMachine
{
idleState,
new PatrolShipBehaviorState(),
new AttackTargetShipBehaviorState(),
new TradeHaulShipBehaviorState(),
new ResourceHarvestShipBehaviorState("auto-mine", "ore", "mining"),
new ConstructStationShipBehaviorState(),
};

View File

@@ -93,6 +93,9 @@ internal sealed class ResourceHarvestShipBehaviorState : IShipBehaviorState
case ("dock", "docked"):
ship.DefaultBehavior.Phase = "unload";
break;
case ("unload", "unloaded"):
ship.DefaultBehavior.Phase = "undock";
break;
case ("undock", "undocked"):
ship.DefaultBehavior.Phase = "travel-to-node";
ship.DefaultBehavior.NodeId = null;
@@ -126,3 +129,58 @@ internal sealed class ConstructStationShipBehaviorState : IShipBehaviorState
}
}
}
internal sealed class AttackTargetShipBehaviorState : IShipBehaviorState
{
public string Kind => "attack-target";
public void Plan(SimulationEngine engine, ShipRuntime ship, SimulationWorld world) =>
engine.PlanAttackTarget(ship, world);
public void ApplyEvent(SimulationEngine engine, ShipRuntime ship, SimulationWorld world, string controllerEvent)
{
if (controllerEvent is "target-destroyed" or "target-lost")
{
ship.DefaultBehavior.TargetEntityId = null;
}
}
}
internal sealed class TradeHaulShipBehaviorState : IShipBehaviorState
{
public string Kind => "trade-haul";
public void Plan(SimulationEngine engine, ShipRuntime ship, SimulationWorld world) =>
engine.PlanTransportHaul(ship, world);
public void ApplyEvent(SimulationEngine engine, ShipRuntime ship, SimulationWorld world, string controllerEvent)
{
switch (ship.DefaultBehavior.Phase, controllerEvent)
{
case ("travel-to-source", "arrived"):
ship.DefaultBehavior.Phase = "dock-source";
break;
case ("dock-source", "docked"):
ship.DefaultBehavior.Phase = "load";
break;
case ("load", "loaded"):
ship.DefaultBehavior.Phase = "undock-from-source";
break;
case ("undock-from-source", "undocked"):
ship.DefaultBehavior.Phase = "travel-to-destination";
break;
case ("travel-to-destination", "arrived"):
ship.DefaultBehavior.Phase = "dock-destination";
break;
case ("dock-destination", "docked"):
ship.DefaultBehavior.Phase = "unload";
break;
case ("unload", "unloaded"):
ship.DefaultBehavior.Phase = "undock-from-destination";
break;
case ("undock-from-destination", "undocked"):
ship.DefaultBehavior.Phase = "travel-to-source";
break;
}
}
}

View File

@@ -9,6 +9,13 @@ public sealed class ShipPlanningState
public bool HasMiningCapability { get; set; }
public bool FactionWantsOre { get; set; }
public bool FactionWantsExpansion { get; set; }
public bool FactionWantsCombat { get; set; }
public bool FactionNeedsShipyard { get; set; }
public string? TargetEnemySystemId { get; set; }
public string? TargetEnemyEntityId { get; set; }
public string? TradeItemId { get; set; }
public string? TradeSourceStationId { get; set; }
public string? TradeDestinationStationId { get; set; }
public string? CurrentObjective { get; set; }
public ShipPlanningState Clone() => (ShipPlanningState)MemberwiseClone();
@@ -102,13 +109,45 @@ public sealed class SetPatrolObjectiveAction : GoapAction<ShipPlanningState>
}
}
public sealed class SetAttackObjectiveAction : GoapAction<ShipPlanningState>
{
public override string Name => "set-attack-objective";
public override float Cost => 1f;
public override bool CheckPreconditions(ShipPlanningState state) =>
string.Equals(state.ShipKind, "military", StringComparison.Ordinal)
&& state.FactionWantsCombat
&& state.TargetEnemyEntityId is not null;
public override ShipPlanningState ApplyEffects(ShipPlanningState state)
{
state.CurrentObjective = "attack-target";
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)
{
return;
}
ship.DefaultBehavior.Kind = "attack-target";
ship.DefaultBehavior.AreaSystemId = commander.ActiveBehavior?.AreaSystemId ?? ship.DefaultBehavior.AreaSystemId;
ship.DefaultBehavior.TargetEntityId = commander.ActiveBehavior?.TargetEntityId ?? ship.DefaultBehavior.TargetEntityId;
ship.DefaultBehavior.Phase = null;
}
}
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;
string.Equals(state.ShipKind, "construction", StringComparison.Ordinal)
&& (state.FactionWantsExpansion || state.FactionNeedsShipyard);
public override ShipPlanningState ApplyEffects(ShipPlanningState state)
{
@@ -129,6 +168,39 @@ public sealed class SetConstructionObjectiveAction : GoapAction<ShipPlanningStat
}
}
public sealed class SetTradeObjectiveAction : GoapAction<ShipPlanningState>
{
public override string Name => "set-trade-objective";
public override float Cost => 1f;
public override bool CheckPreconditions(ShipPlanningState state) =>
string.Equals(state.ShipKind, "transport", StringComparison.Ordinal)
&& state.TradeItemId is not null
&& state.TradeSourceStationId is not null
&& state.TradeDestinationStationId is not null;
public override ShipPlanningState ApplyEffects(ShipPlanningState state)
{
state.CurrentObjective = "trade-haul";
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 || commander.ActiveBehavior is null)
{
return;
}
ship.DefaultBehavior.Kind = "trade-haul";
ship.DefaultBehavior.ItemId = commander.ActiveBehavior.ItemId;
ship.DefaultBehavior.StationId = commander.ActiveBehavior.StationId;
ship.DefaultBehavior.TargetEntityId = commander.ActiveBehavior.TargetEntityId;
ship.DefaultBehavior.Phase ??= "travel-to-source";
}
}
public sealed class SetIdleObjectiveAction : GoapAction<ShipPlanningState>
{
public override string Name => "set-idle-objective";