private void Transition_3_TargetCenter() { _state = TutorialState.TargetCenter; _cannon5.RotateTo(_cannon4); if (FloatMath.DiffRadiansAbs(_cannon2.Rotation.ActualValue, FloatMath.RAD_POS_090) < FloatMath.RAD_POS_060) { _cannon2.Rotation.Set(FloatMath.RAD_POS_180); } SetInfoBox(L10N.T(L10NImpl.STR_TUT_INFO3)); AddTouchAnimation(_cannon2, _cannon3); }
private bool OnCollision(Fixture fixtureA, Fixture fixtureB, Contact contact) { if (!Alive || IsDying || RemoteState != RemoteBulletState.Normal || PredictionState != RemoteBulletState.Normal) { return(false); } #region RemoteBullet var otherBullet = fixtureB.UserData as RemoteBullet; if (otherBullet != null) { if (otherBullet.Fraction == Fraction) { return(true); } if (!otherBullet.Alive) { return(false); } if (otherBullet.Scale / Scale >= 2f && !otherBullet.Fraction.IsNeutral) { // split other otherBullet.PredictKill(RemoteBulletState.Dying_Instant); PredictKill(RemoteBulletState.Dying_Explosion); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } else if (Scale / otherBullet.Scale >= 2f && Fraction.IsNeutral) { // split me otherBullet.PredictKill(RemoteBulletState.Dying_Explosion); PredictKill(RemoteBulletState.Dying_Instant); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } else { otherBullet.PredictKill(RemoteBulletState.Dying_Explosion); PredictKill(RemoteBulletState.Dying_Explosion); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } } #endregion #region Cannon var otherCannon = fixtureB.UserData as Cannon; if (otherCannon != null) { if (otherCannon.Fraction == Fraction) { // stupid approximation to prevent source cannon coll (lifetime > 0.5s) if (Lifetime > 0.5f || fixtureB == otherCannon.PhysicsFixtureBase) { if (otherCannon.IsLaser && otherCannon.CannonHealth.TargetValue >= 1f) { MainGame.Inst.GDSound.PlayEffectReflect(); return(true); } else { PredictKill(RemoteBulletState.Dying_ShrinkFast); otherCannon.ApplyBoost(); MainGame.Inst.GDSound.PlayEffectBoost(); } } } else // if (otherCannon.Fraction != this.Fraction) { PredictKill(RemoteBulletState.Dying_Fade); otherCannon.TakeDamage(Fraction, Scale); MainGame.Inst.GDSound.PlayEffectHit(); } return(false); } #endregion #region VoidWall var otherVoidWall = fixtureB.UserData as VoidWall; if (otherVoidWall != null) { PredictKill(RemoteBulletState.Dying_Explosion); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } #endregion #region VoidCircle var otherVoidCircle = fixtureB.UserData as VoidCircle; if (otherVoidCircle != null) { PredictKill(RemoteBulletState.Dying_Explosion); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } #endregion #region Shield var otherShield = fixtureB.UserData as ShieldCollisionMarker; if (otherShield != null) { if (!otherShield.Active) { return(false); } PredictKill(RemoteBulletState.Dying_Explosion); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } #endregion #region GlassBlock var otherGlassBlock = fixtureB.UserData as GlassBlock; if (otherGlassBlock != null) { MainGame.Inst.GDSound.PlayEffectGlass(); return(true); } #endregion #region Portal var otherPortal = fixtureB.UserData as Portal; if (otherPortal != null) { var inPortal = otherPortal; Vector2 normal; FixedArray2 <Vector2> t; contact.GetWorldManifold(out normal, out t); bool hit = FloatMath.DiffRadiansAbs(normal.ToAngle(), inPortal.Normal) < FloatMath.RAD_POS_001; if (!hit) { // back-side hit PredictKill(RemoteBulletState.Dying_Explosion); return(false); } if (inPortal.Links.Count == 0) { // void portal PredictKill(RemoteBulletState.Dying_ShrinkFast); return(false); } PredictKill(RemoteBulletState.Dying_ShrinkFast); return(false); } #endregion #region MirrorBlock var otherMirrorBlock = fixtureB.UserData as MirrorBlock; if (otherMirrorBlock != null) { MainGame.Inst.GDSound.PlayEffectReflect(); return(true); } #endregion #region MirrorCircle var otherMirrorCircle = fixtureB.UserData as MirrorCircle; if (otherMirrorCircle != null) { MainGame.Inst.GDSound.PlayEffectReflect(); return(true); } #endregion #region RefractionMarker var otherRefractionMarker1 = fixtureB.UserData as MarkerRefractionEdge; if (otherRefractionMarker1 != null) { return(false); } var otherRefractionMarker2 = fixtureB.UserData as MarkerRefractionCorner; if (otherRefractionMarker2 != null) { return(false); } #endregion #region BorderMarker var otherBorderMarker = fixtureB.UserData as MarkerCollisionBorder; if (otherBorderMarker != null) { if (GDOwner.WrapMode == GameWrapMode.Reflect) { return(true); } return(false); } #endregion return(false); }
protected override void OnUpdate(GDGameScreen screen, SAMTime gameTime, InputState istate) { if (_state >= TutorialState.TargetFirstNeutral) { _cannon1.CannonHealth.SetForce(1); } if (_state >= TutorialState.TargetCenter) { _cannon2.CannonHealth.SetForce(1); } if (_state >= TutorialState.AttackEnemy) { _cannon3.CannonHealth.SetForce(1); } _s1_blinkTimer += gameTime.ElapsedSeconds; switch (_state) { case TutorialState.Start: Transition_1_ShootFirstNeutral(); break; case TutorialState.TargetFirstNeutral: if (_cannon2.Fraction != _fracNeutral) { _cannon2.CannonHealth.SetForce(0f); _cannon2.SetFraction(_fracNeutral); } _infobox.Background = _infobox.Background.WithColor(ColorMath.Blend(FlatColors.Concrete, FlatColors.Orange, FloatMath.PercSin(_s1_blinkTimer * 4f))); if (FloatMath.DiffRadiansAbs(_cannon1.Rotation.ActualValue, FloatMath.RAD_POS_270) < FloatMath.RAD_POS_060) { Transition_2_ShootingFirstNeutral(); } break; case TutorialState.ShootingFirstNeutral: _infobox.Background = _infobox.Background.WithColor(FlatColors.Concrete); if (FloatMath.IsOne(_cannon2.CannonHealth.ActualValue)) { Transition_3_TargetCenter(); } break; case TutorialState.TargetCenter: if (FloatMath.DiffRadiansAbs(_cannon2.Rotation.ActualValue, FloatMath.RAD_000) < FloatMath.RAD_POS_060) { Transition_4_BoostWhileShootingCenter(); } break; case TutorialState.BoostWhileShootingCenter: _cannon3.ForceResetBarrelCharge(); // no shooting enemy for now _cannon4.ForceResetBarrelCharge(); // no shooting me for now _cannon3.Rotation.Set(FloatMath.RAD_POS_090); var cap3 = _cannon3.Fraction == _fracPlayer && FloatMath.IsOne(_cannon3.CannonHealth.ActualValue); var cap4 = _cannon4.Fraction == _fracComputer && FloatMath.IsOne(_cannon4.CannonHealth.ActualValue); if (cap3 && _cannon4.Fraction == _fracComputer) { _cannon4.CannonHealth.Set(1); } if (cap3 && cap4) { Transition_5_AttackEnemy(); } break; case TutorialState.AttackEnemy: _cannon4.CannonHealth.SetForce(1); if (FloatMath.DiffRadiansAbs(_cannon3.Rotation.ActualValue, FloatMath.RAD_000) < FloatMath.RAD_POS_060) { Transition_6_ChangeGameSpeed(); } break; case TutorialState.ChangeGameSpeed: _cannon4.CannonHealth.SetForce(1); if (_screen.GameSpeedMode > GameSpeedModes.SUPERSLOW) { Transition_7_CaptureEnemy(); } break; case TutorialState.CaptureEnemy: _infobox.Background = _infobox.Background.WithColor(ColorMath.Blend(FlatColors.Alizarin, FlatColors.SunFlower, FloatMath.PercSin(_s1_blinkTimer * 6f))); if (FloatMath.IsOne(_cannon4.CannonHealth.ActualValue) && _cannon4.Fraction == _fracPlayer) { Transition_8_WinGame(); } break; case TutorialState.WinGame: _infobox.Background = _infobox.Background.WithColor(ColorMath.Blend(FlatColors.Emerald, FlatColors.Concrete, FloatMath.PercSin(_s1_blinkTimer * 4f))); break; default: throw new ArgumentOutOfRangeException(); } }
private List <Tuple <BulletPathBlueprint, float> > FindBulletPaths(LevelBlueprint lvl, World wBase, World wCollision, int sourceID, FPoint rcStart, List <Tuple <Vector2, Vector2> > sourcerays, float startRadians, float cannonRadians, int remainingRecasts) { var none = new List <Tuple <BulletPathBlueprint, float> >(); if (remainingRecasts <= 0) { return(none); } var rays = sourcerays.ToList(); var rcEnd = rcStart + new Vector2(2048, 0).Rotate(startRadians); var traceResult = RayCastBullet(wBase, rcStart, rcEnd); var traceResult2 = RayCastBullet(wCollision, rcStart, rcEnd); if (traceResult2 != null && traceResult != null && traceResult2.Item1.UserData != traceResult.Item1.UserData) { // Dirty hit return(none); } if (traceResult == null) { return(none); } var fCannon = traceResult.Item1.UserData as ICannonBlueprint; if (fCannon != null) { if (fCannon.CannonID == sourceID) { return(none); } var quality = Math2D.LinePointDistance(rcStart, traceResult.Item2, new FPoint(fCannon.X, fCannon.Y)); rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var path = new BulletPathBlueprint(fCannon.CannonID, cannonRadians, rays.ToArray()); return(new List <Tuple <BulletPathBlueprint, float> > { Tuple.Create(path, quality) }); } var fGlassBlock = traceResult.Item1.UserData as GlassBlockBlueprint; if (fGlassBlock != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindBulletPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1)); } var fMirrorBlock = traceResult.Item1.UserData as MirrorBlockBlueprint; if (fMirrorBlock != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindBulletPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1)); } var fMirrorCircle = traceResult.Item1.UserData as MirrorCircleBlueprint; if (fMirrorCircle != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindBulletPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1)); } var fVoidWall = traceResult.Item1.UserData as VoidWallBlueprint; if (fVoidWall != null) { return(none); } var fVoidCircle = traceResult.Item1.UserData as VoidCircleBlueprint; if (fVoidCircle != null) { return(none); } var fBlackhole = traceResult.Item1.UserData as BlackHoleBlueprint; if (fBlackhole != null) { return(none); // Black holes are _not_ correctly calculated in this preprocessor } var fPortal = traceResult.Item1.UserData as PortalBlueprint; if (fPortal != null) { bool hit = FloatMath.DiffRadiansAbs(traceResult.Item3.ToAngle(), FloatMath.ToRadians(fPortal.Normal)) < FloatMath.RAD_POS_005; if (!hit) { return(none); } rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var dat = new List <Tuple <BulletPathBlueprint, float> >(); foreach (var outportal in lvl.BlueprintPortals.Where(p => p.Side != fPortal.Side && p.Group == fPortal.Group)) { var cIn = new FPoint(fPortal.X, fPortal.Y); var cOut = new FPoint(outportal.X, outportal.Y); var rot = FloatMath.ToRadians(outportal.Normal - fPortal.Normal + 180); var stretch = outportal.Length / fPortal.Length; var newAngle = FloatMath.NormalizeAngle(startRadians + rot); var newStart = cOut + (traceResult.Item2 - cIn).Rotate(rot) * stretch; newStart = newStart.MirrorAtNormal(cOut, Vector2.UnitX.Rotate(FloatMath.ToRadians(outportal.Normal))); var sub = FindBulletPaths(lvl, wBase, wCollision, sourceID, newStart, rays, newAngle, cannonRadians, remainingRecasts - 1); dat.AddRange(sub); } return(dat); } var fBorder = traceResult.Item1.UserData as MarkerCollisionBorder; if (fBorder != null) { if (lvl.WrapMode == LevelBlueprint.WRAPMODE_DONUT) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStartX = traceResult.Item2.X; var pNewStartY = traceResult.Item2.Y; switch (fBorder.Side) { case FlatAlign4.NN: pNewStartY += lvl.LevelHeight; break; case FlatAlign4.EE: pNewStartX -= lvl.LevelWidth; break; case FlatAlign4.SS: pNewStartY -= lvl.LevelHeight; break; case FlatAlign4.WW: pNewStartX += lvl.LevelWidth; break; default: throw new ArgumentOutOfRangeException(); } var pVec = rcEnd - rcStart; var pNewStart = new FPoint(pNewStartX, pNewStartY); return(FindBulletPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1)); } else if (lvl.WrapMode == LevelBlueprint.WRAPMODE_SOLID) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindBulletPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1)); } throw new Exception("Unsupported WrapMode: " + lvl.WrapMode); } throw new Exception("Unknown rayTrace resturn ficture: " + traceResult.Item1.UserData); }
private List <Tuple <BulletPathBlueprint, float> > FindLaserPaths(LevelBlueprint lvl, World wBase, World wCollision, int sourceID, FPoint rcStart, List <Tuple <Vector2, Vector2> > sourcerays, float startRadians, float cannonRadians, int remainingRecasts, bool inGlassBlock, object objIgnore) { var none = new List <Tuple <BulletPathBlueprint, float> >(); if (remainingRecasts <= 0) { return(none); } var rays = sourcerays.ToList(); var rcEnd = rcStart + new Vector2(2048, 0).Rotate(startRadians); var traceResult = RayCastLaser(wBase, rcStart, rcEnd, objIgnore); var traceResult2 = RayCastLaser(wCollision, rcStart, rcEnd, objIgnore); if (traceResult2 != null && traceResult != null && traceResult2.Item1.UserData != traceResult.Item1.UserData) { // Dirty hit return(none); } if (traceResult == null) { return(none); } foreach (var r in rays) { if (Math2D.LineIntersectionExt(rcStart, traceResult.Item2, r.Item1.ToFPoint(), r.Item2.ToFPoint(), -0.1f, out _, out _, out _)) { return(none); } } var fCannon = traceResult.Item1.UserData as ICannonBlueprint; if (fCannon != null) { if (fCannon.CannonID == sourceID) { return(none); } var quality = Math2D.LinePointDistance(rcStart, traceResult.Item2, new FPoint(fCannon.X, fCannon.Y)); rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var path = new BulletPathBlueprint(fCannon.CannonID, cannonRadians, rays.ToArray()); return(new List <Tuple <BulletPathBlueprint, float> > { Tuple.Create(path, quality) }); } var fGlassBlock = traceResult.Item1.UserData as MarkerRefractionEdge; if (fGlassBlock != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var normal = traceResult.Item3; var aIn = (rcStart - rcEnd).ToAngle() - normal.ToAngle(); // sin(aIn) / sin(aOut) = currRefractIdx / Glass.RefractIdx var n = inGlassBlock ? (GlassBlockBlueprint.REFRACTION_INDEX / 1f) : (1f / GlassBlockBlueprint.REFRACTION_INDEX); var sinaOut = FloatMath.Sin(aIn) * n; var dat = new List <Tuple <BulletPathBlueprint, float> >(); var isRefracting = sinaOut <1 && sinaOut> -1; var isReflecting = FloatMath.Abs(aIn) > LaserNetwork.MIN_REFRACT_ANGLE && (!inGlassBlock || (inGlassBlock && !isRefracting)); if (isRefracting) // refraction { // refraction var aOut = FloatMath.Asin(sinaOut); var pRefractAngle = (-normal).ToAngle() + aOut; var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pRefractAngle, cannonRadians, remainingRecasts - 1, !inGlassBlock, fGlassBlock); dat.AddRange(sub); } if (isReflecting) { // reflection var pReflectVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pReflectVec.ToAngle(), cannonRadians, remainingRecasts - 1, !inGlassBlock, fGlassBlock); dat.AddRange(sub); } return(dat); } var fGlassBlockCorner = traceResult.Item1.UserData as MarkerRefractionCorner; if (fGlassBlockCorner != null) { if (NO_LASER_CORNER_HITS) { return(none); } rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var dat = new List <Tuple <BulletPathBlueprint, float> >(); if (!inGlassBlock) { // reflection var pReflectVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pReflectVec.ToAngle(), cannonRadians, remainingRecasts - 1, !inGlassBlock, fGlassBlockCorner); dat.AddRange(sub); } return(dat); } var fMirrorBlock = traceResult.Item1.UserData as MirrorBlockBlueprint; if (fMirrorBlock != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fMirrorBlock)); } var fMirrorCircle = traceResult.Item1.UserData as MirrorCircleBlueprint; if (fMirrorCircle != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fMirrorCircle)); } var fVoidWall = traceResult.Item1.UserData as VoidWallBlueprint; if (fVoidWall != null) { return(none); } var fVoidCircle = traceResult.Item1.UserData as VoidCircleBlueprint; if (fVoidCircle != null) { return(none); } var fPortal = traceResult.Item1.UserData as PortalBlueprint; if (fPortal != null) { bool hit = FloatMath.DiffRadiansAbs(traceResult.Item3.ToAngle(), FloatMath.ToRadians(fPortal.Normal)) < FloatMath.RAD_POS_005; if (!hit) { return(none); } rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var dat = new List <Tuple <BulletPathBlueprint, float> >(); foreach (var outportal in lvl.BlueprintPortals.Where(p => p.Side != fPortal.Side && p.Group == fPortal.Group)) { var cIn = new FPoint(fPortal.X, fPortal.Y); var cOut = new FPoint(outportal.X, outportal.Y); var rot = FloatMath.ToRadians(outportal.Normal - fPortal.Normal + 180); var stretch = outportal.Length / fPortal.Length; var newAngle = FloatMath.NormalizeAngle(startRadians + rot); var newStart = cOut + stretch * (traceResult.Item2 - cIn).Rotate(rot); newStart = newStart.MirrorAtNormal(cOut, Vector2.UnitX.Rotate(FloatMath.ToRadians(outportal.Normal))); var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, newStart, rays, newAngle, cannonRadians, remainingRecasts - 1, inGlassBlock, outportal); dat.AddRange(sub); } return(dat); } var fBorder = traceResult.Item1.UserData as MarkerCollisionBorder; if (fBorder != null) { if (lvl.WrapMode == LevelBlueprint.WRAPMODE_DONUT) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStartX = traceResult.Item2.X; var pNewStartY = traceResult.Item2.Y; switch (fBorder.Side) { case FlatAlign4.NN: pNewStartY += lvl.LevelHeight; break; case FlatAlign4.EE: pNewStartX -= lvl.LevelWidth; break; case FlatAlign4.SS: pNewStartY -= lvl.LevelHeight; break; case FlatAlign4.WW: pNewStartX += lvl.LevelWidth; break; default: throw new ArgumentOutOfRangeException(); } var pVec = rcEnd - rcStart; var pNewStart = new FPoint(pNewStartX, pNewStartY); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fBorder)); } else if (lvl.WrapMode == LevelBlueprint.WRAPMODE_SOLID) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fBorder)); } throw new Exception("Unsupported WrapMode: " + lvl.WrapMode); } throw new Exception("Unknown rayTrace resturn ficture: " + traceResult.Item1.UserData); }
// test of rays hit each other private bool RayParallality(FPoint v1s, FPoint v1e, float v1d, FPoint v2s, FPoint v2e, float v2d, bool reflTolerant, out FPoint intersect, out float u) { var a1 = (v1e - v1s).ToAngle(); var a2 = (v2e - v2s).ToAngle(); var maxAngle = reflTolerant ? FloatMath.RAD_POS_004 : FloatMath.RAD_POS_090; if (FloatMath.DiffRadiansAbs(a1, a2) < maxAngle) { // same direction var u1 = v2s.ProjectOntoLine(v1s, v1e); var u2 = v1s.ProjectOntoLine(v2s, v2e); if (u1 > 0 && u1 < 1) { intersect = Math2D.PointOnLine(u1, v1s, v1e); u = u1; return(true); } if (u2 > 0 && u2 < 1) { intersect = v1s; u = 0; return(true); } } else if (FloatMath.DiffRadiansAbs(a1 + FloatMath.RAD_POS_180, a2) < maxAngle) { var u1s = FloatMath.Clamp(v2e.ProjectOntoLine(v1s, v1e), 0f, 1f); var u1e = FloatMath.Clamp(v2s.ProjectOntoLine(v1s, v1e), 0f, 1f); var u2s = FloatMath.Clamp(v1e.ProjectOntoLine(v2s, v2e), 0f, 1f); var u2e = FloatMath.Clamp(v1s.ProjectOntoLine(v2s, v2e), 0f, 1f); var v1rs = (u1s == 0) ? v1s : Math2D.PointOnLine(u1s, v1s, v1e); var v1re = (u1e == 1) ? v1e : Math2D.PointOnLine(u1e, v1s, v1e); var v2rs = (u2s == 0) ? v2s : Math2D.PointOnLine(u2s, v2s, v2e); var v2re = (u1e == 1) ? v2e : Math2D.PointOnLine(u2e, v2s, v2e); var distStart = (v1rs - v2re).LengthSquared(); var distEnd = (v1re - v2rs).LengthSquared(); if (distStart < RAY_WIDTH * RAY_WIDTH && distEnd < RAY_WIDTH * RAY_WIDTH) { var pc = FPoint.MiddlePoint(v1s, v2s); pc = pc + (v1s - v2s).WithLength((v1d - v2d) / 2f); var pcu1 = pc.ProjectOntoLine(v1s, v1e); if (pcu1 < 0) { pc = v1s; } if (pcu1 > 1) { pc = v1e; } var pcu2 = pc.ProjectOntoLine(v2s, v2e); if (pcu2 < 0) { pc = v2s; } if (pcu2 > 1) { pc = v2e; } intersect = pc; u = pc.ProjectOntoLine(v1s, v1e); return(true); } else if (distStart < RAY_WIDTH * RAY_WIDTH) { var drStart = FloatMath.Sqrt(distStart); var drEnd = FloatMath.Sqrt(distEnd); var perc = (RAY_WIDTH - drStart) / (drEnd - drStart); u1e = u1s + (u1e - u1s) * perc; u2s = u2e + (u2s - u2e) * perc; v1re = Math2D.PointOnLine(u1e, v1s, v1e); v2rs = Math2D.PointOnLine(u2s, v2s, v2e); var v1rd = v1d + u1s * (v1e - v1s).Length(); var v2rd = v2d + u2s * (v2e - v2s).Length(); var pc = FPoint.MiddlePoint(v1rs, v2rs); pc = pc + (v1rs - v2rs).WithLength((v1rd - v2rd) / 2f); var pcu1 = pc.ProjectOntoLine(v1rs, v1re); if (pcu1 < u1s) { pc = v1rs; } if (pcu1 > u1e) { pc = v1re; } var pcu2 = pc.ProjectOntoLine(v2rs, v2re); if (pcu2 < u2s) { pc = v2rs; } if (pcu2 > u2e) { pc = v2re; } intersect = pc; u = pc.ProjectOntoLine(v1s, v1e); return(true); } else if (distEnd < RAY_WIDTH * RAY_WIDTH) { var drStart = FloatMath.Sqrt(distStart); var drEnd = FloatMath.Sqrt(distEnd); var perc = (RAY_WIDTH - drEnd) / (drStart - drEnd); u1s = u1e + (u1s - u1e) * perc; u2e = u2s + (u2e - u2s) * perc; v1rs = Math2D.PointOnLine(u1s, v1s, v1e); v2re = Math2D.PointOnLine(u2e, v2s, v2e); var v1rd = v1d + u1s * (v1e - v1s).Length(); var v2rd = v2d + u2s * (v2e - v2s).Length(); var pc = FPoint.MiddlePoint(v1rs, v2rs); pc = pc + (v1rs - v2rs).WithLength((v1rd - v2rd) / 2f); var pcu1 = pc.ProjectOntoLine(v1rs, v1re); if (pcu1 < u1s) { pc = v1rs; } if (pcu1 > u1e) { pc = v1re; } var pcu2 = pc.ProjectOntoLine(v2rs, v2re); if (pcu2 < u2s) { pc = v2rs; } if (pcu2 > u2e) { pc = v2re; } intersect = pc; u = pc.ProjectOntoLine(v1s, v1e); return(true); } } intersect = FPoint.Zero; u = float.NaN; return(false); }
private void RecalcFromRay(LaserSource src, FPoint istart, FPoint iend, int idepth, bool iinglass, object iignore, LaserRay isrc, float idist, bool nofault) { Stack <Tuple <FPoint, FPoint, int, bool, object, LaserRay, float> > remaining = new Stack <Tuple <FPoint, FPoint, int, bool, object, LaserRay, float> >(); remaining.Push(Tuple.Create(istart, iend, idepth, iinglass, iignore, isrc, idist)); while (remaining.Any()) { var pop = remaining.Pop(); var start = pop.Item1; var end = pop.Item2; var depth = pop.Item3; var inglass = pop.Item4; var ignore = pop.Item5; var source = pop.Item6; var startdist = pop.Item7; if (src.Lasers.Count + 1 >= MAX_LASER_PER_SOURCE) { continue; } if (depth >= MAX_LASER_RAYCOUNT) { continue; } if (!start.IsValid) { continue; } if (!end.IsValid) { continue; } if (start.EpsilonEquals(end, FloatMath.EPSILON6)) { continue; } var result = RayCast(start, end, ignore); #region OOB if (result == null) { var ray = new LaserRay(start, end, source, LaserRayTerminator.OOB, depth, inglass, ignore, null, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region Cannon var resultCannon = result.Item1.UserData as Cannon; if (resultCannon != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Target, depth, inglass, ignore, resultCannon, startdist, resultCannon, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region GlassBlockRefraction var resultGlassBlockRefrac = result.Item1.UserData as MarkerRefractionEdge; if (resultGlassBlockRefrac != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Glass, depth, inglass, ignore, resultGlassBlockRefrac, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } // sin(aIn) / sin(aOut) = currRefractIdx / Glass.RefractIdx var aIn = (start - end).ToAngle() - result.Item3.ToAngle(); var n = inglass ? (GlassBlock.REFRACTION_INDEX / 1f) : (1f / GlassBlock.REFRACTION_INDEX); var sinaOut = FloatMath.Sin(aIn) * n; var isRefracting = sinaOut <1 && sinaOut> -1; var isReflecting = FloatMath.Abs(aIn) > MIN_REFRACT_ANGLE && (!inglass || (inglass && !isRefracting)); if (isRefracting) // refraction { var aOut = FloatMath.Asin(sinaOut); var pRefractAngle = (-result.Item3).ToAngle() + aOut; remaining.Push(Tuple.Create(result.Item2, result.Item2 + new Vector2(_rayLength, 0).Rotate(pRefractAngle), depth + 1, !inglass, (object)resultGlassBlockRefrac, ray, startdist + ray.Length)); } if (isReflecting) { var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultGlassBlockRefrac, ray, startdist + ray.Length)); } continue; } #endregion #region GlassBlockRefraction (Corner) var resultGlassCornerRefrac = result.Item1.UserData as MarkerRefractionCorner; if (resultGlassCornerRefrac != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.VoidObject, depth, inglass, ignore, resultGlassCornerRefrac, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } // sin(aIn) / sin(aOut) = currRefractIdx / Glass.RefractIdx var aIn = (start - end).ToAngle() - result.Item3.ToAngle(); var n = inglass ? (GlassBlock.REFRACTION_INDEX / 1f) : (1f / GlassBlock.REFRACTION_INDEX); var sinaOut = FloatMath.Sin(aIn) * n; var isRefracting = sinaOut <1 && sinaOut> -1; if (isRefracting) // refraction { // No refrac in corner } if (!inglass) { var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultGlassCornerRefrac, ray, startdist + ray.Length)); continue; } else { // No funtime in corner continue; } } #endregion #region MirrorBlock var resultMirrorBlock = result.Item1.UserData as MirrorBlock; if (resultMirrorBlock != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Mirror, depth, inglass, ignore, resultMirrorBlock, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultMirrorBlock, ray, startdist + ray.Length)); continue; } #endregion #region MirrorCircle var resultMirrorCircle = result.Item1.UserData as MirrorCircle; if (resultMirrorCircle != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Mirror, depth, inglass, ignore, resultMirrorCircle, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultMirrorCircle, ray, startdist + ray.Length)); continue; } #endregion #region VoidWall var resultVoidWall = result.Item1.UserData as VoidWall; if (resultVoidWall != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.VoidObject, depth, inglass, ignore, resultVoidWall, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region VoidCircle var resultVoidCircle = result.Item1.UserData as VoidCircle; if (resultVoidCircle != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.VoidObject, depth, inglass, ignore, resultVoidCircle, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region Shield var resultShield = result.Item1.UserData as ShieldCollisionMarker; if (resultShield != null) { if (resultShield.Active) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Target, depth, inglass, ignore, resultShield, startdist, resultShield.Source, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } else if (src.Type == RayType.Shield && src.LaserFraction == resultShield.Source.Fraction) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Target, depth, inglass, ignore, resultShield, startdist, resultShield.Source, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } else { continue; } } #endregion #region Portal var resultPortal = result.Item1.UserData as Portal; if (resultPortal != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Portal, depth, inglass, ignore, resultPortal, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var inPortal = resultPortal; var normal = result.Item3; bool hit = FloatMath.DiffRadiansAbs(normal.ToAngle(), inPortal.Normal) < FloatMath.RAD_POS_001; if (!hit) { continue; // back-side hit } if (inPortal.Links.Count == 0) { continue; // void portal } foreach (var outportal in inPortal.Links) { var rot = outportal.Normal - inPortal.Normal + FloatMath.RAD_POS_180; var projec = result.Item2.ProjectOntoLine(inPortal.Position, inPortal.VecDirection); var newVelocity = (end - start).Rotate(rot); var newStart = outportal.Position + outportal.VecDirection * (-projec) + outportal.VecNormal * (Portal.WIDTH / 2f); var newEnd = newStart + newVelocity.WithLength(_rayLength); remaining.Push(Tuple.Create(newStart, newEnd, depth + 1, false, (object)outportal, ray, startdist + ray.Length)); } continue; } #endregion #region Border var resultBorderMarker = result.Item1.UserData as MarkerCollisionBorder; if (resultBorderMarker != null) { if (_wrapMode == GameWrapMode.Reflect) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Mirror, depth, inglass, ignore, resultBorderMarker, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultBorderMarker, ray, startdist + ray.Length)); continue; } else if (_wrapMode == GameWrapMode.Donut) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Portal, depth, inglass, ignore, resultBorderMarker, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var pNewStartX = result.Item2.X; var pNewStartY = result.Item2.Y; switch (resultBorderMarker.Side) { case FlatAlign4.NN: pNewStartY += _screen.Blueprint.LevelHeight; break; case FlatAlign4.EE: pNewStartX -= _screen.Blueprint.LevelWidth; break; case FlatAlign4.SS: pNewStartY -= _screen.Blueprint.LevelHeight; break; case FlatAlign4.WW: pNewStartX += _screen.Blueprint.LevelWidth; break; default: SAMLog.Error("LASER::EnumSwitch_RFR", "value: " + resultBorderMarker.Side); break; } var newStart = new FPoint(pNewStartX, pNewStartY); var newEnd = newStart + (end - start); remaining.Push(Tuple.Create(newStart, newEnd, depth + 1, inglass, (object)null, ray, startdist + ray.Length)); continue; } else { SAMLog.Error("LASER::UnknownWrap", "Unknown wrapmode: " + _wrapMode); } continue; } #endregion #region Bullet var resultBullet = result.Item1.UserData as Bullet; if (resultBullet != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.BulletTerm, depth, inglass, ignore, resultBullet, startdist, null, src.Type); ray.TerminatorBullet = resultBullet; src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region RemoteBullet var resultRemoteBullet = result.Item1.UserData as RemoteBullet; if (resultRemoteBullet != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.BulletTerm, depth, inglass, ignore, resultRemoteBullet, startdist, null, src.Type); ray.TerminatorBullet = resultRemoteBullet; src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion // wud ??? SAMLog.Error("LASER::UnknownFixture", string.Format("Ray collided with unkown fixture: {0}", result?.Item1?.UserData ?? "<NULL>")); } }
private bool OnCollision(Fixture fixtureA, Fixture fixtureB, Contact contact) { if (!Alive || IsDying) { return(false); } #region Bullet var otherBullet = fixtureB.UserData as Bullet; if (otherBullet != null) { if (otherBullet.Fraction == Fraction) { return(true); } if (!otherBullet.Alive) { return(false); } if (otherBullet.Scale / Scale >= 2f && !otherBullet.Fraction.IsNeutral) { // split other otherBullet.SplitDestruct(); MutualDestruct(); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } else if (Scale / otherBullet.Scale >= 2f && Fraction.IsNeutral) { // split me otherBullet.MutualDestruct(); SplitDestruct(); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } else { otherBullet.MutualDestruct(); MutualDestruct(); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } } #endregion #region Cannon var otherCannon = fixtureB.UserData as Cannon; if (otherCannon != null) { if (otherCannon.Fraction == Fraction) { // if Source barrel then ignore collision if (otherCannon != Source || fixtureB == otherCannon.PhysicsFixtureBase) { if (otherCannon.IsLaser && otherCannon.CannonHealth.ActualValue >= 1f) { MainGame.Inst.GDSound.PlayEffectReflect(); return(true); } else { DisintegrateIntoFriend(); otherCannon.ApplyBoost(); MainGame.Inst.GDSound.PlayEffectBoost(); } } } else // if (otherCannon.Fraction != this.Fraction) { if (otherCannon != Source || fixtureB == otherCannon.PhysicsFixtureBase) { DisintegrateIntoEnemy(); otherCannon.TakeDamage(Fraction, Scale); MainGame.Inst.GDSound.PlayEffectHit(); } } return(false); } #endregion #region VoidWall var otherVoidWall = fixtureB.UserData as VoidWall; if (otherVoidWall != null) { DisintegrateIntoVoidObject(); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } #endregion #region VoidCircle var otherVoidCircle = fixtureB.UserData as VoidCircle; if (otherVoidCircle != null) { DisintegrateIntoVoidObject(); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } #endregion #region Shield var otherShield = fixtureB.UserData as ShieldCollisionMarker; if (otherShield != null) { if (otherShield.Source == Source) { return(false); } if (!otherShield.Active) { return(false); } DisintegrateIntoVoidObject(); MainGame.Inst.GDSound.PlayEffectCollision(); return(false); } #endregion #region GlassBlock var otherGlassBlock = fixtureB.UserData as GlassBlock; if (otherGlassBlock != null) { MainGame.Inst.GDSound.PlayEffectGlass(); return(true); } #endregion #region Portal var otherPortal = fixtureB.UserData as Portal; if (otherPortal != null) { var inPortal = otherPortal; Vector2 normal; FixedArray2 <Vector2> t; contact.GetWorldManifold(out normal, out t); bool hit = FloatMath.DiffRadiansAbs(normal.ToAngle(), inPortal.Normal) < FloatMath.RAD_POS_001; if (!hit) { // back-side hit DisintegrateIntoVoidObject(); return(false); } if (inPortal.Links.Count == 0) { // void portal DisintegrateIntoPortal(); return(false); } var velocity = ConvertUnits.ToDisplayUnits(PhysicsBody.LinearVelocity); for (int i = 0; i < _ignoredPortals.Count; i++) { if (_ignoredPortals[i].Entity == inPortal) { _ignoredPortals[i].LastCollidedCycle = MonoSAMGame.GameCycleCounter; if (FloatMath.DiffRadiansAbs(velocity.ToAngle(), inPortal.Normal) > FloatMath.RAD_POS_090) { // prevent tunneling Alive = false; return(false); } return(false); } } foreach (var outportal in inPortal.Links) { var stretch = outportal.Length / inPortal.Length; var rot = outportal.Normal - inPortal.Normal + FloatMath.RAD_POS_180; var projec = ConvertUnits.ToDisplayUnits(PhysicsBody.Position).ProjectOntoLine(inPortal.Position, inPortal.VecDirection); var newVelocity = velocity.Rotate(rot); var newStart = outportal.Position + outportal.VecDirection * (-projec) + outportal.VecNormal * (Portal.WIDTH / 2f); var b = new Bullet(GDOwner, Source, newStart, newVelocity, Scale * stretch, Fraction) { Lifetime = Lifetime }; b._ignoredPortals.Add(new CollisionIgnorePortal() { Entity = outportal, LastCollidedCycle = MonoSAMGame.GameCycleCounter }); b.AddOperation(new BulletGrowOperation(0.15f)); Owner.Entities.AddEntity(b); } DisintegrateIntoPortal(); return(false); } #endregion #region MirrorBlock var otherMirrorBlock = fixtureB.UserData as MirrorBlock; if (otherMirrorBlock != null) { MainGame.Inst.GDSound.PlayEffectReflect(); return(true); } #endregion #region MirrorCircle var otherMirrorCircle = fixtureB.UserData as MirrorCircle; if (otherMirrorCircle != null) { MainGame.Inst.GDSound.PlayEffectReflect(); return(true); } #endregion #region RefractionMarker var otherRefractionMarker1 = fixtureB.UserData as MarkerRefractionEdge; if (otherRefractionMarker1 != null) { return(false); } var otherRefractionMarker2 = fixtureB.UserData as MarkerRefractionCorner; if (otherRefractionMarker2 != null) { return(false); } #endregion #region BorderMarker var otherBorderMarker = fixtureB.UserData as MarkerCollisionBorder; if (otherBorderMarker != null) { if (GDOwner.WrapMode == GameWrapMode.Reflect) { return(true); } return(false); } #endregion // wud ??? SAMLog.Error("Collision", string.Format("Bullet collided with unkown fixture: {0}", fixtureB.UserData ?? "<NULL>")); return(false); }
public bool Run(KIController ki) { if (_runDirect != null) { var target = _runDirect(); if (target != null) { var rot = FloatMath.PositiveAtan2(target.Position.Y - ki.Cannon.Position.Y, target.Position.X - ki.Cannon.Position.X); ki.Cannon.KITarget = target; if (ki.MinimumRotationalDelta > 0 && FloatMath.DiffRadiansAbs(rot, ki.Cannon.Rotation.TargetValue) < ki.MinimumRotationalDelta) { ki.LastKIFunction = "Ign[" + Name + "]"; return(true); } ki.Cannon.Rotation.Set(rot); ki.LastKIFunction = Name; return(true); } } else if (_runPrecalc != null) { var target = _runPrecalc(); if (target != null) { ki.Cannon.KITarget = target.TargetCannon; if (ki.MinimumRotationalDelta > 0 && FloatMath.DiffRadiansAbs(target.CannonRotation, ki.Cannon.Rotation.TargetValue) < ki.MinimumRotationalDelta) { ki.LastKIFunction = "Ign[" + Name + "]"; return(true); } ki.Cannon.Rotation.Set(target.CannonRotation); ki.LastKIFunction = Name; return(true); } } else if (_runAntiLaser != null) { var ray = _runAntiLaser(); if (ray != null) { var target = ki.Cannon.Position.MirrorAt(FPoint.MiddlePoint(ray.Start, ray.End)); var rot = target.ToAngle(ki.Cannon.Position); ki.Cannon.KITarget = null; if (ki.MinimumRotationalDelta > 0 && FloatMath.DiffRadiansAbs(rot, ki.Cannon.Rotation.TargetValue) < ki.MinimumRotationalDelta / 4f) { ki.LastKIFunction = "Ign[" + Name + "]"; return(true); } ki.Cannon.Rotation.Set(rot); ki.LastKIFunction = Name; return(true); } return(false); } else if (_runGeneric != null) { _runGeneric(); return(true); } else if (_runCustom != null) { var f = _runCustom(); if (f != null) { var rot = f.Value; if (ki.MinimumRotationalDelta > 0 && FloatMath.DiffRadiansAbs(rot, ki.Cannon.Rotation.TargetValue) < ki.MinimumRotationalDelta / 4f) { ki.LastKIFunction = "Ign[" + Name + "]"; return(true); } ki.Cannon.Rotation.Set(rot); ki.LastKIFunction = Name; return(true); } return(false); } ki.Cannon.KITarget = null; return(false); }
private List <Tuple <List <Vector2>, ICannonBlueprint, float> > FindBulletPaths(LevelBlueprint lvl, World world, int sourceID, FPoint spawnPoint, Vector2 spawnVeloc, List <Vector2> fullpath, float scale, float lifetime) { var none = new List <Tuple <List <Vector2>, ICannonBlueprint, float> >(); // path, cannon, quality fullpath = fullpath.ToList(); object collisionUserObject = null; Contact collisionContact = null; var farseerBullet = BodyFactory.CreateCircle(world, ConvertUnits.ToSimUnits(scale * Bullet.BULLET_DIAMETER / 2), 1, ConvertUnits2.ToSimUnits(spawnPoint), BodyType.Dynamic, null); farseerBullet.LinearVelocity = ConvertUnits.ToSimUnits(spawnVeloc); farseerBullet.CollidesWith = Category.All; farseerBullet.Restitution = 1f; // Bouncability, 1=bounce always elastic farseerBullet.AngularDamping = 1f; // Practically no angular rotation farseerBullet.Friction = 0f; farseerBullet.LinearDamping = 0f; // no slowing down farseerBullet.OnCollision += (fa, fb, cobj) => { collisionUserObject = fb.UserData; collisionContact = cobj; if (fb.UserData is GlassBlockBlueprint) { return(true); } if (fb.UserData is MirrorBlockBlueprint) { return(true); } if (fb.UserData is MirrorCircleBlueprint) { return(true); } if (fb.UserData is MarkerCollisionBorder) { return(true); } return(false); }; farseerBullet.AngularVelocity = 0; fullpath.Add(spawnPoint.ToVec2D()); for (;;) { collisionUserObject = null; foreach (var bh in lvl.BlueprintBlackHoles) { var pp = ConvertUnits.ToDisplayUnits(farseerBullet.Position) - new Vector2(bh.X, bh.Y); var force = bh.Power / pp.LengthSquared(); var vecForce = pp.WithLength(force); farseerBullet.ApplyForce(ConvertUnits.ToSimUnits(vecForce)); } world.Step(1 / SIMULATION_UPS); // x UPS lifetime += 1 / SIMULATION_UPS; fullpath.Add(ConvertUnits2.ToDisplayUnitsPoint(farseerBullet.Position).ToVec2D()); if (collisionUserObject is PortalBlueprint) { var veloc = ConvertUnits.ToDisplayUnits(farseerBullet.LinearVelocity); world.RemoveBody(farseerBullet); var fPortal = (PortalBlueprint)collisionUserObject; Vector2 normal; FixedArray2 <Vector2> t; collisionContact.GetWorldManifold(out normal, out t); bool hit = FloatMath.DiffRadiansAbs(normal.ToAngle(), FloatMath.ToRadians(fPortal.Normal)) < FloatMath.RAD_POS_001; if (!hit) { return(none); } var dat = new List <Tuple <List <Vector2>, ICannonBlueprint, float> >(); foreach (var outportal in lvl.BlueprintPortals.Where(p => p.Side != fPortal.Side && p.Group == fPortal.Group)) { var stretch = outportal.Length / fPortal.Length; var cIn = new FPoint(fPortal.X, fPortal.Y); var cOut = new FPoint(outportal.X, outportal.Y); var cInVecNormal = Vector2.UnitX.RotateDeg(fPortal.Normal); var cInVecDirection = cInVecNormal.RotateWithLength(FloatMath.RAD_POS_090, fPortal.Length / 2f); var cOutVecNormal = Vector2.UnitX.RotateDeg(outportal.Normal); var cOutVecDirection = cOutVecNormal.RotateWithLength(FloatMath.RAD_POS_090, outportal.Length / 2f); var rot = FloatMath.ToRadians(outportal.Normal - fPortal.Normal) + FloatMath.RAD_POS_180; var projec = ConvertUnits.ToDisplayUnits(farseerBullet.Position).ProjectOntoLine(cIn, cInVecDirection); var newVelocity = ConvertUnits.ToDisplayUnits(farseerBullet.LinearVelocity).Rotate(rot); var newStart = cOut + cOutVecDirection * (-projec) + cOutVecNormal * (Portal.WIDTH / 2f) + newVelocity.WithLength(scale * Bullet.BULLET_DIAMETER / 2 + 4); var sub = FindBulletPaths(lvl, world, sourceID, newStart, newVelocity, fullpath, scale * stretch, lifetime); dat.AddRange(sub); } return(dat); } if (collisionUserObject is ICannonBlueprint) { world.RemoveBody(farseerBullet); var tgcannon = (ICannonBlueprint)collisionUserObject; if (tgcannon.CannonID == sourceID) { return(none); } var quality = Math2D.LinePointDistance(ConvertUnits2.ToDisplayUnitsPoint(farseerBullet.Position), ConvertUnits2.ToDisplayUnitsPoint(farseerBullet.Position) + ConvertUnits.ToDisplayUnits(farseerBullet.LinearVelocity), new FPoint(tgcannon.X, tgcannon.Y)); return(new List <Tuple <List <Vector2>, ICannonBlueprint, float> > { Tuple.Create(fullpath, tgcannon, quality) }); } bool oow = (farseerBullet.Position.X < 0 - 64) || (farseerBullet.Position.Y < 0 - 64) || (farseerBullet.Position.X > ConvertUnits.ToSimUnits(lvl.LevelWidth) + 64) || (farseerBullet.Position.Y > ConvertUnits.ToSimUnits(lvl.LevelHeight) + 64); bool ool = (lifetime >= Bullet.MAXIMUM_LIFETIME * LIFETIME_FAC); //if (collisionUserObject != null || oow || ool) //{ // world.RemoveBody(farseerBullet); // return new List<Tuple<List<Vector2>, CannonBlueprint, float>> { Tuple.Create(fullpath, new CannonBlueprint(farseerBullet.Position.X, farseerBullet.Position.Y, 64, 1, 0, FloatMath.GetRangedIntRandom(0, 9999999)), 1f) }; //} if (collisionUserObject is VoidWallBlueprint) { world.RemoveBody(farseerBullet); return(none); } if (collisionUserObject is VoidCircleBlueprint) { world.RemoveBody(farseerBullet); return(none); } if (collisionUserObject is BlackHoleBlueprint) { world.RemoveBody(farseerBullet); return(none); } if (oow || ool) { world.RemoveBody(farseerBullet); return(none); } if (lvl.WrapMode == LevelBlueprint.WRAPMODE_DONUT) { var pbullet = ConvertUnits.ToDisplayUnits(farseerBullet.Position); pbullet.X = (pbullet.X + lvl.LevelWidth) % lvl.LevelWidth; pbullet.Y = (pbullet.Y + lvl.LevelHeight) % lvl.LevelHeight; farseerBullet.Position = ConvertUnits.ToSimUnits(pbullet); } } }