(3a5d98b) v0.9.6.0

This commit is contained in:
Regalis
2019-12-17 14:38:24 +01:00
parent 5c95c53118
commit a3569b8bf0
95 changed files with 1579 additions and 728 deletions
@@ -21,7 +21,7 @@ namespace Barotrauma
/// <summary>
/// How long does it take for the ai target to fade out if not kept alive.
/// </summary>
public float FadeOutTime { get; private set; }
public float FadeOutTime { get; private set; } = 1;
public bool Static { get; private set; }
@@ -611,6 +611,7 @@ namespace Barotrauma
}
bool canAttack = true;
bool pursue = false;
if (IsCoolDownRunning)
{
switch (AttackingLimb.attack.AfterAttack)
@@ -623,6 +624,7 @@ namespace Barotrauma
if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
{
canAttack = false;
pursue = true;
}
else
{
@@ -661,6 +663,7 @@ namespace Barotrauma
if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
{
canAttack = false;
pursue = true;
}
else
{
@@ -853,32 +856,35 @@ namespace Barotrauma
if (pathSteering.CurrentPath != null)
{
// Attack doors
if (canAttackSub && pathSteering.CurrentPath.CurrentNode?.ConnectedDoor != null && SelectedAiTarget != pathSteering.CurrentPath.CurrentNode.ConnectedDoor.Item.AiTarget)
if (canAttackSub)
{
SelectTarget(pathSteering.CurrentPath.CurrentNode.ConnectedDoor.Item.AiTarget);
return;
// If the target is in the same hull, there shouldn't be any doors blocking the path
if (targetCharacter == null || targetCharacter.CurrentHull != Character.CurrentHull)
{
var door = pathSteering.CurrentPath.CurrentNode?.ConnectedDoor ?? pathSteering.CurrentPath.NextNode?.ConnectedDoor;
if (door != null && !door.IsOpen && door.Item.Condition > 0)
{
if (SelectedAiTarget != door.Item.AiTarget)
{
SelectTarget(door.Item.AiTarget, selectedTargetMemory.Priority);
return;
}
}
}
}
else if (canAttackSub && pathSteering.CurrentPath.NextNode?.ConnectedDoor != null && SelectedAiTarget != pathSteering.CurrentPath.NextNode.ConnectedDoor.Item.AiTarget)
// Steer towards the target if in the same room and swimming
if ((Character.AnimController.InWater || pursue) && targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))
{
SelectTarget(pathSteering.CurrentPath.NextNode.ConnectedDoor.Item.AiTarget);
return;
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition));
}
else
{
// Steer towards the target if in the same room and swimming
if (Character.AnimController.InWater && targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))
SteeringManager.SteeringSeek(steerPos, 2);
// Switch to Idle when cannot reach the target and if cannot damage the walls
if ((!canAttackSub || wallTarget == null) && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
{
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(attackSimPos - steeringLimb.SimPosition));
}
else
{
SteeringManager.SteeringSeek(steerPos, 2);
// Switch to Idle when cannot reach the target and if cannot damage the walls
if ((!canAttackSub || wallTarget == null) && !pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
{
State = AIState.Idle;
return;
}
State = AIState.Idle;
return;
}
}
}
@@ -1148,8 +1154,8 @@ namespace Barotrauma
{
selectedTargetMemory.Priority = 0;
}
return true;
}
return true;
}
return false;
}
@@ -525,6 +525,7 @@ namespace Barotrauma
protected void ReportProblems()
{
Order newOrder = null;
Hull targetHull = null;
if (Character.CurrentHull != null)
{
foreach (var hull in VisibleHulls)
@@ -534,21 +535,21 @@ namespace Barotrauma
if (c.CurrentHull != hull || !c.Enabled) { continue; }
if (AIObjectiveFightIntruders.IsValidTarget(c, Character))
{
AddTargets<AIObjectiveFightIntruders, Character>(Character, c);
if (newOrder == null)
if (AddTargets<AIObjectiveFightIntruders, Character>(Character, c) && newOrder == null)
{
var orderPrefab = Order.GetPrefab("reportintruders");
newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character);
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
targetHull = hull;
}
}
}
if (AIObjectiveExtinguishFires.IsValidTarget(hull, Character))
{
AddTargets<AIObjectiveExtinguishFires, Hull>(Character, hull);
if (newOrder == null)
if (AddTargets<AIObjectiveExtinguishFires, Hull>(Character, hull) && newOrder == null)
{
var orderPrefab = Order.GetPrefab("reportfire");
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
targetHull = hull;
}
}
foreach (Character c in Character.CharacterList)
@@ -556,13 +557,11 @@ namespace Barotrauma
if (c.CurrentHull != hull) { continue; }
if (AIObjectiveRescueAll.IsValidTarget(c, Character))
{
if (AddTargets<AIObjectiveRescueAll, Character>(c, Character))
if (AddTargets<AIObjectiveRescueAll, Character>(c, Character) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRescue>())
{
if (newOrder == null)
{
var orderPrefab = Order.GetPrefab("requestfirstaid");
newOrder = new Order(orderPrefab, c.CurrentHull, null, orderGiver: Character);
}
var orderPrefab = Order.GetPrefab("requestfirstaid");
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
targetHull = hull;
}
}
}
@@ -570,11 +569,11 @@ namespace Barotrauma
{
if (AIObjectiveFixLeaks.IsValidTarget(gap, Character))
{
AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap);
if (newOrder == null && !gap.IsRoomToRoom)
if (AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap) && newOrder == null && !gap.IsRoomToRoom)
{
var orderPrefab = Order.GetPrefab("reportbreach");
newOrder = new Order(orderPrefab, hull, null, orderGiver: Character);
targetHull = hull;
}
}
}
@@ -584,11 +583,11 @@ namespace Barotrauma
if (AIObjectiveRepairItems.IsValidTarget(item, Character))
{
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { continue; }
AddTargets<AIObjectiveRepairItems, Item>(Character, item);
if (newOrder == null)
if (AddTargets<AIObjectiveRepairItems, Item>(Character, item) && newOrder == null && !ObjectiveManager.HasActiveObjective<AIObjectiveRepairItem>())
{
var orderPrefab = Order.GetPrefab("reportbrokendevices");
newOrder = new Order(orderPrefab, item.CurrentHull, item.Repairables?.FirstOrDefault(), orderGiver: Character);
newOrder = new Order(orderPrefab, hull, item.Repairables?.FirstOrDefault(), orderGiver: Character);
targetHull = hull;
}
}
}
@@ -598,9 +597,9 @@ namespace Barotrauma
{
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime))
{
Character.Speak(newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
Character.Speak(newOrder.GetChatMessage("", targetHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
#if SERVER
GameMain.Server.SendOrderChatMessage(new OrderChatMessage(newOrder, "", Character.CurrentHull, null, Character));
GameMain.Server.SendOrderChatMessage(new OrderChatMessage(newOrder, "", targetHull, null, Character));
#endif
}
}
@@ -191,7 +191,8 @@ namespace Barotrauma
{
diff.Y = 0.0f;
}
if (diff.LengthSquared() < 0.001f) { return -host.Steering; }
//if (diff.LengthSquared() < 0.001f) { return -host.Steering; }
if (diff == Vector2.Zero) { return Vector2.Zero; }
return Vector2.Normalize(diff) * weight;
}
@@ -71,7 +71,7 @@ namespace Barotrauma
foreach (FireSource fs in targetHull.FireSources)
{
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
bool move = !inRange;
bool move = !inRange || !HumanAIController.VisibleHulls.Contains(fs.Hull);
if (inRange || useExtinquisherTimer > 0.0f)
{
useExtinquisherTimer += deltaTime;
@@ -79,7 +79,6 @@ namespace Barotrauma
{
useExtinquisherTimer = 0.0f;
}
character.AIController.SteeringManager.Reset();
character.CursorPosition = fs.Position;
if (extinguisher.Item.RequireAimToUse)
{
@@ -106,20 +105,19 @@ namespace Barotrauma
{
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
}
if (!character.CanSeeTarget(fs, sightLimb))
if (character.CanSeeTarget(fs, sightLimb))
{
move = true;
}
else
{
move = false;
character.SetInput(extinguisher.Item.IsShootable ? InputType.Shoot : InputType.Use, false, true);
extinguisher.Use(deltaTime, character);
if (!targetHull.FireSources.Contains(fs))
{
character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.Name, true), null, 0, "putoutfire", 10.0f);
{
character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.RoomName, true), null, 0, "putoutfire", 10.0f);
}
}
else
{
move = true;
}
}
if (move)
{
@@ -128,6 +126,10 @@ namespace Barotrauma
onAbandon: () => Abandon = true,
onCompleted: () => RemoveSubObjective(ref gotoObjective));
}
else
{
character.AIController.SteeringManager.Reset();
}
break;
}
}
@@ -11,7 +11,7 @@ namespace Barotrauma
{
public override string DebugTag => "fight intruders";
protected override float IgnoreListClearInterval => 30;
public virtual bool IgnoreUnsafeHulls => true;
public override bool IgnoreUnsafeHulls => true;
public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) { }
@@ -200,7 +200,7 @@ namespace Barotrauma
{
SteeringManager.SteeringSeek(character.GetRelativeSimPosition(Target), 10);
}
if (!insideSteering)
if (!insideSteering && character.CurrentHull == null)
{
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 1);
}
@@ -293,7 +293,7 @@ namespace Barotrauma
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier)
{
IsLoop = true,
// Don't override auto pilot unless it's an order by a player
// Don't override unless it's an order by a player
Override = orderGiver == Character.Controlled || orderGiver.IsRemotePlayer
};
break;
@@ -302,7 +302,7 @@ namespace Barotrauma
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier)
{
IsLoop = true,
// Don't override auto control unless it's an order by a player
// Don't override unless it's an order by a player
Override = orderGiver == Character.Controlled || orderGiver.IsRemotePlayer
};
break;
@@ -24,6 +24,8 @@ namespace Barotrauma
public Entity OperateTarget => operateTarget;
public ItemComponent Component => component;
public ItemComponent GetTarget() => useController ? controller : component;
public Func<bool> completionCondition;
public override float GetPriority()
@@ -35,6 +37,7 @@ namespace Barotrauma
}
if (component.Item.CurrentHull == null) { return 0; }
if (component.Item.CurrentHull.FireSources.Count > 0) { return 0; }
if (IsOperatedByAnother(GetTarget())) { return 0; }
if (Character.CharacterList.Any(c => c.CurrentHull == component.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return 0; }
float devotion = MathHelper.Min(10, Priority);
float value = devotion + AIObjectiveManager.OrderPriority * PriorityModifier;
@@ -58,6 +61,45 @@ namespace Barotrauma
}
}
private bool IsOperatedByAnother(ItemComponent target)
{
foreach (var c in Character.CharacterList)
{
if (c == character) { continue; }
if (!HumanAIController.IsFriendly(c)) { continue; }
if (c.SelectedConstruction != target.Item) { continue; }
// If the other character is player, don't try to operate
if (c.IsRemotePlayer || Character.Controlled == c) { return true; }
if (c.AIController is HumanAIController humanAi)
{
// If the other character is ordered to operate the item, let him do it
if (humanAi.ObjectiveManager.IsCurrentOrder<AIObjectiveOperateItem>())
{
return true;
}
else
{
if (target is Steering)
{
// Steering is hard-coded -> cannot use the required skills collection defined in the xml
return character.GetSkillLevel("helm") <= c.GetSkillLevel("helm");
}
else
{
return target.DegreeOfSuccess(character) <= target.DegreeOfSuccess(c);
}
}
}
else
{
// Shouldn't go here, unless we allow non-humans to operate items
return false;
}
}
return false;
}
protected override void Act(float deltaTime)
{
if (character.LockHands)
@@ -65,24 +107,24 @@ namespace Barotrauma
Abandon = true;
return;
}
ItemComponent target = useController ? controller : component;
ItemComponent target = GetTarget();
if (useController && controller == null)
{
character.Speak(TextManager.GetWithVariable("DialogCantFindController", "[item]", component.Item.Name, true), null, 2.0f, "cantfindcontroller", 30.0f);
Abandon = true;
return;
}
// Don't allow to operate an item that someone with a better skills already operates, unless this is an order
if (objectiveManager.CurrentOrder != this && IsOperatedByAnother(target))
{
// Don't abandon
return;
}
if (target.CanBeSelected)
{
if (character.CanInteractWith(target.Item, out _, checkLinked: false))
{
HumanAIController.FaceTarget(target.Item);
// Don't allow to operate an item that someone already operates, unless this objective is an order
if (objectiveManager.CurrentOrder != this && Character.CharacterList.Any(c => c.SelectedConstruction == target.Item && c != character && HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
{
// Don't abandon
return;
}
if (character.SelectedConstruction != target.Item)
{
target.Item.TryInteract(character, false, true);
@@ -764,12 +764,17 @@ namespace Barotrauma
.Where(p => p.AfflictionType == "huskinfection")
.Select(p => p as AfflictionPrefabHusk)
.FirstOrDefault(p => p.TargetSpecies.Any(t => t.Equals(AfflictionHusk.GetNonHuskedSpeciesName(speciesName, p), StringComparison.InvariantCultureIgnoreCase)));
string nonHuskedSpeciesName = string.Empty;
if (matchingAffliction == null)
{
DebugConsole.ThrowError("Cannot find a husk infection that matches this species! Please add the speciesnames as 'targets' in the husk affliction prefab definition!");
return;
// Crashes if we fail to create a ragdoll -> Let's just use some ragdoll so that the user sees the error msg.
nonHuskedSpeciesName = IsHumanoid ? HumanSpeciesName : "crawler";
}
else
{
nonHuskedSpeciesName = AfflictionHusk.GetNonHuskedSpeciesName(speciesName, matchingAffliction);
}
string nonHuskedSpeciesName = AfflictionHusk.GetNonHuskedSpeciesName(speciesName, matchingAffliction);
ragdollParams = IsHumanoid ? RagdollParams.GetDefaultRagdollParams<HumanRagdollParams>(nonHuskedSpeciesName) : RagdollParams.GetDefaultRagdollParams<FishRagdollParams>(nonHuskedSpeciesName) as RagdollParams;
if (info == null)
{
@@ -1912,7 +1917,7 @@ namespace Barotrauma
{
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
{
focusedCharacter = FindCharacterAtPosition(mouseSimPos);
focusedCharacter = CanInteract ? FindCharacterAtPosition(mouseSimPos) : null;
focusedItem = CanInteract ?
FindItemAtPosition(mouseSimPos, GameMain.Config.AimAssistAmount * (AnimController.InWater ? 1.5f : 1.0f)) : null;
findFocusedTimer = 0.05f;
@@ -1972,11 +1977,11 @@ namespace Barotrauma
{
DeselectCharacter();
}
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanBeDragged)
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanBeDragged && CanInteract)
{
SelectCharacter(focusedCharacter);
}
else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow && CanInteractWith(focusedCharacter, 160f, false))
else if (focusedCharacter != null && IsKeyHit(InputType.Health) && focusedCharacter.CharacterHealth.UseHealthWindow && CanInteract && CanInteractWith(focusedCharacter, 160f, false))
{
if (focusedCharacter == SelectedCharacter)
{
@@ -289,7 +289,7 @@ namespace Barotrauma
public static string GetNonHuskedSpeciesName(string huskedSpeciesName, AfflictionPrefabHusk prefab)
{
string nonTag = prefab.HuskedSpeciesName.Remove(AfflictionPrefabHusk.Tag);
return huskedSpeciesName.Remove(nonTag);
return huskedSpeciesName.ToLowerInvariant().Remove(nonTag);
}
}
}
@@ -41,7 +41,7 @@ namespace Barotrauma
{
public AfflictionPrefabHusk(XElement element, Type type = null) : base(element, type)
{
HuskedSpeciesName = element.GetAttributeString("huskedspeciesname", null);
HuskedSpeciesName = element.GetAttributeString("huskedspeciesname", null).ToLowerInvariant();
if (HuskedSpeciesName == null)
{
DebugConsole.NewMessage($"No 'huskedspeciesname' defined for the husk affliction ({Identifier}) in {element.ToString()}", Color.Orange);
@@ -85,7 +85,11 @@ namespace Barotrauma
public override void Update(float deltaTime)
{
if (IsClient) { return; }
if (IsClient)
{
if (item.ParentInventory != null) { item.body.FarseerBody.IsKinematic = false; }
return;
}
switch (State)
{
case 0:
@@ -296,10 +296,11 @@ namespace Barotrauma
{
get
{
#if DEBUG
return false;
/*#if DEBUG
return false;
#endif
return sendUserStatistics;
return sendUserStatistics;*/
}
set
{
@@ -25,7 +25,18 @@ namespace Barotrauma.Items.Components
private readonly bool autoOrientGap;
private bool isStuck;
public bool IsStuck => isStuck;
public bool IsStuck
{
get { return isStuck; }
private set
{
if (isStuck == value) { return; }
isStuck = value;
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
private float resetPredictionTimer;
@@ -65,12 +76,12 @@ namespace Barotrauma.Items.Components
public float Stuck
{
get { return stuck; }
set
set
{
if (isOpen || isBroken || !CanBeWelded) return;
stuck = MathHelper.Clamp(value, 0.0f, 100.0f);
if (stuck <= 0.0f) isStuck = false;
if (stuck >= 100.0f) isStuck = true;
if (stuck <= 0.0f) { IsStuck = false; }
if (stuck >= 100.0f) { IsStuck = true; }
}
}
@@ -296,7 +307,7 @@ namespace Barotrauma.Items.Components
}
bool isClosing = false;
if (!isStuck)
if (!IsStuck)
{
if (PredictedState == null)
{
@@ -541,7 +552,7 @@ namespace Barotrauma.Items.Components
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
{
if (isStuck) return;
if (IsStuck) return;
bool wasOpen = PredictedState == null ? isOpen : PredictedState.Value;
@@ -9,7 +9,7 @@ namespace Barotrauma.Items.Components
{
partial class ElectricalDischarger : Powered
{
private static List<ElectricalDischarger> list = new List<ElectricalDischarger>();
private static readonly List<ElectricalDischarger> list = new List<ElectricalDischarger>();
public static IEnumerable<ElectricalDischarger> List
{
get { return list; }
@@ -48,14 +48,14 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(100.0f, true, description: "How far the discharge can travel from the item."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
[Serialize(500.0f, true, description: "How far the discharge can travel from the item."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
public float Range
{
get;
set;
}
[Serialize(10.0f, true, description: "How much further can the discharge be carried when moving across walls."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
[Serialize(25.0f, true, description: "How much further can the discharge be carried when moving across walls."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
public float RangeMultiplierInWalls
{
get;
@@ -127,20 +127,42 @@ namespace Barotrauma.Items.Components
#if CLIENT
frameOffset = Rand.Int(electricitySprite.FrameCount);
#endif
if (timer > 0.0f)
{
if (charging)
{
if (Voltage > MinVoltage)
{
Discharge();
}
}
timer -= deltaTime;
}
else
if (timer <= 0.0f)
{
IsActive = false;
return;
}
timer -= deltaTime;
if (charging)
{
if (GetAvailableBatteryPower() >= powerConsumption)
{
var batteries = item.GetConnectedComponents<PowerContainer>();
float neededPower = powerConsumption;
while (neededPower > 0.0001f && batteries.Count > 0)
{
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
float takePower = neededPower / batteries.Count;
takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
foreach (PowerContainer battery in batteries)
{
neededPower -= takePower;
battery.Charge -= takePower / 3600.0f;
#if SERVER
if (GameMain.Server != null)
{
battery.Item.CreateServerEvent(battery);
}
#endif
}
}
Discharge();
}
else if (Voltage > MinVoltage)
{
Discharge();
}
}
}
@@ -141,7 +141,7 @@ namespace Barotrauma.Items.Components
//TODO: refactor the hitting logic (get rid of the magic numbers, make it possible to use different kinds of animations for different items)
if (!hitting)
{
bool aim = picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
bool aim = picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
if (aim)
{
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 5f, MathHelper.PiOver4));
@@ -96,11 +96,14 @@ namespace Barotrauma.Items.Components
{
useState -= deltaTime;
if (useState <= 0.0f) IsActive = false;
if (item.AiTarget != null)
if (useState <= 0.0f)
{
item.AiTarget.SoundRange = IsActive ? item.AiTarget.MaxSoundRange : item.AiTarget.MinSoundRange;
IsActive = false;
}
if (item.AiTarget != null && IsActive)
{
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
}
}
@@ -399,7 +399,7 @@ namespace Barotrauma.Items.Components
case "activate":
case "use":
case "trigger_in":
item.Use(1.0f);
item.Use(1.0f, sender);
break;
case "toggle":
if (signal != "0")
@@ -669,7 +669,7 @@ namespace Barotrauma.Items.Components
}
}
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Character user = null)
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null)
{
if (statusEffectLists == null) return;
@@ -680,20 +680,24 @@ namespace Barotrauma.Items.Components
{
if (broken && effect.type != ActionType.OnBroken) { continue; }
if (user != null) { effect.SetUser(user); }
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, false, false);
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, useTarget, false, false, worldPosition);
}
}
public virtual void Load(XElement componentElement, bool usePrefabValues)
{
if (componentElement == null || usePrefabValues) { return; }
foreach (XAttribute attribute in componentElement.Attributes())
{
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
property.TrySetValue(this, attribute.Value);
if (componentElement != null && !usePrefabValues)
{
foreach (XAttribute attribute in componentElement.Attributes())
{
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
property.TrySetValue(this, attribute.Value);
}
ParseMsg();
OverrideRequiredItems(componentElement);
}
ParseMsg();
OverrideRequiredItems(componentElement);
if (item.Submarine != null) { SerializableProperty.UpgradeGameVersion(this, originalElement, item.Submarine.GameVersion); }
}
/// <summary>
@@ -123,9 +123,9 @@ namespace Barotrauma.Items.Components
if (user.AnimController.InWater)
{
if (diff.Length() > 30.0f)
if (diff.LengthSquared() > 30.0f * 30.0f)
{
user.AnimController.TargetMovement = Vector2.Clamp(diff*0.01f, -Vector2.One, Vector2.One);
user.AnimController.TargetMovement = Vector2.Clamp(diff * 0.01f, -Vector2.One, Vector2.One);
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
}
else
@@ -136,11 +136,29 @@ namespace Barotrauma.Items.Components
else
{
diff.Y = 0.0f;
if (diff != Vector2.Zero && diff.LengthSquared() > 10.0f * 10.0f)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && user != Character.Controlled)
{
user.AnimController.TargetMovement = Vector2.Normalize(diff);
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
return;
if (Math.Abs(diff.X) > 20.0f)
{
//wait for the character to walk to the correct position
return;
}
else if (Math.Abs(diff.X) > 0.1f)
{
//aim to keep the collider at the correct position once close enough
user.AnimController.Collider.LinearVelocity = new Vector2(
diff.X * 0.1f,
user.AnimController.Collider.LinearVelocity.Y);
}
}
else
{
if (Math.Abs(diff.X) > 10.0f)
{
user.AnimController.TargetMovement = Vector2.Normalize(diff);
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
return;
}
}
user.AnimController.TargetMovement = Vector2.Zero;
}
@@ -293,7 +311,7 @@ namespace Barotrauma.Items.Components
private void CancelUsing(Character character)
{
if (character == null || character.Removed) return;
if (character == null || character.Removed) { return; }
foreach (LimbPos lb in limbPositions)
{
@@ -304,18 +322,21 @@ namespace Barotrauma.Items.Components
limb.PullJointEnabled = false;
}
if (character.SelectedConstruction == this.item) character.SelectedConstruction = null;
if (character.SelectedConstruction == this.item) { character.SelectedConstruction = null; }
character.AnimController.Anim = AnimController.Animation.None;
if (character == Character.Controlled)
{
HideHUDs(false);
}
#if SERVER
item.CreateServerEvent(this);
#endif
}
public override bool Select(Character activator)
{
if (activator == null || activator.Removed) return false;
if (activator == null || activator.Removed) { return false; }
//someone already using the item
if (user != null && !user.Removed)
@@ -330,10 +351,12 @@ namespace Barotrauma.Items.Components
}
else
{
user = activator;
user = activator;
IsActive = true;
}
#if SERVER
item.CreateServerEvent(this);
#endif
item.SendSignal(0, "1", "signal_out", user);
return true;
}
@@ -86,7 +86,7 @@ namespace Barotrauma.Items.Components
if (item.CurrentHull == null) { return; }
float powerFactor = currPowerConsumption <= 0.0f ? 1.0f : Voltage;
float powerFactor = Math.Min(currPowerConsumption <= 0.0f ? 1.0f : Voltage, 1.0f);
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
//less effective when in a bad condition
@@ -640,9 +640,15 @@ namespace Barotrauma.Items.Components
}
}
if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item)
if (objective.Override)
{
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
if (lastUser != null && lastUser != character && lastUser != lastAIUser)
{
if (lastUser.SelectedConstruction == item)
{
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
}
}
}
LastUser = lastAIUser = character;
@@ -479,9 +479,12 @@ namespace Barotrauma.Items.Components
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
{
if (user != character && user != null && user.SelectedConstruction == item)
if (objective.Override)
{
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
if (user != character && user != null && user.SelectedConstruction == item)
{
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
}
}
user = character;
if (!AutoPilot)
@@ -175,9 +175,8 @@ namespace Barotrauma.Items.Components
else
{
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, rechargeSpeed, 0.05f);
Charge += currPowerConsumption * Voltage / 3600.0f;
}
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f;
}
if (charge <= 0.0f)
{
@@ -280,6 +280,8 @@ namespace Barotrauma.Items.Components
{
//we've already received this signal
if (lastPowerProbeRecipients.Contains(this)) { return; }
if (item.Condition <= 0.0f) { return; }
lastPowerProbeRecipients.Add(this);
if (power < 0.0f)
@@ -306,15 +308,15 @@ namespace Barotrauma.Items.Components
foreach (ItemComponent ic in recipient.Item.Components)
{
//powertransfer components don't need to receive the signal in the pass-through signal connections
//other junction boxes don't need to receive the signal in the pass-through signal connections
//because we relay it straight to the connected items without going through the whole chain of junction boxes
if (ic is PowerTransfer && connection.Name.Contains("signal")) { continue; }
if (ic is PowerTransfer && !(ic is RelayComponent) && connection.Name.Contains("signal")) { continue; }
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, 0.0f, signalStrength);
}
foreach (StatusEffect effect in recipient.Effects)
{
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f, null, null, false, false);
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
}
}
}
@@ -279,13 +279,30 @@ namespace Barotrauma.Items.Components
var pc = powerSource.Item.GetComponent<PowerContainer>();
if (pc != null)
{
float voltage = -pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
float voltage = pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
powered.voltage += voltage;
}
}
}
}
/// <summary>
/// Returns the amount of power that can be supplied by batteries directly connected to the item
/// </summary>
protected float GetAvailableBatteryPower()
{
var batteries = item.GetConnectedComponents<PowerContainer>();
float availablePower = 0.0f;
foreach (PowerContainer battery in batteries)
{
float batteryPower = Math.Min(battery.Charge * 3600.0f, battery.MaxOutPut);
availablePower += batteryPower;
}
return availablePower;
}
protected override void RemoveComponentSpecific()
{
poweredList.Remove(this);
@@ -458,6 +458,11 @@ namespace Barotrauma.Items.Components
if (character != null) { character.LastDamageSource = item; }
#if CLIENT
PlaySound(ActionType.OnUse, item.WorldPosition, user: user);
PlaySound(ActionType.OnImpact, item.WorldPosition, user: user);
#endif
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
if (target.Body.UserData is Limb targetLimb)
@@ -489,14 +494,26 @@ namespace Barotrauma.Items.Components
}
}
}
}
#if SERVER
if (GameMain.NetworkMember.IsServer)
{
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse });
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact });
}
if (GameMain.NetworkMember.IsServer)
{
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse, this, targetLimb.character.ID, targetLimb, (ushort)0, item.WorldPosition });
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact, this, targetLimb.character.ID, targetLimb, (ushort)0, item.WorldPosition });
}
#endif
}
else
{
ApplyStatusEffects(ActionType.OnUse, 1.0f, useTarget: target.Body.UserData as Entity, user: user);
ApplyStatusEffects(ActionType.OnImpact, 1.0f, useTarget: target.Body.UserData as Entity, user: user);
#if SERVER
if (GameMain.NetworkMember.IsServer)
{
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse, this, (ushort)0, null, (target.Body.UserData as Entity)?.ID ?? 0, item.WorldPosition });
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact, this, (ushort)0, null, (target.Body.UserData as Entity)?.ID ?? 0, item.WorldPosition });
}
#endif
}
}
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
@@ -130,6 +130,12 @@ namespace Barotrauma.Items.Components
}
else
{
#if SERVER
if (CurrentFixer != character || currentFixerAction != action)
{
item.CreateServerEvent(this);
}
#endif
CurrentFixer = character;
CurrentFixerAction = action;
return true;
@@ -140,12 +146,15 @@ namespace Barotrauma.Items.Components
{
if (CurrentFixer == character)
{
#if SERVER
if (CurrentFixer != character || currentFixerAction != FixActions.None)
{
item.CreateServerEvent(this);
}
#endif
CurrentFixer.AnimController.Anim = AnimController.Animation.None;
CurrentFixer = null;
currentFixerAction = FixActions.None;
#if SERVER
item.CreateServerEvent(this);
#endif
#if CLIENT
repairSoundChannel?.FadeOutAndDispose();
repairSoundChannel = null;
@@ -214,16 +223,16 @@ namespace Barotrauma.Items.Components
return;
}
UpdateFixAnimation(CurrentFixer);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (CurrentFixer != null && (CurrentFixer.SelectedConstruction != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
{
StopRepairing(CurrentFixer);
return;
}
UpdateFixAnimation(CurrentFixer);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills);
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
@@ -258,7 +258,7 @@ namespace Barotrauma.Items.Components
foreach (StatusEffect effect in recipient.Effects)
{
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step, null, null, false, false);
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
}
}
}
@@ -35,6 +35,11 @@ namespace Barotrauma.Items.Components
set { /*do nothing*/ }
}
public Character User
{
get { return user; }
}
public ConnectionPanel(Item item, XElement element)
: base(item, element)
{
@@ -126,6 +131,9 @@ namespace Barotrauma.Items.Components
if (user == null || user.SelectedConstruction != item)
{
#if SERVER
if (user != null) { item.CreateServerEvent(this); }
#endif
user = null;
return;
}
@@ -135,6 +143,11 @@ namespace Barotrauma.Items.Components
user.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
}
public override void UpdateBroken(float deltaTime, Camera cam)
{
Update(deltaTime, cam);
}
partial void UpdateProjSpecific(float deltaTime);
public override bool Select(Character picker)
@@ -147,13 +160,16 @@ namespace Barotrauma.Items.Components
}
user = picker;
#if SERVER
if (user != null) { item.CreateServerEvent(this); }
#endif
IsActive = true;
return true;
}
public override bool Use(float deltaTime, Character character = null)
{
if (character == null || character != user) return false;
if (character == null || character != user) { return false; }
var powered = item.GetComponent<Powered>();
if (powered != null)
@@ -257,6 +273,10 @@ namespace Barotrauma.Items.Components
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
#if CLIENT
TriggerRewiringSound();
#endif
foreach (Connection connection in Connections)
{
foreach (Wire wire in connection.Wires)
@@ -172,7 +172,7 @@ namespace Barotrauma.Items.Components
foreach (StatusEffect effect in ciElement.StatusEffects)
{
item.ApplyStatusEffect(effect, ciElement.State ? ActionType.OnUse : ActionType.OnSecondaryUse, 1.0f, null, null, true, false);
item.ApplyStatusEffect(effect, ciElement.State ? ActionType.OnUse : ActionType.OnSecondaryUse, 1.0f, null, null, null, true, false);
}
}
}
@@ -34,7 +34,7 @@ namespace Barotrauma.Items.Components
{
range = MathHelper.Clamp(value, 0.0f, 4096.0f);
#if CLIENT
if (light != null) light.Range = range;
if (light != null) { light.Range = range; }
#endif
}
}
@@ -75,7 +75,7 @@ namespace Barotrauma.Items.Components
get { return IsActive; }
set
{
if (IsActive == value) return;
if (IsActive == value) { return; }
IsActive = value;
#if SERVER
@@ -135,11 +135,8 @@ namespace Barotrauma.Items.Components
{
if (base.IsActive == value) { return; }
base.IsActive = value;
#if CLIENT
if (light == null) return;
light.Color = value ? lightColor : Color.Transparent;
if (!value) lightBrightness = 0.0f;
#endif
SetLightSourceState(value, value ? lightBrightness : 0.0f);
}
}
@@ -153,7 +150,8 @@ namespace Barotrauma.Items.Components
Position = item.Position,
CastShadows = castShadows,
IsBackground = drawBehindSubs,
SpriteScale = Vector2.One * item.Scale
SpriteScale = Vector2.One * item.Scale,
Range = range
};
#endif
@@ -161,40 +159,34 @@ namespace Barotrauma.Items.Components
item.AddTag("light");
}
#if CLIENT
public override void OnScaleChanged()
{
light.SpriteScale = Vector2.One * item.Scale;
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
}
#endif
public override void OnItemLoaded()
{
base.OnItemLoaded();
itemLoaded = true;
#if CLIENT
light.Color = IsActive ? lightColor : Color.Transparent;
if (!IsActive) lightBrightness = 0.0f;
#endif
SetLightSourceState(IsActive, lightBrightness);
}
public override void Update(float deltaTime, Camera cam)
{
if (item.AiTarget != null)
{
UpdateAITarget(item.AiTarget);
}
UpdateOnActiveEffects(deltaTime);
#if CLIENT
light.ParentSub = item.Submarine;
#endif
if (item.Container != null)
{
light.Color = Color.Transparent;
SetLightSourceState(false, 0.0f);
return;
}
#if CLIENT
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
#endif
PhysicsBody body = ParentBody ?? item.body;
if (body != null)
{
#if CLIENT
@@ -203,9 +195,7 @@ namespace Barotrauma.Items.Components
#endif
if (!body.Enabled)
{
#if CLIENT
light.Color = Color.Transparent;
#endif
SetLightSourceState(false, 0.0f);
return;
}
}
@@ -217,7 +207,6 @@ namespace Barotrauma.Items.Components
}
currPowerConsumption = powerConsumption;
if (Rand.Range(0.0f, 1.0f) < 0.05f && Voltage < Rand.Range(0.0f, MinVoltage))
{
#if CLIENT
@@ -240,36 +229,21 @@ namespace Barotrauma.Items.Components
if (blinkTimer > 0.5f)
{
#if CLIENT
light.Color = Color.Transparent;
#endif
SetLightSourceState(false, lightBrightness);
}
else
{
#if CLIENT
light.Color = lightColor * lightBrightness * (1.0f - Rand.Range(0.0f, Flicker));
light.Range = range;
#endif
SetLightSourceState(true, lightBrightness * (1.0f - Rand.Range(0.0f, flicker)));
}
if (item.AiTarget != null)
{
UpdateAITarget(item.AiTarget);
}
}
#if CLIENT
public override void UpdateBroken(float deltaTime, Camera cam)
{
light.Color = Color.Transparent;
lightBrightness = 0.0f;
if (powerIn == null && powerConsumption > 0.0f) { Voltage -= deltaTime; }
}
protected override void RemoveComponentSpecific()
public override void UpdateBroken(float deltaTime, Camera cam)
{
base.RemoveComponentSpecific();
light.Remove();
SetLightSourceState(false, 0.0f);
}
#endif
public override bool Use(float deltaTime, Character character = null)
{
return true;
@@ -277,8 +251,6 @@ namespace Barotrauma.Items.Components
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
{
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power, signalStrength);
switch (connection.Name)
{
case "toggle":
@@ -300,13 +272,14 @@ namespace Barotrauma.Items.Components
private void UpdateAITarget(AITarget target)
{
//voltage > minVoltage || powerConsumption <= 0.0f; <- ?
target.Enabled = IsActive;
if (!IsActive) { return; }
if (target.MaxSightRange <= 0)
{
target.MaxSightRange = Range * 5;
}
target.SightRange = IsActive ? target.MaxSightRange * lightBrightness : 0;
target.SightRange = target.MaxSightRange * lightBrightness;
}
partial void SetLightSourceState(bool enabled, float brightness);
}
}
@@ -102,7 +102,7 @@ namespace Barotrauma.Items.Components
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
{
if (!IsOn) { return; }
if (!IsOn || item.Condition <= 0.0f) { return; }
//we've already received this signal
if (lastPowerProbeRecipients.Contains(this)) { return; }
@@ -276,7 +276,7 @@ namespace Barotrauma.Items.Components
if (reload > 0.0f) return false;
if (GetAvailablePower() < powerConsumption)
if (GetAvailableBatteryPower() < powerConsumption)
{
#if CLIENT
if (!flashLowPower && character != null && character == Character.Controlled)
@@ -410,7 +410,7 @@ namespace Barotrauma.Items.Components
character.AIController.SelectTarget(null);
}
if (GetAvailablePower() < powerConsumption)
if (GetAvailableBatteryPower() < powerConsumption)
{
var batteries = item.GetConnectedComponents<PowerContainer>();
@@ -540,21 +540,6 @@ namespace Barotrauma.Items.Components
return false;
}
private float GetAvailablePower()
{
var batteries = item.GetConnectedComponents<PowerContainer>();
float availablePower = 0.0f;
foreach (PowerContainer battery in batteries)
{
float batteryPower = Math.Min(battery.Charge*3600.0f, battery.MaxOutPut);
availablePower += batteryPower;
}
return availablePower;
}
private void GetAvailablePower(out float availableCharge, out float availableCapacity)
{
var batteries = item.GetConnectedComponents<PowerContainer>();
@@ -425,7 +425,7 @@ namespace Barotrauma
string[] splitTags = value.Split(',');
foreach (string tag in splitTags)
{
string[] splitTag = tag.Split(':');
string[] splitTag = tag.Trim().Split(':');
splitTag[0] = splitTag[0].ToLowerInvariant();
tags.Add(string.Join(":", splitTag));
}
@@ -1060,18 +1060,18 @@ namespace Barotrauma
return true;
}
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false)
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, Entity useTarget = null, bool isNetworkEvent = false, Vector2? worldPosition = null)
{
if (!hasStatusEffectsOfType[(int)type]) { return; }
foreach (StatusEffect effect in statusEffectLists[type])
{
ApplyStatusEffect(effect, type, deltaTime, character, limb, isNetworkEvent, false);
ApplyStatusEffect(effect, type, deltaTime, character, limb, useTarget, isNetworkEvent, false, worldPosition);
}
}
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false, bool checkCondition = true)
public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null, Entity useTarget = null, bool isNetworkEvent = false, bool checkCondition = true, Vector2? worldPosition = null)
{
if (!isNetworkEvent && checkCondition)
{
@@ -1110,6 +1110,12 @@ namespace Barotrauma
if (targets.Count > 0) { hasTargets = true; }
}
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget) && useTarget is ISerializableEntity serializableTarget)
{
hasTargets = true;
targets.Add(serializableTarget);
}
if (!hasTargets) { return; }
if (effect.HasTargetType(StatusEffect.TargetType.Hull) && CurrentHull != null)
@@ -1125,30 +1131,32 @@ namespace Barotrauma
}
}
if (effect.HasTargetType(StatusEffect.TargetType.Character))
if (character != null)
{
if (type == ActionType.OnContained && ParentInventory is CharacterInventory characterInventory)
if (effect.HasTargetType(StatusEffect.TargetType.Character))
{
targets.Add(characterInventory.Owner as ISerializableEntity);
if (type == ActionType.OnContained && ParentInventory is CharacterInventory characterInventory)
{
targets.Add(characterInventory.Owner as ISerializableEntity);
}
else
{
targets.Add(character);
}
}
else
if (effect.HasTargetType(StatusEffect.TargetType.AllLimbs))
{
targets.Add(character);
targets.AddRange(character.AnimController.Limbs.ToList());
}
}
if (effect.HasTargetType(StatusEffect.TargetType.Limb))
{
targets.Add(limb);
}
if (effect.HasTargetType(StatusEffect.TargetType.AllLimbs))
{
targets.AddRange(character.AnimController.Limbs.ToList());
}
if (Container != null && effect.HasTargetType(StatusEffect.TargetType.Parent)) targets.Add(Container);
effect.Apply(type, deltaTime, this, targets);
effect.Apply(type, deltaTime, this, targets, worldPosition);
}
@@ -1360,7 +1368,8 @@ namespace Barotrauma
{
if (transformDirty) { return false; }
Vector2 normal = contact.Manifold.LocalNormal;
contact.GetWorldManifold(out Vector2 normal, out _);
if (contact.FixtureA.Body == f1.Body) { normal = -normal; }
float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal);
OnCollisionProjSpecific(f1, f2, contact, impact);
@@ -1568,7 +1577,7 @@ namespace Barotrauma
foreach (StatusEffect effect in connection.Effects)
{
if (condition <= 0.0f && effect.type != ActionType.OnBroken) { continue; }
if (signal != "0" && !string.IsNullOrEmpty(signal)) { ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step, null, null, false, false); }
if (signal != "0" && !string.IsNullOrEmpty(signal)) { ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step); }
}
connection.SendSignal(stepsTaken, signal, source ?? this, sender, power, signalStrength);
}
@@ -575,6 +575,10 @@ namespace Barotrauma
public void ApplyFlowForces(float deltaTime, Item item)
{
if (item.body.Mass <= 0.0f)
{
return;
}
foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0))
{
var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f);
@@ -180,14 +180,22 @@ namespace Barotrauma
}
linkedSub.filePath = element.GetAttributeString("filepath", "");
int[] linkedToIds = element.GetAttributeIntArray("linkedto", new int[0]);
int[] linkedToIds = element.GetAttributeIntArray("linkedto", new int[0]);
for (int i = 0; i < linkedToIds.Length; i++)
{
linkedSub.linkedToID.Add((ushort)linkedToIds[i]);
if (Screen.Selected == GameMain.SubEditorScreen)
{
if (FindEntityByID((ushort)linkedToIds[i]) is MapEntity linked)
{
linkedSub.linkedTo.Add(linked);
}
}
}
linkedSub.originalLinkedToID = (ushort)element.GetAttributeInt("originallinkedto", 0);
linkedSub.originalMyPortID = (ushort)element.GetAttributeInt("originalmyport", 0);
return linkedSub.loadSub ? linkedSub : null;
}
@@ -107,11 +107,14 @@ namespace Barotrauma
{
public readonly Entity Entity;
public readonly UInt16 OriginalID;
public readonly bool Remove = false;
public SpawnOrRemove(Entity entity, bool remove)
{
Entity = entity;
OriginalID = entity.ID;
Remove = remove;
}
}
@@ -328,7 +328,16 @@ namespace Barotrauma.Networking
}
}
public string ServerName;
private string serverName;
public string ServerName
{
get { return serverName; }
set
{
serverName = value;
if (serverName.Length > NetConfig.ServerNameMaxLength) { ServerName = ServerName.Substring(0, NetConfig.ServerNameMaxLength); }
}
}
private string serverMessageText;
public string ServerMessageText
@@ -177,7 +177,7 @@ namespace Barotrauma
string[] readTags = valStr.Split(',');
int matches = 0;
foreach (string tag in readTags)
if (((Item)target).HasTag(tag)) matches++;
if (target is Item item && item.HasTag(tag)) matches++;
//If operator is == then it needs to match everything, otherwise if its != there must be zero matches.
return Operator == OperatorType.Equals ? matches >= readTags.Length : matches <= 0;
@@ -225,8 +225,7 @@ namespace Barotrauma
return success;
case ConditionType.SpeciesName:
if (target == null) { return Operator == OperatorType.NotEquals; }
Character targetCharacter = target as Character;
if (targetCharacter == null) { return false; }
if (!(target is Character targetCharacter)) { return false; }
return (Operator == OperatorType.Equals) == (targetCharacter.SpeciesName == valStr);
case ConditionType.EntityType:
switch (valStr)
+175 -129
View File
@@ -12,8 +12,10 @@ namespace Barotrauma
//only used if none of the selected content packages contain any text files
const string VanillaTextFilePath = "Content/Texts/EnglishVanilla.xml";
private static readonly object mutex = new object();
//key = language
private static Dictionary<string, List<TextPack>> textPacks = new Dictionary<string, List<TextPack>>();
private static Dictionary<string, List<TextPack>> textPacks;
private static readonly string[] serverMessageCharacters = new string[] { "~", "[", "]", "=" };
@@ -25,10 +27,16 @@ namespace Barotrauma
private set;
}
private static readonly HashSet<string> availableLanguages = new HashSet<string>();
private static HashSet<string> availableLanguages;
public static IEnumerable<string> AvailableLanguages
{
get { return availableLanguages; }
get
{
lock (mutex)
{
return new HashSet<string>(availableLanguages);
}
}
}
public static List<string> GetTextFiles()
@@ -55,25 +63,29 @@ namespace Barotrauma
/// </summary>
public static string GetTranslatedLanguageName(string language)
{
if (!textPacks.ContainsKey(language))
lock (mutex)
{
if (!textPacks.ContainsKey(language))
{
return language;
}
foreach (var textPack in textPacks[language])
{
if (textPack.Language == language)
{
return textPack.TranslatedName;
}
}
return language;
}
foreach (var textPack in textPacks[language])
{
if (textPack.Language == language)
{
return textPack.TranslatedName;
}
}
return language;
}
public static void LoadTextPacks(IEnumerable<ContentPackage> selectedContentPackages)
{
availableLanguages.Clear();
textPacks.Clear();
HashSet<string> newLanguages = new HashSet<string>();
Dictionary<string, List<TextPack>> newTextPacks = new Dictionary<string, List<TextPack>>();
var textFiles = ContentPackage.GetFilesOfType(selectedContentPackages, ContentType.Text);
foreach (string file in textFiles)
@@ -81,12 +93,12 @@ namespace Barotrauma
try
{
var textPack = new TextPack(file);
availableLanguages.Add(textPack.Language);
if (!textPacks.ContainsKey(textPack.Language))
newLanguages.Add(textPack.Language);
if (!newTextPacks.ContainsKey(textPack.Language))
{
textPacks.Add(textPack.Language, new List<TextPack>());
newTextPacks.Add(textPack.Language, new List<TextPack>());
}
textPacks[textPack.Language].Add(textPack);
newTextPacks[textPack.Language].Add(textPack);
}
catch (Exception e)
{
@@ -94,7 +106,7 @@ namespace Barotrauma
}
}
if (textPacks.Count == 0)
if (newTextPacks.Count == 0)
{
DebugConsole.ThrowError("No text files available in any of the selected content packages. Attempting to find a vanilla English text file...");
if (!File.Exists(VanillaTextFilePath))
@@ -102,9 +114,21 @@ namespace Barotrauma
throw new Exception("No text files found in any of the selected content packages or in the default text path!");
}
var textPack = new TextPack(VanillaTextFilePath);
availableLanguages.Add(textPack.Language);
textPacks.Add(textPack.Language, new List<TextPack>() { textPack });
newLanguages.Add(textPack.Language);
newTextPacks.Add(textPack.Language, new List<TextPack>() { textPack });
}
if (newTextPacks.Count == 0)
{
throw new Exception("Failed to load text packs!");
}
lock (mutex)
{
textPacks = newTextPacks;
availableLanguages = newLanguages;
}
Initialized = true;
}
@@ -112,74 +136,81 @@ namespace Barotrauma
{
if (string.IsNullOrEmpty(textTag)) { return false; }
if (!textPacks.ContainsKey(Language))
lock (mutex)
{
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!");
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!");
}
}
foreach (TextPack textPack in textPacks[Language])
{
if (textPack.Get(textTag) != null) { return true; }
}
}
foreach (TextPack textPack in textPacks[Language])
{
if (textPack.Get(textTag) != null) { return true; }
}
return false;
}
public static string Get(string textTag, bool returnNull = false, string fallBackTag = null)
{
if (!textPacks.ContainsKey(Language))
lock (mutex)
{
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!");
}
}
foreach (TextPack textPack in textPacks[Language])
{
string text = textPack.Get(textTag);
if (text != null) { return text; }
}
if (!string.IsNullOrEmpty(fallBackTag))
{
foreach (TextPack textPack in textPacks[Language])
{
string text = textPack.Get(fallBackTag);
if (text != null) { return text; }
}
}
//if text was not found and we're using a language other than English, see if we can find an English version
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
if (Language != "English" && textPacks.ContainsKey("English"))
{
foreach (TextPack textPack in textPacks["English"])
{
string text = textPack.Get(textTag);
if (text != null)
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
Language = "English";
if (!textPacks.ContainsKey(Language))
{
#if DEBUG
DebugConsole.NewMessage("Text \"" + textTag + "\" not found for the language \"" + Language + "\". Using the English text \"" + text + "\" instead.");
#endif
return text;
throw new Exception("No text packs available in English!");
}
}
}
if (returnNull)
{
return null;
}
else
{
DebugConsole.ThrowError("Text \"" + textTag + "\" not found.");
return textTag;
foreach (TextPack textPack in textPacks[Language])
{
string text = textPack.Get(textTag);
if (text != null) { return text; }
}
if (!string.IsNullOrEmpty(fallBackTag))
{
foreach (TextPack textPack in textPacks[Language])
{
string text = textPack.Get(fallBackTag);
if (text != null) { return text; }
}
}
//if text was not found and we're using a language other than English, see if we can find an English version
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
if (Language != "English" && textPacks.ContainsKey("English"))
{
foreach (TextPack textPack in textPacks["English"])
{
string text = textPack.Get(textTag);
if (text != null)
{
#if DEBUG
DebugConsole.NewMessage("Text \"" + textTag + "\" not found for the language \"" + Language + "\". Using the English text \"" + text + "\" instead.");
#endif
return text;
}
}
}
if (returnNull)
{
return null;
}
else
{
DebugConsole.ThrowError("Text \"" + textTag + "\" not found.");
return textTag;
}
}
}
@@ -418,13 +449,16 @@ namespace Barotrauma
// And: replacement=formatter(value)
public static string GetServerMessage(string serverMessage)
{
if (!textPacks.ContainsKey(Language))
lock (mutex)
{
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!");
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!");
}
}
}
@@ -588,58 +622,64 @@ namespace Barotrauma
public static List<string> GetAll(string textTag)
{
if (!textPacks.ContainsKey(Language))
lock (mutex)
{
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!");
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<string> allText;
List<string> allText;
foreach (TextPack textPack in textPacks[Language])
{
allText = textPack.GetAll(textTag);
if (allText != null) return allText;
}
//if text was not found and we're using a language other than English, see if we can find an English version
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
if (Language != "English" && textPacks.ContainsKey("English"))
{
foreach (TextPack textPack in textPacks["English"])
foreach (TextPack textPack in textPacks[Language])
{
allText = textPack.GetAll(textTag);
if (allText != null) return allText;
}
}
return null;
//if text was not found and we're using a language other than English, see if we can find an English version
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
if (Language != "English" && textPacks.ContainsKey("English"))
{
foreach (TextPack textPack in textPacks["English"])
{
allText = textPack.GetAll(textTag);
if (allText != null) return allText;
}
}
return null;
}
}
public static List<KeyValuePair<string, string>> GetAllTagTextPairs()
{
if (!textPacks.ContainsKey(Language))
lock (mutex)
{
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!");
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;
}
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)
@@ -689,35 +729,41 @@ namespace Barotrauma
#if DEBUG
public static void CheckForDuplicates(string lang)
{
if (!textPacks.ContainsKey(lang))
lock (mutex)
{
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
return;
}
if (!textPacks.ContainsKey(lang))
{
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
return;
}
int packIndex = 0;
foreach (TextPack textPack in textPacks[lang])
{
textPack.CheckForDuplicates(packIndex);
packIndex++;
int packIndex = 0;
foreach (TextPack textPack in textPacks[lang])
{
textPack.CheckForDuplicates(packIndex);
packIndex++;
}
}
}
public static void WriteToCSV()
{
string lang = "English";
if (!textPacks.ContainsKey(lang))
lock (mutex)
{
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
return;
}
string lang = "English";
int packIndex = 0;
foreach (TextPack textPack in textPacks[lang])
{
textPack.WriteToCSV(packIndex);
packIndex++;
if (!textPacks.ContainsKey(lang))
{
DebugConsole.ThrowError("No text packs available for the selected language (" + lang + ")!");
return;
}
int packIndex = 0;
foreach (TextPack textPack in textPacks[lang])
{
textPack.WriteToCSV(packIndex);
packIndex++;
}
}
}
#endif
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma
{
public static class TaskPool
{
private struct TaskAction
{
public Task Task;
public Action<Task, object> OnCompletion;
public object UserData;
}
private static List<TaskAction> taskActions = new List<TaskAction>();
private static void AddInternal(Task task, Action<Task, object> onCompletion, object userdata)
{
lock (taskActions)
{
taskActions.Add(new TaskAction() { Task = task, OnCompletion = onCompletion, UserData = userdata });
}
}
public static void Add(Task task, Action<Task> onCompletion)
{
AddInternal(task, (Task t, object obj) => { onCompletion(t); }, null);
}
public static void Add<U>(Task task, U userdata, Action<Task, U> onCompletion) where U : class
{
AddInternal(task, (Task t, object obj) => { onCompletion(t, (U)obj); }, userdata);
}
public static void Add<T>(Task<T> task, Action<Task<T>> onCompletion)
{
AddInternal(task, (Task t, object obj) => { onCompletion((Task<T>)t); }, null);
}
public static void Add<T,U>(Task<T> task, U userdata, Action<Task<T>, U> onCompletion) where U : class
{
AddInternal(task, (Task t, object obj) => { onCompletion((Task<T>)t, (U)obj); }, userdata);
}
public static void Update()
{
lock (taskActions)
{
for (int i = 0; i < taskActions.Count; i++)
{
if (taskActions[i].Task.IsCompleted)
{
taskActions[i].OnCompletion?.Invoke(taskActions[i].Task, taskActions[i].UserData);
taskActions.RemoveAt(i);
i--;
}
}
}
}
public static void PrintTaskExceptions(Task task, string msg)
{
DebugConsole.ThrowError(msg);
foreach (Exception e in task.Exception.InnerExceptions)
{
DebugConsole.ThrowError(e.Message + "\n" + e.StackTrace);
}
}
}
}
@@ -437,5 +437,90 @@ namespace Barotrauma
IEnumerable<string> filtered = splitted.SkipWhile(part => part != currentFolder).Skip(1);
return string.Join("/", filtered);
}
public static string EscapeCharacters(string str)
{
return str.Replace("\\", "\\\\").Replace("\"", "\\\"");
}
public static string UnescapeCharacters(string str)
{
string retVal = "";
for (int i=0;i<str.Length;i++)
{
if (str[i] != '\\')
{
retVal += str[i];
}
else if (i+1<str.Length)
{
if (str[i+1] == '\\')
{
retVal += "\\";
}
else if (str[i+1] == '\"')
{
retVal += "\"";
}
i++;
}
}
return retVal;
}
public static string ParseQuotedArgument(string[] arguments, int startIndex, out int endIndex)
{
#if WINDOWS
endIndex = startIndex + 1;
return arguments[startIndex];
#else
string retVal = "";
int currIndex = startIndex;
bool escaped = false;
if (arguments[startIndex][0] != '\"')
{
endIndex = startIndex+1;
return UnescapeCharacters(arguments[startIndex]);
}
while (currIndex < arguments.Length)
{
for (int i=currIndex == startIndex ? 1 : 0;i<arguments[currIndex].Length;i++)
{
if (!escaped)
{
if (arguments[currIndex][i] == '\\')
{
escaped = true;
}
else if (arguments[currIndex][i] == '\"')
{
endIndex = currIndex+1;
return UnescapeCharacters(retVal);
}
}
else
{
escaped = false;
}
retVal += arguments[currIndex][i];
}
retVal += " ";
currIndex++;
}
endIndex = arguments.Length;
return retVal;
#endif
}
public static string[] MergeArguments(string[] arguments)
{
List<string> mergedArgs = new List<string>();
for (int i=0;i<arguments.Length;)
{
mergedArgs.Add(ParseQuotedArgument(arguments, i, out i));
}
return mergedArgs.ToArray();
}
}
}