v1.4.4.1 (Blood in the Water Update)
This commit is contained in:
@@ -771,7 +771,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
if (item.NonInteractable || item.NonPlayerTeamInteractable)
|
||||
if (!item.IsInteractable(Character.Controlled))
|
||||
{
|
||||
return QuickUseAction.None;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+2
-17
@@ -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 =
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,11 +168,14 @@ namespace Barotrauma
|
||||
//TODO: define this in xml
|
||||
slotSpriteSmall = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(10, 6, 119, 120), null, 0);
|
||||
// Adjustment to match the old size of 75,71
|
||||
SlotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * 0.575f, SlotSpriteSmall.SourceRect.Height * 0.575f);
|
||||
SlotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * SlotSpriteSmallScale, SlotSpriteSmall.SourceRect.Height * SlotSpriteSmallScale);
|
||||
}
|
||||
return slotSpriteSmall;
|
||||
}
|
||||
}
|
||||
|
||||
public const float SlotSpriteSmallScale = 0.575f;
|
||||
|
||||
public static Sprite DraggableIndicator;
|
||||
public static Sprite UnequippedIndicator, UnequippedHoverIndicator, UnequippedClickedIndicator, EquippedIndicator, EquippedHoverIndicator, EquippedClickedIndicator;
|
||||
|
||||
@@ -211,6 +214,7 @@ namespace Barotrauma
|
||||
public RichString Tooltip { get; private set; }
|
||||
|
||||
public int tooltipDisplayedCondition;
|
||||
public bool tooltipShowedContextualOptions;
|
||||
|
||||
public bool ForceTooltipRefresh;
|
||||
|
||||
@@ -230,6 +234,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (ForceTooltipRefresh) { return true; }
|
||||
if (Item == null) { return false; }
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand) != tooltipShowedContextualOptions) { return true; }
|
||||
return (int)Item.ConditionPercentage != tooltipDisplayedCondition;
|
||||
}
|
||||
|
||||
@@ -244,6 +249,7 @@ namespace Barotrauma
|
||||
}
|
||||
Tooltip = GetTooltip(Item, itemsInSlot, Character.Controlled);
|
||||
tooltipDisplayedCondition = (int)Item.ConditionPercentage;
|
||||
tooltipShowedContextualOptions = PlayerInput.KeyDown(InputType.ContextualCommand);
|
||||
}
|
||||
|
||||
private static RichString GetTooltip(Item item, IEnumerable<Item> itemsInSlot, Character character)
|
||||
@@ -323,19 +329,19 @@ namespace Barotrauma
|
||||
.TrimStart();
|
||||
}
|
||||
|
||||
if (itemsInSlot.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))
|
||||
if (itemsInSlot.All(it => !it.IsInteractable(Character.Controlled)))
|
||||
{
|
||||
toolTip += " " + TextManager.Get("connectionlocked");
|
||||
}
|
||||
if (!item.IsFullCondition && !item.Prefab.HideConditionInTooltip)
|
||||
{
|
||||
string conditionColorStr = XMLExtensions.ColorToString(ToolBox.GradientLerp(item.Condition / item.MaxCondition, GUIStyle.ColorInventoryEmpty, GUIStyle.ColorInventoryHalf, GUIStyle.ColorInventoryFull));
|
||||
string conditionColorStr = XMLExtensions.ToStringHex(ToolBox.GradientLerp(item.Condition / item.MaxCondition, GUIStyle.ColorInventoryEmpty, GUIStyle.ColorInventoryHalf, GUIStyle.ColorInventoryFull));
|
||||
toolTip += $"‖color:{conditionColorStr}‖ ({(int)item.ConditionPercentage} %)‖color:end‖";
|
||||
}
|
||||
if (!description.IsNullOrEmpty()) { toolTip += '\n' + description; }
|
||||
if (item.Prefab.ContentPackage != GameMain.VanillaContent && item.Prefab.ContentPackage != null)
|
||||
{
|
||||
colorStr = XMLExtensions.ColorToString(Color.MediumPurple);
|
||||
colorStr = XMLExtensions.ToStringHex(Color.MediumPurple);
|
||||
toolTip += $"\n‖color:{colorStr}‖{item.Prefab.ContentPackage.Name}‖color:end‖";
|
||||
}
|
||||
}
|
||||
@@ -350,7 +356,17 @@ namespace Barotrauma
|
||||
}
|
||||
#if DEBUG
|
||||
toolTip += $" ({item.Prefab.Identifier})";
|
||||
#endif
|
||||
#endif
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand))
|
||||
{
|
||||
toolTip += $"\n‖color:gui.blue‖{TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders"))}‖color:end‖";
|
||||
}
|
||||
else
|
||||
{
|
||||
var colorStr = XMLExtensions.ToStringHex(Color.LightGray * 0.7f);
|
||||
toolTip += $"\n‖color:{colorStr}‖{TextManager.Get("itemmsg.morreoptionsavailable")}‖color:end‖";
|
||||
}
|
||||
|
||||
return RichString.Rich(toolTip);
|
||||
}
|
||||
}
|
||||
@@ -613,10 +629,7 @@ namespace Barotrauma
|
||||
slot.State = GUIComponent.ComponentState.None;
|
||||
|
||||
if (mouseOn && (DraggingItems.Any() || selectedSlot == null || selectedSlot.Slot == slot) && DraggingInventory == null)
|
||||
// &&
|
||||
//(highlightedSubInventories.Count == 0 || highlightedSubInventories.Contains(this) || highlightedSubInventorySlot?.Slot == slot || highlightedSubInventory.Owner == item))
|
||||
{
|
||||
|
||||
{
|
||||
slot.State = GUIComponent.ComponentState.Hover;
|
||||
|
||||
if (selectedSlot == null || (!selectedSlot.IsSubSlot && isSubSlot))
|
||||
@@ -631,27 +644,47 @@ namespace Barotrauma
|
||||
|
||||
if (!DraggingItems.Any())
|
||||
{
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => !it.NonInteractable && !it.NonPlayerTeamInteractable) : slots[slotIndex].Items;
|
||||
if (PlayerInput.PrimaryMouseButtonDown() && interactableItems.Any())
|
||||
{
|
||||
if (PlayerInput.KeyDown(InputType.TakeHalfFromInventorySlot))
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => it.IsInteractable(Character.Controlled)) : slots[slotIndex].Items;
|
||||
if (interactableItems.Any())
|
||||
{
|
||||
if (availableContextualOrder.target != null)
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems.Skip(interactableItems.Count() / 2));
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(character: null,
|
||||
new Order(OrderPrefab.Prefabs[availableContextualOrder.orderIdentifier], availableContextualOrder.target, targetItem: null, orderGiver: Character.Controlled));
|
||||
}
|
||||
availableContextualOrder = default;
|
||||
}
|
||||
else if (PlayerInput.KeyDown(InputType.TakeOneFromInventorySlot))
|
||||
else if (PlayerInput.KeyDown(InputType.Command) &&
|
||||
PlayerInput.KeyDown(InputType.ContextualCommand) &&
|
||||
GameMain.GameSession?.CrewManager != null)
|
||||
{
|
||||
DraggingItems.Add(interactableItems.First());
|
||||
GameMain.GameSession.CrewManager.OpenCommandUI(interactableItems.FirstOrDefault(), forceContextual: true);
|
||||
}
|
||||
else
|
||||
else if (PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems);
|
||||
if (PlayerInput.KeyDown(InputType.TakeHalfFromInventorySlot))
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems.Skip(interactableItems.Count() / 2));
|
||||
}
|
||||
else if (PlayerInput.KeyDown(InputType.TakeOneFromInventorySlot))
|
||||
{
|
||||
DraggingItems.Add(interactableItems.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggingItems.AddRange(interactableItems);
|
||||
}
|
||||
DraggingSlot = slot;
|
||||
}
|
||||
DraggingSlot = slot;
|
||||
}
|
||||
}
|
||||
else if (PlayerInput.PrimaryMouseButtonReleased())
|
||||
{
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ? slots[slotIndex].Items.Where(it => !it.NonInteractable && !it.NonPlayerTeamInteractable) : slots[slotIndex].Items;
|
||||
var interactableItems = Screen.Selected == GameMain.GameScreen ?
|
||||
slots[slotIndex].Items.Where(it => it.IsInteractable(Character.Controlled)) :
|
||||
slots[slotIndex].Items;
|
||||
if (PlayerInput.DoubleClicked() && interactableItems.Any())
|
||||
{
|
||||
doubleClickedItems.Clear();
|
||||
@@ -1286,7 +1319,7 @@ namespace Barotrauma
|
||||
if (selectedInventory.GetItemAt(slotIndex)?.OwnInventory?.Container is { } container &&
|
||||
container.Inventory.CanBePut(item))
|
||||
{
|
||||
if (!container.AllowDragAndDrop || !container.DrawInventory)
|
||||
if (!container.AllowDragAndDrop || !container.AllowAccess)
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
@@ -1562,10 +1595,22 @@ namespace Barotrauma
|
||||
{
|
||||
selectedSlot.RefreshTooltip();
|
||||
}
|
||||
DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect);
|
||||
|
||||
if (!slotIconTooltip.IsNullOrEmpty())
|
||||
{
|
||||
DrawToolTip(spriteBatch, slotIconTooltip, slotRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect);
|
||||
}
|
||||
slotIconTooltip = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static (Item target, Identifier orderIdentifier) availableContextualOrder;
|
||||
private static LocalizedString slotIconTooltip;
|
||||
|
||||
public static void DrawSlot(SpriteBatch spriteBatch, Inventory inventory, VisualSlot slot, Item item, int slotIndex, bool drawItem = true, InvSlotType type = InvSlotType.Any)
|
||||
{
|
||||
Rectangle rect = slot.Rect;
|
||||
@@ -1730,7 +1775,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Color spriteColor = sprite == item.Sprite ? item.GetSpriteColor() : item.GetInventoryIconColor();
|
||||
if (inventory != null && (inventory.Locked || inventory.slots[slotIndex].Items.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))) { spriteColor *= 0.5f; }
|
||||
if (inventory != null && (inventory.Locked || inventory.slots[slotIndex].Items.All(it => !it.IsInteractable(Character.Controlled)))) { spriteColor *= 0.5f; }
|
||||
if (CharacterHealth.OpenHealthWindow != null && !item.UseInHealthInterface && !item.AllowedSlots.Contains(InvSlotType.HealthInterface) && item.GetComponent<GeneticMaterial>() == null)
|
||||
{
|
||||
spriteColor = Color.Lerp(spriteColor, Color.TransparentBlack, 0.5f);
|
||||
@@ -1741,15 +1786,24 @@ namespace Barotrauma
|
||||
}
|
||||
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
|
||||
|
||||
if (((item.SpawnedInCurrentOutpost && !item.AllowStealing) || (inventory != null && inventory.slots[slotIndex].Items.Any(it => it.SpawnedInCurrentOutpost && !it.AllowStealing))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
if (item.OrderedToBeIgnored)
|
||||
{
|
||||
var stealIcon = CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand];
|
||||
Vector2 iconSize = new Vector2(25 * GUI.Scale);
|
||||
stealIcon.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(rect.X + iconSize.X * 0.2f, rect.Bottom - iconSize.Y * 1.2f),
|
||||
color: GUIStyle.Red,
|
||||
scale: iconSize.X / stealIcon.size.X);
|
||||
if (OrderPrefab.Prefabs.TryGet(Tags.IgnoreThis, out OrderPrefab ignoreOrder))
|
||||
{
|
||||
DrawSideIcon(ignoreOrder.SymbolSprite, Direction.Right, TextManager.Get("tooltip.ignored"), ignoreOrder.Color, out bool mouseOn);
|
||||
if (mouseOn) { availableContextualOrder = (item, Tags.UnignoreThis); }
|
||||
|
||||
}
|
||||
}
|
||||
else if (Item.DeconstructItems.Contains(item) &&
|
||||
OrderPrefab.Prefabs.TryGet(Tags.DeconstructThis, out OrderPrefab deconstructOrder))
|
||||
{
|
||||
DrawSideIcon(deconstructOrder.SymbolSprite, Direction.Right, TextManager.Get("tooltip.markedfordeconstruction"), GUIStyle.Red, out bool mouseOn);
|
||||
if (mouseOn) { availableContextualOrder = (item, Tags.DontDeconstructThis); }
|
||||
}
|
||||
else if (((item.SpawnedInCurrentOutpost && !item.AllowStealing) || (inventory != null && inventory.slots[slotIndex].Items.Any(it => it.SpawnedInCurrentOutpost && !it.AllowStealing))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
{
|
||||
DrawSideIcon(CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand], Direction.Left, TextManager.Get("tooltip.stolenitem"), GUIStyle.Red, out _);
|
||||
}
|
||||
int maxStackSize = item.Prefab.GetMaxStackSize(inventory);
|
||||
if (inventory is ItemInventory itemInventory)
|
||||
@@ -1798,6 +1852,22 @@ namespace Barotrauma
|
||||
SpriteEffects.None,
|
||||
layerDepth: 0.0f);
|
||||
}
|
||||
|
||||
void DrawSideIcon(Sprite icon, Direction side, LocalizedString tooltip, Color color, out bool mouseOn)
|
||||
{
|
||||
Vector2 iconSize = new Vector2(25 * GUI.Scale);
|
||||
float margin = 0.2f;
|
||||
Vector2 pos = new Vector2(
|
||||
side == Direction.Left ? rect.X + iconSize.X * margin : rect.Right - iconSize.X * margin,
|
||||
rect.Bottom - iconSize.Y * 1.2f);
|
||||
mouseOn = Vector2.Distance(PlayerInput.MousePosition, pos) < iconSize.X / 2;
|
||||
if (mouseOn)
|
||||
{
|
||||
slotIconTooltip = tooltip;
|
||||
color = Color.Lerp(color, Color.White, 0.5f);
|
||||
}
|
||||
icon.Draw(spriteBatch, pos, color: color, scale: iconSize.X / icon.size.X);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -216,7 +219,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentageRelativeToDefaultMaxCondition;
|
||||
for (int i = 0; i < Prefab.BrokenSprites.Length;i++)
|
||||
{
|
||||
if (Prefab.BrokenSprites[i].FadeIn) { continue; }
|
||||
@@ -340,9 +343,28 @@ namespace Barotrauma
|
||||
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
|
||||
if (renderTransparent) { color *= 0.15f; }
|
||||
|
||||
if (Character.Controlled != null && Character.DebugDrawInteract)
|
||||
{
|
||||
color = Color.Red;
|
||||
foreach (var ic in components)
|
||||
{
|
||||
var interactionType = GetComponentInteractionVisibility(Character.Controlled, ic);
|
||||
if (interactionType == InteractionVisibility.MissingRequirement)
|
||||
{
|
||||
color = Color.Orange;
|
||||
}
|
||||
else if (interactionType == InteractionVisibility.Visible)
|
||||
{
|
||||
color = Color.LightGreen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BrokenItemSprite fadeInBrokenSprite = null;
|
||||
float fadeInBrokenSpriteAlpha = 0.0f;
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentageRelativeToDefaultMaxCondition;
|
||||
Vector2 drawOffset = GetCollapseEffectOffset();
|
||||
drawOffset.Y = -drawOffset.Y;
|
||||
|
||||
@@ -849,27 +871,6 @@ namespace Barotrauma
|
||||
itemEditor.Children.First().Color = Color.Black * 0.7f;
|
||||
if (!inGame)
|
||||
{
|
||||
//create a tag picker for item containers to make it easier to pick relevant tags for PreferredContainers
|
||||
var itemContainer = GetComponent<ItemContainer>();
|
||||
if (itemContainer != null)
|
||||
{
|
||||
var tagsField = itemEditor.Fields["Tags".ToIdentifier()].First().Parent;
|
||||
|
||||
//find all the items that can be put inside the container and add their PreferredContainer identifiers/tags to the available tags
|
||||
ImmutableHashSet<Identifier> availableTags = ItemPrefab.Prefabs
|
||||
.Where(ip => itemContainer.CanBeContained(ip))
|
||||
.SelectMany(ip => ip.PreferredContainers.SelectMany(pc => pc.Primary.Union(pc.Secondary)))
|
||||
//remove identifiers from the available container tags
|
||||
//(otherwise the list will include many irrelevant options,
|
||||
//e.g. "weldingtool" because a welding fuel tank can be placed inside the container, etc)
|
||||
.Where(t => !ItemPrefab.Prefabs.ContainsKey(t))
|
||||
.ToImmutableHashSet();
|
||||
new GUIButton(new RectTransform(new Vector2(0.1f, 1), tagsField.RectTransform, Anchor.TopRight), "...")
|
||||
{
|
||||
OnClicked = (bt, userData) => { CreateTagPicker(tagsField.GetChild<GUITextBox>(), availableTags); return true; }
|
||||
};
|
||||
}
|
||||
|
||||
if (Linkable)
|
||||
{
|
||||
var linkText = new GUITextBlock(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), TextManager.Get("HoldToLink"), font: GUIStyle.SmallFont);
|
||||
@@ -881,6 +882,33 @@ namespace Barotrauma
|
||||
linkText.TextColor = GUIStyle.Orange;
|
||||
itemsText.TextColor = GUIStyle.Orange;
|
||||
}
|
||||
|
||||
//create a tag picker for item containers to make it easier to pick relevant tags for PreferredContainers
|
||||
var itemContainer = GetComponent<ItemContainer>();
|
||||
if (itemContainer != null)
|
||||
{
|
||||
var tagBox = itemEditor.Fields["Tags".ToIdentifier()].First() as GUITextBox;
|
||||
var tagsField = tagBox?.Parent;
|
||||
|
||||
var containerTagLayout = new GUILayoutGroup(new RectTransform(new Point(editingHUD.Rect.Width, heightScaled), isFixedSize: true), isHorizontal: true);
|
||||
var containerTagButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1), containerTagLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
|
||||
new GUIButton(new RectTransform(new Vector2(0.95f, 1), containerTagButtonLayout.RectTransform), text: TextManager.Get("containertaguibutton"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (_, _) => { CreateContainerTagPicker(tagBox); return true; },
|
||||
TextBlock = { AutoScaleHorizontal = true }
|
||||
};
|
||||
var containerTagText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1), containerTagLayout.RectTransform), TextManager.Get("containertaguibuttondescription"), font: GUIStyle.SmallFont)
|
||||
{
|
||||
TextColor = GUIStyle.Orange
|
||||
};
|
||||
var limitedString = ToolBox.LimitString(containerTagText.Text, containerTagText.Font, itemEditor.Rect.Width - containerTagButtonLayout.Rect.Width);
|
||||
if (limitedString != containerTagText.Text)
|
||||
{
|
||||
containerTagText.ToolTip = containerTagText.Text;
|
||||
containerTagText.Text = limitedString;
|
||||
}
|
||||
itemEditor.AddCustomContent(containerTagLayout, 3);
|
||||
}
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)), isHorizontal: true)
|
||||
{
|
||||
@@ -972,6 +1000,12 @@ namespace Barotrauma
|
||||
};
|
||||
itemEditor.AddCustomContent(tickBox, 1);
|
||||
}
|
||||
|
||||
if (!Layer.IsNullOrEmpty())
|
||||
{
|
||||
var layerText = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)) { MinSize = new Point(0, heightScaled) }, TextManager.AddPunctuation(':', TextManager.Get("editor.layer"), Layer));
|
||||
itemEditor.AddCustomContent(layerText, 1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
@@ -987,7 +1021,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ic.requiredItems.Count == 0 && ic.DisabledRequiredItems.Count == 0 && SerializableProperty.GetProperties<Editable>(ic).Count == 0) { continue; }
|
||||
if (ic.RequiredItems.Count == 0 && ic.DisabledRequiredItems.Count == 0 && SerializableProperty.GetProperties<Editable>(ic).Count == 0) { continue; }
|
||||
}
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
|
||||
@@ -1004,7 +1038,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
List<RelatedItem> requiredItems = new List<RelatedItem>();
|
||||
foreach (var kvp in ic.requiredItems)
|
||||
foreach (var kvp in ic.RequiredItems)
|
||||
{
|
||||
foreach (RelatedItem relatedItem in kvp.Value)
|
||||
{
|
||||
@@ -1089,34 +1123,389 @@ namespace Barotrauma
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CreateTagPicker(GUITextBox textBox, IEnumerable<Identifier> availableTags)
|
||||
public void CreateContainerTagPicker([MaybeNull] GUITextBox tagTextBox)
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", "", new LocalizedString[] { TextManager.Get("Cancel") }, new Vector2(0.2f, 0.5f), new Point(300, 400));
|
||||
var msgBox = new GUIMessageBox(string.Empty, string.Empty, new[] { TextManager.Get("Ok") }, new Vector2(0.35f, 0.6f), new Point(400, 400));
|
||||
msgBox.Buttons[0].OnClicked = msgBox.Close;
|
||||
|
||||
var textList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), msgBox.Content.RectTransform, Anchor.TopCenter))
|
||||
var infoIcon = new GUIImage(new RectTransform(new Vector2(0.066f), msgBox.InnerFrame.RectTransform)
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
OnSelected = (component, userData) =>
|
||||
RelativeOffset = new Vector2(0.015f)
|
||||
}, style: "GUIButtonInfo")
|
||||
{
|
||||
ToolTip = TextManager.Get("containertagui.tutorial"),
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
var layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.85f), msgBox.Content.RectTransform));
|
||||
|
||||
var list = new GUIListBox(new RectTransform(new Vector2(1f, 1f), layout.RectTransform));
|
||||
|
||||
const float NameSize = 0.4f;
|
||||
const float ItemSize = 0.5f;
|
||||
const float CountSize = 0.1f;
|
||||
|
||||
var headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), list.Content.RectTransform), isHorizontal: true);
|
||||
new GUIButton(new RectTransform(new Vector2(NameSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.tag"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(ItemSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.items"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(CountSize, 1f), headerLayout.RectTransform), TextManager.Get("tagheader.count"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
|
||||
var itemsByTag =
|
||||
ContainerTagPrefab.Prefabs
|
||||
.ToImmutableDictionary(
|
||||
ct => ct,
|
||||
ct => ct.GetItemsAndSpawnProbabilities());
|
||||
|
||||
// Group the prefabs by category and turn them into a dictionary where the key is the category and value is the list of identifiers of the prefabs.
|
||||
// LINQ GroupBy returns GroupedEnumerable where the enumerable is the list of prefabs and key is what we grouped by.
|
||||
var tagCategories = ContainerTagPrefab.Prefabs
|
||||
.GroupBy(ct => ct.Category)
|
||||
.ToImmutableDictionary(
|
||||
g => g.Key,
|
||||
g => g.Select(ct => ct.Identifier).ToImmutableArray());
|
||||
|
||||
foreach (var (category, categoryTags) in tagCategories)
|
||||
{
|
||||
var categoryButton = new GUIButton(new RectTransform(new Vector2(1f, 0.075f), list.Content.RectTransform), style: "GUIButtonSmallFreeScale");
|
||||
categoryButton.Color *= 0.66f;
|
||||
var categoryLayout = new GUILayoutGroup(new RectTransform(Vector2.One, categoryButton.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
var categoryText = new GUITextBlock(new RectTransform(Vector2.One, categoryLayout.RectTransform), TextManager.Get($"tagcategory.{category}"), font: GUIStyle.SubHeadingFont);
|
||||
var arrowImage = new GUIImage(new RectTransform(new Vector2(1f, 0.5f), categoryLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonVerticalArrowFreeScale");
|
||||
var arrowPadding = new GUIFrame(new RectTransform(new Vector2(0.025f, 1f), categoryLayout.RectTransform), style: null);
|
||||
|
||||
bool hasHiddenCategories = false;
|
||||
foreach (var categoryTag in categoryTags.OrderBy(t => t.Value))
|
||||
{
|
||||
if (!(userData is Identifier)) { return true; }
|
||||
AddTag((Identifier)userData);
|
||||
textBox.Text = Tags;
|
||||
msgBox.Close();
|
||||
var found = itemsByTag.FirstOrNull(kvp => kvp.Key.Identifier == categoryTag);
|
||||
if (found is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find tag with identifier {categoryTag} in itemsByTag");
|
||||
continue;
|
||||
}
|
||||
|
||||
var (tag, prefabsAndProbabilities) = found.Value;
|
||||
|
||||
bool isCorrectSubType = tag.IsRecommendedForSub(Submarine);
|
||||
|
||||
var tagLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), list.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
UserData = category,
|
||||
Visible = isCorrectSubType
|
||||
};
|
||||
|
||||
if (!isCorrectSubType)
|
||||
{
|
||||
hasHiddenCategories = true;
|
||||
}
|
||||
|
||||
var checkBoxLayout = new GUILayoutGroup(new RectTransform(new Vector2(NameSize, 1f), tagLayout.RectTransform), childAnchor: Anchor.Center);
|
||||
var enabledCheckBox = new GUITickBox(new RectTransform(Vector2.One, checkBoxLayout.RectTransform, Anchor.Center), tag.Name, font: GUIStyle.SmallFont)
|
||||
{
|
||||
Selected = tags.Contains(tag.Identifier),
|
||||
ToolTip = tag.Description
|
||||
};
|
||||
|
||||
var tickBoxText = enabledCheckBox.TextBlock;
|
||||
tickBoxText.Text = ToolBox.LimitString(tickBoxText.Text, tickBoxText.Font, tickBoxText.Rect.Width);
|
||||
|
||||
var itemLayout = new GUILayoutGroup(new RectTransform(new Vector2(ItemSize, 1f), tagLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
var itemLayoutScissor = new GUIScissorComponent(new RectTransform(new Vector2(0.8f, 1f), itemLayout.RectTransform)) { CanBeFocused = false };
|
||||
var itemLayoutButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1), itemLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
|
||||
var itemLayoutButton = new GUIButton(new RectTransform(new Vector2(0.8f), itemLayoutButtonLayout.RectTransform), text: "...", style: "GUICharacterInfoButton")
|
||||
{
|
||||
UserData = tag,
|
||||
ToolTip = TextManager.Get("containertagui.viewprobabilities")
|
||||
};
|
||||
|
||||
itemLayoutButtonLayout.Recalculate();
|
||||
|
||||
float scroll = 0f;
|
||||
float localScroll = 0f;
|
||||
int lastSkippedItems = 0;
|
||||
int skippedItems = 0;
|
||||
var itemLayoutDraw = new GUICustomComponent(new RectTransform(new Vector2(1f, 0.9f), itemLayoutScissor.Content.RectTransform, Anchor.CenterLeft), onDraw: (spriteBatch, component) =>
|
||||
{
|
||||
component.ToolTip = string.Empty;
|
||||
|
||||
const float padding = 8f;
|
||||
float offset = 0f;
|
||||
float size = component.Rect.Height;
|
||||
int start = (int)Math.Floor(scroll);
|
||||
int amountToDraw = (int)Math.Ceiling(component.Rect.Width / size) + 1; // +1 just to be on the safe side
|
||||
bool shouldIncrementOnSkip = true;
|
||||
float toDrawWidth = prefabsAndProbabilities.Length * (size + padding);
|
||||
|
||||
// if the width is less than the component width we need to limit how many items we draw or it looks weird
|
||||
if (toDrawWidth < component.Rect.Width)
|
||||
{
|
||||
shouldIncrementOnSkip = false;
|
||||
amountToDraw = prefabsAndProbabilities.Length;
|
||||
}
|
||||
|
||||
for (int i = start; i < start + amountToDraw; i++)
|
||||
{
|
||||
var (ip, probability, _) = prefabsAndProbabilities[i % prefabsAndProbabilities.Length];
|
||||
var sprite = ip.InventoryIcon ?? ip.Sprite;
|
||||
|
||||
if (sprite is null)
|
||||
{
|
||||
// I don't think this should happen but just in case
|
||||
if (shouldIncrementOnSkip)
|
||||
{
|
||||
amountToDraw++;
|
||||
skippedItems++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldHideItemPrefab(ip, probability))
|
||||
{
|
||||
if (shouldIncrementOnSkip)
|
||||
{
|
||||
skippedItems++;
|
||||
amountToDraw++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float partialScroll = localScroll * (size + padding);
|
||||
var drawRect = new RectangleF(itemLayoutScissor.Rect.X + offset - partialScroll, component.Rect.Y, size, size);
|
||||
|
||||
var isMouseOver = drawRect.Contains(PlayerInput.MousePosition);
|
||||
if (isMouseOver)
|
||||
{
|
||||
component.ToolTip = ip.CreateTooltipText();
|
||||
}
|
||||
|
||||
var slotSprite = Inventory.SlotSpriteSmall;
|
||||
slotSprite?.Draw(spriteBatch, drawRect.Location, Color.White, origin: Vector2.Zero, rotate: 0f, scale: size / slotSprite.size.X * Inventory.SlotSpriteSmallScale);
|
||||
|
||||
float iconScale = Math.Min(drawRect.Width / sprite.size.X, drawRect.Height / sprite.size.Y) * 0.9f;
|
||||
|
||||
Color drawColor = ip.InventoryIconColor;
|
||||
|
||||
sprite.Draw(spriteBatch, drawRect.Center, drawColor, origin: sprite.Origin, scale: iconScale);
|
||||
offset += size + padding;
|
||||
}
|
||||
|
||||
// we need to compensate for the skipped items so that the scroll doesn't jump around
|
||||
if (skippedItems < lastSkippedItems)
|
||||
{
|
||||
scroll += lastSkippedItems - skippedItems;
|
||||
}
|
||||
|
||||
lastSkippedItems = skippedItems;
|
||||
skippedItems = 0;
|
||||
}, onUpdate: (deltaTime, component) =>
|
||||
{
|
||||
if (GUI.MouseOn != component && MathUtils.NearlyEqual(localScroll, 0, deltaTime * 2))
|
||||
{
|
||||
localScroll = 0f;
|
||||
return;
|
||||
}
|
||||
|
||||
float totalWidth = prefabsAndProbabilities.Length * (component.Rect.Height + 8f);
|
||||
if (totalWidth < component.Rect.Width) { return; }
|
||||
scroll += deltaTime;
|
||||
localScroll = scroll % 1f;
|
||||
})
|
||||
{
|
||||
HoverCursor = CursorState.Default,
|
||||
AlwaysOverrideCursor = true
|
||||
};
|
||||
|
||||
var tooltip = TextManager.Get(tag.WarnIfLess ? "ContainerTagUI.RecommendedAmount" : "ContainerTagUI.SuggestedAmount");
|
||||
|
||||
var countBlock = new GUITextBlock(new RectTransform(new Vector2(CountSize, 1f), tagLayout.RectTransform), string.Empty, textAlignment: Alignment.Center)
|
||||
{
|
||||
ToolTip = tooltip
|
||||
};
|
||||
UpdateCountBlock(countBlock, tag);
|
||||
|
||||
enabledCheckBox.OnSelected += tickBox =>
|
||||
{
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
AddTag(tag.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveTag(tag.Identifier);
|
||||
}
|
||||
|
||||
if (tagTextBox is not null)
|
||||
{
|
||||
tagTextBox.Text = string.Join(',', tags.Where(t => !Prefab.Tags.Contains(t)));
|
||||
}
|
||||
UpdateCountBlock(countBlock, tag);
|
||||
return true;
|
||||
};
|
||||
|
||||
itemLayoutButton.OnClicked = (button, _) =>
|
||||
{
|
||||
CreateContainerTagItemListPopup(tag, button.Rect.Center, layout, prefabsAndProbabilities);
|
||||
return true;
|
||||
};
|
||||
|
||||
void UpdateCountBlock(GUITextBlock textBlock, ContainerTagPrefab containerTag)
|
||||
{
|
||||
if (textBlock is null) { return; }
|
||||
|
||||
var tagCount = Submarine.GetItems(alsoFromConnectedSubs: true).Count(i => i.HasTag(containerTag.Identifier));
|
||||
textBlock.Text = $"{tagCount} ({containerTag.RecommendedAmount})";
|
||||
|
||||
if (!isCorrectSubType || !containerTag.WarnIfLess || containerTag.RecommendedAmount <= 0) { return; }
|
||||
|
||||
if (tagCount < containerTag.RecommendedAmount)
|
||||
{
|
||||
textBlock.TextColor = GUIStyle.Red;
|
||||
textBlock.Text += "*";
|
||||
textBlock.ToolTip = RichString.Rich($"{tooltip}\n\n‖color:gui.red‖{TextManager.Get("ContainerTagUI.RecommendedAmountWarning")}‖color:end‖");
|
||||
}
|
||||
else if (tagCount >= containerTag.RecommendedAmount)
|
||||
{
|
||||
textBlock.TextColor = GUIStyle.Green;
|
||||
textBlock.ToolTip = tooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrowImage.SpriteEffects = hasHiddenCategories ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
categoryButton.OnClicked = (_, _) =>
|
||||
{
|
||||
arrowImage.SpriteEffects ^= SpriteEffects.FlipVertically;
|
||||
|
||||
foreach (var child in list.Content.Children)
|
||||
{
|
||||
if (child.UserData is Identifier id && id == category)
|
||||
{
|
||||
child.Visible = !child.Visible;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateContainerTagItemListPopup(ContainerTagPrefab tag, Point location, GUIComponent popupParent, ImmutableArray<ContainerTagPrefab.ItemAndProbability> prefabAndProbabilities)
|
||||
{
|
||||
const string TooltipUserData = "tooltip";
|
||||
const string ProbabilityUserData = "probability";
|
||||
|
||||
if (popupParent.GetChildByUserData(TooltipUserData) is { } existingTooltip)
|
||||
{
|
||||
popupParent.RemoveChild(existingTooltip);
|
||||
}
|
||||
|
||||
var tooltip = new GUIFrame(new RectTransform(new Point(popupParent.Rect.Height), popupParent.RectTransform)
|
||||
{
|
||||
AbsoluteOffset = location - popupParent.Rect.Location
|
||||
})
|
||||
{
|
||||
UserData = TooltipUserData,
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
if (tooltip.Rect.Bottom > GameMain.GraphicsHeight)
|
||||
{
|
||||
int diffY = tooltip.Rect.Bottom - GameMain.GraphicsHeight;
|
||||
tooltip.RectTransform.AbsoluteOffset -= new Point(0, diffY);
|
||||
}
|
||||
|
||||
if (tooltip.Rect.Right > GameMain.GraphicsWidth)
|
||||
{
|
||||
int diffX = tooltip.Rect.Right - GameMain.GraphicsWidth;
|
||||
tooltip.RectTransform.AbsoluteOffset -= new Point(diffX, 0);
|
||||
}
|
||||
|
||||
var tooltipLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(tooltip.RectTransform, 0.9f), tooltip.RectTransform, Anchor.Center));
|
||||
|
||||
var tooltipHeader = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), tag.Name, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont);
|
||||
var tooltipList = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), tooltipLayout.RectTransform));
|
||||
|
||||
var tooltipHeaderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), tooltipList.Content.RectTransform), isHorizontal: true);
|
||||
new GUIButton(new RectTransform(new Vector2(0.66f, 1f), tooltipHeaderLayout.RectTransform), TextManager.Get("tagheader.item"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
new GUIButton(new RectTransform(new Vector2(0.33f, 1f), tooltipHeaderLayout.RectTransform), TextManager.Get("tagheader.probability"), style: "GUIButtonSmallFreeScale") { ForceUpperCase = ForceUpperCase.Yes, CanBeFocused = false };
|
||||
|
||||
foreach (var itemAndProbability in prefabAndProbabilities.OrderByDescending(p => p.Probability))
|
||||
{
|
||||
var (ip, probability, campaignOnlyProbability) = itemAndProbability;
|
||||
if (ShouldHideItemPrefab(ip, probability)) { continue; }
|
||||
|
||||
var itemLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), tooltipList.Content.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
UserData = itemAndProbability
|
||||
};
|
||||
|
||||
var itemNameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.66f, 1f), itemLayout.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var itemIcon = new GUIImage(new RectTransform(Vector2.One, itemNameLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), ip.InventoryIcon ?? ip.Sprite, scaleToFit: true)
|
||||
{
|
||||
Color = ip.InventoryIconColor
|
||||
};
|
||||
|
||||
var itemName = new GUITextBlock(new RectTransform(Vector2.One, itemNameLayout.RectTransform), ip.Name);
|
||||
itemName.Text = ToolBox.LimitString(ip.Name, itemName.Font, itemName.Rect.Width);
|
||||
|
||||
var toolTipContainer = new GUIFrame(new RectTransform(Vector2.One, itemNameLayout.RectTransform), style: null)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = ip.CreateTooltipText()
|
||||
};
|
||||
|
||||
var probabilityText = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1f), itemLayout.RectTransform), ProbabilityToPercentage(campaignOnlyProbability), textAlignment: Alignment.Right)
|
||||
{
|
||||
UserData = ProbabilityUserData
|
||||
};
|
||||
if (MathUtils.NearlyEqual(campaignOnlyProbability, 0f)) { probabilityText.TextColor = GUIStyle.Red; }
|
||||
}
|
||||
|
||||
var campaignCheckbox = new GUITickBox(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), label: TextManager.Get("containertagui.campaignonly"))
|
||||
{
|
||||
ToolTip = TextManager.Get("containertagui.campaignonlytooltip"),
|
||||
Selected = true,
|
||||
OnSelected = box =>
|
||||
{
|
||||
foreach (var child in tooltipList.Content.Children)
|
||||
{
|
||||
if (child.UserData is not ContainerTagPrefab.ItemAndProbability data) { continue; }
|
||||
|
||||
if (child.GetChildByUserData(ProbabilityUserData) is not GUITextBlock text) { continue; }
|
||||
|
||||
float probability = box.Selected
|
||||
? data.CampaignProbability
|
||||
: data.Probability;
|
||||
text.Text = ProbabilityToPercentage(probability);
|
||||
|
||||
text.TextColor = MathUtils.NearlyEqual(probability, 0f)
|
||||
? GUIStyle.Red
|
||||
: GUIStyle.TextColorNormal;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var availableTag in availableTags.ToList().OrderBy(t => t))
|
||||
var tooltipClose = new GUIButton(new RectTransform(new Vector2(1f, 0.1f), tooltipLayout.RectTransform), TextManager.Get("Close"))
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) },
|
||||
ToolBox.LimitString(availableTag.Value, GUIStyle.Font, textList.Content.Rect.Width))
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
UserData = availableTag
|
||||
};
|
||||
}
|
||||
popupParent.RemoveChild(tooltip);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static LocalizedString ProbabilityToPercentage(float probability)
|
||||
=> TextManager.GetWithVariable("percentageformat", "[value]", MathF.Round((probability * 100f), 1).ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
}
|
||||
|
||||
private static bool ShouldHideItemPrefab(ItemPrefab ip, float probability)
|
||||
=> ip.HideInMenus && MathUtils.NearlyEqual(probability, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Reposition currently active item interfaces to make sure they don't overlap with each other
|
||||
/// </summary>
|
||||
@@ -1213,10 +1602,21 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
activeHUDs.Clear();
|
||||
maxPriorityHUDs.Clear();
|
||||
bool DrawHud(ItemComponent ic)
|
||||
{
|
||||
if (!ic.ShouldDrawHUD(character)) { return false; }
|
||||
if (character.HasEquippedItem(this))
|
||||
{
|
||||
return ic.DrawHudWhenEquipped;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ic.CanBeSelected && ic.HasRequiredItems(character, addMessage: false);
|
||||
}
|
||||
}
|
||||
//the HUD of the component with the highest priority will be drawn
|
||||
//if all components have a priority of 0, all of them are drawn
|
||||
maxPriorityHUDs.Clear();
|
||||
bool DrawHud(ItemComponent ic) => ic.ShouldDrawHUD(character) && (ic.CanBeSelected && ic.HasRequiredItems(character, addMessage: false) || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped));
|
||||
foreach (ItemComponent ic in activeComponents)
|
||||
{
|
||||
if (ic.HudPriority > 0 && DrawHud(ic) && (maxPriorityHUDs.Count == 0 || ic.HudPriority >= maxPriorityHUDs[0].HudPriority))
|
||||
@@ -1317,7 +1717,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
readonly List<ColoredText> texts = new List<ColoredText>();
|
||||
readonly List<ColoredText> texts = new();
|
||||
public List<ColoredText> GetHUDTexts(Character character, bool recreateHudTexts = true)
|
||||
{
|
||||
// Always create the texts if they have not yet been created
|
||||
@@ -1348,42 +1748,91 @@ namespace Barotrauma
|
||||
nameText += $" x{DroppedStack.Count()}";
|
||||
}
|
||||
|
||||
texts.Add(new ColoredText(nameText, GUIStyle.TextColorNormal, false, false));
|
||||
texts.Add(new ColoredText(nameText, GUIStyle.TextColorNormal, isCommand: false, isError: false));
|
||||
|
||||
if (CampaignMode.BlocksInteraction(CampaignInteractionType))
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use)).Value, Color.Cyan, false, false));
|
||||
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Use)).Value, Color.Cyan, isCommand: false, isError: false));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ItemComponent ic in components)
|
||||
foreach (ItemComponent itemComponent in components)
|
||||
{
|
||||
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
||||
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
||||
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
|
||||
Color color = Color.Gray;
|
||||
if (ic.HasRequiredItems(character, false))
|
||||
{
|
||||
if (ic is Repairable r)
|
||||
{
|
||||
if (r.IsBelowRepairThreshold) { color = Color.Cyan; }
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Cyan;
|
||||
}
|
||||
}
|
||||
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
texts.Add(new ColoredText(ic.DisplayMsg.Value, color, false, false));
|
||||
var interactionVisibility = GetComponentInteractionVisibility(character, itemComponent);
|
||||
if (interactionVisibility == InteractionVisibility.None) { continue; }
|
||||
if (itemComponent.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
|
||||
Color color = interactionVisibility == InteractionVisibility.MissingRequirement ? Color.Gray : Color.Cyan;
|
||||
texts.Add(new ColoredText(itemComponent.DisplayMsg.Value, color, isCommand: false, isError: false));
|
||||
}
|
||||
}
|
||||
if (PlayerInput.IsShiftDown() && CrewManager.DoesItemHaveContextualOrders(this))
|
||||
if (PlayerInput.KeyDown(InputType.ContextualCommand))
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")).Value, Color.Cyan, false, false));
|
||||
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")).Value, Color.Cyan, isCommand: false, isError: false));
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.Get("itemmsg.morreoptionsavailable").Value, Color.LightGray * 0.7f, isCommand: false, isError: false));
|
||||
}
|
||||
return texts;
|
||||
}
|
||||
|
||||
private enum InteractionVisibility
|
||||
{
|
||||
None,
|
||||
MissingRequirement,
|
||||
Visible
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine, for UI display purposes, the type of interaction visibility for an item component.
|
||||
///
|
||||
/// Example:
|
||||
/// Visible -> Display cyan "click to interact" type text on item hover.
|
||||
/// MissingRequirement -> Display gray "need tool" type text on item hover.
|
||||
/// None -> Hide from item hover texts.
|
||||
/// </summary>
|
||||
/// <param name="character">Character, for tool requirement purposes.</param>
|
||||
/// <param name="itemComponent">The item component to inspect.</param>
|
||||
/// <returns>The interaction visibility state for this component.</returns>
|
||||
private static InteractionVisibility GetComponentInteractionVisibility(Character character, ItemComponent itemComponent)
|
||||
{
|
||||
if (!itemComponent.CanBePicked && !itemComponent.CanBeSelected) { return InteractionVisibility.None; }
|
||||
if (itemComponent is Holdable holdable && !holdable.CanBeDeattached()) { return InteractionVisibility.None; }
|
||||
if (itemComponent is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { return InteractionVisibility.None; }
|
||||
|
||||
InteractionVisibility interactionVisibility = InteractionVisibility.MissingRequirement;
|
||||
if (itemComponent.HasRequiredItems(character, addMessage: false))
|
||||
{
|
||||
if (itemComponent is Repairable repairable)
|
||||
{
|
||||
if (repairable.IsBelowRepairThreshold)
|
||||
{
|
||||
interactionVisibility = InteractionVisibility.Visible;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
interactionVisibility = InteractionVisibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
return interactionVisibility;
|
||||
}
|
||||
|
||||
public bool HasVisibleInteraction(Character character)
|
||||
{
|
||||
foreach (var component in components)
|
||||
{
|
||||
if (GetComponentInteractionVisibility(character, component) == InteractionVisibility.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ForceHUDLayoutUpdate(bool ignoreLocking = false)
|
||||
{
|
||||
foreach (ItemComponent ic in activeHUDs)
|
||||
|
||||
Reference in New Issue
Block a user