diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs index ba579f193..064af6c98 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/PhysicsStaticCollision.cs @@ -84,7 +84,7 @@ private static void Collide(ref NativeColliders colliders, ref BallState ball, r case ColliderType.Bumper: ref var bumperState = ref state.GetBumperState(colliderId, ref colliders); BumperCollider.Collide(ref ball, ref state.EventQueue, ref ball.CollisionEvent, ref bumperState.RingAnimation, ref bumperState.SkirtAnimation, - in collHeader, in bumperState.Static, ref state.Env.Random, ref state.InsideOfs); + in collHeader, in bumperState.Static, ref state.Env.Random, ref state.InsideOfs, bumperState.IsSwitchWiredToCoil); break; case ColliderType.Flipper: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs index b68b6d66b..34a0b7c04 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/SwitchHandler.cs @@ -113,6 +113,17 @@ public void RemoveWireDest(string destId) } } + public bool HasWireDest(IWireableComponent device, string deviceItem) + { + if (_wires == null) + return false; + foreach (var wire in _wires) { + if (wire.Device == device && wire.DeviceItem == deviceItem) + return true; + } + return false; + } + /// /// Sends the switch element to the gamelogic engine and linked wires. /// diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs index b8eeffd50..d196de961 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperApi.cs @@ -19,10 +19,7 @@ using VisualPinball.Engine.VPT.Bumper; using System.Collections.Generic; using Unity.Mathematics; -using static UnityEngine.UI.Scrollbar; -using VisualPinball.Engine.PinMAME.MPUs; -using VisualPinball.Engine.VPT; -using JetBrains.Annotations; +using System.Linq; namespace VisualPinball.Unity { @@ -70,43 +67,63 @@ public BumperApi(GameObject go, Player player, PhysicsEngine physicsEngine) : ba void IApiSwitch.RemoveWireDest(string destId) => RemoveWireDest(destId); void IApiCoil.OnCoil(bool enabled) { - if (!enabled) { - return; - } - ref var bumperState = ref PhysicsEngine.BumperState(ItemId); - bumperState.RingAnimation.IsHit = true; - ref var insideOfs = ref PhysicsEngine.InsideOfs; - List idsOfBallsInColl = insideOfs.GetIdsOfBallsInsideItem(ItemId); - foreach (var ballId in idsOfBallsInColl) { - if (PhysicsEngine.Balls.ContainsKey(ballId)) { - ref var ballState = ref PhysicsEngine.BallState(ballId); - float3 bumperPos = new(MainComponent.Position.x, MainComponent.Position.y, MainComponent.PositionZ); - float3 ballPos = ballState.Position; - var bumpDirection = ballPos - bumperPos; - bumpDirection.z = 0f; - bumpDirection = math.normalize(bumpDirection); - var collEvent = new CollisionEventData { - HitTime = 0f, - HitNormal = bumpDirection, - HitVelocity = new float2(bumpDirection.x, bumpDirection.y) * ColliderComponent.Force, - HitDistance = 0f, - HitFlag = false, - HitOrgNormalVelocity = math.dot(bumpDirection, math.normalize(ballState.Velocity)), - IsContact = true, - ColliderId = switchColliderId, - IsKinematic = false, - BallId = ballId - }; - var physicsMaterialData = ColliderComponent.PhysicsMaterialData; - var random = PhysicsEngine.Random; - BallCollider.Collide3DWall(ref ballState, in physicsMaterialData, in collEvent, in bumpDirection, ref random); - ballState.Velocity += bumpDirection * ColliderComponent.Force; - } + if (enabled) { + PhysicsEngine.ScheduleAction(0, () => { + ref var bumperState = ref PhysicsEngine.BumperState(ItemId); + bumperState.RingAnimation.IsHit = true; + ref var insideOfs = ref PhysicsEngine.InsideOfs; + List idsOfBallsInColl = insideOfs.GetIdsOfBallsInsideItem(ItemId); + foreach (var ballId in idsOfBallsInColl) { + if (PhysicsEngine.Balls.ContainsKey(ballId)) { + ref var ballState = ref PhysicsEngine.BallState(ballId); + float3 bumperPos = new(MainComponent.Position.x, MainComponent.Position.y, MainComponent.PositionZ); + float3 ballPos = ballState.Position; + var bumpDirection = ballPos - bumperPos; + bumpDirection.z = 0f; + bumpDirection = math.normalize(bumpDirection); + var collEvent = new CollisionEventData { + HitTime = 0f, + HitNormal = bumpDirection, + HitVelocity = new float2(bumpDirection.x, bumpDirection.y) * ColliderComponent.Force, + HitDistance = 0f, + HitFlag = false, + HitOrgNormalVelocity = math.dot(bumpDirection, math.normalize(ballState.Velocity)), + IsContact = true, + ColliderId = switchColliderId, + IsKinematic = false, + BallId = ballId + }; + var physicsMaterialData = ColliderComponent.PhysicsMaterialData; + var random = PhysicsEngine.Random; + BumperCollider.PushBallAway(ref ballState, in bumperState.Static, ref collEvent, in physicsMaterialData, ref random); + } + } + }); } } void IApiWireDest.OnChange(bool enabled) => (this as IApiCoil).OnCoil(enabled); + internal override void AddWireDest(WireDestConfig wireConfig) + { + base.AddWireDest(wireConfig); + UpdateBumperWireState(); + } + + internal override void RemoveWireDest(string destId) + { + base.RemoveWireDest(destId); + UpdateBumperWireState(); + } + + private void UpdateBumperWireState() + { + string coilId = MainComponent.AvailableCoils.FirstOrDefault().Id; + BumperComponent bumperComponent = MainComponent; + ref var bumperState = ref PhysicsEngine.BumperState(ItemId); + bumperState.IsSwitchWiredToCoil = HasWireDest(bumperComponent, coilId); + } + #endregion #region Collider Generation @@ -156,14 +173,12 @@ void IApiHittable.OnHit(int ballId, bool isUnHit) } } else { Hit?.Invoke(this, new HitEventArgs(ballId)); - if (insideOfs.GetInsideCount(ItemId) == 1) { // Must've been empty before - ref var bumperState = ref PhysicsEngine.BumperState(ItemId); - bumperState.SkirtAnimation.HitEvent = true; - ref var ballState = ref PhysicsEngine.BallState(ballId); - bumperState.SkirtAnimation.BallPosition = ballState.Position; - Switch?.Invoke(this, new SwitchEventArgs(true, ballId)); - OnSwitch(true); - } + ref var bumperState = ref PhysicsEngine.BumperState(ItemId); + bumperState.SkirtAnimation.HitEvent = true; + ref var ballState = ref PhysicsEngine.BallState(ballId); + bumperState.SkirtAnimation.BallPosition = ballState.Position; + Switch?.Invoke(this, new SwitchEventArgs(true, ballId)); + OnSwitch(true); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs index 4db7d96ca..72f41486c 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperCollider.cs @@ -25,13 +25,15 @@ internal static class BumperCollider { public static void Collide(ref BallState ball, ref NativeQueue.ParallelWriter events, ref CollisionEventData collEvent, ref BumperRingAnimationState ringState, ref BumperSkirtAnimationState skirtState, - in ColliderHeader collHeader, in BumperStaticState state, ref Random random, ref InsideOfs insideOfs) + in ColliderHeader collHeader, in BumperStaticState state, ref Random random, ref InsideOfs insideOfs, bool isSwitchWiredToCoil) { var wasBallInside = insideOfs.IsInsideOf(collHeader.ItemId, ball.Id); var isBallInside = !collEvent.HitFlag; if (isBallInside != wasBallInside) { ball.Position += ball.Velocity * PhysicsConstants.StaticTime; if (isBallInside) { + if (isSwitchWiredToCoil) + PushBallAway(ref ball, in state, ref collEvent, in collHeader.Material, ref random); insideOfs.SetInsideOf(collHeader.ItemId, ball.Id); events.Enqueue(new EventData(EventId.HitEventsHit, collHeader.ItemId, ball.Id, true)); } else { @@ -40,5 +42,11 @@ public static void Collide(ref BallState ball, ref NativeQueue.Parall } } } + + public static void PushBallAway(ref BallState ballState, in BumperStaticState state, ref CollisionEventData collEvent, in PhysicsMaterialData physicsMaterialData, ref Random random) + { + BallCollider.Collide3DWall(ref ballState, in physicsMaterialData, in collEvent, in collEvent.HitNormal, ref random); + ballState.Velocity += collEvent.HitNormal * state.Force; + } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs index b5c66b8db..afbfdab87 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperComponent.cs @@ -360,12 +360,15 @@ internal BumperState CreateState() Speed = ringAnimComponent.RingSpeed, } : default; + bool isSwitchWiredToCoil = BumperApi.HasWireDest(this, AvailableCoils.FirstOrDefault().Id); + return new BumperState( skirtAnimComponent ? skirtAnimComponent.gameObject.GetInstanceID() : 0, ringAnimComponent ? ringAnimComponent.gameObject.GetInstanceID() : 0, staticData, ringAnimation, - skirtAnimation + skirtAnimation, + isSwitchWiredToCoil ); } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs index 0013b549c..32fa6205d 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Bumper/BumperState.cs @@ -23,15 +23,17 @@ internal struct BumperState internal BumperStaticState Static; internal BumperRingAnimationState RingAnimation; internal BumperSkirtAnimationState SkirtAnimation; + internal bool IsSwitchWiredToCoil; public BumperState(int skirtItemId, int ringItemId, BumperStaticState @static, - BumperRingAnimationState ringAnimation, BumperSkirtAnimationState skirtAnimation) + BumperRingAnimationState ringAnimation, BumperSkirtAnimationState skirtAnimation, bool isSwitchWiredToCoil) { SkirtItemId = skirtItemId; RingItemId = ringItemId; Static = @static; RingAnimation = ringAnimation; SkirtAnimation = skirtAnimation; + IsSwitchWiredToCoil = isSwitchWiredToCoil; } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemApi.cs index 83569970f..d1427d8e0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ItemApi.cs @@ -67,8 +67,9 @@ protected void OnInit(BallManager ballManager) private protected IApiSwitchStatus AddSwitchDest(SwitchConfig switchConfig,IApiSwitchStatus switchStatus) => SwitchHandler.AddSwitchDest(switchConfig, switchStatus); - internal void AddWireDest(WireDestConfig wireConfig) => SwitchHandler.AddWireDest(wireConfig); - internal void RemoveWireDest(string destId) => SwitchHandler.RemoveWireDest(destId); + internal virtual void AddWireDest(WireDestConfig wireConfig) => SwitchHandler.AddWireDest(wireConfig); + internal virtual void RemoveWireDest(string destId) => SwitchHandler.RemoveWireDest(destId); + internal bool HasWireDest(IWireableComponent device, string deviceItem) => SwitchHandler.HasWireDest(device, deviceItem); private protected void OnSwitch(bool closed) => SwitchHandler.OnSwitch(closed);