Merge branch 'master' into moStuff
This commit is contained in:
+56
-13
@@ -1,29 +1,63 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveContainItem: AIObjective
|
||||
{
|
||||
private string itemName;
|
||||
public int MinContainedAmount = 1;
|
||||
|
||||
private string[] itemNames;
|
||||
|
||||
private ItemContainer container;
|
||||
|
||||
bool isCompleted;
|
||||
|
||||
private bool isCompleted;
|
||||
|
||||
public bool IgnoreAlreadyContainedItems;
|
||||
|
||||
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
private AIObjectiveGetItem getItemObjective;
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
|
||||
public AIObjectiveContainItem(Character character, string itemName, ItemContainer container)
|
||||
: this(character, new string[] { itemName }, container)
|
||||
{
|
||||
}
|
||||
|
||||
public AIObjectiveContainItem(Character character, string[] itemNames, ItemContainer container)
|
||||
: base (character, "")
|
||||
{
|
||||
this.itemName = itemName;
|
||||
this.itemNames = itemNames;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
return isCompleted || container.Inventory.FindItem(itemName)!=null;
|
||||
if (isCompleted) return true;
|
||||
|
||||
int containedItemCount = 0;
|
||||
foreach (Item item in container.Inventory.Items)
|
||||
{
|
||||
if (item != null && itemNames.Any(name => item.Prefab.NameMatches(name) || item.HasTag(name))) containedItemCount++;
|
||||
}
|
||||
|
||||
return containedItemCount >= MinContainedAmount;
|
||||
}
|
||||
|
||||
public override bool CanBeCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (goToObjective != null)
|
||||
{
|
||||
return goToObjective.CanBeCompleted;
|
||||
}
|
||||
|
||||
return getItemObjective == null || !getItemObjective.CanBeCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPriority(AIObjectiveManager objectiveManager)
|
||||
@@ -41,12 +75,13 @@ namespace Barotrauma
|
||||
if (isCompleted) return;
|
||||
|
||||
//get the item that should be contained
|
||||
var itemToContain = character.Inventory.FindItem(itemName);
|
||||
var itemToContain = character.Inventory.FindItem(itemNames);
|
||||
if (itemToContain == null)
|
||||
{
|
||||
var getItem = new AIObjectiveGetItem(character, itemName);
|
||||
getItem.IgnoreContainedItems = IgnoreAlreadyContainedItems;
|
||||
AddSubObjective(getItem);
|
||||
getItemObjective = new AIObjectiveGetItem(character, itemNames);
|
||||
getItemObjective.GetItemPriority = GetItemPriority;
|
||||
getItemObjective.IgnoreContainedItems = IgnoreAlreadyContainedItems;
|
||||
AddSubObjective(getItemObjective);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,9 +98,10 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
if (Vector2.Distance(character.Position, container.Item.Position) > container.Item.InteractDistance
|
||||
&& !container.Item.IsInsideTrigger(character.Position))
|
||||
&& !container.Item.IsInsideTrigger(character.WorldPosition))
|
||||
{
|
||||
AddSubObjective(new AIObjectiveGoTo(container.Item, character));
|
||||
goToObjective = new AIObjectiveGoTo(container.Item, character);
|
||||
AddSubObjective(goToObjective);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -79,8 +115,15 @@ namespace Barotrauma
|
||||
{
|
||||
AIObjectiveContainItem objective = otherObjective as AIObjectiveContainItem;
|
||||
if (objective == null) return false;
|
||||
if (objective.container != container) return false;
|
||||
if (objective.itemNames.Length != itemNames.Length) return false;
|
||||
|
||||
return objective.itemName == itemName && objective.container == container;
|
||||
for (int i = 0; i < itemNames.Length; i++)
|
||||
{
|
||||
if (objective.itemNames[i] != itemNames[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+24
-17
@@ -11,12 +11,20 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
var item = character.Inventory.FindItem(gearName);
|
||||
if (item == null) return false;
|
||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||
{
|
||||
if (CharacterInventory.limbSlots[i] == InvSlotType.Any || character.Inventory.Items[i] == null) continue;
|
||||
if (character.Inventory.Items[i].Prefab.NameMatches(gearName) || character.Inventory.Items[i].HasTag(gearName))
|
||||
{
|
||||
var containedItems = character.Inventory.Items[i].ContainedItems;
|
||||
if (containedItems == null) continue;
|
||||
|
||||
var containedItems = item.ContainedItems;
|
||||
var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank") && i.Condition > 0.0f);
|
||||
return oxygenTank != null;
|
||||
var oxygenTank = Array.Find(containedItems, it => (it.Prefab.NameMatches("Oxygen Tank") || it.HasTag("oxygensource")) && it.Condition > 0.0f);
|
||||
if (oxygenTank != null) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit)
|
||||
@@ -41,25 +49,24 @@ namespace Barotrauma
|
||||
var containedItems = item.ContainedItems;
|
||||
if (containedItems == null) return;
|
||||
|
||||
//check if there's an oxygen tank in the mask
|
||||
var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank"));
|
||||
|
||||
if (oxygenTank != null)
|
||||
//check if there's an oxygen tank in the mask/suit
|
||||
foreach (Item containedItem in containedItems)
|
||||
{
|
||||
if (oxygenTank.Condition > 0.0f)
|
||||
if (containedItem == null) continue;
|
||||
if (containedItem.Condition <= 0.0f)
|
||||
{
|
||||
containedItem.Drop();
|
||||
}
|
||||
else if (containedItem.Prefab.NameMatches("Oxygen Tank") || containedItem.HasTag("oxygensource"))
|
||||
{
|
||||
//we've got an oxygen source inside the mask/suit, all good
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
oxygenTank.Drop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted())
|
||||
{
|
||||
subObjective = new AIObjectiveContainItem(character, "Oxygen Tank", item.GetComponent<ItemContainer>());
|
||||
subObjective = new AIObjectiveContainItem(character, new string[] { "Oxygen Tank", "oxygensource" }, item.GetComponent<ItemContainer>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,9 @@ namespace Barotrauma
|
||||
if (gap.ConnectedWall == null) continue;
|
||||
if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue;
|
||||
|
||||
//TODO: prevent the AI characters from fixing leaks in the enemy sub in sub-vs-sub missions if/when multiplayer bots are implemented
|
||||
if (gap.Submarine == null) continue;
|
||||
|
||||
float gapPriority = GetGapFixPriority(gap);
|
||||
|
||||
int index = 0;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveGetItem : AIObjective
|
||||
{
|
||||
private string itemName;
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
private string[] itemNames;
|
||||
|
||||
private Item targetItem, moveToTarget;
|
||||
|
||||
@@ -20,6 +22,8 @@ namespace Barotrauma
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
|
||||
private float currItemPriority;
|
||||
|
||||
private bool equip;
|
||||
|
||||
public override bool CanBeCompleted
|
||||
@@ -49,24 +53,33 @@ namespace Barotrauma
|
||||
this.targetItem = targetItem;
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, string itemName, bool equip=false)
|
||||
: base (character, "")
|
||||
public AIObjectiveGetItem(Character character, string itemName, bool equip = false)
|
||||
: this(character, new string[] { itemName }, equip)
|
||||
{
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, string[] itemNames, bool equip = false)
|
||||
: base(character, "")
|
||||
{
|
||||
canBeCompleted = true;
|
||||
|
||||
this.equip = equip;
|
||||
|
||||
currSearchIndex = 0;
|
||||
|
||||
this.itemName = itemName;
|
||||
|
||||
this.itemNames = itemNames;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
FindTargetItem();
|
||||
if (targetItem == null || moveToTarget == null) return;
|
||||
if (targetItem == null || moveToTarget == null)
|
||||
{
|
||||
character?.AIController?.SteeringManager?.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.InteractDistance*2.0f)
|
||||
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.InteractDistance * 2.0f)
|
||||
{
|
||||
int targetSlot = -1;
|
||||
if (equip)
|
||||
@@ -82,8 +95,8 @@ namespace Barotrauma
|
||||
foreach (InvSlotType slots in pickable.AllowedSlots)
|
||||
{
|
||||
if (slots.HasFlag(InvSlotType.Any)) continue;
|
||||
|
||||
for (int i = 0; i<character.Inventory.Items.Length; i++)
|
||||
|
||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||
{
|
||||
//slot not needed by the item, continue
|
||||
if (!slots.HasFlag(CharacterInventory.limbSlots[i])) continue;
|
||||
@@ -111,15 +124,19 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (goToObjective == null)
|
||||
if (goToObjective == null || moveToTarget != goToObjective.Target)
|
||||
{
|
||||
bool gettingDivingGear = itemName == "diving" || itemName == "Diving Gear";
|
||||
//check if we're already looking for a diving gear
|
||||
bool gettingDivingGear = (targetItem != null && targetItem.Prefab.NameMatches("Diving Gear") || targetItem.HasTag("diving")) ||
|
||||
(itemNames != null && (itemNames.Contains("diving") || itemNames.Contains("Diving Gear")));
|
||||
|
||||
//don't attempt to get diving gear to reach the destination if the item we're trying to get is diving gear
|
||||
goToObjective = new AIObjectiveGoTo(moveToTarget, character, false, !gettingDivingGear);
|
||||
}
|
||||
|
||||
goToObjective.TryComplete(deltaTime);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -127,14 +144,14 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
private void FindTargetItem()
|
||||
{
|
||||
if (itemName == null)
|
||||
if (itemNames == null)
|
||||
{
|
||||
if (targetItem == null) canBeCompleted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float currDist = moveToTarget == null ? 0.0f : Vector2.DistanceSquared(moveToTarget.Position, character.Position);
|
||||
|
||||
|
||||
for (int i = 0; i < 10 && currSearchIndex < Item.ItemList.Count - 2; i++)
|
||||
{
|
||||
currSearchIndex++;
|
||||
@@ -143,21 +160,38 @@ namespace Barotrauma
|
||||
|
||||
if (item.CurrentHull == null || item.Condition <= 0.0f) continue;
|
||||
if (IgnoreContainedItems && item.Container != null) continue;
|
||||
if (item.Name != itemName && !item.HasTag(itemName)) continue;
|
||||
if (!itemNames.Any(name => item.Prefab.NameMatches(name) || item.HasTag(name))) continue;
|
||||
|
||||
//if the item is inside a character's inventory, don't steal it
|
||||
if (item.ParentInventory is CharacterInventory) continue;
|
||||
|
||||
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
||||
if (item.ParentInventory != null && item.ParentInventory.Owner is Item)
|
||||
//if the item is inside a character's inventory, don't steal it unless the character is dead
|
||||
if (item.ParentInventory is CharacterInventory)
|
||||
{
|
||||
if (((Item)item.ParentInventory.Owner).ParentInventory is CharacterInventory) continue;
|
||||
Character owner = item.ParentInventory.Owner as Character;
|
||||
if (owner != null && !owner.IsDead) continue;
|
||||
}
|
||||
|
||||
//ignore if item is further away than the currently targeted item
|
||||
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
if (moveToTarget != null && Vector2.DistanceSquared((rootContainer ?? item).Position, character.Position) > currDist) continue;
|
||||
|
||||
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||
{
|
||||
Character owner = rootContainer.ParentInventory.Owner as Character;
|
||||
if (owner != null && !owner.IsDead) continue;
|
||||
}
|
||||
|
||||
float itemPriority = 0.0f;
|
||||
if (GetItemPriority != null)
|
||||
{
|
||||
//ignore if the item has zero priority
|
||||
itemPriority = GetItemPriority(item);
|
||||
if (itemPriority <= 0.0f) continue;
|
||||
}
|
||||
|
||||
itemPriority = itemPriority - Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
|
||||
|
||||
//ignore if the item has a lower priority than the currently selected one
|
||||
if (moveToTarget != null && itemPriority < currItemPriority) continue;
|
||||
|
||||
currItemPriority = itemPriority;
|
||||
|
||||
targetItem = item;
|
||||
moveToTarget = rootContainer ?? item;
|
||||
}
|
||||
@@ -165,23 +199,44 @@ namespace Barotrauma
|
||||
//if searched through all the items and a target wasn't found, can't be completed
|
||||
if (currSearchIndex >= Item.ItemList.Count && targetItem == null) canBeCompleted = false;
|
||||
}
|
||||
|
||||
|
||||
public override bool IsDuplicate(AIObjective otherObjective)
|
||||
{
|
||||
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
|
||||
if (getItem == null) return false;
|
||||
return (getItem.itemName == itemName);
|
||||
if (getItem.equip != equip) return false;
|
||||
if (getItem.itemNames != null && itemNames != null)
|
||||
{
|
||||
if (getItem.itemNames.Length != itemNames.Length) return false;
|
||||
for (int i = 0; i < getItem.itemNames.Length; i++)
|
||||
{
|
||||
if (getItem.itemNames[i] != itemNames[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (getItem.itemNames == null && itemNames == null)
|
||||
{
|
||||
return getItem.targetItem == targetItem;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
if (itemName!=null)
|
||||
if (itemNames != null)
|
||||
{
|
||||
return character.Inventory.FindItem(itemName) != null;
|
||||
foreach (string itemName in itemNames)
|
||||
{
|
||||
var matchingItem = character.Inventory.FindItem(itemName);
|
||||
if (matchingItem != null && (!equip || character.HasEquippedItem(matchingItem))) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
else if (targetItem!= null)
|
||||
else if (targetItem != null)
|
||||
{
|
||||
return character.Inventory.Items.Contains(targetItem);
|
||||
return character.Inventory.Items.Contains(targetItem) && (!equip || character.HasEquippedItem(targetItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//re-enable collider
|
||||
if (!Collider.FarseerBody.Enabled)
|
||||
if (!Collider.Enabled)
|
||||
{
|
||||
var lowestLimb = FindLowestLimb();
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Barotrauma
|
||||
Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)),
|
||||
0.0f);
|
||||
|
||||
Collider.FarseerBody.Enabled = true;
|
||||
Collider.Enabled = true;
|
||||
}
|
||||
|
||||
ResetPullJoints();
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Barotrauma
|
||||
|
||||
|
||||
//re-enable collider
|
||||
if (!Collider.FarseerBody.Enabled)
|
||||
if (!Collider.Enabled)
|
||||
{
|
||||
var lowestLimb = FindLowestLimb();
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma
|
||||
Collider.Rotation);
|
||||
|
||||
Collider.FarseerBody.ResetDynamics();
|
||||
Collider.FarseerBody.Enabled = true;
|
||||
Collider.Enabled = true;
|
||||
}
|
||||
|
||||
if (swimming)
|
||||
@@ -1034,7 +1034,8 @@ namespace Barotrauma
|
||||
{
|
||||
Limb targetLimb = target.AnimController.GetLimb(GrabLimb);
|
||||
|
||||
if (targetLimb == null || targetLimb.IsSevered)
|
||||
//grab hands if GrabLimb is not specified (or torso if the character has no hands)
|
||||
if (GrabLimb == LimbType.None || targetLimb.IsSevered)
|
||||
{
|
||||
targetLimb = target.AnimController.GetLimb(LimbType.Torso);
|
||||
if (i == 0)
|
||||
|
||||
@@ -527,7 +527,7 @@ namespace Barotrauma
|
||||
|
||||
public void SeverLimbJoint(LimbJoint limbJoint)
|
||||
{
|
||||
if (!limbJoint.CanBeSevered)
|
||||
if (!limbJoint.CanBeSevered || limbJoint.IsSevered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -541,12 +541,29 @@ namespace Barotrauma
|
||||
GetConnectedLimbs(connectedLimbs, checkedJoints, MainLimb);
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (!connectedLimbs.Contains(limb))
|
||||
if (connectedLimbs.Contains(limb)) continue;
|
||||
|
||||
limb.IsSevered = true;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (character.UseBloodParticles)
|
||||
{
|
||||
foreach (Limb limb in new Limb[] { limbJoint.LimbA, limbJoint.LimbB })
|
||||
{
|
||||
limb.IsSevered = true;
|
||||
for (int i = 0; i < MathHelper.Clamp(limb.Mass * 2.0f, 1.0f, 50.0f); i++)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("gib", limb.WorldPosition, Rand.Range(0.0f, MathHelper.TwoPi), Rand.Range(200.0f, 700.0f), character.CurrentHull);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MathHelper.Clamp(limb.Mass * 2.0f, 1.0f, 10.0f); i++)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("heavygib", limb.WorldPosition, Rand.Range(0.0f, MathHelper.TwoPi), Rand.Range(50.0f, 250.0f), character.CurrentHull);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(character, new object[] { NetEntityEvent.Type.Status });
|
||||
@@ -1469,5 +1486,12 @@ namespace Barotrauma
|
||||
list.Remove(this);
|
||||
}
|
||||
|
||||
public static void RemoveAll()
|
||||
{
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
list[i].Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ namespace Barotrauma
|
||||
Any = Blunt | Slash | Burn
|
||||
}
|
||||
|
||||
public enum HitDetection
|
||||
{
|
||||
Distance,
|
||||
Contact
|
||||
}
|
||||
|
||||
struct AttackResult
|
||||
{
|
||||
public readonly float Damage;
|
||||
@@ -39,90 +45,87 @@ namespace Barotrauma
|
||||
|
||||
partial class Attack
|
||||
{
|
||||
public readonly float Range;
|
||||
public readonly float DamageRange;
|
||||
public readonly float Duration;
|
||||
[Serialize(HitDetection.Distance, false)]
|
||||
public HitDetection HitDetectionType { get; private set; }
|
||||
|
||||
public readonly DamageType DamageType;
|
||||
[Serialize(0.0f, false)]
|
||||
public float Range { get; private set; }
|
||||
|
||||
private readonly float structureDamage;
|
||||
private readonly float damage;
|
||||
private readonly float bleedingDamage;
|
||||
[Serialize(0.0f, false)]
|
||||
public float DamageRange { get; private set; }
|
||||
|
||||
private readonly bool onlyHumans;
|
||||
[Serialize(0.0f, false)]
|
||||
public float Duration { get; private set; }
|
||||
|
||||
private readonly List<StatusEffect> statusEffects;
|
||||
[Serialize(DamageType.None, false)]
|
||||
public DamageType DamageType { get; private set; }
|
||||
|
||||
public readonly float Force;
|
||||
[Serialize(0.0f, false)]
|
||||
public float StructureDamage { get; private set; }
|
||||
|
||||
public readonly float Torque;
|
||||
[Serialize(0.0f, false)]
|
||||
public float Damage { get; private set; }
|
||||
|
||||
public readonly float TargetForce;
|
||||
[Serialize(0.0f, false)]
|
||||
public float BleedingDamage { get; private set; }
|
||||
|
||||
public readonly float SeverLimbsProbability;
|
||||
[Serialize(0.0f, false)]
|
||||
public float Stun { get; private set; }
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool OnlyHumans { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float Force { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float Torque { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float TargetForce { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float SeverLimbsProbability { get; set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float Priority { get; private set; }
|
||||
|
||||
//the indices of the limbs Force is applied on
|
||||
//(if none, force is applied only to the limb the attack is attached to)
|
||||
public readonly List<int> ApplyForceOnLimbs;
|
||||
|
||||
public readonly float Stun;
|
||||
|
||||
private float priority;
|
||||
private readonly List<StatusEffect> statusEffects;
|
||||
|
||||
public float GetDamage(float deltaTime)
|
||||
{
|
||||
return (Duration == 0.0f) ? damage : damage * deltaTime;
|
||||
return (Duration == 0.0f) ? Damage : Damage * deltaTime;
|
||||
}
|
||||
|
||||
public float GetBleedingDamage(float deltaTime)
|
||||
{
|
||||
return (Duration == 0.0f) ? bleedingDamage : bleedingDamage * deltaTime;
|
||||
return (Duration == 0.0f) ? BleedingDamage : BleedingDamage * deltaTime;
|
||||
}
|
||||
|
||||
public float GetStructureDamage(float deltaTime)
|
||||
{
|
||||
return (Duration == 0.0f) ? structureDamage : structureDamage * deltaTime;
|
||||
return (Duration == 0.0f) ? StructureDamage : StructureDamage * deltaTime;
|
||||
}
|
||||
|
||||
public Attack(float damage, float structureDamage, float bleedingDamage, float range = 0.0f)
|
||||
{
|
||||
Range = range;
|
||||
DamageRange = range;
|
||||
this.damage = damage;
|
||||
this.structureDamage = structureDamage;
|
||||
this.bleedingDamage = bleedingDamage;
|
||||
this.Damage = damage;
|
||||
this.StructureDamage = structureDamage;
|
||||
this.BleedingDamage = bleedingDamage;
|
||||
}
|
||||
|
||||
public Attack(XElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
DamageType = (DamageType)Enum.Parse(typeof(DamageType), element.GetAttributeString("damagetype", "None"), true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
DamageType = DamageType.None;
|
||||
}
|
||||
|
||||
damage = element.GetAttributeFloat("damage", 0.0f);
|
||||
structureDamage = element.GetAttributeFloat("structuredamage", 0.0f);
|
||||
bleedingDamage = element.GetAttributeFloat("bleedingdamage", 0.0f);
|
||||
Stun = element.GetAttributeFloat("stun", 0.0f);
|
||||
|
||||
SeverLimbsProbability = element.GetAttributeFloat("severlimbsprobability", 0.0f);
|
||||
|
||||
Force = element.GetAttributeFloat("force", 0.0f);
|
||||
TargetForce = element.GetAttributeFloat("targetforce", 0.0f);
|
||||
Torque = element.GetAttributeFloat("torque", 0.0f);
|
||||
|
||||
Range = element.GetAttributeFloat("range", 0.0f);
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
DamageRange = element.GetAttributeFloat("damagerange", Range);
|
||||
Duration = element.GetAttributeFloat("duration", 0.0f);
|
||||
|
||||
priority = element.GetAttributeFloat("priority", 1.0f);
|
||||
|
||||
onlyHumans = element.GetAttributeBool("onlyhumans", false);
|
||||
|
||||
|
||||
InitProjSpecific(element);
|
||||
|
||||
string limbIndicesStr = element.GetAttributeString("applyforceonlimbs", "");
|
||||
@@ -158,7 +161,7 @@ namespace Barotrauma
|
||||
|
||||
public AttackResult DoDamage(Character attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true)
|
||||
{
|
||||
if (onlyHumans)
|
||||
if (OnlyHumans)
|
||||
{
|
||||
Character character = target as Character;
|
||||
if (character != null && character.ConfigPath != Character.HumanConfigFile) return new AttackResult();
|
||||
@@ -189,7 +192,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetLimb == null) return new AttackResult();
|
||||
|
||||
if (onlyHumans)
|
||||
if (OnlyHumans)
|
||||
{
|
||||
if (targetLimb.character != null && targetLimb.character.ConfigPath != Character.HumanConfigFile) return new AttackResult();
|
||||
}
|
||||
|
||||
@@ -344,18 +344,18 @@ namespace Barotrauma
|
||||
if (GameMain.Client != null) return;
|
||||
|
||||
float newHealth = MathHelper.Clamp(value, minHealth, maxHealth);
|
||||
if (newHealth == health) return;
|
||||
//if (newHealth == health) return;
|
||||
|
||||
health = newHealth;
|
||||
|
||||
if (GameMain.Server != null)
|
||||
/*if (GameMain.Server != null)
|
||||
{
|
||||
if (Math.Abs(health - lastSentHealth) > (maxHealth - minHealth) / 255.0f || Math.Sign(health) != Math.Sign(lastSentHealth))
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
lastSentHealth = health;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,12 +374,12 @@ namespace Barotrauma
|
||||
if (!DoesBleed) return;
|
||||
|
||||
float newBleeding = MathHelper.Clamp(value, 0.0f, 5.0f);
|
||||
if (newBleeding == bleeding) return;
|
||||
//if (newBleeding == bleeding) return;
|
||||
|
||||
bleeding = newBleeding;
|
||||
|
||||
if (GameMain.Server != null)
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
|
||||
/*if (GameMain.Server != null)
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +426,12 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool UseBloodParticles
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float BleedingDecreaseSpeed
|
||||
{
|
||||
get;
|
||||
@@ -634,6 +640,7 @@ namespace Barotrauma
|
||||
health = maxHealth;
|
||||
|
||||
DoesBleed = doc.Root.GetAttributeBool("doesbleed", true);
|
||||
UseBloodParticles = doc.Root.GetAttributeBool("usebloodparticles", true);
|
||||
BleedingDecreaseSpeed = doc.Root.GetAttributeFloat("bleedingdecreasespeed", 0.05f);
|
||||
|
||||
needsAir = doc.Root.GetAttributeBool("needsair", false);
|
||||
@@ -1046,6 +1053,17 @@ namespace Barotrauma
|
||||
return !inventory.IsInLimbSlot(item, InvSlotType.Any);
|
||||
}
|
||||
|
||||
public bool HasEquippedItem(string itemName)
|
||||
{
|
||||
for (int i = 0; i < inventory.Items.Length; i++)
|
||||
{
|
||||
if (CharacterInventory.limbSlots[i] == InvSlotType.Any || inventory.Items[i] == null) continue;
|
||||
if (inventory.Items[i].Prefab.NameMatches(itemName) || inventory.Items[i].HasTag(itemName)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasSelectedItem(Item item)
|
||||
{
|
||||
return selectedItems.Contains(item);
|
||||
@@ -1541,11 +1559,11 @@ namespace Barotrauma
|
||||
if (stunTimer > 0.0f)
|
||||
{
|
||||
stunTimer -= deltaTime;
|
||||
if (stunTimer < 0.0f && GameMain.Server != null)
|
||||
/*if (stunTimer < 0.0f && GameMain.Server != null)
|
||||
{
|
||||
//stun ended -> notify clients
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
//Skip health effects as critical health handles it differently
|
||||
@@ -1575,8 +1593,8 @@ namespace Barotrauma
|
||||
if (IsRagdolled)
|
||||
{
|
||||
if (AnimController is HumanoidAnimController) ((HumanoidAnimController)AnimController).Crouching = false;
|
||||
if(GameMain.Server != null)
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
/*if(GameMain.Server != null)
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });*/
|
||||
AnimController.ResetPullJoints();
|
||||
selectedConstruction = null;
|
||||
return;
|
||||
@@ -1831,11 +1849,11 @@ namespace Barotrauma
|
||||
|
||||
if ((newStun <= stunTimer && !allowStunDecrease) || !MathUtils.IsValid(newStun)) return;
|
||||
|
||||
if (GameMain.Server != null &&
|
||||
/*if (GameMain.Server != null &&
|
||||
(Math.Sign(newStun) != Math.Sign(stunTimer) || Math.Abs(newStun - stunTimer) > 0.1f))
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
}
|
||||
}*/
|
||||
|
||||
if (Math.Sign(newStun) != Math.Sign(stunTimer)) AnimController.ResetPullJoints();
|
||||
|
||||
@@ -1894,11 +1912,11 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
/*if (GameMain.NetworkMember != null)
|
||||
{
|
||||
if (GameMain.Server != null)
|
||||
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
}
|
||||
}*/
|
||||
|
||||
AnimController.Frozen = false;
|
||||
|
||||
|
||||
@@ -332,6 +332,7 @@ namespace Barotrauma
|
||||
inventory.ServerRead(type, msg, c);
|
||||
break;
|
||||
case 1:
|
||||
bool doingCPR = msg.ReadBoolean();
|
||||
if (c.Character != this)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -340,7 +341,6 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
bool doingCPR = msg.ReadBoolean();
|
||||
AnimController.Anim = doingCPR ? AnimController.Animation.CPR : AnimController.Animation.None;
|
||||
break;
|
||||
case 2:
|
||||
@@ -358,7 +358,15 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
AnimController.GrabLimb = (LimbType)msg.ReadUInt16();
|
||||
LimbType grabLimb = (LimbType)msg.ReadUInt16();
|
||||
if (c.Character != this)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.Log("Received a character update message from a client who's not controlling the character");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
AnimController.GrabLimb = grabLimb;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -384,10 +392,6 @@ namespace Barotrauma
|
||||
Client owner = ((Client)extraData[1]);
|
||||
msg.Write(owner == null ? (byte)0 : owner.ID);
|
||||
break;
|
||||
case NetEntityEvent.Type.Status:
|
||||
msg.WriteRangedInteger(0, 2, 2);
|
||||
WriteStatus(msg);
|
||||
break;
|
||||
}
|
||||
msg.WritePadBits();
|
||||
}
|
||||
@@ -474,6 +478,8 @@ namespace Barotrauma
|
||||
tempBuffer.Write(SimPosition.Y);
|
||||
tempBuffer.Write(AnimController.Collider.Rotation);
|
||||
|
||||
WriteStatus(tempBuffer);
|
||||
|
||||
tempBuffer.WritePadBits();
|
||||
|
||||
msg.Write((byte)tempBuffer.LengthBytes);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -386,23 +387,25 @@ namespace Barotrauma
|
||||
SoundPlayer.PlayDamageSound(damageSoundType, amount, position);
|
||||
}
|
||||
|
||||
float bloodParticleAmount = bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
|
||||
float bloodParticleSize = MathHelper.Clamp(amount / 50.0f, 0.1f, 1.0f);
|
||||
|
||||
for (int i = 0; i < bloodParticleAmount; i++)
|
||||
if (character.UseBloodParticles)
|
||||
{
|
||||
var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
|
||||
if (blood != null)
|
||||
float bloodParticleAmount = bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
|
||||
float bloodParticleSize = MathHelper.Clamp(amount / 50.0f, 0.1f, 1.0f);
|
||||
|
||||
for (int i = 0; i < bloodParticleAmount; i++)
|
||||
{
|
||||
blood.Size *= bloodParticleSize;
|
||||
var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
|
||||
if (blood != null)
|
||||
{
|
||||
blood.Size *= bloodParticleSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (bloodParticleAmount > 0 && character.CurrentHull != null)
|
||||
{
|
||||
character.CurrentHull.AddDecal("blood", WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (bloodParticleAmount > 0 && character.CurrentHull != null)
|
||||
{
|
||||
character.CurrentHull.AddDecal("blood", WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (damageType == DamageType.Burn)
|
||||
@@ -479,12 +482,69 @@ namespace Barotrauma
|
||||
|
||||
body.ApplyTorque(Mass * character.AnimController.Dir * attack.Torque);
|
||||
|
||||
if (dist < attack.DamageRange)
|
||||
bool wasHit = false;
|
||||
|
||||
if (damageTarget != null)
|
||||
{
|
||||
switch (attack.HitDetectionType)
|
||||
{
|
||||
case HitDetection.Distance:
|
||||
wasHit = dist < attack.DamageRange;
|
||||
break;
|
||||
case HitDetection.Contact:
|
||||
List<Body> targetBodies = new List<Body>();
|
||||
if (damageTarget is Character)
|
||||
{
|
||||
Character targetCharacter = (Character)damageTarget;
|
||||
foreach (Limb limb in targetCharacter.AnimController.Limbs)
|
||||
{
|
||||
if (!limb.IsSevered && limb.body?.FarseerBody != null) targetBodies.Add(limb.body.FarseerBody);
|
||||
}
|
||||
}
|
||||
else if (damageTarget is Structure)
|
||||
{
|
||||
Structure targetStructure = (Structure)damageTarget;
|
||||
|
||||
if (character.Submarine == null && targetStructure.Submarine != null)
|
||||
{
|
||||
targetBodies.Add(targetStructure.Submarine.PhysicsBody.FarseerBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetBodies.AddRange(targetStructure.Bodies);
|
||||
}
|
||||
}
|
||||
else if (damageTarget is Item)
|
||||
{
|
||||
Item targetItem = damageTarget as Item;
|
||||
if (targetItem.body?.FarseerBody != null) targetBodies.Add(targetItem.body.FarseerBody);
|
||||
}
|
||||
|
||||
if (targetBodies != null)
|
||||
{
|
||||
ContactEdge contactEdge = body.FarseerBody.ContactList;
|
||||
while (contactEdge != null)
|
||||
{
|
||||
if (contactEdge.Contact != null &&
|
||||
contactEdge.Contact.IsTouching &&
|
||||
targetBodies.Any(b => b == contactEdge.Contact.FixtureA?.Body || b == contactEdge.Contact.FixtureB?.Body))
|
||||
{
|
||||
wasHit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
contactEdge = contactEdge.Next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wasHit)
|
||||
{
|
||||
if (AttackTimer >= attack.Duration && damageTarget != null)
|
||||
{
|
||||
attack.DoDamage(character, damageTarget, WorldPosition, 1.0f, (SoundTimer <= 0.0f));
|
||||
|
||||
SoundTimer = SoundInterval;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user