Progress on tutorial, gap tweaking (water flows faster from room to room), UPnP error messages, input keys in array, underwater aiming tweaking, tons of misc stuff commit more often ffs

This commit is contained in:
Regalis
2015-08-31 19:57:49 +03:00
parent 1e990784b2
commit f739808520
150 changed files with 15933 additions and 588 deletions
@@ -34,6 +34,7 @@ namespace Subsurface
public AiState State
{
get { return state; }
set { state = value; }
}
public AIController (Character c)
@@ -469,8 +469,8 @@ namespace Subsurface
public override void FillNetworkData(NetOutgoingMessage message)
{
message.Write((byte)state);
bool wallAttack = (wallAttackPos!=Vector2.Zero && state == AiState.Attack);
bool wallAttack = (wallAttackPos != Vector2.Zero && state == AiState.Attack);
message.Write(wallAttack);
+185 -148
View File
@@ -46,9 +46,11 @@ namespace Subsurface
get { return Properties; }
}
protected Key selectKeyHit;
protected Key actionKeyHit, actionKeyDown;
protected Key secondaryKeyHit, secondaryKeyDown;
protected Key[] keys;
//protected Key selectKeyHit;
//protected Key actionKeyHit, actionKeyDown;
//protected Key secondaryKeyHit, secondaryKeyDown;
private Item selectedConstruction;
private Item[] selectedItems;
@@ -123,10 +125,10 @@ namespace Subsurface
get { return cursorPosition; }
}
public AITarget AiTarget
{
get { return aiTarget; }
}
//public AITarget AiTarget
//{
// get { return aiTarget; }
//}
public float SoundRange
{
@@ -244,31 +246,6 @@ namespace Subsurface
get { return closestItem; }
}
public Key SelectKeyHit
{
get { return selectKeyHit; }
}
public Key ActionKeyHit
{
get { return actionKeyHit; }
}
public Key ActionKeyDown
{
get { return actionKeyDown; }
}
public Key SecondaryKeyHit
{
get { return secondaryKeyHit; }
}
public Key SecondaryKeyDown
{
get { return secondaryKeyDown; }
}
public AIController AIController
{
get { return aiController; }
@@ -311,11 +288,19 @@ namespace Subsurface
public Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false)
{
selectKeyHit = new Key(false);
actionKeyDown = new Key(true);
actionKeyHit = new Key(false);
secondaryKeyHit = new Key(false);
secondaryKeyDown = new Key(true);
keys = new Key[Enum.GetNames(typeof(InputType)).Length];
keys[(int)InputType.Select] = new Key(false);
keys[(int)InputType.ActionHeld] = new Key(true);
keys[(int)InputType.ActionHit] = new Key(false);
keys[(int)InputType.SecondaryHit] = new Key(false);
keys[(int)InputType.SecondaryHeld] = new Key(true);
keys[(int)InputType.Left] = new Key(true);
keys[(int)InputType.Right] = new Key(true);
keys[(int)InputType.Up] = new Key(true);
keys[(int)InputType.Down] = new Key(true);
keys[(int)InputType.Run] = new Key(true);
selectedItems = new Item[2];
@@ -426,6 +411,19 @@ namespace Subsurface
}
}
public bool GetInputState(InputType inputType)
{
return keys[(int)inputType].State;
}
public void ClearInputs()
{
foreach (Key key in keys)
{
key.State = false;
}
}
public override string ToString()
{
return (info != null && !string.IsNullOrWhiteSpace(info.Name)) ? info.Name : SpeciesName;
@@ -477,6 +475,42 @@ namespace Subsurface
{
if (isDead) return;
Vector2 targetMovement = Vector2.Zero;
if (GetInputState(InputType.Left)) targetMovement.X -= 1.0f;
if (GetInputState(InputType.Right)) targetMovement.X += 1.0f;
if (GetInputState(InputType.Up)) targetMovement.Y += 1.0f;
if (GetInputState(InputType.Down)) targetMovement.Y -= 1.0f;
//the vertical component is only used for falling through platforms and climbing ladders when not in water,
//so the movement can't be normalized or the character would walk slower when pressing down/up
if (AnimController.InWater)
{
float length = targetMovement.Length();
if (length > 0.0f) targetMovement = targetMovement / length;
}
if (Math.Sign(targetMovement.X) == Math.Sign(AnimController.Dir) && GetInputState(InputType.Run))
targetMovement *= 3.0f;
AnimController.TargetMovement = targetMovement;
AnimController.IsStanding = true;
if (AnimController.onGround &&
!AnimController.InWater &&
AnimController.Anim != AnimController.Animation.UsingConstruction)
{
Limb head = AnimController.GetLimb(LimbType.Head);
if (cursorPosition.X < head.Position.X - 10.0f)
{
AnimController.TargetDir = Direction.Left;
}
else if (cursorPosition.X > head.Position.X + 10.0f)
{
AnimController.TargetDir = Direction.Right;
}
}
//find the closest item if selectkey has been hit, or if the character is being
//controlled by the player (in order to highlight it)
if (controlled == this)
@@ -487,7 +521,7 @@ namespace Subsurface
if (closestItem != null)
{
closestItem.IsHighlighted = true;
if (selectKeyHit.State && closestItem.Pick(this, forcePick))
if (GetInputState(InputType.Select) && closestItem.Pick(this, forcePick))
{
new NetworkEvent(NetworkEventType.PickItem, ID, true, closestItem.ID);
}
@@ -497,7 +531,7 @@ namespace Subsurface
if (closestCharacter != selectedCharacter) selectedCharacter = null;
if (closestCharacter!=null)
{
if (selectKeyHit.State) selectedCharacter = (selectedCharacter==null) ? closestCharacter : null;
if (GetInputState(InputType.Select)) selectedCharacter = (selectedCharacter == null) ? closestCharacter : null;
}
}
@@ -506,23 +540,22 @@ namespace Subsurface
if (selectedItems[i] == null) continue;
if (i == 1 && selectedItems[0] == selectedItems[1]) continue;
if (actionKeyDown.State) selectedItems[i].Use(deltaTime, this);
if (secondaryKeyDown.State && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this);
if (GetInputState(InputType.ActionHeld)) selectedItems[i].Use(deltaTime, this);
if (GetInputState(InputType.SecondaryHeld) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this);
}
if (selectedConstruction != null)
{
if (actionKeyDown.State) selectedConstruction.Use(deltaTime, this);
if (secondaryKeyDown.State) selectedConstruction.SecondaryUse(deltaTime, this);
if (GetInputState(InputType.ActionHeld)) selectedConstruction.Use(deltaTime, this);
if (GetInputState(InputType.SecondaryHeld)) selectedConstruction.SecondaryUse(deltaTime, this);
}
if (IsNetworkPlayer)
{
selectKeyHit.Reset();
actionKeyHit.Reset();
actionKeyDown.Reset();
secondaryKeyHit.Reset();
secondaryKeyDown.Reset();
foreach (Key key in keys)
{
key.Reset();
}
}
}
@@ -574,44 +607,29 @@ namespace Subsurface
Lights.LightManager.ViewPos = ConvertUnits.ToDisplayUnits(head.SimPosition);
Vector2 targetMovement = Vector2.Zero;
if (!DisableControls)
{
if (PlayerInput.KeyDown(Keys.W)) targetMovement.Y += 1.0f;
if (PlayerInput.KeyDown(Keys.S)) targetMovement.Y -= 1.0f;
if (PlayerInput.KeyDown(Keys.A)) targetMovement.X -= 1.0f;
if (PlayerInput.KeyDown(Keys.D)) targetMovement.X += 1.0f;
keys[(int)InputType.Left].SetState(PlayerInput.KeyDown(Keys.A));
keys[(int)InputType.Right].SetState(PlayerInput.KeyDown(Keys.D));
keys[(int)InputType.Up].SetState(PlayerInput.KeyDown(Keys.W));
keys[(int)InputType.Down].SetState(PlayerInput.KeyDown(Keys.S));
//the vertical component is only used for falling through platforms and climbing ladders when not in water,
//so the movement can't be normalized or the character would walk slower when pressing down/up
if (AnimController.InWater)
{
float length = targetMovement.Length();
if (length > 0.0f) targetMovement = targetMovement / length;
}
keys[(int)InputType.Select].SetState(PlayerInput.KeyHit(Keys.E));
keys[(int)InputType.ActionHit].SetState(PlayerInput.LeftButtonClicked());
keys[(int)InputType.ActionHeld].SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed);
keys[(int)InputType.SecondaryHit].SetState(PlayerInput.RightButtonClicked());
keys[(int)InputType.SecondaryHeld].SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed);
if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) && Math.Sign(targetMovement.X) == Math.Sign(AnimController.Dir))
targetMovement *= 3.0f;
selectKeyHit.SetState(PlayerInput.KeyHit(Keys.E));
actionKeyHit.SetState(PlayerInput.LeftButtonClicked());
actionKeyDown.SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed);
secondaryKeyHit.SetState(PlayerInput.RightButtonClicked());
secondaryKeyDown.SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed);
keys[(int)InputType.Run].SetState(PlayerInput.KeyDown(Keys.LeftShift));
}
else
{
selectKeyHit.SetState(false);
actionKeyHit.SetState(false);
actionKeyDown.SetState(false);
secondaryKeyHit.SetState(false);
secondaryKeyDown.SetState(false);
foreach (Key key in keys)
{
key.SetState(false);
}
}
AnimController.TargetMovement = targetMovement;
AnimController.IsStanding = true;
if (moveCam)
{
cam.TargetPos = ConvertUnits.ToDisplayUnits(AnimController.limbs[0].SimPosition);
@@ -634,20 +652,6 @@ namespace Subsurface
}
}
if (AnimController.onGround &&
!AnimController.InWater &&
AnimController.Anim != AnimController.Animation.UsingConstruction)
{
if (mouseSimPos.X < head.SimPosition.X-1.0f)
{
AnimController.TargetDir = Direction.Left;
}
else if (mouseSimPos.X > head.SimPosition.X + 1.0f)
{
AnimController.TargetDir = Direction.Right;
}
}
DisableControls = false;
}
@@ -810,7 +814,7 @@ namespace Subsurface
Color color = Color.Orange;
if (closestCharacter != null && closestCharacter.isDead)
if (closestCharacter != null && closestCharacter.isDead && closestCharacter.isHumanoid)
{
Vector2 startPos = Position + (closestCharacter.Position - Position) * 0.7f;
startPos = cam.WorldToScreen(startPos);
@@ -840,10 +844,10 @@ namespace Subsurface
textPos.Y += 50.0f;
foreach (ColoredText coloredText in closestItem.GetHUDTexts(Controlled))
{
textPos.X = startPos.X - GUI.Font.MeasureString(coloredText.text).X / 2;
textPos.X = startPos.X - GUI.Font.MeasureString(coloredText.Text).X / 2;
spriteBatch.DrawString(GUI.Font, coloredText.text, textPos, Color.Black);
spriteBatch.DrawString(GUI.Font, coloredText.text, textPos + new Vector2(1, -1), coloredText.color);
spriteBatch.DrawString(GUI.Font, coloredText.Text, textPos, Color.Black);
spriteBatch.DrawString(GUI.Font, coloredText.Text, textPos + new Vector2(1, -1), coloredText.Color);
textPos.Y += 25;
}
@@ -919,13 +923,7 @@ namespace Subsurface
Limb torso= AnimController.GetLimb(LimbType.Torso);
if (torso == null) torso = AnimController.GetLimb(LimbType.Head);
Vector2 centerOfMass = Vector2.Zero;
foreach (Limb limb in AnimController.limbs)
{
centerOfMass += limb.Mass * limb.SimPosition;
}
centerOfMass /= AnimController.Mass;
Vector2 centerOfMass = AnimController.GetCenterOfMass();
health = 0.0f;
@@ -1022,34 +1020,44 @@ namespace Subsurface
}
else if (type == NetworkEventType.KillCharacter)
{
return;
}
else if (type== NetworkEventType.NotMoving)
{
return;
return;
}
var hasInputs = (GetInputState(InputType.Left) ||
GetInputState(InputType.Right) ||
GetInputState(InputType.Up) ||
GetInputState(InputType.Down) ||
GetInputState(InputType.ActionHeld) ||
GetInputState(InputType.SecondaryHeld));
//if (type == Networking.NetworkEventType.KeyHit)
//{
// message.Write(selectKeyHit.Dequeue);
message.Write(actionKeyDown.Dequeue);
message.Write(secondaryKeyDown.Dequeue);
//}
message.Write(hasInputs || LargeUpdateTimer <= 0);
message.Write((float)NetTime.Now);
// Write byte = move direction
message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.X, -10.0f, 10.0f), -10.0f, 10.0f, 8);
message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.Y, -10.0f, 10.0f), -10.0f, 10.0f, 8);
message.Write(keys[(int)InputType.ActionHeld].Dequeue);
message.Write(keys[(int)InputType.SecondaryHeld].Dequeue);
message.Write(keys[(int)InputType.Left].Dequeue);
message.Write(keys[(int)InputType.Right].Dequeue);
message.Write(AnimController.TargetDir==Direction.Right);
message.Write(keys[(int)InputType.Up].Dequeue);
message.Write(keys[(int)InputType.Down].Dequeue);
message.Write(keys[(int)InputType.Run].Dequeue);
// Write byte = move direction
//message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.X, -10.0f, 10.0f), -10.0f, 10.0f, 8);
//message.WriteRangedSingle(MathHelper.Clamp(AnimController.TargetMovement.Y, -10.0f, 10.0f), -10.0f, 10.0f, 8);
if (aiController==null)
{
message.Write(cursorPosition.X);
message.Write(cursorPosition.Y);
}
else
{
message.Write(AnimController.TargetDir == Direction.Right);
}
message.Write(LargeUpdateTimer <= 0);
@@ -1070,7 +1078,7 @@ namespace Subsurface
}
message.WriteRangedSingle(MathHelper.Clamp(AnimController.StunTimer,0.0f,60.0f), 0.0f, 60.0f, 8);
message.Write((byte)health);
message.Write((byte)((health/maxHealth)*255.0f));
if (aiController != null) aiController.FillNetworkData(message);
@@ -1122,40 +1130,39 @@ namespace Subsurface
}
return;
}
else if (type == NetworkEventType.NotMoving)
{
AnimController.TargetMovement = Vector2.Zero;
actionKeyDown.State = false;
secondaryKeyDown.State = false;
return;
}
bool actionKeyState = false;
bool secondaryKeyState = false;
float sendingTime = 0.0f;
Vector2 targetMovement = Vector2.Zero;
float sendingTime = 0.0f;
//Vector2 targetMovement = Vector2.Zero;
bool targetDir = false;
Vector2 cursorPos = Vector2.Zero;
bool leftKeyState = false, rightKeyState = false;
bool upKeyState = false, downKeyState = false;
bool runState = false;
try
{
bool hasInputs = message.ReadBoolean();
if (!hasInputs)
{
ClearInputs();
return;
}
sendingTime = message.ReadFloat();
actionKeyState = message.ReadBoolean();
secondaryKeyState = message.ReadBoolean();
sendingTime = message.ReadFloat();
leftKeyState = message.ReadBoolean();
rightKeyState = message.ReadBoolean();
upKeyState = message.ReadBoolean();
downKeyState = message.ReadBoolean();
targetMovement = new Vector2(message.ReadRangedSingle(-10.0f, 10.0f, 8), message.ReadRangedSingle(-10.0f, 10.0f, 8));
targetMovement.X = MathUtils.Round(targetMovement.X, 0.1f);
targetMovement.Y = MathUtils.Round(targetMovement.Y, 0.1f);
targetDir = message.ReadBoolean();
if (aiController==null)
{
cursorPos = new Vector2(
message.ReadFloat(),
message.ReadFloat());
}
runState = message.ReadBoolean();
}
catch
@@ -1165,22 +1172,52 @@ namespace Subsurface
AnimController.IsStanding = true;
actionKeyDown.State = actionKeyState;
secondaryKeyDown.State = secondaryKeyState;
keys[(int)InputType.ActionHeld].State = actionKeyState;
keys[(int)InputType.SecondaryHeld].State = secondaryKeyState;
if (sendingTime <= LastNetworkUpdate) return;
cursorPosition = cursorPos;
AnimController.TargetMovement= targetMovement;
AnimController.TargetDir = (targetDir) ? Direction.Right : Direction.Left;
keys[(int)InputType.Left].State = leftKeyState;
keys[(int)InputType.Right].State = rightKeyState;
keys[(int)InputType.Up].State = upKeyState;
keys[(int)InputType.Down].State = downKeyState;
keys[(int)InputType.Run].State = runState;
try
{
if (aiController == null)
{
cursorPos = new Vector2(
message.ReadFloat(),
message.ReadFloat());
}
else
{
targetDir = message.ReadBoolean();
}
}
catch
{
return;
}
if (aiController == null)
{
cursorPosition = cursorPos;
}
else
{
AnimController.TargetDir = (targetDir) ? Direction.Right : Direction.Left;
}
if (message.ReadBoolean())
{
foreach (Limb limb in AnimController.limbs)
{
Vector2 pos = Vector2.Zero, vel = Vector2.Zero;
float rotation = 0.0f, angularVel = 0.0f;
float rotation = 0.0f;
try
{
@@ -1213,7 +1250,7 @@ namespace Subsurface
try
{
newStunTimer = message.ReadRangedSingle(0.0f, 60.0f, 8);
newHealth = message.ReadByte();
newHealth = (message.ReadByte() / 255.0f) * maxHealth;
}
catch { return; }
@@ -368,7 +368,7 @@ namespace Subsurface
}
}
float midX = (leftX + rightX) / 2.0f;
float midX = GetCenterOfMass().X;
foreach (Limb l in limbs)
{
@@ -9,6 +9,8 @@ namespace Subsurface
{
class HumanoidAnimController : AnimController
{
private bool aiming;
public HumanoidAnimController(Character character, XElement element)
: base(character, element)
{
@@ -144,6 +146,7 @@ namespace Subsurface
limb.Disabled = false;
}
aiming = false;
}
void UpdateStanding()
@@ -398,7 +401,7 @@ namespace Subsurface
rotation = MathHelper.ToDegrees(rotation);
if (rotation < 0.0f) rotation += 360;
if (!character.IsNetworkPlayer)
if (!character.IsNetworkPlayer && !aiming)
{
if (rotation > 20 && rotation < 170)
TargetDir = Direction.Left;
@@ -413,12 +416,17 @@ namespace Subsurface
//if trying to head to the opposite direction, apply torque
//to the torso to flip the ragdoll around
if (Math.Sign(TargetMovement.X) != Dir && TargetMovement.X != 0.0f)
{
float torque = torso.Mass * 10.0f;
torque *= (rotation > 90 && rotation < 270) ? -Dir : Dir;
//if (Math.Sign(TargetMovement.X) != Dir && TargetMovement.X != 0.0f)
//{
// float torque = torso.Mass * 10.0f;
// torque *= (rotation > 90 && rotation < 270) ? -Dir : Dir;
torso.body.ApplyTorque(torque);
// torso.body.ApplyTorque(torque);
//}
if (targetSpeed > 0.1f && !aiming)
{
torso.body.SmoothRotate(MathUtils.VectorToAngle(TargetMovement)-MathHelper.PiOver2);
}
movement = MathUtils.SmoothStep(movement, TargetMovement, 0.3f);
@@ -578,7 +586,7 @@ namespace Subsurface
handPos = new Vector2(
ladderSimPos.X,
head.SimPosition.Y + 0.5f + movement.Y * 0.1f - ladderSimPos.Y);
head.SimPosition.Y + 0.0f + movement.Y * 0.1f - ladderSimPos.Y);
MoveLimb(leftHand,
new Vector2(handPos.X,
@@ -595,7 +603,7 @@ namespace Subsurface
footPos = new Vector2(
handPos.X - Dir*0.05f,
head.SimPosition.Y - stepHeight * 2.7f - ladderSimPos.Y);
head.SimPosition.Y - stepHeight * 2.7f - ladderSimPos.Y - 0.7f);
//if (movement.Y < 0) footPos.Y += 0.05f;
@@ -679,10 +687,10 @@ namespace Subsurface
Limb rightHand = GetLimb(LimbType.RightHand);
Limb rightArm = GetLimb(LimbType.RightArm);
Vector2 itemPos = character.SecondaryKeyDown.State ? aimPos : holdPos;
Vector2 itemPos = character.GetInputState(InputType.SecondaryHeld) ? aimPos : holdPos;
float itemAngle;
if (character.SecondaryKeyDown.State && itemPos != Vector2.Zero)
if (character.GetInputState(InputType.SecondaryHeld) && itemPos != Vector2.Zero)
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
@@ -697,9 +705,11 @@ namespace Subsurface
if (TargetMovement == Vector2.Zero && inWater)
{
torso.body.SmoothRotate(0.2f * Dir);
torso.body.AngularVelocity -= torso.body.AngularVelocity * 0.1f;
torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f);
}
aiming = true;
}
else
{
+25 -15
View File
@@ -8,6 +8,7 @@ using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Subsurface.Networking;
namespace Subsurface
{
@@ -39,7 +40,7 @@ namespace Subsurface
//a movement vector that overrides targetmovement if trying to steer
//a character to the position sent by server in multiplayer mode
public Vector2 correctionMovement;
private Vector2 correctionMovement;
protected float floorY;
protected float surfaceY;
@@ -73,11 +74,7 @@ namespace Subsurface
}
set
{
if (float.IsNaN(value.X) || float.IsNaN(value.Y))
{
targetMovement = Vector2.Zero;
return;
}
if (!MathUtils.IsValid(value)) return;
targetMovement.X = MathHelper.Clamp(value.X, -3.0f, 3.0f);
targetMovement.Y = MathHelper.Clamp(value.Y, -3.0f, 3.0f);
}
@@ -408,10 +405,24 @@ namespace Subsurface
new Vector2(
-limbs[i].pullJoint.LocalAnchorA.X,
limbs[i].pullJoint.LocalAnchorA.Y);
}
}
}
public Vector2 GetCenterOfMass()
{
Vector2 centerOfMass = Vector2.Zero;
foreach (Limb limb in limbs)
{
centerOfMass += limb.Mass * limb.SimPosition;
}
centerOfMass /= Mass;
return centerOfMass;
}
/// <summary>
///
/// </summary>
@@ -567,10 +578,10 @@ namespace Subsurface
if (refLimb.body.TargetPosition == Vector2.Zero) return;
//if the limb is further away than resetdistance, all limbs are immediately snapped to their targetpositions
float resetDistance = 1.5f;
float resetDistance = NetConfig.ResetRagdollDistance;
//if the limb is closer than alloweddistance, limb positions aren't updated
float allowedDistance = 0.1f;
//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);
bool resetAll = (dist > resetDistance && character.LargeUpdateTimer == 1);
@@ -596,8 +607,7 @@ namespace Subsurface
}
else
{
correctionMovement = Vector2.Normalize(newMovement) * ((targetMovement == Vector2.Zero) ? 1.0f : targetMovement.Length());
correctionMovement = Vector2.Normalize(newMovement) * Math.Min(1.0f + dist, 3.0f);
}
}
@@ -627,13 +637,13 @@ namespace Subsurface
foreach (MapEntity e in MapEntity.mapEntityList)
{
Gap gap = e as Gap;
if (gap == null || gap.FlowTargetHull!=currentHull ||gap.FlowForce == Vector2.Zero) continue;
if (gap == null || gap.FlowTargetHull != currentHull || gap.FlowForce == Vector2.Zero) continue;
Vector2 gapPos = gap.SimPosition;
float dist = Vector2.Distance(limbPos, gapPos);
force += Vector2.Normalize(gap.FlowForce)*(Math.Max(gap.FlowForce.Length() - dist, 0.0f)/1000.0f);
force += Vector2.Normalize(gap.FlowForce) * (Math.Max(gap.FlowForce.Length() - dist, 0.0f) / 500.0f);
}
if (force.Length() > 20.0f) return force;