v1.4.4.1 (Blood in the Water Update)

This commit is contained in:
Regalis11
2024-04-24 18:09:05 +03:00
parent 89b91d1c3e
commit ff1b8951a7
397 changed files with 15250 additions and 6479 deletions
@@ -75,6 +75,7 @@ namespace Barotrauma.Items.Components
private void UpdateConvexHulls()
{
if (item.Removed) { return; }
if (doorSprite == null) { return; }
doorRect = new Rectangle(
item.Rect.Center.X - (int)(doorSprite.size.X / 2 * item.Scale),
@@ -83,7 +84,7 @@ namespace Barotrauma.Items.Components
(int)(doorSprite.size.Y * item.Scale));
Rectangle rect = doorRect;
if (IsHorizontal)
if (IsConvexHullHorizontal)
{
rect.Width = (int)(rect.Width * (1.0f - openState));
}
@@ -94,7 +95,7 @@ namespace Barotrauma.Items.Components
if (Window.Height > 0 && Window.Width > 0)
{
if (IsHorizontal)
if (IsConvexHullHorizontal)
{
rect.Width = (int)(Window.X * item.Scale);
rect.X -= (int)(doorRect.Width * openState);
@@ -163,8 +164,8 @@ namespace Barotrauma.Items.Components
var verts = GetConvexHullCorners(rect);
Vector2 center = (verts[0] + verts[2]) / 2;
convexHull.SetVertices(
verts,
IsHorizontal ?
verts,
IsConvexHullHorizontal ?
new Vector2[] { new Vector2(verts[0].X, center.Y), new Vector2(verts[2].X, center.Y) } :
new Vector2[] { new Vector2(center.X, verts[0].Y), new Vector2(center.X, verts[2].Y) });
convexHull.MaxMergeLosVerticesDist = 35.0f;
@@ -304,6 +305,7 @@ namespace Barotrauma.Items.Components
}
else
{
bool stateChanged = open != isOpen;
isOpen = open;
if (!isNetworkMessage || open != PredictedState)
{
@@ -314,6 +316,11 @@ namespace Barotrauma.Items.Components
}
if (isOpen) { stuck = MathHelper.Clamp(stuck - StuckReductionOnOpen, 0.0f, 100.0f); }
}
if (stateChanged)
{
ActionType actionType = open ? ActionType.OnOpen : ActionType.OnClose;
item.ApplyStatusEffects(actionType, deltaTime: 1.0f);
}
}
void PlayInteractionSound()
@@ -64,7 +64,8 @@ namespace Barotrauma.Items.Components
spriteBatch,
new Vector2(attachPos.X, -attachPos.Y),
item.SpriteColor * 0.5f,
0.0f, item.Scale, SpriteEffects.None, 0.0f);
item.RotationRad,
item.Scale, SpriteEffects.None, 0.0f);
GUI.DrawRectangle(spriteBatch, new Vector2(attachPos.X - 2, -attachPos.Y - 2), Vector2.One * 5, GUIStyle.Red, thickness: 3);
}
@@ -18,11 +18,31 @@ namespace Barotrauma.Items.Components
protected float currentCrossHairScale, currentCrossHairPointerScale;
private RoundSound chargeSound;
private SoundChannel chargeSoundChannel;
[Serialize(defaultValue: "0.5, 1.5", IsPropertySaveable.No, description: "Pitch slides from X to Y over the charge time")]
public Vector2 ChargeSoundWindupPitchSlide
{
get => _chargeSoundWindupPitchSlide;
set
{
_chargeSoundWindupPitchSlide = new Vector2(
Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
}
}
private Vector2 _chargeSoundWindupPitchSlide;
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
/// <summary>
/// The orientation of the item is briefly wrong after the character holding it flips and before the holding logic forces it to the correct position.
/// We disable the crosshair briefly during that time to prevent it from momentarily jumping to an incorrect position.
/// </summary>
private float crossHairPosDirtyTimer;
[Serialize(1.0f, IsPropertySaveable.No, description: "The scale of the crosshair sprite (if there is one).")]
public float CrossHairScale
{
@@ -30,9 +50,9 @@ namespace Barotrauma.Items.Components
private set;
}
partial void InitProjSpecific(ContentXElement element)
partial void InitProjSpecific(ContentXElement rangedWeaponElement)
{
foreach (var subElement in element.Elements())
foreach (var subElement in rangedWeaponElement.Elements())
{
string textureDir = GetTextureDirectory(subElement);
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -62,6 +82,7 @@ namespace Barotrauma.Items.Components
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
{
crossHairPosDirtyTimer -= deltaTime;
currentCrossHairScale = currentCrossHairPointerScale = cam == null ? 1.0f : cam.Zoom;
if (crosshairSprite != null)
{
@@ -92,6 +113,15 @@ namespace Barotrauma.Items.Components
crosshairPointerPos = PlayerInput.MousePosition;
}
public override void FlipX(bool relativeToSub)
{
crossHairPosDirtyTimer = 0.02f;
}
public override void FlipY(bool relativeToSub)
{
crossHairPosDirtyTimer = 0.02f;
}
partial void UpdateProjSpecific(float deltaTime)
{
float chargeRatio = currentChargeTime / MaxChargeTime;
@@ -117,7 +147,7 @@ namespace Barotrauma.Items.Components
}
else if (chargeSoundChannel != null)
{
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(ChargeSoundWindupPitchSlide.X, ChargeSoundWindupPitchSlide.Y, chargeRatio);
chargeSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
}
break;
@@ -143,15 +173,19 @@ namespace Barotrauma.Items.Components
if (character == null || !character.IsKeyDown(InputType.Aim) || !character.CanAim) { return; }
//camera focused on some other item/device, don't draw the crosshair
if (character.ViewTarget != null && (character.ViewTarget is Item viewTargetItem) && viewTargetItem.Prefab.FocusOnSelected) { return; }
if (character.ViewTarget is Item viewTargetItem && viewTargetItem.Prefab.FocusOnSelected) { return; }
//don't draw the crosshair if the item is in some other type of equip slot than hands (e.g. assault rifle in the bag slot)
if (!character.HeldItems.Contains(item)) { return; }
GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;
if (GUI.HideCursor)
if (GUI.HideCursor && !character.AnimController.IsHoldingToRope)
{
crosshairSprite?.Draw(spriteBatch, crosshairPos, ReloadTimer <= 0.0f ? Color.White : Color.White * 0.2f, 0, currentCrossHairScale);
if (crossHairPosDirtyTimer <= 0.0f)
{
crosshairSprite?.Draw(spriteBatch, crosshairPos, ReloadTimer <= 0.0f ? Color.White : Color.White * 0.2f, 0, currentCrossHairScale);
}
crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, currentCrossHairPointerScale);
}
@@ -205,7 +205,7 @@ namespace Barotrauma.Items.Components
int buttonSize = GUIStyle.ItemFrameTopBarHeight;
Point margin = new Point(buttonSize / 4, buttonSize / 6);
GUILayoutGroup buttonArea = new GUILayoutGroup(new RectTransform(new Point(GuiFrame.Rect.Width - margin.X * 2, buttonSize - margin.Y * 2), GuiFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, margin.Y) },
GUILayoutGroup buttonArea = new GUILayoutGroup(new RectTransform(new Point(content.Rect.Width, buttonSize - margin.Y * 2), content.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(0, margin.Y) },
isHorizontal: true, childAnchor: Anchor.TopRight)
{
AbsoluteSpacing = margin.X / 2
@@ -334,7 +334,7 @@ namespace Barotrauma.Items.Components
}
else
{
return item?.Name;
return item?.Prefab.Name;
}
}
@@ -40,6 +40,7 @@ namespace Barotrauma.Items.Components
partial void SetLightSourceState(bool enabled, float brightness)
{
if (Light == null) { return; }
if (item.HiddenInGame) { enabled = false; }
Light.Enabled = enabled;
lightColorMultiplier = brightness;
if (enabled)
@@ -57,7 +57,7 @@ namespace Barotrauma.Items.Components
RelativeSpacing = 0.08f
};
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform) { MinSize = new Point(0, GUI.IntScale(25)) }, item.Name, font: GUIStyle.SubHeadingFont)
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform) { MinSize = new Point(0, GUI.IntScale(25)) }, item.Prefab.Name, font: GUIStyle.SubHeadingFont)
{
TextAlignment = Alignment.Center,
AutoScaleHorizontal = true
@@ -341,7 +341,7 @@ namespace Barotrauma.Items.Components
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 1f), parent.RectTransform), style: null)
{
UserData = identifier,
ToolTip = GetTooltip(prefab)
ToolTip = prefab.CreateTooltipText()
};
Sprite icon = prefab.InventoryIcon ?? prefab.Sprite;
@@ -372,21 +372,6 @@ namespace Barotrauma.Items.Components
textBlock.Text = TextManager.GetWithVariable("campaignstore.quantity", "[amount]", count.ToString());
}
static RichString GetTooltip(ItemPrefab prefab)
{
LocalizedString toolTip = $"‖color:{Color.White.ToStringHex()}‖{prefab.Name}‖color:end‖";
LocalizedString description = prefab.Description;
if (!description.IsNullOrEmpty()) { toolTip += '\n' + description; }
if (prefab.ContentPackage != GameMain.VanillaContent && prefab.ContentPackage != null)
{
toolTip += $"\n‖color:{Color.MediumPurple.ToStringHex()}‖{prefab.ContentPackage.Name}‖color:end‖";
}
return RichString.Rich(toolTip);
}
}
partial void OnItemLoadedProjSpecific()
@@ -134,11 +134,11 @@ namespace Barotrauma.Items.Components
spriteIndex += (force / 100.0f) * AnimSpeed * deltaTime;
if (spriteIndex < 0)
{
spriteIndex = propellerSprite.FrameCount;
spriteIndex = propellerSprite.FrameCount - Math.Abs(spriteIndex) % propellerSprite.FrameCount;
}
if (spriteIndex >= propellerSprite.FrameCount)
else
{
spriteIndex = 0.0f;
spriteIndex = spriteIndex % propellerSprite.FrameCount;
}
}
@@ -80,7 +80,7 @@ namespace Barotrauma.Items.Components
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter);
// === LABEL === //
new GUITextBlock(new RectTransform(new Vector2(1f, 0.05f), paddedFrame.RectTransform), item.Name, font: GUIStyle.SubHeadingFont)
new GUITextBlock(new RectTransform(new Vector2(1f, 0.05f), paddedFrame.RectTransform), item.Prefab.Name, font: GUIStyle.SubHeadingFont)
{
TextAlignment = Alignment.Center,
AutoScaleVertical = true
@@ -326,7 +326,7 @@ namespace Barotrauma.Items.Components
UserData = fi,
HoverColor = Color.Gold * 0.2f,
SelectedColor = Color.Gold * 0.5f,
ToolTip = fi.TargetItem.Description
ToolTip = RichString.Rich(fi.TargetItem.Description)
};
var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform),
@@ -339,7 +339,7 @@ namespace Barotrauma.Items.Components
itemIcon, scaleToFit: true)
{
Color = fi.TargetItem.InventoryIconColor,
ToolTip = fi.TargetItem.Description
ToolTip = RichString.Rich(fi.TargetItem.Description)
};
}
@@ -347,7 +347,7 @@ namespace Barotrauma.Items.Components
{
Padding = Vector4.Zero,
AutoScaleVertical = true,
ToolTip = fi.TargetItem.Description
ToolTip = RichString.Rich(fi.TargetItem.Description)
};
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), frame.RectTransform, Anchor.BottomRight),
@@ -925,8 +925,6 @@ namespace Barotrauma.Items.Components
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, GUI.IntScale(5), nameBlock.Padding.W);
if (nameBlock.TextScale < 0.7f)
{
nameBlock.SetRichText(TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName)
.Fallback(TextManager.GetWithVariable("itemname.quality3", "[itemname]", itemName)));
nameBlock.AutoScaleHorizontal = false;
nameBlock.TextScale = 0.7f;
nameBlock.Wrap = true;
@@ -937,7 +935,7 @@ namespace Barotrauma.Items.Components
if (!selectedItem.TargetItem.Description.IsNullOrEmpty())
{
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
selectedItem.TargetItem.Description,
RichString.Rich(selectedItem.TargetItem.Description),
font: GUIStyle.SmallFont, wrap: true);
description.Padding = new Vector4(0, description.Padding.Y, description.Padding.Z, description.Padding.W);
@@ -1090,7 +1090,12 @@ namespace Barotrauma.Items.Components
float totalVolume = 0.0f;
foreach (Hull linkedHull in hullData.LinkedHulls)
{
waterVolume += linkedHull.WaterVolume;
//water detector ignores very small amounts of water,
//do it here too so the nav terminal doesn't display the water
if (WaterDetector.GetWaterPercentage(linkedHull) > 0.0f)
{
waterVolume += linkedHull.WaterVolume;
}
totalVolume += linkedHull.Volume;
}
hullData.HullWaterAmount =
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
public override bool Select(Character character)
{
if (GameMain.GameSession?.Campaign == null)
if (GameMain.GameSession?.Campaign == null || !Level.IsLoadedFriendlyOutpost)
{
return false;
}
@@ -1446,7 +1446,7 @@ namespace Barotrauma.Items.Components
//only relevant in the end levels or maybe custom subs with some kind of non-hulled parts
Rectangle worldBorders = submarine.GetDockedBorders();
worldBorders.Location += submarine.WorldPosition.ToPoint();
if (Submarine.RectContains(worldBorders, pingSource))
if (Submarine.RectContains(worldBorders, pingSource) || submarine.Info.OutpostGenerationParams is { AlwaysShowStructuresOnSonar: true })
{
CreateBlipsForSubmarineWalls(submarine, pingSource, transducerPos, pingRadius, prevPingRadius, range, passive);
continue;
@@ -1502,7 +1502,9 @@ namespace Barotrauma.Items.Components
foreach (Voronoi2.GraphEdge edge in cell.Edges)
{
if (!edge.IsSolid) { continue; }
float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center);
//the normal of the edge must be pointing towards the ping source to be visible
float cellDot = Vector2.Dot((edge.Center + cell.Translation) - pingSource, edge.GetNormal(cell));
if (cellDot > 0) { continue; }
float facingDot = Vector2.Dot(
@@ -1543,6 +1545,7 @@ namespace Barotrauma.Items.Components
{
if (c.AnimController.CurrentHull != null || !c.Enabled) { continue; }
if (!c.IsUnconscious && c.Params.HideInSonar) { continue; }
if (c.InDetectable) { continue; }
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) { continue; }
if (c.AnimController.SimplePhysicsEnabled)
@@ -316,28 +316,28 @@ namespace Barotrauma.Items.Components
{
case 0:
leftText = TextManager.Get("DescentVelocity");
centerText = $"({TextManager.Get("KilometersPerHour")})";
centerText = TextManager.Get("KilometersPerHour");
rightTextGetter = () =>
{
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.Y * Physics.DisplayToRealWorldRatio) * 3.6f;
return ((int)(-realWorldVel)).ToString();
return (-realWorldVel).ToString("0.0");
};
break;
case 1:
leftText = TextManager.Get("Velocity");
centerText = $"({TextManager.Get("KilometersPerHour")})";
centerText = TextManager.Get("KilometersPerHour");
rightTextGetter = () =>
{
Vector2 vel = controlledSub == null ? Vector2.Zero : controlledSub.Velocity;
var realWorldVel = ConvertUnits.ToDisplayUnits(vel.X * Physics.DisplayToRealWorldRatio) * 3.6f;
if (controlledSub != null && controlledSub.FlippedX) { realWorldVel *= -1; }
return ((int)realWorldVel).ToString();
return realWorldVel.ToString("0.0");
};
break;
case 2:
leftText = TextManager.Get("Depth");
centerText = $"({TextManager.Get("Meter")})";
centerText = TextManager.Get("Meter");
rightTextGetter = () =>
{
if (Level.Loaded is { IsEndBiome: true })
@@ -789,9 +789,9 @@ namespace Barotrauma.Items.Components
}
pressureWarningText.Visible = item.Submarine != null && Timing.TotalTime % 1.0f < 0.8f;
float depthEffectThreshold = 500.0f;
if (Level.Loaded != null && pressureWarningText.Visible &&
item.Submarine.RealWorldDepth > Level.Loaded.RealWorldCrushDepth - depthEffectThreshold && item.Submarine.RealWorldDepth > item.Submarine.RealWorldCrushDepth - depthEffectThreshold)
item.Submarine.RealWorldDepth > Level.Loaded.RealWorldCrushDepth - PressureWarningThreshold &&
item.Submarine.RealWorldDepth > item.Submarine.RealWorldCrushDepth - PressureWarningThreshold)
{
pressureWarningText.Visible = true;
pressureWarningText.Text =
@@ -148,11 +148,29 @@ namespace Barotrauma.Items.Components
Vector2 particlePos = item.WorldPosition;
float rotation = -item.body.Rotation;
if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; }
//if the position is in a sub's local coordinates, convert to world coordinates
particlePos = ConvertToWorldCoordinates(particlePos);
//if the start location is in a sub's local coordinates, convert to world coordinates
startLocation = ConvertToWorldCoordinates(startLocation);
//same for end location
endLocation = ConvertToWorldCoordinates(endLocation);
Tuple<Vector2, Vector2> tracerPoints = new Tuple<Vector2, Vector2>(startLocation, endLocation);
foreach (ParticleEmitter emitter in particleEmitters)
{
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier, tracerPoints: tracerPoints);
}
static Vector2 ConvertToWorldCoordinates(Vector2 position)
{
Submarine containing = Submarine.FindContainingInLocalCoordinates(position);
if (containing != null)
{
position += containing.Position;
}
return position;
}
}
partial void InitProjSpecific(ContentXElement element)
@@ -63,10 +63,7 @@ namespace Barotrauma.Items.Components
if (item.HiddenInGame) { return false; }
if (!HasRequiredItems(character, false) || character.SelectedItem != item) { return false; }
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
float defaultMaxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
if (MathUtils.Percentage(item.Condition, defaultMaxCondition) < RepairThreshold) { return true; }
if (item.ConditionPercentageRelativeToDefaultMaxCondition < RepairThreshold) { return true; }
if (CurrentFixer == character)
{
@@ -135,13 +132,13 @@ namespace Barotrauma.Items.Components
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
TextManager.Get("RequiredRepairSkills"), font: GUIStyle.SubHeadingFont);
skillTextContainer = paddedFrame;
for (int i = 0; i < requiredSkills.Count; i++)
for (int i = 0; i < RequiredSkills.Count; i++)
{
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillTextContainer.RectTransform),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + requiredSkills[i].Identifier), ((int) Math.Round(requiredSkills[i].Level * SkillRequirementMultiplier)).ToString()),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + RequiredSkills[i].Identifier), ((int) Math.Round(RequiredSkills[i].Level * SkillRequirementMultiplier)).ToString()),
font: GUIStyle.SmallFont)
{
UserData = requiredSkills[i]
UserData = RequiredSkills[i]
};
}
@@ -262,7 +259,7 @@ namespace Barotrauma.Items.Components
}
}
float conditionPercentage = item.Condition / (item.MaxCondition / item.MaxRepairConditionMultiplier) * 100f;
float conditionPercentage = item.ConditionPercentageRelativeToDefaultMaxCondition;
for (int i = 0; i < particleEmitters.Count; i++)
{
@@ -2,13 +2,16 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Xml.Linq;
using Barotrauma.Sounds;
namespace Barotrauma.Items.Components
{
partial class Rope : ItemComponent, IDrawableComponent
{
private Sprite sprite, startSprite, endSprite;
private RoundSound snapSound, reelSound;
private SoundChannel reelSoundChannel;
[Serialize(5, IsPropertySaveable.No)]
public int SpriteWidth
@@ -54,6 +57,19 @@ namespace Barotrauma.Items.Components
Math.Abs(target.DrawPosition.Y - sourcePos.Y)) * 1.5f;
}
}
[Serialize("1.0, 1.0", IsPropertySaveable.No, description: "When reeling in, the pitch slides from X to Y, depending on the length of the rope.")]
public Vector2 ReelSoundPitchSlide
{
get => _reelSoundPitchSlide;
set
{
_reelSoundPitchSlide = new Vector2(
Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
}
}
private Vector2 _reelSoundPitchSlide;
partial void InitProjSpecific(ContentXElement element)
{
@@ -70,9 +86,28 @@ namespace Barotrauma.Items.Components
case "endsprite":
endSprite = new Sprite(subElement);
break;
case "snapsound":
snapSound = RoundSound.Load(subElement);
break;
case "reelsound":
reelSound = RoundSound.Load(subElement);
break;
}
}
}
partial void UpdateProjSpecific()
{
if (isReelingIn && !Snapped)
{
PlaySound(reelSound, source.WorldPosition);
}
else
{
reelSoundChannel?.FadeOutAndDispose();
reelSoundChannel = null;
}
}
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
{
@@ -184,6 +219,32 @@ namespace Barotrauma.Items.Components
overrideColor ?? SpriteColor, depth: depth, width: width);
}
}
private void PlaySound(RoundSound sound, Vector2 position)
{
if (sound == null) { return; }
if (sound == reelSound)
{
if (reelSoundChannel is not { IsPlaying: true })
{
reelSoundChannel = SoundPlayer.PlaySound(sound.Sound, position, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
if (reelSoundChannel != null)
{
reelSoundChannel.Looping = true;
}
}
else
{
reelSoundChannel.Position = new Vector3(position, 0);
reelSoundChannel.Gain = MathHelper.Lerp(0, 1.0f, MathUtils.InverseLerp(MinPullDistance, MaxLength, MathUtils.Pow(currentRopeLength, 1.5f)));
reelSoundChannel.FrequencyMultiplier = MathHelper.Lerp(ReelSoundPitchSlide.X, ReelSoundPitchSlide.Y, MathUtils.InverseLerp(MinPullDistance, MaxLength, currentRopeLength));
}
}
else
{
SoundPlayer.PlaySound(sound.Sound, position, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
}
}
public void ClientEventRead(IReadMessage msg, float sendingTime)
{
@@ -191,30 +252,38 @@ namespace Barotrauma.Items.Components
if (!snapped)
{
UInt16 targetId = msg.ReadUInt16();
UInt16 sourceId = msg.ReadUInt16();
ushort targetId = msg.ReadUInt16();
ushort sourceId = msg.ReadUInt16();
byte limbIndex = msg.ReadByte();
Item target = Entity.FindEntityByID(targetId) as Item;
if (target == null) { return; }
if (Entity.FindEntityByID(targetId) is not Item target) { return; }
var source = Entity.FindEntityByID(sourceId);
if (source is Character sourceCharacter && limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length)
switch (source)
{
Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex];
Attach(sourceLimb, target);
}
else if (source is ISpatialEntity spatialEntity)
{
Attach(spatialEntity, target);
case Character sourceCharacter when limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length:
{
Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex];
Attach(sourceLimb, target);
sourceCharacter.AnimController.DragWithRope();
break;
}
case ISpatialEntity spatialEntity:
Attach(spatialEntity, target);
break;
}
}
}
protected override void RemoveComponentSpecific()
{
sprite?.Remove(); sprite = null;
startSprite?.Remove(); startSprite = null;
endSprite?.Remove(); endSprite = null;
sprite?.Remove();
sprite = null;
startSprite?.Remove();
startSprite = null;
endSprite?.Remove();
endSprite = null;
reelSoundChannel?.FadeOutAndDispose();
reelSoundChannel = null;
}
}
}
@@ -129,6 +129,7 @@ namespace Barotrauma.Items.Components
public void RemoveComponents(IReadOnlyCollection<CircuitBoxComponent> node)
{
if (Locked) { return; }
var ids = node.Select(static n => n.ID).ToImmutableArray();
if (GameMain.NetworkMember is null)
@@ -145,6 +146,7 @@ namespace Barotrauma.Items.Components
public void AddWire(CircuitBoxConnection one, CircuitBoxConnection two)
{
if (Locked) { return; }
if (GameMain.NetworkMember is null)
{
Connect(one, two, static delegate { }, CircuitBoxWire.SelectedWirePrefab);
@@ -158,6 +160,7 @@ namespace Barotrauma.Items.Components
public void RemoveWires(IReadOnlyCollection<CircuitBoxWire> wires)
{
if (Locked) { return; }
var ids = wires.Select(static w => w.ID).ToImmutableArray();
if (GameMain.NetworkMember is null)
{
@@ -175,6 +178,7 @@ namespace Barotrauma.Items.Components
var ids = ImmutableArray.CreateBuilder<ushort>();
var ios = ImmutableArray.CreateBuilder<CircuitBoxInputOutputNode.Type>();
var labelIds = ImmutableArray.CreateBuilder<ushort>();
foreach (var moveable in moveables)
{
@@ -188,6 +192,9 @@ namespace Barotrauma.Items.Components
case CircuitBoxInputOutputNode io:
ios.Add(io.NodeType);
break;
case CircuitBoxLabelNode label:
labelIds.Add(label.ID);
break;
}
}
@@ -195,12 +202,13 @@ namespace Barotrauma.Items.Components
{
SelectComponentsInternal(ids, controlledId, overwrite);
SelectInputOutputInternal(ios, controlledId, overwrite);
SelectLabelsInternal(labelIds, controlledId, overwrite);
return;
}
if ((!ids.Any() && !ios.Any()) && !overwrite) { return; }
if (!ids.Any() && !ios.Any() && !labelIds.Any() && !overwrite) { return; }
CreateClientEvent(new CircuitBoxSelectNodesEvent(ids.ToImmutable(), ios.ToImmutable(), overwrite, controlledId));
CreateClientEvent(new CircuitBoxSelectNodesEvent(ids.ToImmutable(), ios.ToImmutable(), labelIds.ToImmutable(), overwrite, controlledId));
}
public void SelectWires(IReadOnlyCollection<CircuitBoxWire> wires, bool overwrite)
@@ -222,8 +230,10 @@ namespace Barotrauma.Items.Components
public void MoveComponent(Vector2 moveAmount, IReadOnlyCollection<CircuitBoxNode> moveables)
{
if (Locked) { return; }
var ids = ImmutableArray.CreateBuilder<ushort>();
var ios = ImmutableArray.CreateBuilder<CircuitBoxInputOutputNode.Type>();
var labelIds = ImmutableArray.CreateBuilder<ushort>();
foreach (CircuitBoxNode move in moveables)
{
@@ -235,23 +245,27 @@ namespace Barotrauma.Items.Components
case CircuitBoxInputOutputNode io:
ios.Add(io.NodeType);
break;
case CircuitBoxLabelNode label:
labelIds.Add(label.ID);
break;
}
}
if (GameMain.NetworkMember is null)
{
MoveNodesInternal(ids, ios, moveAmount);
MoveNodesInternal(ids, ios, labelIds, moveAmount);
return;
}
if (!ids.Any() && !ios.Any()) { return; }
if (!ids.Any() && !ios.Any() && !labelIds.Any()) { return; }
CreateClientEvent(new CircuitBoxMoveComponentEvent(ids.ToImmutable(), ios.ToImmutable(), moveAmount));
CreateClientEvent(new CircuitBoxMoveComponentEvent(ids.ToImmutable(), ios.ToImmutable(), labelIds.ToImmutable(), moveAmount));
}
public void AddComponent(ItemPrefab prefab, Vector2 pos)
{
if (Locked) { return; }
if (GameMain.NetworkMember is null)
{
ItemPrefab resource;
@@ -276,6 +290,72 @@ namespace Barotrauma.Items.Components
CreateClientEvent(new CircuitBoxAddComponentEvent(prefab.UintIdentifier, pos));
}
public void RenameLabel(CircuitBoxLabelNode label, Color color, NetLimitedString header, NetLimitedString body)
{
if (Locked) { return; }
if (GameMain.NetworkMember is null)
{
label.EditText(header, body);
label.Color = color;
return;
}
CreateClientEvent(new CircuitBoxRenameLabelEvent(label.ID, color, header, body));
}
public void ResizeNode(CircuitBoxNode node, CircuitBoxResizeDirection dir, Vector2 amount)
{
if (Locked) { return; }
var resize = node.ResizeBy(dir, amount);
if (GameMain.NetworkMember is null)
{
node.ApplyResize(resize.Size, resize.Pos);
return;
}
// TODO this needs to be refactored at some point, probably not now
// the problem here is that the circuit box supports resizing all nodes
// but we limit the resizing to only labels on the client
// and on the server we only have a network message that targets labels
// so if we ever want the ability to resize other nodes (could be useful) the network message
// needs to know what type of ID it's targeting
if (node is not ICircuitBoxIdentifiable identifiable)
{
DebugConsole.ThrowError("Tried to resize a node that doesn't have an ID.");
return;
}
CreateClientEvent(new CircuitBoxResizeLabelEvent(identifiable.ID, resize.Pos, resize.Size));
}
public void AddLabel(Vector2 pos)
{
if (Locked) { return; }
if (GameMain.NetworkMember is null)
{
AddLabelInternal(ICircuitBoxIdentifiable.FindFreeID(Labels), GUIStyle.Blue, pos, CircuitBoxLabelNode.DefaultHeaderText, NetLimitedString.Empty);
return;
}
CreateClientEvent(new CircuitBoxAddLabelEvent(pos, GUIStyle.Blue, CircuitBoxLabelNode.DefaultHeaderText, NetLimitedString.Empty));
}
public void RemoveLabel(IReadOnlyCollection<CircuitBoxLabelNode> labels)
{
if (Locked) { return; }
if (!labels.Any()) { return; }
var ids = labels.Select(static n => n.ID).ToImmutableArray();
if (GameMain.NetworkMember is null)
{
RemoveLabelInternal(ids);
return;
}
CreateClientEvent(new CircuitBoxRemoveLabelEvent(ids));
}
public partial void OnViewUpdateProjSpecific()
{
UI?.MouseSnapshotHandler.UpdateConnections();
@@ -396,7 +476,7 @@ namespace Barotrauma.Items.Components
case CircuitBoxOpcode.MoveComponent:
{
var data = INetSerializableStruct.Read<CircuitBoxMoveComponentEvent>(msg);
MoveNodesInternal(data.TargetIDs, data.IOs, data.MoveAmount);
MoveNodesInternal(data.TargetIDs, data.IOs, data.LabelIDs, data.MoveAmount);
break;
}
case CircuitBoxOpcode.UpdateSelection:
@@ -406,8 +486,9 @@ namespace Barotrauma.Items.Components
var nodeDict = data.ComponentIds.ToImmutableDictionary(static s => s.ID, static s => s.SelectedBy);
var wireDict = data.WireIds.ToImmutableDictionary(static s => s.ID, static s => s.SelectedBy);
var ioDict = data.InputOutputs.ToImmutableDictionary(static s => s.Type, static s => s.SelectedBy);
var labelDict = data.LabelIds.ToImmutableDictionary(static s => s.ID, static s => s.SelectedBy);
UpdateSelections(nodeDict, wireDict, ioDict);
UpdateSelections(nodeDict, wireDict, ioDict, labelDict);
break;
}
case CircuitBoxOpcode.AddWire:
@@ -426,11 +507,18 @@ namespace Barotrauma.Items.Components
{
Components.Clear();
Wires.Clear();
Labels.Clear();
var data = INetSerializableStruct.Read<CircuitBoxInitializeStateFromServerEvent>(msg);
foreach (var compData in data.Components) { AddComponentFromData(compData); }
foreach (var wireData in data.Wires) { AddWireFromData(wireData); }
foreach (var labelData in data.Labels)
{
AddLabelInternal(labelData.ID, labelData.Color, labelData.Position, labelData.Header, labelData.Body);
ResizeLabelInternal(labelData.ID, labelData.Position, labelData.Size);
}
foreach (var node in InputOutputNodes)
{
node.Position = node.NodeType switch
@@ -443,6 +531,31 @@ namespace Barotrauma.Items.Components
wasInitializedByServer = true;
break;
}
case CircuitBoxOpcode.RenameLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxRenameLabelEvent>(msg);
RenameLabelInternal(data.LabelId, data.Color, data.NewHeader, data.NewBody);
break;
}
case CircuitBoxOpcode.AddLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxServerAddLabelEvent>(msg);
AddLabelInternal(data.ID, data.Color, data.Position, data.Header, data.Body);
ResizeLabelInternal(data.ID, data.Position, data.Size);
break;
}
case CircuitBoxOpcode.RemoveLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxRemoveLabelEvent>(msg);
RemoveLabelInternal(data.TargetIDs);
break;
}
case CircuitBoxOpcode.ResizeLabel:
{
var data = INetSerializableStruct.Read<CircuitBoxResizeLabelEvent>(msg);
ResizeLabelInternal(data.ID, data.Position, data.Size);
break;
}
default:
throw new ArgumentOutOfRangeException(nameof(header), header, "This opcode cannot be handled using entity events");
}
@@ -12,11 +12,13 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
{
if (!editing || !MapEntity.SelectedList.Contains(item)) { return; }
Vector2 pos = item.WorldPosition + TransformedDetectOffset;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, pos - new Vector2(rangeX, rangeY), new Vector2(rangeX, rangeY) * 2.0f, Color.Cyan * 0.5f, isFilled: false, thickness: 2);
if ((editing && MapEntity.SelectedList.Contains(item)) ||
(ConnectionPanel.ShouldDebugDrawWiring && Character.Controlled?.SelectedItem == item))
{
Vector2 pos = item.WorldPosition + TransformedDetectOffset;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, pos - new Vector2(rangeX, rangeY), new Vector2(rangeX, rangeY) * 2.0f, Color.Cyan * 0.5f, isFilled: false, thickness: 2);
}
}
}
}
@@ -137,6 +137,7 @@ namespace Barotrauma.Items.Components
{
if (c == equipper || !c.Enabled || c.Removed) { continue; }
if (!ShowDeadCharacters && c.IsDead) { continue; }
if (c.InDetectable) { continue; }
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
if (dist < Range * Range)
@@ -118,6 +118,19 @@ namespace Barotrauma.Items.Components
get;
private set;
}
[Serialize(defaultValue: "0.5, 1.5", IsPropertySaveable.No, description: "Pitch slides from X to Y over the charge time")]
public Vector2 ChargeSoundWindupPitchSlide
{
get => _chargeSoundWindupPitchSlide;
set
{
_chargeSoundWindupPitchSlide = new Vector2(
Math.Max(value.X, SoundChannel.MinFrequencyMultiplier),
Math.Min(value.Y, SoundChannel.MaxFrequencyMultiplier));
}
}
private Vector2 _chargeSoundWindupPitchSlide;
partial void InitProjSpecific(ContentXElement element)
{
@@ -220,9 +233,9 @@ namespace Barotrauma.Items.Components
{
if (moveSound != null)
{
moveSoundChannel.FadeOutAndDispose();
moveSoundChannel?.FadeOutAndDispose();
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, item.WorldPosition, moveSound.Volume, moveSound.Range, ignoreMuffling: moveSound.IgnoreMuffling, freqMult: moveSound.GetRandomFrequencyMultiplier());
if (moveSoundChannel != null) moveSoundChannel.Looping = true;
if (moveSoundChannel != null) { moveSoundChannel.Looping = true;}
}
}
}
@@ -268,7 +281,7 @@ namespace Barotrauma.Items.Components
}
else if (chargeSoundChannel != null)
{
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(ChargeSoundWindupPitchSlide.X, ChargeSoundWindupPitchSlide.Y, chargeRatio);
chargeSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
}
break;
@@ -482,12 +495,12 @@ namespace Barotrauma.Items.Components
};
widget.MouseDown += () =>
{
widget.color = GUIStyle.Green;
widget.Color = GUIStyle.Green;
prevAngle = minRotation;
};
widget.Deselected += () =>
{
widget.color = Color.Yellow;
widget.Color = Color.Yellow;
item.CreateEditingHUD();
RotationLimits = RotationLimits;
if (SubEditorScreen.IsSubEditor())
@@ -513,7 +526,7 @@ namespace Barotrauma.Items.Components
};
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.Tooltip = "Min: " + (int)MathHelper.ToDegrees(minRotation);
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(minRotation), (float)Math.Sin(minRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
};
});
@@ -526,12 +539,12 @@ namespace Barotrauma.Items.Components
};
widget.MouseDown += () =>
{
widget.color = GUIStyle.Green;
widget.Color = GUIStyle.Green;
prevAngle = maxRotation;
};
widget.Deselected += () =>
{
widget.color = Color.Yellow;
widget.Color = Color.Yellow;
item.CreateEditingHUD();
RotationLimits = RotationLimits;
if (SubEditorScreen.IsSubEditor())
@@ -557,7 +570,7 @@ namespace Barotrauma.Items.Components
};
widget.PreDraw += (sprtBtch, deltaTime) =>
{
widget.tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
widget.Tooltip = "Max: " + (int)MathHelper.ToDegrees(maxRotation);
widget.DrawPos = GetDrawPos() + new Vector2((float)Math.Cos(maxRotation), (float)Math.Sin(maxRotation)) * coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
widget.Update(deltaTime);
};
@@ -584,20 +597,20 @@ namespace Barotrauma.Items.Components
Vector2 offset = new Vector2(size / 2 + 5, -10);
if (!widgets.TryGetValue(id, out Widget widget))
{
widget = new Widget(id, size, Widget.Shape.Rectangle)
widget = new Widget(id, size, WidgetShape.Rectangle)
{
color = Color.Yellow,
tooltipOffset = offset,
inputAreaMargin = 20,
Color = Color.Yellow,
TooltipOffset = offset,
InputAreaMargin = 20,
RequireMouseOn = false
};
widgets.Add(id, widget);
initMethod?.Invoke(widget);
}
widget.size = size;
widget.tooltipOffset = offset;
widget.thickness = thickness;
widget.Size = size;
widget.TooltipOffset = offset;
widget.Thickness = thickness;
return widget;
}