(ce8e185aa) Merge branch 'dev' of https://github.com/Regalis11/Barotrauma-development into dev

This commit is contained in:
Joonas Rikkonen
2019-04-16 17:12:09 +03:00
parent bd07acf26d
commit 6262b3ea54
96 changed files with 1972 additions and 825 deletions
@@ -12,6 +12,8 @@ namespace Barotrauma
{
partial class EnemyAIController : AIController
{
public static bool DisableEnemyAI;
class WallTarget
{
public Vector2 Position;
@@ -249,7 +251,7 @@ namespace Barotrauma
}
}
public TargetingPriority GetTargetingPriority(string targetTag)
private TargetingPriority GetTargetingPriority(string targetTag)
{
if (targetingPriorities.TryGetValue(targetTag, out TargetingPriority priority))
{
@@ -485,7 +487,7 @@ namespace Barotrauma
}
else
{
if (!IsProperlyLatched)
if (!IsProperlyLatchedOnSub)
{
UpdateWallTarget();
}
@@ -1001,7 +1003,7 @@ namespace Barotrauma
private void UpdateEating(float deltaTime)
{
if (SelectedAiTarget == null)
if (SelectedAiTarget == null) //SelectedAiTarget.Entity is Character c && !c.IsDead
{
State = AIState.Idle;
return;
@@ -1038,14 +1040,14 @@ namespace Barotrauma
#region Targeting
private bool IsProperlyLatched => LatchOntoAI != null && LatchOntoAI.IsAttached && SelectedAiTarget?.Entity == wallTarget?.Structure;
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
//goes through all the AItargets, evaluates how preferable it is to attack the target,
//whether the Character can see/hear the target and chooses the most preferable target within
//sight/hearing range
public AITarget UpdateTargets(Character character, out TargetingPriority priority)
{
if (IsProperlyLatched)
if (IsProperlyLatchedOnSub)
{
// If attached to a valid target, just keep the target.
// Priority not used in this case.
@@ -267,7 +267,7 @@ namespace Barotrauma
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime))
{
Character.Speak(
newOrder.GetChatMessage("", Character.CurrentHull?.RoomName, givingOrderToSelf: false), ChatMessageType.Order);
newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
}
}
}
@@ -286,7 +286,7 @@ namespace Barotrauma
if (Character.PressureTimer > 50.0f && Character.CurrentHull != null)
{
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.RoomName), null, 0, "pressure", 30.0f);
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.DisplayName), null, 0, "pressure", 30.0f);
}
}
@@ -388,6 +388,18 @@ namespace Barotrauma
buttonPressCooldown = ButtonPressInterval;
break;
}
else
{
if (!door.HasRequiredItems(character, false) && shouldBeOpen)
{
currentPath.Unreachable = true;
return;
}
door.Item.TryInteract(character, false, true, true);
buttonPressCooldown = ButtonPressInterval;
break;
}
}
}
}
@@ -414,20 +426,12 @@ namespace Barotrauma
if (!nextNode.Waypoint.ConnectedDoor.HasRequiredItems(character, false)) { return null; }
}
if (!canBreakDoors)
{
//door closed and the character can't open doors -> node can't be traversed
if (!canOpenDoors || character.LockHands) return null;
var doorButtons = nextNode.Waypoint.ConnectedDoor.Item.GetConnectedComponents<Controller>();
if (!doorButtons.Any()) return null;
foreach (Controller button in doorButtons)
{
if (Math.Sign(button.Item.Position.X - nextNode.Waypoint.Position.X) !=
Math.Sign(node.Position.X - nextNode.Position.X)) continue;
Math.Sign(node.Position.X - nextNode.Position.X)) { continue; }
if (!button.HasRequiredItems(character, false)) return null;
if (!button.HasRequiredItems(character, false)) { return null; }
}
}
}
@@ -54,6 +54,8 @@ namespace Barotrauma
get { return attachJoints.Count > 0; }
}
public bool IsAttachedToSub => IsAttached && attachTargetBody?.UserData is Entity entity && (entity is Submarine sub || entity?.Submarine != null);
public LatchOntoAI(XElement element, EnemyAIController enemyAI)
{
attachToWalls = element.GetAttributeBool("attachtowalls", false);
@@ -207,10 +209,10 @@ namespace Barotrauma
break;
}
if (attachTargetBody != null && deattachTimer < 0.0f)
if (IsAttached && attachTargetBody != null && deattachTimer < 0.0f)
{
Entity entity = attachTargetBody.UserData as Entity;
Submarine attachedSub = entity is Submarine ? (Submarine)entity : entity?.Submarine;
Submarine attachedSub = entity is Submarine sub ? sub : entity?.Submarine;
if (attachedSub != null)
{
float velocity = attachedSub.Velocity == Vector2.Zero ? 0.0f : attachedSub.Velocity.Length();
@@ -44,6 +44,7 @@ namespace Barotrauma
private AIObjectiveContainItem reloadWeaponObjective;
private Hull retreatTarget;
private AIObjectiveGoTo retreatObjective;
private AIObjectiveFindSafety findSafety;
private float coolDownTimer;
@@ -60,7 +61,9 @@ namespace Barotrauma
{
Enemy = enemy;
coolDownTimer = CoolDown;
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 0;
findSafety = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>();
findSafety.Priority = 0;
findSafety.unreachable.Clear();
Mode = mode;
if (Enemy == null)
{
@@ -175,7 +178,7 @@ namespace Barotrauma
{
if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted))
{
retreatTarget = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull(new List<Hull>() { character.CurrentHull });
retreatTarget = findSafety.FindBestHull(new List<Hull>() { character.CurrentHull });
}
if (retreatTarget != null)
{
@@ -235,6 +238,7 @@ namespace Barotrauma
{
if (Vector2.DistanceSquared(character.Position, Enemy.Position) <= meleeWeapon.Range * meleeWeapon.Range)
{
character.SetInput(InputType.Shoot, false, true);
Weapon.Use(deltaTime, character);
}
}
@@ -264,6 +268,7 @@ namespace Barotrauma
}
if (target != null && target == Enemy)
{
character.SetInput(InputType.Shoot, false, true);
Weapon.Use(deltaTime, character);
}
}
@@ -275,7 +280,6 @@ namespace Barotrauma
{
abandon = true;
SteeringManager.Reset();
//HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
}
public override bool IsCompleted()
@@ -18,7 +18,7 @@ namespace Barotrauma
const float SearchHullInterval = 3.0f;
const float clearUnreachableInterval = 30;
private List<Hull> unreachable = new List<Hull>();
public readonly List<Hull> unreachable = new List<Hull>();
private float currenthullSafety;
private float unreachableClearTimer;
@@ -60,11 +60,16 @@ namespace Barotrauma
else
{
divingGearObjective = null;
// Reset the timer so that we get a safe hull target.
searchHullTimer = 0;
// Reduce the timer so that we get a safe hull target faster.
searchHullTimer = Math.Min(1, searchHullTimer);
}
}
if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
{
searchHullTimer = Math.Min(1, searchHullTimer);
}
if (unreachableClearTimer > 0)
{
unreachableClearTimer -= deltaTime;
@@ -188,10 +193,17 @@ namespace Barotrauma
float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f;
float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist));
hullSafety *= distanceFactor;
//skip the hull if the safety is already less than the best hull
//(no need to do the expensive pathfinding if we already know we're not going to choose this hull)
if (hullSafety < bestValue) { continue; }
// Each unsafe node reduces the hull safety value.
// Ignore current hull, because otherwise the would block all paths from the current hull to the target hull.
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
if (path.Unreachable) { continue; }
if (path.Unreachable)
{
unreachable.Add(hull);
continue;
}
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
hullSafety /= 1 + unsafeNodes;
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
@@ -219,7 +231,6 @@ namespace Barotrauma
}
}
}
// Huge preference for closer targets
float distance = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance));
@@ -26,6 +26,9 @@ namespace Barotrauma
private float standStillTimer;
private float walkDuration;
private readonly List<Hull> targetHulls = new List<Hull>(20);
private readonly List<float> hullWeights = new List<float>(20);
public AIObjectiveIdle(Character character) : base(character, "")
{
standStillTimer = Rand.Range(-10.0f, 10.0f);
@@ -106,7 +109,7 @@ namespace Barotrauma
if (isCurrentHullOK)
{
// Check that there is no unsafe or forbidden hulls on the way to the target
// Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull.
// Only do this when the current hull is ok, because otherwise would block all paths from the current hull to the target hull.
var path = PathSteering.PathFinder.FindPath(character.SimPosition, randomHull.SimPosition);
if (path.Unreachable ||
path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull)))
@@ -128,8 +131,8 @@ namespace Barotrauma
character.AIController.SelectTarget(currentTarget.AiTarget);
string errorMsg = null;
#if DEBUG
bool isRoomNameFound = currentTarget.RoomName != null;
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.RoomName : currentTarget.ToString()) + ")";
bool isRoomNameFound = currentTarget.DisplayName != null;
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.DisplayName : currentTarget.ToString()) + ")";
#endif
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsg);
PathSteering.SetPath(path);
@@ -230,13 +233,9 @@ namespace Barotrauma
}
}
private readonly List<Hull> targetHulls = new List<Hull>(20);
private readonly List<float> hullWeights = new List<float>(20);
private void FindTargetHulls()
{
bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull);
targetHulls.Clear();
hullWeights.Clear();
foreach (var hull in Hull.hullList)
@@ -266,7 +265,6 @@ namespace Barotrauma
hullWeights.Add(weight);
}
}
}
private bool IsForbidden(Hull hull)
@@ -83,7 +83,7 @@ namespace Barotrauma
if (character.SelectedCharacter == null)
{
character?.Speak(TextManager.Get("DialogFoundUnconsciousTarget")
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.RoomName),
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.DisplayName),
null, 1.0f,
"foundunconscioustarget" + targetCharacter.Name, 60.0f);
}
@@ -527,7 +527,12 @@ namespace Barotrauma
Limb leftLeg = GetLimb(LimbType.LeftLeg);
Limb rightLeg = GetLimb(LimbType.RightLeg);
float limpAmount =
character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) +
character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f);
float walkCycleMultiplier = 1.0f;
if (Stairs != null)
{
@@ -556,13 +561,17 @@ namespace Barotrauma
float walkPosX = (float)Math.Cos(WalkPos);
float walkPosY = (float)Math.Sin(WalkPos);
Vector2 stepSize = StepSize.Value;
stepSize.X *= walkPosX;
stepSize.Y *= walkPosY;
stepSize.Y *= walkPosY;
float footMid = colliderPos.X;// (leftFoot.SimPosition.X + rightFoot.SimPosition.X) / 2.0f;
float footMid = colliderPos.X;
if (limpAmount > 0.0f)
{
//make the footpos oscillate when limping
footMid += ((float)Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * 0.3f);
}
movement = overrideTargetMovement == Vector2.Zero ?
MathUtils.SmoothStep(movement, TargetMovement, movementLerp) :
@@ -576,7 +585,7 @@ namespace Barotrauma
movement.Y = 0.0f;
if (torso == null) { return; }
bool isNotRemote = true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) isNotRemote = !character.IsRemotePlayer;
@@ -680,7 +689,7 @@ namespace Barotrauma
//make the character limp if the feet are damaged
float footAfflictionStrength = character.CharacterHealth.GetAfflictionStrength("damage", foot, true);
footPos *= MathHelper.Lerp(1.0f, 0.5f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
footPos.X *= MathHelper.Lerp(1.0f, 0.75f, MathHelper.Clamp(footAfflictionStrength / 50.0f, 0.0f, 1.0f));
if (onSlope && Stairs == null)
{
@@ -84,6 +84,7 @@ namespace Barotrauma
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
{
//DebugConsole.NewMessage("[RagollParams] Using the default folder.");
folder = GetDefaultFolder(speciesName);
}
return folder;
@@ -692,7 +692,7 @@ namespace Barotrauma
ImpactProjSpecific(impact, f1.Body);
}
public void SeverLimbJoint(LimbJoint limbJoint)
public void SeverLimbJoint(LimbJoint limbJoint, bool playSound = true)
{
if (!limbJoint.CanBeSevered || limbJoint.IsSevered)
{
@@ -721,7 +721,7 @@ namespace Barotrauma
}
}
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint);
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint, bool playSound = true);
private void GetConnectedLimbs(List<Limb> connectedLimbs, List<LimbJoint> checkedJoints, Limb limb)
{
@@ -1103,14 +1103,14 @@ namespace Barotrauma
if (leftFoot != null)
{
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", leftFoot, true);
speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
}
var rightFoot = AnimController.GetLimb(LimbType.RightFoot);
if (rightFoot != null)
{
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
}
return speed;
@@ -231,7 +231,8 @@ namespace Barotrauma
: afflictions.Concat(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter)));
}
private LimbHealth GetMathingLimbHealth(Affliction affliction) => limbHealths[Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb).HealthIndex];
private LimbHealth GetMatchingLimbHealth(Limb limb) => limbHealths[limb.HealthIndex];
private LimbHealth GetMathingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb));
/// <summary>
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
@@ -255,8 +255,10 @@ namespace Barotrauma
commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
{
ThrowError("Karma has not been fully implemented yet, and is disabled in this version of Barotrauma.");
return;
HumanAIController.DisableCrewAI = true;
NewMessage("Crew AI disabled", Color.Red);
// This is probably not where it should be?
//ThrowError("Karma has not been fully implemented yet, and is disabled in this version of Barotrauma.");
/*if (GameMain.Server == null) return;
GameMain.Server.KarmaEnabled = !GameMain.Server.KarmaEnabled;*/
}));
@@ -264,7 +266,19 @@ namespace Barotrauma
commands.Add(new Command("enablecrewai", "enablecrewai: Enable the AI of the NPCs in the crew.", (string[] args) =>
{
HumanAIController.DisableCrewAI = false;
NewMessage("Crew AI enabled", Color.White);
NewMessage("Crew AI enabled", Color.Green);
}, isCheat: true));
commands.Add(new Command("disableenemyai", "disableenemyai: Disable the AI of the Enemy characters (monsters).", (string[] args) =>
{
EnemyAIController.DisableEnemyAI = true;
NewMessage("Enemy AI disabled", Color.Red);
}, isCheat: true));
commands.Add(new Command("enableenemyai", "enableenemyai: Enable the AI of the Enemy characters (monsters).", (string[] args) =>
{
EnemyAIController.DisableEnemyAI = false;
NewMessage("Enemy AI enabled", Color.Green);
}, isCheat: true));
commands.Add(new Command("botcount", "botcount [x]: Set the number of bots in the crew in multiplayer.", null));
@@ -209,6 +209,7 @@ namespace Barotrauma
//isActive = false;
bool spawnReady = false;
if (spawnPending)
{
//wait until there are no submarines at the spawnpos
@@ -219,25 +220,31 @@ namespace Barotrauma
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos) < minDist * minDist) return;
}
spawnPending = false;
//+1 because Range returns an integer less than the max value
int amount = Rand.Range(minAmount, maxAmount + 1, Rand.RandSync.Server);
monsters = new Character[amount];
monsters = new List<Character>();
float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100;
for (int i = 0; i < amount; i++)
{
bool isClient = false;
{
CoroutineManager.InvokeAfter(() =>
{
bool isClient = false;
#if CLIENT
isClient = GameMain.Client != null;
isClient = GameMain.Client != null;
#endif
monsters[i] = Character.Create(
characterFile, spawnPos + Rand.Vector(100.0f, Rand.RandSync.Server),
i.ToString(), null, isClient, true, true);
monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true));
if (monsters.Count == amount)
{
spawnReady = true;
}
}, Rand.Range(0f, amount / 2, Rand.RandSync.Server));
}
spawnPending = false;
}
if (!spawnReady) { return; }
Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter);
#if CLIENT
if (Character.Controlled != null) targetEntity = (Entity)Character.Controlled;
@@ -8,6 +8,22 @@ namespace Barotrauma
{
public static class StringFormatter
{
public static string Replace(this string s, string replacement, Func<char, bool> predicate)
{
var newString = new string[s.Length];
for (int i = 0; i < s.Length; i++)
{
char letter = s[i];
string newLetter = letter.ToString();
if (predicate(letter))
{
newLetter = replacement;
}
newString[i] = newLetter;
}
return new string(newString.SelectMany(str => str.ToCharArray()).ToArray());
}
public static string Remove(this string s, Func<char, bool> predicate)
{
return new string(s.ToCharArray().Where(c => !predicate(c)).ToArray());
@@ -108,7 +108,7 @@ namespace Barotrauma
}
#if CLIENT
new GUIMessageBox("", TextManager.Get("CargoSpawnNotification").Replace("[roomname]", cargoRoom.RoomName));
new GUIMessageBox("", TextManager.Get("CargoSpawnNotification").Replace("[roomname]", cargoRoom.DisplayName));
#endif
Dictionary<ItemContainer, int> availableContainers = new Dictionary<ItemContainer, int>();
@@ -13,6 +13,7 @@ namespace Barotrauma
public bool CheatsEnabled;
const int InitialMoney = 4700;
public const int HullRepairCost = 500, ItemRepairCost = 500;
protected bool watchmenSpawned;
protected Character startWatchman, endWatchman;
@@ -20,6 +21,8 @@ namespace Barotrauma
//key = dialog flag, double = Timing.TotalTime when the line was last said
private Dictionary<string, double> dialogLastSpoken = new Dictionary<string, double>();
public bool PurchasedHullRepairs, PurchasedItemRepairs;
protected Map map;
public Map Map
{
@@ -70,6 +73,37 @@ namespace Barotrauma
watchmenSpawned = false;
startWatchman = null;
endWatchman = null;
if (PurchasedHullRepairs)
{
foreach (Structure wall in Structure.WallList)
{
if (wall.Submarine == null || wall.Submarine.IsOutpost) { continue; }
if (wall.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(wall.Submarine))
{
for (int i = 0; i < wall.SectionCount; i++)
{
wall.AddDamage(i, -100000.0f);
}
}
}
PurchasedHullRepairs = false;
}
if (PurchasedItemRepairs)
{
foreach (Item item in Item.ItemList)
{
if (item.Submarine == null || item.Submarine.IsOutpost) { continue; }
if (item.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(item.Submarine))
{
if (item.GetComponent<Items.Components.Repairable>() != null)
{
item.Condition = item.Health;
}
}
}
PurchasedItemRepairs = false;
}
}
public override void Update(float deltaTime)
@@ -44,6 +44,7 @@ namespace Barotrauma
public bool ChromaticAberrationEnabled { get; set; }
public bool MuteOnFocusLost { get; set; }
public bool UseDirectionalVoiceChat { get; set; }
public enum VoiceMode
{
@@ -149,6 +150,9 @@ namespace Barotrauma
public bool EnableMouseLook { get; set; } = true;
public bool CrewMenuOpen { get; set; } = true;
public bool ChatOpen { get; set; } = true;
private bool unsavedSettings;
public bool UnsavedSettings
{
@@ -826,6 +830,8 @@ namespace Barotrauma
SoundVolume = audioSettings.GetAttributeFloat("soundvolume", SoundVolume);
MusicVolume = audioSettings.GetAttributeFloat("musicvolume", MusicVolume);
VoiceChatVolume = audioSettings.GetAttributeFloat("voicechatvolume", VoiceChatVolume);
MuteOnFocusLost = audioSettings.GetAttributeBool("muteonfocuslost", false);
UseDirectionalVoiceChat = audioSettings.GetAttributeBool("usedirectionalvoicechat", true);
string voiceSettingStr = audioSettings.GetAttributeString("voicesetting", "Disabled");
VoiceCaptureDevice = audioSettings.GetAttributeString("voicecapturedevice", "");
NoiseGateThreshold = audioSettings.GetAttributeFloat("noisegatethreshold", -45);
@@ -844,6 +850,9 @@ namespace Barotrauma
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", AimAssistAmount);
EnableMouseLook = doc.Root.GetAttributeBool("enablemouselook", EnableMouseLook);
CrewMenuOpen = doc.Root.GetAttributeBool("crewmenuopen", CrewMenuOpen);
ChatOpen = doc.Root.GetAttributeBool("chatopen", ChatOpen);
foreach (XElement subElement in doc.Root.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -1019,7 +1028,9 @@ namespace Barotrauma
new XAttribute("requiresteamauthentication", requireSteamAuthentication),
new XAttribute("autoupdateworkshopitems", AutoUpdateWorkshopItems),
new XAttribute("aimassistamount", aimAssistAmount),
new XAttribute("enablemouselook", EnableMouseLook));
new XAttribute("enablemouselook", EnableMouseLook),
new XAttribute("chatopen", ChatOpen),
new XAttribute("crewmenuopen", CrewMenuOpen));
if (!ShowUserStatisticsPrompt)
{
@@ -1055,6 +1066,8 @@ namespace Barotrauma
audio.ReplaceAttributes(
new XAttribute("musicvolume", musicVolume),
new XAttribute("soundvolume", soundVolume),
new XAttribute("muteonfocuslost", MuteOnFocusLost),
new XAttribute("usedirectionalvoicechat", UseDirectionalVoiceChat),
new XAttribute("voicesetting", VoiceSetting),
new XAttribute("voicecapturedevice", VoiceCaptureDevice ?? ""),
new XAttribute("noisegatethreshold", NoiseGateThreshold));
@@ -56,7 +56,7 @@ namespace Barotrauma
switch (SlotTypes[i])
{
//case InvSlotType.Head:
case InvSlotType.OuterClothes:
//case InvSlotType.OuterClothes:
case InvSlotType.LeftHand:
case InvSlotType.RightHand:
hideEmptySlot[i] = true;
@@ -176,6 +176,9 @@ namespace Barotrauma
{
if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] != null && Items[i] != item)
{
#if CLIENT
if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; }
#endif
if (!Items[i].AllowedSlots.Contains(InvSlotType.Any) || !TryPutItem(Items[i], character, new List<InvSlotType> { InvSlotType.Any }, true))
{
free = false;
@@ -195,6 +198,9 @@ namespace Barotrauma
{
if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] == null)
{
#if CLIENT
if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; }
#endif
bool removeFromOtherSlots = item.ParentInventory != this;
if (placedInSlot == -1 && inWrongSlot)
{
@@ -248,7 +254,9 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("CharacterInventory.TryPutItem:IndexOutOfRange", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return false;
}
#if CLIENT
if (PersonalSlots.HasFlag(SlotTypes[index])) { hidePersonalSlots = false; }
#endif
//there's already an item in the slot
if (Items[index] != null)
{
@@ -273,7 +281,9 @@ namespace Barotrauma
foreach (InvSlotType allowedSlot in allowedSlots)
{
if (!allowedSlot.HasFlag(SlotTypes[index])) continue;
#if CLIENT
if (PersonalSlots.HasFlag(allowedSlot)) { hidePersonalSlots = false; }
#endif
for (int i = 0; i < capacity; i++)
{
if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] != null && Items[i] != item)
@@ -221,29 +221,64 @@ namespace Barotrauma.Items.Components
{
if (item.Condition <= RepairThreshold) return true; //For repairing
var idCard = character.Inventory.FindItemByIdentifier("idcard");
hasValidIdCard = requiredItems.Any(ri => ri.Value.Any(r => r.MatchesItem(idCard)));
Msg = requiredItems.None() || hasValidIdCard ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar";
ParseMsg();
if (addMessage)
{
msg = msg ?? (HasIntegratedButtons ? accessDeniedTxt : cannotOpenText);
}
//this is a bit pointless atm because if canBePicked is false it won't allow you to do Pick() anyway, however it's still good for future-proofing.
return requiredItems.Any() ? base.HasRequiredItems(character, addMessage, msg) : canBePicked;
}
public override bool Pick(Character picker)
{
return item.Condition <= RepairThreshold ? true : base.Pick(picker);
if (item.Condition <= RepairThreshold) { return true; }
if (requiredItems.None()) { return false; }
if (HasRequiredItems(picker, false) && hasValidIdCard) { return false; }
return base.Pick(picker);
}
public override bool OnPicked(Character picker)
{
if (item.Condition <= RepairThreshold) return true; //repairs
if (requiredItems.Any() && !hasValidIdCard)
{
ForceOpen(ActionType.OnPicked);
}
return false;
}
private void ForceOpen(ActionType actionType)
{
SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true); //crowbar function
#if CLIENT
PlaySound(ActionType.OnPicked, item.WorldPosition, picker);
PlaySound(actionType, item.WorldPosition, picker);
#endif
}
public override bool Select(Character character)
{
//can only be selected if the item is broken
return item.Condition <= RepairThreshold;
if (item.Condition <= RepairThreshold) return true; //repairs
bool hasRequiredItems = HasRequiredItems(character, false);
if (requiredItems.None() || hasRequiredItems && hasValidIdCard)
{
float originalPickingTime = PickingTime;
PickingTime = 0;
ForceOpen(ActionType.OnUse);
PickingTime = originalPickingTime;
}
else if (hasRequiredItems)
{
#if CLIENT
GUI.AddMessage(accessDeniedTxt, Color.Red);
#endif
}
return false;
}
public override void Update(float deltaTime, Camera cam)
@@ -34,6 +34,20 @@ namespace Barotrauma.Items.Components
}
}
public override bool IsActive
{
get { return base.IsActive; }
set
{
base.IsActive = value;
if (!value)
{
nodes.Clear();
charactersInRange.Clear();
}
}
}
[Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
public float Range
{
@@ -126,14 +140,19 @@ namespace Barotrauma.Items.Components
}
else
{
nodes.Clear();
charactersInRange.Clear();
IsActive = false;
}
voltage = 0.0f;
}
public override void UpdateBroken(float deltaTime, Camera cam)
{
base.UpdateBroken(deltaTime, cam);
nodes.Clear();
charactersInRange.Clear();
}
private void Discharge()
{
ApplyStatusEffects(ActionType.OnUse, 1.0f);
@@ -58,13 +58,6 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(true, true)]
public bool Aimable
{
get;
set;
}
[Serialize(false, false)]
public bool ControlPose
{
@@ -11,6 +11,12 @@ namespace Barotrauma.Items.Components
{
private float lastSentDeattachTimer;
private PhysicsBody trigger;
private Holdable holdable;
private float deattachTimer;
[Serialize(1.0f, false)]
public float DeattachDuration
{
@@ -49,13 +55,7 @@ namespace Barotrauma.Items.Components
#endif
}
}
private PhysicsBody trigger;
private Holdable holdable;
private float deattachTimer;
public LevelResource(Item item, XElement element) : base(item, element)
{
IsActive = true;
@@ -142,8 +142,6 @@ namespace Barotrauma.Items.Components
pickTimer / requiredTime,
Color.Red, Color.Green);
#endif
picker.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
picker.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
pickTimer += CoroutineManager.DeltaTime;
@@ -322,6 +322,7 @@ namespace Barotrauma.Items.Components
// If the character is climbing, ignore the check, because we cannot aim while climbing.
if (VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak) < MathHelper.PiOver4)
{
character.SetInput(InputType.Shoot, false, true);
Use(deltaTime, character);
}
else
@@ -337,11 +338,11 @@ namespace Barotrauma.Items.Components
sinTime = 0;
if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
{
character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leaksfixed", 10.0f);
character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.DisplayName), null, 0.0f, "leaksfixed", 10.0f);
}
else
{
character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leakfixed", 10.0f);
character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.DisplayName), null, 0.0f, "leakfixed", 10.0f);
}
}
@@ -190,7 +190,7 @@ namespace Barotrauma.Items.Components
get { return name; }
}
[Editable, Serialize("", true)]
[Editable, Serialize("", true, translationTextTag: "ItemMsg")]
public string Msg
{
get;
@@ -538,7 +538,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++)
@@ -555,7 +554,7 @@ namespace Barotrauma.Items.Components
public virtual void FlipY(bool relativeToSub) { }
public bool HasRequiredContainedItems(bool addMessage)
public bool HasRequiredContainedItems(bool addMessage, string msg = null)
{
if (!requiredItems.ContainsKey(RelatedItem.RelationType.Contained)) return true;
if (item.OwnInventory == null) return false;
@@ -582,33 +581,52 @@ namespace Barotrauma.Items.Components
{
if (!requiredItems.Any()) return true;
if (character.Inventory == null) return false;
bool hasRequiredItems = false;
bool canContinue = true;
if (requiredItems.ContainsKey(RelatedItem.RelationType.Equipped))
{
foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Equipped])
{
if (character.SelectedItems.FirstOrDefault(it => it != null && it.Condition > 0.0f && ri.MatchesItem(it)) == null)
canContinue = CheckItems(ri, character.SelectedItems);
if (!canContinue) { break; }
}
}
if (canContinue)
{
if (requiredItems.ContainsKey(RelatedItem.RelationType.Picked))
{
foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Picked])
{
#if CLIENT
if (addMessage && !string.IsNullOrEmpty(ri.Msg)) GUI.AddMessage(ri.Msg, Color.Red);
#endif
return false;
if (!CheckItems(ri, character.Inventory.Items)) { break; }
}
}
}
if (requiredItems.ContainsKey(RelatedItem.RelationType.Picked))
{
foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Picked])
{
if (character.Inventory.Items.FirstOrDefault(it => it != null && it.Condition > 0.0f && ri.MatchesItem(it)) == null)
{
#if CLIENT
if (!hasRequiredItems && addMessage && !string.IsNullOrEmpty(msg))
{
GUI.AddMessage(msg, Color.Red);
}
#endif
return false;
return hasRequiredItems;
bool CheckItems(RelatedItem relatedItem, IEnumerable<Item> itemList)
{
bool Predicate(Item it) => it != null && it.Condition > 0.0f && relatedItem.MatchesItem(it);
bool shouldBreak = false;
if (relatedItem.IsOptional)
{
if (!hasRequiredItems)
{
hasRequiredItems = itemList.Any(Predicate);
}
}
else
{
hasRequiredItems = itemList.Any(Predicate);
if (!hasRequiredItems)
{
shouldBreak = true;
}
}
if (!hasRequiredItems)
@@ -620,8 +638,6 @@ namespace Barotrauma.Items.Components
}
return !shouldBreak;
}
return true;
}
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Character user = null)
@@ -766,6 +782,7 @@ namespace Barotrauma.Items.Components
{
newRequiredItem.statusEffects = prevRequiredItem.statusEffects;
newRequiredItem.Msg = prevRequiredItem.Msg;
newRequiredItem.IsOptional = prevRequiredItem.IsOptional;
}
if (!requiredItems.ContainsKey(newRequiredItem.Type))
@@ -343,5 +343,7 @@ namespace Barotrauma.Items.Components
limbPositions[i] = new LimbPos(limbPositions[i].limbType, flippedPos);
}
}
partial void HideHUDs(bool value);
}
}
@@ -8,11 +8,19 @@ namespace Barotrauma.Items.Components
{
partial class CustomInterface : ItemComponent, IClientSerializable, IServerSerializable
{
class CustomInterfaceElement
class CustomInterfaceElement : ISerializableEntity
{
public bool ContinuousSignal;
public bool State;
public string Label, Connection, Signal;
public string Connection;
[Serialize("", false, translationTextTag = "Label.")]
public string Label { get; set; }
[Serialize("1", false)]
public string Signal { get; set; }
public string Name => "CustomInterfaceElement";
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
public List<StatusEffect> StatusEffects = new List<StatusEffect>();
@@ -33,7 +41,7 @@ namespace Barotrauma.Items.Components
}
private string[] labels;
[Serialize("", true), Editable()]
[Serialize("", true)]
public string Labels
{
get { return string.Join(",", labels); }
@@ -48,7 +56,7 @@ namespace Barotrauma.Items.Components
}
}
private string[] signals;
[Serialize("", true), Editable()]
[Serialize("", true)]
public string Signals
{
//use semicolon as a separator because comma may be needed in the signals (for color or vector values for example)
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -66,6 +67,8 @@ namespace Barotrauma
public LightComponent LightComponent { get; set; }
public int Variant { get; set; }
private Gender _gender;
/// <summary>
/// None = Any/Not Defined -> no effect.
@@ -112,30 +115,65 @@ namespace Barotrauma
/// <summary>
/// Note: this constructor cannot initialize automatically, because the gender is unknown at this point. We only know it when the item is equipped.
/// </summary>
public WearableSprite(XElement subElement, Wearable wearable)
public WearableSprite(XElement subElement, Wearable wearable, int variant = 0)
{
Type = WearableType.Item;
WearableComponent = wearable;
Variant = Math.Max(variant, 0);
SpritePath = ParseSpritePath(subElement.GetAttributeString("texture", string.Empty));
SourceElement = subElement;
}
private string ParseSpritePath(string texturePath) => texturePath.Contains("/") ? texturePath : $"{Path.GetDirectoryName(WearableComponent.Item.Prefab.ConfigFile)}/{texturePath}";
public void RefreshPath()
{
if (Variant > 0)
{
// Restore the tag so that we can parse it again.
ReplaceNumbersWith("[VARIANT]");
}
ParsePath(true);
}
private void ReplaceNumbersWith(string replacement)
{
var fileName = Path.GetFileName(SpritePath);
var path = Path.GetDirectoryName(SpritePath);
fileName = fileName.Replace(replacement, c => char.IsNumber(c));
SpritePath = Path.Combine(path, fileName);
}
private void ParsePath(bool parseSpritePath)
{
if (_gender != Gender.None)
{
SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male");
}
SpritePath = SpritePath.Replace("[VARIANT]", Variant.ToString());
if (!File.Exists(SpritePath))
{
// If the variant does not exist, parse the path so that it uses first variant.
Variant = 1;
ReplaceNumbersWith(Variant.ToString());
}
if (parseSpritePath)
{
Sprite.ParseTexturePath(file: SpritePath);
}
}
public bool IsInitialized { get; private set; }
public void Init(Gender gender = Gender.None)
{
if (IsInitialized) { return; }
_gender = SpritePath.Contains("[GENDER]") ? gender : Gender.None;
if (_gender != Gender.None)
{
SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male");
}
ParsePath(false);
if (Sprite != null)
{
Sprite.Remove();
}
Sprite = new Sprite(SourceElement, "", SpritePath);
Sprite = new Sprite(SourceElement, file: SpritePath);
Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true);
HideLimb = SourceElement.GetAttributeBool("hidelimb", false);
HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false);
@@ -170,7 +208,7 @@ namespace Barotrauma.Items.Components
get { return damageModifiers; }
}
public Wearable (Item item, XElement element) : base(item, element)
public Wearable(Item item, XElement element) : base(item, element)
{
this.item = item;
@@ -197,7 +235,7 @@ namespace Barotrauma.Items.Components
limbType[i] = (LimbType)Enum.Parse(typeof(LimbType),
subElement.GetAttributeString("limb", "Head"), true);
wearableSprites[i] = new WearableSprite(subElement, this);
wearableSprites[i] = new WearableSprite(subElement, this, variant);
foreach (XElement lightElement in subElement.Elements())
{
@@ -801,7 +801,12 @@ namespace Barotrauma
if (findNewHull) FindHull();
}
partial void SetActiveSprite();
public void SetActiveSprite()
{
SetActiveSpriteProjSpecific();
}
partial void SetActiveSpriteProjSpecific();
public override void Move(Vector2 amount)
{
@@ -1432,13 +1437,13 @@ namespace Barotrauma
selectHit = picker.IsKeyHit(ic.SelectKey);
#if CLIENT
//if the cursor is on a UI component, disable interaction with the left mouse button
//to prevent accidentally selecting items when clicking UI elements
if (picker == Character.Controlled && GUI.MouseOn != null)
{
if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0) pickHit = false;
if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0) selectHit = false;
}
//if the cursor is on a UI component, disable interaction with the left mouse button
//to prevent accidentally selecting items when clicking UI elements
if (picker == Character.Controlled && GUI.MouseOn != null)
{
if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0) pickHit = false;
if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0) selectHit = false;
}
#endif
}
}
@@ -1608,6 +1613,7 @@ namespace Barotrauma
if (remove) { Spawner?.AddToRemoveQueue(this); }
}
List<ColoredText> texts = new List<ColoredText>();
public List<ColoredText> GetHUDTexts(Character character)
{
texts.Clear();
@@ -1617,6 +1623,13 @@ namespace Barotrauma
if (!ic.CanBePicked && !ic.CanBeSelected) continue;
if (ic is Holdable holdable && !holdable.CanBeDeattached()) continue;
Color color = Color.Gray;
bool hasRequiredSkillsAndItems = ic.HasRequiredSkills(character) && ic.HasRequiredItems(character, false);
if (hasRequiredSkillsAndItems)
{
color = Color.Cyan;
}
texts.Add(new ColoredText(ic.DisplayMsg, color, false));
}
@@ -2054,6 +2067,8 @@ namespace Barotrauma
public virtual void Reset()
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
Sprite.ReloadXML();
SpriteDepth = Sprite.Depth;
components.ForEach(c => c.Reset());
}
@@ -621,10 +621,25 @@ namespace Barotrauma
string treatmentIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
var matchingAffliction = AfflictionPrefab.List.Find(a => a.Identifier == treatmentIdentifier);
if (matchingAffliction != null)
List<AfflictionPrefab> matchingAfflictions = AfflictionPrefab.List.FindAll(a => a.Identifier == treatmentIdentifier || a.AfflictionType == treatmentIdentifier);
if (matchingAfflictions.Count == 0)
{
matchingAffliction.TreatmentSuitability.Add(identifier, subElement.GetAttributeFloat("suitability", 0.0f));
DebugConsole.ThrowError("Error in item prefab \"" + Name + "\" - couldn't define as a treatment, no treatments with the identifier or type \"" + treatmentIdentifier + "\" were found.");
continue;
}
float suitability = subElement.GetAttributeFloat("suitability", 0.0f);
foreach (AfflictionPrefab matchingAffliction in matchingAfflictions)
{
if (matchingAffliction.TreatmentSuitability.ContainsKey(identifier))
{
matchingAffliction.TreatmentSuitability[identifier] =
Math.Max(matchingAffliction.TreatmentSuitability[identifier], suitability);
}
else
{
matchingAffliction.TreatmentSuitability.Add(identifier, suitability);
}
}
break;
}
@@ -16,6 +16,8 @@ namespace Barotrauma
Container
}
public bool IsOptional { get; set; }
private string[] identifiers;
private string[] excludedIdentifiers;
@@ -138,7 +140,8 @@ namespace Barotrauma
{
element.Add(
new XAttribute("identifiers", JoinedIdentifiers),
new XAttribute("type", type.ToString()));
new XAttribute("type", type.ToString()),
new XAttribute("optional", IsOptional));
if (excludedIdentifiers.Length > 0)
{
+25 -6
View File
@@ -90,6 +90,25 @@ namespace Barotrauma
}
}
public string DisplayName
{
get;
private set;
}
private string roomName;
[Editable, Serialize("", true, translationTextTag: "RoomName.")]
public string RoomName
{
get { return roomName; }
set
{
if (roomName == value) { return; }
roomName = value;
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
}
}
public override Rectangle Rect
{
get
@@ -817,17 +836,17 @@ namespace Barotrauma
}
if (roomItems.Contains("reactor"))
return TextManager.Get("ReactorRoom");
return "RoomName.ReactorRoom";
else if (roomItems.Contains("engine"))
return TextManager.Get("EngineRoom");
return "RoomName.EngineRoom";
else if (roomItems.Contains("steering") && roomItems.Contains("sonar"))
return TextManager.Get("CommandRoom");
return "RoomName.CommandRoom";
else if (roomItems.Contains("ballast"))
return TextManager.Get("Ballast");
return "RoomName.Ballast";
if (ConnectedGaps.Any(g => !g.IsRoomToRoom && g.ConnectedDoor != null))
{
return TextManager.Get("Airlock");
return "RoomName.Airlock";
}
Rectangle subRect = Submarine.CalculateDimensions();
@@ -847,7 +866,7 @@ namespace Barotrauma
else
roomPos |= Alignment.Right;
return TextManager.Get("Sub" + roomPos.ToString());
return "RoomName.Sub" + roomPos.ToString();
}
public static Hull Load(XElement element, Submarine submarine)
@@ -144,6 +144,20 @@ namespace Barotrauma
get { return spriteColor; }
set { spriteColor = value; }
}
[Editable, Serialize(false, true)]
public bool UseDropShadow
{
get;
private set;
}
[Serialize("0,0", true), Editable(ToolTip = "The position of the drop shadow relative to the structure. If set to zero, the shadow is positioned automatically so that it points towards the sub's center of mass.")]
public Vector2 DropShadowOffset
{
get;
private set;
}
public override Rectangle Rect
{
@@ -299,8 +313,8 @@ namespace Barotrauma
}
}
// Only add ai targets automatically to walls
if (aiTarget == null && HasBody && Tags.Contains("wall"))
// Only add ai targets automatically to submarine/outpost walls
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null)
{
aiTarget = new AITarget(this);
}
@@ -1139,6 +1153,13 @@ namespace Barotrauma
if (element.GetAttributeBool("flippedx", false)) s.FlipX(false);
if (element.GetAttributeBool("flippedy", false)) s.FlipY(false);
SerializableProperty.DeserializeProperties(s, element);
//structures with a body drop a shadow by default
if (element.Attribute("usedropshadow") == null)
{
s.UseDropShadow = prefab.Body;
}
return s;
}
@@ -194,7 +194,14 @@ namespace Barotrauma
break;
case "backgroundsprite":
sp.BackgroundSprite = new Sprite(subElement, lazyLoad: true);
if (subElement.Attribute("sourcerect") == null && sp.sprite != null)
{
sp.BackgroundSprite.SourceRect = sp.sprite.SourceRect;
sp.BackgroundSprite.size = sp.sprite.size;
sp.BackgroundSprite.size.X *= sp.sprite.SourceRect.Width;
sp.BackgroundSprite.size.Y *= sp.sprite.SourceRect.Height;
sp.BackgroundSprite.RelativeOrigin = subElement.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
}
if (subElement.GetAttributeBool("fliphorizontal", false))
sp.BackgroundSprite.effects = SpriteEffects.FlipHorizontally;
if (subElement.GetAttributeBool("flipvertical", false))
@@ -417,7 +417,10 @@ namespace Barotrauma
if (me.Submarine != this) { continue; }
if (me is Item item)
{
item.Indestructible = true;
if (item.GetComponent<Repairable>() != null)
{
item.Indestructible = true;
}
foreach (ItemComponent ic in item.Components)
{
if (ic is ConnectionPanel connectionPanel)
@@ -20,7 +20,7 @@ namespace Barotrauma.Networking
public OrderChatMessage(Order order, string orderOption, Entity targetEntity, Character targetCharacter, Character sender)
: this(order, orderOption,
order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.RoomName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption),
order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption),
targetEntity, targetCharacter, sender)
{
}
@@ -44,6 +44,7 @@ namespace Barotrauma
base.Deselect();
#if CLIENT
GameMain.Config.SaveNewPlayerConfig();
GameMain.SoundManager.SetCategoryMuffle("default", false);
GUI.ClearMessages();
#endif
@@ -87,7 +87,6 @@ namespace Barotrauma
public string FullPath { get; private set; }
public override string ToString()
{
return FilePath + ": " + sourceRect;
@@ -107,25 +106,7 @@ namespace Barotrauma
{
this.lazyLoad = lazyLoad;
SourceElement = element;
if (file == "")
{
file = SourceElement.GetAttributeString("texture", "");
}
if (file == "")
{
DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!");
return;
}
if (!string.IsNullOrEmpty(path))
{
LoadTexture(ref sourceVector, ref shouldReturn, preMultipliedAlpha);
}
FilePath = path + file;
if (!string.IsNullOrEmpty(FilePath))
{
FullPath = Path.GetFullPath(FilePath);
}
if (!ParseTexturePath(path, file)) { return; }
Name = SourceElement.GetAttributeString("name", null);
Vector4 sourceVector = SourceElement.GetAttributeVector4("sourcerect", Vector4.Zero);
preMultipliedAlpha = preMultiplyAlpha ?? SourceElement.GetAttributeBool("premultiplyalpha", true);
@@ -269,6 +250,29 @@ namespace Barotrauma
ID = GetID(SourceElement);
}
}
public bool ParseTexturePath(string path = "", string file = "")
{
if (file == "")
{
file = SourceElement.GetAttributeString("texture", "");
}
if (file == "")
{
DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!");
return false;
}
if (!string.IsNullOrEmpty(path))
{
if (!path.EndsWith("/")) path += "/";
}
FilePath = path + file;
if (!string.IsNullOrEmpty(FilePath))
{
FullPath = Path.GetFullPath(FilePath);
}
return true;
}
}
}
@@ -252,6 +252,28 @@ namespace Barotrauma
return null;
}
public static List<KeyValuePair<string, string>> GetAllTagTextPairs()
{
if (!textPacks.ContainsKey(Language))
{
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
Language = "English";
if (!textPacks.ContainsKey(Language))
{
throw new Exception("No text packs available in English!");
}
}
List<KeyValuePair<string, string>> allText = new List<KeyValuePair<string, string>>();
foreach (TextPack textPack in textPacks[Language])
{
allText.AddRange(textPack.GetAllTagTextPairs());
}
return allText;
}
public static string ReplaceGenderPronouns(string text, Gender gender)
{
if (gender == Gender.Male)
@@ -37,6 +37,7 @@ namespace Barotrauma
text = text.Replace("&amp;", "&");
text = text.Replace("&lt;", "<");
text = text.Replace("&gt;", ">");
text = text.Replace("&quot;", "\"");
infoList.Add(text);
}
}
@@ -62,6 +63,20 @@ namespace Barotrauma
return textList;
}
public List<KeyValuePair<string, string>> GetAllTagTextPairs()
{
var pairs = new List<KeyValuePair<string, string>>();
foreach (KeyValuePair<string, List<string>> kvp in texts)
{
foreach (string line in kvp.Value)
{
pairs.Add(new KeyValuePair<string, string>(kvp.Key, line));
}
}
return pairs;
}
#if DEBUG
public void CheckForDuplicates(int index)
{