Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop
This commit is contained in:
23
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
23
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -47,14 +47,33 @@ body:
|
||||
- Happens regularly
|
||||
- Happens every time I play
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: mporsp
|
||||
attributes:
|
||||
label: Single player or multiplayer?
|
||||
description: Did the issue happen in single player, multiplayer, or both? How was the server being hosted?
|
||||
options:
|
||||
- Single player
|
||||
- Multiplayer hosted from the in-game menu (= using a listen server)
|
||||
- Multiplayer hosted using a dedicated server
|
||||
- Happens in both single player and multiplayer
|
||||
- Happens outside single player or multiplayer game modes (e.g. game launches on startup, something broken in the main menu)
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: othermporsp
|
||||
attributes:
|
||||
label: "-"
|
||||
description: If you selected "Other" in the above dropdown, please clarify here.
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version of the game did the bug happen in? You can see the current version number in the bottom left corner of your screen in the main menu.
|
||||
options:
|
||||
- v1.0.21.0
|
||||
- v1.1.18.0 (Treacherous Tides)
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -255,7 +255,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool Freeze { get; set; }
|
||||
|
||||
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true, bool? followSub = null)
|
||||
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true, bool allowInput = true, bool? followSub = null)
|
||||
{
|
||||
prevPosition = position;
|
||||
prevZoom = zoom;
|
||||
@@ -268,7 +268,7 @@ namespace Barotrauma
|
||||
Vector2 moveInput = Vector2.Zero;
|
||||
if (allowMove && !Freeze)
|
||||
{
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null)
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null && allowInput)
|
||||
{
|
||||
if (PlayerInput.KeyDown(Keys.LeftShift)) { moveSpeed *= 2.0f; }
|
||||
if (PlayerInput.KeyDown(Keys.LeftControl)) { moveSpeed *= 0.5f; }
|
||||
|
||||
@@ -50,9 +50,9 @@ namespace Barotrauma
|
||||
}
|
||||
else if (Entity is Item i)
|
||||
{
|
||||
if (i.Submarine != null && i.GetComponent<Items.Components.Door>() == null)
|
||||
if (i.Submarine != null && i.Container != null)
|
||||
{
|
||||
// Don't show items that are inside the submarine, because monsters shouldn't target them when they are inside and the monsters are outside.
|
||||
// Don't show contained items that are inside the submarine, because they shouldn't attract monsters.
|
||||
return;
|
||||
}
|
||||
color = Color.CadetBlue;
|
||||
|
||||
@@ -82,15 +82,20 @@ namespace Barotrauma
|
||||
{
|
||||
var previousNode = path.Nodes[i - 1];
|
||||
var currentNode = path.Nodes[i];
|
||||
bool isPathActive = !path.Finished && !path.IsAtEndNode;
|
||||
Color pathColor = isPathActive ? Color.Blue * 0.5f : Color.Gray;
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y),
|
||||
new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y),
|
||||
Color.Blue * 0.5f, 0, 3);
|
||||
pathColor, 0, 3);
|
||||
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch,
|
||||
currentNode.ID.ToString(),
|
||||
new Vector2(currentNode.DrawPosition.X - 10, -currentNode.DrawPosition.Y - 30),
|
||||
Color.Blue);
|
||||
if (isPathActive)
|
||||
{
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch,
|
||||
currentNode.ID.ToString(),
|
||||
new Vector2(currentNode.DrawPosition.X - 10, -currentNode.DrawPosition.Y - 30),
|
||||
Color.Blue);
|
||||
}
|
||||
}
|
||||
if (path.CurrentNode != null)
|
||||
{
|
||||
|
||||
@@ -469,6 +469,7 @@ namespace Barotrauma
|
||||
float gibParticleAmount = MathHelper.Clamp(limb.Mass / character.AnimController.Mass, 0.1f, 1.0f);
|
||||
foreach (ParticleEmitter emitter in character.GibEmitters)
|
||||
{
|
||||
if (emitter?.Prefab == null) { continue; }
|
||||
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
|
||||
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ namespace Barotrauma
|
||||
protected float hudInfoTimer = 1.0f;
|
||||
protected bool hudInfoVisible = false;
|
||||
|
||||
private float pressureParticleTimer;
|
||||
|
||||
private float findFocusedTimer;
|
||||
|
||||
protected float lastRecvPositionUpdateTime;
|
||||
@@ -299,7 +297,9 @@ namespace Barotrauma
|
||||
keys[i].SetState();
|
||||
}
|
||||
|
||||
if (CharacterInventory.IsMouseOnInventory && CharacterHUD.ShouldDrawInventory(this))
|
||||
if (CharacterInventory.IsMouseOnInventory &&
|
||||
!keys[(int)InputType.Aim].Held &&
|
||||
CharacterHUD.ShouldDrawInventory(this))
|
||||
{
|
||||
ResetInputIfPrimaryMouse(InputType.Use);
|
||||
ResetInputIfPrimaryMouse(InputType.Shoot);
|
||||
@@ -340,13 +340,6 @@ namespace Barotrauma
|
||||
cam.Zoom = MathHelper.Lerp(cam.Zoom,
|
||||
cam.DefaultZoom + (Math.Max(pressure, 10) / 150.0f) * Rand.Range(0.9f, 1.1f),
|
||||
zoomInEffectStrength);
|
||||
|
||||
pressureParticleTimer += pressure * deltaTime;
|
||||
if (pressureParticleTimer > 10.0f)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle(Params.BleedParticleWater, WorldPosition + Rand.Vector(5.0f), Rand.Vector(10.0f));
|
||||
pressureParticleTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,7 +715,8 @@ namespace Barotrauma
|
||||
break;
|
||||
default:
|
||||
var petBehavior = enemyAI.PetBehavior;
|
||||
if (petBehavior != null && petBehavior.Happiness < petBehavior.MaxHappiness * 0.25f)
|
||||
if (petBehavior != null &&
|
||||
(petBehavior.Happiness < petBehavior.UnhappyThreshold || petBehavior.Hunger > petBehavior.HungryThreshold))
|
||||
{
|
||||
PlaySound(CharacterSound.SoundType.Unhappy);
|
||||
}
|
||||
@@ -859,21 +853,11 @@ namespace Barotrauma
|
||||
|
||||
if (Controlled.AnimController.Stairs != null)
|
||||
{
|
||||
//consider the bottom of the stairs the "floor of the room the controlled character is in"
|
||||
yPos = Controlled.AnimController.Stairs.SimPosition.Y - Controlled.AnimController.Stairs.RectHeight * 0.5f;
|
||||
}
|
||||
|
||||
foreach (var ladder in Ladder.List)
|
||||
{
|
||||
if (CanInteractWith(ladder.Item) && Controlled.CanInteractWith(ladder.Item))
|
||||
{
|
||||
float xPos = ladder.Item.SimPosition.X;
|
||||
if (Math.Abs(xPos - SimPosition.X) < 3.0)
|
||||
{
|
||||
yPos = ladder.Item.SimPosition.Y - ladder.Item.RectHeight * 0.5f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
//don't show the HUD texts if the character is below the floor of the room the controlled character is in
|
||||
if (AnimController.FloorY < yPos) { return; }
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Barotrauma
|
||||
public override bool Interrupted => Character.Removed || !Character.Enabled;
|
||||
|
||||
public override Color Color =>
|
||||
Character.CharacterHealth.GetAfflictionStrength(AfflictionPrefab.PoisonType) > 0 || Character.CharacterHealth.GetAfflictionStrength(AfflictionPrefab.ParalysisType) > 0 ?
|
||||
Character.CharacterHealth.GetAfflictionStrengthByType(AfflictionPrefab.PoisonType) > 0 || Character.CharacterHealth.GetAfflictionStrengthByType(AfflictionPrefab.ParalysisType) > 0 ?
|
||||
GUIStyle.HealthBarColorPoisoned : GUIStyle.Red;
|
||||
|
||||
public override string NumberToDisplay => string.Empty;
|
||||
@@ -737,7 +737,8 @@ namespace Barotrauma
|
||||
if (!character.DisableHealthWindow &&
|
||||
character.IsFriendly(character.FocusedCharacter) &&
|
||||
character.FocusedCharacter.CharacterHealth.UseHealthWindow &&
|
||||
character.CanInteractWith(character.FocusedCharacter, 160f, false))
|
||||
character.CanInteractWith(character.FocusedCharacter, 160f, false) &&
|
||||
!character.IsClimbing)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", InputType.Health),
|
||||
GUIStyle.Green, Color.Black, 2, GUIStyle.SmallFont);
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace Barotrauma
|
||||
public bool LastControlled;
|
||||
public int CrewListIndex { get; set; } = -1;
|
||||
|
||||
#warning TODO: Refactor
|
||||
private Sprite disguisedPortrait;
|
||||
private List<WearableSprite> disguisedAttachmentSprites;
|
||||
private Vector2? disguisedSheetIndex;
|
||||
@@ -609,7 +608,6 @@ namespace Barotrauma
|
||||
CharacterInfo = info;
|
||||
parentComponent = parent;
|
||||
HasIcon = hasIcon;
|
||||
|
||||
RecreateFrameContents();
|
||||
}
|
||||
|
||||
@@ -848,6 +846,12 @@ namespace Barotrauma
|
||||
|
||||
var info = CharacterInfo;
|
||||
|
||||
if (info.HeadSprite == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Head Selection: the head sprite is null! Failed to open the head selection.");
|
||||
return false;
|
||||
}
|
||||
|
||||
float characterHeightWidthRatio = info.HeadSprite.size.Y / info.HeadSprite.size.X;
|
||||
HeadSelectionList ??= new GUIListBox(
|
||||
new RectTransform(
|
||||
@@ -885,8 +889,13 @@ namespace Barotrauma
|
||||
GUILayoutGroup row = null;
|
||||
int itemsInRow = 0;
|
||||
|
||||
ContentXElement headElement = info.Ragdoll.MainElement.Elements().FirstOrDefault(e =>
|
||||
ContentXElement headElement = info.Ragdoll.MainElement?.Elements().FirstOrDefault(e =>
|
||||
e.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase));
|
||||
if (headElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Head Selection: the head element is null in {info.ragdoll.FileName}! Failed to open the head selection.");
|
||||
return false;
|
||||
}
|
||||
ContentXElement headSpriteElement = headElement.GetChildElement("sprite");
|
||||
ContentPath spritePathWithTags = headSpriteElement.GetAttributeContentPath("texture");
|
||||
|
||||
@@ -963,7 +972,7 @@ namespace Barotrauma
|
||||
private bool SwitchAttachment(GUIScrollBar scrollBar, WearableType type)
|
||||
{
|
||||
var info = CharacterInfo;
|
||||
int index = (int)scrollBar.BarScrollValue;
|
||||
int index = (int)Math.Round(scrollBar.BarScrollValue);
|
||||
switch (type)
|
||||
{
|
||||
case WearableType.Beard:
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory.ClientEventRead(msg, sendingTime);
|
||||
Inventory.ClientEventRead(msg);
|
||||
}
|
||||
break;
|
||||
case EventType.Control:
|
||||
@@ -406,7 +406,7 @@ namespace Barotrauma
|
||||
float targetY = msg.ReadSingle();
|
||||
Vector2 targetSimPos = new Vector2(targetX, targetY);
|
||||
//255 = entity already removed, no need to do anything
|
||||
if (attackLimbIndex == 255 || Removed) { break; }
|
||||
if (attackLimbIndex == 255 || targetEntityID == NullEntityID || Removed) { break; }
|
||||
if (attackLimbIndex >= AnimController.Limbs.Length)
|
||||
{
|
||||
//it's possible to get these errors when mid-round syncing, as the client may not
|
||||
@@ -666,7 +666,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Could not set order \"" + orderPrefab.Identifier + "\" for character \"" + character.Name + "\" because required target entity was not found.");
|
||||
DebugConsole.AddSafeError("Could not set order \"" + orderPrefab.Identifier + "\" for character \"" + character.Name + "\" because required target entity was not found.");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -257,7 +257,8 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < character.Inventory.Capacity; i++)
|
||||
{
|
||||
if (character.Inventory.SlotTypes[i] != InvSlotType.HealthInterface || Character.Controlled != Character) { continue; }
|
||||
if (character.Inventory.SlotTypes[i] != InvSlotType.HealthInterface) { continue; }
|
||||
if (character.Inventory.HideSlot(i)) { continue; }
|
||||
|
||||
//don't draw the item if it's being dragged out of the slot
|
||||
bool drawItem = !Inventory.DraggingItems.Any() || !Character.Inventory.GetItemsAt(i).All(it => Inventory.DraggingItems.Contains(it)) || character.Inventory.visualSlots[i].MouseOn();
|
||||
@@ -479,6 +480,17 @@ namespace Barotrauma
|
||||
inventoryScale = Inventory.UIScale;
|
||||
uiScale = GUI.Scale;
|
||||
|
||||
showHiddenAfflictionsButton.RectTransform.NonScaledSize = new Point(afflictionIconContainer.Rect.Height);
|
||||
//remove affliction icons so we recreate and resize them
|
||||
for (int i = afflictionIconContainer.CountChildren - 1; i >= 0; i--)
|
||||
{
|
||||
var child = afflictionIconContainer.GetChild(i);
|
||||
if (child.UserData is AfflictionPrefab)
|
||||
{
|
||||
afflictionIconContainer.RemoveChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
healthBarHolder.RectTransform.AbsoluteOffset = HUDLayoutSettings.HealthBarArea.Location;
|
||||
healthBarHolder.RectTransform.NonScaledSize = HUDLayoutSettings.HealthBarArea.Size;
|
||||
healthBarHolder.RectTransform.RelativeOffset = Vector2.Zero;
|
||||
@@ -496,6 +508,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
healthWindow.RectTransform.RecalculateChildren(false);
|
||||
|
||||
Character.Inventory?.RefreshSlotPositions();
|
||||
}
|
||||
|
||||
public void UpdateClientSpecific(float deltaTime)
|
||||
@@ -579,7 +593,7 @@ namespace Barotrauma
|
||||
|
||||
bool inWater = Character.AnimController.InWater;
|
||||
var drawTarget = inWater ? Particles.ParticlePrefab.DrawTargetType.Water : Particles.ParticlePrefab.DrawTargetType.Air;
|
||||
var emitter = Character.BloodEmitters.FirstOrDefault(e => e.Prefab.ParticlePrefab.DrawTarget == drawTarget || e.Prefab.ParticlePrefab.DrawTarget == Particles.ParticlePrefab.DrawTargetType.Both);
|
||||
var emitter = Character.BloodEmitters.FirstOrDefault(e => e.Prefab.ParticlePrefab?.DrawTarget == drawTarget || e.Prefab.ParticlePrefab?.DrawTarget == Particles.ParticlePrefab.DrawTargetType.Both);
|
||||
float particleMinScale = emitter?.Prefab.Properties.ScaleMin ?? 0.5f;
|
||||
float particleMaxScale = emitter?.Prefab.Properties.ScaleMax ?? 1;
|
||||
float severity = Math.Min(affliction.Strength / affliction.Prefab.MaxStrength * Character.Params.BleedParticleMultiplier, 1);
|
||||
@@ -2129,7 +2143,7 @@ namespace Barotrauma
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
float burnStrength = affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.BurnOverlayAlpha;
|
||||
if (kvp.Value == limbHealths[limb.HealthIndex])
|
||||
if (kvp.Value == limbHealths[limb.HealthIndex] || !affliction.Prefab.LimbSpecific)
|
||||
{
|
||||
limb.BurnOverlayStrength += burnStrength;
|
||||
limb.DamageOverlayStrength += affliction.Strength / Math.Min(affliction.Prefab.MaxStrength, 100) * affliction.Prefab.DamageOverlayAlpha;
|
||||
|
||||
@@ -12,8 +12,6 @@ namespace Barotrauma
|
||||
private static DateTimeOffset OnCooldownUntil = DateTimeOffset.MinValue;
|
||||
private const float CooldownDuration = 0.5f;
|
||||
|
||||
public static readonly Identifier MedicalItemTag = new Identifier("medical");
|
||||
|
||||
public static void PutOnCooldown()
|
||||
{
|
||||
OnCooldownUntil = DateTimeOffset.UtcNow.AddSeconds(CooldownDuration);
|
||||
|
||||
@@ -432,20 +432,20 @@ namespace Barotrauma
|
||||
{
|
||||
if (Sprite != null)
|
||||
{
|
||||
Sprite.Remove();
|
||||
var source = Sprite.SourceElement;
|
||||
Sprite.Remove();
|
||||
Sprite = new Sprite(source, file: GetSpritePath(source, Params.normalSpriteParams, ref _texturePath));
|
||||
}
|
||||
if (_deformSprite != null)
|
||||
{
|
||||
_deformSprite.Remove();
|
||||
var source = _deformSprite.Sprite.SourceElement;
|
||||
_deformSprite.Remove();
|
||||
_deformSprite = new DeformableSprite(source, filePath: GetSpritePath(source, Params.deformSpriteParams, ref _texturePath));
|
||||
}
|
||||
if (DamagedSprite != null)
|
||||
{
|
||||
DamagedSprite.Remove();
|
||||
var source = DamagedSprite.SourceElement;
|
||||
DamagedSprite.Remove();
|
||||
DamagedSprite = new Sprite(source, file: GetSpritePath(source, Params.damagedSpriteParams, ref _damagedTexturePath));
|
||||
}
|
||||
for (int i = 0; i < ConditionalSprites.Count; i++)
|
||||
@@ -458,8 +458,8 @@ namespace Barotrauma
|
||||
for (int i = 0; i < DecorativeSprites.Count; i++)
|
||||
{
|
||||
var decorativeSprite = DecorativeSprites[i];
|
||||
decorativeSprite.Remove();
|
||||
var source = decorativeSprite.Sprite.SourceElement;
|
||||
decorativeSprite.Remove();
|
||||
DecorativeSprites[i] = new DecorativeSprite(source, file: GetSpritePath(source, Params.decorativeSpriteParams[i], ref _texturePath));
|
||||
}
|
||||
}
|
||||
@@ -478,9 +478,18 @@ namespace Barotrauma
|
||||
{
|
||||
if (spriteParams != null)
|
||||
{
|
||||
ContentPath texturePath =
|
||||
character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage)
|
||||
?? ContentPath.FromRaw(spriteParams.Element.ContentPackage ?? character.Prefab.ContentPackage, spriteParams.GetTexturePath());
|
||||
//1. check if the variant file redefines the texture
|
||||
ContentPath texturePath = character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage);
|
||||
//2. check if the base prefab defines the texture
|
||||
if (texturePath.IsNullOrEmpty() && !character.Prefab.VariantOf.IsEmpty)
|
||||
{
|
||||
RagdollParams parentRagdollParams = character.IsHumanoid ?
|
||||
RagdollParams.GetRagdollParams<HumanRagdollParams>(character.Prefab.VariantOf) :
|
||||
RagdollParams.GetRagdollParams<FishRagdollParams>(character.Prefab.VariantOf);
|
||||
texturePath = parentRagdollParams.OriginalElement?.GetAttributeContentPath("texture");
|
||||
}
|
||||
//3. "default case", get the texture from this character's XML
|
||||
texturePath ??= ContentPath.FromRaw(spriteParams.Element.ContentPackage ?? character.Prefab.ContentPackage, spriteParams.GetTexturePath());
|
||||
path = GetSpritePath(texturePath);
|
||||
}
|
||||
else
|
||||
@@ -592,6 +601,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (ParticleEmitter emitter in character.DamageEmitters)
|
||||
{
|
||||
if (emitter?.Prefab == null) { continue; }
|
||||
if (InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
|
||||
if (!InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
|
||||
ParticlePrefab overrideParticle = null;
|
||||
@@ -614,6 +624,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (ParticleEmitter emitter in character.BloodEmitters)
|
||||
{
|
||||
if (emitter?.Prefab == null) { continue; }
|
||||
if (InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
|
||||
if (!InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
|
||||
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, sizeMultiplier: bloodParticleSize, amountMultiplier: bloodParticleAmount);
|
||||
@@ -727,7 +738,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float herpesStrength = character.CharacterHealth.GetAfflictionStrength(AfflictionPrefab.SpaceHerpesType);
|
||||
float herpesStrength = character.CharacterHealth.GetAfflictionStrengthByType(AfflictionPrefab.SpaceHerpesType);
|
||||
|
||||
bool hideLimb = Hide ||
|
||||
OtherWearables.Any(w => w.HideLimb) ||
|
||||
@@ -890,6 +901,28 @@ namespace Barotrauma
|
||||
foreach (WearableSprite wearable in WearingItems)
|
||||
{
|
||||
if (onlyDrawable != null && onlyDrawable != wearable && wearable.CanBeHiddenByOtherWearables) { continue; }
|
||||
if (wearable.CanBeHiddenByItem.Any())
|
||||
{
|
||||
bool hiddenByOtherItem = false;
|
||||
foreach (var otherWearable in WearingItems)
|
||||
{
|
||||
if (otherWearable == wearable) { continue; }
|
||||
if (wearable.CanBeHiddenByItem.Contains(otherWearable.WearableComponent.Item.Prefab.Identifier))
|
||||
{
|
||||
hiddenByOtherItem = true;
|
||||
break;
|
||||
}
|
||||
foreach (Identifier tag in wearable.CanBeHiddenByItem)
|
||||
{
|
||||
if (otherWearable.WearableComponent.Item.HasTag(tag))
|
||||
{
|
||||
hiddenByOtherItem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hiddenByOtherItem) { continue; }
|
||||
}
|
||||
DrawWearable(wearable, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
|
||||
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
||||
depthStep += step;
|
||||
@@ -1245,6 +1278,12 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
paramsToPass.Params["wearableUvToClipperUv"] = wearableUvToClipperUv;
|
||||
paramsToPass.Params["stencilUVmin"] = new Vector2(
|
||||
(float)alphaClipper.Sprite.SourceRect.X / alphaClipper.Sprite.Texture.Width,
|
||||
(float)alphaClipper.Sprite.SourceRect.Y / alphaClipper.Sprite.Texture.Height);
|
||||
paramsToPass.Params["stencilUVmax"] = new Vector2(
|
||||
(float)alphaClipper.Sprite.SourceRect.Right / alphaClipper.Sprite.Texture.Width,
|
||||
(float)alphaClipper.Sprite.SourceRect.Bottom / alphaClipper.Sprite.Texture.Height);
|
||||
paramsToPass.Params["clipperTexelSize"] = 2f / alphaClipper.Sprite.Texture.Width;
|
||||
paramsToPass.Params["aCutoff"] = 2f / 255f;
|
||||
paramsToPass.Params["xTexture"] = wearable.Sprite.Texture;
|
||||
@@ -1254,12 +1293,11 @@ namespace Barotrauma
|
||||
|
||||
private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, float alpha, SpriteEffects spriteEffect)
|
||||
{
|
||||
var (finalColor, origin, rotation, scale, depth)
|
||||
= CalculateDrawParameters(wearable, depthStep, color, alpha);
|
||||
|
||||
if (wearable.Sprite?.Texture == null) { return; }
|
||||
var (finalColor, origin, rotation, scale, depth) = CalculateDrawParameters(wearable, depthStep, color, alpha);
|
||||
var prevEffect = spriteBatch.GetCurrentEffect();
|
||||
var alphaClipper = WearingItems.Find(w => w.AlphaClipOtherWearables);
|
||||
bool shouldApplyAlphaClip = alphaClipper != null && wearable != alphaClipper;
|
||||
bool shouldApplyAlphaClip = alphaClipper?.Sprite?.Texture != null && wearable != alphaClipper;
|
||||
if (shouldApplyAlphaClip)
|
||||
{
|
||||
ApplyAlphaClip(spriteBatch, wearable, alphaClipper, spriteEffect);
|
||||
@@ -1302,13 +1340,13 @@ namespace Barotrauma
|
||||
OtherWearables.ForEach(w => w.Sprite.Remove());
|
||||
OtherWearables.Clear();
|
||||
|
||||
HuskSprite?.Sprite.Remove();
|
||||
HuskSprite?.Sprite?.Remove();
|
||||
HuskSprite = null;
|
||||
|
||||
HairWithHatSprite?.Sprite.Remove();
|
||||
HairWithHatSprite?.Sprite?.Remove();
|
||||
HairWithHatSprite = null;
|
||||
|
||||
HerpesSprite?.Sprite.Remove();
|
||||
HerpesSprite?.Sprite?.Remove();
|
||||
HerpesSprite = null;
|
||||
|
||||
TintMask?.Remove();
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxComponent
|
||||
{
|
||||
public static Option<GUIComponent> EditingHUD = Option.None;
|
||||
|
||||
private Sprite Sprite => Item.Prefab.InventoryIcon ?? Item.Prefab.Sprite;
|
||||
|
||||
private CircuitBoxLabel? label;
|
||||
private CircuitBoxLabel Label
|
||||
{
|
||||
get
|
||||
{
|
||||
if (label is { } l)
|
||||
{
|
||||
return l;
|
||||
}
|
||||
|
||||
var name = TextManager.Get($"circuitboxnode.{Item.Prefab.Identifier}").Fallback($"[FALLBACK] {Item.Name}");
|
||||
label = new CircuitBoxLabel(name, GUIStyle.LargeFont);
|
||||
return label.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateEditing(RectTransform parent)
|
||||
{
|
||||
if (EditingHUD.TryUnwrap(out var editor))
|
||||
{
|
||||
if (editor.UserData == this) { return; }
|
||||
RemoveEditingHUD();
|
||||
}
|
||||
EditingHUD = Option.Some(CreateEditingHUD(parent));
|
||||
}
|
||||
|
||||
public static void RemoveEditingHUD()
|
||||
{
|
||||
if (!EditingHUD.TryUnwrap(out var editor)) { return; }
|
||||
|
||||
editor.RectTransform.Parent = null;
|
||||
EditingHUD = Option.None;
|
||||
}
|
||||
|
||||
public GUIComponent CreateEditingHUD(RectTransform parent)
|
||||
{
|
||||
GUIFrame frame = new(new RectTransform(new Vector2(0.4f, 0.3f), parent, Anchor.TopRight))
|
||||
{
|
||||
UserData = this
|
||||
};
|
||||
|
||||
GUIListBox listBox = new(new RectTransform(ToolBox.PaddingSizeParentRelative(frame.RectTransform, 0.8f), frame.RectTransform, Anchor.Center))
|
||||
{
|
||||
KeepSpaceForScrollBar = true,
|
||||
AutoHideScrollBar = false,
|
||||
CanTakeKeyBoardFocus = false
|
||||
};
|
||||
|
||||
bool isEditor = Screen.Selected is { IsEditor: true };
|
||||
|
||||
GUILayoutGroup titleHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), listBox.Content.RectTransform));
|
||||
new GUITextBlock(new RectTransform(Vector2.One, titleHolder.RectTransform), Item.Name, font: GUIStyle.LargeFont)
|
||||
{
|
||||
TextColor = Color.White,
|
||||
Color = Color.Black
|
||||
};
|
||||
int fieldCount = 0;
|
||||
|
||||
foreach (ItemComponent ic in Item.Components)
|
||||
{
|
||||
if (ic is Holdable) { continue; }
|
||||
if (!ic.AllowInGameEditing) { continue; }
|
||||
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 &&
|
||||
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), listBox.Content.RectTransform), style: "HorizontalLine");
|
||||
|
||||
var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame: !isEditor, showName: false, titleFont: GUIStyle.SubHeadingFont);
|
||||
fieldCount += componentEditor.Fields.Count;
|
||||
|
||||
ic.CreateEditingHUD(componentEditor);
|
||||
componentEditor.Recalculate();
|
||||
}
|
||||
|
||||
if (fieldCount == 0)
|
||||
{
|
||||
frame.Visible = false;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public override void DrawHeader(SpriteBatch spriteBatch, RectangleF drawRect, Color color)
|
||||
{
|
||||
// scale to topRect height
|
||||
Vector2 scale = new(drawRect.Height / MathF.Min(Sprite.size.X, Sprite.size.Y)),
|
||||
spritePosition = new(drawRect.Left, drawRect.Top);
|
||||
|
||||
float spriteWidth = Sprite.size.X * scale.X;
|
||||
|
||||
Sprite.Draw(spriteBatch, spritePosition, Color.White, Vector2.Zero, 0f, scale);
|
||||
GUI.DrawString(spriteBatch, new Vector2(spritePosition.X + spriteWidth + CircuitBoxSizes.NodeHeaderTextPadding, drawRect.Center.Y - Label.Size.Y / 2f), Label.Value, GUIStyle.TextColorNormal, font: GUIStyle.LargeFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#nullable enable
|
||||
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal abstract partial class CircuitBoxConnection
|
||||
{
|
||||
public string Name => Label.Value.Value;
|
||||
public CircuitBoxLabel Label { get; private set; }
|
||||
|
||||
private Sprite? knobSprite,
|
||||
screwSprite,
|
||||
connectorSprite;
|
||||
|
||||
private static int padding => GUI.IntScale(8);
|
||||
|
||||
private Option<LocalizedString> tooltip = Option.None;
|
||||
|
||||
private partial void InitProjSpecific(CircuitBox circuitBox)
|
||||
{
|
||||
Label = new CircuitBoxLabel(Connection.Name, GUIStyle.SubHeadingFont);
|
||||
knobSprite = circuitBox.ConnectionSprite;
|
||||
screwSprite = circuitBox.ConnectionScrewSprite;
|
||||
connectorSprite = circuitBox.WireConnectorSprite;
|
||||
Length = Rect.Width + padding + Label.Size.X;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos, Vector2 parentPos, Color color)
|
||||
{
|
||||
if (CircuitBox.UI is not { } circuitBoxUi) { return; }
|
||||
var drawRect = CircuitBoxNode.OverrideRectLocation(Rect, drawPos, parentPos);
|
||||
|
||||
Vector2 cursorPos = circuitBoxUi.GetCursorPosition();
|
||||
cursorPos.Y = -cursorPos.Y;
|
||||
|
||||
bool isMouseOver = drawRect.Contains(cursorPos);
|
||||
|
||||
float xPos;
|
||||
if (IsOutput)
|
||||
{
|
||||
xPos = drawRect.Left - padding - Label.Size.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
xPos = drawRect.Right + padding;
|
||||
}
|
||||
|
||||
Vector2 stringPos = new Vector2(xPos, drawRect.Center.Y - Label.Size.Y / 2f);
|
||||
GUI.DrawString(spriteBatch, stringPos, Label.Value, GUIStyle.TextColorNormal, font: Label.Font);
|
||||
|
||||
if (knobSprite is null)
|
||||
{
|
||||
CircuitBoxUI.DrawRectangleWithBorder(spriteBatch, drawRect, GUIStyle.Blue * 0.3f, GUIStyle.Blue);
|
||||
}
|
||||
else
|
||||
{
|
||||
float scale = drawRect.Height / knobSprite.size.Y;
|
||||
knobSprite?.Draw(spriteBatch, drawRect.Center, color, 0f, scale);
|
||||
}
|
||||
|
||||
bool isScrewed = this switch
|
||||
{
|
||||
CircuitBoxOutputConnection output => output.ExternallyConnectedFrom.Count > 0,
|
||||
CircuitBoxInputConnection input => input.ExternallyConnectedTo.Count > 0,
|
||||
_ => Connection.Wires.Count > 0 ||
|
||||
Connection.CircuitBoxConnections.Count > 0 ||
|
||||
ExternallyConnectedFrom.Count > 0
|
||||
};
|
||||
|
||||
if (isMouseOver)
|
||||
{
|
||||
var glowSprite = GUIStyle.UIGlowCircular.Value.Sprite;
|
||||
float glowScale = 40f / glowSprite.size.X;
|
||||
if (isScrewed)
|
||||
{
|
||||
glowScale *= 1.2f;
|
||||
}
|
||||
glowSprite.Draw(spriteBatch, position, GUIStyle.Yellow, glowSprite.size / 2, scale: glowScale);
|
||||
}
|
||||
|
||||
tooltip = Option.None;
|
||||
if (ConnectionPanel.ShouldDebugDrawWiring)
|
||||
{
|
||||
Connection.DrawConnectionDebugInfo(spriteBatch, Connection, drawRect.Center, isScrewed ? 1.1f : 0.9f, out var tooltipText);
|
||||
|
||||
if (isMouseOver && !tooltipText.IsNullOrEmpty())
|
||||
{
|
||||
tooltip = Option.Some(tooltipText);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isScrewed) { return; }
|
||||
|
||||
if (screwSprite is not null)
|
||||
{
|
||||
float screwScale = drawRect.Height / screwSprite.size.Y;
|
||||
screwSprite.Draw(spriteBatch, drawRect.Center, color, 0f, screwScale);
|
||||
}
|
||||
|
||||
if (connectorSprite is not null)
|
||||
{
|
||||
float screwScale = drawRect.Height / connectorSprite.size.Y * 2f;
|
||||
Vector2 pos = drawRect.Center;
|
||||
|
||||
connectorSprite.Draw(spriteBatch,
|
||||
pos: pos,
|
||||
color: Color.White,
|
||||
origin: connectorSprite.Origin,
|
||||
rotate: MathHelper.Pi / (IsOutput ? -2f : 2f),
|
||||
scale: screwScale,
|
||||
spriteEffect: SpriteEffects.None);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawHUD(SpriteBatch spriteBatch, Camera camera)
|
||||
{
|
||||
if (!tooltip.TryUnwrap(out var text)) { return; }
|
||||
|
||||
var drawPos = camera.WorldToScreen(new Vector2(Rect.Right, -Rect.Bottom));
|
||||
|
||||
GUIComponent.DrawToolTip(spriteBatch, text, drawPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal readonly struct CircuitBoxLabel
|
||||
{
|
||||
public LocalizedString Value { get; }
|
||||
|
||||
public Vector2 Size { get; }
|
||||
|
||||
public GUIFont Font { get; }
|
||||
|
||||
public CircuitBoxLabel(LocalizedString value, GUIFont font)
|
||||
{
|
||||
Value = value;
|
||||
Font = font;
|
||||
Size = font.MeasureString(font.ForceUpperCase ? value.Value.ToUpperInvariant() : value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// This class handles a couple things:
|
||||
/// - Figuring out which components should be moved when dragging a certain part of the UI.
|
||||
/// - Finding components, connectors and wires under cursor.
|
||||
/// - Determines whether the user is dragging something.
|
||||
/// </summary>
|
||||
internal sealed class CircuitBoxMouseDragSnapshotHandler
|
||||
{
|
||||
public IEnumerable<CircuitBoxNode> Nodes => circuitBoxUi.CircuitBox.Components.Union<CircuitBoxNode>(circuitBoxUi.CircuitBox.InputOutputNodes);
|
||||
|
||||
private IReadOnlyList<CircuitBoxWire> Wires => circuitBoxUi.CircuitBox.Wires;
|
||||
|
||||
// List of all connections in the circuit box
|
||||
private ImmutableArray<CircuitBoxConnection> connections = ImmutableArray<CircuitBoxConnection>.Empty;
|
||||
|
||||
// Nodes that were under cursor when dragging started
|
||||
private ImmutableHashSet<CircuitBoxNode> lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty,
|
||||
// Nodes that were selected when dragging started
|
||||
lastSelectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty,
|
||||
// Nodes that should be moved when dragging
|
||||
moveAffectedComponents = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
|
||||
public ImmutableHashSet<CircuitBoxNode> GetLastComponentsUnderCursor() => lastNodesUnderCursor;
|
||||
public ImmutableHashSet<CircuitBoxNode> GetMoveAffectedComponents() => moveAffectedComponents;
|
||||
|
||||
public Option<CircuitBoxConnection> LastConnectorUnderCursor = Option.None;
|
||||
public Option<CircuitBoxWire> LastWireUnderCursor = Option.None;
|
||||
|
||||
/// <summary>
|
||||
/// If the user is currently dragging a node
|
||||
/// </summary>
|
||||
public bool IsDragging { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the user is currently dragging a wire
|
||||
/// </summary>
|
||||
public bool IsWiring { get; private set; }
|
||||
|
||||
private Vector2 startClick = Vector2.Zero;
|
||||
private readonly CircuitBoxUI circuitBoxUi;
|
||||
|
||||
/// <summary>
|
||||
/// How far the user has to drag the mouse while holding down the button before dragging starts
|
||||
/// </summary>
|
||||
private const float dragTreshold = 16f;
|
||||
|
||||
public CircuitBoxMouseDragSnapshotHandler(CircuitBoxUI ui)
|
||||
{
|
||||
circuitBoxUi = ui;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the user holds down the mouse button
|
||||
/// </summary>
|
||||
public void StartDragging()
|
||||
{
|
||||
Vector2 cursorPos = circuitBoxUi.GetCursorPosition();
|
||||
SnapshotNodesUnderCursor(cursorPos);
|
||||
SnapshotSelectedNodes();
|
||||
SnapshotMoveAffectedNodes();
|
||||
startClick = cursorPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all connections and gathers them into a single list for easier iteration.
|
||||
/// </summary>
|
||||
public void UpdateConnections()
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<CircuitBoxConnection>();
|
||||
|
||||
builder.AddRange(circuitBoxUi.CircuitBox.Inputs);
|
||||
builder.AddRange(circuitBoxUi.CircuitBox.Outputs);
|
||||
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
builder.AddRange(node.Connectors);
|
||||
}
|
||||
|
||||
connections = builder.ToImmutable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a possible connector under the cursor.
|
||||
/// </summary>
|
||||
public Option<CircuitBoxConnection> FindConnectorUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
foreach (var connection in connections)
|
||||
{
|
||||
if (connection.Contains(cursorPos))
|
||||
{
|
||||
return Option.Some(connection);
|
||||
}
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a possible wire under the cursor.
|
||||
/// </summary>
|
||||
public Option<CircuitBoxWire> FindWireUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
foreach (CircuitBoxWire wire in Wires)
|
||||
{
|
||||
if (wire is { IsSelected: true, IsSelectedByMe: false }) { continue; }
|
||||
if (wire.Renderer.Contains(cursorPos))
|
||||
{
|
||||
return Option.Some(wire);
|
||||
}
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all nodes that are currently under the cursor that are not selected by someone else.
|
||||
/// </summary>
|
||||
public ImmutableHashSet<CircuitBoxNode> FindNodesUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
var builder = ImmutableHashSet.CreateBuilder<CircuitBoxNode>();
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (node is { IsSelected: true, IsSelectedByMe: false }) { continue; }
|
||||
if (node.Rect.Contains(cursorPos))
|
||||
{
|
||||
builder.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and stores all nodes, connectors and wires that are under the cursor when dragging starts.
|
||||
/// </summary>
|
||||
private void SnapshotNodesUnderCursor(Vector2 cursorPos)
|
||||
{
|
||||
lastNodesUnderCursor = FindNodesUnderCursor(cursorPos);
|
||||
LastConnectorUnderCursor = FindConnectorUnderCursor(cursorPos);
|
||||
LastWireUnderCursor = FindWireUnderCursor(cursorPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores all nodes that are currently selected when dragging starts.
|
||||
/// There's no real way to change your selection while dragging so this is kinda pointless
|
||||
/// but we snapshot it anyway just in case.
|
||||
/// </summary>
|
||||
private void SnapshotSelectedNodes()
|
||||
{
|
||||
lastSelectedComponents = Nodes.Where(static n => n is { IsSelected: true, IsSelectedByMe: true }).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores all nodes that should be moved when dragging starts.
|
||||
/// </summary>
|
||||
private void SnapshotMoveAffectedNodes()
|
||||
{
|
||||
bool moveSelection = lastNodesUnderCursor.Any(node => lastSelectedComponents.Contains(node));
|
||||
|
||||
/*
|
||||
* If the user is dragging a selection, we should move all selected nodes (true).
|
||||
*
|
||||
* But for convenience, if the user is dragging a single node that is not part of the selection,
|
||||
* we should move that node only instead and leave the selection alone. (false)
|
||||
*/
|
||||
moveAffectedComponents = moveSelection switch
|
||||
{
|
||||
true => lastSelectedComponents,
|
||||
false => circuitBoxUi.GetTopmostNode(lastNodesUnderCursor) switch
|
||||
{
|
||||
null => ImmutableHashSet<CircuitBoxNode>.Empty,
|
||||
var node => ImmutableHashSet.Create(node)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Vector2 GetDragAmount(Vector2 mousePos) => mousePos - startClick;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the user releases the mouse button
|
||||
/// </summary>
|
||||
public void EndDragging()
|
||||
{
|
||||
startClick = Vector2.Zero;
|
||||
IsDragging = false;
|
||||
IsWiring = false;
|
||||
lastNodesUnderCursor = ImmutableHashSet<CircuitBoxNode>.Empty;
|
||||
}
|
||||
|
||||
public void UpdateDrag(Vector2 cursorPos)
|
||||
{
|
||||
// if there are no connectors under cursor, we can't be wiring anything
|
||||
if (LastConnectorUnderCursor.IsNone())
|
||||
{
|
||||
IsWiring = false;
|
||||
}
|
||||
|
||||
// if there are no nodes under cursor, we can't be dragging anything
|
||||
if (lastNodesUnderCursor.IsEmpty)
|
||||
{
|
||||
IsDragging = false;
|
||||
}
|
||||
|
||||
// startClick is set to zero when the user releases the mouse button, so we should be neither dragging nor wiring in this state
|
||||
if (startClick == Vector2.Zero)
|
||||
{
|
||||
IsDragging = false;
|
||||
IsWiring = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool isDragTresholdExceeded = Vector2.DistanceSquared(startClick, cursorPos) > dragTreshold * dragTreshold;
|
||||
|
||||
if (LastConnectorUnderCursor.IsNone())
|
||||
{
|
||||
IsDragging |= isDragTresholdExceeded;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsWiring |= isDragTresholdExceeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxNode
|
||||
{
|
||||
private RectangleF DrawRect,
|
||||
TopDrawRect;
|
||||
|
||||
private void UpdateDrawRects()
|
||||
{
|
||||
var drawRect = new RectangleF(Position - Size / 2f, Size);
|
||||
drawRect.Y = -drawRect.Y;
|
||||
drawRect.Y -= drawRect.Height;
|
||||
DrawRect = drawRect;
|
||||
|
||||
TopDrawRect = new RectangleF(drawRect.X, drawRect.Y - (CircuitBoxSizes.NodeHeaderHeight - 1), drawRect.Width, CircuitBoxSizes.NodeHeaderHeight);
|
||||
}
|
||||
|
||||
public void OnUICreated()
|
||||
{
|
||||
Size = CalculateSize(Connectors);
|
||||
UpdatePositions();
|
||||
}
|
||||
|
||||
public void DrawBackground(SpriteBatch spriteBatch, RectangleF drawRect, RectangleF topDrawRect, Color color)
|
||||
{
|
||||
CircuitBox.NodeFrameSprite?.Draw(spriteBatch, drawRect, color);
|
||||
CircuitBox.NodeTopSprite?.Draw(spriteBatch, topDrawRect, color);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos, Color color)
|
||||
{
|
||||
RectangleF drawRect = OverrideRectLocation(DrawRect, drawPos, Position),
|
||||
topDrawRect = OverrideRectLocation(TopDrawRect, drawPos, Position);
|
||||
|
||||
DrawBackground(spriteBatch, drawRect, topDrawRect, color);
|
||||
DrawHeader(spriteBatch, topDrawRect, color);
|
||||
|
||||
DrawConnectors(spriteBatch, drawPos);
|
||||
}
|
||||
|
||||
public void DrawHUD(SpriteBatch spriteBatch, Camera camera)
|
||||
{
|
||||
foreach (var c in Connectors)
|
||||
{
|
||||
c.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void DrawHeader(SpriteBatch spriteBatch, RectangleF rect, Color color) { }
|
||||
|
||||
public void DrawConnectors(SpriteBatch spriteBatch, Vector2 drawPos)
|
||||
{
|
||||
var color = Color.White * Opacity;
|
||||
foreach (var c in Connectors)
|
||||
{
|
||||
c.Draw(spriteBatch, drawPos, Position, color);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSelection(SpriteBatch spriteBatch, Color color)
|
||||
{
|
||||
int pad = GUI.IntScale(8);
|
||||
|
||||
var rect = Rect;
|
||||
rect.Y = -rect.Y;
|
||||
rect.Y -= rect.Height;
|
||||
|
||||
rect.Inflate(pad, pad);
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, rect, color * Opacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the location of the rectangle to a specific position, keeping origin intact.
|
||||
/// </summary>
|
||||
public static RectangleF OverrideRectLocation(RectangleF rect, Vector2 overridePos, Vector2 originalPos)
|
||||
{
|
||||
rect.Location -= new Vector2(originalPos.X, -originalPos.Y);
|
||||
rect.Location += new Vector2(overridePos.X, -overridePos.Y);
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal sealed class CircuitBoxUI
|
||||
{
|
||||
private readonly Camera camera;
|
||||
private static readonly Vector2 gridSize = new Vector2(128f);
|
||||
public readonly CircuitBox CircuitBox;
|
||||
private bool componentMenuOpen;
|
||||
private float componentMenuOpenState;
|
||||
|
||||
private GUICustomComponent? circuitComponent;
|
||||
private GUIFrame? componentMenu;
|
||||
private GUIButton? toggleMenuButton;
|
||||
private GUIFrame? selectedWireFrame;
|
||||
private GUIListBox? componentList;
|
||||
private GUITextBlock? inventoryIndicatorText;
|
||||
private readonly Sprite cursorSprite = GUIStyle.CursorSprite[CursorState.Default];
|
||||
|
||||
private Option<RectangleF> selection = Option.None;
|
||||
private string searchTerm = string.Empty;
|
||||
|
||||
public static Option<CircuitBoxWireRenderer> DraggedWire = Option.None;
|
||||
|
||||
public readonly CircuitBoxMouseDragSnapshotHandler MouseSnapshotHandler;
|
||||
|
||||
public List<CircuitBoxWireRenderer> VirtualWires = new();
|
||||
|
||||
public CircuitBoxUI(CircuitBox box)
|
||||
{
|
||||
camera = new Camera
|
||||
{
|
||||
MinZoom = 0.25f,
|
||||
MaxZoom = 2f
|
||||
};
|
||||
|
||||
CircuitBox = box;
|
||||
MouseSnapshotHandler = new CircuitBoxMouseDragSnapshotHandler(this);
|
||||
}
|
||||
|
||||
#region UI
|
||||
|
||||
public void CreateGUI(GUIFrame parent)
|
||||
{
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.97f, 0.95f), parent.RectTransform, Anchor.Center), style: null);
|
||||
circuitComponent = new GUICustomComponent(new RectTransform(Vector2.One, paddedFrame.RectTransform), onDraw: (spriteBatch, component) =>
|
||||
{
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = component.Rect;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable, transformMatrix: camera.Transform);
|
||||
DrawCircuits(spriteBatch);
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
DrawHUD(spriteBatch);
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
});
|
||||
|
||||
GUIScissorComponent menuContainer = new GUIScissorComponent(new RectTransform(Vector2.One, paddedFrame.RectTransform, anchor: Anchor.Center))
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
componentMenuOpen = true;
|
||||
componentMenu = new GUIFrame(new RectTransform(new Vector2(1f, 0.4f), menuContainer.Content.RectTransform, Anchor.BottomRight));
|
||||
toggleMenuButton = new GUIButton(new RectTransform(new Point(300, 30), GUI.Canvas) { MinSize = new Point(0, 15) }, style: "UIToggleButtonVertical")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
componentMenuOpen = !componentMenuOpen;
|
||||
foreach (GUIComponent child in btn.Children)
|
||||
{
|
||||
child.SpriteEffects = componentMenuOpen ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUILayoutGroup menuLayout = new GUILayoutGroup(new RectTransform(Vector2.One, componentMenu.RectTransform), childAnchor: Anchor.TopCenter) { RelativeSpacing = 0.02f };
|
||||
GUILayoutGroup headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), menuLayout.RectTransform), isHorizontal: true);
|
||||
|
||||
GUILayoutGroup labelLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1f), headerLayout.RectTransform), isHorizontal: true);
|
||||
|
||||
GUILayoutGroup searchBarLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1f), headerLayout.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true);
|
||||
GUITextBlock searchBarLabel = new GUITextBlock(new RectTransform(new Vector2(0.15f, 1f), searchBarLayout.RectTransform), "Filter");
|
||||
GUITextBox searchbar = new GUITextBox(new RectTransform(new Vector2(0.85f, 1f), searchBarLayout.RectTransform), string.Empty, createClearButton: true);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(0.5f, 0.01f), menuLayout.RectTransform), style: "HorizontalLine");
|
||||
|
||||
componentList = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.65f), menuLayout.RectTransform))
|
||||
{
|
||||
PlaySoundOnSelect = true,
|
||||
UseGridLayout = true,
|
||||
OnSelected = (_, o) =>
|
||||
{
|
||||
if (o is not ItemPrefab prefab) { return false; }
|
||||
|
||||
CircuitBox.HeldComponent = Option.Some(prefab);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GUILayoutGroup inventoryLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1f), headerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
|
||||
GUILayoutGroup indicatorLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1f), inventoryLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
GUIImage indicatorIcon = new GUIImage(new RectTransform(new Vector2(0.5f, 0.8f), indicatorLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "CircuitIndicatorIcon");
|
||||
inventoryIndicatorText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), indicatorLayout.RectTransform), GetInventoryText(), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
int gapSize = GUI.IntScale(8);
|
||||
selectedWireFrame = SubEditorScreen.CreateWiringPanel(Point.Zero, SelectWire);
|
||||
selectedWireFrame.RectTransform.AbsoluteOffset = new Point(parent.Rect.X - (selectedWireFrame.Rect.Width + gapSize), parent.Rect.Y);
|
||||
|
||||
foreach (ItemPrefab prefab in ItemPrefab.Prefabs.OrderBy(static p => p.Name))
|
||||
{
|
||||
if (!prefab.Tags.Contains("circuitboxcomponent")) { continue; }
|
||||
|
||||
CreateComponentElement(prefab, componentList.Content.RectTransform);
|
||||
}
|
||||
|
||||
searchbar.OnTextChanged += (tb, s) =>
|
||||
{
|
||||
searchTerm = s;
|
||||
UpdateComponentList();
|
||||
return true;
|
||||
};
|
||||
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
|
||||
var settingsIcon = new GUIButton(new RectTransform(new Point(buttonHeight), parent.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
|
||||
style: "GUIButtonSettings")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(
|
||||
new ContextMenuOption("circuitboxsetting.resetview", isEnabled: true, onSelected: ResetCamera)
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.resetview")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.find", isEnabled: true,
|
||||
new ContextMenuOption("circuitboxsetting.focusinput", isEnabled: true, onSelected: () => FindInputOuput(CircuitBoxInputOutputNode.Type.Input))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusinput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.focusoutput", isEnabled: true, onSelected: () => FindInputOuput(CircuitBoxInputOutputNode.Type.Output))
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focusoutput")
|
||||
},
|
||||
new ContextMenuOption("circuitboxsetting.focuscircuits", isEnabled: CircuitBox.Components.Any(), onSelected: FindCircuit)
|
||||
{
|
||||
Tooltip = TextManager.Get("circuitboxsettingdescription.focuscircuits")
|
||||
}));
|
||||
|
||||
|
||||
void ResetCamera()
|
||||
{
|
||||
// Vector2.One because Vector2.Zero means no value
|
||||
camera.TargetPos = Vector2.One;
|
||||
}
|
||||
|
||||
void FindInputOuput(CircuitBoxInputOutputNode.Type type)
|
||||
{
|
||||
var input = CircuitBox.InputOutputNodes.FirstOrDefault(n => n.NodeType == type);
|
||||
if (input is null) { return; }
|
||||
|
||||
camera.TargetPos = input.Position;
|
||||
}
|
||||
|
||||
void FindCircuit()
|
||||
{
|
||||
var closestComponent = CircuitBox.Components.MinBy(c => Vector2.DistanceSquared(c.Position, camera.Position));
|
||||
if (closestComponent is null) { return; }
|
||||
|
||||
camera.TargetPos = closestComponent.Position;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
MouseSnapshotHandler.UpdateConnections();
|
||||
|
||||
// update scales of everything
|
||||
foreach (var node in CircuitBox.Components) { node.OnUICreated(); }
|
||||
foreach (var node in CircuitBox.InputOutputNodes) { node.OnUICreated(); }
|
||||
foreach (var wire in CircuitBox.Wires) { wire.Update(); }
|
||||
}
|
||||
|
||||
private string GetInventoryText()
|
||||
=> CircuitBox.ComponentContainer is { } container
|
||||
? $"{container.Inventory.AllItems.Count()}/{container.Capacity}"
|
||||
: "0/0";
|
||||
|
||||
public void UpdateComponentList()
|
||||
{
|
||||
if (inventoryIndicatorText is { } text)
|
||||
{
|
||||
text.Text = GetInventoryText();
|
||||
}
|
||||
|
||||
if (componentList is null) { return; }
|
||||
var playerInventory = CircuitBox.GetSortedCircuitBoxSortedItemsFromPlayer(Character.Controlled);
|
||||
|
||||
foreach (GUIComponent child in componentList.Content.Children)
|
||||
{
|
||||
if (child.UserData is not ItemPrefab prefab) { continue; }
|
||||
|
||||
child.Enabled = !CircuitBox.IsFull && (!CircuitBox.IsInGame() || CircuitBox.GetApplicableResourcePlayerHas(prefab, playerInventory).IsSome());
|
||||
|
||||
if (child.GetChild<GUILayoutGroup>()?.GetChild<GUIImage>() is { } image)
|
||||
{
|
||||
image.Enabled = child.Enabled;
|
||||
}
|
||||
|
||||
child.ToolTip = child.Enabled
|
||||
? prefab.Description
|
||||
: RichString.Rich(TextManager.GetWithVariable(new Identifier("CircuitBoxUIComponentNotAvailable"), new Identifier("[item]"), prefab.Name));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
child.Visible = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
child.Visible = prefab.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SelectWire(GUIComponent component, object obj)
|
||||
{
|
||||
if (obj is not ItemPrefab prefab) { return false; }
|
||||
|
||||
CircuitBoxWire.SelectedWirePrefab = prefab;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void CreateComponentElement(ItemPrefab prefab, RectTransform parent)
|
||||
{
|
||||
GUIFrame itemFrame = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.9f), parent) { MinSize = new Point(0, 50) }, style: "GUITextBox")
|
||||
{
|
||||
UserData = prefab
|
||||
};
|
||||
|
||||
itemFrame.RectTransform.MinSize = new Point(0, itemFrame.Rect.Width);
|
||||
itemFrame.RectTransform.MaxSize = new Point(int.MaxValue, itemFrame.Rect.Width);
|
||||
itemFrame.ToolTip = prefab.Name;
|
||||
|
||||
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), itemFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.03f,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
Sprite icon;
|
||||
Color iconColor;
|
||||
|
||||
if (prefab.InventoryIcon != null)
|
||||
{
|
||||
icon = prefab.InventoryIcon;
|
||||
iconColor = prefab.InventoryIconColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = prefab.Sprite;
|
||||
iconColor = prefab.SpriteColor;
|
||||
}
|
||||
|
||||
GUIImage? img = null;
|
||||
if (icon != null)
|
||||
{
|
||||
img = new GUIImage(new RectTransform(new Vector2(1.0f, 0.8f), paddedFrame.RectTransform, Anchor.TopCenter), icon)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
LoadAsynchronously = true,
|
||||
DisabledColor = Color.DarkGray * 0.8f,
|
||||
Color = iconColor
|
||||
};
|
||||
}
|
||||
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform, Anchor.BottomCenter),
|
||||
text: prefab.Name, textAlignment: Alignment.Center, font: GUIStyle.SmallFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
textBlock.Text = ToolBox.LimitString(textBlock.Text, textBlock.Font, textBlock.Rect.Width);
|
||||
paddedFrame.Recalculate();
|
||||
|
||||
if (img != null)
|
||||
{
|
||||
img.Scale = Math.Min(Math.Min(img.Rect.Width / img.Sprite.size.X, img.Rect.Height / img.Sprite.size.Y), 1.5f);
|
||||
img.RectTransform.NonScaledSize = new Point((int)(img.Sprite.size.X * img.Scale), img.Rect.Height);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void DrawHUD(SpriteBatch spriteBatch)
|
||||
{
|
||||
float scale = GUI.Scale / 1.5f;
|
||||
Vector2 offset = new Vector2(20, 40) * scale;
|
||||
|
||||
foreach (var (character, cursor) in CircuitBox.ActiveCursors)
|
||||
{
|
||||
if (!cursor.IsActive) { continue; }
|
||||
|
||||
Vector2 cursorWorldPos = camera.WorldToScreen(cursor.DrawPosition);
|
||||
|
||||
if (cursor.Info.DragStart.TryUnwrap(out Vector2 dragStart))
|
||||
{
|
||||
DrawSelection(spriteBatch, dragStart, cursor.DrawPosition, cursor.Color);
|
||||
}
|
||||
|
||||
if (cursor.HeldPrefab.TryUnwrap(out ItemPrefab? otherHeldPrefab))
|
||||
{
|
||||
otherHeldPrefab.Sprite.Draw(spriteBatch, cursorWorldPos);
|
||||
}
|
||||
|
||||
cursorSprite?.Draw(spriteBatch, cursorWorldPos, cursor.Color, 0f, scale);
|
||||
GUI.DrawString(spriteBatch, cursorWorldPos + offset, character.Name, cursor.Color, Color.Black, GUI.IntScale(4), GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
if (selection.TryUnwrap(out RectangleF rect))
|
||||
{
|
||||
Vector2 pos1 = rect.Location;
|
||||
Vector2 pos2 = new Vector2(rect.Location.X + rect.Size.X, rect.Location.Y + rect.Size.Y);
|
||||
DrawSelection(spriteBatch, pos1, pos2, GUIStyle.Blue);
|
||||
}
|
||||
|
||||
if (CircuitBox.HeldComponent.TryUnwrap(out ItemPrefab? component))
|
||||
{
|
||||
component.Sprite.Draw(spriteBatch, PlayerInput.MousePosition);
|
||||
}
|
||||
|
||||
foreach (var c in CircuitBox.Components)
|
||||
{
|
||||
c.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
|
||||
foreach (var n in CircuitBox.InputOutputNodes)
|
||||
{
|
||||
n.DrawHUD(spriteBatch, camera);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSelection(SpriteBatch spriteBatch, Vector2 pos1, Vector2 pos2, Color color)
|
||||
{
|
||||
Vector2 location = camera.WorldToScreen(pos1);
|
||||
location.Y = -location.Y;
|
||||
Vector2 location2 = camera.WorldToScreen(pos2);
|
||||
location2.Y = -location2.Y;
|
||||
MapEntity.DrawSelectionRect(spriteBatch, location, new Vector2(-(location.X - location2.X), location.Y - location2.Y), color);
|
||||
}
|
||||
|
||||
private const float lineBaseWidth = 1f;
|
||||
private static float lineWidth;
|
||||
|
||||
public static void DrawRectangleWithBorder(SpriteBatch spriteBatch, RectangleF rect, Color fillColor, Color borderColor)
|
||||
{
|
||||
Vector2 topRight = new Vector2(rect.Right, rect.Top),
|
||||
topLeft = new Vector2(rect.Left, rect.Top),
|
||||
bottomRight = new Vector2(rect.Right, rect.Bottom),
|
||||
bottomLeft = new Vector2(rect.Left, rect.Bottom);
|
||||
|
||||
Vector2 offset = new Vector2(0f, lineWidth / 2f);
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, rect, fillColor);
|
||||
|
||||
spriteBatch.DrawLine(topRight, topLeft, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(topLeft - offset, bottomLeft + offset, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(bottomLeft, bottomRight, borderColor, thickness: lineWidth);
|
||||
spriteBatch.DrawLine(bottomRight + offset, topRight - offset, borderColor, thickness: lineWidth);
|
||||
}
|
||||
|
||||
private void DrawCircuits(SpriteBatch spriteBatch)
|
||||
{
|
||||
camera.UpdateTransform(interpolate: true, updateListener: false);
|
||||
SubEditorScreen.DrawOutOfBoundsArea(spriteBatch, camera, CircuitBoxSizes.PlayableAreaSize, GUIStyle.Red * 0.33f);
|
||||
SubEditorScreen.DrawGrid(spriteBatch, camera, gridSize.X, gridSize.Y, zoomTreshold: false);
|
||||
lineWidth = lineBaseWidth / camera.Zoom;
|
||||
|
||||
Vector2 mousePos = GetCursorPosition();
|
||||
mousePos.Y = -mousePos.Y;
|
||||
|
||||
foreach (CircuitBoxWire wire in CircuitBox.Wires)
|
||||
{
|
||||
wire.Renderer.Draw(spriteBatch, GetSelectionColor(wire));
|
||||
}
|
||||
|
||||
foreach (var node in CircuitBox.Components)
|
||||
{
|
||||
if (node.IsSelected)
|
||||
{
|
||||
node.DrawSelection(spriteBatch, GetSelectionColor(node));
|
||||
}
|
||||
|
||||
node.Draw(spriteBatch, node.Position, node.Item.Prefab.SignalComponentColor * CircuitBoxNode.Opacity);
|
||||
}
|
||||
|
||||
foreach (var ioNode in CircuitBox.InputOutputNodes)
|
||||
{
|
||||
if (ioNode.IsSelected)
|
||||
{
|
||||
ioNode.DrawSelection(spriteBatch, GetSelectionColor(ioNode));
|
||||
}
|
||||
|
||||
Color color = ioNode.NodeType is CircuitBoxInputOutputNode.Type.Input ? GUIStyle.Green : GUIStyle.Red;
|
||||
ioNode.Draw(spriteBatch, ioNode.Position, color * CircuitBoxNode.Opacity);
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.IsDragging)
|
||||
{
|
||||
var draggedNodes = MouseSnapshotHandler.GetMoveAffectedComponents();
|
||||
Vector2 dragOffset = MouseSnapshotHandler.GetDragAmount(GetCursorPosition());
|
||||
foreach (CircuitBoxNode moveable in draggedNodes)
|
||||
{
|
||||
Color color = moveable switch
|
||||
{
|
||||
CircuitBoxComponent node => node.Item.Prefab.SignalComponentColor,
|
||||
CircuitBoxInputOutputNode ioNode => ioNode.NodeType is CircuitBoxInputOutputNode.Type.Input ? GUIStyle.Green : GUIStyle.Red,
|
||||
_ => Color.White
|
||||
};
|
||||
moveable.Draw(spriteBatch, moveable.Position + dragOffset, color * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
if (DraggedWire.TryUnwrap(out CircuitBoxWireRenderer? draggedWire))
|
||||
{
|
||||
draggedWire.Draw(spriteBatch, GUIStyle.Yellow);
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetSelectionColor(CircuitBoxNode node)
|
||||
=> GetSelectionColor(node.SelectedBy, node.IsSelectedByMe);
|
||||
|
||||
private Color GetSelectionColor(CircuitBoxWire wire)
|
||||
=> GetSelectionColor(wire.SelectedBy, wire.IsSelectedByMe);
|
||||
|
||||
private Color GetSelectionColor(ushort selectedBy, bool isSelectedByMe)
|
||||
{
|
||||
#if !DEBUG
|
||||
if (isSelectedByMe)
|
||||
{
|
||||
return GUIStyle.Yellow;
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (var (_, cursor) in CircuitBox.ActiveCursors)
|
||||
{
|
||||
if (cursor.Info.CharacterID == selectedBy)
|
||||
{
|
||||
return cursor.Color;
|
||||
}
|
||||
}
|
||||
|
||||
return GUIStyle.Yellow;
|
||||
}
|
||||
|
||||
private Vector2 cursorPos;
|
||||
public Vector2 GetCursorPosition() => cursorPos;
|
||||
public Option<Vector2> GetDragStart() => selection.Select(static f => f.Location);
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
cursorPos = camera.ScreenToWorld(PlayerInput.MousePosition);
|
||||
foreach (CircuitBoxWire wire in CircuitBox.Wires)
|
||||
{
|
||||
wire.Update();
|
||||
}
|
||||
|
||||
bool foundSelected = false;
|
||||
foreach (var node in CircuitBox.Components)
|
||||
{
|
||||
if (!node.IsSelectedByMe) { continue; }
|
||||
|
||||
foundSelected = true;
|
||||
if (circuitComponent is not null)
|
||||
{
|
||||
node.UpdateEditing(circuitComponent.RectTransform);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundSelected)
|
||||
{
|
||||
CircuitBoxComponent.RemoveEditingHUD();
|
||||
}
|
||||
|
||||
bool isMouseOn = GUI.MouseOn == circuitComponent;
|
||||
|
||||
if (isMouseOn)
|
||||
{
|
||||
Character.DisableControls = true;
|
||||
}
|
||||
camera.MoveCamera(deltaTime, allowMove: true, allowZoom: isMouseOn, allowInput: isMouseOn, followSub: false);
|
||||
|
||||
if (camera.TargetPos != Vector2.Zero && MathUtils.NearlyEqual(camera.Position, camera.TargetPos, 0.01f))
|
||||
{
|
||||
camera.TargetPos = Vector2.Zero;
|
||||
}
|
||||
|
||||
if (isMouseOn)
|
||||
{
|
||||
if (CircuitBox.HeldComponent.IsNone() && PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
MouseSnapshotHandler.StartDragging();
|
||||
}
|
||||
|
||||
if (PlayerInput.MidButtonHeld() || (PlayerInput.IsAltDown() && PlayerInput.PrimaryMouseButtonHeld()))
|
||||
{
|
||||
Vector2 moveSpeed = PlayerInput.MouseSpeed / camera.Zoom;
|
||||
moveSpeed.X = -moveSpeed.X;
|
||||
camera.Position += moveSpeed;
|
||||
}
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
MouseSnapshotHandler.UpdateDrag(GetCursorPosition());
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.IsWiring && MouseSnapshotHandler.LastConnectorUnderCursor.TryUnwrap(out var c))
|
||||
{
|
||||
Vector2 start = c.Rect.Center,
|
||||
end = GetCursorPosition();
|
||||
|
||||
end.Y = -end.Y;
|
||||
|
||||
if (!c.IsOutput)
|
||||
{
|
||||
(start, end) = (end, start);
|
||||
}
|
||||
|
||||
if (DraggedWire.TryUnwrap(out var wire))
|
||||
{
|
||||
wire.Recompute(start, end, CircuitBoxWire.SelectedWirePrefab.SpriteColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggedWire = Option.Some(new CircuitBoxWireRenderer(Option.None,start, end, GUIStyle.Red, CircuitBox.WireSprite));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DraggedWire = Option.None;
|
||||
}
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked())
|
||||
{
|
||||
OpenContextMenu();
|
||||
}
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
if (CircuitBox.HeldComponent.TryUnwrap(out ItemPrefab? prefab))
|
||||
{
|
||||
CircuitBox.AddComponent(prefab, cursorPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MouseSnapshotHandler.IsDragging && PlayerInput.PrimaryMouseButtonReleased())
|
||||
{
|
||||
CircuitBox.MoveComponent(MouseSnapshotHandler.GetDragAmount(cursorPos), MouseSnapshotHandler.GetMoveAffectedComponents());
|
||||
}
|
||||
else if (!MouseSnapshotHandler.IsWiring)
|
||||
{
|
||||
TrySelectComponentsUnderCursor();
|
||||
}
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.IsWiring && MouseSnapshotHandler.LastConnectorUnderCursor.TryUnwrap(out var one))
|
||||
{
|
||||
if (MouseSnapshotHandler.FindConnectorUnderCursor(cursorPos).TryUnwrap(out var two))
|
||||
{
|
||||
CircuitBox.AddWire(one, two);
|
||||
}
|
||||
}
|
||||
|
||||
CircuitBox.SelectWires(MouseSnapshotHandler.LastWireUnderCursor.TryUnwrap(out var wire) ? ImmutableArray.Create(wire) : ImmutableArray<CircuitBoxWire>.Empty, !PlayerInput.IsShiftDown());
|
||||
|
||||
CircuitBox.HeldComponent = Option.None;
|
||||
MouseSnapshotHandler.EndDragging();
|
||||
}
|
||||
|
||||
if (MouseSnapshotHandler.GetLastComponentsUnderCursor().IsEmpty && MouseSnapshotHandler.LastConnectorUnderCursor.IsNone())
|
||||
{
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
// Allow using both Delete key and Ctrl+D for those who don't have a Delete key
|
||||
bool hitDeleteCombo = PlayerInput.KeyHit(Keys.Delete) || (PlayerInput.IsCtrlDown() && PlayerInput.KeyHit(Keys.D));
|
||||
|
||||
if (GUI.KeyboardDispatcher.Subscriber is null && hitDeleteCombo)
|
||||
{
|
||||
CircuitBox.RemoveComponents(CircuitBox.Components.Where(static node => node.IsSelectedByMe).ToArray());
|
||||
CircuitBox.RemoveWires(CircuitBox.Wires.Where(static wire => wire.IsSelectedByMe).ToImmutableArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (componentMenu is { } menu && toggleMenuButton is { } button)
|
||||
{
|
||||
componentMenuOpenState = componentMenuOpen ? Math.Min(componentMenuOpenState + deltaTime * 5.0f, 1.0f) : Math.Max(componentMenuOpenState - deltaTime * 5.0f, 0.0f);
|
||||
|
||||
menu.RectTransform.ScreenSpaceOffset = Vector2.Lerp(new Vector2(0.0f, menu.Rect.Height - 10), Vector2.Zero, componentMenuOpenState).ToPoint();
|
||||
button.RectTransform.AbsoluteOffset = new Point(menu.Rect.X + ((menu.Rect.Width / 2) - (button.Rect.Width / 2)), menu.Rect.Y - button.Rect.Height);
|
||||
}
|
||||
|
||||
camera.Position = Vector2.Clamp(camera.Position,
|
||||
new Vector2(-CircuitBoxSizes.PlayableAreaSize / 2f),
|
||||
new Vector2(CircuitBoxSizes.PlayableAreaSize / 2f));
|
||||
}
|
||||
|
||||
private void UpdateSelection()
|
||||
{
|
||||
if (!PlayerInput.IsAltDown() && PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
selection = Option.Some(new RectangleF(GetCursorPosition(), Vector2.Zero));
|
||||
}
|
||||
|
||||
if (!selection.TryUnwrap(out RectangleF rect)) { return; }
|
||||
|
||||
if (!PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
selection = Option.None;
|
||||
RectangleF selectionRect = Submarine.AbsRectF(rect.Location, rect.Size);
|
||||
|
||||
float treshold = 12f / camera.Zoom;
|
||||
if (selectionRect.Size.X < treshold || selectionRect.Size.Y < treshold) { return; }
|
||||
|
||||
CircuitBox.SelectComponents(MouseSnapshotHandler.Nodes.Where(n => selectionRect.Intersects(n.Rect)).ToImmutableHashSet(), !PlayerInput.IsShiftDown());
|
||||
}
|
||||
else
|
||||
{
|
||||
RectangleF oldRect = rect;
|
||||
rect.Size = camera.ScreenToWorld(PlayerInput.MousePosition) - rect.Location;
|
||||
if (rect.Equals(oldRect)) { return; }
|
||||
|
||||
selection = Option.Some(rect);
|
||||
}
|
||||
}
|
||||
|
||||
private void TrySelectComponentsUnderCursor()
|
||||
{
|
||||
CircuitBoxNode? foundNode = GetTopmostNode(MouseSnapshotHandler.GetLastComponentsUnderCursor());
|
||||
|
||||
CircuitBox.SelectComponents(foundNode is null ? ImmutableArray<CircuitBoxNode>.Empty : ImmutableArray.Create(foundNode), !PlayerInput.IsShiftDown());
|
||||
}
|
||||
|
||||
private void OpenContextMenu()
|
||||
{
|
||||
var wireOption = MouseSnapshotHandler.FindWireUnderCursor(cursorPos);
|
||||
var wireSelection = CircuitBox.Wires.Where(static w => w.IsSelectedByMe).ToImmutableArray();
|
||||
var nodeOption = GetTopmostNode(MouseSnapshotHandler.FindNodesUnderCursor(cursorPos));
|
||||
var nodeSelection = CircuitBox.Components.Where(static n => n.IsSelectedByMe).ToImmutableArray();
|
||||
|
||||
var option = new ContextMenuOption(TextManager.Get("delete"), isEnabled: wireOption.IsSome() || nodeOption is CircuitBoxComponent, () =>
|
||||
{
|
||||
if (wireOption.TryUnwrap(out var wire))
|
||||
{
|
||||
CircuitBox.RemoveWires(wire.IsSelected ? wireSelection : ImmutableArray.Create(wire));
|
||||
}
|
||||
|
||||
if (nodeOption is CircuitBoxComponent node)
|
||||
{
|
||||
CircuitBox.RemoveComponents(node.IsSelected ? nodeSelection : ImmutableArray.Create(node));
|
||||
}
|
||||
});
|
||||
|
||||
// show component name in the header to better indicate what is about to be deleted
|
||||
if (nodeOption is CircuitBoxComponent comp)
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, comp.Item.Name, comp.Item.Prefab.SignalComponentColor, option);
|
||||
return;
|
||||
}
|
||||
|
||||
// also check if a wire is being deleted
|
||||
if (wireOption.TryUnwrap(out var foundWire))
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu(PlayerInput.MousePosition, foundWire.UsedItemPrefab.Name, foundWire.Color, option);
|
||||
return;
|
||||
}
|
||||
|
||||
GUIContextMenu.CreateContextMenu(option);
|
||||
}
|
||||
|
||||
public CircuitBoxNode? GetTopmostNode(ImmutableHashSet<CircuitBoxNode> nodes)
|
||||
{
|
||||
CircuitBoxNode? foundNode = null;
|
||||
|
||||
var allNodes = MouseSnapshotHandler.Nodes.ToImmutableArray();
|
||||
|
||||
for (int i = allNodes.Length - 1; i >= 0; i--)
|
||||
{
|
||||
CircuitBoxNode node = allNodes[i];
|
||||
|
||||
if (nodes.Contains(node))
|
||||
{
|
||||
foundNode = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
toggleMenuButton?.AddToGUIUpdateList();
|
||||
selectedWireFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class CircuitBoxWire
|
||||
{
|
||||
public CircuitBoxWireRenderer Renderer;
|
||||
|
||||
public void Update() => Renderer.Recompute(From.AnchorPoint, To.AnchorPoint, Color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class CircuitBoxWireRenderer
|
||||
{
|
||||
private const int VertsPerQuad = 4, // how many points per quad
|
||||
QuadsPerLine = 10, // how many quads per line
|
||||
VertsPerLine = QuadsPerLine * VertsPerQuad, // how many points we need to draw all the quads for a single line
|
||||
TotalVertsPerWire = VertsPerLine * 2; // we are drawing 2 lines
|
||||
|
||||
private readonly Texture2D texture;
|
||||
|
||||
private VertexPositionColorTexture[] verts = new VertexPositionColorTexture[TotalVertsPerWire];
|
||||
private readonly Vector2[][] colliders = new Vector2[2][];
|
||||
private SquareLine skeleton;
|
||||
|
||||
private Vector2 lastStart, lastEnd;
|
||||
private Color lastColor;
|
||||
private readonly Option<CircuitBoxWire> wire;
|
||||
|
||||
public CircuitBoxWireRenderer(Option<CircuitBoxWire> wire, Vector2 start, Vector2 end, Color color, Sprite? wireSprite)
|
||||
{
|
||||
this.wire = wire;
|
||||
texture = wireSprite?.Texture ?? GUI.WhiteTexture;
|
||||
Recompute(start, end, color);
|
||||
}
|
||||
|
||||
private void UpdateColor(Color color)
|
||||
{
|
||||
for (int i = 0; i < TotalVertsPerWire; i++)
|
||||
{
|
||||
verts[i].Color = color;
|
||||
}
|
||||
|
||||
lastColor = color;
|
||||
}
|
||||
|
||||
public void Recompute(Vector2 start, Vector2 end, Color color)
|
||||
{
|
||||
if (MathUtils.NearlyEqual(lastStart, start) && MathUtils.NearlyEqual(lastEnd, end))
|
||||
{
|
||||
if (lastColor == color) { return; }
|
||||
|
||||
UpdateColor(color);
|
||||
return;
|
||||
}
|
||||
|
||||
lastStart = start;
|
||||
lastEnd = end;
|
||||
lastColor = color;
|
||||
|
||||
skeleton = ToolBox.GetSquareLineBetweenPoints(start, end, CircuitBoxSizes.WireKnobLength);
|
||||
var points = skeleton.Points;
|
||||
|
||||
Vector2 centerOfLine = (points[2] + points[3]) / 2f;
|
||||
|
||||
ImmutableArray<Vector2> points1 = GetLinePoints(points[1], points[2], centerOfLine),
|
||||
points2 = GetLinePoints(centerOfLine, points[3], points[4]);
|
||||
|
||||
colliders[0] = ConstructQuads(ref verts, 0, points1, color);
|
||||
colliders[1] = ConstructQuads(ref verts, VertsPerLine, points2, color);
|
||||
|
||||
static ImmutableArray<Vector2> GetLinePoints(Vector2 start, Vector2 control, Vector2 end)
|
||||
{
|
||||
var points = ImmutableArray.CreateBuilder<Vector2>(QuadsPerLine);
|
||||
for (int i = 0; i < QuadsPerLine; i++)
|
||||
{
|
||||
float t = (float)i / (QuadsPerLine - 1);
|
||||
Vector2 pos = MathUtils.Bezier(start, control, end, t);
|
||||
points.Add(pos);
|
||||
}
|
||||
|
||||
return points.ToImmutable();
|
||||
}
|
||||
|
||||
static Vector2[] ConstructQuads(ref VertexPositionColorTexture[] verts, int startOffset, IReadOnlyList<Vector2> points, Color color)
|
||||
{
|
||||
// ok I don't know why this needs to be one quad less, maybe we are drawing with only 9 quads lol
|
||||
var collider = new Vector2[VertsPerLine - VertsPerQuad];
|
||||
|
||||
int leftIndex = collider.Length - 1,
|
||||
rightIndex = 0;
|
||||
|
||||
// we need to calculate half of the width since the way we expand the quads from origin, otherwise the line will be twice as wide
|
||||
const float halfWidth = CircuitBoxSizes.WireWidth / 2f;
|
||||
|
||||
// draw the line using quads
|
||||
for (int i = 0; i < points.Count - 1; i++)
|
||||
{
|
||||
bool isFirst = i == 0 && startOffset == 0,
|
||||
isLast = i == points.Count - 2 && startOffset > 0;
|
||||
|
||||
Vector2 start = points[i],
|
||||
end = points[i + 1];
|
||||
|
||||
Vector2 dir = Vector2.Normalize(end - start);
|
||||
Vector2 length = new Vector2(dir.Y, -dir.X) * halfWidth;
|
||||
|
||||
int vertIndex = startOffset + i * 4;
|
||||
|
||||
Vector2 topRight = end + length;
|
||||
Vector2 topLeft = end - length;
|
||||
|
||||
Vector2 bottomRight;
|
||||
Vector2 bottomLeft;
|
||||
|
||||
// get previous points if any
|
||||
int prevIndex = vertIndex - 4;
|
||||
|
||||
if ((prevIndex - startOffset) >= 0)
|
||||
{
|
||||
// connect the previous "upper" corners into the current "lower" corners to stitch the line together
|
||||
Vector3 prevTopRight = verts[TopRight(prevIndex)].Position,
|
||||
prevTopLeft = verts[TopLeft(prevIndex)].Position;
|
||||
|
||||
bottomRight = ToVector2(prevTopRight);
|
||||
bottomLeft = ToVector2(prevTopLeft);
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomRight = start + length;
|
||||
bottomLeft = start - length;
|
||||
}
|
||||
|
||||
if (isFirst)
|
||||
{
|
||||
if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
|
||||
{
|
||||
float offset = dir.Y < 0 ? halfWidth : -halfWidth;
|
||||
// if the line is more vertical than horizontal, we want to move the bottom corners to the left
|
||||
bottomRight.Y = start.Y - offset;
|
||||
bottomLeft.Y = start.Y - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we want to move the bottom corners to the top
|
||||
bottomRight.X = start.X;
|
||||
bottomLeft.X = start.X;
|
||||
}
|
||||
}
|
||||
else if (isLast)
|
||||
{
|
||||
if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
|
||||
{
|
||||
float offset = dir.Y < 0 ? halfWidth : -halfWidth;
|
||||
// if the line is more vertical than horizontal, we want to move the bottom corners to the left
|
||||
topRight.Y = end.Y + offset;
|
||||
topLeft.Y = end.Y + offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we want to move the bottom corners to the top
|
||||
topRight.X = end.X;
|
||||
topLeft.X = end.X;
|
||||
}
|
||||
}
|
||||
|
||||
collider[rightIndex++] = bottomLeft;
|
||||
collider[rightIndex++] = topLeft;
|
||||
|
||||
collider[leftIndex--] = bottomRight;
|
||||
collider[leftIndex--] = topRight;
|
||||
|
||||
// adjust this if we want sprites to support sourceRects
|
||||
Vector2 uvTopRight = new Vector2(0, 1),
|
||||
uvTopLeft = new Vector2(0, 0),
|
||||
uvBottomRight = new Vector2(1, 1),
|
||||
uvBottomLeft = new Vector2(1, 0);
|
||||
|
||||
SetPos(ref verts, TopRight(vertIndex), topRight, color, uvTopRight);
|
||||
SetPos(ref verts, TopLeft(vertIndex), topLeft, color, uvTopLeft);
|
||||
SetPos(ref verts, BottomRight(vertIndex), bottomRight, color, uvBottomRight);
|
||||
SetPos(ref verts, BottomLeft(vertIndex), bottomLeft, color, uvBottomLeft);
|
||||
|
||||
static void SetPos(ref VertexPositionColorTexture[] verts, int index, Vector2 pos, Color color, Vector2 uv)
|
||||
{
|
||||
verts[index].Position = ToVector3(pos);
|
||||
verts[index].Color = color;
|
||||
verts[index].TextureCoordinate = uv;
|
||||
static Vector3 ToVector3(Vector2 v) => new Vector3(v.X, v.Y, 0f);
|
||||
}
|
||||
|
||||
static int TopRight(int vertIndex) => vertIndex;
|
||||
static int TopLeft(int vertIndex) => vertIndex + 1;
|
||||
static int BottomRight(int vertIndex) => vertIndex + 2;
|
||||
static int BottomLeft(int vertIndex) => vertIndex + 3;
|
||||
|
||||
static Vector2 ToVector2(Vector3 v) => new Vector2(v.X, v.Y);
|
||||
}
|
||||
|
||||
return collider;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(Vector2 pos)
|
||||
{
|
||||
pos.Y = -pos.Y;
|
||||
foreach (Vector2[] collider in colliders)
|
||||
{
|
||||
if (ToolBox.PointIntersectsWithPolygon(pos, collider, checkBoundingBox: false)) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Color selectionColor)
|
||||
{
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
for (int i = 0; i < skeleton.Points.Length; i++)
|
||||
{
|
||||
Vector2 point = skeleton.Points[i];
|
||||
spriteBatch.DrawPoint(point, Color.White, 25f);
|
||||
GUI.DrawString(spriteBatch, point - new Vector2(5f, 17f), i.ToString(), Color.Black, font: GUIStyle.LargeFont);
|
||||
}
|
||||
|
||||
spriteBatch.DrawLine(skeleton.Points[0], skeleton.Points[1], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[1], skeleton.Points[2], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[2], skeleton.Points[3], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[3], skeleton.Points[4], GUIStyle.Green, thickness: 2f);
|
||||
spriteBatch.DrawLine(skeleton.Points[4], skeleton.Points[5], GUIStyle.Green, thickness: 2f);
|
||||
}
|
||||
|
||||
bool isSelected = wire.TryUnwrap(out var w) && w.IsSelected;
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
foreach (var colliderPolys in colliders)
|
||||
{
|
||||
spriteBatch.DrawPolygon(Vector2.Zero, colliderPolys, selectionColor, 5f);
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.Draw(texture, verts, 0f);
|
||||
|
||||
if (skeleton.Type is SquareLine.LineType.SixPointBackwardsLine)
|
||||
{
|
||||
// we need to expand the start and end points to make the line look like it's connected to the "smooth" part of the line
|
||||
Vector2 expandedEnd = skeleton.Points[1],
|
||||
expandedStart = skeleton.Points[4];
|
||||
|
||||
expandedEnd.X += CircuitBoxSizes.WireWidth / 2f;
|
||||
expandedStart.X -= CircuitBoxSizes.WireWidth / 2f;
|
||||
|
||||
spriteBatch.DrawLineWithTexture(texture, skeleton.Points[0], expandedEnd, lastColor, thickness: CircuitBoxSizes.WireWidth);
|
||||
spriteBatch.DrawLineWithTexture(texture, expandedStart, skeleton.Points[5], lastColor, thickness: CircuitBoxSizes.WireWidth);
|
||||
|
||||
const float rectSize = CircuitBoxSizes.WireWidth * 1.5f;
|
||||
RectangleF startKnob = new RectangleF(skeleton.Points[1] - new Vector2(rectSize / 2f), new Vector2(rectSize)),
|
||||
endKnob = new RectangleF(skeleton.Points[4] - new Vector2(rectSize / 2f), new Vector2(rectSize));
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, startKnob, lastColor);
|
||||
GUI.DrawFilledRectangle(spriteBatch, endKnob, lastColor);
|
||||
}
|
||||
|
||||
if (!GameMain.DebugDraw) { return; }
|
||||
|
||||
foreach (var colliderPolys in colliders)
|
||||
{
|
||||
spriteBatch.DrawPolygonInner(Vector2.Zero, colliderPolys, Color.Lime, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,7 +317,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform),
|
||||
msg.Text, font: GUIStyle.SmallFont, wrap: true)
|
||||
RichString.Rich(msg.Text), font: GUIStyle.SmallFont, wrap: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
TextColor = msg.Color
|
||||
@@ -405,7 +405,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Screen.Selected != GameMain.SubEditorScreen) return;
|
||||
|
||||
if (MapEntity.mapEntityList.Any(e => e is Hull || e is Gap))
|
||||
if (MapEntity.MapEntityList.Any(e => e is Hull || e is Gap))
|
||||
{
|
||||
ShowQuestionPrompt("This submarine already has hulls and/or gaps. This command will delete them. Do you want to continue? Y/N",
|
||||
(option) =>
|
||||
@@ -984,7 +984,7 @@ namespace Barotrauma
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
bool entityFound = false;
|
||||
foreach (MapEntity entity in MapEntity.mapEntityList)
|
||||
foreach (MapEntity entity in MapEntity.MapEntityList)
|
||||
{
|
||||
if (entity is Item item)
|
||||
{
|
||||
@@ -1106,19 +1106,19 @@ namespace Barotrauma
|
||||
|
||||
commands.Add(new Command("cleansub", "", (string[] args) =>
|
||||
{
|
||||
for (int i = MapEntity.mapEntityList.Count - 1; i >= 0; i--)
|
||||
for (int i = MapEntity.MapEntityList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
MapEntity me = MapEntity.mapEntityList[i];
|
||||
MapEntity me = MapEntity.MapEntityList[i];
|
||||
|
||||
if (me.SimPosition.Length() > 2000.0f)
|
||||
{
|
||||
NewMessage("Removed " + me.Name + " (simposition " + me.SimPosition + ")", Color.Orange);
|
||||
MapEntity.mapEntityList.RemoveAt(i);
|
||||
MapEntity.MapEntityList.RemoveAt(i);
|
||||
}
|
||||
else if (!me.ShouldBeSaved)
|
||||
{
|
||||
NewMessage("Removed " + me.Name + " (!ShouldBeSaved)", Color.Orange);
|
||||
MapEntity.mapEntityList.RemoveAt(i);
|
||||
MapEntity.MapEntityList.RemoveAt(i);
|
||||
}
|
||||
else if (me is Item)
|
||||
{
|
||||
@@ -1487,14 +1487,19 @@ namespace Barotrauma
|
||||
string itemNameOrId = args[0].ToLowerInvariant();
|
||||
|
||||
ItemPrefab itemPrefab =
|
||||
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
|
||||
MapEntityPrefab.Find(null, identifier: itemNameOrId.ToIdentifier(), showErrorMessages: false)) as ItemPrefab;
|
||||
(MapEntityPrefab.FindByName(itemNameOrId) ??
|
||||
MapEntityPrefab.FindByIdentifier(itemNameOrId.ToIdentifier())) as ItemPrefab;
|
||||
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
NewMessage("Item not found for analyzing.");
|
||||
return;
|
||||
}
|
||||
if (itemPrefab.DefaultPrice == null)
|
||||
{
|
||||
NewMessage($"Item \"{itemPrefab.Name}\" is not sellable/purchaseable.");
|
||||
return;
|
||||
}
|
||||
NewMessage("Analyzing item " + itemPrefab.Name + " with base cost " + itemPrefab.DefaultPrice.Price);
|
||||
|
||||
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab);
|
||||
@@ -1857,6 +1862,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var eventPrefab in EventPrefab.Prefabs)
|
||||
{
|
||||
if (eventPrefab is not TraitorEventPrefab traitorEventPrefab) { continue; }
|
||||
addIfMissing($"eventname.{traitorEventPrefab.Identifier}".ToIdentifier(), language);
|
||||
}
|
||||
|
||||
foreach (Type itemComponentType in typeof(ItemComponent).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(ItemComponent))))
|
||||
{
|
||||
checkSerializableEntityType(itemComponentType);
|
||||
@@ -2356,7 +2367,20 @@ namespace Barotrauma
|
||||
WaterRenderer.BlurAmount = blurAmount;
|
||||
}));
|
||||
|
||||
|
||||
commands.Add(new Command("generatelevels", "generatelevels [amount]: generate a bunch of levels with the currently selected parameters in the level editor.", (string[] args) =>
|
||||
{
|
||||
if (GameMain.GameSession == null)
|
||||
{
|
||||
int amount = 1;
|
||||
if (args.Length > 0) { int.TryParse(args[0], out amount); }
|
||||
GameMain.LevelEditorScreen.TestLevelGenerationForErrors(amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage("Can't use the command while round is running.");
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("refreshrect", "Updates the dimensions of the selected items to match the ones defined in the prefab. Applied only in the subeditor.", (string[] args) =>
|
||||
{
|
||||
//TODO: maybe do this automatically during loading when possible?
|
||||
@@ -2538,8 +2562,11 @@ namespace Barotrauma
|
||||
HashSet<XDocument> docs = new HashSet<XDocument>();
|
||||
HashSet<string> textIds = new HashSet<string>();
|
||||
|
||||
Dictionary<string, string> existingTexts = new Dictionary<string, string>();
|
||||
|
||||
foreach (EventPrefab eventPrefab in EventSet.GetAllEventPrefabs())
|
||||
{
|
||||
if (eventPrefab is not TraitorEventPrefab) { continue; }
|
||||
if (eventPrefab.Identifier.IsEmpty)
|
||||
{
|
||||
continue;
|
||||
@@ -2576,35 +2603,74 @@ namespace Barotrauma
|
||||
void getTextsFromElement(XElement element, List<string> list, string parentName)
|
||||
{
|
||||
string text = element.GetAttributeString("text", null);
|
||||
string textId = $"EventText.{parentName}";
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("EventText.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.Add($"<{textId}>{text}</{textId}>");
|
||||
element.SetAttributeValue("text", textId);
|
||||
string textAttribute = "text";
|
||||
XElement textElement = element;
|
||||
if (text == null)
|
||||
{
|
||||
var subTextElement = element?.Element("Text");
|
||||
if (subTextElement != null)
|
||||
{
|
||||
textAttribute = "tag";
|
||||
text = subTextElement?.GetAttributeString(textAttribute, null);
|
||||
textElement = subTextElement;
|
||||
}
|
||||
if (text == null)
|
||||
{
|
||||
AddWarning("Failed to find text from the element " + element.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
string textId = $"EventText.{parentName}";
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("EventText.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (existingTexts.TryGetValue(text, out string existingTextId))
|
||||
{
|
||||
textElement.SetAttributeValue(textAttribute, existingTextId);
|
||||
}
|
||||
else
|
||||
{
|
||||
textIds.Add(parentName);
|
||||
list.Add($"<{textId}>{text}</{textId}>");
|
||||
existingTexts.Add(text, textId);
|
||||
textElement.SetAttributeValue(textAttribute, textId);
|
||||
}
|
||||
}
|
||||
|
||||
int conversationIndex = 1;
|
||||
int objectiveIndex = 1;
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
string elementName = parentName;
|
||||
bool ignore = false;
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "conversationaction":
|
||||
while (textIds.Contains(parentName+".c"+i))
|
||||
case "conversationaction":
|
||||
while (textIds.Contains(elementName + ".c" + conversationIndex))
|
||||
{
|
||||
i++;
|
||||
conversationIndex++;
|
||||
}
|
||||
parentName += ".c" + i;
|
||||
elementName += ".c" + conversationIndex;
|
||||
break;
|
||||
case "eventlogaction":
|
||||
while (textIds.Contains(elementName + ".objective" + objectiveIndex))
|
||||
{
|
||||
objectiveIndex++;
|
||||
}
|
||||
elementName += ".objective" + objectiveIndex;
|
||||
break;
|
||||
case "option":
|
||||
while (textIds.Contains(parentName.Substring(0, parentName.Length - 3) + ".o" + i))
|
||||
while (textIds.Contains(elementName.Substring(0, elementName.Length - 3) + ".o" + conversationIndex))
|
||||
{
|
||||
i++;
|
||||
conversationIndex++;
|
||||
}
|
||||
parentName = parentName.Substring(0, parentName.Length - 3) + ".o" + i;
|
||||
elementName = elementName.Substring(0, elementName.Length - 3) + ".o" + conversationIndex;
|
||||
break;
|
||||
case "text":
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
textIds.Add(parentName);
|
||||
getTextsFromElement(subElement, list, parentName);
|
||||
if (ignore) { continue; }
|
||||
getTextsFromElement(subElement, list, elementName);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -2769,9 +2835,9 @@ namespace Barotrauma
|
||||
ep.DebugCreateInstance();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MapEntity.mapEntityList.Count; i++)
|
||||
for (int i = 0; i < MapEntity.MapEntityList.Count; i++)
|
||||
{
|
||||
var entity = MapEntity.mapEntityList[i] as ISerializableEntity;
|
||||
var entity = MapEntity.MapEntityList[i] as ISerializableEntity;
|
||||
if (entity != null)
|
||||
{
|
||||
List<(object obj, SerializableProperty property)> allProperties = new List<(object obj, SerializableProperty property)>();
|
||||
|
||||
@@ -42,15 +42,15 @@ namespace Barotrauma
|
||||
|
||||
partial void ShowDialog(Character speaker, Character targetCharacter)
|
||||
{
|
||||
CreateDialog(Text, speaker, Options.Select(opt => opt.Text), GetEndingOptions(), actionInstance: this, spriteIdentifier: EventSprite, fadeToBlack: FadeToBlack, dialogType: DialogType, continueConversation: ContinueConversation);
|
||||
CreateDialog(GetDisplayText(), speaker, Options.Select(opt => opt.Text), GetEndingOptions(), actionInstance: this, spriteIdentifier: EventSprite, fadeToBlack: FadeToBlack, dialogType: DialogType, continueConversation: ContinueConversation);
|
||||
}
|
||||
|
||||
public static void CreateDialog(string text, Character speaker, IEnumerable<string> options, int[] closingOptions, string eventSprite, UInt16 actionId, bool fadeToBlack, DialogTypes dialogType, bool continueConversation = false)
|
||||
public static void CreateDialog(LocalizedString text, Character speaker, IEnumerable<string> options, int[] closingOptions, string eventSprite, UInt16 actionId, bool fadeToBlack, DialogTypes dialogType, bool continueConversation = false)
|
||||
{
|
||||
CreateDialog(text, speaker, options, closingOptions, actionInstance: null, actionId: actionId, spriteIdentifier: eventSprite, fadeToBlack: fadeToBlack, dialogType: dialogType, continueConversation: continueConversation);
|
||||
}
|
||||
|
||||
private static void CreateDialog(string text, Character speaker, IEnumerable<string> options, int[] closingOptions, string spriteIdentifier = null,
|
||||
private static void CreateDialog(LocalizedString text, Character speaker, IEnumerable<string> options, int[] closingOptions, string spriteIdentifier = null,
|
||||
ConversationAction actionInstance = null, UInt16? actionId = null, bool fadeToBlack = false, DialogTypes dialogType = DialogTypes.Regular, bool continueConversation = false)
|
||||
{
|
||||
Debug.Assert(actionInstance == null || actionId == null);
|
||||
@@ -126,6 +126,7 @@ namespace Barotrauma
|
||||
if (actionInstance != null)
|
||||
{
|
||||
lastActiveAction = actionInstance;
|
||||
actionInstance.lastActiveTime = Timing.TotalTime;
|
||||
actionInstance.dialogBox = messageBox;
|
||||
}
|
||||
else
|
||||
@@ -313,7 +314,7 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
private static List<GUIButton> CreateConversation(GUIListBox parentBox, string text, Character speaker, IEnumerable<string> options, bool drawChathead = true)
|
||||
private static List<GUIButton> CreateConversation(GUIListBox parentBox, LocalizedString text, Character speaker, IEnumerable<string> options, bool drawChathead = true)
|
||||
{
|
||||
var content = new GUILayoutGroup(new RectTransform(Vector2.One, parentBox.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
|
||||
{
|
||||
@@ -322,9 +323,11 @@ namespace Barotrauma
|
||||
AlwaysOverrideCursor = true
|
||||
};
|
||||
|
||||
LocalizedString translatedText = speaker?.DisplayName is not null ?
|
||||
TextManager.GetWithVariable(text, "[speakername]", speaker?.DisplayName) :
|
||||
TextManager.Get(text);
|
||||
LocalizedString translatedText = text.Replace("\\n", "\n");
|
||||
if (speaker?.DisplayName is not null)
|
||||
{
|
||||
translatedText = translatedText.Replace("[speakername]", speaker.DisplayName);
|
||||
}
|
||||
translatedText = TextManager.ParseInputTypes(translatedText).Fallback(text);
|
||||
|
||||
if (speaker?.Info != null && drawChathead)
|
||||
@@ -341,7 +344,7 @@ namespace Barotrauma
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform), translatedText, wrap: true)
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform), RichString.Rich(translatedText), wrap: true)
|
||||
{
|
||||
AlwaysOverrideCursor = true,
|
||||
UserData = "text"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class EventLogAction : EventAction
|
||||
{
|
||||
partial void AddEntryProjSpecific(EventLog? eventLog, string displayText)
|
||||
{
|
||||
eventLog?.AddEntry(ParentEvent.Prefab.Identifier, Id, displayText);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class EventObjectiveAction : EventAction
|
||||
{
|
||||
public static void Trigger(
|
||||
SegmentActionType Type,
|
||||
Identifier Identifier,
|
||||
Identifier ObjectiveTag,
|
||||
Identifier ParentObjectiveId,
|
||||
Identifier TextTag,
|
||||
bool CanBeCompleted,
|
||||
bool autoPlayVideo = false,
|
||||
string videoFile = "",
|
||||
int width = 450,
|
||||
int height = 80)
|
||||
{
|
||||
ObjectiveManager.Segment? segment = null;
|
||||
// Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance)
|
||||
if (Type == SegmentActionType.Trigger)
|
||||
{
|
||||
segment = ObjectiveManager.Segment.CreateInfoBoxSegment(Identifier, ObjectiveTag, autoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No,
|
||||
new ObjectiveManager.Segment.Text(TextTag, width, height, Anchor.Center),
|
||||
new ObjectiveManager.Segment.Video(videoFile, TextTag, width, height));
|
||||
}
|
||||
else if (Type == SegmentActionType.Add)
|
||||
{
|
||||
segment = ObjectiveManager.Segment.CreateObjectiveSegment(Identifier, !ObjectiveTag.IsEmpty ? ObjectiveTag : Identifier);
|
||||
}
|
||||
if (segment is not null)
|
||||
{
|
||||
segment.CanBeCompleted = CanBeCompleted;
|
||||
segment.ParentId = ParentObjectiveId;
|
||||
}
|
||||
switch (Type)
|
||||
{
|
||||
case SegmentActionType.Trigger:
|
||||
case SegmentActionType.Add:
|
||||
ObjectiveManager.TriggerSegment(segment);
|
||||
break;
|
||||
case SegmentActionType.Complete:
|
||||
ObjectiveManager.CompleteSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.Remove:
|
||||
ObjectiveManager.RemoveSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.CompleteAndRemove:
|
||||
ObjectiveManager.CompleteSegment(Identifier);
|
||||
ObjectiveManager.RemoveSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.Fail:
|
||||
ObjectiveManager.FailSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.FailAndRemove:
|
||||
ObjectiveManager.FailSegment(Identifier);
|
||||
ObjectiveManager.RemoveSegment(Identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific()
|
||||
{
|
||||
Trigger(Type, Identifier, ObjectiveTag, ParentObjectiveId, TextTag, CanBeCompleted, AutoPlayVideo, VideoFile, Width, Height);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ partial class MessageBoxAction : EventAction
|
||||
var segment = ObjectiveManager.Segment.CreateMessageBoxSegment(id, ObjectiveTag, CreateMessageBox);
|
||||
segment.CanBeCompleted = ObjectiveCanBeCompleted;
|
||||
segment.ParentId = ParentObjectiveId;
|
||||
ObjectiveManager.TriggerTutorialSegment(segment, connectObjective: Type == ActionType.ConnectObjective);
|
||||
ObjectiveManager.TriggerSegment(segment, connectObjective: Type == ActionType.ConnectObjective);
|
||||
}
|
||||
}
|
||||
else if (Type == ActionType.Close)
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class TutorialSegmentAction : EventAction
|
||||
{
|
||||
private ObjectiveManager.Segment segment;
|
||||
|
||||
partial void UpdateProjSpecific()
|
||||
{
|
||||
// Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance)
|
||||
if (Type == SegmentActionType.Trigger)
|
||||
{
|
||||
segment = ObjectiveManager.Segment.CreateInfoBoxSegment(Identifier, ObjectiveTag, AutoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No,
|
||||
new ObjectiveManager.Segment.Text(TextTag, Width, Height, Anchor.Center),
|
||||
new ObjectiveManager.Segment.Video(VideoFile, TextTag, Width, Height));
|
||||
}
|
||||
else if (Type == SegmentActionType.Add)
|
||||
{
|
||||
segment = ObjectiveManager.Segment.CreateObjectiveSegment(Identifier, !ObjectiveTag.IsEmpty ? ObjectiveTag : Identifier);
|
||||
}
|
||||
if (segment is not null)
|
||||
{
|
||||
segment.CanBeCompleted = CanBeCompleted;
|
||||
segment.ParentId = ParentObjectiveId;
|
||||
}
|
||||
switch (Type)
|
||||
{
|
||||
case SegmentActionType.Trigger:
|
||||
case SegmentActionType.Add:
|
||||
ObjectiveManager.TriggerTutorialSegment(segment);
|
||||
break;
|
||||
case SegmentActionType.Complete:
|
||||
ObjectiveManager.CompleteTutorialSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.Remove:
|
||||
ObjectiveManager.RemoveTutorialSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.CompleteAndRemove:
|
||||
ObjectiveManager.CompleteTutorialSegment(Identifier);
|
||||
ObjectiveManager.RemoveTutorialSegment(Identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Barotrauma/BarotraumaClient/ClientSource/Events/EventLog.cs
Normal file
61
Barotrauma/BarotraumaClient/ClientSource/Events/EventLog.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class EventLog
|
||||
{
|
||||
public bool UnreadEntries { get; private set; }
|
||||
|
||||
public void AddEntry(Identifier eventPrefabId, Identifier entryId, string text)
|
||||
{
|
||||
TryAddEntryInternal(eventPrefabId, entryId, text);
|
||||
GameMain.GameSession?.EnableEventLogNotificationIcon(enabled: true);
|
||||
UnreadEntries = true;
|
||||
}
|
||||
|
||||
public void CreateEventLogUI(GUIComponent parent, TraitorManager.TraitorResults? traitorResults = null)
|
||||
{
|
||||
UnreadEntries = false;
|
||||
|
||||
int spacing = GUI.IntScale(5);
|
||||
foreach (var ev in events.Values)
|
||||
{
|
||||
LocalizedString nameString = string.Empty;
|
||||
int difficultyIconCount = 0;
|
||||
|
||||
EventPrefab.Prefabs.TryGet(ev.EventIdentifier, out EventPrefab? eventPrefab);
|
||||
if (eventPrefab is not null)
|
||||
{
|
||||
nameString = RichString.Rich(eventPrefab.Name);
|
||||
if (eventPrefab is TraitorEventPrefab traitorEventPrefab)
|
||||
{
|
||||
difficultyIconCount = traitorEventPrefab.DangerLevel;
|
||||
}
|
||||
}
|
||||
var textContent = new List<LocalizedString>();
|
||||
textContent.AddRange(ev.Entries.Select(e => (LocalizedString)e.Text));
|
||||
|
||||
var icon = GUIStyle.GetComponentStyle("TraitorMissionIcon")?.GetDefaultSprite();
|
||||
|
||||
RoundSummary.CreateMissionEntry(
|
||||
parent,
|
||||
nameString,
|
||||
textContent,
|
||||
difficultyIconCount,
|
||||
icon, GUIStyle.Red,
|
||||
out GUIImage missionIcon);
|
||||
|
||||
if (traitorResults != null &&
|
||||
traitorResults.Value.TraitorEventIdentifier == ev.EventIdentifier)
|
||||
{
|
||||
RoundSummary.UpdateMissionStateIcon(traitorResults.Value.ObjectiveSuccessful, missionIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,23 +413,9 @@ namespace Barotrauma
|
||||
|
||||
private Rectangle DrawScriptedEvent(SpriteBatch spriteBatch, ScriptedEvent scriptedEvent, Rectangle? parentRect = null)
|
||||
{
|
||||
EventAction? currentEvent = !scriptedEvent.IsFinished ? scriptedEvent.Actions[scriptedEvent.CurrentActionIndex] : null;
|
||||
|
||||
List<DebugLine> positions = new List<DebugLine>();
|
||||
|
||||
string text = $"Finished: {scriptedEvent.IsFinished.ColorizeObject()}\n" +
|
||||
$"Action index: {scriptedEvent.CurrentActionIndex.ColorizeObject()}\n" +
|
||||
$"Current action: {currentEvent?.ToDebugString() ?? ToolBox.ColorizeObject(null)}\n";
|
||||
|
||||
text += "All actions:\n";
|
||||
text += FindActions(scriptedEvent).Aggregate(string.Empty, (current, action) => current + $"{new string(' ', action.Item1 * 6)}{action.Item2.ToDebugString()}\n");
|
||||
|
||||
text += "Targets:\n";
|
||||
foreach (var (key, value) in scriptedEvent.Targets)
|
||||
{
|
||||
text += $" {key.ColorizeObject()}: {value.Aggregate(string.Empty, (current, entity) => current + $"{entity.ColorizeObject()} ")}\n";
|
||||
}
|
||||
|
||||
string text = scriptedEvent.GetDebugInfo();
|
||||
if (scriptedEvent.Targets != null)
|
||||
{
|
||||
foreach ((_, List<Entity> entities) in scriptedEvent.Targets)
|
||||
@@ -452,10 +438,7 @@ namespace Barotrauma
|
||||
{
|
||||
debugPositions.Clear();
|
||||
|
||||
string text = $"Finished: {artifactEvent.IsFinished.ColorizeObject()}\n" +
|
||||
$"Item: {artifactEvent.Item.ColorizeObject()}\n" +
|
||||
$"Spawn pending: {artifactEvent.SpawnPending.ColorizeObject()}\n" +
|
||||
$"Spawn position: {artifactEvent.SpawnPos.ColorizeObject()}\n";
|
||||
string text = artifactEvent.GetDebugInfo();
|
||||
|
||||
if (artifactEvent.Item != null && !artifactEvent.Item.Removed)
|
||||
{
|
||||
@@ -470,10 +453,7 @@ namespace Barotrauma
|
||||
{
|
||||
debugPositions.Clear();
|
||||
|
||||
string text = $"Finished: {monsterEvent.IsFinished.ColorizeObject()}\n" +
|
||||
$"Amount: {monsterEvent.MinAmount.ColorizeObject()} - {monsterEvent.MaxAmount.ColorizeObject()}\n" +
|
||||
$"Spawn pending: {monsterEvent.SpawnPending.ColorizeObject()}\n" +
|
||||
$"Spawn position: {monsterEvent.SpawnPos.ColorizeObject()}\n";
|
||||
string text = monsterEvent.GetDebugInfo();
|
||||
|
||||
if (monsterEvent.SpawnPos != null && Submarine.MainSub != null)
|
||||
{
|
||||
@@ -712,7 +692,30 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetworkEventType.EVENTLOG:
|
||||
ClientReadEventLog(GameMain.Client, msg);
|
||||
break;
|
||||
case NetworkEventType.EVENTOBJECTIVE:
|
||||
ClientReadEventObjective(GameMain.Client, msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClientReadEventLog(GameClient client, IReadMessage msg)
|
||||
{
|
||||
NetEventLogEntry entry = INetSerializableStruct.Read<NetEventLogEntry>(msg);
|
||||
EventLog.AddEntry(entry.EventPrefabId, entry.LogEntryId, entry.Text.Replace("\\n", "\n"));
|
||||
}
|
||||
private static void ClientReadEventObjective(GameClient client, IReadMessage msg)
|
||||
{
|
||||
NetEventObjective entry = INetSerializableStruct.Read<NetEventObjective>(msg);
|
||||
EventObjectiveAction.Trigger(
|
||||
entry.Type,
|
||||
entry.Identifier,
|
||||
entry.ObjectiveTag,
|
||||
entry.ParentObjectiveId,
|
||||
entry.TextTag,
|
||||
entry.CanBeCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -9,22 +10,37 @@ namespace Barotrauma
|
||||
public override bool DisplayAsCompleted => false;
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
public override int State
|
||||
{
|
||||
get => base.State;
|
||||
set
|
||||
{
|
||||
base.State = value;
|
||||
if (base.State > 0)
|
||||
{
|
||||
caves.ForEach(c => c.MissionsToDisplayOnSonar.Remove(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
byte caveCount = msg.ReadByte();
|
||||
for (int i = 0; i < caveCount; i++)
|
||||
{
|
||||
byte selectedCave = msg.ReadByte();
|
||||
if (selectedCave < 255 && Level.Loaded != null)
|
||||
byte selectedCaveIndex = msg.ReadByte();
|
||||
if (selectedCaveIndex < 255 && Level.Loaded != null)
|
||||
{
|
||||
if (selectedCave < Level.Loaded.Caves.Count)
|
||||
if (selectedCaveIndex < Level.Loaded.Caves.Count)
|
||||
{
|
||||
Level.Loaded.Caves[selectedCave].DisplayOnSonar = true;
|
||||
var selectedCave = Level.Loaded.Caves[selectedCaveIndex];
|
||||
selectedCave.MissionsToDisplayOnSonar.Add(this);
|
||||
caves.Add(selectedCave);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Cave index out of bounds when reading nest mission data. Index: {selectedCave}, number of caves: {Level.Loaded.Caves.Count}");
|
||||
DebugConsole.ThrowError($"Cave index out of bounds when reading nest mission data. Index: {selectedCaveIndex}, number of caves: {Level.Loaded.Caves.Count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using static Barotrauma.MissionPrefab;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -27,7 +28,11 @@ namespace Barotrauma
|
||||
|
||||
public Color GetDifficultyColor()
|
||||
{
|
||||
int v = Difficulty ?? MissionPrefab.MinDifficulty;
|
||||
return GetDifficultyColor(Difficulty ?? MissionPrefab.MinDifficulty);
|
||||
}
|
||||
public static Color GetDifficultyColor(int difficulty)
|
||||
{
|
||||
int v = difficulty;
|
||||
float t = MathUtils.InverseLerp(MissionPrefab.MinDifficulty, MissionPrefab.MaxDifficulty, v);
|
||||
return ToolBox.GradientLerp(t, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
|
||||
}
|
||||
@@ -61,36 +66,48 @@ namespace Barotrauma
|
||||
List<LocalizedString> reputationRewardTexts = new List<LocalizedString>();
|
||||
foreach (var reputationReward in ReputationRewards)
|
||||
{
|
||||
FactionPrefab targetFactionPrefab;
|
||||
if (reputationReward.Key == "location" )
|
||||
FactionPrefab factionPrefab;
|
||||
if (reputationReward.FactionIdentifier == "location" )
|
||||
{
|
||||
targetFactionPrefab = OriginLocation.Faction?.Prefab;
|
||||
factionPrefab = OriginLocation.Faction?.Prefab;
|
||||
}
|
||||
else
|
||||
{
|
||||
FactionPrefab.Prefabs.TryGet(reputationReward.Key, out targetFactionPrefab);
|
||||
}
|
||||
|
||||
if (targetFactionPrefab == null)
|
||||
{
|
||||
return string.Empty;
|
||||
FactionPrefab.Prefabs.TryGet(reputationReward.FactionIdentifier, out factionPrefab);
|
||||
}
|
||||
|
||||
float totalReputationChange = reputationReward.Value;
|
||||
if (GameMain.GameSession?.Campaign?.Factions.Find(f => f.Prefab == targetFactionPrefab) is Faction faction)
|
||||
if (factionPrefab != null)
|
||||
{
|
||||
totalReputationChange = reputationReward.Value * faction.Reputation.GetReputationChangeMultiplier(reputationReward.Value);
|
||||
AddReputationText(factionPrefab, reputationReward.Amount);
|
||||
if (!MathUtils.NearlyEqual(reputationReward.AmountForOpposingFaction, 0.0f) &&
|
||||
FactionPrefab.Prefabs.TryGet(factionPrefab.OpposingFaction, out var opposingFactionPrefab))
|
||||
{
|
||||
AddReputationText(opposingFactionPrefab, reputationReward.AmountForOpposingFaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddReputationText(FactionPrefab factionPrefab, float amount)
|
||||
{
|
||||
if (factionPrefab == null) { return; }
|
||||
|
||||
float totalReputationChange = amount;
|
||||
if (GameMain.GameSession?.Campaign?.Factions.Find(f => f.Prefab == factionPrefab) is Faction faction)
|
||||
{
|
||||
totalReputationChange = amount * faction.Reputation.GetReputationChangeMultiplier(amount);
|
||||
}
|
||||
|
||||
LocalizedString name = $"‖color:{XMLExtensions.ToStringHex(targetFactionPrefab.IconColor)}‖{targetFactionPrefab.Name}‖end‖";
|
||||
LocalizedString name = $"‖color:{XMLExtensions.ToStringHex(factionPrefab.IconColor)}‖{factionPrefab.Name}‖end‖";
|
||||
float normalizedValue = MathUtils.InverseLerp(-100.0f, 100.0f, totalReputationChange);
|
||||
string formattedValue = ((int)Math.Round(totalReputationChange)).ToString("+#;-#;0"); //force plus sign for positive numbers
|
||||
LocalizedString rewardText = TextManager.GetWithVariables(
|
||||
"reputationformat",
|
||||
("[reputationname]", name),
|
||||
("[reputationvalue]", $"‖color:{XMLExtensions.ToStringHex(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" ));
|
||||
("[reputationvalue]", $"‖color:{XMLExtensions.ToStringHex(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖"));
|
||||
reputationRewardTexts.Add(rewardText.Value);
|
||||
}
|
||||
|
||||
|
||||
if (reputationRewardTexts.Any())
|
||||
{
|
||||
return RichString.Rich(TextManager.AddPunctuation(':', TextManager.Get("reputation"), LocalizedString.Join(", ", reputationRewardTexts)));
|
||||
@@ -100,6 +117,20 @@ namespace Barotrauma
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
partial void DistributeExperienceToCrew(IEnumerable<Character> crew, int experienceGain)
|
||||
{
|
||||
foreach (Character character in crew)
|
||||
{
|
||||
GiveMissionExperience(character.Info);
|
||||
}
|
||||
void GiveMissionExperience(CharacterInfo info)
|
||||
{
|
||||
if (info == null) { return; }
|
||||
var experienceGainMultiplierIndividual = new AbilityMissionExperienceGainMultiplier(this, 1f);
|
||||
info.Character?.CheckTalents(AbilityEffectType.OnGainMissionExperience, experienceGainMultiplierIndividual);
|
||||
info.GiveExperience((int)(experienceGain * experienceGainMultiplierIndividual.Value));
|
||||
}
|
||||
}
|
||||
|
||||
partial void ShowMessageProjSpecific(int missionState)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,19 @@ namespace Barotrauma
|
||||
public override bool DisplayAsCompleted => State > 0 && !requireDelivery;
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
public override int State
|
||||
{
|
||||
get => base.State;
|
||||
set
|
||||
{
|
||||
base.State = value;
|
||||
if (base.State > 0 && selectedCave != null)
|
||||
{
|
||||
selectedCave.MissionsToDisplayOnSonar.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
@@ -20,8 +33,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (selectedCaveIndex < Level.Loaded.Caves.Count)
|
||||
{
|
||||
Level.Loaded.Caves[selectedCaveIndex].DisplayOnSonar = true;
|
||||
SpawnNestObjects(Level.Loaded, Level.Loaded.Caves[selectedCaveIndex]);
|
||||
selectedCave = Level.Loaded.Caves[selectedCaveIndex];
|
||||
selectedCave.MissionsToDisplayOnSonar.Add(this);
|
||||
SpawnNestObjects(Level.Loaded, selectedCave);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -99,10 +99,10 @@ namespace Barotrauma
|
||||
}))
|
||||
.Aggregate(TextManager.SpeciallyHandledCharCategory.None, (current, category) => current | category);
|
||||
|
||||
public ScalableFont(ContentXElement element, GraphicsDevice gd = null)
|
||||
public ScalableFont(ContentXElement element, uint defaultSize = 14, GraphicsDevice gd = null)
|
||||
: this(
|
||||
element.GetAttributeContentPath("file")?.Value,
|
||||
(uint)element.GetAttributeInt("size", 14),
|
||||
(uint)element.GetAttributeInt("size", (int)defaultSize),
|
||||
gd,
|
||||
element.GetAttributeBool("dynamicloading", false),
|
||||
ExtractShccFromXElement(element))
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Barotrauma
|
||||
private float prevUIScale;
|
||||
|
||||
private readonly GUIFrame channelSettingsFrame;
|
||||
private readonly GUITextBlock radioJammedWarning;
|
||||
private readonly GUITextBox channelText;
|
||||
private readonly GUILayoutGroup channelPickerContent;
|
||||
private readonly GUIButton memButton;
|
||||
@@ -107,6 +108,13 @@ namespace Barotrauma
|
||||
RelativeSpacing = 0.01f
|
||||
};
|
||||
|
||||
radioJammedWarning = new GUITextBlock(new RectTransform(Vector2.One, channelSettingsFrame.RectTransform), TextManager.Get("radiojammedwarning"),
|
||||
textColor: GUIStyle.Orange, color: Color.Black,
|
||||
textAlignment: Alignment.Center, style: "OuterGlow")
|
||||
{
|
||||
ToolTip = TextManager.Get("hint.radiojammed")
|
||||
};
|
||||
|
||||
var buttonLeft = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
|
||||
{
|
||||
PlaySoundOnSelect = false,
|
||||
@@ -649,7 +657,7 @@ namespace Barotrauma
|
||||
ToggleButton.RectTransform.AbsoluteOffset = new Point(GUIFrame.Rect.Right, GUIFrame.Rect.Y + HUDLayoutSettings.ChatBoxArea.Height - ToggleButton.Rect.Height);
|
||||
}
|
||||
|
||||
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
|
||||
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio, ignoreJamming: true))
|
||||
{
|
||||
if (prevRadio != radio)
|
||||
{
|
||||
@@ -678,10 +686,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
channelSettingsFrame.Visible = true;
|
||||
radioJammedWarning.Visible = radio is { JamTimer: > 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
channelSettingsFrame.Visible = false;
|
||||
radioJammedWarning.Visible = channelSettingsFrame.Visible = false;
|
||||
channelPickerContent.Children.First().CanBeFocused = true;
|
||||
channelMemPending = false;
|
||||
memButton.Enabled = true;
|
||||
|
||||
@@ -756,7 +756,7 @@ namespace Barotrauma
|
||||
|
||||
private bool CreateRenamingComponent(GUIButton button, object userData)
|
||||
{
|
||||
if (!HasPermission || !(userData is CharacterInfo characterInfo)) { return false; }
|
||||
if (!HasPermission || userData is not CharacterInfo characterInfo) { return false; }
|
||||
var outerGlowFrame = new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
|
||||
style: "OuterGlow", color: Color.Black * 0.7f);
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor: Anchor.Center)
|
||||
@@ -843,7 +843,7 @@ namespace Barotrauma
|
||||
|
||||
private bool FireCharacter(GUIButton button, object selection)
|
||||
{
|
||||
if (!(selection is CharacterInfo characterInfo)) { return false; }
|
||||
if (selection is not CharacterInfo characterInfo) { return false; }
|
||||
|
||||
campaign.CrewManager.FireCharacter(characterInfo);
|
||||
SelectCharacter(null, null, null);
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace Barotrauma
|
||||
return 1;
|
||||
}
|
||||
|
||||
return string.Compare(file1, file2);
|
||||
return string.Compare(file1, file2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static void InitIfNecessary()
|
||||
@@ -419,6 +419,8 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
fileList.Content.RectTransform.SortChildren(SortFiles);
|
||||
|
||||
directoryBox!.Text = currentDirectory;
|
||||
fileBox!.Text = "";
|
||||
fileList.Deselect();
|
||||
|
||||
@@ -1010,16 +1010,13 @@ namespace Barotrauma
|
||||
// Sub editor drag and highlight
|
||||
case SubEditorScreen editor:
|
||||
{
|
||||
foreach (var mapEntity in MapEntity.mapEntityList)
|
||||
if (MapEntity.StartMovingPos != Vector2.Zero || MapEntity.Resizing)
|
||||
{
|
||||
if (MapEntity.StartMovingPos != Vector2.Zero)
|
||||
{
|
||||
return CursorState.Dragging;
|
||||
}
|
||||
if (mapEntity.IsHighlighted)
|
||||
{
|
||||
return CursorState.Hand;
|
||||
}
|
||||
return CursorState.Dragging;
|
||||
}
|
||||
if (MapEntity.HighlightedEntities.Any(h => !h.IsSelected))
|
||||
{
|
||||
return CursorState.Hand;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2442,8 +2439,7 @@ namespace Barotrauma
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 0.8f), pauseMenuInner.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, padding) })
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
AbsoluteSpacing = IntScale(15)
|
||||
};
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.1f, 0.07f), pauseMenuInner.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(padding) },
|
||||
@@ -2526,7 +2522,7 @@ namespace Barotrauma
|
||||
pauseMenuInner.RectTransform.MinSize = new Point(
|
||||
pauseMenuInner.RectTransform.MinSize.X,
|
||||
Math.Max(
|
||||
(int)(buttonContainer.Children.Sum(c => c.Rect.Height + buttonContainer.Rect.Height * buttonContainer.RelativeSpacing)),
|
||||
(int)(buttonContainer.Children.Sum(c => c.Rect.Height + buttonContainer.AbsoluteSpacing) / buttonContainer.RectTransform.RelativeSize.Y),
|
||||
pauseMenuInner.RectTransform.MinSize.X));
|
||||
|
||||
}
|
||||
|
||||
@@ -781,7 +781,7 @@ namespace Barotrauma
|
||||
|
||||
if (toolTipBlock.Rect.Right > GameMain.GraphicsWidth - 10)
|
||||
{
|
||||
toolTipBlock.RectTransform.AbsoluteOffset -= new Point(toolTipBlock.Rect.Width, 0);
|
||||
toolTipBlock.RectTransform.AbsoluteOffset -= new Point(toolTipBlock.Rect.Width + targetElement.Width, 0);
|
||||
}
|
||||
if (toolTipBlock.Rect.Bottom > GameMain.GraphicsHeight - 10)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Barotrauma
|
||||
get { return lifeTime; }
|
||||
}
|
||||
|
||||
public ScalableFont Font
|
||||
public GUIFont Font
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
@@ -69,7 +69,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public GUIMessage(string text, Color color, float lifeTime, ScalableFont font = null)
|
||||
public GUIMessage(string text, Color color, float lifeTime, GUIFont font = null)
|
||||
{
|
||||
coloredText = new ColoredText(text, color, false, false);
|
||||
this.lifeTime = lifeTime;
|
||||
@@ -81,7 +81,7 @@ namespace Barotrauma
|
||||
Font = font;
|
||||
}
|
||||
|
||||
public GUIMessage(string text, Color color, Vector2 position, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, ScalableFont font = null, Submarine sub = null)
|
||||
public GUIMessage(string text, Color color, Vector2 position, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, GUIFont font = null, Submarine sub = null)
|
||||
{
|
||||
coloredText = new ColoredText(text, color, false, false);
|
||||
WorldSpace = true;
|
||||
|
||||
@@ -693,5 +693,52 @@ namespace Barotrauma
|
||||
rectT.Parent = RectTransform;
|
||||
Buttons.Add(new GUIButton(rectT, text) { OnClicked = onClick });
|
||||
}
|
||||
|
||||
public static GUIMessageBox CreateLoadingBox(LocalizedString text, (LocalizedString Label, Action<GUIMessageBox> Action)[] buttons = null, Vector2? relativeSize = null)
|
||||
{
|
||||
buttons ??= Array.Empty<(LocalizedString Label, Action<GUIMessageBox> Action)>();
|
||||
var relativeSizeFallback = relativeSize ?? (0.7f, 0.5f);
|
||||
var newMessageBox = new GUIMessageBox(
|
||||
headerText: "",
|
||||
text: "",
|
||||
relativeSize: relativeSizeFallback,
|
||||
buttons: buttons.Select(b => b.Label).ToArray());
|
||||
newMessageBox.InnerFrame.RectTransform.ScaleBasis = ScaleBasis.BothHeight;
|
||||
|
||||
for (int i = 0; i < buttons.Length; i++)
|
||||
{
|
||||
var capturedIndex = i;
|
||||
newMessageBox.Buttons[i].OnClicked = (_, _) =>
|
||||
{
|
||||
buttons[capturedIndex].Action(newMessageBox);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
const float throbberSize = 0.25f;
|
||||
|
||||
new GUITextBlock(
|
||||
new RectTransform((0.9f, 0f), newMessageBox.InnerFrame.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = (0f, -throbberSize * 0.5f) },
|
||||
text: text, textAlignment: Alignment.Center, wrap: true);
|
||||
|
||||
// Throbber
|
||||
new GUICustomComponent(
|
||||
new RectTransform(Vector2.One * throbberSize, newMessageBox.InnerFrame.RectTransform, Anchor.Center, scaleBasis: ScaleBasis.BothHeight),
|
||||
onDraw: static (sb, component) =>
|
||||
{
|
||||
GUIStyle.GenericThrobber.Draw(
|
||||
sb,
|
||||
spriteIndex: (int)(Timing.TotalTime * 20f) % GUIStyle.GenericThrobber.FrameCount,
|
||||
pos: component.Rect.Center.ToVector2(),
|
||||
color: Color.White,
|
||||
origin: GUIStyle.GenericThrobber.FrameSize.ToVector2() * 0.5f,
|
||||
rotate: 0f,
|
||||
scale: component.Rect.Size.ToVector2() / GUIStyle.GenericThrobber.FrameSize.ToVector2());
|
||||
});
|
||||
|
||||
MessageBoxes.Remove(newMessageBox);
|
||||
MessageBoxes.Insert(0, newMessageBox);
|
||||
return newMessageBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Barotrauma
|
||||
if (subElement.NameAsIdentifier() != "override") { continue; }
|
||||
if (ScalableFont.ExtractShccFromXElement(subElement).HasFlag(flag))
|
||||
{
|
||||
return new ScalableFont(subElement, GameMain.Instance.GraphicsDevice);
|
||||
return new ScalableFont(subElement, font.Size, GameMain.Instance.GraphicsDevice);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUIScissorComponent: GUIComponent
|
||||
public sealed class GUIScissorComponent : GUIComponent
|
||||
{
|
||||
public GUIComponent Content;
|
||||
|
||||
@@ -14,19 +14,14 @@ namespace Barotrauma
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
rectT.ChildrenChanged += CheckForChildren;
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
private void CheckForChildren(RectTransform rectT)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
foreach (GUIComponent child in Children)
|
||||
{
|
||||
if (child == Content) { continue; }
|
||||
throw new InvalidOperationException($"Children were found in {nameof(GUIScissorComponent)}, Add them to {nameof(GUIScissorComponent)}.{nameof(Content)} instead.");
|
||||
}
|
||||
|
||||
ClampChildMouseRects(Content);
|
||||
if (rectT == Content.RectTransform) { return; }
|
||||
throw new InvalidOperationException($"Children were found in {nameof(GUIScissorComponent)}, Add them to {nameof(GUIScissorComponent)}.{nameof(Content)} instead.");
|
||||
}
|
||||
|
||||
public override void DrawChildren(SpriteBatch spriteBatch, bool recursive)
|
||||
@@ -57,7 +52,13 @@ namespace Barotrauma
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: prevRasterizerState);
|
||||
}
|
||||
|
||||
private void ClampChildMouseRects(GUIComponent child)
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
ClampChildMouseRects(Content);
|
||||
}
|
||||
|
||||
private static void ClampChildMouseRects(GUIComponent child)
|
||||
{
|
||||
child.ClampMouseRectToParent = true;
|
||||
|
||||
|
||||
@@ -142,11 +142,13 @@ namespace Barotrauma
|
||||
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh");
|
||||
public readonly static GUIColor HealthBarColorPoisoned = new GUIColor("HealthBarColorPoisoned");
|
||||
|
||||
private readonly static Point defaultItemFrameMargin = new Point(50, 56);
|
||||
|
||||
public static Point ItemFrameMargin
|
||||
{
|
||||
get
|
||||
{
|
||||
Point size = new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
|
||||
Point size = defaultItemFrameMargin.Multiply(GUI.SlicedSpriteScale);
|
||||
|
||||
var style = GetComponentStyle("ItemUI");
|
||||
var sprite = style?.Sprites[GUIComponent.ComponentState.None].First();
|
||||
@@ -159,6 +161,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static int ItemFrameTopBarHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
var style = GetComponentStyle("ItemUI");
|
||||
var sprite = style?.Sprites[GUIComponent.ComponentState.None].First();
|
||||
return (int)Math.Min(sprite?.Slices[0].Height ?? 0, defaultItemFrameMargin.Y / 2 * GUI.SlicedSpriteScale);
|
||||
}
|
||||
}
|
||||
|
||||
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
|
||||
|
||||
public static GUIComponentStyle GetComponentStyle(string styleName)
|
||||
|
||||
@@ -12,8 +12,6 @@ namespace Barotrauma
|
||||
public delegate bool OnSelectedHandler(GUITickBox obj);
|
||||
public OnSelectedHandler OnSelected;
|
||||
|
||||
public static int size = 20;
|
||||
|
||||
private GUIRadioButtonGroup radioButtonGroup;
|
||||
|
||||
public override bool Selected
|
||||
@@ -21,21 +19,7 @@ namespace Barotrauma
|
||||
get { return isSelected; }
|
||||
set
|
||||
{
|
||||
if (value == isSelected) { return; }
|
||||
if (radioButtonGroup != null && radioButtonGroup.SelectedRadioButton == this)
|
||||
{
|
||||
isSelected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
isSelected = value;
|
||||
State = isSelected ? ComponentState.Selected : ComponentState.None;
|
||||
if (value && radioButtonGroup != null)
|
||||
{
|
||||
radioButtonGroup.SelectRadioButton(this);
|
||||
}
|
||||
|
||||
OnSelected?.Invoke(this);
|
||||
SetSelected(value, callOnSelected: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +170,27 @@ namespace Barotrauma
|
||||
text.SetTextPos();
|
||||
ContentWidth = box.Rect.Width + text.Padding.X + text.TextSize.X + text.Padding.Z;
|
||||
}
|
||||
|
||||
public void SetSelected(bool selected, bool callOnSelected = true)
|
||||
{
|
||||
if (selected == isSelected) { return; }
|
||||
if (radioButtonGroup != null && radioButtonGroup.SelectedRadioButton == this)
|
||||
{
|
||||
isSelected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
isSelected = selected;
|
||||
State = isSelected ? ComponentState.Selected : ComponentState.None;
|
||||
if (selected && radioButtonGroup != null)
|
||||
{
|
||||
radioButtonGroup.SelectRadioButton(this);
|
||||
}
|
||||
if (callOnSelected)
|
||||
{
|
||||
OnSelected?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
|
||||
@@ -4,12 +4,13 @@ using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class LoadingScreen
|
||||
sealed class LoadingScreen
|
||||
{
|
||||
private readonly Sprite defaultBackgroundTexture, overlay;
|
||||
private readonly SpriteSheet decorativeGraph, decorativeMap;
|
||||
@@ -37,36 +38,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<PendingSplashScreen> pendingSplashScreens = new Queue<PendingSplashScreen>();
|
||||
/// <summary>
|
||||
/// Triplet.first = filepath, Triplet.second = resolution, Triplet.third = audio gain
|
||||
/// </summary>
|
||||
public Queue<PendingSplashScreen> PendingSplashScreens
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
return pendingSplashScreens;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
pendingSplashScreens = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
public readonly ConcurrentQueue<PendingSplashScreen> PendingSplashScreens = new ConcurrentQueue<PendingSplashScreen>();
|
||||
|
||||
public bool PlayingSplashScreen
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
return currSplashScreen != null || pendingSplashScreens.Count > 0;
|
||||
}
|
||||
return currSplashScreen != null || PendingSplashScreens.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,33 +57,7 @@ namespace Barotrauma
|
||||
selectedTip = RichString.Rich(tip);
|
||||
}
|
||||
|
||||
private readonly object loadMutex = new object();
|
||||
private float? loadState;
|
||||
|
||||
public float? LoadState
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
return loadState;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
loadState = value;
|
||||
DrawLoadingText = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool DrawLoadingText
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public float LoadState;
|
||||
|
||||
public bool WaitForLanguageSelection
|
||||
{
|
||||
@@ -121,7 +76,7 @@ namespace Barotrauma
|
||||
|
||||
overlay = new Sprite("Content/UI/MainMenuVignette.png", Vector2.Zero);
|
||||
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
|
||||
DrawLoadingText = true;
|
||||
|
||||
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
|
||||
}
|
||||
|
||||
@@ -170,10 +125,11 @@ namespace Barotrauma
|
||||
{
|
||||
DrawLanguageSelectionPrompt(spriteBatch, graphics);
|
||||
}
|
||||
else if (DrawLoadingText)
|
||||
else
|
||||
{
|
||||
LocalizedString loadText;
|
||||
if (LoadState == 100.0f)
|
||||
var loadState = LoadState; // avoid multiple reads here to prevent jank
|
||||
if (loadState >= 100.0f)
|
||||
{
|
||||
#if DEBUG
|
||||
if (GameSettings.CurrentConfig.AutomaticQuickStartEnabled || GameSettings.CurrentConfig.AutomaticCampaignLoadEnabled || (GameSettings.CurrentConfig.TestScreenEnabled && GameMain.FirstLoad))
|
||||
@@ -191,17 +147,17 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
loadText = TextManager.Get("Loading");
|
||||
if (LoadState != null)
|
||||
if (loadState >= 0f)
|
||||
{
|
||||
loadText += " " + (int)LoadState + " %";
|
||||
loadText += $" {loadState:N0} %";
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (GameMain.FirstLoad && GameMain.CancelQuickStart)
|
||||
{
|
||||
loadText += " (Quickstart aborted)";
|
||||
}
|
||||
#endif
|
||||
if (GameMain.FirstLoad && GameMain.CancelQuickStart)
|
||||
{
|
||||
loadText += " (Quickstart aborted)";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (GUIStyle.LargeFont.HasValue)
|
||||
@@ -343,11 +299,9 @@ namespace Barotrauma
|
||||
|
||||
private void DrawSplashScreen(SpriteBatch spriteBatch, GraphicsDevice graphics)
|
||||
{
|
||||
if (currSplashScreen == null && PendingSplashScreens.Count == 0) { return; }
|
||||
|
||||
if (currSplashScreen == null)
|
||||
{
|
||||
var newSplashScreen = PendingSplashScreens.Dequeue();
|
||||
if (!PendingSplashScreens.TryDequeue(out var newSplashScreen)) { return; }
|
||||
string fileName = newSplashScreen.Filename;
|
||||
try
|
||||
{
|
||||
@@ -362,10 +316,10 @@ namespace Barotrauma
|
||||
PendingSplashScreens.Clear();
|
||||
currSplashScreen = null;
|
||||
}
|
||||
|
||||
if (currSplashScreen == null) { return; }
|
||||
}
|
||||
|
||||
if (currSplashScreen == null) { return; }
|
||||
|
||||
if (currSplashScreen.IsPlaying)
|
||||
{
|
||||
graphics.Clear(Color.Black);
|
||||
@@ -425,7 +379,7 @@ namespace Barotrauma
|
||||
public IEnumerable<CoroutineStatus> DoLoading(IEnumerable<CoroutineStatus> loader)
|
||||
{
|
||||
drawn = false;
|
||||
LoadState = null;
|
||||
LoadState = -1f;
|
||||
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
|
||||
currentBackgroundTexture = LocationType.Prefabs.Where(p => p.UsePortraitInRandomLoadingScreens).GetRandomUnsynced()?.GetPortrait(Rand.Int(int.MaxValue));
|
||||
if (GameMain.GameSession?.GameMode?.Missions is { } missions && missions.Any(m => m.Prefab.HasPortraits))
|
||||
|
||||
@@ -946,7 +946,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (truncated)
|
||||
{
|
||||
descriptionBlock.Text += "...";
|
||||
descriptionBlock.Text += TextManager.Get("ellipsis");
|
||||
}
|
||||
|
||||
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.25f), bottomTextLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
@@ -306,6 +306,8 @@ namespace Barotrauma
|
||||
{
|
||||
_scaleBasis = value;
|
||||
RecalculateAbsoluteSize();
|
||||
RecalculateAnchorPoint();
|
||||
RecalculatePivotOffset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ namespace Barotrauma
|
||||
var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
|
||||
var scale = new Vector2(length, thickness);
|
||||
Vector2 middle = new Vector2((point1.X + point2.X) / 2f, (point1.Y + point2.Y) / 2f);
|
||||
Texture2D tex = GetTexture(spriteBatch);
|
||||
spriteBatch.Draw(GetTexture(spriteBatch), middle, null, color, angle, new Vector2(tex.Width / 2f, tex.Height / 2f), scale, SpriteEffects.None, 0);
|
||||
Texture2D tex = GUI.WhiteTexture;
|
||||
spriteBatch.Draw(tex, middle, null, color, angle, new Vector2(tex.Width / 2f, tex.Height / 2f), scale, SpriteEffects.None, 0);
|
||||
}
|
||||
|
||||
private static void DrawPolygonEdge(SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness)
|
||||
@@ -112,6 +112,18 @@ namespace Barotrauma
|
||||
DrawLine(spriteBatch, new Vector2(x1, y1), new Vector2(x2, y2), color, thickness);
|
||||
}
|
||||
|
||||
public static void DrawLineWithTexture(this SpriteBatch spriteBatch, Texture2D tex, Vector2 point1, Vector2 point2,
|
||||
Color color, float thickness = 1f)
|
||||
{
|
||||
// calculate the distance between the two vectors
|
||||
var distance = Vector2.Distance(point1, point2);
|
||||
|
||||
// calculate the angle between the two vectors
|
||||
var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
|
||||
|
||||
DrawLine(spriteBatch, tex, point1, distance, angle, color, thickness);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a line from point1 to point2 with an offset
|
||||
/// </summary>
|
||||
@@ -124,18 +136,18 @@ namespace Barotrauma
|
||||
// calculate the angle between the two vectors
|
||||
var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
|
||||
|
||||
DrawLine(spriteBatch, point1, distance, angle, color, thickness);
|
||||
DrawLine(spriteBatch, GetTexture(spriteBatch), point1, distance, angle, color, thickness);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a line from point1 to point2 with an offset
|
||||
/// </summary>
|
||||
public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point, float length, float angle, Color color,
|
||||
public static void DrawLine(this SpriteBatch spriteBatch, Texture2D tex, Vector2 point, float length, float angle, Color color,
|
||||
float thickness = 1f)
|
||||
{
|
||||
var origin = new Vector2(0f, 0.5f);
|
||||
var scale = new Vector2(length, thickness);
|
||||
spriteBatch.Draw(GetTexture(spriteBatch), point, null, color, angle, origin, scale, SpriteEffects.None, 0);
|
||||
var origin = new Vector2(0f, tex.Height / 2f);
|
||||
var scale = new Vector2(length / tex.Width, thickness / tex.Height);
|
||||
spriteBatch.Draw(tex, point, null, color, angle, origin, scale, SpriteEffects.None, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma
|
||||
private static UISprite spectateIcon, disconnectedIcon;
|
||||
private static Sprite ownerIcon, moderatorIcon;
|
||||
|
||||
public enum InfoFrameTab { Crew, Mission, Reputation, Traitor, Submarine, Talents };
|
||||
public enum InfoFrameTab { Crew, Mission, Reputation, Submarine, Talents };
|
||||
public static InfoFrameTab SelectedTab { get; private set; }
|
||||
private GUIFrame infoFrame, contentFrame;
|
||||
|
||||
@@ -299,9 +299,15 @@ namespace Barotrauma
|
||||
|
||||
var crewButton = createTabButton(InfoFrameTab.Crew, "crew");
|
||||
|
||||
if (!(GameMain.GameSession?.GameMode is TestGameMode))
|
||||
if (GameMain.GameSession?.GameMode is not TestGameMode)
|
||||
{
|
||||
createTabButton(InfoFrameTab.Mission, "mission");
|
||||
var missionBtn = createTabButton(InfoFrameTab.Mission, "mission");
|
||||
eventLogNotification = GameSession.CreateNotificationIcon(missionBtn);
|
||||
eventLogNotification.Visible = GameMain.GameSession.EventManager?.EventLog?.UnreadEntries ?? false;
|
||||
if (eventLogNotification.Visible)
|
||||
{
|
||||
eventLogNotification.Pulsate(Vector2.One, Vector2.One * 2, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
|
||||
@@ -340,14 +346,6 @@ namespace Barotrauma
|
||||
text.Text = TextManager.GetWithVariable("bankbalanceformat", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", balance));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
|
||||
if (isTraitor && GameMain.Client.TraitorMission != null)
|
||||
{
|
||||
var traitorButton = createTabButton(InfoFrameTab.Traitor, "tabmenu.traitor");
|
||||
}
|
||||
}
|
||||
|
||||
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
|
||||
|
||||
@@ -361,7 +359,7 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
talentPointNotification = GameSession.CreateTalentIconNotification(talentsButton);
|
||||
talentPointNotification = GameSession.CreateNotificationIcon(talentsButton);
|
||||
}
|
||||
|
||||
public void SelectInfoFrameTab(InfoFrameTab selectedTab)
|
||||
@@ -387,12 +385,6 @@ namespace Barotrauma
|
||||
GameMain.GameSession.RoundSummary.CreateReputationInfoPanel(reputationFrame, campaignMode);
|
||||
}
|
||||
break;
|
||||
case InfoFrameTab.Traitor:
|
||||
TraitorMissionPrefab traitorMission = GameMain.Client?.TraitorMission;
|
||||
Character traitor = GameMain.Client?.Character;
|
||||
if (traitor == null || traitorMission == null) { return; }
|
||||
CreateTraitorInfo(infoFrameHolder, traitorMission, traitor);
|
||||
break;
|
||||
case InfoFrameTab.Submarine:
|
||||
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
|
||||
break;
|
||||
@@ -1539,96 +1531,44 @@ namespace Barotrauma
|
||||
|
||||
int locationInfoYOffset = locationInfoContainer.Rect.Height + padding * 2;
|
||||
|
||||
|
||||
GUIListBox missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrameContent.Rect.Height - locationInfoYOffset), missionFrameContent.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
missionList.ContentBackground.Color = Color.Transparent;
|
||||
missionList.Spacing = GUI.IntScale(15);
|
||||
|
||||
if (GameMain.GameSession?.Missions != null)
|
||||
{
|
||||
int spacing = GUI.IntScale(5);
|
||||
int iconSize = (int)(GUIStyle.LargeFont.MeasureChar('T').Y + GUIStyle.Font.MeasureChar('T').Y * 4 + spacing * 4);
|
||||
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
if (!mission.Prefab.ShowInMenus) { continue; }
|
||||
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(iconSize + spacing, 0) }, false, childAnchor: Anchor.TopLeft)
|
||||
|
||||
var textContent = new List<LocalizedString>()
|
||||
{
|
||||
AbsoluteSpacing = spacing
|
||||
mission.GetMissionRewardText(Submarine.MainSub),
|
||||
mission.GetReputationRewardText(),
|
||||
mission.Description
|
||||
};
|
||||
LocalizedString descriptionText = mission.Description;
|
||||
foreach (LocalizedString missionMessage in mission.ShownMessages)
|
||||
textContent.AddRange(mission.ShownMessages);
|
||||
|
||||
RoundSummary.CreateMissionEntry(
|
||||
missionList.Content,
|
||||
mission.Name,
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
out GUIImage missionIcon);
|
||||
if (missionIcon != null)
|
||||
{
|
||||
descriptionText += "\n\n" + missionMessage;
|
||||
}
|
||||
RichString rewardText = mission.GetMissionRewardText(Submarine.MainSub);
|
||||
RichString reputationText = mission.GetReputationRewardText();
|
||||
UpdateMissionStateIcon();
|
||||
mission.OnMissionStateChanged += (mission) => UpdateMissionStateIcon();
|
||||
|
||||
Func<string, string> wrapMissionText(GUIFont font)
|
||||
{
|
||||
return (str) => ToolBox.WrapText(str, missionTextGroup.Rect.Width, font.Value);
|
||||
}
|
||||
RichString missionNameString = RichString.Rich(mission.Name, wrapMissionText(GUIStyle.LargeFont));
|
||||
RichString missionRewardString = RichString.Rich(rewardText, wrapMissionText(GUIStyle.Font));
|
||||
RichString missionReputationString = RichString.Rich(reputationText, wrapMissionText(GUIStyle.Font));
|
||||
RichString missionDescriptionString = RichString.Rich(descriptionText, wrapMissionText(GUIStyle.Font));
|
||||
|
||||
Vector2 missionNameSize = GUIStyle.LargeFont.MeasureString(missionNameString.SanitizedValue);
|
||||
Vector2 missionDescriptionSize = GUIStyle.Font.MeasureString(missionDescriptionString.SanitizedValue);
|
||||
Vector2 missionRewardSize = GUIStyle.Font.MeasureString(missionRewardString.SanitizedValue);
|
||||
Vector2 missionReputationSize = GUIStyle.Font.MeasureString(missionReputationString.SanitizedValue);
|
||||
|
||||
float ySize = missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y + missionReputationSize.Y + missionTextGroup.AbsoluteSpacing * 4;
|
||||
bool displayDifficulty = mission.Difficulty.HasValue;
|
||||
if (displayDifficulty) { ySize += missionRewardSize.Y; }
|
||||
|
||||
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)ySize);
|
||||
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
|
||||
|
||||
if (mission.Prefab.Icon != null)
|
||||
{
|
||||
/*float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
|
||||
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
|
||||
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
|
||||
Point iconSize = new Point(iconWidth, iconHeight);*/
|
||||
|
||||
var icon = new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
|
||||
void UpdateMissionStateIcon()
|
||||
{
|
||||
Color = mission.Prefab.IconColor,
|
||||
HoverColor = mission.Prefab.IconColor,
|
||||
SelectedColor = mission.Prefab.IconColor,
|
||||
CanBeFocused = false
|
||||
};
|
||||
UpdateMissionStateIcon(mission, icon);
|
||||
mission.OnMissionStateChanged += (mission) => UpdateMissionStateIcon(mission, icon);
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUIStyle.LargeFont);
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (displayDifficulty)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Point(missionTextGroup.Rect.Width, (int)missionRewardSize.Y), parent: missionTextGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1
|
||||
};
|
||||
var difficultyColor = mission.GetDifficultyColor();
|
||||
for (int i = 0; i < mission.Difficulty.Value; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), "DifficultyIndicator", scaleToFit: true)
|
||||
if (mission.DisplayAsCompleted || mission.DisplayAsFailed)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = difficultyColor
|
||||
};
|
||||
RoundSummary.UpdateMissionStateIcon(mission.DisplayAsCompleted, missionIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
var rewardTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString);
|
||||
if (difficultyIndicatorGroup != null)
|
||||
{
|
||||
difficultyIndicatorGroup.RectTransform.Resize(new Point((int)(difficultyIndicatorGroup.Rect.Width - rewardTextBlock.Padding.X - rewardTextBlock.Padding.Z), difficultyIndicatorGroup.Rect.Height));
|
||||
difficultyIndicatorGroup.RectTransform.AbsoluteOffset = new Point((int)rewardTextBlock.Padding.X, 0);
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionReputationString);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1636,66 +1576,14 @@ namespace Barotrauma
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0f), missionList.RectTransform, Anchor.CenterLeft), false, childAnchor: Anchor.TopLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), TextManager.Get("NoMission"), font: GUIStyle.LargeFont);
|
||||
}
|
||||
|
||||
GameMain.GameSession?.EventManager?.EventLog?.CreateEventLogUI(missionList.Content);
|
||||
GameMain.GameSession.EnableEventLogNotificationIcon(enabled: false);
|
||||
|
||||
RoundSummary.AddSeparators(missionList.Content);
|
||||
}
|
||||
|
||||
private void UpdateMissionStateIcon(Mission mission, GUIImage missionIcon)
|
||||
{
|
||||
if (mission == null || missionIcon == null) { return; }
|
||||
string style = string.Empty;
|
||||
if (mission.DisplayAsFailed)
|
||||
{
|
||||
style = "MissionFailedIcon";
|
||||
}
|
||||
else if (mission.DisplayAsCompleted)
|
||||
{
|
||||
style = "MissionCompletedIcon";
|
||||
}
|
||||
GUIImage stateIcon = missionIcon.GetChild<GUIImage>();
|
||||
if (string.IsNullOrEmpty(style))
|
||||
{
|
||||
if (stateIcon != null)
|
||||
{
|
||||
stateIcon.Visible = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stateIcon ??= new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), style, scaleToFit: true);
|
||||
stateIcon.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTraitorInfo(GUIFrame infoFrame, TraitorMissionPrefab traitorMission, Character traitor)
|
||||
{
|
||||
GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
|
||||
int padding = (int)(0.0245f * missionFrame.Rect.Height);
|
||||
|
||||
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(new Point(missionFrame.Rect.Width - padding * 2, 0), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, padding) }, style: null);
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.65f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.319f, 0f) }, false, childAnchor: Anchor.TopLeft);
|
||||
|
||||
LocalizedString missionNameString = ToolBox.WrapText(TextManager.Get("tabmenu.traitor"), missionTextGroup.Rect.Width, GUIStyle.LargeFont);
|
||||
LocalizedString missionDescriptionString = ToolBox.WrapText(traitor.TraitorCurrentObjective, missionTextGroup.Rect.Width, GUIStyle.Font);
|
||||
|
||||
Vector2 missionNameSize = GUIStyle.LargeFont.MeasureString(missionNameString);
|
||||
Vector2 missionDescriptionSize = GUIStyle.Font.MeasureString(missionDescriptionString);
|
||||
|
||||
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)(missionNameSize.Y + missionDescriptionSize.Y));
|
||||
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
|
||||
|
||||
float aspectRatio = traitorMission.Icon.SourceRect.Width / traitorMission.Icon.SourceRect.Height;
|
||||
|
||||
int iconWidth = (int)(0.319f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
|
||||
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * aspectRatio));
|
||||
Point iconSize = new Point(iconWidth, iconHeight);
|
||||
|
||||
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), traitorMission.Icon, null, true) { Color = traitorMission.IconColor };
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUIStyle.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);
|
||||
}
|
||||
|
||||
private void CreateSubmarineInfo(GUIFrame infoFrame, Submarine sub)
|
||||
private static void CreateSubmarineInfo(GUIFrame infoFrame, Submarine sub)
|
||||
{
|
||||
GUIFrame subInfoFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(Vector2.One * 0.97f, subInfoFrame.RectTransform, Anchor.Center), style: null);
|
||||
@@ -1793,7 +1681,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private GUIImage talentPointNotification;
|
||||
private GUIImage talentPointNotification, eventLogNotification;
|
||||
|
||||
public static void CreateSkillList(Character character, CharacterInfo info, GUIListBox parent)
|
||||
{
|
||||
|
||||
@@ -117,6 +117,9 @@ namespace Barotrauma
|
||||
return MathHelper.Clamp(Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale), minBorderScale, maxBorderScale);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, RectangleF rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None, Vector2? uvOffset = null)
|
||||
=> Draw(spriteBatch, new Rectangle(rect.Location.ToPoint(), rect.Size.ToPoint()), color, spriteEffects, uvOffset);
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None, Vector2? uvOffset = null)
|
||||
{
|
||||
uvOffset ??= Vector2.Zero;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (consentTextAvailable)
|
||||
{
|
||||
var background = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker");
|
||||
var background = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas), style: "GUIBackgroundBlocker");
|
||||
var frame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.7f), background.RectTransform, Anchor.Center) { MinSize = new Point(800, 0), MaxSize = new Point(1500, int.MaxValue) });
|
||||
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), frame.RectTransform, Anchor.Center))
|
||||
@@ -55,7 +55,8 @@ namespace Barotrauma
|
||||
yesBtn.OnClicked += (btn, userdata) =>
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(background);
|
||||
SetConsentInternal(Consent.Yes);
|
||||
var loadingBox = GUIMessageBox.CreateLoadingBox(TextManager.Get("PleaseWait"));
|
||||
SetConsentInternal(Consent.Yes, onAnswerSent: loadingBox.Close);
|
||||
return true;
|
||||
};
|
||||
yesBtn.Enabled = false;
|
||||
@@ -76,7 +77,8 @@ namespace Barotrauma
|
||||
noBtn.OnClicked += (btn, userdata) =>
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(background);
|
||||
SetConsent(Consent.No);
|
||||
var loadingBox = GUIMessageBox.CreateLoadingBox(TextManager.Get("PleaseWait"));
|
||||
SetConsent(Consent.No, onAnswerSent: loadingBox.Close);
|
||||
return true;
|
||||
};
|
||||
noBtn.Enabled = false;
|
||||
|
||||
@@ -112,7 +112,8 @@ namespace Barotrauma
|
||||
public static LoadingScreen TitleScreen;
|
||||
private bool loadingScreenOpen;
|
||||
|
||||
private CoroutineHandle loadingCoroutine;
|
||||
private Thread initialLoadingThread;
|
||||
|
||||
public bool HasLoaded { get; private set; }
|
||||
|
||||
private readonly GameTime fixedTime;
|
||||
@@ -414,24 +415,21 @@ namespace Barotrauma
|
||||
WaitForLanguageSelection = GameSettings.CurrentConfig.Language == LanguageIdentifier.None
|
||||
};
|
||||
|
||||
bool canLoadInSeparateThread = true;
|
||||
|
||||
loadingCoroutine = CoroutineManager.StartCoroutine(Load(canLoadInSeparateThread), "Load", canLoadInSeparateThread);
|
||||
initialLoadingThread = new Thread(Load);
|
||||
initialLoadingThread.Start();
|
||||
}
|
||||
|
||||
public class LoadingException : Exception
|
||||
private void Load()
|
||||
{
|
||||
public LoadingException(Exception e) : base("Loading was interrupted due to an error.", innerException: e)
|
||||
static void log(string str)
|
||||
{
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage(str, Color.Lime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> Load(bool isSeparateThread)
|
||||
{
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("LOADING COROUTINE", Color.Lime);
|
||||
}
|
||||
|
||||
log("LOADING COROUTINE");
|
||||
|
||||
ContentPackageManager.LoadVanillaFileList();
|
||||
|
||||
@@ -441,7 +439,7 @@ namespace Barotrauma
|
||||
TitleScreen.AvailableLanguages = TextManager.AvailableLanguages.OrderBy(l => l.Value != "english".ToIdentifier()).ThenBy(l => l.Value).ToArray();
|
||||
while (TitleScreen.WaitForLanguageSelection)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
Thread.Sleep((int)(Timing.Step * 1000));
|
||||
}
|
||||
ContentPackageManager.VanillaCorePackage.UnloadFilesOfType<TextFile>();
|
||||
}
|
||||
@@ -453,25 +451,13 @@ namespace Barotrauma
|
||||
{
|
||||
var pendingSplashScreens = TitleScreen.PendingSplashScreens;
|
||||
float baseVolume = MathHelper.Clamp(GameSettings.CurrentConfig.Audio.SoundVolume * 2.0f, 0.0f, 1.0f);
|
||||
pendingSplashScreens?.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_UTG.webm", baseVolume * 0.5f));
|
||||
pendingSplashScreens?.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_FF.webm", baseVolume));
|
||||
pendingSplashScreens?.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_Daedalic.webm", baseVolume * 0.1f));
|
||||
}
|
||||
|
||||
//if not loading in a separate thread, wait for the splash screens to finish before continuing the loading
|
||||
//otherwise the videos will look extremely choppy
|
||||
if (!isSeparateThread)
|
||||
{
|
||||
while (TitleScreen.PlayingSplashScreen || TitleScreen.PendingSplashScreens.Count > 0)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
pendingSplashScreens.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_UTG.webm", baseVolume * 0.5f));
|
||||
pendingSplashScreens.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_FF.webm", baseVolume));
|
||||
pendingSplashScreens.Enqueue(new LoadingScreen.PendingSplashScreen("Content/SplashScreens/Splash_Daedalic.webm", baseVolume * 0.1f));
|
||||
}
|
||||
|
||||
GUI.Init();
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
LegacySteamUgcTransition.Prepare();
|
||||
var contentPackageLoadRoutine = ContentPackageManager.Init();
|
||||
foreach (var progress in contentPackageLoadRoutine
|
||||
@@ -479,7 +465,6 @@ namespace Barotrauma
|
||||
{
|
||||
const float min = 1f, max = 70f;
|
||||
TitleScreen.LoadState = MathHelper.Lerp(min, max, progress);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
var corePackage = ContentPackageManager.EnabledPackages.Core;
|
||||
@@ -508,7 +493,6 @@ namespace Barotrauma
|
||||
TaskPool.Add("InitRelayNetworkAccess", SteamManager.InitRelayNetworkAccess(), (t) => { });
|
||||
|
||||
HintManager.Init();
|
||||
yield return CoroutineStatus.Running;
|
||||
CoreEntityPrefab.InitCorePrefabs();
|
||||
GameModePreset.Init();
|
||||
|
||||
@@ -516,7 +500,6 @@ namespace Barotrauma
|
||||
SubmarineInfo.RefreshSavedSubs();
|
||||
|
||||
TitleScreen.LoadState = 75.0f;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
GameScreen = new GameScreen(GraphicsDeviceManager.GraphicsDevice);
|
||||
|
||||
@@ -524,13 +507,11 @@ namespace Barotrauma
|
||||
LightManager = new Lights.LightManager(base.GraphicsDevice);
|
||||
|
||||
TitleScreen.LoadState = 80.0f;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
MainMenuScreen = new MainMenuScreen(this);
|
||||
ServerListScreen = new ServerListScreen();
|
||||
|
||||
TitleScreen.LoadState = 85.0f;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
#if USE_STEAM
|
||||
if (SteamManager.IsInitialized)
|
||||
@@ -556,12 +537,10 @@ namespace Barotrauma
|
||||
TestScreen = new TestScreen();
|
||||
|
||||
TitleScreen.LoadState = 90.0f;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
ParticleEditorScreen = new ParticleEditorScreen();
|
||||
|
||||
TitleScreen.LoadState = 95.0f;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
LevelEditorScreen = new LevelEditorScreen();
|
||||
SpriteEditorScreen = new SpriteEditorScreen();
|
||||
@@ -569,8 +548,6 @@ namespace Barotrauma
|
||||
CharacterEditorScreen = new CharacterEditor.CharacterEditorScreen();
|
||||
CampaignEndScreen = new CampaignEndScreen();
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
#if DEBUG
|
||||
LevelGenerationParams.CheckValidity();
|
||||
#endif
|
||||
@@ -586,17 +563,10 @@ namespace Barotrauma
|
||||
|
||||
TitleScreen.LoadState = 100.0f;
|
||||
HasLoaded = true;
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
DebugConsole.NewMessage("LOADING COROUTINE FINISHED", Color.Lime);
|
||||
}
|
||||
|
||||
log("LOADING COROUTINE FINISHED");
|
||||
#if CLIENT
|
||||
LuaCsInstaller.CheckUpdate();
|
||||
#endif
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -749,11 +719,6 @@ namespace Barotrauma
|
||||
#endif
|
||||
|
||||
Client?.Update((float)Timing.Step);
|
||||
|
||||
if (!HasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine))
|
||||
{
|
||||
throw new LoadingException(loadingCoroutine.Exception);
|
||||
}
|
||||
}
|
||||
else if (HasLoaded)
|
||||
{
|
||||
@@ -1188,7 +1153,7 @@ namespace Barotrauma
|
||||
{
|
||||
waitForKeyHit = waitKeyHit;
|
||||
loadingScreenOpen = true;
|
||||
TitleScreen.LoadState = null;
|
||||
TitleScreen.LoadState = 0f;
|
||||
return CoroutineManager.StartCoroutine(TitleScreen.DoLoading(loader));
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace Barotrauma
|
||||
private bool _isCrewMenuOpen = true;
|
||||
private Point crewListEntrySize;
|
||||
|
||||
private readonly List<GUITickBox> traitorButtons = new List<GUITickBox>();
|
||||
|
||||
/// <summary>
|
||||
/// Present only in single player games. In multiplayer. The chatbox is found from GameSession.Client.
|
||||
/// </summary>
|
||||
@@ -194,7 +196,7 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
var reports = OrderPrefab.Prefabs.Where(o => o.IsReport && o.SymbolSprite != null && !o.Hidden).OrderBy(o => o.Identifier).ToArray();
|
||||
var reports = OrderPrefab.Prefabs.Where(o => o.IsVisibleAsReportButton).OrderBy(o => o.Identifier).ToArray();
|
||||
if (reports.None())
|
||||
{
|
||||
DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined.");
|
||||
@@ -226,7 +228,8 @@ namespace Barotrauma
|
||||
//report buttons
|
||||
foreach (OrderPrefab orderPrefab in reports)
|
||||
{
|
||||
if (!orderPrefab.IsReport || orderPrefab.SymbolSprite == null || orderPrefab.Hidden) { continue; }
|
||||
if (!orderPrefab.IsVisibleAsReportButton) { continue; }
|
||||
|
||||
var btn = new GUIButton(new RectTransform(Vector2.One, parent.RectTransform, scaleBasis: isHorizontal ? ScaleBasis.BothHeight : ScaleBasis.BothWidth), style: null)
|
||||
{
|
||||
OnClicked = (button, userData) =>
|
||||
@@ -367,7 +370,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
int iconsVisible = isJobIconVisible ? 5 : 4;
|
||||
int iconsVisible = isJobIconVisible ? 6 : 5;
|
||||
var nameRelativeWidth = 1.0f
|
||||
// Start padding
|
||||
- paddingRelativeWidth
|
||||
@@ -413,7 +416,6 @@ namespace Barotrauma
|
||||
{
|
||||
AllowMouseWheelScroll = false,
|
||||
CurrentDragMode = GUIListBox.DragMode.DragWithinBox,
|
||||
HideChildrenOutsideFrame = false,
|
||||
KeepSpaceForScrollBar = false,
|
||||
OnRearranged = OnOrdersRearranged,
|
||||
ScrollBarVisible = false,
|
||||
@@ -439,13 +441,13 @@ namespace Barotrauma
|
||||
Stretch = false
|
||||
};
|
||||
|
||||
var extraIconFrame = new GUIFrame(new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), style: null)
|
||||
var extraIconFrame = new GUIFrame(new RectTransform(new Vector2(0.8f * iconRelativeWidth * 2, 0.8f), layoutGroup.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = "extraicons"
|
||||
};
|
||||
|
||||
var soundIconParent = new GUIFrame(new RectTransform(Vector2.One, extraIconFrame.RectTransform), style: null)
|
||||
var soundIconParent = new GUIFrame(new RectTransform(new Vector2(0.8f), extraIconFrame.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest), style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = "soundicons",
|
||||
@@ -470,16 +472,6 @@ namespace Barotrauma
|
||||
Visible = false
|
||||
};
|
||||
|
||||
if (character.IsBot)
|
||||
{
|
||||
new GUIFrame(new RectTransform(Vector2.One, extraIconFrame.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = "objectiveicon",
|
||||
Visible = false
|
||||
};
|
||||
}
|
||||
|
||||
new GUIButton(new RectTransform(new Point((int)commandButtonAbsoluteHeight), background.RectTransform), style: "CrewListCommandButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("inputtype.command"),
|
||||
@@ -490,6 +482,44 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
if (character.IsBot)
|
||||
{
|
||||
new GUIFrame(new RectTransform(Vector2.One, extraIconFrame.RectTransform, scaleBasis: ScaleBasis.Smallest), style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = "objectiveicon",
|
||||
Visible = false
|
||||
};
|
||||
}
|
||||
else if (GameMain.GameSession is { TraitorsEnabled: true } && GameMain.Client != null && character != Character.Controlled)
|
||||
{
|
||||
Client targetClient = GameMain.Client.ConnectedClients.FirstOrDefault(c => c.Character == character);
|
||||
if (targetClient != null)
|
||||
{
|
||||
if (OrderPrefab.Prefabs.TryGet("reporttraitor", out OrderPrefab order))
|
||||
{
|
||||
var voteTraitorBtn = new GUITickBox(new RectTransform(Vector2.One, extraIconFrame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest), label: string.Empty, style: "TraitorVoteButton")
|
||||
{
|
||||
UserData = character,
|
||||
ToolTip =
|
||||
RichString.Rich(
|
||||
$"‖color:{XMLExtensions.ToStringHex(GUIStyle.TextColorBright)}‖{TextManager.Get("traitor.blamebutton")}‖color:end‖\n"
|
||||
+ TextManager.Get("traitor.blamebutton.tooltip")),
|
||||
OnSelected = (GUITickBox obj) =>
|
||||
{
|
||||
foreach (var traitorBtn in traitorButtons)
|
||||
{
|
||||
//deselect other traitor buttons
|
||||
if (traitorBtn != obj) { traitorBtn.SetSelected(false, callOnSelected: false); }
|
||||
}
|
||||
GameMain.Client?.Vote(VoteType.Traitor, obj.Selected ? targetClient : null);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
traitorButtons.Add(voteTraitorBtn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return background;
|
||||
}
|
||||
@@ -499,6 +529,7 @@ namespace Barotrauma
|
||||
if (crewList?.Content.GetChildByUserData(character) is { } component)
|
||||
{
|
||||
crewList.RemoveChild(component);
|
||||
traitorButtons.RemoveAll(t => t.IsChildOf(component, recursive: true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,7 +1236,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
crewArea.Visible = !(GameMain.GameSession?.GameMode is CampaignMode campaign) || (!campaign.ForceMapUI && !campaign.ShowCampaignUI);
|
||||
crewArea.Visible = GameMain.GameSession?.GameMode is not CampaignMode campaign || (!campaign.ForceMapUI && !campaign.ShowCampaignUI);
|
||||
|
||||
guiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
@@ -1372,7 +1403,8 @@ namespace Barotrauma
|
||||
|
||||
if (PlayerInput.KeyDown(InputType.Command) &&
|
||||
(GUI.KeyboardDispatcher.Subscriber == null || (GUI.KeyboardDispatcher.Subscriber is GUIComponent component && (component == crewList || component.IsChildOf(crewList)))) &&
|
||||
commandFrame == null && !clicklessSelectionActive && CanIssueOrders && !(GameMain.GameSession?.Campaign?.ShowCampaignUI ?? false))
|
||||
commandFrame == null && !clicklessSelectionActive && CanIssueOrders && !(GameMain.GameSession?.Campaign?.ShowCampaignUI ?? false) &&
|
||||
Character.Controlled?.SelectedItem?.Prefab is not { DisableCommandMenuWhenSelected: true })
|
||||
{
|
||||
if (PlayerInput.IsShiftDown())
|
||||
{
|
||||
@@ -1557,6 +1589,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (characterComponent.UserData is Character character)
|
||||
{
|
||||
if (character.Removed)
|
||||
{
|
||||
characterComponent.Visible = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
characterComponent.Visible = Character.Controlled == null || Character.Controlled.TeamID == character.TeamID;
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && Character.Controlled != null &&
|
||||
(character.CurrentHull == Character.Controlled.CurrentHull || Vector2.DistanceSquared(Character.Controlled.WorldPosition, character.WorldPosition) < 500.0f * 500.0f))
|
||||
@@ -1633,6 +1671,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
traitorButtons.ForEach(btn => btn.Visible = Character.Controlled is { IsDead: false } && btn.UserData as Character != Character.Controlled);
|
||||
|
||||
crewArea.RectTransform.AbsoluteOffset = Vector2.SmoothStep(
|
||||
new Vector2(-crewArea.Rect.Width - HUDLayoutSettings.Padding, 0.0f),
|
||||
Vector2.Zero,
|
||||
@@ -2396,7 +2436,7 @@ namespace Barotrauma
|
||||
if (!(GetTargetSubmarine() is { } sub)) { return; }
|
||||
shortcutNodes.Clear();
|
||||
var subItems = sub.GetItems(false);
|
||||
if (CanFitMoreNodes() && subItems.Find(i => i.HasTag("reactor") && i.IsPlayerTeamInteractable)?.GetComponent<Reactor>() is Reactor reactor)
|
||||
if (CanFitMoreNodes() && subItems.Find(i => i.HasTag(Tags.Reactor) && i.IsPlayerTeamInteractable)?.GetComponent<Reactor>() is Reactor reactor)
|
||||
{
|
||||
float reactorOutput = -reactor.CurrPowerConsumption;
|
||||
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
|
||||
@@ -2415,7 +2455,7 @@ namespace Barotrauma
|
||||
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
|
||||
// --> Create shortcut node for Steer order
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("steer") && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["steer"]) &&
|
||||
subItems.Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedItem == nav) &&
|
||||
subItems.Find(i => i.HasTag(Tags.NavTerminal) && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedItem == nav) &&
|
||||
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering);
|
||||
|
||||
@@ -157,11 +157,15 @@ namespace Barotrauma
|
||||
|
||||
SlideshowPlayer?.DrawManually(spriteBatch);
|
||||
|
||||
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
|
||||
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI ||
|
||||
CoroutineManager.IsCoroutineRunning("LevelTransition"))
|
||||
{
|
||||
endRoundButton.Visible = false;
|
||||
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = false; }
|
||||
return;
|
||||
if (ReadyCheckButton != null)
|
||||
{
|
||||
ReadyCheckButton.Visible = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (Submarine.MainSub == null || Level.Loaded == null) { return; }
|
||||
|
||||
@@ -216,11 +220,15 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (Level.IsLoadedOutpost && !ObjectiveManager.AllActiveObjectivesCompleted())
|
||||
if (Level.IsLoadedOutpost &&
|
||||
(!ObjectiveManager.AllActiveObjectivesCompleted() && this is not MultiPlayerCampaign))
|
||||
{
|
||||
allowEndingRound = false;
|
||||
}
|
||||
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = allowEndingRound; }
|
||||
if (ReadyCheckButton != null)
|
||||
{
|
||||
ReadyCheckButton.Visible = allowEndingRound && GameMain.GameSession != null && GameMain.GameSession.RoundDuration > 10.0f;
|
||||
}
|
||||
|
||||
endRoundButton.Visible = allowEndingRound && Character.Controlled is { IsIncapacitated: false };
|
||||
if (endRoundButton.Visible)
|
||||
@@ -384,8 +392,11 @@ namespace Barotrauma
|
||||
protected void TryEndRoundWithFuelCheck(Action onConfirm, Action onReturnToMapScreen)
|
||||
{
|
||||
Submarine.MainSub.CheckFuel();
|
||||
SubmarineInfo nextSub = PendingSubmarineSwitch ?? Submarine.MainSub.Info;
|
||||
bool lowFuel = nextSub.Name == Submarine.MainSub.Info.Name ? Submarine.MainSub.Info.LowFuel : nextSub.LowFuel;
|
||||
bool lowFuel = Submarine.MainSub.Info.LowFuel;
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
lowFuel = TransferItemsOnSubSwitch ? (lowFuel && PendingSubmarineSwitch.LowFuel) : PendingSubmarineSwitch.LowFuel;
|
||||
}
|
||||
if (Level.IsLoadedFriendlyOutpost && lowFuel && CargoManager.PurchasedItems.None(i => i.Value.Any(pi => pi.ItemPrefab.Tags.Contains("reactorfuel"))))
|
||||
{
|
||||
var extraConfirmationBox =
|
||||
@@ -433,11 +444,21 @@ namespace Barotrauma
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData is RoundSummary);
|
||||
}
|
||||
#if DEBUG
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.M))
|
||||
{
|
||||
if (GUIMessageBox.MessageBoxes.Any()) { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.MessageBoxes.Last()); }
|
||||
|
||||
GUIFrame summaryFrame = GameMain.GameSession.RoundSummary.CreateSummaryFrame(GameMain.GameSession, "");
|
||||
GUIMessageBox.MessageBoxes.Add(summaryFrame);
|
||||
GameMain.GameSession.RoundSummary.ContinueButton.OnClicked = (_, __) => { GUIMessageBox.MessageBoxes.Remove(summaryFrame); return true; };
|
||||
}
|
||||
#endif
|
||||
if (ShowCampaignUI || ForceMapUI)
|
||||
{
|
||||
CampaignUI?.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
protected override IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null)
|
||||
protected override IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
@@ -1014,9 +1014,9 @@ namespace Barotrauma
|
||||
public void LoadState(string filePath)
|
||||
{
|
||||
DebugConsole.Log($"Loading save file for an existing game session ({filePath})");
|
||||
SaveUtil.DecompressToDirectory(filePath, SaveUtil.TempPath, null);
|
||||
SaveUtil.DecompressToDirectory(filePath, SaveUtil.TempPath);
|
||||
|
||||
string gamesessionDocPath = Path.Combine(SaveUtil.TempPath, "gamesession.xml");
|
||||
string gamesessionDocPath = Path.Combine(SaveUtil.TempPath, SaveUtil.GameSessionFileName);
|
||||
XDocument doc = XMLExtensions.TryLoadXml(gamesessionDocPath);
|
||||
if (doc == null)
|
||||
{
|
||||
|
||||
@@ -368,7 +368,7 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
protected override IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null)
|
||||
protected override IEnumerable<CoroutineStatus> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror)
|
||||
{
|
||||
NextLevel = newLevel;
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
@@ -382,7 +382,7 @@ namespace Barotrauma
|
||||
// Event history must be registered before ending the round or it will be cleared
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
}
|
||||
GameMain.GameSession.EndRound("", traitorResults, transitionType);
|
||||
GameMain.GameSession.EndRound("", transitionType);
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
RoundSummary roundSummary = null;
|
||||
if (GUIMessageBox.VisibleBox?.UserData is RoundSummary)
|
||||
@@ -513,17 +513,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.M))
|
||||
{
|
||||
if (GUIMessageBox.MessageBoxes.Any()) { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.MessageBoxes.Last()); }
|
||||
|
||||
GUIFrame summaryFrame = GameMain.GameSession.RoundSummary.CreateSummaryFrame(GameMain.GameSession, "", null);
|
||||
GUIMessageBox.MessageBoxes.Add(summaryFrame);
|
||||
GameMain.GameSession.RoundSummary.ContinueButton.OnClicked = (_, __) => { GUIMessageBox.MessageBoxes.Remove(summaryFrame); return true; };
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ShowCampaignUI || ForceMapUI)
|
||||
{
|
||||
Character.DisableControls = true;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
enum AutoPlayVideo { Yes, No };
|
||||
|
||||
enum TutorialSegmentType { MessageBox, InfoBox, Objective };
|
||||
enum SegmentType { MessageBox, InfoBox, Objective };
|
||||
|
||||
sealed class Tutorial
|
||||
{
|
||||
@@ -108,7 +108,7 @@ namespace Barotrauma.Tutorials
|
||||
GameMain.GameSession.StartRound(LevelSeed);
|
||||
}
|
||||
|
||||
GameMain.GameSession.EventManager.ActiveEvents.Clear();
|
||||
GameMain.GameSession.EventManager.ClearEvents();
|
||||
GameMain.GameSession.EventManager.Enabled = true;
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Barotrauma.Tutorials;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -15,8 +14,6 @@ namespace Barotrauma
|
||||
public static bool IsTabMenuOpen => GameMain.GameSession?.tabMenu != null;
|
||||
public static TabMenu TabMenuInstance => GameMain.GameSession?.tabMenu;
|
||||
|
||||
private float prevHudScale;
|
||||
|
||||
private TabMenu tabMenu;
|
||||
|
||||
public bool ToggleTabMenu()
|
||||
@@ -27,7 +24,7 @@ namespace Barotrauma
|
||||
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu = null;
|
||||
if (GameMain.NetLobbyScreen.JobSelectionFrame != null) { GameMain.NetLobbyScreen.JobSelectionFrame.Visible = false; }
|
||||
}
|
||||
if (tabMenu == null && !(GameMode is TutorialMode) && !ConversationAction.IsDialogOpen)
|
||||
if (tabMenu == null && GameMode is not TutorialMode && !ConversationAction.IsDialogOpen)
|
||||
{
|
||||
tabMenu = new TabMenu();
|
||||
HintManager.OnShowTabMenu();
|
||||
@@ -49,6 +46,8 @@ namespace Barotrauma
|
||||
private GUITextBlock respawnInfoText;
|
||||
private GUITickBox respawnTickBox;
|
||||
|
||||
private GUIImage eventLogNotification;
|
||||
|
||||
private void CreateTopLeftButtons()
|
||||
{
|
||||
if (topLeftButtonGroup != null)
|
||||
@@ -96,7 +95,8 @@ namespace Barotrauma
|
||||
OnClicked = (button, userData) => ToggleTabMenu()
|
||||
};
|
||||
|
||||
talentPointNotification = CreateTalentIconNotification(tabMenuButton);
|
||||
talentPointNotification = CreateNotificationIcon(tabMenuButton);
|
||||
eventLogNotification = CreateNotificationIcon(tabMenuButton);
|
||||
|
||||
GameMain.Instance.ResolutionChanged += CreateTopLeftButtons;
|
||||
|
||||
@@ -121,7 +121,6 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
prevHudScale = GameSettings.CurrentConfig.Graphics.HUDScale;
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
@@ -152,7 +151,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static GUIImage CreateTalentIconNotification(GUIComponent parent, bool offset = true)
|
||||
public static GUIImage CreateNotificationIcon(GUIComponent parent, bool offset = true)
|
||||
{
|
||||
GUIImage indicator = new GUIImage(new RectTransform(new Vector2(0.45f), parent.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.BothWidth), style: "TalentPointNotification")
|
||||
{
|
||||
@@ -167,19 +166,22 @@ namespace Barotrauma
|
||||
return indicator;
|
||||
}
|
||||
|
||||
public void EnableEventLogNotificationIcon(bool enabled)
|
||||
{
|
||||
if (eventLogNotification == null) { return; }
|
||||
if (!eventLogNotification.Visible && enabled)
|
||||
{
|
||||
eventLogNotification.Pulsate(Vector2.One, Vector2.One * 2, 1.0f);
|
||||
}
|
||||
eventLogNotification.Visible = enabled;
|
||||
}
|
||||
|
||||
public static void UpdateTalentNotificationIndicator(GUIImage indicator)
|
||||
{
|
||||
if (indicator != null)
|
||||
{
|
||||
if (Character.Controlled?.Info == null)
|
||||
{
|
||||
indicator.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
indicator.Visible = Character.Controlled.Info.GetAvailableTalentPoints() > 0 && !Character.Controlled.HasUnlockedAllTalents();
|
||||
}
|
||||
}
|
||||
if (indicator == null) { return; }
|
||||
indicator.Visible =
|
||||
Character.Controlled?.Info != null &&
|
||||
Character.Controlled.Info.GetAvailableTalentPoints() > 0 && !Character.Controlled.HasUnlockedAllTalents();
|
||||
}
|
||||
|
||||
public void HUDScaleChanged()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Tutorials;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -152,8 +151,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// onstartedinteracting.turretperiscope
|
||||
if (item.HasTag("periscope") &&
|
||||
item.GetConnectedComponents<Turret>().FirstOrDefault(t => t.Item.HasTag("turret")) is Turret)
|
||||
if (item.HasTag(Tags.Periscope) &&
|
||||
item.GetConnectedComponents<Turret>().FirstOrDefault(t => t.Item.HasTag(Tags.Turret)) is Turret)
|
||||
{
|
||||
if (DisplayHint($"{hintIdentifierBase}.turretperiscope".ToIdentifier(),
|
||||
variables: new[]
|
||||
@@ -175,6 +174,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnStartRepairing(Character character, Repairable repairable)
|
||||
{
|
||||
if (repairable.ForceDeteriorationTimer > 0.0f && !character.IsTraitor)
|
||||
{
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
DisplayHint($"repairingsabotageditem".ToIdentifier());
|
||||
}, delay: 5.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckIsInteracting()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
@@ -182,7 +192,7 @@ namespace Barotrauma
|
||||
|
||||
if (Character.Controlled.SelectedItem.GetComponent<Reactor>() is Reactor reactor && reactor.PowerOn &&
|
||||
Character.Controlled.SelectedItem.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
|
||||
containedItems.Count(i => i.HasTag("reactorfuel")) > 1)
|
||||
containedItems.Count(i => i.HasTag(Tags.Fuel)) > 1)
|
||||
{
|
||||
if (DisplayHint("onisinteracting.reactorwithextrarods".ToIdentifier())) { return; }
|
||||
}
|
||||
@@ -210,7 +220,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (item.CurrentHull == null) { continue; }
|
||||
if (item.GetComponent<Pump>() == null) { continue; }
|
||||
if (!item.HasTag("ballast") && !item.CurrentHull.RoomName.Contains("ballast", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (!item.HasTag(Tags.Ballast) && !item.CurrentHull.RoomName.Contains("ballast", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
BallastHulls.Add(item.CurrentHull);
|
||||
}
|
||||
}
|
||||
@@ -246,8 +256,14 @@ namespace Barotrauma
|
||||
});
|
||||
}
|
||||
|
||||
if (GameMain.GameSession is { TraitorsEnabled: true })
|
||||
{
|
||||
DisplayHint("traitorsonboard".ToIdentifier());
|
||||
DisplayHint("traitorsonboard2".ToIdentifier());
|
||||
}
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void OnRoundEnded()
|
||||
@@ -395,8 +411,8 @@ namespace Barotrauma
|
||||
if (DisplayHint($"onobtaineditem.{tag}".ToIdentifier())) { return; }
|
||||
}
|
||||
|
||||
if ((item.HasTag("geneticmaterial") && character.Inventory.FindItemByTag("geneticdevice".ToIdentifier(), recursive: true) != null) ||
|
||||
(item.HasTag("geneticdevice") && character.Inventory.FindItemByTag("geneticmaterial".ToIdentifier(), recursive: true) != null))
|
||||
if ((item.HasTag(Tags.GeneticMaterial) && character.Inventory.FindItemByTag(Tags.GeneticMaterial, recursive: true) != null) ||
|
||||
(item.HasTag(Tags.GeneticDevice) && character.Inventory.FindItemByTag(Tags.GeneticDevice, recursive: true) != null))
|
||||
{
|
||||
if (DisplayHint($"geneticmaterial.useinstructions".ToIdentifier())) { return; }
|
||||
}
|
||||
@@ -437,6 +453,14 @@ namespace Barotrauma
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnRadioJammed(Item radioItem)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (radioItem?.ParentInventory is not CharacterInventory characterInventory) { return; }
|
||||
if (characterInventory.Owner != Character.Controlled) { return; }
|
||||
DisplayHint("radiojammed".ToIdentifier());
|
||||
}
|
||||
|
||||
public static void OnReactorOutOfFuel(Reactor reactor)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
@@ -450,6 +474,13 @@ namespace Barotrauma
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnAssignedAsTraitor()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
DisplayHint("assignedastraitor".ToIdentifier());
|
||||
DisplayHint("assignedastraitor2".ToIdentifier());
|
||||
}
|
||||
|
||||
public static void OnAvailableTransition(CampaignMode.TransitionType transitionType)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
@@ -556,7 +587,7 @@ namespace Barotrauma
|
||||
private static void CheckIfDivingGearOutOfOxygen()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
var divingGear = Character.Controlled.GetEquippedItem("diving", InvSlotType.OuterClothes);
|
||||
var divingGear = Character.Controlled.GetEquippedItem(Tags.DivingGear, InvSlotType.OuterClothes);
|
||||
if (divingGear?.OwnInventory == null) { return; }
|
||||
if (divingGear.GetContainedItemConditionPercentage() > 0.0f) { return; }
|
||||
DisplayHint("ondivinggearoutofoxygen".ToIdentifier(), onUpdate: () =>
|
||||
@@ -599,7 +630,7 @@ namespace Barotrauma
|
||||
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint("onadjacenthull.highwaterpercentage".ToIdentifier())) { return; }
|
||||
}
|
||||
|
||||
static bool IsWearingDivingSuit() => Character.Controlled.GetEquippedItem("deepdiving", InvSlotType.OuterClothes) is Item;
|
||||
static bool IsWearingDivingSuit() => Character.Controlled.GetEquippedItem(Tags.HeavyDivingGear, InvSlotType.OuterClothes) is Item;
|
||||
}
|
||||
|
||||
static bool IsOnFriendlySub() => Character.Controlled.Submarine is Submarine sub && (sub.TeamID == Character.Controlled.TeamID || sub.TeamID == CharacterTeamType.FriendlyNPC);
|
||||
|
||||
@@ -49,7 +49,7 @@ static class ObjectiveManager
|
||||
|
||||
public Identifier ParentId { get; set; }
|
||||
|
||||
public TutorialSegmentType SegmentType { get; private set; }
|
||||
public SegmentType SegmentType { get; private set; }
|
||||
|
||||
public static Segment CreateInfoBoxSegment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
@@ -69,31 +69,31 @@ static class ObjectiveManager
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag).Fallback(objectiveTextTag.Value));
|
||||
AutoPlayVideo = autoPlayVideo;
|
||||
TextContent = textContent;
|
||||
VideoContent = videoContent;
|
||||
SegmentType = TutorialSegmentType.InfoBox;
|
||||
SegmentType = SegmentType.InfoBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, Action onClickObjective)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag).Fallback(objectiveTextTag.Value));
|
||||
OnClickObjective = onClickObjective;
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
SegmentType = SegmentType.MessageBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
SegmentType = TutorialSegmentType.Objective;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag).Fallback(objectiveTextTag.Value));
|
||||
SegmentType = SegmentType.Objective;
|
||||
}
|
||||
|
||||
public void ConnectMessageBox(Segment messageBoxSegment)
|
||||
{
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
SegmentType = SegmentType.MessageBox;
|
||||
OnClickObjective = messageBoxSegment.OnClickObjective;
|
||||
}
|
||||
}
|
||||
@@ -139,9 +139,9 @@ static class ObjectiveManager
|
||||
VideoPlayer.AddToGUIUpdateList(order: 100);
|
||||
}
|
||||
|
||||
public static void TriggerTutorialSegment(Segment segment, bool connectObjective = false)
|
||||
public static void TriggerSegment(Segment segment, bool connectObjective = false)
|
||||
{
|
||||
if (segment.SegmentType != TutorialSegmentType.InfoBox)
|
||||
if (segment.SegmentType != SegmentType.InfoBox)
|
||||
{
|
||||
activeObjectives.Add(segment);
|
||||
AddToObjectiveList(segment, connectObjective);
|
||||
@@ -153,15 +153,15 @@ static class ObjectiveManager
|
||||
ActiveContentSegment = segment;
|
||||
|
||||
var title = TextManager.Get(segment.Id);
|
||||
LocalizedString tutorialText = TextManager.GetFormatted(segment.TextContent.Tag);
|
||||
tutorialText = TextManager.ParseInputTypes(tutorialText);
|
||||
LocalizedString text = TextManager.GetFormatted(segment.TextContent.Tag).Fallback(segment.TextContent.Tag.Value);
|
||||
text = TextManager.ParseInputTypes(text);
|
||||
|
||||
switch (segment.AutoPlayVideo)
|
||||
{
|
||||
case AutoPlayVideo.Yes:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
text,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
@@ -171,7 +171,7 @@ static class ObjectiveManager
|
||||
case AutoPlayVideo.No:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
text,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
@@ -182,31 +182,54 @@ static class ObjectiveManager
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompleteTutorialSegment(Identifier segmentId)
|
||||
public static void CompleteSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment || !segment.CanBeCompleted || segment.IsCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!MarkSegmentCompleted(segment))
|
||||
CompleteSegment(segment, failed: false);
|
||||
}
|
||||
|
||||
public static void FailSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CompleteSegment(segment, failed: true);
|
||||
}
|
||||
|
||||
private static void CompleteSegment(Segment segment, bool failed = false)
|
||||
{
|
||||
if (failed)
|
||||
{
|
||||
if (!MarkSegmentFailed(segment)) { return; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!MarkSegmentCompleted(segment)) { return; }
|
||||
}
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent($"Tutorial:{tutorialMode.Tutorial?.Identifier}:{segmentId}:Completed");
|
||||
}
|
||||
else if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent($"Tutorial:CampaignMode:{segmentId}:Completed");
|
||||
campaign?.CampaignMetadata?.SetValue(segmentId, true);
|
||||
GameAnalyticsManager.AddDesignEvent($"Tutorial:{tutorialMode.Tutorial?.Identifier}:{segment.Id}:{(failed ? "Failed" : "Completed")}");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool MarkSegmentCompleted(Segment segment, bool flash = true)
|
||||
private static bool MarkSegmentCompleted(Segment segment, bool flash = true)
|
||||
{
|
||||
return MarkSegment(segment, "ObjectiveIndicatorCompleted", flash, flashColor: GUIStyle.Green);
|
||||
}
|
||||
|
||||
private static bool MarkSegmentFailed(Segment segment, bool flash = true)
|
||||
{
|
||||
return MarkSegment(segment, "MissionFailedIcon", flash, flashColor: GUIStyle.Red);
|
||||
}
|
||||
|
||||
private static bool MarkSegment(Segment segment, string iconStyleName, bool flash, Color flashColor)
|
||||
{
|
||||
segment.IsCompleted = true;
|
||||
if (GUIStyle.GetComponentStyle("ObjectiveIndicatorCompleted") is GUIComponentStyle style)
|
||||
if (GUIStyle.GetComponentStyle(iconStyleName) is GUIComponentStyle style)
|
||||
{
|
||||
if (segment.ObjectiveStateIndicator.Style == style)
|
||||
{
|
||||
@@ -216,21 +239,17 @@ static class ObjectiveManager
|
||||
}
|
||||
if (flash)
|
||||
{
|
||||
segment.ObjectiveStateIndicator.Parent.Flash(color: GUIStyle.Green, flashDuration: 0.35f, useRectangleFlash: true);
|
||||
}
|
||||
segment.ObjectiveStateIndicator.Parent.Flash(color: flashColor, flashDuration: 0.35f, useRectangleFlash: true);
|
||||
}
|
||||
segment.ObjectiveButton.OnClicked = null;
|
||||
segment.ObjectiveButton.CanBeFocused = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void RemoveTutorialSegment(Identifier segmentId)
|
||||
public static void RemoveSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment)
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
DebugConsole.AddWarning($"Warning: tried to remove the tutorial segment \"{segmentId}\" in tutorial \"{tutorialMode.Tutorial?.Identifier}\" but it isn't active!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
segment.ObjectiveStateIndicator.FadeOut(ObjectiveComponentAnimationTime, false);
|
||||
@@ -400,10 +419,10 @@ static class ObjectiveManager
|
||||
|
||||
void SetButtonBehavior(Segment segment)
|
||||
{
|
||||
segment.ObjectiveButton.CanBeFocused = segment.SegmentType != TutorialSegmentType.Objective;
|
||||
segment.ObjectiveButton.CanBeFocused = segment.SegmentType != SegmentType.Objective;
|
||||
segment.ObjectiveButton.OnClicked = (GUIButton btn, object userdata) =>
|
||||
{
|
||||
if (segment.SegmentType == TutorialSegmentType.InfoBox)
|
||||
if (segment.SegmentType == SegmentType.InfoBox)
|
||||
{
|
||||
if (segment.AutoPlayVideo == AutoPlayVideo.Yes)
|
||||
{
|
||||
@@ -414,7 +433,7 @@ static class ObjectiveManager
|
||||
ShowSegmentText(segment);
|
||||
}
|
||||
}
|
||||
else if (segment.SegmentType == TutorialSegmentType.MessageBox)
|
||||
else if (segment.SegmentType == SegmentType.MessageBox)
|
||||
{
|
||||
segment.OnClickObjective?.Invoke();
|
||||
}
|
||||
@@ -438,8 +457,8 @@ static class ObjectiveManager
|
||||
ContentRunning = true;
|
||||
ActiveContentSegment = segment;
|
||||
infoBox = CreateInfoFrame(
|
||||
TextManager.Get(segment.Id),
|
||||
TextManager.Get(segment.TextContent.Tag),
|
||||
TextManager.Get(segment.Id).Fallback(segment.Id.Value),
|
||||
TextManager.Get(segment.TextContent.Tag).Fallback(segment.TextContent.Tag.Value),
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
|
||||
@@ -10,12 +10,12 @@ namespace Barotrauma
|
||||
{
|
||||
internal partial class ReadyCheck
|
||||
{
|
||||
private static LocalizedString readyCheckBody(string name) => string.IsNullOrWhiteSpace(name) ? TextManager.Get("readycheck.serverbody") : TextManager.GetWithVariable("readycheck.body", "[player]", name);
|
||||
private static LocalizedString ReadyCheckBody(string name) => string.IsNullOrWhiteSpace(name) ? TextManager.Get("readycheck.serverbody") : TextManager.GetWithVariable("readycheck.body", "[player]", name);
|
||||
|
||||
private static LocalizedString readyCheckStatus(int ready, int total) => TextManager.GetWithVariables("readycheck.readycount",
|
||||
private static LocalizedString ReadyCheckStatus(int ready, int total) => TextManager.GetWithVariables("readycheck.readycount",
|
||||
("[ready]", ready.ToString()),
|
||||
("[total]", total.ToString()));
|
||||
private static LocalizedString readyCheckPleaseWait(int seconds) => TextManager.GetWithVariable("readycheck.pleasewait", "[seconds]", seconds.ToString());
|
||||
private static LocalizedString ReadyCheckPleaseWait(int seconds) => TextManager.GetWithVariable("readycheck.pleasewait", "[seconds]", seconds.ToString());
|
||||
|
||||
private static readonly LocalizedString readyCheckHeader = TextManager.Get("ReadyCheck.Title");
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma
|
||||
{
|
||||
Vector2 relativeSize = new Vector2(0.2f / GUI.AspectRatioAdjustment, 0.15f);
|
||||
Point minSize = new Point(300, 200);
|
||||
msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true };
|
||||
msgBox = new GUIMessageBox(readyCheckHeader, ReadyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true };
|
||||
|
||||
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.125f), msgBox.Content.RectTransform), childAnchor: Anchor.Center);
|
||||
new GUIProgressBar(new RectTransform(new Vector2(0.8f, 1f), contentLayout.RectTransform), 0.0f, GUIStyle.Orange) { UserData = TimerData };
|
||||
@@ -82,9 +82,15 @@ namespace Barotrauma
|
||||
|
||||
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), resultsBox.Content.RectTransform)) { UserData = UserListData };
|
||||
|
||||
foreach (var (id, _) in Clients)
|
||||
foreach (var (id, status) in Clients)
|
||||
{
|
||||
Client? client = GameMain.Client.ConnectedClients.FirstOrDefault(c => c.SessionId == id);
|
||||
if (client == null)
|
||||
{
|
||||
string list = GameMain.Client.ConnectedClients.Aggregate("Available clients:\n", (current, c) => current + $"{c.SessionId}: {c.Name}\n");
|
||||
DebugConsole.AddWarning($"Client ID {id} was reported in ready check but was not found.\n" + list.TrimEnd('\n'));
|
||||
continue;
|
||||
}
|
||||
GUIFrame container = new GUIFrame(new RectTransform(new Vector2(1f, 0.15f), listBox.Content.RectTransform), style: "ListBoxElement") { UserData = id };
|
||||
GUILayoutGroup frame = new GUILayoutGroup(new RectTransform(Vector2.One, container.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
|
||||
@@ -92,11 +98,6 @@ namespace Barotrauma
|
||||
|
||||
JobPrefab? jobPrefab = client?.Character?.Info?.Job?.Prefab;
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
string list = GameMain.Client.ConnectedClients.Aggregate("Available clients:\n", (current, c) => current + $"{c.SessionId}: {c.Name}\n");
|
||||
DebugConsole.ThrowError($"Client ID {id} was reported in ready check but was not found.\n" + list.TrimEnd('\n'));
|
||||
}
|
||||
|
||||
if (jobPrefab?.Icon != null)
|
||||
{
|
||||
@@ -105,7 +106,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.75f, 1), frame.RectTransform), client?.Name ?? $"Unknown ID {id}", jobPrefab?.UIColor ?? Color.White, textAlignment: Alignment.Center) { AutoScaleHorizontal = true };
|
||||
new GUIImage(new RectTransform(new Point(height, height), frame.RectTransform), null, scaleToFit: true) { UserData = ReadySpriteData };
|
||||
var statusIcon = new GUIImage(new RectTransform(new Point(height, height), frame.RectTransform), null, scaleToFit: true) { UserData = ReadySpriteData };
|
||||
UpdateStatusIcon(statusIcon, status);
|
||||
}
|
||||
|
||||
resultsBox.Buttons[0].OnClicked = delegate
|
||||
@@ -214,10 +216,7 @@ namespace Barotrauma
|
||||
case ReadyCheckState.Update:
|
||||
ReadyStatus newState = (ReadyStatus)inc.ReadByte();
|
||||
byte targetId = inc.ReadByte();
|
||||
if (crewManager.ActiveReadyCheck != null)
|
||||
{
|
||||
crewManager.ActiveReadyCheck?.UpdateState(targetId, newState);
|
||||
}
|
||||
crewManager.ActiveReadyCheck?.UpdateState(targetId, newState);
|
||||
break;
|
||||
case ReadyCheckState.End:
|
||||
ushort count = inc.ReadUInt16();
|
||||
@@ -242,7 +241,7 @@ namespace Barotrauma
|
||||
|
||||
int readyCount = Clients.Count(static pair => pair.Value == ReadyStatus.Yes);
|
||||
int totalCount = Clients.Count;
|
||||
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount).Value, ChatMessageType.Server, null));
|
||||
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, ReadyCheckStatus(readyCount, totalCount).Value, ChatMessageType.Server, null));
|
||||
}
|
||||
|
||||
private void UpdateState(byte id, ReadyStatus status)
|
||||
@@ -253,31 +252,28 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (resultsBox == null || resultsBox.Closed || !GUIMessageBox.MessageBoxes.Contains(resultsBox)) { return; }
|
||||
|
||||
if (resultsBox.Content.FindChild(UserListData) is not GUIListBox userList) { return; }
|
||||
|
||||
// for some reason FindChild doesn't work here?
|
||||
foreach (GUIComponent child in userList.Content.Children)
|
||||
var child = userList.Content.FindChild(id);
|
||||
if (child?.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is not GUIImage image) { return; }
|
||||
UpdateStatusIcon(image, status);
|
||||
}
|
||||
|
||||
private static void UpdateStatusIcon(GUIImage image, ReadyStatus status)
|
||||
{
|
||||
string style;
|
||||
switch (status)
|
||||
{
|
||||
if (child.UserData is not byte b || b != id) { continue; }
|
||||
|
||||
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is not GUIImage image) { continue; }
|
||||
|
||||
string style;
|
||||
switch (status)
|
||||
{
|
||||
case ReadyStatus.Yes:
|
||||
style = "MissionCompletedIcon";
|
||||
break;
|
||||
case ReadyStatus.No:
|
||||
style = "MissionFailedIcon";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
|
||||
case ReadyStatus.Yes:
|
||||
style = "MissionCompletedIcon";
|
||||
break;
|
||||
case ReadyStatus.No:
|
||||
style = "MissionFailedIcon";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
|
||||
}
|
||||
|
||||
private static void SendState(ReadyStatus status)
|
||||
@@ -303,7 +299,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, readyCheckPleaseWait((ReadyCheckCooldown - DateTime.Now).Seconds), new[] { closeButton });
|
||||
GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, ReadyCheckPleaseWait((ReadyCheckCooldown - DateTime.Now).Seconds), new[] { closeButton });
|
||||
msgBox.Buttons[0].OnClicked = delegate
|
||||
{
|
||||
msgBox.Close();
|
||||
|
||||
@@ -10,6 +10,9 @@ namespace Barotrauma
|
||||
{
|
||||
class RoundSummary
|
||||
{
|
||||
private float crewListAnimDelay = 0.25f;
|
||||
private float missionIconAnimDelay;
|
||||
|
||||
private const float jobColumnWidthPercentage = 0.11f;
|
||||
private const float characterColumnWidthPercentage = 0.44f;
|
||||
private const float statusColumnWidthPercentage = 0.45f;
|
||||
@@ -44,7 +47,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public GUIFrame CreateSummaryFrame(GameSession gameSession, string endMessage, List<TraitorMissionResult> traitorResults, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
|
||||
public GUIFrame CreateSummaryFrame(GameSession gameSession, string endMessage, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None, TraitorManager.TraitorResults? traitorResults = null)
|
||||
{
|
||||
bool singleplayer = GameMain.NetworkMember == null;
|
||||
bool gameOver =
|
||||
@@ -70,7 +73,7 @@ namespace Barotrauma
|
||||
|
||||
//crew panel -------------------------------------------------------------------------------
|
||||
|
||||
GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.45f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.4f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
GUIFrame crewFrameInner = new GUIFrame(new RectTransform(new Point(crewFrame.Rect.Width - padding * 2, crewFrame.Rect.Height - padding * 2), crewFrame.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
|
||||
var crewContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner.RectTransform, Anchor.Center))
|
||||
@@ -82,7 +85,14 @@ namespace Barotrauma
|
||||
TextManager.Get("crew"), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
|
||||
crewHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader.Rect.Height * 2.0f));
|
||||
|
||||
CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != CharacterTeamType.Team2));
|
||||
var crewList = CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != CharacterTeamType.Team2), traitorResults);
|
||||
if (traitorResults != null && traitorResults.Value.VotedAsTraitorClientSessionId > 0)
|
||||
{
|
||||
var traitorInfoPanel = CreateTraitorInfoPanel(crewList.Content, traitorResults.Value, crewListAnimDelay);
|
||||
traitorInfoPanel.RectTransform.SetAsFirstChild();
|
||||
var spacing = new GUIFrame(new RectTransform(new Point(0, GUI.IntScale(20)), crewList.Content.RectTransform), style: null);
|
||||
spacing.RectTransform.RepositionChildInHierarchy(1);
|
||||
}
|
||||
|
||||
//another crew frame for the 2nd team in combat missions
|
||||
if (gameSession.Missions.Any(m => m is CombatMission))
|
||||
@@ -98,7 +108,7 @@ namespace Barotrauma
|
||||
var crewHeader2 = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent2.RectTransform),
|
||||
CombatMission.GetTeamName(CharacterTeamType.Team2), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
|
||||
crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f));
|
||||
CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == CharacterTeamType.Team2));
|
||||
CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == CharacterTeamType.Team2), traitorResults);
|
||||
}
|
||||
|
||||
//header -------------------------------------------------------------------------------
|
||||
@@ -111,71 +121,6 @@ namespace Barotrauma
|
||||
headerText, textAlignment: Alignment.BottomLeft, font: GUIStyle.LargeFont, wrap: true);
|
||||
}
|
||||
|
||||
//traitor panel -------------------------------------------------------------------------------
|
||||
|
||||
if (traitorResults != null && traitorResults.Any())
|
||||
{
|
||||
GUIFrame traitorframe = new GUIFrame(new RectTransform(crewFrame.RectTransform.RelativeSize, background.RectTransform, Anchor.TopCenter, minSize: crewFrame.RectTransform.MinSize));
|
||||
rightPanels.Add(traitorframe);
|
||||
GUIFrame traitorframeInner = new GUIFrame(new RectTransform(new Point(traitorframe.Rect.Width - padding * 2, traitorframe.Rect.Height - padding * 2), traitorframe.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
|
||||
var traitorContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), traitorframeInner.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var traitorHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), traitorContent.RectTransform),
|
||||
TextManager.Get("traitors"), font: GUIStyle.SubHeadingFont);
|
||||
traitorHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(traitorHeader.Rect.Height * 2.0f));
|
||||
|
||||
GUIListBox listBox = CreateCrewList(traitorContent, traitorResults.SelectMany(tr => tr.Characters.Select(c => c.Info)));
|
||||
|
||||
foreach (var traitorResult in traitorResults)
|
||||
{
|
||||
var traitorMission = TraitorMissionPrefab.Prefabs.Find(t => t.Identifier == traitorResult.MissionIdentifier);
|
||||
if (traitorMission == null) { continue; }
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, GUI.IntScale(25)), listBox.Content.RectTransform), style: null);
|
||||
|
||||
var traitorResultHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), listBox.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUIImage(new RectTransform(new Point(traitorResultHorizontal.Rect.Height), traitorResultHorizontal.RectTransform), traitorMission.Icon, scaleToFit: true)
|
||||
{
|
||||
Color = traitorMission.IconColor
|
||||
};
|
||||
|
||||
LocalizedString traitorMessage = TextManager.GetServerMessage(traitorResult.EndMessage);
|
||||
if (!traitorMessage.IsNullOrEmpty())
|
||||
{
|
||||
var textContent = new GUILayoutGroup(new RectTransform(Vector2.One, traitorResultHorizontal.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.025f
|
||||
};
|
||||
|
||||
var traitorStatusText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
|
||||
TextManager.Get(traitorResult.Success ? "missioncompleted" : "missionfailed"),
|
||||
textColor: traitorResult.Success ? GUIStyle.Green : GUIStyle.Red, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
var traitorMissionInfo = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
|
||||
traitorMessage, font: GUIStyle.SmallFont, wrap: true);
|
||||
|
||||
traitorResultHorizontal.Recalculate();
|
||||
|
||||
traitorStatusText.CalculateHeightFromText();
|
||||
traitorMissionInfo.CalculateHeightFromText();
|
||||
traitorStatusText.RectTransform.MinSize = new Point(0, traitorStatusText.Rect.Height);
|
||||
traitorMissionInfo.RectTransform.MinSize = new Point(0, traitorMissionInfo.Rect.Height);
|
||||
textContent.RectTransform.MaxSize = new Point(int.MaxValue, (int)((traitorStatusText.Rect.Height + traitorMissionInfo.Rect.Height) * 1.2f));
|
||||
traitorResultHorizontal.RectTransform.MinSize = new Point(0, traitorStatusText.RectTransform.MinSize.Y + traitorMissionInfo.RectTransform.MinSize.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//reputation panel -------------------------------------------------------------------------------
|
||||
|
||||
var campaignMode = gameMode as CampaignMode;
|
||||
@@ -185,7 +130,7 @@ namespace Barotrauma
|
||||
rightPanels.Add(reputationframe);
|
||||
GUIFrame reputationframeInner = new GUIFrame(new RectTransform(new Point(reputationframe.Rect.Width - padding * 2, reputationframe.Rect.Height - padding * 2), reputationframe.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
|
||||
var reputationContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), reputationframeInner.RectTransform, Anchor.Center))
|
||||
var reputationContent = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), reputationframeInner.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
@@ -199,15 +144,15 @@ namespace Barotrauma
|
||||
|
||||
//mission panel -------------------------------------------------------------------------------
|
||||
|
||||
GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.39f, 0.3f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4)));
|
||||
GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.4f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4)));
|
||||
GUILayoutGroup missionFrameContent = new GUILayoutGroup(new RectTransform(new Point(missionframe.Rect.Width - padding * 2, missionframe.Rect.Height - padding * 2), missionframe.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
RelativeSpacing = 0.03f
|
||||
};
|
||||
GUIFrame missionframeInner = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), missionFrameContent.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
|
||||
var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.93f), missionframeInner.RectTransform, Anchor.Center))
|
||||
var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.93f), missionframeInner.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
@@ -227,16 +172,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (missionsToDisplay.Any())
|
||||
{
|
||||
var missionHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContent.RectTransform),
|
||||
TextManager.Get(missionsToDisplay.Count > 1 ? "Missions" : "Mission"), textAlignment: Alignment.TopLeft, font: GUIStyle.SubHeadingFont);
|
||||
missionHeader.RectTransform.MinSize = new Point(0, (int)(missionHeader.Rect.Height * 1.2f));
|
||||
}
|
||||
|
||||
GUIListBox missionList = new GUIListBox(new RectTransform(Vector2.One, missionContent.RectTransform, Anchor.Center))
|
||||
{
|
||||
Padding = new Vector4(4, 10, 0, 0) * GUI.Scale
|
||||
Spacing = GUI.IntScale(15)
|
||||
};
|
||||
missionList.ContentBackground.Color = Color.Transparent;
|
||||
|
||||
@@ -255,120 +193,76 @@ namespace Barotrauma
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
endText.RectTransform.MinSize = new Point(0, endText.Rect.Height);
|
||||
var line = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), missionList.Content.RectTransform), style: "HorizontalLine");
|
||||
line.RectTransform.NonScaledSize = new Point(line.Rect.Width, GUI.IntScale(5.0f));
|
||||
}
|
||||
|
||||
foreach (Mission displayedMission in missionsToDisplay)
|
||||
float animDelay = missionIconAnimDelay;
|
||||
foreach (Mission mission in missionsToDisplay)
|
||||
{
|
||||
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.8f), missionList.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.025f,
|
||||
Stretch = true
|
||||
};
|
||||
var textContent = new List<LocalizedString>();
|
||||
|
||||
LocalizedString missionMessage =
|
||||
selectedMissions.Contains(displayedMission) ?
|
||||
displayedMission.Completed ? displayedMission.SuccessMessage : displayedMission.FailureMessage :
|
||||
displayedMission.Description;
|
||||
GUIImage missionIcon = new GUIImage(new RectTransform(new Point((int)(missionContentHorizontal.Rect.Height)), missionContentHorizontal.RectTransform), displayedMission.Prefab.Icon, scaleToFit: true)
|
||||
if (selectedMissions.Contains(mission))
|
||||
{
|
||||
Color = displayedMission.Prefab.IconColor,
|
||||
HoverColor = displayedMission.Prefab.IconColor,
|
||||
SelectedColor = displayedMission.Prefab.IconColor
|
||||
};
|
||||
missionIcon.RectTransform.MinSize = new Point((int)(missionContentHorizontal.Rect.Height * 0.9f));
|
||||
if (selectedMissions.Contains(displayedMission))
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
|
||||
}
|
||||
textContent.Add(mission.Completed ? mission.SuccessMessage : mission.FailureMessage);
|
||||
|
||||
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), missionContentHorizontal.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
missionContentHorizontal.Recalculate();
|
||||
var missionNameTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
displayedMission.Name, font: GUIStyle.SubHeadingFont);
|
||||
if (displayedMission.Difficulty.HasValue)
|
||||
{
|
||||
var groupSize = missionNameTextBlock.Rect.Size;
|
||||
groupSize.X -= (int)(missionNameTextBlock.Padding.X + missionNameTextBlock.Padding.Z);
|
||||
var indicatorGroup = new GUILayoutGroup(new RectTransform(groupSize, missionTextContent.RectTransform) { AbsoluteOffset = new Point((int)missionNameTextBlock.Padding.X, 0) },
|
||||
isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1
|
||||
};
|
||||
var difficultyColor = displayedMission.GetDifficultyColor();
|
||||
for (int i = 0; i < displayedMission.Difficulty; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, indicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = difficultyColor
|
||||
};
|
||||
}
|
||||
}
|
||||
var repText = mission.GetReputationRewardText();
|
||||
if (!repText.IsNullOrEmpty()) { textContent.Add(repText); }
|
||||
|
||||
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
RichString.Rich(missionMessage), wrap: true);
|
||||
if (selectedMissions.Contains(displayedMission))
|
||||
{
|
||||
RichString reputationText = displayedMission.GetReputationRewardText();
|
||||
if (!reputationText.IsNullOrEmpty())
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), reputationText, wrap: true);
|
||||
}
|
||||
|
||||
int totalReward = displayedMission.GetFinalReward(Submarine.MainSub);
|
||||
int totalReward = mission.GetFinalReward(Submarine.MainSub);
|
||||
if (totalReward > 0)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
|
||||
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled && displayedMission.Completed)
|
||||
textContent.Add(mission.GetMissionRewardText(Submarine.MainSub));
|
||||
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled && mission.Completed)
|
||||
{
|
||||
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(totalReward));
|
||||
if (share > 0)
|
||||
{
|
||||
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
|
||||
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
|
||||
RichString yourShareString = TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}"));
|
||||
textContent.Add(yourShareString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayedMission != missionsToDisplay.Last())
|
||||
else
|
||||
{
|
||||
var spacing = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), missionList.Content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(15)) }, style: null);
|
||||
new GUIFrame(new RectTransform(new Vector2(0.8f, 1.0f), spacing.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.1f, 0.0f) }, "HorizontalLine");
|
||||
var repText = mission.GetReputationRewardText();
|
||||
if (!repText.IsNullOrEmpty()) { textContent.Add(repText); }
|
||||
textContent.Add(mission.GetMissionRewardText(Submarine.MainSub));
|
||||
textContent.Add(mission.Description);
|
||||
textContent.AddRange(mission.ShownMessages);
|
||||
}
|
||||
|
||||
foreach (GUIComponent child in missionTextContent.Children)
|
||||
CreateMissionEntry(
|
||||
missionList.Content,
|
||||
mission.Name,
|
||||
textContent,
|
||||
mission.Difficulty ?? 0,
|
||||
mission.Prefab.Icon, mission.Prefab.IconColor,
|
||||
out GUIImage missionIcon);
|
||||
|
||||
if (selectedMissions.Contains(mission))
|
||||
{
|
||||
child.RectTransform.IsFixedSize = true;
|
||||
UpdateMissionStateIcon(mission.Completed, missionIcon, animDelay);
|
||||
animDelay += 0.25f;
|
||||
}
|
||||
missionTextContent.RectTransform.MinSize = new Point(0, missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing));
|
||||
missionContentHorizontal.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Rect.Height / missionTextContent.RectTransform.RelativeSize.Y));
|
||||
}
|
||||
|
||||
if (!missionsToDisplay.Any())
|
||||
{
|
||||
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(Vector2.One, missionList.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
|
||||
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.4f), missionList.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.025f,
|
||||
Stretch = true
|
||||
Stretch = true,
|
||||
CanBeFocused = true
|
||||
};
|
||||
GUIImage missionIcon = new GUIImage(new RectTransform(new Point((int)(missionContentHorizontal.Rect.Height * 0.7f)), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
|
||||
missionIcon.RectTransform.MinSize = new Point((int)(missionContentHorizontal.Rect.Height * 0.7f));
|
||||
GUIImage missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContentHorizontal.RectTransform),
|
||||
TextManager.Get("nomission"), font: GUIStyle.LargeFont);
|
||||
}
|
||||
|
||||
/*missionContentHorizontal.Recalculate();
|
||||
missionContent.Recalculate();
|
||||
missionIcon.RectTransform.MinSize = new Point(0, missionContentHorizontal.Rect.Height);
|
||||
missionTextContent.RectTransform.MaxSize = new Point(int.MaxValue, missionIcon.Rect.Width);*/
|
||||
gameSession?.EventManager?.EventLog?.CreateEventLogUI(missionList.Content, traitorResults);
|
||||
|
||||
AddSeparators(missionList.Content);
|
||||
|
||||
ContinueButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), ButtonArea.RectTransform), TextManager.Get("Close"));
|
||||
ButtonArea.RectTransform.NonScaledSize = new Point(ButtonArea.Rect.Width, ContinueButton.Rect.Height);
|
||||
@@ -405,10 +299,7 @@ namespace Barotrauma
|
||||
|
||||
public void CreateReputationInfoPanel(GUIComponent parent, CampaignMode campaignMode)
|
||||
{
|
||||
GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
|
||||
{
|
||||
Padding = new Vector4(4, 10, 0, 0) * GUI.Scale
|
||||
};
|
||||
GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform));
|
||||
reputationList.ContentBackground.Color = Color.Transparent;
|
||||
|
||||
foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
|
||||
@@ -506,6 +397,171 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIComponent CreateTraitorInfoPanel(GUIComponent parent, TraitorManager.TraitorResults traitorResults, float iconAnimDelay)
|
||||
{
|
||||
var traitorCharacter = traitorResults.GetTraitorClient()?.Character;
|
||||
|
||||
string resultTag =
|
||||
traitorResults.VotedCorrectTraitor ?
|
||||
traitorResults.ObjectiveSuccessful ? "traitor.blameresult.correct.objectivesuccessful" : "traitor.blameresult.correct.objectivefailed" :
|
||||
"traitor.blameresult.failure";
|
||||
|
||||
var textContent = new List<LocalizedString>()
|
||||
{
|
||||
TextManager.GetWithVariable("traitor.blameresult", "[name]", traitorCharacter?.Name ?? "unknown"),
|
||||
TextManager.Get(resultTag)
|
||||
};
|
||||
|
||||
if (traitorResults.MoneyPenalty > 0)
|
||||
{
|
||||
textContent.Add(
|
||||
TextManager.GetWithVariable(
|
||||
"traitor.blameresult.failure.penalty",
|
||||
"[money]",
|
||||
TextManager.FormatCurrency(traitorResults.MoneyPenalty, includeCurrencySymbol: false)));
|
||||
}
|
||||
|
||||
var icon = GUIStyle.GetComponentStyle("TraitorMissionIcon")?.GetDefaultSprite();
|
||||
|
||||
var content = CreateMissionEntry(
|
||||
parent,
|
||||
string.Empty,
|
||||
textContent,
|
||||
difficultyIconCount: 0,
|
||||
icon, GUIStyle.Red,
|
||||
out GUIImage missionIcon);
|
||||
UpdateMissionStateIcon(traitorResults.VotedCorrectTraitor, missionIcon, iconAnimDelay);
|
||||
return content;
|
||||
}
|
||||
|
||||
public static GUIComponent CreateMissionEntry(GUIComponent parent, LocalizedString header, List<LocalizedString> textContent, int difficultyIconCount,
|
||||
Sprite icon, Color iconColor, out GUIImage missionIcon)
|
||||
{
|
||||
int spacing = GUI.IntScale(5);
|
||||
|
||||
int defaultLineHeight = (int)GUIStyle.Font.MeasureChar('T').Y;
|
||||
|
||||
//make the icon big enough for header + some lines of text
|
||||
int iconSize = (int)(GUIStyle.SubHeadingFont.MeasureChar('T').Y + defaultLineHeight * 6);
|
||||
|
||||
GUILayoutGroup content = new GUILayoutGroup(new RectTransform(new Point(parent.Rect.Width, iconSize), parent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = spacing,
|
||||
CanBeFocused = true
|
||||
};
|
||||
if (icon != null)
|
||||
{
|
||||
missionIcon = new GUIImage(new RectTransform(new Point(iconSize), content.RectTransform), icon, null, true)
|
||||
{
|
||||
Color = iconColor,
|
||||
HoverColor = iconColor,
|
||||
SelectedColor = iconColor,
|
||||
CanBeFocused = false
|
||||
};
|
||||
missionIcon.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
missionIcon = null;
|
||||
}
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), content.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.TopLeft)
|
||||
{
|
||||
AbsoluteSpacing = spacing
|
||||
};
|
||||
content.Recalculate();
|
||||
|
||||
RichString missionNameString = RichString.Rich(header);
|
||||
List<RichString> contentStrings = new List<RichString>(textContent.Select(t => RichString.Rich(t)));
|
||||
|
||||
if (!header.IsNullOrEmpty())
|
||||
{
|
||||
var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform),
|
||||
missionNameString, font: GUIStyle.SubHeadingFont, wrap: true);
|
||||
nameText.RectTransform.MinSize = new Point(0, (int)nameText.TextSize.Y);
|
||||
}
|
||||
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (difficultyIconCount > 0)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Point(missionTextGroup.Rect.Width, defaultLineHeight), parent: missionTextGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteSpacing = 1
|
||||
};
|
||||
difficultyIndicatorGroup.RectTransform.MinSize = new Point(0, defaultLineHeight);
|
||||
var difficultyColor = Mission.GetDifficultyColor(difficultyIconCount);
|
||||
for (int i = 0; i < difficultyIconCount; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest), "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = difficultyColor
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
GUITextBlock firstContentText = null;
|
||||
foreach (var contentString in contentStrings)
|
||||
{
|
||||
var text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), contentString, wrap: true);
|
||||
text.RectTransform.MinSize = new Point(0, (int)text.TextSize.Y);
|
||||
firstContentText ??= text;
|
||||
}
|
||||
if (difficultyIndicatorGroup != null && firstContentText != null)
|
||||
{
|
||||
//make the icons align with the text content
|
||||
difficultyIndicatorGroup.RectTransform.AbsoluteOffset = new Point((int)firstContentText.Padding.X, 0);
|
||||
}
|
||||
missionTextGroup.RectTransform.MinSize =
|
||||
new Point(0, missionTextGroup.Children.Sum(c => c.Rect.Height + missionTextGroup.AbsoluteSpacing) - missionTextGroup.AbsoluteSpacing);
|
||||
missionTextGroup.Recalculate();
|
||||
content.RectTransform.MinSize = new Point(0, Math.Max(missionTextGroup.Rect.Height, iconSize));
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public static void AddSeparators(GUIComponent container)
|
||||
{
|
||||
var children = container.Children.ToList();
|
||||
if (children.Count < 2) { return; }
|
||||
|
||||
var lastChild = children.Last();
|
||||
foreach (var child in children)
|
||||
{
|
||||
if (child != lastChild)
|
||||
{
|
||||
var separator = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), container.RectTransform), style: "HorizontalLine");
|
||||
separator.RectTransform.RepositionChildInHierarchy(container.GetChildIndex(child) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateMissionStateIcon(bool success, GUIImage missionIcon, float delay = 0.5f)
|
||||
{
|
||||
if (missionIcon == null) { return; }
|
||||
string style = success ? "MissionCompletedIcon" : "MissionFailedIcon";
|
||||
GUIImage stateIcon = missionIcon.GetChild<GUIImage>();
|
||||
if (string.IsNullOrEmpty(style))
|
||||
{
|
||||
if (stateIcon != null)
|
||||
{
|
||||
stateIcon.Visible = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool wasVisible = stateIcon is { Visible: true };
|
||||
stateIcon ??= new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform, Anchor.Center), style, scaleToFit: true);
|
||||
stateIcon.Visible = true;
|
||||
if (!wasVisible)
|
||||
{
|
||||
stateIcon.FadeIn(delay, 0.15f);
|
||||
stateIcon.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.0f + delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private LocalizedString GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
|
||||
{
|
||||
string locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.Name : startLocation?.Name;
|
||||
@@ -568,7 +624,7 @@ namespace Barotrauma
|
||||
return TextManager.GetWithVariables(textTag, ("[sub]", subName), ("[location]", locationName));
|
||||
}
|
||||
|
||||
private GUIListBox CreateCrewList(GUIComponent parent, IEnumerable<CharacterInfo> characterInfos)
|
||||
private GUIListBox CreateCrewList(GUIComponent parent, IEnumerable<CharacterInfo> characterInfos, TraitorManager.TraitorResults? traitorResults)
|
||||
{
|
||||
var headerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform, Anchor.TopCenter, minSize: new Point(0, (int)(30 * GUI.Scale))) { }, isHorizontal: true)
|
||||
{
|
||||
@@ -602,16 +658,19 @@ namespace Barotrauma
|
||||
|
||||
headerFrame.RectTransform.RelativeSize -= new Vector2(crewList.ScrollBar.RectTransform.RelativeSize.X, 0.0f);
|
||||
|
||||
float delay = crewListAnimDelay;
|
||||
foreach (CharacterInfo characterInfo in characterInfos)
|
||||
{
|
||||
if (characterInfo == null) { continue; }
|
||||
CreateCharacterElement(characterInfo, crewList);
|
||||
CreateCharacterElement(characterInfo, crewList, traitorResults, delay);
|
||||
delay += crewListAnimDelay;
|
||||
}
|
||||
missionIconAnimDelay = delay;
|
||||
|
||||
return crewList;
|
||||
}
|
||||
|
||||
private void CreateCharacterElement(CharacterInfo characterInfo, GUIListBox listBox)
|
||||
private void CreateCharacterElement(CharacterInfo characterInfo, GUIListBox listBox, TraitorManager.TraitorResults? traitorResults, float animDelay)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, GUI.IntScale(45)), listBox.Content.RectTransform), style: "ListBoxElement")
|
||||
{
|
||||
@@ -684,9 +743,31 @@ namespace Barotrauma
|
||||
|
||||
GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(statusText.Value, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: statusColor);
|
||||
|
||||
frame.FadeIn(animDelay, 0.15f);
|
||||
foreach (var child in frame.GetAllChildren())
|
||||
{
|
||||
child.FadeIn(animDelay, 0.15f);
|
||||
}
|
||||
|
||||
if (traitorResults.HasValue && GameMain.NetworkMember != null)
|
||||
{
|
||||
var clientVotedAsTraitor = traitorResults.Value.GetTraitorClient();
|
||||
bool isTraitor = clientVotedAsTraitor != null && clientVotedAsTraitor.Character == character;
|
||||
if (isTraitor)
|
||||
{
|
||||
var img = new GUIImage(new RectTransform(new Point(paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.CenterRight), style: "TraitorVoteButton")
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = TextManager.GetWithVariable("traitor.blameresult", "[name]", characterInfo.Name)
|
||||
};
|
||||
img.FadeIn(1.0f + animDelay, 0.15f);
|
||||
img.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.5f + animDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GUIFrame CreateReputationElement(GUIComponent parent,
|
||||
private static GUIFrame CreateReputationElement(GUIComponent parent,
|
||||
LocalizedString name, Reputation reputation, float initialReputation,
|
||||
LocalizedString shortDescription, LocalizedString fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
@@ -42,16 +43,29 @@ namespace Barotrauma
|
||||
if (limbSlotIcons == null)
|
||||
{
|
||||
limbSlotIcons = new Dictionary<InvSlotType, Sprite>();
|
||||
int margin = 2;
|
||||
limbSlotIcons.Add(InvSlotType.Headset, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
limbSlotIcons.Add(InvSlotType.InnerClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
limbSlotIcons.Add(InvSlotType.Card, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(640 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
foreach (InvSlotType invSlotType in Enum.GetValues(typeof(InvSlotType)))
|
||||
{
|
||||
var sprite = GUIStyle.GetComponentStyle($"InventorySlot.{invSlotType}")?.GetDefaultSprite();
|
||||
if (sprite != null)
|
||||
{
|
||||
limbSlotIcons.Add(invSlotType, sprite);
|
||||
}
|
||||
}
|
||||
|
||||
limbSlotIcons.Add(InvSlotType.Head, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(896 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128)));
|
||||
limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128)));
|
||||
limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
limbSlotIcons.Add(InvSlotType.Bag, new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(639, 926, 128,80)));
|
||||
int margin = 2;
|
||||
AddIfMissing(InvSlotType.Headset, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
AddIfMissing(InvSlotType.InnerClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
AddIfMissing(InvSlotType.Card, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(640 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
AddIfMissing(InvSlotType.Head, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(896 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
AddIfMissing(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128)));
|
||||
AddIfMissing(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128)));
|
||||
AddIfMissing(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
|
||||
AddIfMissing(InvSlotType.Bag, new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(639, 926, 128,80)));
|
||||
|
||||
static void AddIfMissing(InvSlotType slotType, Sprite sprite)
|
||||
{
|
||||
limbSlotIcons.TryAdd(slotType, sprite);
|
||||
}
|
||||
}
|
||||
return limbSlotIcons;
|
||||
}
|
||||
@@ -179,11 +193,30 @@ namespace Barotrauma
|
||||
BackgroundFrame = frame;
|
||||
}
|
||||
|
||||
protected override bool HideSlot(int i)
|
||||
public override bool HideSlot(int i)
|
||||
{
|
||||
if (visualSlots[i].Disabled || (slots[i].HideIfEmpty && slots[i].Empty())) { return true; }
|
||||
|
||||
if (CharacterHealth.OpenHealthWindow != Character.Controlled?.CharacterHealth && SlotTypes[i] == InvSlotType.HealthInterface) { return true; }
|
||||
if (SlotTypes[i] == InvSlotType.HealthInterface)
|
||||
{
|
||||
//hide health interface slot unless this character's health window is open
|
||||
if (CharacterHealth.OpenHealthWindow == null || Character.Controlled == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
bool ownHealthWindowOpen = CharacterHealth.OpenHealthWindow == Character.Controlled.CharacterHealth;
|
||||
if (!ownHealthWindowOpen) { return true; }
|
||||
}
|
||||
else if (character == Character.Controlled.SelectedCharacter)
|
||||
{
|
||||
bool otherCharacterHealthWindowOpen = CharacterHealth.OpenHealthWindow == Character.Controlled?.SelectedCharacter?.CharacterHealth;
|
||||
if (!otherCharacterHealthWindowOpen) { return true; }
|
||||
//can't access the health interface slot of a non-incapacitated player (bots are fine though)
|
||||
if (character.IsPlayer && !character.IsIncapacitated) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
if (layout == Layout.Default)
|
||||
{
|
||||
@@ -216,6 +249,11 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RefreshSlotPositions()
|
||||
{
|
||||
SetSlotPositions(CurrentLayout);
|
||||
}
|
||||
|
||||
private void SetSlotPositions(Layout layout)
|
||||
{
|
||||
int spacing = GUI.IntScale(5);
|
||||
@@ -450,6 +488,9 @@ namespace Barotrauma
|
||||
((s.SlotIndex < 0 || s.SlotIndex >= slots.Length || slots[s.SlotIndex] == null) || (Character.Controlled != null && !Character.Controlled.CanAccessInventory(s.Inventory))));
|
||||
//remove highlighted subinventory slots that refer to items no longer in this inventory
|
||||
highlightedSubInventorySlots.RemoveWhere(s => s.Item != null && s.ParentInventory == this && s.Item.ParentInventory != this);
|
||||
//remove highlighted subinventory slots if we're dragging that item out of the inventory
|
||||
highlightedSubInventorySlots.RemoveWhere(s => s.Item != null && s.ParentInventory == this && DraggingItems.Contains(s.Item));
|
||||
|
||||
tempHighlightedSubInventorySlots.Clear();
|
||||
tempHighlightedSubInventorySlots.AddRange(highlightedSubInventorySlots);
|
||||
foreach (var highlightedSubInventorySlot in tempHighlightedSubInventorySlots)
|
||||
@@ -525,6 +566,7 @@ namespace Barotrauma
|
||||
var itemContainer = item.GetComponent<ItemContainer>();
|
||||
if (itemContainer != null &&
|
||||
itemContainer.KeepOpenWhenEquippedBy(character) &&
|
||||
!DraggingItems.Contains(item) &&
|
||||
character.CanAccessInventory(itemContainer.Inventory) &&
|
||||
!highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory))
|
||||
{
|
||||
@@ -538,9 +580,11 @@ namespace Barotrauma
|
||||
if (doubleClickedItems.Any())
|
||||
{
|
||||
var quickUseAction = GetQuickUseAction(doubleClickedItems.First(), true, true, true);
|
||||
int itemCount = 0;
|
||||
foreach (Item doubleClickedItem in doubleClickedItems)
|
||||
{
|
||||
QuickUseItem(doubleClickedItem, true, true, true, quickUseAction, playSound: doubleClickedItem == doubleClickedItems.First());
|
||||
itemCount++;
|
||||
//only use one item if we're equipping or using it as a treatment
|
||||
if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment)
|
||||
{
|
||||
@@ -556,6 +600,12 @@ namespace Barotrauma
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((quickUseAction == QuickUseAction.TakeFromContainer || quickUseAction == QuickUseAction.PutToEquippedItem) &&
|
||||
doubleClickedItem.ParentInventory != null &&
|
||||
itemCount >= doubleClickedItem.Prefab.GetMaxStackSize(doubleClickedItem.ParentInventory))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,10 +839,30 @@ namespace Barotrauma
|
||||
{
|
||||
return QuickUseAction.TakeFromCharacter;
|
||||
}
|
||||
else if (character.HeldItems.Any(i =>
|
||||
else if (character.HeldItems.FirstOrDefault(i =>
|
||||
i.OwnInventory != null &&
|
||||
(i.OwnInventory.CanBePut(item) || ((i.OwnInventory.Capacity == 1 || i.OwnInventory.Container.HasSubContainers) && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
|
||||
(i.OwnInventory.CanBePut(item) || ((i.OwnInventory.Capacity == 1 || i.OwnInventory.Container.HasSubContainers) && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))) is { } equippedContainer)
|
||||
{
|
||||
if (allowEquip)
|
||||
{
|
||||
if (!character.HasEquippedItem(item))
|
||||
{
|
||||
if (equippedContainer.GetComponent<ItemContainer>() is { QuickUseMovesItemsInside: false})
|
||||
{
|
||||
//put the item in a hand slot if that hand is free
|
||||
if ((item.AllowedSlots.Contains(InvSlotType.RightHand) && character.Inventory.GetItemInLimbSlot(InvSlotType.RightHand) == null) ||
|
||||
(item.AllowedSlots.Contains(InvSlotType.LeftHand) && character.Inventory.GetItemInLimbSlot(InvSlotType.LeftHand) == null))
|
||||
{
|
||||
return QuickUseAction.Equip;
|
||||
}
|
||||
}
|
||||
}
|
||||
//equipped -> attempt to unequip
|
||||
else if (item.AllowedSlots.Contains(InvSlotType.Any))
|
||||
{
|
||||
return QuickUseAction.Unequip;
|
||||
}
|
||||
}
|
||||
return QuickUseAction.PutToEquippedItem;
|
||||
}
|
||||
else if (allowEquip) //doubleclicked and no other inventory is selected
|
||||
@@ -1171,5 +1241,11 @@ namespace Barotrauma
|
||||
GUIComponent.DrawToolTip(spriteBatch, highlightedQuickUseSlot.QuickUseButtonToolTip, highlightedQuickUseSlot.EquipButtonRect);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, Character.InventoryStateEventData extraData)
|
||||
{
|
||||
SharedWrite(msg, extraData.SlotRange);
|
||||
syncItemsDelay = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +185,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
Color color = item.GetSpriteColor(withHighlight: true);
|
||||
Color color = overrideColor ?? item.GetSpriteColor(withHighlight: true);
|
||||
if (brokenSprite == null)
|
||||
{
|
||||
//broken doors turn black if no broken sprite has been configured
|
||||
@@ -202,7 +202,7 @@ namespace Barotrauma.Items.Components
|
||||
weldSpritePos.Y = -weldSpritePos.Y;
|
||||
|
||||
weldedSprite.Draw(spriteBatch,
|
||||
weldSpritePos, item.SpriteColor * (stuck / 100.0f), scale: item.Scale);
|
||||
weldSpritePos, overrideColor ?? (item.SpriteColor * (stuck / 100.0f)), scale: item.Scale);
|
||||
}
|
||||
|
||||
if (openState >= 1.0f) { return; }
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public Vector2 DrawSize => Vector2.Zero;
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (!editing) { return; }
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return item.Rect.Size.ToVector2(); }
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (!IsActive || picker == null || !CanBeAttached(picker) || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled)
|
||||
{
|
||||
@@ -50,7 +50,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.4f);
|
||||
|
||||
item.Sprite.Draw(
|
||||
Sprite sprite = item.Sprite;
|
||||
foreach (ContainedItemSprite containedSprite in item.Prefab.ContainedSprites)
|
||||
{
|
||||
if (containedSprite.UseWhenAttached)
|
||||
{
|
||||
sprite = containedSprite.Sprite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sprite.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(attachPos.X, -attachPos.Y),
|
||||
item.SpriteColor * 0.5f,
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -48,33 +45,36 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ContentXElement limbElement in characterInfo.Ragdoll.MainElement.Elements())
|
||||
if (characterInfo.Ragdoll.MainElement?.Elements() is { } limbElements)
|
||||
{
|
||||
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
|
||||
ContentXElement spriteElement = limbElement.GetChildElement("sprite");
|
||||
if (spriteElement == null) { continue; }
|
||||
|
||||
ContentPath contentPath = spriteElement.GetAttributeContentPath("texture");
|
||||
|
||||
string spritePath = characterInfo.ReplaceVars(contentPath.Value);
|
||||
string fileName = Path.GetFileNameWithoutExtension(spritePath);
|
||||
|
||||
//go through the files in the directory to find a matching sprite
|
||||
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
|
||||
foreach (ContentXElement limbElement in limbElements)
|
||||
{
|
||||
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
||||
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
|
||||
ContentXElement spriteElement = limbElement.GetChildElement("sprite");
|
||||
if (spriteElement == null) { continue; }
|
||||
|
||||
ContentPath contentPath = spriteElement.GetAttributeContentPath("texture");
|
||||
|
||||
string spritePath = characterInfo.ReplaceVars(contentPath.Value);
|
||||
string fileName = Path.GetFileNameWithoutExtension(spritePath);
|
||||
|
||||
//go through the files in the directory to find a matching sprite
|
||||
foreach (string file in Directory.GetFiles(Path.GetDirectoryName(spritePath)))
|
||||
{
|
||||
continue;
|
||||
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string fileWithoutTags = Path.GetFileNameWithoutExtension(file);
|
||||
fileWithoutTags = fileWithoutTags.Split('[', ']').First();
|
||||
if (fileWithoutTags != fileName) { continue; }
|
||||
Portrait = new Sprite(spriteElement, "", file) { RelativeOrigin = Vector2.Zero };
|
||||
break;
|
||||
}
|
||||
string fileWithoutTags = Path.GetFileNameWithoutExtension(file);
|
||||
fileWithoutTags = fileWithoutTags.Split('[', ']').First();
|
||||
if (fileWithoutTags != fileName) { continue; }
|
||||
Portrait = new Sprite(spriteElement, "", file) { RelativeOrigin = Vector2.Zero };
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (characterInfo.Wearables != null)
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
currentCrossHairScale = currentCrossHairPointerScale = cam == null ? 1.0f : cam.Zoom;
|
||||
if (crosshairSprite != null)
|
||||
@@ -118,6 +118,7 @@ namespace Barotrauma.Items.Components
|
||||
else if (chargeSoundChannel != null)
|
||||
{
|
||||
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
|
||||
chargeSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Barotrauma.Items.Components
|
||||
private int spraySetting = 0;
|
||||
private readonly Point[] sprayArray = new Point[8];
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (character == null || !character.IsKeyDown(InputType.Aim)) return;
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma.Items.Components
|
||||
if (body.UserData is Item item)
|
||||
{
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door != null && door.CanBeTraversed) { continue; }
|
||||
if (door != null && (door.IsOpen || door.IsBroken)) { continue; }
|
||||
}
|
||||
|
||||
targetHull = null;
|
||||
@@ -288,7 +288,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
#if DEBUG
|
||||
if (GameMain.DebugDraw && Character.Controlled != null && Character.Controlled.IsKeyDown(InputType.Aim))
|
||||
|
||||
@@ -150,7 +150,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public GUIFrame GuiFrame { get; set; }
|
||||
|
||||
public bool LockGuiFramePosition;
|
||||
private GUIDragHandle guiFrameDragHandle;
|
||||
|
||||
private bool guiFrameUpdatePending;
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool AllowUIOverlap
|
||||
@@ -466,7 +468,21 @@ namespace Barotrauma.Items.Components
|
||||
GuiFrame?.AddToGUIUpdateList(order: order);
|
||||
}
|
||||
|
||||
public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { }
|
||||
public void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
UpdateHUDComponentSpecific(character, deltaTime, cam);
|
||||
if (guiFrameUpdatePending && !PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
//send a guiframe position update once the player stops dragging the frame
|
||||
guiFrameUpdatePending = false;
|
||||
if (SerializableProperties.TryGetValue(nameof(GuiFrameOffset).ToIdentifier(), out var property))
|
||||
{
|
||||
GameMain.Client?.CreateEntityEvent(Item, new Item.ChangePropertyEventData(property, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam) { }
|
||||
|
||||
public virtual void UpdateEditing(float deltaTime) { }
|
||||
|
||||
@@ -572,6 +588,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
string style = GuiFrameSource.Attribute("style") == null ? null : GuiFrameSource.GetAttributeString("style", "");
|
||||
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas, Anchor.Center), style, color);
|
||||
GuiFrame.RectTransform.ScreenSpaceOffset = GuiFrameOffset;
|
||||
|
||||
TryCreateDragHandle();
|
||||
|
||||
@@ -583,24 +600,25 @@ namespace Barotrauma.Items.Components
|
||||
GameMain.Instance.ResolutionChanged += OnResolutionChangedPrivate;
|
||||
}
|
||||
|
||||
protected virtual void TryCreateDragHandle()
|
||||
protected void TryCreateDragHandle()
|
||||
{
|
||||
if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true))
|
||||
{
|
||||
bool hideDragIcons = GuiFrameSource.GetAttributeBool("hidedragicons", false);
|
||||
|
||||
var handle = new GUIDragHandle(new RectTransform(Vector2.One, GuiFrame.RectTransform, Anchor.Center),
|
||||
guiFrameDragHandle = new GUIDragHandle(new RectTransform(Vector2.One, GuiFrame.RectTransform, Anchor.Center),
|
||||
GuiFrame.RectTransform, style: null)
|
||||
{
|
||||
Enabled = !LockGuiFramePosition,
|
||||
DragArea = HUDLayoutSettings.ItemHUDArea
|
||||
};
|
||||
|
||||
int iconHeight = GUIStyle.ItemFrameMargin.Y / 4;
|
||||
var dragIcon = new GUIImage(new RectTransform(new Point(GuiFrame.Rect.Width, iconHeight), handle.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, iconHeight / 2) },
|
||||
var dragIcon = new GUIImage(new RectTransform(new Point(GuiFrame.Rect.Width, iconHeight), guiFrameDragHandle.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, iconHeight / 2) },
|
||||
style: "GUIDragIndicatorHorizontal");
|
||||
dragIcon.RectTransform.MinSize = new Point(0, iconHeight);
|
||||
|
||||
handle.ValidatePosition = (RectTransform rectT) =>
|
||||
guiFrameDragHandle.ValidatePosition = (RectTransform rectT) =>
|
||||
{
|
||||
var activeHuds = Character.Controlled?.SelectedItem?.ActiveHUDs ?? item.ActiveHUDs;
|
||||
foreach (ItemComponent ic in activeHuds)
|
||||
@@ -624,11 +642,13 @@ namespace Barotrauma.Items.Components
|
||||
//refresh slots to ensure they're rendered at the correct position
|
||||
(ic as ItemContainer)?.Inventory.CreateSlots();
|
||||
}
|
||||
GuiFrameOffset = GuiFrame.RectTransform.ScreenSpaceOffset;
|
||||
guiFrameUpdatePending = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
|
||||
var settingsIcon = new GUIButton(new RectTransform(new Point(buttonHeight), handle.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
|
||||
var settingsIcon = new GUIButton(new RectTransform(new Point(buttonHeight), guiFrameDragHandle.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
|
||||
style: "GUIButtonSettings")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
@@ -636,6 +656,14 @@ namespace Barotrauma.Items.Components
|
||||
GUIContextMenu.CreateContextMenu(
|
||||
new ContextMenuOption("item.resetuiposition", isEnabled: true, onSelected: () =>
|
||||
{
|
||||
foreach (var ic in item.Components)
|
||||
{
|
||||
if (ic.GuiFrame != null && ic.GuiFrameOffset != Point.Zero)
|
||||
{
|
||||
ic.GuiFrameOffset = Point.Zero;
|
||||
ic.guiFrameUpdatePending = true;
|
||||
}
|
||||
}
|
||||
if (Character.Controlled?.SelectedItem != null && item != Character.Controlled.SelectedItem)
|
||||
{
|
||||
Character.Controlled.SelectedItem.ForceHUDLayoutUpdate(ignoreLocking: true);
|
||||
@@ -648,7 +676,11 @@ namespace Barotrauma.Items.Components
|
||||
new ContextMenuOption(TextManager.Get(LockGuiFramePosition ? "item.unlockuiposition" : "item.lockuiposition"), isEnabled: true, onSelected: () =>
|
||||
{
|
||||
LockGuiFramePosition = !LockGuiFramePosition;
|
||||
handle.Enabled = !LockGuiFramePosition;
|
||||
guiFrameDragHandle.Enabled = !LockGuiFramePosition;
|
||||
if (SerializableProperties.TryGetValue(nameof(LockGuiFramePosition).ToIdentifier(), out var property))
|
||||
{
|
||||
GameMain.Client?.CreateEntityEvent(Item, new Item.ChangePropertyEventData(property, this));
|
||||
}
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Barotrauma.Inventory;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -97,6 +97,8 @@ namespace Barotrauma.Items.Components
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
slotIcons = new Sprite[capacity];
|
||||
|
||||
int currCapacity = MainContainerCapacity;
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -127,6 +129,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "subcontainer":
|
||||
int subContainerCapacity = subElement.GetAttributeInt("capacity", 1);
|
||||
var slotIconElement = subElement.GetChildElement("sloticon");
|
||||
if (slotIconElement != null)
|
||||
{
|
||||
var slotIcon = new Sprite(slotIconElement);
|
||||
for (int i = currCapacity; i < currCapacity + subContainerCapacity; i++)
|
||||
{
|
||||
slotIcons[i] = slotIcon;
|
||||
}
|
||||
}
|
||||
currCapacity += subContainerCapacity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,11 +195,40 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
|
||||
LocalizedString labelText = GetUILabel();
|
||||
GUITextBlock label = null;
|
||||
if (!labelText.IsNullOrEmpty())
|
||||
GUITextBlock label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform, Anchor.TopCenter),
|
||||
labelText, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft, wrap: true)
|
||||
{
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
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) },
|
||||
isHorizontal: true, childAnchor: Anchor.TopRight)
|
||||
{
|
||||
label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform, Anchor.TopCenter),
|
||||
labelText, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
|
||||
AbsoluteSpacing = margin.X / 2
|
||||
};
|
||||
if (Inventory.Capacity > 1)
|
||||
{
|
||||
new GUIButton(new RectTransform(Vector2.One, buttonArea.RectTransform, scaleBasis: ScaleBasis.Smallest), style: "SortItemsButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("SortItemsAlphabetically"),
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
SortItems();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(Vector2.One, buttonArea.RectTransform, scaleBasis: ScaleBasis.Smallest), style: "MergeStacksButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("MergeItemStacks"),
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
MergeStacks();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
float minInventoryAreaSize = 0.5f;
|
||||
@@ -215,6 +259,71 @@ namespace Barotrauma.Items.Components
|
||||
Inventory.RectTransform = guiCustomComponent.RectTransform;
|
||||
}
|
||||
|
||||
private void SortItems()
|
||||
{
|
||||
List<List<Item>> itemsPerSlot = new List<List<Item>>();
|
||||
|
||||
for (int i = 0; i < Inventory.Capacity; i++)
|
||||
{
|
||||
var items = Inventory.GetItemsAt(i).ToList();
|
||||
if (items.Any())
|
||||
{
|
||||
itemsPerSlot.Add(items);
|
||||
items.ForEach(it => it.Drop(dropper: null, createNetworkEvent: false, setTransform: false));
|
||||
}
|
||||
}
|
||||
|
||||
itemsPerSlot.Sort((i1, i2) => i1.First().Name.CompareTo(i2.First().Name));
|
||||
foreach (var items in itemsPerSlot)
|
||||
{
|
||||
int firstFreeSlot = -1;
|
||||
for (int i = 0; i < Inventory.Capacity; i++)
|
||||
{
|
||||
if (Inventory.GetItemAt(i) == null && Inventory.CanBePut(items.First()))
|
||||
{
|
||||
firstFreeSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstFreeSlot == -1)
|
||||
{
|
||||
items.ForEach(it => it.Drop(dropper: null));
|
||||
continue;
|
||||
}
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!Inventory.TryPutItem(item, firstFreeSlot, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: false))
|
||||
{
|
||||
//if putting in the specific slot fails (prevented by containable restrictions?), just put in the first free slot
|
||||
if (!Inventory.TryPutItem(item, user: null, createNetworkEvent: false))
|
||||
{
|
||||
item.Drop(dropper: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Inventory.CreateNetworkEvent();
|
||||
}
|
||||
|
||||
private void MergeStacks()
|
||||
{
|
||||
for (int i = Inventory.Capacity - 1; i >= 0; i--)
|
||||
{
|
||||
var items = Inventory.GetItemsAt(i).ToList();
|
||||
if (items.None()) { continue; }
|
||||
//find the first stack we can put the item in
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
if (Inventory.GetItemsAt(j).Any() && Inventory.CanBePutInSlot(items.First(), j))
|
||||
{
|
||||
items.ForEach(it => Inventory.TryPutItem(it, j, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: false));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Inventory.CreateNetworkEvent();
|
||||
}
|
||||
|
||||
public LocalizedString GetUILabel()
|
||||
{
|
||||
if (UILabel == string.Empty) { return string.Empty; }
|
||||
@@ -277,7 +386,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
int ignoredItemCount = 0;
|
||||
var subContainableItems = AllSubContainableItems;
|
||||
float targetSlotCapacity = GetMaxStackSize(targetSlot);
|
||||
float targetSlotCapacity = Math.Min(containedItem.Prefab.MaxStackSize, GetMaxStackSize(targetSlot));
|
||||
float capacity = targetSlotCapacity * MainContainerCapacity;
|
||||
if (subContainableItems != null)
|
||||
{
|
||||
@@ -310,29 +419,36 @@ namespace Barotrauma.Items.Components
|
||||
int itemCount = Inventory.AllItems.Count() - ignoredItemCount;
|
||||
return Math.Min(itemCount / Math.Max(capacity, 1), 1);
|
||||
}
|
||||
|
||||
//display the state of an item in a specific slot
|
||||
if (Inventory.Capacity == 1 || ContainedStateIndicatorSlot > -1)
|
||||
{
|
||||
if (containedItem == null) { return 0.0f; }
|
||||
//if the contained item has some contained state indicator, show that
|
||||
if (containedItem.GetComponent<ItemContainer>() is { ShowContainedStateIndicator: true } containedItemContainer)
|
||||
{
|
||||
return containedItemContainer.GetContainedIndicatorState();
|
||||
}
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.GetMaxStackSize(Inventory), GetMaxStackSize(targetSlot));
|
||||
if (maxStackSize == 1)
|
||||
{
|
||||
return containedItem.Condition / containedItem.MaxCondition;
|
||||
}
|
||||
return containedItems.Count() / (float)maxStackSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (containedItem != null && (Inventory.Capacity == 1 || HasSubContainers))
|
||||
{
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, GetMaxStackSize(targetSlot));
|
||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||
{
|
||||
return containedItems.Count() / (float)maxStackSize;
|
||||
}
|
||||
}
|
||||
return Inventory.Capacity == 1 || ContainedStateIndicatorSlot > -1 ?
|
||||
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
|
||||
Inventory.EmptySlotCount / (float)Inventory.Capacity;
|
||||
}
|
||||
return Inventory.EmptySlotCount / (float)Inventory.Capacity;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
|
||||
DrawContainedItems(spriteBatch, itemDepth);
|
||||
DrawContainedItems(spriteBatch, itemDepth, overrideColor);
|
||||
}
|
||||
|
||||
public void DrawContainedItems(SpriteBatch spriteBatch, float itemDepth)
|
||||
public void DrawContainedItems(SpriteBatch spriteBatch, float itemDepth, Color? overrideColor = null)
|
||||
{
|
||||
Vector2 transformedItemPos = ItemPos * item.Scale;
|
||||
Vector2 transformedItemInterval = ItemInterval * item.Scale;
|
||||
@@ -388,7 +504,7 @@ namespace Barotrauma.Items.Components
|
||||
bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
|
||||
|
||||
int i = 0;
|
||||
foreach (DrawableContainedItem contained in drawableContainedItems)
|
||||
foreach (ContainedItem contained in containedItems)
|
||||
{
|
||||
Vector2 itemPos = currentItemPos;
|
||||
|
||||
@@ -466,7 +582,7 @@ namespace Barotrauma.Items.Components
|
||||
contained.Item.Sprite.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(itemPos.X, -itemPos.Y),
|
||||
isWiringMode ? contained.Item.GetSpriteColor(withHighlight: true) * 0.15f : contained.Item.GetSpriteColor(withHighlight: true),
|
||||
overrideColor ?? (isWiringMode ? contained.Item.GetSpriteColor(withHighlight: true) * 0.15f : contained.Item.GetSpriteColor(withHighlight: true)),
|
||||
origin,
|
||||
-(contained.Item.body == null ? 0.0f : contained.Item.body.DrawRotation),
|
||||
contained.Item.Scale,
|
||||
@@ -476,7 +592,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (ItemContainer ic in contained.Item.GetComponents<ItemContainer>())
|
||||
{
|
||||
if (ic.hideItems) { continue; }
|
||||
ic.DrawContainedItems(spriteBatch, containedSpriteDepth);
|
||||
ic.DrawContainedItems(spriteBatch, containedSpriteDepth, overrideColor);
|
||||
}
|
||||
|
||||
i++;
|
||||
@@ -497,7 +613,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (!item.IsInteractable(character)) { return; }
|
||||
if (Inventory.RectTransform != null)
|
||||
@@ -512,19 +628,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//if the item is in the character's inventory, no need to update the item's inventory
|
||||
//because the player can see it by hovering the cursor over the item
|
||||
guiCustomComponent.Visible = DrawInventory && item.ParentInventory?.Owner != character;
|
||||
guiCustomComponent.Visible = DrawInventory && (item.ParentInventory?.Owner != character || Inventory.DrawWhenEquipped);
|
||||
if (!guiCustomComponent.Visible) { return; }
|
||||
|
||||
Inventory.Update(deltaTime, cam);
|
||||
}
|
||||
|
||||
/*public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
//if the item is in the character's inventory, no need to draw the item's inventory
|
||||
//because the player can see it by hovering the cursor over the item
|
||||
if (item.ParentInventory?.Owner == character || !DrawInventory) return;
|
||||
|
||||
Inventory.Draw(spriteBatch);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ namespace Barotrauma.Items.Components
|
||||
prevRect = item.Rect;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (item.ParentInventory != null) { return; }
|
||||
if (editing)
|
||||
|
||||
@@ -19,13 +19,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Sprite backgroundSprite;
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (backgroundSprite == null) { return; }
|
||||
|
||||
backgroundSprite.DrawTiled(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X - item.Rect.Width / 2 * item.Scale, -(item.DrawPosition.Y + item.Rect.Height / 2)) - backgroundSprite.Origin * item.Scale,
|
||||
new Vector2(backgroundSprite.size.X * item.Scale, item.Rect.Height), color: item.Color,
|
||||
new Vector2(backgroundSprite.size.X * item.Scale, item.Rect.Height),
|
||||
color: overrideColor ?? item.Color,
|
||||
textureScale: Vector2.One * item.Scale,
|
||||
depth: BackgroundSpriteDepth);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (Light?.LightSprite == null) { return; }
|
||||
if ((item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
|
||||
|
||||
@@ -428,7 +428,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
inSufficientPowerWarning.Visible = IsActive && !hasPower;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
powerIndicator.Selected = hasPower && IsActive;
|
||||
autoControlIndicator.Selected = controlLockTimer > 0.0f;
|
||||
@@ -138,14 +138,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (propellerSprite != null)
|
||||
{
|
||||
Vector2 drawPos = item.DrawPosition;
|
||||
drawPos += PropellerPos;
|
||||
drawPos.Y = -drawPos.Y;
|
||||
propellerSprite.Draw(spriteBatch, (int)Math.Floor(spriteIndex), drawPos, Color.White, propellerSprite.Origin, 0.0f, Vector2.One);
|
||||
propellerSprite.Draw(spriteBatch, (int)Math.Floor(spriteIndex), drawPos, overrideColor ?? Color.White, propellerSprite.Origin, 0.0f, Vector2.One);
|
||||
}
|
||||
|
||||
if (editing && !DisablePropellerDamage && propellerDamage != null && !GUI.DisableHUD)
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
private FabricationRecipe selectedItem;
|
||||
|
||||
/// <summary>
|
||||
/// Which character's skills the current view is displayed based on
|
||||
/// </summary>
|
||||
private Character displayingForCharacter;
|
||||
|
||||
public Identifier SelectedItemIdentifier => SelectedItem?.TargetItem.Identifier ?? Identifier.Empty;
|
||||
|
||||
private GUIComponent inSufficientPowerWarning;
|
||||
@@ -358,9 +363,11 @@ namespace Barotrauma.Items.Components
|
||||
if (inputInventoryHolder != null)
|
||||
{
|
||||
inputContainer.AllowUIOverlap = true;
|
||||
inputContainer.Inventory.DrawWhenEquipped = true;
|
||||
inputContainer.Inventory.RectTransform = inputInventoryHolder.RectTransform;
|
||||
}
|
||||
outputContainer.AllowUIOverlap = true;
|
||||
outputContainer.Inventory.DrawWhenEquipped = true;
|
||||
outputContainer.Inventory.RectTransform = outputInventoryHolder.RectTransform;
|
||||
}
|
||||
|
||||
@@ -453,13 +460,8 @@ namespace Barotrauma.Items.Components
|
||||
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
|
||||
}
|
||||
|
||||
FilterEntities(selectedItemCategory, itemFilterBox?.Text ?? string.Empty);
|
||||
HideEmptyItemListCategories();
|
||||
|
||||
if (selectedItem != null)
|
||||
{
|
||||
//reselect to recreate the info based on the new user's skills
|
||||
SelectItem(character, selectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<FabricationRecipe.RequiredItem, int> missingIngredientCounts = new Dictionary<FabricationRecipe.RequiredItem, int>();
|
||||
@@ -538,6 +540,8 @@ namespace Barotrauma.Items.Components
|
||||
int slotIndex = 0;
|
||||
foreach (var kvp in missingIngredientCounts)
|
||||
{
|
||||
if (inputContainer.Inventory?.visualSlots == null) { break; }
|
||||
|
||||
var requiredItem = kvp.Key;
|
||||
int missingCount = kvp.Value;
|
||||
|
||||
@@ -560,11 +564,10 @@ namespace Barotrauma.Items.Components
|
||||
var requiredItemPrefab = requiredItem.FirstMatchingPrefab;
|
||||
|
||||
float iconAlpha = 0.0f;
|
||||
ItemPrefab requiredItemToDisplay;
|
||||
int count = requiredItem.ItemPrefabs.Count();
|
||||
if (count > 1)
|
||||
ItemPrefab requiredItemToDisplay = requiredItem.DefaultItem.IsEmpty ? null : requiredItem.ItemPrefabs.FirstOrDefault(p => p.Identifier == requiredItem.DefaultItem);
|
||||
if (requiredItemToDisplay == null && requiredItem.ItemPrefabs.Multiple())
|
||||
{
|
||||
float iconCycleSpeed = 0.5f / count;
|
||||
float iconCycleSpeed = 0.75f;
|
||||
float iconCycleT = (float)Timing.TotalTime * iconCycleSpeed;
|
||||
int iconIndex = (int)(iconCycleT % requiredItem.ItemPrefabs.Count());
|
||||
|
||||
@@ -573,7 +576,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
requiredItemToDisplay = requiredItem.ItemPrefabs.FirstOrDefault();
|
||||
requiredItemToDisplay ??= requiredItem.ItemPrefabs.FirstOrDefault();
|
||||
iconAlpha = 1.0f;
|
||||
}
|
||||
if (iconAlpha > 0.0f)
|
||||
@@ -616,9 +619,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (slotRect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
var suitableIngredients = requiredItem.ItemPrefabs.Select(ip => ip.Name).Distinct();
|
||||
LocalizedString toolTipText = string.Join(", ", suitableIngredients.Count() > 3 ? suitableIngredients.SkipLast(suitableIngredients.Count() - 3) : suitableIngredients);
|
||||
if (suitableIngredients.Count() > 3) { toolTipText += "..."; }
|
||||
LocalizedString toolTipText = requiredItem.OverrideHeader;
|
||||
if (requiredItem.OverrideHeader.IsNullOrEmpty())
|
||||
{
|
||||
var suitableIngredients = requiredItem.ItemPrefabs.Where(ip => !ip.HideInMenus).OrderBy(ip => ip.DefaultPrice?.Price ?? 0).Select(ip => ip.Name).Distinct();
|
||||
toolTipText = GetSuitableIngredientText(suitableIngredients);
|
||||
}
|
||||
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
|
||||
{
|
||||
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
|
||||
@@ -656,15 +662,68 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private LocalizedString GetSuitableIngredientText(IEnumerable<LocalizedString> itemNameList)
|
||||
{
|
||||
int count = itemNameList.Count();
|
||||
if (count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
else if (count == 1)
|
||||
{
|
||||
return itemNameList.First();
|
||||
}
|
||||
else if (count == 2)
|
||||
{
|
||||
//[item1] or [item2]
|
||||
return TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsLast",
|
||||
("[treatment1]", itemNameList.ElementAt(0)),
|
||||
("[treatment2]", itemNameList.ElementAt(1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// [item1], [item2], [item3] ... or [lastitem]
|
||||
LocalizedString itemListStr = TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsFirst",
|
||||
("[treatment1]", itemNameList.ElementAt(0)),
|
||||
("[treatment2]", itemNameList.ElementAt(1)));
|
||||
|
||||
int i;
|
||||
bool isTruncated = false;
|
||||
for (i = 2; i < count - 1; i++)
|
||||
{
|
||||
if (itemListStr.Length > 50)
|
||||
{
|
||||
isTruncated = true;
|
||||
break;
|
||||
}
|
||||
itemListStr = TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsFirst",
|
||||
("[treatment1]", itemListStr),
|
||||
("[treatment2]", itemNameList.ElementAt(i)));
|
||||
}
|
||||
itemListStr = TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsLast",
|
||||
("[treatment1]", itemListStr),
|
||||
("[treatment2]", itemNameList.ElementAt(i)));
|
||||
|
||||
if (isTruncated)
|
||||
{
|
||||
itemListStr += TextManager.Get("ellipsis");
|
||||
}
|
||||
return itemListStr;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawOutputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
|
||||
{
|
||||
overlayComponent.RectTransform.SetAsLastChild();
|
||||
|
||||
FabricationRecipe targetItem = fabricatedItem ?? selectedItem;
|
||||
if (targetItem != null)
|
||||
if (targetItem != null && outputContainer.Inventory?.visualSlots != null)
|
||||
{
|
||||
Rectangle slotRect = outputContainer.Inventory.visualSlots[0].Rect;
|
||||
|
||||
if (fabricatedItem != null)
|
||||
{
|
||||
float clampedProgressState = Math.Clamp(progressState, 0f, 1f);
|
||||
@@ -699,6 +758,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
FabricationRecipe recipe = child.UserData as FabricationRecipe;
|
||||
if (recipe?.DisplayName == null) { continue; }
|
||||
|
||||
if (recipe.HideForNonTraitors)
|
||||
{
|
||||
if (Character.Controlled is not { IsTraitor: true })
|
||||
{
|
||||
child.Visible = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
child.Visible =
|
||||
(string.IsNullOrWhiteSpace(filter) || recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase)) &&
|
||||
(!category.HasValue || recipe.TargetItem.Category.HasFlag(category.Value));
|
||||
@@ -749,8 +818,9 @@ namespace Barotrauma.Items.Components
|
||||
private bool SelectItem(Character user, FabricationRecipe selectedItem, float? overrideRequiredTime = null)
|
||||
{
|
||||
this.selectedItem = selectedItem;
|
||||
displayingForCharacter = user;
|
||||
|
||||
int max = Math.Max(selectedItem.TargetItem.MaxStackSize / selectedItem.Amount, 1);
|
||||
int max = Math.Max(selectedItem.TargetItem.GetMaxStackSize(outputContainer.Inventory) / selectedItem.Amount, 1);
|
||||
|
||||
if (amountInput != null)
|
||||
{
|
||||
@@ -924,7 +994,7 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
activateButton.Enabled = false;
|
||||
inSufficientPowerWarning.Visible = IsActive && !hasPower;
|
||||
@@ -933,6 +1003,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!IsActive)
|
||||
{
|
||||
if (selectedItem != null && displayingForCharacter != character)
|
||||
{
|
||||
//reselect to recreate the info based on the new user's skills
|
||||
SelectItem(character, selectedItem);
|
||||
}
|
||||
|
||||
//only check ingredients if the fabricator isn't active (if it is, this is done in Update)
|
||||
if (refreshIngredientsTimer <= 0.0f)
|
||||
{
|
||||
@@ -998,9 +1074,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
fabricationLimits[msg.ReadUInt32()] = 0;
|
||||
}
|
||||
|
||||
State = newState;
|
||||
this.amountToFabricate = amountToFabricate;
|
||||
//don't touch the amount unless another character changed it or the fabricator is running
|
||||
//otherwise we may end up reverting the changes the client just did to the amount
|
||||
if ((user != null && user != Character.Controlled) || State != FabricatorState.Stopped)
|
||||
{
|
||||
this.amountToFabricate = amountToFabricate;
|
||||
}
|
||||
this.amountRemaining = amountRemaining;
|
||||
if (newState == FabricatorState.Stopped || recipeHash == 0)
|
||||
{
|
||||
|
||||
@@ -295,7 +295,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
OrderPrefab[] reports = OrderPrefab.Prefabs.Where(o => o.IsReport && o.SymbolSprite != null && !o.Hidden).OrderBy(o => o.Identifier).ToArray();
|
||||
OrderPrefab[] reports = OrderPrefab.Prefabs.Where(o => o.IsVisibleAsReportButton).OrderBy(o => o.Identifier).ToArray();
|
||||
|
||||
GUIFrame bottomFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.15f), paddedContainer.RectTransform, Anchor.BottomCenter) { MaxSize = new Point(int.MaxValue, GUI.IntScale(40)) }, style: null)
|
||||
{
|
||||
@@ -350,10 +350,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
};
|
||||
|
||||
List<ItemPrefab> shownItemPrefabs = new List<ItemPrefab>();
|
||||
foreach (ItemPrefab prefab in ItemPrefab.Prefabs.OrderBy(prefab => prefab.Name))
|
||||
{
|
||||
if (prefab.HideInMenus) { continue; }
|
||||
if (shownItemPrefabs.Any(ip => DisplayAsSameItem(ip, prefab)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CreateItemFrame(prefab, listBox.Content.RectTransform);
|
||||
shownItemPrefabs.Add(prefab);
|
||||
}
|
||||
|
||||
searchBar.OnDeselected += (sender, key) =>
|
||||
@@ -398,6 +404,27 @@ namespace Barotrauma.Items.Components
|
||||
new Point(int.MaxValue, paddedContainer.Rect.Height - bottomFrame.Rect.Height - buttonLayout.Rect.Height);
|
||||
}
|
||||
|
||||
private static Sprite GetPreviewSprite(ItemPrefab prefab)
|
||||
{
|
||||
return prefab.InventoryIcon ?? prefab.Sprite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the items have an identical name and icon (e.g. a variant with an alternative fabrication/deconstruction recipe),
|
||||
/// they're displayed as if they were the same item, not as two separate entries.
|
||||
/// </summary>
|
||||
private static bool DisplayAsSameItem(ItemPrefab prefab1, ItemPrefab prefab2)
|
||||
{
|
||||
if (prefab1 == prefab2) { return true; }
|
||||
if (prefab1.Name == prefab2.Name)
|
||||
{
|
||||
var sprite1 = GetPreviewSprite(prefab1);
|
||||
var sprite2 = GetPreviewSprite(prefab2);
|
||||
return sprite1?.FullPath == sprite2?.FullPath && sprite1?.SourceRect == sprite2?.SourceRect;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool VisibleOnItemFinder(Item it)
|
||||
{
|
||||
if (it?.Submarine == null) { return false; }
|
||||
@@ -413,7 +440,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (it.Container?.GetComponent<ItemContainer>() is { DrawInventory: false } or { AllowAccess: false }) { return false; }
|
||||
|
||||
if (it.HasTag("traitormissionitem")) { return false; }
|
||||
if (it.HasTag(Tags.TraitorMissionItem)) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -539,13 +566,13 @@ namespace Barotrauma.Items.Components
|
||||
displayedSubs.Add(item.Submarine);
|
||||
displayedSubs.AddRange(item.Submarine.DockedTo.Where(s => s.TeamID == item.Submarine.TeamID));
|
||||
|
||||
subEntities = MapEntity.mapEntityList.Where(me => (item.Submarine is { } sub && sub.IsEntityFoundOnThisSub(me, includingConnectedSubs: true, allowDifferentType: false)) && !me.HiddenInGame).OrderByDescending(w => w.SpriteDepth).ToList();
|
||||
subEntities = MapEntity.MapEntityList.Where(me => (item.Submarine is { } sub && sub.IsEntityFoundOnThisSub(me, includingConnectedSubs: true, allowDifferentType: false)) && !me.HiddenInGame).OrderByDescending(w => w.SpriteDepth).ToList();
|
||||
|
||||
BakeSubmarine(item.Submarine, parentRect);
|
||||
elementSize = GuiFrame.Rect.Size;
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
//recreate HUD if the subs we should display have changed
|
||||
if (item.Submarine == null && displayedSubs.Count > 0 || // item not inside a sub anymore, but display is still showing subs
|
||||
@@ -824,7 +851,8 @@ namespace Barotrauma.Items.Components
|
||||
foreach (GUIComponent component in listBox.Content.Children)
|
||||
{
|
||||
component.Visible = false;
|
||||
if (component.UserData is ItemPrefab { Name: { } prefabName} prefab && itemsFoundOnSub.Contains(prefab))
|
||||
if (component.UserData is ItemPrefab { Name: { } prefabName} prefab &&
|
||||
(itemsFoundOnSub.Contains(prefab) || itemsFoundOnSub.Any(ip => DisplayAsSameItem(ip, prefab))))
|
||||
{
|
||||
component.Visible = prefabName.ToLower().Contains(text.ToLower());
|
||||
|
||||
@@ -851,9 +879,9 @@ namespace Barotrauma.Items.Components
|
||||
tooltip.RectTransform.ScreenSpaceOffset = new Point(box.Rect.X, box.Rect.Y - height);
|
||||
}
|
||||
|
||||
private void CreateItemFrame(ItemPrefab prefab, RectTransform parent)
|
||||
private static void CreateItemFrame(ItemPrefab prefab, RectTransform parent)
|
||||
{
|
||||
Sprite sprite = prefab.InventoryIcon ?? prefab.Sprite;
|
||||
Sprite sprite = GetPreviewSprite(prefab);
|
||||
if (sprite is null) { return; }
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Vector2(1f, 0.25f), parent), style: "ListBoxElement")
|
||||
{
|
||||
@@ -899,7 +927,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!VisibleOnItemFinder(it)) { continue; }
|
||||
|
||||
if (it.Prefab == searchedPrefab)
|
||||
if (DisplayAsSameItem(it.Prefab, searchedPrefab))
|
||||
{
|
||||
// ignore items on players and hidden inventories
|
||||
if (it.FindParentInventory(inv => inv is CharacterInventory || inv is ItemInventory { Owner: Item { HiddenInGame: true }}) is { }) { continue; }
|
||||
@@ -1079,7 +1107,7 @@ namespace Barotrauma.Items.Components
|
||||
if (ShowHullIntegrity)
|
||||
{
|
||||
float amount = 1f + hullData.LinkedHulls.Count;
|
||||
gapOpenSum = hull.ConnectedGaps.Concat(hullData.LinkedHulls.SelectMany(h => h.ConnectedGaps)).Where(g => !g.IsRoomToRoom && !g.HiddenInGame).Sum(g => g.Open) / amount;
|
||||
gapOpenSum = hull.ConnectedGaps.Concat(hullData.LinkedHulls.SelectMany(h => h.ConnectedGaps)).Where(g => g.linkedTo.Count == 1 && !g.HiddenInGame).Sum(g => g.Open) / amount;
|
||||
borderColor = Color.Lerp(neutralColor, GUIStyle.Red, Math.Min(gapOpenSum, 1.0f));
|
||||
}
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float flickerTimer;
|
||||
private readonly float flickerFrequency = 1;
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
autoControlIndicator.Selected = IsAutoControlled;
|
||||
PowerButton.Enabled = isActiveLockTimer <= 0.0f;
|
||||
|
||||
@@ -615,7 +615,7 @@ namespace Barotrauma.Items.Components
|
||||
turbineOutputMeter, TurbineOutput, new Vector2(0.0f, 100.0f), clampedOptimalTurbineOutput, clampedAllowedTurbineOutput);
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Barotrauma.Items.Components
|
||||
},
|
||||
{
|
||||
BlipType.Destructible,
|
||||
new Color[] { Color.TransparentBlack, new Color(74, 113, 75) * 0.8f, new Color(151, 236, 172) * 0.8f, new Color(153, 217, 234) * 0.8f }
|
||||
new Color[] { Color.TransparentBlack, new Color(94, 114, 73) * 0.8f, new Color(255, 236, 151) * 0.8f, new Color(242, 243, 194) * 0.8f }
|
||||
},
|
||||
{
|
||||
BlipType.Door,
|
||||
@@ -358,11 +358,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected override void TryCreateDragHandle()
|
||||
{
|
||||
base.TryCreateDragHandle();
|
||||
}
|
||||
|
||||
private void SetPingDirection(Vector2 direction)
|
||||
{
|
||||
pingDirection = direction;
|
||||
@@ -471,7 +466,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
showDirectionalIndicatorTimer -= deltaTime;
|
||||
if (GameMain.Client != null)
|
||||
@@ -981,38 +976,41 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
if (GameMain.GameSession == null) { return; }
|
||||
|
||||
if (Level.Loaded.StartLocation?.Type is { ShowSonarMarker: true })
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.StartLocation.Name,
|
||||
(Level.Loaded.StartOutpost != null ? "outpost" : "location").ToIdentifier(),
|
||||
"startlocation",
|
||||
Level.Loaded.StartExitPosition, transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
if (Level.Loaded.StartLocation?.Type is { ShowSonarMarker: true })
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.StartLocation.Name,
|
||||
(Level.Loaded.StartOutpost != null ? "outpost" : "location").ToIdentifier(),
|
||||
"startlocation",
|
||||
Level.Loaded.StartExitPosition, transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
|
||||
if (Level.Loaded is { EndLocation.Type.ShowSonarMarker: true, Type: LevelData.LevelType.LocationConnection })
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.EndLocation.Name,
|
||||
(Level.Loaded.EndOutpost != null ? "outpost" : "location").ToIdentifier(),
|
||||
"endlocation",
|
||||
Level.Loaded.EndExitPosition, transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
if (Level.Loaded is { EndLocation.Type.ShowSonarMarker: true, Type: LevelData.LevelType.LocationConnection })
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.EndLocation.Name,
|
||||
(Level.Loaded.EndOutpost != null ? "outpost" : "location").ToIdentifier(),
|
||||
"endlocation",
|
||||
Level.Loaded.EndExitPosition, transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Level.Loaded.Caves.Count; i++)
|
||||
{
|
||||
var cave = Level.Loaded.Caves[i];
|
||||
if (!cave.DisplayOnSonar) { continue; }
|
||||
DrawMarker(spriteBatch,
|
||||
caveLabel.Value,
|
||||
"cave".ToIdentifier(),
|
||||
"cave" + i,
|
||||
cave.StartPos.ToVector2(), transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
for (int i = 0; i < Level.Loaded.Caves.Count; i++)
|
||||
{
|
||||
var cave = Level.Loaded.Caves[i];
|
||||
if (cave.MissionsToDisplayOnSonar.None()) { continue; }
|
||||
DrawMarker(spriteBatch,
|
||||
caveLabel.Value,
|
||||
"cave".ToIdentifier(),
|
||||
"cave" + i,
|
||||
cave.StartPos.ToVector2(), transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
}
|
||||
|
||||
int missionIndex = 0;
|
||||
@@ -1070,7 +1068,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!sub.ShowSonarMarker) { continue; }
|
||||
if (connectedSubs.Contains(sub)) { continue; }
|
||||
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
if (Level.Loaded != null && sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
|
||||
if (item.Submarine != null || Character.Controlled != null)
|
||||
{
|
||||
|
||||
@@ -532,6 +532,22 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map the rectangular steering vector to a circular area using FG-Squircular Mapping which preserves the angle of the vector.
|
||||
/// </summary>
|
||||
private static Vector2 MapSquareToCircle(Vector2 steeringVector)
|
||||
{
|
||||
float xSqr = steeringVector.X * steeringVector.X;
|
||||
float ySqr = steeringVector.Y * steeringVector.Y;
|
||||
float length = MathF.Sqrt(ySqr + xSqr);
|
||||
if (MathUtils.NearlyEqual(length, 0.0f)) { return Vector2.Zero; }
|
||||
|
||||
//FG-Squircular mapping formula from https://arxiv.org/ftp/arxiv/papers/1509/1509.06344.pdf
|
||||
float x = steeringVector.X * MathF.Sqrt(xSqr + ySqr - xSqr * ySqr) / length;
|
||||
float y = steeringVector.Y * MathF.Sqrt(xSqr + ySqr - xSqr * ySqr) / length;
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
public void DrawHUD(SpriteBatch spriteBatch, Rectangle rect)
|
||||
{
|
||||
int width = rect.Width, height = rect.Height;
|
||||
@@ -545,11 +561,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!AutoPilot)
|
||||
{
|
||||
Vector2 unitSteeringInput = steeringInput / 100.0f;
|
||||
//map input from rectangle to circle
|
||||
Vector2 steeringInputPos = new Vector2(
|
||||
steeringInput.X * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.Y * unitSteeringInput.Y),
|
||||
-steeringInput.Y * (float)Math.Sqrt(1.0f - 0.5f * unitSteeringInput.X * unitSteeringInput.X));
|
||||
Vector2 steeringInputPos = MapSquareToCircle(steeringInput / 100f) * 100.0f;
|
||||
steeringInputPos.Y = -steeringInputPos.Y;
|
||||
steeringInputPos += steeringOrigin;
|
||||
|
||||
if (steeringIndicator != null)
|
||||
@@ -604,10 +618,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//map velocity from rectangle to circle
|
||||
Vector2 unitTargetVel = targetVelocity / 100.0f;
|
||||
Vector2 steeringPos = new Vector2(
|
||||
targetVelocity.X * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.Y * unitTargetVel.Y),
|
||||
-targetVelocity.Y * 0.9f * (float)Math.Sqrt(1.0f - 0.5f * unitTargetVel.X * unitTargetVel.X));
|
||||
Vector2 steeringPos = MapSquareToCircle(targetVelocity / 100f) * 90.0f;
|
||||
steeringPos.Y = -steeringPos.Y;
|
||||
steeringPos += steeringOrigin;
|
||||
|
||||
if (steeringIndicator != null)
|
||||
@@ -682,7 +694,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (swapDestinationOrder == null)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
for (var i = 0; i < GrowableSeeds.Length; i++)
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (chargeIndicator != null)
|
||||
{
|
||||
@@ -131,7 +131,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
Vector2 scaledIndicatorSize = indicatorSize * item.Scale;
|
||||
if (scaledIndicatorSize.X <= 2.0f || scaledIndicatorSize.Y <= 2.0f) { return; }
|
||||
|
||||
@@ -10,6 +10,10 @@ namespace Barotrauma.Items.Components
|
||||
private GUITickBox highVoltageIndicator;
|
||||
private GUITickBox lowVoltageIndicator;
|
||||
|
||||
private GUITextBlock powerLabel, loadLabel;
|
||||
|
||||
private LanguageIdentifier prevLanguage;
|
||||
|
||||
partial void InitProjectSpecific(XElement element)
|
||||
{
|
||||
if (GuiFrame == null) { return; }
|
||||
@@ -56,12 +60,12 @@ namespace Barotrauma.Items.Components
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var powerLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), upperTextArea.RectTransform),
|
||||
powerLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), upperTextArea.RectTransform),
|
||||
TextManager.Get("PowerTransferPowerLabel"), textColor: GUIStyle.TextColorBright, font: GUIStyle.LargeFont, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
ToolTip = TextManager.Get("PowerTransferTipPower")
|
||||
};
|
||||
var loadLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), lowerTextArea.RectTransform),
|
||||
loadLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), lowerTextArea.RectTransform),
|
||||
TextManager.Get("PowerTransferLoadLabel"), textColor: GUIStyle.TextColorBright, font: GUIStyle.LargeFont, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
ToolTip = TextManager.Get("PowerTransferTipLoad")
|
||||
@@ -75,7 +79,7 @@ namespace Barotrauma.Items.Components
|
||||
ToolTip = TextManager.Get("PowerTransferTipPower"),
|
||||
TextGetter = () => {
|
||||
float currPower = powerLoad < 0 ? -powerLoad: 0;
|
||||
if (!(this is RelayComponent) && PowerConnections != null && PowerConnections.Count > 0 && PowerConnections[0].Grid != null)
|
||||
if (this is not RelayComponent && PowerConnections != null && PowerConnections.Count > 0 && PowerConnections[0].Grid != null)
|
||||
{
|
||||
currPower = PowerConnections[0].Grid.Power;
|
||||
}
|
||||
@@ -119,9 +123,11 @@ namespace Barotrauma.Items.Components
|
||||
GUITextBlock.AutoScaleAndNormalize(powerLabel, loadLabel);
|
||||
GUITextBlock.AutoScaleAndNormalize(true, true, powerText, loadText);
|
||||
GUITextBlock.AutoScaleAndNormalize(kw1, kw2);
|
||||
|
||||
prevLanguage = GameSettings.CurrentConfig.Language;
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (GuiFrame == null) return;
|
||||
|
||||
@@ -129,6 +135,13 @@ namespace Barotrauma.Items.Components
|
||||
powerIndicator.Selected = IsActive && voltage > 0;
|
||||
highVoltageIndicator.Selected = Timing.TotalTime % 0.5f < 0.25f && powerIndicator.Selected && voltage > 1.2f;
|
||||
lowVoltageIndicator.Selected = Timing.TotalTime % 0.5f < 0.25f && powerIndicator.Selected && voltage < 0.8f;
|
||||
|
||||
if (prevLanguage != GameSettings.CurrentConfig.Language)
|
||||
{
|
||||
GUITextBlock.AutoScaleAndNormalize(powerIndicator.TextBlock, highVoltageIndicator.TextBlock, lowVoltageIndicator.TextBlock);
|
||||
GUITextBlock.AutoScaleAndNormalize(powerLabel, loadLabel);
|
||||
prevLanguage = GameSettings.CurrentConfig.Language;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
float rotation = msg.ReadSingle();
|
||||
spreadIndex = msg.ReadByte();
|
||||
ushort submarineID = msg.ReadUInt16();
|
||||
if (User != null)
|
||||
{
|
||||
Shoot(User, simPosition, simPosition, rotation, ignoredBodies: User.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: false);
|
||||
item.Submarine = Entity.FindEntityByID(submarineID) as Submarine;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -42,59 +44,98 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 axis = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
UInt16 entityID = msg.ReadUInt16();
|
||||
StickTargetType targetType = (StickTargetType)msg.ReadByte();
|
||||
|
||||
Entity entity = Entity.FindEntityByID(entityID);
|
||||
Submarine submarine = Entity.FindEntityByID(submarineID) as Submarine;
|
||||
Hull hull = Entity.FindEntityByID(hullID) as Hull;
|
||||
Hull hull = Entity.FindEntityByID(hullID) as Hull;
|
||||
item.Submarine = submarine;
|
||||
item.CurrentHull = hull;
|
||||
item.body.SetTransform(simPosition, item.body.Rotation);
|
||||
if (entity is Character character)
|
||||
|
||||
switch (targetType)
|
||||
{
|
||||
byte limbIndex = msg.ReadByte();
|
||||
if (limbIndex >= character.AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Limb index out of bounds ({limbIndex}, character: {character.ToString()})");
|
||||
return;
|
||||
}
|
||||
if (character.Removed) { return; }
|
||||
var limb = character.AnimController.Limbs[limbIndex];
|
||||
StickToTarget(limb.body.FarseerBody, axis);
|
||||
}
|
||||
else if (entity is Structure structure)
|
||||
{
|
||||
byte bodyIndex = msg.ReadByte();
|
||||
if (bodyIndex == 255) { bodyIndex = 0; }
|
||||
if (bodyIndex >= structure.Bodies.Count)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Structure body index out of bounds ({bodyIndex}, structure: {structure.ToString()})");
|
||||
return;
|
||||
}
|
||||
var body = structure.Bodies[bodyIndex];
|
||||
StickToTarget(body, axis);
|
||||
}
|
||||
else if (entity is Item item)
|
||||
{
|
||||
if (item.Removed) { return; }
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door != null)
|
||||
{
|
||||
StickToTarget(door.Body.FarseerBody, axis);
|
||||
}
|
||||
else if (item.body != null)
|
||||
{
|
||||
StickToTarget(item.body.FarseerBody, axis);
|
||||
}
|
||||
}
|
||||
else if (entity is Submarine sub)
|
||||
{
|
||||
StickToTarget(sub.PhysicsBody.FarseerBody, axis);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Invalid stick target ({entity?.ToString() ?? "null"}, {entityID})");
|
||||
}
|
||||
case StickTargetType.Structure:
|
||||
UInt16 structureId = msg.ReadUInt16();
|
||||
byte bodyIndex = msg.ReadByte();
|
||||
if (Entity.FindEntityByID(structureId) is Structure structure)
|
||||
{
|
||||
if (bodyIndex == 255) { bodyIndex = 0; }
|
||||
if (bodyIndex >= structure.Bodies.Count)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Structure body index out of bounds ({bodyIndex}, structure: {structure})");
|
||||
return;
|
||||
}
|
||||
var body = structure.Bodies[bodyIndex];
|
||||
StickToTarget(body, axis);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"\"{item.Prefab.Identifier}\" failed to stick to a structure. Could not find a structure with the ID {structureId}");
|
||||
}
|
||||
break;
|
||||
case StickTargetType.Limb:
|
||||
UInt16 characterId = msg.ReadUInt16();
|
||||
byte limbIndex = msg.ReadByte();
|
||||
if (Entity.FindEntityByID(characterId) is Character character)
|
||||
{
|
||||
if (limbIndex >= character.AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Limb index out of bounds ({limbIndex}, character: {character})");
|
||||
return;
|
||||
}
|
||||
if (character.Removed) { return; }
|
||||
var limb = character.AnimController.Limbs[limbIndex];
|
||||
StickToTarget(limb.body.FarseerBody, axis);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"\"{this.item.Prefab.Identifier}\" failed to stick to a limb. Could not find a character with the ID {characterId}");
|
||||
}
|
||||
break;
|
||||
case StickTargetType.Item:
|
||||
UInt16 itemID = msg.ReadUInt16();
|
||||
if (Entity.FindEntityByID(itemID) is Item targetItem)
|
||||
{
|
||||
if (targetItem.Removed) { return; }
|
||||
var door = targetItem.GetComponent<Door>();
|
||||
if (door != null)
|
||||
{
|
||||
StickToTarget(door.Body.FarseerBody, axis);
|
||||
}
|
||||
else if (targetItem.body != null)
|
||||
{
|
||||
StickToTarget(targetItem.body.FarseerBody, axis);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"\"{this.item.Prefab.Identifier}\" failed to stick to an item. Could not find n item with the ID {itemID}");
|
||||
}
|
||||
break;
|
||||
case StickTargetType.Submarine:
|
||||
UInt16 targetSubmarineId = msg.ReadUInt16();
|
||||
if (Entity.FindEntityByID(targetSubmarineId) is Submarine targetSub)
|
||||
{
|
||||
StickToTarget(targetSub.PhysicsBody.FarseerBody, axis);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"\"{item.Prefab.Identifier}\" failed to stick to a submarine. Could not find a structure with the ID {targetSubmarineId}");
|
||||
}
|
||||
break;
|
||||
case StickTargetType.LevelWall:
|
||||
int levelWallIndex = msg.ReadInt32();
|
||||
var allCells = Level.Loaded.GetAllCells();
|
||||
if (levelWallIndex >= 0 && levelWallIndex < allCells.Count)
|
||||
{
|
||||
StickToTarget(allCells[levelWallIndex].Body, axis);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to read a projectile update from the server. Level wall index out of bounds ({levelWallIndex}, wall count: {allCells.Count})");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma.Items.Components
|
||||
currentTarget?.DrawHUD(spriteBatch, Screen.Selected.Cam, character);
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
currentTarget?.UpdateHUD(cam, character,deltaTime);
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (GameMain.DebugDraw && IsActive)
|
||||
{
|
||||
|
||||
@@ -373,12 +373,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (GameMain.DebugDraw && Character.Controlled?.FocusedItem == item)
|
||||
{
|
||||
bool paused = !ShouldDeteriorate();
|
||||
if (deteriorationTimer > 0.0f)
|
||||
bool paused = !ShouldDeteriorate() && ForceDeteriorationTimer <= 0.0f;
|
||||
if (ForceDeteriorationTimer > 0.0f)
|
||||
{
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), "Forced deterioration for " + ((int)ForceDeteriorationTimer) + " s",
|
||||
Color.Red, Color.Black * 0.5f);
|
||||
|
||||
}
|
||||
else if (deteriorationTimer > 0.0f)
|
||||
{
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), "Deterioration delay " + ((int)deteriorationTimer) + (paused ? " [PAUSED]" : ""),
|
||||
@@ -430,8 +437,7 @@ namespace Barotrauma.Items.Components
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
deteriorationTimer = msg.ReadSingle();
|
||||
deteriorateAlwaysResetTimer = msg.ReadSingle();
|
||||
DeteriorateAlways = msg.ReadBoolean();
|
||||
ForceDeteriorationTimer = msg.ReadSingle();
|
||||
tinkeringDuration = msg.ReadSingle();
|
||||
tinkeringStrength = msg.ReadSingle();
|
||||
tinkeringPowersDevices = msg.ReadBoolean();
|
||||
|
||||
@@ -55,20 +55,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 GetSourcePos()
|
||||
{
|
||||
Vector2 sourcePos = source.WorldPosition;
|
||||
if (source is Item sourceItem)
|
||||
{
|
||||
sourcePos = sourceItem.DrawPosition;
|
||||
}
|
||||
else if (source is Limb sourceLimb && sourceLimb.body != null)
|
||||
{
|
||||
sourcePos = sourceLimb.body.DrawPosition;
|
||||
}
|
||||
return sourcePos;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
foreach (var subElement in element.Elements())
|
||||
@@ -88,34 +74,22 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (target == null || target.Removed) { return; }
|
||||
if (target.ParentInventory != null) { return; }
|
||||
if (source is Limb limb && limb.Removed) { return; }
|
||||
if (source is Entity e && e.Removed) { return; }
|
||||
|
||||
Vector2 startPos = GetSourcePos();
|
||||
Vector2 startPos = GetSourcePos(useDrawPosition: true);
|
||||
startPos.Y = -startPos.Y;
|
||||
if (source is Item sourceItem && !sourceItem.Removed)
|
||||
if ((source as Item)?.GetComponent<Turret>() is { } turret)
|
||||
{
|
||||
var turret = sourceItem.GetComponent<Turret>();
|
||||
var weapon = sourceItem.GetComponent<RangedWeapon>();
|
||||
if (turret != null)
|
||||
if (turret.BarrelSprite != null)
|
||||
{
|
||||
startPos = new Vector2(sourceItem.WorldRect.X + turret.TransformedBarrelPos.X, -(sourceItem.WorldRect.Y - turret.TransformedBarrelPos.Y));
|
||||
if (turret.BarrelSprite != null)
|
||||
{
|
||||
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
|
||||
}
|
||||
startPos -= turret.GetRecoilOffset();
|
||||
}
|
||||
else if (weapon != null)
|
||||
{
|
||||
Vector2 barrelPos = FarseerPhysics.ConvertUnits.ToDisplayUnits(weapon.TransformedBarrelPos);
|
||||
barrelPos.Y = -barrelPos.Y;
|
||||
startPos += barrelPos;
|
||||
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
|
||||
}
|
||||
startPos -= turret.GetRecoilOffset();
|
||||
}
|
||||
Vector2 endPos = new Vector2(target.DrawPosition.X, target.DrawPosition.Y);
|
||||
Vector2 flippedPos = target.Sprite.size * target.Scale * (Origin - new Vector2(0.5f));
|
||||
@@ -156,28 +130,28 @@ namespace Barotrauma.Items.Components
|
||||
if (startSprite != null)
|
||||
{
|
||||
float depth = Math.Min(item.GetDrawDepth() + (startSprite.Depth - item.Sprite.Depth), 0.999f);
|
||||
startSprite?.Draw(spriteBatch, startPos, SpriteColor, angle, depth: depth);
|
||||
startSprite?.Draw(spriteBatch, startPos, overrideColor ?? SpriteColor, angle, depth: depth);
|
||||
}
|
||||
if (endSprite != null && (!Snapped || BreakFromMiddle))
|
||||
{
|
||||
float depth = Math.Min(item.GetDrawDepth() + (endSprite.Depth - item.Sprite.Depth), 0.999f);
|
||||
endSprite?.Draw(spriteBatch, endPos, SpriteColor, angle, depth: depth);
|
||||
endSprite?.Draw(spriteBatch, endPos, overrideColor ?? SpriteColor, angle, depth: depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRope(SpriteBatch spriteBatch, Vector2 startPos, Vector2 endPos, int width)
|
||||
private void DrawRope(SpriteBatch spriteBatch, Vector2 startPos, Vector2 endPos, int width, Color? overrideColor = null)
|
||||
{
|
||||
float depth = sprite == null ?
|
||||
item.Sprite.Depth + 0.001f :
|
||||
Math.Min(item.GetDrawDepth() + (sprite.Depth - item.Sprite.Depth), 0.999f);
|
||||
|
||||
|
||||
if (sprite?.Texture == null)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch,
|
||||
startPos,
|
||||
endPos,
|
||||
SpriteColor, depth: depth, width: width);
|
||||
overrideColor ?? SpriteColor, depth: depth, width: width);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,7 +165,7 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawLine(spriteBatch, sprite,
|
||||
startPos + dir * (x - 5.0f),
|
||||
startPos + dir * (x + sprite.size.X),
|
||||
SpriteColor, depth: depth, width: width);
|
||||
overrideColor ?? SpriteColor, depth: depth, width: width);
|
||||
}
|
||||
float leftOver = length - x;
|
||||
if (leftOver > 0.0f)
|
||||
@@ -199,7 +173,7 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawLine(spriteBatch, sprite,
|
||||
startPos + dir * (x - 5.0f),
|
||||
endPos,
|
||||
SpriteColor, depth: depth, width: width);
|
||||
overrideColor ?? SpriteColor, depth: depth, width: width);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -207,7 +181,7 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawLine(spriteBatch, sprite,
|
||||
startPos,
|
||||
endPos,
|
||||
SpriteColor, depth: depth, width: width);
|
||||
overrideColor ?? SpriteColor, depth: depth, width: width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
internal sealed partial class CircuitBox
|
||||
{
|
||||
public CircuitBoxUI? UI;
|
||||
public readonly Dictionary<Character, CircuitBoxCursor> ActiveCursors = new Dictionary<Character, CircuitBoxCursor>();
|
||||
public Option<ItemPrefab> HeldComponent = Option.None;
|
||||
|
||||
private const float CursorUpdateInterval = 1f;
|
||||
private float cursorUpdateTimer;
|
||||
|
||||
private readonly Vector2[] recordedCursorPositions = new Vector2[10];
|
||||
private Option<Vector2> recordedDragStart = Option.None;
|
||||
private Option<ItemPrefab> recordedHeldPrefab = Option.None;
|
||||
|
||||
/// <summary>
|
||||
/// If the circuit box was initialized by the server instead of from the save file.
|
||||
/// Used to ensure the wires the server sends are properly connected up when we load in.
|
||||
/// </summary>
|
||||
private bool wasInitializedByServer;
|
||||
|
||||
public Sprite? WireSprite { get; private set; }
|
||||
public Sprite? ConnectionSprite { get; private set; }
|
||||
public Sprite? WireConnectorSprite { get; private set; }
|
||||
public Sprite? ConnectionScrewSprite { get; private set; }
|
||||
public UISprite? NodeFrameSprite { get; private set; }
|
||||
public UISprite? NodeTopSprite { get; private set; }
|
||||
|
||||
protected override void CreateGUI()
|
||||
{
|
||||
base.CreateGUI();
|
||||
GuiFrame.ClearChildren();
|
||||
UI?.CreateGUI(GuiFrame);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
UI = new CircuitBoxUI(this);
|
||||
IsActive = true;
|
||||
CreateGUI();
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "wiresprite":
|
||||
WireSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "connectionsprite":
|
||||
ConnectionSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "wireconnectorsprite":
|
||||
WireConnectorSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "connectionscrewsprite":
|
||||
ConnectionScrewSprite = new Sprite(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GUIStyle.GetComponentStyle("CircuitBoxTop") is { } topStyle)
|
||||
{
|
||||
NodeTopSprite = topStyle.Sprites[GUIComponent.ComponentState.None][0];
|
||||
}
|
||||
|
||||
if (GUIStyle.GetComponentStyle("CircuitBoxFrame") is { } compStyle)
|
||||
{
|
||||
NodeFrameSprite = compStyle.Sprites[GUIComponent.ComponentState.None][0];
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ShouldDrawHUD(Character character)
|
||||
=> character == Character.Controlled && (character.SelectedItem == item || character.SelectedSecondaryItem == item);
|
||||
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (UI is null) { return; }
|
||||
|
||||
UI.Update(deltaTime);
|
||||
|
||||
if (GameMain.NetworkMember is null) { return; }
|
||||
|
||||
foreach (var (cursorChar, cursor) in ActiveCursors)
|
||||
{
|
||||
if (!cursor.IsActive) { continue; }
|
||||
|
||||
ActiveCursors[cursorChar].Update(deltaTime);
|
||||
}
|
||||
|
||||
Vector2 cursorPos = UI.GetCursorPosition();
|
||||
int lastCursorPosIndex = recordedCursorPositions.Length - 1;
|
||||
|
||||
if (cursorUpdateTimer < CursorUpdateInterval)
|
||||
{
|
||||
cursorUpdateTimer += deltaTime;
|
||||
int cursorIndex = (int)MathF.Floor(cursorUpdateTimer * lastCursorPosIndex);
|
||||
RecordCursorPosition(cursorIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
RecordCursorPosition(lastCursorPosIndex);
|
||||
SendCursorState(recordedCursorPositions, recordedDragStart, recordedHeldPrefab.Select(static c => c.Identifier));
|
||||
|
||||
recordedDragStart = Option.None;
|
||||
recordedHeldPrefab = Option.None;
|
||||
cursorUpdateTimer = 0f;
|
||||
}
|
||||
|
||||
void RecordCursorPosition(int index)
|
||||
{
|
||||
var dragStart = UI.GetDragStart();
|
||||
if (dragStart.IsSome()) { recordedDragStart = dragStart; }
|
||||
|
||||
var heldComponent = HeldComponent;
|
||||
if (heldComponent.IsSome()) { recordedHeldPrefab = heldComponent; }
|
||||
|
||||
if (index >= 0 && index < recordedCursorPositions.Length) { recordedCursorPositions[index] = cursorPos; }
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveComponents(IReadOnlyCollection<CircuitBoxComponent> node)
|
||||
{
|
||||
var ids = node.Select(static n => n.ID).ToImmutableArray();
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
CreateRefundItemsForUsedResources(ids, Character.Controlled);
|
||||
RemoveComponentInternal(ids);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.Any()) { return; }
|
||||
|
||||
CreateClientEvent(new CircuitBoxRemoveComponentEvent(ids));
|
||||
}
|
||||
|
||||
public void AddWire(CircuitBoxConnection one, CircuitBoxConnection two)
|
||||
{
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
Connect(one, two, static delegate { }, CircuitBoxWire.SelectedWirePrefab);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VerifyConnection(one, two)) { return; }
|
||||
|
||||
CreateClientEvent(new CircuitBoxClientAddWireEvent(Color.White, CircuitBoxConnectorIdentifier.FromConnection(one), CircuitBoxConnectorIdentifier.FromConnection(two), CircuitBoxWire.SelectedWirePrefab.UintIdentifier));
|
||||
}
|
||||
|
||||
public void RemoveWires(IReadOnlyCollection<CircuitBoxWire> wires)
|
||||
{
|
||||
var ids = wires.Select(static w => w.ID).ToImmutableArray();
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
RemoveWireInternal(ids);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ids.Any()) { return; }
|
||||
CreateClientEvent(new CircuitBoxRemoveWireEvent(ids));
|
||||
}
|
||||
|
||||
public void SelectComponents(IReadOnlyCollection<CircuitBoxNode> moveables, bool overwrite)
|
||||
{
|
||||
if (Character.Controlled is not { ID: var controlledId }) { return; }
|
||||
|
||||
var ids = ImmutableArray.CreateBuilder<ushort>();
|
||||
var ios = ImmutableArray.CreateBuilder<CircuitBoxInputOutputNode.Type>();
|
||||
|
||||
foreach (var moveable in moveables)
|
||||
{
|
||||
if (moveable is { IsSelected: true, IsSelectedByMe: false }) { continue; }
|
||||
|
||||
switch (moveable)
|
||||
{
|
||||
case CircuitBoxComponent node:
|
||||
ids.Add(node.ID);
|
||||
break;
|
||||
case CircuitBoxInputOutputNode io:
|
||||
ios.Add(io.NodeType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
SelectComponentsInternal(ids, controlledId, overwrite);
|
||||
SelectInputOutputInternal(ios, controlledId, overwrite);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!ids.Any() && !ios.Any()) && !overwrite) { return; }
|
||||
|
||||
CreateClientEvent(new CircuitBoxSelectNodesEvent(ids.ToImmutable(), ios.ToImmutable(), overwrite, controlledId));
|
||||
}
|
||||
|
||||
public void SelectWires(IReadOnlyCollection<CircuitBoxWire> wires, bool overwrite)
|
||||
{
|
||||
if (Character.Controlled is not { ID: var controlledId }) { return; }
|
||||
|
||||
var ids = (from wire in wires where !wire.IsSelected || wire.IsSelectedByMe select wire.ID).ToImmutableArray();
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
SelectWiresInternal(ids, controlledId, overwrite);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ids.Any() && !overwrite) { return; }
|
||||
|
||||
CreateClientEvent(new CircuitBoxSelectWiresEvent(ids, overwrite, Character.Controlled.ID));
|
||||
}
|
||||
|
||||
public void MoveComponent(Vector2 moveAmount, IReadOnlyCollection<CircuitBoxNode> moveables)
|
||||
{
|
||||
var ids = ImmutableArray.CreateBuilder<ushort>();
|
||||
var ios = ImmutableArray.CreateBuilder<CircuitBoxInputOutputNode.Type>();
|
||||
|
||||
foreach (CircuitBoxNode move in moveables)
|
||||
{
|
||||
switch (move)
|
||||
{
|
||||
case CircuitBoxComponent node:
|
||||
ids.Add(node.ID);
|
||||
break;
|
||||
case CircuitBoxInputOutputNode io:
|
||||
ios.Add(io.NodeType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
MoveNodesInternal(ids, ios, moveAmount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ids.Any() && !ios.Any()) { return; }
|
||||
|
||||
|
||||
CreateClientEvent(new CircuitBoxMoveComponentEvent(ids.ToImmutable(), ios.ToImmutable(), moveAmount));
|
||||
}
|
||||
|
||||
public void AddComponent(ItemPrefab prefab, Vector2 pos)
|
||||
{
|
||||
if (GameMain.NetworkMember is null)
|
||||
{
|
||||
ItemPrefab resource;
|
||||
|
||||
if (IsFull) { return; }
|
||||
|
||||
if (IsInGame())
|
||||
{
|
||||
if (!GetApplicableResourcePlayerHas(prefab, Character.Controlled).TryUnwrap(out var r)) { return; }
|
||||
resource = r.Prefab;
|
||||
RemoveItem(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
resource = ItemPrefab.Prefabs[Tags.FPGACircuit];
|
||||
}
|
||||
|
||||
AddComponentInternal(ICircuitBoxIdentifiable.FindFreeID(Components), prefab, resource, pos, static delegate { });
|
||||
return;
|
||||
}
|
||||
|
||||
CreateClientEvent(new CircuitBoxAddComponentEvent(prefab.UintIdentifier, pos));
|
||||
}
|
||||
|
||||
public partial void OnViewUpdateProjSpecific()
|
||||
{
|
||||
UI?.MouseSnapshotHandler.UpdateConnections();
|
||||
UI?.UpdateComponentList();
|
||||
}
|
||||
|
||||
protected override void OnResolutionChanged()
|
||||
{
|
||||
base.OnResolutionChanged();
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
// Remove selection when the circuit box is deselected
|
||||
public partial void OnDeselected(Character c)
|
||||
{
|
||||
cursorUpdateTimer = 0f;
|
||||
|
||||
// Server will broadcast the deselection, we don't need to do it ourselves
|
||||
if (GameMain.NetworkMember is not null) { return; }
|
||||
ClearAllSelectionsInternal(c.ID);
|
||||
}
|
||||
|
||||
public void ClientRead(INetSerializableStruct data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
case NetCircuitBoxCursorInfo cursorInfo:
|
||||
{
|
||||
ClientReadCursor(cursorInfo);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxErrorEvent errorData:
|
||||
{
|
||||
DebugConsole.ThrowError($"The server responded with an error: {errorData.Message}");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(data), data, "This data cannot be handled using direct network messages.");
|
||||
}
|
||||
}
|
||||
|
||||
public void SendMessage(CircuitBoxOpcode opcode, INetSerializableStruct data)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.CIRCUITBOX);
|
||||
|
||||
msg.WriteNetSerializableStruct(new NetCircuitBoxHeader(
|
||||
Opcode: opcode,
|
||||
ItemID: item.ID,
|
||||
ComponentIndex: (byte)item.GetComponentIndex(this)));
|
||||
|
||||
msg.WriteNetSerializableStruct(data);
|
||||
|
||||
DeliveryMethod deliveryMethod =
|
||||
UnrealiableOpcodes.Contains(opcode)
|
||||
? DeliveryMethod.Unreliable
|
||||
: DeliveryMethod.Reliable;
|
||||
|
||||
GameMain.Client?.ClientPeer?.Send(msg, deliveryMethod);
|
||||
}
|
||||
|
||||
private void SendCursorState(Vector2[] cursorPositions, Option<Vector2> dragStart, Option<Identifier> heldComponent)
|
||||
{
|
||||
if (!IsRoundRunning()) { return; }
|
||||
|
||||
var msg = new NetCircuitBoxCursorInfo(
|
||||
RecordedPositions: cursorPositions,
|
||||
DragStart: dragStart,
|
||||
HeldItem: heldComponent);
|
||||
|
||||
SendMessage(CircuitBoxOpcode.Cursor, msg);
|
||||
}
|
||||
|
||||
public void ClientReadCursor(NetCircuitBoxCursorInfo info)
|
||||
{
|
||||
if (Entity.FindEntityByID(info.CharacterID) is not Character character) { return; }
|
||||
|
||||
if (!ActiveCursors.ContainsKey(character))
|
||||
{
|
||||
var newCursor = new CircuitBoxCursor(info);
|
||||
ActiveCursors.Add(character, newCursor);
|
||||
return;
|
||||
}
|
||||
|
||||
var activeCursor = ActiveCursors[character];
|
||||
activeCursor.UpdateInfo(info);
|
||||
activeCursor.ResetTimers();
|
||||
}
|
||||
|
||||
public void CreateClientEvent(INetSerializableStruct data)
|
||||
=> item.CreateClientEvent(this, new CircuitBoxEventData(data));
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData? extraData = null)
|
||||
{
|
||||
if (extraData is null) { return; }
|
||||
var eventData = ExtractEventData<CircuitBoxEventData>(extraData);
|
||||
msg.WriteByte((byte)eventData.Opcode);
|
||||
msg.WriteNetSerializableStruct(eventData.Data);
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
var header = (CircuitBoxOpcode)msg.ReadByte();
|
||||
|
||||
switch (header)
|
||||
{
|
||||
case CircuitBoxOpcode.AddComponent:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxServerCreateComponentEvent>(msg);
|
||||
AddComponentFromData(data);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.DeleteComponent:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxRemoveComponentEvent>(msg);
|
||||
RemoveComponentInternal(data.TargetIDs);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.MoveComponent:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxMoveComponentEvent>(msg);
|
||||
MoveNodesInternal(data.TargetIDs, data.IOs, data.MoveAmount);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.UpdateSelection:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxServerUpdateSelection>(msg);
|
||||
|
||||
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);
|
||||
|
||||
UpdateSelections(nodeDict, wireDict, ioDict);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.AddWire:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxServerCreateWireEvent>(msg);
|
||||
AddWireFromData(data);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.RemoveWire:
|
||||
{
|
||||
var data = INetSerializableStruct.Read<CircuitBoxRemoveWireEvent>(msg);
|
||||
RemoveWireInternal(data.TargetIDs);
|
||||
break;
|
||||
}
|
||||
case CircuitBoxOpcode.ServerInitialize:
|
||||
{
|
||||
Components.Clear();
|
||||
Wires.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 node in InputOutputNodes)
|
||||
{
|
||||
node.Position = node.NodeType switch
|
||||
{
|
||||
CircuitBoxInputOutputNode.Type.Input => data.InputPos,
|
||||
CircuitBoxInputOutputNode.Type.Output => data.OutputPos,
|
||||
_ => node.Position
|
||||
};
|
||||
}
|
||||
wasInitializedByServer = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(header), header, "This opcode cannot be handled using entity events");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddComponentFromData(CircuitBoxServerCreateComponentEvent data)
|
||||
{
|
||||
if (ItemPrefab.Prefabs.Find(p => p.UintIdentifier == data.UsedResource) is not { } prefab)
|
||||
{
|
||||
throw new Exception($"No item prefab found for \"{data.UsedResource}\"");
|
||||
}
|
||||
|
||||
AddComponentInternalUnsafe(data.ComponentId, FindItemByID(data.BackingItemId), prefab, data.Position);
|
||||
}
|
||||
|
||||
public void AddWireFromData(CircuitBoxServerCreateWireEvent data)
|
||||
{
|
||||
var (req, wireId, possibleItemId) = data;
|
||||
var prefab = ItemPrefab.Prefabs.Find(p => p.UintIdentifier == req.SelectedWirePrefabIdentifier);
|
||||
if (prefab is null)
|
||||
{
|
||||
throw new Exception($"No prefab found for \"{req.SelectedWirePrefabIdentifier}\"");
|
||||
}
|
||||
|
||||
if (!req.Start.FindConnection(this).TryUnwrap(out var start))
|
||||
{
|
||||
throw new Exception($"No connection found for ({req.Start})");
|
||||
}
|
||||
|
||||
if (!req.End.FindConnection(this).TryUnwrap(out var end))
|
||||
{
|
||||
throw new Exception($"No connection found for ({req.Start})");
|
||||
}
|
||||
|
||||
if (possibleItemId.TryUnwrap(out var backingItem))
|
||||
{
|
||||
CreateWireWithItem(start, end, wireId, FindItemByID(backingItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateWireWithoutItem(start, end, wireId, prefab);
|
||||
}
|
||||
}
|
||||
|
||||
public static Item FindItemByID(ushort id)
|
||||
=> Entity.FindEntityByID(id) as Item ?? throw new Exception($"No item with ID {id} exists.");
|
||||
|
||||
public override void AddToGUIUpdateList(int order = 0)
|
||||
{
|
||||
base.AddToGUIUpdateList(order);
|
||||
UI?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ namespace Barotrauma.Items.Components
|
||||
public float FlashTimer { get; private set; }
|
||||
public static Wire DraggingConnected { get; private set; }
|
||||
|
||||
private static float ConnectionSpriteSize => 35.0f * GUI.Scale;
|
||||
|
||||
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Rectangle dragArea, Character character,
|
||||
out (Vector2 tooltipPos, LocalizedString text) tooltip)
|
||||
{
|
||||
@@ -33,8 +35,6 @@ namespace Barotrauma.Items.Components
|
||||
int x = panelRect.X, y = panelRect.Y;
|
||||
int width = panelRect.Width, height = panelRect.Height;
|
||||
|
||||
Vector2 scale = GetScale(panel.GuiFrame.RectTransform.MaxSize, panel.GuiFrame.Rect.Size);
|
||||
|
||||
bool mouseInRect = panelRect.Contains(PlayerInput.MousePosition);
|
||||
|
||||
int totalWireCount = 0;
|
||||
@@ -70,15 +70,15 @@ namespace Barotrauma.Items.Components
|
||||
//two passes: first the connector, then the wires to get the wires to render in front
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Vector2 rightPos = GetRightPos(x, y, width, scale);
|
||||
Vector2 leftPos = GetLeftPos(x, y, scale);
|
||||
Vector2 rightPos = GetRightPos(x, y, width);
|
||||
Vector2 leftPos = GetLeftPos(x, y);
|
||||
|
||||
Vector2 rightWirePos = new Vector2(x + width - 5 * scale.X, y + 30 * scale.Y);
|
||||
Vector2 leftWirePos = new Vector2(x + 5 * scale.X, y + 30 * scale.Y);
|
||||
Vector2 rightWirePos = new Vector2(x + width - 5 * GUI.Scale, y + 30 * GUI.Scale);
|
||||
Vector2 leftWirePos = new Vector2(x + 5 * GUI.Scale, y + 30 * GUI.Scale);
|
||||
|
||||
int wireInterval = (height - (int)(20 * scale.Y)) / Math.Max(totalWireCount, 1);
|
||||
int connectorIntervalLeft = GetConnectorIntervalLeft(height, scale, panel);
|
||||
int connectorIntervalRight = GetConnectorIntervalRight(height, scale, panel);
|
||||
int wireInterval = (height - (int)(20 * GUI.Scale)) / Math.Max(totalWireCount, 1);
|
||||
int connectorIntervalLeft = GetConnectorIntervalLeft(height, panel);
|
||||
int connectorIntervalRight = GetConnectorIntervalRight(height, panel);
|
||||
|
||||
foreach (Connection c in panel.Connections)
|
||||
{
|
||||
@@ -101,47 +101,26 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
Vector2 position = c.IsOutput ? rightPos : leftPos;
|
||||
Color highlightColor = Color.Transparent;
|
||||
if (ConnectionPanel.ShouldDebugDrawWiring)
|
||||
{
|
||||
if (c.IsPower)
|
||||
{
|
||||
highlightColor = VisualizeSignal(0.0f, highlightColor, Color.Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightColor = VisualizeSignal(c.LastReceivedSignal.TimeSinceCreated, highlightColor, Color.LightGreen);
|
||||
highlightColor = VisualizeSignal(c.LastSentSignal.TimeSinceCreated, highlightColor, Color.Orange);
|
||||
}
|
||||
bool mouseOn = Vector2.DistanceSquared(position, PlayerInput.MousePosition) < MathUtils.Pow2(35 * GUI.Scale);
|
||||
DrawConnectionDebugInfo(spriteBatch, c, position, GUI.Scale, out var tooltipText);
|
||||
|
||||
LocalizedString toolTipText = c.GetToolTip();
|
||||
if (mouseOn) { tooltip = (position, toolTipText); }
|
||||
if (!toolTipText.IsNullOrEmpty())
|
||||
if (!tooltipText.IsNullOrEmpty())
|
||||
{
|
||||
var glowSprite = GUIStyle.UIGlowCircular.Value.Sprite;
|
||||
glowSprite.Draw(spriteBatch, position, highlightColor, glowSprite.size / 2,
|
||||
scale: 45.0f / glowSprite.size.X * panel.Scale);
|
||||
bool mouseOn = Vector2.DistanceSquared(position, PlayerInput.MousePosition) < MathUtils.Pow2(35 * GUI.Scale);
|
||||
if (mouseOn)
|
||||
{
|
||||
tooltip = (position, tooltipText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Color VisualizeSignal(double timeSinceCreated, Color defaultColor, Color color)
|
||||
{
|
||||
if (timeSinceCreated < 1.0f)
|
||||
{
|
||||
float pulseAmount = (MathF.Sin((float)Timing.TotalTimeUnpaused * 10.0f) + 3.0f) / 4.0f;
|
||||
Color targetColor = Color.Lerp(defaultColor, color, pulseAmount);
|
||||
return Color.Lerp(targetColor, defaultColor, (float)timeSinceCreated);
|
||||
}
|
||||
return defaultColor;
|
||||
}
|
||||
|
||||
//outputs are drawn at the right side of the panel, inputs at the left
|
||||
if (c.IsOutput)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
c.DrawConnection(spriteBatch, panel, rightPos, GetOutputLabelPosition(rightPos, panel, c), scale);
|
||||
c.DrawConnection(spriteBatch, panel, rightPos, GetOutputLabelPosition(rightPos, panel, c));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -154,7 +133,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
c.DrawConnection(spriteBatch, panel, leftPos, GetInputLabelPosition(leftPos, panel, c), scale);
|
||||
c.DrawConnection(spriteBatch, panel, leftPos, GetInputLabelPosition(leftPos, panel, c));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -224,8 +203,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
|
||||
float step = (width * 0.75f) / panel.DisconnectedWires.Count();
|
||||
x = (int)(x + width / 2 - step * (panel.DisconnectedWires.Count() - 1) / 2);
|
||||
float step = (width * 0.75f) / panel.DisconnectedWires.Count;
|
||||
x = (int)(x + width / 2 - step * (panel.DisconnectedWires.Count - 1) / 2);
|
||||
foreach (Wire wire in panel.DisconnectedWires)
|
||||
{
|
||||
if (wire == DraggingConnected && mouseInRect) { continue; }
|
||||
@@ -243,26 +222,61 @@ namespace Barotrauma.Items.Components
|
||||
//stop dragging a wire item if the cursor is within any connection panel
|
||||
//(so we don't drop the item when dropping the wire on a connection)
|
||||
if (mouseInRect || (GUI.MouseOn?.UserData is ConnectionPanel && GUI.MouseOn.MouseRect.Contains(PlayerInput.MousePosition)))
|
||||
{
|
||||
Inventory.DraggingItems.Clear();
|
||||
}
|
||||
{
|
||||
Inventory.DraggingItems.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConnection(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 labelPos, Vector2 scale)
|
||||
public static void DrawConnectionDebugInfo(SpriteBatch spriteBatch, Connection c, Vector2 position, float scale, out LocalizedString tooltip)
|
||||
{
|
||||
Color highlightColor = Color.Transparent;
|
||||
if (c.IsPower)
|
||||
{
|
||||
highlightColor = VisualizeSignal(0.0f, highlightColor, Color.Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightColor = VisualizeSignal(c.LastReceivedSignal.TimeSinceCreated, highlightColor, Color.LightGreen);
|
||||
highlightColor = VisualizeSignal(c.LastSentSignal.TimeSinceCreated, highlightColor, Color.Orange);
|
||||
}
|
||||
|
||||
LocalizedString toolTipText = c.GetToolTip();
|
||||
if (!toolTipText.IsNullOrEmpty())
|
||||
{
|
||||
var glowSprite = GUIStyle.UIGlowCircular.Value.Sprite;
|
||||
glowSprite.Draw(spriteBatch, position, highlightColor, glowSprite.size / 2,
|
||||
scale: 45.0f / glowSprite.size.X * scale);
|
||||
}
|
||||
|
||||
tooltip = toolTipText;
|
||||
|
||||
static Color VisualizeSignal(double timeSinceCreated, Color defaultColor, Color color)
|
||||
{
|
||||
if (timeSinceCreated < 1.0f)
|
||||
{
|
||||
float pulseAmount = (MathF.Sin((float)Timing.TotalTimeUnpaused * 10.0f) + 3.0f) / 4.0f;
|
||||
Color targetColor = Color.Lerp(defaultColor, color, pulseAmount);
|
||||
return Color.Lerp(targetColor, defaultColor, (float)timeSinceCreated);
|
||||
}
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConnection(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 labelPos)
|
||||
{
|
||||
string text = DisplayName.Value.ToUpperInvariant();
|
||||
|
||||
//nasty
|
||||
if (GUIStyle.GetComponentStyle("ConnectionPanelLabel")?.Sprites.Values.First().First() is UISprite labelSprite)
|
||||
{
|
||||
Rectangle labelArea = GetLabelArea(labelPos, text, scale);
|
||||
Rectangle labelArea = GetLabelArea(labelPos, text);
|
||||
labelSprite.Draw(spriteBatch, labelArea, IsPower ? GUIStyle.Red : Color.SteelBlue);
|
||||
}
|
||||
|
||||
GUI.DrawString(spriteBatch, labelPos + Vector2.UnitY, text, Color.Black * 0.8f, font: GUIStyle.SmallFont);
|
||||
GUI.DrawString(spriteBatch, labelPos, text, GUIStyle.TextColorBright, font: GUIStyle.SmallFont);
|
||||
|
||||
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
|
||||
float connectorSpriteScale = ConnectionSpriteSize / connectionSprite.SourceRect.Width;
|
||||
|
||||
connectionSprite.Draw(spriteBatch, position, scale: connectorSpriteScale);
|
||||
|
||||
@@ -270,7 +284,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void DrawWires(SpriteBatch spriteBatch, ConnectionPanel panel, Vector2 position, Vector2 wirePosition, bool mouseIn, Wire equippedWire, float wireInterval)
|
||||
{
|
||||
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
|
||||
float connectorSpriteScale = ConnectionSpriteSize / connectionSprite.SourceRect.Width;
|
||||
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
@@ -285,9 +299,14 @@ namespace Barotrauma.Items.Components
|
||||
wirePosition.Y += wireInterval;
|
||||
}
|
||||
|
||||
if (DraggingConnected != null && Vector2.Distance(position, PlayerInput.MousePosition) < (20.0f * GUI.Scale))
|
||||
bool isMouseOn = Vector2.Distance(position, PlayerInput.MousePosition) < (20.0f * GUI.Scale);
|
||||
if (isMouseOn)
|
||||
{
|
||||
connectionSpriteHighlight.Draw(spriteBatch, position, scale: connectorSpriteScale);
|
||||
}
|
||||
|
||||
if (DraggingConnected != null && isMouseOn)
|
||||
{
|
||||
|
||||
if (!PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
@@ -418,7 +437,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
bool mouseOn =
|
||||
canDrag &&
|
||||
!(GUI.MouseOn is GUIDragHandle) &&
|
||||
GUI.MouseOn is not GUIDragHandle &&
|
||||
((PlayerInput.MousePosition.X > Math.Min(start.X, end.X) &&
|
||||
PlayerInput.MousePosition.X < Math.Max(start.X, end.X) &&
|
||||
MathUtils.LineToPointDistanceSquared(start, end, PlayerInput.MousePosition) < 36) ||
|
||||
@@ -442,12 +461,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
var wireEnd = end + Vector2.Normalize(start - end) * 30.0f * panel.Scale;
|
||||
var wireEnd = end + Vector2.Normalize(start - end) * 30.0f * GUI.Scale;
|
||||
|
||||
float dist = Vector2.Distance(start, wireEnd);
|
||||
|
||||
float wireWidth = 12 * panel.Scale;
|
||||
float highlight = 5 * panel.Scale;
|
||||
float wireWidth = 12 * GUI.Scale;
|
||||
float highlight = 5 * GUI.Scale;
|
||||
if (mouseOn)
|
||||
{
|
||||
spriteBatch.Draw(wireVertical.Texture, new Rectangle(wireEnd.ToPoint(), new Point((int)(wireWidth + highlight), (int)dist)), wireVertical.SourceRect,
|
||||
@@ -488,110 +507,84 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Rectangle panelRect = panel.GuiFrame.Rect;
|
||||
int x = panelRect.X, y = panelRect.Y;
|
||||
Vector2 scale = GetScale(panel.GuiFrame.RectTransform.MaxSize, panel.GuiFrame.Rect.Size);
|
||||
Vector2 rightPos = GetRightPos(x, y, panelRect.Width, scale);
|
||||
Vector2 leftPos = GetLeftPos(x, y, scale);
|
||||
int connectorIntervalLeft = GetConnectorIntervalLeft(panelRect.Height, scale, panel);
|
||||
int connectorIntervalRight = GetConnectorIntervalRight(panelRect.Height, scale, panel);
|
||||
Vector2 rightPos = GetRightPos(x, y, panelRect.Width);
|
||||
Vector2 leftPos = GetLeftPos(x, y);
|
||||
int connectorIntervalLeft = GetConnectorIntervalLeft(panelRect.Height, panel);
|
||||
int connectorIntervalRight = GetConnectorIntervalRight(panelRect.Height, panel);
|
||||
newRectSize = panelRect.Size;
|
||||
var labelAreas = new List<Rectangle>();
|
||||
for (int i = 0; i < 100; i++)
|
||||
|
||||
//make sure the connection labels don't overlap horizontally
|
||||
float rightMostInput = panelRect.Center.X;
|
||||
float leftMostOutput = panelRect.Center.X;
|
||||
foreach (var c in panel.Connections)
|
||||
{
|
||||
labelAreas.Clear();
|
||||
foreach (var c in panel.Connections)
|
||||
if (c.IsOutput)
|
||||
{
|
||||
if (c.IsOutput)
|
||||
{
|
||||
var labelArea = GetLabelArea(GetOutputLabelPosition(rightPos, panel, c), c.DisplayName.Value.ToUpperInvariant(), scale);
|
||||
labelAreas.Add(labelArea);
|
||||
rightPos.Y += connectorIntervalLeft;
|
||||
}
|
||||
else
|
||||
{
|
||||
var labelArea = GetLabelArea(GetInputLabelPosition(leftPos, panel, c), c.DisplayName.Value.ToUpperInvariant(), scale);
|
||||
labelAreas.Add(labelArea);
|
||||
leftPos.Y += connectorIntervalRight;
|
||||
}
|
||||
var labelArea = GetLabelArea(GetOutputLabelPosition(rightPos, panel, c), c.DisplayName.Value.ToUpperInvariant());
|
||||
leftMostOutput = Math.Min(leftMostOutput, labelArea.X);
|
||||
rightPos.Y += connectorIntervalLeft;
|
||||
}
|
||||
bool foundOverlap = false;
|
||||
for (int j = 0; j < labelAreas.Count; j++)
|
||||
else
|
||||
{
|
||||
for (int k = 0; k < labelAreas.Count; k++)
|
||||
{
|
||||
if (k == j) { continue; }
|
||||
if (!labelAreas[j].Intersects(labelAreas[k])) { continue; }
|
||||
newRectSize += new Point(10);
|
||||
Point maxSize = new Point(
|
||||
Math.Max(panel.GuiFrame.RectTransform.MaxSize.X, newRectSize.X),
|
||||
Math.Max(panel.GuiFrame.RectTransform.MaxSize.Y, newRectSize.Y));
|
||||
scale = GetScale(maxSize, newRectSize);
|
||||
rightPos = GetRightPos(x, y, newRectSize.X, scale);
|
||||
leftPos = GetLeftPos(x, y, scale);
|
||||
connectorIntervalLeft = GetConnectorIntervalLeft(newRectSize.Y, scale, panel);
|
||||
connectorIntervalRight = GetConnectorIntervalRight(newRectSize.Y, scale, panel);
|
||||
foundOverlap = true;
|
||||
break;
|
||||
}
|
||||
var labelArea = GetLabelArea(GetInputLabelPosition(leftPos, panel, c), c.DisplayName.Value.ToUpperInvariant());
|
||||
rightMostInput = Math.Max(rightMostInput, labelArea.Right);
|
||||
leftPos.Y += connectorIntervalRight;
|
||||
}
|
||||
if (!foundOverlap) { break; }
|
||||
}
|
||||
if (leftMostOutput < rightMostInput)
|
||||
{
|
||||
newRectSize += new Point((int)(rightMostInput - leftMostOutput) + GUI.IntScale(15), 0);
|
||||
}
|
||||
|
||||
//make sure connection sprites don't overlap vertically
|
||||
while (GetConnectorIntervalLeft(newRectSize.Y, panel) < ConnectionSpriteSize ||
|
||||
GetConnectorIntervalRight(newRectSize.Y, panel) < ConnectionSpriteSize)
|
||||
{
|
||||
newRectSize.Y += 10;
|
||||
}
|
||||
return newRectSize.X != panel.GuiFrame.Rect.Width || newRectSize.Y > panel.GuiFrame.Rect.Height;
|
||||
}
|
||||
|
||||
private static Vector2 GetScale(Point maxSize, Point size)
|
||||
{
|
||||
Vector2 scale = new Vector2(GUI.Scale);
|
||||
if (maxSize.X < int.MaxValue)
|
||||
{
|
||||
scale.X = maxSize.X / size.X;
|
||||
}
|
||||
if (maxSize.Y < int.MaxValue)
|
||||
{
|
||||
scale.Y = maxSize.Y / size.Y;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
private static Vector2 GetInputLabelPosition(Vector2 connectorPosition, ConnectionPanel panel, Connection connection)
|
||||
{
|
||||
return new Vector2(
|
||||
connectorPosition.X + 25 * panel.Scale,
|
||||
connectorPosition.Y - 5 * panel.Scale - GUIStyle.SmallFont.MeasureString(connection.DisplayName.ToUpper()).Y);
|
||||
connectorPosition.X + 25 * GUI.Scale,
|
||||
connectorPosition.Y - GUIStyle.SmallFont.MeasureString(connection.DisplayName.ToUpper()).Y / 2);
|
||||
}
|
||||
|
||||
private static Vector2 GetOutputLabelPosition(Vector2 connectorPosition, ConnectionPanel panel, Connection connection)
|
||||
{
|
||||
return new Vector2(
|
||||
connectorPosition.X - 25 * panel.Scale - GUIStyle.SmallFont.MeasureString(connection.DisplayName.ToUpper()).X,
|
||||
connectorPosition.Y + 5 * panel.Scale);
|
||||
connectorPosition.X - 25 * GUI.Scale - GUIStyle.SmallFont.MeasureString(connection.DisplayName.ToUpper()).X,
|
||||
connectorPosition.Y - GUIStyle.SmallFont.MeasureString(connection.DisplayName.ToUpper()).Y / 2);
|
||||
}
|
||||
|
||||
private static Rectangle GetLabelArea(Vector2 labelPos, string text, Vector2 scale)
|
||||
private static Rectangle GetLabelArea(Vector2 labelPos, string text)
|
||||
{
|
||||
Vector2 textSize = GUIStyle.SmallFont.MeasureString(text);
|
||||
Rectangle labelArea = new Rectangle(labelPos.ToPoint(), textSize.ToPoint());
|
||||
labelArea.Inflate(10 * scale.X, 3 * scale.Y);
|
||||
labelArea.Inflate(GUI.IntScale(10), GUI.IntScale(3));
|
||||
return labelArea;
|
||||
}
|
||||
|
||||
private static Vector2 GetLeftPos(int x, int y, Vector2 scale)
|
||||
private static Vector2 GetLeftPos(int x, int y)
|
||||
{
|
||||
return new Vector2(x + 80 * scale.X, y + 60 * scale.Y);
|
||||
return new Vector2(x + 80 * GUI.Scale, y + 60 * GUI.Scale);
|
||||
}
|
||||
|
||||
private static Vector2 GetRightPos(int x, int y, int width, Vector2 scale)
|
||||
private static Vector2 GetRightPos(int x, int y, int width)
|
||||
{
|
||||
return new Vector2(x + width - 80 * scale.X, y + 60 * scale.Y);
|
||||
return new Vector2(x + width - 80 * GUI.Scale, y + 60 * GUI.Scale);
|
||||
}
|
||||
|
||||
private static int GetConnectorIntervalLeft(int height, Vector2 scale, ConnectionPanel panel)
|
||||
private static int GetConnectorIntervalLeft(int height, ConnectionPanel panel)
|
||||
{
|
||||
return (height - (int)(100 * scale.Y)) / Math.Max(panel.Connections.Count(c => c.IsOutput), 1);
|
||||
return (height - GUI.IntScale(60)) / Math.Max(panel.Connections.Count(c => c.IsOutput), 1);
|
||||
}
|
||||
|
||||
private static int GetConnectorIntervalRight(int height, Vector2 scale, ConnectionPanel panel)
|
||||
private static int GetConnectorIntervalRight(int height, ConnectionPanel panel)
|
||||
{
|
||||
return (height - (int)(100 * scale.Y)) / Math.Max(panel.Connections.Count(c => !c.IsOutput), 1);
|
||||
return (height - GUI.IntScale(60)) / Math.Max(panel.Connections.Count(c => !c.IsOutput), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,6 @@ namespace Barotrauma.Items.Components
|
||||
private SoundChannel rewireSoundChannel;
|
||||
private float rewireSoundTimer;
|
||||
|
||||
public float Scale
|
||||
{
|
||||
get { return GuiFrame.Rect.Width / 400.0f; }
|
||||
}
|
||||
|
||||
private Point originalMaxSize;
|
||||
private Vector2 originalRelativeSize;
|
||||
|
||||
@@ -104,10 +99,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool ShouldDrawHUD(Character character)
|
||||
{
|
||||
return character == Character.Controlled && character == user && character.SelectedItem == item;
|
||||
return character == Character.Controlled && character == user && (character.SelectedItem == item || character.SelectedSecondaryItem == item);
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (character != Character.Controlled || character != user || character.SelectedItem != item) { return; }
|
||||
|
||||
@@ -162,10 +157,11 @@ namespace Barotrauma.Items.Components
|
||||
//because some of the wires connected to the panel may not exist yet
|
||||
long msgStartPos = msg.BitPosition;
|
||||
msg.ReadUInt16(); //user ID
|
||||
foreach (Connection _ in Connections)
|
||||
byte connectionCount = msg.ReadByte();
|
||||
for (int i = 0; i < connectionCount; i++)
|
||||
{
|
||||
uint wireCount = msg.ReadVariableUInt32();
|
||||
for (int i = 0; i < wireCount; i++)
|
||||
for (int j = 0; j < wireCount; j++)
|
||||
{
|
||||
msg.ReadUInt16();
|
||||
}
|
||||
@@ -208,21 +204,25 @@ namespace Barotrauma.Items.Components
|
||||
connection.ClearConnections();
|
||||
}
|
||||
|
||||
foreach (Connection connection in Connections)
|
||||
byte connectionCount = msg.ReadByte();
|
||||
for (int i = 0; i < connectionCount; i++)
|
||||
{
|
||||
HashSet<Wire> newWires = new HashSet<Wire>();
|
||||
uint wireCount = msg.ReadVariableUInt32();
|
||||
for (int i = 0; i < wireCount; i++)
|
||||
for (int j = 0; j < wireCount; j++)
|
||||
{
|
||||
ushort wireId = msg.ReadUInt16();
|
||||
|
||||
if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; }
|
||||
if (Entity.FindEntityByID(wireId) is not Item wireItem) { continue; }
|
||||
Wire wireComponent = wireItem.GetComponent<Wire>();
|
||||
if (wireComponent == null) { continue; }
|
||||
|
||||
newWires.Add(wireComponent);
|
||||
}
|
||||
|
||||
//this may happen if the item has been deleted server-side at the point the server is writing this event to the client
|
||||
if (i >= Connections.Count) { continue; }
|
||||
|
||||
var connection = Connections[i];
|
||||
Wire[] oldWires = connection.Wires.Where(w => !newWires.Contains(w)).ToArray();
|
||||
foreach (var wire in oldWires)
|
||||
{
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
bool elementVisibilityChanged = false;
|
||||
int visibleElementCount = 0;
|
||||
@@ -335,17 +335,18 @@ namespace Barotrauma.Items.Components
|
||||
if (signals == null) { return; }
|
||||
for (int i = 0; i < signals.Length && i < uiElements.Count; i++)
|
||||
{
|
||||
string signal = customInterfaceElementList[i].Signal;
|
||||
if (uiElements[i] is GUITextBox tb)
|
||||
{
|
||||
tb.Text = Screen.Selected is { IsEditor: true } ?
|
||||
customInterfaceElementList[i].Signal :
|
||||
TextManager.Get(customInterfaceElementList[i].Signal).Value;
|
||||
signal :
|
||||
TextManager.Get(signal).Fallback(signal).Value;
|
||||
}
|
||||
else if (uiElements[i] is GUINumberInput ni)
|
||||
{
|
||||
if (ni.InputType == NumberType.Int)
|
||||
{
|
||||
int.TryParse(customInterfaceElementList[i].Signal, out int value);
|
||||
int.TryParse(signal, out int value);
|
||||
ni.IntValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return new Vector2(rangeX, rangeY) * 2.0f; }
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (!editing || !MapEntity.SelectedList.Contains(item)) { return; }
|
||||
|
||||
|
||||
@@ -86,15 +86,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
OutputValue = input;
|
||||
ShowOnDisplay(input, addToHistory: true, TextColor);
|
||||
ShowOnDisplay(input, addToHistory: true, TextColor, isWelcomeMessage: false);
|
||||
item.SendSignal(input, "signal_out");
|
||||
}
|
||||
|
||||
partial void ShowOnDisplay(string input, bool addToHistory, Color color)
|
||||
partial void ShowOnDisplay(string input, bool addToHistory, Color color, bool isWelcomeMessage)
|
||||
{
|
||||
if (addToHistory)
|
||||
{
|
||||
messageHistory.Add(new TerminalMessage(input, color));
|
||||
messageHistory.Add(new TerminalMessage(input, color, isWelcomeMessage));
|
||||
while (messageHistory.Count > MaxMessages)
|
||||
{
|
||||
messageHistory.RemoveAt(0);
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace Barotrauma.Items.Components
|
||||
get { return new Vector2(range * 2); }
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (!editing || !MapEntity.SelectedList.Contains(item)) return;
|
||||
if (!editing || !MapEntity.SelectedList.Contains(item)) { return; }
|
||||
|
||||
Vector2 pos = new Vector2(item.DrawPosition.X, -item.DrawPosition.Y);
|
||||
ShapeExtensions.DrawLine(spriteBatch, pos + Vector2.UnitY * range, pos - Vector2.UnitY * range, Color.Cyan * 0.5f, 2);
|
||||
|
||||
@@ -186,12 +186,12 @@ namespace Barotrauma.Items.Components
|
||||
return Color.LightBlue;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
Draw(spriteBatch, editing, Vector2.Zero, itemDepth);
|
||||
Draw(spriteBatch, editing, Vector2.Zero, itemDepth, overrideColor);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (sections.Count == 0 && !IsActive || Hidden)
|
||||
{
|
||||
@@ -223,7 +223,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (WireSection section in sections)
|
||||
{
|
||||
section.Draw(spriteBatch, wireSprite, item.Color, drawOffset, depth, Width);
|
||||
section.Draw(spriteBatch, wireSprite, overrideColor ?? item.Color, drawOffset, depth, Width);
|
||||
}
|
||||
|
||||
if (nodes.Count > 0)
|
||||
@@ -271,13 +271,13 @@ namespace Barotrauma.Items.Components
|
||||
spriteBatch, wireSprite,
|
||||
nodes[^1] + drawOffset,
|
||||
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
|
||||
item.Color, 0.0f, Width);
|
||||
overrideColor ?? item.Color, 0.0f, Width);
|
||||
|
||||
WireSection.Draw(
|
||||
spriteBatch, wireSprite,
|
||||
new Vector2(newNodePos.X, newNodePos.Y) + drawOffset,
|
||||
item.DrawPosition,
|
||||
item.Color, itemDepth, Width);
|
||||
overrideColor ?? item.Color, itemDepth, Width);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(newNodePos.X + drawOffset.X, -(newNodePos.Y + drawOffset.Y)) - Vector2.One * 3, Vector2.One * 6, item.Color);
|
||||
}
|
||||
@@ -287,7 +287,7 @@ namespace Barotrauma.Items.Components
|
||||
spriteBatch, wireSprite,
|
||||
nodes[^1] + drawOffset,
|
||||
item.DrawPosition,
|
||||
item.Color, 0.0f, Width);
|
||||
overrideColor ?? item.Color, 0.0f, Width);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,7 +594,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
selectedWire.shouldClearConnections = false;
|
||||
Character.Controlled.Inventory.TryPutItem(selectedWire.item, Character.Controlled, new List<InvSlotType> { InvSlotType.LeftHand, InvSlotType.RightHand });
|
||||
foreach (var entity in MapEntity.mapEntityList)
|
||||
foreach (var entity in MapEntity.MapEntityList)
|
||||
{
|
||||
if (entity is Item item)
|
||||
{
|
||||
|
||||
@@ -57,13 +57,6 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
|
||||
|
||||
[Editable, Serialize("0,0,0,0", IsPropertySaveable.Yes, description: "Optional screen tint color when the item is being operated (R,G,B,A).")]
|
||||
public Color HudTint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the charge of the connected batteries/supercapacitors be shown at the top of the screen when operating the item.")]
|
||||
public bool ShowChargeIndicator
|
||||
{
|
||||
@@ -117,6 +110,13 @@ namespace Barotrauma.Items.Components
|
||||
get { return barrelSprite; }
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool HideBarrelWhenBroken
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element)
|
||||
{
|
||||
foreach (var subElement in element.Elements())
|
||||
@@ -232,7 +232,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, item.WorldPosition, endMoveSound.Volume, endMoveSound.Range, ignoreMuffling: endMoveSound.IgnoreMuffling, freqMult: endMoveSound.GetRandomFrequencyMultiplier());
|
||||
if (moveSoundChannel != null) moveSoundChannel.Looping = false;
|
||||
if (moveSoundChannel != null) { moveSoundChannel.Looping = false; }
|
||||
}
|
||||
else if (!moveSoundChannel.IsPlaying)
|
||||
{
|
||||
@@ -261,12 +261,13 @@ namespace Barotrauma.Items.Components
|
||||
if (chargeSound != null)
|
||||
{
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling, freqMult: chargeSound.GetRandomFrequencyMultiplier());
|
||||
if (chargeSoundChannel != null) chargeSoundChannel.Looping = true;
|
||||
if (chargeSoundChannel != null) { chargeSoundChannel.Looping = true; }
|
||||
}
|
||||
}
|
||||
else if (chargeSoundChannel != null)
|
||||
{
|
||||
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
|
||||
chargeSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -318,7 +319,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
public override void UpdateHUDComponentSpecific(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
if (crosshairSprite != null)
|
||||
{
|
||||
@@ -359,7 +360,7 @@ namespace Barotrauma.Items.Components
|
||||
return new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation) || !MathUtils.NearlyEqual(item.Scale, prevScale))
|
||||
{
|
||||
@@ -367,53 +368,55 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
Vector2 drawPos = GetDrawPos();
|
||||
|
||||
|
||||
railSprite?.Draw(spriteBatch,
|
||||
drawPos,
|
||||
item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
barrelSprite?.Draw(spriteBatch,
|
||||
drawPos - GetRecoilOffset() * item.Scale,
|
||||
item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
float chargeRatio = currentChargeTime / MaxChargeTime;
|
||||
|
||||
foreach ((Sprite chargeSprite, Vector2 position) in chargeSprites)
|
||||
if (item.Condition > 0.0f || !HideBarrelWhenBroken)
|
||||
{
|
||||
chargeSprite?.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(position.X * chargeRatio, position.Y * chargeRatio) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
item.SpriteColor,
|
||||
railSprite?.Draw(spriteBatch,
|
||||
drawPos,
|
||||
overrideColor ?? item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (chargeSprite.Depth - item.Sprite.Depth));
|
||||
}
|
||||
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
int spinningBarrelCount = spinningBarrelSprites.Count;
|
||||
|
||||
for (int i = 0; i < spinningBarrelCount; i++)
|
||||
{
|
||||
// this block is messy since I was debugging it with a bunch of values, should be cleaned up / optimized if prototype is accepted
|
||||
Sprite spinningBarrel = spinningBarrelSprites[i];
|
||||
float barrelCirclePosition = (MaxCircle * i / spinningBarrelCount + currentBarrelSpin) % MaxCircle;
|
||||
|
||||
float newDepth = item.SpriteDepth + (spinningBarrel.Depth - item.Sprite.Depth) + (barrelCirclePosition > HalfCircle ? 0.0f : 0.001f);
|
||||
|
||||
float barrelColorPosition = (barrelCirclePosition + QuarterCircle) % MaxCircle;
|
||||
float colorOffset = Math.Abs(barrelColorPosition - HalfCircle) / HalfCircle;
|
||||
Color newColorModifier = Color.Lerp(Color.Black, Color.Gray, colorOffset);
|
||||
|
||||
float barrelHalfCirclePosition = Math.Abs(barrelCirclePosition - HalfCircle);
|
||||
float barrelPositionModifier = MathUtils.SmoothStep(barrelHalfCirclePosition / HalfCircle);
|
||||
float newPositionOffset = barrelPositionModifier * SpinningBarrelDistance;
|
||||
|
||||
spinningBarrel.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(newPositionOffset, 0f) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
Color.Lerp(item.SpriteColor, newColorModifier, 0.8f),
|
||||
barrelSprite?.Draw(spriteBatch,
|
||||
drawPos - GetRecoilOffset() * item.Scale,
|
||||
overrideColor ?? item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, newDepth);
|
||||
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
float chargeRatio = currentChargeTime / MaxChargeTime;
|
||||
|
||||
foreach ((Sprite chargeSprite, Vector2 position) in chargeSprites)
|
||||
{
|
||||
chargeSprite?.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(position.X * chargeRatio, position.Y * chargeRatio) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (chargeSprite.Depth - item.Sprite.Depth));
|
||||
}
|
||||
|
||||
int spinningBarrelCount = spinningBarrelSprites.Count;
|
||||
|
||||
for (int i = 0; i < spinningBarrelCount; i++)
|
||||
{
|
||||
// this block is messy since I was debugging it with a bunch of values, should be cleaned up / optimized if prototype is accepted
|
||||
Sprite spinningBarrel = spinningBarrelSprites[i];
|
||||
float barrelCirclePosition = (MaxCircle * i / spinningBarrelCount + currentBarrelSpin) % MaxCircle;
|
||||
|
||||
float newDepth = item.SpriteDepth + (spinningBarrel.Depth - item.Sprite.Depth) + (barrelCirclePosition > HalfCircle ? 0.0f : 0.001f);
|
||||
|
||||
float barrelColorPosition = (barrelCirclePosition + QuarterCircle) % MaxCircle;
|
||||
float colorOffset = Math.Abs(barrelColorPosition - HalfCircle) / HalfCircle;
|
||||
Color newColorModifier = Color.Lerp(Color.Black, Color.Gray, colorOffset);
|
||||
|
||||
float barrelHalfCirclePosition = Math.Abs(barrelCirclePosition - HalfCircle);
|
||||
float barrelPositionModifier = MathUtils.SmoothStep(barrelHalfCirclePosition / HalfCircle);
|
||||
float newPositionOffset = barrelPositionModifier * SpinningBarrelDistance;
|
||||
|
||||
spinningBarrel.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(newPositionOffset, 0f) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
Color.Lerp(overrideColor ?? item.SpriteColor, newColorModifier, 0.8f),
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, newDepth);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
@@ -593,6 +596,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
|
||||
var battery = recipient.Item?.GetComponent<PowerContainer>();
|
||||
if (battery == null || battery.Item.Condition <= 0.0f) { continue; }
|
||||
if (battery.OutputDisabled) { continue; }
|
||||
availableCharge += battery.Charge;
|
||||
availableCapacity += battery.GetCapacity();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return Vector2.Zero; }
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1, Color? overrideColor = null)
|
||||
{
|
||||
if (dockingState == 0.0f) return;
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace Barotrauma.Items.Components
|
||||
drawPos,
|
||||
new Rectangle(
|
||||
rect.Center.X + (int)(rect.Width / 2 * (1.0f - dockingState)), rect.Y,
|
||||
(int)(rect.Width / 2 * dockingState), rect.Height), Color.White);
|
||||
(int)(rect.Width / 2 * dockingState), rect.Height),
|
||||
overrideColor ?? Color.White);
|
||||
|
||||
}
|
||||
else
|
||||
@@ -48,7 +49,8 @@ namespace Barotrauma.Items.Components
|
||||
drawPos - Vector2.UnitX * (rect.Width / 2 * dockingState),
|
||||
new Rectangle(
|
||||
rect.X, rect.Y,
|
||||
(int)(rect.Width / 2 * dockingState), rect.Height), Color.White);
|
||||
(int)(rect.Width / 2 * dockingState), rect.Height),
|
||||
overrideColor ?? Color.White);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -61,7 +63,8 @@ namespace Barotrauma.Items.Components
|
||||
drawPos - Vector2.UnitY * (rect.Height / 2 * dockingState),
|
||||
new Rectangle(
|
||||
rect.X, rect.Y,
|
||||
rect.Width, (int)(rect.Height / 2 * dockingState)), Color.White);
|
||||
rect.Width, (int)(rect.Height / 2 * dockingState)),
|
||||
overrideColor ?? Color.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -69,7 +72,8 @@ namespace Barotrauma.Items.Components
|
||||
drawPos,
|
||||
new Rectangle(
|
||||
rect.X, rect.Y + rect.Height / 2 + (int)(rect.Height / 2 * (1.0f - dockingState)),
|
||||
rect.Width, (int)(rect.Height / 2 * dockingState)), Color.White);
|
||||
rect.Width, (int)(rect.Height / 2 * dockingState)),
|
||||
overrideColor ?? Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ namespace Barotrauma
|
||||
|
||||
public Rectangle BackgroundFrame { get; protected set; }
|
||||
|
||||
private List<ushort>[] partialReceivedItemIDs;
|
||||
private List<ushort>[] receivedItemIDs;
|
||||
private CoroutineHandle syncItemsCoroutine;
|
||||
|
||||
@@ -257,7 +258,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
LocalizedString description = item.Description;
|
||||
if (item.HasTag("identitycard") || item.HasTag("despawncontainer"))
|
||||
if (item.HasTag(Tags.IdCard) || item.HasTag(Tags.DespawnContainer))
|
||||
{
|
||||
string[] readTags = item.Tags.Split(',');
|
||||
string idName = null;
|
||||
@@ -347,6 +348,9 @@ namespace Barotrauma
|
||||
{
|
||||
toolTip += item.Prefab.GetSkillRequirementHints(character);
|
||||
}
|
||||
#if DEBUG
|
||||
toolTip += $" ({item.Prefab.Identifier})";
|
||||
#endif
|
||||
return RichString.Rich(toolTip);
|
||||
}
|
||||
}
|
||||
@@ -387,6 +391,12 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public RectTransform RectTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Normally false - we don't draw the UI because it's drawn when the player hovers the cursor over the item in their inventory.
|
||||
/// Enabled in special cases like equippable fabricators where the inventory is a part of the fabricator UI.
|
||||
/// </summary>
|
||||
public bool DrawWhenEquipped;
|
||||
|
||||
public static SlotReference SelectedSlot
|
||||
{
|
||||
get
|
||||
@@ -417,6 +427,7 @@ namespace Barotrauma
|
||||
|
||||
padding = new Vector4(spacing.X, spacing.Y, spacing.X, spacing.X);
|
||||
|
||||
|
||||
Vector2 slotAreaSize = new Vector2(
|
||||
columns * rectSize.X + (columns - 1) * spacing.X,
|
||||
rows * rectSize.Y + (rows - 1) * spacing.Y);
|
||||
@@ -427,6 +438,8 @@ namespace Barotrauma
|
||||
GameMain.GraphicsWidth / 2 - slotAreaSize.X / 2,
|
||||
GameMain.GraphicsHeight / 2 - slotAreaSize.Y / 2);
|
||||
|
||||
Vector2 center = topLeft + slotAreaSize / 2;
|
||||
|
||||
if (RectTransform != null)
|
||||
{
|
||||
Vector2 scale = new Vector2(
|
||||
@@ -438,6 +451,8 @@ namespace Barotrauma
|
||||
padding.X *= scale.X; padding.Z *= scale.X;
|
||||
padding.Y *= scale.Y; padding.W *= scale.Y;
|
||||
|
||||
center = RectTransform.Rect.Center.ToVector2();
|
||||
|
||||
topLeft = RectTransform.TopLeft.ToVector2() + new Vector2(padding.X, padding.Y);
|
||||
prevRect = RectTransform.Rect;
|
||||
}
|
||||
@@ -445,8 +460,14 @@ namespace Barotrauma
|
||||
Rectangle slotRect = new Rectangle((int)topLeft.X, (int)topLeft.Y, (int)rectSize.X, (int)rectSize.Y);
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
slotRect.X = (int)(topLeft.X + (rectSize.X + spacing.X) * (i % slotsPerRow));
|
||||
slotRect.Y = (int)(topLeft.Y + (rectSize.Y + spacing.Y) * ((int)Math.Floor((double)i / slotsPerRow)));
|
||||
int row = (int)Math.Floor((double)i / slotsPerRow);
|
||||
int slotsPerThisRow = Math.Min(slotsPerRow, capacity - row * slotsPerRow);
|
||||
|
||||
int rowWidth = (int)(rectSize.X * slotsPerThisRow + spacing.X * (slotsPerThisRow - 1));
|
||||
slotRect.X = (int)(center.X) - rowWidth / 2;
|
||||
slotRect.X += (int)((rectSize.X + spacing.X) * (i % slotsPerThisRow));
|
||||
|
||||
slotRect.Y = (int)(topLeft.Y + (rectSize.Y + spacing.Y) * row);
|
||||
visualSlots[i] = new VisualSlot(slotRect);
|
||||
visualSlots[i].InteractRect = new Rectangle(
|
||||
(int)(visualSlots[i].Rect.X - spacing.X / 2 - 1), (int)(visualSlots[i].Rect.Y - spacing.Y / 2 - 1),
|
||||
@@ -489,7 +510,7 @@ namespace Barotrauma
|
||||
return owner.SelectedCharacter != null|| (!(owner is Character character)) || !container.KeepOpenWhenEquippedBy(character) || !owner.HasEquippedItem(container.Item);
|
||||
}
|
||||
|
||||
protected virtual bool HideSlot(int i)
|
||||
public virtual bool HideSlot(int i)
|
||||
{
|
||||
return visualSlots[i].Disabled || (slots[i].HideIfEmpty && slots[i].Empty());
|
||||
}
|
||||
@@ -673,6 +694,7 @@ namespace Barotrauma
|
||||
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
if (container == null || !container.DrawInventory) { return; }
|
||||
if (container.Inventory.DrawWhenEquipped) { return; }
|
||||
|
||||
var subInventory = container.Inventory;
|
||||
if (subInventory.visualSlots == null) { subInventory.CreateSlots(); }
|
||||
@@ -871,44 +893,34 @@ namespace Barotrauma
|
||||
|
||||
if (Character.Controlled.Inventory != null && !isSubEditor)
|
||||
{
|
||||
var inv = Character.Controlled.Inventory;
|
||||
for (var i = 0; i < inv.visualSlots.Length; i++)
|
||||
if (IsOnInventorySlot(Character.Controlled.Inventory)) { return true; }
|
||||
}
|
||||
|
||||
if (Character.Controlled.SelectedCharacter?.Inventory != null && !isSubEditor)
|
||||
{
|
||||
if (IsOnInventorySlot(Character.Controlled.SelectedCharacter.Inventory)) { return true; }
|
||||
}
|
||||
|
||||
bool IsOnInventorySlot(Inventory inventory)
|
||||
{
|
||||
for (var i = 0; i < inventory.visualSlots.Length; i++)
|
||||
{
|
||||
var slot = inv.visualSlots[i];
|
||||
if (inventory.HideSlot(i)) { continue; }
|
||||
var slot = inventory.visualSlots[i];
|
||||
if (slot.InteractRect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if the equip button actually exists
|
||||
if (slot.EquipButtonRect.Contains(PlayerInput.MousePosition) &&
|
||||
i >= 0 && inv.slots.Length > i &&
|
||||
!inv.slots[i].Empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Character.Controlled.SelectedCharacter?.Inventory != null && !isSubEditor)
|
||||
{
|
||||
var inv = Character.Controlled.SelectedCharacter.Inventory;
|
||||
for (var i = 0; i < inv.visualSlots.Length; i++)
|
||||
{
|
||||
var slot = inv.visualSlots[i];
|
||||
if (slot.InteractRect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if the equip button actually exists
|
||||
if (slot.EquipButtonRect.Contains(PlayerInput.MousePosition) &&
|
||||
i >= 0 && inv.slots.Length > i &&
|
||||
!inv.slots[i].Empty())
|
||||
if (slot.EquipButtonRect.Contains(PlayerInput.MousePosition) &&
|
||||
i >= 0 && inventory.slots.Length > i &&
|
||||
!inventory.slots[i].Empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Character.Controlled.SelectedItem != null)
|
||||
@@ -1034,7 +1046,7 @@ namespace Barotrauma
|
||||
|
||||
protected static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Rectangle highlightedSlot)
|
||||
{
|
||||
GUIComponent.DrawToolTip(spriteBatch, toolTip, highlightedSlot);
|
||||
GUIComponent.DrawToolTip(spriteBatch, toolTip, highlightedSlot, Anchor.BottomRight);
|
||||
}
|
||||
|
||||
public void DrawSubInventory(SpriteBatch spriteBatch, int slotIndex)
|
||||
@@ -1046,6 +1058,7 @@ namespace Barotrauma
|
||||
if (container == null || !container.DrawInventory) { return; }
|
||||
|
||||
if (container.Inventory.visualSlots == null || !container.Inventory.isSubInventory) { return; }
|
||||
if (container.Inventory.DrawWhenEquipped) { return; }
|
||||
|
||||
int itemCapacity = container.Capacity;
|
||||
|
||||
@@ -1215,8 +1228,8 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
DraggingItems.ForEachMod(it => it.Drop(Character.Controlled));
|
||||
DraggingItems.First().CreateDroppedStack(DraggingItems, allowClientExecute: false);
|
||||
}
|
||||
|
||||
SoundPlayer.PlayUISound(removed ? GUISoundType.PickItem : GUISoundType.DropItem);
|
||||
}
|
||||
}
|
||||
@@ -1258,11 +1271,19 @@ namespace Barotrauma
|
||||
{
|
||||
allowCombine = false;
|
||||
}
|
||||
int itemCount = 0;
|
||||
foreach (Item item in DraggingItems)
|
||||
{
|
||||
bool success = selectedInventory.TryPutItem(item, slotIndex, allowSwapping: !anySuccess, allowCombine, Character.Controlled);
|
||||
anySuccess |= success;
|
||||
if (!success) { break; }
|
||||
if (success)
|
||||
{
|
||||
anySuccess = true;
|
||||
itemCount++;
|
||||
}
|
||||
if (!success || itemCount >= item.Prefab.GetMaxStackSize(selectedInventory))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anySuccess)
|
||||
@@ -1360,10 +1381,12 @@ namespace Barotrauma
|
||||
protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
|
||||
{
|
||||
Rectangle hoverArea;
|
||||
if (!subSlot.Inventory.Movable() ||
|
||||
if ((Screen.Selected != GameMain.SubEditorScreen || GameMain.SubEditorScreen.DrawCharacterInventory) &&
|
||||
(!subSlot.Inventory.Movable() ||
|
||||
(Character.Controlled?.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item)) ||
|
||||
(subSlot.ParentInventory is CharacterInventory characterInventory && characterInventory.CurrentLayout != CharacterInventory.Layout.Default))
|
||||
(subSlot.ParentInventory is CharacterInventory characterInventory && characterInventory.CurrentLayout != CharacterInventory.Layout.Default)))
|
||||
{
|
||||
//slot not visible as a separate, movable panel -> just use the area of the slot directly
|
||||
hoverArea = subSlot.Slot.Rect;
|
||||
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
|
||||
hoverArea = Rectangle.Union(hoverArea, subSlot.Slot.EquipButtonRect);
|
||||
@@ -1372,7 +1395,10 @@ namespace Barotrauma
|
||||
{
|
||||
hoverArea = subSlot.Inventory.BackgroundFrame;
|
||||
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
|
||||
hoverArea = Rectangle.Union(hoverArea, subSlot.Inventory.movableFrameRect);
|
||||
if (subSlot.Inventory.movableFrameRect != Rectangle.Empty)
|
||||
{
|
||||
hoverArea = Rectangle.Union(hoverArea, subSlot.Inventory.movableFrameRect);
|
||||
}
|
||||
}
|
||||
|
||||
if (subSlot.Inventory?.visualSlots != null)
|
||||
@@ -1465,18 +1491,28 @@ namespace Barotrauma
|
||||
color: Character.Controlled.FocusedItem == null && !mouseOnHealthInterface ? GUIStyle.Red : Color.LightGreen,
|
||||
font: GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
Item draggedItem = DraggingItems.First();
|
||||
|
||||
sprite.Draw(spriteBatch, itemPos + Vector2.One * 2, Color.Black, scale: scale);
|
||||
sprite.Draw(spriteBatch,
|
||||
itemPos,
|
||||
sprite == DraggingItems.First().Sprite ? DraggingItems.First().GetSpriteColor() : DraggingItems.First().GetInventoryIconColor(),
|
||||
sprite == draggedItem.Sprite ? draggedItem.GetSpriteColor() : draggedItem.GetInventoryIconColor(),
|
||||
scale: scale);
|
||||
|
||||
if (DraggingItems.First().Prefab.MaxStackSize > 1)
|
||||
if (draggedItem.Prefab.GetMaxStackSize(null) > 1)
|
||||
{
|
||||
int stackAmount = DraggingItems.Count;
|
||||
if (selectedSlot?.ParentInventory != null)
|
||||
{
|
||||
stackAmount = Math.Min(
|
||||
stackAmount,
|
||||
selectedSlot.ParentInventory.HowManyCanBePut(draggedItem.Prefab, selectedSlot.SlotIndex, draggedItem.Condition));
|
||||
}
|
||||
Vector2 stackCountPos = itemPos + Vector2.One * iconSize * 0.25f;
|
||||
string stackCountText = "x" + DraggingItems.Count;
|
||||
string stackCountText = "x" + stackAmount;
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos + Vector2.One, Color.Black);
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, Color.White);
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, GUIStyle.TextColorBright);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1673,7 +1709,7 @@ namespace Barotrauma
|
||||
color: GUIStyle.Red,
|
||||
scale: iconSize.X / stealIcon.size.X);
|
||||
}
|
||||
int maxStackSize = item.Prefab.MaxStackSize;
|
||||
int maxStackSize = item.Prefab.GetMaxStackSize(inventory);
|
||||
if (inventory is ItemInventory itemInventory)
|
||||
{
|
||||
maxStackSize = Math.Min(maxStackSize, itemInventory.Container.GetMaxStackSize(slotIndex));
|
||||
@@ -1691,7 +1727,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (HealingCooldown.IsOnCooldown && item.HasTag(HealingCooldown.MedicalItemTag))
|
||||
if (HealingCooldown.IsOnCooldown && item.HasTag(Tags.MedicalItem))
|
||||
{
|
||||
RectangleF cdRect = rect;
|
||||
// shrink the rect from top to bottom depending on HealingCooldown.NormalizedCooldown
|
||||
@@ -1794,10 +1830,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg)
|
||||
{
|
||||
UInt16 lastEventID = msg.ReadUInt16();
|
||||
SharedRead(msg, out receivedItemIDs);
|
||||
partialReceivedItemIDs ??= new List<ushort>[capacity];
|
||||
SharedRead(msg, partialReceivedItemIDs, out bool readyToApply);
|
||||
if (!readyToApply) { return; }
|
||||
|
||||
receivedItemIDs = partialReceivedItemIDs.ToArray();
|
||||
partialReceivedItemIDs = null;
|
||||
|
||||
//delay applying the new state if less than 1 second has passed since this client last sent a state to the server
|
||||
//prevents the inventory from briefly reverting to an old state if items are moved around in quick succession
|
||||
@@ -1866,7 +1907,7 @@ namespace Barotrauma
|
||||
if (!receivedItemIDs[i].Any()) { continue; }
|
||||
foreach (UInt16 id in receivedItemIDs[i])
|
||||
{
|
||||
if (!(Entity.FindEntityByID(id) is Item item) || slots[i].Contains(item)) { continue; }
|
||||
if (Entity.FindEntityByID(id) is not Item item || slots[i].Contains(item)) { continue; }
|
||||
if (!TryPutItem(item, i, false, false, null, false))
|
||||
{
|
||||
ForceToSlot(item, i);
|
||||
@@ -1883,11 +1924,5 @@ namespace Barotrauma
|
||||
|
||||
receivedItemIDs = null;
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
SharedWrite(msg, extraData);
|
||||
syncItemsDelay = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user