Merge branch 'master' of https://github.com/Regalis11/Barotrauma.git into Regalis11-master

This commit is contained in:
Evil Factory
2021-10-27 14:41:47 -03:00
450 changed files with 26043 additions and 10457 deletions

View File

@@ -48,8 +48,13 @@ namespace Barotrauma
{
color = Color.CornflowerBlue;
}
else if (Entity is Item)
else if (Entity is Item i)
{
if (i.Submarine != null && i.GetComponent<Items.Components.Door>() == 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.
return;
}
color = Color.CadetBlue;
}
else

View File

@@ -81,10 +81,10 @@ namespace Barotrauma
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), GUI.Style.Green, 0, 4);
}
if (LatchOntoAI.WallAttachPos.HasValue)
if (LatchOntoAI.AttachPos.HasValue)
{
//GUI.DrawLine(spriteBatch, pos,
// ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.WallAttachPos.Value.X, -LatchOntoAI.WallAttachPos.Value.Y)), GUI.Style.Green, 0, 3);
GUI.DrawLine(spriteBatch, pos,
ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.AttachPos.Value.X, -LatchOntoAI.AttachPos.Value.Y)), GUI.Style.Green, 0, 3);
}
}

View File

@@ -284,7 +284,7 @@ namespace Barotrauma
limb.LastImpactSoundTime = (float)Timing.TotalTime;
if (!string.IsNullOrWhiteSpace(limb.HitSoundTag))
{
bool inWater = limb.inWater;
bool inWater = limb.InWater;
if (character.CurrentHull != null &&
character.CurrentHull.Surface > character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height + 5.0f &&
limb.SimPosition.Y < ConvertUnits.ToSimUnits(character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height) + limb.body.GetMaxExtent())
@@ -342,22 +342,41 @@ namespace Barotrauma
partial void SetupDrawOrder()
{
//make sure every character gets drawn at a distinct "layer"
//make sure every character gets drawn at a distinct "layer"
//(instead of having some of the limbs appear behind and some in front of other characters)
float startDepth = 0.1f;
float increment = 0.001f;
foreach (Character otherCharacter in Character.CharacterList)
{
if (otherCharacter == character) continue;
if (otherCharacter == character) { continue; }
startDepth += increment;
}
//make sure each limb has a distinct depth value
List<Limb> depthSortedLimbs = Limbs.OrderBy(l => l.ActiveSprite == null ? 0.0f : l.ActiveSprite.Depth).ToList();
//make sure each limb has a distinct depth value
List<Limb> depthSortedLimbs = Limbs.OrderBy(l => l.DefaultSpriteDepth).ToList();
foreach (Limb limb in Limbs)
{
if (limb.ActiveSprite != null)
limb.ActiveSprite.Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f;
var sprite = limb.GetActiveSprite();
if (sprite == null) { continue; }
sprite.Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f;
foreach (var conditionalSprite in limb.ConditionalSprites)
{
if (conditionalSprite.Exclusive)
{
conditionalSprite.ActiveSprite.Depth = sprite.Depth;
}
}
}
foreach (Limb limb in Limbs)
{
if (limb.ActiveSprite == null) { continue; }
if (limb.Params.InheritLimbDepth == LimbType.None) { continue; }
var matchingLimb = GetLimb(limb.Params.InheritLimbDepth);
if (matchingLimb != null)
{
limb.ActiveSprite.Depth = matchingLimb.ActiveSprite.Depth - 0.0000001f;
}
}
depthSortedLimbs.Reverse();
inversedLimbDrawOrder = depthSortedLimbs.ToArray();
}
@@ -562,9 +581,16 @@ namespace Barotrauma
if (this is HumanoidAnimController humanoid)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos);
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.Position; }
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.Position; }
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
Vector2 aimPos = humanoid.AimSourceWorldPos;
aimPos.Y = -aimPos.Y;
GUI.DrawLine(spriteBatch, aimPos - Vector2.UnitY * 3, aimPos + Vector2.UnitY * 3, Color.Red);
GUI.DrawLine(spriteBatch, aimPos - Vector2.UnitX * 3, aimPos + Vector2.UnitX * 3, Color.Red);
}
if (character.MemState.Count > 1)

View File

@@ -1,5 +1,4 @@
using Barotrauma.Sounds;
using Barotrauma.Particles;
using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using System.Xml.Linq;

View File

@@ -21,7 +21,6 @@ namespace Barotrauma
public static bool DebugDrawInteract;
protected float soundTimer;
protected float soundInterval;
protected float hudInfoTimer = 1.0f;
protected bool hudInfoVisible = false;
@@ -129,8 +128,52 @@ namespace Barotrauma
get { return gibEmitters; }
}
private class GUIMessage
{
public string RawText;
public string Identifier;
public string Text;
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
Text = RawText.Replace("[value]", _value.ToString());
Size = GUI.Font.MeasureString(Text);
}
}
public Color Color;
public float Lifetime;
public float Timer;
public Vector2 Size;
public bool PlaySound;
public GUIMessage(string rawText, Color color, float delay, string identifier = null, int? value = null)
{
RawText = Text = rawText;
if (value.HasValue)
{
Text = rawText.Replace("[value]", value.Value.ToString());
Value = value.Value;
}
Timer = -delay;
Size = GUI.Font.MeasureString(Text);
Color = color;
Identifier = identifier;
Lifetime = 3.0f;
}
}
private List<GUIMessage> guiMessages = new List<GUIMessage>();
public static bool IsMouseOnUI => GUI.MouseOn != null ||
(CharacterInventory.IsMouseOnInventory() && !CharacterInventory.DraggingItemToWorld);
(CharacterInventory.IsMouseOnInventory && !CharacterInventory.DraggingItemToWorld);
public class ObjectiveEntity
{
@@ -161,8 +204,7 @@ namespace Barotrauma
partial void InitProjSpecific(XElement mainElement)
{
soundInterval = mainElement.GetAttributeFloat("soundinterval", 10.0f);
soundTimer = Rand.Range(0.0f, soundInterval);
soundTimer = Rand.Range(0.0f, Params.SoundInterval);
sounds = new List<CharacterSound>();
Params.Sounds.ForEach(s => sounds.Add(new CharacterSound(s)));
@@ -390,12 +432,7 @@ namespace Barotrauma
{
if (attackResult.Damage <= 1.0f) { return; }
}
if (soundTimer < soundInterval * 0.5f)
{
PlaySound(CharacterSound.SoundType.Damage);
soundTimer = soundInterval;
}
PlaySound(CharacterSound.SoundType.Damage, maxInterval: 2);
}
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log)
@@ -412,7 +449,7 @@ namespace Barotrauma
if (GameMain.NetworkMember.RespawnManager?.UseRespawnPrompt ?? false)
{
CoroutineManager.InvokeAfter(() =>
CoroutineManager.Invoke(() =>
{
if (controlled != null || (!(GameMain.GameSession?.IsRunning ?? false))) { return; }
var respawnPrompt = new GUIMessageBox(
@@ -470,9 +507,9 @@ namespace Barotrauma
}
private List<Item> debugInteractablesInRange = new List<Item>();
private List<Item> debugInteractablesAtCursor = new List<Item>();
private List<Pair<Item, float>> debugInteractablesNearCursor = new List<Pair<Item, float>>();
private readonly List<Item> debugInteractablesInRange = new List<Item>();
private readonly List<Item> debugInteractablesAtCursor = new List<Item>();
private readonly List<(Item item, float dist)> debugInteractablesNearCursor = new List<(Item item, float dist)>();
/// <summary>
/// Finds the front (lowest depth) interactable item at a position. "Interactable" in this case means that the character can "reach" the item.
@@ -568,7 +605,7 @@ namespace Barotrauma
if (distanceToItem > closestItemDistance) { continue; }
if (!CanInteractWith(item)) { continue; }
debugInteractablesNearCursor.Add(new Pair<Item, float>(item, 1.0f - distanceToItem / (100.0f * aimAssistModifier)));
debugInteractablesNearCursor.Add((item, 1.0f - distanceToItem / (100.0f * aimAssistModifier)));
closestItem = item;
closestItemDistance = distanceToItem;
}
@@ -579,31 +616,20 @@ namespace Barotrauma
private Character FindCharacterAtPosition(Vector2 mouseSimPos, float maxDist = 150.0f)
{
Character closestCharacter = null;
float closestDist = 0.0f;
maxDist = ConvertUnits.ToSimUnits(maxDist);
float closestDist = maxDist * maxDist;
foreach (Character c in CharacterList)
{
if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition);
if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist))
if (dist < closestDist ||
(c.CampaignInteractionType != CampaignMode.InteractionType.None && closestCharacter?.CampaignInteractionType == CampaignMode.InteractionType.None && dist * 0.9f < closestDist))
{
closestCharacter = c;
closestDist = dist;
}
/*FarseerPhysics.Common.Transform transform;
c.AnimController.Collider.FarseerBody.GetTransform(out transform);
for (int i = 0; i < c.AnimController.Collider.FarseerBody.FixtureList.Count; i++)
{
if (c.AnimController.Collider.FarseerBody.FixtureList[i].Shape.TestPoint(ref transform, ref mouseSimPos))
{
Console.WriteLine("Hit: " + i);
closestCharacter = c;
}
}*/
}
return closestCharacter;
@@ -636,9 +662,20 @@ namespace Barotrauma
}
}
foreach (GUIMessage message in guiMessages)
{
bool wasPending = message.Timer < 0.0f;
message.Timer += deltaTime;
if (wasPending && message.Timer >= 0.0f && message.PlaySound)
{
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
}
}
guiMessages.RemoveAll(m => m.Timer >= m.Lifetime);
if (!enabled) { return; }
if (!IsDead && !IsIncapacitated)
if (!IsIncapacitated)
{
if (soundTimer > 0)
{
@@ -649,7 +686,14 @@ namespace Barotrauma
switch (enemyAI.State)
{
case AIState.Attack:
PlaySound(CharacterSound.SoundType.Attack);
if (Rand.Value() > 0.5f)
{
PlaySound(CharacterSound.SoundType.Attack);
}
else
{
PlaySound(CharacterSound.SoundType.Idle);
}
break;
default:
var petBehavior = enemyAI.PetBehavior;
@@ -660,7 +704,6 @@ namespace Barotrauma
else
{
PlaySound(CharacterSound.SoundType.Idle);
}
break;
}
@@ -748,6 +791,27 @@ namespace Barotrauma
CharacterHUD.Draw(spriteBatch, this, cam);
if (drawHealth && !CharacterHUD.IsCampaignInterfaceOpen) { CharacterHealth.DrawHUD(spriteBatch); }
}
public void DrawGUIMessages(SpriteBatch spriteBatch, Camera cam)
{
if (info == null || !Enabled || InvisibleTimer > 0.0f)
{
return;
}
Vector2 messagePos = DrawPosition;
messagePos.Y += hudInfoHeight;
messagePos = cam.WorldToScreen(messagePos) - Vector2.UnitY * GUI.IntScale(60);
foreach (GUIMessage message in guiMessages)
{
if (message.Timer < 0) { continue; }
Vector2 drawPos = messagePos + Vector2.UnitX * (GUI.IntScale(60) - message.Size.X);
drawPos = new Vector2((int)drawPos.X, (int)drawPos.Y);
float alpha = MathHelper.SmoothStep(1.0f, 0.0f, message.Timer / message.Lifetime);
GUI.DrawString(spriteBatch, drawPos, message.Text, message.Color * alpha);
messagePos -= Vector2.UnitY * message.Size.Y * 1.2f;
}
}
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{
@@ -827,12 +891,12 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y),
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), Color.White * 0.1f, width: 4);
}
foreach (Pair<Item, float> item in debugInteractablesNearCursor)
foreach ((Item item, float dist) in debugInteractablesNearCursor)
{
GUI.DrawLine(spriteBatch,
cursorPos,
new Vector2(item.First.DrawPosition.X, -item.First.DrawPosition.Y),
ToolBox.GradientLerp(item.Second, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green), width: 2);
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y),
ToolBox.GradientLerp(dist, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green), width: 2);
}
}
return;
@@ -856,6 +920,7 @@ namespace Barotrauma
Vector2 nameSize = GUI.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Color nameColor = GetNameColor();
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
@@ -865,18 +930,6 @@ namespace Barotrauma
namePos *= viewportSize / screenSize;
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
Color nameColor = Color.White;
if (Controlled != null && TeamID != Controlled.TeamID)
{
if (TeamID == CharacterTeamType.FriendlyNPC)
{
nameColor = UniqueNameColor ?? Color.SkyBlue;
}
else
{
nameColor = GUI.Style.Red;
}
}
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
@@ -931,6 +984,89 @@ namespace Barotrauma
}
}
public Color GetNameColor()
{
CharacterTeamType team = teamID;
if (Info?.IsDisguisedAsAnother != null)
{
var idCard = Inventory.GetItemInLimbSlot(InvSlotType.Card)?.GetComponent<IdCard>();
if (idCard != null)
{
if (team == CharacterTeamType.Team2 && idCard.TeamID != CharacterTeamType.Team2)
{
team = CharacterTeamType.Team1;
}
else if (team == CharacterTeamType.Team1 && idCard.TeamID == CharacterTeamType.Team2)
{
team = CharacterTeamType.Team2;
}
}
}
Color nameColor = GUI.Style.TextColor;
if (Controlled != null && team != Controlled.TeamID)
{
if (TeamID == CharacterTeamType.FriendlyNPC)
{
nameColor = UniqueNameColor ?? Color.SkyBlue;
}
else
{
nameColor = GUI.Style.Red;
}
}
return nameColor;
}
public void AddMessage(string rawText, Color color, bool playSound, string identifier = null, int? value = null)
{
GUIMessage existingMessage = null;
float delay = 0.0f;
if (guiMessages.Any())
{
delay = guiMessages.Min(m => m.Timer) - 0.5f;
if (delay < 0)
{
delay = -delay;
if (guiMessages.Count > 5)
{
//reduce delays if there's lots of messages
guiMessages.Where(m => m.Timer < 0.0f).ForEach(m => m.Timer *= 0.9f);
}
}
else
{
delay = 0;
}
}
if (identifier != null)
{
existingMessage = guiMessages.Find(m => m.Identifier == identifier && m.Timer < m.Lifetime * 0.5f);
}
if (existingMessage == null || !value.HasValue)
{
var newMessage = new GUIMessage(rawText, color, delay, identifier, value);
guiMessages.Insert(0, newMessage);
if (playSound)
{
if (delay > 0.0f)
{
newMessage.PlaySound = true;
}
else
{
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
}
}
}
else
{
existingMessage.Value += value.Value;
}
}
/// <summary>
/// Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's one already linked to the object)
/// The progress bar will automatically fade out after 1 sec if the method hasn't been called during that time
@@ -958,12 +1094,13 @@ namespace Barotrauma
private readonly List<CharacterSound> matchingSounds = new List<CharacterSound>();
private SoundChannel soundChannel;
public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f)
public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f, float maxInterval = 0)
{
if (sounds == null || sounds.Count == 0) { return; }
if (soundChannel != null && soundChannel.IsPlaying) { return; }
if (GameMain.SoundManager?.Disabled ?? true) { return; }
if (soundTimer > soundInterval * soundIntervalFactor) { return; }
if (soundTimer > Params.SoundInterval * soundIntervalFactor) { return; }
if (Params.SoundInterval - soundTimer < maxInterval) { return; }
matchingSounds.Clear();
foreach (var s in sounds)
{
@@ -975,7 +1112,7 @@ namespace Barotrauma
var selectedSound = matchingSounds.GetRandom();
if (selectedSound?.Sound == null) { return; }
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: CurrentHull, ignoreMuffling: selectedSound.IgnoreMuffling);
soundTimer = soundInterval;
soundTimer = Params.SoundInterval;
}
public void AddActiveObjectiveEntity(Entity entity, Sprite sprite, Color? color = null)
@@ -1028,5 +1165,20 @@ namespace Barotrauma
Rand.Range(50.0f, 500.0f), null);
}
}
partial void OnMoneyChanged(int prevAmount, int newAmount)
{
if (newAmount > prevAmount)
{
int increase = newAmount - prevAmount;
AddMessage("+" + TextManager.GetWithVariable("currencyformat", "[credits]", "[value]"),
GUI.Style.Yellow, playSound: this == Controlled, "money", increase);
}
}
partial void OnTalentGiven(string talentIdentifier)
{
AddMessage(TextManager.Get("talentname." + talentIdentifier.ToString()), GUI.Style.Yellow, playSound: this == Controlled);
}
}
}

View File

@@ -100,7 +100,7 @@ namespace Barotrauma
}
}
private static bool shouldRecreateHudTexts = true;
public static bool ShouldRecreateHudTexts { get; set; } = true;
private static bool heldDownShiftWhenGotHudTexts;
public static bool IsCampaignInterfaceOpen =>
@@ -150,7 +150,7 @@ namespace Barotrauma
}
}
if (character.IsHumanoid && character.SelectedCharacter != null)
if (character.Params.CanInteract && character.SelectedCharacter != null)
{
character.SelectedCharacter.CharacterHealth.AddToGUIUpdateList();
}
@@ -195,7 +195,7 @@ namespace Barotrauma
}
}
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
if (character.Params.CanInteract && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
{
if (character.SelectedCharacter.CanInventoryBeAccessed)
{
@@ -219,7 +219,7 @@ namespace Barotrauma
if (focusedItemOverlayTimer <= 0.0f)
{
focusedItem = null;
shouldRecreateHudTexts = true;
ShouldRecreateHudTexts = true;
}
}
}
@@ -285,6 +285,21 @@ namespace Barotrauma
i.GetRootInventoryOwner() == i);
}
if (GameMain.GameSession != null)
{
foreach (var mission in GameMain.GameSession.Missions)
{
if (!mission.DisplayTargetHudIcons) { continue; }
foreach (var target in mission.HudIconTargets)
{
if (target.Submarine != character.Submarine) { continue; }
float alpha = GetDistanceBasedIconAlpha(target, maxDistance: mission.Prefab.HudIconMaxDistance);
if (alpha <= 0.0f) { continue; }
GUI.DrawIndicator(spriteBatch, target.DrawPosition, cam, 100.0f, mission.Prefab.HudIcon, mission.Prefab.HudIconColor * alpha);
}
}
}
foreach (Character.ObjectiveEntity objectiveEntity in character.ActiveObjectiveEntities)
{
DrawObjectiveIndicator(spriteBatch, cam, character, objectiveEntity, 1.0f);
@@ -317,7 +332,7 @@ namespace Barotrauma
if (focusedItem != character.FocusedItem)
{
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
shouldRecreateHudTexts = true;
ShouldRecreateHudTexts = true;
}
focusedItem = character.FocusedItem;
}
@@ -342,13 +357,13 @@ namespace Barotrauma
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
{
bool shiftDown = PlayerInput.IsShiftDown();
if (shouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
if (ShouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
{
shouldRecreateHudTexts = true;
ShouldRecreateHudTexts = true;
heldDownShiftWhenGotHudTexts = shiftDown;
}
var hudTexts = focusedItem.GetHUDTexts(character, shouldRecreateHudTexts);
shouldRecreateHudTexts = false;
var hudTexts = focusedItem.GetHUDTexts(character, ShouldRecreateHudTexts);
ShouldRecreateHudTexts = false;
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
@@ -391,7 +406,26 @@ namespace Barotrauma
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; }
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType);
GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, npc.CurrentHull == character.CurrentHull ? 500.0f : 100.0f, iconStyle.GetDefaultSprite(), iconStyle.Color);
Range<float> visibleRange = new Range<float>(npc.CurrentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, float.PositiveInfinity);
if (npc.CampaignInteractionType == CampaignMode.InteractionType.Examine)
{
//TODO: we could probably do better than just hardcoding
//a check for InteractionType.Examine here.
if (Vector2.DistanceSquared(character.Position, npc.Position) > 500f * 500f) { continue; }
var body = Submarine.CheckVisibility(character.SimPosition, npc.SimPosition, ignoreLevel: true);
if (body != null && body.UserData as Character != npc) { continue; }
visibleRange = new Range<float>(-100f, 500f);
}
GUI.DrawIndicator(
spriteBatch,
npc.WorldPosition,
cam,
visibleRange,
iconStyle.GetDefaultSprite(),
iconStyle.Color);
}
foreach (Item item in Item.ItemList)
@@ -400,7 +434,7 @@ namespace Barotrauma
if (Vector2.DistanceSquared(character.Position, item.Position) > 500f*500f) { continue; }
var body = Submarine.CheckVisibility(character.SimPosition, item.SimPosition, ignoreLevel: true);
if (body != null && body.UserData as Item != item) { continue; }
GUI.DrawIndicator(spriteBatch, item.WorldPosition + new Vector2(0f, item.RectHeight * 0.65f), cam, new Vector2(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);
GUI.DrawIndicator(spriteBatch, item.WorldPosition + new Vector2(0f, item.RectHeight * 0.65f), cam, new Range<float>(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);
}
}
@@ -471,7 +505,7 @@ namespace Barotrauma
if (!character.IsIncapacitated && character.Stun <= 0.0f)
{
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
if (character.Params.CanInteract && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
{
if (character.SelectedCharacter.CanInventoryBeAccessed)
{
@@ -525,12 +559,7 @@ namespace Barotrauma
textPos -= new Vector2(textSize.X / 2, textSize.Y);
Color nameColor = GUI.Style.TextColor;
if (character.TeamID != character.FocusedCharacter.TeamID)
{
nameColor = character.FocusedCharacter.TeamID == CharacterTeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
}
Color nameColor = character.FocusedCharacter.GetNameColor();
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUI.SubHeadingFont.MeasureString(focusName).Y;
@@ -544,11 +573,14 @@ namespace Barotrauma
if (character.FocusedCharacter.CanBeDragged)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)),
string text = character.CanEat ? "EatHint" : "GrabHint";
GUI.DrawString(spriteBatch, textPos, GetCachedHudText(text, GameMain.Config.KeyBindText(InputType.Grab)),
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
textPos.Y += largeTextSize.Y;
}
if (!character.DisableHealthWindow &&
character.IsFriendly(character.FocusedCharacter) &&
character.FocusedCharacter.CharacterHealth.UseHealthWindow &&
character.CanInteractWith(character.FocusedCharacter, 160f, false))
{

View File

@@ -17,11 +17,19 @@ namespace Barotrauma
public bool LastControlled;
#warning TODO: Refactor
private Sprite disguisedPortrait;
private List<WearableSprite> disguisedAttachmentSprites;
private Vector2? disguisedSheetIndex;
private Sprite disguisedJobIcon;
private Color disguisedJobColor;
private Color disguisedHairColor;
private Color disguisedFacialHairColor;
private Color disguisedSkinColor;
private Sprite tintMask;
private float tintHighlightThreshold;
private float tintHighlightMultiplier;
public static void Init()
{
@@ -29,6 +37,20 @@ namespace Barotrauma
new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(833, 298, 142, 98), null, 0);
}
partial void LoadHeadSpriteProjectSpecific(XElement limbElement)
{
XElement maskElement = limbElement.Element("tintmask");
if (maskElement != null)
{
string tintMaskPath = maskElement.GetAttributeString("texture", "");
if (!string.IsNullOrWhiteSpace(tintMaskPath))
{
tintMask = new Sprite(maskElement, file: Limb.GetSpritePath(tintMaskPath, this));
tintHighlightThreshold = maskElement.GetAttributeFloat("highlightthreshold", 0.6f);
tintHighlightMultiplier = maskElement.GetAttributeFloat("highlightmultiplier", 0.8f);
}
}
}
public GUIComponent CreateInfoFrame(GUIFrame frame, bool returnParent, Sprite permissionIcon = null)
{
@@ -143,10 +165,10 @@ namespace Barotrauma
private void DrawInfoFrameCharacterIcon(SpriteBatch sb, Rectangle componentRect)
{
if (headSprite == null) { return; }
if (_headSprite == null) { return; }
Vector2 targetAreaSize = componentRect.Size.ToVector2();
float scale = Math.Min(targetAreaSize.X / headSprite.size.X, targetAreaSize.Y / headSprite.size.Y);
DrawIcon(sb, componentRect.Location.ToVector2() + headSprite.size / 2 * scale, targetAreaSize);
float scale = Math.Min(targetAreaSize.X / _headSprite.size.X, targetAreaSize.Y / _headSprite.size.Y);
DrawIcon(sb, componentRect.Location.ToVector2() + _headSprite.size / 2 * scale, targetAreaSize);
}
public GUIFrame CreateCharacterFrame(GUIComponent parent, string text, object userData)
@@ -165,21 +187,36 @@ namespace Barotrauma
return frame;
}
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos)
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel)
{
if (TeamID == CharacterTeamType.FriendlyNPC) { return; }
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
// if we increased by more than 1 in one increase, then display special color (for talents)
bool specialIncrease = Math.Abs(newLevel - prevLevel) >= 1.0f;
if ((int)newLevel > (int)prevLevel)
{
int increase = Math.Max((int)newLevel - (int)prevLevel, 1);
GUI.AddMessage(
string.Format("+{0} {1}", increase, TextManager.Get("SkillName." + skillIdentifier)),
GUI.Style.Green,
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: false,
subId: Character?.Submarine?.ID ?? -1);
Character?.AddMessage(
"+[value] "+ TextManager.Get("SkillName." + skillIdentifier),
specialIncrease ? GUI.Style.Orange : GUI.Style.Green,
playSound: Character == Character.Controlled, skillIdentifier, increase);
}
}
partial void OnExperienceChanged(int prevAmount, int newAmount)
{
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
GameSession.TabMenuInstance?.OnExperienceChanged(Character);
if (newAmount > prevAmount)
{
int increase = newAmount - prevAmount;
Character?.AddMessage(
"+[value] " + TextManager.Get("experienceshort"),
GUI.Style.Blue, playSound: Character == Character.Controlled, "exp", increase);
}
}
@@ -187,193 +224,36 @@ namespace Barotrauma
{
if (idCard.Item.Tags == string.Empty) return;
if (idCard.StoredJobPrefab == null || idCard.StoredPortrait == null)
if (idCard.StoredOwnerAppearance.JobPrefab == null || idCard.StoredOwnerAppearance.Portrait == null)
{
string[] readTags = idCard.Item.Tags.Split(',');
if (readTags.Length == 0) return;
if (readTags.Length == 0) { return; }
if (idCard.StoredJobPrefab == null)
if (idCard.StoredOwnerAppearance.JobPrefab == null)
{
string jobIdTag = readTags.FirstOrDefault(s => s.StartsWith("jobid:"));
if (jobIdTag != null && jobIdTag.Length > 6)
{
string jobId = jobIdTag.Substring(6);
if (jobId != string.Empty)
{
idCard.StoredJobPrefab = JobPrefab.Get(jobId);
}
}
idCard.StoredOwnerAppearance.ExtractJobPrefab(readTags);
}
if (idCard.StoredPortrait == null)
if (idCard.StoredOwnerAppearance.Portrait == null)
{
string disguisedGender = string.Empty;
string disguisedRace = string.Empty;
string disguisedHeadSpriteId = string.Empty;
int disguisedHairIndex = -1;
int disguisedBeardIndex = -1;
int disguisedMoustacheIndex = -1;
int disguisedFaceAttachmentIndex = -1;
foreach (string tag in readTags)
{
string[] s = tag.Split(':');
switch (s[0])
{
case "gender":
disguisedGender = s[1];
break;
case "race":
disguisedRace = s[1];
break;
case "headspriteid":
disguisedHeadSpriteId = s[1];
break;
case "hairindex":
disguisedHairIndex = int.Parse(s[1]);
break;
case "beardindex":
disguisedBeardIndex = int.Parse(s[1]);
break;
case "moustacheindex":
disguisedMoustacheIndex = int.Parse(s[1]);
break;
case "faceattachmentindex":
disguisedFaceAttachmentIndex = int.Parse(s[1]);
break;
case "sheetindex":
string[] vectorValues = s[1].Split(";");
idCard.StoredSheetIndex = new Vector2(float.Parse(vectorValues[0]), float.Parse(vectorValues[1]));
break;
}
}
if (disguisedGender == string.Empty || disguisedRace == string.Empty || disguisedHeadSpriteId == string.Empty)
{
idCard.StoredPortrait = null;
idCard.StoredAttachments = null;
return;
}
foreach (XElement limbElement in Ragdoll.MainElement.Elements())
{
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
XElement spriteElement = limbElement.Element("sprite");
if (spriteElement == null) { continue; }
string spritePath = spriteElement.Attribute("texture").Value;
spritePath = spritePath.Replace("[GENDER]", disguisedGender);
spritePath = spritePath.Replace("[RACE]", disguisedRace.ToLowerInvariant());
spritePath = spritePath.Replace("[HEADID]", disguisedHeadSpriteId);
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)))
{
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
{
continue;
}
string fileWithoutTags = Path.GetFileNameWithoutExtension(file);
fileWithoutTags = fileWithoutTags.Split('[', ']').First();
if (fileWithoutTags != fileName) { continue; }
idCard.StoredPortrait = new Sprite(spriteElement, "", file) { RelativeOrigin = Vector2.Zero };
break;
}
break;
}
if (Wearables != null)
{
XElement disguisedHairElement, disguisedBeardElement, disguisedMoustacheElement, disguisedFaceAttachmentElement;
List<XElement> disguisedHairs, disguisedBeards, disguisedMoustaches, disguisedFaceAttachments;
Gender disguisedGenderEnum = disguisedGender == "female" ? Gender.Female : Gender.Male;
Race disguisedRaceEnum = (Race)Enum.Parse(typeof(Race), disguisedRace);
int headSpriteId = int.Parse(disguisedHeadSpriteId);
float commonness = disguisedGenderEnum == Gender.Female ? 0.05f : 0.2f;
disguisedHairs = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Hair, headSpriteId), WearableType.Hair, commonness);
disguisedBeards = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Beard, headSpriteId), WearableType.Beard);
disguisedMoustaches = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.Moustache, headSpriteId), WearableType.Moustache);
disguisedFaceAttachments = AddEmpty(FilterByTypeAndHeadID(FilterElementsByGenderAndRace(wearables, disguisedGenderEnum, disguisedRaceEnum), WearableType.FaceAttachment, headSpriteId), WearableType.FaceAttachment);
if (IsValidIndex(disguisedHairIndex, disguisedHairs))
{
disguisedHairElement = disguisedHairs[disguisedHairIndex];
}
else
{
disguisedHairElement = GetRandomElement(disguisedHairs);
}
if (IsValidIndex(disguisedBeardIndex, disguisedBeards))
{
disguisedBeardElement = disguisedBeards[disguisedBeardIndex];
}
else
{
disguisedBeardElement = GetRandomElement(disguisedBeards);
}
if (IsValidIndex(disguisedMoustacheIndex, disguisedMoustaches))
{
disguisedMoustacheElement = disguisedMoustaches[disguisedMoustacheIndex];
}
else
{
disguisedMoustacheElement = GetRandomElement(disguisedMoustaches);
}
if (IsValidIndex(disguisedFaceAttachmentIndex, disguisedFaceAttachments))
{
disguisedFaceAttachmentElement = disguisedFaceAttachments[disguisedFaceAttachmentIndex];
}
else
{
disguisedFaceAttachmentElement = GetRandomElement(disguisedFaceAttachments);
}
idCard.StoredAttachments = new List<WearableSprite>();
disguisedFaceAttachmentElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.FaceAttachment)));
disguisedBeardElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Beard)));
disguisedMoustacheElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Moustache)));
disguisedHairElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.Hair)));
if (OmitJobInPortraitClothing)
{
JobPrefab.NoJobElement?.Element("PortraitClothing")?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
else
{
idCard.StoredJobPrefab?.ClothingElement?.Elements("sprite").ForEach(s => idCard.StoredAttachments.Add(new WearableSprite(s, WearableType.JobIndicator)));
}
}
idCard.StoredOwnerAppearance.ExtractAppearance(this, readTags);
}
}
if (idCard.StoredJobPrefab != null)
if (idCard.StoredOwnerAppearance.JobPrefab != null)
{
disguisedJobIcon = idCard.StoredJobPrefab.Icon;
disguisedJobColor = idCard.StoredJobPrefab.UIColor;
disguisedJobIcon = idCard.StoredOwnerAppearance.JobPrefab.Icon;
disguisedJobColor = idCard.StoredOwnerAppearance.JobPrefab.UIColor;
}
disguisedPortrait = idCard.StoredPortrait;
disguisedSheetIndex = idCard.StoredSheetIndex;
disguisedAttachmentSprites = idCard.StoredAttachments;
disguisedPortrait = idCard.StoredOwnerAppearance.Portrait;
disguisedSheetIndex = idCard.StoredOwnerAppearance.SheetIndex;
disguisedAttachmentSprites = idCard.StoredOwnerAppearance.Attachments;
disguisedHairColor = idCard.StoredOwnerAppearance.HairColor;
disguisedFacialHairColor = idCard.StoredOwnerAppearance.FacialHairColor;
disguisedSkinColor = idCard.StoredOwnerAppearance.SkinColor;
}
partial void LoadAttachmentSprites(bool omitJob)
@@ -422,43 +302,107 @@ namespace Barotrauma
public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false)
{
if (evaluateDisguise && IsDisguised) return;
if (evaluateDisguise && IsDisguised) { return; }
Vector2? sheetIndex;
Sprite portraitToDraw;
List<WearableSprite> attachmentsToDraw;
Color hairColor;
Color facialHairColor;
Color skinColor;
if (!IsDisguisedAsAnother || !evaluateDisguise)
{
sheetIndex = Head.SheetIndex;
portraitToDraw = Portrait;
attachmentsToDraw = AttachmentSprites;
hairColor = Head.HairColor;
facialHairColor = Head.FacialHairColor;
skinColor = Head.SkinColor;
}
else
{
sheetIndex = disguisedSheetIndex;
portraitToDraw = disguisedPortrait;
attachmentsToDraw = disguisedAttachmentSprites;
hairColor = disguisedHairColor;
facialHairColor = disguisedFacialHairColor;
skinColor = disguisedSkinColor;
}
if (portraitToDraw != null)
{
var currEffect = spriteBatch.GetCurrentEffect();
// Scale down the head sprite 10%
float scale = targetWidth * 0.9f / Portrait.size.X;
if (sheetIndex.HasValue)
{
SetHeadEffect(spriteBatch);
portraitToDraw.SourceRect = new Rectangle(CalculateOffset(portraitToDraw, sheetIndex.Value.ToPoint()), portraitToDraw.SourceRect.Size);
}
portraitToDraw.Draw(spriteBatch, screenPos + offset, Color.White, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
portraitToDraw.Draw(spriteBatch, screenPos + offset, skinColor, portraitToDraw.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
if (attachmentsToDraw != null)
{
float depthStep = 0.000001f;
foreach (var attachment in attachmentsToDraw)
{
DrawAttachmentSprite(spriteBatch, attachment, portraitToDraw, sheetIndex, screenPos + offset, scale, depthStep, flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
SetAttachmentEffect(spriteBatch, attachment);
DrawAttachmentSprite(spriteBatch, attachment, portraitToDraw, sheetIndex, screenPos + offset, scale, depthStep, GetAttachmentColor(attachment, hairColor, facialHairColor), flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
depthStep += depthStep;
}
}
spriteBatch.SwapEffect(currEffect);
}
}
//TODO: I hate this so much :(
private SpriteBatch.EffectWithParams headEffectParameters;
private Dictionary<WearableType, SpriteBatch.EffectWithParams> attachmentEffectParameters
= new Dictionary<WearableType, SpriteBatch.EffectWithParams>();
private void SetHeadEffect(SpriteBatch spriteBatch)
{
headEffectParameters.Effect ??= GameMain.GameScreen.ThresholdTintEffect;
headEffectParameters.Params ??= new Dictionary<string, object>();
headEffectParameters.Params["xBaseTexture"] = HeadSprite.Texture;
headEffectParameters.Params["xTintMaskTexture"] = tintMask?.Texture ?? GUI.WhiteTexture;
headEffectParameters.Params["xCutoffTexture"] = GUI.WhiteTexture;
headEffectParameters.Params["baseToCutoffSizeRatio"] = 1.0f;
headEffectParameters.Params["highlightThreshold"] = tintHighlightThreshold;
headEffectParameters.Params["highlightMultiplier"] = tintHighlightMultiplier;
spriteBatch.SwapEffect(headEffectParameters);
}
private void SetAttachmentEffect(SpriteBatch spriteBatch, WearableSprite attachment)
{
if (!attachmentEffectParameters.ContainsKey(attachment.Type))
{
attachmentEffectParameters.Add(attachment.Type, new SpriteBatch.EffectWithParams(GameMain.GameScreen.ThresholdTintEffect, new Dictionary<string, object>()));
}
var parameters = attachmentEffectParameters[attachment.Type].Params;
parameters["xBaseTexture"] = attachment.Sprite.Texture;
parameters["xTintMaskTexture"] = GUI.WhiteTexture;
parameters["xCutoffTexture"] = GUI.WhiteTexture;
parameters["baseToCutoffSizeRatio"] = 1.0f;
parameters["highlightThreshold"] = tintHighlightThreshold;
parameters["highlightMultiplier"] = tintHighlightMultiplier;
spriteBatch.SwapEffect(attachmentEffectParameters[attachment.Type]);
}
private Color GetAttachmentColor(WearableSprite attachment, Color hairColor, Color facialHairColor)
{
switch (attachment.Type)
{
case WearableType.Hair:
return hairColor;
case WearableType.Beard:
case WearableType.Moustache:
return facialHairColor;
default:
return Color.White;
}
}
@@ -467,34 +411,28 @@ namespace Barotrauma
var headSprite = HeadSprite;
if (headSprite != null)
{
var currEffect = spriteBatch.GetCurrentEffect();
float scale = Math.Min(targetAreaSize.X / headSprite.size.X, targetAreaSize.Y / headSprite.size.Y);
if (Head.SheetIndex.HasValue)
{
headSprite.SourceRect = new Rectangle(CalculateOffset(headSprite, Head.SheetIndex.Value.ToPoint()), headSprite.SourceRect.Size);
}
headSprite.Draw(spriteBatch, screenPos, scale: scale);
SetHeadEffect(spriteBatch);
headSprite.Draw(spriteBatch, screenPos, scale: scale, color: SkinColor);
if (AttachmentSprites != null)
{
float depthStep = 0.000001f;
foreach (var attachment in AttachmentSprites)
{
DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep);
SetAttachmentEffect(spriteBatch, attachment);
DrawAttachmentSprite(spriteBatch, attachment, headSprite, Head.SheetIndex, screenPos, scale, depthStep, GetAttachmentColor(attachment, HairColor, FacialHairColor));
depthStep += depthStep;
}
}
spriteBatch.SwapEffect(currEffect);
}
}
public void DrawJobIcon(SpriteBatch spriteBatch, Vector2 pos, float scale = 1.0f, bool evaluateDisguise = false)
{
if (evaluateDisguise && IsDisguised) return;
var icon = !IsDisguisedAsAnother || !evaluateDisguise ? Job?.Prefab?.Icon : disguisedJobIcon;
if (icon == null) { return; }
Color iconColor = !IsDisguisedAsAnother || !evaluateDisguise ? Job.Prefab.UIColor : disguisedJobColor;
icon.Draw(spriteBatch, pos, iconColor, scale: scale);
}
public void DrawJobIcon(SpriteBatch spriteBatch, Rectangle area, bool evaluateDisguise = false)
{
if (evaluateDisguise && IsDisguised) return;
@@ -505,7 +443,7 @@ namespace Barotrauma
icon.Draw(spriteBatch, area.Center.ToVector2(), iconColor, scale: Math.Min(area.Width / (float)icon.SourceRect.Width, area.Height / (float)icon.SourceRect.Height));
}
private void DrawAttachmentSprite(SpriteBatch spriteBatch, WearableSprite attachment, Sprite head, Vector2? sheetIndex, Vector2 drawPos, float scale, float depthStep, SpriteEffects spriteEffects = SpriteEffects.None)
private void DrawAttachmentSprite(SpriteBatch spriteBatch, WearableSprite attachment, Sprite head, Vector2? sheetIndex, Vector2 drawPos, float scale, float depthStep, Color? color = null, SpriteEffects spriteEffects = SpriteEffects.None)
{
if (attachment.InheritSourceRect)
{
@@ -522,7 +460,7 @@ namespace Barotrauma
attachment.Sprite.SourceRect = head.SourceRect;
}
}
Vector2 origin = attachment.Sprite.Origin;
Vector2 origin;
if (attachment.InheritOrigin)
{
origin = head.Origin;
@@ -537,7 +475,7 @@ namespace Barotrauma
{
depth = head.Depth - depthStep;
}
attachment.Sprite.Draw(spriteBatch, drawPos, Color.White, origin, rotate: 0, scale: scale, depth: depth, spriteEffect: spriteEffects);
attachment.Sprite.Draw(spriteBatch, drawPos, color ?? Color.White, origin, rotate: 0, scale: scale, depth: depth, spriteEffect: spriteEffects);
}
public static CharacterInfo ClientRead(string speciesName, IReadMessage inc)
@@ -552,6 +490,9 @@ namespace Barotrauma
int beardIndex = inc.ReadByte();
int moustacheIndex = inc.ReadByte();
int faceAttachmentIndex = inc.ReadByte();
Color skinColor = inc.ReadColorR8G8B8();
Color hairColor = inc.ReadColorR8G8B8();
Color facialHairColor = inc.ReadColorR8G8B8();
string ragdollFile = inc.ReadString();
string jobIdentifier = inc.ReadString();
@@ -577,6 +518,9 @@ namespace Barotrauma
ID = infoID,
};
ch.RecreateHead(headSpriteID,(Race)race, (Gender)gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
ch.SkinColor = skinColor;
ch.HairColor = hairColor;
ch.FacialHairColor = facialHairColor;
if (ch.Job != null)
{
foreach (KeyValuePair<string, float> skill in skillLevels)
@@ -591,7 +535,449 @@ namespace Barotrauma
}
ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier));
}
byte savedStatValueCount = inc.ReadByte();
for (int i = 0; i < savedStatValueCount; i++)
{
int statType = inc.ReadByte();
string statIdentifier = inc.ReadString();
float statValue = inc.ReadSingle();
bool removeOnDeath = inc.ReadBoolean();
ch.ChangeSavedStatValue((StatTypes)statType, statValue, statIdentifier, removeOnDeath);
}
ch.ExperiencePoints = inc.ReadUInt16();
ch.AdditionalTalentPoints = inc.ReadUInt16();
return ch;
}
public void CreateIcon(RectTransform rectT)
{
LoadHeadAttachments();
new GUICustomComponent(rectT,
onDraw: (sb, component) => DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()));
}
public class AppearanceCustomizationMenu : IDisposable
{
public readonly CharacterInfo CharacterInfo;
public GUIListBox HeadSelectionList = null;
public bool HasIcon = true;
public GUIScrollBar.OnMovedHandler OnSliderMoved = null;
public GUIScrollBar.OnMovedHandler OnSliderReleased = null;
public Action<AppearanceCustomizationMenu> OnHeadSwitch = null;
private readonly GUIComponent parentComponent;
private readonly List<Sprite> characterSprites = new List<Sprite>();
public GUIButton RandomizeButton;
public AppearanceCustomizationMenu(CharacterInfo info, GUIComponent parent, bool hasIcon = true)
{
CharacterInfo = info;
parentComponent = parent;
HasIcon = hasIcon;
RecreateFrameContents();
}
public void RecreateFrameContents()
{
var info = CharacterInfo;
HeadSelectionList = null;
parentComponent.ClearChildren();
ClearSprites();
float contentWidth = HasIcon ? 0.75f : 1.0f;
var listBox = new GUIListBox(
new RectTransform(new Vector2(contentWidth, 1.0f), parentComponent.RectTransform,
Anchor.CenterLeft))
{ CanBeFocused = false, CanTakeKeyBoardFocus = false };
var content = listBox.Content;
info.LoadHeadAttachments();
if (HasIcon)
{
info.CreateIcon(
new RectTransform(new Vector2(0.25f, 1.0f), parentComponent.RectTransform, Anchor.CenterRight)
{ RelativeOffset = new Vector2(-0.01f, 0.0f) });
}
RectTransform createItemRectTransform(string labelTag, float width = 0.6f)
{
var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.166f), content.RectTransform));
var label = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), layoutGroup.RectTransform),
TextManager.Get(labelTag), font: GUI.SubHeadingFont);
var bottomItem = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), layoutGroup.RectTransform),
style: null);
return new RectTransform(new Vector2(width, 1.0f), bottomItem.RectTransform, Anchor.Center);
}
RectTransform genderItemRT = createItemRectTransform("Gender", 1.0f);
GUILayoutGroup genderContainer =
new GUILayoutGroup(genderItemRT, isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
void createGenderButton(Gender gender)
{
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), genderContainer.RectTransform),
TextManager.Get(gender.ToString()), style: "ListBoxElement")
{
UserData = gender,
OnClicked = OpenHeadSelection,
Selected = info.Gender == gender
};
}
createGenderButton(Gender.Male);
createGenderButton(Gender.Female);
int countAttachmentsOfType(WearableType wearableType)
=> info.FilterByTypeAndHeadID(
info.FilterElementsByGenderAndRace(info.Wearables, info.Head.gender, info.Head.race),
wearableType, info.HeadSpriteId).Count();
List<GUIScrollBar> attachmentSliders = new List<GUIScrollBar>();
void createAttachmentSlider(int initialValue, WearableType wearableType)
{
int attachmentCount = countAttachmentsOfType(wearableType);
if (attachmentCount > 0)
{
var labelTag = wearableType == WearableType.FaceAttachment
? "FaceAttachment.Accessories"
: $"FaceAttachment.{wearableType}";
var sliderItemRT = createItemRectTransform(labelTag);
var slider =
new GUIScrollBar(sliderItemRT, style: "GUISlider")
{
Range = new Vector2(0, attachmentCount),
StepValue = 1,
OnMoved = (bar, scroll) => SwitchAttachment(bar, wearableType),
OnReleased = OnSliderReleased,
BarSize = 1.0f / (float)(attachmentCount + 1)
};
slider.BarScrollValue = initialValue;
attachmentSliders.Add(slider);
}
}
createAttachmentSlider(info.HairIndex, WearableType.Hair);
createAttachmentSlider(info.BeardIndex, WearableType.Beard);
createAttachmentSlider(info.MoustacheIndex, WearableType.Moustache);
createAttachmentSlider(info.FaceAttachmentIndex, WearableType.FaceAttachment);
void createColorSelector(string labelTag, IEnumerable<(Color Color, float Commonness)> options, Func<Color> getter,
Action<Color> setter)
{
var selectorItemRT = createItemRectTransform(labelTag, 0.4f);
var dropdown =
new GUIDropDown(selectorItemRT)
{ AllowNonText = true };
var listBoxSize = dropdown.ListBox.RectTransform.RelativeSize;
dropdown.ListBox.RectTransform.RelativeSize = new Vector2(listBoxSize.X * 1.75f, listBoxSize.Y);
var dropdownButton = dropdown.GetChild<GUIButton>();
var buttonFrame =
new GUIFrame(
new RectTransform(Vector2.One * 0.7f, dropdownButton.RectTransform, Anchor.CenterLeft)
{ RelativeOffset = new Vector2(0.05f, 0.0f) }, style: null);
Color? previewingColor = null;
dropdown.OnSelected = (component, color) =>
{
previewingColor = null;
setter((Color)color);
buttonFrame.Color = getter();
buttonFrame.HoverColor = getter();
return true;
};
buttonFrame.Color = getter();
buttonFrame.HoverColor = getter();
dropdown.ListBox.UseGridLayout = true;
foreach (var option in options)
{
var optionElement =
new GUIFrame(
new RectTransform(new Vector2(0.25f, 1.0f / 3.0f),
dropdown.ListBox.Content.RectTransform),
style: "ListBoxElement")
{
UserData = option.Color,
CanBeFocused = true
};
var colorElement =
new GUIFrame(
new RectTransform(Vector2.One * 0.75f, optionElement.RectTransform, Anchor.Center,
scaleBasis: ScaleBasis.Smallest),
style: null)
{
Color = option.Color,
HoverColor = option.Color,
OutlineColor = Color.Lerp(Color.Black, option.Color, 0.5f),
CanBeFocused = false
};
}
var childToSelect = dropdown.ListBox.Content.FindChild(c => (Color)c.UserData == getter());
dropdown.Select(dropdown.ListBox.Content.GetChildIndex(childToSelect));
//The following exists to track mouseover to preview colors before selecting them
new GUICustomComponent(new RectTransform(Vector2.One, buttonFrame.RectTransform),
onUpdate: (deltaTime, component) =>
{
if (GUI.MouseOn is GUIFrame { Parent: { } p } hoveredFrame && dropdown.ListBox.Content.IsParentOf(hoveredFrame))
{
previewingColor ??= getter();
Color color = (Color)(dropdown.ListBox.Content.FindChild(c =>
c == hoveredFrame || c.IsParentOf(hoveredFrame))?.UserData ?? dropdown.SelectedData ?? getter());
setter(color);
buttonFrame.Color = getter();
buttonFrame.HoverColor = getter();
}
else if (previewingColor.HasValue)
{
setter(previewingColor.Value);
buttonFrame.Color = getter();
buttonFrame.HoverColor = getter();
previewingColor = null;
}
}, onDraw: null)
{
CanBeFocused = false,
Visible = true
};
}
if (countAttachmentsOfType(WearableType.Hair) > 0)
{
createColorSelector($"Customization.{nameof(info.HairColor)}", info.HairColors,
() => info.HairColor, (color) => info.HairColor = color);
}
if (countAttachmentsOfType(WearableType.Moustache) > 0 ||
countAttachmentsOfType(WearableType.Beard) > 0)
{
createColorSelector($"Customization.{nameof(info.FacialHairColor)}", info.FacialHairColors,
() => info.FacialHairColor, (color) => info.FacialHairColor = color);
}
createColorSelector($"Customization.{nameof(info.SkinColor)}", info.SkinColors, () => info.SkinColor,
(color) => info.SkinColor = color);
RandomizeButton = new GUIButton(new RectTransform(Vector2.One * 0.12f,
parentComponent.RectTransform,
anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
{ RelativeOffset = new Vector2(0.01f, 0.005f) }, style: "RandomizeButton")
{
OnClicked = (button, o) =>
{
info.Head = new HeadInfo();
info.SetGenderAndRace(Rand.RandSync.Unsynced);
info.SetColors();
RecreateFrameContents();
info.RefreshHead();
OnHeadSwitch?.Invoke(this);
attachmentSliders.ForEach(s => OnSliderMoved?.Invoke(s, s.BarScroll));
return false;
}
};
//force update twice because the listbox is insanely janky
//TODO: fix all of the UI :)
listBox.ForceUpdate();
listBox.ForceUpdate();
foreach (var childLayoutGroup in listBox.Content.GetAllChildren<GUILayoutGroup>())
{
childLayoutGroup.Recalculate();
}
}
private bool OpenHeadSelection(GUIButton button, object userData)
{
Gender selectedGender = (Gender)userData;
var info = CharacterInfo;
float characterHeightWidthRatio = info.HeadSprite.size.Y / info.HeadSprite.size.X;
HeadSelectionList ??= new GUIListBox(
new RectTransform(
new Point(parentComponent.Rect.Width,
(int)(parentComponent.Rect.Width * characterHeightWidthRatio * 0.6f)), GUI.Canvas)
{
AbsoluteOffset = new Point(parentComponent.Rect.Right - parentComponent.Rect.Width,
button.Rect.Bottom)
});
HeadSelectionList.Visible = true;
HeadSelectionList.Content.ClearChildren();
ClearSprites();
parentComponent.RectTransform.SizeChanged += () =>
{
if (parentComponent == null || HeadSelectionList?.RectTransform == null || button == null)
{
return;
}
HeadSelectionList.RectTransform.Resize(new Point(parentComponent.Rect.Width,
(int)(parentComponent.Rect.Width * characterHeightWidthRatio * 0.6f)));
HeadSelectionList.RectTransform.AbsoluteOffset =
new Point(parentComponent.Rect.Right - parentComponent.Rect.Width, button.Rect.Bottom);
};
new GUIFrame(
new RectTransform(new Vector2(1.25f, 1.25f), HeadSelectionList.RectTransform, Anchor.Center),
style: "OuterGlow", color: Color.Black)
{
UserData = "outerglow",
CanBeFocused = false
};
GUILayoutGroup row = null;
int itemsInRow = 0;
XElement headElement = info.Ragdoll.MainElement.Elements().FirstOrDefault(e =>
e.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase));
XElement headSpriteElement = headElement.Element("sprite");
string spritePathWithTags = headSpriteElement.Attribute("texture").Value;
var characterConfigElement = info.CharacterConfigElement;
var heads = info.Heads;
if (heads != null)
{
row = null;
itemsInRow = 0;
foreach (var kvp in heads.Where(kv => kv.Key.Gender == selectedGender))
{
var headPreset = kvp.Key;
Race race = headPreset.Race;
int headIndex = headPreset.ID;
string spritePath = spritePathWithTags
.Replace("[GENDER]", selectedGender.ToString().ToLowerInvariant())
.Replace("[RACE]", race.ToString().ToLowerInvariant());
if (!File.Exists(spritePath))
{
continue;
}
Sprite headSprite = new Sprite(headSpriteElement, "", spritePath);
headSprite.SourceRect =
new Rectangle(CalculateOffset(headSprite, kvp.Value.ToPoint()),
headSprite.SourceRect.Size);
characterSprites.Add(headSprite);
if (itemsInRow >= 4 || row == null)
{
row = new GUILayoutGroup(
new RectTransform(new Vector2(1.0f, 0.333f), HeadSelectionList.Content.RectTransform),
true)
{
UserData = selectedGender,
Visible = true
};
itemsInRow = 0;
}
var btn = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), row.RectTransform),
style: "ListBoxElementSquare")
{
OutlineColor = Color.White * 0.5f,
PressedColor = Color.White * 0.5f,
UserData = new Tuple<Gender, Race, int>(selectedGender, race, headIndex),
OnClicked = SwitchHead,
Selected = selectedGender == info.Gender && race == info.Race && headIndex == info.HeadSpriteId,
Visible = true
};
new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), headSprite, scaleToFit: true);
itemsInRow++;
}
}
return false;
}
private bool SwitchHead(GUIButton button, object obj)
{
var info = CharacterInfo;
Gender gender = ((Tuple<Gender, Race, int>)obj).Item1;
Race race = ((Tuple<Gender, Race, int>)obj).Item2;
int id = ((Tuple<Gender, Race, int>)obj).Item3;
info.Gender = gender;
info.Race = race;
info.Head.HeadSpriteId = id;
RecreateFrameContents();
OnHeadSwitch?.Invoke(this);
return true;
}
private bool SwitchAttachment(GUIScrollBar scrollBar, WearableType type)
{
var info = CharacterInfo;
int index = (int)scrollBar.BarScrollValue;
switch (type)
{
case WearableType.Beard:
info.BeardIndex = index;
break;
case WearableType.FaceAttachment:
info.FaceAttachmentIndex = index;
break;
case WearableType.Hair:
info.HairIndex = index;
break;
case WearableType.Moustache:
info.MoustacheIndex = index;
break;
default:
DebugConsole.ThrowError($"Wearable type not implemented: {type}");
return false;
}
info.RefreshHead();
OnSliderMoved?.Invoke(scrollBar, scrollBar.BarScroll);
return true;
}
public void Update()
{
if (HeadSelectionList != null && PlayerInput.PrimaryMouseButtonDown() &&
!GUI.IsMouseOn(HeadSelectionList))
{
HeadSelectionList.Visible = false;
}
}
public void AddToGUIUpdateList()
{
HeadSelectionList?.AddToGUIUpdateList();
}
private void ClearSprites()
{
foreach (Sprite sprite in characterSprites) { sprite.Remove(); }
characterSprites.Clear();
}
public void Dispose()
{
ClearSprites();
}
~AppearanceCustomizationMenu()
{
Dispose();
}
}
}
}

