Combat & Visibility & Collisions

Converted entity materials to new fade shader
Implemented visibility system
Refactored per-instance entity code to run inside monobehavior on entity instances
Created state machine AI for enemy ships with basic patrol and combat routines
Removed legacy AI code (controller item behaviors)
Added automatic trigger group assignment for matched weapons
Created ship collision system
Created shield VFX and colliders
This commit is contained in:
Reinier Vens 2021-02-26 19:01:47 +00:00
commit b53e4aec8c
204 changed files with 8182 additions and 1919 deletions

View file

@ -5,6 +5,9 @@ using static Unity.Mathematics.math;
public static class AetheriaMath
{
public const float Deg2Rad = 0.01745329f;
public const float Rad2Deg = 57.29578f;
//first-order intercept using absolute target position
public static float3 FirstOrderIntercept
(

View file

@ -0,0 +1,96 @@
/* 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 RethinkDb.Driver.Ast;
using UniRx;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.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; }
public float TopSpeed => _velocityLimit?.Limit ?? 100;
public Agent(Ship ship)
{
Ship = ship;
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)
{
_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.LookDirection = float3(deltaVDirection.x, 0, deltaVDirection.y);
Ship.MovementDirection = float2(0, pow(dot(Ship.Direction, deltaVDirection), 2));
}
// If Delta V is low, direct the ship towards the target and use all thrusters
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; }
}

View file

@ -1,203 +0,0 @@
/* 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 RethinkDb.Driver.Ast;
using UniRx;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
public abstract class AgentBehavior
{
public abstract void Update(float delta);
protected float ThrustInput(float2 deltaV) =>
saturate( // Input must be normalized
pow( // Apply specificity as exponent to dot product to control how specific the direction needs to be
saturate( // Discard negative dot product (we're facing away from desired direction)
dot(normalize(Entity.Direction), normalize(deltaV))), // Cosine of angle between direction and deltaV
ControllerData.ThrustSpecificity)
* length(deltaV) * ControllerData.ThrustSensitivity);
protected float TurningInput(float2 deltaV) => clamp(-Entity.Direction.AngleDiff(deltaV) * ControllerData.TurningSensitivity, -1, 1);
protected ControllerData ControllerData;
protected ItemManager Context;
protected Entity Entity;
public AgentBehavior(ItemManager context, Entity entity, ControllerData controllerData)
{
Context = context;
Entity = entity;
ControllerData = controllerData;
}
}
public class Agent
{
private BaseState _rootState;
private BaseState _currentState;
private Dictionary<BaseState, List<StateTransition>> _stateTransitions = new Dictionary<BaseState, List<StateTransition>>();
public AgentTask Task { get; set; }
public Ship Ship { get; }
public ItemManager ItemManager { get; }
public GameplaySettings Settings { get; }
public Agent(Ship ship)
{
Ship = ship;
ItemManager = Ship.ItemManager;
Settings = Ship.ItemManager.GameplaySettings;
_rootState = _currentState = new BaseState(this);
var patrolState = new PatrolOrbitsState(this);
AddTransition(_rootState, patrolState,
() => Task is PatrolOrbitsTask,
() => patrolState.Task = Task as PatrolOrbitsTask);
var patrolMoveState = new MoveToOrbitState(this);
AddTransition(patrolState, patrolMoveState,
() => true,
() => patrolMoveState.Orbit = patrolState.CurrentTarget);
AddTransition(patrolMoveState, patrolState,
() => patrolMoveState.Distance < 10,
() => patrolState.NextTarget());
}
public void AddTransition(BaseState source, BaseState targetState, Func<bool> condition, Action onTransition = null)
{
if(!_stateTransitions.ContainsKey(source))
_stateTransitions[source] = new List<StateTransition>();
_stateTransitions[source].Add(new StateTransition(targetState, condition, onTransition));
}
public void Transition(BaseState targetState, Action onTransition = null)
{
_currentState.OnExitState();
_currentState = targetState;
onTransition?.Invoke();
_currentState.OnEnterState();
}
public void Update(float delta)
{
_currentState.Update(delta);
foreach (var transition in _stateTransitions[_currentState])
if (transition.Condition())
{
Transition(transition.TargetState, transition.OnTransition);
break;
}
}
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; }
}
}
public class BaseState
{
protected Agent _agent;
public BaseState(Agent agent)
{
_agent = agent;
}
public virtual void Update(float delta){}
public virtual void OnEnterState(){}
public virtual void OnExitState(){}
}
public abstract class MoveToState : BaseState
{
private const float FORWARD_DELTA_THRESHOLD = 20;
private const float THRUST_DELTA_THRESHOLD = 1;
private VelocityLimit _velocityLimit;
protected abstract float2 TargetPosition { get; }
public float Distance { get; private set; }
protected MoveToState(Agent agent) : base(agent)
{
_velocityLimit = agent.Ship.GetBehavior<VelocityLimit>();
}
public override void Update(float delta)
{
var diff = TargetPosition - _agent.Ship.Position.xz;
var dir = normalize(diff);
Distance = length(diff);
// We want to go top speed in the direction of our target
var topSpeed = _velocityLimit?.Limit ?? 100;
var desiredVelocity = dir * topSpeed;
var deltaV = desiredVelocity - _agent.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 (deltaVMag > FORWARD_DELTA_THRESHOLD)
{
_agent.Ship.LookDirection = float3(deltaVDirection.x, 0, deltaVDirection.y);
_agent.Ship.MovementDirection = float2(0, pow(dot(_agent.Ship.Direction, deltaVDirection), 2));
}
// If Delta V is low, direct the ship towards the target and use all thrusters
else if(deltaVMag > THRUST_DELTA_THRESHOLD)
{
var right = _agent.Ship.Direction.Rotate(ItemRotation.Clockwise);
_agent.Ship.LookDirection = float3(dir.x, 0, dir.y);
_agent.Ship.MovementDirection = float2(dot(right, deltaVDirection), dot(_agent.Ship.Direction, deltaVDirection));
}
else
{
_agent.Ship.LookDirection = float3(dir.x, 0, dir.y);
_agent.Ship.MovementDirection = float2.zero;
}
}
}
public class MoveToEntityState : MoveToState
{
public Entity TargetEntity { get; set; }
protected override float2 TargetPosition => TargetEntity?.Position.xz ?? float2.zero;
public MoveToEntityState(Agent agent) : base(agent) { }
}
public class MoveToOrbitState : MoveToState
{
public Guid Orbit { get; set; }
public MoveToOrbitState(Agent agent) : base(agent) { }
protected override float2 TargetPosition => _agent.Ship.Zone.GetOrbitPosition(Orbit);
}
public class PatrolOrbitsState : BaseState
{
public PatrolOrbitsTask Task;
public Guid CurrentTarget
{
get => Task.Circuit[_currentTargetIndex];
}
private int _currentTargetIndex;
public void NextTarget()
{
_currentTargetIndex++;
_currentTargetIndex %= Task.Circuit.Length;
}
public PatrolOrbitsState(Agent agent) : base(agent) { }
}

View file

@ -1,58 +0,0 @@
/* 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 Unity.Mathematics;
using static Unity.Mathematics.math;
public class Locomotion : AgentBehavior
{
public float2 Objective;
private const float TangentSensitivity = 2;
private VelocityLimit _velocityLimit;
private Thruster[] _thrusters;
private float _thrust;
public Locomotion(ItemManager context, Entity entity, ControllerData controllerData) : base(context, entity, controllerData)
{
_velocityLimit = Entity.GetBehavior<VelocityLimit>();
_thrusters = entity.GetBehaviors<Thruster>()
.Where(t => t.Item.EquippableItem.Rotation == ItemRotation.Reversed)
.ToArray();
_thrust = _thrusters.Sum(t => t.Thrust);
}
public override void Update(float delta)
{
var diff = Objective - Entity.Position.xz;
// We want to go top speed in the direction of our target
var topSpeed = _velocityLimit?.Limit ?? 100;
float2 desiredVelocity = normalize(diff) * topSpeed;
var accelerationTime = length(desiredVelocity - Entity.Velocity) / (_thrust / Entity.Mass);
var accelerationDistance = accelerationTime * length((desiredVelocity + Entity.Velocity) / 2);
// We do not have enough space to get to full speed, use approximate maneuvering
// if (accelerationDistance > length(diff))
// {
// float2 tangential = Entity.Velocity - dot(normalize(diff), normalize(Entity.Velocity)) * Entity.Velocity;
// float2 adjustedDirection = normalize(diff) - tangential * TangentSensitivity;
//
// _turning.Axis = TurningInput(adjustedDirection);
// _thrust.Axis = 1;
// }
// // We have plenty of space, use exact maneuvering
// else
// {
// var deltaV = desiredVelocity - Entity.Velocity;
// _turning.Axis = TurningInput(deltaV);
// _thrust.Axis = ThrustInput(deltaV);
// }
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: c3af5050375d08242a52bdf7f7de558e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
using System.Linq;
using UniRx;
public class Minion : Agent
{
public Minion(Ship ship) : base(ship)
{
var patrolState = new PatrolOrbitsState(this);
_rootState.AddTransition(patrolState,
() => Task is PatrolOrbitsTask,
() => patrolState.Task = Task as PatrolOrbitsTask);
Ship.VisibleHostiles.ObserveAdd().Where(_ => Ship.Target.Value == null).Subscribe(add => Ship.Target.Value = add.Value);
var combatState = new CombatState(this);
_rootState.AddTransition(combatState,
() => Ship.Target.Value != null, null, true, _rootState);
combatState.AddTransition(_rootState,
() => Ship.Target.Value == null, null, true, _rootState);
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b764e85a1fbe4d68abcc77480d6523e3
timeCreated: 1613182738

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 823a891ccfdd4b13be60053a85bfc073
timeCreated: 1613182847

View file

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
public class BaseState
{
public List<StateTransition> Transitions { get; } = new List<StateTransition>();
protected Agent _agent;
public BaseState(Agent agent)
{
_agent = agent;
}
private static List<BaseState> _lastVisitedStates = new List<BaseState>();
private static List<BaseState> _leafStates = new List<BaseState>();
private static List<BaseState> _visitedStates = new List<BaseState>();
private static HashSet<BaseState> _ignoreStates = new HashSet<BaseState>();
public void AddTransition(BaseState targetState, Func<bool> condition, Action onTransition = null, bool includeChildren = false, params BaseState[] ignoreStates)
{
Transitions.Add(new StateTransition(targetState, condition, onTransition));
// Traverse the state graph, adding every state that is reachable from this state without traversing over ignored nodes
if (includeChildren)
{
_visitedStates.Clear();
_ignoreStates.Clear();
if(ignoreStates!=null)
foreach (var ignoreState in ignoreStates)
_ignoreStates.Add(ignoreState);
_leafStates.Clear();
_leafStates.Add(this);
_ignoreStates.Add(this);
_ignoreStates.Add(targetState);
while (_leafStates.Count > 0)
{
_lastVisitedStates.Clear();
_lastVisitedStates.AddRange(_leafStates);
_leafStates.Clear();
foreach (var state in _lastVisitedStates)
{
foreach (var transition in state.Transitions)
{
if(!_ignoreStates.Contains(transition.TargetState))
{
_visitedStates.Add(transition.TargetState);
_ignoreStates.Add(transition.TargetState);
_leafStates.Add(transition.TargetState);
}
}
}
}
foreach (var state in _visitedStates)
{
state.AddTransition(targetState, condition, onTransition);
}
}
}
public virtual void Update(float delta){}
public virtual void OnEnterState(){}
public virtual void OnExitState(){}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a3f09ceb47034c18b938cb8732df690b
timeCreated: 1613182963

View file

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
using float3 = Unity.Mathematics.float3;
using float2x2 = Unity.Mathematics.float2x2;
public class CombatState : BaseState
{
private const int DPS_SAMPLE_COUNT = 32;
private float _optimumRange;
private readonly List<(int index, float dps)> _availableGroups = new List<(int index, float dps)>();
private readonly List<LockWeapon> _availableLockingWeapons = new List<LockWeapon>();
public CombatState(Agent agent) : base(agent)
{
SampleDps();
foreach (var weapon in _agent.Ship.Weapons)
{
weapon.Item.OnDisable += SampleDps;
weapon.Item.OnEnable += SampleDps;
}
}
public override void Update(float delta)
{
var target = _agent.Ship.Target.Value;
if (target == null) return;
_availableGroups.Clear();
_availableLockingWeapons.Clear();
var toTarget = target.Position - _agent.Ship.Position;
var targetDistance = length(toTarget);
for (var i = 0; i < _agent.Ship.TriggerGroups.Length; i++)
{
var group = _agent.Ship.TriggerGroups[i];
var dps = 0f;
foreach (var weapon in group.weapons)
if (weapon.Item.Online &&
weapon.MinRange < targetDistance &&
targetDistance < weapon.Range &&
(weapon is ConstantWeapon ||
weapon is InstantWeapon instantWeapon && instantWeapon.CanFire))
{
if(weapon is LockWeapon lockWeapon)
_availableLockingWeapons.Add(lockWeapon);
else
dps += weapon.RangeDamagePerSecond(targetDistance);
}
if (dps > .1) _availableGroups.Add((i, dps));
}
foreach(var w in _availableLockingWeapons) w.Activate();
var selectedGroup = -1;
var maxDps = Single.MinValue;
foreach (var group in _availableGroups)
{
if (group.dps > maxDps)
{
maxDps = group.dps;
selectedGroup = group.index;
}
}
var targetRight = target.Direction.Rotate(ItemRotation.Clockwise);
var optimumRangeDelta = abs(_optimumRange - targetDistance);
var directionToTarget = normalize(toTarget.xz);
var targetPortAlignment = dot(targetRight, directionToTarget);
var targetForeAlignment = dot(-target.Direction, directionToTarget);
var forwardness = saturate(optimumRangeDelta / _agent.Settings.AgentMaxForwardDistance) * _agent.Settings.AgentForwardLerp;
if (targetForeAlignment > 0) forwardness = lerp(forwardness, 1, pow(targetPortAlignment, 2));
var movementDirection = normalize(lerp(
directionToTarget.Rotate(targetPortAlignment > 0 ? ItemRotation.Clockwise : ItemRotation.CounterClockwise),
targetDistance > _optimumRange ? directionToTarget : -directionToTarget, forwardness));
if (selectedGroup >= 0)
{
var testWeapon = _agent.Ship.TriggerGroups[selectedGroup].weapons.First();
if(testWeapon.Velocity > 1)
{
var targetHullData = _agent.ItemManager.GetData(target.Hull) as HullData;
var targetVelocity = float3(target.Velocity.x, 0, target.Velocity.y);
var predictedPosition = AetheriaMath.FirstOrderIntercept(
_agent.Ship.Position,
float3.zero,
testWeapon.Velocity,
target.Position,
targetVelocity
);
predictedPosition.y = _agent.Ship.Zone.GetHeight(predictedPosition.xz) + targetHullData.GridOffset;
toTarget = normalize(predictedPosition - _agent.Ship.Position);
}
var shouldFire = dot(
_agent.Ship.HardpointTransforms[_agent.Ship.Hardpoints[testWeapon.Item.Position.x, testWeapon.Item.Position.y]].direction,
toTarget) > .99f;
foreach (var weapon in _agent.Ship.TriggerGroups[selectedGroup].weapons)
{
if (shouldFire)
weapon.Activate();
else if (weapon.Firing)
weapon.Deactivate();
}
}
_agent.Ship.LookDirection = toTarget;
_agent.Accelerate(movementDirection * _agent.TopSpeed, true);//selectedGroup >= 0);
// Fire charged guns!
}
private void SampleDps()
{
var minRange = Single.MaxValue;
var maxRange = Single.MinValue;
foreach (var weapon in _agent.Ship.Weapons)
{
if (weapon.Range < minRange)
minRange = weapon.Range;
if (weapon.Range > maxRange)
maxRange = weapon.Range;
}
var offset = _agent.ItemManager.Random.NextFloat((maxRange - minRange) / DPS_SAMPLE_COUNT);
var optimumDPS = 0f;
for (int i = 0; i < DPS_SAMPLE_COUNT; i++)
{
var range = offset + minRange + (maxRange - minRange) * ((float) i / DPS_SAMPLE_COUNT);
float dps = 0;
foreach (var weapon in _agent.Ship.Weapons)
{
if (weapon.Item.Online && weapon.MinRange < range && range < weapon.Range)
dps += weapon.RangeDamagePerSecond(range);
}
// Multiply by range raised to a (sublinear) exponent to bias DPS preference towards longer ranges
dps *= pow(range, _agent.Settings.AgentRangeExponent);
if (dps > optimumDPS)
{
optimumDPS = dps;
_optimumRange = range;
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 059405941f2c482c8476a4b28196568e
timeCreated: 1613189894

View file

@ -0,0 +1,32 @@
using Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
public abstract class MatchVelocityState : BaseState
{
protected abstract float2 TargetVelocity { get; }
protected MatchVelocityState(Agent agent) : base(agent) { }
public override void Update(float delta)
{
_agent.Accelerate(TargetVelocity);
}
public float2 MatchDistanceTime
{
get
{
var velocity = length(_agent.Ship.Velocity);
var deltaV = TargetVelocity - _agent.Ship.Velocity;
var stoppingTime = length(deltaV) / (_agent.Ship.ForwardThrust / _agent.Ship.Mass);
var stoppingDistance = stoppingTime * (velocity / 2);
var turnaroundTime = _agent.Ship.TurnTime(deltaV);
var turnaroundDistance = turnaroundTime * velocity;
return float2(stoppingDistance + turnaroundDistance, stoppingTime + turnaroundTime);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b584acceb113424a8fb7d1982e27b2db
timeCreated: 1613185253

View file

@ -0,0 +1,44 @@
using System;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
public abstract class MoveToState : BaseState
{
private VelocityLimit _velocityLimit;
protected abstract float2 TargetPosition { get; }
public float Distance { get; private set; }
protected MoveToState(Agent agent) : base(agent)
{
}
public override void Update(float delta)
{
var diff = TargetPosition - _agent.Ship.Position.xz;
var dir = normalize(diff);
Distance = length(diff);
// We want to go top speed in the direction of our target
var desiredVelocity = dir * _agent.TopSpeed;
_agent.Ship.LookDirection = float3(dir.x, 0, dir.y);
_agent.Accelerate(desiredVelocity);
}
}
public class MoveToEntityState : MoveToState
{
public Entity TargetEntity { get; set; }
protected override float2 TargetPosition => TargetEntity?.Position.xz ?? float2.zero;
public MoveToEntityState(Agent agent) : base(agent) { }
}
public class MoveToOrbitState : MoveToState
{
public Guid Orbit { get; set; }
public MoveToOrbitState(Agent agent) : base(agent) { }
protected override float2 TargetPosition => _agent.Ship.Zone.GetOrbitPosition(Orbit);
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ddb2b91d6c3340ccadfc7c20020846e8
timeCreated: 1613182864

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9d106c37ec34e29aab0c8ee52a7ed5d
timeCreated: 1613182162

View file

@ -40,8 +40,3 @@ public enum TaskType
Explore
}
public class PatrolOrbitsTask : AgentTask
{
public override TaskType Type => TaskType.Defend;
public Guid[] Circuit;
}

View file

@ -0,0 +1,31 @@
using System;
public class PatrolOrbitsTask : AgentTask
{
public override TaskType Type => TaskType.Defend;
public Guid[] Circuit;
}
public class PatrolOrbitsState : BaseState
{
public PatrolOrbitsTask Task;
public Guid CurrentTarget
{
get => Task.Circuit[_currentTargetIndex];
}
private int _currentTargetIndex;
public void NextTarget()
{
_currentTargetIndex++;
_currentTargetIndex %= Task.Circuit.Length;
}
public PatrolOrbitsState(Agent agent) : base(agent)
{
var patrolMoveState = new MoveToOrbitState(agent);
Transitions.Add(new StateTransition(patrolMoveState,
() => true,
() => patrolMoveState.Orbit = CurrentTarget));
patrolMoveState.Transitions.Add(new StateTransition(this, () => patrolMoveState.Distance < 10, NextTarget));
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dcfc94fde9bb4aa2ac3c7d88180689cb
timeCreated: 1613182188

View file

@ -1,63 +0,0 @@
/* 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 Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
public class VelocityMatch : AgentBehavior
{
public event Action OnMatch;
public float2 TargetVelocity;
private Thruster _thrust;
private const float TargetThreshold = .1f;
private ItemManager _context;
public VelocityMatch(ItemManager context, Entity entity, ControllerData controllerData) : base(context, entity, controllerData)
{
_context = context;
_thrust = Entity.GetBehavior<Thruster>();
}
public void Clear()
{
OnMatch = null;
}
public override void Update(float delta)
{
// if (_thrust != null && _turning != null)
// {
// var deltaV = TargetVelocity - Entity.Velocity;
// if(length(deltaV) < TargetThreshold)
// OnMatch?.Invoke();
// _turning.Axis = TurningInput(deltaV);
// _thrust.Axis = ThrustInput(deltaV);
// }
}
public float2 MatchDistanceTime
{
get
{
// var velocity = length(Entity.Velocity);
// var deltaV = TargetVelocity - Entity.Velocity;
//
// var stoppingTime = length(deltaV) / (_thrust.Thrust / Entity.Mass);
// var stoppingDistance = stoppingTime * (velocity / 2);
//
// var angleDiff = Entity.Direction.AngleDiff(deltaV);
// var turnaroundTime = angleDiff / (_turning.Torque / Entity.Mass);
// var turnaroundDistance = turnaroundTime * velocity;
//
// return float2(stoppingDistance + turnaroundDistance, stoppingTime + turnaroundTime);
return float2.zero;
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: df5185d5435658841878b6a8c9ff693b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -71,14 +71,6 @@ public interface IPopulationAssignment
int AssignedPopulation { get; set; }
}
public interface IController<T> where T: AgentTask
{
bool Available { get; }
//TaskType TaskType { get; }
Zone Zone { get; }
void AssignTask(T task);
}
[MessagePackObject,
Union(0, typeof(FactoryPersistence)),
JsonObject(MemberSerialization.OptIn),
@ -101,8 +93,8 @@ public abstract class PersistentBehaviorData
Union(10, typeof(VelocityConversionData)),
Union(11, typeof(VelocityLimitData)),
Union(12, typeof(FactoryData)),
Union(13, typeof(PatrolControllerData)),
Union(14, typeof(TowingControllerData)),
// Union(13, typeof(PatrolControllerData)),
// Union(14, typeof(TowingControllerData)),
Union(15, typeof(CooldownData)),
Union(16, typeof(HeatData)),
// Union(17, typeof(HitscanData)),
@ -113,12 +105,12 @@ public abstract class PersistentBehaviorData
Union(22, typeof(VisibilityData)),
Union(23, typeof(ThermotoggleData)),
Union(24, typeof(EnergyDrawData)),
Union(25, typeof(MiningControllerData)),
// Union(25, typeof(MiningControllerData)),
Union(26, typeof(MiningToolData)),
Union(27, typeof(SurveyControllerData)),
// Union(27, typeof(SurveyControllerData)),
Union(28, typeof(ResourceScannerData)),
Union(29, typeof(WanderControllerData)),
Union(30, typeof(HaulingControllerData)),
// Union(29, typeof(WanderControllerData)),
// Union(30, typeof(HaulingControllerData)),
Union(31, typeof(CapacitorData)),
Union(32, typeof(CockpitData)),
Union(33, typeof(HeatsinkData)),

View file

@ -62,6 +62,15 @@ public class ChargedWeapon : InstantWeapon
public float ChargeTime { get; protected set; }
public float ChargeEnergy { get; protected set; }
public float ChargeHeat { get; protected set; }
public override float DamagePerSecond => Damage * _data.ChargeFiringDamageMultiplier / (Cooldown + ChargeTime);
public override float RangeDamagePerSecond(float range)
{
return Damage *
_data.ChargeFiringDamageMultiplier *
_data.DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))) /
(Cooldown + ChargeTime);
}
public event Action OnStartCharging;
public event Action OnStopCharging;

View file

@ -37,6 +37,12 @@ public class ConstantWeapon : Weapon, IProgressBehavior
{
get { return saturate(_reload); }
}
public override float DamagePerSecond => Damage;
public override float RangeDamagePerSecond(float range)
{
return Damage * _data.DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range)));
}
public event Action OnReloadBegin;
public event Action OnReloadComplete;

View file

@ -1,257 +0,0 @@
/* 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 MessagePack;
using Newtonsoft.Json;
using UniRx;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
[Union(0, typeof(TowingControllerData))]
public abstract class ControllerData : BehaviorData
{
[InspectableField, JsonProperty("thrustSensitivity"), Key(1)]
public float ThrustSensitivity = 1;
[InspectableField, JsonProperty("thrustSpecificity"), Key(2)]
public float ThrustSpecificity = 8;
[InspectableField, JsonProperty("turningSensitivity"), Key(3)]
public float TurningSensitivity = 1;
[InspectableField, JsonProperty("tangentSensitivity"), Key(4)]
public float TangentSensitivity = 4;
[InspectableField, JsonProperty("targetDistance"), Key(5)]
public float TargetDistance = 10;
}
public abstract class ControllerBase<T> : IBehavior, IController<T>, IInitializableBehavior where T : AgentTask
{
protected Entity HomeEntity => (Entity as Ship)?.HomeEntity;
public bool Available => _spaceworthy && Task == null;
//public abstract TaskType TaskType { get; }
public Zone Zone => Entity.Zone;
public BehaviorData Data => _controllerData;
protected ItemManager ItemManager { get; }
protected Entity Entity { get; }
protected Locomotion Locomotion;
protected VelocityMatch VelocityMatch;
protected Func<float2> TargetPosition;
protected Func<float2> TargetVelocity;
protected bool MatchVelocity;
// protected List<ZoneDefinition> Path;
protected T Task;
protected bool Moving;
protected bool Waiting;
private ControllerData _controllerData;
private MovementPhase _movementPhase;
private Action _onFinishMoving;
private bool _spaceworthy;
private float _waitTime;
private Action _onFinishWaiting;
public ControllerBase(ItemManager itemManager, ControllerData data, Entity entity)
{
ItemManager = itemManager;
Entity = entity;
_controllerData = data;
CheckSpaceworthiness();
entity.Equipment.ObserveAdd().Subscribe(added => CheckSpaceworthiness());
}
private void CheckSpaceworthiness()
{
_spaceworthy = Entity.GetBehavior<Thruster>() != null && Entity.GetBehavior<Reactor>() != null;
if(_spaceworthy)
CreateBehaviors();
}
public void CreateBehaviors()
{
Locomotion = new Locomotion(ItemManager, Entity, _controllerData);
VelocityMatch = new VelocityMatch(ItemManager, Entity, _controllerData);
}
public void Initialize()
{
CreateBehaviors();
// _context.CorporationControllers[_entity.Corporation].Add(this);
}
public void AssignTask(T task)
{
Task = task;
Moving = false;
}
public bool Execute(float delta)
{
if (Available)
{
// No task is assigned and we're at home, do nothing!
if (Entity.Parent == HomeEntity)
return false;
// No task is assigned, but we're not home, go home!
if (!Moving)
{
GoHome();
}
}
if (Waiting)
{
_waitTime -= delta;
if (_waitTime < 0)
{
Waiting = false;
_onFinishWaiting?.Invoke();
}
return true;
}
if (Moving)
{
if(Entity.Parent!=null)
Entity.RemoveParent();
var targetPosition = TargetPosition();
var distance = length(targetPosition - Entity.Position.xz);
if (MatchVelocity)
{
var targetVelocity = TargetVelocity();
VelocityMatch.TargetVelocity = targetVelocity;
if (_movementPhase == MovementPhase.Locomotion)
{
var matchDistanceTime = VelocityMatch.MatchDistanceTime;
Locomotion.Objective = targetPosition + targetVelocity * matchDistanceTime.y;
Locomotion.Update(delta);
if (distance < matchDistanceTime.x)
{
_movementPhase = MovementPhase.Slowdown;
//_context.Log($"Controller {_entity.Name} has entered slowdown phase.");
VelocityMatch.Clear();
VelocityMatch.OnMatch += () =>
{
Moving = false;
_onFinishMoving?.Invoke();
};
}
}
else
{
VelocityMatch.TargetVelocity = targetVelocity;
VelocityMatch.Update(delta);
}
}
else
{
Locomotion.Objective = TargetPosition();
Locomotion.Update(delta);
if (distance < _controllerData.TargetDistance)
{
Moving = false;
_onFinishMoving();
}
}
}
return true;
}
public void FinishTask()
{
if (Task == null)
return;
Task = null;
}
public void GoHome(Action onFinish = null)
{
MoveTo(HomeEntity, true, () =>
{
Entity.SetParent(HomeEntity);
onFinish?.Invoke();
});
// else MoveTo(homeEntity.Zone.Data.ID, () => MoveTo(homeEntity, true, () =>
// {
// _entity.SetParent(homeEntity);
// onFinish?.Invoke();
// }));
}
public void Wait(float time, Action onFinish = null)
{
_waitTime = time;
Waiting = true;
_onFinishWaiting = onFinish;
}
public void MoveTo(Entity entity, bool matchVelocity = true, Action onFinish = null)
{
TargetPosition = () => entity.Position.xz;
TargetVelocity = () => entity.Velocity;
MatchVelocity = matchVelocity;
_movementPhase = MovementPhase.Locomotion;
Moving = true;
_onFinishMoving = onFinish;
}
public void MoveTo(float2 position, bool matchVelocity = true, Action onFinish = null)
{
TargetPosition = () => position;
TargetVelocity = () => float2.zero;
MatchVelocity = matchVelocity;
_movementPhase = MovementPhase.Locomotion;
Moving = true;
_onFinishMoving = onFinish;
}
public void MoveTo(Func<float2> position, Func<float2> velocity, Action onFinish = null)
{
TargetPosition = position;
TargetVelocity = velocity;
MatchVelocity = true;
_movementPhase = MovementPhase.Locomotion;
Moving = true;
_onFinishMoving = onFinish;
}
// public void MoveTo(Guid zone, Action onFinish = null)
// {
// var path = _context.FindPath(_context.GalaxyZones[Zone.Data.ID], _context.GalaxyZones[zone]);
//
// void OnNextZone()
// {
// path.RemoveAt(0);
// if (path.Count > 0)
// {
// MoveTo(Zone.WormholePosition(Path[0]), false, OnNextZone);
// }
// else
// {
// onFinish?.Invoke();
// }
// }
//
// OnNextZone();
// }
}
public enum MovementPhase
{
Locomotion,
Slowdown
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 0bd22c9213b660e4d845f7a751f18848
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -2,146 +2,146 @@
* 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 MessagePack;
using Newtonsoft.Json;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
public class HaulingControllerData : ControllerData
{
[InspectableField, JsonProperty("targetDistance"), Key(6)]
public float DockTime = 2;
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
{
return new HaulingController(context, this, entity, item);
}
}
public class HaulingController : ControllerBase<HaulingTask>, IBehavior, IPersistentBehavior
{
private HaulingControllerData _data;
private EquippedItem Item { get; }
private bool _taskStarted;
private int _itemsDelivered;
private int _itemsCarried;
private SimpleCommodity _simpleCommodityDelivery;
private List<CraftedItemInstance> _craftedItemDelivery = new List<CraftedItemInstance>();
public HaulingController(ItemManager itemManager, HaulingControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
{
_data = data;
Item = item;
}
public new bool Execute(float delta)
{
if (Task != null)
{
if (!_taskStarted)
{
MoveTo(Task.Origin, true, OnPickup);
//MoveTo(haulingTask.Zone, OnOriginZoneArrival);
_taskStarted = true;
}
}
return base.Execute(delta);
}
// void OnOriginZoneArrival()
// {
// var haulingTask = _context.ItemData.Get<HaulingTask>(Task);
//
// _entity.SetMessage("Entering pickup phase.");
//
// MoveTo(_context.ItemData.Get<Entity>(haulingTask.Origin), true, OnPickup);
// }
void OnPickup()
{
Entity.SetParent(Task.Origin);
_itemsCarried = Task.Origin.TryTransferItems(Entity, Task.ItemType, Task.Quantity - _itemsDelivered);
if (_itemsCarried == 0)
{
FinishTask();
return;
}
Wait(_data.DockTime, OnItemObtained);
}
void OnItemObtained()
{
Entity.SetMessage("Entering delivery phase.");
MoveTo(Task.Target, true, OnDelivery);
//MoveTo(target.Zone.Data.ID, OnTargetZoneArrival);
}
// void OnTargetZoneArrival()
// {
// var haulingTask = _context.ItemData.Get<HaulingTask>(Task);
//
// MoveTo(_context.ItemData.Get<Entity>(haulingTask.Target), true, OnDelivery);
// }
void OnDelivery()
{
Entity.SetParent(Task.Target);
var itemsDelivered = Entity.TryTransferItems(Task.Target, Task.ItemType, _itemsCarried);
_itemsCarried -= itemsDelivered;
_itemsDelivered += itemsDelivered;
Wait(_data.DockTime, () =>
{
// We were unable to transfer all onboard items to target entity, cargo is probably full. Bring back the surplus and end the mission
if(_itemsCarried > 0)
MoveTo(Task.Origin, true, ReturnSurplusToOrigin);
// We have delivered the target quantity of items, end the mission
else if(_itemsDelivered >= Task.Quantity)
FinishTask();
// We aren't done yet. Resetting the task will make us go for another delivery run
else
_taskStarted = false;
});
}
void ReturnSurplusToOrigin()
{
Entity.SetParent(Task.Origin);
Entity.TryTransferItems(Task.Origin, Task.ItemType, _itemsCarried);
Wait(_data.DockTime, OnItemObtained);
FinishTask();
}
public PersistentBehaviorData Store()
{
return new HaulingControllerPersistence
{
Task = Task
};
}
public void Restore(PersistentBehaviorData data)
{
var towingControllerPersistence = data as HaulingControllerPersistence;
Task = towingControllerPersistence.Task;
}
}
public class HaulingControllerPersistence : PersistentBehaviorData
{
[JsonProperty("task"), Key(0)]
public HaulingTask Task;
}
// using System;
// using System.Collections;
// using System.Collections.Generic;
// using System.Linq;
// using MessagePack;
// using Newtonsoft.Json;
// using Unity.Mathematics;
// using static Unity.Mathematics.math;
//
// [MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
// public class HaulingControllerData : ControllerData
// {
// [InspectableField, JsonProperty("targetDistance"), Key(6)]
// public float DockTime = 2;
// public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
// {
// return new HaulingController(context, this, entity, item);
// }
// }
//
// public class HaulingController : ControllerBase<HaulingTask>, IBehavior, IPersistentBehavior
// {
// private HaulingControllerData _data;
// private EquippedItem Item { get; }
//
// private bool _taskStarted;
// private int _itemsDelivered;
// private int _itemsCarried;
// private SimpleCommodity _simpleCommodityDelivery;
// private List<CraftedItemInstance> _craftedItemDelivery = new List<CraftedItemInstance>();
//
// public HaulingController(ItemManager itemManager, HaulingControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
// {
// _data = data;
// Item = item;
// }
//
// public new bool Execute(float delta)
// {
// if (Task != null)
// {
// if (!_taskStarted)
// {
// MoveTo(Task.Origin, true, OnPickup);
// //MoveTo(haulingTask.Zone, OnOriginZoneArrival);
// _taskStarted = true;
// }
// }
// return base.Execute(delta);
// }
//
// // void OnOriginZoneArrival()
// // {
// // var haulingTask = _context.ItemData.Get<HaulingTask>(Task);
// //
// // _entity.SetMessage("Entering pickup phase.");
// //
// // MoveTo(_context.ItemData.Get<Entity>(haulingTask.Origin), true, OnPickup);
// // }
//
// void OnPickup()
// {
// Entity.SetParent(Task.Origin);
//
// _itemsCarried = Task.Origin.TryTransferItems(Entity, Task.ItemType, Task.Quantity - _itemsDelivered);
//
// if (_itemsCarried == 0)
// {
// FinishTask();
// return;
// }
//
// Wait(_data.DockTime, OnItemObtained);
// }
//
// void OnItemObtained()
// {
// Entity.SetMessage("Entering delivery phase.");
//
// MoveTo(Task.Target, true, OnDelivery);
// //MoveTo(target.Zone.Data.ID, OnTargetZoneArrival);
// }
//
// // void OnTargetZoneArrival()
// // {
// // var haulingTask = _context.ItemData.Get<HaulingTask>(Task);
// //
// // MoveTo(_context.ItemData.Get<Entity>(haulingTask.Target), true, OnDelivery);
// // }
//
// void OnDelivery()
// {
// Entity.SetParent(Task.Target);
//
// var itemsDelivered = Entity.TryTransferItems(Task.Target, Task.ItemType, _itemsCarried);
// _itemsCarried -= itemsDelivered;
// _itemsDelivered += itemsDelivered;
//
// Wait(_data.DockTime, () =>
// {
// // We were unable to transfer all onboard items to target entity, cargo is probably full. Bring back the surplus and end the mission
// if(_itemsCarried > 0)
// MoveTo(Task.Origin, true, ReturnSurplusToOrigin);
// // We have delivered the target quantity of items, end the mission
// else if(_itemsDelivered >= Task.Quantity)
// FinishTask();
// // We aren't done yet. Resetting the task will make us go for another delivery run
// else
// _taskStarted = false;
// });
// }
//
// void ReturnSurplusToOrigin()
// {
// Entity.SetParent(Task.Origin);
//
// Entity.TryTransferItems(Task.Origin, Task.ItemType, _itemsCarried);
//
// Wait(_data.DockTime, OnItemObtained);
//
// FinishTask();
// }
//
// public PersistentBehaviorData Store()
// {
// return new HaulingControllerPersistence
// {
// Task = Task
// };
// }
//
// public void Restore(PersistentBehaviorData data)
// {
// var towingControllerPersistence = data as HaulingControllerPersistence;
// Task = towingControllerPersistence.Task;
// }
// }
//
// public class HaulingControllerPersistence : PersistentBehaviorData
// {
// [JsonProperty("task"), Key(0)]
// public HaulingTask Task;
// }

View file

@ -37,10 +37,20 @@ public class InstantWeapon : Weapon, IProgressBehavior
protected float _cooldown; // Normalized
private int _ammo = 0;
protected bool _coolingDown;
public float BurstCount { get; protected set; }
public float BurstTime { get; protected set; }
public float Cooldown { get; protected set; }
public virtual bool CanFire
{
get => !_coolingDown;
}
public override float DamagePerSecond => Damage / Cooldown;
public override float RangeDamagePerSecond(float range)
{
return Damage * _data.DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))) / Cooldown;
}
public override int Ammo
{
@ -126,11 +136,11 @@ public class InstantWeapon : Weapon, IProgressBehavior
base.Execute(delta);
if (_coolingDown)
{
_cooldown -= delta / (_ammo == 0 ? _data.ReloadTime : Cooldown);
_cooldown -= delta / (_data.AmmoType != Guid.Empty && _ammo == 0 ? _data.ReloadTime : Cooldown);
if (_cooldown < 0)
{
_coolingDown = false;
if (_ammo == 0)
if (_data.AmmoType != Guid.Empty && _ammo == 0)
{
_ammo = _data.MagazineSize;
OnReloadComplete?.Invoke();
@ -159,7 +169,7 @@ public class InstantWeapon : Weapon, IProgressBehavior
public override void Activate()
{
if(!_coolingDown)
if(CanFire)
Trigger();
base.Activate();
}

View file

@ -49,6 +49,8 @@ public class LockWeapon : InstantWeapon
public override float Progress => saturate(_cooldown > 0 ? _cooldown : _lock);
public override bool CanFire => base.CanFire && _lock > .99f && Entity.TargetRange > MinRange && Entity.TargetRange < Range;
public float Lock
{
get => saturate(_lock);
@ -86,13 +88,5 @@ public class LockWeapon : InstantWeapon
return base.Execute(delta);
}
public override void Activate()
{
if(_lock > .99f)
{
base.Activate();
}
}
}

View file

@ -11,131 +11,131 @@ using Newtonsoft.Json;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
public class MiningControllerData : ControllerData
{
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
{
return new MiningController(context, this, entity, item);
}
}
public class MiningController : ControllerBase<Mining>, IBehavior, IPersistentBehavior, IInitializableBehavior
{
private MiningControllerData _data;
private EquippedItem Item { get; }
private bool _taskStarted;
private int _asteroid = -1;
private MiningTool _miningTool;
private Switch _toolSwitch;
public MiningController(ItemManager itemManager, MiningControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
{
_data = data;
Item = item;
}
public new void Initialize()
{
// _miningTool = Entity.GetBehavior<MiningTool>();
// _toolSwitch = Entity.GetSwitch<MiningTool>();
base.Initialize();
}
public new bool Execute(float delta)
{
// if (Task != null)
// {
// if (!_taskStarted)
// {
// NextAsteroid();
// _taskStarted = true;
// }
// else
// {
// if (!Moving)
// {
// if (_entity.OccupiedCapacity < _entity.Capacity - 1)
// {
// var asteroidTransform = Zone.GetAsteroidTransform(Task.Asteroids, _asteroid);
// if (length(_entity.Position - asteroidTransform.xy) - asteroidTransform.w > _miningTool.Range)
// {
// _entity.SetMessage("Moving to target asteroid.");
// MoveTo(() => Zone.GetAsteroidTransform(Task.Asteroids, _asteroid).xy,
// () => Zone.GetAsteroidVelocity(Task.Asteroids, _asteroid));
// _toolSwitch.Activated = false;
// }
// else
// {
// if (Zone.AsteroidExists(Task.Asteroids, _asteroid))
// {
// _miningTool.AsteroidBelt = Task.Asteroids;
// _miningTool.Asteroid = _asteroid;
// _toolSwitch.Activated = true;
// Aim.Objective = Zone.GetAsteroidTransform(Task.Asteroids, _asteroid).xy;
// Aim.Update(delta);
// }
// else NextAsteroid();
// }
// }
// else
// {
// _entity.SetMessage("Out of cargo space. Returning home to offload cargo.");
// GoHome(OnArriveHome);
// }
// }
// }
// }
return base.Execute(delta);
}
// private void OnArriveHome()
// {
// var homeEntity = _context.ItemData.Get<Entity>(HomeEntity);
// foreach (var item in _entity.Cargo
// .Select(id => _context.ItemData.Get<ItemInstance>(id))
// .Where(item => item is SimpleCommodity)
// .Cast<SimpleCommodity>()
// .ToArray())
// {
// int quantity = min((int) ((homeEntity.Capacity - homeEntity.OccupiedCapacity) / _context.GetData(item).Size), item.Quantity);
// var newItem = _entity.RemoveCargo(item, quantity);
// homeEntity.AddCargo(newItem);
//
// if (quantity < item.Quantity)
// {
// homeEntity.SetMessage("Colony is out of cargo space. Closing Mining Task.");
// FinishTask();
// _taskStarted = false;
// return;
// }
// }
// }
private void NextAsteroid()
{
_asteroid = Zone.NearestAsteroid(Task.Asteroids, Entity.Position.xz);
Entity.SetMessage("Selecting new asteroid.");
}
public PersistentBehaviorData Store()
{
return new MiningControllerPersistence
{
Task = Task
};
}
public void Restore(PersistentBehaviorData data)
{
var miningControllerPersistence = data as MiningControllerPersistence;
Task = miningControllerPersistence.Task;
}
}
public class MiningControllerPersistence : PersistentBehaviorData
{
[JsonProperty("task"), Key(0)]
public Mining Task;
}
// [MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
// public class MiningControllerData : ControllerData
// {
// public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
// {
// return new MiningController(context, this, entity, item);
// }
// }
//
// public class MiningController : ControllerBase<Mining>, IBehavior, IPersistentBehavior, IInitializableBehavior
// {
//
// private MiningControllerData _data;
// private EquippedItem Item { get; }
// private bool _taskStarted;
// private int _asteroid = -1;
// private MiningTool _miningTool;
// private Switch _toolSwitch;
//
// public MiningController(ItemManager itemManager, MiningControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
// {
// _data = data;
// Item = item;
// }
//
// public new void Initialize()
// {
// // _miningTool = Entity.GetBehavior<MiningTool>();
// // _toolSwitch = Entity.GetSwitch<MiningTool>();
// base.Initialize();
// }
//
// public new bool Execute(float delta)
// {
// // if (Task != null)
// // {
// // if (!_taskStarted)
// // {
// // NextAsteroid();
// // _taskStarted = true;
// // }
// // else
// // {
// // if (!Moving)
// // {
// // if (_entity.OccupiedCapacity < _entity.Capacity - 1)
// // {
// // var asteroidTransform = Zone.GetAsteroidTransform(Task.Asteroids, _asteroid);
// // if (length(_entity.Position - asteroidTransform.xy) - asteroidTransform.w > _miningTool.Range)
// // {
// // _entity.SetMessage("Moving to target asteroid.");
// // MoveTo(() => Zone.GetAsteroidTransform(Task.Asteroids, _asteroid).xy,
// // () => Zone.GetAsteroidVelocity(Task.Asteroids, _asteroid));
// // _toolSwitch.Activated = false;
// // }
// // else
// // {
// // if (Zone.AsteroidExists(Task.Asteroids, _asteroid))
// // {
// // _miningTool.AsteroidBelt = Task.Asteroids;
// // _miningTool.Asteroid = _asteroid;
// // _toolSwitch.Activated = true;
// // Aim.Objective = Zone.GetAsteroidTransform(Task.Asteroids, _asteroid).xy;
// // Aim.Update(delta);
// // }
// // else NextAsteroid();
// // }
// // }
// // else
// // {
// // _entity.SetMessage("Out of cargo space. Returning home to offload cargo.");
// // GoHome(OnArriveHome);
// // }
// // }
// // }
// // }
// return base.Execute(delta);
// }
//
// // private void OnArriveHome()
// // {
// // var homeEntity = _context.ItemData.Get<Entity>(HomeEntity);
// // foreach (var item in _entity.Cargo
// // .Select(id => _context.ItemData.Get<ItemInstance>(id))
// // .Where(item => item is SimpleCommodity)
// // .Cast<SimpleCommodity>()
// // .ToArray())
// // {
// // int quantity = min((int) ((homeEntity.Capacity - homeEntity.OccupiedCapacity) / _context.GetData(item).Size), item.Quantity);
// // var newItem = _entity.RemoveCargo(item, quantity);
// // homeEntity.AddCargo(newItem);
// //
// // if (quantity < item.Quantity)
// // {
// // homeEntity.SetMessage("Colony is out of cargo space. Closing Mining Task.");
// // FinishTask();
// // _taskStarted = false;
// // return;
// // }
// // }
// // }
//
// private void NextAsteroid()
// {
// _asteroid = Zone.NearestAsteroid(Task.Asteroids, Entity.Position.xz);
// Entity.SetMessage("Selecting new asteroid.");
// }
//
// public PersistentBehaviorData Store()
// {
// return new MiningControllerPersistence
// {
// Task = Task
// };
// }
//
// public void Restore(PersistentBehaviorData data)
// {
// var miningControllerPersistence = data as MiningControllerPersistence;
// Task = miningControllerPersistence.Task;
// }
// }
//
// public class MiningControllerPersistence : PersistentBehaviorData
// {
// [JsonProperty("task"), Key(0)]
// public Mining Task;
// }

View file

@ -1,63 +0,0 @@
/* 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 MessagePack;
using Newtonsoft.Json;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
public class PatrolControllerData : ControllerData
{
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
{
return new PatrolController(context, this, entity, item);
}
}
public class PatrolController : IBehavior, IInitializableBehavior
{
public BehaviorData Data => _data;
private PatrolControllerData _data;
private ItemManager Context { get; }
private Entity Entity { get; }
private EquippedItem Item { get; }
private Locomotion _locomotion;
private Guid _targetOrbit;
public PatrolController(ItemManager context, PatrolControllerData data, Entity entity, EquippedItem item)
{
Context = context;
_data = data;
Entity = entity;
Item = item;
}
public void Initialize()
{
_locomotion = new Locomotion(Context, Entity, _data);
RandomTarget();
}
public bool Execute(float delta)
{
_locomotion.Objective = Entity.Zone.GetOrbitPosition(_targetOrbit);
_locomotion.Update(delta);
if(length(Entity.Position.xz - _locomotion.Objective) < _data.TargetDistance)
RandomTarget();
return true;
}
private void RandomTarget()
{
_targetOrbit = Entity.Zone.Planets.Keys.ToArray()[Context.Random.NextInt(Entity.Zone.Planets.Count)];
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: cc2d16773614011488d30e38edf7c3bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -13,7 +13,7 @@ public class SensorData : BehaviorData
[InspectableField, JsonProperty("sensitivity"), Key(3), RuntimeInspectable]
public PerformanceStat Sensitivity = new PerformanceStat();
[InspectableField, JsonProperty("sensitivityCurve"), Key(4), RuntimeInspectable]
[InspectableAnimationCurve, JsonProperty("sensitivityCurve"), Key(4), RuntimeInspectable]
public float4[] SensitivityCurve;
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
@ -44,19 +44,28 @@ public class Sensor : IBehavior
{
// TODO: Handle Active Detection / Visibility From Reflected Radiance
var hardpoint = Entity.Hardpoints[Item.Position.x, Item.Position.y];
var forward = Entity.HardpointTransforms.ContainsKey(hardpoint) ?
var forward = hardpoint != null && Entity.HardpointTransforms.ContainsKey(hardpoint) ?
normalize(Entity.HardpointTransforms[hardpoint].direction.xz) :
Entity.Direction;
foreach (var entity in Entity.Zone.Entities)
{
var diff = entity.Position.xz - Entity.Position.xz;
var angle = acos(dot(forward, normalize(diff)));
var dist = length(diff);
Entity.EntityInfoGathered[entity] +=
entity.Visibility *
Context.Evaluate(_data.Sensitivity, Item.EquippableItem, Entity) *
_data.SensitivityCurve.Evaluate(angle / PI) *
delta / dist;
{
if(entity != Entity)
{
var diff = entity.Position.xz - Entity.Position.xz;
var angle = acos(dot(forward, normalize(diff)));
var dist = length(diff);
float previous;
Entity.EntityInfoGathered.TryGetValue(entity, out previous);
var next = saturate(
previous +
entity.Visibility *
Context.Evaluate(_data.Sensitivity, Item.EquippableItem, Entity) *
_data.SensitivityCurve.Evaluate(angle / PI) *
delta / dist);
next *= 1 - Context.GameplaySettings.TargetInfoDecay * delta;
//Context.Log($"{entity.Name} visibility {(int)(previous * 100)}% -> {(int)(next * 100)}%");
Entity.EntityInfoGathered[entity] = next;
}
}
return true;
// var ship = Hardpoint.Ship.Ship.transform;

View file

@ -12,144 +12,144 @@ using RethinkDb.Driver.Ast;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
public class SurveyControllerData : ControllerData
{
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
{
return new SurveyController(context, this, entity, item);
}
}
public class SurveyController : ControllerBase<Survey>, IBehavior, IPersistentBehavior, IInitializableBehavior
{
private EquippedItem Item { get; }
private SurveyControllerData _data;
private bool _taskStarted;
private ResourceScanner _scanningTool;
private Switch _toolSwitch;
private Guid _targetPlanet;
private int _asteroid = -1;
public SurveyController(ItemManager itemManager, SurveyControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
{
_data = data;
Item = item;
}
public new void Initialize()
{
_scanningTool = Entity.GetBehavior<ResourceScanner>();
// _toolSwitch = Entity.GetSwitch<ResourceScanner>();
base.Initialize();
}
public new bool Execute(float delta)
{
// var corporation = _context.ItemData.Get<Corporation>(_entity.Corporation);
// if (surveyTask != null)
// {
// if (!_taskStarted)
// {
// if (surveyTask.Planets.Any())
// {
// _entity.SetMessage("Moving to survey target.");
// NextPlanet();
// _scanningTool.ScanTarget = _targetPlanet;
// _scanningTool.Asteroid = _asteroid;
// if (!corporation.PlanetSurveyFloor.ContainsKey(_targetPlanet) ||
// corporation.PlanetSurveyFloor[_targetPlanet] + .5f < _scanningTool.MinimumDensity)
// {
// var planetData = _context.ItemData.Get<BodyData>(_targetPlanet);
// MoveTo(() =>
// {
// var target = GetPosition(planetData, _asteroid);
// var targetToUs = _entity.Position - target;
// return target + normalize(targetToUs) * _scanningTool.Range / 2;
// },
// () => GetVelocity(planetData, _asteroid),
// () => _entity.SetMessage("Arrived at target. Scanning for resources."));
// _taskStarted = true;
// }
// else
// surveyTask.Planets.Remove(_targetPlanet);
// }
// else
// {
// _entity.SetMessage("Survey complete. Returning Home.");
// FinishTask();
// }
// }
// else
// {
// if (!Moving)
// {
// var planetData = _context.ItemData.Get<BodyData>(_targetPlanet);
// if ((!corporation.PlanetSurveyFloor.ContainsKey(_targetPlanet) ||
// corporation.PlanetSurveyFloor[_targetPlanet] + .5f < _scanningTool.MinimumDensity) &&
// length(_entity.Position - GetPosition(planetData, _asteroid)) < _scanningTool.Range)
// {
// _toolSwitch.Activated = true;
// Aim.Objective = GetPosition(planetData, _asteroid);
// Aim.Update(delta);
// }
// else
// {
// _toolSwitch.Activated = false;
// _taskStarted = false;
// }
// }
// }
// }
return base.Execute(delta);
}
private void NextPlanet()
{
var planets = Task.Planets.Select(id => Zone.Planets[id]);
var nearestPlanet = planets.MinBy(p => lengthsq(Entity.Position.xz - GetPosition(p)));
_asteroid = nearestPlanet is AsteroidBeltData ? Zone.NearestAsteroid(nearestPlanet.ID, Entity.Position.xz) : -1;
_targetPlanet = nearestPlanet.ID;
}
private float2 GetPosition(BodyData planet, int asteroid = -1)
{
if (planet is AsteroidBeltData)
{
if(asteroid == -1) asteroid = Zone.NearestAsteroid(planet.ID, Entity.Position.xz);
return Zone.AsteroidBelts[planet.ID].Positions[asteroid].xz;
}
return Zone.GetOrbitPosition(planet.Orbit);
}
private float2 GetVelocity(BodyData planet, int asteroid = -1)
{
if (planet is AsteroidBeltData)
{
if(asteroid == -1) asteroid = Zone.NearestAsteroid(planet.ID, Entity.Position.xz);
return Zone.AsteroidBelts[planet.ID].Velocities[asteroid];
}
return Zone.GetOrbitVelocity(planet.Orbit);
}
public PersistentBehaviorData Store()
{
return new SurveyControllerPersistence
{
Task = Task
};
}
public void Restore(PersistentBehaviorData data)
{
var surveyControllerPersistence = data as SurveyControllerPersistence;
Task = surveyControllerPersistence.Task;
}
}
public class SurveyControllerPersistence : PersistentBehaviorData
{
[JsonProperty("task"), Key(0)]
public Survey Task;
}
// [MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
// public class SurveyControllerData : ControllerData
// {
// public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
// {
// return new SurveyController(context, this, entity, item);
// }
// }
//
// public class SurveyController : ControllerBase<Survey>, IBehavior, IPersistentBehavior, IInitializableBehavior
// {
// private EquippedItem Item { get; }
//
// private SurveyControllerData _data;
// private bool _taskStarted;
// private ResourceScanner _scanningTool;
// private Switch _toolSwitch;
// private Guid _targetPlanet;
// private int _asteroid = -1;
//
// public SurveyController(ItemManager itemManager, SurveyControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
// {
// _data = data;
// Item = item;
// }
//
// public new void Initialize()
// {
// _scanningTool = Entity.GetBehavior<ResourceScanner>();
// // _toolSwitch = Entity.GetSwitch<ResourceScanner>();
// base.Initialize();
// }
//
// public new bool Execute(float delta)
// {
// // var corporation = _context.ItemData.Get<Corporation>(_entity.Corporation);
// // if (surveyTask != null)
// // {
// // if (!_taskStarted)
// // {
// // if (surveyTask.Planets.Any())
// // {
// // _entity.SetMessage("Moving to survey target.");
// // NextPlanet();
// // _scanningTool.ScanTarget = _targetPlanet;
// // _scanningTool.Asteroid = _asteroid;
// // if (!corporation.PlanetSurveyFloor.ContainsKey(_targetPlanet) ||
// // corporation.PlanetSurveyFloor[_targetPlanet] + .5f < _scanningTool.MinimumDensity)
// // {
// // var planetData = _context.ItemData.Get<BodyData>(_targetPlanet);
// // MoveTo(() =>
// // {
// // var target = GetPosition(planetData, _asteroid);
// // var targetToUs = _entity.Position - target;
// // return target + normalize(targetToUs) * _scanningTool.Range / 2;
// // },
// // () => GetVelocity(planetData, _asteroid),
// // () => _entity.SetMessage("Arrived at target. Scanning for resources."));
// // _taskStarted = true;
// // }
// // else
// // surveyTask.Planets.Remove(_targetPlanet);
// // }
// // else
// // {
// // _entity.SetMessage("Survey complete. Returning Home.");
// // FinishTask();
// // }
// // }
// // else
// // {
// // if (!Moving)
// // {
// // var planetData = _context.ItemData.Get<BodyData>(_targetPlanet);
// // if ((!corporation.PlanetSurveyFloor.ContainsKey(_targetPlanet) ||
// // corporation.PlanetSurveyFloor[_targetPlanet] + .5f < _scanningTool.MinimumDensity) &&
// // length(_entity.Position - GetPosition(planetData, _asteroid)) < _scanningTool.Range)
// // {
// // _toolSwitch.Activated = true;
// // Aim.Objective = GetPosition(planetData, _asteroid);
// // Aim.Update(delta);
// // }
// // else
// // {
// // _toolSwitch.Activated = false;
// // _taskStarted = false;
// // }
// // }
// // }
// // }
// return base.Execute(delta);
// }
//
// private void NextPlanet()
// {
// var planets = Task.Planets.Select(id => Zone.Planets[id]);
// var nearestPlanet = planets.MinBy(p => lengthsq(Entity.Position.xz - GetPosition(p)));
// _asteroid = nearestPlanet is AsteroidBeltData ? Zone.NearestAsteroid(nearestPlanet.ID, Entity.Position.xz) : -1;
// _targetPlanet = nearestPlanet.ID;
// }
//
// private float2 GetPosition(BodyData planet, int asteroid = -1)
// {
// if (planet is AsteroidBeltData)
// {
// if(asteroid == -1) asteroid = Zone.NearestAsteroid(planet.ID, Entity.Position.xz);
// return Zone.AsteroidBelts[planet.ID].Positions[asteroid].xz;
// }
// return Zone.GetOrbitPosition(planet.Orbit);
// }
//
// private float2 GetVelocity(BodyData planet, int asteroid = -1)
// {
// if (planet is AsteroidBeltData)
// {
// if(asteroid == -1) asteroid = Zone.NearestAsteroid(planet.ID, Entity.Position.xz);
// return Zone.AsteroidBelts[planet.ID].Velocities[asteroid];
// }
// return Zone.GetOrbitVelocity(planet.Orbit);
// }
//
// public PersistentBehaviorData Store()
// {
// return new SurveyControllerPersistence
// {
// Task = Task
// };
// }
//
// public void Restore(PersistentBehaviorData data)
// {
// var surveyControllerPersistence = data as SurveyControllerPersistence;
// Task = surveyControllerPersistence.Task;
// }
// }
//
// public class SurveyControllerPersistence : PersistentBehaviorData
// {
// [JsonProperty("task"), Key(0)]
// public Survey Task;
// }

View file

@ -11,94 +11,94 @@ using Newtonsoft.Json;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
public class TowingControllerData : ControllerData
{
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
{
return new TowingController(context, this, entity, item);
}
}
public class TowingController : ControllerBase<StationTowing>, IBehavior, IPersistentBehavior
{
private TowingControllerData _data;
private EquippedItem Item { get; }
private bool _taskStarted;
public TowingController(ItemManager itemManager, TowingControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
{
_data = data;
Item = item;
}
public new bool Execute(float delta)
{
if (Task != null)
{
if (!_taskStarted)
{
MoveTo(Task.Station, true, OnPickup);
_taskStarted = true;
}
}
return base.Execute(delta);
}
// void OnZoneArrival()
// {
// var towingTask = _context.ItemData.Get<StationTowing>(Task);
//
// _entity.SetMessage("Entering pickup phase.");
//
// MoveTo(_context.ItemData.Get<Entity>(towingTask.Station), true, OnPickup);
// }
void OnPickup()
{
Task.Station.SetParent(Entity);
Entity.SetMessage("Entering delivery phase.");
MoveTo(() =>
{
var orbitParent = Zone.GetOrbitPosition(Task.OrbitParent);
var parentToUs = Entity.Position.xz - orbitParent;
return orbitParent + normalize(parentToUs) * Task.OrbitDistance;
}, () => Zone.GetOrbitVelocity(Task.OrbitParent), OnDelivery);
}
void OnDelivery()
{
Task.Station.RemoveParent();
Entity.SetMessage("Target delivered. Returning Home.");
var orbit = Zone.CreateOrbit(Task.OrbitParent, Entity.Position.xz);
Task.Station.OrbitData = orbit.ID;
FinishTask();
_taskStarted = false;
}
public PersistentBehaviorData Store()
{
return new TowingControllerPersistence
{
Task = Task
};
}
public void Restore(PersistentBehaviorData data)
{
var towingControllerPersistence = data as TowingControllerPersistence;
Task = towingControllerPersistence.Task;
}
}
public class TowingControllerPersistence : PersistentBehaviorData
{
[JsonProperty("task"), Key(0)]
public StationTowing Task;
}
// [MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
// public class TowingControllerData : ControllerData
// {
// public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
// {
// return new TowingController(context, this, entity, item);
// }
// }
//
// public class TowingController : ControllerBase<StationTowing>, IBehavior, IPersistentBehavior
// {
//
// private TowingControllerData _data;
// private EquippedItem Item { get; }
// private bool _taskStarted;
//
// public TowingController(ItemManager itemManager, TowingControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
// {
// _data = data;
// Item = item;
// }
//
// public new bool Execute(float delta)
// {
// if (Task != null)
// {
// if (!_taskStarted)
// {
// MoveTo(Task.Station, true, OnPickup);
// _taskStarted = true;
// }
// }
// return base.Execute(delta);
// }
//
// // void OnZoneArrival()
// // {
// // var towingTask = _context.ItemData.Get<StationTowing>(Task);
// //
// // _entity.SetMessage("Entering pickup phase.");
// //
// // MoveTo(_context.ItemData.Get<Entity>(towingTask.Station), true, OnPickup);
// // }
//
// void OnPickup()
// {
// Task.Station.SetParent(Entity);
//
// Entity.SetMessage("Entering delivery phase.");
//
// MoveTo(() =>
// {
// var orbitParent = Zone.GetOrbitPosition(Task.OrbitParent);
// var parentToUs = Entity.Position.xz - orbitParent;
// return orbitParent + normalize(parentToUs) * Task.OrbitDistance;
// }, () => Zone.GetOrbitVelocity(Task.OrbitParent), OnDelivery);
// }
//
// void OnDelivery()
// {
// Task.Station.RemoveParent();
//
// Entity.SetMessage("Target delivered. Returning Home.");
//
// var orbit = Zone.CreateOrbit(Task.OrbitParent, Entity.Position.xz);
// Task.Station.OrbitData = orbit.ID;
//
// FinishTask();
// _taskStarted = false;
// }
//
// public PersistentBehaviorData Store()
// {
// return new TowingControllerPersistence
// {
// Task = Task
// };
// }
//
// public void Restore(PersistentBehaviorData data)
// {
// var towingControllerPersistence = data as TowingControllerPersistence;
// Task = towingControllerPersistence.Task;
// }
// }
//
// public class TowingControllerPersistence : PersistentBehaviorData
// {
// [JsonProperty("task"), Key(0)]
// public StationTowing Task;
// }

View file

@ -105,7 +105,7 @@ public class TurretController : IBehavior
}
else
{
EquippedEntity.Target.Value = EquippedEntity.Zone.Entities.FirstOrDefault(e => e is Ship);
EquippedEntity.Target.Value = EquippedEntity.VisibleHostiles.FirstOrDefault(e => e is Ship);
}
return true;
}

View file

@ -12,67 +12,67 @@ using RethinkDb.Driver.Ast;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
public class WanderControllerData : ControllerData
{
[InspectableField, JsonProperty("randomDockTime"), Key(6)]
public float RandomDockTime = 5;
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
{
return new WanderController(context, this, entity, item);
}
}
public class WanderController : ControllerBase<WanderTask>, IBehavior
{
public WanderTarget WanderTarget;
private WanderControllerData _data;
private EquippedItem Item { get; }
private Guid _target;
private float _dockTime = -1;
public WanderController(ItemManager itemManager, WanderControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
{
_data = data;
Item = item;
}
public new bool Execute(float delta)
{
if (!Moving && _dockTime < 0)
{
NextTarget();
}
_dockTime -= delta;
return base.Execute(delta);
}
private void NextTarget()
{
if (WanderTarget == WanderTarget.Planets)
{
var planets = Zone.Planets.Values.ToArray();
var randomPlanet = planets[ItemManager.Random.NextInt(planets.Length)];
MoveTo(() => Zone.GetOrbitPosition(randomPlanet.Orbit), () => Zone.GetOrbitVelocity(randomPlanet.Orbit));
}
else if (WanderTarget == WanderTarget.Orbitals)
{
var entities = Zone.Entities.Where(e=>e is OrbitalEntity).ToArray();
var randomEntity = entities[ItemManager.Random.NextInt(entities.Length)];
MoveTo(randomEntity, true, () =>
{
Entity.SetParent(randomEntity);
_dockTime = ItemManager.Random.NextFloat(_data.RandomDockTime);
});
}
}
}
public enum WanderTarget
{
Planets,
Orbitals
}
// [MessagePackObject, JsonObject(MemberSerialization.OptIn), EntityTypeRestriction(HullType.Ship), Order(-100)]
// public class WanderControllerData : ControllerData
// {
// [InspectableField, JsonProperty("randomDockTime"), Key(6)]
// public float RandomDockTime = 5;
//
// public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
// {
// return new WanderController(context, this, entity, item);
// }
// }
//
// public class WanderController : ControllerBase<WanderTask>, IBehavior
// {
// public WanderTarget WanderTarget;
//
// private WanderControllerData _data;
// private EquippedItem Item { get; }
// private Guid _target;
// private float _dockTime = -1;
//
// public WanderController(ItemManager itemManager, WanderControllerData data, Entity entity, EquippedItem item) : base(itemManager, data, entity)
// {
// _data = data;
// Item = item;
// }
//
// public new bool Execute(float delta)
// {
// if (!Moving && _dockTime < 0)
// {
// NextTarget();
// }
//
// _dockTime -= delta;
// return base.Execute(delta);
// }
//
// private void NextTarget()
// {
// if (WanderTarget == WanderTarget.Planets)
// {
// var planets = Zone.Planets.Values.ToArray();
// var randomPlanet = planets[ItemManager.Random.NextInt(planets.Length)];
// MoveTo(() => Zone.GetOrbitPosition(randomPlanet.Orbit), () => Zone.GetOrbitVelocity(randomPlanet.Orbit));
// }
// else if (WanderTarget == WanderTarget.Orbitals)
// {
// var entities = Zone.Entities.Where(e=>e is OrbitalEntity).ToArray();
// var randomEntity = entities[ItemManager.Random.NextInt(entities.Length)];
// MoveTo(randomEntity, true, () =>
// {
// Entity.SetParent(randomEntity);
// _dockTime = ItemManager.Random.NextFloat(_data.RandomDockTime);
// });
// }
// }
// }
//
// public enum WanderTarget
// {
// Planets,
// Orbitals
// }

View file

@ -73,6 +73,8 @@ public abstract class Weapon : IActivatedBehavior
public EquippedItem Item { get; }
public ItemManager Context { get; }
public abstract float DamagePerSecond { get; }
public abstract float RangeDamagePerSecond(float range);
public abstract int Ammo { get; }
public WeaponData WeaponData => _data;
public BehaviorData Data => _data;

View file

@ -37,6 +37,8 @@ public abstract class Entity
public readonly ReactiveCollection<EquippedItem> Equipment = new ReactiveCollection<EquippedItem>();
public readonly ReactiveCollection<EquippedCargoBay> CargoBays = new ReactiveCollection<EquippedCargoBay>();
public readonly ReactiveCollection<EquippedDockingBay> DockingBays = new ReactiveCollection<EquippedDockingBay>();
public readonly ReactiveCollection<Entity> VisibleEntities = new ReactiveCollection<Entity>();
public readonly ReactiveCollection<Entity> VisibleHostiles = new ReactiveCollection<Entity>();
public Entity Parent;
public List<Entity> Children = new List<Entity>();
@ -51,11 +53,11 @@ public abstract class Entity
public readonly Dictionary<string, float> Messages = new Dictionary<string, float>();
public readonly Dictionary<object, float> VisibilitySources = new Dictionary<object, float>();
public readonly Dictionary<Entity, float> EntityInfoGathered = new Dictionary<Entity, float>();
public readonly ReactiveDictionary<Entity, float> EntityInfoGathered = new ReactiveDictionary<Entity, float>();
public readonly Dictionary<HardpointData, (float3 position, float3 direction)> HardpointTransforms =
new Dictionary<HardpointData, (float3 position, float3 direction)>();
public readonly (List<IActivatedBehavior> behaviors, List<EquippedItem> items)[] TriggerGroups;
public readonly (List<Weapon> weapons, List<EquippedItem> items)[] TriggerGroups;
public List<IPopulationAssignment> PopulationAssignments = new List<IPopulationAssignment>();
@ -66,20 +68,20 @@ public abstract class Entity
private EquippedItem[] _orderedEquipment;
private List<Weapon> _weapons = new List<Weapon>();
private List<Weapon> _weaponsInRange = new List<Weapon>();
protected bool _active;
public IEnumerable<Weapon> Weapons
{
get => _weapons;
}
public IEnumerable<Weapon> WeaponsInRange
{
get => _weaponsInRange;
}
public float TargetRange { get; private set; }
public float MaxTemp { get; private set; }
public float MinTemp { get; private set; }
public bool ShieldEnabled // TODO: MAKE SHIELDS
{
get => true;
}
public ItemManager ItemManager { get; }
public int AssignedPopulation => PopulationAssignments.Sum(pa => pa.AssignedPopulation);
public float Mass { get; private set; }
@ -88,31 +90,48 @@ public abstract class Entity
public Subject<(int2 pos, float damage)> ArmorDamage = new Subject<(int2, float)>();
public Subject<(EquippedItem item, float damage)> ItemDamage = new Subject<(EquippedItem, float)>();
public Subject<float> HullDamage = new Subject<float>();
public Subject<Entity> Docked = new Subject<Entity>();
public UniRx.IObservable<EquippedItem> ItemDestroyed;
public UniRx.IObservable<int2> HullArmorDepleted;
public UniRx.IObservable<HardpointData> HardpointArmorDepleted;
public UniRx.IObservable<Weapon> WeaponDestroyed;
public bool Active
private List<IDisposable> _subscriptions = new List<IDisposable>();
public virtual void Activate()
{
get => _active;
set
_active = true;
foreach (var item in Equipment)
foreach (var behavior in item.Behaviors)
{
_active = value;
if (_active)
{
foreach (var item in Equipment)
foreach (var behavior in item.Behaviors)
{
if(behavior is IInitializableBehavior initializableBehavior)
initializableBehavior.Initialize();
}
OnActivate();
}
if(behavior is IInitializableBehavior initializableBehavior)
initializableBehavior.Initialize();
}
//foreach(var entity in Zone.Entities) EntityInfoGathered.Add(entity, 0);
_subscriptions.Add(Zone.Entities.ObserveRemove().Subscribe(remove =>
{
if (Target.Value == remove.Value) Target.Value = null;
EntityInfoGathered.Remove(remove.Value);
VisibleEntities.Remove(remove.Value);
VisibleHostiles.Remove(remove.Value);
}));
_subscriptions.Add(VisibleEntities.ObserveRemove().Subscribe(remove =>
{
if (Target.Value == remove.Value) Target.Value = null;
}));
GenerateTriggerGroups();
}
protected virtual void OnActivate(){}
public virtual void Deactivate()
{
foreach(var s in _subscriptions) s.Dispose();
_subscriptions.Clear();
_active = false;
EntityInfoGathered.Clear();
VisibleEntities.Clear();
VisibleHostiles.Clear();
}
public Entity(ItemManager itemManager, Zone zone, EquippableItem hull)
{
@ -121,12 +140,42 @@ public abstract class Entity
Hull = hull;
Name = hull.Name;
MapEntity();
TriggerGroups = new (List<IActivatedBehavior> triggers, List<EquippedItem> items)[itemManager.GameplaySettings.TriggerGroupCount];
TriggerGroups = new (List<Weapon> triggers, List<EquippedItem> items)[itemManager.GameplaySettings.TriggerGroupCount];
for(int i=0; i<itemManager.GameplaySettings.TriggerGroupCount; i++)
TriggerGroups[i] = (new List<IActivatedBehavior>(), new List<EquippedItem>());
TriggerGroups[i] = (new List<Weapon>(), new List<EquippedItem>());
ItemDestroyed = ItemDamage.Where(x => x.item.EquippableItem.Durability < .01f).Select(x=>x.item);
WeaponDestroyed = ItemDestroyed.Select(x => x.Behaviors.FirstOrDefault(b => b is Weapon) as Weapon).Where(x => x != null);
HullArmorDepleted = ArmorDamage.Where(x => Armor[x.pos.x, x.pos.y] < .01f).Select(x => x.pos);
EntityInfoGathered.ObserveReplace().Subscribe(replace =>
{
if (replace.OldValue < ItemManager.GameplaySettings.TargetDetectionInfoThreshold &&
replace.NewValue > ItemManager.GameplaySettings.TargetDetectionInfoThreshold)
{
VisibleEntities.Add(replace.Key);
if(IsHostileTo(replace.Key))
VisibleHostiles.Add(replace.Key);
}
if (replace.OldValue > ItemManager.GameplaySettings.TargetDetectionInfoThreshold &&
replace.NewValue < ItemManager.GameplaySettings.TargetDetectionInfoThreshold)
{
VisibleEntities.Remove(replace.Key);
if(IsHostileTo(replace.Key))
VisibleHostiles.Add(replace.Key);
}
});
EntityInfoGathered.ObserveRemove().Subscribe(remove => VisibleEntities.Remove(remove.Key));
}
public bool IsHostileTo(Entity other)
{
if (Faction == null)
{
return other.Faction != null;
}
return Faction.PlayerHostile && other.Faction == null;
}
private void MapEntity()
@ -165,6 +214,17 @@ public abstract class Entity
GearOccupancy = new EquippedItem[hullData.Shape.Width, hullData.Shape.Height];
}
public void GenerateTriggerGroups()
{
foreach (var group in Weapons
.GroupBy(w => w.Item.EquippableItem.Data)
.Select((weapons, index) => (weapons, index)))
{
TriggerGroups[group.index].weapons = group.weapons.ToList();
TriggerGroups[group.index].items = group.weapons.Select(w=>w.Item).ToList();
}
}
public void AddHeat(int2 position, float heat, bool ignoreThermalMass = false)
{
if (ignoreThermalMass)
@ -439,6 +499,8 @@ public abstract class Entity
bay.DockedShip = ship;
ship.SetParent(this);
Zone.Entities.Remove(ship);
Deactivate();
ship.Docked.OnNext(this);
}
return bay;
@ -459,6 +521,7 @@ public abstract class Entity
bay.DockedShip = null;
ship.RemoveParent();
Zone.Entities.Add(ship);
ship.Activate();
return true;
}
@ -556,15 +619,8 @@ public abstract class Entity
public virtual void Update(float delta)
{
var hullData = ItemManager.GetData(Hull) as HullData;
_weaponsInRange.Clear();
if (Target.Value != null)
{
var dist = length(Position - Target.Value.Position);
foreach (var w in _weapons)
if (w.MinRange < dist && dist < w.Range)
_weaponsInRange.Add(w);
}
TargetRange = Target.Value == null ? -1 : length(Position - Target.Value.Position);
foreach (var v in VisibilitySources.Keys.ToArray())
{
@ -572,6 +628,37 @@ public abstract class Entity
if (VisibilitySources[v] < 0.01f) VisibilitySources.Remove(v);
}
UpdateTemperature(delta);
if (_active)
{
foreach (var equippedItem in _orderedEquipment)
{
equippedItem.Update(delta);
}
foreach (var message in Messages.Keys.ToArray())
{
Messages[message] = Messages[message] - delta;
if (Messages[message] < 0)
Messages.Remove(message);
}
}
foreach(var child in Children)
child.Update(delta);
if (Parent != null)
{
Position = Parent.Position;
Velocity = Parent.Velocity;
}
else Position.y = Zone.GetHeight(Position.xz) + hullData.GridOffset;
}
private void UpdateTemperature(float delta)
{
var hullData = ItemManager.GetData(Hull) as HullData;
MaxTemp = Single.MinValue;
MinTemp = Single.MaxValue;
@ -657,65 +744,12 @@ public abstract class Entity
var swap = Temperature;
Temperature = NewTemperature;
NewTemperature = swap;
if (Active)
{
foreach (var equippedItem in _orderedEquipment)
{
equippedItem.Update(delta);
}
foreach (var message in Messages.Keys.ToArray())
{
Messages[message] = Messages[message] - delta;
if (Messages[message] < 0)
Messages.Remove(message);
}
}
foreach(var child in Children)
child.Update(delta);
if (Parent != null)
{
Position = Parent.Position;
Velocity = Parent.Velocity;
}
else Position.y = Zone.GetHeight(Position.xz) + hullData.GridOffset;
}
public void SetMessage(string message)
{
Messages[message] = ItemManager.GameplaySettings.MessageDuration;
}
// public void OnBeforeSerialize()
// {
// // Filter item behavior collections by those with any persistent behaviors
// // For each item create an object containing the item ID and a list of persistent behaviors
// // Then turn that into a dictionary mapping from item ID to an array of every behaviors persistent data
// PersistedBehaviors = EquippedItems
// .Where(item => item.Behaviors.Any(b=>b is IPersistentBehavior))
// .Select(item => new {equippable=item, behaviors = item.Behaviors
// .Where(b=>b is IPersistentBehavior)
// .Cast<IPersistentBehavior>()})
// .ToDictionary(x=> x.equippable.Position, x=>x.behaviors.Select(b => b.Store()).ToArray());
// }
//
// public void OnAfterDeserialize()
// {
// foreach(var item in EquippedItems)
// Hydrate(item);
//
// // Iterate only over the behaviors of items which contain persistent data
// // Filter the behaviors for each item to get the persistent ones, then cast them and combine with the persisted data array for that item
// foreach (var persistentBehaviorData in EquippedItems
// .Where(item => PersistedBehaviors.ContainsKey(item.Position))
// .SelectMany(item => item.Behaviors
// .Where(b=> b is IPersistentBehavior)
// .Cast<IPersistentBehavior>()
// .Zip(PersistedBehaviors[item.Position], (behavior, data) => new{behavior, data})))
// persistentBehaviorData.behavior.Restore(persistentBehaviorData.data);
// }
}
public class EquippedItem
@ -727,6 +761,9 @@ public class EquippedItem
public IBehavior[] Behaviors;
public Dictionary<int, BehaviorGroup> BehaviorGroups;
public event Action OnDisable;
public event Action OnEnable;
public float Conductivity { get; }
@ -734,7 +771,10 @@ public class EquippedItem
public int SortPosition;
public bool Online => EquippableItem.Durability > .01f && Temperature > _data.MinimumTemperature && Temperature < _data.MaximumTemperature;
public bool Online
{
get => _online;
}
public float Temperature
{
@ -749,6 +789,7 @@ public class EquippedItem
protected readonly ItemManager _itemManager;
private readonly EquippableItemData _data;
private Entity _entity;
private bool _online;
public EquippedItem(ItemManager itemManager, EquippableItem item, int2 position, Entity entity)
{
@ -792,16 +833,22 @@ public class EquippedItem
public void Update(float delta)
{
if (Temperature < _data.MinimumTemperature || Temperature > _data.MaximumTemperature)
{
_entity.ItemDamage.OnNext((this, delta));
EquippableItem.Durability -= delta;
}
foreach (var behavior in Behaviors)//.Where(b => b is IAlwaysUpdatedBehavior).Cast<IAlwaysUpdatedBehavior>())
foreach (var behavior in Behaviors)
if(behavior is IAlwaysUpdatedBehavior alwaysUpdatedBehavior) alwaysUpdatedBehavior.Update(delta);
if (Online)
var previouslyOnline = _online;
_online = EquippableItem.Durability > .01f && Temperature > _data.MinimumTemperature && Temperature < _data.MaximumTemperature;
if (!previouslyOnline && _online) OnEnable?.Invoke();
if (previouslyOnline && !_online) OnDisable?.Invoke();
if (_online)
{
foreach (var group in BehaviorGroups.Values)
{

View file

@ -114,12 +114,14 @@ public class EntityPack
}
for (var bayIndex = 0; bayIndex < pack.CargoContents.Length; bayIndex++)
foreach (var (position, item) in pack.CargoContents[bayIndex])
entity.CargoBays[bayIndex].TryStore(instantiate ? itemManager.Instantiate(item) : item, position);
if(entity.CargoBays.Count >= bayIndex + 1)
foreach (var (position, item) in pack.CargoContents[bayIndex])
entity.CargoBays[bayIndex].TryStore(instantiate ? itemManager.Instantiate(item) : item, position);
for (var bayIndex = 0; bayIndex < pack.DockingBayContents.Length; bayIndex++)
foreach (var (position, item) in pack.DockingBayContents[bayIndex])
entity.DockingBays[bayIndex].TryStore(instantiate ? itemManager.Instantiate(item) : item, position);
if(entity.DockingBays.Count >= bayIndex + 1)
foreach (var (position, item) in pack.DockingBayContents[bayIndex])
entity.DockingBays[bayIndex].TryStore(instantiate ? itemManager.Instantiate(item) : item, position);
// Iterate only over the behaviors of items which contain persistent data
// Filter the behaviors for each item to get the persistent ones, then cast them and combine with the persisted data array for that item

View file

@ -289,14 +289,10 @@ public static class Extensions
return t * (t * (a * t + b) + c) + d;
}
public static float AngleDiff(this float2 a, float2 b)
public static float Angle(this float2 from, float2 to)
{
var directionAngle = atan2(b.y, b.x);
var currentAngle = atan2(a.y, a.x);
var d1 = directionAngle - currentAngle;
var d2 = directionAngle - 2 * PI - currentAngle;
var d3 = directionAngle + 2 * PI - currentAngle;
return abs(d1) < abs(d2) ? abs(d1) < abs(d3) ? d1 : d3 : abs(d2) < abs(d3) ? d2 : d3;
var num = sqrt(lengthsq(from) * lengthsq(to));
return num < 1.00000000362749E-15 ? 0.0f : acos(clamp(dot(from, to) / num, -1f, 1f)) * 57.29578f;
}
// https://stackoverflow.com/a/3188835

View file

@ -1,10 +1,4 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Mathematics;
using Unity.Mathematics;
using static Unity.Mathematics.math;
public static class Noise1D

View file

@ -116,4 +116,8 @@ public class GameplaySettings
public float TargetArmorInfoThreshold;
public float TargetGearInfoThreshold;
public float ConvergenceMinimumDistance;
public float AgentRangeExponent = .25f;
public float AgentForwardLerp = .5f;
public float AgentMaxForwardDistance = 50;
public float AgentFiringMinDot = .99f;
}

View file

@ -40,11 +40,20 @@ public class Ship : Entity
public float RightStrafeThrust { get; private set; }
public float ClockwiseTorque { get; private set; }
public float CounterClockwiseTorque { get; private set; }
public float TurnTime(float2 direction)
{
var angleDiff = Direction.Angle(normalize(direction));
var clockwise = dot(direction, Direction.Rotate(ItemRotation.Clockwise)) > 0;
return angleDiff / ((clockwise ? ClockwiseTorque : CounterClockwiseTorque) / Mass);
}
public quaternion Rotation { get; private set; }
protected override void OnActivate()
public override void Activate()
{
base.Activate();
_allThrusters = GetBehaviors<Thruster>().ToArray();
_forwardThrusters = new HashSet<Thruster>(_allThrusters