Merge controls-helm: Ship.Turn is the only facing command; the helm split and face-the-aim
Soul gate closed at verdict cut-controls-helm.s3 (head b644fceb). Merged
under operator ruling aetheria-release:ruling:completed-cuts-merge.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
3fcdc20c00
34 changed files with 950 additions and 129 deletions
|
|
@ -251,22 +251,86 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
""interactions"": """",
|
||||
""initialStateCheck"": true,
|
||||
""priority"": 0
|
||||
},
|
||||
{
|
||||
""name"": ""Face Aim"",
|
||||
""type"": ""Button"",
|
||||
""id"": ""6d5bd565-8c0e-4789-9d51-177dc0ec8e11"",
|
||||
""expectedControlType"": ""Button"",
|
||||
""processors"": """",
|
||||
""interactions"": """",
|
||||
""initialStateCheck"": false,
|
||||
""priority"": 0
|
||||
},
|
||||
{
|
||||
""name"": ""Toggle Face Aim"",
|
||||
""type"": ""Button"",
|
||||
""id"": ""4067c72f-3e3d-4d7b-a8b5-356cab9793fc"",
|
||||
""expectedControlType"": ""Button"",
|
||||
""processors"": """",
|
||||
""interactions"": """",
|
||||
""initialStateCheck"": false,
|
||||
""priority"": 0
|
||||
}
|
||||
],
|
||||
""bindings"": [
|
||||
{
|
||||
""name"": """",
|
||||
""name"": ""Gamepad Move"",
|
||||
""id"": ""978bfe49-cc26-4a3d-ab7b-7d7a29327403"",
|
||||
""path"": ""<Gamepad>/leftStick"",
|
||||
""path"": ""2DVector(mode=2)"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": """",
|
||||
""action"": ""Move"",
|
||||
""isComposite"": true,
|
||||
""isPartOfComposite"": false
|
||||
},
|
||||
{
|
||||
""name"": ""up"",
|
||||
""id"": ""e9c57b4d-1cd8-4b2f-93d6-1745a37cabc9"",
|
||||
""path"": ""<Gamepad>/leftStick/up"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Gamepad"",
|
||||
""action"": ""Move"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": false
|
||||
""isPartOfComposite"": true
|
||||
},
|
||||
{
|
||||
""name"": ""WASD"",
|
||||
""name"": ""down"",
|
||||
""id"": ""05f19282-8364-4ae0-b5c3-d471c10601a7"",
|
||||
""path"": ""<Gamepad>/leftStick/down"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Gamepad"",
|
||||
""action"": ""Move"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": true
|
||||
},
|
||||
{
|
||||
""name"": ""left"",
|
||||
""id"": ""1ce4a07f-43af-4e82-a631-dbb43c8191d3"",
|
||||
""path"": ""<Gamepad>/leftShoulder"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Gamepad"",
|
||||
""action"": ""Move"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": true
|
||||
},
|
||||
{
|
||||
""name"": ""right"",
|
||||
""id"": ""bd1aed7e-b80b-4341-a550-4ef10bacf4b4"",
|
||||
""path"": ""<Gamepad>/rightShoulder"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Gamepad"",
|
||||
""action"": ""Move"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": true
|
||||
},
|
||||
{
|
||||
""name"": ""W S Q E"",
|
||||
""id"": ""00ca640b-d935-4593-8157-c05846ea39b3"",
|
||||
""path"": ""Dpad(mode=1)"",
|
||||
""interactions"": """",
|
||||
|
|
@ -301,7 +365,7 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
{
|
||||
""name"": ""left"",
|
||||
""id"": ""d2581a9b-1d11-4566-b27d-b92aff5fabbc"",
|
||||
""path"": ""<Keyboard>/a"",
|
||||
""path"": ""<Keyboard>/q"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Keyboard&Mouse"",
|
||||
|
|
@ -312,7 +376,7 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
{
|
||||
""name"": ""right"",
|
||||
""id"": ""fcfe95b8-67b9-4526-84b5-5d0bc98d6400"",
|
||||
""path"": ""<Keyboard>/d"",
|
||||
""path"": ""<Keyboard>/e"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Keyboard&Mouse"",
|
||||
|
|
@ -521,7 +585,7 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
{
|
||||
""name"": ""negative"",
|
||||
""id"": ""8536ca60-1fed-4f29-8060-04d46cfffcab"",
|
||||
""path"": ""<Keyboard>/q"",
|
||||
""path"": ""<Keyboard>/a"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": """",
|
||||
|
|
@ -532,13 +596,57 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
{
|
||||
""name"": ""positive"",
|
||||
""id"": ""19c175bf-d4e6-4626-8bee-91e0c473a94b"",
|
||||
""path"": ""<Keyboard>/e"",
|
||||
""path"": ""<Keyboard>/d"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": """",
|
||||
""action"": ""Turn"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": true
|
||||
},
|
||||
{
|
||||
""name"": """",
|
||||
""id"": ""fdff8f1f-0824-49bd-b09a-c1b792f16299"",
|
||||
""path"": ""<Gamepad>/leftStick/x"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Gamepad"",
|
||||
""action"": ""Turn"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": false
|
||||
},
|
||||
{
|
||||
""name"": """",
|
||||
""id"": ""1ee2fadd-3db4-45e6-8fa4-7a621ef7d165"",
|
||||
""path"": ""<Keyboard>/leftShift"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": ""Keyboard&Mouse"",
|
||||
""action"": ""Face Aim"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": false
|
||||
},
|
||||
{
|
||||
""name"": """",
|
||||
""id"": ""e9520d95-0458-439b-a37c-84c45237e110"",
|
||||
""path"": ""<Keyboard>/capsLock"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": ""Keyboard&Mouse"",
|
||||
""action"": ""Toggle Face Aim"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": false
|
||||
},
|
||||
{
|
||||
""name"": """",
|
||||
""id"": ""596ed367-be34-4951-9aac-d8caa57b411f"",
|
||||
""path"": ""<Gamepad>/leftStickPress"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": "";Gamepad"",
|
||||
""action"": ""Toggle Face Aim"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1208,6 +1316,8 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
m_Player_HideUI = m_Player.FindAction("Hide UI", throwIfNotFound: true);
|
||||
m_Player_TractorBeam = m_Player.FindAction("Tractor Beam", throwIfNotFound: true);
|
||||
m_Player_Turn = m_Player.FindAction("Turn", throwIfNotFound: true);
|
||||
m_Player_FaceAim = m_Player.FindAction("Face Aim", throwIfNotFound: true);
|
||||
m_Player_ToggleFaceAim = m_Player.FindAction("Toggle Face Aim", throwIfNotFound: true);
|
||||
// UI
|
||||
m_UI = asset.FindActionMap("UI", throwIfNotFound: true);
|
||||
m_UI_Navigate = m_UI.FindAction("Navigate", throwIfNotFound: true);
|
||||
|
|
@ -1325,6 +1435,8 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
private readonly InputAction m_Player_HideUI;
|
||||
private readonly InputAction m_Player_TractorBeam;
|
||||
private readonly InputAction m_Player_Turn;
|
||||
private readonly InputAction m_Player_FaceAim;
|
||||
private readonly InputAction m_Player_ToggleFaceAim;
|
||||
/// <summary>
|
||||
/// Provides access to input actions defined in input action map "Player".
|
||||
/// </summary>
|
||||
|
|
@ -1401,6 +1513,14 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
/// </summary>
|
||||
public InputAction @Turn => m_Wrapper.m_Player_Turn;
|
||||
/// <summary>
|
||||
/// Provides access to the underlying input action "Player/FaceAim".
|
||||
/// </summary>
|
||||
public InputAction @FaceAim => m_Wrapper.m_Player_FaceAim;
|
||||
/// <summary>
|
||||
/// Provides access to the underlying input action "Player/ToggleFaceAim".
|
||||
/// </summary>
|
||||
public InputAction @ToggleFaceAim => m_Wrapper.m_Player_ToggleFaceAim;
|
||||
/// <summary>
|
||||
/// Provides access to the underlying input action map instance.
|
||||
/// </summary>
|
||||
public InputActionMap Get() { return m_Wrapper.m_Player; }
|
||||
|
|
@ -1474,6 +1594,12 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
@Turn.started += instance.OnTurn;
|
||||
@Turn.performed += instance.OnTurn;
|
||||
@Turn.canceled += instance.OnTurn;
|
||||
@FaceAim.started += instance.OnFaceAim;
|
||||
@FaceAim.performed += instance.OnFaceAim;
|
||||
@FaceAim.canceled += instance.OnFaceAim;
|
||||
@ToggleFaceAim.started += instance.OnToggleFaceAim;
|
||||
@ToggleFaceAim.performed += instance.OnToggleFaceAim;
|
||||
@ToggleFaceAim.canceled += instance.OnToggleFaceAim;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1533,6 +1659,12 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
@Turn.started -= instance.OnTurn;
|
||||
@Turn.performed -= instance.OnTurn;
|
||||
@Turn.canceled -= instance.OnTurn;
|
||||
@FaceAim.started -= instance.OnFaceAim;
|
||||
@FaceAim.performed -= instance.OnFaceAim;
|
||||
@FaceAim.canceled -= instance.OnFaceAim;
|
||||
@ToggleFaceAim.started -= instance.OnToggleFaceAim;
|
||||
@ToggleFaceAim.performed -= instance.OnToggleFaceAim;
|
||||
@ToggleFaceAim.canceled -= instance.OnToggleFaceAim;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -2059,6 +2191,20 @@ public partial class @AetheriaInput: IInputActionCollection2, IDisposable
|
|||
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
|
||||
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
|
||||
void OnTurn(InputAction.CallbackContext context);
|
||||
/// <summary>
|
||||
/// Method invoked when associated input action "Face Aim" is either <see cref="UnityEngine.InputSystem.InputAction.started" />, <see cref="UnityEngine.InputSystem.InputAction.performed" /> or <see cref="UnityEngine.InputSystem.InputAction.canceled" />.
|
||||
/// </summary>
|
||||
/// <seealso cref="UnityEngine.InputSystem.InputAction.started" />
|
||||
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
|
||||
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
|
||||
void OnFaceAim(InputAction.CallbackContext context);
|
||||
/// <summary>
|
||||
/// Method invoked when associated input action "Toggle Face Aim" is either <see cref="UnityEngine.InputSystem.InputAction.started" />, <see cref="UnityEngine.InputSystem.InputAction.performed" /> or <see cref="UnityEngine.InputSystem.InputAction.canceled" />.
|
||||
/// </summary>
|
||||
/// <seealso cref="UnityEngine.InputSystem.InputAction.started" />
|
||||
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
|
||||
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
|
||||
void OnToggleFaceAim(InputAction.CallbackContext context);
|
||||
}
|
||||
/// <summary>
|
||||
/// Interface to implement callback methods for all input action callbacks associated with input actions defined by "UI" which allows adding and removing callbacks.
|
||||
|
|
|
|||
|
|
@ -192,6 +192,9 @@ public class ActionGameManager : MonoBehaviour
|
|||
// private ShipInput _shipInput;
|
||||
private float2 _entityYawPitch;
|
||||
private float3 _viewDirection;
|
||||
// The player's face-the-aim state (Left Shift held, Caps Lock latched); Ship.Turn is its only output.
|
||||
private readonly HelmInput _helm = new HelmInput();
|
||||
private TextMeshProUGUI _faceAimMark;
|
||||
private (HardpointData[] hardpoints, Transform[] barrels, PlaceUIElementWorldspace crosshair)[] _articulationGroups;
|
||||
private (LockWeapon targetLock, PlaceUIElementWorldspace indicator, Rotate spin)[] _lockingIndicators;
|
||||
private Dictionary<Entity, VisibleTargetIndicator> _visibleHostileIndicators = new Dictionary<Entity, VisibleTargetIndicator>();
|
||||
|
|
@ -290,6 +293,20 @@ public class ActionGameManager : MonoBehaviour
|
|||
InputDisplayLayout.Input = Input.asset;
|
||||
Input.Global.Enable();
|
||||
|
||||
// The latch is the game's own, so it can disagree with the keyboard's Caps Lock light: show it.
|
||||
Input.Player.ToggleFaceAim.performed += context => _helm.Toggle();
|
||||
var markParent = GameplayUI != null ? GameplayUI.transform : UiRoot.transform;
|
||||
var markObject = new GameObject("Face Aim Mark", typeof(RectTransform));
|
||||
markObject.transform.SetParent(markParent, false);
|
||||
_faceAimMark = markObject.AddComponent<TextMeshProUGUI>();
|
||||
_faceAimMark.text = "FACE AIM";
|
||||
_faceAimMark.fontSize = 24;
|
||||
_faceAimMark.alignment = TextAlignmentOptions.Center;
|
||||
_faceAimMark.raycastTarget = false;
|
||||
_faceAimMark.rectTransform.anchorMin = _faceAimMark.rectTransform.anchorMax = new Vector2(.5f, .12f);
|
||||
_faceAimMark.rectTransform.sizeDelta = new Vector2(300, 40);
|
||||
_faceAimMark.enabled = false;
|
||||
|
||||
_zoomLevelIndex = Settings.DefaultMinimapZoom;
|
||||
Input.Player.MinimapZoom.performed += context =>
|
||||
{
|
||||
|
|
@ -663,7 +680,7 @@ public class ActionGameManager : MonoBehaviour
|
|||
if (!(CurrentEntity is Ship ship) || ship.WormholeAnimationInProgress) return;
|
||||
// var wormholeCameraFollow = new GameObject("Wormhole Camera Follow").transform;
|
||||
// wormholeCameraFollow.position = new Vector3(wormhole.Position.x, -50, wormhole.Position.y);
|
||||
// wormholeCameraFollow.rotation = Quaternion.LookRotation(Vector3.down, ship.LookDirection);
|
||||
// wormholeCameraFollow.rotation = Quaternion.LookRotation(Vector3.down, ship.Aim);
|
||||
// WormholeCamera.enabled = true;
|
||||
// WormholeCamera.Follow = wormholeCameraFollow;
|
||||
// FollowCamera.enabled = false;
|
||||
|
|
@ -1281,16 +1298,19 @@ public class ActionGameManager : MonoBehaviour
|
|||
var sensitivity = PlayerSettings.InputSettings.Sensitivity;
|
||||
_entityYawPitch = float2(_entityYawPitch.x + look.x * sensitivity.x, clamp(_entityYawPitch.y + look.y * sensitivity.y, -.45f * PI, .45f * PI));
|
||||
_viewDirection = mul(float3(0, 0, 1), CultMath.float3x3.Euler(float3(_entityYawPitch.yx, 0), RotationOrder.YXZ));
|
||||
CurrentEntity.LookDirection = _viewDirection;
|
||||
CurrentEntity.Aim = _viewDirection;
|
||||
HeatstrokePost.weight = saturate(unlerp(0, Settings.GameplaySettings.SevereHeatstrokeRiskThreshold, CurrentEntity.Heatstroke));
|
||||
var severeHeatstrokeLerp = saturate(unlerp(Settings.GameplaySettings.SevereHeatstrokeRiskThreshold, 1, CurrentEntity.Heatstroke));
|
||||
SevereHeatstrokePost.weight =
|
||||
severeHeatstrokeLerp + severeHeatstrokeLerp * (1 - severeHeatstrokeLerp) *
|
||||
max(Settings.HeatstrokePhasingFloor, sin(Time.time * Settings.HeatstrokePhasingFrequency));
|
||||
|
||||
_faceAimMark.enabled = _helm.Latched;
|
||||
if(CurrentEntity is Ship ship)
|
||||
{
|
||||
ship.MovementDirection = Input.Player.Move.ReadValue<Vector2>().ToCultMath();
|
||||
_helm.Held = Input.Player.FaceAim.IsPressed();
|
||||
ship.Turn = _helm.Demand(ship, _viewDirection.xz, Input.Player.Turn.ReadValue<float>());
|
||||
}
|
||||
|
||||
UpdateFireControlDebug(CurrentEntity.Target.Value);
|
||||
|
|
|
|||
|
|
@ -398,7 +398,7 @@ public class EntityInstance : MonoBehaviour
|
|||
x.Value.material.SetFloat("_Emission", Entity.ItemManager.GameplaySettings.TemperatureEmissionCurve.Evaluate(x.Key.RadiatorTemperature));
|
||||
}
|
||||
|
||||
LookAtPoint.position = transform.position + Entity.LookDirection.ToUnity() *
|
||||
LookAtPoint.position = transform.position + Entity.Aim.ToUnity() *
|
||||
(!Entity.Target.Value.IsNone ? max(Entity.TargetRange,Entity.ItemManager.GameplaySettings.ConvergenceMinimumDistance) : 10000);
|
||||
LocalSpace.localPosition = transform.position = Entity.Position.ToUnity();
|
||||
if (_influenceInstance)
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public class ShipInstance : EntityInstance
|
|||
base.Update();
|
||||
|
||||
TractorBeam.Power = Entity.TractorPower;
|
||||
TractorBeam.Direction = Entity.LookDirection.ToUnity();
|
||||
TractorBeam.Direction = Entity.Aim.ToUnity();
|
||||
|
||||
if (_aetherDrive != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ public class GuidedProjectileManager : InstantWeaponEffectManager
|
|||
p.Velocity = barrel.forward * weapon.Velocity;
|
||||
p.Thrust = item.Evaluate(guidance.Thrust);
|
||||
p.TopSpeed = item.Evaluate(guidance.MissileVelocity);
|
||||
p.TargetPosition = () => (source.Entity.Position + length(source.LookAtPoint.position.ToCultMath() - source.Entity.Position) * source.Entity.LookDirection).ToUnity();
|
||||
p.TargetPosition = () => (source.Entity.Position + length(source.LookAtPoint.position.ToCultMath() - source.Entity.Position) * source.Entity.Aim).ToUnity();
|
||||
Bind(p, source.Entity.Zone, shotId);
|
||||
}
|
||||
else Debug.LogError($"Weapon {item.Data.Name} linked to {name} effect, but is not a Launcher!");
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ public class Agent
|
|||
|
||||
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())
|
||||
|
|
@ -63,10 +66,10 @@ public class Agent
|
|||
// 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.Turn = Steering.Toward(Ship, deltaVDirection);
|
||||
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
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,8 @@ public class CombatState : BaseState
|
|||
weapon.Deactivate();
|
||||
}
|
||||
}
|
||||
_agent.Ship.LookDirection = toTarget;
|
||||
_agent.Ship.Aim = toTarget;
|
||||
_agent.Ship.Turn = Steering.Toward(_agent.Ship, toTarget.xz);
|
||||
_agent.Accelerate(movementDirection * _agent.TopSpeed, true);//selectedGroup >= 0);
|
||||
|
||||
// Fire charged guns!
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ public abstract class MoveToState : BaseState
|
|||
|
||||
// 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.Ship.Aim = float3(dir.x, 0, dir.y);
|
||||
_agent.Ship.Turn = Steering.Toward(_agent.Ship, dir);
|
||||
_agent.Accelerate(desiredVelocity);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ public class LockWeapon : InstantWeapon
|
|||
DirectionImpact = Evaluate(_data.DirectionImpact);
|
||||
Decay = Evaluate(_data.Decay);
|
||||
|
||||
var degrees = acos(dot(normalize(target.Position - Entity.Position), normalize(Entity.LookDirection))) * 57.2958f;
|
||||
var degrees = acos(dot(normalize(target.Position - Entity.Position), normalize(Entity.Aim))) * 57.2958f;
|
||||
if (degrees < LockAngle)
|
||||
{
|
||||
var lerp = 1 - unlerp(0, 90, degrees);
|
||||
|
|
|
|||
|
|
@ -71,10 +71,10 @@ public class TurretController : Behavior, IInitializableBehavior
|
|||
target.Position, targetVelocity
|
||||
);
|
||||
predictedPosition.y = Entity.Zone.GetHeight(predictedPosition.xz) + targetHullData.GridOffset;
|
||||
Entity.LookDirection = normalize(predictedPosition - Entity.Position);
|
||||
Entity.Aim = normalize(predictedPosition - Entity.Position);
|
||||
}
|
||||
else
|
||||
Entity.LookDirection = normalize(diff);
|
||||
Entity.Aim = normalize(diff);
|
||||
|
||||
foreach (var x in _weapons)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ public abstract class Entity
|
|||
// notice and clear it.
|
||||
public ReactiveProperty<EquippedItem> TargetItem = new ReactiveProperty<EquippedItem>((EquippedItem)null);
|
||||
|
||||
public float3 LookDirection;
|
||||
// Where the pilot points: reticle, lock cone, tractor, guided rounds, fire direction. Steers nothing; the
|
||||
// hull turns only by Ship.Turn.
|
||||
public float3 Aim;
|
||||
|
||||
public string Name;
|
||||
|
||||
|
|
@ -406,7 +408,7 @@ public abstract class Entity
|
|||
// The candidate nearest the look direction by planar angle; picking the held target again clears it.
|
||||
public bool TargetUnderReticle()
|
||||
{
|
||||
var look = float2(LookDirection.x, LookDirection.z);
|
||||
var look = float2(Aim.x, Aim.z);
|
||||
if (!PickTarget(new AngleKey(this, Position.xz, look), out var picked)) return false;
|
||||
return SetTarget(Target.Value.Equals(picked) ? TargetRef.None : picked);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,16 +107,16 @@ public static class FireControl
|
|||
}
|
||||
|
||||
// Operator ruling 2026-09-30: a shot with nothing to bear on flies along where its shooter is aiming
|
||||
// (Entity.LookDirection -- the player's mouse or a turret's tracking), restricted by the mount's arc. The
|
||||
// (Entity.Aim -- the player's mouse or a turret's tracking), restricted by the mount's arc. The
|
||||
// arc is InArc's own cone: an aim inside it is used as is, one outside it is turned to the nearest edge of
|
||||
// the same cone. There is no second bearing test here; the edge is where InArc stops passing. A shooter
|
||||
// with no aim set (LookDirection zero) aims down the mount, the same fallback InArc takes for a zero bearing.
|
||||
// with no aim set (Aim zero) aims down the mount, the same fallback InArc takes for a zero bearing.
|
||||
// The simulation had no aim clamp before this: the Unity barrel's ArticulationPoint clamps the picture,
|
||||
// and a target outside the arc merely priced the shot at zero.
|
||||
public static float2 AimDirection(EquippedItem item, Entity shooter)
|
||||
{
|
||||
var mount = MountDirection(item).xz;
|
||||
var look = shooter.LookDirection.xz;
|
||||
var look = shooter.Aim.xz;
|
||||
if (lengthsq(look) < 1e-6f) return mount;
|
||||
look = normalize(look);
|
||||
if (InArc(item, float3(look.x, 0, look.y))) return look;
|
||||
|
|
|
|||
18
Assets/Scripts/ServerShared/HelmInput.cs
Normal file
18
Assets/Scripts/ServerShared/HelmInput.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using CultMath;
|
||||
using static CultMath.math;
|
||||
|
||||
// The player's face-the-aim state, engine-free. Held follows the Face Aim hold input each frame; Latched is
|
||||
// flipped by the Caps Lock press. Either one faces the aim; neither leaves the hull to the turn axis.
|
||||
// Runtime only, never saved: Latched starts false each session.
|
||||
public class HelmInput
|
||||
{
|
||||
public bool Held;
|
||||
public bool Latched;
|
||||
|
||||
public bool FaceAim => Held || Latched;
|
||||
|
||||
public void Toggle() => Latched = !Latched;
|
||||
|
||||
public float Demand(Ship ship, float2 aim, float turnAxis) =>
|
||||
FaceAim ? Steering.Toward(ship, aim) : clamp(turnAxis, -1f, 1f);
|
||||
}
|
||||
2
Assets/Scripts/ServerShared/HelmInput.cs.meta
Normal file
2
Assets/Scripts/ServerShared/HelmInput.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f863bb50e53ae30408f5ef8be1462e12
|
||||
|
|
@ -25,6 +25,9 @@ public class Ship : Entity
|
|||
// [IgnoreMember] public Targetable Target;
|
||||
public Entity HomeEntity;
|
||||
public float2 MovementDirection;
|
||||
// The one facing command, -1..1, positive clockwise. Written each tick by the pilot (player input or the
|
||||
// ship's agent); a heading becomes a demand only through Steering.Toward. Runtime only, never packed.
|
||||
public float Turn;
|
||||
public bool IsPlayerShip;
|
||||
|
||||
private HashSet<EquippedItem> _thrusterItems;
|
||||
|
|
@ -275,20 +278,11 @@ public class Ship : Entity
|
|||
foreach (var thruster in _forwardThrusters) thruster.Axis += MovementDirection.y;
|
||||
foreach (var thruster in _reverseThrusters) thruster.Axis += -MovementDirection.y;
|
||||
|
||||
var look = normalize(LookDirection.xz);
|
||||
var deltaRot = dot(look, normalize(Direction).Rotate(ItemRotation.Clockwise));
|
||||
if (abs(deltaRot) < .01f)
|
||||
{
|
||||
deltaRot = 0;
|
||||
Direction = lerp(Direction, look, min(delta, 1));
|
||||
}
|
||||
deltaRot = pow(abs(deltaRot), .5f) * sign(deltaRot);
|
||||
|
||||
foreach (var thruster in _clockwiseThrusters) thruster.Axis += deltaRot;
|
||||
foreach (var thruster in _counterClockwiseThrusters) thruster.Axis += -deltaRot;
|
||||
foreach (var thruster in _clockwiseThrusters) thruster.Axis += Turn;
|
||||
foreach (var thruster in _counterClockwiseThrusters) thruster.Axis += -Turn;
|
||||
|
||||
foreach (var drive in _aetherDrives)
|
||||
drive.Axis = float3(MovementDirection.y, MovementDirection.x, deltaRot);
|
||||
drive.Axis = float3(MovementDirection.y, MovementDirection.x, Turn);
|
||||
}
|
||||
|
||||
var velocityMagnitude = length(Velocity);
|
||||
|
|
|
|||
24
Assets/Scripts/ServerShared/Steering.cs
Normal file
24
Assets/Scripts/ServerShared/Steering.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using CultMath;
|
||||
using static CultMath.math;
|
||||
|
||||
// The one law that turns a wanted heading into a facing command. Ship.Update reads only Ship.Turn; whoever
|
||||
// wants the hull to face somewhere (the player's face-the-aim state, an agent state) asks here and writes Turn.
|
||||
public static class Steering
|
||||
{
|
||||
const float Deadband = .01f;
|
||||
|
||||
// Positive is clockwise. Lateral component of the heading with sqrt shaping and a deadband; a heading
|
||||
// behind the hull turns at full demand (its lateral component alone would vanish at exactly astern).
|
||||
// A zero or non-finite heading holds course.
|
||||
public static float Toward(Ship ship, float2 heading)
|
||||
{
|
||||
if (!float.IsFinite(heading.x) || !float.IsFinite(heading.y) || lengthsq(heading) < 1e-12f) return 0f;
|
||||
var dir = normalize(ship.Direction);
|
||||
var wanted = normalize(heading);
|
||||
var d = dot(wanted, dir.Rotate(ItemRotation.Clockwise));
|
||||
if (dot(wanted, dir) < 0f) return d < 0f ? -1f : 1f;
|
||||
if (abs(d) < Deadband) return 0f;
|
||||
return sqrt(abs(d)) * sign(d);
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/ServerShared/Steering.cs.meta
Normal file
2
Assets/Scripts/ServerShared/Steering.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b84ac5db42877d45b7f6286cc2e2b93
|
||||
|
|
@ -437,7 +437,7 @@ public class ZoneRenderer : MonoBehaviour
|
|||
foreach (var loot in _loot)
|
||||
{
|
||||
loot.ViewOrigin = PerspectiveEntity.Position;
|
||||
loot.ViewDirection = PerspectiveEntity.LookDirection;
|
||||
loot.ViewDirection = PerspectiveEntity.Aim;
|
||||
}
|
||||
|
||||
// if (SlimeRenderer.SpawnPositions.Length != _suns.Length)
|
||||
|
|
|
|||
Loading…
Reference in a new issue