View File

@@ -119,15 +119,23 @@ namespace Barotrauma
switch ((NetEntityEvent.Type)extraData[0])
{
case NetEntityEvent.Type.InventoryState:
msg.WriteRangedInteger(0, 0, 3);
msg.WriteRangedInteger(0, 0, 4);
Inventory.ClientWrite(msg, extraData);
break;
case NetEntityEvent.Type.Treatment:
msg.WriteRangedInteger(1, 0, 3);
msg.WriteRangedInteger(1, 0, 4);
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
break;
case NetEntityEvent.Type.Status:
msg.WriteRangedInteger(2, 0, 3);
msg.WriteRangedInteger(2, 0, 4);
break;
case NetEntityEvent.Type.UpdateTalents:
msg.WriteRangedInteger(3, 0, 4);
msg.Write((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.Prefab.UIntIdentifier);
}
break;
}
}
@@ -258,7 +266,7 @@ namespace Barotrauma
if (readStatus)
{
ReadStatus(msg);
(AIController as EnemyAIController)?.PetBehavior?.ClientRead(msg);
AIController?.ClientRead(msg);
}
msg.ReadPadBits();
@@ -291,7 +299,7 @@ namespace Barotrauma
break;
case ServerNetObject.ENTITY_EVENT:
int eventType = msg.ReadRangedInteger(0, 9);
int eventType = msg.ReadRangedInteger(0, 13);
switch (eventType)
{
case 0: //NetEntityEvent.Type.InventoryState
@@ -350,7 +358,7 @@ namespace Barotrauma
{
string skillIdentifier = msg.ReadString();
float skillLevel = msg.ReadSingle();
info?.SetSkillLevel(skillIdentifier, skillLevel, Position + Vector2.UnitY * 150.0f);
info?.SetSkillLevel(skillIdentifier, skillLevel);
}
break;
case 4: // NetEntityEvent.Type.SetAttackTarget
@@ -382,11 +390,12 @@ namespace Barotrauma
}
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
}
if (attackLimb?.attack != null)
if (attackLimb?.attack != null && Controlled != this)
{
if (eventType == 4)
{
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
}
else
{
@@ -450,6 +459,36 @@ namespace Barotrauma
}
}
break;
case 10: //NetEntityEvent.Type.UpdateExperience
int experienceAmount = msg.ReadInt32();
info?.SetExperience(experienceAmount);
break;
case 11: //NetEntityEvent.Type.UpdateTalents:
ushort talentCount = msg.ReadUInt16();
for (int i = 0; i < talentCount; i++)
{
bool addedThisRound = msg.ReadBoolean();
UInt32 talentIdentifier = msg.ReadUInt32();
GiveTalent(talentIdentifier, addedThisRound);
}
break;
case 12: //NetEntityEvent.Type.UpdateMoney:
int moneyAmount = msg.ReadInt32();
SetMoney(moneyAmount);
break;
case 13: //NetEntityEvent.Type.UpdatePermanentStats:
byte savedStatValueCount = msg.ReadByte();
StatTypes statType = (StatTypes)msg.ReadByte();
info?.ClearSavedStatValues(statType);
for (int i = 0; i < savedStatValueCount; i++)
{
string statIdentifier = msg.ReadString();
float statValue = msg.ReadSingle();
bool removeOnDeath = msg.ReadBoolean();
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
}
break;
}
msg.ReadPadBits();
break;

View File

@@ -1,10 +1,4 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
namespace Barotrauma
{
partial class AfflictionHusk : Affliction
{

View File

@@ -109,6 +109,7 @@ namespace Barotrauma
private float wetTimer;
private float dripParticleTimer;
private float deadTimer;
private Color? randomColor;
/// <summary>
/// Note that different limbs can share the same deformations.
@@ -120,6 +121,15 @@ namespace Barotrauma
public List<SpriteDeformation> ActiveDeformations { get; set; } = new List<SpriteDeformation>();
public Sprite Sprite { get; protected set; }
public Sprite TintMask { get; protected set; }
public Sprite HuskMask { get; protected set; }
public float TintHighlightThreshold { get; protected set; }
public float TintHighlightMultiplier { get; protected set; }
private SpriteBatch.EffectWithParams tintEffectParams;
private SpriteBatch.EffectWithParams huskSpriteParams;
protected DeformableSprite _deformSprite;
@@ -157,6 +167,12 @@ namespace Barotrauma
}
}
public Sprite GetActiveSprite(bool excludeConditionalSprites = true)
=> excludeConditionalSprites ? (_deformSprite != null ? _deformSprite.Sprite : Sprite)
: ActiveSprite;
public float DefaultSpriteDepth { get; private set; }
public WearableSprite HuskSprite { get; private set; }
public WearableSprite HerpesSprite { get; private set; }
@@ -273,6 +289,7 @@ namespace Barotrauma
DecorativeSpriteGroups[groupID].Add(decorativeSprite);
spriteAnimState.Add(decorativeSprite, new SpriteState());
}
TintMask = null;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -299,15 +316,42 @@ namespace Barotrauma
Deformations.AddRange(deformations);
NonConditionalDeformations.AddRange(deformations);
break;
case "randomcolor":
randomColor = subElement.GetAttributeColorArray("colors", null)?.GetRandom();
if (randomColor.HasValue)
{
Params.GetSprite().Color = randomColor.Value;
}
break;
case "lightsource":
LightSource = new LightSource(subElement, GetConditionalTarget())
{
ParentBody = body,
SpriteScale = Vector2.One * Scale * TextureScale
};
if (randomColor.HasValue)
{
LightSource.Color = new Color(randomColor.Value.R, randomColor.Value.G, randomColor.Value.B, LightSource.Color.A);
}
InitialLightSourceColor = LightSource.Color;
InitialLightSpriteAlpha = LightSource.OverrideLightSpriteAlpha;
break;
case "tintmask":
string tintMaskPath = subElement.GetAttributeString("texture", "");
if (!string.IsNullOrWhiteSpace(tintMaskPath))
{
TintMask = new Sprite(subElement, file: GetSpritePath(tintMaskPath));
TintHighlightThreshold = subElement.GetAttributeFloat("highlightthreshold", 0.6f);
TintHighlightMultiplier = subElement.GetAttributeFloat("highlightmultiplier", 0.8f);
}
break;
case "huskmask":
string huskMaskPath = subElement.GetAttributeString("texture", "");
if (!string.IsNullOrWhiteSpace(huskMaskPath))
{
HuskMask = new Sprite(subElement, file: GetSpritePath(huskMaskPath));
}
break;
}
ISerializableEntity GetConditionalTarget()
@@ -357,6 +401,7 @@ namespace Barotrauma
return deformations;
}
}
DefaultSpriteDepth = GetActiveSprite()?.Depth ?? 0.0f;
LightSource?.CheckConditionals();
}
@@ -449,20 +494,20 @@ namespace Barotrauma
/// <summary>
/// Get the full path of a limb sprite, taking into account tags, gender and head id
/// </summary>
private string GetSpritePath(string texturePath)
public static string GetSpritePath(string texturePath, CharacterInfo characterInfo)
{
string spritePath = texturePath;
string spritePathWithTags = spritePath;
if (character.Info != null && character.IsHumanoid)
if (characterInfo != null)
{
spritePath = spritePath.Replace("[GENDER]", (character.Info.Gender == Gender.Female) ? "female" : "male");
spritePath = spritePath.Replace("[RACE]", character.Info.Race.ToString().ToLowerInvariant());
spritePath = spritePath.Replace("[HEADID]", character.Info.HeadSpriteId.ToString());
spritePath = spritePath.Replace("[GENDER]", (characterInfo.Gender == Gender.Female) ? "female" : "male");
spritePath = spritePath.Replace("[RACE]", characterInfo.Race.ToString().ToLowerInvariant());
spritePath = spritePath.Replace("[HEADID]", characterInfo.HeadSpriteId.ToString());
if (character.Info.HeadSprite != null && character.Info.SpriteTags.Any())
if (characterInfo.HeadSprite != null && characterInfo.SpriteTags.Any())
{
string tags = "";
character.Info.SpriteTags.ForEach(tag => tags += "[" + tag + "]");
characterInfo.SpriteTags.ForEach(tag => tags += "[" + tag + "]");
spritePathWithTags = Path.Combine(
Path.GetDirectoryName(spritePath),
@@ -472,6 +517,13 @@ namespace Barotrauma
return File.Exists(spritePathWithTags) ? spritePathWithTags : spritePath;
}
private string GetSpritePath(string texturePath)
{
if (!character.IsHumanoid) { return texturePath; }
return GetSpritePath(texturePath, character?.Info);
}
partial void LoadParamsProjSpecific()
{
bool isFlipped = dir == Direction.Left;
@@ -538,8 +590,8 @@ namespace Barotrauma
{
foreach (ParticleEmitter emitter in character.DamageEmitters)
{
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
if (InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
if (!InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
ParticlePrefab overrideParticle = null;
foreach (DamageModifier damageModifier in result.AppliedDamageModifiers)
{
@@ -560,8 +612,8 @@ namespace Barotrauma
foreach (ParticleEmitter emitter in character.BloodEmitters)
{
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { 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);
}
}
@@ -585,7 +637,7 @@ namespace Barotrauma
}
}
if (inWater)
if (InWater)
{
wetTimer = 1.0f;
}
@@ -632,19 +684,38 @@ namespace Barotrauma
RefreshDeformations();
}
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null)
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null, bool disableDeformations = false)
{
float brightness = 1.0f - (burnOverLayStrength / 100.0f) * 0.5f;
var spriteParams = Params.GetSprite();
if (spriteParams == null) { return; }
Color color = new Color(spriteParams.Color.R / 255f * brightness, spriteParams.Color.G / 255f * brightness, spriteParams.Color.B / 255f * brightness, spriteParams.Color.A / 255f);
Color clr = spriteParams.Color;
if (!spriteParams.IgnoreTint)
{
clr = clr.Multiply(ragdoll.RagdollParams.Color);
if (character.Info != null)
{
clr = clr.Multiply(character.Info.SkinColor);
}
if (character.CharacterHealth.FaceTint.A > 0 && type == LimbType.Head)
{
clr = Color.Lerp(clr, character.CharacterHealth.FaceTint.Opaque(), character.CharacterHealth.FaceTint.A / 255.0f);
}
if (character.CharacterHealth.BodyTint.A > 0)
{
clr = Color.Lerp(clr, character.CharacterHealth.BodyTint.Opaque(), character.CharacterHealth.BodyTint.A / 255.0f);
}
}
Color color = new Color((byte)(clr.R * brightness), (byte)(clr.G * brightness), (byte)(clr.B * brightness), clr.A);
Color blankColor = new Color(brightness, brightness, brightness, 1);
if (deadTimer > 0)
{
color = Color.Lerp(color, spriteParams.DeadColor, MathUtils.InverseLerp(0, spriteParams.DeadColorTime, deadTimer));
}
color = overrideColor ?? color;
blankColor = overrideColor ?? blankColor;
if (isSevered)
{
@@ -667,6 +738,8 @@ namespace Barotrauma
OtherWearables.Any(w => w.HideLimb) ||
wearingItems.Any(w => w != null && w.HideLimb);
bool drawHuskSprite = HuskSprite != null && !wearableTypesToHide.Contains(WearableType.Husk);
var activeSprite = ActiveSprite;
if (type == LimbType.Head)
{
@@ -674,11 +747,12 @@ namespace Barotrauma
}
body.UpdateDrawPosition();
float depthStep = 0.000001f;
if (!hideLimb)
{
var deformSprite = DeformSprite;
if (deformSprite != null)
if (deformSprite != null && !disableDeformations)
{
if (ActiveDeformations.Any())
{
@@ -698,7 +772,33 @@ namespace Barotrauma
}
else
{
bool useTintMask = TintMask != null && spriteBatch.GetCurrentEffect() is null;
if (useTintMask)
{
tintEffectParams.Effect ??= GameMain.GameScreen.ThresholdTintEffect;
tintEffectParams.Params ??= new Dictionary<string, object>();
var parameters = tintEffectParams.Params;
parameters["xBaseTexture"] = Sprite.Texture;
parameters["xTintMaskTexture"] = TintMask.Texture;
if (drawHuskSprite && HuskMask != null)
{
parameters["xCutoffTexture"] = HuskMask.Texture;
parameters["baseToCutoffSizeRatio"] = (float)Sprite.Texture.Width / (float)HuskMask.Texture.Width;
}
else
{
parameters["xCutoffTexture"] = GUI.WhiteTexture;
parameters["baseToCutoffSizeRatio"] = 1.0f;
}
parameters["highlightThreshold"] = TintHighlightThreshold;
parameters["highlightMultiplier"] = TintHighlightMultiplier;
spriteBatch.SwapEffect(tintEffectParams);
}
body.Draw(spriteBatch, activeSprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
if (useTintMask)
{
spriteBatch.SwapEffect(null);
}
}
// Handle non-exlusive, i.e. additional conditional sprites
foreach (var conditionalSprite in ConditionalSprites)
@@ -722,7 +822,7 @@ namespace Barotrauma
}
else
{
body.Draw(spriteBatch, conditionalSprite.Sprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
body.Draw(spriteBatch, conditionalSprite.Sprite, color, depth: activeSprite.Depth - (depthStep * 50), Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
}
}
}
@@ -737,7 +837,7 @@ namespace Barotrauma
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color * Math.Min(damageOverlayStrength, 1.0f), activeSprite.Origin,
-body.DrawRotation,
Scale, spriteEffect, activeSprite.Depth - 0.0000015f);
Scale, spriteEffect, activeSprite.Depth - (depthStep * 90));
}
foreach (var decorativeSprite in DecorativeSprites)
{
@@ -755,9 +855,8 @@ namespace Barotrauma
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
depth: decorativeSprite.Sprite.Depth);
depth: activeSprite.Depth - (depthStep * 100));
}
float depthStep = 0.000001f;
float step = depthStep;
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
if (Params.MirrorHorizontally)
@@ -770,23 +869,44 @@ namespace Barotrauma
}
if (onlyDrawable == null)
{
if (HerpesSprite != null && !wearableTypesToHide.Contains(WearableType.Herpes))
if (HerpesSprite != null && !wearableTypesToHide.Contains(WearableType.Herpes) && herpesStrength > 0)
{
DrawWearable(HerpesSprite, depthStep, spriteBatch, color * Math.Min(herpesStrength / 10.0f, 1.0f), spriteEffect);
float alpha = Math.Min(herpesStrength * 2 / 100.0f, 1.0f);
DrawWearable(HerpesSprite, depthStep, spriteBatch, blankColor, alpha: alpha, spriteEffect);
depthStep += step;
}
if (drawHuskSprite)
{
bool useTintEffect = HuskMask != null && spriteBatch.GetCurrentEffect() is null;
if (useTintEffect)
{
huskSpriteParams.Effect ??= GameMain.GameScreen.ThresholdTintEffect;
huskSpriteParams.Params ??= new Dictionary<string, object>();
var parameters = huskSpriteParams.Params;
parameters["xCutoffTexture"] = GUI.WhiteTexture;
parameters["baseToCutoffSizeRatio"] = 1.0f;
spriteBatch.SwapEffect(huskSpriteParams);
}
DrawWearable(HuskSprite, depthStep, spriteBatch, color, alpha: color.A / 255f, spriteEffect);
if (useTintEffect)
{
spriteBatch.SwapEffect(null);
}
depthStep += step;
}
foreach (WearableSprite wearable in OtherWearables)
{
if (wearable.Type == WearableType.Husk) { continue; }
if (wearableTypesToHide.Contains(wearable.Type)) { continue; }
DrawWearable(wearable, depthStep, spriteBatch, color, spriteEffect);
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;
}
}
foreach (WearableSprite wearable in WearingItems)
{
if (onlyDrawable != null && onlyDrawable != wearable) continue;
DrawWearable(wearable, depthStep, spriteBatch, color, spriteEffect);
if (onlyDrawable != null && onlyDrawable != wearable && wearable.CanBeHiddenByOtherWearables) { 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;
}
@@ -936,7 +1056,7 @@ namespace Barotrauma
}
}
private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, SpriteEffects spriteEffect)
private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, float alpha, SpriteEffects spriteEffect)
{
var sprite = ActiveSprite;
if (wearable.InheritSourceRect)
@@ -955,7 +1075,7 @@ namespace Barotrauma
}
}
Vector2 origin = wearable.Sprite.Origin;
Vector2 origin;
if (wearable.InheritOrigin)
{
origin = sprite.Origin;
@@ -986,24 +1106,49 @@ namespace Barotrauma
Color wearableColor = Color.White;
if (wearableItemComponent != null)
{
// Draw outer cloths on top of inner cloths.
// Draw outer clothes on top of inner clothes.
if (wearableItemComponent.AllowedSlots.Contains(InvSlotType.OuterClothes))
{
depth -= depthStep;
}
if (wearableItemComponent.AllowedSlots.Contains(InvSlotType.Bag))
{
depth -= depthStep * 2;
depth -= depthStep * 4;
}
wearableColor = wearableItemComponent.Item.GetSpriteColor();
}
float textureScale = wearable.InheritTextureScale ? TextureScale : wearable.Scale;
wearable.Sprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
new Color((color.R * wearableColor.R) / (255.0f * 255.0f), (color.G * wearableColor.G) / (255.0f * 255.0f), (color.B * wearableColor.B) / (255.0f * 255.0f)) * ((color.A * wearableColor.A) / (255.0f * 255.0f)),
origin, -body.DrawRotation,
Scale * textureScale, spriteEffect, depth);
else if (character.Info != null)
{
if (wearable.Type == WearableType.Hair)
{
wearableColor = character.Info.HairColor;
}
else if (wearable.Type == WearableType.Beard || wearable.Type == WearableType.Moustache)
{
wearableColor = character.Info.FacialHairColor;
}
}
float scale = wearable.Scale;
if (wearable.InheritScale)
{
if (!wearable.IgnoreTextureScale)
{
scale *= TextureScale;
}
if (!wearable.IgnoreLimbScale)
{
scale *= Params.Scale;
}
if (!wearable.IgnoreRagdollScale)
{
scale *= ragdoll.RagdollParams.LimbScale;
}
}
float rotation = -body.DrawRotation - wearable.Rotation * Dir;
float finalAlpha = alpha * wearableColor.A;
Color finalColor = color.Multiply(wearableColor);
finalColor = new Color(finalColor.R, finalColor.G, finalColor.B, (byte)finalAlpha);
wearable.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), finalColor, origin, rotation, scale, spriteEffect, depth);
}
private WearableSprite GetWearableSprite(WearableType type, bool random = false)
@@ -1054,6 +1199,9 @@ namespace Barotrauma
HerpesSprite?.Sprite.Remove();
HerpesSprite = null;
TintMask?.Remove();
TintMask = null;
}
}
}

View File

