} // end of Setup() /// <summary> /// c'tor for use when targetting a point in space rather than an object. /// </summary> /// <param name="position"></param> /// <param name="targetPosition"></param> /// <param name="launcher"></param> /// <param name="verbPayload"></param> /// <param name="trackingMode"></param> /// <param name="color"></param> /// <returns></returns> static public CruiseMissile Create( Vector3 position, // Starting position. Vector3 targetPosition, // Location we're trying to hit. GameActor launcher, // Actor that launched this missile. float initialRotation, // GameThing.Verbs verbPayload, int damage, MissileChassis.BehaviorFlags behavior, Classification.Colors color, float desiredSpeed, float missileLifetime, bool wantSmoke) { CruiseMissile cm = NextAvailable(); cm.Setup( position, launcher, initialRotation, verbPayload, damage, behavior, color, desiredSpeed, wantSmoke); //Vector3 forward = Vector3.Normalize(targetPosition - position); //Vector3 side = Vector3.Cross(Vector3.UnitZ, forward); //if (side.LengthSquared() < 0.01f) //{ // side = Vector3.Cross(Vector3.UnitY, forward); //} //side.Normalize(); //Vector3 up = Vector3.Normalize(Vector3.Cross(forward, side)); //Matrix l2w = Matrix.Identity; //l2w.Right = forward; //l2w.Up = up; //l2w.Forward = side; //l2w.Translation = position; //cm.Movement.LocalMatrix = l2w; MissileChassis missileChassis = cm.Chassis as MissileChassis; Vector3 direction = targetPosition - launcher.WorldCollisionCenter; Vector3 delta = Vector3.Normalize(direction); float desiredPitch = (float)(Math.Atan2(delta.Z, new Vector2(delta.X, delta.Y).Length())); missileChassis.PitchAngle = desiredPitch; missileChassis.DeathTime = Time.GameTimeTotalSeconds + missileLifetime * 1.1f; missileChassis.TargetPosition = position + delta * desiredSpeed * missileLifetime * 10f; missileChassis.TargetObject = null; return(cm); } // end of CruiseMissile Create
} // end of CruiseMissile Create /// <summary> /// c'tor to use when shooting at a particular object /// </summary> /// <param name="position"></param> /// <param name="targetPosition"></param> /// <param name="targetThing"></param> /// <param name="launcher"></param> /// <param name="verbPayload"></param> /// <param name="trackingMode"></param> /// <param name="color"></param> /// <returns></returns> static public CruiseMissile Create( Vector3 position, // Starting position. GameThing targetThing, // Object we're trying to hit. GameActor launcher, // Actor that launched this missile. float initialRotation, // GameThing.Verbs verbPayload, int damage, MissileChassis.BehaviorFlags behavior, Classification.Colors color, float desiredSpeed, float missileLifetime, bool wantSmoke) { CruiseMissile cm = NextAvailable(); cm.Setup( position, launcher, initialRotation, verbPayload, damage, behavior, color, desiredSpeed, wantSmoke); MissileChassis missileChassis = cm.Chassis as MissileChassis; // Set initial speed taking into account the velocity of the launcher. Vector3 missileVelocity = new Vector3((float)Math.Cos(initialRotation), (float)Math.Sin(initialRotation), 0); float dot = Vector3.Dot(missileVelocity, launcher.Movement.Velocity); missileChassis.Speed = Math.Max(desiredSpeed, dot); missileChassis.DeathTime = Time.GameTimeTotalSeconds + missileLifetime; missileChassis.TargetPosition = targetThing.WorldCollisionCenter; missileChassis.TargetObject = targetThing; return(cm); } // end of CruiseMissile Create
/// <summary> /// Start, end and radii are passed in from a mover. This method tests /// this mover's swept ellipsoid against all the non-moving things as /// well as all the other movers which appear after it in the list. /// The "first" param is the index of the next item in the list. /// /// The listAll param seems to determine whether we just keep the nearest hit /// or keep them all. It appears that listAll=false is used for physics collision /// testing while listAll=true is used for WHEN Bumped testing. But, of course, /// this is just a guess on my part since it's not documented. Argh. /// </summary> /// <param name="startPos0">start position of swept sphere</param> /// <param name="endPos0">end position of swept sphere</param> /// <param name="radii0">Radii of swept ellipsoid.</param> /// <param name="first">This is the index of the first mover to check for collisions with. Start, end, and radii come from the previous mover. TODO (****) Change numbering so this is more clear.</param> /// <param name="listAll">When true, all hits are listed. When false, only the nearest hit is listed.</param> /// <param name="hits"></param> /// <returns></returns> private bool CollisionCheck(Vector3 startPos0, Vector3 endPos0, Vector3 radii0, int first, bool listAll, List <HitInfo> hits) { /// These are structs, no real allocation done here. CollInfo curCollPrim = new CollInfo(); curCollPrim.DistSq = Single.MaxValue; CollInfo best = new CollInfo(); best.DistSq = Single.MaxValue; bool hit = false; // Test the current swept ellipsoid against all the non-moving things. List <CollisionPrimitive> relevants = things; for (int ithing = 0; ithing < relevants.Count; ++ithing) { CollisionPrimitive thing = relevants[ithing]; if (thing.Owner.Ignored) { continue; } // Note we used the Z part of the radii. So, on squashed movers we // might get some intersection. // TODO (****) Actually figure out how to test a swept ellipsoid against // the Primitive based collision shapes. if (thing.Collide(startPos0, endPos0, radii0.Z, ref curCollPrim)) { if (listAll) { HitInfo hitScratch = MakeHitScratch(curCollPrim, radii0.X); hits.Add(hitScratch); curCollPrim.DistSq = Single.MaxValue; } else if (curCollPrim.DistSq <= best.DistSq) { hit = true; best = curCollPrim; endPos0 = best.Center; if (best.Touching) { HitInfo hitScratch = MakeHitScratch(best, radii0.X); hits.Add(hitScratch); } } } } // Test the current mover against the remaining movers in the list. // Note that in the case of hit testing blips this index may be -1. int curMoverIndex = first - 1; // If listAll is true, then we're testing for bump so test the full // list since we have to worry about TouchCushions. if (listAll) { first = 0; } for (int second = first; second < movers.Count; ++second) { Mover sphere = movers[second]; // Don't test against self. if (second == curMoverIndex) { continue; } if (sphere.Owner.Ignored) { continue; } // Don't collide missiles with their launcher. Note that this assumes // that the launcher is always in the list _ahead_ of the missile. MissileChassis mc = movers[second].Owner.Chassis as MissileChassis; if (mc != null && curMoverIndex >= 0 && mc.Launcher == movers[curMoverIndex].Owner) { continue; } Vector3 radii1 = sphere.Radius * sphere.Owner.SquashScale; Vector3 hitContactPosition = Vector3.Zero; Vector3 hitNormal = Vector3.UnitZ; float hitT = -1; bool hitTest = SweptPrims.SweptEllipsoidSweptEllipsoid(startPos0, endPos0, radii0, sphere.Center, sphere.Center + sphere.Delta, sphere.Radius * sphere.Owner.SquashScale, ref hitContactPosition, ref hitNormal, ref hitT); if (hitTest) { Vector3 posAtCollision = MyMath.Lerp(startPos0, endPos0, hitT); // Already touching? if (hitT <= 0) { sphere.SetCollPrimTouching(startPos0, posAtCollision, hitContactPosition, -hitNormal, sphere.Center + hitT * sphere.Delta, ref curCollPrim); } else { sphere.SetCollPrim(startPos0, posAtCollision, hitContactPosition, -hitNormal, sphere.Center + hitT * sphere.Delta, ref curCollPrim); } if (listAll) { HitInfo hitScratch = MakeHitScratch(curCollPrim, radii0.X); hits.Add(hitScratch); curCollPrim.DistSq = Single.MaxValue; } else if (curCollPrim.DistSq <= best.DistSq) { best = curCollPrim; hit = true; if (best.Touching) { HitInfo hitScratch = MakeHitScratch(best, radii0.X); hits.Add(hitScratch); } } } // end if hitTest } // end of loop over other movers. if (hit) { Debug.Assert(!listAll, "Hit shouldn't get set when listing all hits"); /// If it's touching, we've already applied, otherwise /// do it here. if (!best.Touching) { HitInfo hitScratch = MakeHitScratch(best, radii0.X); hits.Add(hitScratch); } } if (listAll && (hits.Count > 1)) { hits.Sort(comparer); } return(hits.Count > 0); }
/// <summary> /// Give the actor hitting the other and the other a chance /// to respond to impact. /// </summary> /// <param name="mover"></param> /// <param name="hitInfo"></param> private void ApplyCollision(Mover mover, HitInfo hitInfo) { Debug.Assert(hitInfo.Other != null); if (mover.Owner.ActorHoldingThis == hitInfo.Other) { /// I'm touching what's holding me. return; } if (hitInfo.Other.ActorHoldingThis == mover.Owner) { /// I'm touching what I'm holding. return; } if (InGame.inGame.IsPickedUp(mover.Owner) && InGame.inGame.LastClonedThing == hitInfo.Other) { // Don't collide the selected object with the recent clone. clearLastClonedThing = false; return; } mover.Owner.ApplyCollisions(ref hitInfo); AdjustDelta(mover, hitInfo); GameActor other = hitInfo.Other; // The ordering of tests can change so we need to check if either of // the acotrs involved are missiles. if (mover.Owner is CruiseMissile || other is CruiseMissile) { // Actor has been hit by cruise missile. // Yes, it kind of sucks to have this here but none of the collision // information is stored on the Actor so we have to use it while we have it. // Note that we don't allow missiles to hit their launcher. if (other is CruiseMissile) { MissileChassis mc = other.Chassis as MissileChassis; if (mc != null && mc.Launcher != mover.Owner) { mc.HitTarget(mover.Owner, hitInfo); } } else { MissileChassis mc = mover.Owner.Chassis as MissileChassis; if (mc != null && mc.Launcher != other) { mc.HitTarget(other, hitInfo); } } } else { // Normal path that happens when two actors bump. hitInfo.Center = hitInfo.Struck; hitInfo.Normal = -hitInfo.Normal; hitInfo.Other = mover.Owner; hitInfo.Offset = MakeOffset(hitInfo, other.CollisionRadius); other.ApplyCollisions(ref hitInfo); AdjustDelta(hitInfo.OtherMover, hitInfo); } } // end of ApplyCollision()
private void Setup(Vector3 position, // Starting position. GameActor launcher, // Actor that launched this missile. float initialRotation, GameThing.Verbs verbPayload, int damage, MissileChassis.BehaviorFlags behavior, Classification.Colors color, float desiredSpeed, bool wantSmoke) { smokeEnabled = wantSmoke; // Or, we could change the sound to something other than the rumble. if (!smokeEnabled) { XmlActorParams.IdleSoundName = null; } MissileChassis missileChassis = Chassis as MissileChassis; missileChassis.Missile = this; missileChassis.Launcher = launcher; missileChassis.VerbPayload = verbPayload; missileChassis.Damage = damage; missileChassis.Behavior = behavior; missileChassis.Rotation = initialRotation; missileChassis.DesiredSpeed = desiredSpeed; Mass = 10.0f; Movement.Position = position; // Calculate initial missile speed. float initialSpeed = desiredSpeed; Vector3 gunVel = launcher.Movement.Velocity; Vector3 shotVel = new Vector3((float)Math.Cos(initialRotation), (float)Math.Sin(initialRotation), 0f) * initialSpeed; if (gunVel.X != 0 || gunVel.Y != 0) { // Add in some of the launcher's velocity. Missile will // gradually adjust its speed to its desired velocity. Vector3 gunVelNorm = Vector3.Normalize(gunVel); Vector3 shotVelNorm = Vector3.Normalize(shotVel); float dot = Vector3.Dot(gunVelNorm, shotVelNorm); Vector3 proj = gunVel * dot; initialSpeed = proj.Length() * MyMath.Direction(dot); } // Don't allow initial speed to be negative because it looks odd. missileChassis.Speed = Math.Max(desiredSpeed, initialSpeed); classification.Color = color; InitSmokeEmitter(Classification.ColorVector4(classification.Color)); InitMuzzleFlash(Classification.ColorVector4(classification.Color), 2.0f, 8.0f); missileChassis.IgnoreGlassWalls = true; missileChassis.Feelers.Clear(); // Don't want missiles to hit glass walls. // Register for collisions. //InGame.inGame.RegisterCollide(this); } // end of Setup()