Aetheria/Assets/Scripts/ServerShared/Agents/Agent.cs
Meta Vens 8f42265de3 Faction play 1: typed doctrine on Faction and one flight per faction per zone
FactionDoctrine/RoleDoctrine (Faction key 17), Flight as the only writer of
members' Target and Task, hail/grace/compliance, FollowTask/FollowState,
agent removal on Entities removal, doctrine-catalog command, FlightTests.
Not yet built or run; verification follows in later commits.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 14:18:41 +02:00

101 lines
3.6 KiB
C#

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;
using CultMath;
using static CultMath.math;
using float2 = CultMath.float2;
public class Agent
{
private const float FORWARD_DELTA_THRESHOLD = 20;
private const float THRUST_DELTA_THRESHOLD = 1;
protected BaseState _rootState;
private BaseState _currentState;
private VelocityLimit _velocityLimit;
public AgentTask Task { get; set; }
public Ship Ship { get; }
public ItemManager ItemManager { get; }
public GameplaySettings Settings { get; }
// How this pilot fights: its faction's doctrine for its role (Zone.CreateAgent resolves it).
public RoleDoctrine Doctrine { get; }
public float TopSpeed => _velocityLimit?.Limit ?? 100;
public Agent(Ship ship, RoleDoctrine doctrine)
{
Ship = ship;
Doctrine = doctrine;
ItemManager = Ship.ItemManager;
Settings = Ship.ItemManager.GameplaySettings;
_rootState = _currentState = new BaseState(this);
_velocityLimit = Ship.GetBehavior<VelocityLimit>();
}
public void Transition(BaseState targetState, Action onTransition = null)
{
//ItemManager.Log($"{Ship.Name} changing state from {_currentState.GetType().Name} to {targetState.GetType().Name}");
_currentState.OnExitState();
_currentState = targetState;
onTransition?.Invoke();
_currentState.OnEnterState();
}
public void Update(float delta)
{
// The pilot tick writes Turn every tick, in every state: a state that pilots overwrites this zero with its
// demand, and one that does not leaves the hull holding course instead of inheriting the last state's rate.
Ship.Turn = 0;
_currentState.Update(delta);
foreach (var transition in _currentState.Transitions)
if (transition.Condition())
{
Transition(transition.TargetState, transition.OnTransition);
break;
}
}
public void Accelerate(float2 targetVelocity, bool noTurn = false)
{
var deltaV = targetVelocity - Ship.Velocity;
var deltaVMag = length(deltaV);
var deltaVDirection = normalize(deltaV);
// If Delta V is above the threshold, direct the ship towards the delta and use only main thrusters
if (!noTurn && deltaVMag > FORWARD_DELTA_THRESHOLD)
{
Ship.Turn = Steering.Toward(Ship, deltaVDirection);
Ship.MovementDirection = float2(0, pow(dot(Ship.Direction, deltaVDirection), 2));
}
// If Delta V is low, use all thrusters and leave Turn to the caller (MoveTo already pointed it at the target)
else if(deltaVMag > THRUST_DELTA_THRESHOLD)
{
var right = Ship.Direction.Rotate(ItemRotation.Clockwise);
Ship.MovementDirection = float2(dot(right, deltaVDirection), dot(Ship.Direction, deltaVDirection));
}
else
{
Ship.MovementDirection = float2.zero;
}
}
}
public class StateTransition
{
public StateTransition(BaseState targetState, Func<bool> condition, Action onTransition)
{
TargetState = targetState;
Condition = condition;
OnTransition = onTransition;
}
public BaseState TargetState { get; }
public Func<bool> Condition { get; }
public Action OnTransition { get; }
}