@@ -418,14 +418,14 @@ namespace Barotrauma
ShowQuestionPrompt("The automatic hull generation may not work correctly if your submarine uses curved walls. Do you want to continue? Y/N",
(option2) =>
{
if (option2.ToLower() == "y") { GameMain.SubEditorScreen.AutoHull(); }
if (option2.ToLowerInvariant() == "y") { GameMain.SubEditorScreen.AutoHull(); }
});
});
}
else
{
ShowQuestionPrompt("The automatic hull generation may not work correctly if your submarine uses curved walls. Do you want to continue? Y/N",
(option) => { if (option.ToLower() == "y") GameMain.SubEditorScreen.AutoHull(); });
(option) => { if (option.ToLowerInvariant() == "y") GameMain.SubEditorScreen.AutoHull(); });
}
}));
@@ -476,6 +476,7 @@ namespace Barotrauma
if (Screen.Selected == GameMain.SubEditorScreen)
{
NewMessage("WARNING: Switching directly from the submarine editor to the game view may cause bugs and crashes. Use with caution.", Color.Orange);
Entity.Spawner ??= new EntitySpawner();
}
GameMain.GameScreen.Select();
}));
@@ -488,6 +489,8 @@ namespace Barotrauma
Submarine.MainSub = Submarine.Load(subInfo, true);
}
GameMain.SubEditorScreen.Select(enableAutoSave: Screen.Selected != GameMain.GameScreen);
Entity.Spawner?.Remove();
Entity.Spawner = null;
}, isCheat: true));
commands.Add(new Command("editparticles|particleeditor", "editparticles/particleeditor: Switch to the Particle Editor to edit particle effects.", (string[] args) =>
@@ -608,7 +611,7 @@ namespace Barotrauma
ShowQuestionPrompt($"Some keybinds may render the game unusable, are you sure you want to make these keybinds persistent? ({Keybinds.Count} keybind(s) assigned) Y/N",
(option2) =>
{
if (option2.ToLower() != "y")
if (option2.ToLowerInvariant() != "y")
{
NewMessage("Aborted.", GUI.Style.Red);
return;
@@ -689,9 +692,18 @@ namespace Barotrauma
AssignRelayToServer("setskill", true);
AssignRelayToServer("readycheck", true);
AssignRelayToServer("givetalent", true);
AssignRelayToServer("unlocktalents", true);
AssignRelayToServer("giveexperience", true);
AssignOnExecute("control", (string[] args) =>
{
if (args.Length < 1) return;
if (args.Length < 1) { return; }
if (GameMain.NetworkMember != null)
{
GameMain.Client?.SendConsoleCommand("control " + string.Join(' ', args[0]));
return;
}
var character = FindMatchingCharacter(args, true);
if (character != null)
{
@@ -1096,9 +1108,35 @@ namespace Barotrauma
commands.Add(new Command("load|loadsub", "load [submarine name]: Load a submarine.", (string[] args) =>
{
if (args.Length == 0) return;
SubmarineInfo subInfo = new SubmarineInfo(string.Join(" ", args));
if (args.Length == 0) { return; }
if (GameMain.GameSession != null)
{
ThrowError("The loadsub command cannot be used when a round is running. You should probably be using spawnsub instead.");
return;
}
string name = string.Join(" ", args);
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => name.Equals(s.Name, StringComparison.OrdinalIgnoreCase));
if (subInfo == null)
{
string path = Path.Combine(SubmarineInfo.SavePath, name);
if (!File.Exists(path))
{
ThrowError($"Could not find a submarine with the name \"{name}\" or in the path {path}.");
return;
}
subInfo = new SubmarineInfo(path);
}
Submarine.Load(subInfo, true);
},
() =>
{
return new string[][]
{
SubmarineInfo.SavedSubmarines.Select(s => s.Name).ToArray()
};
}));
commands.Add(new Command("cleansub", "", (string[] args) =>
@@ -1317,11 +1355,13 @@ namespace Barotrauma
continue;
}
float avgOutCondition = (deconstructItem.OutConditionMin + deconstructItem.OutConditionMax) / 2;
int? deconstructProductPrice = targetItem.GetMinPrice();
if (deconstructProductPrice.HasValue)
{
if (!deconstructProductCost.HasValue) { deconstructProductCost = 0; }
deconstructProductCost += (int)(deconstructProductPrice * deconstructItem.OutCondition);
deconstructProductCost += (int)(deconstructProductPrice * avgOutCondition);
}
if (fabricationRecipe != null)
@@ -1331,9 +1371,9 @@ namespace Barotrauma
{
NewMessage("Deconstructing \"" + itemPrefab.Name + "\" produces \"" + deconstructItem.ItemIdentifier + "\", which isn't required in the fabrication recipe of the item.", Color.Red);
}
else if (ingredient.UseCondition && ingredient.MinCondition < deconstructItem.OutCondition)
else if (ingredient.UseCondition && ingredient.MinCondition < avgOutCondition)
{
NewMessage($"Deconstructing \"{itemPrefab.Name}\" produces more \"{deconstructItem.ItemIdentifier}\", than what's required to fabricate the item (required: {targetItem.Name} {(int)(ingredient.MinCondition * 100)}%, output: {deconstructItem.ItemIdentifier} {(int)(deconstructItem.OutCondition * 100)}%)", Color.Red);
NewMessage($"Deconstructing \"{itemPrefab.Name}\" produces more \"{deconstructItem.ItemIdentifier}\", than what's required to fabricate the item (required: {targetItem.Name} {(int)(ingredient.MinCondition * 100)}%, output: {deconstructItem.ItemIdentifier} {(int)(avgOutCondition * 100)}%)", Color.Red);
}
}
}
@@ -1692,13 +1732,14 @@ namespace Barotrauma
//check missing mission texts
foreach (var missionPrefab in MissionPrefab.List)
{
string nameIdentifier = "missionname." + missionPrefab.Identifier;
string missionId = (missionPrefab.ConfigElement.Attribute("textidentifier") == null ? missionPrefab.Identifier : missionPrefab.ConfigElement.GetAttributeString("textidentifier", string.Empty));
string nameIdentifier = "missionname." + missionId;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
string descriptionIdentifier = "missiondescription." + missionPrefab.Identifier;
string descriptionIdentifier = "missiondescription." + missionId;
if (!tags[language].Contains(descriptionIdentifier))
{
if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet<string>(); }
@@ -1708,6 +1749,7 @@ namespace Barotrauma
foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{
if (sub.Type != SubmarineType.Player) { continue; }
string nameIdentifier = "submarine.name." + sub.Name.ToLowerInvariant();
if (!tags[language].Contains(nameIdentifier))
{
@@ -1724,14 +1766,23 @@ namespace Barotrauma
foreach (AfflictionPrefab affliction in AfflictionPrefab.List)
{
string nameIdentifier = "afflictionname." + affliction.Identifier;
if (affliction.ShowIconThreshold > affliction.MaxStrength &&
affliction.ShowIconToOthersThreshold > affliction.MaxStrength &&
affliction.ShowInHealthScannerThreshold > affliction.MaxStrength)
{
//hidden affliction, no need for localization
continue;
}
string afflictionId = affliction.TranslationOverride ?? affliction.Identifier;
string nameIdentifier = "afflictionname." + afflictionId;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
string descriptionIdentifier = "afflictiondescription." + affliction.Identifier;
string descriptionIdentifier = "afflictiondescription." + afflictionId;
if (!tags[language].Contains(descriptionIdentifier))
{
if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet<string>(); }
@@ -1739,6 +1790,29 @@ namespace Barotrauma
}
}
foreach (var talentTree in TalentTree.JobTalentTrees)
{
foreach (var talentSubTree in talentTree.Value.TalentSubTrees)
{
string nameIdentifier = "talenttree." + talentSubTree.Identifier;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
}
}
foreach (var talent in TalentPrefab.TalentPrefabs)
{
string nameIdentifier = "talentname." + talent.Identifier;
if (!tags[language].Contains(nameIdentifier))
{
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<string>(); }
missingTags[nameIdentifier].Add(language);
}
}
//check missing entity names
foreach (MapEntityPrefab me in MapEntityPrefab.List)
{

View File

@@ -22,6 +22,7 @@ namespace Barotrauma
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
ushort targetItemCount = msg.ReadUInt16();
for (int i = 0; i < targetItemCount; i++)
{

View File

@@ -0,0 +1,32 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class AlienRuinMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
existingTargets.Clear();
spawnedTargets.Clear();
allTargets.Clear();
ushort existingTargetsCount = msg.ReadUInt16();
for (int i = 0; i < existingTargetsCount; i++)
{
ushort targetId = msg.ReadUInt16();
if (targetId == Entity.NullEntityID) { continue; }
Entity target = Entity.FindEntityByID(targetId);
if (target == null) { continue; }
existingTargets.Add(target);
allTargets.Add(target);
}
ushort spawnedTargetsCount = msg.ReadUInt16();
for (int i = 0; i < spawnedTargetsCount; i++)
{
var enemy = Character.ReadSpawnData(msg);
existingTargets.Add(enemy);
allTargets.Add(enemy);
}
}
}
}

View File

@@ -1,12 +0,0 @@
using Barotrauma.Networking;
namespace Barotrauma
{
partial class BeaconMission : Mission
{
public override void ClientReadInitial(IReadMessage msg)
{
return;
}
}
}

View File

@@ -25,6 +25,7 @@ namespace Barotrauma
}
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
items.Clear();
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)

View File

@@ -20,10 +20,5 @@ namespace Barotrauma
return descriptions[GameMain.Client.Character.TeamID == CharacterTeamType.Team1 ? 1 : 2];
}
}
public override void ClientReadInitial(IReadMessage msg)
{
//do nothing
}
}
}

View File

@@ -6,6 +6,8 @@ namespace Barotrauma
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
byte characterCount = msg.ReadByte();
for (int i = 0; i < characterCount; i++)

View File

@@ -12,6 +12,7 @@ namespace Barotrauma
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
byte caveCount = msg.ReadByte();
for (int i = 0; i < caveCount; i++)
{
@@ -29,7 +30,7 @@ namespace Barotrauma
}
}
for (int i = 0; i < ResourceClusters.Count; i++)
for (int i = 0; i < resourceClusters.Count; i++)
{
var amount = msg.ReadByte();
var rotation = msg.ReadSingle();
@@ -41,20 +42,20 @@ namespace Barotrauma
h.AttachToWall();
item.Rotation = rotation;
}
if (SpawnedResources.TryGetValue(item.Prefab.Identifier, out var resources))
if (spawnedResources.TryGetValue(item.Prefab.Identifier, out var resources))
{
resources.Add(item);
}
else
{
SpawnedResources.Add(item.Prefab.Identifier, new List<Item>() { item });
spawnedResources.Add(item.Prefab.Identifier, new List<Item>() { item });
}
}
}
CalculateMissionClusterPositions();
for(int i = 0; i < ResourceClusters.Count; i++)
for(int i = 0; i < resourceClusters.Count; i++)
{
var identifier = msg.ReadString();
var count = msg.ReadByte();
@@ -66,7 +67,7 @@ namespace Barotrauma
if (!(entity is Item item)) { continue; }
resources[j] = item;
}
RelevantLevelResources.Add(identifier, resources);
relevantLevelResources.Add(identifier, resources);
}
}
}

View File

@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma
{
@@ -14,6 +15,10 @@ namespace Barotrauma
get { return shownMessages; }
}
public bool DisplayTargetHudIcons => Prefab.DisplayTargetHudIcons;
public virtual IEnumerable<Entity> HudIconTargets => Enumerable.Empty<Entity>();
public Color GetDifficultyColor()
{
int v = Difficulty ?? MissionPrefab.MinDifficulty;
@@ -92,11 +97,14 @@ namespace Barotrauma
};
}
public void ClientRead(IReadMessage msg)
public virtual void ClientRead(IReadMessage msg)
{
State = msg.ReadInt16();
}
public abstract void ClientReadInitial(IReadMessage msg);
public virtual void ClientReadInitial(IReadMessage msg)
{
state = msg.ReadInt16();
}
}
}

View File

@@ -18,13 +18,54 @@ namespace Barotrauma
private set;
}
public bool DisplayTargetHudIcons
{
get;
private set;
}
public float HudIconMaxDistance
{
get;
private set;
}
public Sprite HudIcon
{
get
{
return hudIcon ?? Icon;
}
}
public Color HudIconColor
{
get
{
return hudIconColor ?? IconColor;
}
}
private Sprite hudIcon;
private Color? hudIconColor;
partial void InitProjSpecific(XElement element)
{
DisplayTargetHudIcons = element.GetAttributeBool("displaytargethudicons", false);
HudIconMaxDistance = element.GetAttributeFloat("hudiconmaxdistance", 1000.0f);
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("icon", StringComparison.OrdinalIgnoreCase)) { continue; }
Icon = new Sprite(subElement);
IconColor = subElement.GetAttributeColor("color", Color.White);
string name = subElement.Name.ToString();
if (name.Equals("icon", StringComparison.OrdinalIgnoreCase))
{
Icon = new Sprite(subElement);
IconColor = subElement.GetAttributeColor("color", Color.White);
}
else if (name.Equals("hudicon", StringComparison.OrdinalIgnoreCase))
{
hudIcon = new Sprite(subElement);
hudIconColor = subElement.GetAttributeColor("color");
}
}
}
}

View File

@@ -6,6 +6,7 @@ namespace Barotrauma
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
byte monsterCount = msg.ReadByte();
for (int i = 0; i < monsterCount; i++)
{

View File

@@ -8,6 +8,7 @@ namespace Barotrauma
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
byte selectedCaveIndex = msg.ReadByte();
nestPosition = new Vector2(
msg.ReadSingle(),

View File

@@ -6,6 +6,7 @@ namespace Barotrauma
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
// duplicate code from escortmission, should possibly be combined, though additional loot items might be added so maybe not
byte characterCount = msg.ReadByte();

View File

@@ -7,6 +7,7 @@ namespace Barotrauma
{
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
bool usedExistingItem = msg.ReadBoolean();
if (usedExistingItem)
{

View File

@@ -0,0 +1,67 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class ScanMission : Mission
{
public override IEnumerable<Entity> HudIconTargets
{
get
{
if (State == 0)
{
return scanTargets.Where(kvp => !kvp.Value).Select(kvp => kvp.Key);
}
else
{
return Enumerable.Empty<Entity>();
}
}
}
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
startingItems.Clear();
ushort itemCount = msg.ReadUInt16();
for (int i = 0; i < itemCount; i++)
{
startingItems.Add(Item.ReadSpawnData(msg));
}
if (startingItems.Contains(null))
{
throw new Exception($"Error in ScanMission.ClientReadInitial: item list contains null (mission: {Prefab.Identifier})");
}
if (startingItems.Count != itemCount)
{
throw new Exception($"Error in ScanMission.ClientReadInitial: item count does not match the server count ({itemCount} != {startingItems.Count}, mission: {Prefab.Identifier})");
}
scanners.Clear();
GetScanners();
ClientReadScanTargetStatus(msg);
}
public override void ClientRead(IReadMessage msg)
{
base.ClientRead(msg);
ClientReadScanTargetStatus(msg);
}
private void ClientReadScanTargetStatus(IReadMessage msg)
{
scanTargets.Clear();
byte targetsToScan = msg.ReadByte();
for (int i = 0; i < targetsToScan; i++)
{
ushort id = msg.ReadUInt16();
bool scanned = msg.ReadBoolean();
Entity entity = Entity.FindEntityByID(id);
scanTargets.Add(entity as WayPoint, scanned);
}
}
}
}

View File

@@ -130,6 +130,7 @@ namespace Barotrauma
public static GUIStyle Style;
private static Texture2D t;
public static Texture2D WhiteTexture => t;
private static Sprite[] MouseCursorSprites => Style.CursorSprite;
private static bool debugDrawSounds, debugDrawEvents, debugDrawMetadata;
@@ -163,6 +164,7 @@ namespace Barotrauma
public static ScalableFont SubHeadingFont => Style?.SubHeadingFont;
public static ScalableFont DigitalFont => Style?.DigitalFont;
public static ScalableFont HotkeyFont => Style?.HotkeyFont;
public static ScalableFont MonospacedFont => Style?.MonospacedFont;
public static ScalableFont CJKFont { get; private set; }
@@ -306,7 +308,7 @@ namespace Barotrauma
});
SubmarineIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(452, 385, 182, 81), new Vector2(0.5f, 0.5f));
arrow = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(392, 393, 49, 45), new Vector2(0.5f, 0.5f));
arrow = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(393, 393, 49, 45), new Vector2(0.5f, 0.5f));
SpeechBubbleIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(385, 449, 66, 60), new Vector2(0.5f, 0.5f));
BrokenIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(898, 386, 123, 123), new Vector2(0.5f, 0.5f));
}
@@ -672,6 +674,12 @@ namespace Barotrauma
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: SamplerStateClamp, rasterizerState: GameMain.ScissorTestEnable);
if (GameMain.GameSession?.CrewManager is { DraggedOrder: { SymbolSprite: { } orderSprite, Color: var color }, DragOrder: true })
{
float spriteSize = Math.Max(orderSprite.size.X, orderSprite.size.Y);
orderSprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, color, orderSprite.size / 2f, scale: 32f / spriteSize * Scale);
}
var sprite = MouseCursorSprites[(int)MouseCursor] ?? MouseCursorSprites[(int)CursorState.Default];
sprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, Color.White, sprite.Origin, 0f, Scale / 1.5f);
@@ -855,11 +863,10 @@ namespace Barotrauma
int index = 0;
if (updateList.Count > 0)
{
index = updateList.Count - 1;
while (updateList[index].UpdateOrder > item.UpdateOrder)
index = updateList.Count;
while (index > 0 && updateList[index-1].UpdateOrder > item.UpdateOrder)
{
index--;
if (index == 0) { break; }
}
}
if (!updateListSet.Contains(item))
@@ -927,13 +934,14 @@ namespace Barotrauma
GUIComponent prevMouseOn = MouseOn;
MouseOn = null;
int inventoryIndex = -1;
if (Inventory.IsMouseOnInventory())
Inventory.RefreshMouseOnInventory();
if (Inventory.IsMouseOnInventory)
{
inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame);
}
if (!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked())
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || (prevMouseOn == null && !PlayerInput.SecondaryMouseButtonHeld()))
{
for (var i = updateList.Count - 1; i > inventoryIndex; i--)
{
@@ -941,10 +949,9 @@ namespace Barotrauma
if (!c.CanBeFocused) { continue; }
if (c.MouseRect.Contains(PlayerInput.MousePosition))
{
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn)
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn || prevMouseOn == null)
{
MouseOn = c;
var sakdjfnsjkd = c.MouseRect;
}
break;
}
@@ -958,7 +965,6 @@ namespace Barotrauma
MouseCursor = UpdateMouseCursorState(MouseOn);
return MouseOn;
}
}
private static CursorState UpdateMouseCursorState(GUIComponent c)
@@ -1050,23 +1056,26 @@ namespace Barotrauma
{
if (listBox.DraggedElement != null) { return CursorState.Dragging; }
if (listBox.CanDragElements) { return CursorState.Move; }
var hoverParent = c;
while (true)
if (listBox.HoverCursor != CursorState.Default)
{
if (hoverParent == parent || hoverParent == null) { break; }
if (hoverParent.State == GUIComponent.ComponentState.Hover) { return CursorState.Hand; }
hoverParent = hoverParent.Parent;
var hoverParent = c;
while (true)
{
if (hoverParent == parent || hoverParent == null) { break; }
if (hoverParent.State == GUIComponent.ComponentState.Hover) { return CursorState.Hand; }
hoverParent = hoverParent.Parent;
}
}
}
if (parent != null && parent.CanBeFocused)
{
if (!parent.Rect.Equals(monitorRect)) { return parent.HoverCursor; }
}
}
if (Inventory.IsMouseOnInventory()) { return Inventory.GetInventoryMouseCursor(); }
if (Inventory.IsMouseOnInventory) { return Inventory.GetInventoryMouseCursor(); }
var character = Character.Controlled;
// ReSharper disable once InvertIf
@@ -1343,7 +1352,7 @@ namespace Barotrauma
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
/// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param>
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Vector2 visibleRange, Sprite sprite, in Color color,
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Range<float> visibleRange, Sprite sprite, in Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
{
Vector2 diff = worldPosition - cam.WorldViewCenter;
@@ -1351,9 +1360,9 @@ namespace Barotrauma
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale;
if (overrideAlpha.HasValue || (dist > visibleRange.X && dist < visibleRange.Y))
if (overrideAlpha.HasValue || (dist > visibleRange.Start && dist < visibleRange.End))
{
float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.X) / 100.0f, 1.0f - ((dist - visibleRange.Y + 100f) / 100.0f), 1.0f);
float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.Start) / 100.0f, 1.0f - ((dist - visibleRange.End + 100f) / 100.0f), 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
if (!createOffset)
@@ -1417,7 +1426,7 @@ namespace Barotrauma
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
{
DrawIndicator(spriteBatch, worldPosition, cam, new Vector2(hideDist, float.PositiveInfinity), sprite, color, createOffset, scaleMultiplier, overrideAlpha);
DrawIndicator(spriteBatch, worldPosition, cam, new Range<float>(hideDist, float.PositiveInfinity), sprite, color, createOffset, scaleMultiplier, overrideAlpha);
}
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1)
@@ -1520,6 +1529,11 @@ namespace Barotrauma
}
}
public static void DrawFilledRectangle(SpriteBatch sb, RectangleF rect, Color clr, float depth = 0.0f)
{
DrawFilledRectangle(sb, rect.Location, rect.Size, clr, depth);
}
public static void DrawFilledRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, float depth = 0.0f)
{
if (size.X < 0)
@@ -2433,14 +2447,43 @@ namespace Barotrauma
{
if (messages.Any(msg => msg.Text == message)) { return; }
messages.Add(new GUIMessage(message, color, lifeTime ?? MathHelper.Clamp(message.Length / 5.0f, 3.0f, 10.0f), font ?? LargeFont));
if (playSound) SoundPlayer.PlayUISound(GUISoundType.UIMessage);
if (playSound) { SoundPlayer.PlayUISound(GUISoundType.UIMessage); }
}
public static void AddMessage(string message, Color color, Vector2 pos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true, GUISoundType soundType = GUISoundType.UIMessage, int subId = -1)
{
Submarine sub = Submarine.Loaded.FirstOrDefault(s => s.ID == subId);
messages.Add(new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, LargeFont, sub: sub));
if (playSound) SoundPlayer.PlayUISound(soundType);
var newMessage = new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, Font, sub: sub);
if (playSound) { SoundPlayer.PlayUISound(soundType); }
bool overlapFound = true;
int tries = 0;
while (overlapFound)
{
overlapFound = false;
foreach (var otherMessage in messages)
{
float xDiff = otherMessage.Pos.X - newMessage.Pos.X;
if (Math.Abs(xDiff) > (newMessage.Size.X + otherMessage.Size.X) / 2) { continue; }
float yDiff = otherMessage.Pos.Y - newMessage.Pos.Y;
if (Math.Abs(yDiff) > (newMessage.Size.Y + otherMessage.Size.Y) / 2) { continue; }
Vector2 moveDir = -(new Vector2(xDiff, yDiff) + Rand.Vector(1.0f));
if (moveDir.LengthSquared() > 0.0001f)
{
moveDir = Vector2.Normalize(moveDir);
}
else
{
moveDir = Rand.Vector(1.0f);
}
moveDir.Y = -Math.Abs(moveDir.Y);
newMessage.Pos -= Vector2.UnitY * 10;
}
tries++;
if (tries > 20) { break; }
}
messages.Add(newMessage);
}
public static void ClearMessages()

View File

@@ -52,13 +52,13 @@ namespace Barotrauma
public GUIComponent GetChild(int index)
{
if (index < 0 || index >= CountChildren) return null;
if (index < 0 || index >= CountChildren) { return null; }
return RectTransform.GetChild(index).GUIComponent;
}
public int GetChildIndex(GUIComponent child)
{
if (child == null) return -1;
if (child == null) { return -1; }
return RectTransform.GetChildIndex(child.RectTransform);
}
@@ -66,7 +66,7 @@ namespace Barotrauma
{
foreach (GUIComponent child in Children)
{
if (child.UserData == obj || (child.userData != null && child.userData.Equals(obj))) return child;
if (child.UserData == obj || (child.userData != null && child.userData.Equals(obj))) { return child; }
}
return null;
}
@@ -175,6 +175,8 @@ namespace Barotrauma
public bool GlowOnSelect { get; set; }
public Vector2 UVOffset { get; set; }
private CoroutineHandle pulsateCoroutine;
protected Color flashColor;
@@ -256,9 +258,9 @@ namespace Barotrauma
protected Rectangle ClampRect(Rectangle r)
{
if (Parent == null || !ClampMouseRectToParent) return r;
if (Parent == null || !ClampMouseRectToParent) { return r; }
Rectangle parentRect = Parent.ClampRect(Parent.Rect);
if (parentRect.Width <= 0 || parentRect.Height <= 0) return Rectangle.Empty;
if (parentRect.Width <= 0 || parentRect.Height <= 0) { return Rectangle.Empty; }
if (parentRect.X > r.X)
{
int diff = parentRect.X - r.X;
@@ -281,7 +283,7 @@ namespace Barotrauma
int diff = (r.Y + r.Height) - (parentRect.Y + parentRect.Height);
r.Height -= diff;
}
if (r.Width <= 0 || r.Height <= 0) return Rectangle.Empty;
if (r.Width <= 0 || r.Height <= 0) { return Rectangle.Empty; }
return r;
}
@@ -295,7 +297,7 @@ namespace Barotrauma
{
get
{
if (!CanBeFocused) return Rectangle.Empty;
if (!CanBeFocused) { return Rectangle.Empty; }
return ClampMouseRectToParent ? ClampRect(Rect) : Rect;
}
}
@@ -431,7 +433,7 @@ namespace Barotrauma
#region Updating
public virtual void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
{
if (!Visible) return;
if (!Visible) { return; }
UpdateOrder = order;
GUI.AddToUpdateList(this);
@@ -463,7 +465,7 @@ namespace Barotrauma
/// </summary>
public void UpdateManually(float deltaTime, bool alsoChildren = false, bool recursive = true)
{
if (!Visible) return;
if (!Visible) { return; }
AutoUpdate = false;
Update(deltaTime);
@@ -475,7 +477,7 @@ namespace Barotrauma
protected virtual void Update(float deltaTime)
{
if (!Visible) return;
if (!Visible) { return; }
if (CanBeFocused && OnSecondaryClicked != null)
{
@@ -555,7 +557,7 @@ namespace Barotrauma
/// </summary>
public virtual void DrawManually(SpriteBatch spriteBatch, bool alsoChildren = false, bool recursive = true)
{
if (!Visible) return;
if (!Visible) { return; }
AutoDraw = false;
Draw(spriteBatch);
@@ -598,7 +600,7 @@ namespace Barotrauma
protected virtual void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
if (!Visible) { return; }
var rect = Rect;
GetBlendedColor(GetColor(State), ref _currentColor);
@@ -653,7 +655,7 @@ namespace Barotrauma
? MathUtils.InverseLerp(0, SpriteCrossFadeTime, ToolBox.GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : 0;
if (alphaMultiplier > 0)
{
uiSprite.Draw(spriteBatch, rect, previousColor * alphaMultiplier, SpriteEffects);
uiSprite.Draw(spriteBatch, rect, previousColor * alphaMultiplier, SpriteEffects, uvOffset: UVOffset);
}
}
}
@@ -667,7 +669,11 @@ namespace Barotrauma
? MathUtils.InverseLerp(SpriteCrossFadeTime, 0, ToolBox.GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : (_currentColor.A / 255.0f);
if (alphaMultiplier > 0)
{
uiSprite.Draw(spriteBatch, rect, _currentColor * alphaMultiplier, SpriteEffects);
// * (rect.Location.Y - PlayerInput.MousePosition.Y) / rect.Height
Vector2 offset = new Vector2(
MathUtils.PositiveModulo((int)-UVOffset.X, uiSprite.Sprite.SourceRect.Width),
MathUtils.PositiveModulo((int)-UVOffset.Y, uiSprite.Sprite.SourceRect.Height));
uiSprite.Draw(spriteBatch, rect, _currentColor * alphaMultiplier, SpriteEffects, uvOffset: offset);
}
}
}
@@ -708,7 +714,7 @@ namespace Barotrauma
/// </summary>
public void DrawToolTip(SpriteBatch spriteBatch)
{
if (!Visible) return;
if (!Visible) { return; }
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData);
}
@@ -1048,7 +1054,7 @@ namespace Barotrauma
{
case "language":
string[] languages = element.GetAttributeStringArray(attribute.Name.ToString(), new string[0]);
if (!languages.Any(l => GameMain.Config.Language.ToLower() == l.ToLower())) { return false; }
if (!languages.Any(l => GameMain.Config.Language.Equals(l, StringComparison.OrdinalIgnoreCase))) { return false; }
break;
case "gameversion":
var version = new Version(attribute.Value);
@@ -1213,8 +1219,7 @@ namespace Barotrauma
private static GUIImage LoadGUIImage(XElement element, RectTransform parent)
{
Sprite sprite = null;
Sprite sprite;
string url = element.GetAttributeString("url", "");
if (!string.IsNullOrEmpty(url))
{

View File

@@ -23,6 +23,8 @@ namespace Barotrauma
private bool selectMultiple;
public bool Dropped { get; set; }
public bool AllowNonText { get; set; }
public object SelectedItemData
{
@@ -318,9 +320,9 @@ namespace Barotrauma
if (textBlock == null)
{
textBlock = component.GetChild<GUITextBlock>();
if (textBlock == null) return false;
if (textBlock is null && !AllowNonText) { return false; }
}
button.Text = textBlock.Text;
button.Text = textBlock?.Text ?? "";
}
Dropped = false;
// TODO: OnSelected can be called multiple times and when it shouldn't be called -> turn into an event so that nobody else can call it.

View File

@@ -5,8 +5,8 @@ using System.Linq;
namespace Barotrauma
{
public class GUIFrame : GUIComponent
{
public int OutlineThickness { get; set; }
{
public float OutlineThickness { get; set; }
public GUIFrame(RectTransform rectT, string style = "", Color? color = null) : base(style, rectT)
{
@@ -28,7 +28,7 @@ namespace Barotrauma
if (OutlineColor != Color.Transparent)
{
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor * (OutlineColor.A/255.0f), false, thickness: OutlineThickness);
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor, false, thickness: OutlineThickness);
}
}
}

View File

@@ -174,7 +174,7 @@ namespace Barotrauma
if (BlendState != null)
{
spriteBatch.End();
spriteBatch.Begin(blendState: BlendState, samplerState: GUI.SamplerState);
spriteBatch.Begin(blendState: BlendState, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
}
if (style != null)

View File

@@ -33,7 +33,7 @@ namespace Barotrauma
public GUIFrame Content { get; private set; }
public GUIScrollBar ScrollBar { get; private set; }
private Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
private readonly Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
private int totalSize;
private bool childrenNeedsRecalculation;
@@ -224,7 +224,7 @@ namespace Barotrauma
{
if (value == false && canDragElements && draggedElement != null)
{
draggedElement = null;
DraggedElement = null;
}
canDragElements = value;
}
@@ -233,11 +233,29 @@ namespace Barotrauma
private GUIComponent draggedElement;
private Rectangle draggedReferenceRectangle;
private Point draggedReferenceOffset;
public bool HasDraggedElementIndexChanged { get; private set; }
public GUIComponent DraggedElement => draggedElement;
public GUIComponent DraggedElement
{
get
{
return draggedElement;
}
set
{
if (value == draggedElement) { return; }
draggedElement = value;
HasDraggedElementIndexChanged = false;
}
}
private readonly bool isHorizontal;
/// <summary>
/// Setting this to true and CanBeFocused to false allows the list background to be unfocusable while the elements can still be interacted with.
/// </summary>
public bool CanInteractWhenUnfocusable { get; set; } = false;
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
{
@@ -472,7 +490,7 @@ namespace Barotrauma
if (!PlayerInput.PrimaryMouseButtonHeld())
{
OnRearranged?.Invoke(this, draggedElement.UserData);
draggedElement = null;
DraggedElement = null;
RepositionChildren();
}
else
@@ -518,6 +536,7 @@ namespace Barotrauma
if (currIndex != index)
{
draggedElement.RectTransform.RepositionChildInHierarchy(currIndex);
HasDraggedElementIndexChanged = true;
}
return;
@@ -556,7 +575,7 @@ namespace Barotrauma
if (child == null || !child.Visible) { continue; }
// selecting
if (Enabled && CanBeFocused && child.CanBeFocused && child.Rect.Contains(PlayerInput.MousePosition) && GUI.IsMouseOn(child))
if (Enabled && (CanBeFocused || CanInteractWhenUnfocusable) && child.CanBeFocused && child.Rect.Contains(PlayerInput.MousePosition) && GUI.IsMouseOn(child))
{
child.State = ComponentState.Hover;
@@ -577,7 +596,7 @@ namespace Barotrauma
if (CanDragElements && PlayerInput.PrimaryMouseButtonDown() && GUI.MouseOn == child)
{
draggedElement = child;
DraggedElement = child;
draggedReferenceRectangle = child.Rect;
draggedReferenceOffset = child.RectTransform.AbsoluteOffset;
}
@@ -608,7 +627,7 @@ namespace Barotrauma
{
if (child == Content || child == ScrollBar || child == ContentBackground) { continue; }
child.AddToGUIUpdateList(ignoreChildren, order);
}
}
}
foreach (GUIComponent child in Content.Children)
@@ -637,7 +656,7 @@ namespace Barotrauma
OnAddedToGUIUpdateList?.Invoke(this);
return;
}
int lastVisible = 0;
for (int i = 0; i < Content.CountChildren; i++)
{
@@ -681,6 +700,8 @@ namespace Barotrauma
}
}
public void ForceUpdate() => Update((float)Timing.Step);
protected override void Update(float deltaTime)
{
if (!Visible) { return; }
@@ -750,7 +771,7 @@ namespace Barotrauma
}
}
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && AllowMouseWheelScroll && PlayerInput.ScrollWheelSpeed != 0)
if (PlayerInput.ScrollWheelSpeed != 0 && AllowMouseWheelScroll && (FindScrollableParentListBox(GUI.MouseOn) == this || GUI.IsMouseOn(ScrollBar)))
{
if (SmoothScroll)
{
@@ -773,7 +794,6 @@ namespace Barotrauma
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
}
}
ScrollBar.Enabled = ScrollBarEnabled && BarSize < 1.0f;
if (AutoHideScrollBar)
@@ -785,6 +805,13 @@ namespace Barotrauma
UpdateDimensions();
}
}
private static GUIListBox FindScrollableParentListBox(GUIComponent target)
{
if (target is GUIListBox listBox && listBox.ScrollBarEnabled && listBox.BarSize < 1.0f) { return listBox; }
if (target?.Parent == null) { return null; }
return FindScrollableParentListBox(target.Parent);
}
public void SelectNext(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
@@ -982,7 +1009,7 @@ namespace Barotrauma
if (child == null) { return; }
child.RectTransform.Parent = null;
if (selected.Contains(child)) { selected.Remove(child); }
if (draggedElement == child) { draggedElement = null; }
if (draggedElement == child) { DraggedElement = null; }
UpdateScrollBarSize();
}
@@ -999,7 +1026,6 @@ namespace Barotrauma
ContentBackground.DrawManually(spriteBatch, alsoChildren: false);
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
RasterizerState prevRasterizerState = spriteBatch.GraphicsDevice.RasterizerState;
if (HideChildrenOutsideFrame)
{
spriteBatch.End();
@@ -1027,7 +1053,7 @@ namespace Barotrauma
{
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: prevRasterizerState);
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
}
if (ScrollBarVisible)

View File

@@ -79,14 +79,14 @@ namespace Barotrauma
new Rectangle(
sliderArea.X + (int)sliceBorderSizes.X,
sliderArea.Y,
(int)((sliderArea.Width - sliceBorderSizes.X - sliceBorderSizes.Z) * fillAmount),
(int)Math.Round((sliderArea.Width - sliceBorderSizes.X - sliceBorderSizes.Z) * fillAmount),
sliderArea.Height)
:
new Rectangle(
sliderArea.X,
(int)(sliderArea.Bottom - (sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount - sliceBorderSizes.W),
(int)Math.Round(sliderArea.Bottom - (sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount - sliceBorderSizes.W),
sliderArea.Width,
(int)((sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount));
(int)Math.Round((sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount));
sliderRect.Width = Math.Max(sliderRect.Width, 1);
sliderRect.Height = Math.Max(sliderRect.Height, 1);

View File

@@ -0,0 +1,87 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public class GUIScissorComponent: GUIComponent
{
public GUIComponent Content;
public GUIScissorComponent(RectTransform rectT) : base(null, rectT)
{
Content = new GUIFrame(new RectTransform(Vector2.One, rectT), style: null)
{
CanBeFocused = false
};
}
protected override void Update(float deltaTime)
{
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);
}
protected override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) { return; }
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
RasterizerState prevRasterizerState = spriteBatch.GraphicsDevice.RasterizerState;
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, Rect);
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
foreach (GUIComponent child in Content.Children)
{
if (!child.Visible) { continue; }
child.DrawManually(spriteBatch, alsoChildren: true, recursive: true);
}
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: prevRasterizerState);
}
private void ClampChildMouseRects(GUIComponent child)
{
child.ClampMouseRectToParent = true;
if (child is GUIListBox) { return; }
foreach (GUIComponent grandChild in child.Children)
{
ClampChildMouseRects(grandChild);
}
}
public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
{
if (!Visible) { return; }
UpdateOrder = order;
GUI.AddToUpdateList(this);
if (ignoreChildren)
{
OnAddedToGUIUpdateList?.Invoke(this);
return;
}
foreach (GUIComponent child in Content.Children)
{
if (!child.Visible) { continue; }
child.AddToGUIUpdateList(false, order);
}
OnAddedToGUIUpdateList?.Invoke(this);
}
}
}

View File

@@ -11,7 +11,7 @@ namespace Barotrauma
{
private Dictionary<string, GUIComponentStyle> componentStyles;
private XElement configElement;
private readonly XElement configElement;
private GraphicsDevice graphicsDevice;
@@ -25,6 +25,7 @@ namespace Barotrauma
public ScalableFont SubHeadingFont { get; private set; }
public ScalableFont DigitalFont { get; private set; }
public ScalableFont HotkeyFont { get; private set; }
public ScalableFont MonospacedFont { get; private set; }
public Dictionary<ScalableFont, bool> ForceFontUpperCase
{
@@ -40,12 +41,20 @@ namespace Barotrauma
public SpriteSheet SavingIndicator { get; private set; }
public UISprite UIGlow { get; private set; }
public UISprite PingCircle { get; private set; }
public UISprite UIGlowCircular { get; private set; }
public UISprite UIGlowSolidCircular { get; private set; }
public UISprite UIThermalGlow { get; private set; }
public UISprite ButtonPulse { get; private set; }
public SpriteSheet FocusIndicator { get; private set; }
public UISprite IconOverflowIndicator { get; private set; }
/// <summary>
/// General green color used for elements whose colors are set from code
/// </summary>
@@ -82,6 +91,12 @@ namespace Barotrauma
public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
public Color TextColorDim { get; private set; } = Color.White * 0.6f;
public Color ItemQualityColorPoor { get; private set; } = Color.DarkRed;
public Color ItemQualityColorNormal { get; private set; } = Color.Gray;
public Color ItemQualityColorGood { get; private set; } = Color.LightGreen;
public Color ItemQualityColorExcellent { get; private set; } = Color.LightBlue;
public Color ItemQualityColorMasterwork { get; private set; } = Color.MediumPurple;
public Color ColorReputationVeryLow { get; private set; } = Color.Red;
public Color ColorReputationLow { get; private set; } = Color.Orange;
public Color ColorReputationNeutral { get; private set; } = Color.White * 0.8f;
@@ -235,6 +250,9 @@ namespace Barotrauma
case "uiglow":
UIGlow = new UISprite(subElement);
break;
case "pingcircle":
PingCircle = new UISprite(subElement);
break;
case "radiation":
RadiationSprite = new UISprite(subElement);
break;
@@ -244,9 +262,18 @@ namespace Barotrauma
case "uiglowcircular":
UIGlowCircular = new UISprite(subElement);
break;
case "uiglowsolidcircular":
UIGlowSolidCircular = new UISprite(subElement);
break;
case "uithermalglow":
UIThermalGlow = new UISprite(subElement);
break;
case "endroundbuttonpulse":
ButtonPulse = new UISprite(subElement);
break;
case "iconoverflowindicator":
IconOverflowIndicator = new UISprite(subElement);
break;
case "focusindicator":
FocusIndicator = new SpriteSheet(subElement);
break;
@@ -277,6 +304,10 @@ namespace Barotrauma
DigitalFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[DigitalFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "monospacedfont":
MonospacedFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[MonospacedFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "hotkeyfont":
HotkeyFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[HotkeyFont] = subElement.GetAttributeBool("forceuppercase", false);
@@ -446,7 +477,7 @@ namespace Barotrauma
public void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null)
{
GUIComponentStyle componentStyle = null;
GUIComponentStyle componentStyle = null;
if (parent != null)
{
GUIComponentStyle parentStyle = parent.Style;
@@ -461,7 +492,7 @@ namespace Barotrauma
return;
}
}
string childStyleName = string.IsNullOrEmpty(styleName) ? targetComponent.GetType().Name : styleName;
parentStyle.ChildStyles.TryGetValue(childStyleName.ToLowerInvariant(), out componentStyle);
}
@@ -477,8 +508,25 @@ namespace Barotrauma
return;
}
}
targetComponent.ApplyStyle(componentStyle);
targetComponent.ApplyStyle(componentStyle);
}
public Color GetQualityColor(int quality)
{
switch (quality)
{
case 1:
return ItemQualityColorGood;
case 2:
return ItemQualityColorExcellent;
case 3:
return ItemQualityColorMasterwork;
case -1:
return ItemQualityColorPoor;
default:
return ItemQualityColorNormal;
}
}
}
}

View File

@@ -185,7 +185,7 @@ namespace Barotrauma
if (GUI.MouseOn != null) { return false; }
//don't close when hovering over an inventory element
if (Inventory.IsMouseOnInventory()) { return false; }
if (Inventory.IsMouseOnInventory) { return false; }
bool input = PlayerInput.PrimaryMouseButtonDown() || PlayerInput.SecondaryMouseButtonClicked();
return input && !rect.Contains(PlayerInput.MousePosition);

View File

