Merge remote-tracking branch 'refs/remotes/origin/master'
Conflicts: Subsurface/Barotrauma.csproj Subsurface/Source/Characters/AI/EnemyAIController.cs Subsurface/Source/Characters/AICharacter.cs Subsurface/Source/Characters/Character.cs Subsurface/Source/Items/Components/DockingPort.cs Subsurface/Source/Items/Components/Door.cs Subsurface/Source/Items/Item.cs Subsurface/Source/Networking/GameClient.cs Subsurface/Source/Networking/GameServer.cs Subsurface/Source/Physics/PhysicsBody.cs
This commit is contained in:
@@ -10,6 +10,8 @@ namespace Barotrauma
|
||||
public enum AiState { None, Attack, GoTo, Escape }
|
||||
public enum SteeringState { Wander, Seek, Escape }
|
||||
|
||||
public bool Enabled;
|
||||
|
||||
public readonly Character Character;
|
||||
|
||||
protected AiState state;
|
||||
@@ -51,6 +53,8 @@ namespace Barotrauma
|
||||
public AIController (Character c)
|
||||
{
|
||||
Character = c;
|
||||
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
public virtual void DebugDraw(SpriteBatch spriteBatch) { }
|
||||
|
||||
@@ -218,24 +218,27 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//steeringManager.SteeringAvoid(deltaTime, 1.0f);
|
||||
steeringManager.SteeringSeek(attackSimPosition);
|
||||
|
||||
|
||||
Limb attackLimb = attackingLimb;
|
||||
//check if any of the limbs is close enough to attack the target
|
||||
if (attackingLimb == null)
|
||||
{
|
||||
foreach (Limb limb in Character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.attack==null || limb.attack.Type == AttackType.None) continue;
|
||||
if (limb.attack==null) continue;
|
||||
attackLimb = limb;
|
||||
|
||||
if (ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackSimPosition)) > limb.attack.Range) continue;
|
||||
|
||||
attackingLimb = limb;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition);
|
||||
|
||||
if (attackLimb != null)
|
||||
{
|
||||
steeringManager.SteeringSeek(attackSimPosition - (attackLimb.SimPosition - SimPosition));
|
||||
if (attackingLimb != null) UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCoolDown(Vector2 attackPosition, float deltaTime)
|
||||
@@ -330,84 +333,15 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackPosition)
|
||||
{
|
||||
IDamageable damageTarget = null;
|
||||
var damageTarget = (wallAttackPos != Vector2.Zero && targetEntity != null) ? targetEntity : selectedAiTarget.Entity as IDamageable;
|
||||
|
||||
float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackPosition));
|
||||
limb.UpdateAttack(deltaTime, attackPosition, damageTarget);
|
||||
|
||||
switch (limb.attack.Type)
|
||||
{
|
||||
case AttackType.PinchCW:
|
||||
case AttackType.PinchCCW:
|
||||
|
||||
float dir = (limb.attack.Type == AttackType.PinchCW) ? 1.0f : -1.0f;
|
||||
|
||||
damageTarget = (wallAttackPos != Vector2.Zero && targetEntity != null) ? targetEntity : selectedAiTarget.Entity as IDamageable;
|
||||
|
||||
attackTimer += deltaTime*0.05f;
|
||||
|
||||
if (damageTarget == null)
|
||||
{
|
||||
attackTimer = limb.attack.Duration;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dist < limb.attack.Range * 0.5f)
|
||||
{
|
||||
attackTimer += deltaTime;
|
||||
limb.body.ApplyTorque(limb.Mass * 50.0f * Character.AnimController.Dir * dir);
|
||||
|
||||
if (attackTimer >= limb.attack.Duration)
|
||||
{
|
||||
limb.attack.DoDamage(Character, damageTarget, limb.WorldPosition, 1.0f, (limb.soundTimer <= 0.0f));
|
||||
|
||||
limb.soundTimer = Limb.SoundInterval;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 diff = attackPosition - limb.SimPosition;
|
||||
if (diff.LengthSquared() > 0.00001f)
|
||||
{
|
||||
limb.body.ApplyLinearImpulse(limb.Mass * 10.0f *
|
||||
Vector2.Normalize(attackPosition - limb.SimPosition));
|
||||
}
|
||||
|
||||
steeringManager.SteeringSeek(attackPosition + (limb.SimPosition-SimPosition), 5.0f);
|
||||
|
||||
break;
|
||||
case AttackType.Hit:
|
||||
damageTarget = (wallAttackPos != Vector2.Zero && targetEntity != null) ? targetEntity : selectedAiTarget.Entity as IDamageable;
|
||||
|
||||
if (damageTarget == null)
|
||||
{
|
||||
attackTimer = limb.attack.Duration;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dist < limb.attack.Range)
|
||||
{
|
||||
attackTimer += deltaTime;
|
||||
limb.body.ApplyForce(limb.Mass * limb.attack.Force * Vector2.Normalize(attackPosition - limb.SimPosition));
|
||||
|
||||
if (damageTarget is Character && dist<limb.attack.Range*0.5f)
|
||||
{
|
||||
limb.attack.DoDamage(Character, damageTarget, limb.WorldPosition, deltaTime, (limb.soundTimer <= 0.0f));
|
||||
limb.soundTimer = Limb.SoundInterval;
|
||||
}
|
||||
}
|
||||
|
||||
steeringManager.SteeringSeek(attackPosition + (limb.SimPosition-SimPosition), 5.0f);
|
||||
break;
|
||||
default:
|
||||
attackTimer = limb.attack.Duration;
|
||||
break;
|
||||
}
|
||||
|
||||
if (attackTimer >= limb.attack.Duration)
|
||||
if (limb.AttackTimer >= limb.attack.Duration)
|
||||
{
|
||||
wallAttackPos = Vector2.Zero;
|
||||
attackTimer = 0.0f;
|
||||
if (Vector2.Distance(limb.SimPosition, attackPosition)<5.0) coolDownTimer = attackCoolDown;
|
||||
|
||||
limb.AttackTimer = 0.0f;
|
||||
if (Vector2.Distance(limb.SimPosition, attackPosition)<5.0) coolDownTimer = attackCoolDown;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Barotrauma
|
||||
|
||||
if (isDead || health <= 0.0f) return;
|
||||
|
||||
if (Controlled == this) return;
|
||||
if (Controlled == this || !aiController.Enabled) return;
|
||||
|
||||
if (soundTimer > 0)
|
||||
{
|
||||
|
||||
@@ -338,13 +338,13 @@ namespace Barotrauma
|
||||
|
||||
if (!onGround || (LowestLimb.SimPosition.Y - floorY > 0.5f && stairs == null)) return;
|
||||
|
||||
float? ceilingY = null;
|
||||
if (Submarine.PickBody(head.SimPosition, head.SimPosition + Vector2.UnitY, null, Physics.CollisionWall)!=null)
|
||||
{
|
||||
ceilingY = Submarine.LastPickedPosition.Y;
|
||||
//float? ceilingY = null;
|
||||
//if (Submarine.PickBody(head.SimPosition, head.SimPosition + Vector2.UnitY, null, Physics.CollisionWall)!=null)
|
||||
//{
|
||||
// ceilingY = Submarine.LastPickedPosition.Y;
|
||||
|
||||
if (ceilingY - floorY < HeadPosition) Crouching = true;
|
||||
}
|
||||
// if (ceilingY - floorY < HeadPosition) Crouching = true;
|
||||
//}
|
||||
|
||||
getUpSpeed = getUpSpeed * Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f);
|
||||
|
||||
@@ -456,24 +456,20 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
float movementFactor = (movement.X / 4.0f) * movement.X * Math.Sign(movement.X);
|
||||
|
||||
|
||||
|
||||
//MoveLimb(leftFoot, footPos, 2.5f);
|
||||
|
||||
for (int i = -1; i < 2; i+=2 )
|
||||
for (int i = -1; i < 2; i += 2)
|
||||
{
|
||||
Vector2 footPos = new Vector2(
|
||||
Crouching ? waist.SimPosition.X + Math.Sign(stepSize.X*i)*Dir*0.3f : waist.SimPosition.X,
|
||||
colliderPos.Y - 0.2f);
|
||||
Vector2 footPos = new Vector2(
|
||||
Crouching ? waist.SimPosition.X + Math.Sign(stepSize.X * i) * Dir * 0.3f : GetCenterOfMass().X,
|
||||
colliderPos.Y - 0.1f);
|
||||
|
||||
var foot = i == -1 ? rightFoot : leftFoot;
|
||||
|
||||
MoveLimb(foot, footPos, Math.Abs(foot.SimPosition.X - footPos.X)*50.0f);
|
||||
MoveLimb(foot, footPos, Math.Abs(foot.SimPosition.X - footPos.X) * 100.0f, true);
|
||||
}
|
||||
|
||||
leftFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 5.0f);
|
||||
rightFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 5.0f);
|
||||
leftFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 50.0f);
|
||||
rightFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 50.0f);
|
||||
|
||||
if (!rightHand.Disabled)
|
||||
{
|
||||
@@ -1183,7 +1179,7 @@ namespace Barotrauma
|
||||
|
||||
Vector2 position = limb.SimPosition;
|
||||
|
||||
if (!limb.pullJoint.Enabled && mirror)
|
||||
if ((limb.pullJoint==null || !limb.pullJoint.Enabled) && mirror)
|
||||
{
|
||||
difference = limb.body.SimPosition - torso.SimPosition;
|
||||
difference = Vector2.Transform(difference, torsoTransform);
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace Barotrauma
|
||||
|
||||
protected float strongestImpact;
|
||||
|
||||
float headPosition, headAngle;
|
||||
float torsoPosition, torsoAngle;
|
||||
public float headPosition, headAngle;
|
||||
public float torsoPosition, torsoAngle;
|
||||
|
||||
protected double onFloorTimer;
|
||||
|
||||
@@ -231,8 +231,7 @@ namespace Barotrauma
|
||||
byte ID = Convert.ToByte(subElement.Attribute("id").Value);
|
||||
|
||||
Limb limb = new Limb(character, subElement, scale);
|
||||
|
||||
|
||||
|
||||
limb.body.FarseerBody.OnCollision += OnLimbCollision;
|
||||
|
||||
Limbs[ID] = limb;
|
||||
@@ -240,38 +239,7 @@ namespace Barotrauma
|
||||
if (!limbDictionary.ContainsKey(limb.type)) limbDictionary.Add(limb.type, limb);
|
||||
break;
|
||||
case "joint":
|
||||
Byte limb1ID = Convert.ToByte(subElement.Attribute("limb1").Value);
|
||||
Byte limb2ID = Convert.ToByte(subElement.Attribute("limb2").Value);
|
||||
|
||||
Vector2 limb1Pos = ToolBox.GetAttributeVector2(subElement, "limb1anchor", Vector2.Zero) * scale;
|
||||
limb1Pos = ConvertUnits.ToSimUnits(limb1Pos);
|
||||
|
||||
Vector2 limb2Pos = ToolBox.GetAttributeVector2(subElement, "limb2anchor", Vector2.Zero) * scale;
|
||||
limb2Pos = ConvertUnits.ToSimUnits(limb2Pos);
|
||||
|
||||
RevoluteJoint joint = new RevoluteJoint(Limbs[limb1ID].body.FarseerBody, Limbs[limb2ID].body.FarseerBody, limb1Pos, limb2Pos);
|
||||
|
||||
joint.CollideConnected = false;
|
||||
|
||||
if (subElement.Attribute("lowerlimit")!=null)
|
||||
{
|
||||
joint.LimitEnabled = true;
|
||||
joint.LowerLimit = float.Parse(subElement.Attribute("lowerlimit").Value) * ((float)Math.PI / 180.0f);
|
||||
joint.UpperLimit = float.Parse(subElement.Attribute("upperlimit").Value) * ((float)Math.PI / 180.0f);
|
||||
}
|
||||
|
||||
joint.MotorEnabled = true;
|
||||
joint.MaxMotorTorque = 0.25f;
|
||||
|
||||
GameMain.World.AddJoint(joint);
|
||||
|
||||
for (int i = 0; i < limbJoints.Length; i++ )
|
||||
{
|
||||
if (limbJoints[i] != null) continue;
|
||||
|
||||
limbJoints[i] = joint;
|
||||
break;
|
||||
}
|
||||
AddJoint(subElement, scale);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -308,6 +276,58 @@ namespace Barotrauma
|
||||
|
||||
FindLowestLimb();
|
||||
}
|
||||
|
||||
public void AddJoint(XElement subElement, float scale = 1.0f)
|
||||
{
|
||||
byte limb1ID = Convert.ToByte(subElement.Attribute("limb1").Value);
|
||||
byte limb2ID = Convert.ToByte(subElement.Attribute("limb2").Value);
|
||||
|
||||
Vector2 limb1Pos = ToolBox.GetAttributeVector2(subElement, "limb1anchor", Vector2.Zero) * scale;
|
||||
limb1Pos = ConvertUnits.ToSimUnits(limb1Pos);
|
||||
|
||||
Vector2 limb2Pos = ToolBox.GetAttributeVector2(subElement, "limb2anchor", Vector2.Zero) * scale;
|
||||
limb2Pos = ConvertUnits.ToSimUnits(limb2Pos);
|
||||
|
||||
RevoluteJoint joint = new RevoluteJoint(Limbs[limb1ID].body.FarseerBody, Limbs[limb2ID].body.FarseerBody, limb1Pos, limb2Pos);
|
||||
|
||||
joint.CollideConnected = false;
|
||||
|
||||
if (subElement.Attribute("lowerlimit") != null)
|
||||
{
|
||||
joint.LimitEnabled = true;
|
||||
joint.LowerLimit = float.Parse(subElement.Attribute("lowerlimit").Value) * ((float)Math.PI / 180.0f);
|
||||
joint.UpperLimit = float.Parse(subElement.Attribute("upperlimit").Value) * ((float)Math.PI / 180.0f);
|
||||
}
|
||||
|
||||
joint.MotorEnabled = true;
|
||||
joint.MaxMotorTorque = 0.25f;
|
||||
|
||||
GameMain.World.AddJoint(joint);
|
||||
|
||||
for (int i = 0; i < limbJoints.Length; i++)
|
||||
{
|
||||
if (limbJoints[i] != null) continue;
|
||||
|
||||
limbJoints[i] = joint;
|
||||
return;
|
||||
}
|
||||
|
||||
Array.Resize(ref limbJoints, limbJoints.Length + 1);
|
||||
limbJoints[limbJoints.Length - 1] = joint;
|
||||
|
||||
}
|
||||
|
||||
public void AddLimb(Limb limb)
|
||||
{
|
||||
limb.body.FarseerBody.OnCollision += OnLimbCollision;
|
||||
|
||||
Array.Resize(ref Limbs, Limbs.Length + 1);
|
||||
|
||||
Limbs[Limbs.Length-1] = limb;
|
||||
|
||||
Mass += limb.Mass;
|
||||
if (!limbDictionary.ContainsKey(limb.type)) limbDictionary.Add(limb.type, limb);
|
||||
}
|
||||
|
||||
public bool OnLimbCollision(Fixture f1, Fixture f2, Contact contact)
|
||||
{
|
||||
|
||||
@@ -2,22 +2,18 @@
|
||||
using Barotrauma.Particles;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
enum CauseOfDeath
|
||||
{
|
||||
Damage, Bloodloss, Pressure, Suffocation, Drowning, Burn, Disconnected
|
||||
Damage, Bloodloss, Pressure, Suffocation, Drowning, Burn, Husk, Disconnected
|
||||
}
|
||||
|
||||
public enum DamageType { None, Blunt, Slash, Burn }
|
||||
|
||||
public enum AttackType
|
||||
{
|
||||
None, PinchCW, PinchCCW, Hit
|
||||
}
|
||||
|
||||
struct AttackResult
|
||||
{
|
||||
public readonly float Damage;
|
||||
@@ -36,8 +32,6 @@ namespace Barotrauma
|
||||
|
||||
class Attack
|
||||
{
|
||||
|
||||
public readonly AttackType Type;
|
||||
public readonly float Range;
|
||||
public readonly float Duration;
|
||||
|
||||
@@ -47,8 +41,12 @@ namespace Barotrauma
|
||||
private readonly float damage;
|
||||
private readonly float bleedingDamage;
|
||||
|
||||
private readonly List<StatusEffect> statusEffects;
|
||||
|
||||
public readonly float Force;
|
||||
|
||||
public readonly float Torque;
|
||||
|
||||
public readonly float TargetForce;
|
||||
|
||||
private Sound sound;
|
||||
@@ -74,23 +72,8 @@ namespace Barotrauma
|
||||
return (Duration == 0.0f) ? structureDamage : structureDamage * deltaTime;
|
||||
}
|
||||
|
||||
|
||||
//public Attack(AttackType type, float range,)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
public Attack(XElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
Type = (AttackType)Enum.Parse(typeof(AttackType), element.Attribute("type").Value, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Type = AttackType.None;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DamageType = (DamageType)Enum.Parse(typeof(DamageType), ToolBox.GetAttributeString(element, "damagetype", "None"), true);
|
||||
@@ -106,9 +89,10 @@ namespace Barotrauma
|
||||
bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f);
|
||||
|
||||
Force = ToolBox.GetAttributeFloat(element,"force", 0.0f);
|
||||
|
||||
TargetForce = ToolBox.GetAttributeFloat(element, "targetforce", 0.0f);
|
||||
|
||||
Torque = ToolBox.GetAttributeFloat(element, "torque", 0.0f);
|
||||
|
||||
Stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f);
|
||||
|
||||
string soundPath = ToolBox.GetAttributeString(element, "sound", "");
|
||||
@@ -123,10 +107,20 @@ namespace Barotrauma
|
||||
|
||||
priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f);
|
||||
|
||||
statusEffects = new List<StatusEffect>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "particleemitter") continue;
|
||||
particleEmitterPrefab = new ParticleEmitterPrefab(subElement);
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "particleemitter":
|
||||
particleEmitterPrefab = new ParticleEmitterPrefab(subElement);
|
||||
break;
|
||||
case "statuseffect":
|
||||
statusEffects.Add(StatusEffect.Load(subElement));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,9 +136,24 @@ namespace Barotrauma
|
||||
{
|
||||
sound.Play(1.0f, 500.0f, worldPosition);
|
||||
}
|
||||
|
||||
var attackResult = target.AddDamage(attacker, worldPosition, this, deltaTime, playSound);
|
||||
|
||||
return target.AddDamage(attacker, worldPosition, this, deltaTime, playSound);
|
||||
var effectType = attackResult.Damage > 0.0f ? ActionType.OnUse : ActionType.OnFailure;
|
||||
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
if (effect.Targets.HasFlag(StatusEffect.TargetType.This) && attacker is Character)
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, (Character)attacker, (Character)attacker);
|
||||
}
|
||||
if (effect.Targets.HasFlag(StatusEffect.TargetType.Character) && target is Character)
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, (Character)target, (Character)target);
|
||||
}
|
||||
}
|
||||
|
||||
return attackResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,8 @@ namespace Barotrauma
|
||||
//which AIstate each sound is for
|
||||
private AIController.AiState[] soundStates;
|
||||
|
||||
private float attackCoolDown;
|
||||
|
||||
public Entity ViewTarget
|
||||
{
|
||||
get;
|
||||
@@ -248,6 +250,7 @@ namespace Barotrauma
|
||||
public bool NeedsAir
|
||||
{
|
||||
get { return needsAir; }
|
||||
set { needsAir = value; }
|
||||
}
|
||||
|
||||
public float Oxygen
|
||||
@@ -298,6 +301,38 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public HuskInfection huskInfection;
|
||||
public float HuskInfectionState
|
||||
{
|
||||
get
|
||||
{
|
||||
return huskInfection == null ? 0.0f : huskInfection.IncubationTimer;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (ConfigPath != humanConfigFile) return;
|
||||
|
||||
if (value <= 0.0f)
|
||||
{
|
||||
if (huskInfection != null && huskInfection.State == HuskInfection.InfectionState.Active) return;
|
||||
huskInfection = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (huskInfection == null) huskInfection = new HuskInfection(this);
|
||||
huskInfection.IncubationTimer = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanSpeak
|
||||
{
|
||||
get
|
||||
{
|
||||
return !IsUnconscious && Stun <= 0.0f && (huskInfection == null || huskInfection.CanSpeak);
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoesBleed
|
||||
{
|
||||
get;
|
||||
@@ -310,7 +345,6 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public float PressureTimer
|
||||
{
|
||||
get;
|
||||
@@ -474,13 +508,16 @@ namespace Barotrauma
|
||||
|
||||
Properties = ObjectProperty.GetProperties(this);
|
||||
|
||||
if (file == humanConfigFile)
|
||||
Info = characterInfo;
|
||||
if (file == humanConfigFile && characterInfo == null)
|
||||
{
|
||||
Info = characterInfo == null ? new CharacterInfo(file) : characterInfo;
|
||||
Info = new CharacterInfo(file);
|
||||
}
|
||||
|
||||
XDocument doc = ToolBox.TryLoadXml(file);
|
||||
if (doc == null || doc.Root == null) return;
|
||||
|
||||
|
||||
|
||||
SpeciesName = ToolBox.GetAttributeString(doc.Root, "name", "Unknown");
|
||||
|
||||
@@ -696,6 +733,49 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (attackCoolDown >0.0f)
|
||||
{
|
||||
attackCoolDown -= deltaTime;
|
||||
}
|
||||
else if (IsKeyDown(InputType.Attack))
|
||||
{
|
||||
var attackLimb = AnimController.Limbs.FirstOrDefault(l => l.attack != null);
|
||||
|
||||
if (attackLimb != null)
|
||||
{
|
||||
Vector2 attackPos =
|
||||
attackLimb.SimPosition + Vector2.Normalize(cursorPosition - attackLimb.Position) * ConvertUnits.ToSimUnits(attackLimb.attack.Range);
|
||||
|
||||
var body = Submarine.PickBody(
|
||||
attackLimb.SimPosition,
|
||||
attackPos,
|
||||
AnimController.Limbs.Select(l => l.body.FarseerBody).ToList(),
|
||||
Physics.CollisionCharacter | Physics.CollisionWall);
|
||||
|
||||
IDamageable attackTarget = null;
|
||||
if (body != null)
|
||||
{
|
||||
if (body.UserData is IDamageable)
|
||||
{
|
||||
attackTarget = (IDamageable)body.UserData;
|
||||
}
|
||||
else if (body.UserData is Limb)
|
||||
{
|
||||
attackTarget = ((Limb)body.UserData).character;
|
||||
}
|
||||
attackPos = Submarine.LastPickedPosition;
|
||||
}
|
||||
|
||||
attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget);
|
||||
|
||||
if (attackLimb.AttackTimer > attackLimb.attack.Duration)
|
||||
{
|
||||
attackLimb.AttackTimer = 0.0f;
|
||||
attackCoolDown = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < selectedItems.Length; i++ )
|
||||
{
|
||||
if (selectedItems[i] == null) continue;
|
||||
@@ -784,6 +864,14 @@ namespace Barotrauma
|
||||
return owner.isDead || owner.IsUnconscious || owner.Stun > 0.0f || owner.LockHands;
|
||||
}
|
||||
|
||||
if (inventory.Owner is Item)
|
||||
{
|
||||
var owner = (Item)inventory.Owner;
|
||||
if (!CanAccessItem(owner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -791,10 +879,22 @@ namespace Barotrauma
|
||||
{
|
||||
if (item.ParentInventory != null)
|
||||
{
|
||||
if (!CanAccessInventory(item.ParentInventory)) return false;
|
||||
return CanAccessInventory(item.ParentInventory);
|
||||
}
|
||||
|
||||
return true;
|
||||
float maxDist = item.PickDistance * 1.2f;
|
||||
if (maxDist <= 0.01f)
|
||||
{
|
||||
maxDist = 150.0f;
|
||||
}
|
||||
|
||||
if (Vector2.Distance(WorldPosition, item.WorldPosition) < maxDist ||
|
||||
item.IsInsideTrigger(WorldPosition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return item.GetComponent<Items.Components.Ladder>() != null;
|
||||
}
|
||||
|
||||
private Item FindClosestItem(Vector2 mouseSimPos, out float distance)
|
||||
@@ -859,7 +959,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Limb limb in selectedCharacter.AnimController.Limbs)
|
||||
{
|
||||
limb.pullJoint.Enabled = false;
|
||||
if (limb.pullJoint != null) limb.pullJoint.Enabled = false;
|
||||
}
|
||||
|
||||
selectedCharacter = null;
|
||||
@@ -923,7 +1023,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!LockHands)
|
||||
{
|
||||
//find the closest item if selectkey has been hit, or if the Character is being
|
||||
@@ -1014,9 +1113,9 @@ namespace Barotrauma
|
||||
// new Character(NewCharacterQueue.Dequeue(), Vector2.Zero);
|
||||
//}
|
||||
|
||||
foreach (Character c in CharacterList)
|
||||
for (int i = 0; i<CharacterList.Count; i++)
|
||||
{
|
||||
c.Update(cam, deltaTime);
|
||||
CharacterList[i].Update(cam, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1039,6 +1138,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (huskInfection != null) huskInfection.Update(deltaTime, this);
|
||||
|
||||
if (isDead) return;
|
||||
|
||||
if (networkUpdateSent)
|
||||
@@ -1084,7 +1185,7 @@ namespace Barotrauma
|
||||
Lights.LightManager.ViewTarget = this;
|
||||
CharacterHUD.Update(deltaTime, this);
|
||||
}
|
||||
|
||||
|
||||
if (IsUnconscious)
|
||||
{
|
||||
UpdateUnconscious(deltaTime);
|
||||
@@ -1096,7 +1197,12 @@ namespace Barotrauma
|
||||
ControlLocalPlayer(deltaTime, cam);
|
||||
}
|
||||
|
||||
if (controlled == this || !(this is AICharacter)) Control(deltaTime, cam);
|
||||
if (controlled == this ||
|
||||
!(this is AICharacter) ||
|
||||
!((AICharacter)this).AIController.Enabled)
|
||||
{
|
||||
Control(deltaTime, cam);
|
||||
}
|
||||
|
||||
if (selectedCharacter != null && AnimController.Anim == AnimController.Animation.CPR)
|
||||
{
|
||||
@@ -1323,10 +1429,18 @@ namespace Barotrauma
|
||||
if (GameMain.NetworkMember != null && controlled != this) return;
|
||||
}
|
||||
|
||||
Vector2 centerOfMass = AnimController.GetCenterOfMass();
|
||||
|
||||
health = minHealth;
|
||||
|
||||
BreakJoints();
|
||||
|
||||
Kill(CauseOfDeath.Pressure, isNetworkMessage);
|
||||
}
|
||||
|
||||
public void BreakJoints()
|
||||
{
|
||||
Vector2 centerOfMass = AnimController.GetCenterOfMass();
|
||||
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
limb.AddDamage(limb.SimPosition, DamageType.Blunt, 500.0f, 0.0f, false);
|
||||
@@ -1334,28 +1448,27 @@ namespace Barotrauma
|
||||
Vector2 diff = centerOfMass - limb.SimPosition;
|
||||
if (diff == Vector2.Zero) continue;
|
||||
limb.body.ApplyLinearImpulse(diff * 10.0f);
|
||||
// limb.Damage = 100.0f;
|
||||
// limb.Damage = 100.0f;
|
||||
}
|
||||
|
||||
SoundPlayer.PlayDamageSound(DamageSoundType.Implode, 50.0f, AnimController.RefLimb.body);
|
||||
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Particle p = GameMain.ParticleManager.CreateParticle("waterblood",
|
||||
ConvertUnits.ToDisplayUnits(centerOfMass) + Rand.Vector(5.0f),
|
||||
Rand.Vector(10.0f));
|
||||
if (p!=null) p.Size *= 2.0f;
|
||||
if (p != null) p.Size *= 2.0f;
|
||||
|
||||
GameMain.ParticleManager.CreateParticle("bubbles",
|
||||
ConvertUnits.ToDisplayUnits(centerOfMass) + Rand.Vector(5.0f),
|
||||
new Vector2(Rand.Range(-50f, 50f), Rand.Range(-100f,50f)));
|
||||
new Vector2(Rand.Range(-50f, 50f), Rand.Range(-100f, 50f)));
|
||||
}
|
||||
|
||||
foreach (var joint in AnimController.limbJoints)
|
||||
{
|
||||
joint.LimitEnabled = false;
|
||||
}
|
||||
Kill(CauseOfDeath.Pressure, isNetworkMessage);
|
||||
}
|
||||
|
||||
public void Kill(CauseOfDeath causeOfDeath, bool isNetworkMessage = false)
|
||||
|
||||
@@ -197,6 +197,7 @@ namespace Barotrauma
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.Kill(Character.Controlled.CauseOfDeath);
|
||||
Character.Controlled = null;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -232,11 +233,12 @@ namespace Barotrauma
|
||||
healthBar.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
int bloodDropCount = (int)Math.Floor(character.Bleeding);
|
||||
bloodDropCount = MathHelper.Clamp(bloodDropCount, 0, 5);
|
||||
for (int i = 1; i < bloodDropCount; i++)
|
||||
float bloodDropCount = character.Bleeding;
|
||||
bloodDropCount = MathHelper.Clamp(bloodDropCount, 0.0f, 5.0f);
|
||||
for (int i = 0; i < Math.Ceiling(bloodDropCount); i++)
|
||||
{
|
||||
spriteBatch.Draw(statusIcons.Texture, new Vector2(5.0f + 20 * i, healthBar.Rect.Y - 20.0f), new Rectangle(39, 3, 15, 19), Color.White * 0.8f);
|
||||
float alpha = MathHelper.Clamp(bloodDropCount-i, 0.2f, 1.0f);
|
||||
spriteBatch.Draw(statusIcons.Texture, new Vector2(25.0f + 20 * i, healthBar.Rect.Y - 20.0f), new Rectangle(39, 3, 15, 19), Color.White * alpha);
|
||||
}
|
||||
|
||||
float pressureFactor = (character.AnimController.CurrentHull == null) ?
|
||||
|
||||
186
Subsurface/Source/Characters/HuskInfection.cs
Normal file
186
Subsurface/Source/Characters/HuskInfection.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class HuskInfection
|
||||
{
|
||||
public enum InfectionState
|
||||
{
|
||||
Dormant, Transition, Active
|
||||
}
|
||||
|
||||
const float IncubationDuration = 300.0f;
|
||||
|
||||
private InfectionState state;
|
||||
|
||||
private float incubationTimer;
|
||||
public float IncubationTimer
|
||||
{
|
||||
get { return incubationTimer; }
|
||||
set
|
||||
{
|
||||
incubationTimer = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public InfectionState State
|
||||
{
|
||||
get { return state; }
|
||||
}
|
||||
|
||||
public bool CanSpeak
|
||||
{
|
||||
get { return IncubationTimer < 0.5f; }
|
||||
}
|
||||
|
||||
public HuskInfection(Character character)
|
||||
{
|
||||
character.OnDeath += CharacterDead;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime, Character character)
|
||||
{
|
||||
if (IncubationTimer < 0.5f)
|
||||
{
|
||||
UpdateDormantState(deltaTime, character);
|
||||
}
|
||||
else if (IncubationTimer < 1.0f)
|
||||
{
|
||||
UpdateTransitionState(deltaTime, character);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateActiveState(deltaTime, character);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDormantState(float deltaTime, Character character)
|
||||
{
|
||||
float prevTimer = IncubationTimer;
|
||||
|
||||
state = InfectionState.Dormant;
|
||||
|
||||
IncubationTimer += deltaTime / IncubationDuration;
|
||||
|
||||
if (Character.Controlled != character) return;
|
||||
|
||||
if (prevTimer % 0.1f > 0.05f && IncubationTimer % 0.1f < 0.05f)
|
||||
{
|
||||
GUI.AddMessage(InfoTextManager.GetInfoText("HuskDormant"), Color.Red, 4.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTransitionState(float deltaTime, Character character)
|
||||
{
|
||||
IncubationTimer += deltaTime / IncubationDuration;
|
||||
|
||||
if (state == InfectionState.Dormant && Character.Controlled == character)
|
||||
{
|
||||
new GUIMessageBox("", InfoTextManager.GetInfoText("HuskCantSpeak"));
|
||||
}
|
||||
|
||||
state = InfectionState.Transition;
|
||||
}
|
||||
|
||||
private void UpdateActiveState(float deltaTime, Character character)
|
||||
{
|
||||
if (state != InfectionState.Active)
|
||||
{
|
||||
if (Character.Controlled==character) new GUIMessageBox("", InfoTextManager.GetInfoText("HuskActivate"));
|
||||
ActivateHusk(character);
|
||||
state = InfectionState.Active;
|
||||
}
|
||||
|
||||
character.AddDamage(CauseOfDeath.Husk, 0.5f*deltaTime, null);
|
||||
|
||||
if (character.AnimController.limbJoints[0].LimitEnabled &&
|
||||
(character.AnimController.CurrentHull == null ||
|
||||
character.AnimController.CurrentHull.LethalPressure > 50.0f))
|
||||
{
|
||||
character.BreakJoints();
|
||||
character.AnimController.limbJoints.Last().LimitEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ActivateHusk(Character character)
|
||||
{
|
||||
character.NeedsAir = false;
|
||||
AttachHuskAppendage(character);
|
||||
}
|
||||
|
||||
private void AttachHuskAppendage(Character character)
|
||||
{
|
||||
XDocument doc = ToolBox.TryLoadXml(Path.Combine("Content", "Characters", "Human", "huskappendage.xml"));
|
||||
if (doc == null || doc.Root == null) return;
|
||||
|
||||
var limbElement = doc.Root.Element("limb");
|
||||
if (limbElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in huskappendage.xml - limb element not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var jointElement = doc.Root.Element("joint");
|
||||
if (jointElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in huskappendage.xml - joint element not found");
|
||||
return;
|
||||
}
|
||||
|
||||
character.StartStun(0.5f);
|
||||
if (character.AnimController.Dir < 1.0f)
|
||||
{
|
||||
character.AnimController.Flip();
|
||||
}
|
||||
|
||||
var torso = character.AnimController.GetLimb(LimbType.Torso);
|
||||
torso.body.SetTransform(torso.SimPosition, 0.0f);
|
||||
|
||||
var newLimb = new Limb(character, limbElement);
|
||||
newLimb.body.Submarine = character.Submarine;
|
||||
newLimb.body.SetTransform(torso.SimPosition, 0.0f);
|
||||
|
||||
character.AnimController.AddLimb(newLimb);
|
||||
character.AnimController.AddJoint(jointElement);
|
||||
}
|
||||
|
||||
private void CharacterDead(Character character, CauseOfDeath causeOfDeath)
|
||||
{
|
||||
var husk = Character.Create(
|
||||
Path.Combine("Content", "Characters", "Human", "humanhusk.xml"),
|
||||
character.WorldPosition,
|
||||
character.Info,
|
||||
false, true);
|
||||
|
||||
foreach (Limb limb in husk.AnimController.Limbs)
|
||||
{
|
||||
if (limb.type == LimbType.None)
|
||||
{
|
||||
limb.body.SetTransform(character.SimPosition, 0.0f);
|
||||
continue;
|
||||
}
|
||||
|
||||
var matchingLimb = character.AnimController.GetLimb(limb.type);
|
||||
limb.body.SetTransform(matchingLimb.SimPosition, matchingLimb.Rotation);
|
||||
}
|
||||
|
||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||
{
|
||||
if (character.Inventory.Items[i] == null) continue;
|
||||
husk.Inventory.TryPutItem(character.Inventory.Items[i], i, false, true);
|
||||
}
|
||||
|
||||
character.Enabled = false;
|
||||
|
||||
husk.SpawnedMidRound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,8 @@ namespace Barotrauma
|
||||
|
||||
private float scale;
|
||||
|
||||
public float AttackTimer;
|
||||
|
||||
//public float Damage
|
||||
//{
|
||||
// get { return damage; }
|
||||
@@ -223,6 +225,8 @@ namespace Barotrauma
|
||||
|
||||
refJointIndex = -1;
|
||||
|
||||
Vector2 pullJointPos = Vector2.Zero;
|
||||
|
||||
if (element.Attribute("type") != null)
|
||||
{
|
||||
try
|
||||
@@ -236,25 +240,26 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
|
||||
Vector2 jointPos = ToolBox.GetAttributeVector2(element, "pullpos", Vector2.Zero) * scale;
|
||||
jointPos = ConvertUnits.ToSimUnits(jointPos);
|
||||
pullJointPos = ToolBox.GetAttributeVector2(element, "pullpos", Vector2.Zero) * scale;
|
||||
pullJointPos = ConvertUnits.ToSimUnits(pullJointPos);
|
||||
|
||||
stepOffset = ToolBox.GetAttributeVector2(element, "stepoffset", Vector2.Zero) * scale;
|
||||
stepOffset = ConvertUnits.ToSimUnits(stepOffset);
|
||||
|
||||
refJointIndex = ToolBox.GetAttributeInt(element, "refjoint", -1);
|
||||
|
||||
pullJoint = new FixedMouseJoint(body.FarseerBody, jointPos);
|
||||
pullJoint.Enabled = false;
|
||||
pullJoint.MaxForce = ((type == LimbType.LeftHand || type == LimbType.RightHand) ? 400.0f : 150.0f) * body.Mass;
|
||||
|
||||
GameMain.World.AddJoint(pullJoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = LimbType.None;
|
||||
}
|
||||
|
||||
pullJoint = new FixedMouseJoint(body.FarseerBody, pullJointPos);
|
||||
pullJoint.Enabled = false;
|
||||
pullJoint.MaxForce = ((type == LimbType.LeftHand || type == LimbType.RightHand) ? 400.0f : 150.0f) * body.Mass;
|
||||
|
||||
GameMain.World.AddJoint(pullJoint);
|
||||
|
||||
steerForce = ToolBox.GetAttributeFloat(element, "steerforce", 0.0f);
|
||||
|
||||
//maxHealth = Math.Max(ToolBox.GetAttributeFloat(element, "health", 100.0f),1.0f);
|
||||
@@ -484,6 +489,36 @@ namespace Barotrauma
|
||||
//}
|
||||
}
|
||||
|
||||
public void ActivateDamagedSprite()
|
||||
{
|
||||
damage = 100.0f;
|
||||
}
|
||||
|
||||
public void UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget)
|
||||
{
|
||||
float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
|
||||
|
||||
AttackTimer += deltaTime;
|
||||
body.ApplyTorque(Mass * character.AnimController.Dir * attack.Torque);
|
||||
|
||||
if (dist < attack.Range * 0.5f)
|
||||
{
|
||||
if (AttackTimer >= attack.Duration && damageTarget != null)
|
||||
{
|
||||
attack.DoDamage(character, damageTarget, WorldPosition, 1.0f, (soundTimer <= 0.0f));
|
||||
|
||||
soundTimer = Limb.SoundInterval;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 diff = attackPosition - SimPosition;
|
||||
if (diff.LengthSquared() > 0.00001f)
|
||||
{
|
||||
Vector2 pos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
|
||||
body.ApplyLinearImpulse(Mass * attack.Force *
|
||||
Vector2.Normalize(attackPosition - SimPosition), pos);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
@@ -492,7 +527,9 @@ namespace Barotrauma
|
||||
|
||||
body.Dir = Dir;
|
||||
|
||||
if (wearingItems.Find(w => w != null && w.HideLimb) == null)
|
||||
bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb);
|
||||
|
||||
if (!hideLimb)
|
||||
{
|
||||
body.Draw(spriteBatch, sprite, color, null, scale);
|
||||
}
|
||||
@@ -526,7 +563,7 @@ namespace Barotrauma
|
||||
scale, spriteEffect, depth);
|
||||
}
|
||||
|
||||
if (damage>0.0f && damagedSprite!=null)
|
||||
if (damage>0.0f && damagedSprite!=null && !hideLimb)
|
||||
{
|
||||
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
|
||||
|
||||
|
||||
@@ -173,20 +173,8 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//oxygen = ToolBox.GetAttributeFloat(element, "oxygen", 0.0f);
|
||||
|
||||
|
||||
//deteriorateOnActive = ToolBox.GetAttributeFloat(element, "deteriorateonactive", 0.0f);
|
||||
//deteriorateOnUse = ToolBox.GetAttributeFloat(element, "deteriorateonuse", 0.0f);
|
||||
}
|
||||
|
||||
|
||||
//public virtual void Apply(ActionType type, float deltaTime, Item item, Character Character = null)
|
||||
//{
|
||||
// if (this.type == type) Apply(deltaTime, Character, item);
|
||||
//}
|
||||
|
||||
private bool HasRequiredItems(Entity entity)
|
||||
{
|
||||
if (requiredItems == null) return true;
|
||||
|
||||
Reference in New Issue
Block a user