diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 1f66c8b6b..5c0c7cf19 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -17031,13 +17031,10 @@ static void RequireUnityPhysicsIsNotGameplayAuthority(string root) var ymirBridgePath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Physics", "AetheriaYmirPhysicsBridge.cs"); var projectilePath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "Projectile.cs"); - var projectileManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ProjectileManager.cs"); var laserPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "Laser.cs"); var laserManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "LaserManager.cs"); var constantLaserPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ConstantLaser.cs"); var constantLaserManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ConstantLaserManager.cs"); - var guidedProjectilePath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "GuidedProjectile.cs"); - var guidedProjectileManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "GuidedProjectileManager.cs"); var hitscanPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "HitscanEffect.cs"); var hitscanManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "HitscanManager.cs"); var clickRaycasterPath = Path.Combine(root, "Assets", "Scripts", "UI", "ClickRaycaster.cs"); @@ -17046,28 +17043,20 @@ static void RequireUnityPhysicsIsNotGameplayAuthority(string root) { [ymirBridgePath] = new[] { - "YmirPhysicsQueries.Step(request)", "YmirPhysicsQueries.OverlapSphere(", "YmirPhysicsQueries.CastSphere(request)", "YmirPhysicsQueries.OverlapCircle(request)", "YmirPhysicsQueries.CastCircle(request)", - "TryStepProjectile(", "TryCastZoneHulls(", "TryBuildDaemonWorld(", "private const string DaemonEntityBodyPrefix = \"aetheria.daemon.entity.\"", "id = DaemonEntityBodyPrefix + daemonEntityIndex", "TryResolveDaemonEntityHull(zoneRenderer, hit.bodyId, out var hull)", "TryResolveTargetDaemonHull(target, hit.bodyId, out var hull)", - "TryResolveTargetDaemonHull(projectile.TargetInstance, otherBody, out var hull)", "TargetDaemonEntityIndex(", "onlyDaemonEntityIndex.HasValue && onlyDaemonEntityIndex.Value != daemonEntityIndex", "TryParseDaemonEntityBodyId(" }, - [projectilePath] = new[] - { - "AetheriaYmirPhysicsBridge.Instance.TryStepProjectile", - "projectile killed instead of falling back to Unity physics." - }, [laserPath] = new[] { "public ZoneRenderer ZoneRenderer { get; set; }", @@ -17088,17 +17077,6 @@ static void RequireUnityPhysicsIsNotGameplayAuthority(string root) { "p.ZoneRenderer = source.ZoneRenderer" }, - [guidedProjectilePath] = new[] - { - "public ZoneRenderer ZoneRenderer { get; set; }", - "AetheriaYmirPhysicsBridge.Instance.TryCastZoneHulls", - "ZoneRenderer,", - "child.ZoneRenderer = ZoneRenderer" - }, - [guidedProjectileManagerPath] = new[] - { - "p.ZoneRenderer = source.ZoneRenderer" - }, [hitscanPath] = new[] { "public ZoneRenderer ZoneRenderer { get; set; }", @@ -17159,6 +17137,26 @@ static void RequireUnityPhysicsIsNotGameplayAuthority(string root) string.Join("; ", ymirTransportHits)); } + var forbiddenUnityProjectileAuthorityPaths = new[] + { + projectilePath, + Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ProjectileManager.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "GuidedProjectile.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "GuidedProjectileManager.cs"), + Path.Combine(root, "Assets", "Scripts", "Gameplay", "Physics", "YmirUnityRuntimeProofTests.cs") + }; + var survivingUnityProjectileAuthority = forbiddenUnityProjectileAuthorityPaths + .Where(File.Exists) + .Select(path => Path.GetRelativePath(root, path)) + .ToArray(); + if (survivingUnityProjectileAuthority.Length > 0 || + File.ReadAllText(ymirBridgePath).Contains("TryStepProjectile", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Ordinary weapon travel is daemon receipt presentation, not Unity/Ymir projectile simulation: " + + string.Join(", ", survivingUnityProjectileAuthority)); + } + if (ymirBridge.Contains("BuildZoneWorld(", StringComparison.Ordinal) || ymirBridge.Contains("BuildTargetWorld(", StringComparison.Ordinal) || ymirBridge.Contains("_zoneBodies", StringComparison.Ordinal) || @@ -17181,8 +17179,6 @@ static void RequireUnityPhysicsIsNotGameplayAuthority(string root) var forbiddenWeaponZoneHandles = new Dictionary { - [projectilePath] = new[] { "public Zone Zone", "Zone { get; set; }" }, - [projectileManagerPath] = new[] { "p.Zone =", "source.Entity.Zone" }, [hitscanPath] = new[] { "public Zone Zone", "Zone { get; set; }" }, [hitscanManagerPath] = new[] { "p.Zone =", "source.Entity.Zone" } }; diff --git a/Assets/Scripts/Gameplay/Physics/AetheriaYmirPhysicsBridge.cs b/Assets/Scripts/Gameplay/Physics/AetheriaYmirPhysicsBridge.cs index de6c524ff..2d75f4fde 100644 --- a/Assets/Scripts/Gameplay/Physics/AetheriaYmirPhysicsBridge.cs +++ b/Assets/Scripts/Gameplay/Physics/AetheriaYmirPhysicsBridge.cs @@ -4,18 +4,14 @@ using UnityEngine; public sealed class AetheriaYmirPhysicsBridge : MonoBehaviour { - private const string ProjectileBodyId = "aetheria.projectile"; private const string DaemonEntityBodyPrefix = "aetheria.daemon.entity."; private static AetheriaYmirPhysicsBridge _instance; public bool EnableProjectileCutover = true; - public float ProjectileRadius = 0.1f; - public float ProjectileMass = 1.0f; public AetheriaDaemonObserver DaemonObserver; public IYmirDaemonBodySource DaemonBodySource { get; set; } - private readonly List _projectileBodies = new List(); private readonly List _daemonBodies = new List(); private readonly List _clickableBodies = new List(); private readonly Dictionary _hullBodyMap = new Dictionary(StringComparer.Ordinal); @@ -37,60 +33,6 @@ public sealed class AetheriaYmirPhysicsBridge : MonoBehaviour } } - public bool TryStepProjectile(Projectile projectile, float deltaTime, out AetheriaYmirProjectileStep step) - { - step = default; - if (!EnableProjectileCutover || projectile == null || deltaTime <= 0) - return false; - - var request = BuildProjectileRequest(projectile, deltaTime); - if (request.world.bodies.Length == 0) - return false; - - YmirStepResult result; - try - { - result = YmirPhysicsQueries.Step(request); - } - catch (Exception error) - { - Debug.LogWarning($"Ymir projectile typed step failed: {error.Message}"); - return false; - } - - if (result == null || result.world == null) - return false; - - var projectileBody = FindBody(result.world, ProjectileBodyId); - if (projectileBody == null) - return false; - - step.Position = new Vector3(projectileBody.position.x, projectile.transform.position.y, projectileBody.position.y); - step.Velocity = new Vector3(projectileBody.velocity.x, projectile.Velocity.y, projectileBody.velocity.y); - - foreach (var contact in result.contacts ?? Array.Empty()) - { - var otherBody = string.Equals(contact.bodyA, ProjectileBodyId, StringComparison.Ordinal) - ? contact.bodyB - : string.Equals(contact.bodyB, ProjectileBodyId, StringComparison.Ordinal) - ? contact.bodyA - : ""; - if (string.IsNullOrWhiteSpace(otherBody)) - continue; - - if (!TryResolveTargetDaemonHull(projectile.TargetInstance, otherBody, out var hull)) - continue; - - step.Hit = new AetheriaYmirProjectileHit( - hull, - new Vector3(contact.point.x, projectile.transform.position.y, contact.point.y), - new Vector3(contact.normal.x, 0, contact.normal.y).normalized); - break; - } - - return true; - } - public bool TryOverlapDaemonBodies( Vector3 center, float radius, @@ -386,54 +328,6 @@ public sealed class AetheriaYmirPhysicsBridge : MonoBehaviour return true; } - private YmirStepRequest BuildProjectileRequest(Projectile projectile, float deltaTime) - { - var transform = projectile.transform; - _projectileBodies.Clear(); - _projectileBodies.Add(new YmirPhysicsBody - { - id = ProjectileBodyId, - position = ToVec2(transform.position), - velocity = ToVec2(projectile.Velocity), - direction = ToVec2(transform.forward), - angularVelocity = 0, - torque = 0, - momentOfInertia = 1, - radius = Mathf.Max(ProjectileRadius, 0.001f), - mass = Mathf.Max(ProjectileMass, 0.001f), - isStatic = false, - restitution = 0 - }); - - var targetDaemonEntityIndex = TargetDaemonEntityIndex(projectile.TargetInstance); - if (targetDaemonEntityIndex < 0 || - !TryBuildDaemonWorld(projectile.SourceEntity, targetDaemonEntityIndex, out var targetWorld)) - { - return new YmirStepRequest - { - deltaTime = deltaTime, - world = new YmirWorld - { - time = projectile.YmirWorldTime, - bodies = Array.Empty(), - fields = Array.Empty() - } - }; - } - - _projectileBodies.AddRange(targetWorld.bodies); - return new YmirStepRequest - { - deltaTime = deltaTime, - world = new YmirWorld - { - time = projectile.YmirWorldTime, - bodies = _projectileBodies.ToArray(), - fields = Array.Empty() - } - }; - } - private bool TryBuildDaemonWorld(Entity sourceEntity, int? onlyDaemonEntityIndex, out YmirWorld world) { world = null; @@ -741,28 +635,6 @@ public interface IYmirDaemonBodySource List bodies); } -public struct AetheriaYmirProjectileStep -{ - public Vector3 Position; - public Vector3 Velocity; - public AetheriaYmirProjectileHit Hit; - public bool HasHit => Hit.Hull != null; -} - -public readonly struct AetheriaYmirProjectileHit -{ - public AetheriaYmirProjectileHit(HullCollider hull, Vector3 point, Vector3 normal) - { - Hull = hull; - Point = point; - Normal = normal; - } - - public HullCollider Hull { get; } - public Vector3 Point { get; } - public Vector3 Normal { get; } -} - public readonly struct AetheriaYmirOverlapHit { public AetheriaYmirOverlapHit(HullCollider hull, Vector3 point, Vector3 normal, float penetration, float distance) diff --git a/Assets/Scripts/Gameplay/Physics/YmirUnityRuntimeProofTests.cs b/Assets/Scripts/Gameplay/Physics/YmirUnityRuntimeProofTests.cs deleted file mode 100644 index 1533f3f6b..000000000 --- a/Assets/Scripts/Gameplay/Physics/YmirUnityRuntimeProofTests.cs +++ /dev/null @@ -1,404 +0,0 @@ -#if UNITY_INCLUDE_TESTS -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.Serialization; -using NUnit.Framework; -using UnityEngine; - -public sealed class YmirUnityRuntimeProofTests -{ - private readonly List _objects = new List(); - - [SetUp] - public void SetUp() - { - ResetBridgeSingleton(); - Time.captureDeltaTime = 0.1f; - } - - [TearDown] - public void TearDown() - { - Time.captureDeltaTime = 0; - ResetBridgeSingleton(); - for (var i = _objects.Count - 1; i >= 0; i--) - { - if (_objects[i] != null) - UnityEngine.Object.DestroyImmediate(_objects[i]); - } - - _objects.Clear(); - } - - [Test] - public void DisabledUnityCollidersDoNotPreventYmirProjectileHit() - { - var bridge = CreateBridge(BodySource(TargetBody(7, 1.0f, 0, 0.5f))); - var target = CreateTarget(7, disableCollider: true); - var projectile = CreateProjectile(target, disableCollider: true); - projectile.Velocity = new Vector3(10, 0, 0); - - Assert.That(bridge.TryStepProjectile(projectile, 0.1f, out var step), Is.True); - Assert.That(step.Position.x, Is.EqualTo(0.4f).Within(0.0001f)); - Assert.That(step.HasHit, Is.True); - Assert.That(step.Hit.Hull, Is.SameAs(target.Hull)); - Assert.That(projectile.GetComponent().enabled, Is.False); - Assert.That(target.Hull.GetComponent().enabled, Is.False); - } - - [Test] - public void CollisionCallbackObserversDoNotChangeYmirHitSequence() - { - var body = TargetBody(7, 1.0f, 0, 0.5f); - var target = CreateTarget(7, disableCollider: false); - var projectile = CreateProjectile(target, disableCollider: false); - projectile.Velocity = new Vector3(10, 0, 0); - - var observer = projectile.gameObject.AddComponent(); - var withObserver = StepHitBodyIds(BodySource(body), projectile); - var observerCallbackCount = observer.CallbackCount; - UnityEngine.Object.DestroyImmediate(observer); - var withoutObserver = StepHitBodyIds(BodySource(body), projectile); - - Assert.That(observerCallbackCount, Is.EqualTo(0)); - CollectionAssert.AreEqual(withObserver, withoutObserver); - CollectionAssert.AreEqual(new[] { "aetheria.projectile", "aetheria.daemon.entity.7" }, withObserver); - } - - [Test] - public void ProjectileUpdateCommitsVisibleTransformFromYmirSnapshot() - { - var bridge = CreateBridge(BodySource(TargetBody(7, 5.0f, 0, 0.25f))); - var target = CreateTarget(7, disableCollider: true); - var projectile = CreateProjectile(target, disableCollider: true); - projectile.Velocity = new Vector3(10, 0, 0); - - var deltaTime = Time.deltaTime; - Assert.That(bridge.TryStepProjectile(projectile, deltaTime, out var expected), Is.True); - - projectile.transform.position = Vector3.zero; - projectile.Velocity = new Vector3(10, 0, 0); - InvokeProjectileUpdate(projectile); - - Assert.That(projectile.transform.position.x, Is.EqualTo(expected.Position.x).Within(0.02f)); - Assert.That(projectile.transform.position.z, Is.EqualTo(expected.Position.z).Within(0.02f)); - Assert.That(projectile.Velocity.x, Is.EqualTo(expected.Velocity.x).Within(0.0001f)); - } - - [Test] - public void ProjectileUpdateFailsClosedWhenYmirBodiesAreUnavailable() - { - CreateBridge(BodySource()); - var target = CreateTarget(7, disableCollider: true); - var projectile = CreateProjectile(target, disableCollider: true); - projectile.Velocity = new Vector3(10, 0, 0); - projectile.Trail = projectile.gameObject.AddComponent(); - projectile.gameObject.AddComponent(); - - InvokeProjectileUpdate(projectile); - - Assert.That(ReadAlive(projectile), Is.False); - } - - [Test] - public void ReplayingCapturedYmirRequestKeepsContactOrderStable() - { - var request = new YmirStepRequest - { - deltaTime = 0.1f, - world = new YmirWorld - { - time = 0, - fields = Array.Empty(), - bodies = new[] - { - ProjectileBody(0, 0, 10, 0), - TargetBody(9, 1.0f, 0, 0.5f), - TargetBody(7, 1.0f, 0, 0.5f) - } - } - }; - - var first = ContactPairs(YmirPhysicsQueries.Step(request)); - var second = ContactPairs(YmirPhysicsQueries.Step(request)); - - CollectionAssert.AreEqual(first, second); - CollectionAssert.AreEqual( - new[] - { - "aetheria.projectile->aetheria.daemon.entity.9", - "aetheria.projectile->aetheria.daemon.entity.7" - }, - first); - } - - private static string[] StepHitBodyIds(StaticDaemonBodySource source, Projectile projectile) - { - var bridge = CreateBridge(source); - Assert.That(bridge.TryStepProjectile(projectile, 0.1f, out var step), Is.True); - Assert.That(step.HasHit, Is.True); - - var result = YmirPhysicsQueries.Step(new YmirStepRequest - { - deltaTime = 0.1f, - world = new YmirWorld - { - time = 0, - fields = Array.Empty(), - bodies = new[] { ProjectileBody(0, 0, 10, 0), source.Bodies[0] } - } - }); - - return result.contacts.Length == 0 - ? Array.Empty() - : new[] { result.contacts[0].bodyA, result.contacts[0].bodyB }; - } - - private static string[] ContactPairs(YmirStepResult result) - { - var contacts = result.contacts ?? Array.Empty(); - var pairs = new string[contacts.Length]; - for (var i = 0; i < contacts.Length; i++) - pairs[i] = contacts[i].bodyA + "->" + contacts[i].bodyB; - return pairs; - } - - private static AetheriaYmirPhysicsBridge CreateBridge(IYmirDaemonBodySource source) - { - ResetBridgeSingleton(); - var gameObject = new GameObject("test-yimir-bridge"); - var bridge = gameObject.AddComponent(); - bridge.EnableProjectileCutover = true; - bridge.ProjectileRadius = 0.1f; - bridge.ProjectileMass = 1.0f; - bridge.DaemonBodySource = source; - _activeBridgeObject = gameObject; - return bridge; - } - - private static GameObject _activeBridgeObject; - - private TargetFixture CreateTarget(int daemonEntityIndex, bool disableCollider) - { - var targetObject = Track(new GameObject("target")); - var hullObject = Track(new GameObject("target-hull")); - hullObject.transform.SetParent(targetObject.transform); - var collider = hullObject.AddComponent(); - collider.radius = 0.5f; - collider.enabled = !disableCollider; - - var targetEntity = TestEntity.Create(daemonEntityIndex); - var hull = hullObject.AddComponent(); - hull.Entity = targetEntity; - - var instance = targetObject.AddComponent(); - instance.HullColliders = new[] { hull }; - SetEntityInstanceEntity(instance, targetEntity); - return new TargetFixture(instance, hull); - } - - private Projectile CreateProjectile(TargetFixture target, bool disableCollider) - { - var projectileObject = Track(new GameObject("projectile")); - var collider = projectileObject.AddComponent(); - collider.radius = 0.1f; - collider.enabled = !disableCollider; - - var projectile = projectileObject.AddComponent(); - projectile.SourceEntity = TestEntity.Create(1); - projectile.TargetInstance = target.Instance; - projectile.StartPosition = Vector3.zero; - projectile.Range = 100; - projectile.Gravity = 0; - projectile.Drag = 0; - SetAlive(projectile, true); - return projectile; - } - - private GameObject Track(GameObject gameObject) - { - _objects.Add(gameObject); - return gameObject; - } - - private static StaticDaemonBodySource BodySource(params YmirPhysicsBody[] bodies) - { - return new StaticDaemonBodySource(bodies); - } - - private static YmirPhysicsBody ProjectileBody(float x, float z, float velocityX, float velocityZ) - { - return new YmirPhysicsBody - { - id = "aetheria.projectile", - position = Vec2(x, z), - velocity = Vec2(velocityX, velocityZ), - direction = Vec2(1, 0), - radius = 0.1f, - mass = 1, - momentOfInertia = 1, - restitution = 0 - }; - } - - private static YmirPhysicsBody TargetBody(int daemonEntityIndex, float x, float z, float radius) - { - return new YmirPhysicsBody - { - id = "aetheria.daemon.entity." + daemonEntityIndex, - position = Vec2(x, z), - velocity = Vec2(0, 0), - direction = Vec2(0, 1), - radius = radius, - mass = 1, - momentOfInertia = 1, - isStatic = true, - restitution = 0 - }; - } - - private static YmirVec2 Vec2(float x, float y) - { - return new YmirVec2 { x = x, y = y }; - } - - private static void InvokeProjectileUpdate(Projectile projectile) - { - typeof(Projectile) - .GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic) - .Invoke(projectile, Array.Empty()); - } - - private static bool ReadAlive(Projectile projectile) - { - return (bool)typeof(Projectile) - .GetField("_alive", BindingFlags.Instance | BindingFlags.NonPublic) - .GetValue(projectile); - } - - private static void SetAlive(Projectile projectile, bool alive) - { - typeof(Projectile) - .GetField("_alive", BindingFlags.Instance | BindingFlags.NonPublic) - .SetValue(projectile, alive); - } - - private static void SetEntityInstanceEntity(EntityInstance instance, Entity entity) - { - typeof(EntityInstance) - .GetProperty("Entity", BindingFlags.Instance | BindingFlags.Public) - .GetSetMethod(true) - .Invoke(instance, new object[] { entity }); - } - - private static void ResetBridgeSingleton() - { - if (_activeBridgeObject != null) - { - UnityEngine.Object.DestroyImmediate(_activeBridgeObject); - _activeBridgeObject = null; - } - - typeof(AetheriaYmirPhysicsBridge) - .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic) - .SetValue(null, null); - } - - private sealed class StaticDaemonBodySource : IYmirDaemonBodySource - { - public StaticDaemonBodySource(params YmirPhysicsBody[] bodies) - { - Bodies = bodies ?? Array.Empty(); - } - - public YmirPhysicsBody[] Bodies { get; } - - public bool TryAppendDaemonBodies( - Entity sourceEntity, - int? onlyDaemonEntityIndex, - List bodies) - { - for (var i = 0; i < Bodies.Length; i++) - { - var body = Bodies[i]; - if (!TryParseEntityIndex(body.id, out var daemonEntityIndex)) - continue; - if (sourceEntity != null && sourceEntity.DaemonEntityIndex == daemonEntityIndex) - continue; - if (onlyDaemonEntityIndex.HasValue && onlyDaemonEntityIndex.Value != daemonEntityIndex) - continue; - - bodies.Add(Clone(body)); - } - - return bodies.Count > 0; - } - - private static bool TryParseEntityIndex(string bodyId, out int daemonEntityIndex) - { - const string prefix = "aetheria.daemon.entity."; - daemonEntityIndex = -1; - return !string.IsNullOrWhiteSpace(bodyId) && - bodyId.StartsWith(prefix, StringComparison.Ordinal) && - int.TryParse(bodyId.Substring(prefix.Length), out daemonEntityIndex); - } - - private static YmirPhysicsBody Clone(YmirPhysicsBody body) - { - return new YmirPhysicsBody - { - id = body.id, - position = body.position, - velocity = body.velocity, - direction = body.direction, - angularVelocity = body.angularVelocity, - torque = body.torque, - momentOfInertia = body.momentOfInertia, - radius = body.radius, - mass = body.mass, - isStatic = body.isStatic, - restitution = body.restitution - }; - } - } - - private sealed class TestEntity : Entity - { - private TestEntity() - : base(null, null, null, null) - { - } - - public static TestEntity Create(int daemonEntityIndex) - { - var entity = (TestEntity)FormatterServices.GetUninitializedObject(typeof(TestEntity)); - entity.DaemonEntityIndex = daemonEntityIndex; - entity.Name = "test-entity-" + daemonEntityIndex; - return entity; - } - } - - private sealed class CollisionObserverOnly : MonoBehaviour - { - public int CallbackCount { get; private set; } - - private void OnCollisionEnter(Collision collision) - { - CallbackCount++; - } - } - - private readonly struct TargetFixture - { - public TargetFixture(EntityInstance instance, HullCollider hull) - { - Instance = instance; - Hull = hull; - } - - public EntityInstance Instance { get; } - public HullCollider Hull { get; } - } -} -#endif diff --git a/Assets/Scripts/Gameplay/Physics/YmirUnityRuntimeProofTests.cs.meta b/Assets/Scripts/Gameplay/Physics/YmirUnityRuntimeProofTests.cs.meta deleted file mode 100644 index 81baeebca..000000000 --- a/Assets/Scripts/Gameplay/Physics/YmirUnityRuntimeProofTests.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 94d0ced5a34e7024c89289d554c55bfb \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/Weapons/GuidedProjectile.cs b/Assets/Scripts/Gameplay/Weapons/GuidedProjectile.cs deleted file mode 100644 index 695c7aa7d..000000000 --- a/Assets/Scripts/Gameplay/Weapons/GuidedProjectile.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System; -using System.Collections; -using UnityEngine; -using Random = UnityEngine.Random; -using static Noise1D; - -public class GuidedProjectile : MonoBehaviour -{ - public Prototype HitEffect; - public Prototype ChildProjectile; - public int Children; - public float SplitTime; - public float SplitSeparationForwardness; - public float SplitSeparationVelocity; - public ParticleSystem Particles; - public float FadeOutTime; - public AnimationCurve ThrustCurve; - public AnimationCurve GuidanceCurve; - public AnimationCurve LiftCurve; - public float Frequency; - public float Thrust; - public float TopSpeed; - public Transform Source; - public Func TargetPosition; - - private float _phase; - private float _prevDist; - private bool _active; - private bool _alive; - private Vector3 _targetVelocity; - private Vector3 _previousTargetPosition; - - public Transform Target { get; set; } - public Vector3 StartPosition { get; set; } - public float Range { get; set; } - public Vector3 Velocity { get; set; } - public float ImpactIntensity { get; set; } = 1; - public Entity SourceEntity { get; set; } - public ZoneRenderer ZoneRenderer { get; set; } - - public event Action OnKill; - - void OnEnable() - { - _active = _alive = true; - _phase = Random.value * 100; - _prevDist = Single.MaxValue; - SetParticleColor(Color.white); - Particles.Clear(true); - Particles.Play(true); - } - - void Update () - { - if (SourceEntity == null) return; - - var t = transform; - - if (_active) - { - if (TargetPosition == null && !Target) - { - StartCoroutine(FadeOut()); - return; - } - var position = t.position; - - var targetPosition = TargetPosition?.Invoke() ?? Target.position; - _targetVelocity = Vector3.Lerp(_targetVelocity, targetPosition - _previousTargetPosition, Mathf.Clamp01(Time.deltaTime * 5)); - _previousTargetPosition = targetPosition; - targetPosition = AetheriaMath.FirstOrderIntercept(position, Vector3.zero, TopSpeed, targetPosition, _targetVelocity); - - var diff = targetPosition - transform.position; - var targetDist = diff.magnitude; - var sourceDist = Vector2.Distance(new Vector2(StartPosition.x, StartPosition.z), new Vector2(position.x, position.z)); - - if (sourceDist > Range || Vector3.Dot(diff, Velocity) < 0) - { - StartCoroutine(FadeOut()); - if (HitEffect != null) - { - var ht = HitEffect.Instantiate(); - ht.position = t.position; - } - return; - } - _prevDist = targetDist; - - var targetDistFlat = diff.Flatland().magnitude; - var curveLerp = 1 - targetDistFlat / (sourceDist + targetDistFlat); - var dir = diff.normalized; - var right = Vector3.Cross(dir, Vector3.up); - var up = Vector3.Cross(dir, right); - - if (Children > 0 && SplitTime < curveLerp) - { - for (int i = 0; i < Children; i++) - { - var child = ChildProjectile.Instantiate(); - child.transform.position = t.position; - child.StartPosition = StartPosition; - var randomDirection = Random.insideUnitCircle.normalized; - var perpendicularRandom = randomDirection.x * right + randomDirection.y * up; - child.Velocity = Vector3.Lerp(perpendicularRandom, dir, SplitSeparationForwardness).normalized * Velocity.magnitude * SplitSeparationVelocity; - child.Range = Range; - child.ImpactIntensity = ImpactIntensity; - child.Source = Source; - child.Target = Target; - child.SourceEntity = SourceEntity; - child.ZoneRenderer = ZoneRenderer; - child.GuidanceCurve = GuidanceCurve; - child.LiftCurve = LiftCurve; - child.ThrustCurve = ThrustCurve; - child.Thrust = Thrust; - child.TopSpeed = TopSpeed; - child.Frequency = Frequency; - child.Thrust = Thrust; - } - StartCoroutine(Kill()); - if (HitEffect != null) - { - var ht = HitEffect.Instantiate(); - ht.position = t.position; - } - } - - var dodge = Vector3.Lerp( - (right * noise(Time.time * Frequency + _phase) + up * noise(Time.time * Frequency + (100 + _phase))).normalized, - Vector3.up, - LiftCurve.Evaluate(curveLerp)).normalized; - var desired = Vector3.Slerp(dodge, dir, GuidanceCurve.Evaluate(curveLerp)).normalized * TopSpeed; - var thrustCurve = ThrustCurve.Evaluate(curveLerp); - var thrust = Thrust * thrustCurve; - var c = Color.white * thrustCurve; - c.a = 1; - SetParticleColor(c); - Velocity += (desired-Velocity).normalized * (thrust * Time.deltaTime); - } - - if(_alive) - { - if (AetheriaYmirPhysicsBridge.Instance.TryCastZoneHulls( - ZoneRenderer, - SourceEntity, - t.position, - Velocity, - Velocity.magnitude * Time.deltaTime, - 0, - out var hits) && - hits.Count > 0) - { - var hit = hits[0]; - var hull = hit.Hull; - var entity = hull.Entity; - if (entity.Shield != null && entity.Shield.Item.Active.Value) - { - hit.Shield?.ShowHit(hit.Point, Mathf.Max(0.01f, ImpactIntensity)); - } - else - { - } - - transform.position = hit.Point; - StartCoroutine(Kill()); - - if (HitEffect != null) - { - var ht = HitEffect.Instantiate(); - ht.SetParent(hull.transform); - ht.position = hit.Point; - } - - return; - } - - t.position += Velocity * Time.deltaTime; - } - } - - IEnumerator FadeOut() - { - _active = false; - var startTime = Time.time; - while (Time.time - startTime < FadeOutTime) - { - var lerp = 1 - (Time.time - startTime) / FadeOutTime; - var c = Color.white * lerp; - c.a = 1; - SetParticleColor(c); - yield return null; - } - - StartCoroutine(Kill()); - } - - private void SetParticleColor(Color color) - { - var main = Particles.main; - main.startColor = color; - } - - IEnumerator Kill() - { - _active = false; - _alive = false; - Particles.Stop(true, ParticleSystemStopBehavior.StopEmitting); - var startTime = Time.time; - var lifetime = Particles.main.startLifetime.constant; - while (Time.time - startTime < lifetime) - { - yield return null; - } - OnKill?.Invoke(); - GetComponent().ReturnToPool(); - } -} diff --git a/Assets/Scripts/Gameplay/Weapons/GuidedProjectile.cs.meta b/Assets/Scripts/Gameplay/Weapons/GuidedProjectile.cs.meta deleted file mode 100644 index 028d27e68..000000000 --- a/Assets/Scripts/Gameplay/Weapons/GuidedProjectile.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 56c973b8f3781c848b6530c2e7efce05 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 70 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Gameplay/Weapons/GuidedProjectileManager.cs b/Assets/Scripts/Gameplay/Weapons/GuidedProjectileManager.cs deleted file mode 100644 index 9350c2226..000000000 --- a/Assets/Scripts/Gameplay/Weapons/GuidedProjectileManager.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using UniRx; -using UnityEngine; -using cfloat3 = CultMath.float3; - -public class GuidedProjectileManager : InstantWeaponEffectManager -{ - public Prototype ProjectilePrototype; - - public Subject<(Entity source, Transform target, GuidedProjectile missile)> OnFireGuided = new Subject<(Entity source, Transform target, GuidedProjectile missile)>(); - - public override void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target) - { - if(!weapon.HasGuidedProjectileProfile) - { - Debug.LogError($"Weapon {item.RuntimeItem?.Name ?? "Unknown item"} linked to {name} effect, but has no guided projectile profile!"); - return; - } - - if(weapon.GuidedProjectileTargeting == GuidedProjectileTargetMode.TargetEntity) - { - if (target == null) return; - var p = ProjectilePrototype.Instantiate(); - p.Source = source.transform; - p.SourceEntity = source.Entity; - p.ZoneRenderer = source.ZoneRenderer; - p.Target = target.transform; - p.Frequency = weapon.GuidedProjectileDodgeFrequency; - var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y]; - var barrel = source.GetBarrel(hp); - p.StartPosition = p.transform.position = barrel.position; - p.Range = weapon.Range; - p.ImpactIntensity = 1; - p.GuidanceCurve = weapon.GuidedProjectileGuidanceCurve.ToCurve(); - p.LiftCurve = weapon.GuidedProjectileLiftCurve.ToCurve(); - p.ThrustCurve = weapon.GuidedProjectileThrustCurve.ToCurve(); - p.Velocity = barrel.forward * weapon.Velocity; - p.Thrust = weapon.EvaluateGuidedProjectileThrust(); - p.TopSpeed = weapon.EvaluateGuidedProjectileVelocity(); - OnFireGuided.OnNext((source.Entity, target.transform, p)); - } - else if(weapon.GuidedProjectileTargeting == GuidedProjectileTargetMode.LookDirection) - { - var p = ProjectilePrototype.Instantiate(); - p.Source = source.transform; - p.SourceEntity = source.Entity; - p.ZoneRenderer = source.ZoneRenderer; - p.Frequency = weapon.GuidedProjectileDodgeFrequency; - var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y]; - var barrel = source.GetBarrel(hp); - p.StartPosition = p.transform.position = barrel.position; - p.Range = weapon.Range; - p.ImpactIntensity = 1; - p.GuidanceCurve = weapon.GuidedProjectileGuidanceCurve.ToCurve(); - p.LiftCurve = weapon.GuidedProjectileLiftCurve.ToCurve(); - p.ThrustCurve = weapon.GuidedProjectileThrustCurve.ToCurve(); - p.Velocity = barrel.forward * weapon.Velocity; - p.Thrust = weapon.EvaluateGuidedProjectileThrust(); - p.TopSpeed = weapon.EvaluateGuidedProjectileVelocity(); - p.TargetPosition = () => - { - var lookPosition = source.LookAtPoint.position; - var lookPoint = new cfloat3(lookPosition.x, lookPosition.y, lookPosition.z); - var lookDistance = CultMath.math.length(lookPoint - source.Entity.CultPosition); - return (Vector3)AetheriaMath.ToUnity(source.Entity.CultPosition + lookDistance * source.Entity.CultLookDirection); - }; - } - } -} diff --git a/Assets/Scripts/Gameplay/Weapons/GuidedProjectileManager.cs.meta b/Assets/Scripts/Gameplay/Weapons/GuidedProjectileManager.cs.meta deleted file mode 100644 index b5f177a82..000000000 --- a/Assets/Scripts/Gameplay/Weapons/GuidedProjectileManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ee3949bbc28566148b9f661741b6e25d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Gameplay/Weapons/Projectile.cs b/Assets/Scripts/Gameplay/Weapons/Projectile.cs deleted file mode 100644 index 35a2894d1..000000000 --- a/Assets/Scripts/Gameplay/Weapons/Projectile.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using Random = UnityEngine.Random; - -public class Projectile : MonoBehaviour -{ - public TrailRenderer Trail; - public float Gravity; - public float Drag = .1f; - public Prototype HitEffect; - - public float AirburstDistance; - public float AirburstRange; - public float DirectHitIntensityMultiplier = 1; - - private bool _alive; - - public Vector3 StartPosition { get; set; } - public Vector3 Velocity { get; set; } - public float ImpactIntensity { get; set; } = 1; - public Entity SourceEntity { get; set; } - public EntityInstance TargetInstance { get; set; } - public float Range { get; set; } - public float YmirWorldTime { get; private set; } - - // Start is called before the first frame update - void Start() - { - - } - - private void OnEnable() - { - _alive = true; - } - - // Update is called once per frame - void Update() - { - if (SourceEntity == null) return; - - if(_alive) - { - var t = transform; - var position = t.position; - Velocity -= Vector3.up * (Gravity * Time.deltaTime); - Velocity *= Mathf.Max(0, 1 - Drag * Time.deltaTime); - var forward = Velocity.normalized; - t.forward = forward; - if (!AetheriaYmirPhysicsBridge.Instance.TryStepProjectile(this, Time.deltaTime, out var ymirStep)) - { - Debug.LogWarning("Ymir projectile step unavailable; projectile killed instead of falling back to Unity physics."); - StartCoroutine(Kill()); - return; - } - - YmirWorldTime += Time.deltaTime; - Velocity = ymirStep.Velocity; - t.position = ymirStep.Position; - if (ymirStep.HasHit) - { - ApplyYmirHit(ymirStep, forward); - return; - } - - var distanceTraveled = (transform.position - StartPosition).magnitude; - if(distanceTraveled > Range) - StartCoroutine(Kill()); - if (AirburstRange > 1 && distanceTraveled > AirburstDistance) - { - StartCoroutine(Kill()); - if (HitEffect != null) - { - var ht = HitEffect.Instantiate(); - ht.position = t.position; - } - - if (AetheriaYmirPhysicsBridge.Instance.TryOverlapTargetHulls(TargetInstance, t.position, AirburstRange, out var hits)) - { - foreach (var hit in hits) - { - var direction = (hit.Hull.transform.position - t.position).normalized; - } - } - } - } - } - - private void ApplyYmirHit(AetheriaYmirProjectileStep step, Vector3 forward) - { - var hull = step.Hit.Hull; - if (hull == null || hull.Entity == SourceEntity) - return; - - var shieldManager = hull.GetComponentInParent()?.Shield; - if (shieldManager != null && - hull.Entity.Shield != null && - hull.Entity.Shield.Item.Active.Value) - shieldManager.ShowHit(step.Hit.Point, Mathf.Max(0.01f, ImpactIntensity * DirectHitIntensityMultiplier)); - - transform.position = step.Hit.Point; - if (HitEffect != null) - { - var ht = HitEffect.Instantiate(); - ht.SetParent(hull.transform); - ht.position = step.Hit.Point; - } - - StartCoroutine(Kill()); - } - - IEnumerator Kill() - { - _alive = false; - var startTime = Time.time; - var lifetime = Trail.time; - while (Time.time - startTime < lifetime) - yield return null; - GetComponent().ReturnToPool(); - } -} diff --git a/Assets/Scripts/Gameplay/Weapons/Projectile.cs.meta b/Assets/Scripts/Gameplay/Weapons/Projectile.cs.meta deleted file mode 100644 index 331cded7a..000000000 --- a/Assets/Scripts/Gameplay/Weapons/Projectile.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4528540a139424748a7b6c47bfecc7fd -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 60 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Gameplay/Weapons/ProjectileManager.cs b/Assets/Scripts/Gameplay/Weapons/ProjectileManager.cs deleted file mode 100644 index 088613e99..000000000 --- a/Assets/Scripts/Gameplay/Weapons/ProjectileManager.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using Random = UnityEngine.Random; - -public class ProjectileManager : InstantWeaponEffectManager -{ - public Prototype ProjectilePrototype; - public bool InheritVelocity; - - public override void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target) - { - var p = ProjectilePrototype.Instantiate(); - var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y]; - var barrel = source.GetBarrel(hp); - var angle = weapon.Spread / 2; - p.SourceEntity = source.Entity; - p.TargetInstance = target; - p.Velocity = Quaternion.Euler( - Random.Range(-angle, angle), - Random.Range(-angle, angle), - Random.Range(-angle, angle)) * - barrel.forward * - weapon.Velocity; - p.StartPosition = p.transform.position = barrel.position + p.Velocity * (Random.value * Time.deltaTime); - if(InheritVelocity) - p.Velocity += (Vector3)AetheriaMath.ToUnityXZ(source.Entity.CultVelocity); - p.Range = weapon.Range; - p.ImpactIntensity = 1; - p.AirburstDistance = target != null - ? CultMath.math.length(source.Entity.CultPosition - target.Entity.CultPosition) - : (weapon.Range * .75f); - p.Trail.Clear(); - } -} diff --git a/Assets/Scripts/Gameplay/Weapons/ProjectileManager.cs.meta b/Assets/Scripts/Gameplay/Weapons/ProjectileManager.cs.meta deleted file mode 100644 index 7e0c30fd6..000000000 --- a/Assets/Scripts/Gameplay/Weapons/ProjectileManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 49c173e3c2313ca4d9d04d0d87ec0f06 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/docs/aetheria-current-codebase-model.md b/docs/aetheria-current-codebase-model.md index cc5435d41..35c953cc4 100644 --- a/docs/aetheria-current-codebase-model.md +++ b/docs/aetheria-current-codebase-model.md @@ -153,6 +153,9 @@ Current Ymir control flow: - `AetheriaYmirPhysicalPayloadPhysics` advances only objects whose continued physical motion is gameplay state. Aetheria interprets returned contacts; Ymir does not own damage policy. +- The fossil `Projectile`, `GuidedProjectile`, their managers, Unity-side Ymir + stepping adapter, and dedicated proof tests are deleted. EveUnity lowers + ordinary weapon travel from daemon `shot.receipt` facts. - Target/zone hit queries call `TryOverlapTargetHulls`, `TryCastTargetHulls`, `TryOverlapZoneHulls`, or `TryCastZoneHulls`. - `TryBuildDaemonWorld` builds Ymir bodies from `AetheriaDaemonRenderNativeView`