@@ -81,7 +81,7 @@ namespace Barotrauma
private readonly object loadMutex = new object();
private float? loadState;
public float? LoadState
{
get
@@ -90,8 +90,8 @@ namespace Barotrauma
{
return loadState;
}
}
set
}
set
{
lock (loadMutex)
{
@@ -141,7 +141,7 @@ namespace Barotrauma
GameMain.Config.EnableSplashScreen = false;
}
}
var titleStyle = GUI.Style?.GetComponentStyle("TitleText");
Sprite titleSprite = null;
if (!WaitForLanguageSelection && titleStyle != null && titleStyle.Sprites.ContainsKey(GUIComponent.ComponentState.None))
@@ -177,8 +177,8 @@ namespace Barotrauma
color: Color.White * noiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f),
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f),
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
scale: GameMain.GraphicsHeight / 2000.0f);
if (WaitForLanguageSelection)
@@ -193,7 +193,7 @@ namespace Barotrauma
if (LoadState == 100.0f)
{
#if DEBUG
if (GameMain.Config.AutomaticQuickStartEnabled || GameMain.Config.AutomaticCampaignLoadEnabled && GameMain.FirstLoad)
if (GameMain.Config.AutomaticQuickStartEnabled || GameMain.Config.AutomaticCampaignLoadEnabled || GameMain.Config.TestScreenEnabled && GameMain.FirstLoad)
{
loadText = "QUICKSTARTING ...";
}
@@ -211,6 +211,13 @@ namespace Barotrauma
if (LoadState != null)
{
loadText += " " + (int)LoadState + " %";
#if DEBUG
if (GameMain.FirstLoad && GameMain.CancelQuickStart)
{
loadText += " (Quickstart aborted)";
}
#endif
}
}
if (GUI.LargeFont != null)
@@ -265,7 +272,7 @@ namespace Barotrauma
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.001f, GameMain.GraphicsHeight * 0.24f),
Color.White, Vector2.Zero, 0.0f, decorativeScale, SpriteEffects.FlipVertically);
decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.66f),
Color.White, decorativeMap.FrameSize.ToVector2(), 0.0f, decorativeScale);
@@ -281,9 +288,9 @@ namespace Barotrauma
}
else if (noiseVal < 0.5f)
{
randText =
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
randText =
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
Rand.Int(100).ToString().PadLeft(2, '0');
}
@@ -299,12 +306,12 @@ namespace Barotrauma
{
if (languageSelectionFont == null)
{
languageSelectionFont = new ScalableFont("Content/Fonts/NotoSans/NotoSans-Bold.ttf",
languageSelectionFont = new ScalableFont("Content/Fonts/NotoSans/NotoSans-Bold.ttf",
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice);
}
if (languageSelectionFontCJK == null)
{
languageSelectionFontCJK = new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
languageSelectionFontCJK = new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice, dynamicLoading: true);
}
if (languageSelectionCursor == null)
@@ -320,11 +327,11 @@ namespace Barotrauma
var font = TextManager.IsCJK(localizedLanguageName) ? languageSelectionFontCJK : languageSelectionFont;
Vector2 textSize = font.MeasureString(localizedLanguageName);
bool hover =
Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 &&
bool hover =
Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 &&
Math.Abs(PlayerInput.MousePosition.Y - textPos.Y) < textSpacing.Y / 2;
font.DrawString(spriteBatch, localizedLanguageName, textPos - textSize / 2,
font.DrawString(spriteBatch, localizedLanguageName, textPos - textSize / 2,
hover ? Color.White : Color.White * 0.6f);
if (hover && PlayerInput.PrimaryMouseButtonClicked())
{
@@ -394,14 +401,14 @@ namespace Barotrauma
LoadState = null;
SetSelectedTip(TextManager.Get("LoadingScreenTip", true));
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
while (!drawn)
{
yield return CoroutineStatus.Running;
}
CoroutineManager.StartCoroutine(loader);
yield return CoroutineStatus.Running;
while (CoroutineManager.IsCoroutineRunning(loader.ToString()))

View File

@@ -55,14 +55,47 @@ namespace Barotrauma
var texture = GetTexture(spriteBatch);
for (var i = 0; i < points.Count - 1; i++)
DrawPolygonEdge(spriteBatch, texture, points[i] + offset, points[i + 1] + offset, color, thickness);
DrawPolygonEdge(spriteBatch, points[i] + offset, points[i + 1] + offset, color, thickness);
DrawPolygonEdge(spriteBatch, texture, points[points.Count - 1] + offset, points[0] + offset, color,
DrawPolygonEdge(spriteBatch, points[points.Count - 1] + offset, points[0] + offset, color,
thickness);
}
/// <summary>
/// Draws a closed polygon from an array of points
/// </summary>
public static void DrawPolygonInner(this SpriteBatch spriteBatch, Vector2 offset, IReadOnlyList<Vector2> points, Color color, float thickness = 1f)
{
if (points.Count == 0) { return; }
private static void DrawPolygonEdge(SpriteBatch spriteBatch, Texture2D texture, Vector2 point1, Vector2 point2,
Color color, float thickness)
if (points.Count == 1)
{
DrawPoint(spriteBatch, points[0], color, (int)thickness);
return;
}
for (var i = 0; i < points.Count - 1; i++)
{
Vector2 point1 = points[i] + offset,
point2 = points[i + 1] + offset;
DrawPolygonEdgeInner(spriteBatch, point1, point2, color, thickness);
}
DrawPolygonEdgeInner(spriteBatch, points[^1] + offset, points[0] + offset, color, thickness);
}
private static void DrawPolygonEdgeInner(SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness)
{
var length = Vector2.Distance(point1, point2) + thickness;
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);
}
private static void DrawPolygonEdge(SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness)
{
var length = Vector2.Distance(point1, point2);
var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);

View File

@@ -37,7 +37,7 @@ namespace Barotrauma
private Color storeSpecialColor;
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList, shoppingCrateSellFromSubList;
private GUITextBlock shoppingCrateTotal;
private GUITextBlock relevantBalanceName, shoppingCrateTotal;
private GUIButton clearAllButton, confirmButton;
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
@@ -58,7 +58,6 @@ namespace Barotrauma
StoreTab.SellFromSub => false,
_ => throw new NotImplementedException()
};
private bool IsSelling => !IsBuying;
private GUIListBox ActiveShoppingCrateList => activeTab switch
{
StoreTab.Buy => shoppingCrateBuyList,
@@ -222,16 +221,8 @@ namespace Barotrauma
TextScale = 1.1f,
TextGetter = () =>
{
if (CurrentLocation != null)
{
merchantBalanceBlock.TextColor = CurrentLocation.BalanceColor;
return GetCurrencyFormatted(CurrentLocation.StoreCurrentBalance);
}
else
{
merchantBalanceBlock.TextColor = Color.Red;
return GetCurrencyFormatted(0);
}
merchantBalanceBlock.TextColor = CurrentLocation?.BalanceColor ?? Color.Red;
return GetMerchantBalanceText();
}
};
@@ -375,28 +366,37 @@ namespace Barotrauma
//don't show categories with no buyable items
itemCategories.RemoveAll(c => !ItemPrefab.Prefabs.Any(ep => ep.Category.HasFlag(c) && ep.CanBeBought));
itemCategoryButtons.Clear();
var categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform), style: "CategoryButton.All")
{
ToolTip = TextManager.Get("MapEntityCategory.All"),
OnClicked = OnClickedCategoryButton
};
itemCategoryButtons.Add(categoryButton);
foreach (MapEntityCategory category in itemCategories)
{
var categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform),
categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform),
style: "CategoryButton." + category)
{
ToolTip = TextManager.Get("MapEntityCategory." + category),
UserData = category,
OnClicked = (btn, userdata) =>
{
MapEntityCategory? newCategory = !btn.Selected ? (MapEntityCategory?)userdata : null;
if (newCategory.HasValue) { searchBox.Text = ""; }
if (newCategory != selectedItemCategory) { tabLists[activeTab].ScrollBar.BarScroll = 0f; }
FilterStoreItems(newCategory, searchBox.Text);
return true;
}
OnClicked = OnClickedCategoryButton
};
itemCategoryButtons.Add(categoryButton);
categoryButton.RectTransform.SizeChanged += () =>
}
bool OnClickedCategoryButton(GUIButton button, object userData)
{
MapEntityCategory? newCategory = !button.Selected ? (MapEntityCategory?)userData : null;
if (newCategory.HasValue) { searchBox.Text = ""; }
if (newCategory != selectedItemCategory) { tabLists[activeTab].ScrollBar.BarScroll = 0f; }
FilterStoreItems(newCategory, searchBox.Text);
return true;
}
foreach (var btn in itemCategoryButtons)
{
btn.RectTransform.SizeChanged += () =>
{
var sprite = categoryButton.Frame.sprites[GUIComponent.ComponentState.None].First();
categoryButton.RectTransform.NonScaledSize =
new Point(categoryButton.Rect.Width, (int)(categoryButton.Rect.Width * ((float)sprite.Sprite.SourceRect.Height / sprite.Sprite.SourceRect.Width)));
var sprite = btn.Frame.sprites[GUIComponent.ComponentState.None].First();
btn.RectTransform.NonScaledSize = new Point(btn.Rect.Width, (int)(btn.Rect.Width * ((float)sprite.Sprite.SourceRect.Height / sprite.Sprite.SourceRect.Width)));
};
}
@@ -503,11 +503,11 @@ namespace Barotrauma
ForceUpperCase = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
"", font: GUI.SubHeadingFont, textAlignment: Alignment.TopRight)
"", textColor: Color.White, font: GUI.SubHeadingFont, textAlignment: Alignment.TopRight)
{
AutoScaleVertical = true,
TextScale = 1.1f,
TextGetter = () => GetCurrencyFormatted(PlayerMoney)
TextGetter = GetPlayerBalanceText
};
// Divider ------------------------------------------------
@@ -523,7 +523,7 @@ namespace Barotrauma
RelativeSpacing = 0.015f,
Stretch = true
};
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.85f), shoppingCrateInventoryContainer.RectTransform), style: null);
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), shoppingCrateInventoryContainer.RectTransform), style: null);
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
if (GameMain.IsSingleplayer)
@@ -531,6 +531,21 @@ namespace Barotrauma
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
}
var relevantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
relevantBalanceName = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), relevantBalanceContainer.RectTransform), "", font: GUI.Font)
{
CanBeFocused = false
};
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), relevantBalanceContainer.RectTransform), "", textColor: Color.White, font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{
CanBeFocused = false,
TextScale = 1.1f,
TextGetter = () => IsBuying ? GetPlayerBalanceText() : GetMerchantBalanceText()
};
var totalContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
{
Stretch = true
@@ -576,10 +591,14 @@ namespace Barotrauma
resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList)
private string GetMerchantBalanceText() => GetCurrencyFormatted(CurrentLocation?.StoreCurrentBalance ?? 0);
private string GetPlayerBalanceText() => GetCurrencyFormatted(PlayerMoney);
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount = 4)
{
var elementHeight = (int)(GUI.yScale * 80);
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, 4 * elementHeight + 3), parent: parentList.Content.RectTransform), style: null);
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, elementCount * elementHeight + 3), parent: parentList.Content.RectTransform), style: null);
frame.UserData = "deals";
var dealsGroup = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter);
var dealsHeader = new GUILayoutGroup(new RectTransform(new Point((int)(0.95f * parentList.Content.Rect.Width), elementHeight), parent: dealsGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
@@ -604,17 +623,14 @@ namespace Barotrauma
{
prevLocation.Reputation.OnReputationValueChanged = null;
}
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
if (ItemPrefab.Prefabs.Any(p => p.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo _)))
{
if (itemPrefab.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo _))
selectedItemCategory = null;
searchBox.Text = "";
ChangeStoreTab(StoreTab.Buy);
if (newLocation?.Reputation != null)
{
ChangeStoreTab(StoreTab.Buy);
if (newLocation?.Reputation != null)
{
newLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
}
return;
newLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
}
}
}
@@ -628,6 +644,7 @@ namespace Barotrauma
tabButton.Selected = (StoreTab)tabButton.UserData == activeTab;
}
sortingDropDown.SelectItem(tabSortingMethods[tab]);
relevantBalanceName.Text = IsBuying ? TextManager.Get("campaignstore.balance") : TextManager.Get("campaignstore.storebalance");
SetShoppingCrateTotalText();
SetClearAllButtonStatus();
SetConfirmButtonBehavior();
@@ -697,7 +714,7 @@ namespace Barotrauma
}
foreach (GUIButton btn in itemCategoryButtons)
{
btn.Selected = category.HasValue && (MapEntityCategory)btn.UserData == selectedItemCategory;
btn.Selected = (MapEntityCategory?)btn.UserData == selectedItemCategory;
}
list.UpdateScrollBarSize();
}
@@ -709,6 +726,8 @@ namespace Barotrauma
FilterStoreItems(category, searchBox.Text);
}
int prevDailySpecialCount;
private void RefreshStoreBuyList()
{
float prevBuyListScroll = storeBuyList.BarScroll;
@@ -717,11 +736,14 @@ namespace Barotrauma
bool hasPermissions = HasPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeDailySpecialsGroup != null) != CurrentLocation.DailySpecials.Any())
int dailySpecialCount = CurrentLocation?.DailySpecials.Count() ?? 3;
if ((storeDailySpecialsGroup != null) != CurrentLocation.DailySpecials.Any() || dailySpecialCount != prevDailySpecialCount)
{
if (storeDailySpecialsGroup == null)
if (storeDailySpecialsGroup == null || dailySpecialCount != prevDailySpecialCount)
{
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList);
storeBuyList.RemoveChild(storeDailySpecialsGroup?.Parent);
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList, 1 + dailySpecialCount);
storeDailySpecialsGroup.Parent.SetAsFirstChild();
}
else
@@ -730,6 +752,7 @@ namespace Barotrauma
storeDailySpecialsGroup = null;
}
storeBuyList.RecalculateChildren();
prevDailySpecialCount = dailySpecialCount;
}
foreach (PurchasedItem item in CurrentLocation.StoreStock)

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
@@ -18,8 +19,8 @@ namespace Barotrauma
private static UISprite spectateIcon, disconnectedIcon;
private static Sprite ownerIcon, moderatorIcon;
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine };
private static InfoFrameTab selectedTab;
public enum InfoFrameTab { Crew, Mission, Reputation, Traitor, Submarine, Talents };
public static InfoFrameTab selectedTab;
private GUIFrame infoFrame, contentFrame;
private readonly List<GUIButton> tabButtons = new List<GUIButton>();
@@ -33,6 +34,8 @@ namespace Barotrauma
private List<CharacterTeamType> teamIDs;
private const string inLobbyString = "\u2022 \u2022 \u2022";
private GUIFrame pendingChangesFrame = null;
public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
private class LinkedGUI
@@ -48,7 +51,7 @@ namespace Barotrauma
private readonly GUIFrame frame;
public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock)
{
{
this.client = client;
this.textBlock = textBlock;
this.frame = frame;
@@ -125,7 +128,7 @@ namespace Barotrauma
public TabMenu()
{
if (!initialized) Initialize();
if (!initialized) { Initialize(); }
CreateInfoFrame(selectedTab);
SelectInfoFrameTab(null, selectedTab);
@@ -133,6 +136,17 @@ namespace Barotrauma
public void Update()
{
GameSession.UpdateTalentNotificationIndicator(talentPointNotification);
if (Character.Controlled is { } controlled && talentResetButton != null && talentApplyButton != null)
{
int talentCount = selectedTalents.Count - controlled.Info.GetUnlockedTalentsInTree().Count();
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
{
talentApplyButton.Flash(GUI.Style.Orange);
}
}
if (selectedTab != InfoFrameTab.Crew) return;
if (linkedGUIList == null) return;
@@ -182,16 +196,8 @@ namespace Barotrauma
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, infoFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
//this used to be a switch expression but i changed it because it killed enc :(
Vector2 contentFrameSize;
switch (selectedTab)
{
case InfoFrameTab.MyCharacter:
contentFrameSize = new Vector2(0.45f, 0.5f);
break;
default:
contentFrameSize = new Vector2(0.45f, 0.667f);
break;
}
//now it's not even a switch statement anymore :(
Vector2 contentFrameSize = new Vector2(0.45f, 0.667f);
contentFrame = new GUIFrame(new RectTransform(contentFrameSize, infoFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.12f) });
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.958f, 0.943f), contentFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, GUI.IntScale(25f)) }, isHorizontal: true)
@@ -242,6 +248,17 @@ namespace Barotrauma
{
TextGetter = () => TextManager.GetWithVariable("campaignmoney", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", campaignMode.Money))
};
GUIFrame bottomDisclaimerFrame = new GUIFrame(new RectTransform(new Vector2(contentFrameSize.X, 0.1f), infoFrame.RectTransform)
{
AbsoluteOffset = new Point(contentFrame.Rect.X, contentFrame.Rect.Bottom + GUI.IntScale(8))
}, style: null);
pendingChangesFrame = new GUIFrame(new RectTransform(Vector2.One, bottomDisclaimerFrame.RectTransform, Anchor.Center), style: null);
if (GameMain.NetLobbyScreen?.CampaignCharacterDiscarded ?? false)
{
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
}
}
else
{
@@ -254,10 +271,17 @@ namespace Barotrauma
var submarineButton = createTabButton(InfoFrameTab.Submarine, "submarine");
if (GameMain.NetworkMember != null)
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.character");
talentsButton.OnAddedToGUIUpdateList += (component) =>
{
var myCharacterButton = createTabButton(InfoFrameTab.MyCharacter, "tabmenu.character");
}
talentsButton.Enabled = Character.Controlled?.Info != null;
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
{
SelectInfoFrameTab(null, InfoFrameTab.Crew);
}
};
talentPointNotification = GameSession.CreateTalentIconNotification(talentsButton);
}
private bool SelectInfoFrameTab(GUIButton button, object userData)
@@ -289,13 +313,12 @@ namespace Barotrauma
if (traitor == null || traitorMission == null) return false;
CreateTraitorInfo(infoFrameHolder, traitorMission, traitor);
break;
case InfoFrameTab.MyCharacter:
if (GameMain.NetworkMember == null) { return false; }
GameMain.NetLobbyScreen.CreatePlayerFrame(infoFrameHolder);
break;
case InfoFrameTab.Submarine:
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
break;
case InfoFrameTab.Talents:
CreateTalentInfo(infoFrameHolder);
break;
}
return true;
@@ -408,7 +431,7 @@ namespace Barotrauma
if (GameMain.IsMultiplayer)
{
CreateMultiPlayerList(false);
CreateMultiPlayerLogContent(crewFrame);
CreateMultiPlayerLogContent(crewFrame);
}
else
{
@@ -599,7 +622,7 @@ namespace Barotrauma
AbsoluteSpacing = 2
};
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center),
onDraw: (sb, component) => DrawNotInGameIcon(sb, component.Rect, client))
{
CanBeFocused = false,
@@ -828,7 +851,7 @@ namespace Barotrauma
}
private static readonly List<Pair<string, PlayerConnectionChangeType>> storedMessages = new List<Pair<string, PlayerConnectionChangeType>>();
public static void StorePlayerConnectionChangeMessage(ChatMessage message)
{
if (!GameMain.GameSession?.IsRunning ?? true) { return; }
@@ -841,7 +864,7 @@ namespace Barotrauma
TabMenu instance = GameSession.TabMenuInstance;
instance.AddLineToLog(msg, message.ChangeType);
instance.RemoveCurrentElements();
instance.CreateMultiPlayerList(true);
instance.CreateMultiPlayerList(true);
}
}
@@ -960,7 +983,7 @@ namespace Barotrauma
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)
{
AbsoluteSpacing = spacing
AbsoluteSpacing = spacing
};
string descriptionText = mission.Description;
foreach (string missionMessage in mission.ShownMessages)
@@ -988,7 +1011,7 @@ namespace Barotrauma
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);
@@ -999,8 +1022,8 @@ namespace Barotrauma
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);*/
new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
{
new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
{
Color = mission.Prefab.IconColor,
HoverColor = mission.Prefab.IconColor,
SelectedColor = mission.Prefab.IconColor,
@@ -1159,5 +1182,506 @@ namespace Barotrauma
sub.Info.CreateSpecsWindow(specsListBox, GUI.Font, includeTitle: false, includeClass: false, includeDescription: true);
}
}
private Color unselectedColor = new Color(240, 255, 255, 225);
private Color selectedColor = new Color(220, 255, 220, 225);
private Color ownedColor = new Color(140, 180, 140, 225);
private Color unselectableColor = new Color(100, 100, 100, 225);
private Color pressedColor = new Color(60, 60, 60, 225);
private readonly List<(GUIButton button, GUIComponent icon)> talentButtons = new List<(GUIButton button, GUIComponent icon)>();
private readonly List<(string talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)> talentCornerIcons = new List<(string talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)>();
private List<string> selectedTalents = new List<string>();
private GUITextBlock experienceText;
private GUIProgressBar experienceBar;
private GUITextBlock talentPointText;
private GUIListBox skillListBox;
private GUIButton talentApplyButton,
talentResetButton;
private GUIImage talentPointNotification;
private readonly ImmutableDictionary<TalentTree.TalentTreeStageState, GUIComponentStyle> talentStageStyles = new Dictionary<TalentTree.TalentTreeStageState, GUIComponentStyle>
{
{ TalentTree.TalentTreeStageState.Invalid, GUI.Style.GetComponentStyle("TalentTreeLocked") },
{ TalentTree.TalentTreeStageState.Locked, GUI.Style.GetComponentStyle("TalentTreeLocked") },
{ TalentTree.TalentTreeStageState.Unlocked, GUI.Style.GetComponentStyle("TalentTreePurchased") },
{ TalentTree.TalentTreeStageState.Available, GUI.Style.GetComponentStyle("TalentTreeUnlocked") },
{ TalentTree.TalentTreeStageState.Highlighted, GUI.Style.GetComponentStyle("TalentTreeAvailable") },
}.ToImmutableDictionary();
private readonly ImmutableDictionary<TalentTree.TalentTreeStageState, Color> talentStageBackgroundColors = new Dictionary<TalentTree.TalentTreeStageState, Color>
{
{ TalentTree.TalentTreeStageState.Invalid, new Color(48,48,48,255) },
{ TalentTree.TalentTreeStageState.Locked, new Color(48,48,48,255) },
{ TalentTree.TalentTreeStageState.Unlocked, new Color(24,37,31,255) },
{ TalentTree.TalentTreeStageState.Available, new Color(50,47,33,255) },
{ TalentTree.TalentTreeStageState.Highlighted, new Color(50,47,33,255) },
}.ToImmutableDictionary();
private void CreateTalentInfo(GUIFrame infoFrame)
{
infoFrame.ClearChildren();
talentButtons.Clear();
talentCornerIcons.Clear();
Character controlledCharacter = Character.Controlled;
if (controlledCharacter == null) { return; }
GUIFrame talentFrameBackground = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = GUI.IntScale(15);
GUIFrame talentFrameContent = new GUIFrame(new RectTransform(new Point(talentFrameBackground.Rect.Width - padding, talentFrameBackground.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
GUIFrame paddedTalentFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), talentFrameContent.RectTransform, Anchor.Center), style: null);
GUIFrame talentFrameMain = new GUIFrame(new RectTransform(Vector2.One, paddedTalentFrame.RectTransform), style: null);
GUIFrame characterSettingsFrame = null;
GUILayoutGroup characterLayout = null;
if (!(GameMain.NetworkMember is null))
{
characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrameContent.RectTransform), style: null) { Visible = false };
characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null);
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
}
if (controlledCharacter.Info is null)
{
DebugConsole.ThrowError("No character info found for talent UI");
return;
}
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameMain.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = GUI.IntScale(5)
};
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), talentFrameLayoutGroup.RectTransform, Anchor.Center), isHorizontal: true);
CharacterInfo info = controlledCharacter.Info;
Job job = info.Job;
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), talentInfoLayoutGroup.RectTransform), onDraw: (batch, component) =>
{
float posY = component.Rect.Center.Y - component.Rect.Width / 2;
info.DrawPortrait(batch, new Vector2(component.Rect.X, posY), Vector2.Zero, component.Rect.Width, false, false);
});
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), talentInfoLayoutGroup.RectTransform)) { RelativeSpacing = 0.05f };
Vector2 nameSize = GUI.SubHeadingFont.MeasureString(info.Name);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), info.Name, font: GUI.SubHeadingFont) { TextColor = job.Prefab.UIColor };
nameBlock.RectTransform.NonScaledSize = nameSize.Pad(nameBlock.Padding).ToPoint();
Vector2 jobSize = GUI.SmallFont.MeasureString(job.Name);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), job.Name, font: GUI.SmallFont) { TextColor = job.Prefab.UIColor };
jobBlock.RectTransform.NonScaledSize = jobSize.Pad(jobBlock.Padding).ToPoint();
string traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + info.PersonalityTrait.Name.Replace(" ", "")));
Vector2 traitSize = GUI.SmallFont.MeasureString(traitString);
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUI.SmallFont);
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
GUIFrame endocrineFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), nameLayout.RectTransform, Anchor.BottomCenter), style: null);
if (!(GameMain.NetworkMember is null))
{
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.675f, 1f), endocrineFrame.RectTransform, Anchor.TopLeft), text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"))
{
IgnoreLayoutGroups = true
};
newCharacterBox.OnClicked = (button, o) =>
{
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
{
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
{
newCharacterBox.Text = TextManager.Get("settings");
if (pendingChangesFrame != null)
{
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
}
OpenMenu();
});
return true;
}
OpenMenu();
return true;
void OpenMenu()
{
characterSettingsFrame!.Visible = true;
talentFrameMain.Visible = false;
}
};
if (!(characterLayout is null))
{
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomRight);
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
{
OnClicked = (button, o) =>
{
characterSettingsFrame!.Visible = false;
talentFrameMain.Visible = true;
return true;
}
};
}
}
IEnumerable<TalentPrefab> endocrineTalents = info.GetEndocrineTalents().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier.Equals(e, StringComparison.OrdinalIgnoreCase)));
if (endocrineTalents.Count() > 0)
{
GUIImage endocrineIcon = new GUIImage(new RectTransform(new Vector2(0.275f, 1f), endocrineFrame.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.Normal), style: "EndocrineReminderIcon")
{
ToolTip = $"{TextManager.Get("afflictionname.endocrineboost")}\n\n{string.Join(", ", endocrineTalents.Select(e => e.DisplayName))}"
};
}
GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), talentInfoLayoutGroup.RectTransform)) { Stretch = true };
string skillString = TextManager.Get("skills");
Vector2 skillSize = GUI.SubHeadingFont.MeasureString(skillString);
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(Vector2.One, skillLayout.RectTransform), skillString, font: GUI.SubHeadingFont);
skillBlock.RectTransform.NonScaledSize = skillSize.Pad(skillBlock.Padding).ToPoint();
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
CreateTalentSkillList(controlledCharacter, skillListBox);
if (!TalentTree.JobTalentTrees.TryGetValue(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine");
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
foreach (var subTree in talentTree.TalentSubTrees)
{
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
int elementPadding = GUI.IntScale(8);
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUI.SubHeadingFont, textAlignment: Alignment.Center));
for (int i = 0; i < 4; i++)
{
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground");
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
{
CanBeFocused = false
};
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
if (subTree.TalentOptionStages.Count > i)
{
TalentOption talentOption = subTree.TalentOptionStages[i];
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.7f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
foreach (TalentPrefab talent in talentOption.Talents)
{
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentOptionLayoutGroup.RectTransform), style: null)
{
CanBeFocused = false
};
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
{
ToolTip = $"{talent.DisplayName}\n\n{talent.Description}",
UserData = talent.Identifier,
PressedColor = pressedColor,
OnClicked = (button, userData) =>
{
// deselect other buttons in tier by removing their selected talents from pool
foreach (GUIButton guiButton in talentOptionLayoutGroup.GetAllChildren<GUIButton>())
{
if (guiButton.UserData is string otherTalentIdentifier && guiButton != button)
{
if (!controlledCharacter.HasTalent(otherTalentIdentifier))
{
selectedTalents.Remove(otherTalentIdentifier);
}
}
}
string talentIdentifier = userData as string;
if (TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents))
{
if (!selectedTalents.Contains(talentIdentifier))
{
selectedTalents.Add(talentIdentifier);
}
}
else if (!controlledCharacter.HasTalent(talentIdentifier))
{
selectedTalents.Remove(talentIdentifier);
}
UpdateTalentButtons();
return true;
},
};
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = Color.Transparent;
GUIComponent iconImage;
if (talent.Icon is null)
{
iconImage = new GUITextBlock(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), text: "???", font: GUI.LargeFont, textAlignment: Alignment.Center, style: null)
{
OutlineColor = GUI.Style.Red,
TextColor = GUI.Style.Red,
PressedColor = unselectableColor,
CanBeFocused = false,
};
}
else
{
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
{
PressedColor = unselectableColor,
CanBeFocused = false,
};
}
talentButtons.Add((talentButton, iconImage));
}
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
}
}
}
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f };
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), talentBottomFrame.RectTransform));
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: GUI.Style.Green)
{
IsHorizontal = true
};
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUI.Font, textAlignment: Alignment.CenterRight)
{
Shadow = true
};
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUI.SubHeadingFont, parseRichText: true, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
{
OnClicked = ResetTalentSelection
};
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
{
OnClicked = ApplyTalentSelection,
};
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
UpdateTalentButtons();
}
private void CreateTalentSkillList(Character character, GUIListBox parent)
{
parent.Content.ClearChildren();
List<GUITextBlock> skillNames = new List<GUITextBlock>();
foreach (Skill skill in character.Info.Job.Skills)
{
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false };
skillNames.Add(new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}", returnNull: true) ?? skill.Identifier));
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.CenterRight) { Padding = new Vector4(0, 0, 4, 0) };
float modifiedSkillLevel = character.GetSkillLevel(skill.Identifier);
if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level)))
{
int skillChange = (int)MathF.Floor(modifiedSkillLevel - skill.Level);
string stringColor = true switch
{
true when skillChange > 0 => XMLExtensions.ColorToString(GUI.Style.Green),
true when skillChange < 0 => XMLExtensions.ColorToString(GUI.Style.Red),
_ => XMLExtensions.ColorToString(GUI.Style.TextColor)
};
string changeText = $"(‖color:{stringColor}‖{(skillChange > 0 ? "+" : string.Empty) + skillChange}‖color:end‖)";
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), changeText, parseRichText: true) { Padding = Vector4.Zero };
}
skillContainer.Recalculate();
}
parent.RecalculateChildren();
GUITextBlock.AutoScaleAndNormalize(skillNames);
}
private void UpdateTalentButtons()
{
Character controlledCharacter = Character.Controlled;
experienceText.Text = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
experienceBar.BarSize = controlledCharacter.Info.GetProgressTowardsNextLevel();
//experienceBar.ToolTip = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
string pointsLeft = controlledCharacter.Info.GetAvailableTalentPoints().ToString();
int talentCount = selectedTalents.Count - controlledCharacter.Info.GetUnlockedTalentsInTree().Count();
if (talentCount > 0)
{
string pointsUsed = $"‖color:{XMLExtensions.ColorToString(GUI.Style.Red)}‖{-talentCount}‖color:end‖";
string localizedString = TextManager.GetWithVariables("talentmenu.points.spending", new []{ "[amount]", "[used]" }, new []{ pointsLeft, pointsUsed});
talentPointText.SetRichText(localizedString);
}
else
{
talentPointText.SetRichText(TextManager.GetWithVariable("talentmenu.points", "[amount]", pointsLeft));
}
foreach (var (talentTree, index, icon, frame, glow) in talentCornerIcons)
{
TalentTree.TalentTreeStageState state = TalentTree.GetTalentOptionStageState(controlledCharacter, talentTree, index, selectedTalents);
GUIComponentStyle newStyle = talentStageStyles[state];
icon.ApplyStyle(newStyle);
icon.Color = newStyle.Color;
frame.Color = talentStageBackgroundColors[state];
glow.Visible = state == TalentTree.TalentTreeStageState.Highlighted;
}
foreach (var talentButton in talentButtons)
{
string talentIdentifier = talentButton.button.UserData as string;
bool unselectable = !TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents) || controlledCharacter.HasTalent(talentIdentifier);
Color newTalentColor = unselectable ? unselectableColor : unselectedColor;
Color hoverColor = Color.White;
if (controlledCharacter.HasTalent(talentIdentifier))
{
newTalentColor = GUI.Style.Green;
}
else if (selectedTalents.Contains(talentIdentifier))
{
newTalentColor = GUI.Style.Orange;
hoverColor = Color.Lerp(GUI.Style.Orange, Color.White, 0.7f);
}
talentButton.icon.Color = newTalentColor;
talentButton.icon.HoverColor = hoverColor;
}
CreateTalentSkillList(controlledCharacter, skillListBox);
}
private void ApplyTalents(Character controlledCharacter)
{
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
foreach (string talent in selectedTalents)
{
controlledCharacter.GiveTalent(talent);
if (GameMain.Client != null)
{
GameMain.Client.CreateEntityEvent(controlledCharacter, new object[] { NetEntityEvent.Type.UpdateTalents });
}
}
UpdateTalentButtons();
}
private bool ApplyTalentSelection(GUIButton guiButton, object userData)
{
Character controlledCharacter = Character.Controlled;
ApplyTalents(controlledCharacter);
return true;
}
private bool ResetTalentSelection(GUIButton guiButton, object userData)
{
Character controlledCharacter = Character.Controlled;
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
UpdateTalentButtons();
return true;
}
public void OnExperienceChanged(Character character)
{
if (character != Character.Controlled) { return; }
UpdateTalentButtons();
}
private readonly StatTypes[] basicStats = new StatTypes[]
{
StatTypes.MaximumHealthMultiplier,
StatTypes.MovementSpeed,
StatTypes.SwimmingSpeed,
StatTypes.RepairSpeed,
};
private readonly StatTypes[] combatStats = new StatTypes[]
{
StatTypes.MeleeAttackMultiplier,
StatTypes.MeleeAttackSpeed,
StatTypes.RangedAttackSpeed,
StatTypes.TurretAttackSpeed,
};
private readonly StatTypes[] miscStats = new StatTypes[]
{
StatTypes.ReputationGainMultiplier,
StatTypes.MissionMoneyGainMultiplier,
StatTypes.ExperienceGainMultiplier,
StatTypes.MissionExperienceGainMultiplier,
};
private void CreateCharacterSheet(GUILayoutGroup characterInfoColumn)
{
Character controlledCharacter = Character.Controlled;
CreateRow(basicStats);
CreateRow(combatStats);
CreateRow(miscStats);
void CreateRow(StatTypes[] statTypes)
{
GUILayoutGroup characterInfoRow = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1.0f), characterInfoColumn.RectTransform, anchor: Anchor.TopLeft), childAnchor: Anchor.TopCenter);
foreach (StatTypes statType in statTypes)
{
ShowStat(statType, characterInfoRow);
}
}
void ShowStat(StatTypes statType, GUILayoutGroup characterInfoRow)
{
GUIFrame textInfoFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.33f), characterInfoRow.RectTransform, Anchor.TopCenter), style: null);
new GUITextBlock(new RectTransform(new Vector2(1f, 1f), textInfoFrame.RectTransform, Anchor.TopLeft), statType.ToString(), font: GUI.SmallFont, textAlignment: Alignment.TopLeft);
new GUITextBlock(new RectTransform(new Vector2(1f, 1f), textInfoFrame.RectTransform, Anchor.TopLeft), (int)(100f * (1 + controlledCharacter.GetStatValue(statType))) + "%", font: GUI.Font, textAlignment: Alignment.TopRight);
}
}
}
}

View File

@@ -115,8 +115,9 @@ namespace Barotrauma
return MathHelper.Clamp(Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale), minBorderScale, maxBorderScale);
}
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None, Vector2? uvOffset = null)
{
uvOffset ??= Vector2.Zero;
if (Sprite.Texture == null)
{
GUI.DrawRectangle(spriteBatch, rect, Color.Magenta);
@@ -157,7 +158,7 @@ namespace Barotrauma
else if (Tile)
{
Vector2 startPos = new Vector2(rect.X, rect.Y);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color, startOffset: uvOffset);
}
else
{

View File

@@ -1045,10 +1045,10 @@ namespace Barotrauma
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
{
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton);
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
}
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, string title, string body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton")
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, string title, string body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab upgradePrefab = null, int currentLevel = 0)
{
float progressBarHeight = 0.25f;
@@ -1089,7 +1089,7 @@ namespace Barotrauma
//negative price = refund
if (price < 0) { formattedPrice = "+" + formattedPrice; }
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
var priceText = new GUITextBlock(rectT(1, 0.4f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center);
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center);
if (price < 0)
{
priceText.TextColor = GUI.Style.Green;
@@ -1099,6 +1099,11 @@ namespace Barotrauma
priceText.Text = string.Empty;
}
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle) { Enabled = false };
if (upgradePrefab != null)
{
var increaseText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), "", textAlignment: Alignment.Center);
UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
}
}
description.CalculateHeightFromText();
@@ -1127,6 +1132,19 @@ namespace Barotrauma
return prefabFrame;
}
private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab, int currentLevel)
{
float nextIncrease = upgradePrefab.IncreaseOnTooltip * (Math.Min(currentLevel + 1, upgradePrefab.MaxLevel));
if (nextIncrease != 0f)
{
text.Text = $"{Math.Round(nextIncrease, 1)} %";
if (currentLevel == upgradePrefab.MaxLevel)
{
text.TextColor = Color.Gray;
}
}
}
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item>? itemsOnSubmarine)
{
if (Campaign is null) { return; }
@@ -1541,7 +1559,9 @@ namespace Barotrauma
if (prefabFrame.FindChild("buybutton", true) is { } buttonParent)
{
GUITextBlock priceLabel = buttonParent.GetChild<GUITextBlock>();
List<GUITextBlock> textBlocks = buttonParent.GetAllChildren<GUITextBlock>().ToList();
GUITextBlock priceLabel = textBlocks[0];
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
if (priceLabel != null && !WaitForServerUpdate)
@@ -1562,6 +1582,11 @@ namespace Barotrauma
button.Enabled = false;
}
}
GUITextBlock increaseLabel = textBlocks[1];
if (increaseLabel != null && !WaitForServerUpdate)
{
UpdateUpgradePercentageText(increaseLabel, prefab, currentLevel);
}
}
}

View File

