(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);
}
}
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+93 -1
View File
@@ -1,3 +1,80 @@
---------------------------------------------------------------------------------------------------------
v0.9.0.7
---------------------------------------------------------------------------------------------------------
Changes:
- Made husks more common.
Multiplayer fixes:
- Fixed an ID mismatch problem that occasionally caused clients to get kicked out in the multiplayer
campaign, usually with an error message warning about a missing item.
- Fixed servers using a local campaign save path sent by the client setupping the campaign, preventing
the campaign from starting up if the path does not exist at the server's end.
- Fixed radio communication occasionally not working between characters in the multiplayer campaign. Characters
that had made it through at least one round were not able to communicate through radio with characters that
have just been spawned for the first time.
- Kick votes persist during a multiplayer session even if the client disconnects (= disconnecting and rejoining
just before you get kicked doesn't work anymore).
- The server sends a "this client previously used the name xxx" message when a client rejoins with a different name.
- Fixed GameServer.UnbanPlayer passing the name to BanList in lower case even though BanList was case
sensitive (preventing unbanning clients with the "unban" command if their name is not in lower case).
- Fixed server not saving the whitelist when it's enabled/disabled, causing the setting to revert when
relaunching the server.
- Fixed structure texture scale and texture offset now being reverting to default values when the
submarine is saved by the server, causing them to reset between campaign rounds.
- Fixed client always launching the default "DedicatedServer.exe" even if using a content package that
should replace the server exe.
- Servers only report content packages with files that cause multiplayer incompatibility to the master server.
Fixes servers showing up as incompatible in the server list if they have custom sub files (or other types
of files that don't cause compatibility issues) installed.
- Fixed crashing when clients attempted to kick/ban players through the in-game info menu when a round
was running.
- Fixed client that's hosting a server not being taken into account in the player count visible on the server list.
- Fixed server list not displaying non-English symbols correctly (e.g. Cyrillic or Chinese characters).
Misc fixes:
- Replaced WinForms with SDL which should resolve most of the fullscreen/resolution issues. It's also now
possible to change the resolution when in fullscreen without having to restart the game.
- Fixes to the DXGI crashes during startup.
- Fixed Character.GetConfigFile searching for the character file from all available content packages, not
just the ones that are currently selected. Caused modded character files to affect the game even when
the mod was not enabled, leading to various issues such as characters failing to equip items if the mod
changes the number or type of inventory slots a character has.
- Fixed a bug in monster/item spawnpoint logic that occasionally caused perfectly valid spawnpoints to be
discarded when there were floating ice chunks in the level, sometimes causing monsters to spawn very
far from the submarine (which often lead to the player not running into them at all).
- Fixed bots not being able to retaliate when attacked with a repair tool.
- Fixed crashing when firing a revolver in the sub editor.
- Fixed items applying their status effects twice if they're put into a container by swapping them with
another item. For example, replacing a fuel rod in the reactor by dropping a new fuel rod on it caused
the reactor behave as if it had two rods in it.
- Switches send out a continuous 0/1 signal that can be flipped by interacting with the switch (as opposed
to working like buttons which send out a pulse when interacted with).
- Fixed an issue that occasionally caused the main menu to look distorted when launching the game on Mac.
- Fixed crash after opening the file browser from the Workshop menu more than once on Linux.
- Fixed deconstructing coilgun ammo boxes giving out more materials than the materials required to fabricate them.
- Fixed combat mission achievements being impossible to unlock.
- Fixed item scales not being saved when the scale is modified in the sub editor.
- Fixed incorrect physicorium shell description.
- Fixed server list & workshop menu not resizing properly when changing resolution.
- Fixed player input being used to determine whether a character should be holding on to ladders, causing
AI characters to let go of ladders when holding RMB.
- Fixed explosion damage bypassing armor (both creature shells and wearable items).
- Fixed OverrideSaveFolder and OverrideMultiplayerSaveFolder settings not being saved.
- Fixed order/report icons "twitching" when the sub moves.
- Fixed players being able to repair submerged electrical items indefinitely.
- Fixed a "attempted to access a removed ragdoll" console error when a character wearing the health
scanner HUD is removed.
- Fixed console errors when a battery's or supercapacitor's capacity is set to 0 and the interface is open.
- Fixed ability to drop items into secure lockers (or other containers that require specific items)
without having access to it.
- Automatically move the gaps linked to doors when the door is moved (and vice versa).
- Fixed items disappearing when they're dropped into a full container.
- Fixed crashing when an item that requires aim to use is used by something else than a character
(e.g. a status effect).
- Fixed having a broken device on a sub in the sub editor and switching to Character Mode causing a crash.
- Fixed the bottom of the background ice texture not rendering correctly on high resolutions.
---------------------------------------------------------------------------------------------------------
v0.9.0.6
---------------------------------------------------------------------------------------------------------
@@ -17,13 +94,19 @@ Additions and changes:
- Added a close button to the debug console and a text that explains it can be opened/closed by pressing F3.
- Electrical components are attached to walls with the left mouse button instead of E.
- Flares are activated with the left mouse button instead of E.
- Nerfed plasma cutters.
- Nerfed plasma cutters and welding tools.
- Made coilgun ammunition boxes deconstructable.
- Removed minimum job count requirement from Captain, Engineer & Mechanic (i.e. it's not possible to play
other jobs even when there's 3 or less players on the server).
- Increased default respawn transport time to 5 minutes, decreased respawn interval to 3 minutes.
- Allow muting players mid-round through the info menu.
- Allow to edit the door opening/closing speeds and increase the defaults.
- Reduce the follow-up distance and require the bot to be in the same room than the player before stopping,
so that bots with a follow order don't stay on the doorways when the player is trying to enter the airlocks.
- Stop "breathing" deformations when the character is dead.
- Disable obstructed paths when the submarine docks to another submarine/outpost.
- The location of save files can be changed in config_player.xml using the attributes "overridesavefolder"
and "overridemultiplayersavefolder".
Bugfixes:
- Fixed crashing during startup due to faulty OpenAL installations.
@@ -35,8 +118,13 @@ excessive desync" error message.
- Fixed items not being repaired after purchasing repairs in the campaign.
- Removed outdated Launch_BarotraumaServer script from the Linux version (does not work anymore, the
dedicated server should be launched by running the file called "DedicatedServer").
- Fixed server owners not being able to join their own server if whitelist is enabled and the owner is not
on the whitelist.
- Fixed inability to ban clients by their Steam ID using the console.
- Fixed all servers showing up as "Round has not started" in the server list.
- Fixed server ignoring the max players value set in the "host server" menu and using the setting
configured in "serversettings.xml" instead.
- Fixed spectators not hearing the living players' voice chat.
- VOIP improvements (less crackling and pops).
- Increased oxygen output in vanilla submarines to prevent characters from suffocating when playing with
an oversized crew.
@@ -44,6 +132,8 @@ an oversized crew.
- Waypoint fixes in vanilla subs.
- Disallow shooting and attacking when the cursor is over a UI element (to prevent, for example,
accidentally firing a gun when dismissing a message box).
- Fixed "file not found" errors on Linux when attempting to enable a mod that defines file paths
using a backslash instead of a slash.
- Fixed monsters ignoring decoys.
- Fixed store menu switching back to the equipment category every time something is bought/sold in
the multiplayer campaign.
@@ -60,8 +150,10 @@ in the leftmost inventory slot.
- Prevent welding doors shut during the tutorials (as the player doesn't have access to any tools to
reopen the door).
- Fixed AI pathfinding when the path is from a submarine to another submarine.
- Fixed crashing when AI crew tries to find a path out of the ruins.
- Fixed Watchmen looking at the wrong direction.
- Fixed crashing due to an invalid display resolution.
- Fixed alien item assemblies not being hidden from the sub editor.
---------------------------------------------------------------------------------------------------------
v0.9.0.4
+32 -18
View File
@@ -1,38 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<serversettings
name="Server"
ServerMessage=""
port="27015"
queryport="27016"
password=""
public="True"
maxplayers="10"
attemptupnp="false"
tickrate="20"
randomizeseed="True"
userespawnshuttle="True"
respawninterval="300"
maxtransporttime="180"
minrespawnratio="0.2"
autorestartinterval="60"
startwhenclientsready="False"
startwhenclientsreadyratio="0.8"
allowspectating="True"
voicechatenabled="True"
voipenabled="True"
endroundatlevelend="True"
saveserverlogs="True"
allowragdollbutton="True"
allowfiletransfers="True"
allowrespawn="True"
voicechatenabled="True"
allowvotekick="True"
allowendvoting="True"
allowrespawn="True"
botcount="0"
maxbotcount="16"
allowdisguises="True"
subselectionmode="Manual"
modeselectionmode="Manual"
endvoterequiredratio="0.6"
kickvoterequiredratio="0.6"
killdisconnectedtime="30"
kickafktime="120"
traitoruseratio="True"
traitorratio="0.2"
karmaenabled="False"
KillDisconnectedTime="30"
SubSelection="Manual"
ModeSelection="Manual"
GameMode="Sandbox"
MissionType="Random"
TraitorsEnabled="No"
gamemodeidentifier="sandbox"
missiontype="Random"
autobantime="60"
maxautobantime="360"
AllowedClientNameChars="32-33,38-46,48-57,65-90,91,93,95-122,192-255,384-591,1024-1279"
/>
name="Server"
public="false"
port="27015"
queryport="27016"
maxplayers="10"
enableupnp="false"
autorestart="false"
SubSelection="Manual"
ModeSelection="Manual"
LevelDifficulty="20"
TraitorsEnabled="No"
BotSpawnMode="Normal"
AllowedRandomMissionTypes="Random,Salvage,Monster,Cargo,Combat"
AllowedClientNameChars="32-33,38-46,48-57,65-90,91,93,95-122,192-255,384-591,1024-1279"
ServerMessage="" />