(98ad00fa2) v0.9.1.0

This commit is contained in:
Joonas Rikkonen
2019-07-10 13:52:12 +03:00
parent 2a791887ed
commit afa2137bd2
104 changed files with 3041 additions and 3134 deletions
@@ -218,6 +218,35 @@ namespace Barotrauma
if (run || speedMultiplier <= 0.0f) targetMovement *= speedMultiplier;
Character.ResetSpeedMultiplier(); // Reset, items will set the value before the next update
Character.AnimController.TargetMovement = targetMovement;
if (!Character.LockHands)
{
DropUnnecessaryItems();
}
if (Character.IsKeyDown(InputType.Aim))
{
var cursorDiffX = Character.CursorPosition.X - Character.Position.X;
if (cursorDiffX > 10.0f)
{
Character.AnimController.TargetDir = Direction.Right;
}
else if (cursorDiffX < -10.0f)
{
Character.AnimController.TargetDir = Direction.Left;
}
if (Character.SelectedConstruction != null) Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
}
else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
{
Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
}
}
private void DropUnnecessaryItems()
{
if (!NeedsDivingGear(Character.CurrentHull))
{
bool oxygenLow = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold;
@@ -278,26 +307,6 @@ namespace Barotrauma
}
}
}
if (Character.IsKeyDown(InputType.Aim))
{
var cursorDiffX = Character.CursorPosition.X - Character.Position.X;
if (cursorDiffX > 10.0f)
{
Character.AnimController.TargetDir = Direction.Right;
}
else if (cursorDiffX < -10.0f)
{
Character.AnimController.TargetDir = Direction.Left;
}
if (Character.SelectedConstruction != null) Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
}
else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
{
Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
}
}
protected void ReportProblems()
@@ -428,16 +437,24 @@ namespace Barotrauma
}
else
{
float currentVitality = Character.CharacterHealth.Vitality;
float dmgPercentage = damage / currentVitality * 100;
if (dmgPercentage < currentVitality / 10)
// If not on the same team, always stay defensive
if (attacker.TeamID != Character.TeamID)
{
// Don't retaliate on minor (accidental) dmg done by friendly characters
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
}
else
{
AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
float currentVitality = Character.CharacterHealth.Vitality;
float dmgPercentage = damage / currentVitality * 100;
if (dmgPercentage < currentVitality / 10)
{
// Don't retaliate on minor (accidental) dmg done by characters that are in the same team
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
}
else
{
AddCombatObjective(AIObjectiveCombat.CombatMode.Defensive, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
}
}
}
}
@@ -448,24 +465,25 @@ namespace Barotrauma
void AddCombatObjective(AIObjectiveCombat.CombatMode mode, float delay = 0)
{
bool holdPosition = Character.Info?.Job?.Prefab.Identifier == "watchman";
if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective)
{
if (combatObjective.Enemy != attacker || (combatObjective.Enemy == null && attacker == null))
{
// Replace the old objective with the new.
ObjectiveManager.Objectives.Remove(combatObjective);
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager));
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager) { HoldPosition = holdPosition});
}
}
else
{
if (delay > 0)
{
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager), delay);
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager) { HoldPosition = holdPosition }, delay);
}
else
{
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager));
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker, mode, objectiveManager) { HoldPosition = holdPosition });
}
}
}
@@ -17,7 +17,8 @@ namespace Barotrauma
const float coolDown = 10.0f;
public Character Enemy { get; private set; }
public bool HoldPosition { get; set; }
private Item _weapon;
private Item Weapon
{
@@ -125,7 +126,10 @@ namespace Barotrauma
TryArm();
if (seekAmmunition == null || !subObjectives.Contains(seekAmmunition))
{
Move();
if (!HoldPosition)
{
Move();
}
if (WeaponComponent != null)
{
OperateWeapon(deltaTime);
@@ -151,6 +155,8 @@ namespace Barotrauma
private bool TryArm()
{
if (character.LockHands) { return false; }
if (Weapon != null)
{
if (!character.Inventory.Items.Contains(Weapon) || WeaponComponent == null)
@@ -160,7 +166,7 @@ namespace Barotrauma
else if (!WeaponComponent.HasRequiredContainedItems(false))
{
// Seek ammunition only if cannot find a new weapon
if (!Reload(true, () => GetWeapon(out _) == null))
if (!Reload(!HoldPosition, () => GetWeapon(out _) == null))
{
if (seekAmmunition != null && subObjectives.Contains(seekAmmunition))
{
@@ -266,7 +272,7 @@ namespace Barotrauma
private void Unequip()
{
if (character.SelectedItems.Contains(Weapon))
if (!character.LockHands && character.SelectedItems.Contains(Weapon))
{
if (!Weapon.AllowedSlots.Contains(InvSlotType.Any) || !character.Inventory.TryPutItem(Weapon, character, new List<InvSlotType>() { InvSlotType.Any }))
{
@@ -277,6 +283,7 @@ namespace Barotrauma
private bool Equip()
{
if (character.LockHands) { return false; }
if (!WeaponComponent.HasRequiredContainedItems(false))
{
Mode = CombatMode.Retreat;
@@ -322,6 +329,13 @@ namespace Barotrauma
private void Engage()
{
if (character.LockHands)
{
Mode = CombatMode.Retreat;
SteeringManager.Reset();
return;
}
retreatTarget = null;
RemoveSubObjective(ref retreatObjective);
RemoveSubObjective(ref seekAmmunition);
@@ -186,13 +186,14 @@ namespace Barotrauma
}
}
public Hull FindBestHull(IEnumerable<Hull> ignoredHulls = null)
public Hull FindBestHull(IEnumerable<Hull> ignoredHulls = null, bool allowChangingTheSubmarine = true)
{
Hull bestHull = null;
float bestValue = 0;
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine == null) { continue; }
if (!allowChangingTheSubmarine && hull.Submarine != character.Submarine) { continue; }
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
if (unreachable.Contains(hull)) { continue; }
float hullSafety = 0;
@@ -96,6 +96,12 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
if (character.LockHands)
{
abandon = true;
return;
}
FindTargetItem();
if (targetItem == null || moveToTarget == null)
{
@@ -37,14 +37,21 @@ namespace Barotrauma
float isSelected = character.SelectedConstruction == Item ? 50 : 0;
float devotion = (Math.Min(Priority, 10) + isSelected) / 100;
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - 1, 90);
bool isCompleted = Item.IsFullCondition;
if (isCompleted && character.SelectedConstruction == Item)
{
character?.Speak(TextManager.GetWithVariable("DialogItemRepaired", "[itemname]", Item.Name, true), null, 0.0f, "itemrepaired", 10.0f);
}
return MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + damagePriority * distanceFactor * successFactor * PriorityModifier, 0, 1));
}
public override bool IsCompleted()
{
bool isCompleted = Item.IsFullCondition;
if (isCompleted)
{
if (isCompleted && character.SelectedConstruction == Item)
{
character?.Speak(TextManager.GetWithVariable("DialogItemRepaired", "[itemname]", Item.Name, true), null, 0.0f, "itemrepaired", 10.0f);
}
return isCompleted;
@@ -97,7 +97,7 @@ namespace Barotrauma
}
else
{
string[] splitOptionNames = translatedOptionNames.Split(',');
string[] splitOptionNames = translatedOptionNames.Split(',', '');
OptionNames = new string[Options.Length];
for (int i = 0; i < Options.Length && i < splitOptionNames.Length; i++)
{
@@ -30,11 +30,13 @@ namespace Barotrauma
[Serialize(0f, true), Editable(-360, 360, ToolTip = "Rotation offset (in degrees) used for animations and widgets. If the sprites in the sheet are in different orientations, use the orientation of the torso for the final version of your character (while editing the character in the editor, you can change the orientation freely).")]
public float SpritesheetOrientation { get; set; }
private float limbScale;
[Serialize(1.0f, true), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)]
public float LimbScale { get; set; }
public float LimbScale { get { return limbScale; } set { limbScale = MathHelper.Clamp(value, MIN_SCALE, MAX_SCALE); } }
private float jointScale;
[Serialize(1.0f, true), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)]
public float JointScale { get; set; }
public float JointScale { get { return jointScale; } set { jointScale = MathHelper.Clamp(value, MIN_SCALE, MAX_SCALE); } }
// Don't show in the editor, because shouldn't be edited in runtime. Requires that the limb scale and the collider sizes are adjusted. TODO: automatize.
[Serialize(1f, false)]
@@ -368,16 +370,18 @@ namespace Barotrauma
#endregion
#if CLIENT
public override void AddToEditor(ParamsEditor editor)
public void AddToEditor(ParamsEditor editor, bool alsoChildren = true)
{
base.AddToEditor(editor);
var subParams = GetAllSubParams();
foreach (var subParam in subParams)
if (alsoChildren)
{
subParam.AddToEditor(editor);
//TODO: divider sprite
new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, 10), editor.EditorBox.Content.RectTransform),
style: "ConnectionPanelWire");
var subParams = GetAllSubParams();
foreach (var subParam in subParams)
{
subParam.AddToEditor(editor);
new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, 10), editor.EditorBox.Content.RectTransform),
style: null, color: Color.Black);
}
}
}
#endif
@@ -1427,7 +1427,7 @@ namespace Barotrauma
Vector2 rayEnd = rayStart - new Vector2(0.0f, height);
var lowestLimb = FindLowestLimb();
//var lowestLimb = FindLowestLimb();
float closestFraction = 1;
GameMain.World.RayCast((fixture, point, normal, fraction) =>
@@ -1440,7 +1440,7 @@ namespace Barotrauma
break;
case Physics.CollisionPlatform:
Structure platform = fixture.Body.UserData as Structure;
if (IgnorePlatforms || lowestLimb.Position.Y < platform.Rect.Y) return -1;
if (IgnorePlatforms && TargetMovement.Y < -0.5f || Collider.Position.Y < platform.Rect.Y) return -1;
break;
case Physics.CollisionWall:
case Physics.CollisionLevel:
@@ -130,16 +130,20 @@ namespace Barotrauma
{
get
{
if (ViewTarget == null) return AnimController.AimSourcePos;
if (ViewTarget == null) { return AnimController.AimSourcePos; }
Vector2 viewTargetWorldPos = ViewTarget.WorldPosition;
if (ViewTarget is Item targetItem)
{
Turret turret = targetItem.GetComponent<Turret>();
if (turret != null)
{
return new Vector2(targetItem.Rect.X + turret.TransformedBarrelPos.X, targetItem.Rect.Y - turret.TransformedBarrelPos.Y);
viewTargetWorldPos = new Vector2(
targetItem.WorldRect.X + turret.TransformedBarrelPos.X,
targetItem.WorldRect.Y - turret.TransformedBarrelPos.Y);
}
}
return ViewTarget.Position;
return Position + (viewTargetWorldPos - WorldPosition);
}
}
@@ -1517,7 +1521,7 @@ namespace Barotrauma
public bool CanAccessInventory(Inventory inventory)
{
if (!CanInteract) return false;
if (!CanInteract || inventory.Locked) { return false; }
//the inventory belongs to some other character
if (inventory.Owner is Character && inventory.Owner != this)
@@ -1558,7 +1562,11 @@ namespace Barotrauma
{
distanceToItem = -1.0f;
if (!CanInteract || item.HiddenInGame) return false;
bool hidden = item.HiddenInGame;
#if CLIENT
if (Screen.Selected == GameMain.SubEditorScreen) { hidden = false; }
#endif
if (!CanInteract || hidden) return false;
if (item.ParentInventory != null)
{
@@ -1802,7 +1810,7 @@ namespace Barotrauma
{
SelectCharacter(focusedCharacter);
}
else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow)
else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow && CanInteractWith(focusedCharacter, 160f, false))
{
if (focusedCharacter == SelectedCharacter)
{
@@ -105,7 +105,7 @@ namespace Barotrauma
public static bool CheatsEnabled;
private static List<ColoredText> unsavedMessages = new List<ColoredText>();
private static int messagesPerFile = 800;
private static int messagesPerFile = 5000;
public const string SavePath = "ConsoleLogs";
private static void AssignOnExecute(string names, Action<string[]> onExecute)
@@ -648,6 +648,20 @@ namespace Barotrauma
},null));
#if DEBUG
commands.Add(new Command("teleportsub", "teleportsub [start/end]: Teleport the submarine to the start or end of the level. WARNING: does not take outposts into account, so often leads to physics glitches. Only use for debugging.", (string[] args) =>
{
if (Submarine.MainSub == null || Level.Loaded == null) return;
if (args.Length > 0 && args[0].ToLowerInvariant() == "start")
{
Submarine.MainSub.SetPosition(Level.Loaded.StartPosition);
}
else
{
Submarine.MainSub.SetPosition(Level.Loaded.EndPosition);
}
}, isCheat: true));
commands.Add(new Command("waterphysicsparams", "waterphysicsparams [stiffness] [spread] [damping]: defaults 0.02, 0.05, 0.05", (string[] args) =>
{
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
@@ -1582,7 +1596,14 @@ namespace Barotrauma
}
}
string fileName = "DebugConsoleLog_" + DateTime.Now.ToShortDateString() + "_" + DateTime.Now.ToShortTimeString();
string fileName = "DebugConsoleLog_";
#if SERVER
fileName += "Server_";
#else
fileName += "Client_";
#endif
fileName += DateTime.Now.ToShortDateString() + "_" + DateTime.Now.ToShortTimeString();
var invalidChars = Path.GetInvalidFileNameChars();
foreach (char invalidChar in invalidChars)
{
@@ -79,6 +79,7 @@ namespace Barotrauma
public int GetTotalItemCost()
{
if (purchasedItems == null) return 0;
return purchasedItems.Sum(i => i.ItemPrefab.GetPrice(campaign.Map.CurrentLocation).BuyPrice * i.Quantity);
}
@@ -267,7 +267,13 @@ namespace Barotrauma
private static bool sendUserStatistics = true;
public static bool SendUserStatistics
{
get { return sendUserStatistics; }
get
{
#if DEBUG
return false;
#endif
return sendUserStatistics;
}
set
{
sendUserStatistics = value;
@@ -178,29 +178,35 @@ namespace Barotrauma.Items.Components
private void SetUser(Character character)
{
if (user == character) return;
if (user != null && user.Removed) user = null;
if (user == character) { return; }
if (user != null && user.Removed) { user = null; }
user = character;
if (item.body?.FarseerBody == null || item.Removed ||
!GameMain.World.BodyList.Contains(item.body.FarseerBody))
{
return;
}
if (user != null)
{
foreach (Limb limb in user.AnimController.Limbs)
{
if (limb.body.FarseerBody != null)
if (limb.body.FarseerBody != null && GameMain.World.BodyList.Contains(limb.body.FarseerBody))
{
if (GameMain.World.BodyList.Contains(limb.body.FarseerBody))
{
item.body.FarseerBody.RestoreCollisionWith(limb.body.FarseerBody);
}
item.body.FarseerBody.RestoreCollisionWith(limb.body.FarseerBody);
}
}
}
foreach (Limb limb in character.AnimController.Limbs)
{
item.body.FarseerBody.IgnoreCollisionWith(limb.body.FarseerBody);
if (limb.body.FarseerBody != null && GameMain.World.BodyList.Contains(limb.body.FarseerBody))
{
item.body.FarseerBody.IgnoreCollisionWith(limb.body.FarseerBody);
}
}
user = character;
}
private void RestoreCollision()
@@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components
private UsableIn usableIn;
[Serialize(0.0f, false), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
[Serialize(0.0f, false), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)]
public float Force
{
get { return force; }
@@ -11,6 +11,8 @@ namespace Barotrauma.Items.Components
private float progressTimer;
private float progressState;
private bool hasPower;
private ItemContainer inputContainer, outputContainer;
public ItemContainer InputContainer
@@ -58,7 +60,8 @@ namespace Barotrauma.Items.Components
return;
}
if (voltage < minVoltage) { return; }
hasPower = voltage >= minVoltage;
if (!hasPower) { return; }
var repairable = item.GetComponent<Repairable>();
if (repairable != null)
@@ -68,7 +71,7 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (powerConsumption == 0.0f) voltage = 1.0f;
if (powerConsumption == 0.0f) { voltage = 1.0f; }
progressTimer += deltaTime * voltage;
Voltage -= deltaTime * 10.0f;
@@ -107,28 +110,41 @@ namespace Barotrauma.Items.Components
}
}
if (targetItem.Prefab.DeconstructItems.Any())
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
MoveInputQueue();
PutItemsToLinkedContainer();
}
else
{
if (outputContainer.Inventory.Items.All(i => i != null))
if (targetItem.Prefab.DeconstructItems.Any())
{
targetItem.Drop(dropper: null);
//drop all items that are inside the deconstructed item
foreach (ItemContainer ic in targetItem.GetComponents<ItemContainer>())
{
if (ic?.Inventory?.Items == null) { continue; }
foreach (Item containedItem in ic.Inventory.Items)
{
containedItem?.Drop(dropper: null, createNetworkEvent: true);
}
}
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
MoveInputQueue();
PutItemsToLinkedContainer();
}
else
{
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
if (outputContainer.Inventory.Items.All(i => i != null))
{
targetItem.Drop(dropper: null);
}
else
{
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
}
}
}
if (inputContainer.Inventory.Items.Any(i => i != null))
{
#if SERVER
item.CreateServerEvent(this);
#endif
progressTimer = 0.0f;
progressState = 0.0f;
}
}
@@ -188,21 +204,17 @@ namespace Barotrauma.Items.Components
}
#endif
if (!IsActive) { progressState = 0.0f; }
#if CLIENT
if (!IsActive)
{
progressTimer = 0.0f;
activateButton.Text = TextManager.Get("DeconstructorDeconstruct");
}
else
{
activateButton.Text = TextManager.Get("DeconstructorCancel");
progressState = 0.0f;
}
#if CLIENT
activateButton.Text = TextManager.Get(IsActive ? "DeconstructorCancel" : "DeconstructorDeconstruct");
#endif
inputContainer.Inventory.Locked = IsActive;
inputContainer.Inventory.Locked = IsActive;
}
}
}
@@ -19,6 +19,8 @@ namespace Barotrauma.Items.Components
private float timeUntilReady;
private float requiredTime;
private bool hasPower;
private Character user;
private ItemContainer inputContainer, outputContainer;
@@ -198,8 +200,9 @@ namespace Barotrauma.Items.Components
progressState = fabricatedItem == null ? 0.0f : (requiredTime - timeUntilReady) / requiredTime;
if (voltage < minVoltage) { return; }
hasPower = voltage >= minVoltage;
if (!hasPower) { return; }
var repairable = item.GetComponent<Repairable>();
if (repairable != null)
{
@@ -215,43 +218,50 @@ namespace Barotrauma.Items.Components
if (timeUntilReady > 0.0f) { return; }
var availableIngredients = GetAvailableIngredients();
foreach (FabricationRecipe.RequiredItem ingredient in fabricatedItem.RequiredItems)
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
for (int i = 0; i < ingredient.Amount; i++)
var availableIngredients = GetAvailableIngredients();
foreach (FabricationRecipe.RequiredItem ingredient in fabricatedItem.RequiredItems)
{
var requiredItem = inputContainer.Inventory.Items.FirstOrDefault(it => it != null && it.Prefab == ingredient.ItemPrefab && it.Condition >= ingredient.ItemPrefab.Health * ingredient.MinCondition);
if (requiredItem == null) continue;
//Item4 = use condition bool
if (ingredient.UseCondition && requiredItem.Condition - ingredient.ItemPrefab.Health * ingredient.MinCondition > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0
for (int i = 0; i < ingredient.Amount; i++)
{
requiredItem.Condition -= ingredient.ItemPrefab.Health * ingredient.MinCondition;
continue;
var availableItem = availableIngredients.FirstOrDefault(it => it != null && it.Prefab == ingredient.ItemPrefab && it.Condition >= ingredient.ItemPrefab.Health * ingredient.MinCondition);
if (availableItem == null) { continue; }
//Item4 = use condition bool
if (ingredient.UseCondition && availableItem.Condition - ingredient.ItemPrefab.Health * ingredient.MinCondition > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0
{
availableItem.Condition -= ingredient.ItemPrefab.Health * ingredient.MinCondition;
continue;
}
availableIngredients.Remove(availableItem);
Entity.Spawner.AddToRemoveQueue(availableItem);
inputContainer.Inventory.RemoveItem(availableItem);
}
Entity.Spawner.AddToRemoveQueue(requiredItem);
inputContainer.Inventory.RemoveItem(requiredItem);
}
}
if (outputContainer.Inventory.Items.All(i => i != null))
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
}
else
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
}
if ((GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) && user != null && !user.Removed)
{
foreach (Skill skill in fabricatedItem.RequiredSkills)
if (outputContainer.Inventory.Items.All(i => i != null))
{
user.Info.IncreaseSkillLevel(skill.Identifier, skill.Level / 100.0f * SkillIncreaseMultiplier, user.WorldPosition + Vector2.UnitY * 150.0f);
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
}
else
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
}
if (user != null && !user.Removed)
{
foreach (Skill skill in fabricatedItem.RequiredSkills)
{
user.Info.IncreaseSkillLevel(skill.Identifier, skill.Level / 100.0f * SkillIncreaseMultiplier, user.WorldPosition + Vector2.UnitY * 150.0f);
}
}
}
CancelFabricating(null);
CancelFabricating(null);
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
private bool CanBeFabricated(FabricationRecipe fabricableItem)
@@ -328,6 +338,8 @@ namespace Barotrauma.Items.Components
{
var matchingItem = availableIngredients.Find(it => !usedItems.Contains(it) && IsItemValidIngredient(it, requiredItem));
if (matchingItem == null) { continue; }
availableIngredients.Remove(matchingItem);
if (matchingItem.ParentInventory == inputContainer.Inventory)
{
@@ -254,15 +254,19 @@ namespace Barotrauma.Items.Components
{
foreach (Connection connection in connections)
{
if (!connection.IsPower) continue;
if (!connection.IsPower) { continue; }
foreach (Connection recipient in connection.Recipients)
{
if (!(recipient.Item is Item it)) continue;
if (!(recipient.Item is Item it)) { continue; }
PowerTransfer pt = it.GetComponent<PowerTransfer>();
if (pt == null) continue;
if (pt == null) { continue; }
load = Math.Max(load, pt.PowerLoad);
//calculate how much external power there is in the grid
//(power coming from somewhere else than this reactor, e.g. batteries)
float externalPower = CurrPowerConsumption - pt.CurrPowerConsumption;
//reduce the external power from the load to prevent overloading the grid
load = Math.Max(load, pt.PowerLoad - externalPower);
}
}
}
@@ -176,7 +176,7 @@ namespace Barotrauma.Items.Components
float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f;
//item must have been below 50% condition for the player to get an achievement or XP for repairing it
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
if (item.Condition < ShowRepairUIThreshold)
{
wasBroken = true;
@@ -61,6 +61,7 @@ namespace Barotrauma.Items.Components
{
get
{
if (GameMain.NetworkMember?.ServerSettings != null && !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return false; }
return locked || connections.Any(c => c != null && c.ConnectionPanel.Locked);
}
set { locked = value; }
@@ -152,13 +152,12 @@ namespace Barotrauma
get { return description ?? prefab.Description; }
set { description = value; }
}
private bool hiddenInGame;
[Editable, Serialize(false, true)]
public bool HiddenInGame
{
get { return hiddenInGame; }
set { hiddenInGame = value; }
get;
set;
}
public float ImpactTolerance
@@ -262,7 +261,26 @@ namespace Barotrauma
/// </summary>
public string ContainerIdentifier
{
get { return Container?.prefab.Identifier ?? ""; }
get
{
return
Container?.prefab.Identifier ??
ParentInventory?.Owner?.ToString() ??
"";
}
set { /*do nothing*/ }
}
[Serialize(false, false)]
/// <summary>
/// Can be used by status effects or conditionals to check if the physics body of the item is active
/// </summary>
public bool PhysicsBodyActive
{
get
{
return body != null && body.Enabled;
}
set { /*do nothing*/ }
}
@@ -315,7 +333,7 @@ namespace Barotrauma
get { return spriteColor; }
}
public bool IsFullCondition => Condition >= MaxCondition;
public bool IsFullCondition => MathUtils.NearlyEqual(Condition, MaxCondition);
public float MaxCondition => Prefab.Health;
public float ConditionPercentage => MathUtils.Percentage(Condition, MaxCondition);
@@ -334,6 +352,8 @@ namespace Barotrauma
if (Indestructible) return;
float prev = condition;
bool wasInFullCondition = IsFullCondition;
condition = MathHelper.Clamp(value, 0.0f, Prefab.Health);
if (condition == 0.0f && prev > 0.0f)
{
@@ -349,9 +369,17 @@ namespace Barotrauma
SetActiveSprite();
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !MathUtils.NearlyEqual(lastSentCondition, condition))
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
if (Math.Abs(lastSentCondition - condition) > 1.0f || condition == 0.0f || condition == Prefab.Health)
if (Math.Abs(lastSentCondition - condition) > 1.0f)
{
conditionUpdatePending = true;
}
else if (wasInFullCondition != IsFullCondition)
{
conditionUpdatePending = true;
}
else if (!MathUtils.NearlyEqual(lastSentCondition, condition) && (condition <= 0.0f || condition >= Prefab.Health))
{
conditionUpdatePending = true;
}
@@ -2086,11 +2114,13 @@ namespace Barotrauma
System.Diagnostics.Debug.Assert(Submarine != null || rootContainer.ParentInventory?.Owner is Character);
Vector2 subPosition = Submarine == null ? Vector2.Zero : Submarine.HiddenSubPosition;
int width = ResizeHorizontal ? rect.Width : defaultRect.Width;
int height = ResizeVertical ? rect.Height : defaultRect.Height;
element.Add(new XAttribute("rect",
(int)(rect.X - subPosition.X) + "," +
(int)(rect.Y - subPosition.Y) + "," +
defaultRect.Width + "," + defaultRect.Height));
width + "," + height));
if (linkedTo != null && linkedTo.Count > 0)
{
@@ -17,7 +17,8 @@ namespace Barotrauma
}
public bool IsOptional { get; set; }
public bool MatchOnEmpty { get; set; }
public bool IgnoreInEditor { get; set; }
private string[] excludedIdentifiers;
@@ -99,6 +100,11 @@ namespace Barotrauma
var containedItems = parentItem.ContainedItems;
if (containedItems == null) return false;
if (MatchOnEmpty && !containedItems.Any(ci => ci != null))
{
return true;
}
foreach (Item contained in containedItems)
{
if (contained.Condition > 0.0f && MatchesItem(contained)) return true;
@@ -222,6 +228,7 @@ namespace Barotrauma
ri.IsOptional = element.GetAttributeBool("optional", false);
ri.IgnoreInEditor = element.GetAttributeBool("ignoreineditor", false);
ri.MatchOnEmpty = element.GetAttributeBool("matchonempty", false);
return ri;
}
}
@@ -627,7 +627,8 @@ namespace Barotrauma
if (IsHorizontal)
{
if (Math.Max(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface + hull2.WaveY[0]) > rect.Y) return;
//if the water level is above the gap, oxygen doesn't circulate
if (Math.Max(hull1.WorldSurface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.WorldSurface + hull2.WaveY[0]) > WorldRect.Y) { return; }
}
float totalOxygen = hull1.Oxygen + hull2.Oxygen;
@@ -1548,13 +1548,13 @@ namespace Barotrauma
Point? minSize = null;
DockingPort subPort = null;
float closestDistance = float.MaxValue;
if (Submarine.MainSub != null)
{
Point subSize = Submarine.MainSub.GetDockedBorders().Size;
Point outpostSize = outpost.GetDockedBorders().Size;
minSize = new Point(Math.Max(subSize.X, outpostSize.X), subSize.Y + outpostSize.Y);
float closestDistance = float.MaxValue;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal || port.Docked) { continue; }
@@ -1570,17 +1570,43 @@ namespace Barotrauma
}
}
DockingPort outpostPort = null;
closestDistance = float.MaxValue;
foreach (DockingPort port in DockingPort.List)
{
if (port.IsHorizontal || port.Docked) { continue; }
if (port.Item.Submarine != outpost) { continue; }
//the outpost port has to be at the bottom of the outpost
if (port.Item.WorldPosition.Y > outpost.WorldPosition.Y) { continue; }
float dist = Math.Abs(port.Item.WorldPosition.X - outpost.WorldPosition.X);
if (dist < closestDistance)
{
outpostPort = port;
closestDistance = dist;
}
}
float subDockingPortOffset = subPort == null ? 0.0f : subPort.Item.WorldPosition.X - Submarine.MainSub.WorldPosition.X;
//don't try to compensate if the port is very far from the sub's center of mass
if (Math.Abs(subDockingPortOffset) > 2000.0f)
if (Math.Abs(subDockingPortOffset) > 5000.0f)
{
subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -2000.0f, 2000.0f);
subDockingPortOffset = MathHelper.Clamp(subDockingPortOffset, -5000.0f, 5000.0f);
string warningMsg = "Docking port very far from the sub's center of mass (submarine: " + Submarine.MainSub.Name + ", dist: " + subDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
DebugConsole.NewMessage(warningMsg, Color.Orange);
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:DockingPortVeryFar" + Submarine.MainSub.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg);
}
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset));
float outpostDockingPortOffset = subPort == null ? 0.0f : outpostPort.Item.WorldPosition.X - outpost.WorldPosition.X;
//don't try to compensate if the port is very far from the outpost's center of mass
if (Math.Abs(outpostDockingPortOffset) > 5000.0f)
{
outpostDockingPortOffset = MathHelper.Clamp(outpostDockingPortOffset, -5000.0f, 5000.0f);
string warningMsg = "Docking port very far from the outpost's center of mass (outpost: " + outpost.Name + ", dist: " + outpostDockingPortOffset + "). The level generator may not be able to place the outpost so that docking is possible.";
DebugConsole.NewMessage(warningMsg, Color.Orange);
GameAnalyticsManager.AddErrorEventOnce("Lever.CreateOutposts:OutpostDockingPortVeryFar" + outpost.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, warningMsg);
}
outpost.SetPosition(outpost.FindSpawnPos(i == 0 ? StartPosition : EndPosition, minSize, subDockingPortOffset - outpostDockingPortOffset));
if ((i == 0) == !Mirrored)
{
StartOutpost = outpost;
@@ -308,18 +308,24 @@ namespace Barotrauma
if (ResizeHorizontal) placeSize.X = position.X - placePosition.X;
if (ResizeVertical) placeSize.Y = placePosition.Y - position.Y;
newRect = Submarine.AbsRect(placePosition, placeSize);
if (PlayerInput.LeftButtonReleased())
//don't allow resizing width/height to less than the grid size
if (ResizeHorizontal && Math.Abs(placeSize.X) < Submarine.GridSize.X)
{
//don't allow resizing width/height to zero
if ((!ResizeHorizontal || placeSize.X != 0.0f) && (!ResizeVertical || placeSize.Y != 0.0f))
{
newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position);
placeSize.X = Submarine.GridSize.X;
}
if (ResizeVertical && Math.Abs(placeSize.Y) < Submarine.GridSize.Y)
{
placeSize.X = Submarine.GridSize.Y;
}
var structure = new Structure(newRect, this, Submarine.MainSub);
structure.Submarine = Submarine.MainSub;
}
newRect = Submarine.AbsRect(placePosition, placeSize);
if (PlayerInput.LeftButtonReleased())
{
newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position);
var structure = new Structure(newRect, this, Submarine.MainSub)
{
Submarine = Submarine.MainSub
};
selected = null;
return;
@@ -297,6 +297,12 @@ namespace Barotrauma
}
}
public bool IsFileCorrupted
{
get;
private set;
}
//constructors & generation ----------------------------------------------------
public Submarine(string filePath, string hash = "", bool tryLoad = true) : base(null)
@@ -322,12 +328,17 @@ namespace Barotrauma
int maxLoadRetries = 4;
for (int i = 0; i <= maxLoadRetries; i++)
{
doc = OpenFile(filePath);
doc = OpenFile(filePath, out Exception e);
if (e != null && !(e is IOException)) { break; }
if (doc != null || i == maxLoadRetries || !File.Exists(filePath)) { break; }
DebugConsole.NewMessage("Opening submarine file \"" + filePath + "\" failed, retrying in 250 ms...");
Thread.Sleep(250);
}
if (doc == null || doc.Root == null) { return; }
if (doc == null || doc.Root == null)
{
IsFileCorrupted = true;
return;
}
if (doc != null && doc.Root != null)
{
@@ -1139,7 +1150,11 @@ namespace Barotrauma
savedSubmarines[i].Dispose();
}
}
savedSubmarines.Add(new Submarine(filePath));
var sub = new Submarine(filePath);
if (!sub.IsFileCorrupted)
{
savedSubmarines.Add(sub);
}
savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList();
}
@@ -1193,16 +1208,26 @@ namespace Barotrauma
foreach (string path in filePaths)
{
savedSubmarines.Add(new Submarine(path));
var sub = new Submarine(path);
if (!sub.IsFileCorrupted)
{
savedSubmarines.Add(sub);
}
}
}
static readonly string TempFolder = Path.Combine("Submarine", "Temp");
public static XDocument OpenFile(string file)
{
return OpenFile(file, out _);
}
public static XDocument OpenFile(string file, out Exception exception)
{
XDocument doc = null;
string extension = "";
exception = null;
try
{
@@ -1229,6 +1254,7 @@ namespace Barotrauma
}
catch (Exception e)
{
exception = e;
DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed!", e);
return null;
}
@@ -1243,6 +1269,7 @@ namespace Barotrauma
catch (Exception e)
{
exception = e;
DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed! (" + e.Message + ")");
return null;
}
@@ -1257,6 +1284,7 @@ namespace Barotrauma
catch (Exception e)
{
exception = e;
DebugConsole.ThrowError("Loading submarine \"" + file + "\" failed! (" + e.Message + ")");
return null;
}
@@ -10,6 +10,8 @@ namespace Barotrauma.Networking
public const int MaxPlayers = 16;
public const int ServerNameMaxLength = 60;
public static string MasterServerUrl = GameMain.Config.MasterServerUrl;
//if a Character is further than this from the sub and the players, the server will disable it
@@ -21,16 +21,28 @@ namespace Barotrauma.Networking
public readonly Entity Entity;
public readonly UInt16 ID;
public UInt16 EntityID
{
get;
private set;
}
//arbitrary extra data that will be passed to the Write method of the serializable entity
//(the index of an itemcomponent for example)
protected object[] Data;
public bool Sent;
protected NetEntityEvent(INetSerializable entity, UInt16 id)
protected NetEntityEvent(INetSerializable serializableEntity, UInt16 id)
{
this.ID = id;
this.Entity = entity as Entity;
this.Entity = serializableEntity as Entity;
RefreshEntityID();
}
public void RefreshEntityID()
{
this.EntityID = this.Entity is Entity entity ? entity.ID : Entity.NullEntityID;
}
public void SetData(object[] data)
@@ -65,7 +65,7 @@ namespace Barotrauma.Networking
break;
}
tempBuffer.Write((UInt16)e.Entity.ID);
tempBuffer.Write(e.EntityID);
tempBuffer.Write((byte)tempEventBuffer.LengthBytes);
tempBuffer.Write(tempEventBuffer);
tempBuffer.WritePadBits();
@@ -241,6 +241,32 @@ namespace Barotrauma.Networking
public virtual void Update(float deltaTime) { }
public virtual void Disconnect() { }
}
/// <summary>
/// Check if the two version are compatible (= if they can play together in multiplayer).
/// Returns null if compatibility could not be determined (invalid/unknown version number).
/// </summary>
public static bool? IsCompatible(string myVersion, string remoteVersion)
{
if (string.IsNullOrEmpty(myVersion) || string.IsNullOrEmpty(remoteVersion)) { return null; }
if (!Version.TryParse(myVersion, out Version myVersionNumber)) { return null; }
if (!Version.TryParse(remoteVersion, out Version remoteVersionNumber)) { return null; }
return IsCompatible(myVersionNumber, remoteVersionNumber);
}
/// <summary>
/// Check if the two version are compatible (= if they can play together in multiplayer).
/// </summary>
public static bool IsCompatible(Version myVersion, Version remoteVersion)
{
//major.minor.build.revision
//revision number is ignored, other values have to match
return
myVersion.Major == remoteVersion.Major &&
myVersion.Minor == remoteVersion.Minor &&
myVersion.Build == remoteVersion.Build;
}
}
}
@@ -31,6 +31,8 @@ namespace Barotrauma.Networking
partial class ServerSettings : ISerializableEntity
{
public const string SettingsFile = "serversettings.xml";
[Flags]
public enum NetFlags : byte
{
@@ -539,7 +541,14 @@ namespace Barotrauma.Networking
get;
set;
}
[Serialize(true, true)]
public bool AllowRewiring
{
get;
set;
}
private YesNoMaybe traitorsEnabled;
public YesNoMaybe TraitorsEnabled
{
@@ -21,7 +21,11 @@ namespace Barotrauma.Steam
{ "monster", 8 },
{ "art", 8 },
{ "mission", 8 },
{ "environment", 5 }
{ "event set", 8 },
{ "total conversion", 5 },
{ "environment", 5 },
{ "item assembly", 5 },
{ "language", 5 }
};
private List<string> popularTags = new List<string>();
@@ -375,7 +375,7 @@ namespace Barotrauma
//Enum.TryParse(element.GetAttributeString("bodytype", "Dynamic"), out BodyType bodyType);
body.BodyType = BodyType.Dynamic;
body.CollisionCategories = Physics.CollisionItem;
body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform;
body.Friction = element.GetAttributeFloat("friction", 0.3f);
body.Restitution = element.GetAttributeFloat("restitution", 0.05f);
body.UserData = this;
@@ -414,9 +414,6 @@ namespace Barotrauma
{
switch (Name)
{
case "Condition":
if (parentObject is Item item) { return item.Condition; }
break;
case "Voltage":
if (parentObject is Powered powered) { return powered.Voltage; }
break;
@@ -460,6 +457,21 @@ namespace Barotrauma
case "IsOn":
{ if (parentObject is LightComponent lightComponent) { return lightComponent.IsOn; } }
break;
case "Condition":
{
if (parentObject is Item item) { return item.Condition; }
}
break;
case "ContainerIdentifier":
{
if (parentObject is Item item) { return item.ContainerIdentifier; }
}
break;
case "PhysicsBodyActive":
{
if (parentObject is Item item) { return item.PhysicsBodyActive; }
}
break;
}
return null;
@@ -114,7 +114,7 @@ namespace Barotrauma
string stringValue = element.Attribute(name).Value;
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
string[] splitValue = stringValue.Split(',');
string[] splitValue = stringValue.Split(',', '');
if (convertToLowerInvariant)
{
@@ -277,7 +277,7 @@ namespace Barotrauma
case OperatorType.Equals:
if (type == typeof(bool))
{
return ((bool)propertyValue) == (AttributeValue == "true");
return ((bool)propertyValue) == (AttributeValue == "true" || AttributeValue == "True");
}
else if (FloatValue == null)
{
@@ -290,7 +290,7 @@ namespace Barotrauma
case OperatorType.NotEquals:
if (type == typeof(bool))
{
return ((bool)propertyValue) != (AttributeValue == "true");
return ((bool)propertyValue) != (AttributeValue == "true" || AttributeValue == "True");
}
else if (FloatValue == null)
{
@@ -424,6 +424,7 @@ namespace Barotrauma
public virtual bool HasRequiredConditions(List<ISerializableEntity> targets)
{
if (!propertyConditionals.Any()) return true;
if (requiredItems.All(ri => ri.MatchOnEmpty) && targets.Count == 0) return true;
switch (conditionalComparison)
{
case PropertyConditional.Comparison.Or:
@@ -145,7 +145,7 @@ namespace Barotrauma
UnlockAchievement(c, "clowncostume");
}
if (Submarine.MainSub != null && c.Submarine == null)
if (Submarine.MainSub != null && c.Submarine == null && c.ConfigPath == Character.HumanConfigFile)
{
float dist = 500 / Physics.DisplayToRealWorldRatio;
if (Vector2.DistanceSquared(c.WorldPosition, Submarine.MainSub.WorldPosition) >
@@ -322,6 +322,8 @@ namespace Barotrauma
string[] messages = serverMessage.Split('/');
bool translationsFound = false;
try
{
for (int i = 0; i < messages.Length; i++)
@@ -333,6 +335,7 @@ namespace Barotrauma
if (msg != null) // If a translation was found, otherwise use the original
{
messages[i] = msg;
translationsFound = true;
}
}
else
@@ -343,6 +346,7 @@ namespace Barotrauma
if (msg != null) // If a translation was found, otherwise use the original
{
messages[i] = msg;
translationsFound = true;
}
else
{
@@ -358,12 +362,19 @@ namespace Barotrauma
}
}
string translatedServerMessage = string.Empty;
for (int i = 0; i < messages.Length; i++)
if (translationsFound)
{
translatedServerMessage += messages[i];
string translatedServerMessage = string.Empty;
for (int i = 0; i < messages.Length; i++)
{
translatedServerMessage += messages[i];
}
return translatedServerMessage;
}
else
{
return serverMessage;
}
return translatedServerMessage;
}
catch (IndexOutOfRangeException exception)