@@ -45,6 +45,7 @@ namespace Barotrauma
public static SteamWorkshopScreen SteamWorkshopScreen;
public static SubEditorScreen SubEditorScreen;
public static TestScreen TestScreen;
public static ParticleEditorScreen ParticleEditorScreen;
public static LevelEditorScreen LevelEditorScreen;
public static SpriteEditorScreen SpriteEditorScreen;
@@ -91,7 +92,16 @@ namespace Barotrauma
public static ParticleManager ParticleManager;
public static DecalManager DecalManager;
public static World World;
private static World world;
public static World World
{
get
{
if (world == null) { world = new World(new Vector2(0, -9.82f)); }
return world;
}
set { world = value; }
}
public static LoadingScreen TitleScreen;
private bool loadingScreenOpen;
@@ -186,6 +196,8 @@ namespace Barotrauma
#if DEBUG
public static bool FirstLoad = true;
public static bool CancelQuickStart;
#endif
public GameMain(string[] args)
@@ -243,7 +255,6 @@ namespace Barotrauma
GameMain.ResetFrameTime();
fixedTime = new GameTime();
World = new World(new Vector2(0, -9.82f));
FarseerPhysics.Settings.AllowSleep = true;
FarseerPhysics.Settings.ContinuousPhysics = false;
FarseerPhysics.Settings.VelocityIterations = 1;
@@ -304,7 +315,7 @@ namespace Barotrauma
GraphicsDeviceManager.PreferredBackBufferWidth = GraphicsWidth;
GraphicsDeviceManager.PreferredBackBufferHeight = GraphicsHeight;
GraphicsDeviceManager.ApplyChanges();
if (windowMode == WindowMode.BorderlessWindowed)
@@ -571,6 +582,8 @@ namespace Barotrauma
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
TalentPrefab.LoadAll(GetFilesOfType(ContentType.Talents));
TalentTree.LoadAll(GetFilesOfType(ContentType.TalentTrees));
Order.Init();
EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
@@ -581,7 +594,7 @@ namespace Barotrauma
StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure));
TitleScreen.LoadState = 55.0f;
yield return CoroutineStatus.Running;
UpgradePrefab.LoadAll(GetFilesOfType(ContentType.UpgradeModules));
TitleScreen.LoadState = 56.0f;
yield return CoroutineStatus.Running;
@@ -594,7 +607,7 @@ namespace Barotrauma
ItemAssemblyPrefab.LoadAll();
TitleScreen.LoadState = 60.0f;
yield return CoroutineStatus.Running;
GameModePreset.Init();
SaveUtil.DeleteDownloadedSubs();
@@ -624,6 +637,7 @@ namespace Barotrauma
#endif
SubEditorScreen = new SubEditorScreen();
TestScreen = new TestScreen();
TitleScreen.LoadState = 75.0f;
yield return CoroutineStatus.Running;
@@ -646,7 +660,7 @@ namespace Barotrauma
ParticleManager.LoadPrefabs();
TitleScreen.LoadState = 88.0f;
LevelObjectPrefab.LoadAll();
TitleScreen.LoadState = 90.0f;
yield return CoroutineStatus.Running;
@@ -796,12 +810,18 @@ namespace Barotrauma
}
#if DEBUG
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled) && FirstLoad && !PlayerInput.KeyDown(Keys.LeftShift))
CancelQuickStart |= PlayerInput.KeyDown(Keys.LeftShift);
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled || Config.TestScreenEnabled) && FirstLoad && !CancelQuickStart)
{
loadingScreenOpen = false;
FirstLoad = false;
if (Config.AutomaticQuickStartEnabled)
if (Config.TestScreenEnabled)
{
TestScreen.Select();
}
else if (Config.AutomaticQuickStartEnabled)
{
MainMenuScreen.QuickStart();
}
@@ -906,8 +926,7 @@ namespace Barotrauma
}
//open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC
else if ((Character.Controlled == null || !itemHudActive())
//TODO: do we need to check Inventory.SelectedSlot?
&& Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null
&& CharacterHealth.OpenHealthWindow == null
&& !CrewManager.IsCommandInterfaceOpen
&& !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedConstruction != null))
{
@@ -918,8 +937,8 @@ namespace Barotrauma
static bool itemHudActive()
{
if (Character.Controlled?.SelectedConstruction == null) { return false; }
return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
return
Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) ||
((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false);
}
}
@@ -1054,7 +1073,6 @@ namespace Barotrauma
spriteBatch.End();
}
sw.Stop();
PerformanceCounter.AddElapsedTicks("Draw total", sw.ElapsedTicks);
PerformanceCounter.DrawTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency);
@@ -1085,7 +1103,7 @@ namespace Barotrauma
if (save)
{
GUI.SetSavingIndicatorState(true);
if (GameSession.Submarine != null && !GameSession.Submarine.Removed)
{
GameSession.SubmarineInfo = new SubmarineInfo(GameSession.Submarine);
@@ -1257,7 +1275,7 @@ namespace Barotrauma
string text = TextManager.GetWithVariable("openlinkinbrowserprompt", "[link]", url);
string extensionText = TextManager.Get(promptExtensionTag, returnNull: true, useEnglishAsFallBack: false);
if (!string.IsNullOrEmpty(extensionText))
{
{
text += $"\n\n{extensionText}";
}

View File

@@ -48,11 +48,7 @@ namespace Barotrauma
var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
return character.Inventory.FindAllItems(item =>
{
if (item.SpawnedInOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
// There must be no contained items or the contained items must be confirmed as sold
if (!item.ContainedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
// Item must be in a non-equipment slot if possible
if (!item.AllowedSlots.All(s => equipmentSlots.Contains(s)) && IsInEquipmentSlot(item)) { return false; }
// Item must not be contained inside an item in an equipment slot
@@ -76,15 +72,10 @@ namespace Barotrauma
var confirmedSoldEntities = GetConfirmedSoldEntities();
return Submarine.MainSub.GetItems(true).FindAll(item =>
{
if (!item.Prefab.CanBeSold) { return false; }
if (item.SpawnedInOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; }
if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; }
if (!ItemAndAllContainersInteractable(item)) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
// There must be no contained items or the contained items must be confirmed as sold
if (!item.ContainedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
return true;
}).Distinct();
@@ -107,6 +98,24 @@ namespace Barotrauma
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
}
private bool IsItemSellable(Item item, IEnumerable<SoldEntity> confirmedSoldEntities)
{
if (!item.Prefab.CanBeSold) { return false; }
if (item.SpawnedInOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
if (item.OwnInventory?.Container is ItemContainer itemContainer)
{
var containedItems = item.ContainedItems;
if (containedItems.None()) { return true; }
// Allow selling the item if contained items are unsellable and set to be removed on deconstruct
if (itemContainer.RemoveContainedItemsOnDeconstruct && containedItems.All(it => !it.Prefab.CanBeSold)) { return true; }
// Otherwise there must be no contained items or the contained items must be confirmed as sold
if (!containedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
}
return true;
}
public void SetItemsInBuyCrate(List<PurchasedItem> items)
{
ItemsInBuyCrate.Clear();

View File

@@ -60,7 +60,7 @@ namespace Barotrauma
var newCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
var loadCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
GameMain.NetLobbyScreen.CampaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, null, saveFiles);
GameMain.NetLobbyScreen.CampaignSetupUI = new MultiPlayerCampaignSetupUI(newCampaignContainer, loadCampaignContainer, null, saveFiles);
var newCampaignButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform),
TextManager.Get("NewCampaign"), style: "GUITabButton")
@@ -761,7 +761,7 @@ namespace Barotrauma
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
if (faction?.Reputation != null)
{
faction.Reputation.Value = rep;
faction.Reputation.SetReputation(rep);
}
else
{
@@ -771,7 +771,7 @@ namespace Barotrauma
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.Value = reputation.Value;
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
}

View File

@@ -21,10 +21,11 @@ namespace Barotrauma
{
if (GameMain.NetworkMember != null && GameMain.NetLobbyScreen != null)
{
if (GameMain.NetLobbyScreen.HeadSelectionList != null) { GameMain.NetLobbyScreen.HeadSelectionList.Visible = false; }
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu?.Dispose();
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu = null;
if (GameMain.NetLobbyScreen.JobSelectionFrame != null) { GameMain.NetLobbyScreen.JobSelectionFrame.Visible = false; }
}
if (tabMenu == null && GameMode is TutorialMode == false)
if (tabMenu == null && !(GameMode is TutorialMode) && !ConversationAction.IsDialogOpen)
{
tabMenu = new TabMenu();
HintManager.OnShowTabMenu();
@@ -34,17 +35,17 @@ namespace Barotrauma
tabMenu = null;
NetLobbyScreen.JobInfoFrame = null;
}
return true;
}
private GUILayoutGroup topLeftButtonGroup;
private GUIButton crewListButton, commandButton, tabMenuButton;
private GUIImage talentPointNotification;
private GUIComponent respawnInfoFrame, respawnButtonContainer;
private GUITextBlock respawnInfoText;
private GUITickBox respawnTickBox;
private GUILayoutGroup TopLeftButtonGroup;
private void CreateTopLeftButtons()
{
if (topLeftButtonGroup != null)
@@ -89,11 +90,11 @@ namespace Barotrauma
tabMenuButton = new GUIButton(new RectTransform(buttonSize, parent: topLeftButtonGroup.RectTransform), style: "TabMenuButton")
{
ToolTip = TextManager.GetWithVariable("hudbutton.tabmenu", "[key]", GameMain.Config.KeyBindText(InputType.InfoTab)),
OnClicked = (button, userData) =>
{
return ToggleTabMenu();
}
OnClicked = (button, userData) => ToggleTabMenu()
};
talentPointNotification = CreateTalentIconNotification(tabMenuButton);
GameMain.Instance.ResolutionChanged += CreateTopLeftButtons;
respawnInfoFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), parent: topLeftButtonGroup.RectTransform)
@@ -140,11 +141,41 @@ namespace Barotrauma
if (GameMain.NetworkMember != null)
{
GameMain.NetLobbyScreen?.HeadSelectionList?.AddToGUIUpdateList();
GameMain.NetLobbyScreen.CharacterAppearanceCustomizationMenu?.AddToGUIUpdateList();
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList();
}
}
public static GUIImage CreateTalentIconNotification(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")
{
Visible = false,
CanBeFocused = false
};
Point notificationSize = indicator.RectTransform.NonScaledSize;
if (offset)
{
indicator.RectTransform.AbsoluteOffset = new Point(-(notificationSize.X / 2), -(notificationSize.Y / 2));
}
return indicator;
}
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;
}
}
}
partial void UpdateProjSpecific(float deltaTime)
{
if (GUI.DisableHUD) { return; }
@@ -159,28 +190,24 @@ namespace Barotrauma
else
{
tabMenu.Update();
if ((PlayerInput.KeyHit(InputType.InfoTab) || PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) &&
if ((PlayerInput.KeyHit(InputType.InfoTab) || PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) &&
!(GUI.KeyboardDispatcher.Subscriber is GUITextBox))
{
ToggleTabMenu();
}
}
UpdateTalentNotificationIndicator(talentPointNotification);
if (GameMain.NetworkMember != null)
{
if (GameMain.NetLobbyScreen?.HeadSelectionList != null)
{
if (PlayerInput.PrimaryMouseButtonDown() && !GUI.IsMouseOn(GameMain.NetLobbyScreen.HeadSelectionList))
{
if (GameMain.NetLobbyScreen.HeadSelectionList != null) { GameMain.NetLobbyScreen.HeadSelectionList.Visible = false; }
}
}
GameMain.NetLobbyScreen?.CharacterAppearanceCustomizationMenu?.Update();
if (GameMain.NetLobbyScreen?.JobSelectionFrame != null)
{
if (PlayerInput.PrimaryMouseButtonDown() && !GUI.IsMouseOn(GameMain.NetLobbyScreen.JobSelectionFrame))
if (GameMain.NetLobbyScreen.JobSelectionFrame != null && PlayerInput.PrimaryMouseButtonDown() && !GUI.IsMouseOn(GameMain.NetLobbyScreen.JobSelectionFrame))
{
GameMain.NetLobbyScreen.JobList.Deselect();
if (GameMain.NetLobbyScreen.JobSelectionFrame != null) { GameMain.NetLobbyScreen.JobSelectionFrame.Visible = false; }
GameMain.NetLobbyScreen.JobSelectionFrame.Visible = false;
}
}
}

View File

@@ -544,7 +544,7 @@ namespace Barotrauma
if (Character.Controlled.CurrentHull == null) { return; }
if (HumanAIController.IsBallastFloraNoticeable(Character.Controlled, Character.Controlled.CurrentHull))
{
if (DisplayHint("onballastflorainfected")) { return; }
if (IsOnFriendlySub() && DisplayHint("onballastflorainfected")) { return; }
}
foreach (var gap in Character.Controlled.CurrentHull.ConnectedGaps)
{
@@ -552,7 +552,7 @@ namespace Barotrauma
if (Vector2.DistanceSquared(Character.Controlled.WorldPosition, gap.ConnectedDoor.Item.WorldPosition) > 400 * 400) { continue; }
if (!gap.IsRoomToRoom)
{
if (!(Character.Controlled.GetEquippedItem("deepdiving", InvSlotType.OuterClothes) is Item)) { continue; }
if (!IsWearingDivingSuit()) { continue; }
if (Character.Controlled.IsProtectedFromPressure()) { continue; }
if (DisplayHint("divingsuitwarning", extendTextTag: false)) { return; }
continue;
@@ -561,10 +561,16 @@ namespace Barotrauma
{
if (me == Character.Controlled.CurrentHull) { continue; }
if (!(me is Hull adjacentHull)) { continue; }
if (!IsOnFriendlySub()) { continue; }
if (IsWearingDivingSuit()) { continue; }
if (adjacentHull.LethalPressure > 5.0f && DisplayHint("onadjacenthull.highpressure")) { return; }
if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint("onadjacenthull.highwaterpercentage")) { return; }
}
static bool IsWearingDivingSuit() => Character.Controlled.GetEquippedItem("deepdiving", InvSlotType.OuterClothes) is Item;
}
static bool IsOnFriendlySub() => Character.Controlled.Submarine is Submarine sub && (sub.TeamID == Character.Controlled.TeamID || sub.TeamID == CharacterTeamType.FriendlyNPC);
}
private static void CheckReminders()

View File

@@ -51,7 +51,7 @@ namespace Barotrauma
{
keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length];
keyMapping[(int)InputType.Run] = new KeyOrMouse(Keys.LeftShift);
keyMapping[(int)InputType.Attack] = new KeyOrMouse(Keys.R);
keyMapping[(int)InputType.Attack] = new KeyOrMouse(Keys.F);
keyMapping[(int)InputType.Crouch] = new KeyOrMouse(Keys.LeftControl);
keyMapping[(int)InputType.Grab] = new KeyOrMouse(Keys.G);
keyMapping[(int)InputType.Health] = new KeyOrMouse(Keys.H);
@@ -173,7 +173,7 @@ namespace Barotrauma
}
}
private void LoadKeyBinds(XElement element)
private void LoadKeyBinds(XElement element, Version gameVersion)
{
foreach (XAttribute attribute in element.Attributes())
{
@@ -183,7 +183,6 @@ namespace Barotrauma
keyMapping[(int)InputType.TakeHalfFromInventorySlot] = new KeyOrMouse(Keys.LeftShift);
keyMapping[(int)InputType.TakeOneFromInventorySlot] = new KeyOrMouse(Keys.LeftControl);
}
if (!Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType)) { continue; }
if (int.TryParse(attribute.Value.ToString(), out int mouseButtonInt))
@@ -199,6 +198,13 @@ namespace Barotrauma
keyMapping[(int)inputType] = new KeyOrMouse(key);
}
}
//v0.15 added creature attacks that can be used with a character capable of speaking (with mudraptor or spineling genes),
//which causes the previous attack keybind R to conflict with the radio keybind
// -> automatically change it to F
if (gameVersion < new Version(0, 15, 0, 0))
{
keyMapping[(int)InputType.Attack] = new KeyOrMouse(Keys.F);
}
}
private void LoadInventoryKeybinds(XElement element)
@@ -223,10 +229,12 @@ namespace Barotrauma
private void LoadControls(XDocument doc)
{
var gameVersion = new Version(doc.Root.GetAttributeString("gameversion", "0.0.0.0"));
XElement keyMapping = doc.Root.Element("keymapping");
if (keyMapping != null)
{
LoadKeyBinds(keyMapping);
LoadKeyBinds(keyMapping, gameVersion);
}
XElement inventoryKeyMapping = doc.Root.Element("inventorykeymapping");
@@ -1518,6 +1526,12 @@ namespace Barotrauma
"Automatic quickstart enabled",
"Will the game automatically move on to Quickstart when the game is launched");
addDebugTickBox(
TestScreenEnabled,
(b) => TestScreenEnabled = b,
"Test screen enabled",
"Will the game automatically move on to a test screen when the game is launched");
addDebugTickBox(
AutomaticCampaignLoadEnabled,
(b) => AutomaticCampaignLoadEnabled = b,
@@ -1831,6 +1845,7 @@ namespace Barotrauma
ic.ParseMsg();
}
}
CharacterHUD.ShouldRecreateHudTexts = true;
}
private void ApplySettings()

View File

@@ -161,7 +161,7 @@ namespace Barotrauma
public override void CreateSlots()
{
if (visualSlots == null) { visualSlots = new VisualSlot[capacity]; }
visualSlots ??= new VisualSlot[capacity];
float multiplier = !GUI.IsFourByThree() ? UIScale : UIScale * 0.925f;
@@ -174,6 +174,12 @@ namespace Barotrauma
(int)SlotPositions[i].X,
(int)SlotPositions[i].Y,
(int)(slotSprite.size.X * multiplier), (int)(slotSprite.size.Y * multiplier));
if (SlotTypes[i] == InvSlotType.HealthInterface &&
character.CharacterHealth?.InventorySlotContainer != null)
{
slotRect.Width = slotRect.Height = (int)(character.CharacterHealth.InventorySlotContainer.Rect.Width * 1.2f);
}
ItemContainer itemContainer = slots[i].FirstOrDefault()?.GetComponent<ItemContainer>();
if (itemContainer != null)
@@ -238,6 +244,8 @@ namespace Barotrauma
{
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 (layout == Layout.Default)
{
if (PersonalSlots.HasFlag(SlotTypes[i]) && !personalSlotArea.Contains(visualSlots[i].Rect.Center + visualSlots[i].DrawOffset.ToPoint())) { return true; }
@@ -315,7 +323,7 @@ namespace Barotrauma
case Layout.Default:
{
int personalSlotCount = SlotTypes.Count(s => PersonalSlots.HasFlag(s));
int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s));
int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s) && s != InvSlotType.HealthInterface);
int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (SlotSize.X + Spacing) / 2;
int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - Spacing * 4 - HideButtonWidth;
@@ -359,7 +367,8 @@ namespace Barotrauma
int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - Spacing;
for (int i = 0; i < visualSlots.Length; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; }
if (PersonalSlots.HasFlag(SlotTypes[i]))
{
//upperX -= slotSize.X + spacing;
@@ -371,10 +380,18 @@ namespace Barotrauma
}
int lowerX = x;
int handSlotX = x;
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - Spacing * 2 - (int)(!GUI.IsFourByThree() ? UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment : UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment * 2f);
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand)
{
SlotPositions[i] = new Vector2(handSlotX, personalSlotY);
handSlotX += visualSlots[i].Rect.Width + Spacing;
continue;
}
if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
if (PersonalSlots.HasFlag(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
@@ -390,7 +407,8 @@ namespace Barotrauma
x = lowerX;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (!HideSlot(i)) continue;
if (!HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; }
x -= visualSlots[i].Rect.Width + Spacing;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
}
@@ -404,7 +422,8 @@ namespace Barotrauma
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; }
if (PersonalSlots.HasFlag(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
@@ -416,9 +435,16 @@ namespace Barotrauma
x += visualSlots[i].Rect.Width + Spacing;
}
}
int handSlotX = x - visualSlots[0].Rect.Width - Spacing;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (!HideSlot(i)) continue;
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand)
{
bool rightSlot = SlotTypes[i] == InvSlotType.RightHand;
SlotPositions[i] = new Vector2(rightSlot ? handSlotX : handSlotX - visualSlots[0].Rect.Width - Spacing, personalSlotY);
continue;
}
if (!HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += visualSlots[i].Rect.Width + Spacing;
}
@@ -432,7 +458,7 @@ namespace Barotrauma
int x = startX, y = startY;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
if (SlotTypes[i] == InvSlotType.Card || SlotTypes[i] == InvSlotType.Headset || SlotTypes[i] == InvSlotType.InnerClothes)
{
SlotPositions[i] = new Vector2(x, y);
@@ -444,7 +470,7 @@ namespace Barotrauma
int n = 0;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
if (SlotTypes[i] != InvSlotType.Card && SlotTypes[i] != InvSlotType.Headset && SlotTypes[i] != InvSlotType.InnerClothes)
{
SlotPositions[i] = new Vector2(x, y);
@@ -461,13 +487,23 @@ namespace Barotrauma
}
break;
}
if (character.CharacterHealth?.UseHealthWindow ?? false)
{
Vector2 pos = character.CharacterHealth.InventorySlotContainer.Rect.Location.ToVector2();
for (int i = 0; i < capacity; i++)
{
if (SlotTypes[i] != InvSlotType.HealthInterface) { continue; }
SlotPositions[i] = pos;
pos.Y += visualSlots[i].Rect.Height + Spacing;
}
}
CreateSlots();
if (layout == Layout.Default)
{
HUDLayoutSettings.InventoryTopY = visualSlots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale);
}
}
protected override void ControlInput(Camera cam)
@@ -652,6 +688,11 @@ namespace Barotrauma
{
break;
}
//if putting an item to a container with a max stack size of 1, only put one item from the stack
if (quickUseAction == QuickUseAction.PutToContainer && (character.SelectedConstruction?.GetComponent<ItemContainer>()?.MaxStackSize ?? 0) <= 1)
{
break;
}
}
}
@@ -661,7 +702,7 @@ namespace Barotrauma
if (item != null)
{
var slot = visualSlots[i];
if (item.AllowedSlots.Any(a => a != InvSlotType.Any))
if (item.AllowedSlots.Any(a => a != InvSlotType.Any && a != InvSlotType.HealthInterface))
{
HandleButtonEquipStates(item, slot, deltaTime);
}
@@ -840,7 +881,9 @@ namespace Barotrauma
private QuickUseAction GetQuickUseAction(Item item, bool allowEquip, bool allowInventorySwap, bool allowApplyTreatment)
{
if (allowApplyTreatment && CharacterHealth.OpenHealthWindow != null)
if (allowApplyTreatment && CharacterHealth.OpenHealthWindow != null &&
//if the item can be equipped in the health interface slot, don't use it as a treatment but try to equip it
!item.AllowedSlots.Contains(InvSlotType.HealthInterface))
{
return QuickUseAction.UseTreatment;
}
@@ -1135,7 +1178,7 @@ namespace Barotrauma
for (int i = 0; i < capacity; i++)
{
if (HideSlot(i)) { continue; }
if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
//don't draw the item if it's being dragged out of the slot
bool drawItem = !DraggingItems.Any() || !slots[i].Items.All(it => DraggingItems.Contains(it)) || visualSlots[i].MouseOn();
@@ -1189,7 +1232,7 @@ namespace Barotrauma
highlightedQuickUseSlot = visualSlots[i];
}
if (!slots[i].First().AllowedSlots.Any(a => a == InvSlotType.Any))
if (slots[i].First().AllowedSlots.Count() == 1 || SlotTypes[i] == InvSlotType.HealthInterface)
{
continue;
}

View File

@@ -0,0 +1,61 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
internal partial class EntitySpawnerComponent
{
public Vector2 DrawSize => Vector2.Zero;
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
if (!editing) { return; }
switch (SpawnAreaShape)
{
case AreaShape.Rectangle:
{
RectangleF rect = GetAreaRectangle(SpawnAreaBounds, SpawnAreaOffset, draw: true);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Red, isFilled: false, 0f, 4f);
if (MaximumAmountRangePadding > 0f)
{
rect.Inflate(MaximumAmountRangePadding, MaximumAmountRangePadding);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Red, isFilled: false, 0f, 2f);
}
break;
}
case AreaShape.Circle:
Vector2 center = item.WorldPosition;
center.Y = -center.Y;
center += SpawnAreaOffset;
spriteBatch.DrawCircle(center, SpawnAreaRadius, 32, GUI.Style.Red, thickness: 4f);
if (MaximumAmountRangePadding > 0f)
{
spriteBatch.DrawCircle(center, SpawnAreaRadius + MaximumAmountRangePadding, 32, GUI.Style.Red, thickness: 2f);
}
break;
}
if (!OnlySpawnWhenCrewInRange) { return; }
switch (CrewAreaShape)
{
case AreaShape.Rectangle:
{
RectangleF rect = GetAreaRectangle(CrewAreaBounds, CrewAreaOffset, draw: true);
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Green, isFilled: false, 0f, 4f);
break;
}
case AreaShape.Circle:
Vector2 center = item.WorldPosition;
center.Y = -center.Y;
center += CrewAreaOffset;
spriteBatch.DrawCircle(center, CrewAreaRadius, 32, GUI.Style.Green);
break;
}
}
}
}

View File

@@ -0,0 +1,86 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Linq;
namespace Barotrauma.Items.Components
{
partial class GeneticMaterial : ItemComponent
{
[Serialize(0.0f, false)]
public float TooltipValueMin { get; set; }
[Serialize(0.0f, false)]
public float TooltipValueMax { get; set; }
public override void AddTooltipInfo(ref string name, ref string description)
{
if (!string.IsNullOrEmpty(materialName) && item.ContainedItems.Count() > 0)
{
string mergedMaterialName = materialName;
foreach (Item containedItem in item.ContainedItems)
{
var containedMaterial = containedItem.GetComponent<GeneticMaterial>();
if (containedMaterial == null) { continue; }
mergedMaterialName += ", " + containedMaterial.materialName;
}
name = name.Replace(materialName, mergedMaterialName);
}
if (Tainted)
{
name = TextManager.GetWithVariable("entityname.taintedgeneticmaterial", "[geneticmaterialname]", name);
}
if (TextManager.ContainsTag("entitydescription." + Item.prefab.Identifier))
{
int value = (int)MathHelper.Lerp(TooltipValueMin, TooltipValueMax, item.ConditionPercentage / 100.0f);
description = TextManager.GetWithVariable("entitydescription." + Item.prefab.Identifier, "[value]", value.ToString());
}
foreach (Item containedItem in item.ContainedItems)
{
var containedGeneticMaterial = containedItem.GetComponent<GeneticMaterial>();
if (containedGeneticMaterial == null) { continue; }
string _ = string.Empty;
string containedDescription = containedItem.Description;
containedGeneticMaterial.AddTooltipInfo(ref _, ref containedDescription);
if (!string.IsNullOrEmpty(containedDescription))
{
description += '\n' + containedDescription;
}
}
}
public void ModifyDeconstructInfo(Deconstructor deconstructor, ref string buttonText, ref string infoText)
{
if (deconstructor.InputContainer.Inventory.AllItems.Count() == 2)
{
if (!deconstructor.InputContainer.Inventory.AllItems.All(it => it.prefab == item.prefab))
{
buttonText = TextManager.Get("researchstation.combine");
infoText = TextManager.Get("researchstation.combine.infotext");
}
else
{
buttonText = TextManager.Get("researchstation.refine");
int taintedProbability = (int)(GetTaintedProbabilityOnRefine(Character.Controlled) * 100);
infoText = TextManager.GetWithVariable("researchstation.refine.infotext", "[taintedprobability]", taintedProbability.ToString());
}
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
Tainted = msg.ReadBoolean();
if (Tainted)
{
uint selectedTaintedEffectId = msg.ReadUInt32();
selectedTaintedEffect = AfflictionPrefab.Prefabs.Find(a => a.UIntIdentifier == selectedTaintedEffectId);
}
else
{
uint selectedEffectId = msg.ReadUInt32();
selectedEffect = AfflictionPrefab.Prefabs.Find(a => a.UIntIdentifier == selectedEffectId);
}
}
}
}

View File

@@ -1,13 +1,201 @@
using Microsoft.Xna.Framework;
using System;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.IO;
namespace Barotrauma.Items.Components
{
partial class IdCard
{
public Sprite StoredPortrait;
public Vector2 StoredSheetIndex;
public JobPrefab StoredJobPrefab;
public List<WearableSprite> StoredAttachments;
public struct OwnerAppearance
{
public Sprite Portrait;
public Vector2 SheetIndex;
public JobPrefab JobPrefab;
public List<WearableSprite> Attachments;
public Color HairColor;
public Color FacialHairColor;
public Color SkinColor;
public void ExtractJobPrefab(string[] tags)
{
string jobIdTag = tags.FirstOrDefault(s => s.StartsWith("jobid:"));
if (jobIdTag != null && jobIdTag.Length > 6)
{
string jobId = jobIdTag.Substring(6);
if (jobId != string.Empty)
{
JobPrefab = JobPrefab.Get(jobId);
}
}
}
public void ExtractAppearance(CharacterInfo characterInfo, string[] tags)
{
Gender disguisedGender = Gender.None;
Race disguisedRace = Race.None;
int disguisedHeadSpriteId = -1;
int disguisedHairIndex = -1;
int disguisedBeardIndex = -1;
int disguisedMoustacheIndex = -1;
int disguisedFaceAttachmentIndex = -1;
Color hairColor = Color.Black;
Color facialHairColor = Color.Black;
Color skinColor = Color.Black;
foreach (string tag in tags)
{
string[] s = tag.Split(':');
switch (s[0].ToLowerInvariant())
{
case "haircolor":
hairColor = XMLExtensions.ParseColor(s[1]);
break;
case "facialhaircolor":
facialHairColor = XMLExtensions.ParseColor(s[1]);
break;
case "skincolor":
skinColor = XMLExtensions.ParseColor(s[1]);
break;
case "gender":
Enum.TryParse(s[1], ignoreCase: true, out disguisedGender);
break;
case "race":
Enum.TryParse(s[1], ignoreCase: true, out disguisedRace);
break;
case "headspriteid":
int.TryParse(s[1], NumberStyles.Any, CultureInfo.InvariantCulture, out disguisedHeadSpriteId);
break;
case "hairindex":
disguisedHairIndex = int.Parse(s[1]);
break;
case "beardindex":
disguisedBeardIndex = int.Parse(s[1]);
break;
case "moustacheindex":
disguisedMoustacheIndex = int.Parse(s[1]);
break;
case "faceattachmentindex":
disguisedFaceAttachmentIndex = int.Parse(s[1]);
break;
case "sheetindex":
string[] vectorValues = s[1].Split(";");
SheetIndex = new Vector2(float.Parse(vectorValues[0]), float.Parse(vectorValues[1]));
break;
}
}
if ((characterInfo.HasGenders && disguisedGender == Gender.None)
|| (characterInfo.HasRaces && disguisedRace == Race.None)
|| disguisedHeadSpriteId <= 0)
{
Portrait = null;
Attachments = null;
return;
}
foreach (XElement limbElement in characterInfo.Ragdoll.MainElement.Elements())
{
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
XElement spriteElement = limbElement.Element("sprite");
if (spriteElement == null) { continue; }
string spritePath = spriteElement.Attribute("texture").Value;
spritePath = spritePath.Replace("[GENDER]", disguisedGender.ToString().ToLowerInvariant());
spritePath = spritePath.Replace("[RACE]", disguisedRace.ToString().ToLowerInvariant());
spritePath = spritePath.Replace("[HEADID]", disguisedHeadSpriteId.ToString());
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)))
{
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;
}
break;
}
if (characterInfo.Wearables != null)
{
float baldnessChance = disguisedGender == Gender.Female ? 0.05f : 0.2f;
List<XElement> createElementList(WearableType wearableType, float emptyCommonness = 1.0f)
=> CharacterInfo.AddEmpty(
characterInfo.FilterByTypeAndHeadID(
characterInfo.FilterElementsByGenderAndRace(characterInfo.Wearables, disguisedGender, disguisedRace),
wearableType, disguisedHeadSpriteId),
wearableType, emptyCommonness);
var disguisedHairs = createElementList(WearableType.Hair, baldnessChance);
var disguisedBeards = createElementList(WearableType.Beard);
var disguisedMoustaches = createElementList(WearableType.Moustache);
var disguisedFaceAttachments = createElementList(WearableType.FaceAttachment);
XElement getElementFromList(List<XElement> list, int index)
=> CharacterInfo.IsValidIndex(index, list)
? list[index]
: characterInfo.GetRandomElement(list);
var disguisedHairElement = getElementFromList(disguisedHairs, disguisedHairIndex);
var disguisedBeardElement = getElementFromList(disguisedBeards, disguisedBeardIndex);
var disguisedMoustacheElement = getElementFromList(disguisedMoustaches, disguisedMoustacheIndex);
var disguisedFaceAttachmentElement = getElementFromList(disguisedFaceAttachments, disguisedFaceAttachmentIndex);
Attachments = new List<WearableSprite>();
void loadAttachments(List<WearableSprite> attachments, XElement element, WearableType wearableType)
{
foreach (var s in element?.Elements("sprite") ?? Enumerable.Empty<XElement>())
{
attachments.Add(new WearableSprite(s, wearableType));
}
}
loadAttachments(Attachments, disguisedFaceAttachmentElement, WearableType.FaceAttachment);
loadAttachments(Attachments, disguisedBeardElement, WearableType.Beard);
loadAttachments(Attachments, disguisedMoustacheElement, WearableType.Moustache);
loadAttachments(Attachments, disguisedHairElement, WearableType.Hair);
loadAttachments(Attachments,
characterInfo.OmitJobInPortraitClothing
? JobPrefab.NoJobElement?.Element("PortraitClothing")
: JobPrefab?.ClothingElement,
WearableType.JobIndicator);
}
HairColor = hairColor;
FacialHairColor = facialHairColor;
SkinColor = skinColor;
}
}
public OwnerAppearance StoredOwnerAppearance = default;
}
}

View File

@@ -7,6 +7,8 @@ using System.Collections.Generic;
using Barotrauma.IO;
using System.Text;
using System.Xml.Linq;
using Barotrauma.Sounds;
using System.Linq;
namespace Barotrauma.Items.Components
{
@@ -18,7 +20,11 @@ namespace Barotrauma.Items.Components
protected float currentCrossHairScale, currentCrossHairPointerScale;
private RoundSound chargeSound;
private SoundChannel chargeSoundChannel;
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
[Serialize(1.0f, false, description: "The scale of the crosshair sprite (if there is one).")]
public float CrossHairScale
@@ -48,6 +54,12 @@ namespace Barotrauma.Items.Components
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
break;
case "particleemittercharge":
particleEmitterCharges.Add(new ParticleEmitter(subElement));
break;
case "chargesound":
chargeSound = Submarine.LoadRoundSound(subElement, false);
break;
}
}
}
@@ -84,15 +96,62 @@ namespace Barotrauma.Items.Components
crosshairPointerPos = PlayerInput.MousePosition;
}
partial void UpdateProjSpecific(float deltaTime)
{
float chargeRatio = currentChargeTime / MaxChargeTime;
switch (currentChargingState)
{
case ChargingState.WindingUp:
case ChargingState.WindingDown:
Vector2 particlePos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
float sizeMultiplier = Math.Clamp(chargeRatio, 0.1f, 1f);
foreach (ParticleEmitter emitter in particleEmitterCharges)
{
emitter.Emit(deltaTime, particlePos, hullGuess: null, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
}
if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying)
{
if (chargeSound != null)
{
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling);
if (chargeSoundChannel != null) chargeSoundChannel.Looping = true;
}
}
else if (chargeSoundChannel != null)
{
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
}
break;
default:
if (chargeSoundChannel != null)
{
if (chargeSoundChannel.IsPlaying)
{
chargeSoundChannel.FadeOutAndDispose();
chargeSoundChannel.Looping = false;
}
else
{
chargeSoundChannel = null;
}
}
break;
}
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
//camera focused on some other item/device, don't draw the crosshair
if (character.ViewTarget != null && (character.ViewTarget is Item item) && item.Prefab.FocusOnSelected) { return; }
if (character.ViewTarget != null && (character.ViewTarget is Item viewTargetItem) && viewTargetItem.Prefab.FocusOnSelected) { return; }
//don't draw the crosshair if the item is in some other type of equip slot than hands (e.g. assault rifle in the bag slot)
if (!character.HeldItems.Contains(item)) { return; }
GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
GUI.MouseOn == null && !Inventory.IsMouseOnInventory() && !GameMain.Instance.Paused;
GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;
if (GUI.HideCursor)
{
crosshairSprite?.Draw(spriteBatch, crosshairPos, Color.White, 0, currentCrossHairScale);

View File

@@ -246,6 +246,7 @@ namespace Barotrauma.Items.Components
public void PlaySound(ActionType type, Character user = null)
{
if (!hasSoundsOfType[(int)type]) { return; }
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
if (loopingSound != null)
{
@@ -429,7 +430,7 @@ namespace Barotrauma.Items.Components
}
foreach (ItemComponent component in item.Components)
{
if (component.name.ToLower() == LinkUIToComponent.ToLower())
if (component.name.Equals(LinkUIToComponent, StringComparison.OrdinalIgnoreCase))
{
linkToUIComponent = component;
}
@@ -443,9 +444,9 @@ namespace Barotrauma.Items.Components
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
public virtual void AddToGUIUpdateList()
public virtual void AddToGUIUpdateList(int order = 0)
{
GuiFrame?.AddToGUIUpdateList();
GuiFrame?.AddToGUIUpdateList(order: order);
}
public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { }
@@ -620,6 +621,6 @@ namespace Barotrauma.Items.Components
}
OnResolutionChanged();
}
public virtual void AddTooltipInfo(ref string description) { }
public virtual void AddTooltipInfo(ref string name, ref string description) { }
}
}

View File

@@ -19,6 +19,8 @@ namespace Barotrauma.Items.Components
/// </summary>
private float[] containedSpriteDepths;
private Sprite[] slotIcons;
public Sprite InventoryTopSprite
{
get { return inventoryTopSprite; }
@@ -58,6 +60,9 @@ namespace Barotrauma.Items.Components
[Serialize(null, false)]
public string ContainedStateIndicatorStyle { get; set; }
[Serialize(-1, false, description: "Can be used to make the contained state indicator display the condition of the item in a specific slot even when the container's capacity is more than 1.")]
public int ContainedStateIndicatorSlot { get; set; }
[Serialize(true, false, description: "Should an indicator displaying the state of the contained items be displayed on this item's inventory slot. "+
"If this item can only contain one item, the indicator will display the condition of the contained item, otherwise it will indicate how full the item is.")]
public bool ShowContainedStateIndicator { get; set; }
@@ -85,6 +90,7 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific(XElement element)
{
slotIcons = new Sprite[capacity];
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -104,6 +110,17 @@ namespace Barotrauma.Items.Components
case "containedstateindicatorempty":
ContainedStateIndicatorEmpty = new Sprite(subElement);
break;
case "sloticon":
int index = subElement.GetAttributeInt("slotindex", -1);
Sprite icon = new Sprite(subElement);
for (int i = 0; i < capacity; i++)
{
if (i == index || index == -1)
{
slotIcons[i] = icon;
}
}
break;
}
}
@@ -197,7 +214,7 @@ namespace Barotrauma.Items.Components
if (UILabel == string.Empty) { return string.Empty; }
if (UILabel != null)
{
return TextManager.Get("UILabel." + UILabel);
return TextManager.Get("UILabel." + UILabel, returnNull: true) ?? TextManager.Get(UILabel);
}
else
{
@@ -205,6 +222,12 @@ namespace Barotrauma.Items.Components
}
}
public Sprite GetSlotIcon(int slotIndex)
{
if (slotIndex < 0 || slotIndex >= slotIcons.Length) { return null; }
return slotIcons[slotIndex];
}
public bool KeepOpenWhenEquippedBy(Character character)
{
if (!character.CanAccessInventory(Inventory) ||
@@ -266,7 +289,7 @@ namespace Barotrauma.Items.Components
}
else
{
Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
Matrix transform = Matrix.CreateRotationZ(item.body.DrawRotation);
if (item.body.Dir == -1.0f)
{
transformedItemPos.X = -transformedItemPos.X;
@@ -277,7 +300,7 @@ namespace Barotrauma.Items.Components
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemPos += item.DrawPosition;
transformedItemPos += item.body.DrawPosition;
}
Vector2 currentItemPos = transformedItemPos;
@@ -319,7 +342,7 @@ namespace Barotrauma.Items.Components
new Vector2(currentItemPos.X, -currentItemPos.Y),
isWiringMode ? containedItem.GetSpriteColor() * 0.15f : containedItem.GetSpriteColor(),
origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)),
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation ),
containedItem.Scale,
spriteEffects,
depth: containedSpriteDepth);

View File

@@ -39,7 +39,7 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn && Light.Enabled)
{
Vector2 origin = Light.LightSprite.Origin;
if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }

View File

