(d9829ac) v0.9.4.0

This commit is contained in:
Regalis
2019-10-24 18:05:42 +02:00
parent 9aa12bcac2
commit b39922a074
319 changed files with 12516 additions and 6815 deletions
@@ -30,7 +30,9 @@ namespace Barotrauma
FallBack,
FallBackUntilCanAttack,
PursueIfCanAttack,
Pursue
Pursue,
FollowThrough,
FollowThroughUntilCanAttack
}
struct AttackResult
@@ -65,42 +67,43 @@ namespace Barotrauma
AppliedDamageModifiers = appliedDamageModifiers;
}
}
partial class Attack : ISerializableEntity
{
public readonly XElement SourceElement;
[Serialize(AttackContext.NotDefined, true), Editable]
[Serialize(AttackContext.NotDefined, true, description: "Is the attack used only in a specific condition?"), Editable]
public AttackContext Context { get; private set; }
[Serialize(AttackTarget.Any, true), Editable]
[Serialize(AttackTarget.Any, true, description: "Does the attack target only specific targets?"), Editable]
public AttackTarget TargetType { get; private set; }
[Serialize(HitDetection.Distance, true), Editable]
[Serialize(LimbType.None, true, description: "If not defined or set to none, the closest limb is used (default)."), Editable]
public LimbType TargetLimbType { get; private set; }
[Serialize(HitDetection.Distance, true, description: "Collision detection is more accurate, but it only affects targets that are in contact with the limb."), Editable]
public HitDetection HitDetectionType { get; private set; }
[Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")]
[Serialize(AIBehaviorAfterAttack.FallBack, true, description: "The preferred AI behavior after the attack."), Editable]
public AIBehaviorAfterAttack AfterAttack { get; set; }
[Serialize(false, true), Editable(ToolTip = "Should the ai try to reverse when aiming with this attack?")]
[Serialize(false, true, description: "Should the AI try to reverse when aiming with this attack?"), Editable]
public bool Reverse { get; private set; }
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")]
[Serialize(0.0f, true, description: "The min distance from the attack limb to the target before the AI tries to attack."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f)]
public float Range { get; set; }
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target to do damage. In distance based hit detection, the hit will be registered as soon as the target is within the damage range, unless the attack duration has expired.")]
[Serialize(0.0f, true, description: "The min distance from the attack limb to the target to do damage. In distance-based hit detection, the hit will be registered as soon as the target is within the damage range, unless the attack duration has expired."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f)]
public float DamageRange { get; set; }
[Serialize(0.25f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, DecimalCount = 2, ToolTip = "An approximation of the attack duration. Effectively defines the time window in which the hit can be registered. If set to too low value, it's possible that the attack won't hit the target in time.")]
[Serialize(0.25f, true, description: "An approximation of the attack duration. Effectively defines the time window in which the hit can be registered. If set to too low value, it's possible that the attack won't hit the target in time."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f, DecimalCount = 2)]
public float Duration { get; private set; }
[Serialize(5f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "How long the AI waits between the attacks.")]
[Serialize(5f, true, description: "How long the AI waits between the attacks."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2)]
public float CoolDown { get; set; } = 5;
[Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")]
[Serialize(0f, true, description: "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2)]
public float SecondaryCoolDown { get; set; } = 0;
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2, ToolTip = "Random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases).")]
[Serialize(0f, true, description: "A random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases)."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
public float CoolDownRandomFactor { get; private set; } = 0;
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
@@ -115,7 +118,7 @@ namespace Barotrauma
[Serialize(0.0f, false)]
public float Stun { get; private set; }
[Serialize(false, true), Editable]
[Serialize(false, true, description: "Can damage only Humans."), Editable]
public bool OnlyHumans { get; private set; }
[Serialize("", true), Editable]
@@ -139,36 +142,38 @@ namespace Barotrauma
}
}
[Serialize(0.0f, true), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f, ToolTip = "Applied to the attacking limb (or limbs defined using ApplyForceOnLimbs). The direction of the force is towards the target that's being attacked.")]
[Serialize(0.0f, true, description: "Applied to the attacking limb (or limbs defined using ApplyForceOnLimbs). The direction of the force is towards the target that's being attacked."), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)]
public float Force { get; private set; }
[Serialize(0.0f, true), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f, ToolTip = "Applied to the attacking limb.")]
[Serialize(0.0f, true, description: "Applied to the attacking limb."), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)]
public float Torque { get; private set; }
[Serialize(false, true), Editable]
public bool ApplyForcesOnlyOnce { get; private set; }
[Serialize(0.0f, true), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f, ToolTip = "Applied to the target the attack hits. The direction of the impulse is from this limb towards the target (use negative values to pull the target closer).")]
[Serialize(0.0f, true, description: "Applied to the target the attack hits. The direction of the impulse is from this limb towards the target (use negative values to pull the target closer)."), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)]
public float TargetImpulse { get; private set; }
[Serialize("0.0, 0.0", true), Editable(ToolTip = "Applied to the target, in world space coordinates(i.e. 0, -1 pushes the target downwards).")]
[Serialize("0.0, 0.0", true, description: "Applied to the target, in world space coordinates(i.e. 0, -1 pushes the target downwards)."), Editable]
public Vector2 TargetImpulseWorld { get; private set; }
[Serialize(0.0f, true), Editable(-1000.0f, 1000.0f, ToolTip = "Applied to the target the attack hits. The direction of the force is from this limb towards the target (use negative values to pull the target closer).")]
[Serialize(0.0f, true, description: "Applied to the target the attack hits. The direction of the force is from this limb towards the target (use negative values to pull the target closer)."), Editable(-1000.0f, 1000.0f)]
public float TargetForce { get; private set; }
[Serialize("0.0, 0.0", true), Editable(ToolTip = "Applied to the target, in world space coordinates(i.e. 0, -1 pushes the target downwards).")]
[Serialize("0.0, 0.0", true, description: "Applied to the target, in world space coordinates(i.e. 0, -1 pushes the target downwards)."), Editable]
public Vector2 TargetForceWorld { get; private set; }
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
[Serialize(0.0f, true, description: "How likely the attack causes target limbs to be severed when the target is dead."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
public float SeverLimbsProbability { get; set; }
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
public float StickChance { get; set; }
// TODO: disabled because not synced
//[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
//public float StickChance { get; set; }
public float StickChance => 0f;
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
[Serialize(0.0f, true, description: ""), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
public float Priority { get; private set; }
public IEnumerable<StatusEffect> StatusEffects
{
get { return statusEffects; }
@@ -186,7 +191,7 @@ namespace Barotrauma
//(if none, force is applied only to the limb the attack is attached to)
public readonly List<int> ForceOnLimbIndices = new List<int>();
public readonly List<Affliction> Afflictions = new List<Affliction>();
public readonly Dictionary<Affliction, XElement> Afflictions = new Dictionary<Affliction, XElement>();
/// <summary>
/// Only affects ai decision making. All the conditionals has to be met in order to select the attack. TODO: allow to define conditionals using any (implemented in StatusEffect -> move from there to PropertyConditional?)
@@ -207,7 +212,7 @@ namespace Barotrauma
public List<Affliction> GetMultipliedAfflictions(float multiplier)
{
List<Affliction> multipliedAfflictions = new List<Affliction>();
foreach (Affliction affliction in Afflictions)
foreach (Affliction affliction in Afflictions.Keys)
{
multipliedAfflictions.Add(affliction.Prefab.Instantiate(affliction.Strength * multiplier, affliction.Source));
}
@@ -227,7 +232,7 @@ namespace Barotrauma
public float GetTotalDamage(bool includeStructureDamage = false)
{
float totalDamage = includeStructureDamage ? StructureDamage : 0.0f;
foreach (Affliction affliction in Afflictions)
foreach (Affliction affliction in Afflictions.Keys)
{
totalDamage += affliction.GetVitalityDecrease(null);
}
@@ -236,9 +241,9 @@ namespace Barotrauma
public Attack(float damage, float bleedingDamage, float burnDamage, float structureDamage, float range = 0.0f)
{
if (damage > 0.0f) Afflictions.Add(AfflictionPrefab.InternalDamage.Instantiate(damage));
if (bleedingDamage > 0.0f) Afflictions.Add(AfflictionPrefab.Bleeding.Instantiate(bleedingDamage));
if (burnDamage > 0.0f) Afflictions.Add(AfflictionPrefab.Burn.Instantiate(burnDamage));
if (damage > 0.0f) Afflictions.Add(AfflictionPrefab.InternalDamage.Instantiate(damage), null);
if (bleedingDamage > 0.0f) Afflictions.Add(AfflictionPrefab.Bleeding.Instantiate(bleedingDamage), null);
if (burnDamage > 0.0f) Afflictions.Add(AfflictionPrefab.Burn.Instantiate(burnDamage), null);
Range = range;
DamageRange = range;
@@ -247,8 +252,7 @@ namespace Barotrauma
public Attack(XElement element, string parentDebugName)
{
SourceElement = element;
Deserialize();
Deserialize(element);
if (element.Attribute("damage") != null ||
element.Attribute("bluntdamage") != null ||
@@ -258,8 +262,6 @@ namespace Barotrauma
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Define damage as afflictions instead of using the damage attribute (e.g. <Affliction identifier=\"internaldamage\" strength=\"10\" />).");
}
DamageRange = element.GetAttributeFloat("damagerange", 0f);
InitProjSpecific(element);
foreach (XElement subElement in element.Elements())
@@ -297,10 +299,9 @@ namespace Barotrauma
}
}
float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength");
var affliction = afflictionPrefab.Instantiate(afflictionStrength);
affliction.ApplyProbability = subElement.GetAttributeFloat("probability", 1.0f);
Afflictions.Add(affliction);
//float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength");
//var affliction = afflictionPrefab.Instantiate(afflictionStrength);
//Afflictions.Add(affliction, subElement);
break;
case "conditional":
@@ -310,21 +311,50 @@ namespace Barotrauma
}
break;
}
}
}
partial void InitProjSpecific(XElement element);
partial void InitProjSpecific(XElement element = null);
public void Serialize()
public void ReloadAfflictions(XElement element)
{
if (SourceElement == null) { return; }
SerializableProperty.SerializeProperties(this, SourceElement, true);
Afflictions.Clear();
foreach (var subElement in element.GetChildElements("affliction"))
{
AfflictionPrefab afflictionPrefab;
Affliction affliction;
string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
afflictionPrefab = AfflictionPrefab.List.Find(ap => ap.Identifier.ToLowerInvariant() == afflictionIdentifier);
if (afflictionPrefab != null)
{
float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength");
affliction = afflictionPrefab.Instantiate(afflictionStrength);
}
else
{
affliction = new Affliction(null, 0);
}
affliction.Deserialize(subElement);
// add the affliction anyway, so that it can be shown in the editor.
Afflictions.Add(affliction, subElement);
}
}
public void Deserialize()
public void Serialize(XElement element)
{
if (SourceElement == null) { return; }
SerializableProperties = SerializableProperty.DeserializeProperties(this, SourceElement);
SerializableProperty.SerializeProperties(this, element, true);
foreach (var affliction in Afflictions)
{
if (affliction.Value != null)
{
affliction.Key.Serialize(affliction.Value);
}
}
}
public void Deserialize(XElement element)
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
ReloadAfflictions(element);
}
public AttackResult DoDamage(Character attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true)
@@ -332,7 +362,10 @@ namespace Barotrauma
Character targetCharacter = target as Character;
if (OnlyHumans)
{
if (targetCharacter != null && targetCharacter.ConfigPath != Character.HumanConfigFile) return new AttackResult();
if (targetCharacter != null && !targetCharacter.IsHuman)
{
return new AttackResult();
}
}
SetUser(attacker);
@@ -389,7 +422,10 @@ namespace Barotrauma
if (OnlyHumans)
{
if (targetLimb.character != null && targetLimb.character.ConfigPath != Character.HumanConfigFile) return new AttackResult();
if (targetLimb.character != null && !targetLimb.character.IsHuman)
{
return new AttackResult();
}
}
SetUser(attacker);
@@ -418,7 +454,6 @@ namespace Barotrauma
{
effect.Apply(effectType, deltaTime, targetLimb.character, targetLimb.character.AnimController.Limbs.Cast<ISerializableEntity>().ToList());
}
}
return attackResult;