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);
|
||||
}
|
||||
|
||||
|
||||
@@ -969,14 +969,14 @@ namespace Barotrauma
|
||||
|
||||
public void SharedWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write((byte)capacity);
|
||||
msg.WriteByte((byte)capacity);
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
msg.WriteRangedInteger(slots[i].Items.Count, 0, MaxStackSize);
|
||||
for (int j = 0; j < Math.Min(slots[i].Items.Count, MaxStackSize); j++)
|
||||
{
|
||||
var item = slots[i].Items[j];
|
||||
msg.Write(item?.ID ?? (ushort)0);
|
||||
msg.WriteUInt16(item?.ID ?? (ushort)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ namespace Barotrauma
|
||||
private bool hasComponentsToDraw;
|
||||
|
||||
public PhysicsBody body;
|
||||
private float waterDragCoefficient;
|
||||
|
||||
public readonly XElement StaticBodyConfig;
|
||||
|
||||
@@ -304,6 +305,10 @@ namespace Barotrauma
|
||||
{
|
||||
light.SetLightSourceTransform();
|
||||
}
|
||||
foreach (var turret in GetComponents<Turret>())
|
||||
{
|
||||
turret.UpdateLightComponents();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -418,6 +423,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Color? HighlightColor;
|
||||
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
|
||||
@@ -459,7 +466,7 @@ namespace Barotrauma
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.No)]
|
||||
/// <summary>
|
||||
/// Can be used by status effects or conditionals to modify the sound range
|
||||
/// Can be used by status effects or conditionals to modify the sight range
|
||||
/// </summary>
|
||||
public new float SightRange
|
||||
{
|
||||
@@ -519,6 +526,7 @@ namespace Barotrauma
|
||||
{
|
||||
float prevConditionPercentage = ConditionPercentage;
|
||||
healthMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity);
|
||||
RecalculateConditionValues();
|
||||
condition = MaxCondition * prevConditionPercentage / 100.0f;
|
||||
RecalculateConditionValues();
|
||||
}
|
||||
@@ -747,6 +755,9 @@ namespace Barotrauma
|
||||
get { return Prefab.Linkable; }
|
||||
}
|
||||
|
||||
public float WorldPositionX => WorldPosition.X;
|
||||
public float WorldPositionY => WorldPosition.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to move the item from XML (e.g. to correct the positions of items whose sprite origin has been changed)
|
||||
/// </summary>
|
||||
@@ -807,6 +818,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLadder { get; }
|
||||
|
||||
public bool IsSecondaryItem { get; }
|
||||
|
||||
public Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine, ushort id = Entity.NullEntityID, bool callOnItemLoaded = true)
|
||||
: this(new Rectangle(
|
||||
(int)(position.X - itemPrefab.Sprite.size.X / 2 * itemPrefab.Scale),
|
||||
@@ -853,12 +868,14 @@ namespace Barotrauma
|
||||
|
||||
SetActiveSprite();
|
||||
|
||||
ContentXElement bodyElement = null;
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "body":
|
||||
float density = subElement.GetAttributeFloat("density", 10.0f);
|
||||
bodyElement = subElement;
|
||||
float density = subElement.GetAttributeFloat("density", Physics.NeutralDensity);
|
||||
float minDensity = subElement.GetAttributeFloat("mindensity", density);
|
||||
float maxDensity = subElement.GetAttributeFloat("maxdensity", density);
|
||||
if (minDensity < maxDensity)
|
||||
@@ -898,6 +915,7 @@ namespace Barotrauma
|
||||
body = new PhysicsBody(subElement, ConvertUnits.ToSimUnits(Position), Scale, density, collisionCategory, collidesWith, findNewContacts: false);
|
||||
body.FarseerBody.AngularDamping = subElement.GetAttributeFloat("angulardamping", 0.2f);
|
||||
body.FarseerBody.LinearDamping = subElement.GetAttributeFloat("lineardamping", 0.1f);
|
||||
body.FarseerBody.LinearDamping = subElement.GetAttributeFloat("lineardamping", 0.1f);
|
||||
body.UserData = this;
|
||||
break;
|
||||
case "trigger":
|
||||
@@ -987,6 +1005,8 @@ namespace Barotrauma
|
||||
if (body != null)
|
||||
{
|
||||
body.Submarine = submarine;
|
||||
waterDragCoefficient = bodyElement.GetAttributeFloat("waterdragcoefficient",
|
||||
GetComponent<Projectile>() != null || GetComponent<Throwable>() != null ? 0.1f : 1.0f);
|
||||
}
|
||||
|
||||
//cache connections into a dictionary for faster lookups
|
||||
@@ -1014,6 +1034,9 @@ namespace Barotrauma
|
||||
|
||||
qualityComponent = GetComponent<Quality>();
|
||||
|
||||
IsLadder = GetComponent<Ladder>() != null;
|
||||
IsSecondaryItem = IsLadder || GetComponent<Controller>() is { IsSecondaryItem: true };
|
||||
|
||||
InitProjSpecific();
|
||||
|
||||
if (callOnItemLoaded)
|
||||
@@ -1522,7 +1545,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ConditionalMatches(PropertyConditional conditional)
|
||||
public bool ConditionalMatches(PropertyConditional conditional)
|
||||
{
|
||||
if (string.IsNullOrEmpty(conditional.TargetItemComponentName))
|
||||
{
|
||||
@@ -1890,10 +1913,22 @@ namespace Barotrauma
|
||||
|
||||
if (needsWaterCheck)
|
||||
{
|
||||
bool wasInWater = inWater;
|
||||
inWater = IsInWater();
|
||||
bool waterProof = WaterProof;
|
||||
if (inWater)
|
||||
{
|
||||
//the item has gone through the surface of the water
|
||||
if (!wasInWater && CurrentHull != null && body != null && body.LinearVelocity.Y < -1.0f)
|
||||
{
|
||||
Splash();
|
||||
if (GetComponent<Projectile>() is not { IsActive: true })
|
||||
{
|
||||
//slow the item down (not physically accurate, but looks good enough)
|
||||
body.LinearVelocity *= 0.2f;
|
||||
}
|
||||
}
|
||||
|
||||
Item container = this.Container;
|
||||
while (!waterProof && container != null)
|
||||
{
|
||||
@@ -1921,7 +1956,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
partial void Splash();
|
||||
|
||||
public void UpdateTransform()
|
||||
{
|
||||
if (body == null) { return; }
|
||||
@@ -2009,23 +2045,47 @@ namespace Barotrauma
|
||||
{
|
||||
float floor = CurrentHull.Rect.Y - CurrentHull.Rect.Height;
|
||||
float waterLevel = floor + CurrentHull.WaterVolume / CurrentHull.Rect.Width;
|
||||
|
||||
//forceFactor is 1.0f if the item is completely submerged,
|
||||
//and goes to 0.0f as the item goes through the surface
|
||||
forceFactor = Math.Min((waterLevel - Position.Y) / rect.Height, 1.0f);
|
||||
if (forceFactor <= 0.0f) return;
|
||||
if (forceFactor <= 0.0f) { return; }
|
||||
}
|
||||
|
||||
bool moving = body.LinearVelocity.LengthSquared() > 0.001f;
|
||||
float volume = body.Mass / body.Density;
|
||||
if (moving)
|
||||
{
|
||||
//measure velocity from the velocity of the front of the item and apply the drag to the other end to get the drag to turn the item the "pointy end first"
|
||||
|
||||
var uplift = -GameMain.World.Gravity * forceFactor * volume;
|
||||
//a more "proper" (but more expensive) way to do this would be to e.g. calculate the drag separately for each edge of the fixture
|
||||
//but since we define the "front" as the "pointy end", we can cheat a bit by using that, and actually even make the drag appear more realistic in some cases
|
||||
//(e.g. a bullet with a rectangular fixture would be just as "aerodynamic" travelling backwards, but with this method we get it to turn the correct way)
|
||||
Vector2 localFront = body.GetLocalFront();
|
||||
Vector2 frontVel = body.FarseerBody.GetLinearVelocityFromLocalPoint(localFront);
|
||||
|
||||
Vector2 drag = body.LinearVelocity * volume;
|
||||
float speed = frontVel.Length();
|
||||
float drag = speed * speed * waterDragCoefficient * volume * Physics.NeutralDensity;
|
||||
//very small drag on active projectiles to prevent affecting their trajectories much
|
||||
if (body.FarseerBody.IsBullet) { drag *= 0.1f; }
|
||||
Vector2 dragVec = -frontVel / speed * drag;
|
||||
|
||||
body.ApplyForce((uplift - drag) * 10.0f);
|
||||
//apply the force slightly towards the back of the item to make it turn the front first
|
||||
Vector2 back = body.FarseerBody.GetWorldPoint(-localFront * 0.01f);
|
||||
body.ApplyForce(dragVec, back);
|
||||
}
|
||||
|
||||
//no need to apply buoyancy if the item is still and not light enough to float
|
||||
if (moving || body.Density < 10.0f)
|
||||
{
|
||||
Vector2 buoyancy = -GameMain.World.Gravity * forceFactor * volume * Physics.NeutralDensity;
|
||||
body.ApplyForce(buoyancy);
|
||||
}
|
||||
|
||||
//apply simple angular drag
|
||||
body.ApplyTorque(body.AngularVelocity * volume * -0.05f);
|
||||
if (Math.Abs(body.AngularVelocity) > 0.0001f)
|
||||
{
|
||||
body.ApplyTorque(body.AngularVelocity * volume * -0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2131,7 +2191,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Note: This function generates garbage and might be a bit too heavy to be used once per frame.
|
||||
/// </summary>
|
||||
public List<T> GetConnectedComponents<T>(bool recursive = false, bool allowTraversingBackwards = true) where T : ItemComponent
|
||||
public List<T> GetConnectedComponents<T>(bool recursive = false, bool allowTraversingBackwards = true, Func<Connection, bool> connectionFilter = null) where T : ItemComponent
|
||||
{
|
||||
List<T> connectedComponents = new List<T>();
|
||||
|
||||
@@ -2147,6 +2207,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Connection c in connectionPanel.Connections)
|
||||
{
|
||||
if (connectionFilter != null && !connectionFilter.Invoke(c)) { continue; }
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
@@ -2494,16 +2555,30 @@ namespace Barotrauma
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (user.SelectedConstruction == this)
|
||||
if (user.SelectedItem == this)
|
||||
{
|
||||
if (user.IsKeyHit(InputType.Select) || forceSelectKey)
|
||||
{
|
||||
user.SelectedConstruction = null;
|
||||
user.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
else if (user.SelectedSecondaryItem == this)
|
||||
{
|
||||
if (user.IsKeyHit(InputType.Select) || forceSelectKey)
|
||||
{
|
||||
user.SelectedSecondaryItem = null;
|
||||
}
|
||||
}
|
||||
else if (selected)
|
||||
{
|
||||
user.SelectedConstruction = this;
|
||||
if (IsSecondaryItem)
|
||||
{
|
||||
user.SelectedSecondaryItem = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.SelectedItem = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2809,77 +2884,77 @@ namespace Barotrauma
|
||||
var propertyOwner = allProperties.Find(p => p.property == property);
|
||||
if (allProperties.Count > 1)
|
||||
{
|
||||
msg.Write((byte)allProperties.FindIndex(p => p.property == property));
|
||||
msg.WriteByte((byte)allProperties.FindIndex(p => p.property == property));
|
||||
}
|
||||
|
||||
object value = property.GetValue(propertyOwner.obj);
|
||||
if (value is string stringVal)
|
||||
{
|
||||
msg.Write(stringVal);
|
||||
msg.WriteString(stringVal);
|
||||
}
|
||||
else if (value is Identifier idValue)
|
||||
{
|
||||
msg.Write(idValue);
|
||||
msg.WriteIdentifier(idValue);
|
||||
}
|
||||
else if (value is float floatVal)
|
||||
{
|
||||
msg.Write(floatVal);
|
||||
msg.WriteSingle(floatVal);
|
||||
}
|
||||
else if (value is int intVal)
|
||||
{
|
||||
msg.Write(intVal);
|
||||
msg.WriteInt32(intVal);
|
||||
}
|
||||
else if (value is bool boolVal)
|
||||
{
|
||||
msg.Write(boolVal);
|
||||
msg.WriteBoolean(boolVal);
|
||||
}
|
||||
else if (value is Color color)
|
||||
{
|
||||
msg.Write(color.R);
|
||||
msg.Write(color.G);
|
||||
msg.Write(color.B);
|
||||
msg.Write(color.A);
|
||||
msg.WriteByte(color.R);
|
||||
msg.WriteByte(color.G);
|
||||
msg.WriteByte(color.B);
|
||||
msg.WriteByte(color.A);
|
||||
}
|
||||
else if (value is Vector2 vector2)
|
||||
{
|
||||
msg.Write(vector2.X);
|
||||
msg.Write(vector2.Y);
|
||||
msg.WriteSingle(vector2.X);
|
||||
msg.WriteSingle(vector2.Y);
|
||||
}
|
||||
else if (value is Vector3 vector3)
|
||||
{
|
||||
msg.Write(vector3.X);
|
||||
msg.Write(vector3.Y);
|
||||
msg.Write(vector3.Z);
|
||||
msg.WriteSingle(vector3.X);
|
||||
msg.WriteSingle(vector3.Y);
|
||||
msg.WriteSingle(vector3.Z);
|
||||
}
|
||||
else if (value is Vector4 vector4)
|
||||
{
|
||||
msg.Write(vector4.X);
|
||||
msg.Write(vector4.Y);
|
||||
msg.Write(vector4.Z);
|
||||
msg.Write(vector4.W);
|
||||
msg.WriteSingle(vector4.X);
|
||||
msg.WriteSingle(vector4.Y);
|
||||
msg.WriteSingle(vector4.Z);
|
||||
msg.WriteSingle(vector4.W);
|
||||
}
|
||||
else if (value is Point point)
|
||||
{
|
||||
msg.Write(point.X);
|
||||
msg.Write(point.Y);
|
||||
msg.WriteInt32(point.X);
|
||||
msg.WriteInt32(point.Y);
|
||||
}
|
||||
else if (value is Rectangle rect)
|
||||
{
|
||||
msg.Write(rect.X);
|
||||
msg.Write(rect.Y);
|
||||
msg.Write(rect.Width);
|
||||
msg.Write(rect.Height);
|
||||
msg.WriteInt32(rect.X);
|
||||
msg.WriteInt32(rect.Y);
|
||||
msg.WriteInt32(rect.Width);
|
||||
msg.WriteInt32(rect.Height);
|
||||
}
|
||||
else if (value is Enum)
|
||||
{
|
||||
msg.Write((int)value);
|
||||
msg.WriteInt32((int)value);
|
||||
}
|
||||
else if (value is string[] a)
|
||||
{
|
||||
msg.Write(a.Length);
|
||||
msg.WriteInt32(a.Length);
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
msg.Write(a[i] ?? "");
|
||||
msg.WriteString(a[i] ?? "");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3292,10 +3367,8 @@ namespace Barotrauma
|
||||
item.PurchasedNewSwap = false;
|
||||
}
|
||||
|
||||
float condition = element.GetAttributeFloat("condition", item.MaxCondition);
|
||||
item.condition = MathHelper.Clamp(condition, 0, item.MaxCondition);
|
||||
item.condition = MathHelper.Clamp(item.condition, 0, item.MaxCondition);
|
||||
item.lastSentCondition = item.condition;
|
||||
|
||||
item.RecalculateConditionValues();
|
||||
item.SetActiveSprite();
|
||||
|
||||
@@ -3453,7 +3526,8 @@ namespace Barotrauma
|
||||
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.SelectedConstruction == this) { character.SelectedConstruction = null; }
|
||||
if (character.SelectedItem == this) { character.SelectedItem = null; }
|
||||
if (character.SelectedSecondaryItem == this) { character.SelectedSecondaryItem = null; }
|
||||
}
|
||||
|
||||
Door door = GetComponent<Door>();
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Barotrauma
|
||||
readonly struct DeconstructItem
|
||||
{
|
||||
public readonly Identifier ItemIdentifier;
|
||||
//number of items to output
|
||||
public readonly int Amount;
|
||||
//minCondition does <= check, meaning that below or equal to min condition will be skipped.
|
||||
public readonly float MinCondition;
|
||||
//maxCondition does > check, meaning that above this max the deconstruct item will be skipped.
|
||||
@@ -37,6 +39,7 @@ namespace Barotrauma
|
||||
public DeconstructItem(XElement element, Identifier parentDebugName)
|
||||
{
|
||||
ItemIdentifier = element.GetAttributeIdentifier("identifier", "");
|
||||
Amount = element.GetAttributeInt("amount", 1);
|
||||
MinCondition = element.GetAttributeFloat("mincondition", -0.1f);
|
||||
MaxCondition = element.GetAttributeFloat("maxcondition", 1.0f);
|
||||
OutConditionMin = element.GetAttributeFloat("outconditionmin", element.GetAttributeFloat("outcondition", 1.0f));
|
||||
@@ -50,6 +53,11 @@ namespace Barotrauma
|
||||
InfoText = element.GetAttributeString("infotext", string.Empty);
|
||||
InfoTextOnOtherItemMissing = element.GetAttributeString("infotextonotheritemmissing", string.Empty);
|
||||
}
|
||||
|
||||
public bool IsValidDeconstructor(Item deconstructor)
|
||||
{
|
||||
return RequiredDeconstructor.Length == 0 || RequiredDeconstructor.Any(r => deconstructor.HasTag(r) || deconstructor.Prefab.Identifier == r);
|
||||
}
|
||||
}
|
||||
|
||||
class FabricationRecipe
|
||||
@@ -59,6 +67,10 @@ namespace Barotrauma
|
||||
public abstract IEnumerable<ItemPrefab> ItemPrefabs { get; }
|
||||
public abstract UInt32 UintIdentifier { get; }
|
||||
|
||||
public abstract bool MatchesItem(Item item);
|
||||
|
||||
public abstract ItemPrefab FirstMatchingPrefab { get; }
|
||||
|
||||
public RequiredItem(int amount, float minCondition, float maxCondition, bool useCondition)
|
||||
{
|
||||
Amount = amount;
|
||||
@@ -70,17 +82,40 @@ namespace Barotrauma
|
||||
public readonly float MinCondition;
|
||||
public readonly float MaxCondition;
|
||||
public readonly bool UseCondition;
|
||||
|
||||
public bool IsConditionSuitable(float conditionPercentage)
|
||||
{
|
||||
float normalizedCondition = conditionPercentage / 100.0f;
|
||||
if (MathUtils.NearlyEqual(normalizedCondition, MinCondition) || MathUtils.NearlyEqual(normalizedCondition, MaxCondition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (normalizedCondition >= MinCondition && normalizedCondition <= MaxCondition)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class RequiredItemByIdentifier : RequiredItem
|
||||
{
|
||||
public readonly Identifier ItemPrefabIdentifier;
|
||||
|
||||
public ItemPrefab ItemPrefab => ItemPrefab.Prefabs.TryGet(ItemPrefabIdentifier, out var prefab) ? prefab
|
||||
: MapEntityPrefab.FindByName(ItemPrefabIdentifier.Value) as ItemPrefab ?? throw new Exception($"No ItemPrefab with identifier or name \"{ItemPrefabIdentifier}\"");
|
||||
|
||||
public override UInt32 UintIdentifier { get; }
|
||||
|
||||
public override IEnumerable<ItemPrefab> ItemPrefabs => ItemPrefab.ToEnumerable();
|
||||
|
||||
public override ItemPrefab FirstMatchingPrefab => ItemPrefab;
|
||||
|
||||
public override bool MatchesItem(Item item)
|
||||
{
|
||||
return item?.Prefab.Identifier == ItemPrefabIdentifier;
|
||||
}
|
||||
|
||||
public RequiredItemByIdentifier(Identifier itemPrefab, int amount, float minCondition, float maxCondition, bool useCondition) : base(amount, minCondition, maxCondition, useCondition)
|
||||
{
|
||||
ItemPrefabIdentifier = itemPrefab;
|
||||
@@ -92,10 +127,19 @@ namespace Barotrauma
|
||||
public class RequiredItemByTag : RequiredItem
|
||||
{
|
||||
public readonly Identifier Tag;
|
||||
|
||||
public override UInt32 UintIdentifier { get; }
|
||||
|
||||
public override IEnumerable<ItemPrefab> ItemPrefabs => ItemPrefab.Prefabs.Where(p => p.Tags.Contains(Tag));
|
||||
|
||||
public override ItemPrefab FirstMatchingPrefab => ItemPrefab.Prefabs.FirstOrDefault(p => p.Tags.Contains(Tag));
|
||||
|
||||
public override bool MatchesItem(Item item)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
return item.HasTag(Tag);
|
||||
}
|
||||
|
||||
public RequiredItemByTag(Identifier tag, int amount, float minCondition, float maxCondition, bool useCondition) : base(amount, minCondition, maxCondition, useCondition)
|
||||
{
|
||||
Tag = tag;
|
||||
@@ -191,10 +235,10 @@ namespace Barotrauma
|
||||
if (requiredItemIdentifier != Identifier.Empty)
|
||||
{
|
||||
var existing = requiredItems.FindIndex(r =>
|
||||
r is RequiredItemByIdentifier ri &&
|
||||
ri.ItemPrefabIdentifier == requiredItemIdentifier &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
r is RequiredItemByIdentifier ri &&
|
||||
ri.ItemPrefabIdentifier == requiredItemIdentifier &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
if (existing >= 0)
|
||||
{
|
||||
amount += requiredItems[existing].Amount;
|
||||
@@ -205,10 +249,10 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
var existing = requiredItems.FindIndex(r =>
|
||||
r is RequiredItemByTag rt &&
|
||||
rt.Tag == requiredItemTag &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
r is RequiredItemByTag rt &&
|
||||
rt.Tag == requiredItemTag &&
|
||||
MathUtils.NearlyEqual(r.MinCondition, minCondition) &&
|
||||
MathUtils.NearlyEqual(r.MaxCondition, maxCondition));
|
||||
if (existing >= 0)
|
||||
{
|
||||
amount += requiredItems[existing].Amount;
|
||||
@@ -393,12 +437,106 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public readonly struct CommonnessInfo
|
||||
{
|
||||
public float Commonness
|
||||
{
|
||||
get
|
||||
{
|
||||
return commonness;
|
||||
}
|
||||
}
|
||||
public float AbyssCommonness
|
||||
{
|
||||
get
|
||||
{
|
||||
return abyssCommonness ?? 0.0f;
|
||||
}
|
||||
}
|
||||
public float CaveCommonness
|
||||
{
|
||||
get
|
||||
{
|
||||
return caveCommonness ?? Commonness;
|
||||
}
|
||||
}
|
||||
public bool CanAppear
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Commonness > 0.0f) { return true; }
|
||||
if (AbyssCommonness > 0.0f) { return true; }
|
||||
if (CaveCommonness > 0.0f) { return true; }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly float commonness;
|
||||
public readonly float? abyssCommonness;
|
||||
public readonly float? caveCommonness;
|
||||
|
||||
public CommonnessInfo(XElement element)
|
||||
{
|
||||
this.commonness = Math.Max(element?.GetAttributeFloat("commonness", 0.0f) ?? 0.0f, 0.0f);
|
||||
|
||||
float? abyssCommonness = null;
|
||||
XAttribute abyssCommonnessAttribute = element?.GetAttribute("abysscommonness") ?? element?.GetAttribute("abyss");
|
||||
if (abyssCommonnessAttribute != null)
|
||||
{
|
||||
abyssCommonness = Math.Max(abyssCommonnessAttribute.GetAttributeFloat(0.0f), 0.0f);
|
||||
}
|
||||
this.abyssCommonness = abyssCommonness;
|
||||
|
||||
float? caveCommonness = null;
|
||||
XAttribute caveCommonnessAttribute = element?.GetAttribute("cavecommonness") ?? element?.GetAttribute("cave");
|
||||
if (caveCommonnessAttribute != null)
|
||||
{
|
||||
caveCommonness = Math.Max(caveCommonnessAttribute.GetAttributeFloat(0.0f), 0.0f);
|
||||
}
|
||||
this.caveCommonness = caveCommonness;
|
||||
}
|
||||
|
||||
public CommonnessInfo(float commonness, float? abyssCommonness, float? caveCommonness)
|
||||
{
|
||||
this.commonness = commonness;
|
||||
this.abyssCommonness = abyssCommonness != null ? (float?)Math.Max(abyssCommonness.Value, 0.0f) : null;
|
||||
this.caveCommonness = caveCommonness != null ? (float?)Math.Max(caveCommonness.Value, 0.0f) : null;
|
||||
}
|
||||
|
||||
public CommonnessInfo WithInheritedCommonness(CommonnessInfo? parentInfo)
|
||||
{
|
||||
return new CommonnessInfo(commonness,
|
||||
abyssCommonness ?? parentInfo?.abyssCommonness,
|
||||
caveCommonness ?? parentInfo?.caveCommonness);
|
||||
}
|
||||
|
||||
public CommonnessInfo WithInheritedCommonness(params CommonnessInfo?[] parentInfos)
|
||||
{
|
||||
CommonnessInfo info = this;
|
||||
foreach (var parentInfo in parentInfos)
|
||||
{
|
||||
info = info.WithInheritedCommonness(parentInfo);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
public float GetCommonness(Level.TunnelType tunnelType)
|
||||
{
|
||||
if (tunnelType == Level.TunnelType.Cave)
|
||||
{
|
||||
return CaveCommonness;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Commonness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How likely it is for the item to spawn in a level of a given type.
|
||||
/// Key = name of the LevelGenerationParameters (empty string = default value) /* TODO: empty string = default value???? */
|
||||
/// Value = commonness
|
||||
/// </summary>
|
||||
public ImmutableDictionary<Identifier, float> LevelCommonness { get; private set; }
|
||||
private ImmutableDictionary<Identifier, CommonnessInfo> LevelCommonness { get; set; }
|
||||
|
||||
public readonly struct FixedQuantityResourceInfo
|
||||
{
|
||||
@@ -669,22 +807,12 @@ namespace Barotrauma
|
||||
//only used if the item doesn't have a name/description defined in the currently selected language
|
||||
string fallbackNameIdentifier = ConfigElement.GetAttributeString("fallbacknameidentifier", "");
|
||||
|
||||
//works the same as nameIdentifier, but just replaces the description
|
||||
Identifier descriptionIdentifier = ConfigElement.GetAttributeIdentifier("descriptionidentifier", "");
|
||||
|
||||
if (string.IsNullOrEmpty(OriginalName))
|
||||
{
|
||||
name = TextManager.Get(nameIdentifier.IsEmpty
|
||||
? $"EntityName.{Identifier}"
|
||||
: $"EntityName.{nameIdentifier}",
|
||||
$"EntityName.{fallbackNameIdentifier}");
|
||||
}
|
||||
else if (Category.HasFlag(MapEntityCategory.Legacy))
|
||||
{
|
||||
// Legacy items use names as identifiers, so we have to define them in the xml. But we also want to support the translations. Therefore
|
||||
name = TextManager.Get(nameIdentifier.IsEmpty
|
||||
name = TextManager.Get(nameIdentifier.IsEmpty
|
||||
? $"EntityName.{Identifier}"
|
||||
: $"EntityName.{nameIdentifier}");
|
||||
: $"EntityName.{nameIdentifier}",
|
||||
$"EntityName.{fallbackNameIdentifier}");
|
||||
if (!string.IsNullOrEmpty(OriginalName))
|
||||
{
|
||||
name = name.Fallback(OriginalName);
|
||||
}
|
||||
|
||||
@@ -727,27 +855,13 @@ namespace Barotrauma
|
||||
|
||||
SerializableProperty.DeserializeProperties(this, ConfigElement);
|
||||
|
||||
if (Description.IsNullOrEmpty())
|
||||
{
|
||||
if (descriptionIdentifier != Identifier.Empty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{descriptionIdentifier}");
|
||||
}
|
||||
else if (nameIdentifier == Identifier.Empty)
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{Identifier}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get($"EntityDescription.{nameIdentifier}");
|
||||
}
|
||||
}
|
||||
LoadDescription(ConfigElement);
|
||||
|
||||
var allowDroppingOnSwapWith = ConfigElement.GetAttributeIdentifierArray("allowdroppingonswapwith", Array.Empty<Identifier>());
|
||||
AllowDroppingOnSwapWith = allowDroppingOnSwapWith.ToImmutableHashSet();
|
||||
AllowDroppingOnSwap = allowDroppingOnSwapWith.Any();
|
||||
|
||||
var levelCommonness = new Dictionary<Identifier, float>();
|
||||
var levelCommonness = new Dictionary<Identifier, CommonnessInfo>();
|
||||
var levelQuantity = new Dictionary<Identifier, FixedQuantityResourceInfo>();
|
||||
|
||||
foreach (ContentXElement subElement in ConfigElement.Elements())
|
||||
@@ -871,7 +985,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!levelCommonness.ContainsKey(levelName))
|
||||
{
|
||||
levelCommonness.Add(levelName, levelCommonnessElement.GetAttributeFloat("commonness", 0.0f));
|
||||
levelCommonness.Add(levelName, new CommonnessInfo(levelCommonnessElement));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -962,6 +1076,40 @@ namespace Barotrauma
|
||||
this.allowedLinks = ConfigElement.GetAttributeIdentifierArray("allowedlinks", Array.Empty<Identifier>()).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
public CommonnessInfo? GetCommonnessInfo(Level level)
|
||||
{
|
||||
CommonnessInfo? levelCommonnessInfo = GetValueOrNull(level.GenerationParams.Identifier);
|
||||
CommonnessInfo? biomeCommonnessInfo = GetValueOrNull(level.LevelData.Biome.Identifier);
|
||||
CommonnessInfo? defaultCommonnessInfo = GetValueOrNull(Identifier.Empty);
|
||||
|
||||
if (levelCommonnessInfo.HasValue)
|
||||
{
|
||||
return levelCommonnessInfo?.WithInheritedCommonness(biomeCommonnessInfo, defaultCommonnessInfo);
|
||||
}
|
||||
else if (biomeCommonnessInfo.HasValue)
|
||||
{
|
||||
return biomeCommonnessInfo?.WithInheritedCommonness(defaultCommonnessInfo);
|
||||
}
|
||||
else if (defaultCommonnessInfo.HasValue)
|
||||
{
|
||||
return defaultCommonnessInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
CommonnessInfo? GetValueOrNull(Identifier identifier)
|
||||
{
|
||||
if (LevelCommonness.TryGetValue(identifier, out CommonnessInfo info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float GetTreatmentSuitability(Identifier treatmentIdentifier)
|
||||
{
|
||||
return treatmentSuitability.TryGetValue(treatmentIdentifier, out float suitability) ? suitability : 0.0f;
|
||||
@@ -975,7 +1123,7 @@ namespace Barotrauma
|
||||
{
|
||||
string message = $"Tried to get price info for \"{Identifier}\" with a null store parameter!\n{Environment.StackTrace.CleanupStackTrace()}";
|
||||
#if DEBUG
|
||||
DebugConsole.ShowError(message);
|
||||
DebugConsole.LogError(message);
|
||||
#else
|
||||
DebugConsole.AddWarning(message);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ItemPrefab.GetPriceInfo:StoreParameterNull", GameAnalyticsManager.ErrorSeverity.Error, message);
|
||||
@@ -1158,12 +1306,8 @@ namespace Barotrauma
|
||||
throw new InvalidOperationException("Can't call ItemPrefab.CreateInstance");
|
||||
}
|
||||
|
||||
private bool disposed = false;
|
||||
public override void Dispose()
|
||||
{
|
||||
if (disposed) { return; }
|
||||
disposed = true;
|
||||
Prefabs.Remove(this);
|
||||
Item.RemoveByPrefab(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -22,7 +23,7 @@ namespace Barotrauma
|
||||
|
||||
public bool IgnoreInEditor { get; set; }
|
||||
|
||||
private Identifier[] excludedIdentifiers;
|
||||
private ImmutableHashSet<Identifier> excludedIdentifiers;
|
||||
|
||||
private RelationType type;
|
||||
|
||||
@@ -60,11 +61,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
Identifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToArray();
|
||||
Identifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToImmutableHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
public Identifier[] Identifiers { get; private set; }
|
||||
public ImmutableHashSet<Identifier> Identifiers { get; private set; }
|
||||
|
||||
public string JoinedExcludedIdentifiers
|
||||
{
|
||||
@@ -73,27 +74,53 @@ namespace Barotrauma
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
excludedIdentifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToArray();
|
||||
excludedIdentifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToImmutableHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
public bool MatchesItem(Item item)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; }
|
||||
return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && item.Prefab.VariantOf == id));
|
||||
if (excludedIdentifiers.Contains(item.Prefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
||||
{
|
||||
if (item.HasTag(excludedIdentifier)) { return false; }
|
||||
}
|
||||
if (Identifiers.Contains(item.Prefab.Identifier)) { return true; }
|
||||
foreach (var identifier in Identifiers)
|
||||
{
|
||||
if (item.HasTag(identifier)) { return true; }
|
||||
}
|
||||
if (AllowVariants && !item.Prefab.VariantOf.IsEmpty)
|
||||
{
|
||||
if (Identifiers.Contains(item.Prefab.VariantOf)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool MatchesItem(ItemPrefab itemPrefab)
|
||||
{
|
||||
if (itemPrefab == null) { return false; }
|
||||
if (excludedIdentifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id))) { return false; }
|
||||
return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (AllowVariants && !itemPrefab.VariantOf.IsEmpty && itemPrefab.VariantOf == id));
|
||||
if (excludedIdentifiers.Contains(itemPrefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
||||
{
|
||||
if (itemPrefab.Tags.Contains(excludedIdentifier)) { return false; }
|
||||
}
|
||||
if (Identifiers.Contains(itemPrefab.Identifier)) { return true; }
|
||||
foreach (var identifier in Identifiers)
|
||||
{
|
||||
if (itemPrefab.Tags.Contains(identifier)) { return true; }
|
||||
}
|
||||
if (AllowVariants && !itemPrefab.VariantOf.IsEmpty)
|
||||
{
|
||||
if (Identifiers.Contains(itemPrefab.VariantOf)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public RelatedItem(Identifier[] identifiers, Identifier[] excludedIdentifiers)
|
||||
{
|
||||
this.Identifiers = identifiers.Select(id => id.Value.Trim().ToIdentifier()).ToArray();
|
||||
this.excludedIdentifiers = excludedIdentifiers.Select(id => id.Value.Trim().ToIdentifier()).ToArray();
|
||||
this.Identifiers = identifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
||||
this.excludedIdentifiers = excludedIdentifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
||||
|
||||
statusEffects = new List<StatusEffect>();
|
||||
}
|
||||
@@ -161,7 +188,7 @@ namespace Barotrauma
|
||||
new XAttribute("targetslot", TargetSlot),
|
||||
new XAttribute("allowvariants", AllowVariants));
|
||||
|
||||
if (excludedIdentifiers.Length > 0)
|
||||
if (excludedIdentifiers.Count > 0)
|
||||
{
|
||||
element.Add(new XAttribute("excludedidentifiers", JoinedExcludedIdentifiers));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user