@@ -14,12 +14,22 @@ namespace Barotrauma.Items.Components
}
private GUIButton activateButton;
private GUIComponent inputInventoryHolder, outputInventoryHolder;
private GUICustomComponent inputInventoryOverlay;
private GUIComponent inSufficientPowerWarning;
private bool pendingState;
private GUITextBlock infoArea;
[Serialize("DeconstructorDeconstruct", true)]
public string ActivateButtonText { get; set; }
[Serialize("", true)]
public string InfoText { get; set; }
[Serialize(0.0f, true)]
public float InfoAreaWidth { get; set; }
partial void InitProjSpecific(XElement element)
{
CreateGUI();
@@ -39,6 +49,12 @@ namespace Barotrauma.Items.Components
RelativeSpacing = 0.08f
};
new GUITextBlock(new RectTransform(new Vector2(1f, 0.07f), paddedFrame.RectTransform), item.Name, font: GUI.SubHeadingFont)
{
TextAlignment = Alignment.Center,
AutoScaleHorizontal = true
};
var topFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), paddedFrame.RectTransform), style: null);
// === INPUT LABEL === //
@@ -55,22 +71,23 @@ namespace Barotrauma.Items.Components
// === INPUT SLOTS === //
inputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(0.7f, 1f), inputArea.RectTransform), style: null);
inputInventoryOverlay = new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawOverLay, null) { CanBeFocused = false };
new GUICustomComponent(new RectTransform(Vector2.One, inputInventoryHolder.RectTransform), DrawOverLay, null) { CanBeFocused = false };
// === ACTIVATE BUTTON === //
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.7f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft);
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterLeft);
activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton")
{
TextBlock = { AutoScaleHorizontal = true },
OnClicked = ToggleActive
};
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform),
TextManager.Get("DeconstructorNoPower"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow")
TextManager.Get("DeconstructorNoPower"), textColor: GUI.Style.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow", wrap: true)
{
HoverColor = Color.Black,
IgnoreLayoutGroups = true,
Visible = false,
CanBeFocused = false
CanBeFocused = false,
AutoScaleHorizontal = true
};
// === OUTPUT AREA === //
@@ -86,8 +103,70 @@ namespace Barotrauma.Items.Components
outputLabel.RectTransform.Resize(new Point((int) outputLabel.Font.MeasureString(outputLabel.Text).X, outputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, outputLabelArea.RectTransform), style: "HorizontalLine");
// === OUTPUT SLOTS === //
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), style: null);
var outputArea = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), bottomFrame.RectTransform, Anchor.CenterLeft), childAnchor: Anchor.BottomLeft, isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f };
// === OUTPUT SLOTS === //
outputInventoryHolder = new GUIFrame(new RectTransform(new Vector2(1f - InfoAreaWidth, 1f), outputArea.RectTransform, Anchor.CenterLeft), style: null);
if (InfoAreaWidth >= 0.0f)
{
var infoAreaContainer = new GUILayoutGroup(new RectTransform(new Vector2(InfoAreaWidth, 0.8f), outputArea.RectTransform), childAnchor: Anchor.CenterLeft);
infoArea = new GUITextBlock(new RectTransform(new Vector2(0.95f, 0.95f), infoAreaContainer.RectTransform), string.Empty, wrap: true);
}
ActivateButton.OnAddedToGUIUpdateList += (GUIComponent component) =>
{
activateButton.Enabled = true;
if (string.IsNullOrEmpty(InfoText))
{
infoArea.Text = string.Empty;
}
else
{
infoArea.Text = TextManager.Get(InfoText, returnNull: true) ?? InfoText;
}
if (IsActive)
{
activateButton.Text = TextManager.Get("DeconstructorCancel");
infoArea.Text = string.Empty;
return;
}
bool outputsFound = false;
foreach (var (inputItem, deconstructItem) in GetAvailableOutputs(checkRequiredOtherItems: true))
{
outputsFound = true;
if (!string.IsNullOrEmpty(deconstructItem.ActivateButtonText))
{
string buttonText = TextManager.Get(deconstructItem.ActivateButtonText, returnNull: true) ?? deconstructItem.ActivateButtonText;
string infoText = string.Empty;
if (!string.IsNullOrEmpty(deconstructItem.InfoText))
{
infoText = TextManager.Get(deconstructItem.InfoText, returnNull: true) ?? deconstructItem.InfoText;
}
inputItem.GetComponent<GeneticMaterial>()?.ModifyDeconstructInfo(this, ref buttonText, ref infoText);
activateButton.Text = buttonText;
if (infoArea != null)
{
infoArea.Text = infoText;
}
return;
}
}
//no valid outputs found: check if we're missing some required items from the input slots and display a message about it if possible
if (!outputsFound && infoArea != null)
{
foreach (var (inputItem, deconstructItem) in GetAvailableOutputs(checkRequiredOtherItems: false))
{
if (deconstructItem.RequiredOtherItem.Any() && !string.IsNullOrEmpty(deconstructItem.InfoTextOnOtherItemMissing))
{
string missingItemName = TextManager.Get("entityname." + deconstructItem.RequiredOtherItem.First(), returnNull: true);
infoArea.Text = TextManager.GetWithVariable(deconstructItem.InfoTextOnOtherItemMissing, "[itemname]", missingItemName);
}
}
}
activateButton.Enabled = outputsFound;
activateButton.Text = TextManager.Get(ActivateButtonText);
};
}
public override bool Select(Character character)
@@ -126,13 +205,30 @@ namespace Barotrauma.Items.Components
private void DrawOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
overlayComponent.RectTransform.SetAsLastChild();
var lastSlot = inputContainer.Inventory.visualSlots.Last();
GUI.DrawRectangle(spriteBatch,
new Rectangle(
lastSlot.Rect.X, lastSlot.Rect.Y + (int)(lastSlot.Rect.Height * (1.0f - progressState)),
lastSlot.Rect.Width, (int)(lastSlot.Rect.Height * progressState)),
GUI.Style.Green * 0.5f, isFilled: true);
if (!(inputContainer?.Inventory?.visualSlots is { } visualSlots)) { return; }
if (DeconstructItemsSimultaneously)
{
for (int i = 0; i < InputContainer.Inventory.Capacity; i++)
{
if (InputContainer.Inventory.GetItemAt(i) == null) { continue; }
DrawProgressBar(InputContainer.Inventory.visualSlots[i]);
}
}
else
{
DrawProgressBar(inputContainer.Inventory.visualSlots.Last());
}
void DrawProgressBar(VisualSlot slot)
{
GUI.DrawRectangle(spriteBatch,
new Rectangle(
slot.Rect.X, slot.Rect.Y + (int)(slot.Rect.Height * (1.0f - progressState)),
slot.Rect.Width, (int)(slot.Rect.Height * progressState)),
GUI.Style.Green * 0.5f, isFilled: true);
}
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)

View File

@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
private FabricationRecipe pendingFabricatedItem;
private Pair<Rectangle, string> tooltip;
private (Rectangle area, string text)? tooltip;
private GUITextBlock requiredTimeBlock;
@@ -255,19 +255,22 @@ namespace Barotrauma.Items.Components
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
bool hasSkills1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f;
bool hasSkills2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f;
int itemPlacement1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f ? 0 : -1;
int itemPlacement2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f ? 0 : -1;
if (hasSkills1 != hasSkills2)
itemPlacement1 += item1.RequiresRecipe && !character.HasRecipeForItem(item1.TargetItem.Identifier) ? -2 : 0;
itemPlacement2 += item2.RequiresRecipe && !character.HasRecipeForItem(item2.TargetItem.Identifier) ? -2 : 0;
if (itemPlacement1 != itemPlacement2)
{
return hasSkills1 ? -1 : 1;
return itemPlacement1 > itemPlacement2 ? -1 : 1;
}
return string.Compare(item1.DisplayName, item2.DisplayName);
});
var sufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorsufficientskills", returnNull: true) ?? "Sufficient skills to fabricate", textColor: GUI.Style.Green, font: GUI.SubHeadingFont)
TextManager.Get("fabricatorsufficientskills"), textColor: GUI.Style.Green, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
@@ -275,7 +278,7 @@ namespace Barotrauma.Items.Components
sufficientSkillsText.RectTransform.SetAsFirstChild();
var insufficientSkillsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorinsufficientskills", returnNull: true) ?? "Insufficient skills to fabricate", textColor: Color.Orange, font: GUI.SubHeadingFont)
TextManager.Get("fabricatorinsufficientskills"), textColor: Color.Orange, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
@@ -285,6 +288,18 @@ namespace Barotrauma.Items.Components
{
insufficientSkillsText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstinSufficient.RectTransform));
}
var requiresRecipeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorrequiresrecipe"), textColor: Color.Red, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
};
var firstRequiresRecipe = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && (fabricableItem.RequiresRecipe && !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier)));
if (firstRequiresRecipe != null)
{
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
}
}
private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
@@ -297,6 +312,7 @@ namespace Barotrauma.Items.Components
int slotIndex = 0;
var missingItems = new List<FabricationRecipe.RequiredItem>();
foreach (FabricationRecipe.RequiredItem requiredItem in targetItem.RequiredItems)
{
for (int i = 0; i < requiredItem.Amount; i++)
@@ -308,6 +324,8 @@ namespace Barotrauma.Items.Components
{
missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefabs.Contains(item.prefab)));
}
var missingCounts = missingItems.GroupBy(missingItem => missingItem).ToDictionary(x => x.Key, x => x.Count());
missingItems = missingItems.Distinct().ToList();
var availableIngredients = GetAvailableIngredients();
@@ -318,30 +336,30 @@ namespace Barotrauma.Items.Components
slotIndex++;
}
//highlight suitable ingredients in linked inventories
foreach (Item item in availableIngredients)
{
if (item.ParentInventory != inputContainer.Inventory && IsItemValidIngredient(item, requiredItem))
{
int availableSlotIndex = item.ParentInventory.FindIndex(item);
//slots are null if the inventory has never been displayed
//(linked item, but the UI is not set to be displayed at the same time)
if (item.ParentInventory.visualSlots != null)
{
if (item.ParentInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
item.ParentInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
requiredItem.ItemPrefabs
.Where(requiredPrefab => availableIngredients.ContainsKey(requiredPrefab.Identifier))
.ForEach(requiredPrefab => {
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
availablePrefabs
.Where(availablePrefab => availablePrefab.ParentInventory != inputContainer.Inventory)
.Where(availablePrefab => availablePrefab.ParentInventory.visualSlots != null) //slots are null if the inventory has never been displayed
.ForEach(availablePrefab => { //(linked item, but the UI is not set to be displayed at the same time)
int availableSlotIndex = availablePrefab.ParentInventory.FindIndex(availablePrefab);
if (availablePrefab.ParentInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
availablePrefab.ParentInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
}
}
}
}
}
}
});
});
if (slotIndex >= inputContainer.Capacity) { break; }
var itemIcon = requiredItem.ItemPrefabs.First().InventoryIcon ?? requiredItem.ItemPrefabs.First().sprite;
Rectangle slotRect = inputContainer.Inventory.visualSlots[slotIndex].Rect;
itemIcon.Draw(
@@ -350,11 +368,32 @@ namespace Barotrauma.Items.Components
color: requiredItem.ItemPrefabs.First().InventoryIconColor * 0.3f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
if (missingCounts[requiredItem] > 1)
{
Vector2 stackCountPos = new Vector2(slotRect.Right, slotRect.Bottom);
string stackCountText = "x" + missingCounts[requiredItem];
stackCountPos -= GUI.SmallFont.MeasureString(stackCountText) + new Vector2(4, 2);
GUI.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos + Vector2.One, Color.Black);
GUI.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, Color.White);
}
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
{
GUI.DrawRectangle(spriteBatch, new Rectangle(slotRect.X, slotRect.Bottom - 8, slotRect.Width, 8), Color.Black * 0.8f, true);
DrawConditionBar(spriteBatch, requiredItem.MinCondition);
}
else if (requiredItem.MaxCondition < 1.0f)
{
DrawConditionBar(spriteBatch, requiredItem.MaxCondition);
}
void DrawConditionBar(SpriteBatch sb, float condition)
{
int spacing = GUI.IntScale(4);
int height = GUI.IntScale(10);
GUI.DrawRectangle(spriteBatch, new Rectangle(slotRect.X + spacing, slotRect.Bottom - spacing - height, slotRect.Width - spacing * 2, height), Color.Black * 0.8f, true);
GUI.DrawRectangle(spriteBatch,
new Rectangle(slotRect.X, slotRect.Bottom - 8, (int)(slotRect.Width * requiredItem.MinCondition), 8),
new Rectangle(slotRect.X + spacing, slotRect.Bottom - spacing - height, (int)((slotRect.Width - spacing * 2) * condition), height),
GUI.Style.Green * 0.8f, true);
}
@@ -367,6 +406,10 @@ namespace Barotrauma.Items.Components
{
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
}
else if(requiredItem.MaxCondition < 1.0f)
{
toolTipText += " 0-" + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
}
else if (requiredItem.MaxCondition <= 0.0f)
{
toolTipText = TextManager.GetWithVariable("displayname.emptyitem", "[itemname]", toolTipText);
@@ -375,7 +418,7 @@ namespace Barotrauma.Items.Components
{
toolTipText += '\n' + requiredItem.ItemPrefabs.First().Description;
}
tooltip = new Pair<Rectangle, string>(slotRect, toolTipText);
tooltip = (slotRect, toolTipText);
}
slotIndex++;
@@ -415,7 +458,7 @@ namespace Barotrauma.Items.Components
if (tooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
GUIComponent.DrawToolTip(spriteBatch, tooltip.Value.text, tooltip.Value.area);
tooltip = null;
}
}
@@ -435,6 +478,22 @@ namespace Barotrauma.Items.Components
if (recipe?.DisplayName == null) { continue; }
child.Visible = recipe.DisplayName.ToLower().Contains(filter);
}
//go through the elements backwards, and disable the labels ("insufficient skills to fabricate", "recipe required...") if there's no items below them
bool recipeVisible = false;
foreach (GUIComponent child in itemList.Content.Children.Reverse())
{
if (!(child.UserData is FabricationRecipe recipe))
{
child.Visible = recipeVisible;
recipeVisible = false;
}
else
{
recipeVisible = child.Visible;
}
}
itemList.UpdateScrollBarSize();
itemList.BarScroll = 0.0f;
@@ -470,14 +529,30 @@ namespace Barotrauma.Items.Components
};
}*/
string itemName = GetRecipeNameAndAmount(selectedItem);
string name = itemName;
float quality = GetFabricatedItemQuality(selectedItem, user);
if (quality > 0)
{
name = TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName + '\n', fallBackTag: "itemname.quality3");
}
var nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
GetRecipeNameAndAmount(selectedItem), textAlignment: Alignment.CenterLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont)
name, textAlignment: Alignment.TopLeft, textColor: Color.Aqua, font: GUI.SubHeadingFont, parseRichText: true)
{
AutoScaleHorizontal = true
};
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, nameBlock.Padding.Z, nameBlock.Padding.W);
nameBlock.Padding = new Vector4(0, nameBlock.Padding.Y, GUI.IntScale(5), nameBlock.Padding.W);
if (nameBlock.TextScale < 0.7f)
{
nameBlock.SetRichText(TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName, fallBackTag: "itemname.quality3"));
nameBlock.AutoScaleHorizontal = false;
nameBlock.TextScale = 0.7f;
nameBlock.Wrap = true;
nameBlock.SetTextPos();
nameBlock.RectTransform.MinSize = new Point(0, (int)(nameBlock.TextSize.Y * nameBlock.TextScale));
}
if (!string.IsNullOrWhiteSpace(selectedItem.TargetItem.Description))
{
@@ -489,6 +564,7 @@ namespace Barotrauma.Items.Components
while (description.Rect.Height + nameBlock.Rect.Height > paddedFrame.Rect.Height)
{
var lines = description.WrappedText.Split('\n');
if (lines.Length <= 1) { break; }
var newString = string.Join('\n', lines.Take(lines.Length - 1));
description.Text = newString.Substring(0, newString.Length - 4) + "...";
description.CalculateHeightFromText();
@@ -598,10 +674,15 @@ namespace Barotrauma.Items.Components
{
foreach (GUIComponent child in itemList.Content.Children)
{
var itemPrefab = child.UserData as FabricationRecipe;
if (itemPrefab == null) continue;
if (!(child.UserData is FabricationRecipe itemPrefab)) { continue; }
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients);
if (itemPrefab != selectedItem &&
(child.Rect.Y > itemList.Rect.Bottom || child.Rect.Bottom < itemList.Rect.Y))
{
continue;
}
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients, character);
if (itemPrefab == selectedItem)
{
activateButton.Enabled = canBeFabricated;

View File

@@ -142,6 +142,7 @@ namespace Barotrauma.Items.Components
partial void UpdateProjSpecific(float deltaTime)
{
float rotationRad = MathHelper.ToRadians(item.Rotation);
if (FlowPercentage < 0.0f)
{
foreach (var (position, emitter) in pumpOutEmitters)
@@ -149,12 +150,13 @@ namespace Barotrauma.Items.Components
if (item.CurrentHull != null && item.CurrentHull.Surface < item.Rect.Location.Y + position.Y) { continue; }
//only emit "pump out" particles when underwater
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
float angle = 0.0f;
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? rotationRad : -rotationRad);
float angle = -rotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;
angle = MathHelper.Pi;
angle += MathHelper.Pi;
}
if (item.FlippedY)
{
@@ -170,11 +172,12 @@ namespace Barotrauma.Items.Components
foreach (var (position, emitter) in pumpInEmitters)
{
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
float angle = 0.0f;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? rotationRad : -rotationRad);
float angle = -rotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;
angle = MathHelper.Pi;
angle += MathHelper.Pi;
}
if (item.FlippedY)
{

View File

@@ -134,6 +134,14 @@ namespace Barotrauma.Items.Components
private static string caveLabel;
[Serialize(false, false)]
public bool RightLayout
{
get;
set;
}
private bool AllowUsingMineralScanner =>
HasMineralScanner && !isConnectedToSteering;
@@ -316,7 +324,7 @@ namespace Barotrauma.Items.Components
"", warningColor, GUI.LargeFont, Alignment.Center);
// Setup layout for nav terminal
if (isConnectedToSteering)
if (isConnectedToSteering || RightLayout)
{
controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
controlContainer.RectTransform.SetPosition(Anchor.TopRight);
@@ -446,13 +454,8 @@ namespace Barotrauma.Items.Components
zoomSlider.BarScroll += PlayerInput.ScrollWheelSpeed / 1000.0f;
zoomSlider.OnMoved(zoomSlider, zoomSlider.BarScroll);
}
if (PlayerInput.KeyHit(InputType.Run))
{
SonarModeSwitch.OnClicked(SonarModeSwitch, null);
}
}
float distort = 1.0f - item.Condition / item.MaxCondition;
for (int i = sonarBlips.Count - 1; i >= 0; i--)
{
@@ -885,7 +888,7 @@ namespace Barotrauma.Items.Components
foreach (AITarget aiTarget in AITarget.List)
{
if (!aiTarget.Enabled) { continue; }
if (aiTarget.InDetectable) { continue; }
if (string.IsNullOrEmpty(aiTarget.SonarLabel) || aiTarget.SoundRange <= 0.0f) { continue; }
if (Vector2.DistanceSquared(aiTarget.WorldPosition, transducerCenter) < aiTarget.SoundRange * aiTarget.SoundRange)
@@ -1239,7 +1242,7 @@ namespace Barotrauma.Items.Components
foreach (AITarget aiTarget in AITarget.List)
{
float disruption = aiTarget.Entity is Character c ? c.Params.SonarDisruption : aiTarget.SonarDisruption;
if (disruption <= 0.0f || !aiTarget.Enabled) { continue; }
if (disruption <= 0.0f || aiTarget.InDetectable) { continue; }
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
if (distSqr > worldPingRadiusSqr) { continue; }
float disruptionDist = (float)Math.Sqrt(distSqr);
@@ -1364,28 +1367,6 @@ namespace Barotrauma.Items.Components
blipType : cell.IsDestructible ? BlipType.Destructible : BlipType.Default);
}
}
foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins)
{
if (!MathUtils.CircleIntersectsRectangle(pingSource, range, ruin.Area)) continue;
foreach (var ruinShape in ruin.RuinShapes)
{
foreach (RuinGeneration.Line wall in ruinShape.Walls)
{
float cellDot = Vector2.Dot(
Vector2.Normalize(ruinShape.Center - pingSource),
Vector2.Normalize((wall.A + wall.B) / 2.0f - ruinShape.Center));
if (cellDot > 0) continue;
CreateBlipsForLine(
wall.A, wall.B,
pingSource, transducerPos,
pingRadius, prevPingRadius,
100.0f, 1000.0f, range, pingStrength, passive);
}
}
}
}
foreach (Item item in Item.ItemList)
@@ -1634,7 +1615,7 @@ namespace Barotrauma.Items.Components
void CalculateDistance()
{
pathFinder ??= new PathFinder(WayPoint.WayPointList, indoorsSteering: false);
pathFinder ??= new PathFinder(WayPoint.WayPointList, false);
var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(transducerPosition), ConvertUnits.ToSimUnits(worldPosition));
if (!path.Unreachable)
{

View File

@@ -888,6 +888,7 @@ namespace Barotrauma.Items.Components
maintainPosOriginIndicator?.Remove();
steeringIndicator?.Remove();
enterOutpostPrompt?.Close();
pathFinder = null;
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)

View File

@@ -71,7 +71,7 @@ namespace Barotrauma.Items.Components
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), textArea.RectTransform, Anchor.CenterRight),
"", textColor: GUI.Style.TextColor, font: GUI.Font, textAlignment: Alignment.CenterRight)
{
TextGetter = () => $"{(int)charge}/{(int)capacity} {kWmin} ({((int)MathUtils.Percentage(charge, capacity)).ToString()} %)"
TextGetter = () => $"{(int)Math.Round(charge)}/{(int)capacity} {kWmin} ({(int)Math.Round(MathUtils.Percentage(charge, capacity))} %)"
};
if (chargeText.TextSize.X > chargeText.Rect.Width) { chargeText.Font = GUI.SmallFont; }

View File

@@ -0,0 +1,21 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Quality : ItemComponent
{
public override void AddTooltipInfo(ref string name, ref string description)
{
foreach (var statValue in statValues)
{
int roundedValue = (int)Math.Round(statValue.Value * qualityLevel * 100);
if (roundedValue == 0) { return; }
string colorStr = XMLExtensions.ColorToString(GUI.Style.Green);
description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("+0;-#")}%‖color:end‖ {TextManager.Get("qualitystattypenames." + statValue.Key.ToString(), true) ?? statValue.Key.ToString()}";
}
}
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
{
partial class RemoteController : ItemComponent
{
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
currentTarget?.DrawHUD(spriteBatch, Screen.Selected.Cam, character);
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
currentTarget?.UpdateHUD(cam, character,deltaTime);
}
public override void AddToGUIUpdateList(int order = 0)
{
currentTarget?.AddToGUIUpdateList(order: -1);
}
}
}

View File

@@ -15,16 +15,23 @@ namespace Barotrauma.Items.Components
public GUIButton SabotageButton { get; private set; }
public GUIButton TinkerButton { get; private set; }
private GUIProgressBar progressBar;
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private GUITextBlock progressBarOverlayText;
private GUILayoutGroup extraButtonContainer;
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
//the corresponding particle emitter is active when the condition is within this range
private List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
private readonly List<Vector2> particleEmitterConditionRanges = new List<Vector2>();
private SoundChannel repairSoundChannel;
private string repairButtonText, repairingText;
private string sabotageButtonText, sabotagingText;
private string tinkerButtonText, tinkeringText;
private FixActions requestStartFixAction;
@@ -45,8 +52,24 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character)
{
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false;
return item.ConditionPercentage < RepairThreshold || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition)));
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) { return false; }
if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; }
float maxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(character);
if (item.Condition / maxRepairConditionMultiplier < RepairThreshold) { return true; }
if (CurrentFixer == character)
{
float condition = item.Condition / item.MaxRepairConditionMultiplier;
float maxCondition = item.MaxCondition / item.MaxRepairConditionMultiplier;
if (condition < maxCondition * maxRepairConditionMultiplier)
{
return true;
}
}
if (IsTinkerable(character)) { return true; }
return false;
}
partial void InitProjSpecific(XElement element)
@@ -85,7 +108,7 @@ namespace Barotrauma.Items.Components
}
}
private void CreateGUI()
protected override void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.75f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
@@ -120,6 +143,11 @@ namespace Barotrauma.Items.Components
progressBar = new GUIProgressBar(new RectTransform(new Vector2(0.6f, 1.0f), progressBarHolder.RectTransform),
color: GUI.Style.Green, barSize: 0.0f, style: "DeviceProgressBar");
progressBarOverlayText = new GUITextBlock(new RectTransform(Vector2.One, progressBar.RectTransform), string.Empty, font: GUI.SubHeadingFont, textAlignment: Alignment.Center)
{
IgnoreLayoutGroups = true
};
repairButtonText = TextManager.Get("RepairButton");
repairingText = TextManager.Get("Repairing");
RepairButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), progressBarHolder.RectTransform, Anchor.TopCenter), repairButtonText)
@@ -135,9 +163,16 @@ namespace Barotrauma.Items.Components
progressBarHolder.RectTransform.MinSize = RepairButton.RectTransform.MinSize;
RepairButton.RectTransform.MinSize = new Point((int)(RepairButton.TextBlock.TextSize.X * 1.2f), RepairButton.RectTransform.MinSize.Y);
extraButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform), isHorizontal: true)
{
IgnoreLayoutGroups = true,
Stretch = true,
AbsoluteSpacing = GUI.IntScale(5)
};
sabotageButtonText = TextManager.Get("SabotageButton");
sabotagingText = TextManager.Get("Sabotaging");
SabotageButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.BottomCenter), sabotageButtonText, style: "GUIButtonSmall")
SabotageButton = new GUIButton(new RectTransform(Vector2.One, extraButtonContainer.RectTransform), sabotageButtonText, style: "GUIButtonSmall")
{
IgnoreLayoutGroups = true,
Visible = false,
@@ -148,6 +183,22 @@ namespace Barotrauma.Items.Components
return true;
}
};
tinkerButtonText = TextManager.Get("TinkerButton", returnNull: true) ?? "Tinker";
tinkeringText = TextManager.Get("Tinkering", returnNull: true) ?? "Tinkering";
TinkerButton = new GUIButton(new RectTransform(Vector2.One, extraButtonContainer.RectTransform), tinkerButtonText, style: "GUIButtonSmall")
{
IgnoreLayoutGroups = true,
Visible = false,
OnClicked = (btn, obj) =>
{
requestStartFixAction = FixActions.Tinker;
item.CreateClientEvent(this);
return true;
}
};
extraButtonContainer.RectTransform.MinSize = new Point(0, SabotageButton.RectTransform.MinSize.Y);
}
partial void UpdateProjSpecific(float deltaTime)
@@ -176,6 +227,7 @@ namespace Barotrauma.Items.Components
{
case FixActions.Repair:
case FixActions.Sabotage:
case FixActions.Tinker:
StartRepairing(Character.Controlled, requestStartFixAction);
requestStartFixAction = FixActions.None;
break;
@@ -211,10 +263,24 @@ namespace Barotrauma.Items.Components
{
IsActive = true;
progressBar.BarSize = item.Condition / item.MaxCondition;
float defaultMaxCondition = (item.MaxCondition / item.MaxRepairConditionMultiplier);
progressBar.BarSize = item.Condition / defaultMaxCondition;
progressBar.Color = ToolBox.GradientLerp(progressBar.BarSize, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green);
RepairButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Repair)) && !item.IsFullCondition;
if (item.Condition > defaultMaxCondition)
{
float extraCondition = item.MaxCondition * (item.MaxRepairConditionMultiplier - 1.0f);
progressBar.Color = ToolBox.GradientLerp((item.Condition - defaultMaxCondition) / extraCondition, GUI.Style.ColorReputationHigh, GUI.Style.ColorReputationVeryHigh);
progressBarOverlayText.Visible = true;
progressBarOverlayText.Text = $"{(int)Math.Round((item.Condition / defaultMaxCondition) * 100)}%";
}
else
{
progressBarOverlayText.Visible = false;
}
RepairButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Repair)) && !item.IsFullCondition && item.ConditionPercentage < RepairThreshold;
RepairButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Repair) ?
repairButtonText :
repairingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
@@ -226,7 +292,18 @@ namespace Barotrauma.Items.Components
sabotageButtonText :
sabotagingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
TinkerButton.Visible = IsTinkerable(character);
TinkerButton.IgnoreLayoutGroups = !TinkerButton.Visible;
TinkerButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Tinker)) && CanTinker(character);
TinkerButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Tinker) ?
tinkerButtonText :
tinkeringText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
extraButtonContainer.Visible = SabotageButton.Visible || TinkerButton.Visible;
extraButtonContainer.IgnoreLayoutGroups = !extraButtonContainer.Visible;
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{
if (!(c.UserData is Skill skill)) continue;
@@ -278,9 +355,12 @@ namespace Barotrauma.Items.Components
deteriorationTimer = msg.ReadSingle();
deteriorateAlwaysResetTimer = msg.ReadSingle();
DeteriorateAlways = msg.ReadBoolean();
tinkeringDuration = msg.ReadSingle();
tinkeringStrength = msg.ReadSingle();
ushort currentFixerID = msg.ReadUInt16();
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;
item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer);
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)

View File

@@ -31,6 +31,9 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize("0.5,0.5)", false)]
public Vector2 Origin { get; set; } = new Vector2(0.5f, 0.5f);
public Vector2 DrawSize
{
get
@@ -57,7 +60,6 @@ namespace Barotrauma.Items.Components
sourcePos = sourceLimb.body.DrawPosition;
}
return sourcePos;
}
partial void InitProjSpecific(XElement element)
@@ -81,13 +83,15 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
if (target == null) { return; }
if (target == null || target.Removed) { return; }
if (target.ParentInventory != null) { return; }
Vector2 startPos = GetSourcePos();
startPos.Y = -startPos.Y;
if (source is Item sourceItem)
{
var turret = sourceItem?.GetComponent<Turret>();
var turret = sourceItem.GetComponent<Turret>();
var weapon = sourceItem.GetComponent<RangedWeapon>();
if (turret != null)
{
startPos = new Vector2(sourceItem.WorldRect.X + turret.TransformedBarrelPos.X, -(sourceItem.WorldRect.Y - turret.TransformedBarrelPos.Y));
@@ -96,8 +100,21 @@ namespace Barotrauma.Items.Components
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;
}
}
else if (weapon != null)
{
Vector2 barrelPos = FarseerPhysics.ConvertUnits.ToDisplayUnits(weapon.TransformedBarrelPos);
barrelPos.Y = -barrelPos.Y;
startPos += barrelPos;
}
}
Vector2 endPos = new Vector2(target.DrawPosition.X, -target.DrawPosition.Y);
Vector2 endPos = new Vector2(target.DrawPosition.X, target.DrawPosition.Y);
Vector2 flippedPos = target.Sprite.size * target.Scale * (Origin - new Vector2(0.5f));
if (target.body.Dir < 0.0f)
{
flippedPos.X = -flippedPos.X;
}
endPos += Vector2.Transform(flippedPos, Matrix.CreateRotationZ(target.body.Rotation));
endPos.Y = -endPos.Y;
if (Snapped)
{

View File

@@ -0,0 +1,29 @@
using Barotrauma.Networking;
namespace Barotrauma.Items.Components
{
partial class Scanner : ItemComponent, IServerSerializable
{
partial void UpdateProjSpecific()
{
if (Holdable != null && Holdable.Attached && (AlwaysDisplayProgressBar || DisplayProgressBar) && !IsScanCompleted)
{
Character.Controlled?.UpdateHUDProgressBar(this,
item.WorldPosition,
ScanTimer / ScanDuration,
GUI.Style.Red, GUI.Style.Green,
textTag: "progressbar.scanning");
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
bool wasScanCompletedPreviously = IsScanCompleted;
scanTimer = msg.ReadSingle();
if (!wasScanCompletedPreviously && IsScanCompleted)
{
OnScanCompleted?.Invoke(this);
}
}
}
}

View File

@@ -0,0 +1,115 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class ButtonTerminal : ItemComponent, IClientSerializable, IServerSerializable
{
private string[] terminalButtonStyles;
private GUIFrame containerHolder;
private GUIImage containerIndicator;
private GUIComponentStyle indicatorStyleRed, indicatorStyleGreen;
partial void InitProjSpecific(XElement element)
{
terminalButtonStyles = new string[RequiredSignalCount];
int i = 0;
foreach (var childElement in element.GetChildElements("TerminalButton"))
{
string style = childElement.GetAttributeString("style", null);
if (style == null) { continue; }
terminalButtonStyles[i++] = style;
}
indicatorStyleRed = GUI.Style.GetComponentStyle("IndicatorLightRed");
indicatorStyleGreen = GUI.Style.GetComponentStyle("IndicatorLightGreen");
CreateGUI();
}
protected override void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), GuiFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.08f
};
paddedFrame.OnAddedToGUIUpdateList += (component) =>
{
bool buttonsEnabled = AllowUsingButtons;
foreach (var child in component.Children)
{
if (!(child is GUIButton)) { continue; }
if (!(child.UserData is int)) { continue; }
child.Enabled = buttonsEnabled;
child.Children.ForEach(c => c.Enabled = buttonsEnabled);
}
bool itemsContained = Container.Inventory.AllItems.Any();
if (itemsContained)
{
var indicatorStyle = buttonsEnabled ? indicatorStyleGreen : indicatorStyleRed;
if (containerIndicator.Style != indicatorStyle)
{
containerIndicator.ApplyStyle(indicatorStyle);
}
}
containerIndicator.OverrideState = itemsContained ? GUIComponent.ComponentState.Selected : GUIComponent.ComponentState.None;
};
float x = 1.0f / (1 + RequiredSignalCount);
float y = Math.Min((x * paddedFrame.Rect.Width) / paddedFrame.Rect.Height, 0.5f);
Vector2 relativeSize = new Vector2(x, y);
var containerSection = new GUIFrame(new RectTransform(new Vector2(x, 1.0f), paddedFrame.RectTransform), style: null);
var containerSlot = new GUIFrame(new RectTransform(new Vector2(1.0f, y), containerSection.RectTransform, anchor: Anchor.Center), style: null);
containerHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.2f), containerSlot.RectTransform, Anchor.BottomCenter), style: null);
containerIndicator = new GUIImage(new RectTransform(new Vector2(0.5f, 0.5f * (1.0f - y)), containerSection.RectTransform, anchor: Anchor.BottomCenter),
style: "IndicatorLightRed", scaleToFit: true);
for (int i = 0; i < RequiredSignalCount; i++)
{
var button = new GUIButton(new RectTransform(relativeSize, paddedFrame.RectTransform), style: null)
{
UserData = i,
OnClicked = (button, userData) =>
{
if (GameMain.IsSingleplayer)
{
SendSignal((int)userData);
}
else
{
item.CreateClientEvent(this, new object[] { userData });
}
return true;
}
};
var image = new GUIImage(new RectTransform(Vector2.One, button.RectTransform), terminalButtonStyles[i], scaleToFit: true);
}
}
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
OnItemLoadedProjSpecific();
}
partial void OnItemLoadedProjSpecific()
{
Container.AllowUIOverlap = true;
Container.Inventory.RectTransform = containerHolder.RectTransform;
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
Write(msg, extraData);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), isServerMessage: true);
}
}
}

View File

@@ -54,6 +54,11 @@ namespace Barotrauma.Items.Components
if (wireComponent != null)
{
equippedWire = wireComponent;
var connectedEnd = equippedWire.OtherConnection(null);
if (connectedEnd?.Item.Submarine != null && panel.Item.Submarine != connectedEnd.Item.Submarine)
{
equippedWire = null;
}
}
}
}

View File

@@ -88,11 +88,6 @@ namespace Barotrauma.Items.Components
return character == Character.Controlled && character == user && character.SelectedConstruction == item;
}
public override void AddToGUIUpdateList()
{
GuiFrame?.AddToGUIUpdateList();
}
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
if (character != Character.Controlled || character != user || character.SelectedConstruction != item) { return; }

View File

@@ -63,11 +63,11 @@ namespace Barotrauma.Items.Components
}
OutputValue = input;
ShowOnDisplay(input);
ShowOnDisplay(input, addToHistory: true);
item.SendSignal(input, "signal_out");
}
partial void ShowOnDisplay(string input, bool addToHistory = true)
partial void ShowOnDisplay(string input, bool addToHistory)
{
if (addToHistory)
{
@@ -85,7 +85,7 @@ namespace Barotrauma.Items.Components
GUITextBlock newBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter),
"> " + input,
textColor: Color.LimeGreen, wrap: true)
textColor: Color.LimeGreen, wrap: true, font: UseMonospaceFont ? GUI.MonospacedFont : GUI.GlobalFont)
{
CanBeFocused = false
};
@@ -118,9 +118,9 @@ namespace Barotrauma.Items.Components
// This method is overrided instead of the UpdateHUD method because this ensures the input box is selected
// even when the terminal component is selected for the very first time. Doing the input box selection in the
// UpdateHUD method only selects the input box on every terminal selection except for the very first time.
public override void AddToGUIUpdateList()
public override void AddToGUIUpdateList(int order = 0)
{
base.AddToGUIUpdateList();
base.AddToGUIUpdateList(order: order);
if (shouldSelectInputBox)
{
inputBox.Select();

View File

@@ -39,6 +39,34 @@ namespace Barotrauma.Items.Components
private set;
}
[Serialize(false, false)]
public bool ThermalGoggles
{
get;
private set;
}
[Serialize(true, false)]
public bool ShowDeadCharacters
{
get;
private set;
}
[Serialize(true, false)]
public bool ShowTexts
{
get;
private set;
}
[Serialize("72,119,72,120", false)]
public Color OverlayColor
{
get;
private set;
}
private readonly List<Character> visibleCharacters = new List<Character>();
private const float UpdateInterval = 0.5f;
@@ -48,6 +76,8 @@ namespace Barotrauma.Items.Components
private bool isEquippable;
private float thermalEffectState;
public IEnumerable<Character> VisibleCharacters
{
get
@@ -80,7 +110,10 @@ namespace Barotrauma.Items.Components
{
refEntity = item;
}
thermalEffectState += deltaTime;
thermalEffectState %= 10000.0f;
if (updateTimer > 0.0f)
{
updateTimer -= deltaTime;
@@ -91,6 +124,7 @@ namespace Barotrauma.Items.Components
foreach (Character c in Character.CharacterList)
{
if (c == equipper || !c.Enabled || c.Removed) { continue; }
if (!ShowDeadCharacters && c.IsDead) { continue; }
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
if (dist < Range * Range)
@@ -123,27 +157,71 @@ namespace Barotrauma.Items.Components
{
if (character == null) { return; }
GUI.UIGlow.Draw(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight),
Color.LightGreen * 0.5f);
Character closestCharacter = null;
float closestDist = float.PositiveInfinity;
foreach (Character c in visibleCharacters)
if (OverlayColor.A > 0)
{
if (c == character || !c.Enabled || c.Removed) { continue; }
float dist = Vector2.DistanceSquared(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), c.WorldPosition);
if (dist < closestDist)
{
closestCharacter = c;
closestDist = dist;
}
GUI.UIGlow.Draw(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), OverlayColor);
}
if (closestCharacter != null)
if (ShowTexts)
{
float dist = Vector2.Distance(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), closestCharacter.WorldPosition);
DrawCharacterInfo(spriteBatch, closestCharacter, 1.0f - MathHelper.Max((dist - (Range - FadeOutRange)) / FadeOutRange, 0.0f));
Character closestCharacter = null;
float closestDist = float.PositiveInfinity;
foreach (Character c in visibleCharacters)
{
if (c == character || !c.Enabled || c.Removed) { continue; }
float dist = Vector2.DistanceSquared(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), c.WorldPosition);
if (dist < closestDist)
{
closestCharacter = c;
closestDist = dist;
}
}
if (closestCharacter != null)
{
float dist = Vector2.Distance(GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), closestCharacter.WorldPosition);
DrawCharacterInfo(spriteBatch, closestCharacter, 1.0f - MathHelper.Max((dist - (Range - FadeOutRange)) / FadeOutRange, 0.0f));
}
}
if (ThermalGoggles)
{
spriteBatch.End();
GameMain.LightManager.SolidColorEffect.Parameters["color"].SetValue(Color.Red.ToVector4() * (0.3f + MathF.Sin(thermalEffectState) * 0.05f));
GameMain.LightManager.SolidColorEffect.CurrentTechnique = GameMain.LightManager.SolidColorEffect.Techniques["SolidColorBlur"];
GameMain.LightManager.SolidColorEffect.Parameters["blurDistance"].SetValue(0.01f + MathF.Sin(thermalEffectState) * 0.005f);
GameMain.LightManager.SolidColorEffect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: Screen.Selected.Cam.Transform, effect: GameMain.LightManager.SolidColorEffect);
Entity refEntity = equipper;
if (!isEquippable || refEntity == null)
{
refEntity = item;
}
foreach (Character c in Character.CharacterList)
{
if (c == character || !c.Enabled || c.Removed || c.Params.HideInThermalGoggles) { continue; }
if (!ShowDeadCharacters && c.IsDead) { continue; }
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
if (dist > Range * Range) { continue; }
Sprite pingCircle = GUI.Style.UIThermalGlow.Sprite;
foreach (Limb limb in c.AnimController.Limbs)
{
if (limb.Mass < 1.0f) { continue; }
float noise1 = PerlinNoise.GetPerlin((thermalEffectState + limb.Params.ID + c.ID) * 0.01f, (thermalEffectState + limb.Params.ID + c.ID) * 0.02f);
float noise2 = PerlinNoise.GetPerlin((thermalEffectState + limb.Params.ID + c.ID) * 0.01f, (thermalEffectState + limb.Params.ID + c.ID) * 0.008f);
Vector2 spriteScale = ConvertUnits.ToDisplayUnits(limb.body.GetSize()) / pingCircle.size * (noise1 * 0.5f + 2f);
Vector2 drawPos = new Vector2(limb.body.DrawPosition.X + (noise1 - 0.5f) * 100, -limb.body.DrawPosition.Y + (noise2 - 0.5f) * 100);
pingCircle.Draw(spriteBatch, drawPos, 0.0f, scale: Math.Max(spriteScale.X, spriteScale.Y));
}
}
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
}
}

