Files
LuaCsForBarotraumaEP/Subsurface/Characters/Limb.cs
2015-05-25 01:04:03 +03:00

363 lines
11 KiB
C#

using System;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Subsurface
{
public enum LimbType
{
None, LeftHand, RightHand, LeftArm, RightArm,
LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist
};
class Limb
{
private const float LimbDensity = 15;
private const float LimbAngularDamping = 7;
public readonly Character character;
//the physics body of the limb
public PhysicsBody body;
private Texture2D bodyShapeTexture;
private readonly int refJointIndex;
private readonly float steerForce;
private readonly bool doesFlip;
public Sprite sprite;
public bool inWater;
public FixedMouseJoint pullJoint;
public readonly LimbType type;
public readonly bool ignoreCollisions;
private float maxHealth;
private float damage;
private float bleeding;
public readonly float impactTolerance;
Sound hitSound;
//a timer for delaying when a hitsound/attacksound can be played again
public float soundTimer;
public const float SoundInterval = 0.2f;
public readonly Attack attack;
private Direction dir;
private Item wearingItem;
private Sprite wearingItemSprite;
private Vector2 animTargetPos;
public Texture2D BodyShapeTexture
{
get { return bodyShapeTexture; }
}
public bool DoesFlip
{
get { return doesFlip; }
}
public Vector2 SimPosition
{
get { return body.Position; }
}
public float Rotation
{
get { return body.Rotation; }
}
public Vector2 AnimTargetPos
{
get { return animTargetPos; }
}
public float SteerForce
{
get { return steerForce; }
}
public float Mass
{
get { return body.Mass; }
}
public bool Disabled { get; set; }
public Sound HitSound
{
get { return hitSound; }
}
public Vector2 LinearVelocity
{
get { return body.LinearVelocity; }
}
public float Dir
{
get { return ((dir == Direction.Left) ? -1.0f : 1.0f); }
set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; }
}
public int RefJointIndex
{
get { return refJointIndex; }
}
public float Damage
{
get { return damage; }
set
{
damage = Math.Max(value, 0.0f);
if (damage >=maxHealth) character.Kill();
}
}
public float Bleeding
{
get { return bleeding; }
set { bleeding = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public Item WearingItem
{
get { return wearingItem; }
set { wearingItem = value; }
}
public Sprite WearingItemSprite
{
get { return wearingItemSprite; }
set { wearingItemSprite = value; }
}
public Limb (Character character, XElement element)
{
this.character = character;
dir = Direction.Right;
doesFlip = ToolBox.GetAttributeBool(element, "flip", false);
body = new PhysicsBody(element);
if (ToolBox.GetAttributeBool(element, "ignorecollisions", false))
{
body.CollisionCategories = Category.None;
body.CollidesWith = Category.None;
ignoreCollisions = true;
}
else
{
//limbs don't collide with each other
body.CollisionCategories = Physics.CollisionCharacter;
body.CollidesWith = Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionMisc;
}
impactTolerance = ToolBox.GetAttributeFloat(element, "impacttolerance", 20.0f);
body.UserData = this;
refJointIndex = -1;
if (element.Attribute("type") != null)
{
type = (LimbType)Enum.Parse(typeof(LimbType), element.Attribute("type").Value, true);
Vector2 jointPos = ToolBox.GetAttributeVector2(element, "pullpos", Vector2.Zero);
jointPos = ConvertUnits.ToSimUnits(jointPos);
refJointIndex = ToolBox.GetAttributeInt(element, "refjoint", -1);
pullJoint = new FixedMouseJoint(body.FarseerBody, jointPos);
pullJoint.Enabled = false;
pullJoint.MaxForce = 150.0f * body.Mass;
Game1.world.AddJoint(pullJoint);
}
else
{
type = LimbType.None;
}
steerForce = ToolBox.GetAttributeFloat(element, "steerforce", 0.0f);
maxHealth = Math.Max(ToolBox.GetAttributeFloat(element, "health", 100.0f),1.0f);
body.BodyType = BodyType.Dynamic;
body.FarseerBody.AngularDamping = LimbAngularDamping;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString())
{
case "sprite":
string spritePath = subElement.Attribute("texture").Value;
if (character.info!=null)
spritePath = spritePath.Replace("[GENDER]", (character.info.gender == Gender.Female) ? "f" : "");
sprite = new Sprite(subElement, "", spritePath);
break;
case "attack":
attack = new Attack(subElement);
break;
case "sound":
hitSound = Sound.Load(ToolBox.GetAttributeString(subElement, "file", ""));
break;
}
}
}
public void Move(Vector2 pos, float amount, bool pullFromCenter=false)
{
Vector2 pullPos = body.Position;
if (pullJoint!=null && !pullFromCenter)
{
pullPos = pullJoint.WorldAnchorA;
}
animTargetPos = pos;
Vector2 vel = body.LinearVelocity;
Vector2 deltaPos = pos - pullPos;
deltaPos *= amount;
body.ApplyLinearImpulse((deltaPos - vel * 0.5f) * body.Mass, pullPos);
}
public void Update(float deltaTime)
{
if (LinearVelocity.X>100.0f)
{
DebugConsole.ThrowError("CHARACTER EXPLODED");
foreach (Limb limb in character.animController.limbs)
{
limb.body.ResetDynamics();
limb.body.SetTransform(body.Position, 0.0f);
}
}
if (inWater)
{
//buoyancy
Vector2 buoyancy = new Vector2(0, Mass * 9.6f);
//drag
Vector2 velDir = Vector2.Normalize(LinearVelocity);
Vector2 line = new Vector2((float)Math.Cos(body.Rotation), (float)Math.Sin(body.Rotation));
line *= ConvertUnits.ToSimUnits(sprite.size.Y);
Vector2 normal = new Vector2(-line.Y, line.X);
normal = Vector2.Normalize(-normal);
float dragDot = Vector2.Dot(normal, velDir);
Vector2 dragForce = Vector2.Zero;
if (dragDot > 0)
{
float vel = LinearVelocity.Length();
float drag = dragDot * vel * vel
* ConvertUnits.ToSimUnits(sprite.size.Y);
dragForce = drag * -velDir;
if (dragForce.Length() > 100.0f) { }
}
body.ApplyForce(dragForce + buoyancy);
body.ApplyTorque(body.AngularVelocity * body.Mass * -0.05f);
}
if (character.IsDead) return;
soundTimer -= deltaTime;
if (ToolBox.RandomFloat(0.0f, 1000.0f) < Bleeding)
{
Game1.particleManager.CreateParticle(SimPosition,
MathHelper.Pi,
ToolBox.RandomFloat(0.0f, 0.0f), !inWater ? "blood" : "waterblood");
}
}
public void Draw(SpriteBatch spriteBatch, bool debugDraw)
{
Color color = new Color(1.0f, 1.0f - damage / maxHealth, 1.0f - damage / maxHealth);
body.Dir = Dir;
body.Draw(spriteBatch, sprite, color);
if (wearingItem != null)
{
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
wearingItemSprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color,
-body.DrawRotation,
1.0f, spriteEffect);
}
if (!debugDraw) return;
if (pullJoint!=null)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(pullJoint.WorldAnchorB);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Red, true);
}
if (bodyShapeTexture == null)
{
switch (body.bodyShape)
{
case PhysicsBody.Shape.Rectangle:
bodyShapeTexture = GUI.CreateRectangle(
(int)ConvertUnits.ToDisplayUnits(body.width),
(int)ConvertUnits.ToDisplayUnits(body.height));
break;
case PhysicsBody.Shape.Capsule:
bodyShapeTexture = GUI.CreateCapsule(
(int)ConvertUnits.ToDisplayUnits(body.radius),
(int)ConvertUnits.ToDisplayUnits(body.height));
break;
case PhysicsBody.Shape.Circle:
bodyShapeTexture = GUI.CreateCircle((int)ConvertUnits.ToDisplayUnits(body.radius));
break;
}
}
spriteBatch.Draw(
bodyShapeTexture,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
null,
Color.White,
-body.DrawRotation,
new Vector2(bodyShapeTexture.Width / 2, bodyShapeTexture.Height / 2), 1.0f, SpriteEffects.None, 0.0f);
}
public void Remove()
{
sprite.Remove();
body.Remove();
if (hitSound!=null) hitSound.Remove();
}
}
}