(ded4a3e0a) v0.9.0.7

This commit is contained in:
Joonas Rikkonen
2019-06-25 16:00:44 +03:00
parent e5ae622c77
commit 4a51db77b5
1777 changed files with 421528 additions and 917 deletions
@@ -196,7 +196,7 @@ namespace Barotrauma
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
if (unreachable.Contains(hull)) { continue; }
float hullSafety = 0;
if (character.CurrentHull != null)
if (character.CurrentHull != null && character.Submarine != null)
{
// Inside
if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; }
@@ -1101,7 +1101,7 @@ namespace Barotrauma
//prevent the hands from going above the top of the ladders
handPos.Y = Math.Min(-0.5f, handPos.Y);
if (!PlayerInput.KeyDown(InputType.Aim) || Math.Abs(movement.Y) > 0.01f)
if (!character.IsKeyDown(InputType.Aim) || Math.Abs(movement.Y) > 0.01f)
{
MoveLimb(leftHand,
new Vector2(handPos.X,
@@ -836,13 +836,21 @@ namespace Barotrauma
private static string humanConfigFile;
public static string HumanConfigFile
{
get
get
{
if (string.IsNullOrEmpty(humanConfigFile))
{
humanConfigFile = GetConfigFile("Human");
humanConfigFile = GameMain.Instance.GetFilesOfType(ContentType.Character)?
.FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == "human.xml");
if (humanConfigFile == null)
{
DebugConsole.ThrowError($"Couldn't find a human config file from the selected content packages!");
DebugConsole.ThrowError($"(The config file must end with \"human.xml\")");
return string.Empty;
}
}
return humanConfigFile;
return humanConfigFile;
}
}
@@ -859,12 +867,16 @@ namespace Barotrauma
}
}
/// <summary>
/// Searches for a character config file from all currently selected content packages,
/// or from a specific package if the contentPackage parameter is given.
/// </summary>
public static string GetConfigFile(string speciesName, ContentPackage contentPackage = null)
{
string configFile = null;
if (contentPackage == null)
{
configFile = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true)
configFile = GameMain.Instance.GetFilesOfType(ContentType.Character)
.FirstOrDefault(c => Path.GetFileName(c).ToLowerInvariant() == $"{speciesName.ToLowerInvariant()}.xml");
}
else
@@ -1519,10 +1531,9 @@ namespace Barotrauma
if (inventory.Owner is Item)
{
var owner = (Item)inventory.Owner;
if (!CanInteractWith(owner))
{
return false;
}
if (!CanInteractWith(owner)) { return false; }
ItemContainer container = owner.GetComponents<ItemContainer>().FirstOrDefault(ic => ic.Inventory == inventory);
if (container != null && !container.HasRequiredItems(this, addMessage: false)) { return false; }
}
return true;
}
@@ -747,7 +747,7 @@ namespace Barotrauma
public void IncreaseSkillLevel(string skillIdentifier, float increase, Vector2 worldPos)
{
if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)) return;
if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) || Character == null) { return; }
float prevLevel = Job.GetSkillLevel(skillIdentifier);
Job.IncreaseSkillLevel(skillIdentifier, increase);
@@ -444,6 +444,8 @@ namespace Barotrauma
public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
{
if (armorSector == Vector2.Zero) { return false; }
//sector 360 degrees or more -> always hits
if (Math.Abs(armorSector.Y - armorSector.X) >= MathHelper.TwoPi) { return true; }
float rotation = body.TransformedRotation;
float offset = (MathHelper.PiOver2 - GetArmorSectorRotationOffset(armorSector)) * Dir;
float hitAngle = VectorExtensions.Angle(VectorExtensions.Forward(rotation + offset), SimPosition - simPosition);
@@ -460,9 +462,7 @@ namespace Barotrauma
protected float GetArmorSectorSize(Vector2 armorSector)
{
float min = Math.Min(armorSector.X, armorSector.Y);
float max = Math.Max(armorSector.X, armorSector.Y);
return max - min;
return Math.Abs(armorSector.X - armorSector.Y);
}
public void Update(float deltaTime)
@@ -498,7 +498,7 @@ namespace Barotrauma
{
Path = path;
#if OSX
#if OSX || LINUX
Path = Path.Replace("\\", "/");
#endif
@@ -328,7 +328,7 @@ namespace Barotrauma
};
}));
commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) =>
commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server. You can see the IDs of the clients using the command \"clientlist\".", (string[] args) =>
{
if (GameMain.NetworkMember == null || args.Length == 0) return;
@@ -380,7 +380,7 @@ namespace Barotrauma
};
}));
commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) =>
commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server. You can see the IDs of the clients using the command \"clientlist\".", (string[] args) =>
{
if (GameMain.NetworkMember == null || args.Length == 0) return;
@@ -66,12 +66,13 @@ namespace Barotrauma
if (GameMain.Config?.SelectedContentPackages.Count > 0)
{
StringBuilder sb = new StringBuilder("ContentPackage:");
StringBuilder sb = new StringBuilder("ContentPackage: ");
int i = 0;
foreach (ContentPackage cp in GameMain.Config.SelectedContentPackages)
{
sb.Append(cp.Name.Replace(":", "").Substring(0, Math.Min(32, cp.Name.Length)));
if (i < GameMain.Config.SelectedContentPackages.Count - 1) sb.Append(",");
string trimmedName = cp.Name.Replace(":", "").Replace(" ", "");
sb.Append(trimmedName.Substring(0, Math.Min(32, trimmedName.Length)));
if (i < GameMain.Config.SelectedContentPackages.Count - 1) { sb.Append(" "); }
}
GameAnalytics.AddDesignEvent(sb.ToString());
}
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Xml;
using System.IO;
#if CLIENT
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Tutorials;
@@ -142,6 +143,8 @@ namespace Barotrauma
public bool CrewMenuOpen { get; set; } = true;
public bool ChatOpen { get; set; } = true;
private string overrideSaveFolder, overrideMultiplayerSaveFolder;
private bool unsavedSettings;
public bool UnsavedSettings
{
@@ -277,7 +280,6 @@ namespace Barotrauma
public GameSettings()
{
SelectedContentPackages = new HashSet<ContentPackage>();
ContentPackage.LoadAll(ContentPackage.Folder);
CompletedTutorialNames = new List<string>();
@@ -605,6 +607,17 @@ namespace Barotrauma
LoadControls(doc);
LoadContentPackages(doc);
//allow overriding the save paths in the config file
if (doc.Root.Attribute("overridesavefolder") != null)
{
overrideSaveFolder = SaveUtil.SaveFolder = doc.Root.GetAttributeString("overridesavefolder", "");
overrideMultiplayerSaveFolder = SaveUtil.MultiplayerSaveFolder = Path.Combine(overrideSaveFolder, "Multiplayer");
}
if (doc.Root.Attribute("overridemultiplayersavefolder") != null)
{
overrideMultiplayerSaveFolder = SaveUtil.MultiplayerSaveFolder = doc.Root.GetAttributeString("overridemultiplayersavefolder", "");
}
XElement tutorialsElement = doc.Root.Element("tutorials");
if (tutorialsElement != null)
{
@@ -732,6 +745,15 @@ namespace Barotrauma
new XAttribute("campaigndisclaimershown", CampaignDisclaimerShown),
new XAttribute("editordisclaimershown", EditorDisclaimerShown));
if (!string.IsNullOrEmpty(overrideSaveFolder))
{
doc.Root.Add(new XAttribute("overridesavefolder", overrideSaveFolder));
}
if (!string.IsNullOrEmpty(overrideMultiplayerSaveFolder))
{
doc.Root.Add(new XAttribute("overridemultiplayersavefolder", overrideMultiplayerSaveFolder));
}
if (!ShowUserStatisticsPrompt)
{
doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics));
@@ -22,7 +22,6 @@ namespace Barotrauma.Items.Components
private float openState;
private Sprite doorSprite, weldedSprite, brokenSprite;
private bool scaleBrokenSprite, fadeBrokenSprite;
private bool createdNewGap;
private bool autoOrientGap;
private bool isStuck;
@@ -87,17 +86,19 @@ namespace Barotrauma.Items.Components
{
get
{
if (linkedGap != null) return linkedGap;
foreach (MapEntity e in item.linkedTo)
if (linkedGap == null)
{
linkedGap = e as Gap;
if (linkedGap != null)
{
linkedGap.PassAmbientLight = Window != Rectangle.Empty;
return linkedGap;
}
GetLinkedGap();
}
return linkedGap;
}
}
private void GetLinkedGap()
{
linkedGap = item.linkedTo.FirstOrDefault(e => e is Gap) as Gap;
if (linkedGap == null)
{
Rectangle rect = item.Rect;
if (IsHorizontal)
{
@@ -109,17 +110,13 @@ namespace Barotrauma.Items.Components
rect.X -= 5;
rect.Width += 10;
}
linkedGap = new Gap(rect, !IsHorizontal, Item.Submarine)
{
Submarine = item.Submarine,
PassAmbientLight = Window != Rectangle.Empty,
Open = openState
Submarine = item.Submarine
};
item.linkedTo.Add(linkedGap);
createdNewGap = true;
return linkedGap;
}
RefreshLinkedGap();
}
public bool IsHorizontal { get; private set; }
@@ -162,14 +159,14 @@ namespace Barotrauma.Items.Components
get;
set;
}
public Door(Item item, XElement element)
: base(item, element)
{
IsHorizontal = element.GetAttributeBool("horizontal", false);
canBePicked = element.GetAttributeBool("canbepicked", false);
autoOrientGap = element.GetAttributeBool("autoorientgap", false);
foreach (XElement subElement in element.Elements())
{
string texturePath = subElement.GetAttributeString("texture", "");
@@ -370,12 +367,20 @@ namespace Barotrauma.Items.Components
#endif
}
public override void OnMapLoaded()
public void RefreshLinkedGap()
{
LinkedGap.ConnectedDoor = this;
if (autoOrientGap)
{
LinkedGap.AutoOrient();
}
LinkedGap.Open = openState;
if (createdNewGap && autoOrientGap) linkedGap.AutoOrient();
LinkedGap.PassAmbientLight = Window != Rectangle.Empty;
}
public override void OnMapLoaded()
{
RefreshLinkedGap();
#if CLIENT
Vector2[] corners = GetConvexHullCorners(Rectangle.Empty);
@@ -386,6 +391,18 @@ namespace Barotrauma.Items.Components
#endif
}
public override void OnScaleChanged()
{
#if CLIENT
UpdateConvexHulls();
#endif
if (linkedGap != null)
{
RefreshLinkedGap();
linkedGap.Rect = item.Rect;
}
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
@@ -53,12 +53,6 @@ namespace Barotrauma.Items.Components
set;
}
/// <summary>
/// How useful the weapon is in combat? Used by AI to sort the used weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
/// </summary>
[Serialize(0f, false)]
public float CombatPriority { get; private set; }
public MeleeWeapon(Item item, XElement element)
: base(item, element)
{
@@ -43,12 +43,6 @@ namespace Barotrauma.Items.Components
set;
}
/// <summary>
/// How useful the weapon is in combat? Used by AI to sort the used weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
/// </summary>
[Serialize(0f, false)]
public float CombatPriority { get; private set; }
public Vector2 TransformedBarrelPos
{
get
@@ -170,6 +164,7 @@ namespace Barotrauma.Items.Components
projectile.Item.SetTransform(projectilePos, rotation);
projectile.Use(deltaTime);
if (projectile.Item.Removed) { return true; }
projectile.User = character;
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f));
@@ -157,7 +157,8 @@ namespace Barotrauma.Items.Components
partial void UseProjSpecific(float deltaTime);
private List<FireSource> fireSourcesInRange = new List<FireSource>();
private readonly HashSet<Character> hitCharacters = new HashSet<Character>();
private readonly List<FireSource> fireSourcesInRange = new List<FireSource>();
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies)
{
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair;
@@ -165,6 +166,7 @@ namespace Barotrauma.Items.Components
{
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true);
Type lastHitType = null;
hitCharacters.Clear();
foreach (Body body in bodies)
{
Type bodyType = body.UserData?.GetType();
@@ -173,6 +175,23 @@ namespace Barotrauma.Items.Components
//stop the ray if it already hit a door/wall and is now about to hit some other type of entity
if (lastHitType == typeof(Item) || lastHitType == typeof(Structure)) { break; }
}
Character hitCharacter = null;
if (body.UserData is Limb limb)
{
hitCharacter = limb.character;
}
else if (body.UserData is Character character)
{
hitCharacter = character;
}
//only do damage once to each character even if they ray hit multiple limbs
if (hitCharacter != null)
{
if (hitCharacters.Contains(hitCharacter)) { continue; }
hitCharacters.Add(hitCharacter);
}
if (FixBody(user, deltaTime, degreeOfSuccess, body))
{
if (bodyType != null) { lastHitType = bodyType; }
@@ -545,7 +545,6 @@ namespace Barotrauma.Items.Components
GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return 0.0f;
}
float average = skillSuccessSum / requiredSkills.Count;
float skillSuccessSum = 0.0f;
for (int i = 0; i < requiredSkills.Count; i++)
@@ -693,6 +692,8 @@ namespace Barotrauma.Items.Components
/// </summary>
public virtual void OnItemLoaded() { }
public virtual void OnScaleChanged() { }
// TODO: Consider using generics, interfaces, or inheritance instead of reflection -> would be easier to debug when something changes/goes wrong.
// For example, currently we can edit the constructors but they will fail in runtime because the parameters are not changed here.
// It's also painful to find where the constructors are used, because the references exist only at runtime.
@@ -97,6 +97,7 @@ namespace Barotrauma.Items.Components
RelatedItem ri = containableItems.Find(x => x.MatchesItem(containedItem));
if (ri != null)
{
itemsWithStatusEffects.RemoveAll(i => i.First == containedItem);
foreach (StatusEffect effect in ri.statusEffects)
{
itemsWithStatusEffects.Add(new Pair<Item, StatusEffect>(containedItem, effect));
@@ -107,12 +108,12 @@ namespace Barotrauma.Items.Components
IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null;
}
public void OnItemRemoved(Item item)
public void OnItemRemoved(Item containedItem)
{
itemsWithStatusEffects.RemoveAll(i => i.First == item);
itemsWithStatusEffects.RemoveAll(i => i.First == containedItem);
//deactivate if the inventory is empty
IsActive = itemsWithStatusEffects.Count > 0 || item.body != null;
IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null;
}
public bool CanBeContained(Item item)
@@ -37,6 +37,8 @@ namespace Barotrauma.Items.Components
private Item focusTarget;
private float targetRotation;
private bool state;
public Vector2 UserPos
{
get { return userPos; }
@@ -48,6 +50,13 @@ namespace Barotrauma.Items.Components
get { return user; }
}
[Serialize(false, false), Editable(ToolTip = "When enabled, the item will continuously send out a 0/1 signal and interacting with it will flip the signal (making the item behave like a switch). When disabled, the item will simply send out 1 when interacted with.")]
public bool IsToggle
{
get;
set;
}
public Controller(Item item, XElement element)
: base(item, element)
{
@@ -55,7 +64,7 @@ namespace Barotrauma.Items.Components
userPos = element.GetAttributeVector2("UserPos", Vector2.Zero);
Enum.TryParse<Direction>(element.GetAttributeString("direction", "None"), out dir);
Enum.TryParse(element.GetAttributeString("direction", "None"), out dir);
foreach (XElement el in element.Elements())
{
@@ -83,7 +92,12 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
this.cam = cam;
if (IsToggle)
{
item.SendSignal(0, state ? "1" : "0", "signal_out", sender: null);
}
if (user == null
|| user.Removed
|| user.SelectedConstruction != item
@@ -94,7 +108,7 @@ namespace Barotrauma.Items.Components
CancelUsing(user);
user = null;
}
IsActive = false;
if (!IsToggle) { IsActive = false; }
return;
}
@@ -169,7 +183,7 @@ namespace Barotrauma.Items.Components
}
item.SendSignal(0, "1", "trigger_out", user);
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
return true;
@@ -254,7 +268,14 @@ namespace Barotrauma.Items.Components
public override bool Pick(Character picker)
{
item.SendSignal(0, "1", "signal_out", picker);
if (IsToggle)
{
state = !state;
}
else
{
item.SendSignal(0, "1", "signal_out", picker);
}
#if CLIENT
PlaySound(ActionType.OnUse, item.WorldPosition, picker);
@@ -137,10 +137,10 @@ namespace Barotrauma.Items.Components
public Vector2 AvoidStrength;
public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength)
public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength, Vector2 translation)
{
Point1 = edge.Point1;
Point2 = edge.Point2;
Point1 = edge.Point1 + translation;
Point2 = edge.Point2 + translation;
Intersection = intersection;
Dot = dot;
AvoidStrength = avoidStrength;
@@ -210,6 +210,11 @@ namespace Barotrauma.Items.Components
if (voltage < minVoltage && currPowerConsumption > 0.0f) { return; }
if (user != null && user.Removed)
{
user = null;
}
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (autoPilot)
@@ -333,14 +338,14 @@ namespace Barotrauma.Items.Components
{
foreach (GraphEdge edge in cell.Edges)
{
if (MathUtils.GetLineIntersection(edge.Point1, edge.Point2, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
if (MathUtils.GetLineIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
{
Vector2 diff = controlledSub.WorldPosition - intersection;
//far enough -> ignore
if (Math.Abs(diff.X) > avoidDist.X && Math.Abs(diff.Y) > avoidDist.Y)
{
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, 0.0f, Vector2.Zero));
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, 0.0f, Vector2.Zero, Vector2.Zero));
continue;
}
if (diff.LengthSquared() < 1.0f) diff = Vector2.UnitY;
@@ -352,13 +357,13 @@ namespace Barotrauma.Items.Components
//not heading towards the wall -> ignore
if (dot < 0.5)
{
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, Vector2.Zero));
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, Vector2.Zero, cell.Translation));
continue;
}
Vector2 change = (normalizedDiff * Math.Max((avoidRadius - diff.Length()), 0.0f)) / avoidRadius;
newAvoidStrength += change * dot;
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, change * dot));
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, change * dot, cell.Translation));
}
}
}
@@ -275,7 +275,14 @@ namespace Barotrauma.Items.Components
//the raycast didn't hit anything -> the projectile flew somewhere outside the level and is permanently lost
if (!hitSomething)
{
Entity.Spawner.AddToRemoveQueue(item);
if (Entity.Spawner == null)
{
item.Remove();
}
else
{
Entity.Spawner.AddToRemoveQueue(item);
}
}
}
@@ -9,12 +9,14 @@ namespace Barotrauma.Items.Components
{
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
{
public static float SkillIncreaseMultiplier = 0.4f;
public static float SkillIncreasePerRepair = 5.0f;
private string header;
private float deteriorationTimer;
bool wasBroken;
public float LastActiveTime;
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "How fast the condition of the item deteriorates per second.")]
@@ -173,16 +175,13 @@ namespace Barotrauma.Items.Components
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f;
foreach (Skill skill in requiredSkills)
//item must have been below 50% condition for the player to get an achievement or XP for repairing it
if (item.Condition < ShowRepairUIThreshold)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
if (characterSkillLevel >= skill.Level) successFactor += 1.0f / requiredSkills.Count;
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreaseMultiplier * deltaTime / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
wasBroken = true;
}
bool wasBroken = !item.IsFullCondition;
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f)
{
@@ -195,8 +194,16 @@ namespace Barotrauma.Items.Components
if (wasBroken && item.IsFullCondition)
{
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
SteamAchievementManager.OnItemRepaired(item, currentFixer);
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
wasBroken = false;
#if SERVER
item.CreateServerEvent(this);
#endif
@@ -19,7 +19,8 @@ namespace Barotrauma.Items.Components
private string prevSignal;
public Character.TeamType TeamID;
[Serialize(Character.TeamType.None, false)]
public Character.TeamType TeamID { get; set; }
[Serialize(20000.0f, false)]
public float Range
@@ -82,8 +83,11 @@ namespace Barotrauma.Items.Components
public bool CanReceive(WifiComponent sender)
{
if (sender == null || sender.channel != channel || sender.TeamID != TeamID) return false;
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) return false;
if (sender == null || sender.channel != channel) { return false; }
if (sender.TeamID == Character.TeamType.Team1 && TeamID == Character.TeamType.Team2) { return false; }
if (sender.TeamID == Character.TeamType.Team2 && TeamID == Character.TeamType.Team1) { return false; }
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) { return false; }
return HasRequiredContainedItems(false);
}
@@ -70,7 +70,9 @@ namespace Barotrauma
private Inventory parentInventory;
private Inventory ownInventory;
private Rectangle defaultRect;
private Dictionary<string, Connection> connections;
private List<Repairable> repairables;
@@ -198,6 +200,34 @@ namespace Barotrauma
}
}
private float scale = 1.0f;
public override float Scale
{
get { return scale; }
set
{
if (scale == value) { return; }
scale = MathHelper.Clamp(value, 0.1f, 10.0f);
float relativeScale = scale / prefab.Scale;
if (!ResizeHorizontal || !ResizeVertical)
{
int newWidth = ResizeHorizontal ? rect.Width : (int)(defaultRect.Width * relativeScale);
int newHeight = ResizeVertical ? rect.Height : (int)(defaultRect.Height * relativeScale);
Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight);
}
if (components != null)
{
foreach (ItemComponent component in components)
{
component.OnScaleChanged();
}
}
}
}
public float PositionUpdateInterval
{
get;
@@ -499,9 +529,10 @@ namespace Barotrauma
drawableComponents = new List<IDrawableComponent>();
tags = new HashSet<string>();
repairables = new List<Repairable>();
defaultRect = newRect;
rect = newRect;
condition = itemPrefab.Health;
lastSentCondition = condition;
@@ -626,13 +657,18 @@ namespace Barotrauma
ic.OnItemLoaded();
}
}
DebugConsole.Log("Created " + Name + " (" + ID + ")");
}
partial void InitProjSpecific();
public override MapEntity Clone()
{
Item clone = new Item(rect, Prefab, Submarine, callOnItemLoaded: false);
Item clone = new Item(rect, Prefab, Submarine, callOnItemLoaded: false)
{
defaultRect = defaultRect
};
foreach (KeyValuePair<string, SerializableProperty> property in SerializableProperties)
{
if (!property.Value.Attributes.OfType<Editable>().Any()) continue;
@@ -1552,7 +1588,7 @@ namespace Barotrauma
public void Use(float deltaTime, Character character = null, Limb targetLimb = null)
{
if (RequireAimToUse && !character.IsKeyDown(InputType.Aim))
if (RequireAimToUse && (character == null || !character.IsKeyDown(InputType.Aim)))
{
return;
}
@@ -2050,11 +2086,11 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(Submarine != null || rootContainer.ParentInventory?.Owner is Character);
Vector2 subPosition = Submarine == null ? Vector2.Zero : Submarine.HiddenSubPosition;
element.Add(new XAttribute("rect",
(int)(rect.X - subPosition.X) + "," +
(int)(rect.Y - subPosition.Y) + "," +
rect.Width + "," + rect.Height));
defaultRect.Width + "," + defaultRect.Height));
if (linkedTo != null && linkedTo.Count > 0)
{
@@ -9,6 +9,7 @@ namespace Barotrauma
class Entity : ISpatialEntity
{
public const ushort NullEntityID = 0;
public const ushort EntitySpawnerID = ushort.MaxValue;
private static Dictionary<ushort, Entity> dictionary = new Dictionary<ushort, Entity>();
public static List<Entity> GetEntityList()
@@ -43,12 +44,19 @@ namespace Barotrauma
}
set
{
if (this is EntitySpawner) { return; }
if (value == NullEntityID)
{
DebugConsole.ThrowError("Cannot set the ID of an entity to " + NullEntityID +
"! The value is reserved for entity events referring to a non-existent (e.g. removed) entity.\n" + Environment.StackTrace);
return;
}
if (value == EntitySpawnerID)
{
DebugConsole.ThrowError("Cannot set the ID of an entity to " + EntitySpawnerID +
"! The value is reserved for EntitySpawner.\n" + Environment.StackTrace);
return;
}
if (dictionary.TryGetValue(id, out Entity thisEntity) && thisEntity == this)
{
@@ -107,15 +115,17 @@ namespace Barotrauma
this.Submarine = submarine;
//give a unique ID
id = FindFreeID(submarine == null ? (ushort)1 : submarine.IdOffset);
id = this is EntitySpawner ?
EntitySpawnerID :
FindFreeID(submarine == null ? (ushort)1 : submarine.IdOffset);
dictionary.Add(id, this);
}
public static ushort FindFreeID(ushort idOffset = 0)
{
//ushort.MaxValue - 1 because 0 is a reserved value
if (dictionary.Count >= ushort.MaxValue - 1)
//ushort.MaxValue - 2 because 0 and ushort.MaxValue are reserved values
if (dictionary.Count >= ushort.MaxValue - 2)
{
throw new Exception("Maximum amount of entities (" + (ushort.MaxValue - 1) + ") reached!");
}
@@ -228,7 +228,11 @@ namespace Barotrauma
attacker = item.GetComponent<Projectile>()?.User;
if (attacker == null) attacker = item.GetComponent<MeleeWeapon>()?.User;
}
c.AddDamage(limb.WorldPosition, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
//use a position slightly from the limb's position towards the explosion
//ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
Vector2 hitPos = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f;
c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
if (attack.StatusEffects != null && attack.StatusEffects.Any())
{
@@ -122,6 +122,8 @@ namespace Barotrauma
GapList.Add(this);
InsertToList();
DebugConsole.Log("Created gap (" + ID + ")");
}
public override MapEntity Clone()
@@ -242,6 +242,8 @@ namespace Barotrauma
WaterVolume = 0.0f;
InsertToList();
DebugConsole.Log("Created hull (" + ID + ")");
}
public static Rectangle GetBorders()
@@ -107,7 +107,7 @@ namespace Barotrauma
if (Screen.Selected == GameMain.SubEditorScreen)
{
MapEntity.SelectedList.Clear();
MapEntity.SelectedList.AddRange(entities);
entities.ForEach(e => MapEntity.AddSelection(e));
}
#endif
return entities;
@@ -1274,6 +1274,8 @@ namespace Barotrauma
}
}
DebugConsole.Log("Generating level resources...");
for (int i = 0; i < generationParams.ItemCount; i++)
{
var selectedPrefab = ToolBox.SelectWeightedRandom(
@@ -1286,6 +1288,7 @@ namespace Barotrauma
var selectedEdge = selectedCell.Edges.GetRandom(e => e.IsSolid && !e.OutsideLevel, Rand.RandSync.Server);
if (selectedEdge == null) continue;
float edgePos = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server);
Vector2 selectedPos = Vector2.Lerp(selectedEdge.Point1, selectedEdge.Point2, edgePos);
Vector2 edgeNormal = selectedEdge.GetNormal(selectedCell);
@@ -1306,6 +1309,8 @@ namespace Barotrauma
#endif
}
}
DebugConsole.Log("Level resources generated");
}
public Vector2 GetRandomItemPos(PositionType spawnPosType, float randomSpread, float minDistFromSubs, float offsetFromWall = 10.0f)
@@ -1022,12 +1022,12 @@ namespace Barotrauma.RuinGeneration
{
targetEntity = ruinEntities.GetRandom(e =>
e.Room == targetRoom &&
e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier)?.Entity;
e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier, Rand.RandSync.Server)?.Entity;
}
}
else
{
targetEntity = ruinEntities.GetRandom(e => e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier)?.Entity;
targetEntity = ruinEntities.GetRandom(e => e.Entity.prefab?.Identifier == connection.TargetEntityIdentifier, Rand.RandSync.Server)?.Entity;
}
if (targetEntity == null) continue;
@@ -57,6 +57,8 @@ namespace Barotrauma
linkedToID = new List<ushort>();
InsertToList();
DebugConsole.Log("Created linked submarine (" + ID + ")");
}
public static LinkedSubmarine CreateDummy(Submarine mainSub, Submarine linkedSub)
@@ -207,20 +207,22 @@ namespace Barotrauma
//clone links between the entities
for (int i = 0; i < clones.Count; i++)
{
if (entitiesToClone[i].linkedTo == null) continue;
if (entitiesToClone[i].linkedTo == null) { continue; }
foreach (MapEntity linked in entitiesToClone[i].linkedTo)
{
if (!entitiesToClone.Contains(linked)) continue;
if (!entitiesToClone.Contains(linked)) { continue; }
clones[i].linkedTo.Add(clones[entitiesToClone.IndexOf(linked)]);
}
}
//connect clone wires to the clone items
//connect clone wires to the clone items and refresh links between doors and gaps
for (int i = 0; i < clones.Count; i++)
{
var cloneItem = clones[i] as Item;
if (cloneItem == null) continue;
if (cloneItem == null) { continue; }
var door = cloneItem.GetComponent<Door>();
if (door != null) { door.RefreshLinkedGap(); }
var cloneWire = cloneItem.GetComponent<Wire>();
if (cloneWire == null) continue;
@@ -231,7 +233,7 @@ namespace Barotrauma
for (int n = 0; n < 2; n++)
{
if (originalWire.Connections[n] == null) continue;
if (originalWire.Connections[n] == null) { continue; }
var connectedItem = originalWire.Connections[n].Item;
if (connectedItem == null) continue;
@@ -555,7 +557,7 @@ namespace Barotrauma
}
}
[Serialize(1f, false), Editable(0.1f, 10f, DecimalCount = 3, ValueStep = 0.1f)]
[Serialize(1f, true), Editable(0.1f, 10f, DecimalCount = 3, ValueStep = 0.1f)]
public virtual float Scale { get; set; } = 1;
#endregion
}
@@ -183,6 +183,29 @@ namespace Barotrauma
}
}
protected Vector2 textureScale = Vector2.One;
[Editable(DecimalCount = 3, MinValueFloat = 0.01f, MaxValueFloat = 10f, ValueStep = 0.1f), Serialize("1.0, 1.0", false)]
public Vector2 TextureScale
{
get { return textureScale; }
set
{
textureScale = new Vector2(
MathHelper.Clamp(value.X, 0.01f, 10),
MathHelper.Clamp(value.Y, 0.01f, 10));
}
}
protected Vector2 textureOffset = Vector2.Zero;
[Editable(MinValueFloat = -1000f, MaxValueFloat = 1000f, ValueStep = 10f), Serialize("0.0, 0.0", true)]
public Vector2 TextureOffset
{
get { return textureOffset; }
set { textureOffset = value; }
}
private Rectangle defaultRect;
public override Rectangle Rect
@@ -296,9 +319,8 @@ namespace Barotrauma
defaultRect = rectangle;
rect = rectangle;
#if CLIENT
TextureScale = sp.TextureScale;
#endif
spriteColor = prefab.SpriteColor;
if (sp.IsHorizontal.HasValue)
{
@@ -355,6 +377,8 @@ namespace Barotrauma
}
InsertToList();
DebugConsole.Log("Created " + Name + " (" + ID + ")");
}
partial void InitProjSpecific();
@@ -365,7 +365,9 @@ namespace Barotrauma
{
using (MemoryStream mem = new MemoryStream(Convert.FromBase64String(previewImageData)))
{
PreviewImage = new Sprite(TextureLoader.FromStream(mem, preMultiplyAlpha: false), null, null);
var texture = TextureLoader.FromStream(mem, preMultiplyAlpha: false, path: filePath);
if (texture == null) { throw new Exception("PreviewImage texture returned null"); }
PreviewImage = new Sprite(texture, null, null);
}
}
catch (Exception e)
@@ -382,7 +384,6 @@ namespace Barotrauma
DockedTo = new List<Submarine>();
ID = ushort.MaxValue;
FreeID();
}
@@ -1405,7 +1406,7 @@ namespace Barotrauma
}
ID = (ushort)(ushort.MaxValue - Submarine.loaded.IndexOf(this));
ID = (ushort)(ushort.MaxValue - 1 - Submarine.loaded.IndexOf(this));
}
public static Submarine Load(XElement element, bool unloadPrevious)
@@ -1553,7 +1554,7 @@ namespace Barotrauma
if (MainSub == this) MainSub = null;
if (MainSubs[1] == this) MainSubs[1] = null;
DockedTo.Clear();
DockedTo?.Clear();
}
public void Dispose()
@@ -125,6 +125,8 @@ namespace Barotrauma
InsertToList();
WayPointList.Add(this);
DebugConsole.Log("Created waypoint (" + ID + ")");
currentHull = Hull.FindHull(WorldPosition);
}
@@ -48,6 +48,11 @@ namespace Barotrauma.Networking
get { return "ServerSettings"; }
}
/// <summary>
/// Have some of the properties listed in the server list changed
/// </summary>
public bool ServerDetailsChanged;
public class SavedClientPermission
{
public readonly string IP;
@@ -277,7 +282,17 @@ namespace Barotrauma.Networking
public string ServerName;
public string ServerMessageText;
private string serverMessageText;
public string ServerMessageText
{
get { return serverMessageText; }
set
{
if (serverMessageText == value) { return; }
serverMessageText = value;
ServerDetailsChanged = true;
}
}
public int Port;
@@ -370,19 +385,19 @@ namespace Barotrauma.Networking
private set;
}
private bool allowSpectating;
[Serialize(true, true)]
public bool AllowSpectating
{
get;
private set;
get { return allowSpectating; }
private set
{
if (allowSpectating == value) { return; }
allowSpectating = value;
ServerDetailsChanged = true;
}
}
[Serialize(true, true)]
public bool VoipEnabled {
get;
private set;
}
[Serialize(true, true)]
public bool EndRoundAtLevelEnd
{
@@ -411,11 +426,17 @@ namespace Barotrauma.Networking
private set;
}
private bool voiceChatEnabled;
[Serialize(true, true)]
public bool VoiceChatEnabled
{
get;
set;
get { return voiceChatEnabled; }
set
{
if (voiceChatEnabled == value) { return; }
voiceChatEnabled = value;
ServerDetailsChanged = true;
}
}
[Serialize(800, true)]
@@ -473,11 +494,17 @@ namespace Barotrauma.Networking
}
}
private bool allowRespawn;
[Serialize(true, true)]
public bool AllowRespawn
{
get;
set;
get { return allowRespawn; ; }
set
{
if (allowRespawn == value) { return; }
allowRespawn = value;
ServerDetailsChanged = true;
}
}
[Serialize(0, true)]
@@ -513,10 +540,16 @@ namespace Barotrauma.Networking
set;
}
private YesNoMaybe traitorsEnabled;
public YesNoMaybe TraitorsEnabled
{
get;
set;
get { return traitorsEnabled; }
set
{
if (traitorsEnabled == value) { return; }
traitorsEnabled = value;
ServerDetailsChanged = true;
}
}
private SelectionMode subSelectionMode;
@@ -528,6 +561,7 @@ namespace Barotrauma.Networking
{
subSelectionMode = value;
Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
ServerDetailsChanged = true;
}
}
@@ -540,6 +574,7 @@ namespace Barotrauma.Networking
{
modeSelectionMode = value;
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
ServerDetailsChanged = true;
}
}
@@ -611,6 +646,7 @@ namespace Barotrauma.Networking
public int MaxPlayers
{
get { return maxPlayers; }
set { maxPlayers = MathHelper.Clamp(value, 1, NetConfig.MaxPlayers); }
}
public List<MissionType> AllowedRandomMissionTypes
@@ -635,7 +671,14 @@ namespace Barotrauma.Networking
public void SetPassword(string password)
{
this.password = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password)));
if (string.IsNullOrEmpty(password))
{
this.password = "";
}
else
{
this.password = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password)));
}
}
public bool IsPasswordCorrect(string input, int nonce)
@@ -198,7 +198,7 @@ namespace Voronoi2
{
foreach (GraphEdge edge in Edges)
{
if (MathUtils.LinesIntersect(point, Center, edge.Point1, edge.Point2)) return false;
if (MathUtils.LinesIntersect(point, Center, edge.Point1 + Translation, edge.Point2 + Translation)) return false;
}
return true;
@@ -102,6 +102,14 @@ namespace Barotrauma
partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn, bool premultiplyAlpha = true);
partial void CalculateSourceRect();
private static void AddToList(Sprite elem)
{
lock (list)
{
list.Add(elem);
}
}
public Sprite(XElement element, string path = "", string file = "", bool? preMultiplyAlpha = null, bool lazyLoad = false)
{
this.lazyLoad = lazyLoad;
@@ -128,7 +136,7 @@ namespace Barotrauma
RelativeOrigin = SourceElement.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
Depth = SourceElement.GetAttributeFloat("depth", 0.001f);
ID = GetID(SourceElement);
list.Add(this);
AddToList(this);
}
internal void LoadParams(SpriteParams spriteParams, bool isFlipped)
@@ -147,13 +155,13 @@ namespace Barotrauma
public Sprite(string newFile, Vector2 newOrigin, bool preMultiplyAlpha = true)
{
Init(newFile, newOrigin: newOrigin, preMultiplyAlpha: preMultiplyAlpha);
list.Add(this);
AddToList(this);
}
public Sprite(string newFile, Rectangle? sourceRectangle, Vector2? origin = null, float rotation = 0, bool preMultiplyAlpha = true)
{
Init(newFile, sourceRectangle: sourceRectangle, newOrigin: origin, newRotation: rotation, preMultiplyAlpha: preMultiplyAlpha);
list.Add(this);
AddToList(this);
}
private void Init(string newFile, Rectangle? sourceRectangle = null, Vector2? newOrigin = null, Vector2? newOffset = null, float newRotation = 0,
@@ -198,7 +206,10 @@ namespace Barotrauma
public void Remove()
{
list.Remove(this);
lock (list)
{
list.Remove(this);
}
DisposeTexture();
}
@@ -640,14 +640,14 @@ namespace Barotrauma
character.LastDamageSource = entity;
foreach (Limb limb in character.AnimController.Limbs)
{
limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f);
limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
//only apply non-limb-specific afflictions to the first limb
if (!affliction.Prefab.LimbSpecific) { break; }
}
}
else if (target is Limb limb)
{
limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f);
limb.character.DamageLimb(entity.WorldPosition, limb, new List<Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source);
}
}
@@ -311,11 +311,9 @@ namespace Barotrauma
{
if (gameSession.Mission is CombatMission combatMission)
{
#if CLIENT
//all characters that are alive and in the winning team get an achievement
UnlockAchievement(gameSession.Mission.Prefab.AchievementIdentifier + (int)GameMain.GameSession.WinningTeam, true,
c => c != null && !c.IsDead && !c.IsUnconscious && combatMission.IsInWinningTeam(c));
#endif
}
else if (gameSession.Mission.Completed)
{
@@ -210,8 +210,8 @@ namespace Barotrauma
/// </summary>
public static float GetMidAngle(float from, float to)
{
float max = MathHelper.Max(from, to);
float min = MathHelper.Min(from, to);
float max = Math.Max(from, to);
float min = Math.Min(from, to);
float diff = max - min;
if (from < to)
{
@@ -378,8 +378,6 @@ namespace Barotrauma
Thread.Sleep(250);
}
}
return true;
}
@@ -470,7 +468,20 @@ namespace Barotrauma
foreach (DirectoryInfo di in dir.GetDirectories())
{
ClearFolder(di.FullName, ignoredFileNames);
di.Delete();
int maxRetries = 4;
for (int i = 0; i <= maxRetries; i++)
{
try
{
di.Delete();
break;
}
catch (IOException)
{
if (i >= maxRetries) { throw; }
Thread.Sleep(250);
}
}
}
}
}