View File

@@ -177,6 +177,10 @@ namespace Barotrauma.Items.Components
partial void LaunchProjSpecific()
{
recoilTimer = RetractionTime;
if (user != null)
{
recoilTimer /= 1 + user.GetStatValue(StatTypes.TurretAttackSpeed);
}
PlaySound(ActionType.OnUse);
Vector2 particlePos = GetRelativeFiringPosition(UseFiringOffsetForMuzzleFlash);
foreach (ParticleEmitter emitter in particleEmitters)
@@ -534,20 +538,20 @@ namespace Barotrauma.Items.Components
minRotationWidget.Draw(spriteBatch, (float)Timing.Step);
maxRotationWidget.Draw(spriteBatch, (float)Timing.Step);
Vector2 GetDrawPos()
{
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null) { drawPos += item.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
return drawPos;
}
void UpdateBarrel()
{
rotation = (minRotation + maxRotation) / 2;
}
}
public Vector2 GetDrawPos()
{
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null) { drawPos += item.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
return drawPos;
}
private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, float thickness = 1f, Action<Widget> initMethod = null)
{
Vector2 offset = new Vector2(size / 2 + 5, -10);

View File

@@ -5,17 +5,17 @@ namespace Barotrauma.Items.Components
{
partial class Wearable
{
private void GetDamageModifierText(ref string description, float damageMultiplier, string afflictionIdentifier)
private void GetDamageModifierText(ref string description, DamageModifier damageModifier, string afflictionIdentifier)
{
int roundedValue = (int)Math.Round((1 - damageMultiplier) * 100);
int roundedValue = (int)Math.Round((1 - damageModifier.DamageMultiplier * damageModifier.ProbabilityMultiplier) * 100);
if (roundedValue == 0) { return; }
string colorStr = XMLExtensions.ColorToString(GUI.Style.Green);
description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, StringComparison.OrdinalIgnoreCase))?.Name ?? afflictionIdentifier}";
}
public override void AddTooltipInfo(ref string description)
public override void AddTooltipInfo(ref string name, ref string description)
{
if (damageModifiers.Any(d => !MathUtils.NearlyEqual(d.DamageMultiplier, 1f)) || SkillModifiers.Any())
if (damageModifiers.Any(d => !MathUtils.NearlyEqual(d.DamageMultiplier, 1f) || !MathUtils.NearlyEqual(d.ProbabilityMultiplier, 1f)) || SkillModifiers.Any())
{
description += "\n";
}
@@ -31,11 +31,11 @@ namespace Barotrauma.Items.Components
foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionIdentifiers)
{
GetDamageModifierText(ref description, damageModifier.DamageMultiplier, afflictionIdentifier);
GetDamageModifierText(ref description, damageModifier, afflictionIdentifier);
}
foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionTypes)
{
GetDamageModifierText(ref description, damageModifier.DamageMultiplier, afflictionIdentifier);
GetDamageModifierText(ref description, damageModifier, afflictionIdentifier);
}
}
}

View File

@@ -297,9 +297,10 @@ namespace Barotrauma
}
}
string name = item.Name;
foreach (ItemComponent component in item.Components)
{
component.AddTooltipInfo(ref description);
component.AddTooltipInfo(ref name, ref description);
}
if (item.Prefab.ShowContentsInTooltip && item.OwnInventory != null)
@@ -315,12 +316,18 @@ namespace Barotrauma
string colorStr = XMLExtensions.ColorToString(!item.AllowStealing ? GUI.Style.Red : Color.White);
toolTip = $"‖color:{colorStr}‖{item.Name}‖color:end‖";
toolTip = $"‖color:{colorStr}‖{name}‖color:end‖";
if (item.GetComponent<Quality>() != null)
{
// substring by to get rid of the empty space at start, text file should be adjusted
toolTip += $"\n{TextManager.GetWithVariable("itemname.quality" + item.Quality, "[itemname]", "", fallBackTag: "itemname.quality3")?.Substring(1)}";
}
if (itemsInSlot.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))
{
toolTip += " " + TextManager.Get("connectionlocked");
}
if (!item.IsFullCondition && !item.Prefab.HideConditionBar)
if (!item.IsFullCondition && !item.Prefab.HideConditionInTooltip)
{
string conditionColorStr = XMLExtensions.ColorToString(ToolBox.GradientLerp(item.Condition / item.MaxCondition, GUI.Style.ColorInventoryEmpty, GUI.Style.ColorInventoryHalf, GUI.Style.ColorInventoryFull));
toolTip += $"‖color:{conditionColorStr}‖ ({(int)item.ConditionPercentage} %)‖color:end‖";
@@ -478,7 +485,7 @@ namespace Barotrauma
}
if (container == null) { return false; }
return owner.SelectedCharacter != null|| (!(owner is Character character)) || !container.KeepOpenWhenEquippedBy(character) || !owner.HasEquippedItem(container.Item);
return owner.SelectedCharacter != null|| (!(owner is Character character)) || !container.KeepOpenWhenEquippedBy(character) || !owner.HasEquippedItem(container.Item);
}
protected virtual bool HideSlot(int i)
@@ -594,7 +601,10 @@ namespace Barotrauma
{
var slotRef = new SlotReference(this, slot, slotIndex, isSubSlot, slots[slotIndex].FirstOrDefault()?.GetComponent<ItemContainer>()?.Inventory);
if (Screen.Selected is SubEditorScreen editor && !editor.WiringMode && slotRef.ParentInventory is CharacterInventory) { return; }
selectedSlot = slotRef;
if (CanSelectSlot(slotRef))
{
selectedSlot = slotRef;
}
}
if (!DraggingItems.Any())
@@ -667,6 +677,10 @@ namespace Barotrauma
if (subInventory.visualSlots == null) { subInventory.CreateSlots(); }
canMove = container.MovableFrame && !subInventory.IsInventoryHoverAvailable(Owner as Character, container) && subInventory.originalPos != Point.Zero;
if (this is CharacterInventory characterInventory && characterInventory.CurrentLayout != CharacterInventory.Layout.Default)
{
canMove = false;
}
if (canMove)
{
@@ -715,7 +729,7 @@ namespace Barotrauma
spacing = new Vector2(10 * UIScale, (10 + UnequippedIndicator.size.Y) * UIScale);
}
int columns = (int)Math.Max(Math.Floor(Math.Sqrt(itemCapacity)), 1);
int columns = MathHelper.Clamp((int)Math.Floor(Math.Sqrt(itemCapacity)), 1, container.SlotsPerRow);
while (itemCapacity / columns * (subRect.Height + spacing.Y) > GameMain.GraphicsHeight * 0.5f)
{
columns++;
@@ -826,11 +840,23 @@ namespace Barotrauma
return rect.Contains(PlayerInput.MousePosition);
}
public static bool IsMouseOnInventory
{
get; private set;
}
/// <summary>
/// Refresh the value of IsMouseOnInventory
/// </summary>
public static void RefreshMouseOnInventory()
{
IsMouseOnInventory = DetermineMouseOnInventory();
}
/// <summary>
/// Is the mouse on any inventory element (slot, equip button, subinventory...)
/// </summary>
/// <returns></returns>
public static bool IsMouseOnInventory(bool ignoreDraggedItem = false)
private static bool DetermineMouseOnInventory(bool ignoreDraggedItem = false)
{
if (GameMain.GameSession?.Campaign != null &&
(GameMain.GameSession.Campaign.ShowCampaignUI || GameMain.GameSession.Campaign.ForceMapUI))
@@ -1112,7 +1138,7 @@ namespace Barotrauma
{
Character.Controlled.ClearInputs();
if (!IsMouseOnInventory(ignoreDraggedItem: true) &&
if (!DetermineMouseOnInventory(ignoreDraggedItem: true) &&
CharacterHealth.OpenHealthWindow != null)
{
bool dropSuccessful = false;
@@ -1279,34 +1305,52 @@ namespace Barotrauma
DraggingItems.Clear();
}
if (selectedSlot != null)
if (selectedSlot != null && !CanSelectSlot(selectedSlot))
{
if (!selectedSlot.Slot.MouseOn())
selectedSlot = null;
}
}
private static bool CanSelectSlot(SlotReference selectedSlot)
{
if (!selectedSlot.Slot.MouseOn())
{
return false;
}
else
{
var rootOwner = (selectedSlot.ParentInventory?.Owner as Item)?.GetRootInventoryOwner();
if (selectedSlot.ParentInventory?.Owner != Character.Controlled &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedCharacter &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(selectedSlot.ParentInventory?.Owner) ?? false) &&
rootOwner != Character.Controlled &&
rootOwner != Character.Controlled.SelectedCharacter &&
rootOwner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(rootOwner) ?? false))
{
selectedSlot = null;
return false;
}
else
var parentItem = (selectedSlot?.ParentInventory?.Owner as Item) ?? selectedSlot?.Item;
if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) &&
ownerCharacter == Character.Controlled &&
CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface))
{
var rootOwner = (selectedSlot.ParentInventory?.Owner as Item)?.GetRootInventoryOwner();
if (selectedSlot.ParentInventory?.Owner != Character.Controlled &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedCharacter &&
selectedSlot.ParentInventory?.Owner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(selectedSlot.ParentInventory?.Owner) ?? false) &&
rootOwner != Character.Controlled &&
rootOwner != Character.Controlled.SelectedCharacter &&
rootOwner != Character.Controlled.SelectedConstruction &&
!(Character.Controlled.SelectedConstruction?.linkedTo.Contains(rootOwner) ?? false))
{
selectedSlot = null;
}
highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem);
return false;
}
}
return true;
}
protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
{
Rectangle hoverArea;
if (!subSlot.Inventory.Movable() || Character.Controlled?.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item))
if (!subSlot.Inventory.Movable() ||
(Character.Controlled?.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item)) ||
(subSlot.ParentInventory is CharacterInventory characterInventory && characterInventory.CurrentLayout != CharacterInventory.Layout.Default))
{
hoverArea = subSlot.Slot.Rect;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();
@@ -1375,7 +1419,7 @@ namespace Barotrauma
float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f);
Vector2 itemPos = PlayerInput.MousePosition;
bool mouseOnHealthInterface = CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement;
bool mouseOnHealthInterface = CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement && DraggingItems.Any(it => it.UseInHealthInterface);
if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null)
{
@@ -1434,7 +1478,8 @@ namespace Barotrauma
}
Color slotColor = Color.White;
if (inventory?.Owner is Item i && !i.IsPlayerTeamInteractable) { slotColor = Color.Gray; }
Item parentItem = inventory?.Owner as Item;
if (parentItem != null && !parentItem.IsPlayerTeamInteractable) { slotColor = Color.Gray; }
var itemContainer = item?.GetComponent<ItemContainer>();
if (itemContainer != null && (itemContainer.InventoryTopSprite != null || itemContainer.InventoryBottomSprite != null))
{
@@ -1513,7 +1558,7 @@ namespace Barotrauma
var indicatorStyle = GUI.Style.GetComponentStyle("ContainedStateIndicator.Default");
Sprite indicatorSprite = indicatorStyle?.GetDefaultSprite();
Sprite emptyIndicatorSprite = indicatorStyle?.GetSprite(GUIComponent.ComponentState.Hover);
DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, conditionIndicatorArea, item.Condition / item.MaxCondition);
DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, conditionIndicatorArea, item.Condition / item.MaxCondition);
}
if (itemContainer != null && itemContainer.ShowContainedStateIndicator)
@@ -1525,14 +1570,14 @@ namespace Barotrauma
}
else
{
var containedItem = itemContainer.Inventory.slots[0].FirstOrDefault();
containedState = itemContainer.Inventory.Capacity == 1 ?
var containedItem = itemContainer.Inventory.slots[Math.Max(itemContainer.ContainedStateIndicatorSlot, 0)].FirstOrDefault();
containedState = itemContainer.Inventory.Capacity == 1 || itemContainer.ContainedStateIndicatorSlot > -1 ?
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
itemContainer.Inventory.slots.Count(i => !i.Empty()) / (float)itemContainer.Inventory.capacity;
if (containedItem != null && itemContainer.Inventory.Capacity == 1)
{
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.MaxStackSize);
if (maxStackSize > 1)
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(0));
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
{
containedState = itemContainer.Inventory.slots[0].ItemCount / (float)maxStackSize;
}
@@ -1556,6 +1601,27 @@ namespace Barotrauma
DrawItemStateIndicator(spriteBatch, inventory, indicatorSprite, emptyIndicatorSprite, containedIndicatorArea, containedState,
pulsate: !usingDefaultSprite && containedState >= 0.0f && containedState < 0.25f && inventory == Character.Controlled?.Inventory && Character.Controlled.HasEquippedItem(item));
}
if (item.Quality != 0)
{
var style = GUI.Style.GetComponentStyle("InnerGlowSmall");
if (style == null)
{
GUI.DrawRectangle(spriteBatch, rect, GUI.Style.GetQualityColor(item.Quality) * 0.7f);
}
else
{
style.Sprites[GUIComponent.ComponentState.None].FirstOrDefault()?.Draw(spriteBatch, rect, GUI.Style.GetQualityColor(item.Quality) * 0.5f);
}
}
}
else
{
var slotIcon = parentItem?.GetComponent<ItemContainer>()?.GetSlotIcon(slotIndex);
if (slotIcon != null)
{
slotIcon.Draw(spriteBatch, rect.Center.ToVector2(), GUI.Style.EquipmentSlotIconColor, scale: Math.Min(rect.Width / slotIcon.size.X, rect.Height / slotIcon.size.Y) * 0.8f);
}
}
}
@@ -1590,7 +1656,7 @@ namespace Barotrauma
Color spriteColor = sprite == item.Sprite ? item.GetSpriteColor() : item.GetInventoryIconColor();
if (inventory != null && (inventory.Locked || inventory.slots[slotIndex].Items.All(it => it.NonInteractable || it.NonPlayerTeamInteractable))) { spriteColor *= 0.5f; }
if (CharacterHealth.OpenHealthWindow != null && !item.UseInHealthInterface)
if (CharacterHealth.OpenHealthWindow != null && !item.UseInHealthInterface && !item.AllowedSlots.Contains(InvSlotType.HealthInterface) && item.GetComponent<GeneticMaterial>() == null)
{
spriteColor = Color.Lerp(spriteColor, Color.TransparentBlack, 0.5f);
}
@@ -1611,9 +1677,9 @@ namespace Barotrauma
scale: iconSize.X / stealIcon.size.X);
}
int maxStackSize = item.Prefab.MaxStackSize;
if (item.Container != null)
if (inventory is ItemInventory itemInventory)
{
maxStackSize = Math.Min(maxStackSize, item.Container.GetComponent<ItemContainer>()?.MaxStackSize ?? maxStackSize);
maxStackSize = Math.Min(maxStackSize, itemInventory.Container.GetMaxStackSize(slotIndex));
}
if (maxStackSize > 1 && inventory != null)
{

View File

@@ -276,7 +276,7 @@ namespace Barotrauma
BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f;
float displayCondition = FakeBroken ? 0.0f : condition;
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
Vector2 drawOffset = Vector2.Zero;
if (displayCondition < MaxCondition)
{
@@ -326,13 +326,18 @@ namespace Barotrauma
size, color: color,
textureScale: Vector2.One * Scale,
depth: depth);
fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
textureScale: Vector2.One * Scale,
depth: depth - 0.000001f);
if (fadeInBrokenSprite != null)
{
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
fadeInBrokenSprite.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
textureScale: Vector2.One * Scale,
depth: d);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flippedX && Prefab.CanSpriteFlipX ? rotationRad : -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.DrawTiled(spriteBatch,
@@ -357,7 +362,11 @@ namespace Barotrauma
if (color.A > 0)
{
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
if (fadeInBrokenSprite != null)
{
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
fadeInBrokenSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, d);
}
}
if (Infector != null && (Infector.ParentBallastFlora.HasBrokenThrough || BallastFloraBehavior.AlwaysShowBallastFloraSprite))
{
@@ -368,7 +377,7 @@ namespace Barotrauma
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flippedX && Prefab.CanSpriteFlipX ? rotationRad : -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
@@ -410,8 +419,11 @@ namespace Barotrauma
}
}
body.Draw(spriteBatch, activeSprite, color, depth, Scale);
if (fadeInBrokenSprite != null) { body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, depth - 0.000001f, Scale); }
if (fadeInBrokenSprite != null)
{
float d = Math.Min(depth + (fadeInBrokenSprite.Sprite.Depth - activeSprite.Depth - 0.000001f), 0.999f);
body.Draw(spriteBatch, fadeInBrokenSprite.Sprite, color * fadeInBrokenSpriteAlpha, d, Scale);
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
@@ -464,6 +476,11 @@ namespace Barotrauma
if (GameMain.DebugDraw)
{
body?.DebugDraw(spriteBatch, Color.White);
if (GetComponent<TriggerComponent>()?.PhysicsBody is PhysicsBody triggerBody)
{
triggerBody.UpdateDrawPosition();
triggerBody.DebugDraw(spriteBatch, Color.White);
}
}
if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space))
@@ -704,6 +721,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
Enabled = Prefab.CanFlipX,
OnClicked = (button, data) =>
{
foreach (MapEntity me in SelectedList)
@@ -717,6 +735,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"), style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
Enabled = Prefab.CanFlipY,
OnClicked = (button, data) =>
{
foreach (MapEntity me in SelectedList)
@@ -1068,7 +1087,7 @@ namespace Barotrauma
foreach (Character otherCharacter in Character.CharacterList)
{
if (otherCharacter != character &&
otherCharacter.SelectedConstruction == character.SelectedConstruction)
otherCharacter.SelectedConstruction == this)
{
ItemInUseWarning.Visible = true;
if (mergedHUDRect.Width > GameMain.GraphicsWidth / 2) { mergedHUDRect.Inflate(-GameMain.GraphicsWidth / 4, 0); }
@@ -1180,7 +1199,7 @@ namespace Barotrauma
return texts;
}
public override void AddToGUIUpdateList()
public override void AddToGUIUpdateList(int order = 0)
{
if (Screen.Selected is SubEditorScreen)
{
@@ -1194,7 +1213,14 @@ namespace Barotrauma
}
}
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this) { return; }
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this && GetComponent<RemoteController>() == null)
{
if (Character.Controlled.SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem != this &&
!Character.Controlled.HeldItems.Any(it => it.GetComponent<RemoteController>()?.TargetItem == this))
{
return;
}
}
bool needsLayoutUpdate = false;
foreach (ItemComponent ic in activeHUDs)
@@ -1205,7 +1231,7 @@ namespace Barotrauma
bool wasUsingAlternativeLayout = ic.UseAlternativeLayout;
ic.UseAlternativeLayout = useAlternativeLayout;
needsLayoutUpdate |= ic.UseAlternativeLayout != wasUsingAlternativeLayout;
ic.AddToGUIUpdateList();
ic.AddToGUIUpdateList(order);
}
if (itemInUseWarning != null && itemInUseWarning.Visible)
@@ -1530,6 +1556,7 @@ namespace Barotrauma
byte bodyType = msg.ReadByte();
bool spawnedInOutpost = msg.ReadBoolean();
bool allowStealing = msg.ReadBoolean();
int quality = msg.ReadRangedInteger(0, Items.Components.Quality.MaxQuality);
byte teamID = msg.ReadByte();
bool tagsChanged = msg.ReadBoolean();
string tags = "";
@@ -1605,7 +1632,8 @@ namespace Barotrauma
item = new Item(itemPrefab, pos, sub, id: itemId)
{
SpawnedInOutpost = spawnedInOutpost,
AllowStealing = allowStealing
AllowStealing = allowStealing,
Quality = quality
};
}
catch (Exception e)
@@ -1623,6 +1651,10 @@ namespace Barotrauma
{
wifiComponent.TeamID = (CharacterTeamType)teamID;
}
foreach (IdCard idCard in item.GetComponents<IdCard>())
{
idCard.TeamID = (CharacterTeamType)teamID;
}
if (descriptionChanged) { item.Description = itemDesc; }
if (tagsChanged) { item.Tags = tags; }
var nameTag = item.GetComponent<NameTag>();

View File

@@ -77,6 +77,13 @@ namespace Barotrauma
protected set;
}
[Serialize(true, false)]
public bool ShowInStatusMonitor
{
get;
private set;
}
[Serialize("", false)]
public string ImpactSoundTag { get; private set; }
@@ -84,13 +91,13 @@ namespace Barotrauma
public override void UpdatePlacing(Camera cam)
{
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
if (PlayerInput.SecondaryMouseButtonClicked())
{
selected = null;
return;
}
var potentialContainer = MapEntity.GetPotentialContainer(position);
if (!ResizeHorizontal && !ResizeVertical)
@@ -155,7 +162,7 @@ namespace Barotrauma
{
potentialContainer.IsHighlighted = true;
}
//if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null;

View File

@@ -8,6 +8,8 @@ namespace Barotrauma
{
partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull)
{
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
if (shockwave)
{
GameMain.ParticleManager.CreateParticle("shockwave", worldPosition,

View File

@@ -676,7 +676,7 @@ namespace Barotrauma
}
else
{
remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), i, colorStrength, color, 0));
remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), (ushort)i, colorStrength, color, 0));
}
}
paintAmount = BackgroundSections.Sum(s => s.ColorStrength);

View File

@@ -7,14 +7,9 @@ namespace Barotrauma.RuinGeneration
{
public void DebugDraw(SpriteBatch spriteBatch)
{
foreach (RuinShape shape in allShapes)
{
GUI.DrawString(spriteBatch, new Vector2(shape.Center.X, -shape.Center.Y - 50), shape.DistanceFromEntrance.ToString(), Color.White, Color.Black * 0.5f, font: GUI.LargeFont);
}
foreach (Line line in walls)
{
GUI.DrawLine(spriteBatch, new Vector2(line.A.X, -line.A.Y), new Vector2(line.B.X, -line.B.Y), GUI.Style.Red, 0.0f, 10);
}
Rectangle drawRect = Area;
drawRect.Y = -drawRect.Y - Area.Height;
GUI.DrawRectangle(spriteBatch, drawRect, Color.Cyan, false, 0, 6);
}
}
}

View File

@@ -846,16 +846,15 @@ namespace Barotrauma.Lights
if (chList.Submarine == null)
{
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
}
//light is outside, convexhull inside a sub
else
{
Rectangle subBorders = chList.Submarine.Borders;
subBorders.Y -= chList.Submarine.Borders.Height;
if (!MathUtils.CircleIntersectsRectangle(lightPos - chList.Submarine.WorldPosition, range, subBorders)) continue;
if (!MathUtils.CircleIntersectsRectangle(lightPos - chList.Submarine.WorldPosition, range, subBorders)) { continue; }
lightPos -= (chList.Submarine.WorldPosition - chList.Submarine.HiddenSubPosition);
lightPos -= chList.Submarine.WorldPosition - chList.Submarine.HiddenSubPosition;
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
}
@@ -865,14 +864,6 @@ namespace Barotrauma.Lights
//light is inside, convexhull outside
if (chList.Submarine == null)
{
lightPos += (ParentSub.WorldPosition - ParentSub.HiddenSubPosition);
HashSet<RuinGeneration.Ruin> visibleRuins = new HashSet<RuinGeneration.Ruin>();
foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins)
{
if (!MathUtils.CircleIntersectsRectangle(lightPos, range, ruin.Area)) { continue; }
visibleRuins.Add(ruin);
}
list.AddRange(chList.List.FindAll(ch => ch.ParentEntity?.ParentRuin != null && visibleRuins.Contains(ch.ParentEntity.ParentRuin)));
continue;
}
//light and convexhull are both inside the same sub

View File

