Merge branch 'master' into moStuff

This commit is contained in:
Joonas Rikkonen
2018-01-09 10:38:06 +02:00
committed by GitHub
179 changed files with 116633 additions and 3529 deletions
@@ -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;
}
@@ -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;
}
}
@@ -13,13 +13,15 @@ namespace Barotrauma
{
public string Text;
public Color Color;
public bool IsCommand;
public readonly string Time;
public ColoredText(string text, Color color)
public ColoredText(string text, Color color, bool isCommand)
{
this.Text = text;
this.Color = color;
this.IsCommand = isCommand;
Time = DateTime.Now.ToString();
}
@@ -44,6 +46,8 @@ namespace Barotrauma
/// </summary>
private Action<Client, Vector2, string[]> onClientRequestExecute;
public Func<string[][]> GetValidArgs;
public bool RelayToServer
{
get { return onClientExecute == null; }
@@ -54,7 +58,7 @@ namespace Barotrauma
/// <param name="onExecute">The default action when executing the command.</param>
/// <param name="onClientExecute">The action when a client attempts to execute the command. If null, the command is relayed to the server as-is.</param>
/// <param name="onClientRequestExecute">The server-side action when a client requests executing the command. If null, the default action is executed.</param>
public Command(string name, string help, Action<string[]> onExecute, Action<string[]> onClientExecute, Action<Client, Vector2, string[]> onClientRequestExecute)
public Command(string name, string help, Action<string[]> onExecute, Action<string[]> onClientExecute, Action<Client, Vector2, string[]> onClientRequestExecute, Func<string[][]> getValidArgs = null)
{
names = name.Split('|');
this.help = help;
@@ -62,19 +66,23 @@ namespace Barotrauma
this.onExecute = onExecute;
this.onClientExecute = onClientExecute;
this.onClientRequestExecute = onClientRequestExecute;
this.GetValidArgs = getValidArgs;
}
/// <summary>
/// Use this constructor to create a command that executes the same action regardless of whether it's executed by a client or the server.
/// </summary>
public Command(string name, string help, Action<string[]> onExecute)
public Command(string name, string help, Action<string[]> onExecute, Func<string[][]> getValidArgs = null)
{
names = name.Split('|');
this.help = help;
this.onExecute = onExecute;
this.onClientExecute = onExecute;
this.GetValidArgs = getValidArgs;
}
public void Execute(string[] args)
@@ -194,7 +202,7 @@ namespace Barotrauma
UpdaterUtil.SaveFileList("filelist.xml");
}));
commands.Add(new Command("spawn|spawncharacter", "spawn [creaturename] [near/inside/outside]: Spawn a creature at a random spawnpoint (use the second parameter to only select spawnpoints near/inside/outside the submarine).", (string[] args) =>
commands.Add(new Command("spawn|spawncharacter", "spawn [creaturename] [near/inside/outside/cursor]: Spawn a creature at a random spawnpoint (use the second parameter to only select spawnpoints near/inside/outside the submarine).", (string[] args) =>
{
string errorMsg;
SpawnCharacter(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), out errorMsg);
@@ -212,6 +220,20 @@ namespace Barotrauma
{
ThrowError(errorMsg);
}
},
() =>
{
List<string> characterFiles = GameMain.Config.SelectedContentPackage.GetFilesOfType(ContentType.Character);
for (int i = 0; i < characterFiles.Count; i++)
{
characterFiles[i] = Path.GetFileNameWithoutExtension(characterFiles[i]).ToLowerInvariant();
}
return new string[][]
{
characterFiles.ToArray(),
new string[] { "near", "inside", "outside", "cursor" }
};
}));
commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory]: Spawn an item at the position of the cursor, in the inventory of the controlled character or at a random spawnpoint if the last parameter is omitted.",
@@ -233,6 +255,21 @@ namespace Barotrauma
{
ThrowError(errorMsg);
}
},
() =>
{
List<string> itemNames = new List<string>();
foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
{
ItemPrefab itemPrefab = prefab as ItemPrefab;
if (itemPrefab != null) itemNames.Add(itemPrefab.Name);
}
return new string[][]
{
itemNames.ToArray(),
new string[] { "cursor", "inventory" }
};
}));
commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
@@ -570,6 +607,15 @@ namespace Barotrauma
{
GameMain.NetworkMember.KickPlayer(playerName, reason);
});
},
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray()
};
}));
commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) =>
@@ -615,6 +661,15 @@ namespace Barotrauma
GameMain.NetworkMember.BanPlayer(clientName, reason, false, banDuration);
});
});
},
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray()
};
}));
commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) =>
@@ -728,6 +783,13 @@ namespace Barotrauma
tpCharacter.Submarine = null;
tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cursorWorldPos));
tpCharacter.AnimController.FindHull(cursorWorldPos, true);
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("godmode", "godmode: Toggle submarine godmode. Makes the main submarine invulnerable to damage.", (string[] args) =>
@@ -810,6 +872,13 @@ namespace Barotrauma
healedCharacter.Bleeding = 0.0f;
healedCharacter.SetStun(0.0f, true);
}
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("revive", "revive [character name]: Bring the specified character back from the dead. If the name parameter is omitted, the controlled character will be revived.", (string[] args) =>
@@ -866,6 +935,13 @@ namespace Barotrauma
break;
}
}
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("freeze", "", (string[] args) =>
@@ -894,6 +970,13 @@ namespace Barotrauma
{
ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
}
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("freecamera|freecam", "freecam: Detach the camera from the controlled character.", (string[] args) =>
@@ -1067,6 +1150,16 @@ namespace Barotrauma
var character = FindMatchingCharacter(argsRight, false);
GameMain.Server.SetClientCharacter(client, character);
},
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("campaigninfo|campaignstatus", "campaigninfo: Display information about the state of the currently active campaign.", (string[] args) =>
@@ -1267,28 +1360,80 @@ namespace Barotrauma
public static string AutoComplete(string command)
{
if (string.IsNullOrWhiteSpace(currentAutoCompletedCommand))
{
currentAutoCompletedCommand = command;
}
string[] splitCommand = SplitCommand(command);
string[] args = splitCommand.Skip(1).ToArray();
List<string> matchingCommands = new List<string>();
foreach (Command c in commands)
//if an argument is given or the last character is a space, attempt to autocomplete the argument
if (args.Length > 0 || (command.Length > 0 && command.Last() == ' '))
{
foreach (string name in c.names)
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommand[0]));
if (matchingCommand == null || matchingCommand.GetValidArgs == null) return command;
int autoCompletedArgIndex = args.Length > 0 && command.Last() != ' ' ? args.Length - 1 : args.Length;
//get all valid arguments for the given command
string[][] allArgs = matchingCommand.GetValidArgs();
if (allArgs == null || allArgs.GetLength(0) < autoCompletedArgIndex + 1) return command;
if (string.IsNullOrEmpty(currentAutoCompletedCommand))
{
if (currentAutoCompletedCommand.Length > name.Length) continue;
if (currentAutoCompletedCommand == name.Substring(0, currentAutoCompletedCommand.Length))
currentAutoCompletedCommand = autoCompletedArgIndex > args.Length - 1 ? " " : args.Last();
}
//find all valid autocompletions for the given argument
string[] validArgs = allArgs[autoCompletedArgIndex].Where(arg =>
currentAutoCompletedCommand.Trim().Length <= arg.Length &&
arg.Substring(0, currentAutoCompletedCommand.Trim().Length).ToLower() == currentAutoCompletedCommand.Trim().ToLower()).ToArray();
if (validArgs.Length == 0) return command;
currentAutoCompletedIndex = currentAutoCompletedIndex % validArgs.Length;
string autoCompletedArg = validArgs[currentAutoCompletedIndex++];
//add quotation marks to args that contain spaces
if (autoCompletedArg.Contains(' ')) autoCompletedArg = '"' + autoCompletedArg + '"';
for (int i = 0; i < splitCommand.Length; i++)
{
if (splitCommand[i].Contains(' ')) splitCommand[i] = '"' + splitCommand[i] + '"';
}
return string.Join(" ", autoCompletedArgIndex >= args.Length ? splitCommand : splitCommand.Take(splitCommand.Length - 1)) + " " + autoCompletedArg;
}
else
{
if (string.IsNullOrWhiteSpace(currentAutoCompletedCommand))
{
currentAutoCompletedCommand = command;
}
List<string> matchingCommands = new List<string>();
foreach (Command c in commands)
{
foreach (string name in c.names)
{
matchingCommands.Add(name);
if (currentAutoCompletedCommand.Length > name.Length) continue;
if (currentAutoCompletedCommand == name.Substring(0, currentAutoCompletedCommand.Length))
{
matchingCommands.Add(name);
}
}
}
if (matchingCommands.Count == 0) return command;
currentAutoCompletedIndex = currentAutoCompletedIndex % matchingCommands.Count;
return matchingCommands[currentAutoCompletedIndex++];
}
}
if (matchingCommands.Count == 0) return command;
currentAutoCompletedIndex = currentAutoCompletedIndex % matchingCommands.Count;
return matchingCommands[currentAutoCompletedIndex++];
private static string AutoCompleteStr(string str, IEnumerable<string> validStrings)
{
if (string.IsNullOrEmpty(str)) return str;
foreach (string validStr in validStrings)
{
if (validStr.Length > str.Length && validStr.Substring(0, str.Length) == str) return validStr;
}
return str;
}
public static void ResetAutoComplete()
@@ -1303,9 +1448,14 @@ namespace Barotrauma
direction = MathHelper.Clamp(direction, -1, 1);
selectedIndex += direction;
if (selectedIndex < 0) selectedIndex = Messages.Count - 1;
selectedIndex = selectedIndex % Messages.Count;
int i = 0;
do
{
selectedIndex += direction;
if (selectedIndex < 0) selectedIndex = Messages.Count - 1;
selectedIndex = selectedIndex % Messages.Count;
if (++i >= Messages.Count) break;
} while (!Messages[selectedIndex].IsCommand);
return Messages[selectedIndex].Text;
}
@@ -1317,7 +1467,7 @@ namespace Barotrauma
#if CLIENT
activeQuestionText = null;
#endif
NewMessage(command, Color.White);
NewMessage(command, Color.White, true);
//reset the variable before invoking the delegate because the method may need to activate another question
var temp = activeQuestionCallback;
activeQuestionCallback = null;
@@ -1331,7 +1481,7 @@ namespace Barotrauma
if (!splitCommand[0].ToLowerInvariant().Equals("admin"))
{
NewMessage(command, Color.White);
NewMessage(command, Color.White, true);
}
#if CLIENT
@@ -1611,12 +1761,12 @@ namespace Barotrauma
}
}
public static void NewMessage(string msg, Color color)
public static void NewMessage(string msg, Color color, bool isCommand = false)
{
if (string.IsNullOrEmpty((msg))) return;
#if SERVER
Messages.Add(new ColoredText(msg, color));
Messages.Add(new ColoredText(msg, color, isCommand));
//TODO: REMOVE
Console.ForegroundColor = XnaToConsoleColor.Convert(color);
@@ -1631,7 +1781,7 @@ namespace Barotrauma
#elif CLIENT
lock (queuedMessages)
{
queuedMessages.Enqueue(new ColoredText(msg, color));
queuedMessages.Enqueue(new ColoredText(msg, color, isCommand));
}
#endif
}
@@ -16,43 +16,39 @@ namespace Barotrauma
public void Greet(GameServer server, string codeWords, string codeResponse)
{
//Greeting messages TODO: Move this to a function in Traitor class
string greetingMessage = "You are the Traitor! Your secret task is to assassinate " + TargetCharacter.Name + "! Discretion is an utmost concern; sinking the submarine and killing the entire crew "
+ "will arouse suspicion amongst the Fleet. If possible, make the death look like an accident.";
string moreAgentsMessage = "It is possible that there are other agents on this submarine. You don't know their names, but you do have a method of communication. "
+ "Use the code words to greet the agent and code response to respond. Disguise such words in a normal-looking phrase so the crew doesn't suspect anything.";
moreAgentsMessage += "\nThe code words are: " + codeWords + ".";
moreAgentsMessage += "\nThe code response is: " + codeResponse + ".\n";
string greetingMessage = TextManager.Get("TraitorStartMessage").Replace("[targetname]", TargetCharacter.Name);
string moreAgentsMessage = TextManager.Get("TraitorMoreAgentsMessage")
.Replace("[codewords]", codeWords)
.Replace("[coderesponse]", codeResponse);
if (server.Character != Character)
{
var chatMsg = ChatMessage.Create(
null,
greetingMessage + "\n" + moreAgentsMessage,
(ChatMessageType)ChatMessageType.Server,
null);
var msgBox = ChatMessage.Create(
null,
"There might be other agents. Use these to communicate with them." +
"\nThe code words are: " + codeWords + "." +
"\nThe code response is: " + codeResponse + ".",
(ChatMessageType)ChatMessageType.MessageBox,
null);
var greetingChatMsg = ChatMessage.Create(null, greetingMessage, ChatMessageType.Server, null);
var moreAgentsChatMsg = ChatMessage.Create(null, moreAgentsMessage, ChatMessageType.Server, null);
var greetingMsgBox = ChatMessage.Create(null, greetingMessage, ChatMessageType.MessageBox, null);
var moreAgentsMsgBox = ChatMessage.Create(null, moreAgentsMessage, ChatMessageType.MessageBox, null);
Client client = server.ConnectedClients.Find(c => c.Character == Character);
GameMain.Server.SendChatMessage(chatMsg, client);
GameMain.Server.SendChatMessage(msgBox, client);
GameMain.Server.SendChatMessage(greetingChatMsg, client);
GameMain.Server.SendChatMessage(moreAgentsChatMsg, client);
GameMain.Server.SendChatMessage(greetingMsgBox, client);
GameMain.Server.SendChatMessage(moreAgentsMsgBox, client);
}
#if CLIENT
if (server.Character == null)
{
new GUIMessageBox("New traitor", Character.Name + " is the traitor and the target is " + TargetCharacter.Name+".");
new GUIMessageBox(
TextManager.Get("NewTraitor"),
TextManager.Get("TraitorStartMessageServer").Replace("[targetname]", TargetCharacter.Name).Replace("[traitorname]", Character.Name));
}
else if (server.Character == Character)
{
TraitorManager.CreateStartPopUp(TargetCharacter.Name);
new GUIMessageBox("", greetingMessage);
new GUIMessageBox("", moreAgentsMessage);
GameMain.NetworkMember.AddChatMessage(greetingMessage, ChatMessageType.Server);
GameMain.NetworkMember.AddChatMessage(moreAgentsMessage, ChatMessageType.Server);
return;
}
@@ -151,72 +147,43 @@ namespace Barotrauma
{
Character traitorCharacter = traitor.Character;
Character targetCharacter = traitor.TargetCharacter;
endMessage += traitorCharacter.Name + " was a traitor! ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + targetCharacter.Name;
string messageTag;
if (targetCharacter.IsDead) //Partial or complete mission success
{
endMessage += ". The task was successful";
if (traitorCharacter.IsDead)
{
endMessage += ", but luckily the bastard didn't make it out alive either.";
messageTag = "TraitorEndMessageSuccessTraitorDead";
}
else if (traitorCharacter.LockHands)
{
endMessage += ", but ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
endMessage += " was successfuly detained.";
messageTag = "TraitorEndMessageSuccessTraitorDetained";
}
else
endMessage += ".";
messageTag = "TraitorEndMessageSuccess";
}
else //Partial or complete failure
{
if (traitorCharacter.IsDead)
{
endMessage += ", but ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
endMessage += " got " + ((traitorCharacter.Info.Gender == Gender.Male) ? "himself" : "herself");
endMessage += " killed before completing it.";
messageTag = "TraitorEndMessageFailureTraitorDead";
}
else if (traitorCharacter.LockHands)
{
messageTag = "TraitorEndMessageFailureTraitorDetained";
}
else
{
endMessage += ". The task was unsuccessful";
if (traitorCharacter.LockHands)
{
endMessage += " - ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
endMessage += " was successfuly detained";
}
if (Submarine.MainSub.AtEndPosition)
{
endMessage += (traitorCharacter.LockHands ? " and " : " - ");
endMessage += "the submarine has reached its destination";
}
endMessage += ".";
messageTag = "TraitorEndMessageFailure";
}
}
endMessage += "\n";
endMessage += (TextManager.ReplaceGenderPronouns(TextManager.Get(messageTag), traitorCharacter.Info.Gender) + "\n")
.Replace("[traitorname]", traitorCharacter.Name)
.Replace("[targetname]", targetCharacter.Name);
}
return endMessage;
return endMessage;
}
//public void CharacterLeft(Character character)
//{
// if (character != traitorCharacter && character != targetCharacter) return;
// if (character == traitorCharacter)
// {
// string endMessage = "The traitor has disconnected from the server.";
// End(endMessage);
// }
// else if (character == targetCharacter)
// {
// string endMessage = "The traitor's target has disconnected from the server.";
// End(endMessage);
// }
//}
}
}
@@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
static class InfoTextManager
{
private static Dictionary<string, List<string>> infoTexts;
static InfoTextManager()
{
LoadInfoTexts(Path.Combine("Content", "InfoTexts.xml"));
}
private static void LoadInfoTexts(string file)
{
infoTexts = new Dictionary<string, List<string>>();
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc == null || doc.Root == null) return;
foreach (XElement subElement in doc.Root.Elements())
{
string infoName = subElement.Name.ToString().ToLowerInvariant();
List<string> infoList = null;
if (!infoTexts.TryGetValue(infoName, out infoList))
{
infoList = new List<string>();
infoTexts.Add(infoName, infoList);
}
infoList.Add(subElement.ElementInnerText());
}
}
public static string GetInfoText(string infoName)
{
List<string> infoList = null;
if (!infoTexts.TryGetValue(infoName.ToLowerInvariant(), out infoList) || !infoList.Any())
{
#if DEBUG
return "Info text \"" + infoName + "\" not found";
#else
return "";
#endif
}
string text = infoList[Rand.Int(infoList.Count)];
#if CLIENT
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
{
text = text.Replace("[" + inputType.ToString() + "]", GameMain.Config.KeyBind(inputType).ToString());
}
#endif
if (Submarine.MainSub != null) text = text.Replace("[sub]", Submarine.MainSub.Name);
if (GameMain.GameSession != null && GameMain.GameSession.StartLocation != null)
{
text = text.Replace("[location]", GameMain.GameSession.StartLocation.Name);
}
return text;
}
}
}
@@ -454,7 +454,7 @@ namespace Barotrauma.Items.Components
float[] skillSuccess = new float[requiredSkills.Count];
for (int i = 0; i < requiredSkills.Count; i++ )
for (int i = 0; i < requiredSkills.Count; i++)
{
int characterLevel = character.GetSkillLevel(requiredSkills[i].Name);
@@ -463,7 +463,7 @@ namespace Barotrauma.Items.Components
float average = skillSuccess.Average();
return (average+100.0f)/2.0f;
return (average + 100.0f) / 2.0f;
}
public virtual void FlipX() { }
@@ -570,6 +570,7 @@ namespace Barotrauma.Items.Components
case "requireditem":
if (!overrideRequiredItems) requiredItems.Clear();
overrideRequiredItems = true;
RelatedItem newRequiredItem = RelatedItem.Load(subElement);
if (newRequiredItem == null) continue;
@@ -3,6 +3,7 @@ using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
@@ -141,6 +142,9 @@ namespace Barotrauma.Items.Components
public float AvailableFuel { get; set; }
private float availableHeat, availableCooling;
private float prevTemperature, temperatureChange;
[Serialize(500.0f, true)]
public float ShutDownTemp
{
@@ -191,12 +195,20 @@ namespace Barotrauma.Items.Components
fissionRate = Math.Min(fissionRate, AvailableFuel);
float heat = 80 * fissionRate * (AvailableFuel / 2000.0f);
float heatDissipation = 50 * coolingRate + Math.Max(ExtraCooling, 5.0f);
//the amount of cooling is always non-zero, so that the reactor always needs
//to generate some amount of heat to prevent the temperature from dropping
availableCooling = Math.Max(ExtraCooling, 5.0f);
availableHeat = 80 * (AvailableFuel / 2000.0f);
float deltaTemp = (((heat - heatDissipation) * 5) - temperature) / 10000.0f;
float heat = availableHeat * fissionRate;
float heatDissipation = 50 * coolingRate + availableCooling;
float deltaTemp = (((heat - heatDissipation) * 5) - temperature) / 10000.0f;
Temperature = temperature + deltaTemp;
temperatureChange = Temperature - prevTemperature;
prevTemperature = temperature;
if (temperature > fireTemp && temperature - deltaTemp < fireTemp)
{
#if CLIENT
@@ -345,37 +357,71 @@ namespace Barotrauma.Items.Components
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
{
float degreeOfSuccess = DegreeOfSuccess(character);
//characters with insufficient skill levels don't refuel the reactor
if (degreeOfSuccess > 0.2f)
{
//remove used-up fuel from the reactor
var containedItems = item.ContainedItems;
foreach (Item item in containedItems)
{
if (item != null && item.Condition <= 0.0f)
{
item.Drop();
}
}
//the temperature is too low and not increasing even though the fission rate is high and cooling low
// -> we need more fuel
if (temperature < load * 0.5f && temperatureChange <= 0.0f && fissionRate > 0.9f && coolingRate < 0.1f)
{
var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "Fuel Rod", "reactorfuel" }, item.GetComponent<ItemContainer>());
containFuelObjective.MinContainedAmount = containedItems.Count(i => i != null && i.Prefab.NameMatches("Fuel Rod") || i.HasTag("reactorfuel")) + 1;
containFuelObjective.GetItemPriority = (Item fuelItem) =>
{
if (fuelItem.ParentInventory?.Owner is Item)
{
//don't take fuel from other reactors
if (((Item)fuelItem.ParentInventory.Owner).GetComponent<Reactor>() != null) return 0.0f;
}
return 1.0f;
};
objective.AddSubObjective(containFuelObjective);
return false;
}
}
switch (objective.Option.ToLowerInvariant())
{
case "power up":
float tempDiff = load - temperature;
{
case "power up":
float tempDiff = load - temperature;
shutDownTemp = Math.Min(load + 1000.0f, 7500.0f);
shutDownTemp = Math.Min(load + 1000.0f, 7500.0f);
//temperature too high/low
if (Math.Abs(tempDiff)>500.0f)
{
AutoTemp = false;
FissionRate += deltaTime * 100.0f * Math.Sign(tempDiff);
CoolingRate -= deltaTime * 100.0f * Math.Sign(tempDiff);
}
//temperature OK
else
{
AutoTemp = true;
}
//characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
if (Math.Abs(tempDiff) < 500.0f || degreeOfSuccess < 0.5f)
{
AutoTemp = true;
}
else
{
AutoTemp = false;
//higher skill levels make the character adjust the temperature faster
FissionRate += deltaTime * 100.0f * Math.Sign(tempDiff) * degreeOfSuccess;
CoolingRate -= deltaTime * 100.0f * Math.Sign(tempDiff) * degreeOfSuccess;
}
break;
case "shutdown":
shutDownTemp = 0.0f;
break;
}
break;
case "shutdown":
shutDownTemp = 0.0f;
break;
}
return false;
return false;
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power)
{
switch (connection.Name)
@@ -98,7 +98,6 @@ namespace Barotrauma.Items.Components
: base(item, element)
{
IsActive = true;
InitProjSpecific();
}
@@ -128,8 +127,10 @@ namespace Barotrauma.Items.Components
unsentChanges = false;
}
}
if (voltage < minVoltage && powerConsumption > 0.0f) return;
currPowerConsumption = powerConsumption;
if (voltage < minVoltage && currPowerConsumption > 0.0f) return;
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
@@ -9,9 +9,7 @@ namespace Barotrauma.Items.Components
{
static float fullPower;
static float fullLoad;
//private bool updated;
private int updateTimer;
const float FireProbability = 0.15f;
@@ -19,35 +17,82 @@ namespace Barotrauma.Items.Components
//affects how fast changes in power/load are carried over the grid
static float inertia = 5.0f;
static HashSet<Powered> connectedList = new HashSet<Powered>();
private HashSet<PowerTransfer> connectedPoweredList = new HashSet<PowerTransfer>();
private List<Connection> powerConnections;
private Dictionary<Connection, bool> connectionDirty = new Dictionary<Connection, bool>();
//a list of connections a given connection is connected to, either directly or via other power transfer components
private Dictionary<Connection, HashSet<Connection>> connectedRecipients = new Dictionary<Connection, HashSet<Connection>>();
private float powerLoad;
private bool isBroken;
public float PowerLoad
{
get { return powerLoad; }
}
//can the component transfer power
public virtual bool CanTransfer
private bool canTransfer;
public bool CanTransfer
{
get { return IsActive; }
get { return canTransfer; }
set
{
if (canTransfer == value) return;
canTransfer = value;
SetAllConnectionsDirty();
}
}
public override bool IsActive
{
get
{
return base.IsActive;
}
set
{
if (base.IsActive != value) SetAllConnectionsDirty();
base.IsActive = value;
}
}
public PowerTransfer(Item item, XElement element)
: base(item, element)
{
IsActive = true;
canTransfer = true;
powerConnections = new List<Connection>();
}
public override void UpdateBroken(float deltaTime, Camera cam)
{
base.UpdateBroken(deltaTime, cam);
if (!isBroken)
{
SetAllConnectionsDirty();
isBroken = true;
}
}
public override void Update(float deltaTime, Camera cam)
{
if (!CanTransfer) return;
if (isBroken)
{
SetAllConnectionsDirty();
isBroken = false;
}
RefreshConnections();
if (updateTimer > 0)
{
//this junction box has already been updated this frame
@@ -60,16 +105,13 @@ namespace Barotrauma.Items.Components
fullPower = 0.0f;
fullLoad = 0.0f;
connectedList.Clear();
connectedPoweredList.Clear();
CheckJunctions(deltaTime);
CheckPower(deltaTime);
updateTimer = 0;
foreach (Powered p in connectedList)
{
PowerTransfer pt = p as PowerTransfer;
if (pt == null) continue;
foreach (PowerTransfer pt in connectedPoweredList)
{
pt.powerLoad += (fullLoad - pt.powerLoad) / inertia;
pt.currPowerConsumption += (-fullPower - pt.currPowerConsumption) / inertia;
pt.Item.SendSignal(0, "", "power", null, fullPower / Math.Max(fullLoad, 1.0f));
@@ -112,39 +154,99 @@ namespace Barotrauma.Items.Components
return picker != null;
}
private void RefreshConnections()
{
var connections = item.Connections;
foreach (Connection c in connections)
{
if (!connectionDirty[c]) continue;
HashSet<Connection> connected = new HashSet<Connection>();
if (!connectedRecipients.ContainsKey(c))
{
connectedRecipients.Add(c, connected);
}
else
{
//mark all previous recipients as dirty
foreach (Connection recipient in connectedRecipients[c])
{
var pt = recipient.Item.GetComponent<PowerTransfer>();
if (pt != null) pt.connectionDirty[recipient] = true;
}
}
//find all connections that are connected to this one (directly or via another PowerTransfer)
connected.Add(c);
GetConnected(c, connected);
connectedRecipients[c] = connected;
//go through all the PowerTransfers and we're connected to and set their connections to match the ones we just calculated
//(no need to go through the recursive GetConnected method again)
foreach (Connection recipient in connected)
{
var recipientPowerTransfer = recipient.Item.GetComponent<PowerTransfer>();
if (recipientPowerTransfer == null) continue;
if (!connectedRecipients.ContainsKey(recipient))
{
connectedRecipients.Add(recipient, connected);
}
recipientPowerTransfer.connectedRecipients[recipient] = connected;
recipientPowerTransfer.connectionDirty[recipient] = false;
}
}
}
//Finds all the connections that can receive a signal sent into the given connection and stores them in the hashset.
private void GetConnected(Connection c, HashSet<Connection> connected)
{
var recipients = c.Recipients;
foreach (Connection recipient in recipients)
{
if (recipient == null || connected.Contains(recipient)) continue;
Item it = recipient.Item;
if (it == null || it.Condition <= 0.0f) continue;
connected.Add(recipient);
var powerTransfer = it.GetComponent<PowerTransfer>();
if (powerTransfer != null && powerTransfer.CanTransfer && powerTransfer.IsActive)
{
GetConnected(recipient, connected);
}
}
}
//a recursive function that goes through all the junctions and adds up
//all the generated/consumed power of the constructions connected to the grid
private void CheckJunctions(float deltaTime)
private void CheckPower(float deltaTime)
{
updateTimer = 1;
connectedList.Add(this);
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
connectedPoweredList.Clear();
foreach (Connection c in powerConnections)
{
var recipients = c.Recipients;
HashSet<Connection> recipients = connectedRecipients[c];
foreach (Connection recipient in recipients)
{
if (recipient == null) continue;
Item it = recipient.Item;
if (it == null) continue;
if (it.Condition <= 0.0f) continue;
if (it == null || it.Condition <= 0.0f) continue;
foreach (Powered powered in it.GetComponents<Powered>())
{
if (powered == null || !powered.IsActive) continue;
if (connectedList.Contains(powered)) continue;
PowerTransfer powerTransfer = powered as PowerTransfer;
if (powerTransfer != null)
{
if (!powerTransfer.CanTransfer) continue;
powerTransfer.CheckJunctions(deltaTime);
connectedPoweredList.Add(powerTransfer);
powerTransfer.updateTimer = 1;
continue;
}
@@ -162,25 +264,38 @@ namespace Barotrauma.Items.Components
}
else
{
connectedList.Add(powered);
//positive power consumption = the construction requires power -> increase load
if (powered.CurrPowerConsumption > 0.0f)
{
fullLoad += powered.CurrPowerConsumption;
}
else if (powered.CurrPowerConsumption < 0.0f)
//negative power consumption = the construction is a
//generator/battery or another junction box
//negative power consumption = the construction is a /generator/battery
{
fullPower -= powered.CurrPowerConsumption;
}
}
}
}
}
}
public void SetAllConnectionsDirty()
{
if (item.Connections == null) return;
foreach (Connection c in item.Connections)
{
connectionDirty[c] = true;
}
}
public void SetConnectionDirty(Connection connection)
{
var connections = item.Connections;
if (connections == null || !connections.Contains(connection)) return;
connectionDirty[connection] = true;
}
public override void OnMapLoaded()
{
var connections = item.Connections;
@@ -189,18 +304,38 @@ namespace Barotrauma.Items.Components
IsActive = false;
return;
}
powerConnections = connections.FindAll(c => c.IsPower);
if (powerConnections.Count == 0) IsActive = false;
SetAllConnectionsDirty();
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power)
{
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power);
if (!connectedRecipients.ContainsKey(connection)) return;
if (connection.Name.Length > 5 && connection.Name.Substring(0, 6).ToLowerInvariant() == "signal")
{
connection.SendSignal(stepsTaken, signal, source, sender, 0.0f);
foreach (Connection recipient in connectedRecipients[connection])
{
if (recipient.Item == item || recipient.Item == source) continue;
foreach (ItemComponent ic in recipient.Item.components)
{
//powertransfer components don't need to receive the signal because we relay it straight
//to the connected items without going through the whole chain of junction boxes
if (ic is PowerTransfer) continue;
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, 0.0f);
}
foreach (StatusEffect effect in recipient.effects)
{
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
}
}
}
}
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
private static Wire draggingConnected;
private List<StatusEffect> effects;
public readonly List<StatusEffect> effects;
public readonly ushort[] wireId;
@@ -167,8 +167,6 @@ namespace Barotrauma.Items.Components
foreach (StatusEffect effect in recipient.effects)
{
//effect.Apply(ActionType.OnUse, 1.0f, recipient.item, recipient.item);
recipient.item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
}
}
@@ -31,21 +31,14 @@ namespace Barotrauma.Items.Components
set
{
isOn = value;
CanTransfer = value;
if (!isOn)
{
currPowerConsumption = 0.0f;
}
}
}
public override bool CanTransfer
{
get
{
return isOn;
}
}
public RelayComponent(Item item, XElement element)
: base (item, element)
{
@@ -30,21 +30,13 @@ namespace Barotrauma.Items.Components
const float nodeDistance = 32.0f;
const float heightFromFloor = 128.0f;
static Sprite wireSprite;
private List<Vector2> nodes;
private List<WireSection> sections;
Connection[] connections;
private Connection[] connections;
private Vector2 newNodePos;
private static Wire draggingWire;
private static int? selectedNodeIndex;
private static int? highlightedNodeIndex;
public bool Hidden, Locked;
public Connection[] Connections
@@ -55,12 +47,14 @@ namespace Barotrauma.Items.Components
public Wire(Item item, XElement element)
: base(item, element)
{
#if CLIENT
if (wireSprite == null)
{
wireSprite = new Sprite("Content/Items/wireHorizontal.png", new Vector2(0.5f, 0.5f));
wireSprite.Depth = 0.85f;
}
#endif
nodes = new List<Vector2>();
sections = new List<WireSection>();
@@ -86,14 +80,15 @@ namespace Barotrauma.Items.Components
public void RemoveConnection(Item item)
{
for (int i = 0; i<2; i++)
for (int i = 0; i < 2; i++)
{
if (connections[i]==null || connections[i].Item!=item) continue;
for (int n = 0; n< connections[i].Wires.Length; n++)
if (connections[i] == null || connections[i].Item != item) continue;
for (int n = 0; n < connections[i].Wires.Length; n++)
{
if (connections[i].Wires[n] != this) continue;
SetConnectedDirty();
connections[i].Wires[n] = null;
}
connections[i] = null;
@@ -104,6 +99,8 @@ namespace Barotrauma.Items.Components
{
if (connection == connections[0]) connections[0] = null;
if (connection == connections[1]) connections[1] = null;
SetConnectedDirty();
}
public bool Connect(Connection newConnection, bool addNode = true, bool sendNetworkEvent = false)
@@ -137,8 +134,7 @@ namespace Barotrauma.Items.Components
if (newConnection.Item.Submarine == null) continue;
if (nodes.Count > 0 && nodes[0] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break;
if (nodes.Count > 1 && nodes[nodes.Count-1] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break;
if (nodes.Count > 1 && nodes[nodes.Count - 1] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break;
if (i == 0)
{
@@ -148,11 +144,12 @@ namespace Barotrauma.Items.Components
{
nodes.Add(newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition);
}
break;
}
SetConnectedDirty();
if (connections[0] != null && connections[1] != null)
{
foreach (ItemComponent ic in item.components)
@@ -323,6 +320,8 @@ namespace Barotrauma.Items.Components
connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
}
}
SetConnectedDirty();
for (int i = 0; i < 2; i++)
{
@@ -363,6 +362,18 @@ namespace Barotrauma.Items.Components
return position;
}
public void SetConnectedDirty()
{
for (int i = 0; i < 2; i++)
{
if (connections[i]?.Item != null)
{
var pt = connections[i].Item.GetComponent<PowerTransfer>();
if (pt != null) pt.SetConnectionDirty(connections[i]);
}
}
}
private void CleanNodes()
{
for (int i = nodes.Count - 2; i > 0; i--)
@@ -143,10 +143,21 @@ namespace Barotrauma
public Item FindItem(string itemName)
{
if (itemName == null) return null;
return Items.FirstOrDefault(i => i != null && (i.Prefab.NameMatches(itemName) || i.HasTag(itemName)));
}
public Item FindItem(string[] itemNames)
{
if (itemNames == null) return null;
foreach (string itemName in itemNames)
{
var item = FindItem(itemName);
if (item != null) return item;
}
return null;
}
public virtual void RemoveItem(Item item)
{
if (item == null) return;
@@ -1207,7 +1207,7 @@ namespace Barotrauma
Color color = Color.Red;
if (ic.HasRequiredSkills(character) && ic.HasRequiredItems(character, false)) color = Color.Orange;
texts.Add(new ColoredText(ic.Msg, color));
texts.Add(new ColoredText(ic.Msg, color, false));
}
return texts;
@@ -1608,7 +1608,7 @@ namespace Barotrauma
msg.Write(body.FarseerBody.Awake);
if (body.FarseerBody.Awake)
{
body.FarseerBody.Enabled = true;
body.Enabled = true;
msg.WriteRangedSingle(MathHelper.Clamp(body.LinearVelocity.X, -MaxVel, MaxVel), -MaxVel, MaxVel, 12);
msg.WriteRangedSingle(MathHelper.Clamp(body.LinearVelocity.Y, -MaxVel, MaxVel), -MaxVel, MaxVel, 12);
}
@@ -23,6 +23,7 @@ namespace Barotrauma
public Explosion(float range, float force, float damage, float structureDamage)
{
attack = new Attack(damage, structureDamage, 0.0f, range);
attack.SeverLimbsProbability = 1.0f;
this.force = force;
sparks = true;
shockwave = true;
@@ -118,7 +119,7 @@ namespace Barotrauma
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
bool wasDead = c.IsDead;
Dictionary<Limb, float> distFactors = new Dictionary<Limb, float>();
foreach (Limb limb in c.AnimController.Limbs)
{
float dist = Vector2.Distance(limb.WorldPosition, worldPosition);
@@ -134,6 +135,8 @@ namespace Barotrauma
//solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null) distFactor *= 0.1f;
distFactors.Add(limb, distFactor);
c.AddDamage(limb.WorldPosition, DamageType.None,
attack.GetDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
@@ -143,17 +146,27 @@ namespace Barotrauma
if (limb.WorldPosition != worldPosition && force > 0.0f)
{
limb.body.ApplyLinearImpulse(Vector2.Normalize(limb.WorldPosition - worldPosition) * distFactor * force);
Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
limb.body.ApplyLinearImpulse(limbDiff * distFactor * force, impulsePoint);
}
}
if (!wasDead && c.IsDead)
//sever joints
if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
{
foreach (LimbJoint joint in c.AnimController.LimbJoints)
foreach (Limb limb in c.AnimController.Limbs)
{
if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability)
if (!distFactors.ContainsKey(limb)) continue;
foreach (LimbJoint joint in c.AnimController.LimbJoints)
{
c.AnimController.SeverLimbJoint(joint);
if (joint.IsSevered || (joint.LimbA != limb && joint.LimbB != limb)) continue;
if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
{
c.AnimController.SeverLimbJoint(joint);
}
}
}
}
@@ -264,8 +264,8 @@ namespace Barotrauma
Vector2 nodeInterval = generationParams.MainPathNodeIntervalRange;
for (float x = startPosition.X + Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server);
x < endPosition.X - Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server);
for (float x = startPosition.X + nodeInterval.X;
x < endPosition.X - nodeInterval.X;
x += Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server))
{
pathNodes.Add(new Vector2(x, Rand.Range(pathBorders.Y, pathBorders.Bottom, Rand.RandSync.Server)));
@@ -275,7 +275,7 @@ namespace Barotrauma
if (pathNodes.Count <= 2)
{
pathNodes.Add((startPosition + endPosition) / 2);
pathNodes.Insert(1, borders.Center.ToVector2());
}
GenerateTunnels(pathNodes, minWidth);
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
/*
/*
* Created by SharpDevelop.
* User: Burhan
* Date: 17/06/2014
@@ -8,31 +8,31 @@
*/
/*
Copyright 2011 James Humphreys. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS\" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
Copyright 2011 James Humphreys. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS\" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
*/
/*
@@ -58,63 +58,63 @@ using System.Collections.Generic;
namespace Voronoi2
{
public class Point
{
public double x, y;
public void setPoint ( double x, double y )
{
this.x = x;
this.y = y;
}
}
// use for sites and vertecies
public class Site
{
public Point coord;
public int sitenbr;
{
public double x, y;
public void setPoint ( double x, double y )
{
this.x = x;
this.y = y;
}
}
// use for sites and vertecies
public class Site
{
public Point coord;
public int sitenbr;
public void SetPoint(Vector2 point)
{
coord.setPoint(point.X, point.Y);
}
public Site ()
{
coord = new Point();
}
}
public class Edge
{
public double a = 0, b = 0, c = 0;
public Site[] ep;
public Site[] reg;
public int edgenbr;
public Edge ()
{
ep = new Site[2];
reg = new Site[2];
}
}
public class Halfedge
{
public Halfedge ELleft, ELright;
public Edge ELedge;
public bool deleted;
public int ELpm;
public Site vertex;
public double ystar;
public Halfedge PQnext;
public Halfedge ()
{
PQnext = null;
}
}
public Site ()
{
coord = new Point();
}
}
public class Edge
{
public double a = 0, b = 0, c = 0;
public Site[] ep;
public Site[] reg;
public int edgenbr;
public Edge ()
{
ep = new Site[2];
reg = new Site[2];
}
}
public class Halfedge
{
public Halfedge ELleft, ELright;
public Edge ELedge;
public bool deleted;
public int ELpm;
public Site vertex;
public double ystar;
public Halfedge PQnext;
public Halfedge ()
{
PQnext = null;
}
}
public enum CellType
{
@@ -187,11 +187,11 @@ namespace Voronoi2
return true;
}
}
public class GraphEdge
{
public class GraphEdge
{
public Vector2 point1, point2;
public Site site1, site2;
public Site site1, site2;
public VoronoiCell cell1, cell2;
public bool isSolid;
@@ -239,20 +239,20 @@ namespace Voronoi2
return normal;
}
}
// للترتيب
public class SiteSorterYX : IComparer<Site>
{
public int Compare ( Site p1, Site p2 )
{
Point s1 = p1.coord;
Point s2 = p2.coord;
if ( s1.y < s2.y ) return -1;
if ( s1.y > s2.y ) return 1;
if ( s1.x < s2.x ) return -1;
if ( s1.x > s2.x ) return 1;
return 0;
}
}
}
// للترتيب
public class SiteSorterYX : IComparer<Site>
{
public int Compare ( Site p1, Site p2 )
{
Point s1 = p1.coord;
Point s2 = p2.coord;
if ( s1.y < s2.y ) return -1;
if ( s1.y > s2.y ) return 1;
if ( s1.x < s2.x ) return -1;
if ( s1.x > s2.x ) return 1;
return 0;
}
}
}
@@ -89,11 +89,11 @@ namespace Barotrauma
get { return false; }
}
protected bool ResizeHorizontal
public bool ResizeHorizontal
{
get { return prefab != null && prefab.ResizeHorizontal; }
}
protected bool ResizeVertical
public bool ResizeVertical
{
get { return prefab != null && prefab.ResizeVertical; }
}
@@ -89,6 +89,11 @@ namespace Barotrauma
get { return prefab.Body; }
}
public List<Body> Bodies
{
get { return bodies; }
}
public bool CastShadow
{
get { return prefab.CastShadow; }
@@ -272,7 +277,13 @@ namespace Barotrauma
public override MapEntity Clone()
{
return new Structure(rect, prefab, Submarine);
var clone = new Structure(rect, prefab, Submarine);
foreach (KeyValuePair<string, SerializableProperty> property in SerializableProperties)
{
if (!property.Value.Attributes.OfType<Editable>().Any()) continue;
clone.SerializableProperties[property.Key].TrySetValue(property.Value.GetValue());
}
return clone;
}
private void CreateStairBodies()
@@ -651,23 +662,28 @@ namespace Barotrauma
Vector2 transformedPos = worldPosition;
if (Submarine != null) transformedPos -= Submarine.Position;
int i = FindSectionIndex(transformedPos);
if (i == -1) return new AttackResult(0.0f, 0.0f);
float damageAmount = attack.GetStructureDamage(deltaTime);
AddDamage(i, damageAmount, attacker);
float damageAmount = 0.0f;
for (int i = 0; i < SectionCount; i++)
{
if (Vector2.DistanceSquared(SectionPosition(i, true), worldPosition) <= attack.DamageRange * attack.DamageRange)
{
damageAmount = attack.GetStructureDamage(deltaTime);
AddDamage(i, damageAmount, attacker);
#if CLIENT
GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
#endif
}
}
#if CLIENT
if (playSound)// && !SectionBodyDisabled(i))
{
string damageSoundType = (attack.DamageType == DamageType.Blunt) ? "StructureBlunt" : "StructureSlash";
SoundPlayer.PlayDamageSound(damageSoundType, damageAmount, worldPosition, tags: Tags);
}
#endif
return new AttackResult(damageAmount, 0.0f);
}
@@ -838,6 +854,7 @@ namespace Barotrauma
{
StairDirection = StairDirection == Direction.Left ? Direction.Right : Direction.Left;
bodies.ForEach(b => GameMain.World.RemoveBody(b));
bodies.Clear();
CreateStairBodies();
}
@@ -1208,9 +1208,9 @@ namespace Barotrauma
Item.ItemList.Clear();
}
PhysicsBody.RemoveAll();
Ragdoll.RemoveAll();
Ragdoll.list.Clear();
PhysicsBody.RemoveAll();
GameMain.World.Clear();
@@ -491,6 +491,22 @@ namespace Barotrauma
if (contactDot > 0.0f)
{
Body.LinearVelocity -= Vector2.Normalize(Body.LinearVelocity) * contactDot;
float damageAmount = contactDot * Body.Mass / limb.character.Mass;
Vector2 n;
FixedArray2<Vector2> contactPos;
contact.GetWorldManifold(out n, out contactPos);
limb.character.DamageLimb(ConvertUnits.ToDisplayUnits(contactPos[0]), limb, DamageType.Blunt, damageAmount, 0.0f, 0.0f, true, 0.0f);
if (limb.character.IsDead)
{
foreach (LimbJoint limbJoint in limb.character.AnimController.LimbJoints)
{
if (limbJoint.IsSevered || (limbJoint.LimbA != limb && limbJoint.LimbB != limb)) continue;
limb.character.AnimController.SeverLimbJoint(limbJoint);
}
}
}
}
@@ -150,6 +150,20 @@ namespace Barotrauma
wayPoint.Remove();
}
//find all open doors and temporarily activate their bodies to prevent visibility checks
//from ignoring the doors and generating waypoint connections that go straight through the door
List<Door> openDoors = new List<Door>();
foreach (Item item in Item.ItemList)
{
var door = item.GetComponent<Door>();
if (door != null && !door.Body.Enabled)
{
openDoors.Add(door);
door.Body.Enabled = true;
}
}
float minDist = 150.0f;
float heightFromFloor = 110.0f;
@@ -201,15 +215,15 @@ namespace Barotrauma
borders.Height += inflateAmount;
}
WayPoint[,] cornerWaypoint = new WayPoint[2,2];
for (int i = 0; i<2; i++)
WayPoint[,] cornerWaypoint = new WayPoint[2, 2];
for (int i = 0; i < 2; i++)
{
for (float x = borders.X + outSideWaypointInterval; x < borders.Right - outSideWaypointInterval; x += outSideWaypointInterval)
{
var wayPoint = new WayPoint(
new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
SpawnType.Path, submarine);
if (x == borders.X + outSideWaypointInterval)
@@ -218,13 +232,13 @@ namespace Barotrauma
}
else
{
wayPoint.ConnectTo(WayPoint.WayPointList[WayPointList.Count-2]);
wayPoint.ConnectTo(WayPointList[WayPointList.Count - 2]);
}
}
cornerWaypoint[i, 1] = WayPoint.WayPointList[WayPointList.Count - 1];
cornerWaypoint[i, 1] = WayPointList[WayPointList.Count - 1];
}
for (int i = 0; i < 2; i++)
{
WayPoint wayPoint = null;
@@ -300,7 +314,7 @@ namespace Barotrauma
while (prevPoint != ladderPoints[1])
{
var pickedBody = Submarine.PickBody(prevPos, ladderPoints[1].SimPosition, ignoredBodies);
var pickedBody = Submarine.PickBody(prevPos, ladderPoints[1].SimPosition, ignoredBodies, null, false);
if (pickedBody == null) break;
@@ -333,19 +347,7 @@ namespace Barotrauma
}
prevPoint.ConnectTo(ladderPoints[1]);
//for (float y = ladderPoints[0].Position.Y+100.0f; y < ladderPoints[1].Position.Y; y+=100.0f )
//{
// var midPoint = new WayPoint(new Vector2(item.Rect.Center.X, y), SpawnType.Path, Submarine.Loaded);
// midPoint.Ladders = ladders;
// midPoint.ConnectTo(prevPoint);
// prevPoint = midPoint;
//}
//ladderPoints[1].ConnectTo(prevPoint);
for (int i = 0; i < 2; i++)
{
ladderPoints[i].Ladders = ladders;
@@ -357,8 +359,6 @@ namespace Barotrauma
ladderPoints[i].ConnectTo(closest);
}
}
//ladderPoints[0].ConnectTo(ladderPoints[1]);
}
foreach (Gap gap in Gap.GapList)
@@ -394,7 +394,7 @@ namespace Barotrauma
if (gap.Rect.Width < 100.0f) continue;
var wayPoint = new WayPoint(
new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height/2), SpawnType.Path, submarine, gap);
new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height / 2), SpawnType.Path, submarine, gap);
for (int dir = -1; dir <= 1; dir += 2)
{
@@ -410,6 +410,12 @@ namespace Barotrauma
{
wp.Remove();
}
//re-disable the bodies of the doors that are supposed to be open
foreach (Door door in openDoors)
{
door.Body.Enabled = false;
}
}
private WayPoint FindClosest(int dir, bool horizontalSearch, Vector2 tolerance, Body ignoredBody = null)
@@ -443,7 +449,7 @@ namespace Barotrauma
float dist = Vector2.Distance(wp.Position, Position);
if (closest == null || dist < closestDist)
{
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, true, true);
var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, true, true, false);
if (body != null && body != ignoredBody && !(body.UserData is Submarine))
{
if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall)) continue;
@@ -166,11 +166,11 @@ namespace Barotrauma.Networking
if (c.ChatSpamCount > 3)
{
//kick for spamming too much
GameMain.Server.KickClient(c, "You have been kicked by the spam filter.");
GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked"));
}
else
{
ChatMessage denyMsg = ChatMessage.Create("", "You have been blocked by the spam filter. Try again after 10 seconds.", ChatMessageType.Server, null);
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendChatMessage(denyMsg, c);
}
@@ -181,7 +181,7 @@ namespace Barotrauma.Networking
if (c.ChatSpamTimer > 0.0f)
{
ChatMessage denyMsg = ChatMessage.Create("", "You have been blocked by the spam filter. Try again after 10 seconds.", ChatMessageType.Server, null);
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
c.ChatSpamTimer = 10.0f;
GameMain.Server.SendChatMessage(denyMsg, c);
return;
@@ -126,7 +126,7 @@ namespace Barotrauma.Networking
public static bool IsValidName(string name)
{
if (name.Contains("\n") || name.Contains("\r\n")) return false;
if (name.Contains("\n") || name.Contains("\r")) return false;
return (name.All(c =>
c != ';' &&
@@ -172,7 +172,7 @@ namespace Barotrauma.Networking
DebugConsole.NewMessage(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (invalid name)", Color.Red);
return;
}
if (clName.ToLower() == Name.ToLower())
if (Homoglyphs.Compare(clName.ToLower(),Name.ToLower()))
{
DisconnectUnauthClient(inc, unauthClient, "That name is taken.");
Log(clName + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (name taken by the server)", ServerLog.MessageType.Error);
@@ -18,7 +18,7 @@ namespace Barotrauma.Networking
{
No = 0, Maybe = 1, Yes = 2
}
partial class GameServer : NetworkMember, ISerializableEntity
{
private class SavedClientPermission
@@ -48,7 +48,7 @@ namespace Barotrauma.Networking
get;
private set;
}
public Dictionary<string, int> extraCargo;
public bool ShowNetStats;
@@ -57,24 +57,24 @@ namespace Barotrauma.Networking
private TimeSpan sparseUpdateInterval = new TimeSpan(0, 0, 0, 3);
private SelectionMode subSelectionMode, modeSelectionMode;
private bool registeredToMaster;
private WhiteList whitelist;
private BanList banList;
private string password;
public float AutoRestartTimer;
private bool autoRestart;
private bool isPublic;
private int maxPlayers;
private List<SavedClientPermission> clientPermissions = new List<SavedClientPermission>();
[Serialize(true, true)]
public bool RandomizeSeed
{
@@ -170,7 +170,7 @@ namespace Barotrauma.Networking
AutoRestartTimer = autoRestart ? AutoRestartInterval : 0.0f;
}
}
[Serialize(true, true)]
public bool AllowRespawn
{
@@ -193,7 +193,7 @@ namespace Barotrauma.Networking
{
get { return modeSelectionMode; }
}
public BanList BanList
{
get { return banList; }
@@ -234,7 +234,7 @@ namespace Barotrauma.Networking
private set;
}
[Serialize(false,true)]
[Serialize(false, true)]
public bool KarmaEnabled
{
get;
@@ -246,7 +246,7 @@ namespace Barotrauma.Networking
XDocument doc = new XDocument(new XElement("serversettings"));
SerializableProperty.SerializeProperties(this, doc.Root, true);
doc.Root.SetAttributeValue("name", name);
doc.Root.SetAttributeValue("public", isPublic);
doc.Root.SetAttributeValue("port", config.Port);
@@ -257,7 +257,7 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("SubSelection", subSelectionMode.ToString());
doc.Root.SetAttributeValue("ModeSelection", modeSelectionMode.ToString());
doc.Root.SetAttributeValue("TraitorsEnabled", TraitorsEnabled.ToString());
#if SERVER
@@ -272,7 +272,7 @@ namespace Barotrauma.Networking
{
doc.Root.SetAttributeValue("ServerMessage", GameMain.NetLobbyScreen.ServerMessageText);
}
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = true;
@@ -289,7 +289,7 @@ namespace Barotrauma.Networking
if (File.Exists(SettingsFile))
{
doc = XMLExtensions.TryLoadXml(SettingsFile);
}
}
if (doc == null || doc.Root == null)
{
@@ -318,13 +318,16 @@ namespace Barotrauma.Networking
Enum.TryParse<YesNoMaybe>(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled);
TraitorsEnabled = traitorsEnabled;
GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled);
if (GameMain.NetLobbyScreen != null
#if CLIENT
&& GameMain.NetLobbyScreen.ServerMessage != null
#endif
)
{
#if SERVER
GameMain.NetLobbyScreen.ServerName = doc.Root.GetAttributeString("name", "");
#endif
GameMain.NetLobbyScreen.ServerMessageText = doc.Root.GetAttributeString("ServerMessage", "");
}
@@ -349,9 +352,12 @@ namespace Barotrauma.Networking
{
clientPermissions.Clear();
if (File.Exists("Data/clientpermissions.txt") && !File.Exists(ClientPermissionsFile))
if (!File.Exists(ClientPermissionsFile))
{
LoadClientPermissionsOld("Data/clientpermissions.txt");
if (File.Exists("Data/clientpermissions.txt"))
{
LoadClientPermissionsOld("Data/clientpermissions.txt");
}
return;
}
@@ -1,4 +1,4 @@
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
@@ -94,9 +94,9 @@ namespace Barotrauma
}
}
class Key
{
private bool hit, hitQueue;
class Key
{
private bool hit, hitQueue;
private bool held, heldQueue;
@@ -106,23 +106,23 @@ namespace Barotrauma
//{
// get { return canBeHeld; }
//}
public Key(KeyOrMouse binding)
{
public Key(KeyOrMouse binding)
{
this.binding = binding;
}
}
public bool Hit
{
get
{
return hit;
}
set
{
hit = value;
}
}
public bool Hit
{
get
{
return hit;
}
set
{
hit = value;
}
}
public bool Held
{
@@ -141,14 +141,14 @@ namespace Barotrauma
get { return binding; }
}
public void SetState()
{
hit = binding.IsHit();
if (hit) hitQueue = true;
public void SetState()
{
hit = binding.IsHit();
if (hit) hitQueue = true;
held = binding.IsDown();
if (held) heldQueue = true;
}
}
public void SetState(bool hit, bool held)
{
@@ -156,12 +156,12 @@ namespace Barotrauma
if (held) heldQueue = true;
}
public bool DequeueHit()
{
bool value = hitQueue;
hitQueue = false;
return value;
}
public bool DequeueHit()
{
bool value = hitQueue;
hitQueue = false;
return value;
}
public bool DequeueHeld()
{
@@ -187,11 +187,11 @@ namespace Barotrauma
held = false;
}
public void ResetHit()
{
hit = false;
//stateQueue = false;
}
public void ResetHit()
{
hit = false;
//stateQueue = false;
}
public void ResetHeld()
@@ -199,5 +199,5 @@ namespace Barotrauma
held = false;
//stateQueue = false;
}
}
}
}
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
static class TextManager
{
private static Dictionary<string, List<string>> texts;
static TextManager()
{
Load(Path.Combine("Content", "Texts.xml"));
}
private static void Load(string file)
{
texts = new Dictionary<string, List<string>>();
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc == null || doc.Root == null) return;
foreach (XElement subElement in doc.Root.Elements())
{
string infoName = subElement.Name.ToString().ToLowerInvariant();
List<string> infoList = null;
if (!texts.TryGetValue(infoName, out infoList))
{
infoList = new List<string>();
texts.Add(infoName, infoList);
}
infoList.Add(subElement.ElementInnerText());
}
}
public static string Get(string textTag)
{
List<string> textList = null;
if (!texts.TryGetValue(textTag.ToLowerInvariant(), out textList) || !textList.Any())
{
DebugConsole.ThrowError("Text \"" + textTag + "\" not found");
return textTag;
}
string text = textList[Rand.Int(textList.Count)].Replace(@"\n", "\n");
//todo: get rid of these and only do where needed?
#if CLIENT
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
{
text = text.Replace("[" + inputType.ToString() + "]", GameMain.Config.KeyBind(inputType).ToString());
}
#endif
return text;
}
public static string ReplaceGenderPronouns(string text, Gender gender)
{
if (gender == Gender.Male)
{
return text.Replace("[genderpronoun]", Get("PronounMale").ToLower())
.Replace("[genderpronounpossessive]", Get("PronounPossessiveMale").ToLower())
.Replace("[genderpronounreflexive]", Get("PronounReflexiveMale").ToLower())
.Replace("[Genderpronoun]", Capitalize(Get("PronounMale")))
.Replace("[Genderpronounpossessive]", Capitalize(Get("PronounPossessiveMale")))
.Replace("[Genderpronounreflexive]", Capitalize(Get("PronounReflexiveMale")));
}
else
{
return text.Replace("[genderpronoun]", Get("PronounFemale").ToLower())
.Replace("[genderpronounpossessive]", Get("PronounPossessiveFemale").ToLower())
.Replace("[genderpronounreflexive]", Get("PronounReflexiveFemale").ToLower())
.Replace("[Genderpronoun]", Capitalize(Get("PronounFemale")))
.Replace("[Genderpronounpossessive]", Capitalize(Get("PronounPossessiveFemale")))
.Replace("[Genderpronounreflexive]", Capitalize(Get("PronounReflexiveFemale")));
}
}
private static string Capitalize(string str)
{
if (string.IsNullOrWhiteSpace(str))
{
return str;
}
return char.ToUpper(str[0]) + str.Substring(1);
}
}
}
@@ -336,6 +336,11 @@ namespace Barotrauma
float xDiff = lineB.X - lineA.X;
float yDiff = lineB.Y - lineA.Y;
if (xDiff == 0 && yDiff == 0)
{
return Vector2.Distance(lineA, point);
}
return (float)(Math.Abs(xDiff * (lineA.Y - point.Y) - yDiff * (lineA.X - point.X)) /
Math.Sqrt(xDiff * xDiff + yDiff * yDiff));
}
@@ -508,6 +513,52 @@ namespace Barotrauma
// Return formatted number with suffix
return readable.ToString("0.# ") + suffix;
}
public static void SplitRectanglesHorizontal(List<Rectangle> rects, Vector2 point)
{
for (int i = 0; i < rects.Count; i++)
{
if (point.Y > rects[i].Y && point.Y < rects[i].Y + rects[i].Height)
{
Rectangle rect1 = rects[i];
Rectangle rect2 = rects[i];
rect1.Height = (int)(point.Y - rects[i].Y);
rect2.Height = rects[i].Height - rect1.Height;
rect2.Y = rect1.Y + rect1.Height;
rects[i] = rect1;
rects.Insert(i + 1, rect2); i++;
}
}
}
public static void SplitRectanglesVertical(List<Rectangle> rects, Vector2 point)
{
for (int i = 0; i < rects.Count; i++)
{
if (point.X>rects[i].X && point.X<rects[i].X+rects[i].Width)
{
Rectangle rect1 = rects[i];
Rectangle rect2 = rects[i];
rect1.Width = (int)(point.X-rects[i].X);
rect2.Width = rects[i].Width - rect1.Width;
rect2.X = rect1.X + rect1.Width;
rects[i] = rect1;
rects.Insert(i + 1, rect2); i++;
}
}
/*for (int i = 0; i < rects.Count; i++)
{
if (rects[i].Width <= 0 || rects[i].Height <= 0)
{
rects.RemoveAt(i); i--;
}
}*/
}
}
class CompareCCW : IComparer<Vector2>