From 04d55891f0407f781ce0c9fb1cb72a1cbeabbd56 Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 5 Sep 2015 12:13:44 +0300 Subject: [PATCH] Particles using display coordinates, particle growtime, character death&stun animations --- .../Content/Particles/ParticlePrefabs.xml | 33 +++--- .../BackgroundSpriteManager.cs | 43 +++++--- Subsurface/Source/Characters/Character.cs | 28 +++-- .../Source/Characters/FishAnimController.cs | 43 ++++++-- .../Characters/HumanoidAnimController.cs | 81 ++++++++++----- Subsurface/Source/Characters/Limb.cs | 6 +- Subsurface/Source/Characters/Ragdoll.cs | 48 ++++++--- Subsurface/Source/GameSession/GameSession.cs | 2 + .../Items/Components/Holdable/RepairTool.cs | 2 +- .../Items/Components/Machines/Engine.cs | 4 +- .../Items/Components/Power/PowerTransfer.cs | 6 +- Subsurface/Source/Map/Explosion.cs | 13 +-- Subsurface/Source/Map/Gap.cs | 26 ++--- Subsurface/Source/Map/Hull.cs | 4 +- Subsurface/Source/Map/Structure.cs | 2 +- Subsurface/Source/Particles/Particle.cs | 98 ++++++++---------- .../Source/Particles/ParticleManager.cs | 2 +- Subsurface/Source/Particles/ParticlePrefab.cs | 98 ++++++++++++------ Subsurface/Source/Screens/GameScreen.cs | 8 +- Subsurface/Source/Screens/MainMenu.cs | 3 +- Subsurface/Source/Screens/NetLobbyScreen.cs | 2 +- Subsurface_Solution.v12.suo | Bin 635904 -> 672768 bytes 22 files changed, 346 insertions(+), 206 deletions(-) diff --git a/Subsurface/Content/Particles/ParticlePrefabs.xml b/Subsurface/Content/Particles/ParticlePrefabs.xml index ed5624623..4b744b1ea 100644 --- a/Subsurface/Content/Particles/ParticlePrefabs.xml +++ b/Subsurface/Content/Particles/ParticlePrefabs.xml @@ -7,8 +7,9 @@ startcolor="1.0, 1.0, 1.0" startalpha="0.8" colorchange="0.0, 0.0, 0.0, -0.25" lifetime="3" - deleteonhit="true" - velocitychange="0.0, -9.8"> + growtime ="0.2" + deleteoncollision="true" + velocitychange="0.0, -9.8"> @@ -18,6 +19,7 @@ startrotationmin ="0.0" startrotationmax="6.28" startcolor="1.0, 1.0, 1.0" startalpha="0.5" colorchange="0.0, 0.0, 0.0, -0.25" + growtime ="0.2" lifetime="3" velocitychange="0.0, -0.05"> @@ -25,7 +27,7 @@ @@ -54,15 +56,16 @@ startcolor="0.5, 0.0, 0.0" startalpha="1.0" colorchange="0.0, 0.0, 0.0, -1.0" lifetime="2" - deleteonhit="true" + growtime ="0.1" + deleteoncollision="true" rotatetodirection="true" velocitychange="0.0, -9.8"> @@ -124,10 +129,10 @@ diff --git a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs index 5fa971966..719a3ee08 100644 --- a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs +++ b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs @@ -29,34 +29,53 @@ namespace Subsurface } } - public void Update(float deltaTime) + public void SpawnSprites(int count) { - if (activeSprites.Count < MaxSprites) - { - WayPoint wp = WayPoint.WayPointList[Rand.Int(WayPoint.WayPointList.Count)]; + count = Math.Min(count, MaxSprites); - Vector2 pos = new Vector2(wp.Rect.X, wp.Rect.Y); - pos += Rand.Vector(200.0f); + activeSprites.Clear(); + + for (int i = 0; i < count; i++ ) + { + Vector2 pos = Vector2.Zero; + + if (WayPoint.WayPointList.Count>0) + { + WayPoint wp = WayPoint.WayPointList[Rand.Int(WayPoint.WayPointList.Count)]; + + pos = new Vector2(wp.Rect.X, wp.Rect.Y); + pos += Rand.Vector(200.0f); + } + else + { + pos = Rand.Vector(2000.0f); + } var prefab = prefabs[Rand.Int(prefabs.Count)]; - int amount = Rand.Range(prefab.SwarmMin,prefab.SwarmMax); + int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax); List swarmMembers = new List(); - - for (int i = 0; i0) + if (amount > 0) { Swarm swarm = new Swarm(swarmMembers, prefab.SwarmRadius); } - - } + } + public void ClearSprites() + { + activeSprites.Clear(); + } + + public void Update(float deltaTime) + { foreach (BackgroundSprite sprite in activeSprites) { sprite.Update(deltaTime); diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 0c56b3047..c8d049924 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -940,13 +940,13 @@ namespace Subsurface for (int i = 0; i < 10; i++) { Particle p = Game1.ParticleManager.CreateParticle("waterblood", - torso.SimPosition + new Vector2(Rand.Range(-0.5f, 0.5f), Rand.Range(-0.5f, 0.5f)), + torso.Position + new Vector2(Rand.Range(-50f, 50f), Rand.Range(-50f, 50f)), Vector2.Zero); if (p!=null) p.Size *= 2.0f; Game1.ParticleManager.CreateParticle("bubbles", torso.SimPosition, - new Vector2(Rand.Range(-0.5f, 0.5f), Rand.Range(-1.0f,0.5f))); + new Vector2(Rand.Range(-50f, 50f), Rand.Range(-100f,50f))); } foreach (var joint in AnimController.limbJoints) @@ -956,6 +956,21 @@ namespace Subsurface Kill(true); } + private IEnumerable DeathAnim() + { + float timer = 8.0f; + + while (timer > 0.0f) + { + AnimController.UpdateAnim(1.0f / 60.0f); + timer -= 1.0f / 60.0f; + + yield return CoroutineStatus.Running; + } + + yield return CoroutineStatus.Success; + } + public void Kill(bool networkMessage = false) { if (isDead) return; @@ -973,6 +988,8 @@ namespace Subsurface } } + CoroutineManager.StartCoroutine(DeathAnim()); + health = 0.0f; isDead = true; @@ -1086,11 +1103,8 @@ namespace Subsurface } else { - Limb torso = AnimController.GetLimb(LimbType.Torso); - if (torso == null) torso = AnimController.GetLimb(LimbType.Head); - - message.Write(torso.body.Position.X); - message.Write(torso.body.Position.Y); + message.Write(AnimController.RefLimb.Position.X); + message.Write(AnimController.RefLimb.Position.Y); LargeUpdateTimer = Math.Max(0, LargeUpdateTimer-1); } diff --git a/Subsurface/Source/Characters/FishAnimController.cs b/Subsurface/Source/Characters/FishAnimController.cs index 322ec1488..a185db63f 100644 --- a/Subsurface/Source/Characters/FishAnimController.cs +++ b/Subsurface/Source/Characters/FishAnimController.cs @@ -34,7 +34,11 @@ namespace Subsurface swimSpeed = ToolBox.GetAttributeFloat(element, "swimspeed", 1.0f); float footRot = ToolBox.GetAttributeFloat(element,"footrotation", float.NaN); - if (!float.IsNaN(footRot)) + if (float.IsNaN(footRot)) + { + footRotation = null; + } + else { footRotation = MathHelper.ToRadians(footRot); } @@ -44,6 +48,12 @@ namespace Subsurface public override void UpdateAnim(float deltaTime) { + if (character.IsDead) + { + UpdateStruggling(deltaTime); + return; + } + ResetPullJoints(); if (strongestImpact > 0.0f) @@ -54,7 +64,7 @@ namespace Subsurface if (stunTimer>0.0f) { - UpdateStruggling(); + UpdateStruggling(deltaTime); stunTimer -= deltaTime; return; } @@ -204,25 +214,27 @@ namespace Subsurface movement = MathUtils.SmoothStep(movement, TargetMovement * walkSpeed, 0.2f); if (movement == Vector2.Zero) return; + IgnorePlatforms = (TargetMovement.Y < -Math.Abs(TargetMovement.X)); + Limb colliderLimb; float colliderHeight; - Limb torso = GetLimb(LimbType.Torso); - Limb head = GetLimb(LimbType.Head); + Limb torso = GetLimb(LimbType.Torso); + Limb head = GetLimb(LimbType.Head); - if (torso!=null) + if (torso != null) { colliderLimb = torso; colliderHeight = TorsoPosition; - colliderLimb.body.SmoothRotate(TorsoAngle*Dir, 10.0f); + colliderLimb.body.SmoothRotate(TorsoAngle * Dir, 10.0f); } else { colliderLimb = head; colliderHeight = HeadPosition; - if (onGround) colliderLimb.body.SmoothRotate(HeadAngle*Dir, 100.0f); + if (onGround) colliderLimb.body.SmoothRotate(HeadAngle * Dir, 100.0f); } Vector2 colliderPos = colliderLimb.SimPosition; @@ -331,13 +343,24 @@ namespace Subsurface } } - void UpdateStruggling() + void UpdateStruggling(float deltaTime) { Limb head = GetLimb(LimbType.Head); Limb tail = GetLimb(LimbType.Tail); - if (head != null) head.body.ApplyTorque(head.Mass * Dir * 0.1f); - if (tail != null) tail.body.ApplyTorque(tail.Mass * -Dir * 0.1f); + if (head != null) head.body.ApplyTorque(head.Mass * Dir * (float)Math.Sin(walkPos) * 5.0f); + if (tail != null) tail.body.ApplyTorque(tail.Mass * -Dir * (float)Math.Sin(walkPos) * 5.0f); + + walkPos += deltaTime * 5.0f; + + Vector2 centerOfMass = GetCenterOfMass(); + + foreach (Limb limb in limbs) + { + if (limb.type == LimbType.Head || limb.type == LimbType.Tail) continue; + + limb.body.ApplyForce((centerOfMass - limb.SimPosition) * (float)Math.Sin(walkPos) * limb.Mass * 10.0f); + } } public override void Flip() diff --git a/Subsurface/Source/Characters/HumanoidAnimController.cs b/Subsurface/Source/Characters/HumanoidAnimController.cs index 54ba5d3f5..f6c7a8833 100644 --- a/Subsurface/Source/Characters/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/HumanoidAnimController.cs @@ -18,6 +18,12 @@ namespace Subsurface public override void UpdateAnim(float deltaTime) { + if (character.IsDead) + { + UpdateStruggling(); + return; + } + Vector2 colliderPos = GetLimb(LimbType.Torso).SimPosition; if (inWater) stairs = null; @@ -543,12 +549,9 @@ namespace Subsurface leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix); MoveLimb(leftHand, handPos + leftHandPos, 3.5f); - } - + } } - - void UpdateClimbing() { if (character.SelectedConstruction == null || character.SelectedConstruction.GetComponent()==null) @@ -662,14 +665,17 @@ namespace Subsurface Limb rightLeg = GetLimb(LimbType.RightFoot); Limb torso = GetLimb(LimbType.Torso); - walkPos += 0.2f; + //walkPos += 0.2f; if (inWater) return; - Vector2 footPos = torso.body.Position+ new Vector2(TorsoPosition*Dir,0.0f); + HandIK(GetLimb(LimbType.RightHand), GetLimb(LimbType.Head).SimPosition,0.1f); + HandIK(GetLimb(LimbType.LeftHand), GetLimb(LimbType.Head).SimPosition,0.1f); + + //Vector2 footPos = torso.body.Position+ new Vector2(TorsoPosition*Dir,0.0f); - MoveLimb(leftLeg, footPos, 0.7f); - MoveLimb(rightLeg, footPos, 0.7f); + //MoveLimb(leftLeg, footPos, 0.7f); + //MoveLimb(rightLeg, footPos, 0.7f); } public override void HoldItem(float deltaTime, Camera cam, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, float holdAngle) @@ -690,7 +696,7 @@ namespace Subsurface Vector2 itemPos = character.GetInputState(InputType.SecondaryHeld) ? aimPos : holdPos; float itemAngle; - if (character.GetInputState(InputType.SecondaryHeld) && itemPos != Vector2.Zero) + if (stunTimer <= 0.0f && character.GetInputState(InputType.SecondaryHeld) && itemPos != Vector2.Zero) { Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition); @@ -767,27 +773,54 @@ namespace Subsurface if (itemPos == Vector2.Zero) continue; Limb hand = (i == 0) ? rightHand : leftHand; - Limb arm = (i == 0) ? rightArm : leftArm; - //hand length - float a = 37.0f; + HandIK(hand, transformedHoldPos + transformedHandlePos[i]); - //arm length - float b = 28.0f; + //Limb arm = (i == 0) ? rightArm : leftArm; - //distance from shoulder to holdpos - float c = ConvertUnits.ToDisplayUnits(Vector2.Distance(transformedHoldPos + transformedHandlePos[i], shoulderPos)); - c = MathHelper.Clamp(a + b - 1, b-a, c); + ////hand length + //float a = 37.0f; - float ang2 = MathUtils.VectorToAngle((transformedHoldPos + transformedHandlePos[i]) - shoulderPos)+MathHelper.PiOver2; + ////arm length + //float b = 28.0f; - float armAngle = MathUtils.SolveTriangleSSS(a, b, c); - float handAngle = MathUtils.SolveTriangleSSS(b, a, c); + ////distance from shoulder to holdpos + //float c = ConvertUnits.ToDisplayUnits(Vector2.Distance(transformedHoldPos + transformedHandlePos[i], shoulderPos)); + //c = MathHelper.Clamp(a + b - 1, b-a, c); - arm.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f); - hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f); - } - + //float ang2 = MathUtils.VectorToAngle((transformedHoldPos + transformedHandlePos[i]) - shoulderPos)+MathHelper.PiOver2; + + //float armAngle = MathUtils.SolveTriangleSSS(a, b, c); + //float handAngle = MathUtils.SolveTriangleSSS(b, a, c); + + //arm.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f); + //hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f); + } + } + + private void HandIK(Limb hand, Vector2 pos, float force = 1.0f) + { + Vector2 shoulderPos = limbJoints[2].WorldAnchorA; + + Limb arm = (hand.type == LimbType.LeftHand) ? GetLimb(LimbType.LeftArm) : GetLimb(LimbType.RightArm); + + //hand length + float a = 37.0f; + + //arm length + float b = 28.0f; + + //distance from shoulder to holdpos + float c = ConvertUnits.ToDisplayUnits(Vector2.Distance(pos, shoulderPos)); + c = MathHelper.Clamp(a + b - 1, b - a, c); + + float ang2 = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2; + + float armAngle = MathUtils.SolveTriangleSSS(a, b, c); + float handAngle = MathUtils.SolveTriangleSSS(b, a, c); + + arm.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f*force); + hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f*force); } public override void Flip() diff --git a/Subsurface/Source/Characters/Limb.cs b/Subsurface/Source/Characters/Limb.cs index a633005b7..3b5ecfb33 100644 --- a/Subsurface/Source/Characters/Limb.cs +++ b/Subsurface/Source/Characters/Limb.cs @@ -337,13 +337,13 @@ namespace Subsurface if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel); Game1.ParticleManager.CreateParticle("blood", - SimPosition, - particleVel * Rand.Range(1.0f, 3.0f)); + Position, + particleVel * Rand.Range(100.0f, 300.0f)); } for (int i = 0; i < bloodAmount / 2; i++) { - Game1.ParticleManager.CreateParticle("waterblood", SimPosition, Vector2.Zero); + Game1.ParticleManager.CreateParticle("waterblood", Position, Vector2.Zero); } return new AttackResult(amount, bleedingAmount, hitArmor); diff --git a/Subsurface/Source/Characters/Ragdoll.cs b/Subsurface/Source/Characters/Ragdoll.cs index 4bf77dd34..9d92ece12 100644 --- a/Subsurface/Source/Characters/Ragdoll.cs +++ b/Subsurface/Source/Characters/Ragdoll.cs @@ -49,6 +49,8 @@ namespace Subsurface public bool onGround; private bool ignorePlatforms; + private Limb refLimb; + protected Structure stairs; protected Direction dir; @@ -60,6 +62,14 @@ namespace Subsurface get { return lowestLimb; } } + public Limb RefLimb + { + get + { + return refLimb; + } + } + public float Mass { get; @@ -226,6 +236,10 @@ namespace Subsurface } + refLimb = GetLimb(LimbType.Torso); + if (refLimb == null) refLimb = GetLimb(LimbType.Head); + if (refLimb == null) DebugConsole.ThrowError("Character ''" + character + "'' doesn't have a head or torso!"); + foreach (var joint in limbJoints) { @@ -348,14 +362,12 @@ namespace Subsurface if (limb.pullJoint != null) { Vector2 pos = ConvertUnits.ToDisplayUnits(limb.pullJoint.WorldAnchorA); - pos.Y = -pos.Y; - GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Red, true, 0.01f); + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.Red, true, 0.01f); if (limb.AnimTargetPos == Vector2.Zero) continue; Vector2 pos2 = ConvertUnits.ToDisplayUnits(limb.AnimTargetPos); - pos2.Y = -pos2.Y; - GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos2.X, (int)pos2.Y, 5, 5), Color.Blue, true, 0.01f); + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos2.X, (int)-pos2.Y, 5, 5), Color.Blue, true, 0.01f); GUI.DrawLine(spriteBatch, pos, pos2, Color.Green); } @@ -368,7 +380,14 @@ namespace Subsurface pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorB); GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true); - } + } + + if (refLimb.body.TargetPosition != Vector2.Zero) + { + Vector2 pos = ConvertUnits.ToDisplayUnits(refLimb.body.TargetPosition); + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X-5, (int)-pos.Y-5, 10, 10), Color.LightBlue, false); + + } } @@ -531,17 +550,17 @@ namespace Subsurface //create a splash particle Subsurface.Particles.Particle splash = Game1.ParticleManager.CreateParticle("watersplash", - new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)), - new Vector2(0.0f, Math.Abs(-limb.LinearVelocity.Y * 0.1f)), + new Vector2(limb.Position.X, limbHull.Surface), + new Vector2(0.0f, Math.Abs(-limb.LinearVelocity.Y * 10.0f)), 0.0f); - if (splash != null) splash.yLimits = ConvertUnits.ToSimUnits( - new Vector2( - limbHull.Rect.Y, - limbHull.Rect.Y - limbHull.Rect.Height)); + //if (splash != null) splash.yLimits = ConvertUnits.ToSimUnits( + // new Vector2( + // limbHull.Rect.Y, + // limbHull.Rect.Y - limbHull.Rect.Height)); Game1.ParticleManager.CreateParticle("bubbles", - new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)), + new Vector2(limb.Position.X, limbHull.Surface), limb.LinearVelocity*0.001f, 0.0f); @@ -572,9 +591,6 @@ namespace Subsurface private void UpdateNetplayerPosition() { - Limb refLimb = GetLimb(LimbType.Torso); - if (refLimb == null) refLimb = GetLimb(LimbType.Head); - if (refLimb.body.TargetPosition == Vector2.Zero) return; //if the limb is further away than resetdistance, all limbs are immediately snapped to their targetpositions @@ -583,7 +599,7 @@ namespace Subsurface //if the limb is closer than alloweddistance, just ignore the difference float allowedDistance = NetConfig.AllowedRagdollDistance; - float dist = Vector2.Distance(limbs[0].body.Position, refLimb.body.TargetPosition); + float dist = Vector2.Distance(refLimb.body.Position, refLimb.body.TargetPosition); bool resetAll = (dist > resetDistance && character.LargeUpdateTimer == 1); Vector2 newMovement = (refLimb.body.TargetPosition - refLimb.body.Position); diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index 0f30697bc..092fbc0f9 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -117,6 +117,8 @@ namespace Subsurface { level.Generate(submarine == null ? 100.0f : Math.Max(Submarine.Borders.Width, Submarine.Borders.Height)); submarine.SetPosition(level.StartPosition - new Vector2(0.0f, 2000.0f)); + + Game1.GameScreen.BackgroundSpriteManager.SpawnSprites(80); } if (Quest!=null) Quest.Start(Level.Loaded); diff --git a/Subsurface/Source/Items/Components/Holdable/RepairTool.cs b/Subsurface/Source/Items/Components/Holdable/RepairTool.cs index 6cbbc4c68..c0df3c6b1 100644 --- a/Subsurface/Source/Items/Components/Holdable/RepairTool.cs +++ b/Subsurface/Source/Items/Components/Holdable/RepairTool.cs @@ -200,7 +200,7 @@ namespace Subsurface.Items.Components if (!string.IsNullOrWhiteSpace(particles)) { - Game1.ParticleManager.CreateParticle(particles, TransformedBarrelPos, + Game1.ParticleManager.CreateParticle(particles, ConvertUnits.ToDisplayUnits(TransformedBarrelPos), -item.body.Rotation + ((item.body.Dir>0.0f) ? 0.0f : MathHelper.Pi), 0.0f); } diff --git a/Subsurface/Source/Items/Components/Machines/Engine.cs b/Subsurface/Source/Items/Components/Machines/Engine.cs index 3e7d1051a..4cfe3df6f 100644 --- a/Subsurface/Source/Items/Components/Machines/Engine.cs +++ b/Subsurface/Source/Items/Components/Machines/Engine.cs @@ -72,8 +72,8 @@ namespace Subsurface.Items.Components for (int i = 0; i < 5; i++) { - Game1.ParticleManager.CreateParticle("bubbles", item.SimPosition, - -currForce/500.0f + new Vector2(Rand.Range(-1.0f, 1.0f), Rand.Range(-0.5f, 0.5f))); + Game1.ParticleManager.CreateParticle("bubbles", item.Position, + -currForce/5.0f + new Vector2(Rand.Range(-100.0f, 100.0f), Rand.Range(-50f, 50f))); } } diff --git a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs index e49814559..eea77f1d2 100644 --- a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs @@ -62,11 +62,11 @@ namespace Subsurface.Items.Components { sparkSounds[Rand.Int(sparkSounds.Length)].Play(1.0f, 600.0f, item.Position); - Vector2 baseVel = Rand.Vector(3.0f); + Vector2 baseVel = Rand.Vector(300.0f); for (int i = 0; i < 10; i++) { - var particle = Game1.ParticleManager.CreateParticle("spark", pt.item.SimPosition, - baseVel + Rand.Vector(1.0f), 0.0f); + var particle = Game1.ParticleManager.CreateParticle("spark", pt.item.Position, + baseVel + Rand.Vector(100.0f), 0.0f); if (particle != null) particle.Size *= Rand.Range(0.5f,1.0f); } diff --git a/Subsurface/Source/Map/Explosion.cs b/Subsurface/Source/Map/Explosion.cs index 5f108c8f7..9b25e84b4 100644 --- a/Subsurface/Source/Map/Explosion.cs +++ b/Subsurface/Source/Map/Explosion.cs @@ -51,21 +51,22 @@ namespace Subsurface public void Explode(Vector2 simPosition) { - Game1.ParticleManager.CreateParticle("shockwave", simPosition, + Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition); + + Game1.ParticleManager.CreateParticle("shockwave", displayPosition, Vector2.Zero, 0.0f); for (int i = 0; i < range * 10; i++) { - Game1.ParticleManager.CreateParticle("spark", simPosition, - Rand.Vector(Rand.Range(5.0f, 8.0f)), 0.0f); + Game1.ParticleManager.CreateParticle("spark", displayPosition, + Rand.Vector(Rand.Range(500.0f, 800.0f)), 0.0f); - Game1.ParticleManager.CreateParticle("explosionfire", simPosition + Rand.Vector(0.5f), - Rand.Vector(Rand.Range(0.5f, 1.0f)), 0.0f); + Game1.ParticleManager.CreateParticle("explosionfire", displayPosition + Rand.Vector(50f), + Rand.Vector(Rand.Range(50f, 100.0f)), 0.0f); } - Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition); float displayRange = ConvertUnits.ToDisplayUnits(range); light = new LightSource(displayPosition, displayRange, Color.LightYellow); diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 6b2fb9120..8d04b7edb 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -244,38 +244,38 @@ namespace Subsurface { //UpdateFlowForce(); - Vector2 pos = SimPosition; + Vector2 pos = Position; if (isHorizontal) { - pos.Y = ConvertUnits.ToSimUnits(MathHelper.Clamp(lowerSurface, rect.Y-rect.Height, rect.Y)); + pos.Y = MathHelper.Clamp(lowerSurface, rect.Y - rect.Height, rect.Y); var particle = Game1.ParticleManager.CreateParticle("watersplash", - new Vector2(pos.X, pos.Y - Rand.Range(0.0f, 0.1f)), + new Vector2(pos.X, pos.Y - Rand.Range(0.0f, 10.0f)), new Vector2( - MathHelper.Clamp(flowForce.X, -5000.0f, 5000.0f) * Rand.Range(0.005f, 0.007f), - flowForce.Y * Rand.Range(0.005f, 0.007f))); - if (particle!=null) + MathHelper.Clamp(flowForce.X, -5000.0f, 5000.0f) * Rand.Range(0.5f, 0.7f), + flowForce.Y * Rand.Range(0.5f, 0.7f))); + if (particle != null) { particle.Size = particle.Size * Math.Abs(flowForce.X / 1000.0f); } - - pos.Y = ConvertUnits.ToSimUnits(Rand.Range(lowerSurface, rect.Y - rect.Height)); + + pos.Y = Rand.Range(lowerSurface, rect.Y - rect.Height); Game1.ParticleManager.CreateParticle("bubbles", pos, flowForce / 200.0f); } else { - pos.Y += Math.Sign(flowForce.Y) * ConvertUnits.ToSimUnits(rect.Height / 2.0f); + pos.Y += Math.Sign(flowForce.Y) * rect.Height / 2.0f; for (int i = 0; i < rect.Width; i += (int)Rand.Range(80, 100)) { - pos.X = ConvertUnits.ToSimUnits(Rand.Range(rect.X, rect.X+rect.Width)); + pos.X = Rand.Range(rect.X, rect.X + rect.Width); Subsurface.Particles.Particle splash = Game1.ParticleManager.CreateParticle("watersplash", pos, - new Vector2(flowForce.X * Rand.Range(0.005f, 0.008f), flowForce.Y * Rand.Range(0.005f, 0.008f))); + new Vector2(0, Math.Max(flowForce.Y * Rand.Range(0.5f, 0.8f), 0.0f))); - if (splash!=null) splash.Size = splash.Size * MathHelper.Clamp(rect.Width / 50.0f, 0.8f, 4.0f); + if (splash != null) splash.Size = splash.Size * MathHelper.Clamp(rect.Width / 50.0f, 0.8f, 4.0f); - Game1.ParticleManager.CreateParticle("bubbles", pos, flowForce / 200.0f); + Game1.ParticleManager.CreateParticle("bubbles", pos, flowForce / 2.0f); } } diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index f95c6a561..b36ad9bd7 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -212,8 +212,8 @@ namespace Subsurface if (maxDelta > Rand.Range(0.2f,10.0f)) { Game1.ParticleManager.CreateParticle("mist", - ConvertUnits.ToSimUnits(new Vector2(rect.X + WaveWidth * i,surface + waveY[i])), - new Vector2(0.0f, -0.5f)); + new Vector2(rect.X + WaveWidth * i,surface + waveY[i]), + new Vector2(0.0f, -50.0f)); } waveY[i] = waveY[i] + waveVel[i]; diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index ba386dd30..d6ef4ee57 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -419,7 +419,7 @@ namespace Subsurface int i = FindSectionIndex(ConvertUnits.ToDisplayUnits(position)); if (i == -1) return new AttackResult(0.0f, 0.0f); - Game1.ParticleManager.CreateParticle("dustcloud", ConvertUnits.ToSimUnits(SectionPosition(i)), 0.0f, 0.0f); + Game1.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f); if (playSound && !SectionHasHole(i)) { diff --git a/Subsurface/Source/Particles/Particle.cs b/Subsurface/Source/Particles/Particle.cs index a6210f135..0542fae22 100644 --- a/Subsurface/Source/Particles/Particle.cs +++ b/Subsurface/Source/Particles/Particle.cs @@ -24,25 +24,22 @@ namespace Subsurface.Particles private Color color; private float alpha; + private float totalLifeTime; private float lifeTime; private Vector2 velocityChange; private Vector2 drawPosition; - private float checkCollisionTimer; + //private float checkCollisionTimer; + + private Hull currentHull; public ParticlePrefab.DrawTargetType DrawTarget { get { return prefab.DrawTarget; } } - - public Vector2 yLimits - { - get; - set; - } - + public Vector2 Size { get { return size; } @@ -62,29 +59,33 @@ namespace Subsurface.Particles this.position = position; prevPosition = position; - drawPosition = ConvertUnits.ToDisplayUnits(position); + drawPosition = position; velocity = speed; - this.rotation = rotation + Rand.Range(prefab.startRotationMin, prefab.startRotationMax); + this.rotation = rotation + Rand.Range(prefab.StartRotationMin, prefab.StartRotationMax); prevRotation = rotation; - angularVelocity = prefab.angularVelocityMin + (prefab.angularVelocityMax - prefab.angularVelocityMin) * Rand.Range(0.0f, 1.0f); + angularVelocity = prefab.AngularVelocityMin + (prefab.AngularVelocityMax - prefab.AngularVelocityMin) * Rand.Range(0.0f, 1.0f); - lifeTime = prefab.lifeTime; - - size = prefab.startSizeMin + (prefab.startSizeMax - prefab.startSizeMin) * Rand.Range(0.0f, 1.0f); - - sizeChange = prefab.sizeChangeMin + (prefab.sizeChangeMax - prefab.sizeChangeMin) * Rand.Range(0.0f, 1.0f); - - yLimits = Vector2.Zero; - - color = prefab.startColor; - alpha = prefab.startAlpha; + totalLifeTime = prefab.LifeTime; + lifeTime = prefab.LifeTime; - velocityChange = prefab.velocityChange; + size = prefab.StartSizeMin + (prefab.StartSizeMax - prefab.StartSizeMin) * Rand.Range(0.0f, 1.0f); - if (prefab.rotateToDirection) + sizeChange = prefab.SizeChangeMin + (prefab.SizeChangeMax - prefab.SizeChangeMin) * Rand.Range(0.0f, 1.0f); + + color = prefab.StartColor; + alpha = prefab.StartAlpha; + + velocityChange = prefab.VelocityChange; + + if (prefab.DeleteOnCollision) + { + currentHull = Hull.FindHull(position); + } + + if (prefab.RotateToDirection) { this.rotation = MathUtils.VectorToAngle(new Vector2(velocity.X, -velocity.Y)); @@ -98,7 +99,7 @@ namespace Subsurface.Particles position.X += velocity.X * deltaTime; position.Y += velocity.Y * deltaTime; - if (prefab.rotateToDirection) + if (prefab.RotateToDirection) { if (velocityChange != Vector2.Zero || angularVelocity != 0.0f) { @@ -114,37 +115,18 @@ namespace Subsurface.Particles velocity.Y += velocityChange.Y * deltaTime; size.X += sizeChange.X * deltaTime; - size.Y += sizeChange.Y * deltaTime; + size.Y += sizeChange.Y * deltaTime; - alpha += prefab.colorChange.W * deltaTime; + alpha += prefab.ColorChange.W * deltaTime; color = new Color( - color.R / 255.0f + prefab.colorChange.X * deltaTime, - color.G / 255.0f + prefab.colorChange.Y * deltaTime, - color.B / 255.0f + prefab.colorChange.Z * deltaTime); - - if (yLimits!=Vector2.Zero) + color.R / 255.0f + prefab.ColorChange.X * deltaTime, + color.G / 255.0f + prefab.ColorChange.Y * deltaTime, + color.B / 255.0f + prefab.ColorChange.Z * deltaTime); + + if (prefab.DeleteOnCollision && currentHull!=null) { - if (position.Y>yLimits.X || position.Y 0.0f) - { - checkCollisionTimer -= deltaTime; - } - else - { - if (Submarine.InsideWall(new Vector2(drawPosition.X, -drawPosition.Y))) - { - return false; - } - checkCollisionTimer = 0.05f; - } + if (!Submarine.RectContains(currentHull.Rect, position)) return false; } lifeTime -= deltaTime; @@ -160,9 +142,17 @@ namespace Subsurface.Particles drawPosition.Y = -drawPosition.Y; float drawRotation = Physics.Interpolate(prevRotation, rotation); - drawPosition = ConvertUnits.ToDisplayUnits(drawPosition); - - prefab.sprite.Draw(spriteBatch, drawPosition, color*alpha, prefab.sprite.origin, drawRotation, size, SpriteEffects.None, prefab.sprite.Depth); + //drawPosition = ConvertUnits.ToDisplayUnits(drawPosition); + + Vector2 drawSize = size; + + if (prefab.GrowTime>0.0f && totalLifeTime-lifeTime < prefab.GrowTime) + { + drawSize *= ((totalLifeTime - lifeTime) / prefab.GrowTime); + + } + + prefab.Sprite.Draw(spriteBatch, drawPosition, color*alpha, prefab.Sprite.origin, drawRotation, drawSize, SpriteEffects.None, prefab.Sprite.Depth); //spriteBatch.Draw( // prefab.sprite.Texture, diff --git a/Subsurface/Source/Particles/ParticleManager.cs b/Subsurface/Source/Particles/ParticleManager.cs index 857b4322c..0f802b137 100644 --- a/Subsurface/Source/Particles/ParticleManager.cs +++ b/Subsurface/Source/Particles/ParticleManager.cs @@ -61,7 +61,7 @@ namespace Subsurface.Particles public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation=0.0f) { - if (!Submarine.RectContains(cam.WorldView, ConvertUnits.ToDisplayUnits(position))) return null; + if (!Submarine.RectContains(cam.WorldView, position)) return null; if (particleCount >= MaxParticles) return null; if (particles[particleCount] == null) particles[particleCount] = new Particle(); diff --git a/Subsurface/Source/Particles/ParticlePrefab.cs b/Subsurface/Source/Particles/ParticlePrefab.cs index 9f2b9a1e1..21e3ea4fa 100644 --- a/Subsurface/Source/Particles/ParticlePrefab.cs +++ b/Subsurface/Source/Particles/ParticlePrefab.cs @@ -1,5 +1,6 @@ using System.Xml.Linq; using Microsoft.Xna.Framework; +using FarseerPhysics; namespace Subsurface.Particles { @@ -7,67 +8,104 @@ namespace Subsurface.Particles { public enum DrawTargetType { Air = 1, Water = 2, Both = 3 } - public readonly string name; + public readonly string Name; - public readonly Sprite sprite; + public readonly Sprite Sprite; - public readonly float angularVelocityMin, angularVelocityMax; + public readonly float AngularVelocityMin, AngularVelocityMax; - public readonly float startRotationMin, startRotationMax; + public readonly float StartRotationMin, StartRotationMax; - public readonly Vector2 startSizeMin, startSizeMax; - public readonly Vector2 sizeChangeMin, sizeChangeMax; + public readonly Vector2 StartSizeMin, StartSizeMax; + public readonly Vector2 SizeChangeMin, SizeChangeMax; - public readonly Color startColor; - public readonly float startAlpha; + public readonly Color StartColor; + public readonly float StartAlpha; - public readonly Vector4 colorChange; + public readonly Vector4 ColorChange; - public readonly float lifeTime; + public readonly float LifeTime; - public readonly bool deleteOnHit; + public readonly float GrowTime; - public readonly Vector2 velocityChange; + public readonly bool DeleteOnCollision; + + public readonly Vector2 VelocityChange; public readonly DrawTargetType DrawTarget; - public readonly bool rotateToDirection; + public readonly bool RotateToDirection; public ParticlePrefab(XElement element) { - name = element.Name.ToString(); + Name = element.Name.ToString(); foreach (XElement subElement in element.Elements()) { if (subElement.Name.ToString().ToLower() != "sprite") continue; - sprite = new Sprite(subElement); + Sprite = new Sprite(subElement); } - angularVelocityMin = ToolBox.GetAttributeFloat(element, "angularvelocitymin", 0.0f); - angularVelocityMax = ToolBox.GetAttributeFloat(element, "angularvelocitymax", 0.0f); + if (element.Attribute("angularvelocity") == null) + { + AngularVelocityMin = ToolBox.GetAttributeFloat(element, "angularvelocitymin", 0.0f); + AngularVelocityMax = ToolBox.GetAttributeFloat(element, "angularvelocitymax", 0.0f); + } + else + { + AngularVelocityMin = ToolBox.GetAttributeFloat(element, "angularvelocity", 0.0f); + AngularVelocityMax = AngularVelocityMin; + } - startSizeMin = ToolBox.GetAttributeVector2(element, "startsizemin", Vector2.One); - startSizeMax = ToolBox.GetAttributeVector2(element, "startsizemax", Vector2.One); + if (element.Attribute("startsize") == null) + { + StartSizeMin = ToolBox.GetAttributeVector2(element, "startsizemin", Vector2.One); + StartSizeMax = ToolBox.GetAttributeVector2(element, "startsizemax", Vector2.One); + } + else + { + StartSizeMin = ToolBox.GetAttributeVector2(element, "startsize", Vector2.One); + StartSizeMax = StartSizeMin; + } - sizeChangeMin = ToolBox.GetAttributeVector2(element, "sizechangemin", Vector2.Zero); - sizeChangeMax = ToolBox.GetAttributeVector2(element, "sizechangemax", Vector2.Zero); + if (element.Attribute("sizechange") == null) + { + SizeChangeMin = ToolBox.GetAttributeVector2(element, "sizechangemin", Vector2.Zero); + SizeChangeMax = ToolBox.GetAttributeVector2(element, "sizechangemax", Vector2.Zero); + } + else + { + SizeChangeMin = ToolBox.GetAttributeVector2(element, "sizechange", Vector2.Zero); + SizeChangeMax = SizeChangeMin; + } - startRotationMin = ToolBox.GetAttributeFloat(element, "startrotationmin", 0.0f); - startRotationMax = ToolBox.GetAttributeFloat(element, "startrotationmax", 0.0f); + GrowTime = ToolBox.GetAttributeFloat(element, "growtime", 0.0f); - startColor = new Color(ToolBox.GetAttributeVector4(element, "startcolor", Vector4.One)); - startAlpha = ToolBox.GetAttributeFloat(element, "startalpha", 1.0f); + if (element.Attribute("startrotation") == null) + { + StartRotationMin = ToolBox.GetAttributeFloat(element, "startrotationmin", 0.0f); + StartRotationMax = ToolBox.GetAttributeFloat(element, "startrotationmax", 0.0f); + } + else + { + StartRotationMin = ToolBox.GetAttributeFloat(element, "startrotatio", 0.0f); + StartRotationMax = StartRotationMin; + } - deleteOnHit = ToolBox.GetAttributeBool(element, "deleteonhit", false); + StartColor = new Color(ToolBox.GetAttributeVector4(element, "startcolor", Vector4.One)); + StartAlpha = ToolBox.GetAttributeFloat(element, "startalpha", 1.0f); - colorChange = ToolBox.GetAttributeVector4(element, "colorchange", Vector4.Zero); + DeleteOnCollision = ToolBox.GetAttributeBool(element, "deleteoncollision", false); - lifeTime = ToolBox.GetAttributeFloat(element, "lifetime", 5.0f); + ColorChange = ToolBox.GetAttributeVector4(element, "colorchange", Vector4.Zero); - velocityChange = ToolBox.GetAttributeVector2(element, "velocitychange", Vector2.Zero); + LifeTime = ToolBox.GetAttributeFloat(element, "lifetime", 5.0f); - rotateToDirection = ToolBox.GetAttributeBool(element, "rotatetodirection", false); + VelocityChange = ToolBox.GetAttributeVector2(element, "velocitychange", Vector2.Zero); + VelocityChange = ConvertUnits.ToDisplayUnits(VelocityChange); + + RotateToDirection = ToolBox.GetAttributeBool(element, "rotatetodirection", false); switch (ToolBox.GetAttributeString(element, "drawtarget", "air").ToLower()) { diff --git a/Subsurface/Source/Screens/GameScreen.cs b/Subsurface/Source/Screens/GameScreen.cs index 830f896b4..5af8ebc87 100644 --- a/Subsurface/Source/Screens/GameScreen.cs +++ b/Subsurface/Source/Screens/GameScreen.cs @@ -16,7 +16,7 @@ namespace Subsurface readonly Sprite background, backgroundTop; - private BackgroundSpriteManager backgroundSpriteManager; + public BackgroundSpriteManager BackgroundSpriteManager; public Camera Cam { @@ -35,7 +35,7 @@ namespace Subsurface background = new Sprite("Content/Map/background.png", Vector2.Zero); backgroundTop = new Sprite("Content/Map/background2.png", Vector2.Zero); - backgroundSpriteManager = new BackgroundSpriteManager("Content/BackgroundSprites/BackgroundSpritePrefabs.xml"); + BackgroundSpriteManager = new BackgroundSpriteManager("Content/BackgroundSprites/BackgroundSpritePrefabs.xml"); } public override void Select() @@ -75,7 +75,7 @@ namespace Subsurface Character.UpdateAll(cam, (float)deltaTime); - backgroundSpriteManager.Update((float)deltaTime); + BackgroundSpriteManager.Update((float)deltaTime); Game1.ParticleManager.Update((float)deltaTime); @@ -186,7 +186,7 @@ namespace Subsurface null, null, null, null, cam.Transform); - backgroundSpriteManager.Draw(spriteBatch); + BackgroundSpriteManager.Draw(spriteBatch); spriteBatch.End(); diff --git a/Subsurface/Source/Screens/MainMenu.cs b/Subsurface/Source/Screens/MainMenu.cs index ef53f9a54..018be373c 100644 --- a/Subsurface/Source/Screens/MainMenu.cs +++ b/Subsurface/Source/Screens/MainMenu.cs @@ -160,9 +160,8 @@ namespace Subsurface } this.game = game; - } - + public bool SelectTab(GUIButton button, object obj) { selectedTab = (int)obj; diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 24ce258b6..07282849d 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -721,7 +721,7 @@ namespace Subsurface return; } - if (!string.IsNullOrWhiteSpace(mapName)) TrySelectMap(mapName, md5Hash); + //if (!string.IsNullOrWhiteSpace(mapName)) TrySelectMap(mapName, md5Hash); modeList.Select(modeIndex); diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index a2950347a57a7a6ace982cc82476bbbbae3903e0..12965c30d2faf2a2bf45b0b3d1299d05bee2d7d8 100644 GIT binary patch delta 18098 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