Merge branch 'dev' of https://github.com/Regalis11/Barotrauma into unstable
This commit is contained in:
@@ -46,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
private float forceLockTimer;
|
||||
//if the submarine isn't in the correct position to lock within this time after docking has been activated,
|
||||
//force the sub to the correct position
|
||||
const float ForceLockDelay = 1.0f;
|
||||
const float ForceLockDelay = 1.0f;
|
||||
|
||||
public int DockingDir { get; set; }
|
||||
|
||||
@@ -81,12 +81,18 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(DirectionType.None, IsPropertySaveable.No, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n"+
|
||||
[Editable, Serialize(DirectionType.None, IsPropertySaveable.No, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n" +
|
||||
"Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
|
||||
public DirectionType ForceDockingDirection { get; set; }
|
||||
|
||||
|
||||
public DockingPort DockingTarget { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects
|
||||
/// </summary>
|
||||
public bool AtStartExit => Item.Submarine is { AtStartExit: true};
|
||||
public bool AtEndExit => Item.Submarine is { AtEndExit: true };
|
||||
|
||||
public Door Door { get; private set; }
|
||||
|
||||
public bool Docked
|
||||
@@ -116,6 +122,8 @@ namespace Barotrauma.Items.Components
|
||||
get { return joint is WeldJoint || DockingTarget?.joint is WeldJoint; }
|
||||
}
|
||||
|
||||
public bool AnotherPortInProximity => FindAdjacentPort() != null;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically cleared after docking -> no need to unregister
|
||||
/// </summary>
|
||||
@@ -989,7 +997,7 @@ namespace Barotrauma.Items.Components
|
||||
dockingState = MathHelper.Lerp(dockingState, 0.0f, deltaTime * 10.0f);
|
||||
if (dockingState < 0.01f) { docked = false; }
|
||||
item.SendSignal("0", "state_out");
|
||||
item.SendSignal((FindAdjacentPort() != null) ? "1" : "0", "proximity_sensor");
|
||||
item.SendSignal(AnotherPortInProximity ? "1" : "0", "proximity_sensor");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1191,7 +1199,7 @@ namespace Barotrauma.Items.Components
|
||||
//trying to dock/undock from an outpost and the signal was sent by some automated system instead of a character
|
||||
// -> ask if the player really wants to dock/undock to prevent a softlock if someone's wired the docking port
|
||||
// in a way that makes always makes it dock/undock immediately at the start of the roun
|
||||
if (tryingToToggleOutpostDocking && signal.sender == null)
|
||||
if (GameMain.NetworkMember != null && tryingToToggleOutpostDocking && signal.sender == null)
|
||||
{
|
||||
if (allowOutpostAutoDocking == AllowOutpostAutoDocking.Ask)
|
||||
{
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (GetAvailableInstantaneousBatteryPower() >= PowerConsumption)
|
||||
{
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
List<PowerContainer> batteries = GetDirectlyConnectedBatteries();
|
||||
float neededPower = PowerConsumption;
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
@@ -203,7 +203,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach ((Character character, Node node) in charactersInRange)
|
||||
{
|
||||
if (character == null || character.Removed) { continue; }
|
||||
character.ApplyAttack(null, node.WorldPosition, attack, 1.0f);
|
||||
character.ApplyAttack(null, node.WorldPosition, attack, MathHelper.Clamp(Voltage, 1.0f, MaxOverVoltageFactor));
|
||||
}
|
||||
}
|
||||
DischargeProjSpecific();
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace Barotrauma.Items.Components
|
||||
public static FoliageConfig CreateRandomConfig(int maxVariants, float minScale, float maxScale, Random? random = null)
|
||||
{
|
||||
int flowerVariant = Growable.RandomInt(0, maxVariants, random);
|
||||
float flowerScale = (float) Growable.RandomDouble(minScale, maxScale, random);
|
||||
float flowerRotation = (float) Growable.RandomDouble(0, MathHelper.TwoPi, random);
|
||||
float flowerScale = (float)Growable.RandomDouble(minScale, maxScale, random);
|
||||
float flowerRotation = (float)Growable.RandomDouble(0, MathHelper.TwoPi, random);
|
||||
return new FoliageConfig { Variant = flowerVariant, Scale = flowerScale, Rotation = flowerRotation };
|
||||
}
|
||||
}
|
||||
@@ -169,10 +169,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
const float limit = 1.0f;
|
||||
growthStep = value;
|
||||
VineStep = Math.Min((float) Math.Pow(value, 2), limit);
|
||||
VineStep = Math.Min((float)Math.Pow(value, 2), limit);
|
||||
if (value > limit)
|
||||
{
|
||||
FlowerStep = Math.Min((float) Math.Pow(value - limit, 2), limit);
|
||||
FlowerStep = Math.Min((float)Math.Pow(value - limit, 2), limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (Type == VineTileType.Stem) { return; }
|
||||
|
||||
Type = (VineTileType) Sides;
|
||||
Type = (VineTileType)Sides;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -310,7 +310,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool IsSideBlocked(TileSide side) => BlockedSides.HasFlag(side) || Sides.HasFlag(side);
|
||||
|
||||
public static Rectangle CreatePlantRect(Vector2 pos) => new Rectangle((int) pos.X - Size / 2, (int) pos.Y + Size / 2, Size, Size);
|
||||
public static Rectangle CreatePlantRect(Vector2 pos) => new Rectangle((int)pos.X - Size / 2, (int)pos.Y + Size / 2, Size, Size);
|
||||
}
|
||||
|
||||
internal static class GrowthSideExtension
|
||||
@@ -318,7 +318,7 @@ namespace Barotrauma.Items.Components
|
||||
// K&R algorithm for counting how many bits are set in a bit field
|
||||
public static int Count(this TileSide side)
|
||||
{
|
||||
int n = (int) side;
|
||||
int n = (int)side;
|
||||
int count = 0;
|
||||
while (n != 0)
|
||||
{
|
||||
@@ -607,7 +607,7 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
foreach (VineTile vine in Vines)
|
||||
{
|
||||
vine.DecayDelay = (float) RandomDouble(0f, 30f);
|
||||
vine.DecayDelay = (float)RandomDouble(0f, 30f);
|
||||
}
|
||||
#endif
|
||||
#if SERVER
|
||||
@@ -742,7 +742,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
var (x, y, z, w) = GrowthWeights;
|
||||
float[] weights = { x, y, z, w };
|
||||
int index = (int) Math.Log2((int) side);
|
||||
int index = (int)Math.Log2((int)side);
|
||||
if (MathUtils.NearlyEqual(weights[index], 0f))
|
||||
{
|
||||
oldVines.FailedGrowthAttempts++;
|
||||
@@ -778,7 +778,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (VineTile otherVine in Vines)
|
||||
{
|
||||
var (distX, distY) = pos - otherVine.Position;
|
||||
int absDistX = (int) Math.Abs(distX), absDistY = (int) Math.Abs(distY);
|
||||
int absDistX = (int)Math.Abs(distX), absDistY = (int)Math.Abs(distY);
|
||||
|
||||
// check if the tile is within the with or height distance from us but ignore diagonals
|
||||
if (absDistX > newVine.Rect.Width || absDistY > newVine.Rect.Height || absDistX > 0 && absDistY > 0) { continue; }
|
||||
@@ -872,10 +872,10 @@ namespace Barotrauma.Items.Components
|
||||
foreach (VineTile vine in Vines)
|
||||
{
|
||||
XElement vineElement = new XElement("Vine");
|
||||
vineElement.Add(new XAttribute("sides", (int) vine.Sides));
|
||||
vineElement.Add(new XAttribute("blockedsides", (int) vine.BlockedSides));
|
||||
vineElement.Add(new XAttribute("sides", (int)vine.Sides));
|
||||
vineElement.Add(new XAttribute("blockedsides", (int)vine.BlockedSides));
|
||||
vineElement.Add(new XAttribute("pos", XMLExtensions.Vector2ToString(vine.Position)));
|
||||
vineElement.Add(new XAttribute("tile", (int) vine.Type));
|
||||
vineElement.Add(new XAttribute("tile", (int)vine.Type));
|
||||
vineElement.Add(new XAttribute("failedattempts", vine.FailedGrowthAttempts));
|
||||
#if SERVER
|
||||
vineElement.Add(new XAttribute("growthscale", Decayed ? 1.0f : 2.0f));
|
||||
@@ -902,10 +902,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (element.Name.ToString().Equals("vine", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
VineTileType type = (VineTileType) element.GetAttributeInt("tile", 0);
|
||||
VineTileType type = (VineTileType)element.GetAttributeInt("tile", 0);
|
||||
Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);
|
||||
TileSide sides = (TileSide) element.GetAttributeInt("sides", 0);
|
||||
TileSide blockedSides = (TileSide) element.GetAttributeInt("blockedsides", 0);
|
||||
TileSide sides = (TileSide)element.GetAttributeInt("sides", 0);
|
||||
TileSide blockedSides = (TileSide)element.GetAttributeInt("blockedsides", 0);
|
||||
int failedAttempts = element.GetAttributeInt("failedattempts", 0);
|
||||
float growthscale = element.GetAttributeFloat("growthscale", 0f);
|
||||
int flowerConfig = element.GetAttributeInt("flowerconfig", FoliageConfig.EmptyConfigValue);
|
||||
|
||||
@@ -295,12 +295,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (attachable)
|
||||
{
|
||||
DeattachFromWall();
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
item.body = body;
|
||||
}
|
||||
DeattachFromWall();
|
||||
}
|
||||
|
||||
if (Pusher != null) { Pusher.Enabled = false; }
|
||||
@@ -619,6 +618,10 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
item.DrawDepthOffset = SpriteDepthWhenDropped - item.SpriteDepth;
|
||||
#endif
|
||||
foreach (LightComponent light in item.GetComponents<LightComponent>())
|
||||
{
|
||||
light.CheckIfNeedsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ParseMsg()
|
||||
@@ -691,12 +694,30 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.Drop(character);
|
||||
item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false);
|
||||
//the light source won't get properly updated if lighting is disabled (even though the light sprite is still drawn when lighting is disabled)
|
||||
//so let's ensure the light source is up-to-date
|
||||
RefreshLightSources(item);
|
||||
}
|
||||
AttachToWall();
|
||||
}
|
||||
return true;
|
||||
|
||||
static void RefreshLightSources(Item item)
|
||||
{
|
||||
item.body?.UpdateDrawPosition();
|
||||
foreach (var light in item.GetComponents<LightComponent>())
|
||||
{
|
||||
light.SetLightSourceTransform();
|
||||
}
|
||||
item.GetComponent<ItemContainer>()?.SetContainedItemPositions();
|
||||
foreach (var containedItem in item.ContainedItems)
|
||||
{
|
||||
RefreshLightSources(containedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -64,7 +63,7 @@ namespace Barotrauma.Items.Components
|
||||
/// <summary>
|
||||
/// Defines items that boost the weapon functionality, like battery cell for stun batons.
|
||||
/// </summary>
|
||||
public readonly Identifier[] PreferredContainedItems;
|
||||
public readonly ImmutableHashSet<Identifier> PreferredContainedItems;
|
||||
|
||||
public MeleeWeapon(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
@@ -79,7 +78,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
item.IsShootable = true;
|
||||
item.RequireAimToUse = element.Parent.GetAttributeBool("requireaimtouse", true);
|
||||
PreferredContainedItems = element.GetAttributeIdentifierArray("preferredcontaineditems", Array.Empty<Identifier>());
|
||||
PreferredContainedItems = element.GetAttributeIdentifierArray("preferredcontaineditems", Array.Empty<Identifier>()).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
@@ -292,7 +291,6 @@ namespace Barotrauma.Items.Components
|
||||
item.body.PhysEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
private bool OnCollision(Fixture f1, Fixture f2, Contact contact)
|
||||
{
|
||||
if (User == null || User.Removed)
|
||||
@@ -390,15 +388,17 @@ namespace Barotrauma.Items.Components
|
||||
User = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
float damageMultiplier = 1 + User.GetStatValue(StatTypes.MeleeAttackMultiplier);
|
||||
damageMultiplier *= 1.0f + item.GetQualityModifier(Quality.StatType.StrikingPowerMultiplier);
|
||||
|
||||
Limb targetLimb = target.UserData as Limb;
|
||||
Character targetCharacter = targetLimb?.character ?? target.UserData as Character;
|
||||
GameMain.LuaCs.Hook.Call("meleeWeapon.handleImpact", this, target);
|
||||
if (Attack != null)
|
||||
{
|
||||
Attack.SetUser(User);
|
||||
Attack.DamageMultiplier = 1 + User.GetStatValue(StatTypes.MeleeAttackMultiplier);
|
||||
Attack.DamageMultiplier *= 1.0f + item.GetQualityModifier(Quality.StatType.StrikingPowerMultiplier);
|
||||
Attack.DamageMultiplier = damageMultiplier;
|
||||
|
||||
if (targetLimb != null)
|
||||
{
|
||||
@@ -420,7 +420,18 @@ namespace Barotrauma.Items.Components
|
||||
else if (target.UserData is Item targetItem && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0)
|
||||
{
|
||||
if (targetItem.Removed) { return; }
|
||||
Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
|
||||
var attackResult = Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
|
||||
#if CLIENT
|
||||
if (attackResult.Damage > 0.0f)
|
||||
{
|
||||
Character.Controlled?.UpdateHUDProgressBar(targetItem,
|
||||
targetItem.WorldPosition,
|
||||
targetItem.Condition / targetItem.MaxCondition,
|
||||
emptyColor: GUIStyle.HealthBarColorLow,
|
||||
fullColor: GUIStyle.HealthBarColorHigh,
|
||||
textTag: targetItem.Name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (target.UserData is Holdable holdable && holdable.CanPush)
|
||||
{
|
||||
@@ -460,7 +471,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (targetCharacter != null) //TODO: Allow OnUse to happen on structures too maybe??
|
||||
{
|
||||
ApplyStatusEffects(success ? ActionType.OnUse : ActionType.OnFailure, 1.0f, targetCharacter, targetLimb, user: User);
|
||||
ApplyStatusEffects(success ? ActionType.OnUse : ActionType.OnFailure, 1.0f, targetCharacter, targetLimb, user: User, afflictionMultiplier: damageMultiplier);
|
||||
}
|
||||
|
||||
if (DeleteOnUse)
|
||||
|
||||
@@ -165,31 +165,32 @@ namespace Barotrauma.Items.Components
|
||||
pickTimer = 0.0f;
|
||||
while (pickTimer < requiredTime && Screen.Selected != GameMain.SubEditorScreen)
|
||||
{
|
||||
//cancel if the item is currently selected
|
||||
//attempting to pick does not select the item, so if it is selected at this point, another ItemComponent
|
||||
//must have been selected and we should not keep deattaching (happens when for example interacting with
|
||||
//an electrical component while holding both a screwdriver and a wrench).
|
||||
if (picker.SelectedConstruction == item ||
|
||||
picker.IsKeyDown(InputType.Aim) ||
|
||||
!picker.CanInteractWith(item) ||
|
||||
item.Removed || item.ParentInventory != null)
|
||||
if (!CoroutineManager.Paused)
|
||||
{
|
||||
StopPicking(picker);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
//cancel if the item is currently selected
|
||||
//attempting to pick does not select the item, so if it is selected at this point, another ItemComponent
|
||||
//must have been selected and we should not keep deattaching (happens when for example interacting with
|
||||
//an electrical component while holding both a screwdriver and a wrench).
|
||||
if (picker.IsAnySelectedItem(item) ||
|
||||
picker.IsKeyDown(InputType.Aim) ||
|
||||
!picker.CanInteractWith(item) ||
|
||||
item.Removed || item.ParentInventory != null)
|
||||
{
|
||||
StopPicking(picker);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
Character.Controlled?.UpdateHUDProgressBar(
|
||||
this,
|
||||
item.WorldPosition,
|
||||
pickTimer / requiredTime,
|
||||
GUIStyle.Red, GUIStyle.Green,
|
||||
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
|
||||
Character.Controlled?.UpdateHUDProgressBar(
|
||||
this,
|
||||
item.WorldPosition,
|
||||
pickTimer / requiredTime,
|
||||
GUIStyle.Red, GUIStyle.Green,
|
||||
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
|
||||
#endif
|
||||
|
||||
picker.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
|
||||
pickTimer += CoroutineManager.DeltaTime;
|
||||
|
||||
picker.AnimController.UpdateUseItem(!picker.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
|
||||
pickTimer += CoroutineManager.DeltaTime;
|
||||
}
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
@@ -208,7 +209,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (picker != null)
|
||||
{
|
||||
picker.AnimController.Anim = AnimController.Animation.None;
|
||||
picker.AnimController.StopUsingItem();
|
||||
picker.PickingItem = null;
|
||||
}
|
||||
if (pickingCoroutine != null)
|
||||
@@ -286,7 +287,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(activePicker?.ID ?? (ushort)0);
|
||||
msg.WriteUInt16(activePicker?.ID ?? (ushort)0);
|
||||
}
|
||||
|
||||
public virtual void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
|
||||
private readonly HashSet<Identifier> fixableEntities;
|
||||
private readonly HashSet<Identifier> nonFixableEntities;
|
||||
private Vector2 pickedPosition;
|
||||
private float activeTimer;
|
||||
|
||||
@@ -135,6 +136,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
fixableEntities = new HashSet<Identifier>();
|
||||
nonFixableEntities = new HashSet<Identifier>();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -147,7 +149,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
fixableEntities.Add(subElement.GetAttributeIdentifier("identifier", ""));
|
||||
foreach (Identifier id in subElement.GetAttributeIdentifierArray("identifier", Array.Empty<Identifier>()))
|
||||
{
|
||||
fixableEntities.Add(id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "nonfixable":
|
||||
foreach (Identifier id in subElement.GetAttributeIdentifierArray("identifier", Array.Empty<Identifier>()))
|
||||
{
|
||||
nonFixableEntities.Add(id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -523,6 +534,7 @@ namespace Barotrauma.Items.Components
|
||||
if (sectionIndex < 0) { return false; }
|
||||
|
||||
if (!fixableEntities.Contains("structure") && !fixableEntities.Contains(targetStructure.Prefab.Identifier)) { return true; }
|
||||
if (nonFixableEntities.Contains(targetStructure.Prefab.Identifier) || nonFixableEntities.Any(t => targetStructure.Tags.Contains(t))) { return false; }
|
||||
|
||||
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, structure: targetStructure);
|
||||
FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
|
||||
|
||||
@@ -111,6 +111,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
|
||||
//return if the status effect got rid of the picker somehow
|
||||
if (picker == null || picker.Removed || !picker.HeldItems.Contains(item))
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.body.Dir != picker.AnimController.Dir) { item.FlipX(relativeToSub: false); }
|
||||
|
||||
|
||||
@@ -231,8 +231,6 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
public virtual bool RecreateGUIOnResolutionChange => false;
|
||||
|
||||
/// <summary>
|
||||
/// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
|
||||
/// </summary>
|
||||
@@ -399,7 +397,7 @@ namespace Barotrauma.Items.Components
|
||||
RelatedItem ri = RelatedItem.Load(element, returnEmpty, item.Name);
|
||||
if (ri != null)
|
||||
{
|
||||
if (ri.Identifiers.Length == 0)
|
||||
if (ri.Identifiers.Count == 0)
|
||||
{
|
||||
DisabledRequiredItems.Add(ri);
|
||||
}
|
||||
@@ -818,7 +816,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null, float applyOnUserFraction = 0.0f)
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null, float afflictionMultiplier = 1.0f, float applyOnUserFraction = 0.0f)
|
||||
{
|
||||
if (statusEffectLists == null) { return; }
|
||||
|
||||
@@ -830,13 +828,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (broken && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { continue; }
|
||||
if (user != null) { effect.SetUser(user); }
|
||||
effect.AfflictionMultiplier = afflictionMultiplier;
|
||||
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, useTarget, isNetworkEvent: false, checkCondition: false, worldPosition);
|
||||
if (user != null && applyOnUserFraction > 0.0f && effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
effect.AfflictionMultiplier = applyOnUserFraction;
|
||||
item.ApplyStatusEffect(effect, type, deltaTime, user, targetLimb == null ? null : user.AnimController.GetLimb(targetLimb.type), useTarget, false, false, worldPosition);
|
||||
effect.AfflictionMultiplier = 1.0f;
|
||||
}
|
||||
effect.AfflictionMultiplier = 1.0f;
|
||||
reducesCondition |= effect.ReducesItemCondition();
|
||||
}
|
||||
//if any of the effects reduce the item's condition, set the user for OnBroken effects as well
|
||||
@@ -1072,7 +1071,7 @@ namespace Barotrauma.Items.Components
|
||||
AIObjectiveContainItem containObjective = null;
|
||||
if (character.AIController is HumanAIController aiController)
|
||||
{
|
||||
containObjective = new AIObjectiveContainItem(character, container.ContainableItemIdentifiers.ToArray(), container, currentObjective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
containObjective = new AIObjectiveContainItem(character, container.ContainableItemIdentifiers, container, currentObjective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
{
|
||||
ItemCount = itemCount,
|
||||
Equip = equip,
|
||||
|
||||
@@ -49,9 +49,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public readonly NamedEvent<ItemContainer> OnContainedItemsChanged = new NamedEvent<ItemContainer>();
|
||||
|
||||
private bool alwaysContainedItemsSpawned;
|
||||
|
||||
public ItemInventory Inventory;
|
||||
public readonly ItemInventory Inventory;
|
||||
|
||||
private readonly List<ActiveContainedItem> activeContainedItems = new List<ActiveContainedItem>();
|
||||
|
||||
@@ -187,6 +189,16 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool RemoveContainedItemsOnDeconstruct { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects to lock the inventory
|
||||
/// </summary>
|
||||
public bool Locked
|
||||
{
|
||||
get { return Inventory.Locked; }
|
||||
set { Inventory.Locked = value; }
|
||||
}
|
||||
|
||||
private readonly ImmutableArray<SlotRestrictions> slotRestrictions;
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
@@ -214,9 +226,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private ImmutableHashSet<Identifier> containableItemIdentifiers;
|
||||
public IEnumerable<Identifier> ContainableItemIdentifiers => containableItemIdentifiers;
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
public ImmutableHashSet<Identifier> ContainableItemIdentifiers => containableItemIdentifiers;
|
||||
|
||||
public List<RelatedItem> ContainableItems { get; }
|
||||
|
||||
@@ -347,6 +357,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//no need to Update() if this item has no statuseffects and no physics body
|
||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||
OnContainedItemsChanged.Invoke(this);
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
@@ -360,6 +371,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//deactivate if the inventory is empty
|
||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||
OnContainedItemsChanged.Invoke(this);
|
||||
}
|
||||
|
||||
public bool CanBeContained(Item item)
|
||||
@@ -496,7 +508,7 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (AutoInteractWithContained && character.SelectedConstruction == null)
|
||||
if (AutoInteractWithContained && character.SelectedItem == null)
|
||||
{
|
||||
foreach (Item contained in Inventory.AllItems)
|
||||
{
|
||||
@@ -510,7 +522,15 @@ namespace Barotrauma.Items.Components
|
||||
var abilityItem = new AbilityItemContainer(item);
|
||||
character.CheckTalents(AbilityEffectType.OnOpenItemContainer, abilityItem);
|
||||
|
||||
return base.Select(character);
|
||||
if (item.ParentInventory?.Owner == character)
|
||||
{
|
||||
//can't select ItemContainers in the character's inventory (the inventory is drawn by hovering the cursor over the inventory slot, not as a GUIFrame)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.Select(character);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace Barotrauma.Items.Components
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (character == null || character.LockHands || character.Removed || !(character.AnimController is HumanoidAnimController)) return false;
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.Climbing;
|
||||
character.AnimController.StartClimbing();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<LimbPos> limbPositions = new List<LimbPos>();
|
||||
|
||||
private Direction dir;
|
||||
public Direction Direction => dir;
|
||||
|
||||
//the position where the user walks to when using the controller
|
||||
//(relative to the position of the item)
|
||||
@@ -128,6 +129,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If true, other items can be used simultaneously.")]
|
||||
public bool IsSecondaryItem
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Controller(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -150,7 +158,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (user == null
|
||||
|| user.Removed
|
||||
|| user.SelectedConstruction != item
|
||||
|| !user.IsAnySelectedItem(item)
|
||||
|| item.ParentInventory != null
|
||||
|| !user.CanInteractWith(item)
|
||||
|| (UsableIn == UseEnvironment.Water && !user.AnimController.InWater)
|
||||
@@ -165,7 +173,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
user.AnimController.Anim = AnimController.Animation.UsingConstruction;
|
||||
user.AnimController.StartUsingItem();
|
||||
|
||||
if (userPos != Vector2.Zero)
|
||||
{
|
||||
@@ -186,32 +194,34 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
diff.Y = 0.0f;
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && user != Character.Controlled)
|
||||
// Secondary items (like ladders or chairs) will control the character position over primary items
|
||||
// Only control the character position if the character doesn't have another secondary item already controlling it
|
||||
if (!user.HasSelectedAnotherSecondaryItem(Item))
|
||||
{
|
||||
if (Math.Abs(diff.X) > 20.0f)
|
||||
diff.Y = 0.0f;
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && user != Character.Controlled)
|
||||
{
|
||||
//wait for the character to walk to the correct position
|
||||
return;
|
||||
if (Math.Abs(diff.X) > 20.0f)
|
||||
{
|
||||
//wait for the character to walk to the correct position
|
||||
return;
|
||||
}
|
||||
else if (Math.Abs(diff.X) > 0.1f)
|
||||
{
|
||||
//aim to keep the collider at the correct position once close enough
|
||||
user.AnimController.Collider.LinearVelocity = new Vector2(
|
||||
diff.X * 0.1f,
|
||||
user.AnimController.Collider.LinearVelocity.Y);
|
||||
}
|
||||
}
|
||||
else if (Math.Abs(diff.X) > 0.1f)
|
||||
{
|
||||
//aim to keep the collider at the correct position once close enough
|
||||
user.AnimController.Collider.LinearVelocity = new Vector2(
|
||||
diff.X * 0.1f,
|
||||
user.AnimController.Collider.LinearVelocity.Y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Abs(diff.X) > 10.0f)
|
||||
else if (Math.Abs(diff.X) > 10.0f)
|
||||
{
|
||||
user.AnimController.TargetMovement = Vector2.Normalize(diff);
|
||||
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
return;
|
||||
}
|
||||
user.AnimController.TargetMovement = Vector2.Zero;
|
||||
}
|
||||
user.AnimController.TargetMovement = Vector2.Zero;
|
||||
UserInCorrectPosition = true;
|
||||
}
|
||||
}
|
||||
@@ -220,9 +230,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (limbPositions.Count == 0) { return; }
|
||||
|
||||
user.AnimController.Anim = AnimController.Animation.UsingConstruction;
|
||||
user.AnimController.StartUsingItem();
|
||||
|
||||
user.AnimController.ResetPullJoints();
|
||||
if (user.SelectedItem != null)
|
||||
{
|
||||
user.AnimController.ResetPullJoints(l => l.IsLowerBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.AnimController.ResetPullJoints();
|
||||
}
|
||||
|
||||
if (dir != 0) { user.AnimController.TargetDir = dir; }
|
||||
|
||||
@@ -230,7 +247,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Limb limb = user.AnimController.GetLimb(lb.LimbType);
|
||||
if (limb == null || !limb.body.Enabled) { continue; }
|
||||
|
||||
// Don't move lower body limbs if there's another selected secondary item that should control them
|
||||
if (limb.IsLowerBody && user.HasSelectedAnotherSecondaryItem(Item)) { continue; }
|
||||
// Don't move hands if there's a selected primary item that should control them
|
||||
if (!limb.IsLowerBody && Item == user.SelectedSecondaryItem && user.SelectedItem != null) { continue; }
|
||||
if (lb.AllowUsingLimb)
|
||||
{
|
||||
switch (lb.LimbType)
|
||||
@@ -247,12 +267,9 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
limb.Disabled = true;
|
||||
|
||||
Vector2 worldPosition = new Vector2(item.WorldRect.X, item.WorldRect.Y) + lb.Position * item.Scale;
|
||||
Vector2 diff = worldPosition - limb.WorldPosition;
|
||||
|
||||
limb.PullJointEnabled = true;
|
||||
limb.PullJointWorldAnchorB = limb.SimPosition + ConvertUnits.ToSimUnits(diff);
|
||||
}
|
||||
@@ -266,9 +283,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user == null || user.Removed ||
|
||||
user.SelectedConstruction != item || !user.CanInteractWith(item))
|
||||
if (user == null || user.Removed || !user.IsAnySelectedItem(item) || !user.CanInteractWith(item))
|
||||
{
|
||||
user = null;
|
||||
return false;
|
||||
@@ -290,46 +305,44 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
lastUsed = Timing.TotalTime;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (this.user != character)
|
||||
if (user != character)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.user == null || character.Removed ||
|
||||
this.user.SelectedConstruction != item || !character.CanInteractWith(item))
|
||||
if (user == null || character.Removed || !user.IsAnySelectedItem(item) || !character.CanInteractWith(item))
|
||||
{
|
||||
user = null;
|
||||
return false;
|
||||
}
|
||||
if (character == null)
|
||||
{
|
||||
this.user = null;
|
||||
return false;
|
||||
}
|
||||
if (character == null) return false;
|
||||
|
||||
focusTarget = GetFocusTarget();
|
||||
|
||||
if (focusTarget == null)
|
||||
{
|
||||
Vector2 centerPos = new Vector2(item.WorldRect.Center.X, item.WorldRect.Center.Y);
|
||||
|
||||
Vector2 offset = character.CursorWorldPosition - centerPos;
|
||||
offset.Y = -offset.Y;
|
||||
|
||||
targetRotation = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(offset));
|
||||
return false;
|
||||
}
|
||||
|
||||
character.ViewTarget = focusTarget;
|
||||
|
||||
#if CLIENT
|
||||
if (character == Character.Controlled && cam != null)
|
||||
{
|
||||
Lights.LightManager.ViewTarget = focusTarget;
|
||||
cam.TargetPos = focusTarget.WorldPosition;
|
||||
|
||||
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, (focusTarget as Item).Prefab.OffsetOnSelected * focusTarget.OffsetOnSelectedMultiplier, deltaTime * 10.0f);
|
||||
HideHUDs(true);
|
||||
}
|
||||
@@ -338,16 +351,12 @@ namespace Barotrauma.Items.Components
|
||||
if (!character.IsRemotePlayer || character.ViewTarget == focusTarget)
|
||||
{
|
||||
Vector2 centerPos = new Vector2(focusTarget.WorldRect.Center.X, focusTarget.WorldRect.Center.Y);
|
||||
|
||||
Turret turret = focusTarget.GetComponent<Turret>();
|
||||
if (turret != null)
|
||||
if (focusTarget.GetComponent<Turret>() is { } turret)
|
||||
{
|
||||
centerPos = new Vector2(focusTarget.WorldRect.X + turret.TransformedBarrelPos.X, focusTarget.WorldRect.Y - turret.TransformedBarrelPos.Y);
|
||||
}
|
||||
|
||||
Vector2 offset = character.CursorWorldPosition - centerPos;
|
||||
offset.Y = -offset.Y;
|
||||
|
||||
targetRotation = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(offset));
|
||||
}
|
||||
return true;
|
||||
@@ -425,9 +434,10 @@ namespace Barotrauma.Items.Components
|
||||
humanoidAnim.LockFlippingUntil = (float)Timing.TotalTime + 0.5f;
|
||||
}
|
||||
|
||||
if (character.SelectedConstruction == this.item) { character.SelectedConstruction = null; }
|
||||
if (character.SelectedItem == item) { character.SelectedItem = null; }
|
||||
if (character.SelectedSecondaryItem == item) { character.SelectedSecondaryItem = null; }
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character.AnimController.StopUsingItem();
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
HideHUDs(false);
|
||||
|
||||
+15
-11
@@ -3,6 +3,7 @@ using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -39,8 +40,6 @@ namespace Barotrauma.Items.Components
|
||||
[Editable, Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float DeconstructionSpeed { get; set; }
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
public Deconstructor(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -62,11 +61,18 @@ namespace Barotrauma.Items.Components
|
||||
inputContainer = containers[0];
|
||||
outputContainer = containers[1];
|
||||
|
||||
#if CLIENT
|
||||
Identifier eventIdentifier = new Identifier(nameof(Deconstructor));
|
||||
inputContainer.OnContainedItemsChanged.RegisterOverwriteExisting(eventIdentifier, OnItemSlotsChanged);
|
||||
#endif
|
||||
|
||||
OnItemLoadedProjSpecific();
|
||||
}
|
||||
|
||||
partial void OnItemLoadedProjSpecific();
|
||||
|
||||
partial void OnItemSlotsChanged(ItemContainer container);
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
MoveInputQueue();
|
||||
@@ -88,7 +94,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
progressTimer += deltaTime * Math.Min(powerConsumption <= 0.0f ? 1 : Voltage, 1.0f);
|
||||
progressTimer += deltaTime * Math.Min(powerConsumption <= 0.0f ? 1 : Voltage, MaxOverVoltageFactor);
|
||||
|
||||
float tinkeringStrength = 0f;
|
||||
if (repairable.IsTinkering)
|
||||
@@ -114,7 +120,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if ((Entity.Spawner?.IsInRemoveQueue(targetItem) ?? false) || !inputContainer.Inventory.AllItems.Contains(targetItem)) { continue; }
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
|
||||
(it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)) &&
|
||||
it.IsValidDeconstructor(item) &&
|
||||
(it.RequiredOtherItem.Length == 0 || it.RequiredOtherItem.Any(r => items.Any(it => it != targetItem && (it.HasTag(r) || it.Prefab.Identifier == r))))).ToList();
|
||||
|
||||
ProcessItem(targetItem, items, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.Prefab.DeconstructItems.Any());
|
||||
@@ -132,9 +138,7 @@ namespace Barotrauma.Items.Components
|
||||
var targetItem = inputContainer.Inventory.LastOrDefault();
|
||||
if (targetItem == null) { return; }
|
||||
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
|
||||
it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)).ToList();
|
||||
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it => it.IsValidDeconstructor(item)).ToList();
|
||||
float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * deconstructionSpeedModifier) : 1.0f;
|
||||
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
@@ -197,18 +201,18 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (DeconstructItem deconstructProduct in products)
|
||||
{
|
||||
CreateDeconstructProduct(deconstructProduct, inputItems, amountMultiplier);
|
||||
CreateDeconstructProduct(deconstructProduct, inputItems, (int)(amountMultiplier * deconstructProduct.Amount));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DeconstructItem deconstructProduct in validDeconstructItems)
|
||||
{
|
||||
CreateDeconstructProduct(deconstructProduct, inputItems, amountMultiplier);
|
||||
CreateDeconstructProduct(deconstructProduct, inputItems, (int)(amountMultiplier * deconstructProduct.Amount));
|
||||
}
|
||||
}
|
||||
|
||||
void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems, float amountMultiplier)
|
||||
void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems, int amount)
|
||||
{
|
||||
float percentageHealth = targetItem.Condition / targetItem.MaxCondition;
|
||||
|
||||
@@ -276,11 +280,11 @@ namespace Barotrauma.Items.Components
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructedInventory, itemDeconstructedInventory);
|
||||
}
|
||||
|
||||
int amount = (int)amountMultiplier;
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
spawnedItem.SpawnedInCurrentOutpost = item.SpawnedInCurrentOutpost;
|
||||
spawnedItem.StolenDuringRound = targetItem.StolenDuringRound;
|
||||
spawnedItem.AllowStealing = targetItem.AllowStealing;
|
||||
for (int i = 0; i < outputContainer.Capacity; i++)
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Barotrauma.Items.Components
|
||||
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
|
||||
if (Math.Abs(Force) > 1.0f)
|
||||
{
|
||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
|
||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, MaxOverVoltageFactor);
|
||||
float currForce = force * voltageFactor;
|
||||
float condition = item.Condition / item.MaxCondition;
|
||||
// Broken engine makes more noise.
|
||||
|
||||
@@ -76,8 +76,6 @@ namespace Barotrauma.Items.Components
|
||||
get { return outputContainer; }
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
private float progressState;
|
||||
|
||||
private readonly Dictionary<uint, int> fabricationLimits = new Dictionary<uint, int>();
|
||||
@@ -305,7 +303,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
float fabricationSpeedIncrease = 1f + tinkeringStrength * TinkeringSpeedIncrease;
|
||||
|
||||
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(powerConsumption <= 0 ? 1 : Voltage, 1.0f);
|
||||
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(powerConsumption <= 0 ? 1 : Voltage, MaxOverVoltageFactor);
|
||||
|
||||
UpdateRequiredTimeProjSpecific();
|
||||
|
||||
@@ -371,8 +369,7 @@ namespace Barotrauma.Items.Components
|
||||
var availableItems = availableIngredients[requiredPrefab.Identifier];
|
||||
var availableItem = availableItems.FirstOrDefault(potentialPrefab =>
|
||||
{
|
||||
return potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
|
||||
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
|
||||
return requiredItem.IsConditionSuitable(potentialPrefab.ConditionPercentage);
|
||||
});
|
||||
|
||||
if (availableItem == null) { continue; }
|
||||
@@ -556,8 +553,20 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
const int MaxCraftingSkill = 100;
|
||||
|
||||
//having a higher-than-100 skill (e.g. due to talents) gives +1 quality
|
||||
quality += fabricatedItem.RequiredSkills.All(s => user.GetSkillLevel(s.Identifier) >= MaxCraftingSkill) ? 1 : 0;
|
||||
quality += FabricationDegreeOfSuccess(user, fabricatedItem.RequiredSkills) >= 0.5f ? 1 : 0;
|
||||
foreach (var skill in fabricatedItem.RequiredSkills)
|
||||
{
|
||||
//+1 quality if the character's skill level is >20% from the min requirement towards max skill
|
||||
//e.g. if the skill requirement is 10 -> 28
|
||||
//40 -> 52
|
||||
//90 -> 92
|
||||
float skillRequirement = MathHelper.Lerp(skill.Level, MaxCraftingSkill, 0.2f);
|
||||
if (user.GetSkillLevel(skill.Identifier) > skillRequirement)
|
||||
{
|
||||
quality += 1;
|
||||
}
|
||||
}
|
||||
return quality;
|
||||
}
|
||||
|
||||
@@ -604,8 +613,7 @@ namespace Barotrauma.Items.Components
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
foreach (Item availablePrefab in availablePrefabs)
|
||||
{
|
||||
if (availablePrefab.ConditionPercentage / 100.0f >= requiredItem.MinCondition &&
|
||||
availablePrefab.ConditionPercentage / 100.0f <= requiredItem.MaxCondition)
|
||||
if (requiredItem.IsConditionSuitable(availablePrefab.ConditionPercentage))
|
||||
{
|
||||
availablePrefabsAmount++;
|
||||
}
|
||||
@@ -637,10 +645,13 @@ namespace Barotrauma.Items.Components
|
||||
if (skills.Length == 0) { return 1.0f; }
|
||||
if (character == null) { return 0.0f; }
|
||||
|
||||
float skillSum = (from t in skills let characterLevel = character.GetSkillLevel(t.Identifier) select (characterLevel - (t.Level * SkillRequirementMultiplier))).Sum();
|
||||
float average = skillSum / skills.Length;
|
||||
|
||||
return (average + 100.0f) / 2.0f / 100.0f;
|
||||
float minDegreeOfSuccess = 1.0f;
|
||||
foreach (var skill in skills)
|
||||
{
|
||||
float characterLevel = character.GetSkillLevel(skill.Identifier);
|
||||
minDegreeOfSuccess = Math.Min(minDegreeOfSuccess, (characterLevel - (skill.Level * SkillRequirementMultiplier) + 100.0f) / 2.0f / 100.0f);
|
||||
}
|
||||
return minDegreeOfSuccess;
|
||||
}
|
||||
|
||||
public override float GetSkillMultiplier()
|
||||
@@ -648,13 +659,16 @@ namespace Barotrauma.Items.Components
|
||||
return SkillRequirementMultiplier;
|
||||
}
|
||||
|
||||
|
||||
private readonly HashSet<Inventory> linkedInventories = new HashSet<Inventory>();
|
||||
|
||||
private void RefreshAvailableIngredients()
|
||||
{
|
||||
Character user = this.user;
|
||||
#if CLIENT
|
||||
user ??= Character.Controlled;
|
||||
#endif
|
||||
|
||||
linkedInventories.Clear();
|
||||
List<Item> itemList = new List<Item>();
|
||||
itemList.AddRange(inputContainer.Inventory.AllItems);
|
||||
foreach (MapEntity linkedTo in item.linkedTo)
|
||||
@@ -674,6 +688,7 @@ namespace Barotrauma.Items.Components
|
||||
itemContainer = deconstructor.OutputContainer;
|
||||
}
|
||||
|
||||
linkedInventories.Add(itemContainer.Inventory);
|
||||
itemList.AddRange(itemContainer.Inventory.AllItems);
|
||||
}
|
||||
}
|
||||
@@ -688,6 +703,7 @@ namespace Barotrauma.Items.Components
|
||||
if (user?.Inventory != null)
|
||||
{
|
||||
itemList.AddRange(user.Inventory.AllItems);
|
||||
linkedInventories.Add(user.Inventory);
|
||||
}
|
||||
availableIngredients.Clear();
|
||||
foreach (Item item in itemList)
|
||||
@@ -720,9 +736,7 @@ namespace Barotrauma.Items.Components
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
var availablePrefab = availablePrefabs.FirstOrDefault(potentialPrefab =>
|
||||
{
|
||||
return !usedItems.Contains(potentialPrefab) &&
|
||||
potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
|
||||
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
|
||||
return !usedItems.Contains(potentialPrefab) && requiredItem.IsConditionSuitable(potentialPrefab.ConditionPercentage);
|
||||
});
|
||||
if (availablePrefab == null) { continue; }
|
||||
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -52,7 +51,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
CurrFlow = Math.Min(PowerConsumption > 0 ? Voltage : 1.0f, 1.0f) * generatedAmount * 100.0f;
|
||||
CurrFlow = Math.Min(PowerConsumption > 0 ? Voltage : 1.0f, MaxOverVoltageFactor) * generatedAmount * 100.0f;
|
||||
float conditionMult = item.Condition / item.MaxCondition;
|
||||
//100% condition = 100% oxygen
|
||||
//50% condition = 25% oxygen
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull == null) { return; }
|
||||
|
||||
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, 1.0f);
|
||||
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, MaxOverVoltageFactor);
|
||||
|
||||
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
const float NetworkUpdateIntervalHigh = 0.5f;
|
||||
|
||||
const float TemperatureBoostAmount = 20;
|
||||
|
||||
//the rate at which the reactor is being run on (higher rate -> higher temperature)
|
||||
private float fissionRate;
|
||||
|
||||
@@ -46,6 +48,11 @@ namespace Barotrauma.Items.Components
|
||||
private Vector2 optimalFissionRate, allowedFissionRate;
|
||||
private Vector2 optimalTurbineOutput, allowedTurbineOutput;
|
||||
|
||||
private float? signalControlledTargetFissionRate, signalControlledTargetTurbineOutput;
|
||||
private double lastReceivedFissionRateSignalTime, lastReceivedTurbineOutputSignalTime;
|
||||
|
||||
private float temperatureBoost;
|
||||
|
||||
private bool _powerOn;
|
||||
|
||||
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
|
||||
@@ -226,7 +233,7 @@ namespace Barotrauma.Items.Components
|
||||
// (= bots turn autotemp back on when leaving the reactor)
|
||||
if (LastAIUser != null)
|
||||
{
|
||||
if (LastAIUser.SelectedConstruction != item && LastAIUser.CanInteractWith(item))
|
||||
if (LastAIUser.SelectedItem != item && LastAIUser.CanInteractWith(item))
|
||||
{
|
||||
AutoTemp = true;
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
@@ -241,6 +248,34 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
|
||||
if (signalControlledTargetFissionRate.HasValue && lastReceivedFissionRateSignalTime > Timing.TotalTime - 1)
|
||||
{
|
||||
TargetFissionRate = adjustValueWithoutOverShooting(TargetFissionRate, signalControlledTargetFissionRate.Value, deltaTime * 5.0f);
|
||||
#if CLIENT
|
||||
FissionRateScrollBar.BarScroll = TargetFissionRate / 100.0f;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
signalControlledTargetFissionRate = null;
|
||||
}
|
||||
if (signalControlledTargetTurbineOutput.HasValue && lastReceivedTurbineOutputSignalTime > Timing.TotalTime - 1)
|
||||
{
|
||||
TargetTurbineOutput = adjustValueWithoutOverShooting(TargetTurbineOutput, signalControlledTargetTurbineOutput.Value, deltaTime * 5.0f);
|
||||
#if CLIENT
|
||||
TurbineOutputScrollBar.BarScroll = TargetTurbineOutput / 100.0f;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
signalControlledTargetTurbineOutput = null;
|
||||
}
|
||||
|
||||
static float adjustValueWithoutOverShooting(float current, float target, float speed)
|
||||
{
|
||||
return target < current ? Math.Max(target, current - speed) : Math.Min(target, current + speed);
|
||||
}
|
||||
|
||||
prevAvailableFuel = AvailableFuel;
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
@@ -270,7 +305,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
float temperatureDiff = (heatAmount - turbineOutput) - Temperature;
|
||||
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
|
||||
//if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;
|
||||
temperatureBoost = adjustValueWithoutOverShooting(temperatureBoost, 0.0f, deltaTime);
|
||||
#if CLIENT
|
||||
temperatureBoostUpButton.Enabled = temperatureBoostDownButton.Enabled = Math.Abs(temperatureBoost) < TemperatureBoostAmount * 0.9f;
|
||||
#endif
|
||||
|
||||
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(TargetFissionRate, AvailableFuel), deltaTime);
|
||||
|
||||
@@ -438,7 +476,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float GetGeneratedHeat(float fissionRate)
|
||||
{
|
||||
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f;
|
||||
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f + temperatureBoost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -486,13 +524,15 @@ namespace Barotrauma.Items.Components
|
||||
if (temperature > allowedTemperature.Y)
|
||||
{
|
||||
item.SendSignal("1", "meltdown_warning");
|
||||
//faster meltdown if the item is in a bad condition
|
||||
meltDownTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
|
||||
|
||||
if (meltDownTimer > MeltdownDelay)
|
||||
if (!item.InvulnerableToDamage)
|
||||
{
|
||||
MeltDown();
|
||||
return;
|
||||
//faster meltdown if the item is in a bad condition
|
||||
meltDownTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
|
||||
if (meltDownTimer > MeltdownDelay)
|
||||
{
|
||||
MeltDown();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -505,7 +545,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
|
||||
#if SERVER
|
||||
if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedConstruction == item)
|
||||
if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedItem == item)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnReactorOverHeating(item, blameOnBroken.Character, deltaTime);
|
||||
}
|
||||
@@ -705,7 +745,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (lastUser != null && lastUser != character && lastUser != LastAIUser)
|
||||
{
|
||||
if (lastUser.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
if (lastUser.SelectedItem == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogReactorTaken").Value, null, 0.0f, "reactortaken".ToIdentifier(), 10.0f);
|
||||
}
|
||||
@@ -797,30 +837,31 @@ namespace Barotrauma.Items.Components
|
||||
AutoTemp = false;
|
||||
TargetFissionRate = 0.0f;
|
||||
TargetTurbineOutput = 0.0f;
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
registerUnsentChanges();
|
||||
}
|
||||
break;
|
||||
case "set_fissionrate":
|
||||
if (PowerOn && float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float newFissionRate))
|
||||
{
|
||||
TargetFissionRate = MathHelper.Clamp(newFissionRate, 0.0f, 100.0f);
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
#if CLIENT
|
||||
FissionRateScrollBar.BarScroll = TargetFissionRate / 100.0f;
|
||||
#endif
|
||||
signalControlledTargetFissionRate = MathHelper.Clamp(newFissionRate, 0.0f, 100.0f);
|
||||
lastReceivedFissionRateSignalTime = Timing.TotalTime;
|
||||
registerUnsentChanges();
|
||||
}
|
||||
break;
|
||||
case "set_turbineoutput":
|
||||
if (PowerOn && float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float newTurbineOutput))
|
||||
{
|
||||
TargetTurbineOutput = MathHelper.Clamp(newTurbineOutput, 0.0f, 100.0f);
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
#if CLIENT
|
||||
TurbineOutputScrollBar.BarScroll = TargetTurbineOutput / 100.0f;
|
||||
#endif
|
||||
signalControlledTargetTurbineOutput = MathHelper.Clamp(newTurbineOutput, 0.0f, 100.0f);
|
||||
lastReceivedTurbineOutputSignalTime = Timing.TotalTime;
|
||||
registerUnsentChanges();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
void registerUnsentChanges()
|
||||
{
|
||||
if (GameMain.NetworkMember is { IsServer: true }) { unsentChanges = true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace Barotrauma.Items.Components
|
||||
private const float MinZoom = 1.0f, MaxZoom = 4.0f;
|
||||
private float zoom = 1.0f;
|
||||
|
||||
/// <remarks>Accessed through event actions. Do not remove even if there are no references in code.</remarks>
|
||||
public bool UseDirectionalPing => useDirectionalPing;
|
||||
private bool useDirectionalPing = false;
|
||||
private Vector2 pingDirection = new Vector2(1.0f, 0.0f);
|
||||
private bool useMineralScanner;
|
||||
@@ -113,13 +115,34 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have mineral scanning mode. " +
|
||||
"Only available in-game when the Item has no Steering component.")]
|
||||
public bool HasMineralScanner { get; set; }
|
||||
private bool hasMineralScanner;
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have mineral scanning mode. ")]
|
||||
public bool HasMineralScanner
|
||||
{
|
||||
get => hasMineralScanner;
|
||||
set
|
||||
{
|
||||
#if CLIENT
|
||||
if (controlContainer != null && !hasMineralScanner && value)
|
||||
{
|
||||
AddMineralScannerSwitchToGUI();
|
||||
}
|
||||
#endif
|
||||
hasMineralScanner = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get { return zoom; }
|
||||
set
|
||||
{
|
||||
zoom = MathHelper.Clamp(value, MinZoom, MaxZoom);
|
||||
#if CLIENT
|
||||
zoomSlider.BarScroll = MathUtils.InverseLerp(MinZoom, MaxZoom, zoom);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public Mode CurrentMode
|
||||
@@ -144,8 +167,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
public Sonar(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -396,17 +417,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(currentMode == Mode.Active);
|
||||
msg.WriteBoolean(currentMode == Mode.Active);
|
||||
if (currentMode == Mode.Active)
|
||||
{
|
||||
msg.WriteRangedSingle(zoom, MinZoom, MaxZoom, 8);
|
||||
msg.Write(useDirectionalPing);
|
||||
msg.WriteBoolean(useDirectionalPing);
|
||||
if (useDirectionalPing)
|
||||
{
|
||||
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
|
||||
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
|
||||
}
|
||||
msg.Write(useMineralScanner);
|
||||
msg.WriteBoolean(useMineralScanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,6 @@ namespace Barotrauma.Items.Components
|
||||
set { posToMaintain = value; }
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
struct ObstacleDebugInfo
|
||||
{
|
||||
public Vector2 Point1;
|
||||
@@ -301,7 +299,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
float userSkill = 0.0f;
|
||||
if (user != null && controlledSub != null &&
|
||||
(user.SelectedConstruction == item || item.linkedTo.Contains(user.SelectedConstruction)))
|
||||
(user.SelectedItem == item || item.linkedTo.Contains(user.SelectedItem)))
|
||||
{
|
||||
userSkill = user.GetSkillLevel("helm") / 100.0f;
|
||||
}
|
||||
@@ -333,7 +331,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
showIceSpireWarning = false;
|
||||
if (user != null && user.Info != null &&
|
||||
user.SelectedConstruction == item &&
|
||||
user.SelectedItem == item &&
|
||||
controlledSub != null && controlledSub.Velocity.LengthSquared() > 0.01f)
|
||||
{
|
||||
IncreaseSkillLevel(user, deltaTime);
|
||||
@@ -389,7 +387,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
// if our tactical AI pilot has left, revert back to maintaining position
|
||||
if (navigateTactically && (user == null || user.SelectedConstruction != item))
|
||||
if (navigateTactically && (user == null || user.SelectedItem != item))
|
||||
{
|
||||
navigateTactically = false;
|
||||
AIRamTimer = 0f;
|
||||
@@ -722,7 +720,7 @@ namespace Barotrauma.Items.Components
|
||||
character.AIController.SteeringManager.Reset();
|
||||
if (objective.Override)
|
||||
{
|
||||
if (user != character && user != null && user.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
if (user != character && user != null && user.SelectedItem == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken").Value, null, 0.0f, "steeringtaken".ToIdentifier(), 10.0f);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma.Items.Components
|
||||
set { maxRechargeSpeed = Math.Max(value, 1.0f); }
|
||||
}
|
||||
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "The current recharge speed of the device.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The current recharge speed of the device.")]
|
||||
public float RechargeSpeed
|
||||
{
|
||||
get { return rechargeSpeed; }
|
||||
@@ -117,9 +117,6 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
|
||||
public bool ExponentialRechargeSpeed { get; set; }
|
||||
|
||||
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, IsPropertySaveable.Yes)]
|
||||
public float RechargeAdjustSpeed { get; set; }
|
||||
|
||||
private float efficiency;
|
||||
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, IsPropertySaveable.Yes, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
|
||||
public float Efficiency
|
||||
|
||||
@@ -243,8 +243,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//damage the item if voltage is too high (except if running as a client)
|
||||
float prevCondition = item.Condition;
|
||||
item.Condition -= deltaTime * 10.0f;
|
||||
|
||||
//some randomness to prevent all junction boxes from breaking at the same time
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.01f)
|
||||
{
|
||||
//damaged boxes are more sensitive to overvoltage (also preventing all boxes from breaking at the same time)
|
||||
float conditionFactor = MathHelper.Lerp(5.0f, 1.0f, item.Condition / item.MaxCondition);
|
||||
item.Condition -= deltaTime * Rand.Range(10.0f, 500.0f) * conditionFactor;
|
||||
}
|
||||
if (item.Condition <= 0.0f && prevCondition > 0.0f)
|
||||
{
|
||||
overloadCooldownTimer = OverloadCooldown;
|
||||
@@ -273,7 +278,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
return conn == powerOut ? PowerConsumption + ExtraLoad : 0;
|
||||
//not used in the vanilla game (junction boxes or relays don't output power)
|
||||
return conn == powerOut ? MathHelper.Max(-(PowerConsumption + ExtraLoad), 0) : 0;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
|
||||
@@ -94,6 +94,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected Connection powerIn, powerOut;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum voltage factor when the device is being overvolted. I.e. how many times more effectively the device can function when it's being overvolted
|
||||
/// </summary>
|
||||
protected const float MaxOverVoltageFactor = 2.0f;
|
||||
|
||||
protected virtual PowerPriority Priority { get { return PowerPriority.Default; } }
|
||||
|
||||
[Editable, Serialize(0.5f, IsPropertySaveable.Yes, description: "The minimum voltage required for the device to function. " +
|
||||
@@ -685,43 +690,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Efficient method to retrieve the batteries connected to the device
|
||||
/// Returns a list of batteries directly connected to the item
|
||||
/// </summary>
|
||||
/// <returns>All connected PowerContainers</returns>
|
||||
protected List<PowerContainer> GetConnectedBatteries(bool outputOnly = true)
|
||||
protected List<PowerContainer> GetDirectlyConnectedBatteries()
|
||||
{
|
||||
List<PowerContainer> batteries = new List<PowerContainer>();
|
||||
GridInfo supplyingGrid = null;
|
||||
|
||||
//Determine supplying grid, prefer PowerIn connection
|
||||
if (powerIn != null)
|
||||
if (item.Connections == null || powerIn == null) { return batteries; }
|
||||
foreach (Connection recipient in powerIn.Recipients)
|
||||
{
|
||||
if (powerIn.Grid != null)
|
||||
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
|
||||
var battery = recipient.Item?.GetComponent<PowerContainer>();
|
||||
if (battery != null)
|
||||
{
|
||||
supplyingGrid = powerIn.Grid;
|
||||
batteries.Add(battery);
|
||||
}
|
||||
}
|
||||
else if (powerOut != null)
|
||||
{
|
||||
if (powerOut.Grid != null)
|
||||
{
|
||||
supplyingGrid = powerOut.Grid;
|
||||
}
|
||||
}
|
||||
|
||||
if (supplyingGrid != null)
|
||||
{
|
||||
//Iterate through all connections to fine powerContainers
|
||||
foreach (Connection c in supplyingGrid.Connections)
|
||||
{
|
||||
PowerContainer pc = c.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null && (!outputOnly || pc.powerOut == c))
|
||||
{
|
||||
batteries.Add(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return batteries;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,6 +244,13 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool DamageDoors
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsStuckToTarget => StickTarget != null;
|
||||
|
||||
private Category originalCollisionCategories;
|
||||
@@ -288,7 +295,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void Launch(Character user, Vector2 simPosition, float rotation, float damageMultiplier = 1f)
|
||||
private void Launch(Character user, Vector2 simPosition, float rotation, float damageMultiplier = 1f, float launchImpulseModifier = 0f)
|
||||
{
|
||||
Item.body.ResetDynamics();
|
||||
Item.SetTransform(simPosition, rotation);
|
||||
@@ -299,7 +306,7 @@ namespace Barotrauma.Items.Components
|
||||
// Set user for hitscan projectiles to work properly.
|
||||
User = user;
|
||||
// Need to set null for non-characterusable items.
|
||||
Use(character: null);
|
||||
Use(character: null, launchImpulseModifier);
|
||||
// Set user for normal projectiles to work properly.
|
||||
User = user;
|
||||
if (Item.Removed) { return; }
|
||||
@@ -312,7 +319,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent, float damageMultiplier = 1f)
|
||||
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent, float damageMultiplier = 1f, float launchImpulseModifier = 0f)
|
||||
{
|
||||
//add the limbs of the shooter to the list of bodies to be ignored
|
||||
//so that the player can't shoot himself
|
||||
@@ -320,7 +327,7 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 projectilePos = weaponPos;
|
||||
//make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel
|
||||
if (Submarine.PickBody(weaponPos, spawnPos, IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking,
|
||||
customPredicate: (Fixture f) => { return !IgnoredBodies.Contains(f.Body); }) == null)
|
||||
customPredicate: (Fixture f) => { return IgnoredBodies == null || !IgnoredBodies.Contains(f.Body); }) == null)
|
||||
{
|
||||
//no obstacles -> we can spawn the projectile at the barrel
|
||||
projectilePos = spawnPos;
|
||||
@@ -334,7 +341,7 @@ namespace Barotrauma.Items.Components
|
||||
projectilePos = newPos;
|
||||
}
|
||||
}
|
||||
Launch(user, projectilePos, rotation, damageMultiplier);
|
||||
Launch(user, projectilePos, rotation, damageMultiplier, launchImpulseModifier);
|
||||
if (createNetworkEvent && !Item.Removed && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
#if SERVER
|
||||
@@ -344,7 +351,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool Use(Character character = null)
|
||||
public bool Use(Character character = null, float launchImpulseModifier = 0f)
|
||||
{
|
||||
if (character != null && !characterUsable) { return false; }
|
||||
|
||||
@@ -379,7 +386,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
item.body.SetTransform(item.body.SimPosition, launchAngle);
|
||||
float modifiedLaunchImpulse = LaunchImpulse * (1 + Rand.Range(-ImpulseSpread, ImpulseSpread));
|
||||
float modifiedLaunchImpulse = (LaunchImpulse + launchImpulseModifier) * (1 + Rand.Range(-ImpulseSpread, ImpulseSpread));
|
||||
DoLaunch(launchDir * modifiedLaunchImpulse * item.body.Mass);
|
||||
System.Diagnostics.Debug.WriteLine("launch: " + modifiedLaunchImpulse + " - " + item.body.LinearVelocity);
|
||||
}
|
||||
@@ -723,7 +730,7 @@ namespace Barotrauma.Items.Components
|
||||
private bool OnProjectileCollision(Fixture f1, Fixture target, Contact contact)
|
||||
{
|
||||
if (User != null && User.Removed) { User = null; return false; }
|
||||
if (IgnoredBodies.Contains(target.Body)) { return false; }
|
||||
if (IgnoredBodies != null && IgnoredBodies.Contains(target.Body)) { return false; }
|
||||
//ignore character colliders (the projectile only hits limbs)
|
||||
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
|
||||
{
|
||||
@@ -828,7 +835,7 @@ namespace Barotrauma.Items.Components
|
||||
private bool HandleProjectileCollision(Fixture target, Vector2 collisionNormal, Vector2 velocity)
|
||||
{
|
||||
if (User != null && User.Removed) { User = null; }
|
||||
if (IgnoredBodies.Contains(target.Body)) { return false; }
|
||||
if (IgnoredBodies != null && IgnoredBodies.Contains(target.Body)) { return false; }
|
||||
//ignore character colliders (the projectile only hits limbs)
|
||||
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
|
||||
{
|
||||
@@ -851,7 +858,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (target.Body.UserData is Limb limb)
|
||||
{
|
||||
if (!FriendlyFire && User != null && limb.character.IsFriendly(User))
|
||||
if (!FriendlyFire && User != null && limb.character.IsFriendly(User) && HumanAIController.IsOnFriendlyTeam(limb.character, User))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -870,9 +877,20 @@ namespace Barotrauma.Items.Components
|
||||
else if ((target.Body.UserData as Item ?? (target.Body.UserData as ItemComponent)?.Item) is Item targetItem)
|
||||
{
|
||||
if (targetItem.Removed) { return false; }
|
||||
if (Attack != null && targetItem.Prefab.DamagedByProjectiles && targetItem.Condition > 0)
|
||||
if (Attack != null && (targetItem.Prefab.DamagedByProjectiles || DamageDoors && targetItem.GetComponent<Door>() != null) && targetItem.Condition > 0)
|
||||
{
|
||||
attackResult = Attack.DoDamage(User ?? Attacker, targetItem, item.WorldPosition, 1.0f);
|
||||
attackResult = Attack.DoDamage(User ?? Attacker, targetItem, item.WorldPosition, 1.0f);
|
||||
#if CLIENT
|
||||
if (attackResult.Damage > 0.0f)
|
||||
{
|
||||
Character.Controlled?.UpdateHUDProgressBar(targetItem,
|
||||
targetItem.WorldPosition,
|
||||
targetItem.Condition / targetItem.MaxCondition,
|
||||
emptyColor: GUIStyle.HealthBarColorLow,
|
||||
fullColor: GUIStyle.HealthBarColorHigh,
|
||||
textTag: targetItem.Name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (target.Body.UserData is IDamageable damageable)
|
||||
@@ -1056,7 +1074,7 @@ namespace Barotrauma.Items.Components
|
||||
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
|
||||
}
|
||||
}
|
||||
IgnoredBodies.Clear();
|
||||
IgnoredBodies?.Clear();
|
||||
}
|
||||
|
||||
private void StickToTarget(Body targetBody, Vector2 axis)
|
||||
|
||||
@@ -29,25 +29,14 @@ namespace Barotrauma.Items.Components
|
||||
FirepowerMultiplier,
|
||||
StrikingPowerMultiplier,
|
||||
StrikingSpeedMultiplier,
|
||||
FiringRateMultiplier,
|
||||
// unused as of now
|
||||
AttackMultiplier,
|
||||
// unused as of now
|
||||
AttackSpeedMultiplier,
|
||||
ForceDoorsOpenSpeedMultiplier,
|
||||
RangedSpreadReduction,
|
||||
ChargeSpeedMultiplier,
|
||||
MovementSpeedMultiplier,
|
||||
EffectivenessMultiplier,
|
||||
PowerOutputMultiplier,
|
||||
ConsumptionReductionMultiplier,
|
||||
FiringRateMultiplier
|
||||
}
|
||||
|
||||
private readonly Dictionary<StatType, float> statValues = new Dictionary<StatType, float>();
|
||||
|
||||
private int qualityLevel;
|
||||
|
||||
[Editable, Serialize(0, IsPropertySaveable.Yes)]
|
||||
[Editable(MinValueInt = 0, MaxValueInt = MaxQuality), Serialize(0, IsPropertySaveable.Yes)]
|
||||
public int QualityLevel
|
||||
{
|
||||
get { return qualityLevel; }
|
||||
|
||||
@@ -343,7 +343,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
CurrentFixer.CheckTalents(AbilityEffectType.OnStopTinkering);
|
||||
}
|
||||
CurrentFixer.AnimController.Anim = AnimController.Animation.None;
|
||||
CurrentFixer.AnimController.StopUsingItem();
|
||||
CurrentFixer = null;
|
||||
currentRepairItem = null;
|
||||
currentFixerAction = FixActions.None;
|
||||
@@ -430,7 +430,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
if (CurrentFixer != null && (CurrentFixer.SelectedConstruction != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
|
||||
if (CurrentFixer != null && (CurrentFixer.SelectedItem != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
|
||||
{
|
||||
StopRepairing(CurrentFixer);
|
||||
return;
|
||||
@@ -502,7 +502,7 @@ namespace Barotrauma.Items.Components
|
||||
SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
|
||||
CurrentFixer.CheckTalents(AbilityEffectType.OnRepairComplete);
|
||||
}
|
||||
if (CurrentFixer?.SelectedConstruction == item) { CurrentFixer.SelectedConstruction = null; }
|
||||
if (CurrentFixer?.SelectedItem == item) { CurrentFixer.SelectedItem = null; }
|
||||
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
|
||||
wasBroken = false;
|
||||
StopRepairing(CurrentFixer);
|
||||
@@ -603,6 +603,9 @@ namespace Barotrauma.Items.Components
|
||||
private bool ShouldDeteriorate()
|
||||
{
|
||||
if (Level.IsLoadedFriendlyOutpost) { return false; }
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode) { return false; }
|
||||
#endif
|
||||
|
||||
if (LastActiveTime > Timing.TotalTime) { return true; }
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
|
||||
+11
-2
@@ -48,6 +48,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (value == null) { return; }
|
||||
output = value;
|
||||
//reactivate (we may not have been previously sending a signal, but might now)
|
||||
IsActive = true;
|
||||
if (output.Length > MaxOutputLength && (item.Submarine == null || !item.Submarine.Loading))
|
||||
{
|
||||
output = output.Substring(0, MaxOutputLength);
|
||||
@@ -63,6 +65,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (value == null) { return; }
|
||||
falseOutput = value;
|
||||
//reactivate (we may not have been previously sending a signal, but might now)
|
||||
IsActive = true;
|
||||
if (falseOutput.Length > MaxOutputLength && (item.Submarine == null || !item.Submarine.Loading))
|
||||
{
|
||||
falseOutput = falseOutput.Substring(0, MaxOutputLength);
|
||||
@@ -82,9 +86,14 @@ namespace Barotrauma.Items.Components
|
||||
public sealed override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
int receivedInputs = 0;
|
||||
bool allInputsTimedOut = true;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] <= timeFrame) { receivedInputs += 1; }
|
||||
if (timeSinceReceived[i] <= timeFrame)
|
||||
{
|
||||
allInputsTimedOut = false;
|
||||
receivedInputs += 1;
|
||||
}
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
@@ -93,7 +102,7 @@ namespace Barotrauma.Items.Components
|
||||
if (string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
//deactivate the component if state is false and there's no false output (will be woken up by non-zero signals in ReceiveSignal)
|
||||
if (!state) { IsActive = false; }
|
||||
if (!state && allInputsTimedOut) { IsActive = false; }
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace Barotrauma.Items.Components
|
||||
private readonly HashSet<Wire> wires;
|
||||
public IReadOnlyCollection<Wire> Wires => wires;
|
||||
|
||||
private bool enumeratingWires;
|
||||
private readonly HashSet<Wire> removedWires = new HashSet<Wire>();
|
||||
|
||||
private readonly Item item;
|
||||
|
||||
public readonly bool IsOutput;
|
||||
@@ -239,7 +242,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
prevOtherConnection.recipientsDirty = true;
|
||||
}
|
||||
wires.Remove(wire);
|
||||
if (enumeratingWires)
|
||||
{
|
||||
removedWires.Add(wire);
|
||||
}
|
||||
else
|
||||
{
|
||||
wires.Remove(wire);
|
||||
}
|
||||
recipientsDirty = true;
|
||||
}
|
||||
|
||||
@@ -278,6 +288,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void SendSignal(Signal signal)
|
||||
{
|
||||
enumeratingWires = true;
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
Connection recipient = wire.OtherConnection(this);
|
||||
@@ -305,6 +316,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
enumeratingWires = false;
|
||||
foreach (var removedWire in removedWires)
|
||||
{
|
||||
wires.Remove(removedWire);
|
||||
}
|
||||
removedWires.Clear();
|
||||
}
|
||||
|
||||
public void ClearConnections()
|
||||
@@ -317,13 +334,23 @@ namespace Barotrauma.Items.Components
|
||||
Powered.ChangedConnections.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
wire.RemoveConnection(this);
|
||||
recipientsDirty = true;
|
||||
}
|
||||
wires.Clear();
|
||||
|
||||
if (enumeratingWires)
|
||||
{
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
removedWires.Add(wire);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wires.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeFromLoaded()
|
||||
|
||||
+15
-6
@@ -179,7 +179,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
if (user == null || user.SelectedConstruction != item)
|
||||
if (user == null || user.SelectedItem != item)
|
||||
{
|
||||
#if SERVER
|
||||
if (user != null) { item.CreateServerEvent(this); }
|
||||
@@ -196,7 +196,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
user.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
|
||||
user.AnimController.UpdateUseItem(!user.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
@@ -206,7 +206,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public override bool Select(Character picker)
|
||||
public bool CanRewire()
|
||||
{
|
||||
//attaching wires to items with a body is not allowed
|
||||
//(signal items remove their bodies when attached to a wall)
|
||||
@@ -214,6 +214,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Select(Character picker)
|
||||
{
|
||||
if (!CanRewire())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
user = picker;
|
||||
#if SERVER
|
||||
@@ -392,14 +401,14 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteVariableUInt32((uint)connection.Wires.Count);
|
||||
foreach (Wire wire in connection.Wires)
|
||||
{
|
||||
msg.Write(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
msg.WriteUInt16(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((ushort)DisconnectedWires.Count);
|
||||
msg.WriteUInt16((ushort)DisconnectedWires.Count);
|
||||
foreach (Wire disconnectedWire in DisconnectedWires)
|
||||
{
|
||||
msg.Write(disconnectedWire.Item.ID);
|
||||
msg.WriteUInt16(disconnectedWire.Item.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-2
@@ -155,7 +155,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//use semicolon as a separator because comma may be needed in the signals (for color or vector values for example)
|
||||
//kind of hacky, we should probably add support for (string) arrays to SerializableEntityEditor so this wouldn't be needed
|
||||
get { return signals == null ? "" : string.Join(";", signals); }
|
||||
get { return signals == null ? string.Empty : string.Join(";", signals); }
|
||||
set
|
||||
{
|
||||
if (value == null) { return; }
|
||||
@@ -167,7 +167,31 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
private bool[] elementStates;
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "", alwaysUseInstanceValues: true)]
|
||||
public string ElementStates
|
||||
{
|
||||
get { return elementStates == null ? string.Empty : string.Join(",", elementStates); }
|
||||
set
|
||||
{
|
||||
if (value == null) { return; }
|
||||
if (customInterfaceElementList.Count > 0)
|
||||
{
|
||||
string[] splitValues = value == "" ? Array.Empty<string>() : value.Split(',');
|
||||
for (int i = 0; i < customInterfaceElementList.Count && i < splitValues.Length; i++)
|
||||
{
|
||||
if (!bool.TryParse(splitValues[i], out bool val)) { continue; }
|
||||
customInterfaceElementList[i].State = val;
|
||||
#if CLIENT
|
||||
if (uiElements != null && i < uiElements.Count && uiElements[i] is GUITickBox tickBox)
|
||||
{
|
||||
tickBox.Selected = val;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<CustomInterfaceElement> customInterfaceElementList = new List<CustomInterfaceElement>();
|
||||
|
||||
@@ -207,8 +231,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
IsActive = true;
|
||||
InitProjSpecific();
|
||||
//load these here to ensure the UI elements (created in InitProjSpecific) are up-to-date
|
||||
Labels = element.GetAttributeString("labels", "");
|
||||
Signals = element.GetAttributeString("signals", "");
|
||||
ElementStates = element.GetAttributeString("elementstates", "");
|
||||
}
|
||||
|
||||
private void UpdateLabels(string[] newLabels)
|
||||
@@ -386,6 +412,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
labels = customInterfaceElementList.Select(ci => ci.Label).ToArray();
|
||||
signals = customInterfaceElementList.Select(ci => ci.Signal).ToArray();
|
||||
elementStates = customInterfaceElementList.Select(ci => ci.State).ToArray();
|
||||
return base.Save(parentElement);
|
||||
}
|
||||
|
||||
|
||||
@@ -248,8 +248,19 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null && IsOn &&
|
||||
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
|
||||
CheckIfNeedsUpdate();
|
||||
}
|
||||
|
||||
public void CheckIfNeedsUpdate()
|
||||
{
|
||||
if (!IsOn)
|
||||
{
|
||||
base.IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null &&
|
||||
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
|
||||
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
|
||||
{
|
||||
lightBrightness = 1.0f;
|
||||
@@ -261,6 +272,10 @@ namespace Barotrauma.Items.Components
|
||||
Light.ParentSub = item.Submarine;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
base.IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace Barotrauma.Items.Components
|
||||
Human = 1,
|
||||
Monster = 2,
|
||||
Wall = 4,
|
||||
Any = Human | Monster | Wall,
|
||||
Pet = 8,
|
||||
Any = Human | Monster | Wall | Pet,
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
@@ -253,7 +254,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (Target.HasFlag(TargetType.Human) || Target.HasFlag(TargetType.Monster))
|
||||
if (Target.HasFlag(TargetType.Human) || Target.HasFlag(TargetType.Pet) || Target.HasFlag(TargetType.Monster))
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
@@ -267,7 +268,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Human)) { continue; }
|
||||
}
|
||||
else if (!c.IsPet)
|
||||
else if (c.IsPet)
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Pet)) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Monster)) { continue; }
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(isOn);
|
||||
msg.WriteBoolean(isOn);
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
|
||||
@@ -106,11 +106,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
case "set_text":
|
||||
case "signal_in":
|
||||
if (string.IsNullOrEmpty(signal.value)) { return; }
|
||||
if (signal.value.Length > MaxMessageLength)
|
||||
{
|
||||
signal.value = signal.value.Substring(0, MaxMessageLength);
|
||||
}
|
||||
|
||||
string inputSignal = signal.value.Replace("\\n", "\n");
|
||||
ShowOnDisplay(inputSignal, addToHistory: true, TextColor);
|
||||
break;
|
||||
|
||||
@@ -58,6 +58,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool WaterDetected => isInWater;
|
||||
public int WaterPercentage => GetWaterPercentage(item.CurrentHull);
|
||||
|
||||
public WaterDetector(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
|
||||
@@ -309,7 +309,7 @@ namespace Barotrauma.Items.Components
|
||||
if (nodes.Count == 0) { return; }
|
||||
|
||||
Character user = item.ParentInventory?.Owner as Character;
|
||||
editNodeDelay = (user?.SelectedConstruction == null) ? editNodeDelay - deltaTime : 0.5f;
|
||||
editNodeDelay = (user?.SelectedItem == null) ? editNodeDelay - deltaTime : 0.5f;
|
||||
|
||||
Submarine sub = item.Submarine;
|
||||
if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
|
||||
@@ -369,7 +369,7 @@ namespace Barotrauma.Items.Components
|
||||
user.AnimController.Collider.ApplyForce(forceDir * user.Mass * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.5f);
|
||||
if (diff.LengthSquared() > 50.0f * 50.0f)
|
||||
{
|
||||
user.AnimController.UpdateUseItem(true, user.WorldPosition + pullBackDir * Math.Min(150.0f, diff.Length()));
|
||||
user.AnimController.UpdateUseItem(!user.IsClimbing, user.WorldPosition + pullBackDir * Math.Min(150.0f, diff.Length()));
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
@@ -428,7 +428,7 @@ namespace Barotrauma.Items.Components
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character != Character.Controlled) { return false; }
|
||||
if (character.SelectedConstruction != null) { return false; }
|
||||
if (character.HasSelectedAnyItem) { return false; }
|
||||
#if CLIENT
|
||||
if (Screen.Selected == GameMain.SubEditorScreen && !PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -21,12 +22,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float rotation, targetRotation;
|
||||
|
||||
private float reload, reloadTime;
|
||||
private float reload, reloadTime, delayBetweenBurst;
|
||||
private int shotsPerBurst, shotCounter;
|
||||
|
||||
private float minRotation, maxRotation;
|
||||
|
||||
private float launchImpulse;
|
||||
|
||||
private float damageMultiplier;
|
||||
|
||||
private Camera cam;
|
||||
|
||||
private float angularVelocity;
|
||||
@@ -94,6 +98,16 @@ namespace Barotrauma.Items.Components
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool flipFiringOffset;
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If enabled, the firing offset will alternate from left to right (i.e. flipping the x-component of the offset each shot.)")]
|
||||
public bool AlternatingFiringOffset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Vector2 TransformedBarrelPos
|
||||
{
|
||||
get
|
||||
@@ -116,6 +130,20 @@ namespace Barotrauma.Items.Components
|
||||
set { reloadTime = value; }
|
||||
}
|
||||
|
||||
[Editable(1, 100), Serialize(1, IsPropertySaveable.No, description: "How many projectiles needs to be shot before we add an extra break? Think of the double coilgun.")]
|
||||
public int ShotsPerBurst
|
||||
{
|
||||
get { return shotsPerBurst; }
|
||||
set { shotsPerBurst = value; }
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1000.0f, decimals: 3), Serialize(0.0f, IsPropertySaveable.No, description: "An extra delay between the bursts. Added to the reload.")]
|
||||
public float DelayBetweenBursts
|
||||
{
|
||||
get { return delayBetweenBurst; }
|
||||
set { delayBetweenBurst = value; }
|
||||
}
|
||||
|
||||
[Editable(0.1f, 10f), Serialize(1.0f, IsPropertySaveable.No, description: "Modifies the duration of retraction of the barrell after recoil to get back to the original position after shooting. Reload time affects this too.")]
|
||||
public float RetractionDurationMultiplier
|
||||
{
|
||||
@@ -137,6 +165,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "Multiplies the damage the turret deals by this amount.")]
|
||||
public float DamageMultiplier
|
||||
{
|
||||
get { return damageMultiplier; }
|
||||
set { damageMultiplier = value; }
|
||||
}
|
||||
|
||||
[Serialize(1, IsPropertySaveable.No, description: "How many projectiles the weapon launches when fired once.")]
|
||||
public int ProjectileCount
|
||||
{
|
||||
@@ -525,7 +560,7 @@ namespace Barotrauma.Items.Components
|
||||
UpdateLightComponents();
|
||||
}
|
||||
|
||||
private void UpdateLightComponents()
|
||||
public void UpdateLightComponents()
|
||||
{
|
||||
if (lightComponents != null)
|
||||
{
|
||||
@@ -602,9 +637,9 @@ namespace Barotrauma.Items.Components
|
||||
if (projectiles.Any())
|
||||
{
|
||||
ItemContainer projectileContainer = projectiles.First().Item.Container?.GetComponent<ItemContainer>();
|
||||
if (projectileContainer != null && projectileContainer.Item != item)
|
||||
{
|
||||
projectileContainer?.Item.Use(deltaTime, null);
|
||||
if (projectileContainer != null && projectileContainer.Item != item)
|
||||
{
|
||||
projectileContainer?.Item.Use(deltaTime, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -667,7 +702,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!ignorePower)
|
||||
{
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
List<PowerContainer> batteries = GetDirectlyConnectedBatteries();
|
||||
float neededPower = GetPowerRequiredToShoot();
|
||||
|
||||
// tinkering is currently not factored into the common method as it is checked only when shooting
|
||||
@@ -764,6 +799,15 @@ namespace Barotrauma.Items.Components
|
||||
private void Launch(Item projectile, Character user = null, float? launchRotation = null, float tinkeringStrength = 0f)
|
||||
{
|
||||
reload = reloadTime;
|
||||
if (ShotsPerBurst > 1)
|
||||
{
|
||||
shotCounter++;
|
||||
if (shotCounter >= ShotsPerBurst)
|
||||
{
|
||||
reload += DelayBetweenBursts;
|
||||
shotCounter = 0;
|
||||
}
|
||||
}
|
||||
reload /= 1f + (tinkeringStrength * TinkeringReloadDecrease);
|
||||
|
||||
if (user != null)
|
||||
@@ -773,6 +817,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (projectile != null)
|
||||
{
|
||||
if (AlternatingFiringOffset)
|
||||
{
|
||||
flipFiringOffset = !flipFiringOffset;
|
||||
}
|
||||
activeProjectiles.Add(projectile);
|
||||
projectile.Drop(null, setTransform: false);
|
||||
if (projectile.body != null)
|
||||
@@ -796,9 +844,9 @@ namespace Barotrauma.Items.Components
|
||||
projectileComponent.Attacker = projectileComponent.User = user;
|
||||
if (projectileComponent.Attack != null)
|
||||
{
|
||||
projectileComponent.Attack.DamageMultiplier = 1f + (TinkeringDamageIncrease * tinkeringStrength);
|
||||
projectileComponent.Attack.DamageMultiplier = (1f * DamageMultiplier) + (TinkeringDamageIncrease * tinkeringStrength);
|
||||
}
|
||||
projectileComponent.Use();
|
||||
projectileComponent.Use(null, LaunchImpulse);
|
||||
projectile.GetComponent<Rope>()?.Attach(item, projectile);
|
||||
projectileComponent.User = user;
|
||||
|
||||
@@ -1018,7 +1066,7 @@ namespace Barotrauma.Items.Components
|
||||
bool canShoot = true;
|
||||
if (!HasPowerToShoot())
|
||||
{
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
List<PowerContainer> batteries = GetDirectlyConnectedBatteries();
|
||||
float lowestCharge = 0.0f;
|
||||
PowerContainer batteryToLoad = null;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
@@ -1104,7 +1152,7 @@ namespace Barotrauma.Items.Components
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
var loadItemsObjective = AIContainItems<Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true, dropItemOnDeselected: true);
|
||||
loadItemsObjective.ignoredContainerIdentifiers = new Identifier[] { ((MapEntity)containerItem).Prefab.Identifier };
|
||||
loadItemsObjective.ignoredContainerIdentifiers = ((MapEntity)containerItem).Prefab.Identifier.ToEnumerable().ToImmutableHashSet();
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, formatCapitals: FormatCapitals.Yes).Value,
|
||||
@@ -1260,9 +1308,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
float dist = Vector2.Distance(closestPoint, item.WorldPosition);
|
||||
|
||||
//add one px to make sure the visibility raycast doesn't miss the cell due to the end position being right at the edge of the cell
|
||||
closestPoint += (closestPoint - item.WorldPosition) / Math.Max(dist, 1);
|
||||
|
||||
if (dist > AIRange + 1000) { continue; }
|
||||
float dot = 0;
|
||||
if (item.Submarine.Velocity != Vector2.Zero)
|
||||
if (!MathUtils.NearlyEqual(item.Submarine.Velocity, Vector2.Zero))
|
||||
{
|
||||
dot = Vector2.Dot(Vector2.Normalize(item.Submarine.Velocity), Vector2.Normalize(closestPoint - item.Submarine.WorldPosition));
|
||||
}
|
||||
@@ -1293,7 +1345,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.AIController.SelectedAiTarget == null && !hadCurrentTarget)
|
||||
{
|
||||
if (CreatureMetrics.Instance.RecentlyEncountered.Contains(closestEnemy.SpeciesName))
|
||||
if (CreatureMetrics.Instance.RecentlyEncountered.Contains(closestEnemy.SpeciesName) || closestEnemy.IsHuman)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogNewTargetSpotted").Value,
|
||||
identifier: "newtargetspotted".ToIdentifier(),
|
||||
@@ -1452,7 +1504,9 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 transformedFiringOffset = Vector2.Zero;
|
||||
if (useOffset)
|
||||
{
|
||||
transformedFiringOffset = MathUtils.RotatePoint(new Vector2(-FiringOffset.Y, -FiringOffset.X) * item.Scale, -rotation);
|
||||
Vector2 currOffSet = FiringOffset;
|
||||
if (flipFiringOffset) { currOffSet.X = -currOffSet.X; }
|
||||
transformedFiringOffset = MathUtils.RotatePoint(new Vector2(-currOffSet.Y, -currOffSet.X) * item.Scale, -rotation);
|
||||
}
|
||||
return new Vector2(item.WorldRect.X + transformedBarrelPos.X + transformedFiringOffset.X, item.WorldRect.Y - transformedBarrelPos.Y + transformedFiringOffset.Y);
|
||||
}
|
||||
@@ -1562,6 +1616,7 @@ namespace Barotrauma.Items.Components
|
||||
targetRotation = rotation = (minRotation + maxRotation) / 2;
|
||||
|
||||
UpdateTransformedBarrelPos();
|
||||
UpdateLightComponents();
|
||||
}
|
||||
|
||||
public override void FlipY(bool relativeToSub)
|
||||
@@ -1583,6 +1638,7 @@ namespace Barotrauma.Items.Components
|
||||
targetRotation = rotation = (minRotation + maxRotation) / 2;
|
||||
|
||||
UpdateTransformedBarrelPos();
|
||||
UpdateLightComponents();
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
@@ -1665,12 +1721,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (TryExtractEventData(extraData, out EventData eventData))
|
||||
{
|
||||
msg.Write(eventData.Projectile.ID);
|
||||
msg.WriteUInt16(eventData.Projectile.ID);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(rotation, minRotation, maxRotation), minRotation, maxRotation, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write((ushort)0);
|
||||
msg.WriteUInt16((ushort)0);
|
||||
float wrappedTargetRotation = targetRotation;
|
||||
while (wrappedTargetRotation < minRotation && MathUtils.IsValid(wrappedTargetRotation))
|
||||
{
|
||||
|
||||
@@ -560,7 +560,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
public override void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write((byte)Variant);
|
||||
msg.WriteByte((byte)Variant);
|
||||
base.ServerEventWrite(msg, c, extraData);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user