@@ -126,8 +126,8 @@ namespace Barotrauma.Lights
}
LosTexture?.Dispose();
LosTexture = new RenderTarget2D(graphics,
(int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale),
LosTexture = new RenderTarget2D(graphics,
(int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale),
(int)(GameMain.GraphicsHeight * GameMain.Config.LightMapScale), false, SurfaceFormat.Color, DepthFormat.None);
}
@@ -183,7 +183,7 @@ namespace Barotrauma.Lights
activeLights.Clear();
foreach (LightSource light in lights)
{
if (!light.Enabled) { continue; }
if (!light.Enabled) { continue; }
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
if (light.ParentBody != null)
{
@@ -197,7 +197,9 @@ namespace Barotrauma.Lights
float spriteRange = Math.Max(
light.LightSprite.size.X * light.SpriteScale.X * (0.5f + Math.Abs(light.LightSprite.RelativeOrigin.X - 0.5f)),
light.LightSprite.size.Y * light.SpriteScale.Y * (0.5f + Math.Abs(light.LightSprite.RelativeOrigin.Y - 0.5f)));
range = Math.Max(spriteRange, range);
float targetSize = Math.Max(light.LightTextureTargetSize.X, light.LightTextureTargetSize.Y);
range = Math.Max(Math.Max(spriteRange, targetSize), range);
}
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, range, viewRect)) { continue; }
activeLights.Add(light);
@@ -238,16 +240,16 @@ namespace Barotrauma.Lights
//draw a black rectangle on hulls to hide background lights behind subs
//---------------------------------------------------------------------------------------------------
if (backgroundObstructor != null)
/*if (backgroundObstructor != null)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0,
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
spriteBatch.End();
}
}*/
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform);
Dictionary<Hull, Rectangle> visibleHulls = GetVisibleHulls(cam);
Dictionary<Hull, Rectangle> visibleHulls = GetVisibleHulls(cam);
foreach (KeyValuePair<Hull, Rectangle> hull in visibleHulls)
{
GUI.DrawRectangle(spriteBatch,
@@ -258,18 +260,18 @@ namespace Barotrauma.Lights
spriteBatch.End();
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
SolidColorEffect.Parameters["color"].SetValue(AmbientLight.ToVector4());
SolidColorEffect.Parameters["color"].SetValue(AmbientLight.Opaque().ToVector4());
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform, effect: SolidColorEffect);
Submarine.DrawDamageable(spriteBatch, null);
spriteBatch.End();
graphics.BlendState = BlendState.Additive;
//draw the focused item and character to highlight them,
//and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites)
//---------------------------------------------------------------------------------------------------
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
foreach (LightSource light in activeLights)
{
//don't draw limb lights at this point, they need to be drawn after lights have been obstructed by characters
@@ -294,8 +296,8 @@ namespace Barotrauma.Lights
{
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs)
{
@@ -304,7 +306,7 @@ namespace Barotrauma.Lights
}
}
spriteBatch.End();
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
@@ -312,8 +314,8 @@ namespace Barotrauma.Lights
{
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
foreach (Limb limb in character.AnimController.Limbs)
{
@@ -343,9 +345,9 @@ namespace Barotrauma.Lights
}
lightEffect.World = transform;
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
if (Character.Controlled != null)
{
DrawHalo(Character.Controlled);
@@ -412,7 +414,7 @@ namespace Barotrauma.Lights
}
}
if (highlightedEntities.Count == 0) { return false; }
//draw characters in light blue first
graphics.SetRenderTarget(HighlightMap);
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
@@ -484,9 +486,9 @@ namespace Barotrauma.Lights
//raster pattern on top of everything
spriteBatch.Begin(blendState: BlendState.NonPremultiplied, samplerState: SamplerState.LinearWrap);
spriteBatch.Draw(highlightRaster,
new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height),
new Rectangle(0, 0, (int)(HighlightMap.Width / currLightMapScale * 0.5f), (int)(HighlightMap.Height / currLightMapScale * 0.5f)),
spriteBatch.Draw(highlightRaster,
new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height),
new Rectangle(0, 0, (int)(HighlightMap.Width / currLightMapScale * 0.5f), (int)(HighlightMap.Height / currLightMapScale * 0.5f)),
Color.White * 0.5f);
spriteBatch.End();
@@ -542,7 +544,7 @@ namespace Barotrauma.Lights
{
graphics.Clear(Color.White);
}
//--------------------------------------
@@ -595,9 +597,9 @@ namespace Barotrauma.Lights
}
}
}
graphics.SetRenderTarget(null);
graphics.SetRenderTarget(null);
}
public void ClearLights()
{
lights.Clear();

View File

@@ -104,13 +104,13 @@ namespace Barotrauma.Lights
blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
}
}
public float TextureRange
{
get;
private set;
}
public Sprite OverrideLightTexture
{
get;
@@ -137,7 +137,7 @@ namespace Barotrauma.Lights
public LightSourceParams(XElement element)
{
Deserialize(element);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -206,9 +206,9 @@ namespace Barotrauma.Lights
private short[] indices;
private List<ConvexHullList> hullsInRange;
public Texture2D texture;
public SpriteEffects LightSpriteEffect;
public Submarine ParentSub;
@@ -224,7 +224,7 @@ namespace Barotrauma.Lights
private float prevCalculatedRange;
private Vector2 prevCalculatedPosition;
//do we need to recheck which convex hulls are within range
//do we need to recheck which convex hulls are within range
//(e.g. position or range of the lightsource has changed)
public bool NeedsHullCheck = true;
//do we need to recalculate the vertices of the light volume
@@ -278,7 +278,7 @@ namespace Barotrauma.Lights
translateVertices = position - prevCalculatedPosition;
return;
}
NeedsHullCheck = true;
NeedsRecalculation = true;
}
@@ -360,7 +360,7 @@ namespace Barotrauma.Lights
get;
private set;
}
public float Range
{
get { return lightSourceParams.Range; }
@@ -369,13 +369,29 @@ namespace Barotrauma.Lights
lightSourceParams.Range = value;
if (Math.Abs(prevCalculatedRange - lightSourceParams.Range) < 10.0f) return;
NeedsHullCheck = true;
NeedsRecalculation = true;
prevCalculatedRange = lightSourceParams.Range;
}
}
private Vector2 lightTextureTargetSize;
public Vector2 LightTextureTargetSize
{
get => lightTextureTargetSize;
set
{
NeedsRecalculation = true;
NeedsHullCheck = true;
lightTextureTargetSize = value;
}
}
public Vector2 LightTextureOffset { get; set; }
public Vector2 LightTextureScale { get; set; } = Vector2.One;
public float TextureRange
{
get
@@ -386,7 +402,7 @@ namespace Barotrauma.Lights
/// <summary>
/// Background lights are drawn behind submarines and they don't cast shadows.
/// </summary>
/// </summary>
public bool IsBackground
{
get;
@@ -462,7 +478,7 @@ namespace Barotrauma.Lights
this.ParentSub = submarine;
this.position = position;
lightSourceParams = new LightSourceParams(range, color);
CastShadows = true;
CastShadows = true;
texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>();
if (addLight) { GameMain.LightManager.AddLight(this); }
@@ -494,7 +510,7 @@ namespace Barotrauma.Lights
}
CurrentBrightness = brightness;
}
/// <summary>
/// Update the contents of ConvexHullList and check if we need to recalculate vertices
/// </summary>
@@ -509,7 +525,7 @@ namespace Barotrauma.Lights
}
/// <summary>
/// Recheck which convex hulls are in range (if needed),
/// Recheck which convex hulls are in range (if needed),
/// and check if we need to recalculate vertices due to changes in the convex hulls
/// </summary>
private void CheckHullsInRange()
@@ -561,20 +577,20 @@ namespace Barotrauma.Lights
chList.List.Clear();
continue;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
else
else
{
//light is inside, convexhull outside
if (sub == null) continue;
//light and convexhull are both inside the same sub
if (sub == ParentSub)
{
if (NeedsHullCheck)
{
{
RefreshConvexHullList(chList, lightPos, sub);
}
}
@@ -582,7 +598,7 @@ namespace Barotrauma.Lights
else
{
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) continue;
lightPos -= (sub.Position - ParentSub.Position);
Rectangle subBorders = sub.Borders;
@@ -642,7 +658,7 @@ namespace Barotrauma.Lights
foreach (ConvexHull hull in hulls)
{
hull.RefreshWorldPositions();
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
@@ -829,13 +845,13 @@ namespace Barotrauma.Lights
if (intersection2.index < 0) return null;
Segment seg1 = visibleSegments[intersection1.index];
Segment seg2 = visibleSegments[intersection2.index];
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
if (isPoint1 && isPoint2)
{
//hit at the current segmentpoint -> place the segmentpoint into the list
//hit at the current segmentpoint -> place the segmentpoint into the list
output.Add(p.WorldPos);
foreach (ConvexHullList hullList in hullsInRange)
@@ -938,7 +954,7 @@ namespace Barotrauma.Lights
segment = i;
}
}
return (segment, closestIntersection == null ? rayEnd : (Vector2)closestIntersection);
}
@@ -968,8 +984,8 @@ namespace Barotrauma.Lights
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
Vector2 origin = OverrideLightTextureOrigin;
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
{
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
{
origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
cosAngle = -cosAngle;
sinAngle = -sinAngle;
@@ -981,7 +997,7 @@ namespace Barotrauma.Lights
// Add a vertex for the center of the mesh
vertices[0] = new VertexPositionColorTexture(new Vector3(position.X, position.Y, 0),
Color.White, GetUV(new Vector2(0.5f, 0.5f) + uvOffset, LightSpriteEffect));
//hacky fix to exc excessively large light volumes (they used to be up to 4x the range of the light if there was nothing to block the rays).
//might want to tweak the raycast logic in a way that this isn't necessary
/*float boundRadius = Range * 1.1f / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
@@ -999,7 +1015,7 @@ namespace Barotrauma.Lights
for (int i = 0; i < rayCastHits.Count; i++)
{
Vector2 vertex = rayCastHits[i];
//we'll use the previous and next vertices to calculate the normals
//of the two segments this vertex belongs to
//so we can add new vertices based on these normals
@@ -1007,7 +1023,7 @@ namespace Barotrauma.Lights
Vector2 nextVertex = rayCastHits[i < rayCastHits.Count - 1 ? i + 1 : 0];
Vector2 rawDiff = vertex - drawPos;
//calculate normal of first segment
Vector2 nDiff1 = vertex - nextVertex;
float tx = nDiff1.X; nDiff1.X = -nDiff1.Y; nDiff1.Y = tx;
@@ -1015,7 +1031,7 @@ namespace Barotrauma.Lights
//if the normal is pointing towards the light origin
//rather than away from it, invert it
if (Vector2.DistanceSquared(nDiff1, rawDiff) > Vector2.DistanceSquared(-nDiff1, rawDiff)) nDiff1 = -nDiff1;
//calculate normal of second segment
Vector2 nDiff2 = prevVertex - vertex;
tx = nDiff2.X; nDiff2.X = -nDiff2.Y; nDiff2.Y = tx;
@@ -1112,13 +1128,13 @@ namespace Barotrauma.Lights
static Vector2 GetUV(Vector2 vert, SpriteEffects effects)
{
if (effects == SpriteEffects.FlipHorizontally)
{
vert.X = 1.0f - vert.X;
if (effects == SpriteEffects.FlipHorizontally)
{
vert.X = 1.0f - vert.X;
}
else if (effects == SpriteEffects.FlipVertically)
{
vert.Y = 1.0f - vert.Y;
else if (effects == SpriteEffects.FlipVertically)
{
vert.Y = 1.0f - vert.Y;
}
else if (effects == (SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically))
{
@@ -1228,10 +1244,19 @@ namespace Barotrauma.Lights
}
drawPos.Y = -drawPos.Y;
LightSprite.Draw(
spriteBatch, drawPos,
new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale, LightSpriteEffect);
Color color = new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness);
if (LightTextureTargetSize != Vector2.Zero)
{
LightSprite.DrawTiled(spriteBatch, drawPos, LightTextureTargetSize, color, startOffset: LightTextureOffset, textureScale: LightTextureScale);
}
else
{
LightSprite.Draw(
spriteBatch, drawPos,
color,
origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale, LightSpriteEffect);
}
}
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
@@ -1255,7 +1280,7 @@ namespace Barotrauma.Lights
GUI.DrawLine(spriteBatch, drawPos - Vector2.One * Range, drawPos + Vector2.One * Range, Color);
GUI.DrawLine(spriteBatch, drawPos - new Vector2(1.0f, -1.0f) * Range, drawPos + new Vector2(1.0f, -1.0f) * Range, Color);
}
}
}
}
public void CheckConditionals()
@@ -1280,10 +1305,10 @@ namespace Barotrauma.Lights
if (!CastShadows)
{
Texture2D currentTexture = texture ?? LightTexture;
if (OverrideLightTexture != null) { currentTexture = OverrideLightTexture.Texture; }
if (OverrideLightTexture != null) { currentTexture = OverrideLightTexture.Texture; }
Vector2 center = OverrideLightTexture == null ?
new Vector2(currentTexture.Width / 2, currentTexture.Height / 2) :
Vector2 center = OverrideLightTexture == null ?
new Vector2(currentTexture.Width / 2, currentTexture.Height / 2) :
OverrideLightTexture.Origin;
float scale = Range / (currentTexture.Width / 2.0f);
@@ -1291,7 +1316,7 @@ namespace Barotrauma.Lights
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
drawPos.Y = -drawPos.Y;
spriteBatch.Draw(currentTexture, drawPos, null, Color.Multiply(CurrentBrightness), -rotation, center, scale, SpriteEffects.None, 1);
spriteBatch.Draw(currentTexture, drawPos, null, Color.Multiply(CurrentBrightness), -rotation + MathHelper.ToRadians(LightSourceParams.Rotation), center, scale, SpriteEffects.None, 1);
return;
}
@@ -1317,8 +1342,8 @@ namespace Barotrauma.Lights
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
lightEffect.World =
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
Matrix.CreateRotationZ(rotateVertices) *
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
Matrix.CreateRotationZ(rotateVertices - MathHelper.ToRadians(LightSourceParams.Rotation)) *
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
transform;

View File

@@ -265,7 +265,7 @@ namespace Barotrauma
if (!currentDisplayLocation.Discovered)
{
RemoveFogOfWar(currentDisplayLocation);
currentDisplayLocation.Discovered = true;
currentDisplayLocation.Discover();
if (currentDisplayLocation.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
{
furthestDiscoveredLocation = currentDisplayLocation;
@@ -426,7 +426,7 @@ namespace Barotrauma
Level.Loaded.DebugSetStartLocation(CurrentLocation);
Level.Loaded.DebugSetEndLocation(null);
CurrentLocation.Discovered = true;
CurrentLocation.Discover();
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
SelectLocation(-1);
if (GameMain.Client == null)

View File

@@ -6,6 +6,7 @@ using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Lights;
namespace Barotrauma
{
@@ -17,7 +18,7 @@ namespace Barotrauma
private static Vector2 startMovingPos = Vector2.Zero;
private static float keyDelay;
public static Vector2 StartMovingPos => startMovingPos;
public event Action<Rectangle> Resized;
@@ -97,13 +98,13 @@ namespace Barotrauma
/// </summary>
public float GetDrawDepth(float baseDepth, Sprite sprite)
{
float depth = baseDepth
float depth = baseDepth
//take texture into account to get entities with (roughly) the same base depth and texture to render consecutively to minimize texture swaps
+ (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.00001f
+ ID % 100 * 0.000001f;
return Math.Min(depth, 1.0f);
}
/// <summary>
/// Update the selection logic in submarine editor
/// </summary>
@@ -218,7 +219,7 @@ namespace Barotrauma
}
}
}
}
}
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
MapEntity highLightedEntity = null;
@@ -284,13 +285,13 @@ namespace Barotrauma
//mouse released -> move the entities to the new position of the mouse
Vector2 moveAmount = position - startMovingPos;
if (!isShiftDown)
{
moveAmount.X = (float)(moveAmount.X > 0.0f ? Math.Floor(moveAmount.X / Submarine.GridSize.X) : Math.Ceiling(moveAmount.X / Submarine.GridSize.X)) * Submarine.GridSize.X;
moveAmount.Y = (float)(moveAmount.Y > 0.0f ? Math.Floor(moveAmount.Y / Submarine.GridSize.Y) : Math.Ceiling(moveAmount.Y / Submarine.GridSize.Y)) * Submarine.GridSize.Y;
}
if (Math.Abs(moveAmount.X) >= Submarine.GridSize.X || Math.Abs(moveAmount.Y) >= Submarine.GridSize.Y || isShiftDown)
{
if (!isShiftDown) { moveAmount = Submarine.VectorToWorldGrid(moveAmount); }
@@ -321,10 +322,10 @@ namespace Barotrauma
else
{
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
}
}
}
}
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(SelectedList),SelectedList.Select(entity => entity.Rect).ToList(), oldRects, false));
if (deposited.Any() && deposited.Any(entity => entity is Item))
{
@@ -423,20 +424,24 @@ namespace Barotrauma
//select wire if both items it's connected to are selected
var selectedItems = SelectedList.Where(e => e is Item).Cast<Item>().ToList();
foreach (Item item in selectedItems)
foreach (Item item in Item.ItemList)
{
if (item.Connections == null) continue;
foreach (Connection c in item.Connections)
{
foreach (Wire w in c.Wires)
{
if (w == null || SelectedList.Contains(w.Item)) continue;
var wire = item.GetComponent<Wire>();
if (wire == null) { continue; }
Item item0 = wire.Connections[0]?.Item;
Item item1 = wire.Connections[1]?.Item;
if (w.OtherConnection(c) != null && SelectedList.Contains(w.OtherConnection(c).Item))
{
SelectedList.Add(w.Item);
}
}
if (item0 == null && item1 != null)
{
item0 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
}
else if (item0 != null && item1 == null)
{
item1 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false);
}
if (item0 != null && item1 != null && SelectedList.Contains(item0) && SelectedList.Contains(item1))
{
SelectedList.Add(item);
}
}
@@ -453,8 +458,8 @@ namespace Barotrauma
{
if (PlayerInput.PrimaryMouseButtonHeld() &&
PlayerInput.KeyUp(Keys.Space) &&
PlayerInput.KeyUp(Keys.LeftAlt) &&
PlayerInput.KeyUp(Keys.RightAlt) &&
PlayerInput.KeyUp(Keys.LeftAlt) &&
PlayerInput.KeyUp(Keys.RightAlt) &&
(highlightedListBox == null || (GUI.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUI.MouseOn))))
{
//if clicking a selected entity, start moving it
@@ -482,7 +487,7 @@ namespace Barotrauma
int xKeysDown = (left + right);
int yKeysDown = (up + down);
if (xKeysDown != 0 || yKeysDown != 0) { keyDelay += (float) Timing.Step; } else { keyDelay = 0; }
@@ -512,7 +517,7 @@ namespace Barotrauma
bool isShiftDown = PlayerInput.IsShiftDown();
if (!isShiftDown) return null;
foreach (MapEntity e in mapEntityList)
{
if (!e.SelectableInEditor ||!(e is Item potentialContainer)) { continue; }
@@ -662,7 +667,7 @@ namespace Barotrauma
{
if (SelectedList.Contains(entity)) { return; }
SelectedList.Add(entity);
HandleDoorGapLinks(entity,
HandleDoorGapLinks(entity,
onGapFound: (door, gap) =>
{
door.RefreshLinkedGap();
@@ -670,8 +675,8 @@ namespace Barotrauma
{
SelectedList.Add(gap);
}
},
onDoorFound: (door, gap) =>
},
onDoorFound: (door, gap) =>
{
if (!SelectedList.Contains(door.Item))
{
@@ -715,7 +720,7 @@ namespace Barotrauma
onGapFound: (door, gap) => SelectedList.Remove(gap),
onDoorFound: (door, gap) => SelectedList.Remove(door.Item));
}
static partial void UpdateAllProjSpecific(float deltaTime)
{
var entitiesToRender = Submarine.VisibleEntities ?? mapEntityList;
@@ -748,7 +753,7 @@ namespace Barotrauma
moveAmount.Y = -moveAmount.Y;
bool isShiftDown = PlayerInput.IsShiftDown();
if (!isShiftDown)
{
moveAmount.X = (float)(moveAmount.X > 0.0f ? Math.Floor(moveAmount.X / Submarine.GridSize.X) : Math.Ceiling(moveAmount.X / Submarine.GridSize.X)) * Submarine.GridSize.X;
@@ -761,21 +766,21 @@ namespace Barotrauma
foreach (MapEntity e in SelectedList)
{
SpriteEffects spriteEffects = SpriteEffects.None;
switch (e)
switch (e)
{
case Item item:
case Item item:
{
if (item.FlippedX && item.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
if (item.flippedY && item.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
break;
}
case Structure structure:
case Structure structure:
{
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
break;
}
case WayPoint wayPoint:
case WayPoint wayPoint:
{
Vector2 drawPos = e.WorldPosition;
drawPos.Y = -drawPos.Y;
@@ -812,7 +817,7 @@ namespace Barotrauma
posY = -posY;
Vector2[] corners =
Vector2[] corners =
{
new Vector2(posX, posY),
new Vector2(posX + sizeX, posY),
@@ -878,7 +883,7 @@ namespace Barotrauma
{
MapEntity firstSelected = SelectedList.First();
float minX = firstSelected.WorldRect.X,
float minX = firstSelected.WorldRect.X,
maxX = firstSelected.WorldRect.Right;
foreach (MapEntity entity in SelectedList)
@@ -903,7 +908,7 @@ namespace Barotrauma
foreach (MapEntity entity in SelectedList)
{
minY = Math.Min(minY, entity.WorldRect.Y - entity.WorldRect.Height);
maxY = Math.Max(maxY, entity.WorldRect.Y);
}
@@ -943,21 +948,21 @@ namespace Barotrauma
}
/// <summary>
/// Copy the selected entities to the "clipboard" (copiedList)
/// Copy the selected entities to the "clipboard" (copiedList)
/// </summary>
public static void Copy(List<MapEntity> entities)
{
if (entities.Count == 0) { return; }
CopyEntities(entities);
}
/// <summary>
/// Copy the entities to the "clipboard" (copiedList) and delete them
/// </summary>
public static void Cut(List<MapEntity> entities)
{
if (entities.Count == 0) { return; }
CopyEntities(entities);
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
@@ -989,6 +994,10 @@ namespace Barotrauma
clone.Move(moveAmount);
clone.Submarine = Submarine.MainSub;
}
foreach (MapEntity clone in SelectedList)
{
(clone as Item)?.GetComponent<ItemContainer>()?.SetContainedItemPositions();
}
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false, handleInventoryBehavior: false));
}
@@ -1012,9 +1021,9 @@ namespace Barotrauma
return newEntities;
}
public virtual void AddToGUIUpdateList()
public virtual void AddToGUIUpdateList(int order = 0)
{
if (editingHUD != null && editingHUD.UserData == this) editingHUD.AddToGUIUpdateList();
if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(order: order); }
}
public virtual void UpdateEditing(Camera cam, float deltaTime) { }
@@ -1049,7 +1058,7 @@ namespace Barotrauma
editingHUD.RectTransform.Resize(
new Point(
editingHUD.RectTransform.NonScaledSize.X,
editingHUD.RectTransform.NonScaledSize.X,
MathHelper.Clamp(contentHeight + padding * 2, 50, maxHeight)), resizeChildren: false);
listBox.RectTransform.Resize(new Point(listBox.RectTransform.NonScaledSize.X, editingHUD.RectTransform.NonScaledSize.Y - padding * 2), resizeChildren: false);
}
@@ -1089,7 +1098,7 @@ namespace Barotrauma
{
prevRect = new Rectangle(Rect.Location, Rect.Size);
}
Vector2 placePosition = new Vector2(rect.X, rect.Y);
Vector2 placeSize = new Vector2(rect.Width, rect.Height);
@@ -1140,6 +1149,15 @@ namespace Barotrauma
var oldData = new List<Rectangle> { prevRect.Value };
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity> { this }, newData, oldData, true));
}
if (this is Structure structure)
{
foreach (LightSource light in structure.Lights)
{
light.LightTextureTargetSize = Rect.Size.ToVector2();
light.Position = rect.Location.ToVector2();
}
}
prevRect = null;
}
}

View File

@@ -14,12 +14,14 @@ namespace Barotrauma
{
partial class Structure : MapEntity, IDamageable, IServerSerializable
{
public static bool ShowWalls = true, ShowStructures = true;
public static bool ShowWalls = true, ShowStructures = true;
private List<ConvexHull> convexHulls;
private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>();
public readonly List<LightSource> Lights = new List<LightSource>();
public override bool SelectableInEditor
{
get
@@ -41,7 +43,7 @@ namespace Barotrauma
{
get;
set;
}
}
partial void InitProjSpecific()
{
@@ -88,7 +90,23 @@ namespace Barotrauma
if (editingHUD == null || editingHUD.UserData as Structure != this)
{
editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen);
}
}
}
private void SetLightTextureOffset()
{
Vector2 textOffset = textureOffset;
if (FlippedX) { textOffset.X = -textOffset.X; }
if (FlippedY) { textOffset.Y = -textOffset.Y; }
foreach (LightSource light in Lights)
{
Vector2 bgOffset = new Vector2(
MathUtils.PositiveModulo((int)-textOffset.X, light.texture.Width),
MathUtils.PositiveModulo((int)-textOffset.Y, light.texture.Height));
light.LightTextureOffset = bgOffset;
}
}
public GUIComponent CreateEditingHUD(bool inGame = false)
@@ -175,12 +193,12 @@ namespace Barotrauma
buttonContainer.RectTransform.IsFixedSize = true;
GUITextBlock.AutoScaleAndNormalize(buttonContainer.Children.Where(c => c is GUIButton).Select(b => ((GUIButton)b).TextBlock));
editor.AddCustomContent(buttonContainer, editor.ContentCount);
PositionEditingHUD();
return editingHUD;
}
partial void OnImpactProjSpecific(Fixture f1, Fixture f2, Contact contact)
{
if (!Prefab.Platform && Prefab.StairDirection == Direction.None)
@@ -261,19 +279,21 @@ namespace Barotrauma
else if (HiddenInGame) { return; }
Color color = IsIncludedInSelection && editing ? GUI.Style.Blue : IsHighlighted ? GUI.Style.Orange * Math.Max(spriteColor.A / (float) byte.MaxValue, 0.1f) : spriteColor;
if (IsSelected && editing)
{
//color = Color.Lerp(color, Color.Gold, 0.5f);
color = spriteColor;
Vector2 rectSize = rect.Size.ToVector2();
if (BodyWidth > 0.0f) { rectSize.X = BodyWidth; }
if (BodyHeight > 0.0f) { rectSize.Y = BodyHeight; }
Vector2 bodyPos = WorldPosition + BodyOffset;
GUI.DrawRectangle(spriteBatch, new Vector2(bodyPos.X, -bodyPos.Y), rectSize.X, rectSize.Y, BodyRotation, Color.White,
GUI.DrawRectangle(spriteBatch, new Vector2(bodyPos.X, -bodyPos.Y), rectSize.X, rectSize.Y, BodyRotation, Color.White,
thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
}
@@ -305,14 +325,14 @@ namespace Barotrauma
}
else
{
dropShadowOffset = IsHorizontal ?
new Vector2(0.0f, Math.Sign(Submarine.HiddenSubPosition.Y - Position.Y) * 10.0f) :
dropShadowOffset = IsHorizontal ?
new Vector2(0.0f, Math.Sign(Submarine.HiddenSubPosition.Y - Position.Y) * 10.0f) :
new Vector2(Math.Sign(Submarine.HiddenSubPosition.X - Position.X) * 10.0f, 0.0f);
}
}
dropShadowOffset.Y = -dropShadowOffset.Y;
}
SpriteEffects oldEffects = Prefab.BackgroundSprite.effects;
Prefab.BackgroundSprite.effects ^= SpriteEffects;
@@ -372,13 +392,13 @@ namespace Barotrauma
if (!HasDamage && i == 0)
{
drawSection = new Rectangle(
drawSection.X,
drawSection.Y,
drawSection.X,
drawSection.Y,
Sections[Sections.Length -1 ].rect.Right - drawSection.X,
drawSection.Y - (Sections[Sections.Length - 1].rect.Y - Sections[Sections.Length - 1].rect.Height));
i = Sections.Length;
}
Vector2 sectionOffset = new Vector2(
Math.Abs(rect.Location.X - drawSection.Location.X),
Math.Abs(rect.Location.Y - drawSection.Location.Y));
@@ -496,7 +516,7 @@ namespace Barotrauma
}
else
{
if (!conditional.Matches(this)) { return false; }
if (!conditional.Matches(this)) { return false; }
}
return true;
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
@@ -177,7 +178,6 @@ namespace Barotrauma
//drawing ----------------------------------------------------
private static readonly HashSet<Submarine> visibleSubs = new HashSet<Submarine>();
private static readonly HashSet<Ruin> visibleRuins = new HashSet<Ruin>();
public static void CullEntities(Camera cam)
{
visibleSubs.Clear();
@@ -197,24 +197,6 @@ namespace Barotrauma
}
}
visibleRuins.Clear();
if (Level.Loaded != null)
{
foreach (Ruin ruin in Level.Loaded.Ruins)
{
Rectangle worldBorders = new Rectangle(
ruin.Area.X - 500,
ruin.Area.Y + ruin.Area.Height + 500,
ruin.Area.Width + 1000,
ruin.Area.Height + 1000);
if (RectsOverlap(worldBorders, cam.WorldView))
{
visibleRuins.Add(ruin);
}
}
}
if (visibleEntities == null)
{
visibleEntities = new List<MapEntity>(MapEntity.mapEntityList.Count);
@@ -231,10 +213,6 @@ namespace Barotrauma
{
if (!visibleSubs.Contains(entity.Submarine)) { continue; }
}
else if (entity.ParentRuin != null)
{
if (!visibleRuins.Contains(entity.ParentRuin)) { continue; }
}
if (entity.IsVisible(worldView)) { visibleEntities.Add(entity); }
}
@@ -325,7 +303,7 @@ namespace Barotrauma
depthSortedDamageable.Insert(i, structure);
}
}
foreach (Structure s in depthSortedDamageable)
{
s.DrawDamage(spriteBatch, damageEffect, editing);
@@ -403,6 +381,8 @@ namespace Barotrauma
}
}
// TODO remove
[Obsolete("Use MiniMap.CreateMiniMap()")]
public void CreateMiniMap(GUIComponent parent, IEnumerable<Entity> pointsOfInterest = null, bool ignoreOutpost = false)
{
Rectangle worldBorders = GetDockedBorders();
@@ -415,26 +395,124 @@ namespace Barotrauma
float scale = 0.9f;
GUIFrame hullContainer = new GUIFrame(new RectTransform(
(parentAspectRatio > aspectRatio ? new Vector2(aspectRatio / parentAspectRatio, 1.0f) : new Vector2(1.0f, parentAspectRatio / aspectRatio)) * scale,
parent.RectTransform, Anchor.Center),
style: null);
(parentAspectRatio > aspectRatio ? new Vector2(aspectRatio / parentAspectRatio, 1.0f) : new Vector2(1.0f, parentAspectRatio / aspectRatio)) * scale,
parent.RectTransform, Anchor.Center),
style: null)
{
UserData = "hullcontainer"
};
var connectedSubs = GetConnectedSubs();
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine != this && !connectedSubs.Contains(hull.Submarine)) { continue; }
if (ignoreOutpost && !IsEntityFoundOnThisSub(hull, true)) { continue; }
HashSet<Hull> hullList = Hull.hullList.Where(hull => hull.Submarine == this || connectedSubs.Contains(hull.Submarine)).Where(hull => !ignoreOutpost || IsEntityFoundOnThisSub(hull, true)).ToHashSet();
Dictionary<Hull, HashSet<Hull>> combinedHulls = new Dictionary<Hull, HashSet<Hull>>();
foreach (Hull hull in hullList)
{
if (combinedHulls.ContainsKey(hull) || combinedHulls.Values.Any(hh => hh.Contains(hull))) { continue; }
List<Hull> linkedHulls = new List<Hull>();
MiniMap.GetLinkedHulls(hull, linkedHulls);
linkedHulls.Remove(hull);
foreach (Hull linkedHull in linkedHulls)
{
if (!combinedHulls.ContainsKey(hull))
{
combinedHulls.Add(hull, new HashSet<Hull>());
}
combinedHulls[hull].Add(linkedHull);
}
}
foreach (Hull hull in hullList)
{
Vector2 relativeHullPos = new Vector2(
(hull.WorldRect.X - worldBorders.X) / (float)worldBorders.Width,
(hull.WorldRect.X - worldBorders.X) / (float)worldBorders.Width,
(worldBorders.Y - hull.WorldRect.Y) / (float)worldBorders.Height);
Vector2 relativeHullSize = new Vector2(hull.Rect.Width / (float)worldBorders.Width, hull.Rect.Height / (float)worldBorders.Height);
var hullFrame = new GUIFrame(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, style: "MiniMapRoom", color: Color.DarkCyan * 0.8f)
bool hideHull = combinedHulls.ContainsKey(hull) || combinedHulls.Values.Any(hh => hh.Contains(hull));
if (hideHull) { continue; }
Color color = Color.DarkCyan * 0.8f;
var hullFrame = new GUIFrame(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, style: "MiniMapRoom", color: color)
{
UserData = hull
};
new GUIFrame(new RectTransform(Vector2.One, hullFrame.RectTransform), style: "ScanLines", color: Color.DarkCyan * 0.8f);
new GUIFrame(new RectTransform(Vector2.One, hullFrame.RectTransform), style: "ScanLines", color: color);
}
foreach (var (mainHull, linkedHulls) in combinedHulls)
{
MiniMapHullData data = ConstructLinkedHulls(mainHull, linkedHulls, hullContainer, worldBorders);
Vector2 relativeHullPos = new Vector2(
(data.Bounds.X - worldBorders.X) / worldBorders.Width,
(worldBorders.Y - data.Bounds.Y) / worldBorders.Height);
Vector2 relativeHullSize = new Vector2(data.Bounds.Width / worldBorders.Width, data.Bounds.Height / worldBorders.Height);
Color color = Color.DarkCyan * 0.8f;
float highestY = 0f,
highestX = 0f;
foreach (var (r, _) in data.RectDatas)
{
float y = r.Y - -r.Height,
x = r.X;
if (y > highestY) { highestY = y; }
if (x > highestX) { highestX = x; }
}
HashSet<GUIFrame> frames = new HashSet<GUIFrame>();
foreach (var (snappredRect, hull) in data.RectDatas)
{
RectangleF rect = snappredRect;
rect.Height = -rect.Height;
rect.Y -= rect.Height;
var (parentW, parentH) = hullContainer.Rect.Size.ToVector2();
Vector2 size = new Vector2(rect.Width / parentW, rect.Height / parentH);
// TODO this won't be required if we some day switch RectTransform to use RectangleF
Vector2 pos = new Vector2(rect.X / parentW, rect.Y / parentH);
GUIFrame hullFrame = new GUIFrame(new RectTransform(size, hullContainer.RectTransform) { RelativeOffset = pos }, style: "ScanLinesSeamless", color: color)
{
UserData = hull,
UVOffset = new Vector2(highestX - rect.X, highestY - rect.Y)
};
frames.Add(hullFrame);
}
new GUICustomComponent(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, (spriteBatch, component) =>
{
foreach (List<Vector2> list in data.Polygon)
{
spriteBatch.DrawPolygonInner(hullContainer.Rect.Location.ToVector2(), list, component.Color, 2f);
}
}, (deltaTime, component) =>
{
if (component.Parent.Rect.Size != data.ParentSize)
{
data = ConstructLinkedHulls(mainHull, linkedHulls, hullContainer, worldBorders);
}
})
{
UserData = frames,
Color = color,
CanBeFocused = false
};
}
if (pointsOfInterest != null)
@@ -453,6 +531,64 @@ namespace Barotrauma
}
}
public static MiniMapHullData ConstructLinkedHulls(Hull mainHull, HashSet<Hull> linkedHulls, GUIComponent parent, Rectangle worldBorders)
{
Rectangle parentRect = parent.Rect;
Dictionary<Hull, Rectangle> rects = new Dictionary<Hull, Rectangle>();
Rectangle worldRect = mainHull.WorldRect;
worldRect.Y = -worldRect.Y;
rects.Add(mainHull, worldRect);
foreach (Hull hull in linkedHulls)
{
Rectangle rect = hull.WorldRect;
rect.Y = -rect.Y;
worldRect = Rectangle.Union(worldRect, rect);
rects.Add(hull, rect);
}
worldRect.Y = -worldRect.Y;
List<RectangleF> normalizedRects = new List<RectangleF>();
List<Hull> hullRefs = new List<Hull>();
foreach (var (hull, rect) in rects)
{
Rectangle wRect = rect;
wRect.Y = -wRect.Y;
var (posX, posY) = new Vector2(
(wRect.X - worldBorders.X) / (float)worldBorders.Width,
(worldBorders.Y - wRect.Y) / (float)worldBorders.Height);
var (scaleX, scaleY) = new Vector2(wRect.Width / (float)worldBorders.Width, wRect.Height / (float)worldBorders.Height);
RectangleF newRect = new RectangleF(posX * parentRect.Width, posY * parentRect.Height, scaleX * parentRect.Width, scaleY * parentRect.Height);
normalizedRects.Add(newRect);
hullRefs.Add(hull);
}
ImmutableArray<RectangleF> snappedRectangles = ToolBox.SnapRectangles(normalizedRects, treshold: 1);
List<List<Vector2>> polygon = ToolBox.CombineRectanglesIntoShape(snappedRectangles);
List<List<Vector2>> scaledPolygon = new List<List<Vector2>>();
foreach (List<Vector2> list in polygon)
{
var (polySizeX, polySizeY) = ToolBox.GetPolygonBoundingBoxSize(list);
float sizeX = polySizeX - 1f,
sizeY = polySizeY - 1f;
scaledPolygon.Add(ToolBox.ScalePolygon(list, new Vector2(sizeX / polySizeX, sizeY / polySizeY)));
}
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
}
public void CheckForErrors()
{
List<string> errorMsgs = new List<string>();
@@ -467,7 +603,7 @@ namespace Barotrauma
}
}
if (Info.Type != SubmarineType.OutpostModule ||
if (Info.Type != SubmarineType.OutpostModule ||
(Info.OutpostModuleInfo?.ModuleFlags.Any(f => !f.Equals("hallwayvertical", StringComparison.OrdinalIgnoreCase) && !f.Equals("hallwayhorizontal", StringComparison.OrdinalIgnoreCase)) ?? true))
{
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
@@ -535,7 +671,7 @@ namespace Barotrauma
int wireCount = item.Connections[i].Wires.Count(w => w != null);
if (doorLinks + wireCount > item.Connections[i].MaxWires)
{
errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
new string[] { "[doorcount]", "[freeconnectioncount]" },
new string[] { doorLinks.ToString(), (item.Connections[i].MaxWires - wireCount).ToString() }));
break;
@@ -632,7 +768,7 @@ namespace Barotrauma
return SubEditorScreen.SuppressedWarnings.Contains(type);
}
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
if (type != ServerNetObject.ENTITY_POSITION)

View File

@@ -600,6 +600,8 @@ namespace Barotrauma
var prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
GameMain.Instance.GraphicsDevice.ScissorRectangle = scissorRectangle;
var prevRasterizerState = GameMain.Instance.GraphicsDevice.RasterizerState;
GameMain.Instance.GraphicsDevice.RasterizerState = GameMain.ScissorTestEnable;
spriteRecorder.Render(camera);
@@ -643,6 +645,7 @@ namespace Barotrauma
spriteBatch.End();
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
GameMain.Instance.GraphicsDevice.RasterizerState = prevRasterizerState;
spriteBatch.Begin(SpriteSortMode.Deferred);
}

View File

@@ -127,6 +127,13 @@ namespace Barotrauma
ID.ToString(),
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30),
color);
if (Tunnel?.Type != null)
{
GUI.SmallFont.DrawString(spriteBatch,
Tunnel.Type.ToString(),
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 45),
color);
}
}
public override bool IsMouseOn(Vector2 position)
@@ -164,10 +171,7 @@ namespace Barotrauma
{
foreach (MapEntity e in mapEntityList)
{
if (e.GetType() != typeof(WayPoint)) continue;
if (e == this) continue;
if (!Submarine.RectContains(e.Rect, position)) continue;
if (!(e is WayPoint) || e == this || !e.IsHighlighted) { continue; }
if (linkedTo.Contains(e))
{

View File

@@ -64,7 +64,10 @@ namespace Barotrauma.Networking
string orderOption = orderMessageInfo.OrderOption;
orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ?
orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : "";
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter, orderOption: orderOption);
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName,
givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter,
orderOption: orderOption,
priority: orderMessageInfo.Priority);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{

View File

@@ -552,6 +552,13 @@ namespace Barotrauma.Networking
okButton.OnClicked += msgBox.Close;
var cancelButton = msgBox.Buttons[1];
cancelButton.OnClicked += msgBox.Close;
passwordBox.OnEnterPressed += (GUITextBox textBox, string text) =>
{
msgBox.Close();
clientPeer?.SendPassword(passwordBox.Text);
requiresPw = false;
return true;
};
okButton.OnClicked += (GUIButton button, object obj) =>
{
@@ -567,6 +574,8 @@ namespace Barotrauma.Networking
GameMain.ServerListScreen.Select();
return true;
};
yield return CoroutineStatus.Running;
passwordBox.Select();
while (GUIMessageBox.MessageBoxes.Contains(msgBox))
{
@@ -1461,6 +1470,7 @@ namespace Barotrauma.Networking
bool respawnAllowed = inc.ReadBoolean();
serverSettings.AllowDisguises = inc.ReadBoolean();
serverSettings.AllowRewiring = inc.ReadBoolean();
serverSettings.AllowFriendlyFire = inc.ReadBoolean();
serverSettings.LockAllDefaultWires = inc.ReadBoolean();
serverSettings.AllowRagdollButton = inc.ReadBoolean();
GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
@@ -2753,6 +2763,9 @@ namespace Barotrauma.Networking
msg.Write((byte)characterInfo.BeardIndex);
msg.Write((byte)characterInfo.MoustacheIndex);
msg.Write((byte)characterInfo.FaceAttachmentIndex);
msg.WriteColorR8G8B8(characterInfo.SkinColor);
msg.WriteColorR8G8B8(characterInfo.HairColor);
msg.WriteColorR8G8B8(characterInfo.FacialHairColor);
var jobPreferences = GameMain.NetLobbyScreen.JobPreferences;
int count = Math.Min(jobPreferences.Count, 3);

View File

@@ -288,7 +288,7 @@ namespace Barotrauma.Networking
heartbeatTimer = 5.0;
#if DEBUG
CoroutineManager.InvokeAfter(() =>
CoroutineManager.Invoke(() =>
{
if (GameMain.Client == null) { return; }
if (Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedLoss && sendType != Steamworks.P2PSend.Reliable) { return; }

View File

@@ -110,12 +110,7 @@ namespace Barotrauma.Networking
if (frame == null) { return; }
var previewContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.98f), frame.RectTransform, Anchor.Center))
{
Stretch = true
};
var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), previewContainer.RectTransform, Anchor.CenterLeft), ServerName, font: GUI.LargeFont)
var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), ServerName, font: GUI.LargeFont)
{
ToolTip = ServerName
};
@@ -141,41 +136,30 @@ namespace Barotrauma.Networking
}
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), previewContainer.RectTransform),
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion));
bool hidePlaystyleBanner = previewContainer.Rect.Height < 380 || !PlayStyle.HasValue;
bool hidePlaystyleBanner = !PlayStyle.HasValue;
if (!hidePlaystyleBanner)
{
PlayStyle playStyle = PlayStyle ?? Networking.PlayStyle.Serious;
Sprite playStyleBannerSprite = ServerListScreen.PlayStyleBanners[(int)playStyle];
float playStyleBannerAspectRatio = playStyleBannerSprite.SourceRect.Width / playStyleBannerSprite.SourceRect.Height;
var playStyleBanner = new GUIImage(new RectTransform(new Point(previewContainer.Rect.Width, (int)(previewContainer.Rect.Width / playStyleBannerAspectRatio)), previewContainer.RectTransform),
var playStyleBanner = new GUIImage(new RectTransform(new Point(frame.Rect.Width, (int)(frame.Rect.Width / playStyleBannerAspectRatio)), frame.RectTransform),
playStyleBannerSprite, null, true);
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.01f, 0.06f) },
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.06f) },
TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag."+ playStyle)), textColor: Color.White,
font: GUI.SmallFont, textAlignment: Alignment.Center,
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
playStyleName.RectTransform.IsFixedSize = true;
var serverTypeContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.2f), playStyleBanner.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft),
"MainMenuNotifBackground", Color.Black)
{
CanBeFocused = false,
};
var serverType = new GUITextBlock(new RectTransform(Vector2.One, serverTypeContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
}
else
{
var serverType = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), previewContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
}
var serverType = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.TopLeft);
serverType.RectTransform.MinSize = new Point(0, (int)(serverType.Rect.Height * 1.5f));
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), previewContainer.RectTransform))
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), frame.RectTransform))
{
Stretch = true
};
@@ -285,7 +269,6 @@ namespace Barotrauma.Networking
else
usingWhiteList.Selected = UsingWhiteList.Value;
content.RectTransform.SizeChanged += () =>
{
GUITextBlock.AutoScaleAndNormalize(allowSpectating.TextBlock, allowRespawn.TextBlock, usingWhiteList.TextBlock);
@@ -294,7 +277,7 @@ namespace Barotrauma.Networking
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
TextManager.Get("ServerListContentPackages"), textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), content.RectTransform)) { ScrollBarVisible = true };
var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), frame.RectTransform)) { ScrollBarVisible = true };
if (ContentPackageNames.Count == 0)
{
new GUITextBlock(new RectTransform(Vector2.One, contentPackageList.Content.RectTransform), TextManager.Get("Unknown"), textAlignment: Alignment.Center)
@@ -309,7 +292,7 @@ namespace Barotrauma.Networking
var packageText = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) { MinSize = new Point(0, 15) },
ContentPackageNames[i])
{
Enabled = false
CanBeFocused = false
};
if (i < ContentPackageHashes.Count)
{
@@ -322,7 +305,7 @@ namespace Barotrauma.Networking
//workshop download link found
if (i < ContentPackageWorkshopIds.Count && ContentPackageWorkshopIds[i] != 0)
{
packageText.TextColor = Color.Yellow;
packageText.TextColor = GUI.Style.Yellow;
packageText.ToolTip = TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", ContentPackageNames[i]);
}
else //no package or workshop download link found, tough luck

View File

@@ -1207,7 +1207,7 @@ namespace Barotrauma.Steam
foreach (string file in allPackageFiles)
{
if (file == metaDataFilePath) { continue; }
string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory);
string relativePath = Path.GetRelativePath(item.Directory, file);
string fullPath = Path.GetFullPath(relativePath);
if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return fp == fullPath; })) { continue; }
nonContentFiles.Add(relativePath);

View File

@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Reflection.Metadata;
namespace Barotrauma.Particles
{
@@ -16,6 +15,8 @@ namespace Barotrauma.Particles
public delegate void OnChangeHullHandler(Vector2 position, Hull currentHull);
public OnChangeHullHandler OnChangeHull;
public OnChangeHullHandler OnCollision;
private Vector2 position;
private Vector2 prevPosition;
@@ -166,6 +167,7 @@ namespace Barotrauma.Particles
HighQualityCollisionDetection = false;
OnChangeHull = null;
OnCollision = null;
subEmitters.Clear();
hasSubEmitters = false;
@@ -340,12 +342,20 @@ namespace Barotrauma.Particles
Vector2 collisionNormal = Vector2.Zero;
if (velocity.Y < 0.0f && position.Y - prefab.CollisionRadius * size.Y < hullRect.Y - hullRect.Height)
{
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
if (prefab.DeleteOnCollision)
{
OnCollision?.Invoke(position, currentHull);
return UpdateResult.Delete;
}
collisionNormal = new Vector2(0.0f, 1.0f);
}
else if (velocity.Y > 0.0f && position.Y + prefab.CollisionRadius * size.Y > hullRect.Y)
{
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
if (prefab.DeleteOnCollision)
{
OnCollision?.Invoke(position, currentHull);
return UpdateResult.Delete;
}
collisionNormal = new Vector2(0.0f, -1.0f);
}
@@ -368,6 +378,7 @@ namespace Barotrauma.Particles
handleCollision(gapFound, collisionNormal);
}
collisionNormal = Vector2.Zero;
if (velocity.X < 0.0f && position.X - prefab.CollisionRadius * size.X < hullRect.X)
{
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
@@ -487,6 +498,8 @@ namespace Barotrauma.Particles
velocity.Y = Math.Sign(collisionNormal.Y) * Math.Abs(velocity.Y) * prefab.Restitution;
}
OnCollision?.Invoke(position, currentHull);
velocity += subVel;
}
@@ -523,6 +536,8 @@ namespace Barotrauma.Particles
velocity.Y *= (1.0f - prefab.Friction);
}
OnCollision?.Invoke(position, currentHull);
velocity *= prefab.Restitution;
}

View File

@@ -138,6 +138,8 @@ namespace Barotrauma.Particles
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null)
{
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
if (initialDelay < Prefab.Properties.InitialDelay)
{
initialDelay += deltaTime;

View File

@@ -133,22 +133,41 @@ namespace Barotrauma.Particles
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
{
if (particleCount >= MaxParticles || prefab == null || prefab.Sprites.Count == 0) { return null; }
// this should be optimized for tracers after prototyping
if (tracerPoints == null)
if (prefab == null || prefab.Sprites.Count == 0) { return null; }
if (particleCount >= MaxParticles)
{
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
for (int i = 0; i < particleCount; i++)
{
if (particles[i].Prefab.Priority < prefab.Priority)
{
RemoveParticle(i);
break;
}
}
if (particleCount >= MaxParticles) { return null; }
}
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
if (tracerPoints != null)
{
minPos = new Vector2(
Math.Min(Math.Min(minPos.X, tracerPoints.Item1.X), tracerPoints.Item2.X),
Math.Min(Math.Min(minPos.Y, tracerPoints.Item1.Y), tracerPoints.Item2.Y));
maxPos = new Vector2(
Math.Max(Math.Max(maxPos.X, tracerPoints.Item1.X), tracerPoints.Item2.X),
Math.Max(Math.Max(maxPos.Y, tracerPoints.Item1.Y), tracerPoints.Item2.Y));
}
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
if (particles[particleCount] == null) particles[particleCount] = new Particle();
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, tracerPoints: tracerPoints);
@@ -165,7 +184,7 @@ namespace Barotrauma.Particles
public ParticlePrefab FindPrefab(string prefabName)
{
return Prefabs.Find(p => p.Identifier == prefabName);
return Prefabs.Find(p => p.Identifier.Equals(prefabName, StringComparison.OrdinalIgnoreCase));
}
private void RemoveParticle(int index)

View File

@@ -180,16 +180,13 @@ namespace Barotrauma.Particles
[Editable, Serialize("1.0,1.0", false, description: "The maximum initial size of the particle.")]
public Vector2 StartSizeMax { get; private set; }
[Editable]
[Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
[Editable, Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
public Vector2 SizeChangeMin { get; private set; }
[Editable]
[Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
[Editable, Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
public Vector2 SizeChangeMax { get; private set; }
[Editable]
[Serialize(0.0f, false, description: "How many seconds it takes for the particle to grow to it's initial size.")]
[Editable, Serialize(0.0f, false, description: "How many seconds it takes for the particle to grow to it's initial size.")]
public float GrowTime { get; private set; }
//rendering -----------------------------------------
@@ -215,6 +212,9 @@ namespace Barotrauma.Particles
[Editable, Serialize(ParticleBlendState.AlphaBlend, false, description: "The type of blending to use when rendering the particle.")]
public ParticleBlendState BlendState { get; private set; }
[Editable, Serialize(0, false, description: "Particles with a higher priority can replace lower-priority ones if the maximum number of active particles has been reached.")]
public int Priority { get; private set; }
//animation -----------------------------------------
[Editable(0.0f, float.MaxValue), Serialize(1.0f, false, description: "The duration of the particle's animation cycle (if it's animated).")]

Some files were not shown because too many files have changed in this diff Show More