(3dc4135ce) v0.9.5.1
This commit is contained in:
@@ -20,9 +20,9 @@ namespace Barotrauma.Items.Components
|
||||
private bool isOpen;
|
||||
|
||||
private float openState;
|
||||
private Sprite doorSprite, weldedSprite, brokenSprite;
|
||||
private bool scaleBrokenSprite, fadeBrokenSprite;
|
||||
private bool autoOrientGap;
|
||||
private readonly Sprite doorSprite, weldedSprite, brokenSprite;
|
||||
private readonly bool scaleBrokenSprite, fadeBrokenSprite;
|
||||
private readonly bool autoOrientGap;
|
||||
|
||||
private bool isStuck;
|
||||
public bool IsStuck => isStuck;
|
||||
@@ -221,8 +221,8 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
|
||||
private string accessDeniedTxt = TextManager.Get("AccessDenied");
|
||||
private string cannotOpenText = TextManager.Get("DoorMsgCannotOpen");
|
||||
private readonly string accessDeniedTxt = TextManager.Get("AccessDenied");
|
||||
private readonly string cannotOpenText = TextManager.Get("DoorMsgCannotOpen");
|
||||
private bool hasValidIdCard;
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, string msg = null)
|
||||
{
|
||||
@@ -272,14 +272,15 @@ namespace Barotrauma.Items.Components
|
||||
ToggleState(ActionType.OnUse);
|
||||
PickingTime = originalPickingTime;
|
||||
}
|
||||
else if (hasRequiredItems)
|
||||
{
|
||||
#if CLIENT
|
||||
else if (hasRequiredItems && character != null && character == Character.Controlled)
|
||||
{
|
||||
GUI.AddMessage(accessDeniedTxt, Color.Red);
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return item.Condition <= RepairThreshold;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
@@ -340,6 +341,13 @@ namespace Barotrauma.Items.Components
|
||||
if (!Impassable)
|
||||
{
|
||||
Body.FarseerBody.IsSensor = false;
|
||||
var ce = Body.FarseerBody.ContactList;
|
||||
while (ce != null && ce.Contact != null)
|
||||
{
|
||||
ce.Contact.Enabled = false;
|
||||
ce = ce.Next;
|
||||
}
|
||||
PushCharactersAway();
|
||||
}
|
||||
#if CLIENT
|
||||
UpdateConvexHulls();
|
||||
@@ -354,6 +362,12 @@ namespace Barotrauma.Items.Components
|
||||
if (!Impassable)
|
||||
{
|
||||
Body.FarseerBody.IsSensor = true;
|
||||
var ce = Body.FarseerBody.ContactList;
|
||||
while (ce != null && ce.Contact != null)
|
||||
{
|
||||
ce.Contact.Enabled = false;
|
||||
ce = ce.Next;
|
||||
}
|
||||
}
|
||||
linkedGap.Open = 1.0f;
|
||||
IsOpen = false;
|
||||
@@ -413,15 +427,14 @@ namespace Barotrauma.Items.Components
|
||||
//otherwise the gap will be removed twice and cause console warnings
|
||||
if (!Submarine.Unloading)
|
||||
{
|
||||
if (linkedGap != null) linkedGap.Remove();
|
||||
linkedGap?.Remove();
|
||||
}
|
||||
|
||||
doorSprite.Remove();
|
||||
if (weldedSprite != null) weldedSprite.Remove();
|
||||
doorSprite?.Remove();
|
||||
weldedSprite?.Remove();
|
||||
|
||||
#if CLIENT
|
||||
if (convexHull != null) convexHull.Remove();
|
||||
if (convexHull2 != null) convexHull2.Remove();
|
||||
convexHull?.Remove();
|
||||
convexHull2?.Remove();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -474,7 +487,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool PushBodyOutOfDoorway(Character c, PhysicsBody body, int dir, Vector2 doorRectSimPos, Vector2 doorRectSimSize)
|
||||
{
|
||||
float diff = 0.0f;
|
||||
if (!MathUtils.IsValid(body.SimPosition))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to push a limb out of a doorway - position of the body (character \"" + c.Name + "\") is not valid (" + body.SimPosition + ")");
|
||||
@@ -484,7 +496,8 @@ namespace Barotrauma.Items.Components
|
||||
" Remoteplayer: " + c.IsRemotePlayer);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
float diff;
|
||||
if (IsHorizontal)
|
||||
{
|
||||
if (body.SimPosition.X < doorRectSimPos.X || body.SimPosition.X > doorRectSimPos.X + doorRectSimSize.X) { return false; }
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (charging)
|
||||
{
|
||||
if (voltage > minVoltage || powerConsumption <= 0.0f)
|
||||
if (Voltage > MinVoltage)
|
||||
{
|
||||
Discharge();
|
||||
}
|
||||
@@ -142,8 +142,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
@@ -455,6 +453,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
list.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,9 +242,19 @@ namespace Barotrauma.Items.Components
|
||||
User = null;
|
||||
}
|
||||
|
||||
//ignore collision if there's a wall between the user and the weapon to prevent hitting through walls
|
||||
if (Submarine.PickBody(User.AnimController.AimSourceSimPos,
|
||||
item.SimPosition,
|
||||
collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking,
|
||||
allowInsideFixture: true) != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Character targetCharacter = null;
|
||||
Limb targetLimb = null;
|
||||
Structure targetStructure = null;
|
||||
Item targetItem = null;
|
||||
|
||||
attack?.SetUser(User);
|
||||
|
||||
@@ -292,6 +302,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
hitTargets.Add(targetStructure);
|
||||
}
|
||||
else if (f2.Body.UserData is Item)
|
||||
{
|
||||
targetItem = (Item)f2.Body.UserData;
|
||||
if (AllowHitMultiple)
|
||||
{
|
||||
if (hitTargets.Contains(targetItem)) { return true; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hitTargets.Any(t => t is Item)) { return true; }
|
||||
}
|
||||
hitTargets.Add(targetItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@@ -313,6 +336,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
attack.DoDamage(User, targetStructure, item.WorldPosition, 1.0f);
|
||||
}
|
||||
else if (targetItem != null && targetItem.Prefab.DamagedByMeleeWeapons)
|
||||
{
|
||||
attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -127,6 +127,7 @@ namespace Barotrauma.Items.Components
|
||||
if (activeTimer <= 0.0f) IsActive = false;
|
||||
}
|
||||
|
||||
private List<Body> ignoredBodies = new List<Body>();
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character.Removed) return false;
|
||||
@@ -185,7 +186,7 @@ namespace Barotrauma.Items.Components
|
||||
(float)Math.Cos(angle),
|
||||
(float)Math.Sin(angle)) * Range * item.body.Dir);
|
||||
|
||||
List<Body> ignoredBodies = new List<Body>();
|
||||
ignoredBodies.Clear();
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess) continue;
|
||||
@@ -438,27 +439,48 @@ namespace Barotrauma.Items.Components
|
||||
private float sinTime;
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (!(objective.OperateTarget is Gap leak)) return true;
|
||||
|
||||
Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
|
||||
float dist = fromItemToLeak.Length();
|
||||
if (!(objective.OperateTarget is Gap leak)) { return true; }
|
||||
if (leak.Submarine == null) { return true; }
|
||||
Vector2 fromCharacterToLeak = leak.WorldPosition - character.WorldPosition;
|
||||
float dist = fromCharacterToLeak.Length();
|
||||
float reach = Range + ConvertUnits.ToDisplayUnits(((HumanoidAnimController)character.AnimController).ArmLength);
|
||||
|
||||
//too far away -> consider this done and hope the AI is smart enough to move closer
|
||||
if (dist > Range * 3.0f) { return true; }
|
||||
|
||||
// TODO: use the collider size?
|
||||
if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
|
||||
Math.Abs(fromItemToLeak.X) < 100.0f && fromItemToLeak.Y < 0.0f && fromItemToLeak.Y > -150.0f)
|
||||
{
|
||||
((HumanoidAnimController)character.AnimController).Crouching = true;
|
||||
}
|
||||
|
||||
if (dist > reach * 2) { return true; }
|
||||
character.AIController.SteeringManager.Reset();
|
||||
//steer closer if almost in range
|
||||
if (dist > Range)
|
||||
if (dist > reach)
|
||||
{
|
||||
Vector2 standPos = new Vector2(Math.Sign(-fromItemToLeak.X), Math.Sign(-fromItemToLeak.Y)) / 2;
|
||||
if (!character.AnimController.InWater)
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
{
|
||||
// Swimming inside the sub
|
||||
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Swimming outside the sub
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: use the collider size?
|
||||
if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
|
||||
Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f)
|
||||
{
|
||||
((HumanoidAnimController)character.AnimController).Crouching = true;
|
||||
}
|
||||
Vector2 standPos = new Vector2(Math.Sign(-fromCharacterToLeak.X), Math.Sign(-fromCharacterToLeak.Y)) / 2;
|
||||
if (leak.IsHorizontal)
|
||||
{
|
||||
standPos.X *= 2;
|
||||
@@ -468,43 +490,40 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
standPos.X = 0;
|
||||
}
|
||||
}
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
{
|
||||
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dist < Range / 2)
|
||||
if (dist < reach / 2)
|
||||
{
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
|
||||
}
|
||||
else if (dist <= Range)
|
||||
else if (dist <= reach)
|
||||
{
|
||||
// In range
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
character.CursorPosition = leak.Position;
|
||||
character.CursorPosition += VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
var torso = character.AnimController.GetLimb(LimbType.Torso);
|
||||
// Turn facing the target when not moving (handled in the animcontroller if not moving)
|
||||
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
|
||||
Vector2 diff = (mousePos - torso.SimPosition) * character.AnimController.Dir;
|
||||
float newRotation = MathUtils.VectorToAngle(diff);
|
||||
character.AnimController.Collider.SmoothRotate(newRotation, 5.0f);
|
||||
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4)
|
||||
{
|
||||
// Swim past
|
||||
Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX;
|
||||
moveDir *= character.AnimController.Dir;
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sinTime += deltaTime;
|
||||
character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
|
||||
if (item.RequireAimToUse)
|
||||
{
|
||||
bool isOperatingButtons = false;
|
||||
@@ -520,13 +539,33 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
bool isAiming = false;
|
||||
var holdable = item.GetComponent<Holdable>();
|
||||
if (holdable != null)
|
||||
{
|
||||
isAiming = holdable.ControlPose;
|
||||
}
|
||||
sinTime = isAiming ? sinTime + deltaTime * 5 : 0;
|
||||
}
|
||||
// Press the trigger only when the tool is approximately facing the target.
|
||||
Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
|
||||
var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);
|
||||
if (angle < MathHelper.PiOver4)
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Use(deltaTime, character);
|
||||
// Check that we don't hit any friendlies
|
||||
if (Submarine.PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit =>
|
||||
{
|
||||
if (hit.UserData is Character c)
|
||||
{
|
||||
if (c == character) { return false; }
|
||||
return HumanAIController.IsFriendly(character, c);
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Use(deltaTime, character);
|
||||
}
|
||||
}
|
||||
|
||||
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
|
||||
@@ -534,7 +573,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (leakFixed && leak.FlowTargetHull != null)
|
||||
{
|
||||
sinTime = 0;
|
||||
if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
|
||||
{
|
||||
|
||||
|
||||
@@ -44,8 +44,9 @@ namespace Barotrauma.Items.Components
|
||||
public bool WasUsed;
|
||||
|
||||
public readonly Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
|
||||
|
||||
|
||||
public Dictionary<RelatedItem.RelationType, List<RelatedItem>> requiredItems;
|
||||
public readonly List<RelatedItem> DisabledRequiredItems = new List<RelatedItem>();
|
||||
|
||||
public List<Skill> requiredSkills;
|
||||
|
||||
@@ -271,19 +272,7 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
case "requireditem":
|
||||
case "requireditems":
|
||||
RelatedItem ri = RelatedItem.Load(subElement, item.Name);
|
||||
if (ri != null)
|
||||
{
|
||||
if (!requiredItems.ContainsKey(ri.Type))
|
||||
{
|
||||
requiredItems.Add(ri.Type, new List<RelatedItem>());
|
||||
}
|
||||
requiredItems[ri.Type].Add(ri);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - component " + GetType().ToString() + " requires an item with no identifiers.");
|
||||
}
|
||||
SetRequiredItems(subElement);
|
||||
break;
|
||||
case "requiredskill":
|
||||
case "requiredskills":
|
||||
@@ -323,6 +312,34 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRequiredItems(XElement element)
|
||||
{
|
||||
bool returnEmpty = false;
|
||||
#if CLIENT
|
||||
returnEmpty = Screen.Selected == GameMain.SubEditorScreen;
|
||||
#endif
|
||||
RelatedItem ri = RelatedItem.Load(element, returnEmpty, item.Name);
|
||||
if (ri != null)
|
||||
{
|
||||
if (ri.Identifiers.Length == 0)
|
||||
{
|
||||
DisabledRequiredItems.Add(ri);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!requiredItems.ContainsKey(ri.Type))
|
||||
{
|
||||
requiredItems.Add(ri.Type, new List<RelatedItem>());
|
||||
}
|
||||
requiredItems[ri.Type].Add(ri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - component " + GetType().ToString() + " requires an item with no identifiers.");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Move(Vector2 amount) { }
|
||||
|
||||
/// <summary>a Character has picked the item</summary>
|
||||
@@ -762,6 +779,12 @@ namespace Barotrauma.Items.Components
|
||||
componentElement.Add(newElement);
|
||||
}
|
||||
}
|
||||
foreach (RelatedItem ri in DisabledRequiredItems)
|
||||
{
|
||||
XElement newElement = new XElement("requireditem");
|
||||
ri.Save(newElement);
|
||||
componentElement.Add(newElement);
|
||||
}
|
||||
|
||||
|
||||
SerializableProperty.SerializeProperties(this, componentElement);
|
||||
@@ -783,12 +806,16 @@ namespace Barotrauma.Items.Components
|
||||
var prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(requiredItems);
|
||||
requiredItems.Clear();
|
||||
|
||||
bool returnEmptyRequirements = false;
|
||||
#if CLIENT
|
||||
returnEmptyRequirements = Screen.Selected == GameMain.SubEditorScreen;
|
||||
#endif
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "requireditem":
|
||||
RelatedItem newRequiredItem = RelatedItem.Load(subElement, item.Name);
|
||||
RelatedItem newRequiredItem = RelatedItem.Load(subElement, returnEmptyRequirements, item.Name);
|
||||
if (newRequiredItem == null) continue;
|
||||
|
||||
var prevRequiredItem = prevRequiredItems.ContainsKey(newRequiredItem.Type) ?
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -54,20 +55,44 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(5, false, description: "How many inventory slots the inventory has per row.")]
|
||||
public int SlotsPerRow { get; set; }
|
||||
|
||||
public List<RelatedItem> ContainableItems { get; private set; }
|
||||
private HashSet<string> containableRestrictions = new HashSet<string>();
|
||||
[Editable, Serialize("", true, description: "Define items (by identifiers or tags) that bots should place inside this container. If empty, no restrictions are applied.")]
|
||||
public string ContainableRestrictions
|
||||
{
|
||||
get { return string.Join(",", containableRestrictions); }
|
||||
set
|
||||
{
|
||||
StringFormatter.ParseCommaSeparatedStringToCollection(value, containableRestrictions);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)
|
||||
{
|
||||
isRestrictionsDefined = containableRestrictions.Any();
|
||||
if (!isRestrictionsDefined) { return true; }
|
||||
return identifiersOrTags.Any(id => containableRestrictions.Any(r => r == id));
|
||||
}
|
||||
|
||||
public bool ShouldBeContained(Item item, out bool isRestrictionsDefined)
|
||||
{
|
||||
isRestrictionsDefined = containableRestrictions.Any();
|
||||
if (!isRestrictionsDefined) { return true; }
|
||||
return containableRestrictions.Any(id => item.Prefab.Identifier == id || item.HasTag(id));
|
||||
}
|
||||
|
||||
public List<RelatedItem> ContainableItems { get; private set; } = new List<RelatedItem>();
|
||||
|
||||
public ItemContainer(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
Inventory = new ItemInventory(item, this, capacity, SlotsPerRow);
|
||||
ContainableItems = new List<RelatedItem>();
|
||||
Inventory = new ItemInventory(item, this, capacity, SlotsPerRow);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "containable":
|
||||
RelatedItem containable = RelatedItem.Load(subElement, item.Name);
|
||||
RelatedItem containable = RelatedItem.Load(subElement, returnEmpty: false, parentDebugName: item.Name);
|
||||
if (containable == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - containable with no identifiers.");
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
var containers = item.GetComponents<ItemContainer>().ToList();
|
||||
if (containers.Count < 2)
|
||||
{
|
||||
@@ -59,7 +60,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
hasPower = voltage >= minVoltage;
|
||||
hasPower = Voltage >= MinVoltage;
|
||||
if (!hasPower) { return; }
|
||||
|
||||
var repairable = item.GetComponent<Repairable>();
|
||||
@@ -70,10 +71,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (powerConsumption == 0.0f) { voltage = 1.0f; }
|
||||
|
||||
progressTimer += deltaTime * voltage;
|
||||
Voltage -= deltaTime * 10.0f;
|
||||
if (powerConsumption <= 0.0f) { Voltage = 1.0f; }
|
||||
progressTimer += deltaTime * Voltage;
|
||||
|
||||
var targetItem = inputContainer.Inventory.Items.LastOrDefault(i => i != null);
|
||||
if (targetItem == null) { return; }
|
||||
@@ -99,7 +98,7 @@ namespace Barotrauma.Items.Components
|
||||
float condition = deconstructProduct.CopyCondition ?
|
||||
percentageHealth * itemPrefab.Health :
|
||||
itemPrefab.Health * deconstructProduct.OutCondition;
|
||||
|
||||
|
||||
//container full, drop the items outside the deconstructor
|
||||
if (emptySlots <= 0)
|
||||
{
|
||||
@@ -111,7 +110,7 @@ namespace Barotrauma.Items.Components
|
||||
emptySlots--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (targetItem.Prefab.DeconstructItems.Any())
|
||||
@@ -149,8 +148,6 @@ namespace Barotrauma.Items.Components
|
||||
progressState = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
voltage -= deltaTime * 10.0f;
|
||||
}
|
||||
|
||||
private void PutItemsToLinkedContainer()
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public float CurrentVolume
|
||||
{
|
||||
get { return Math.Abs((force / 100.0f) * (minVoltage <= 0.0f ? 1.0f : Math.Min(prevVoltage / minVoltage, 1.0f))); }
|
||||
get { return Math.Abs((force / 100.0f) * (MinVoltage <= 0.0f ? 1.0f : Math.Min(prevVoltage / MinVoltage, 1.0f))); }
|
||||
}
|
||||
|
||||
public Engine(Item item, XElement element)
|
||||
@@ -83,15 +83,15 @@ namespace Barotrauma.Items.Components
|
||||
//pumps consume more power when in a bad condition
|
||||
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
if (powerConsumption == 0.0f) voltage = 1.0f;
|
||||
if (powerConsumption == 0.0f) { Voltage = 1.0f; }
|
||||
|
||||
prevVoltage = voltage;
|
||||
hasPower = voltage > minVoltage;
|
||||
prevVoltage = Voltage;
|
||||
hasPower = Voltage > MinVoltage;
|
||||
|
||||
Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f);
|
||||
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, 0.1f);
|
||||
if (Math.Abs(Force) > 1.0f)
|
||||
{
|
||||
Vector2 currForce = new Vector2((force / 10.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f);
|
||||
Vector2 currForce = new Vector2((force / 10.0f) * maxForce * Math.Min(Voltage / MinVoltage, 1.0f), 0.0f);
|
||||
//less effective when in a bad condition
|
||||
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
@@ -119,8 +119,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
private void UpdatePropellerDamage(float deltaTime)
|
||||
@@ -172,5 +170,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
Vector2 prevPropellerPos = PropellerPos;
|
||||
//undo flipping before saving
|
||||
if (item.FlippedX) { PropellerPos = new Vector2(-PropellerPos.X, PropellerPos.Y); }
|
||||
if (item.FlippedY) { PropellerPos = new Vector2(PropellerPos.X, -PropellerPos.Y); }
|
||||
XElement element = base.Save(parentElement);
|
||||
PropellerPos = prevPropellerPos;
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
var containers = item.GetComponents<ItemContainer>().ToList();
|
||||
if (containers.Count < 2)
|
||||
{
|
||||
@@ -199,7 +200,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
progressState = fabricatedItem == null ? 0.0f : (requiredTime - timeUntilReady) / requiredTime;
|
||||
|
||||
hasPower = voltage >= minVoltage;
|
||||
hasPower = Voltage >= MinVoltage;
|
||||
if (!hasPower) { return; }
|
||||
|
||||
var repairable = item.GetComponent<Repairable>();
|
||||
@@ -210,10 +211,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (powerConsumption <= 0) { voltage = 1.0f; }
|
||||
if (powerConsumption <= 0) { Voltage = 1.0f; }
|
||||
|
||||
timeUntilReady -= deltaTime * voltage;
|
||||
voltage -= deltaTime * 10.0f;
|
||||
timeUntilReady -= deltaTime * Voltage;
|
||||
|
||||
if (timeUntilReady > 0.0f) { return; }
|
||||
|
||||
|
||||
@@ -75,13 +75,11 @@ namespace Barotrauma.Items.Components
|
||||
currPowerConsumption = powerConsumption;
|
||||
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
hasPower = voltage > minVoltage;
|
||||
hasPower = Voltage > MinVoltage;
|
||||
if (hasPower)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
|
||||
@@ -46,12 +46,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (powerConsumption <= 0.0f)
|
||||
{
|
||||
voltage = 1.0f;
|
||||
Voltage = 1.0f;
|
||||
}
|
||||
|
||||
if (item.CurrentHull == null) return;
|
||||
|
||||
if (voltage < minVoltage)
|
||||
if (Voltage < MinVoltage)
|
||||
{
|
||||
powerDownTimer += deltaTime;
|
||||
return;
|
||||
@@ -61,7 +61,7 @@ namespace Barotrauma.Items.Components
|
||||
powerDownTimer = 0.0f;
|
||||
}
|
||||
|
||||
CurrFlow = Math.Min(voltage, 1.0f) * generatedAmount * 100.0f;
|
||||
CurrFlow = Math.Min(Voltage, 1.0f) * generatedAmount * 100.0f;
|
||||
|
||||
//less effective when in bad condition
|
||||
float conditionMult = item.Condition / item.MaxCondition;
|
||||
@@ -71,8 +71,6 @@ namespace Barotrauma.Items.Components
|
||||
CurrFlow *= conditionMult * conditionMult;
|
||||
|
||||
UpdateVents(CurrFlow);
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components
|
||||
//pumps consume more power when in a bad condition
|
||||
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
if (voltage < minVoltage) { return; }
|
||||
if (Voltage < MinVoltage) { return; }
|
||||
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull == null) { return; }
|
||||
|
||||
float powerFactor = currPowerConsumption <= 0.0f ? 1.0f : voltage;
|
||||
float powerFactor = currPowerConsumption <= 0.0f ? 1.0f : Voltage;
|
||||
|
||||
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
|
||||
//less effective when in a bad condition
|
||||
@@ -94,8 +94,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.CurrentHull.WaterVolume += currFlow;
|
||||
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
@@ -124,7 +122,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempTarget))
|
||||
{
|
||||
targetLevel = MathHelper.Clamp((tempTarget + 100.0f) / 2.0f, 0.0f, 100.0f);
|
||||
targetLevel = MathHelper.Clamp(tempTarget + 50.0f, 0.0f, 100.0f);
|
||||
controlLockTimer = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -35,6 +36,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float maxPowerOutput;
|
||||
|
||||
private Queue<float> loadQueue = new Queue<float>();
|
||||
private float load;
|
||||
|
||||
private bool unsentChanges;
|
||||
@@ -165,7 +167,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float prevAvailableFuel;
|
||||
public float AvailableFuel { get; set; }
|
||||
|
||||
|
||||
private readonly string[] fuelTags = new string[1] { "reactorfuel" };
|
||||
|
||||
|
||||
public Reactor(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -267,8 +272,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateAutoTemp(2.0f, deltaTime);
|
||||
}
|
||||
|
||||
load = 0.0f;
|
||||
float currentLoad = 0.0f;
|
||||
List<Connection> connections = item.Connections;
|
||||
if (connections != null && connections.Count > 0)
|
||||
{
|
||||
@@ -284,13 +288,20 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//calculate how much external power there is in the grid
|
||||
//(power coming from somewhere else than this reactor, e.g. batteries)
|
||||
float externalPower = Math.Max(CurrPowerConsumption - pt.CurrPowerConsumption, 0);
|
||||
float externalPower = Math.Max(CurrPowerConsumption - pt.CurrPowerConsumption, 0) * 0.95f;
|
||||
//reduce the external power from the load to prevent overloading the grid
|
||||
load = Math.Max(load, pt.PowerLoad - externalPower);
|
||||
currentLoad = Math.Max(currentLoad, pt.PowerLoad - externalPower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadQueue.Enqueue(currentLoad);
|
||||
while (loadQueue.Count() > 60.0f)
|
||||
{
|
||||
load = loadQueue.Average();
|
||||
loadQueue.Dequeue();
|
||||
}
|
||||
|
||||
if (fissionRate > 0.0f)
|
||||
{
|
||||
foreach (Item item in item.ContainedItems)
|
||||
@@ -505,6 +516,19 @@ namespace Barotrauma.Items.Components
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
private int itemIndex;
|
||||
private List<Item> ignoredContainers = new List<Item>();
|
||||
private bool FindSuitableContainer(Character character, Func<Item, float> priority, out Item suitableContainer)
|
||||
{
|
||||
suitableContainer = null;
|
||||
if (character.FindItem(ref itemIndex, out Item targetContainer, ignoredItems: ignoredContainers, customPriorityFunction: priority))
|
||||
{
|
||||
suitableContainer = targetContainer;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
@@ -516,13 +540,56 @@ namespace Barotrauma.Items.Components
|
||||
//characters with insufficient skill levels don't refuel the reactor
|
||||
if (degreeOfSuccess > 0.2f)
|
||||
{
|
||||
//remove used-up fuel from the reactor
|
||||
var containedItems = item.ContainedItems;
|
||||
foreach (Item item in containedItems)
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
if (item != null && item.Condition <= 0.0f)
|
||||
var containedItems = item.ContainedItems;
|
||||
foreach (Item fuelRod in containedItems)
|
||||
{
|
||||
item.Drop(character);
|
||||
if (fuelRod != null && fuelRod.Condition <= 0.0f)
|
||||
{
|
||||
if (!FindSuitableContainer(character,
|
||||
i =>
|
||||
{
|
||||
var container = i.GetComponent<ItemContainer>();
|
||||
if (container == null) { return 0; }
|
||||
if (container.Inventory.IsFull()) { return 0; }
|
||||
if (container.ShouldBeContained(fuelRod, out bool isRestrictionsDefined))
|
||||
{
|
||||
if (isRestrictionsDefined)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fuelRod.Prefab.IsContainerPreferred(container, out bool isPreferencesDefined))
|
||||
{
|
||||
return isPreferencesDefined ? 2 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return isPreferencesDefined ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}, out Item targetContainer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var decontainObjective = new AIObjectiveDecontainItem(character, fuelRod, item.GetComponent<ItemContainer>(), objective.objectiveManager, targetContainer?.GetComponent<ItemContainer>());
|
||||
decontainObjective.Abandoned += () =>
|
||||
{
|
||||
itemIndex = 0;
|
||||
if (targetContainer != null)
|
||||
{
|
||||
ignoredContainers.Add(targetContainer);
|
||||
}
|
||||
};
|
||||
objective.AddSubObjectiveInQueue(decontainObjective);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,31 +602,33 @@ namespace Barotrauma.Items.Components
|
||||
//load more fuel if the current maximum output is only 50% of the current load
|
||||
if (NeedMoreFuel(minimumOutputRatio: 0.5f))
|
||||
{
|
||||
var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>(), objective.objectiveManager)
|
||||
{
|
||||
targetItemCount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1,
|
||||
GetItemPriority = (Item fuelItem) =>
|
||||
{
|
||||
if (fuelItem.ParentInventory?.Owner is Item)
|
||||
{
|
||||
//don't take fuel from other reactors
|
||||
if (((Item)fuelItem.ParentInventory.Owner).GetComponent<Reactor>() != null) return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
};
|
||||
objective.AddSubObjective(containFuelObjective);
|
||||
|
||||
character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
|
||||
|
||||
aiUpdateTimer = AIUpdateInterval;
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
var containFuelObjective = new AIObjectiveContainItem(character, fuelTags, item.GetComponent<ItemContainer>(), objective.objectiveManager)
|
||||
{
|
||||
targetItemCount = item.ContainedItems.Count(i => i != null && fuelTags.Any(t => i.Prefab.Identifier == t || i.HasTag(t))) + 1,
|
||||
GetItemPriority = (Item fuelItem) =>
|
||||
{
|
||||
if (fuelItem.ParentInventory?.Owner is Item)
|
||||
{
|
||||
//don't take fuel from other reactors
|
||||
if (((Item)fuelItem.ParentInventory.Owner).GetComponent<Reactor>() != null) return 0.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
};
|
||||
containFuelObjective.Abandoned += () => objective.Abandon = true;
|
||||
objective.AddSubObjective(containFuelObjective);
|
||||
character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (TooMuchFuel())
|
||||
{
|
||||
foreach (Item item in item.ContainedItems)
|
||||
{
|
||||
if (item != null && item.HasTag("reactorfuel"))
|
||||
if (item != null && fuelTags.Any(t => item.Prefab.Identifier == t || item.HasTag(t)))
|
||||
{
|
||||
if (!character.Inventory.TryPutItem(item, character, allowedSlots: item.AllowedSlots))
|
||||
{
|
||||
@@ -577,22 +646,28 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
LastUser = lastAIUser = character;
|
||||
|
||||
|
||||
bool prevAutoTemp = autoTemp;
|
||||
bool prevShutDown = shutDown;
|
||||
float prevFissionRate = targetFissionRate;
|
||||
float prevTurbineOutput = targetTurbineOutput;
|
||||
|
||||
switch (objective.Option.ToLowerInvariant())
|
||||
{
|
||||
case "powerup":
|
||||
shutDown = false;
|
||||
//characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
|
||||
if (degreeOfSuccess < 0.5f)
|
||||
if (objective.Override || !autoTemp)
|
||||
{
|
||||
if (!autoTemp) unsentChanges = true;
|
||||
AutoTemp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoTemp = false;
|
||||
unsentChanges = true;
|
||||
UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
|
||||
//characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
|
||||
if (degreeOfSuccess < 0.5f)
|
||||
{
|
||||
AutoTemp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoTemp = false;
|
||||
UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
onOffSwitch.BarScroll = 0.0f;
|
||||
@@ -604,11 +679,6 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
onOffSwitch.BarScroll = 1.0f;
|
||||
#endif
|
||||
if (AutoTemp || !shutDown || targetFissionRate > 0.0f || targetTurbineOutput > 0.0f)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
AutoTemp = false;
|
||||
shutDown = true;
|
||||
targetFissionRate = 0.0f;
|
||||
@@ -616,6 +686,14 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
|
||||
if (autoTemp != prevAutoTemp ||
|
||||
prevShutDown != shutDown ||
|
||||
Math.Abs(prevFissionRate - targetFissionRate) > 1.0f ||
|
||||
Math.Abs(prevTurbineOutput - targetTurbineOutput) > 1.0f)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
aiUpdateTimer = AIUpdateInterval;
|
||||
|
||||
return false;
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (currentMode == Mode.Active)
|
||||
{
|
||||
if ((voltage >= minVoltage || powerConsumption <= 0.0f) &&
|
||||
if ((Voltage >= MinVoltage) &&
|
||||
(!UseTransducers || connectedTransducers.Count > 0))
|
||||
{
|
||||
if (currentPingIndex != -1)
|
||||
@@ -201,7 +201,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.AiTarget.SectorDegrees = 360.0f;
|
||||
}
|
||||
currentPingIndex = -1;
|
||||
aiPingCheckPending = false;
|
||||
}
|
||||
}
|
||||
@@ -235,6 +234,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
sonarBlip?.Remove();
|
||||
pingCircle?.Remove();
|
||||
directionalPingCircle?.Remove();
|
||||
@@ -247,6 +247,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (currentMode == Mode.Passive || !aiPingCheckPending) return false;
|
||||
|
||||
// TODO: Don't create new collections here
|
||||
Dictionary<string, List<Character>> targetGroups = new Dictionary<string, List<Character>>();
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
if (voltage >= minVoltage || PowerConsumption <= 0.0f)
|
||||
if (Voltage >= MinVoltage)
|
||||
{
|
||||
sendSignalTimer += deltaTime;
|
||||
if (sendSignalTimer > SendSignalInterval)
|
||||
@@ -29,8 +29,6 @@ namespace Barotrauma.Items.Components
|
||||
sendSignalTimer = SendSignalInterval;
|
||||
}
|
||||
}
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@ namespace Barotrauma.Items.Components
|
||||
private const float AutopilotRayCastInterval = 0.5f;
|
||||
private const float RecalculatePathInterval = 5.0f;
|
||||
|
||||
private const float AutopilotMinDistToPathNode = 30.0f;
|
||||
|
||||
private const float AutoPilotSteeringLerp = 0.1f;
|
||||
|
||||
private const float AutoPilotMaxSpeed = 0.5f;
|
||||
private const float AIPilotMaxSpeed = 1.0f;
|
||||
|
||||
private Vector2 currVelocity;
|
||||
private Vector2 targetVelocity;
|
||||
|
||||
@@ -162,6 +169,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
sonar = item.GetComponent<Sonar>();
|
||||
}
|
||||
|
||||
@@ -209,7 +217,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
|
||||
if (voltage < minVoltage && currPowerConsumption > 0.0f) { return; }
|
||||
if (Voltage < MinVoltage) { return; }
|
||||
|
||||
if (user != null && user.Removed)
|
||||
{
|
||||
@@ -221,6 +229,12 @@ namespace Barotrauma.Items.Components
|
||||
if (autoPilot)
|
||||
{
|
||||
UpdateAutoPilot(deltaTime);
|
||||
float userSkill = 0.0f;
|
||||
if (user != null && (user.SelectedConstruction == item || item.linkedTo.Contains(user.SelectedConstruction)))
|
||||
{
|
||||
userSkill = user.GetSkillLevel("helm") / 100.0f;
|
||||
}
|
||||
targetVelocity = targetVelocity.ClampLength(MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -253,8 +267,6 @@ namespace Barotrauma.Items.Components
|
||||
targetLevel += (neutralBallastLevel - 0.5f) * 100.0f;
|
||||
|
||||
item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_y_out", null);
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
private void UpdateAutoPilot(float deltaTime)
|
||||
@@ -262,7 +274,8 @@ namespace Barotrauma.Items.Components
|
||||
if (controlledSub == null) return;
|
||||
if (posToMaintain != null)
|
||||
{
|
||||
SteerTowardsPosition((Vector2)posToMaintain);
|
||||
Vector2 steeringVel = GetSteeringVelocity((Vector2)posToMaintain);
|
||||
TargetVelocity = Vector2.Lerp(TargetVelocity, steeringVel, AutoPilotSteeringLerp);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -284,7 +297,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//if the node is close enough, check if it's visible
|
||||
float lengthSqr = diff.LengthSquared();
|
||||
if (lengthSqr > 0.001f && lengthSqr < 500.0f)
|
||||
if (lengthSqr > 0.001f && lengthSqr < AutopilotMinDistToPathNode * AutopilotMinDistToPathNode)
|
||||
{
|
||||
diff = Vector2.Normalize(diff);
|
||||
|
||||
@@ -298,11 +311,11 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 cornerPos =
|
||||
new Vector2(controlledSub.Borders.Width * x, controlledSub.Borders.Height * y) / 2.0f;
|
||||
|
||||
cornerPos = ConvertUnits.ToSimUnits(cornerPos * 1.2f + controlledSub.WorldPosition);
|
||||
cornerPos = ConvertUnits.ToSimUnits(cornerPos * 1.1f + controlledSub.WorldPosition);
|
||||
|
||||
float dist = Vector2.Distance(cornerPos, steeringPath.NextNode.SimPosition);
|
||||
|
||||
if (Submarine.PickBody(cornerPos, cornerPos + diff * dist, null, Physics.CollisionLevel) == null) continue;
|
||||
if (Submarine.PickBody(cornerPos, cornerPos + diff * dist, null, Physics.CollisionLevel) == null) { continue; }
|
||||
|
||||
nextVisible = false;
|
||||
x = 2;
|
||||
@@ -313,19 +326,18 @@ namespace Barotrauma.Items.Components
|
||||
if (nextVisible) steeringPath.SkipToNextNode();
|
||||
}
|
||||
|
||||
|
||||
|
||||
autopilotRayCastTimer = AutopilotRayCastInterval;
|
||||
}
|
||||
|
||||
Vector2 newVelocity = Vector2.Zero;
|
||||
if (steeringPath.CurrentNode != null)
|
||||
{
|
||||
SteerTowardsPosition(steeringPath.CurrentNode.WorldPosition);
|
||||
newVelocity = GetSteeringVelocity(steeringPath.CurrentNode.WorldPosition);
|
||||
}
|
||||
|
||||
Vector2 avoidDist = new Vector2(
|
||||
Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.X), controlledSub.Borders.Width * 1.5f),
|
||||
Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.Y), controlledSub.Borders.Height * 1.5f));
|
||||
Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.X), controlledSub.Borders.Width * 0.75f),
|
||||
Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.Y), controlledSub.Borders.Height * 0.75f));
|
||||
|
||||
float avoidRadius = avoidDist.Length();
|
||||
|
||||
@@ -356,22 +368,22 @@ namespace Barotrauma.Items.Components
|
||||
0.0f : Vector2.Dot(controlledSub.Velocity, -normalizedDiff);
|
||||
|
||||
//not heading towards the wall -> ignore
|
||||
if (dot < 0.5)
|
||||
if (dot < 1.0)
|
||||
{
|
||||
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, Vector2.Zero, cell.Translation));
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 change = (normalizedDiff * Math.Max((avoidRadius - diff.Length()), 0.0f)) / avoidRadius;
|
||||
newAvoidStrength += change * dot;
|
||||
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, change * dot, cell.Translation));
|
||||
Vector2 change = (normalizedDiff * Math.Max((avoidRadius - diff.Length()), 0.0f)) / avoidRadius;
|
||||
if (change.LengthSquared() < 0.001f) { continue; }
|
||||
newAvoidStrength += change * (dot - 1.0f);
|
||||
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot - 1.0f, change * (dot - 1.0f), cell.Translation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avoidStrength = Vector2.Lerp(avoidStrength, newAvoidStrength, deltaTime * 10.0f);
|
||||
|
||||
targetVelocity += avoidStrength * 100.0f;
|
||||
TargetVelocity = Vector2.Lerp(TargetVelocity, newVelocity + avoidStrength * 100.0f, AutoPilotSteeringLerp);
|
||||
|
||||
//steer away from other subs
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
@@ -447,21 +459,21 @@ namespace Barotrauma.Items.Components
|
||||
UpdatePath();
|
||||
}
|
||||
}
|
||||
private void SteerTowardsPosition(Vector2 worldPosition)
|
||||
private Vector2 GetSteeringVelocity(Vector2 worldPosition)
|
||||
{
|
||||
float prediction = 10.0f;
|
||||
float prediction = 2.0f;
|
||||
|
||||
Vector2 futurePosition = ConvertUnits.ToDisplayUnits(controlledSub.Velocity) * prediction;
|
||||
Vector2 targetSpeed = ((worldPosition - controlledSub.WorldPosition) - futurePosition);
|
||||
|
||||
if (targetSpeed.Length() > 500.0f)
|
||||
if (targetSpeed.LengthSquared() > 500.0f * 500.0f)
|
||||
{
|
||||
targetSpeed = Vector2.Normalize(targetSpeed);
|
||||
TargetVelocity = targetSpeed * 100.0f;
|
||||
|
||||
return Vector2.Normalize(targetSpeed) * 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
TargetVelocity = targetSpeed / 5.0f;
|
||||
return targetSpeed / 5.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,43 +483,53 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
|
||||
}
|
||||
|
||||
user = character;
|
||||
|
||||
if (!AutoPilot)
|
||||
{
|
||||
unsentChanges = true;
|
||||
AutoPilot = true;
|
||||
}
|
||||
switch (objective.Option.ToLowerInvariant())
|
||||
{
|
||||
case "maintainposition":
|
||||
if (!posToMaintain.HasValue)
|
||||
if (objective.Override)
|
||||
{
|
||||
unsentChanges = true;
|
||||
posToMaintain = controlledSub != null ?
|
||||
controlledSub.WorldPosition :
|
||||
item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
|
||||
if (!MaintainPos)
|
||||
{
|
||||
unsentChanges = true;
|
||||
MaintainPos = true;
|
||||
}
|
||||
if (!posToMaintain.HasValue)
|
||||
{
|
||||
unsentChanges = true;
|
||||
posToMaintain = controlledSub != null ?
|
||||
controlledSub.WorldPosition :
|
||||
item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
|
||||
}
|
||||
}
|
||||
|
||||
if (!AutoPilot || !MaintainPos) unsentChanges = true;
|
||||
|
||||
AutoPilot = true;
|
||||
MaintainPos = true;
|
||||
break;
|
||||
case "navigateback":
|
||||
if (!AutoPilot || MaintainPos || LevelEndSelected || !LevelStartSelected)
|
||||
if (objective.Override)
|
||||
{
|
||||
unsentChanges = true;
|
||||
if (MaintainPos || LevelEndSelected || !LevelStartSelected)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
SetDestinationLevelStart();
|
||||
}
|
||||
SetDestinationLevelStart();
|
||||
break;
|
||||
case "navigatetodestination":
|
||||
if (!AutoPilot || MaintainPos || !LevelEndSelected || LevelStartSelected)
|
||||
if (objective.Override)
|
||||
{
|
||||
unsentChanges = true;
|
||||
if (MaintainPos || !LevelEndSelected || LevelStartSelected)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
SetDestinationLevelEnd();
|
||||
}
|
||||
SetDestinationLevelEnd();
|
||||
break;
|
||||
}
|
||||
|
||||
sonar?.AIOperate(deltaTime, character, objective);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float charge;
|
||||
|
||||
private float rechargeVoltage;
|
||||
//private float rechargeVoltage;
|
||||
|
||||
//how fast the battery can be recharged
|
||||
private float maxRechargeSpeed;
|
||||
@@ -28,10 +28,7 @@ namespace Barotrauma.Items.Components
|
||||
protected Vector2 indicatorPosition, indicatorSize;
|
||||
|
||||
protected bool isHorizontal;
|
||||
|
||||
//a list of powered devices connected directly to this item
|
||||
private readonly List<Pair<Powered, Connection>> directlyConnected = new List<Pair<Powered, Connection>>(10);
|
||||
|
||||
|
||||
public float CurrPowerOutput
|
||||
{
|
||||
get;
|
||||
@@ -107,12 +104,18 @@ namespace Barotrauma.Items.Components
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
rechargeSpeed = MathHelper.Clamp(value, 0.0f, maxRechargeSpeed);
|
||||
rechargeSpeed = MathUtils.RoundTowardsClosest(rechargeSpeed, Math.Max(maxRechargeSpeed * 0.1f, 1.0f));
|
||||
if (isRunning)
|
||||
{
|
||||
HasBeenTuned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float RechargeRatio => RechargeSpeed / MaxRechargeSpeed;
|
||||
|
||||
public const float aiRechargeTargetRatio = 0.5f;
|
||||
private bool isRunning;
|
||||
public bool HasBeenTuned { get; private set; }
|
||||
|
||||
public PowerContainer(Item item, XElement element)
|
||||
: base(item, element)
|
||||
@@ -131,14 +134,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
isRunning = true;
|
||||
float chargeRatio = charge / capacity;
|
||||
float gridPower = 0.0f;
|
||||
float gridLoad = 0.0f;
|
||||
directlyConnected.Clear();
|
||||
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (c.Name == "power_in") continue;
|
||||
if (!c.IsPower || !c.IsOutput) { continue; }
|
||||
foreach (Connection c2 in c.Recipients)
|
||||
{
|
||||
if (c2.Item.Condition <= 0.0f) { continue; }
|
||||
@@ -149,15 +151,13 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Powered powered in c2.Item.GetComponents<Powered>())
|
||||
{
|
||||
if (!powered.IsActive) continue;
|
||||
directlyConnected.Add(new Pair<Powered, Connection>(powered, c2));
|
||||
gridLoad += powered.CurrPowerConsumption;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!pt.IsActive || !pt.CanTransfer) { continue; }
|
||||
|
||||
gridLoad += pt.PowerLoad;
|
||||
gridPower -= pt.CurrPowerConsumption;
|
||||
gridLoad += pt.PowerLoad;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,66 +168,51 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (charge >= capacity)
|
||||
{
|
||||
rechargeVoltage = 0.0f;
|
||||
//rechargeVoltage = 0.0f;
|
||||
charge = capacity;
|
||||
|
||||
CurrPowerConsumption = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption = MathHelper.Lerp(currPowerConsumption, rechargeSpeed, 0.05f);
|
||||
Charge += currPowerConsumption * rechargeVoltage / 3600.0f;
|
||||
Charge += currPowerConsumption * Voltage / 3600.0f;
|
||||
}
|
||||
|
||||
//provide power to the grid
|
||||
if (gridLoad > 0.0f)
|
||||
|
||||
|
||||
if (charge <= 0.0f)
|
||||
{
|
||||
if (charge <= 0.0f)
|
||||
{
|
||||
CurrPowerOutput = 0.0f;
|
||||
charge = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gridPower < gridLoad)
|
||||
{
|
||||
//output starts dropping when the charge is less than 10%
|
||||
float maxOutputRatio = 1.0f;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
maxOutputRatio = Math.Max(chargeRatio * 10.0f, 0.0f);
|
||||
}
|
||||
|
||||
CurrPowerOutput = MathHelper.Lerp(
|
||||
CurrPowerOutput,
|
||||
Math.Min(MaxOutPut * maxOutputRatio, gridLoad),
|
||||
deltaTime * 10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrPowerOutput = MathHelper.Lerp(CurrPowerOutput, 0.0f, deltaTime * 10.0f);
|
||||
}
|
||||
|
||||
Charge -= CurrPowerOutput / 3600.0f;
|
||||
CurrPowerOutput = 0.0f;
|
||||
charge = 0.0f;
|
||||
return;
|
||||
}
|
||||
item.SendSignal(0, ((int)Charge).ToString(), "charge", null);
|
||||
item.SendSignal(0, ((int)((Charge / capacity) * 100)).ToString(), "charge_%", null);
|
||||
item.SendSignal(0, ((int)((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null);
|
||||
|
||||
foreach (Pair<Powered, Connection> connected in directlyConnected)
|
||||
//output starts dropping when the charge is less than 10%
|
||||
float maxOutputRatio = 1.0f;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
connected.First.ReceiveSignal(0, "", connected.Second, source: item, sender: null,
|
||||
power: gridLoad <= 0.0f ? 1.0f : CurrPowerOutput / gridLoad);
|
||||
maxOutputRatio = Math.Max(chargeRatio * 10.0f, 0.0f);
|
||||
}
|
||||
|
||||
rechargeVoltage = 0.0f;
|
||||
CurrPowerOutput += (gridLoad - gridPower) * deltaTime;
|
||||
|
||||
float maxOutput = Math.Min(MaxOutPut * maxOutputRatio, gridLoad);
|
||||
CurrPowerOutput = MathHelper.Clamp(CurrPowerOutput, 0.0f, maxOutput);
|
||||
Charge -= CurrPowerOutput / 3600.0f;
|
||||
|
||||
item.SendSignal(0, ((int)Math.Round(Charge)).ToString(), "charge", null);
|
||||
item.SendSignal(0, ((int)Math.Round((Charge / capacity) * 100)).ToString(), "charge_%", null);
|
||||
item.SendSignal(0, ((int)Math.Round((RechargeSpeed / maxRechargeSpeed) * 100)).ToString(), "charge_rate", null);
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null) return false;
|
||||
#endif
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
|
||||
if (objective.Override)
|
||||
{
|
||||
HasBeenTuned = false;
|
||||
}
|
||||
if (HasBeenTuned) { return true; }
|
||||
|
||||
if (string.IsNullOrEmpty(objective.Option) || objective.Option.ToLowerInvariant() == "charge")
|
||||
{
|
||||
@@ -274,6 +259,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection.IsPower) { return; }
|
||||
|
||||
if (connection.Name == "set_rate")
|
||||
{
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempSpeed))
|
||||
@@ -290,12 +277,6 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (!connection.IsPower) { return; }
|
||||
|
||||
if (connection.Name == "power_in")
|
||||
{
|
||||
rechargeVoltage = Math.Min(power, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,37 +8,21 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class PowerTransfer : Powered
|
||||
{
|
||||
private static float fullPower;
|
||||
private static float fullLoad;
|
||||
public List<Connection> PowerConnections { get; private set; }
|
||||
|
||||
private int updateCount;
|
||||
|
||||
//affects how fast changes in power/load are carried over the grid
|
||||
static float inertia = 5.0f;
|
||||
|
||||
private static HashSet<Powered> connectedList = new HashSet<Powered>();
|
||||
private List<Connection> powerConnections;
|
||||
public List<Connection> PowerConnections
|
||||
{
|
||||
get
|
||||
{
|
||||
return powerConnections;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Dictionary<Connection, bool> connectionDirty = new Dictionary<Connection, bool>();
|
||||
private readonly Dictionary<Connection, bool> connectionDirty = new Dictionary<Connection, bool>();
|
||||
|
||||
//a list of connections a given connection is connected to, either directly or via other power transfer components
|
||||
private Dictionary<Connection, HashSet<Connection>> connectedRecipients = new Dictionary<Connection, HashSet<Connection>>();
|
||||
private readonly Dictionary<Connection, HashSet<Connection>> connectedRecipients = new Dictionary<Connection, HashSet<Connection>>();
|
||||
|
||||
private float powerLoad;
|
||||
protected float powerLoad;
|
||||
|
||||
private bool isBroken;
|
||||
protected bool isBroken;
|
||||
|
||||
public float PowerLoad
|
||||
{
|
||||
get { return powerLoad; }
|
||||
set { powerLoad = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the item be damaged if too much power is supplied to the power grid.")]
|
||||
@@ -145,97 +129,43 @@ namespace Barotrauma.Items.Components
|
||||
SetAllConnectionsDirty();
|
||||
isBroken = false;
|
||||
}
|
||||
|
||||
if (updateCount > 0)
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
//if the item can't be fixed, don't allow it to break
|
||||
if (!item.Repairables.Any() || !CanBeOverloaded) { return; }
|
||||
|
||||
float maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
|
||||
Overload = -currPowerConsumption > Math.Max(powerLoad, 200.0f) * maxOverVoltage;
|
||||
if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
|
||||
{
|
||||
//this junction box has already been updated this frame
|
||||
updateCount--;
|
||||
return;
|
||||
}
|
||||
//damage the item if voltage is too high (except if running as a client)
|
||||
float prevCondition = item.Condition;
|
||||
item.Condition -= deltaTime * 10.0f;
|
||||
|
||||
Overload = false;
|
||||
|
||||
//reset and recalculate the power generated/consumed
|
||||
//by the constructions connected to the grid
|
||||
fullPower = 0.0f;
|
||||
fullLoad = 0.0f;
|
||||
|
||||
connectedList.Clear();
|
||||
|
||||
updateCount = 0;
|
||||
CheckJunctions(deltaTime);
|
||||
|
||||
foreach (Powered p in connectedList)
|
||||
{
|
||||
PowerTransfer pt = p as PowerTransfer;
|
||||
if (pt == null || pt.updateCount == 0) { continue; }
|
||||
|
||||
if (pt is RelayComponent != this is RelayComponent) { continue; }
|
||||
|
||||
pt.Overload = false;
|
||||
pt.powerLoad += (fullLoad - pt.powerLoad) / inertia;
|
||||
pt.currPowerConsumption += (-fullPower - pt.currPowerConsumption) / inertia;
|
||||
|
||||
float voltage = fullPower / Math.Max(fullLoad, 1.0f);
|
||||
if (this is RelayComponent)
|
||||
{
|
||||
pt.currPowerConsumption = Math.Max(-fullLoad, pt.currPowerConsumption);
|
||||
voltage = Math.Min(voltage, 1.0f);
|
||||
}
|
||||
|
||||
pt.Item.SendSignal(0, "", "power", null, voltage);
|
||||
pt.Item.SendSignal(0, "", "power_out", null, voltage);
|
||||
|
||||
//items in a bad condition are more sensitive to overvoltage
|
||||
float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, pt.item.Condition / pt.item.MaxCondition);
|
||||
maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
|
||||
|
||||
//if the item can't be fixed, don't allow it to break
|
||||
if (!pt.item.Repairables.Any() || !pt.CanBeOverloaded) { continue; }
|
||||
|
||||
//relays don't blow up if the power is higher than load, only if the output is high enough
|
||||
//(i.e. enough power passing through the relay)
|
||||
if (pt is RelayComponent) { continue; }
|
||||
|
||||
if (-pt.currPowerConsumption < Math.Max(pt.powerLoad, 200.0f) * maxOverVoltage) { continue; }
|
||||
|
||||
pt.Overload = true;
|
||||
#if CLIENT
|
||||
//damage the item if voltage is too high
|
||||
//(except if running as a client)
|
||||
if (GameMain.Client != null) { continue; }
|
||||
#endif
|
||||
float prevCondition = pt.item.Condition;
|
||||
pt.item.Condition -= deltaTime * 10.0f;
|
||||
|
||||
if (pt.item.Condition <= 0.0f && prevCondition > 0.0f)
|
||||
if (item.Condition <= 0.0f && prevCondition > 0.0f)
|
||||
{
|
||||
#if CLIENT
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: pt.item.CurrentHull);
|
||||
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
Vector2 baseVel = Rand.Vector(300.0f);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var particle = GameMain.ParticleManager.CreateParticle("spark", pt.item.WorldPosition,
|
||||
baseVel + Rand.Vector(100.0f), 0.0f, pt.item.CurrentHull);
|
||||
|
||||
var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition,
|
||||
baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull);
|
||||
if (particle != null) particle.Size *= Rand.Range(0.5f, 1.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
float currentIntensity = GameMain.GameSession?.EventManager != null ?
|
||||
float currentIntensity = GameMain.GameSession?.EventManager != null ?
|
||||
GameMain.GameSession.EventManager.CurrentIntensity : 0.5f;
|
||||
|
||||
|
||||
//higher probability for fires if the current intensity is low
|
||||
if (pt.FireProbability > 0.0f &&
|
||||
Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(pt.FireProbability, pt.FireProbability * 0.1f, currentIntensity))
|
||||
if (FireProbability > 0.0f &&
|
||||
Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(FireProbability, FireProbability * 0.1f, currentIntensity))
|
||||
{
|
||||
new FireSource(pt.item.WorldPosition);
|
||||
new FireSource(item.WorldPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateCount = 0;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
@@ -243,7 +173,7 @@ namespace Barotrauma.Items.Components
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
private void RefreshConnections()
|
||||
protected void RefreshConnections()
|
||||
{
|
||||
var connections = item.Connections;
|
||||
foreach (Connection c in connections)
|
||||
@@ -317,102 +247,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
//a recursive function that goes through all the junctions and adds up
|
||||
//all the generated/consumed power of the constructions connected to the grid
|
||||
private void CheckJunctions(float deltaTime, bool increaseUpdateCount = true, float clampPower = float.MaxValue, float clampLoad = float.MaxValue)
|
||||
{
|
||||
if (increaseUpdateCount)
|
||||
{
|
||||
updateCount = 1;
|
||||
}
|
||||
connectedList.Add(this);
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
//float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity;
|
||||
RelayComponent thisRelayComponent = this as RelayComponent;
|
||||
if (thisRelayComponent != null)
|
||||
{
|
||||
clampPower = Math.Min(Math.Min(clampPower, thisRelayComponent.MaxPower), powerLoad);
|
||||
clampLoad = Math.Min(clampLoad, thisRelayComponent.MaxPower);
|
||||
}
|
||||
|
||||
foreach (Connection c in PowerConnections)
|
||||
{
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (recipient?.Item == null || !recipient.IsPower) { continue; }
|
||||
|
||||
Item it = recipient.Item;
|
||||
if (it.Condition <= 0.0f) { continue; }
|
||||
|
||||
foreach (ItemComponent ic in it.Components)
|
||||
{
|
||||
if (!(ic is Powered powered) || !powered.IsActive) { continue; }
|
||||
if (connectedList.Contains(powered)) { continue; }
|
||||
|
||||
if (powered is PowerTransfer powerTransfer)
|
||||
{
|
||||
RelayComponent otherRelayComponent = powerTransfer as RelayComponent;
|
||||
if ((thisRelayComponent == null) == (otherRelayComponent == null))
|
||||
{
|
||||
if (!powerTransfer.CanTransfer) { continue; }
|
||||
powerTransfer.CheckJunctions(deltaTime, increaseUpdateCount, clampPower, clampLoad);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!powerTransfer.CanTransfer) continue;
|
||||
float maxPowerIn = (thisRelayComponent != null && c.IsOutput) ? 0.0f : clampPower;
|
||||
float maxPowerOut = (thisRelayComponent != null && !c.IsOutput) ? 0.0f : clampLoad;
|
||||
if (maxPowerIn > 0.0f || maxPowerOut > 0.0f)
|
||||
{
|
||||
powerTransfer.CheckJunctions(deltaTime, false, maxPowerIn, maxPowerOut);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
float addLoad = 0.0f;
|
||||
float addPower = 0.0f;
|
||||
if (powered is PowerContainer powerContainer)
|
||||
{
|
||||
if (recipient.Name == "power_in")
|
||||
{
|
||||
addLoad = powerContainer.CurrPowerConsumption;
|
||||
}
|
||||
else
|
||||
{
|
||||
addPower = powerContainer.CurrPowerOutput;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
connectedList.Add(powered);
|
||||
//positive power consumption = the construction requires power -> increase load
|
||||
if (powered.CurrPowerConsumption > 0.0f)
|
||||
{
|
||||
addLoad = powered.CurrPowerConsumption;
|
||||
}
|
||||
else if (powered.CurrPowerConsumption < 0.0f)
|
||||
//negative power consumption = the construction is a
|
||||
//generator/battery or another junction box
|
||||
{
|
||||
addPower -= powered.CurrPowerConsumption;
|
||||
}
|
||||
}
|
||||
|
||||
if (addPower + fullPower > clampPower) { addPower -= (addPower + fullPower) - clampPower; };
|
||||
if (addPower > 0) { fullPower += addPower; }
|
||||
|
||||
if (addLoad + fullLoad > clampLoad) { addLoad -= (addLoad + fullLoad) - clampLoad; };
|
||||
if (addLoad > 0) { fullLoad += addLoad; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAllConnectionsDirty()
|
||||
{
|
||||
if (item.Connections == null) return;
|
||||
@@ -431,8 +265,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
var connections = Item.Connections;
|
||||
powerConnections = connections == null ? new List<Connection>() : connections.FindAll(c => c.IsPower);
|
||||
PowerConnections = connections == null ? new List<Connection>() : connections.FindAll(c => c.IsPower);
|
||||
if (connections == null)
|
||||
{
|
||||
IsActive = false;
|
||||
@@ -440,33 +275,45 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
SetAllConnectionsDirty();
|
||||
}
|
||||
|
||||
|
||||
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
|
||||
{
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
lastPowerProbeRecipients.Add(this);
|
||||
|
||||
if (power < 0.0f)
|
||||
{
|
||||
powerLoad -= power;
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption -= power;
|
||||
}
|
||||
powerOut?.SendPowerProbeSignal(source, power);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection.IsPower) return;
|
||||
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power);
|
||||
|
||||
if (!connectedRecipients.ContainsKey(connection)) return;
|
||||
if (item.Condition <= 0.0f || connection.IsPower) { return; }
|
||||
if (!connectedRecipients.ContainsKey(connection)) { return; }
|
||||
|
||||
if (connection.Name.Length > 5 && connection.Name.Substring(0, 6) == "signal")
|
||||
{
|
||||
foreach (Connection recipient in connectedRecipients[connection])
|
||||
{
|
||||
if (recipient.Item == item || recipient.Item == source) continue;
|
||||
if (recipient.Item == item || recipient.Item == source) { continue; }
|
||||
|
||||
foreach (ItemComponent ic in recipient.Item.Components)
|
||||
{
|
||||
//powertransfer components 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 && connection.Name.Contains("signal")) { continue; }
|
||||
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, 0.0f, signalStrength);
|
||||
}
|
||||
|
||||
bool broken = recipient.Item.Condition <= 0.0f;
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
if (broken && effect.type != ActionType.OnBroken) continue;
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f, null, null, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
#if CLIENT
|
||||
using Barotrauma.Sounds;
|
||||
#endif
|
||||
@@ -9,25 +10,47 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Powered : ItemComponent
|
||||
{
|
||||
//the amount of power CURRENTLY consumed by the item
|
||||
//negative values mean that the item is providing power to connected items
|
||||
private static float updateTimer;
|
||||
protected static float UpdateInterval = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// List of all powered ItemComponents
|
||||
/// </summary>
|
||||
private static readonly List<Powered> poweredList = new List<Powered>();
|
||||
|
||||
/// <summary>
|
||||
/// Items that have already received the "probe signal" that's used to distribute power and load across the grid
|
||||
/// </summary>
|
||||
protected static HashSet<PowerTransfer> lastPowerProbeRecipients = new HashSet<PowerTransfer>();
|
||||
|
||||
/// <summary>
|
||||
/// The amount of power currently consumed by the item. Negative values mean that the item is providing power to connected items
|
||||
/// </summary>
|
||||
protected float currPowerConsumption;
|
||||
|
||||
//current voltage of the item (load / power)
|
||||
protected float voltage;
|
||||
/// <summary>
|
||||
/// Current voltage of the item (load / power)
|
||||
/// </summary>
|
||||
private float voltage;
|
||||
|
||||
//the minimum voltage required for the item to work
|
||||
protected float minVoltage;
|
||||
/// <summary>
|
||||
/// The minimum voltage required for the item to work
|
||||
/// </summary>
|
||||
private float minVoltage;
|
||||
|
||||
//the maximum amount of power the item can draw from connected items
|
||||
/// <summary>
|
||||
/// The maximum amount of power the item can draw from connected items
|
||||
/// </summary>
|
||||
protected float powerConsumption;
|
||||
|
||||
protected Connection powerIn, powerOut;
|
||||
|
||||
[Editable, Serialize(0.5f, true, description: "The minimum voltage required for the device to function. " +
|
||||
"The voltage is calculated as power / powerconsumption, meaning that a device " +
|
||||
"with a power consumption of 1000 kW would need at least 500 kW of power to work if the minimum voltage is set to 0.5.")]
|
||||
public float MinVoltage
|
||||
{
|
||||
get { return minVoltage; }
|
||||
get { return powerConsumption <= 0.0f ? 0.0f : minVoltage; }
|
||||
set { minVoltage = value; }
|
||||
}
|
||||
|
||||
@@ -76,34 +99,32 @@ namespace Barotrauma.Items.Components
|
||||
public Powered(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
poweredList.Add(this);
|
||||
InitProjectSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific(XElement element);
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1.0f)
|
||||
{
|
||||
if (currPowerConsumption == 0.0f) voltage = 0.0f;
|
||||
if (connection.IsPower) voltage = Math.Max(0.0f, power);
|
||||
}
|
||||
|
||||
protected void UpdateOnActiveEffects(float deltaTime)
|
||||
{
|
||||
if (currPowerConsumption == 0.0f)
|
||||
if (currPowerConsumption <= 0.0f)
|
||||
{
|
||||
//if the item consumes no power, ignore the voltage requirement and
|
||||
//apply OnActive statuseffects as long as this component is active
|
||||
if (powerConsumption == 0.0f)
|
||||
if (powerConsumption <= 0.0f)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (voltage > minVoltage)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
#if CLIENT
|
||||
if (voltage > minVoltage)
|
||||
{
|
||||
if (!powerOnSoundPlayed && powerOnSound != null)
|
||||
{
|
||||
SoundPlayer.PlaySound(powerOnSound.Sound, item.WorldPosition, powerOnSound.Volume, powerOnSound.Range, item.CurrentHull);
|
||||
@@ -114,21 +135,160 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
powerOnSoundPlayed = false;
|
||||
}
|
||||
#else
|
||||
if (voltage > minVoltage)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
if (item.Connections == null) { return; }
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (!c.IsPower) { continue; }
|
||||
if (this is PowerTransfer pt)
|
||||
{
|
||||
if (c.Name == "power_in")
|
||||
{
|
||||
powerIn = c;
|
||||
}
|
||||
else if (c.Name == "power_out")
|
||||
{
|
||||
powerOut = c;
|
||||
}
|
||||
else if (c.Name == "power")
|
||||
{
|
||||
powerIn = powerOut = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.IsOutput)
|
||||
{
|
||||
if (c.Name == "power_in")
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Item \"{item.Name}\" has a power output connection called power_in. If the item is supposed to receive power through the connection, change it to an input connection.");
|
||||
#else
|
||||
DebugConsole.NewMessage($"Item \"{item.Name}\" has a power output connection called power_in. If the item is supposed to receive power through the connection, change it to an input connection.", Color.Orange);
|
||||
#endif
|
||||
}
|
||||
powerOut = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.Name == "power_out")
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Item \"{item.Name}\" has a power input connection called power_out. If the item is supposed to output power through the connection, change it to an output connection.");
|
||||
#else
|
||||
DebugConsole.NewMessage($"Item \"{item.Name}\" has a power input connection called power_out. If the item is supposed to output power through the connection, change it to an output connection.", Color.Orange);
|
||||
#endif
|
||||
}
|
||||
powerIn = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ReceivePowerProbeSignal(Connection connection, Item source, float power) { }
|
||||
|
||||
public static void UpdatePower(float deltaTime)
|
||||
{
|
||||
if (updateTimer > 0.0f)
|
||||
{
|
||||
updateTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
updateTimer = UpdateInterval;
|
||||
|
||||
//reset power first
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer pt)
|
||||
{
|
||||
powered.CurrPowerConsumption = 0.0f;
|
||||
pt.PowerLoad = 0.0f;
|
||||
if (pt is RelayComponent relay)
|
||||
{
|
||||
relay.DisplayLoad = 0.0f;
|
||||
}
|
||||
}
|
||||
//only reset voltage if the item has a power connector
|
||||
//(other items, such as handheld devices, get power through other means and shouldn't be updated here)
|
||||
if (powered.powerIn != null || powered.powerOut != null) { powered.voltage = 0.0f; }
|
||||
}
|
||||
|
||||
//go through all the devices that are consuming/providing power
|
||||
//and send out a "probe signal" which the PowerTransfer components use to add up the grid power/load
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer) { continue; }
|
||||
if (powered.currPowerConsumption > 0.0f)
|
||||
{
|
||||
//consuming power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerIn?.SendPowerProbeSignal(powered.item, -powered.currPowerConsumption);
|
||||
}
|
||||
}
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer) { continue; }
|
||||
else if (powered.currPowerConsumption < 0.0f)
|
||||
{
|
||||
//providing power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerOut?.SendPowerProbeSignal(powered.item, -powered.currPowerConsumption);
|
||||
}
|
||||
if (powered is PowerContainer pc)
|
||||
{
|
||||
if (pc.CurrPowerOutput <= 0.0f) { continue; }
|
||||
//providing power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerOut?.SendPowerProbeSignal(powered.item, pc.CurrPowerOutput);
|
||||
}
|
||||
}
|
||||
//go through powered items and calculate their current voltage
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer pt1 || (pt1 = powered.Item.GetComponent<PowerTransfer>()) != null)
|
||||
{
|
||||
powered.voltage = -pt1.CurrPowerConsumption / Math.Max(pt1.PowerLoad, 1.0f);
|
||||
continue;
|
||||
}
|
||||
if (powered.powerConsumption <= 0.0f && !(powered is PowerContainer))
|
||||
{
|
||||
powered.voltage = 1.0f;
|
||||
continue;
|
||||
}
|
||||
if (powered.powerIn == null) { continue; }
|
||||
|
||||
foreach (Connection powerSource in powered.powerIn.Recipients)
|
||||
{
|
||||
if (!powerSource.IsPower || !powerSource.IsOutput) { continue; }
|
||||
var pt = powerSource.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null)
|
||||
{
|
||||
float voltage = -pt.CurrPowerConsumption / Math.Max(pt.PowerLoad, 1.0f);
|
||||
powered.voltage = Math.Max(powered.voltage, voltage);
|
||||
continue;
|
||||
}
|
||||
var pc = powerSource.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null)
|
||||
{
|
||||
float voltage = -pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
|
||||
powered.voltage += voltage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
poweredList.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,9 +416,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (IgnoredBodies.Contains(target.Body)) { return false; }
|
||||
|
||||
if (target.UserData is Item) { return false; }
|
||||
|
||||
if (target.CollisionCategories == Physics.CollisionCharacter && !(target.Body.UserData is Limb))
|
||||
//ignore character colliders (the projectile only hits limbs)
|
||||
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -445,17 +444,52 @@ namespace Barotrauma.Items.Components
|
||||
if (attack != null) { attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f); }
|
||||
if (limb.character != null) { character = limb.character; }
|
||||
}
|
||||
else if (target.Body.UserData is Structure structure)
|
||||
else if (target.Body.UserData is Item targetItem)
|
||||
{
|
||||
if (attack != null) { attackResult = attack.DoDamage(User, structure, item.WorldPosition, 1.0f); }
|
||||
if (attack != null && targetItem.Prefab.DamagedByProjectiles)
|
||||
{
|
||||
attackResult = attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
|
||||
}
|
||||
}
|
||||
else if (target.Body.UserData is IDamageable damageable)
|
||||
{
|
||||
if (attack != null) { attackResult = attack.DoDamage(User, damageable, item.WorldPosition, 1.0f); }
|
||||
}
|
||||
|
||||
if (character != null) { character.LastDamageSource = item; }
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, target.Body.UserData as Limb, user: user);
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, target.Body.UserData as Limb, user: user);
|
||||
if (target.Body.UserData is Limb targetLimb)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, targetLimb, user: user);
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, targetLimb, user: user);
|
||||
var attack = targetLimb.attack;
|
||||
if (attack != null)
|
||||
{
|
||||
// Apply the status effects defined in the limb's attack that was hit
|
||||
foreach (var effect in attack.StatusEffects)
|
||||
{
|
||||
if (effect.type == ActionType.OnImpact)
|
||||
{
|
||||
//effect.Apply(effect.type, 1.0f, targetLimb.character, targetLimb.character, targetLimb.WorldPosition);
|
||||
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
||||
{
|
||||
effect.Apply(effect.type, 1.0f, targetLimb.character, targetLimb.character, targetLimb.WorldPosition);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
var targets = new List<ISerializableEntity>();
|
||||
effect.GetNearbyTargets(targetLimb.WorldPosition, targets);
|
||||
effect.Apply(ActionType.OnActive, 1.0f, targetLimb.character, targets);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
|
||||
@@ -92,9 +92,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (XElement connectionElement in subElement.Elements())
|
||||
{
|
||||
if (connectionElement.Name.ToString() != element.Name.ToString()) { continue; }
|
||||
|
||||
string prefabConnectionName = element.GetAttributeString("name", IsOutput ? "output" : "input");
|
||||
string prefabConnectionName = element.GetAttributeString("name", null);
|
||||
if (prefabConnectionName == Name)
|
||||
{
|
||||
displayNameTag = connectionElement.GetAttributeString("displayname", "");
|
||||
@@ -245,31 +243,38 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
if (wires[i] == null) { continue; }
|
||||
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
if (recipient == null) continue;
|
||||
if (recipient.item == this.item || recipient.item == source) continue;
|
||||
if (recipient == null) { continue; }
|
||||
if (recipient.item == this.item || recipient.item == source) { continue; }
|
||||
|
||||
if (source != null && !source.LastSentSignalRecipients.Contains(recipient.item))
|
||||
{
|
||||
source.LastSentSignalRecipients.Add(recipient.item);
|
||||
}
|
||||
source?.LastSentSignalRecipients.Add(recipient.item);
|
||||
|
||||
foreach (ItemComponent ic in recipient.item.Components)
|
||||
{
|
||||
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, power, signalStrength);
|
||||
}
|
||||
|
||||
bool broken = recipient.Item.Condition <= 0.0f;
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
if (broken && effect.type != ActionType.OnBroken) continue;
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step, null, null, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendPowerProbeSignal(Item source, float power)
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wires[i] == null) { continue; }
|
||||
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
if (recipient == null) { continue; }
|
||||
|
||||
recipient.item.GetComponent<Powered>()?.ReceivePowerProbeSignal(recipient, source, power);
|
||||
}
|
||||
}
|
||||
public void ClearConnections()
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class FunctionComponent : ItemComponent
|
||||
{
|
||||
public enum FunctionType
|
||||
{
|
||||
Round,
|
||||
Ceil,
|
||||
Floor,
|
||||
Factorial
|
||||
}
|
||||
|
||||
[Serialize(FunctionType.Round, false, description: "Which kind of function to run the input through.")]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public FunctionComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
float.TryParse(signal, out float value);
|
||||
switch (Function)
|
||||
{
|
||||
case FunctionType.Round:
|
||||
item.SendSignal(0, Math.Round(value).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Ceil:
|
||||
item.SendSignal(0, Math.Ceiling(value).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Floor:
|
||||
item.SendSignal(0, Math.Floor(value).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Factorial:
|
||||
int intVal = (int)Math.Min(value, 20);
|
||||
ulong factorial = 1;
|
||||
for (int i = intVal; i > 0; i--)
|
||||
{
|
||||
factorial *= (ulong)i;
|
||||
}
|
||||
item.SendSignal(0, factorial.ToString(), "signal_out", null);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Function {Function} has not been implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,19 +216,12 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
|
||||
if (powerConsumption == 0.0f)
|
||||
{
|
||||
voltage = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption = powerConsumption;
|
||||
}
|
||||
currPowerConsumption = powerConsumption;
|
||||
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.05f && voltage < Rand.Range(0.0f, minVoltage))
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.05f && Voltage < Rand.Range(0.0f, MinVoltage))
|
||||
{
|
||||
#if CLIENT
|
||||
if (voltage > 0.1f)
|
||||
if (Voltage > 0.1f)
|
||||
{
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
}
|
||||
@@ -237,7 +230,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
lightBrightness = MathHelper.Lerp(lightBrightness, Math.Min(voltage, 1.0f), 0.1f);
|
||||
lightBrightness = MathHelper.Lerp(lightBrightness, Math.Min(Voltage, 1.0f), 0.1f);
|
||||
}
|
||||
|
||||
if (blinkFrequency > 0.0f)
|
||||
@@ -262,8 +255,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateAITarget(item.AiTarget);
|
||||
}
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class ModuloComponent : ItemComponent
|
||||
{
|
||||
private float modulus;
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The modulus of the operation. Must be non-zero.")]
|
||||
public float Modulus
|
||||
{
|
||||
get { return modulus; }
|
||||
set
|
||||
{
|
||||
modulus = MathUtils.NearlyEqual(value, 0.0f) ? 1.0f : value;
|
||||
}
|
||||
}
|
||||
|
||||
public ModuloComponent(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "set_modulus":
|
||||
case "modulus":
|
||||
float.TryParse(signal, out float newModulus);
|
||||
Modulus = newModulus;
|
||||
break;
|
||||
case "signal_in":
|
||||
float.TryParse(signal, out float value);
|
||||
item.SendSignal(0, (value % modulus).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,14 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Should the sensor ignore the bodies of dead characters?")]
|
||||
public bool IgnoreDead
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Horizontal detection range.")]
|
||||
public float RangeX
|
||||
{
|
||||
@@ -109,6 +117,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (IgnoreDead && c.IsDead) { continue; }
|
||||
if (OnlyHumans && !c.IsHuman) { continue; }
|
||||
|
||||
//do a rough check based on the position of the character's collider first
|
||||
@@ -138,5 +147,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
detectOffset.Y = -detectOffset.Y;
|
||||
}
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
Vector2 prevDetectOffset = detectOffset;
|
||||
//undo flipping before saving
|
||||
if (item.FlippedX) { detectOffset.X = -detectOffset.X; }
|
||||
if (item.FlippedY) { detectOffset.Y = -detectOffset.Y; }
|
||||
XElement element = base.Save(parentElement);
|
||||
detectOffset = prevDetectOffset;
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
@@ -8,9 +9,11 @@ namespace Barotrauma.Items.Components
|
||||
class RelayComponent : PowerTransfer, IServerSerializable
|
||||
{
|
||||
private float maxPower;
|
||||
|
||||
|
||||
private bool isOn;
|
||||
|
||||
private float throttlePowerOutput;
|
||||
|
||||
private static readonly Dictionary<string, string> connectionPairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "power_in", "power_out"},
|
||||
@@ -21,6 +24,7 @@ namespace Barotrauma.Items.Components
|
||||
{ "signal_in4", "signal_out4" },
|
||||
{ "signal_in5", "signal_out5" }
|
||||
};
|
||||
public float DisplayLoad { get; set; }
|
||||
|
||||
[Editable, Serialize(1000.0f, true, description: "The maximum amount of power that can pass through the item.")]
|
||||
public float MaxPower
|
||||
@@ -31,7 +35,7 @@ namespace Barotrauma.Items.Components
|
||||
maxPower = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Editable, Serialize(false, true, description: "Can the relay currently pass power and signals through it.")]
|
||||
public bool IsOn
|
||||
{
|
||||
@@ -49,18 +53,46 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public RelayComponent(Item item, XElement element)
|
||||
: base (item, element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
throttlePowerOutput = MaxPower;
|
||||
}
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
RefreshConnections();
|
||||
|
||||
item.SendSignal(0, IsOn ? "1" : "0", "state_out", null);
|
||||
|
||||
if (!CanTransfer) { Voltage = 0.0f; return; }
|
||||
|
||||
if (isBroken)
|
||||
{
|
||||
SetAllConnectionsDirty();
|
||||
isBroken = false;
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (powerOut != null)
|
||||
{
|
||||
bool overloaded = false;
|
||||
foreach (Connection recipient in powerOut.Recipients)
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null)
|
||||
{
|
||||
float overload = -pt.CurrPowerConsumption - pt.PowerLoad;
|
||||
throttlePowerOutput += overload * deltaTime * 0.5f;
|
||||
overloaded = overload > 1.0f;
|
||||
}
|
||||
}
|
||||
throttlePowerOutput = overloaded ?
|
||||
MathHelper.Clamp(throttlePowerOutput, 0.0f, MaxPower):
|
||||
Math.Max(throttlePowerOutput - MaxPower * 0.1f * deltaTime, 0.0f);
|
||||
}
|
||||
|
||||
if (Math.Min(-currPowerConsumption, PowerLoad) > maxPower && CanBeOverloaded)
|
||||
{
|
||||
@@ -68,9 +100,56 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
|
||||
{
|
||||
if (!IsOn) { return; }
|
||||
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
lastPowerProbeRecipients.Add(this);
|
||||
|
||||
if (power < 0.0f)
|
||||
{
|
||||
if (!connection.IsOutput || powerIn == null) { return; }
|
||||
|
||||
//power being drawn from the power_out connection
|
||||
DisplayLoad -= Math.Min(power, 0.0f);
|
||||
powerLoad -= Math.Min(power + throttlePowerOutput, 0.0f);
|
||||
|
||||
//pass the load to items connected to the input
|
||||
powerIn.SendPowerProbeSignal(source, Math.Max(power, -MaxPower));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connection.IsOutput || powerOut == null) { return; }
|
||||
//power being supplied to the power_in connection
|
||||
if (currPowerConsumption - power < -MaxPower)
|
||||
{
|
||||
power += MaxPower + (currPowerConsumption - power);
|
||||
}
|
||||
|
||||
currPowerConsumption -= power;
|
||||
|
||||
foreach (Connection recipient in powerOut.Recipients)
|
||||
{
|
||||
if (!recipient.IsPower) { continue; }
|
||||
var powered = recipient.Item.GetComponent<Powered>();
|
||||
if (powered == null) { continue; }
|
||||
|
||||
float load = powered.CurrPowerConsumption;
|
||||
var powerTransfer = powered as PowerTransfer;
|
||||
if (powerTransfer != null) { load = powerTransfer.PowerLoad; }
|
||||
|
||||
float powerOut = power * (load / Math.Max(powerLoad + throttlePowerOutput, 0.01f));
|
||||
powered.ReceivePowerProbeSignal(recipient, source, Math.Min(powerOut, power));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection.IsPower || item.Condition <= 0.0f) { return; }
|
||||
if (item.Condition <= 0.0f || connection.IsPower) { return; }
|
||||
|
||||
if (connectionPairs.TryGetValue(connection.Name, out string outConnection))
|
||||
{
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class TrigonometricFunctionComponent : ItemComponent
|
||||
{
|
||||
public enum FunctionType
|
||||
{
|
||||
Sin,
|
||||
Cos,
|
||||
Tan,
|
||||
Asin,
|
||||
Acos,
|
||||
Atan,
|
||||
}
|
||||
|
||||
protected float[] receivedSignal = new float[2];
|
||||
|
||||
[Serialize(FunctionType.Sin, false, description: "Which kind of function to run the input through.")]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "If set to true, the trigonometric function uses radians instead of degrees.")]
|
||||
public bool UseRadians
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
public TrigonometricFunctionComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
//reset received signals
|
||||
receivedSignal[0] = float.NaN;
|
||||
receivedSignal[1] = float.NaN;
|
||||
}
|
||||
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
float.TryParse(signal, out float value);
|
||||
switch (Function)
|
||||
{
|
||||
case FunctionType.Sin:
|
||||
if (!UseRadians) { value = MathHelper.ToRadians(value); }
|
||||
item.SendSignal(0, ((float)Math.Sin(value)).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Cos:
|
||||
if (!UseRadians) { value = MathHelper.ToRadians(value); }
|
||||
item.SendSignal(0, ((float)Math.Cos(value)).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Tan:
|
||||
if (!UseRadians) { value = MathHelper.ToRadians(value); }
|
||||
item.SendSignal(0, ((float)Math.Tan(value)).ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
break;
|
||||
case FunctionType.Asin:
|
||||
{
|
||||
float angle = (float)Math.Asin(value);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
break;
|
||||
case FunctionType.Acos:
|
||||
{
|
||||
float angle = (float)Math.Acos(value);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
break;
|
||||
case FunctionType.Atan:
|
||||
if (connection.Name == "signal_in_x")
|
||||
{
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[0]);
|
||||
}
|
||||
else if (connection.Name == "signal_in_y")
|
||||
{
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[1]);
|
||||
if (!float.IsNaN(receivedSignal[0]) && !float.IsNaN(receivedSignal[1]))
|
||||
{
|
||||
float angle = (float)Math.Atan2(receivedSignal[1], receivedSignal[0]);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float angle = (float)Math.Atan(value);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Function {Function} has not been implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(Character.TeamType.None, false, description: "WiFi components can only communicate with components that have the same Team ID.")]
|
||||
public Character.TeamType TeamID { get; set; }
|
||||
|
||||
[Serialize(20000.0f, false, description: "How close the recipient has to be to receive a signal from this WiFi component.")]
|
||||
[Editable, Serialize(20000.0f, false, description: "How close the recipient has to be to receive a signal from this WiFi component.")]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
@@ -174,8 +174,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
|
||||
list.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,22 +83,16 @@ namespace Barotrauma.Items.Components
|
||||
public Wire(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
#if CLIENT
|
||||
if (wireSprite == null)
|
||||
{
|
||||
wireSprite = new Sprite("Content/Items/wireHorizontal.png", new Vector2(0.5f, 0.5f))
|
||||
{
|
||||
Depth = 0.85f
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
nodes = new List<Vector2>();
|
||||
sections = new List<WireSection>();
|
||||
connections = new Connection[2];
|
||||
IsActive = false;
|
||||
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
public Connection OtherConnection(Connection connection)
|
||||
{
|
||||
if (connection == connections[0]) { return connections[1]; }
|
||||
@@ -728,6 +722,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
ClearConnections();
|
||||
base.RemoveComponentSpecific();
|
||||
#if CLIENT
|
||||
overrideSprite?.Remove();
|
||||
overrideSprite = null;
|
||||
wireSprite = null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
|
||||
@@ -192,6 +192,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
var lightComponents = item.GetComponents<LightComponent>();
|
||||
if (lightComponents != null && lightComponents.Count() > 0)
|
||||
{
|
||||
@@ -325,20 +326,24 @@ namespace Barotrauma.Items.Components
|
||||
failedLaunchAttempts = 0;
|
||||
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
float availablePower = 0.0f;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
float neededPower = powerConsumption;
|
||||
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
float batteryPower = Math.Min(battery.Charge * 3600.0f, battery.MaxOutPut);
|
||||
float takePower = Math.Min(powerConsumption - availablePower, batteryPower);
|
||||
|
||||
battery.Charge -= takePower / 3600.0f;
|
||||
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
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)
|
||||
{
|
||||
battery.Item.CreateServerEvent(battery);
|
||||
}
|
||||
neededPower -= takePower;
|
||||
battery.Charge -= takePower / 3600.0f;
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
battery.Item.CreateServerEvent(battery);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Launch(projectiles[0].Item, character);
|
||||
@@ -477,12 +482,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//enough shells and power
|
||||
Character closestEnemy = null;
|
||||
float closestDist = 10000.0f * 10000.0f;
|
||||
float closestDist = 3000 * 3000;
|
||||
foreach (Character enemy in Character.CharacterList)
|
||||
{
|
||||
//ignore humans and characters that are inside the sub
|
||||
if (enemy.IsDead|| enemy.AnimController.CurrentHull != null || !enemy.Enabled) { continue; }
|
||||
if (enemy.SpeciesName == character.SpeciesName && enemy.TeamID == character.TeamID) { continue; }
|
||||
// Ignore friendly and those that are inside the sub
|
||||
if (enemy.IsDead || enemy.AnimController.CurrentHull != null || !enemy.Enabled) { continue; }
|
||||
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
|
||||
if (dist > closestDist) { continue; }
|
||||
@@ -510,8 +515,21 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f) { return false; }
|
||||
|
||||
var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition, null);
|
||||
if (pickedBody != null && !(pickedBody.UserData is Limb)) { return false; }
|
||||
var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition);
|
||||
if (pickedBody == null) { return false; }
|
||||
Character target = null;
|
||||
if (pickedBody.UserData is Character c)
|
||||
{
|
||||
target = c;
|
||||
}
|
||||
else if (pickedBody.UserData is Limb limb)
|
||||
{
|
||||
target = limb.character;
|
||||
}
|
||||
if (target == null || HumanAIController.IsFriendly(character, target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (objective.Option.ToLowerInvariant() == "fireatwill")
|
||||
{
|
||||
@@ -554,8 +572,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
|
||||
if (barrelSprite != null) barrelSprite.Remove();
|
||||
if (railSprite != null) railSprite.Remove();
|
||||
barrelSprite?.Remove(); barrelSprite = null;
|
||||
railSprite?.Remove(); railSprite = null;
|
||||
|
||||
#if CLIENT
|
||||
moveSoundChannel?.Dispose(); moveSoundChannel = null;
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -23,6 +24,7 @@ namespace Barotrauma
|
||||
|
||||
class WearableSprite
|
||||
{
|
||||
public string UnassignedSpritePath { get; private set; }
|
||||
public string SpritePath { get; private set; }
|
||||
public XElement SourceElement { get; private set; }
|
||||
|
||||
@@ -83,7 +85,7 @@ namespace Barotrauma
|
||||
if (value == _gender) { return; }
|
||||
_gender = value;
|
||||
IsInitialized = false;
|
||||
SpritePath = ParseSpritePath(SourceElement.GetAttributeString("texture", string.Empty));
|
||||
UnassignedSpritePath = ParseSpritePath(SourceElement.GetAttributeString("texture", string.Empty));
|
||||
Init(_gender);
|
||||
}
|
||||
}
|
||||
@@ -92,7 +94,7 @@ namespace Barotrauma
|
||||
{
|
||||
Type = type;
|
||||
SourceElement = subElement;
|
||||
SpritePath = subElement.GetAttributeString("texture", string.Empty);
|
||||
UnassignedSpritePath = subElement.GetAttributeString("texture", string.Empty);
|
||||
Init();
|
||||
switch (type)
|
||||
{
|
||||
@@ -122,42 +124,24 @@ namespace Barotrauma
|
||||
Type = WearableType.Item;
|
||||
WearableComponent = wearable;
|
||||
Variant = Math.Max(variant, 0);
|
||||
SpritePath = ParseSpritePath(subElement.GetAttributeString("texture", string.Empty));
|
||||
UnassignedSpritePath = 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)
|
||||
public void ParsePath(bool parseSpritePath)
|
||||
{
|
||||
string tempPath = UnassignedSpritePath;
|
||||
if (_gender != Gender.None)
|
||||
{
|
||||
SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male");
|
||||
tempPath = tempPath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male");
|
||||
}
|
||||
SpritePath = SpritePath.Replace("[VARIANT]", Variant.ToString());
|
||||
SpritePath = tempPath.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());
|
||||
SpritePath = tempPath.Replace("[VARIANT]", "1");
|
||||
}
|
||||
if (parseSpritePath)
|
||||
{
|
||||
@@ -169,13 +153,13 @@ namespace Barotrauma
|
||||
public void Init(Gender gender = Gender.None)
|
||||
{
|
||||
if (IsInitialized) { return; }
|
||||
_gender = SpritePath.Contains("[GENDER]") ? gender : Gender.None;
|
||||
_gender = UnassignedSpritePath.Contains("[GENDER]") ? gender : Gender.None;
|
||||
ParsePath(false);
|
||||
if (Sprite != null)
|
||||
{
|
||||
Sprite.Remove();
|
||||
}
|
||||
Sprite = new Sprite(SourceElement, file: SpritePath);
|
||||
Sprite = new Sprite(SourceElement, file: SpritePath, preMultiplyAlpha: true);
|
||||
Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true);
|
||||
HideLimb = SourceElement.GetAttributeBool("hidelimb", false);
|
||||
HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false);
|
||||
@@ -197,25 +181,60 @@ namespace Barotrauma
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Wearable : Pickable
|
||||
class Wearable : Pickable, IServerSerializable
|
||||
{
|
||||
private WearableSprite[] wearableSprites;
|
||||
private LimbType[] limbType;
|
||||
private Limb[] limb;
|
||||
private readonly XElement[] wearableElements;
|
||||
private readonly WearableSprite[] wearableSprites;
|
||||
private readonly LimbType[] limbType;
|
||||
private readonly Limb[] limb;
|
||||
|
||||
private List<DamageModifier> damageModifiers;
|
||||
private readonly List<DamageModifier> damageModifiers;
|
||||
|
||||
public List<DamageModifier> DamageModifiers
|
||||
public IEnumerable<DamageModifier> DamageModifiers
|
||||
{
|
||||
get { return damageModifiers; }
|
||||
}
|
||||
|
||||
private bool autoEquipWhenFull;
|
||||
public bool AutoEquipWhenFull
|
||||
public bool AutoEquipWhenFull { get; private set; }
|
||||
|
||||
public readonly int Variants;
|
||||
|
||||
private int variant;
|
||||
public int Variant
|
||||
{
|
||||
get { return autoEquipWhenFull; }
|
||||
}
|
||||
|
||||
get { return variant; }
|
||||
set
|
||||
{
|
||||
#if SERVER
|
||||
variant = value;
|
||||
item.CreateServerEvent(this);
|
||||
#elif CLIENT
|
||||
if (variant == value) { return; }
|
||||
|
||||
Character character = picker;
|
||||
if (character != null)
|
||||
{
|
||||
Unequip(character);
|
||||
}
|
||||
|
||||
for (int i = 0; i < wearableSprites.Length; i++)
|
||||
{
|
||||
var subElement = wearableElements[i];
|
||||
|
||||
wearableSprites[i]?.Sprite?.Remove();
|
||||
wearableSprites[i] = new WearableSprite(subElement, this, value);
|
||||
}
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
Equip(character);
|
||||
}
|
||||
|
||||
variant = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public Wearable(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
this.item = item;
|
||||
@@ -223,12 +242,13 @@ namespace Barotrauma.Items.Components
|
||||
damageModifiers = new List<DamageModifier>();
|
||||
|
||||
int spriteCount = element.Elements().Count(x => x.Name.ToString() == "sprite");
|
||||
int variants = element.GetAttributeInt("variants", 0);
|
||||
int variant = variants > 0 ? Rand.Range(1, variants + 1, Rand.RandSync.Server) : 1;
|
||||
Variants = element.GetAttributeInt("variants", 0);
|
||||
variant = Rand.Range(1, Variants + 1, Rand.RandSync.Server);
|
||||
wearableSprites = new WearableSprite[spriteCount];
|
||||
wearableElements = new XElement[spriteCount];
|
||||
limbType = new LimbType[spriteCount];
|
||||
limb = new Limb[spriteCount];
|
||||
autoEquipWhenFull = element.GetAttributeBool("autoequipwhenfull", true);
|
||||
AutoEquipWhenFull = element.GetAttributeBool("autoequipwhenfull", true);
|
||||
int i = 0;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
@@ -245,6 +265,7 @@ namespace Barotrauma.Items.Components
|
||||
subElement.GetAttributeString("limb", "Head"), true);
|
||||
|
||||
wearableSprites[i] = new WearableSprite(subElement, this, variant);
|
||||
wearableElements[i] = subElement;
|
||||
|
||||
foreach (XElement lightElement in subElement.Elements())
|
||||
{
|
||||
@@ -380,5 +401,39 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement componentElement = base.Save(parentElement);
|
||||
componentElement.Add(new XAttribute("variant", variant));
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
private int loadedVariant = -1;
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
loadedVariant = componentElement.GetAttributeInt("variant", -1);
|
||||
}
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
//do this here to prevent creating a network event before the item has been fully initialized
|
||||
if (loadedVariant > 0 && loadedVariant < Variants + 1)
|
||||
{
|
||||
Variant = loadedVariant;
|
||||
}
|
||||
}
|
||||
public override void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write((byte)Variant);
|
||||
base.ServerWrite(msg, c, extraData);
|
||||
}
|
||||
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Variant = (int)msg.ReadByte();
|
||||
base.ClientRead(type, msg, sendingTime);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user