This commit is contained in:
Evil Factory
2022-02-24 14:30:39 -03:00
364 changed files with 10838 additions and 3966 deletions

View File

@@ -11,7 +11,7 @@ namespace Barotrauma
{
if (Character.IsUnconscious || !Character.Enabled || !Enabled) { return; }
Vector2 pos = Character.WorldPosition;
Vector2 pos = Character.DrawPosition;
pos.Y = -pos.Y;
if (State == AIState.Idle && PreviousState == AIState.Attack)
@@ -31,7 +31,7 @@ namespace Barotrauma
}
else if (SelectedAiTarget?.Entity != null)
{
Vector2 targetPos = SelectedAiTarget.WorldPosition;
Vector2 targetPos = SelectedAiTarget.Entity.DrawPosition;
if (State == AIState.Attack)
{
targetPos = attackWorldPos;
@@ -72,7 +72,7 @@ namespace Barotrauma
}
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
if (LatchOntoAI != null)
if (LatchOntoAI != null && (State == AIState.Idle || LatchOntoAI.IsAttachedToSub))
{
foreach (Joint attachJoint in LatchOntoAI.AttachJoints)
{

View File

@@ -1,7 +1,11 @@
namespace Barotrauma
using Microsoft.Xna.Framework;
namespace Barotrauma
{
abstract partial class AIObjective
{
public static Color ObjectiveIconColor => Color.LightGray;
public static Sprite GetSprite(string identifier, string option, Entity targetEntity)
{
if (string.IsNullOrEmpty(identifier))

View File

@@ -154,7 +154,7 @@ namespace Barotrauma
public bool PlaySound;
public GUIMessage(string rawText, Color color, float delay, string identifier = null, int? value = null)
public GUIMessage(string rawText, Color color, float delay, string identifier = null, int? value = null, float lifeTime = 3.0f)
{
RawText = Text = rawText;
if (value.HasValue)
@@ -166,7 +166,7 @@ namespace Barotrauma
Size = GUI.Font.MeasureString(Text);
Color = color;
Identifier = identifier;
Lifetime = 3.0f;
Lifetime = lifeTime;
}
}
@@ -312,11 +312,7 @@ namespace Barotrauma
}
}
cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
if (AnimController.CurrentHull?.Submarine != null)
{
cursorPosition -= AnimController.CurrentHull.Submarine.Position;
}
UpdateLocalCursor(cam);
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
if (GUI.PauseMenuOpen)
@@ -393,6 +389,15 @@ namespace Barotrauma
DisableControls = false;
}
public void UpdateLocalCursor(Camera cam)
{
cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
if (AnimController.CurrentHull?.Submarine != null)
{
cursorPosition -= AnimController.CurrentHull.Submarine.DrawPosition;
}
}
partial void UpdateControlled(float deltaTime, Camera cam)
{
if (controlled != this) return;
@@ -997,7 +1002,7 @@ namespace Barotrauma
return nameColor;
}
public void AddMessage(string rawText, Color color, bool playSound, string identifier = null, int? value = null)
public void AddMessage(string rawText, Color color, bool playSound, string identifier = null, int? value = null, float lifetime = 3.0f)
{
GUIMessage existingMessage = null;
@@ -1026,7 +1031,7 @@ namespace Barotrauma
}
if (existingMessage == null || !value.HasValue)
{
var newMessage = new GUIMessage(rawText, color, delay, identifier, value);
var newMessage = new GUIMessage(rawText, color, delay, identifier, value, lifetime);
guiMessages.Insert(0, newMessage);
if (playSound)
{
@@ -1156,9 +1161,9 @@ namespace Barotrauma
}
}
partial void OnTalentGiven(string talentIdentifier)
partial void OnTalentGiven(TalentPrefab talentPrefab)
{
AddMessage(TextManager.Get("talentname." + talentIdentifier.ToString()), GUI.Style.Yellow, playSound: this == Controlled);
AddMessage(TextManager.Get("talentname." + talentPrefab.Identifier), GUI.Style.Yellow, playSound: this == Controlled);
}
}
}

View File

@@ -113,7 +113,7 @@ namespace Barotrauma
return
character?.Inventory != null &&
character.AllowInput &&
!character.Removed && !character.IsKnockedDown &&
(controller?.User != character || !controller.HideHUD) &&
!IsCampaignInterfaceOpen &&
!ConversationAction.FadeScreenToBlack;
@@ -406,6 +406,7 @@ 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);
if (iconStyle == null) { continue; }
Range<float> visibleRange = new Range<float>(npc.CurrentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, float.PositiveInfinity);
if (npc.CampaignInteractionType == CampaignMode.InteractionType.Examine)
{
@@ -431,7 +432,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList)
{
if (item.IconStyle is null || item.Submarine != character.Submarine) { continue; }
if (Vector2.DistanceSquared(character.Position, item.Position) > 500f*500f) { continue; }
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 Range<float>(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);

View File

@@ -16,6 +16,7 @@ namespace Barotrauma
private static Sprite infoAreaPortraitBG;
public bool LastControlled;
public int CrewListIndex { get; set; } = -1;
#warning TODO: Refactor
private Sprite disguisedPortrait;
@@ -521,6 +522,7 @@ namespace Barotrauma
ch.SkinColor = skinColor;
ch.HairColor = hairColor;
ch.FacialHairColor = facialHairColor;
ch.SetPersonalityTrait();
if (ch.Job != null)
{
foreach (KeyValuePair<string, float> skill in skillLevels)
@@ -830,7 +832,7 @@ namespace Barotrauma
};
new GUIFrame(
new RectTransform(new Vector2(1.25f, 1.25f), HeadSelectionList.RectTransform, Anchor.Center),
new RectTransform(new Vector2(1.25f, 1.25f), HeadSelectionList.ContentBackground.RectTransform, Anchor.Center),
style: "OuterGlow", color: Color.Black)
{
UserData = "outerglow",
@@ -965,10 +967,15 @@ namespace Barotrauma
foreach (Sprite sprite in characterSprites) { sprite.Remove(); }
characterSprites.Clear();
}
public void Dispose()
{
ClearSprites();
if (HeadSelectionList != null)
{
HeadSelectionList.RectTransform.Parent = null;
HeadSelectionList = null;
}
}
~AppearanceCustomizationMenu()

View File

@@ -2,12 +2,20 @@
{
partial class AfflictionHusk : Affliction
{
private InfectionState? prevDisplayedMessage;
partial void UpdateMessages()
{
if (character != Character.Controlled) { return; }
if (Prefab is AfflictionPrefabHusk { SendMessages: false }) { return; }
if (prevDisplayedMessage.HasValue && prevDisplayedMessage.Value == State) { return; }
switch (State)
{
case InfectionState.Dormant:
if (Strength < DormantThreshold * 0.5f)
{
return;
}
GUI.AddMessage(TextManager.Get("HuskDormant"), GUI.Style.Red);
break;
case InfectionState.Transition:
@@ -23,6 +31,7 @@
default:
break;
}
prevDisplayedMessage = State;
}
}
}

View File

@@ -143,7 +143,7 @@ namespace Barotrauma
Hull fireHull = Hull.hullList.GetRandom(h => h.Submarine == character.Submarine);
if (fireHull != null)
{
var fakeFire = new DummyFireSource(Vector2.One * 500.0f, new Vector2(Rand.Range(fireHull.WorldRect.X, fireHull.WorldRect.Right), fireHull.WorldPosition.Y), fireHull, isNetworkMessage: true)
var fakeFire = new DummyFireSource(Vector2.One * 500.0f, new Vector2(Rand.Range(fireHull.WorldRect.X, fireHull.WorldRect.Right), fireHull.WorldPosition.Y + 1), fireHull, isNetworkMessage: true)
{
CausedByPsychosis = true,
DamagesItems = false,

View File

@@ -531,6 +531,8 @@ namespace Barotrauma
bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f);
if (bloodParticleTimer <= 0.0f)
{
Limb limb = targetLimb ?? Character.AnimController.MainLimb;
bool inWater = Character.AnimController.InWater;
var drawTarget = inWater ? Particles.ParticlePrefab.DrawTargetType.Water : Particles.ParticlePrefab.DrawTargetType.Air;
var emitter = Character.BloodEmitters.FirstOrDefault(e => e.Prefab.ParticlePrefab.DrawTarget == drawTarget || e.Prefab.ParticlePrefab.DrawTarget == Particles.ParticlePrefab.DrawTargetType.Both);
@@ -543,13 +545,13 @@ namespace Barotrauma
if (!inWater)
{
bloodParticleSize *= 2.0f;
velocity = targetLimb.LinearVelocity * 100.0f;
velocity = limb.LinearVelocity * 100.0f;
}
// TODO: use the blood emitter?
var blood = GameMain.ParticleManager.CreateParticle(
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
targetLimb.WorldPosition, velocity, 0.0f, Character.AnimController.CurrentHull);
limb.WorldPosition, velocity, 0.0f, Character.AnimController.CurrentHull);
if (blood != null && !inWater)
{
@@ -954,7 +956,7 @@ namespace Barotrauma
public void DrawHUD(SpriteBatch spriteBatch)
{
if (GUI.DisableHUD) { return; }
if (GUI.DisableHUD || Character.Removed) { return; }
if (GameMain.GraphicsWidth != screenResolution.X ||
GameMain.GraphicsHeight != screenResolution.Y ||
Math.Abs(inventoryScale - Inventory.UIScale) > 0.01f ||
@@ -995,14 +997,16 @@ namespace Barotrauma
{
healthBar.RectTransform.ScreenSpaceOffset = healthBarShadow.RectTransform.ScreenSpaceOffset = Point.Zero;
}
// If manning a turret the portrait doesn't get rendered so we push the health bar to remove the empty gap
healthBarHolder.RectTransform.ScreenSpaceOffset = Character.ShouldLockHud() ? new Point(0, HUDLayoutSettings.PortraitArea.Height) : Point.Zero;
if (healthBarHolder != null)
{
// If manning a turret the portrait doesn't get rendered so we push the health bar to remove the empty gap
healthBarHolder.RectTransform.ScreenSpaceOffset = Character.ShouldLockHud() ? new Point(0, HUDLayoutSettings.PortraitArea.Height) : Point.Zero;
}
DrawStatusHUD(spriteBatch);
}
private (Affliction affliction, string text)? highlightedAfflictionIcon;
public void DrawStatusHUD(SpriteBatch spriteBatch)
{
@@ -1122,23 +1126,24 @@ namespace Barotrauma
}
public static Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction)
{
return GetAfflictionIconColor(prefab, affliction.Strength);
}
public static Color GetAfflictionIconColor(AfflictionPrefab prefab, float afflictionStrength)
{
// No specific colors, use generic
if (prefab.IconColors == null)
{
if (prefab.IsBuff)
{
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUI.Style.BuffColorLow, GUI.Style.BuffColorMedium, GUI.Style.BuffColorHigh);
}
else
{
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, GUI.Style.DebuffColorLow, GUI.Style.DebuffColorMedium, GUI.Style.DebuffColorHigh);
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUI.Style.BuffColorLow, GUI.Style.BuffColorMedium, GUI.Style.BuffColorHigh);
}
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUI.Style.DebuffColorLow, GUI.Style.DebuffColorMedium, GUI.Style.DebuffColorHigh);
}
else
{
return ToolBox.GradientLerp(affliction.Strength / prefab.MaxStrength, prefab.IconColors);
}
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, prefab.IconColors);
}
public static Color GetAfflictionIconColor(Affliction affliction) => GetAfflictionIconColor(affliction.Prefab, affliction);
@@ -1153,7 +1158,7 @@ namespace Barotrauma
return;
}
if (afflictionsDirty())
if (afflictionsDirty() || selectedLimb != currentDisplayedLimb)
{
var currentAfflictions = afflictions.Where(a => ShouldDisplayAfflictionOnLimb(a, selectedLimb)).Select(a => a.Key);
CreateAfflictionInfos(currentAfflictions);
@@ -2015,6 +2020,27 @@ namespace Barotrauma
limbIndicatorOverlay?.Remove();
limbIndicatorOverlay = null;
if (healthWindow != null)
{
healthWindow.RectTransform.Parent = null;
healthWindow = null;
}
if (healthBarHolder != null)
{
healthBarHolder.RectTransform.Parent = null;
healthBarHolder = null;
}
if (SuicideButton != null)
{
SuicideButton.RectTransform.Parent = null;
SuicideButton = null;
}
if (afflictionTooltip != null)
{
afflictionTooltip.RectTransform.Parent = null;
afflictionTooltip = null;
}
}
}
}

View File

@@ -73,7 +73,6 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch)
{
// TODO: move this into the character editor
//var mouthPos = ragdoll.GetMouthPosition();
//if (mouthPos != null)
//{
@@ -173,6 +172,7 @@ namespace Barotrauma
public float DefaultSpriteDepth { get; private set; }
public WearableSprite HairWithHatSprite { get; set; }
public WearableSprite HuskSprite { get; private set; }
public WearableSprite HerpesSprite { get; private set; }
@@ -236,8 +236,8 @@ namespace Barotrauma
public string HitSoundTag => Params?.Sound?.Tag;
private List<WearableSprite> wearableTypeHidingSprites = new List<WearableSprite>();
private List<WearableType> wearableTypesToHide = new List<WearableType>();
private readonly List<WearableSprite> wearableTypeHidingSprites = new List<WearableSprite>();
private readonly HashSet<WearableType> wearableTypesToHide = new HashSet<WearableType>();
private bool enableHuskSprite;
public bool EnableHuskSprite
{
@@ -895,7 +895,22 @@ namespace Barotrauma
foreach (WearableSprite wearable in OtherWearables)
{
if (wearable.Type == WearableType.Husk) { continue; }
if (wearableTypesToHide.Contains(wearable.Type)) { continue; }
if (wearableTypesToHide.Contains(wearable.Type))
{
if (wearable.Type == WearableType.Hair)
{
if (HairWithHatSprite != null)
{
DrawWearable(HairWithHatSprite, depthStep, spriteBatch, blankColor, alpha: color.A / 255f, spriteEffect);
depthStep += step;
continue;
}
}
else
{
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;
@@ -1195,6 +1210,9 @@ namespace Barotrauma
HuskSprite?.Sprite.Remove();
HuskSprite = null;
HairWithHatSprite?.Sprite.Remove();
HairWithHatSprite = null;
HerpesSprite?.Sprite.Remove();
HerpesSprite = null;

View File

@@ -32,7 +32,8 @@ namespace Barotrauma
public void ClientExecute(string[] args)
{
if (!CheatsEnabled && IsCheat)
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is EditorScreen);
if (!allowCheats && !CheatsEnabled && IsCheat)
{
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
#if USE_STEAM
@@ -743,7 +744,7 @@ namespace Barotrauma
AssignOnExecute("explosion", (string[] args) =>
{
Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
float range = 500, force = 10, damage = 50, structureDamage = 10, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
float range = 500, force = 10, damage = 50, structureDamage = 20, itemDamage = 100, empStrength = 0.0f, ballastFloraStrength = 50f;
if (args.Length > 0) float.TryParse(args[0], out range);
if (args.Length > 1) float.TryParse(args[1], out force);
if (args.Length > 2) float.TryParse(args[2], out damage);
@@ -1120,7 +1121,7 @@ namespace Barotrauma
return;
}
if (Submarine.MainSub.SaveAs(Barotrauma.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub")))
if (Submarine.MainSub.TrySaveAs(Barotrauma.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub")))
{
NewMessage("Sub saved", Color.Green);
}
@@ -1894,7 +1895,12 @@ namespace Barotrauma
ThrowError($"\"{args[0]}\" is not a valid Level.PositionType. Available options are: {string.Join(", ", enums)}");
return;
}
debugLines = EventSet.GetDebugStatistics(filter: monsterEvent => monsterEvent.SpawnPosType.HasFlag(spawnType));
bool fullLog = false;
if (args.Length > 1)
{
bool.TryParse(args[1], out fullLog);
}
debugLines = EventSet.GetDebugStatistics(filter: monsterEvent => monsterEvent.SpawnPosType.HasFlag(spawnType), fullLog: fullLog);
}
else
{
@@ -2392,8 +2398,9 @@ namespace Barotrauma
commands.Add(new Command("querylobbies", "Queries all SteamP2P lobbies", (args) =>
{
TaskPool.Add("DebugQueryLobbies",
SteamManager.LobbyQueryRequest(), (t) => {
var lobbies = ((Task<List<Steamworks.Data.Lobby>>)t).Result;
SteamManager.LobbyQueryRequest(), (t) =>
{
t.TryGetResult(out List<Steamworks.Data.Lobby> lobbies);
foreach (var lobby in lobbies)
{
NewMessage(lobby.GetData("name") + ", " + lobby.GetData("lobbyowner"), Color.Yellow);
@@ -2408,7 +2415,7 @@ namespace Barotrauma
TextManager.CheckForDuplicates(args[0]);
}));
commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) =>
commands.Add(new Command("writetocsv|xmltocsv", "Writes the default language (English) to a .csv file.", (string[] args) =>
{
TextManager.WriteToCSV();
NPCConversation.WriteToCSV();

View File

@@ -1,3 +1,4 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma
@@ -20,6 +21,9 @@ namespace Barotrauma
}
}
public override bool DisplayAsCompleted => State > 0 && requireRescue.None();
public override bool DisplayAsFailed => State == HostagesKilledState;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -4,6 +4,9 @@ namespace Barotrauma
{
partial class AlienRuinMission : Mission
{
public override bool DisplayAsCompleted => State > 0;
public override bool DisplayAsFailed => false;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -0,0 +1,8 @@
namespace Barotrauma
{
partial class BeaconMission : Mission
{
public override bool DisplayAsCompleted => State > 0;
public override bool DisplayAsFailed => false;
}
}

View File

@@ -5,6 +5,9 @@ namespace Barotrauma
{
partial class CargoMission : Mission
{
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => false;
public override string GetMissionRewardText(Submarine sub)
{
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub)));

View File

@@ -20,5 +20,8 @@ namespace Barotrauma
return descriptions[GameMain.Client.Character.TeamID == CharacterTeamType.Team1 ? 1 : 2];
}
}
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => false;
}
}

View File

@@ -4,6 +4,9 @@ namespace Barotrauma
{
partial class EscortMission : Mission
{
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => State == 1;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -0,0 +1,8 @@
namespace Barotrauma
{
partial class GoToMission : Mission
{
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => false;
}
}

View File

@@ -1,15 +1,14 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class MineralMission : Mission
{
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => false;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -19,6 +19,15 @@ namespace Barotrauma
public virtual IEnumerable<Entity> HudIconTargets => Enumerable.Empty<Entity>();
/// <summary>
/// Is the mission at a state at which the only thing left to do is to reach the end of the level?
/// </summary>
public abstract bool DisplayAsCompleted { get; }
/// <summary>
/// Is the mission at a state at which the mission cannot be completed anymore?
/// </summary>
public abstract bool DisplayAsFailed { get; }
public Color GetDifficultyColor()
{
int v = Difficulty ?? MissionPrefab.MinDifficulty;

View File

@@ -4,6 +4,9 @@ namespace Barotrauma
{
partial class MonsterMission : Mission
{
public override bool DisplayAsCompleted => State > 0;
public override bool DisplayAsFailed => false;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -6,6 +6,9 @@ namespace Barotrauma
{
partial class NestMission : Mission
{
public override bool DisplayAsCompleted => State > 0 && !requireDelivery;
public override bool DisplayAsFailed => false;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -4,6 +4,9 @@ namespace Barotrauma
{
partial class PirateMission : Mission
{
public override bool DisplayAsCompleted => State > 1;
public override bool DisplayAsFailed => false;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -5,6 +5,9 @@ namespace Barotrauma
{
partial class SalvageMission : Mission
{
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => false;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);

View File

@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -23,6 +22,9 @@ namespace Barotrauma
}
}
public override bool DisplayAsCompleted => false;
public override bool DisplayAsFailed => false;
public override void ClientReadInitial(IReadMessage msg)
{
base.ClientReadInitial(msg);
@@ -60,7 +62,16 @@ namespace Barotrauma
ushort id = msg.ReadUInt16();
bool scanned = msg.ReadBoolean();
Entity entity = Entity.FindEntityByID(id);
scanTargets.Add(entity as WayPoint, scanned);
if (!(entity is WayPoint wayPoint))
{
string errorMsg = $"Failed to find a waypoint in ScanMission.ClientReadScanTargetStatus. Entity {id} was {(entity?.ToString() ?? null)}";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("ScanMission.ClientReadScanTargetStatus", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
}
else
{
scanTargets.Add(wayPoint, scanned);
}
}
}
}

View File

@@ -1,8 +1,10 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SharpFont;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
@@ -130,7 +132,7 @@ namespace Barotrauma
/// <param name="charRanges">Character ranges between each even element with their corresponding odd element. Default is 0x20 to 0xFFFF.</param>
/// <param name="texDims">Texture dimensions. Default is 512x512.</param>
/// <param name="baseChar">Base character used to shift all other characters downwards when rendering. Defaults to T.</param>
public void RenderAtlas(GraphicsDevice gd, uint[] charRanges = null, int texDims = 1024, uint baseChar = 0x54)
private void RenderAtlas(GraphicsDevice gd, uint[] charRanges = null, int texDims = 1024, uint baseChar = 0x54)
{
if (DynamicLoading) { return; }
@@ -253,13 +255,19 @@ namespace Barotrauma
}
}
public void DynamicRenderAtlas(GraphicsDevice gd, uint character, int texDims = 1024, uint baseChar = 0x54)
private void DynamicRenderAtlas(GraphicsDevice gd, uint character, int texDims = 1024, uint baseChar = 0x54)
=> DynamicRenderAtlas(gd, character.ToEnumerable(), texDims, baseChar);
private void DynamicRenderAtlas(GraphicsDevice gd, string str, int texDims = 1024, uint baseChar = 0x54)
=> DynamicRenderAtlas(gd, str.Distinct().Select(c => (uint)c), texDims, baseChar);
private void DynamicRenderAtlas(GraphicsDevice gd, IEnumerable<uint> characters, int texDims = 1024, uint baseChar = 0x54)
{
if (System.Threading.Thread.CurrentThread != GameMain.MainThread)
{
CrossThread.RequestExecutionOnMainThread(() =>
{
DynamicRenderAtlas(gd, character, texDims, baseChar);
DynamicRenderAtlas(gd, characters, texDims, baseChar);
});
return;
}
@@ -271,7 +279,6 @@ namespace Barotrauma
lock (mutex)
{
if (texCoords.ContainsKey(character)) { return; }
if (textures.Count == 0)
{
this.texDims = texDims;
@@ -282,79 +289,90 @@ namespace Barotrauma
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
}
uint glyphIndex = face.GetCharIndex(character);
if (glyphIndex == 0) { return; }
face.SetPixelSizes(0, size);
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
bool anyChanges = false;
bool firstChar = true;
foreach (var character in characters)
{
if (face.Glyph.Metrics.HorizontalAdvance > 0)
if (texCoords.ContainsKey(character)) { continue; }
uint glyphIndex = face.GetCharIndex(character);
if (glyphIndex == 0) { continue; }
face.SetPixelSizes(0, size);
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
{
//glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(
advance: (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(character, blankData);
if (face.Glyph.Metrics.HorizontalAdvance > 0)
{
//glyph is empty, but char still applies advance
GlyphData blankData = new GlyphData(
advance: (float)face.Glyph.Metrics.HorizontalAdvance,
texIndex: -1); //indicates no texture because the glyph is empty
texCoords.Add(character, blankData);
}
continue;
}
return;
}
//stacktrace doesn't really work that well when RenderGlyph throws an exception
face.Glyph.RenderGlyph(RenderMode.Normal);
bitmap = (byte[])face.Glyph.Bitmap.BufferData.Clone();
glyphWidth = face.Glyph.Bitmap.Width;
glyphHeight = bitmap.Length / glyphWidth;
horizontalAdvance = face.Glyph.Metrics.HorizontalAdvance;
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop);
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
{
throw new Exception(filename + ", " + size.ToString() + ", " + (char)character + "; Glyph dimensions exceed texture atlas dimensions");
}
currentDynamicAtlasNextY = Math.Max(currentDynamicAtlasNextY, glyphHeight + 2);
if (currentDynamicAtlasCoords.X + glyphWidth + 2 > texDims - 1)
{
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y += currentDynamicAtlasNextY;
currentDynamicAtlasNextY = 0;
}
//no more room in current texture atlas, create a new one
if (currentDynamicAtlasCoords.Y + glyphHeight + 2 > texDims - 1)
{
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y = 0;
currentDynamicAtlasNextY = 0;
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
currentDynamicPixelBuffer = null;
}
//stacktrace doesn't really work that well when RenderGlyph throws an exception
face.Glyph.RenderGlyph(RenderMode.Normal);
bitmap = (byte[])face.Glyph.Bitmap.BufferData.Clone();
glyphWidth = face.Glyph.Bitmap.Width;
glyphHeight = bitmap.Length / glyphWidth;
horizontalAdvance = face.Glyph.Metrics.HorizontalAdvance;
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop);
GlyphData newData = new GlyphData(
advance: (float)horizontalAdvance,
texIndex: textures.Count - 1,
texCoords: new Rectangle((int)currentDynamicAtlasCoords.X, (int)currentDynamicAtlasCoords.Y, glyphWidth, glyphHeight),
drawOffset: drawOffset
);
texCoords.Add(character, newData);
if (currentDynamicPixelBuffer == null)
{
currentDynamicPixelBuffer = new uint[texDims * texDims];
textures[newData.TexIndex].GetData<uint>(currentDynamicPixelBuffer, 0, texDims * texDims);
}
for (int y = 0; y < glyphHeight; y++)
{
for (int x = 0; x < glyphWidth; x++)
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
{
byte byteColor = bitmap[x + y * glyphWidth];
currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff);
throw new Exception(filename + ", " + size.ToString() + ", " + (char)character + "; Glyph dimensions exceed texture atlas dimensions");
}
}
textures[newData.TexIndex].SetData<uint>(currentDynamicPixelBuffer);
currentDynamicAtlasCoords.X += glyphWidth + 2;
currentDynamicAtlasNextY = Math.Max(currentDynamicAtlasNextY, glyphHeight + 2);
if (currentDynamicAtlasCoords.X + glyphWidth + 2 > texDims - 1)
{
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y += currentDynamicAtlasNextY;
currentDynamicAtlasNextY = 0;
}
//no more room in current texture atlas, create a new one
if (currentDynamicAtlasCoords.Y + glyphHeight + 2 > texDims - 1)
{
if (!firstChar) { textures[^1].SetData<uint>(currentDynamicPixelBuffer); }
currentDynamicAtlasCoords.X = 0;
currentDynamicAtlasCoords.Y = 0;
currentDynamicAtlasNextY = 0;
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
currentDynamicPixelBuffer = null;
}
GlyphData newData = new GlyphData(
advance: (float)horizontalAdvance,
texIndex: textures.Count - 1,
texCoords: new Rectangle((int)currentDynamicAtlasCoords.X, (int)currentDynamicAtlasCoords.Y, glyphWidth, glyphHeight),
drawOffset: drawOffset
);
texCoords.Add(character, newData);
if (currentDynamicPixelBuffer == null)
{
currentDynamicPixelBuffer = new uint[texDims * texDims];
textures[newData.TexIndex].GetData<uint>(currentDynamicPixelBuffer, 0, texDims * texDims);
}
for (int y = 0; y < glyphHeight; y++)
{
for (int x = 0; x < glyphWidth; x++)
{
byte byteColor = bitmap[x + y * glyphWidth];
currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff);
}
}
currentDynamicAtlasCoords.X += glyphWidth + 2;
firstChar = false;
anyChanges = true;
}
if (anyChanges) { textures[^1].SetData<uint>(currentDynamicPixelBuffer); }
}
}
@@ -374,6 +392,10 @@ namespace Barotrauma
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
int lineNum = 0;
Vector2 currentPos = position;
@@ -390,10 +412,6 @@ namespace Barotrauma
}
uint charIndex = text[i];
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
@@ -417,6 +435,10 @@ namespace Barotrauma
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
Vector2 currentPos = position;
for (int i = 0; i < text.Length; i++)
@@ -429,10 +451,6 @@ namespace Barotrauma
}
uint charIndex = text[i];
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
GlyphData gd = GetGlyphData(charIndex);
if (gd.TexIndex >= 0)
@@ -452,6 +470,10 @@ namespace Barotrauma
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<RichTextData> richTextData, int rtdOffset = 0)
{
if (textures.Count == 0 && !DynamicLoading) { return; }
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
int lineNum = 0;
Vector2 currentPos = position;
@@ -472,10 +494,6 @@ namespace Barotrauma
}
uint charIndex = text[i];
if (DynamicLoading && !texCoords.ContainsKey(charIndex))
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
Color currentTextColor;
@@ -626,6 +644,10 @@ namespace Barotrauma
{
retVal.Y = baseHeight;
}
if (DynamicLoading)
{
DynamicRenderAtlas(graphicsDevice, text);
}
for (int i = 0; i < text.Length; i++)
{
@@ -636,10 +658,6 @@ namespace Barotrauma
continue;
}
uint charIndex = text[i];
if (DynamicLoading && !texCoords.ContainsKey(charIndex))
{
DynamicRenderAtlas(graphicsDevice, charIndex);
}
GlyphData gd = GetGlyphData(charIndex);
currentLineX += gd.Advance;

View File

@@ -44,12 +44,12 @@ namespace Barotrauma
Waiting, // Hourglass
WaitingBackground // Cursor + Hourglass
}
public static class GUI
{
public static GUICanvas Canvas => GUICanvas.Instance;
public static CursorState MouseCursor = CursorState.Default;
public static readonly SamplerState SamplerState = new SamplerState()
{
Filter = TextureFilter.Linear,
@@ -116,14 +116,14 @@ namespace Barotrauma
public static float SlicedSpriteScale
{
get
get
{
if (Math.Abs(1.0f - Scale) < 0.1f)
{
if (Math.Abs(1.0f - Scale) < 0.1f)
{
//don't scale if very close to the "reference resolution"
return 1.0f;
return 1.0f;
}
return Scale;
return Scale;
}
}
@@ -306,7 +306,7 @@ namespace Barotrauma
t = new Texture2D(GraphicsDevice, 1, 1);
t.SetData(new Color[] { Color.White });// fill the texture with white
});
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(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));
@@ -314,7 +314,7 @@ namespace Barotrauma
}
/// <summary>
/// By default, all the gui elements are drawn automatically in the same order they appear on the update list.
/// By default, all the gui elements are drawn automatically in the same order they appear on the update list.
/// </summary>
public static void Draw(Camera cam, SpriteBatch spriteBatch)
{
@@ -389,6 +389,12 @@ namespace Barotrauma
DrawString(spriteBatch, new Vector2(10, 10),
"FPS: " + Math.Round(GameMain.PerformanceCounter.AverageFramesPerSecond),
Color.White, Color.Black * 0.5f, 0, SmallFont);
if (GameMain.GameSession != null && Timing.TotalTime > GameMain.GameSession.RoundStartTime + 1.0)
{
DrawString(spriteBatch, new Vector2(10, 25),
$"Physics: {GameMain.CurrentUpdateRate}",
(GameMain.CurrentUpdateRate < Timing.FixedUpdateRate) ? Color.Red : Color.White, Color.Black * 0.5f, 0, SmallFont);
}
}
if (GameMain.ShowPerf)
@@ -397,7 +403,7 @@ namespace Barotrauma
DrawString(spriteBatch, new Vector2(300, y),
"Draw - Avg: " + GameMain.PerformanceCounter.DrawTimeGraph.Average().ToString("0.00") + " ms" +
" Max: " + GameMain.PerformanceCounter.DrawTimeGraph.LargestValue().ToString("0.00") + " ms",
GUI.Style.Green, Color.Black * 0.8f, font: SmallFont);
Style.Green, Color.Black * 0.8f, font: SmallFont);
y += 15;
GameMain.PerformanceCounter.DrawTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: Style.Green);
y += 50;
@@ -408,7 +414,6 @@ namespace Barotrauma
Color.LightBlue, Color.Black * 0.8f, font: SmallFont);
y += 15;
GameMain.PerformanceCounter.UpdateTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: Color.LightBlue);
GameMain.PerformanceCounter.UpdateIterationsGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), maxValue: 20, color: Style.Red);
y += 50;
foreach (string key in GameMain.PerformanceCounter.GetSavedIdentifiers)
{
@@ -431,7 +436,7 @@ namespace Barotrauma
}
}
if (GameMain.DebugDraw)
if (GameMain.DebugDraw && !Submarine.Unloading && !(Screen.Selected is RoundSummaryScreen))
{
DrawString(spriteBatch, new Vector2(10, 25),
"Physics: " + GameMain.World.UpdateTime,
@@ -706,11 +711,11 @@ namespace Barotrauma
spriteBatch.Begin(SpriteSortMode.Immediate, effect: GameMain.GameScreen.PostProcessEffect);
float scale = Math.Max(
(float)GameMain.GraphicsWidth / backgroundSprite.SourceRect.Width,
(float)GameMain.GraphicsWidth / backgroundSprite.SourceRect.Width,
(float)GameMain.GraphicsHeight / backgroundSprite.SourceRect.Height) * 1.1f;
float paddingX = backgroundSprite.SourceRect.Width * scale - GameMain.GraphicsWidth;
float paddingY = backgroundSprite.SourceRect.Height * scale - GameMain.GraphicsHeight;
double noiseT = (Timing.TotalTime * 0.02f);
Vector2 pos = new Vector2((float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0.5f) - 0.5f);
pos = new Vector2(pos.X * paddingX, pos.Y * paddingY);
@@ -719,7 +724,7 @@ namespace Barotrauma
new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos,
null, Color.White, 0.0f, backgroundSprite.size / 2,
scale, SpriteEffects.None, 0.0f);
spriteBatch.End();
}
@@ -759,8 +764,8 @@ namespace Barotrauma
else
{
additions.Enqueue(component);
}
}
}
}
}
/// <summary>
@@ -786,7 +791,7 @@ namespace Barotrauma
component.Children.ForEach(c => RemoveFromUpdateList(c));
}
}
}
}
}
public static void ClearUpdateList()
@@ -900,7 +905,7 @@ namespace Barotrauma
{
GUIMessageBox.VisibleBox.AddToGUIUpdateList();
}
}
}
}
#endregion
@@ -941,7 +946,7 @@ namespace Barotrauma
inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame);
}
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) ||
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) ||
(prevMouseOn == null && !PlayerInput.SecondaryMouseButtonHeld() && !Inventory.DraggingItems.Any()))
{
for (var i = updateList.Count - 1; i > inventoryIndex; i--)
@@ -967,7 +972,7 @@ namespace Barotrauma
return MouseOn;
}
}
private static CursorState UpdateMouseCursorState(GUIComponent c)
{
lock (mutex)
@@ -994,7 +999,7 @@ namespace Barotrauma
}
if (Wire.DraggingWire != null) { return CursorState.Dragging; }
}
if (c == null || c is GUICustomComponent)
{
switch (Screen.Selected)
@@ -1027,7 +1032,7 @@ namespace Barotrauma
}
}
}
if (c != null && c.Visible)
{
if (c.AlwaysOverrideCursor) { return c.HoverCursor; }
@@ -1036,20 +1041,20 @@ namespace Barotrauma
// And this is of course picked up as clickable area.
// There has to be a better way of checking this but for now this works.
var monitorRect = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
var parent = FindInteractParent(c);
if (c.Enabled)
{
// Some parent elements take priority
// but not when the child is a GUIButton or GUITickBox
if (!(parent is GUIButton) && !(parent is GUIListBox) ||
if (!(parent is GUIButton) && !(parent is GUIListBox) ||
(c is GUIButton) || (c is GUITickBox))
{
if (!c.Rect.Equals(monitorRect)) { return c.HoverCursor; }
}
}
// Children in list boxes can be interacted with despite not having
// a GUIButton inside of them so instead of hard coding we check if
// the children can be interacted with by checking their hover state
@@ -1084,7 +1089,7 @@ namespace Barotrauma
{
// Health menus
if (character.CharacterHealth.MouseOnElement) { return CursorState.Hand; }
if (character.SelectedCharacter != null)
{
if (character.SelectedCharacter.CharacterHealth.MouseOnElement)
@@ -1096,7 +1101,7 @@ namespace Barotrauma
// Character is hovering over an item placed in the world
if (character.FocusedItem != null) { return CursorState.Hand; }
}
return CursorState.Default;
static GUIComponent FindInteractParent(GUIComponent component)
@@ -1130,7 +1135,7 @@ namespace Barotrauma
}
}
}
static bool ContainsMouse(GUIComponent component)
{
// If component has a mouse rectangle then use that, if not use it's physical rect
@@ -1138,7 +1143,7 @@ namespace Barotrauma
component.MouseRect.Contains(PlayerInput.MousePosition) :
component.Rect.Contains(PlayerInput.MousePosition);
}
}
}
}
/// <summary>
@@ -1153,8 +1158,8 @@ namespace Barotrauma
{
MouseCursor = CursorState.Waiting;
var timeOut = DateTime.Now + new TimeSpan(0, 0, waitSeconds);
while (DateTime.Now < timeOut)
{
while (DateTime.Now < timeOut)
{
if (endCondition != null)
{
try
@@ -1163,13 +1168,13 @@ namespace Barotrauma
}
catch { break; }
}
yield return CoroutineStatus.Running;
yield return CoroutineStatus.Running;
}
if (MouseCursor == CursorState.Waiting) { MouseCursor = CursorState.Default; }
yield return CoroutineStatus.Success;
}
}
public static void ClearCursorWait()
{
lock (mutex)
@@ -1208,7 +1213,7 @@ namespace Barotrauma
{
debugDrawMetadataOffset--;
}
if (PlayerInput.KeyHit(Keys.Down))
{
debugDrawMetadataOffset++;
@@ -1240,17 +1245,20 @@ namespace Barotrauma
debugDrawMetadataOffset = 0;
}
}
}
HandlePersistingElements(deltaTime);
RefreshUpdateList();
UpdateMouseOn();
Debug.Assert(updateList.Count == updateListSet.Count);
updateList.ForEach(c => c.UpdateAuto(deltaTime));
foreach (var c in updateList)
{
c.UpdateAuto(deltaTime);
}
UpdateMessages(deltaTime);
UpdateSavingIndicator(deltaTime);
}
}
}
private static void UpdateMessages(float deltaTime)
@@ -1281,17 +1289,16 @@ namespace Barotrauma
//only the first message (the currently visible one) is updated at a time
break;
}
foreach (GUIMessage msg in messages)
{
if (!msg.WorldSpace) { continue; }
msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime;
msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime;
}
messages.RemoveAll(m => m.Timer <= 0.0f);
}
}
private static void UpdateSavingIndicator(float deltaTime)
@@ -1349,7 +1356,7 @@ namespace Barotrauma
#region Element drawing
private static List<float> usedIndicatorAngles = new List<float>();
private static readonly List<float> usedIndicatorAngles = new List<float>();
/// <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>
@@ -1628,7 +1635,7 @@ namespace Barotrauma
private static void DrawMessages(SpriteBatch spriteBatch, Camera cam)
{
if (messages.Count == 0) return;
if (messages.Count == 0) { return; }
bool useScissorRect = messages.Any(m => !m.WorldSpace);
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
@@ -1647,7 +1654,7 @@ namespace Barotrauma
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos + Vector2.One, Color.Black, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos, msg.Color, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
break;
break;
}
if (useScissorRect)
@@ -1656,11 +1663,11 @@ namespace Barotrauma
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred);
}
foreach (GUIMessage msg in messages)
{
if (!msg.WorldSpace) { continue; }
if (cam != null)
{
float alpha = 1.0f;
@@ -1669,7 +1676,7 @@ namespace Barotrauma
Vector2 drawPos = cam.WorldToScreen(msg.DrawPos);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + Vector2.One, Color.Black * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos, msg.Color * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
}
}
}
messages.RemoveAll(m => m.Timer <= 0.0f);
@@ -1770,7 +1777,7 @@ namespace Barotrauma
{
int textureWidth = Math.Max(radius * 2, 1);
int textureHeight = Math.Max(height + radius * 2, 1);
Color[] data = new Color[textureWidth * textureHeight];
// Colour the entire texture transparent first.
@@ -1880,9 +1887,9 @@ namespace Barotrauma
/// Creates multiple elements with relative size and positions them automatically.
/// </summary>
public static List<T> CreateElements<T>(int count, Vector2 relativeSize, RectTransform parent, Func<RectTransform, T> constructor,
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, Point? minSize = null, Point? maxSize = null,
int absoluteSpacing = 0, float relativeSpacing = 0, Func<int, int> extraSpacing = null,
int startOffsetAbsolute = 0, float startOffsetRelative = 0, bool isHorizontal = false)
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, Point? minSize = null, Point? maxSize = null,
int absoluteSpacing = 0, float relativeSpacing = 0, Func<int, int> extraSpacing = null,
int startOffsetAbsolute = 0, float startOffsetRelative = 0, bool isHorizontal = false)
where T : GUIComponent
{
return CreateElements(count, parent, constructor, relativeSize, null, anchor, pivot, minSize, maxSize, absoluteSpacing, relativeSpacing, extraSpacing, startOffsetAbsolute, startOffsetRelative, isHorizontal);
@@ -1891,8 +1898,8 @@ namespace Barotrauma
/// <summary>
/// Creates multiple elements with absolute size and positions them automatically.
/// </summary>
public static List<T> CreateElements<T>(int count, Point absoluteSize, RectTransform parent, Func<RectTransform, T> constructor,
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null,
public static List<T> CreateElements<T>(int count, Point absoluteSize, RectTransform parent, Func<RectTransform, T> constructor,
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null,
int absoluteSpacing = 0, float relativeSpacing = 0, Func<int, int> extraSpacing = null,
int startOffsetAbsolute = 0, float startOffsetRelative = 0, bool isHorizontal = false)
where T : GUIComponent
@@ -1991,7 +1998,7 @@ namespace Barotrauma
if (i == 0)
numberInput.IntValue = value.X;
else
numberInput.IntValue = value.Y;
numberInput.IntValue = value.Y;
}
return frame;
}
@@ -2028,6 +2035,16 @@ namespace Barotrauma
return frame;
}
public static void NotifyPrompt(string header, string body)
{
GUIMessageBox msgBox = new GUIMessageBox(header, body, new[] { TextManager.Get("Ok") }, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
msgBox.Buttons[0].OnClicked = delegate
{
msgBox.Close();
return true;
};
}
public static GUIMessageBox AskForConfirmation(string header, string body, Action onConfirm, Action onDeny = null)
{
string[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
@@ -2051,6 +2068,32 @@ namespace Barotrauma
return msgBox;
}
public static GUIMessageBox PromptTextInput(string header, string body, Action<string> onConfirm)
{
string[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
GUIMessageBox msgBox = new GUIMessageBox(header, string.Empty, buttons, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
GUITextBox textBox = new GUITextBox(new RectTransform(Vector2.One, msgBox.Content.RectTransform), text: body)
{
OverflowClip = true
};
// Cancel button
msgBox.Buttons[1].OnClicked = delegate
{
msgBox.Close();
return true;
};
// Ok button
msgBox.Buttons[0].OnClicked = delegate
{
onConfirm.Invoke(textBox.Text);
msgBox.Close();
return true;
};
return msgBox;
}
#endregion
#region Element positioning
@@ -2210,7 +2253,7 @@ namespace Barotrauma
if (disallowedAreas == null) { continue; }
foreach (Rectangle rect2 in disallowedAreas)
{
if (!rect1.Intersects(rect2)) { continue; }
if (!rect1.Intersects(rect2)) { continue; }
intersections = true;
Point centerDiff = rect1.Center - rect2.Center;
@@ -2330,8 +2373,8 @@ namespace Barotrauma
});
}
CreateButton(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby" : "EndRound", buttonContainer,
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
CreateButton(GameMain.GameSession.GameMode is CampaignMode ? "ReturnToServerlobby" : "EndRound", buttonContainer,
verificationTextTag: GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd",
action: () =>
{
GameMain.Client?.RequestRoundEnd(save: false);
@@ -2397,6 +2440,11 @@ namespace Barotrauma
private static bool TogglePauseMenu(GUIButton button, object obj)
{
pauseMenuOpen = !pauseMenuOpen;
if (!pauseMenuOpen && PauseMenu != null)
{
PauseMenu.RectTransform.Parent = null;
PauseMenu = null;
}
return true;
}
@@ -2448,7 +2496,10 @@ namespace Barotrauma
public static void ClearMessages()
{
messages.Clear();
lock (mutex)
{
messages.Clear();
}
}
public static bool IsFourByThree()

View File

@@ -219,12 +219,6 @@ namespace Barotrauma
GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
}
if (UserData is string s && s == "ReadyCheckButton" && ReadyCheck.lastReadyCheck > DateTime.Now)
{
float progress = (ReadyCheck.lastReadyCheck - DateTime.Now).Seconds / 60.0f;
Frame.Color = ToolBox.GradientLerp(progress, Color.White, GUI.Style.Red);
}
}
protected override void Update(float deltaTime)

View File

@@ -22,13 +22,14 @@ namespace Barotrauma
{
GameMain.Instance.ResolutionChanged += RecalculateSize;
}
_instance.ItemComponentHolder = new GUIFrame(new RectTransform(Vector2.One, _instance, Anchor.Center)).RectTransform;
_instance.ChildrenChanged += OnChildrenChanged;
}
return _instance;
}
}
public RectTransform ItemComponentHolder;
//GUICanvas stores the children as weak references, to allow elements that we no longer need to get garbage collected
private readonly List<WeakReference<RectTransform>> childrenWeakRef = new List<WeakReference<RectTransform>>();
private static Vector2 size => new Vector2(GameMain.GraphicsWidth / (float)GUI.UIWidth, 1f);
@@ -36,16 +37,41 @@ namespace Barotrauma
private enum ResizeAxis { Both = 0, X = 1, Y = 2 }
private static void OnChildrenChanged(RectTransform _)
{
//add weak reference if we don't have one yet
foreach (var child in _instance.Children)
{
if (!_instance.childrenWeakRef.Any(c => c.TryGetTarget(out var existingChild) && existingChild == child))
{
_instance.childrenWeakRef.Add(new WeakReference<RectTransform>(child));
}
}
//get rid of strong references
_instance.children.Clear();
//remove dead children
for (int i = _instance.childrenWeakRef.Count - 2; i >= 0; i--)
{
if (!_instance.childrenWeakRef[i].TryGetTarget(out var child) || child.Parent != _instance)
{
_instance.childrenWeakRef.RemoveAt(i);
}
}
}
// Turn public, if there is a need to call this manually.
private static void RecalculateSize()
{
Vector2 recalculatedSize = size;
// Scale children that are supposed to encompass the whole screen so that they are properly scaled on ultrawide as well
for (int i = 0; i < Instance.Children.Count(); i++)
for (int i = 0; i < Instance.childrenWeakRef.Count; i++)
{
RectTransform target = Instance.GetChild(i);
if (target == null || target.RelativeSize.X < 1 && target.RelativeSize.Y < 1) continue;
if (!_instance.childrenWeakRef[i].TryGetTarget(out RectTransform target) || target == null) { continue; };
_instance.children.Add(target);
if (target.RelativeSize.X < 1 && target.RelativeSize.Y < 1) { continue; }
ResizeAxis axis;
@@ -80,6 +106,7 @@ namespace Barotrauma
Instance.Resize(size, resizeChildren: true);
Instance.GetAllChildren().Select(c => c.GUIComponent as GUITextBlock).ForEach(t => t?.SetTextPos());
_instance.children.Clear();
}
}
}

View File

@@ -317,7 +317,10 @@ namespace Barotrauma
set
{
selected = value;
Children.ForEach(c => c.Selected = value);
foreach (var child in Children)
{
child.Selected = value;
}
}
}
public virtual ComponentState State
@@ -537,7 +540,10 @@ namespace Barotrauma
//would be real nice to un-jank this some day
ForceUpdate();
ForceUpdate();
foreach (var child in Children) { child.ForceLayoutRecalculation(); }
foreach (var child in Children)
{
child.ForceLayoutRecalculation();
}
}
public void ForceUpdate() => Update((float)Timing.Step);
@@ -547,7 +553,10 @@ namespace Barotrauma
/// </summary>
public void UpdateChildren(float deltaTime, bool recursive)
{
RectTransform.Children.ForEach(c => c.GUIComponent.UpdateManually(deltaTime, recursive, recursive));
foreach (var child in RectTransform.Children)
{
child.GUIComponent.UpdateManually(deltaTime, recursive, recursive);
}
}
#endregion
@@ -583,7 +592,10 @@ namespace Barotrauma
/// </summary>
public virtual void DrawChildren(SpriteBatch spriteBatch, bool recursive)
{
RectTransform.Children.ForEach(c => c.GUIComponent.DrawManually(spriteBatch, recursive, recursive));
foreach (RectTransform child in RectTransform.Children)
{
child.GUIComponent.DrawManually(spriteBatch, recursive, recursive);
}
}
protected Color _currentColor;
@@ -764,8 +776,8 @@ namespace Barotrauma
{
toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip");
toolTipBlock.RectTransform.NonScaledSize = new Point(
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
(int)(toolTipBlock.Font.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z),
(int)(toolTipBlock.Font.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W));
toolTipBlock.userData = toolTip;
}
@@ -1007,8 +1019,10 @@ namespace Barotrauma
case "gridtext":
LoadGridText(element, parent);
return null;
case "conditional":
break;
default:
throw new NotImplementedException("Loading GUI component \""+element.Name+"\" from XML is not implemented.");
throw new NotImplementedException("Loading GUI component \"" + element.Name + "\" from XML is not implemented.");
}
if (component != null)
@@ -1079,6 +1093,29 @@ namespace Barotrauma
var maxVersion = new Version(attribute.Value);
if (GameMain.Version > maxVersion) { return false; }
break;
case "buildconfiguration":
switch (attribute.Value.ToString().ToLowerInvariant())
{
case "debug":
#if DEBUG
return true;
#else
break;
#endif
case "unstable":
#if UNSTABLE
return true;
#else
break;
#endif
case "release":
#if !DEBUG && !UNSTABLE
return true;
#else
break;
#endif
}
return false;
}
}

View File

@@ -362,6 +362,14 @@ namespace Barotrauma
RectTransform.ScaleChanged += () => dimensionsNeedsRecalculation = true;
RectTransform.SizeChanged += () => dimensionsNeedsRecalculation = true;
UpdateDimensions();
rectT.ChildrenChanged += CheckForChildren;
}
private void CheckForChildren(RectTransform rectT)
{
if (rectT == ScrollBar.RectTransform || rectT == Content.RectTransform || rectT == ContentBackground.RectTransform) { return; }
throw new InvalidOperationException($"Children were added to {nameof(GUIListBox)}, Add them to {nameof(GUIListBox)}.{nameof(Content)} instead.");
}
public void UpdateDimensions()
@@ -431,7 +439,7 @@ namespace Barotrauma
for (int i = 0; i < Content.CountChildren; i++)
{
GUIComponent child = Content.GetChild(i);
if (!child.Visible) { continue; }
if (child == null || !child.Visible) { continue; }
if (RectTransform != null)
{
callback(i, new Point(x, y));
@@ -837,7 +845,6 @@ namespace Barotrauma
UpdateScrollBarSize();
}
if (FadeElements)
{
foreach (var (component, _) in childVisible)

View File

@@ -319,28 +319,29 @@ namespace Barotrauma
AbsoluteSpacing = absoluteSpacing.Y,
};
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null);
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null)
{
Stretch = true,
RelativeSpacing = 0.02f
CanBeFocused = true
};
var dontShowAgainTickBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickBoxLayoutGroup.RectTransform),
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft))
{
CanBeFocused = true,
Stretch = true
};
Vector2 tickBoxRelativeSize = new Vector2(1.0f, 0.5f);
var dontShowAgainTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
TextManager.Get("hintmessagebox.dontshowagain"))
{
ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"),
UserData = "dontshowagain"
};
//var disableHintsTickBox = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), tickBoxLayoutGroup.RectTransform),
// TextManager.Get("hintmessagebox.disablehints"))
//{
// ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
// UserData = "disablehints"
//};
var disableHintsTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
TextManager.Get("hintmessagebox.disablehints"))
{
ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
UserData = "disablehints"
};
Buttons = new List<GUIButton>(1)
{
@@ -379,12 +380,16 @@ namespace Barotrauma
upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height);
height += upperContainerHeight;
height += absoluteSpacing.Y;
height += (int)((bottomContainer.RectTransform.RelativeSize.Y / topHorizontalLayoutGroup.RectTransform.RelativeSize.Y) * upperContainerHeight);
int bottomContainerHeight = dontShowAgainTickBox.Rect.Height + disableHintsTickBox.Rect.Height;
height += bottomContainerHeight;
height += absoluteSpacing.Y;
if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height);
verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize();
float upperContainerRelativeHeight = (float)upperContainerHeight / (upperContainerHeight + bottomContainerHeight);
topHorizontalLayoutGroup.RectTransform.RelativeSize = new Vector2(topHorizontalLayoutGroup.RectTransform.RelativeSize.X, upperContainerRelativeHeight);
bottomContainer.RectTransform.RelativeSize = new Vector2(bottomContainer.RectTransform.RelativeSize.X, 1.0f - upperContainerRelativeHeight);
verticalLayoutGroup.Recalculate();
topHorizontalLayoutGroup.Recalculate();
Content.Recalculate();
@@ -613,6 +618,7 @@ namespace Barotrauma
public bool Close(GUIButton button, object obj)
{
RectTransform.Parent = null;
Close();
return true;
}

View File

@@ -29,6 +29,12 @@ namespace Barotrauma
ClampChildMouseRects(Content);
}
public override void DrawChildren(SpriteBatch spriteBatch, bool recursive)
{
//do nothing (the children have to be drawn in the Draw method after the ScissorRectangle has been set)
return;
}
protected override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) { return; }

View File

@@ -204,6 +204,12 @@ namespace Barotrauma
set { textColor = value; }
}
public Color DisabledTextColor
{
get => disabledTextColor;
set => disabledTextColor = value;
}
private Color? hoverTextColor;
public Color HoverTextColor
{
@@ -303,6 +309,10 @@ namespace Barotrauma
if (parseRichText)
{
RichTextData = Barotrauma.RichTextData.GetRichTextData(text, out text);
if (RichTextData != null && RichTextData.Count == 0)
{
RichTextData = null;
}
}
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
@@ -457,7 +467,7 @@ namespace Barotrauma
while (size == Vector2.Zero)
{
try { size = Font.MeasureString(string.IsNullOrEmpty(text) ? " " : text); }
catch { text = text.Substring(0, text.Length - 1); }
catch { text = text.Length > 0 ? text.Substring(0, text.Length - 1) : ""; }
}
return size;

View File

@@ -80,6 +80,8 @@ namespace Barotrauma
private Vector2 selectionEndPos;
private Vector2 selectionRectSize;
private GUICustomComponent caretAndSelectionRenderer;
private bool mouseHeldInside;
private readonly Memento<string> memento = new Memento<string>();
@@ -188,8 +190,7 @@ namespace Barotrauma
}
set
{
base.ToolTip = value;
textBlock.ToolTip = value;
base.ToolTip = textBlock.ToolTip = caretAndSelectionRenderer.ToolTip = value;
}
}
@@ -278,7 +279,7 @@ namespace Barotrauma
CaretEnabled = true;
caretPosDirty = true;
new GUICustomComponent(new RectTransform(Vector2.One, frame.RectTransform), onDraw: DrawCaretAndSelection);
caretAndSelectionRenderer = new GUICustomComponent(new RectTransform(Vector2.One, frame.RectTransform), onDraw: DrawCaretAndSelection);
int clearButtonWidth = 0;
if (createClearButton)

File diff suppressed because it is too large Load Diff

View File

@@ -58,7 +58,7 @@ namespace Barotrauma
}
}
private readonly List<RectTransform> children = new List<RectTransform>();
protected readonly List<RectTransform> children = new List<RectTransform>();
public IEnumerable<RectTransform> Children => children;
public int CountChildren => children.Count;
@@ -637,7 +637,15 @@ namespace Barotrauma
public bool IsParentOf(RectTransform rectT, bool recursive = true)
{
return children.Contains(rectT) || (recursive && children.Any(c => c.IsParentOf(rectT)));
if (children.Contains(rectT)) { return true; }
if (recursive)
{
foreach (var child in children)
{
if (child.IsParentOf(rectT)) { return true; }
}
}
return false;
}
public bool IsChildOf(RectTransform rectT, bool recursive = true)

View File

@@ -43,36 +43,42 @@ namespace Barotrauma
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
private Point resolutionWhenCreated;
private bool hadPermissions;
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
private int PlayerMoney => campaignUI.Campaign.Money;
private bool HasPermissions => campaignUI.Campaign.AllowedToManageCampaign();
private bool IsBuying => activeTab switch
{
StoreTab.Buy => true,
StoreTab.Sell => false,
StoreTab.SellFromSub => false,
StoreTab.SellSub => false,
_ => throw new NotImplementedException()
};
private GUIListBox ActiveShoppingCrateList => activeTab switch
{
StoreTab.Buy => shoppingCrateBuyList,
StoreTab.Sell => shoppingCrateSellList,
StoreTab.SellFromSub => shoppingCrateSellFromSubList,
StoreTab.SellSub => shoppingCrateSellFromSubList,
_ => throw new NotImplementedException()
};
private bool IsTabUnavailable(StoreTab tab) => !tabLists.ContainsKey(tab);
public enum StoreTab
{
/// <summary>
/// Buy items from the store
/// </summary>
Buy,
/// <summary>
/// Sell items from the character inventory
/// </summary>
Sell,
SellFromSub
/// <summary>
/// Sell items from the sub
/// </summary>
SellSub
}
private enum SortingMethod
@@ -84,11 +90,117 @@ namespace Barotrauma
CategoryAsc
}
#region Permissions
private bool hadPermissions, hadBuyPermissions, hadSellInventoryPermissions, hadSellSubPermissions;
private bool HasPermissions
{
get => GetPermissions();
set => hadPermissions = value;
}
private bool HasBuyPermissions
{
get => HasPermissions || GetPermissions(StoreTab.Buy);
set => hadBuyPermissions = value;
}
private bool HasSellInventoryPermissions
{
get => HasPermissions || GetPermissions(StoreTab.Sell);
set => hadSellInventoryPermissions = value;
}
private bool HasSellSubPermissions
{
get => HasPermissions || GetPermissions(StoreTab.SellSub);
set => hadSellSubPermissions = value;
}
private bool GetPermissions(StoreTab? tab = null)
{
if (!tab.HasValue)
{
return campaignUI.Campaign.AllowedToManageCampaign() || campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.CampaignStore);
}
else
{
return tab.Value switch
{
StoreTab.Buy => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.BuyItems),
StoreTab.Sell => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellInventoryItems),
StoreTab.SellSub => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellSubItems),
_ => false,
};
}
}
private void UpdatePermissions(StoreTab? tab = null)
{
HasPermissions = GetPermissions();
if (!tab.HasValue)
{
HasBuyPermissions = GetPermissions(StoreTab.Buy);
HasSellInventoryPermissions = GetPermissions(StoreTab.Sell);
HasSellSubPermissions = GetPermissions(StoreTab.SellSub);
}
else
{
switch (tab.Value)
{
case StoreTab.Buy:
HasBuyPermissions = GetPermissions(tab.Value);
break;
case StoreTab.Sell:
HasSellInventoryPermissions = GetPermissions(tab.Value);
break;
case StoreTab.SellSub:
HasSellSubPermissions = GetPermissions(tab.Value);
break;
}
}
}
private bool HasTabPermissions(StoreTab tab)
{
return tab switch
{
StoreTab.Buy => HasBuyPermissions,
StoreTab.Sell => HasSellInventoryPermissions,
StoreTab.SellSub => HasSellSubPermissions,
_ => false
};
}
private bool HasActiveTabPermissions()
{
return HasTabPermissions(activeTab);
}
private bool HavePermissionsChanged(StoreTab? tab = null)
{
if (!tab.HasValue)
{
return hadPermissions != HasPermissions;
}
else
{
bool hadTabPermissions = tab.Value switch
{
StoreTab.Buy => hadBuyPermissions,
StoreTab.Sell => hadSellInventoryPermissions,
StoreTab.SellSub => hadSellSubPermissions,
_ => false
};
return hadTabPermissions != HasTabPermissions(tab.Value);
}
}
#endregion
public Store(CampaignUI campaignUI, GUIComponent parentComponent)
{
this.campaignUI = campaignUI;
this.parentComponent = parentComponent;
hadPermissions = HasPermissions;
UpdatePermissions();
CreateUI();
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
if (CurrentLocation?.Reputation != null)
@@ -109,7 +221,7 @@ namespace Barotrauma
public void Refresh(bool updateOwned = true)
{
hadPermissions = HasPermissions;
UpdatePermissions();
if (updateOwned) { UpdateOwnedItems(); }
RefreshBuying(updateOwned: false);
RefreshSelling(updateOwned: false);
@@ -122,7 +234,7 @@ namespace Barotrauma
if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateBuyList();
RefreshStoreBuyList();
var hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.Buy);
storeBuyList.Enabled = hasPermissions;
shoppingCrateBuyList.Enabled = hasPermissions;
needsBuyingRefresh = false;
@@ -133,7 +245,7 @@ namespace Barotrauma
if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateSellList();
RefreshStoreSellList();
var hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.Sell);
storeSellList.Enabled = hasPermissions;
shoppingCrateSellList.Enabled = hasPermissions;
needsSellingRefresh = false;
@@ -141,13 +253,11 @@ namespace Barotrauma
private void RefreshSellingFromSub(bool updateOwned = true, bool updateItemsToSellFromSub = true)
{
if (IsTabUnavailable(StoreTab.SellFromSub)) { return; }
if (updateOwned) { UpdateOwnedItems(); }
if (updateItemsToSellFromSub) RefreshItemsToSellFromSub();
RefreshShoppingCrateSellFromSubList();
RefreshStoreSellFromSubList();
// TODO: Separate permissions from regular campaign permissions
var hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.SellSub);
storeSellFromSubList.Enabled = hasPermissions;
shoppingCrateSellFromSubList.Enabled = hasPermissions;
needsSellingFromSubRefresh = false;
@@ -261,7 +371,7 @@ namespace Barotrauma
{
StoreTab.Buy => CurrentLocation.StoreCurrentBalance + buyTotal,
StoreTab.Sell => CurrentLocation.StoreCurrentBalance - sellTotal,
StoreTab.SellFromSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
StoreTab.SellSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
_ => throw new NotImplementedException(),
};
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
@@ -325,10 +435,9 @@ namespace Barotrauma
tabSortingMethods.Clear();
foreach (StoreTab tab in tabs)
{
if (tab == StoreTab.SellFromSub && GameMain.IsMultiplayer) { continue; }
string text = tab switch
{
StoreTab.SellFromSub => TextManager.Get("submarine"),
StoreTab.SellSub => TextManager.Get("submarine"),
_ => TextManager.Get("campaignstoretab." + tab)
};
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
@@ -456,16 +565,13 @@ namespace Barotrauma
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
tabLists.Add(StoreTab.Sell, storeSellList);
if (GameMain.IsSingleplayer)
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
AutoHideScrollBar = false,
Visible = false
};
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
tabLists.Add(StoreTab.SellFromSub, storeSellFromSubList);
}
AutoHideScrollBar = false,
Visible = false
};
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
tabLists.Add(StoreTab.SellSub, storeSellFromSubList);
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
@@ -526,10 +632,7 @@ namespace Barotrauma
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)
{
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
}
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)
{
@@ -569,16 +672,16 @@ namespace Barotrauma
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.35f, 1.0f), buttonContainer.RectTransform), TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.DecreaseQuantity,
Enabled = HasPermissions,
Enabled = HasActiveTabPermissions(),
ForceUpperCase = true,
OnClicked = (button, userData) =>
{
if (!HasPermissions) { return false; }
if (!HasActiveTabPermissions()) { return false; }
var itemsToRemove = activeTab switch
{
StoreTab.Buy => new List<PurchasedItem>(CargoManager.ItemsInBuyCrate),
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
StoreTab.SellSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException(),
};
itemsToRemove.ForEach(i => ClearFromShoppingCrate(i));
@@ -637,7 +740,6 @@ namespace Barotrauma
private void ChangeStoreTab(StoreTab tab)
{
if (IsTabUnavailable(tab)) { return; }
activeTab = tab;
foreach (GUIButton tabButton in storeTabButtons)
{
@@ -680,7 +782,7 @@ namespace Barotrauma
}
shoppingCrateSellList.Visible = true;
break;
case StoreTab.SellFromSub:
case StoreTab.SellSub:
storeBuyList.Visible = false;
storeSellList.Visible = false;
if (storeSellFromSubList != null)
@@ -699,7 +801,6 @@ namespace Barotrauma
private void FilterStoreItems(MapEntityCategory? category, string filter)
{
if (IsTabUnavailable(activeTab)) { return; }
selectedItemCategory = category;
var list = tabLists[activeTab];
filter = filter?.ToLower();
@@ -733,7 +834,7 @@ namespace Barotrauma
float prevBuyListScroll = storeBuyList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateBuyList.BarScroll;
bool hasPermissions = HasPermissions;
bool hasPermissions = HasBuyPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
int dailySpecialCount = CurrentLocation?.DailySpecials.Count() ?? 3;
@@ -816,7 +917,7 @@ namespace Barotrauma
{
float prevSellListScroll = storeSellList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll;
bool hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(StoreTab.Sell);
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeRequestedGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
@@ -894,7 +995,7 @@ namespace Barotrauma
{
float prevSellListScroll = storeSellFromSubList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateSellFromSubList.BarScroll;
bool hasPermissions = HasPermissions;
bool hasPermissions = HasSellSubPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeRequestedSubGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
@@ -938,12 +1039,12 @@ namespace Barotrauma
if (itemFrame == null)
{
var parentComponent = isRequestedGood ? storeRequestedSubGoodGroup : storeSellFromSubList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellFromSub, forceDisable: !hasPermissions);
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellSub, forceDisable: !hasPermissions);
}
else
{
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
SetQuantityLabelText(StoreTab.SellFromSub, itemFrame);
SetQuantityLabelText(StoreTab.SellSub, itemFrame);
SetOwnedLabelText(itemFrame);
SetPriceGetters(itemFrame, false);
}
@@ -961,8 +1062,8 @@ namespace Barotrauma
removedItemFrames.AddRange(storeRequestedSubGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (activeTab == StoreTab.SellFromSub) { FilterStoreItems(); }
SortItems(StoreTab.SellFromSub);
if (activeTab == StoreTab.SellSub) { FilterStoreItems(); }
SortItems(StoreTab.SellSub);
storeSellFromSubList.BarScroll = prevSellListScroll;
shoppingCrateSellFromSubList.BarScroll = prevShoppingCrateScroll;
@@ -1062,17 +1163,14 @@ namespace Barotrauma
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox, StoreTab tab)
{
bool hasPermissions = HasPermissions;
bool hasPermissions = HasTabPermissions(tab);
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
int totalPrice = 0;
foreach (PurchasedItem item in items)
{
PriceInfo priceInfo = item.ItemPrefab.GetPriceInfo(CurrentLocation);
if (priceInfo == null) { continue; }
var itemFrame = listBox.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab.Identifier == item.ItemPrefab.Identifier);
if (!(item.ItemPrefab.GetPriceInfo(CurrentLocation) is { } priceInfo)) { continue; }
GUINumberInput numInput = null;
if (itemFrame == null)
if (!(listBox.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab.Identifier == item.ItemPrefab.Identifier) is { } itemFrame))
{
itemFrame = CreateItemFrame(item, listBox, tab, forceDisable: !hasPermissions);
numInput = itemFrame.FindChild(c => c is GUINumberInput, recursive: true) as GUINumberInput;
@@ -1100,10 +1198,21 @@ namespace Barotrauma
}
suppressBuySell = false;
var price = tab == StoreTab.Buy ?
CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo) :
CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
totalPrice += item.Quantity * price;
try
{
int price = tab switch
{
StoreTab.Buy => CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo),
StoreTab.Sell => CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo),
StoreTab.SellSub => CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo),
_ => throw new NotImplementedException()
};
totalPrice += item.Quantity * price;
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error getting item price: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
}
}
var removedItemFrames = listBox.Content.Children.Except(existingItemFrames).ToList();
@@ -1119,7 +1228,7 @@ namespace Barotrauma
case StoreTab.Sell:
sellTotal = totalPrice;
break;
case StoreTab.SellFromSub:
case StoreTab.SellSub:
sellFromSubTotal = totalPrice;
break;
}
@@ -1135,7 +1244,7 @@ namespace Barotrauma
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList, StoreTab.Sell);
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellFromSub);
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellSub);
private void SortItems(GUIListBox list, SortingMethod sortingMethod)
{
@@ -1286,14 +1395,12 @@ namespace Barotrauma
private void SortItems(StoreTab tab, SortingMethod sortingMethod)
{
if (IsTabUnavailable(tab)) { return; }
tabSortingMethods[tab] = sortingMethod;
SortItems(tabLists[tab], sortingMethod);
}
private void SortItems(StoreTab tab)
{
if (IsTabUnavailable(tab)) { return; }
SortItems(tab, tabSortingMethods[tab]);
}
@@ -1301,12 +1408,6 @@ namespace Barotrauma
private GUIComponent CreateItemFrame(PurchasedItem pi, GUIComponent parentComponent, StoreTab containingTab, bool forceDisable = false)
{
var tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
{
tooltip += "\n" + pi.ItemPrefab.Description;
}
GUIListBox parentListBox = parentComponent as GUIListBox;
int width = 0;
RectTransform parent = null;
@@ -1320,7 +1421,11 @@ namespace Barotrauma
width = parentComponent.Rect.Width;
parent = parentComponent.RectTransform;
}
string tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
{
tooltip += $"\n{pi.ItemPrefab.Description}";
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, (int)(GUI.yScale * 80)), parent: parent), style: "ListBoxElement")
{
ToolTip = tooltip,
@@ -1338,8 +1443,7 @@ namespace Barotrauma
var iconRelativeWidth = 0.0f;
var priceAndButtonRelativeWidth = 1.0f - nameAndIconRelativeWidth;
Sprite itemIcon = pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite;
if (itemIcon != null)
if ((pi.ItemPrefab.InventoryIcon ?? pi.ItemPrefab.sprite) is { } itemIcon)
{
iconRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
GUIImage img = new GUIImage(new RectTransform(new Vector2(iconRelativeWidth, 0.9f), mainGroup.RectTransform), itemIcon, scaleToFit: true)
@@ -1425,7 +1529,7 @@ namespace Barotrauma
{
if (suppressBuySell) { return; }
PurchasedItem purchasedItem = numberInput.UserData as PurchasedItem;
if (!HasPermissions)
if (!HasActiveTabPermissions())
{
numberInput.IntValue = purchasedItem.Quantity;
return;
@@ -1528,11 +1632,16 @@ namespace Barotrauma
OwnedItems.Clear();
// Add items on the sub(s)
Submarine.MainSub?.GetItems(true)
.Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) &&
i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)) &&
ItemAndAllContainersInteractable(i))
.ForEach(i => AddToOwnedItems(i.Prefab));
if (Submarine.MainSub?.GetItems(true) is List<Item> subItems)
{
foreach (var subItem in subItems)
{
if (!subItem.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { continue; }
if (!subItem.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { continue; }
if (!ItemAndAllContainersInteractable(subItem)) { continue; }
AddToOwnedItems(subItem.Prefab);
}
}
// Add items in character inventories
foreach (var item in Item.ItemList)
@@ -1574,8 +1683,9 @@ namespace Barotrauma
private void SetItemFrameStatus(GUIComponent itemFrame, bool enabled)
{
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
if (!(itemFrame?.UserData is PurchasedItem pi)) { return; }
bool refreshFrameStatus = !pi.IsStoreComponentEnabled.HasValue || pi.IsStoreComponentEnabled.Value != enabled;
if (!refreshFrameStatus) { return; }
if (itemFrame.FindChild("icon", recursive: true) is GUIImage icon)
{
if (pi.ItemPrefab?.InventoryIcon != null)
@@ -1587,14 +1697,11 @@ namespace Barotrauma
icon.Color = pi.ItemPrefab.SpriteColor * (enabled ? 1.0f : 0.5f);
}
};
var color = Color.White * (enabled ? 1.0f : 0.5f);
if (itemFrame.FindChild("name", recursive: true) is GUITextBlock name)
{
name.TextColor = color;
}
if (itemFrame.FindChild("quantitylabel", recursive: true) is GUITextBlock qty)
{
qty.TextColor = color;
@@ -1603,25 +1710,21 @@ namespace Barotrauma
{
numberInput.Enabled = enabled;
}
if (itemFrame.FindChild("owned", recursive: true) is GUITextBlock ownedBlock)
{
ownedBlock.TextColor = color;
}
var isDiscounted = false;
bool isDiscounted = false;
if (itemFrame.FindChild("undiscountedprice", recursive: true) is GUITextBlock undiscountedPriceBlock)
{
undiscountedPriceBlock.TextColor = color;
undiscountedPriceBlock.Strikethrough.Color = color;
isDiscounted = true;
}
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
{
priceBlock.TextColor = isDiscounted ? storeSpecialColor * (enabled ? 1.0f : 0.5f) : color;
}
if (itemFrame.FindChild("addbutton", recursive: true) is GUIButton addButton)
{
addButton.Enabled = enabled;
@@ -1630,6 +1733,8 @@ namespace Barotrauma
{
removeButton.Enabled = enabled;
}
pi.IsStoreComponentEnabled = enabled;
itemFrame.UserData = pi;
}
private void SetQuantityLabelText(StoreTab mode, GUIComponent itemFrame)
@@ -1664,14 +1769,22 @@ namespace Barotrauma
private int GetMaxAvailable(ItemPrefab itemPrefab, StoreTab mode)
{
var list = mode switch
List<PurchasedItem> list = null;
try
{
StoreTab.Buy => CurrentLocation.StoreStock,
StoreTab.Sell => itemsToSell,
StoreTab.SellFromSub => itemsToSellFromSub,
_ => throw new NotImplementedException()
};
if (list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
list = mode switch
{
StoreTab.Buy => CurrentLocation?.StoreStock,
StoreTab.Sell => itemsToSell,
StoreTab.SellSub => itemsToSellFromSub,
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error getting item availability: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
}
if (list != null && list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
{
if (mode == StoreTab.Buy)
{
@@ -1691,8 +1804,8 @@ namespace Barotrauma
private bool ModifyBuyQuantity(PurchasedItem item, int quantity)
{
if (item == null || item.ItemPrefab == null) { return false; }
if (!HasPermissions) { return false; }
if (item?.ItemPrefab == null) { return false; }
if (!HasBuyPermissions) { return false; }
if (quantity > 0)
{
var itemInCrate = CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
@@ -1703,13 +1816,13 @@ namespace Barotrauma
}
CargoManager.ModifyItemQuantityInBuyCrate(item.ItemPrefab, quantity);
GameMain.Client?.SendCampaignState();
return false;
return true;
}
private bool ModifySellQuantity(PurchasedItem item, int quantity)
{
if (item == null || item.ItemPrefab == null) { return false; }
if (!HasPermissions) { return false; }
if (item?.ItemPrefab == null) { return false; }
if (!HasSellInventoryPermissions) { return false; }
if (quantity > 0)
{
// Make sure there's enough available to sell
@@ -1718,45 +1831,68 @@ namespace Barotrauma
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.Sell)) { return false; }
}
CargoManager.ModifyItemQuantityInSellCrate(item.ItemPrefab, quantity);
//GameMain.Client?.SendCampaignState();
return false;
return true;
}
private bool ModifySellFromSubQuantity(PurchasedItem item, int quantity)
{
if (item == null || item.ItemPrefab == null) { return false; }
if (!HasPermissions) { return false; }
if (item?.ItemPrefab == null) { return false; }
if (!HasSellSubPermissions) { return false; }
if (quantity > 0)
{
// Make sure there's enough available to sell
var itemToSell = CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
var totalQuantityToSell = itemToSell != null ? itemToSell.Quantity + quantity : quantity;
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellFromSub)) { return false; }
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellSub)) { return false; }
}
CargoManager.ModifyItemQuantityInSellFromSubCrate(item.ItemPrefab, quantity);
// TODO: GameMain.Client?.SendCampaignState();
return false;
GameMain.Client?.SendCampaignState();
return true;
}
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1) => activeTab switch
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1)
{
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
StoreTab.Sell => ModifySellQuantity(item, quantity),
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, quantity),
_ => throw new NotImplementedException(),
};
if (item == null) { return false; }
try
{
return activeTab switch
{
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
StoreTab.Sell => ModifySellQuantity(item, quantity),
StoreTab.SellSub => ModifySellFromSubQuantity(item, quantity),
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error adding an item to the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
}
private bool ClearFromShoppingCrate(PurchasedItem item) => activeTab switch
private bool ClearFromShoppingCrate(PurchasedItem item)
{
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, -item.Quantity),
_ => throw new NotImplementedException(),
};
if (item == null) { return false; }
try
{
return activeTab switch
{
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
StoreTab.SellSub => ModifySellFromSubQuantity(item, -item.Quantity),
_ => throw new NotImplementedException(),
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
}
private bool BuyItems()
{
if (!HasPermissions) { return false; }
if (!HasBuyPermissions) { return false; }
var itemsToPurchase = new List<PurchasedItem>(CargoManager.ItemsInBuyCrate);
var itemsToRemove = new List<PurchasedItem>();
@@ -1788,15 +1924,24 @@ namespace Barotrauma
private bool SellItems()
{
if (!HasPermissions) { return false; }
var itemsToSell = activeTab switch
if (!HasActiveTabPermissions()) { return false; }
List<PurchasedItem> itemsToSell;
try
{
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException()
};
itemsToSell = activeTab switch
{
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error confirming the store transaction: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return false;
}
var itemsToRemove = new List<PurchasedItem>();
var totalValue = 0;
int totalValue = 0;
foreach (PurchasedItem item in itemsToSell)
{
if (item?.ItemPrefab?.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
@@ -1811,11 +1956,7 @@ namespace Barotrauma
itemsToRemove.ForEach(i => itemsToSell.Remove(i));
if (itemsToSell.None() || totalValue > CurrentLocation.StoreCurrentBalance) { return false; }
CargoManager.SellItems(itemsToSell, activeTab);
if (activeTab == StoreTab.Sell)
{
// TODO: Implement selling sub items in multiplayer
GameMain.Client?.SendCampaignState();
}
GameMain.Client?.SendCampaignState();
return false;
}
@@ -1831,7 +1972,7 @@ namespace Barotrauma
int total = activeTab switch
{
StoreTab.Sell => sellTotal,
StoreTab.SellFromSub => sellFromSubTotal,
StoreTab.SellSub => sellFromSubTotal,
_ => throw new NotImplementedException(),
};
shoppingCrateTotal.Text = GetCurrencyFormatted(total);
@@ -1863,21 +2004,29 @@ namespace Barotrauma
}
}
private void SetConfirmButtonStatus() => confirmButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
activeTab switch
{
StoreTab.Buy => buyTotal <= PlayerMoney,
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
StoreTab.SellFromSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
_ => throw new NotImplementedException(),
};
private void SetConfirmButtonStatus()
{
confirmButton.Enabled =
HasActiveTabPermissions() &&
ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
activeTab switch
{
StoreTab.Buy => buyTotal <= PlayerMoney,
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
StoreTab.SellSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
_ => false
};
}
private void SetClearAllButtonStatus() => clearAllButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any();
private void SetClearAllButtonStatus()
{
clearAllButton.Enabled =
HasActiveTabPermissions() &&
ActiveShoppingCrateList.Content.RectTransform.Children.Any();
}
private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f;
private readonly float timerUpdateInterval = 1.5f;
private const float timerUpdateInterval = 1.5f;
public void Update(float deltaTime)
{
@@ -1914,10 +2063,10 @@ namespace Barotrauma
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
if (needsItemsToSellFromSubRefresh) { RefreshItemsToSellFromSub(); }
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsBuyingRefresh) { RefreshBuying(); }
if (needsSellingRefresh) { RefreshSelling(); }
if (needsSellingFromSubRefresh) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
if (needsRefresh || HavePermissionsChanged()) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsBuyingRefresh || HavePermissionsChanged(StoreTab.Buy)) { RefreshBuying(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsSellingRefresh || HavePermissionsChanged(StoreTab.Sell)) { RefreshSelling(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsSellingFromSubRefresh || HavePermissionsChanged(StoreTab.SellSub)) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
}
}
}

View File

@@ -414,15 +414,8 @@ namespace Barotrauma
}
else
{
if (GameMain.Client == null)
{
subsToShow.AddRange(SubmarineInfo.SavedSubmarines.Where(s => s.IsCampaignCompatible && !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
else
{
subsToShow.AddRange(GameMain.NetLobbyScreen.CampaignSubmarines.Where(s => !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
subsToShow.AddRange((GameMain.Client is null ? SubmarineInfo.SavedSubmarines : MultiPlayerCampaign.GetCampaignSubs())
.Where(s => s.IsCampaignCompatible && !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
}
@@ -446,20 +439,11 @@ namespace Barotrauma
if (preview == null)
{
SubmarineInfo potentialMatch;
if (GameMain.Client == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
else
{
potentialMatch = GameMain.NetLobbyScreen.CampaignSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
SubmarineInfo potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
preview = potentialMatch?.PreviewImage;
// Try from savedsubmarines with name comparison as a backup
// Try name comparison as a backup
if (preview == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == info.Name);

View File

@@ -20,7 +20,7 @@ namespace Barotrauma
private static Sprite ownerIcon, moderatorIcon;
public enum InfoFrameTab { Crew, Mission, Reputation, Traitor, Submarine, Talents };
public static InfoFrameTab selectedTab;
public static InfoFrameTab SelectedTab { get; private set; }
private GUIFrame infoFrame, contentFrame;
private readonly List<GUIButton> tabButtons = new List<GUIButton>();
@@ -129,9 +129,8 @@ namespace Barotrauma
public TabMenu()
{
if (!initialized) { Initialize(); }
CreateInfoFrame(selectedTab);
SelectInfoFrameTab(null, selectedTab);
CreateInfoFrame(SelectedTab);
SelectInfoFrameTab(SelectedTab);
}
public void Update()
@@ -147,8 +146,8 @@ namespace Barotrauma
}
}
if (selectedTab != InfoFrameTab.Crew) return;
if (linkedGUIList == null) return;
if (SelectedTab != InfoFrameTab.Crew) { return; }
if (linkedGUIList == null) { return; }
if (GameMain.IsMultiplayer)
{
@@ -226,7 +225,7 @@ namespace Barotrauma
{
UserData = tab,
ToolTip = TextManager.Get(textTag),
OnClicked = SelectInfoFrameTab
OnClicked = (btn, userData) => { SelectInfoFrameTab((InfoFrameTab)userData); return true; }
};
tabButtons.Add(newButton);
return newButton;
@@ -277,16 +276,16 @@ namespace Barotrauma
talentsButton.Enabled = Character.Controlled?.Info != null;
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
{
SelectInfoFrameTab(null, InfoFrameTab.Crew);
SelectInfoFrameTab(InfoFrameTab.Crew);
}
};
talentPointNotification = GameSession.CreateTalentIconNotification(talentsButton);
}
private bool SelectInfoFrameTab(GUIButton button, object userData)
public void SelectInfoFrameTab(InfoFrameTab selectedTab)
{
selectedTab = (InfoFrameTab)userData;
SelectedTab = selectedTab;
CreateInfoFrame(selectedTab);
tabButtons.ForEach(tb => tb.Selected = (InfoFrameTab)tb.UserData == selectedTab);
@@ -300,7 +299,7 @@ namespace Barotrauma
CreateMissionInfo(infoFrameHolder);
break;
case InfoFrameTab.Reputation:
if (GameMain.GameSession.RoundSummary != null && GameMain.GameSession.GameMode is CampaignMode campaignMode)
if (GameMain.GameSession?.RoundSummary != null && GameMain.GameSession?.GameMode is CampaignMode campaignMode)
{
infoFrameHolder.ClearChildren();
GUIFrame reputationFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrameHolder.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
@@ -308,9 +307,9 @@ namespace Barotrauma
}
break;
case InfoFrameTab.Traitor:
TraitorMissionPrefab traitorMission = GameMain.Client.TraitorMission;
Character traitor = GameMain.Client.Character;
if (traitor == null || traitorMission == null) return false;
TraitorMissionPrefab traitorMission = GameMain.Client?.TraitorMission;
Character traitor = GameMain.Client?.Character;
if (traitor == null || traitorMission == null) { return; }
CreateTraitorInfo(infoFrameHolder, traitorMission, traitor);
break;
case InfoFrameTab.Submarine:
@@ -320,8 +319,6 @@ namespace Barotrauma
CreateTalentInfo(infoFrameHolder);
break;
}
return true;
}
private const float jobColumnWidthPercentage = 0.138f;
@@ -755,7 +752,7 @@ namespace Barotrauma
if (character != null)
{
if (GameMain.NetworkMember == null)
if (GameMain.Client == null)
{
GUIComponent preview = character.Info.CreateInfoFrame(background, false, null);
}
@@ -859,7 +856,7 @@ namespace Barotrauma
string msg = ChatMessage.GetTimeStamp() + message.TextWithSender;
storedMessages.Add(new Pair<string, PlayerConnectionChangeType>(msg, message.ChangeType));
if (GameSession.IsTabMenuOpen && selectedTab == InfoFrameTab.Crew)
if (GameSession.IsTabMenuOpen && SelectedTab == InfoFrameTab.Crew)
{
TabMenu instance = GameSession.TabMenuInstance;
instance.AddLineToLog(msg, message.ChangeType);
@@ -1023,13 +1020,15 @@ 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)
var icon = 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,
CanBeFocused = false
};
UpdateMissionStateIcon(mission, icon);
mission.OnMissionStateChanged += (mission) => UpdateMissionStateIcon(mission, icon);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameRichTextData, missionNameString, font: GUI.LargeFont);
GUILayoutGroup difficultyIndicatorGroup = null;
@@ -1066,6 +1065,33 @@ namespace Barotrauma
}
}
private void UpdateMissionStateIcon(Mission mission, GUIImage missionIcon)
{
if (mission == null || missionIcon == null) { return; }
string style = string.Empty;
if (mission.DisplayAsFailed)
{
style = "MissionFailedIcon";
}
else if (mission.DisplayAsCompleted)
{
style = "MissionCompletedIcon";
}
GUIImage stateIcon = missionIcon.GetChild<GUIImage>();
if (string.IsNullOrEmpty(style))
{
if (stateIcon != null)
{
stateIcon.Visible = false;
}
}
else
{
stateIcon ??= new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), style, scaleToFit: true);
stateIcon.Visible = true;
}
}
private void CreateTraitorInfo(GUIFrame infoFrame, TraitorMissionPrefab traitorMission, Character traitor)
{
GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
@@ -1443,7 +1469,7 @@ namespace Barotrauma
selectedTalents.Remove(talentIdentifier);
}
UpdateTalentButtons();
UpdateTalentInfo();
return true;
},
};
@@ -1508,7 +1534,7 @@ namespace Barotrauma
};
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
UpdateTalentButtons();
UpdateTalentInfo();
}
private void CreateTalentSkillList(Character character, GUIListBox parent)
@@ -1543,30 +1569,14 @@ namespace Barotrauma
GUITextBlock.AutoScaleAndNormalize(skillNames);
}
private bool HasUnlockedAllTalents(Character controlledCharacter)
{
if (TalentTree.JobTalentTrees.TryGetValue(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree))
{
foreach (TalentSubTree talentSubTree in talentTree.TalentSubTrees)
{
foreach (TalentOption talentOption in talentSubTree.TalentOptionStages)
{
if (talentOption.Talents.None(t => controlledCharacter.HasTalent(t.Identifier)))
{
return false;
}
}
}
}
return true;
}
private void UpdateTalentButtons()
private void UpdateTalentInfo()
{
Character controlledCharacter = Character.Controlled;
if (controlledCharacter?.Info == null) { return; }
bool unlockedAllTalents = HasUnlockedAllTalents(controlledCharacter);
if (SelectedTab != InfoFrameTab.Talents) { return; }
bool unlockedAllTalents = controlledCharacter.HasUnlockedAllTalents();
if (unlockedAllTalents)
{
@@ -1646,7 +1656,7 @@ namespace Barotrauma
}
}
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
UpdateTalentButtons();
UpdateTalentInfo();
}
private bool ApplyTalentSelection(GUIButton guiButton, object userData)
@@ -1660,63 +1670,14 @@ namespace Barotrauma
{
Character controlledCharacter = Character.Controlled;
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
UpdateTalentButtons();
UpdateTalentInfo();
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);
}
UpdateTalentInfo();
}
}
}

View File

@@ -16,7 +16,6 @@ using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
internal class UpgradeStore
{
public readonly struct CategoryData
@@ -168,6 +167,7 @@ namespace Barotrauma
//TODO: move this somewhere else
public static void UpdateCategoryList(GUIListBox categoryList, CampaignMode campaign, Submarine? drawnSubmarine, IEnumerable<UpgradeCategory> applicableCategories)
{
var subItems = GetSubItems();
foreach (GUIComponent component in categoryList.Content.Children)
{
if (!(component.UserData is CategoryData data)) { continue; }
@@ -179,7 +179,7 @@ namespace Barotrauma
var customizeButton = component.FindChild("customizebutton", true);
if (customizeButton != null)
{
customizeButton.Visible = HasSwappableItems(data.Category);
customizeButton.Visible = HasSwappableItems(data.Category, subItems);
}
}
@@ -434,6 +434,7 @@ namespace Barotrauma
if (AvailableMoney >= hullRepairCost)
{
Campaign.Money -= hullRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
Campaign.PurchasedHullRepairs = true;
button.Enabled = false;
SelectTab(UpgradeTab.Repairs);
@@ -468,6 +469,7 @@ namespace Barotrauma
if (AvailableMoney >= itemRepairCost && !Campaign.PurchasedItemRepairs)
{
Campaign.Money -= itemRepairCost;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
Campaign.PurchasedItemRepairs = true;
button.Enabled = false;
SelectTab(UpgradeTab.Repairs);
@@ -513,6 +515,7 @@ namespace Barotrauma
if (AvailableMoney >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
{
Campaign.Money -= shuttleRetrieveCost;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
Campaign.PurchasedLostShuttles = true;
button.Enabled = false;
SelectTab(UpgradeTab.Repairs);
@@ -717,16 +720,19 @@ namespace Barotrauma
private bool customizeTabOpen;
private static bool HasSwappableItems(UpgradeCategory category)
private static bool HasSwappableItems(UpgradeCategory category, List<Item>? subItems = null)
{
if (Submarine.MainSub == null) { return false; }
return Submarine.MainSub.GetItems(true).Any(i =>
subItems ??= GetSubItems();
return subItems.Any(i =>
i.Prefab.SwappableItem != null &&
!i.HiddenInGame && i.AllowSwapping &&
(i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
Submarine.MainSub.IsEntityFoundOnThisSub(i, true) && category.ItemTags.Any(t => i.HasTag(t)));
}
private static List<Item> GetSubItems() => Submarine.MainSub?.GetItems(true) ?? new List<Item>();
private void SelectUpgradeCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine)
{
if (selectedUpgradeCategoryLayout == null) { return; }
@@ -1688,7 +1694,7 @@ namespace Barotrauma
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
private static string FormatCurrency(int money, bool format = true)
public static string FormatCurrency(int money, bool format = true)
{
return TextManager.GetWithVariable("CurrencyFormat", "[credits]", format ? string.Format(CultureInfo.InvariantCulture, "{0:N0}", money) : money.ToString());
}

View File

@@ -5,7 +5,7 @@ using System.Linq;
namespace Barotrauma
{
public static partial class GameAnalyticsManager
static partial class GameAnalyticsManager
{
static partial void CreateConsentPrompt()
{

View File

@@ -32,6 +32,13 @@ namespace Barotrauma
public static PerformanceCounter PerformanceCounter;
private static Stopwatch performanceCounterTimer;
private static int updateCount = 0;
public static int CurrentUpdateRate
{
get; private set;
}
public static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
public static string[] ConsoleArguments;
@@ -124,6 +131,12 @@ namespace Barotrauma
private bool exiting;
public static bool IsFirstLaunch
{
get;
private set;
}
public static GameMain Instance
{
get;
@@ -353,6 +366,8 @@ namespace Barotrauma
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Item));
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Items.Components.ItemComponent));
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Hull));
performanceCounterTimer = Stopwatch.StartNew();
}
/// <summary>
@@ -441,8 +456,8 @@ namespace Barotrauma
TaskPool.Add("AutoUpdateWorkshopItemsAsync",
SteamManager.AutoUpdateWorkshopItemsAsync(), (task) =>
{
bool result = ((Task<bool>)task).Result;
if (!task.TryGetResult(out bool result)) { return; }
Config.WaitingForAutoUpdate = false;
});
@@ -583,6 +598,18 @@ namespace Barotrauma
{
Steamworks.SteamFriends.OnGameRichPresenceJoinRequested += OnInvitedToGame;
Steamworks.SteamFriends.OnGameLobbyJoinRequested += OnLobbyJoinRequested;
if (SteamManager.TryGetUnlockedAchievements(out List<Steamworks.Data.Achievement> achievements))
{
//check the achievements too, so we don't consider people who've played the game before this "gamelaunchcount" stat was added as being 1st-time-players
//(people who have played previous versions, but not unlocked any achievements, will be incorrectly considered 1st-time-players, but that should be a small enough group to not skew the statistics)
if (!achievements.Any() && SteamManager.GetStatInt("gamelaunchcount") <= 0)
{
IsFirstLaunch = true;
GameAnalyticsManager.AddDesignEvent("FirstLaunch");
}
}
SteamManager.IncrementStat("gamelaunchcount", 1);
}
#endif
@@ -700,11 +727,10 @@ namespace Barotrauma
protected override void Update(GameTime gameTime)
{
Timing.Accumulator += gameTime.ElapsedGameTime.TotalSeconds;
int updateIterations = (int)Math.Floor(Timing.Accumulator / Timing.Step);
if (Timing.Accumulator > Timing.Step * 6.0)
if (Timing.Accumulator > Timing.AccumulatorMax)
{
//if the game's running too slowly then we have no choice
//but to skip a bunch of steps
//prevent spiral of death:
//if the game's running too slowly then we have no choice but to skip a bunch of steps
//otherwise it snowballs and becomes unplayable
Timing.Accumulator = Timing.Step;
}
@@ -740,7 +766,6 @@ namespace Barotrauma
PlayerInput.Update(Timing.Step);
if (loadingScreenOpen)
{
//reset accumulator if loading
@@ -760,7 +785,10 @@ namespace Barotrauma
}
#if DEBUG
CancelQuickStart |= PlayerInput.KeyDown(Keys.LeftShift);
if (PlayerInput.KeyHit(Keys.LeftShift))
{
CancelQuickStart = !CancelQuickStart;
}
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled || Config.TestScreenEnabled) && FirstLoad && !CancelQuickStart)
{
@@ -979,13 +1007,21 @@ namespace Barotrauma
Timing.Accumulator -= Timing.Step;
updateCount++;
sw.Stop();
PerformanceCounter.AddElapsedTicks("Update total", sw.ElapsedTicks);
PerformanceCounter.UpdateTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency);
PerformanceCounter.UpdateIterationsGraph.Update(updateIterations);
}
if (!Paused) Timing.Alpha = Timing.Accumulator / Timing.Step;
if (!Paused) { Timing.Alpha = Timing.Accumulator / Timing.Step; }
if (performanceCounterTimer.ElapsedMilliseconds > 1000)
{
CurrentUpdateRate = (int)Math.Round(updateCount / (double)(performanceCounterTimer.ElapsedMilliseconds / 1000.0));
performanceCounterTimer.Restart();
updateCount = 0;
}
}
public static void ResetFrameTime()
@@ -1090,8 +1126,15 @@ namespace Barotrauma
{
double roundDuration = Timing.TotalTime - GameSession.RoundStartTime;
GameAnalyticsManager.AddProgressionEvent(GameAnalyticsManager.ProgressionStatus.Fail,
GameSession.GameMode?.Name ?? "none",
GameSession.GameMode?.Preset.Identifier ?? "none",
roundDuration);
string eventId = "QuitRound:" + (GameSession.GameMode?.Preset.Identifier ?? "none") + ":";
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:CurrentIntensity", GameSession.EventManager.CurrentIntensity);
foreach (var activeEvent in GameSession.EventManager.ActiveEvents)
{
GameAnalyticsManager.AddDesignEvent(eventId + "EventManager:ActiveEvents:" + activeEvent.ToString());
}
GameSession.LogEndRoundStats(eventId);
if (Tutorial.Initialized)
{
((TutorialMode)GameSession.GameMode).Tutorial?.Stop();
@@ -1183,11 +1226,7 @@ namespace Barotrauma
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), linkHolder.RectTransform), TextManager.Get("bugreportgithubform"), style: "MainMenuGUIButton", textAlignment: Alignment.Left)
{
#if UNSTABLE
UserData = "https://barotraumagame.com/unstable-3rf3w5t4ter/",
#else
UserData = "https://github.com/Regalis11/Barotrauma/issues/new?template=bug_report.md",
#endif
OnClicked = (btn, userdata) =>
{
ShowOpenUrlInWebBrowserPrompt(userdata as string);

View File

@@ -1,5 +1,6 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,37 +8,6 @@ namespace Barotrauma
{
partial class CargoManager
{
private class SoldEntity
{
public enum SellStatus
{
/// <summary>
/// Entity sold in SP. Or, entity sold by client and confirmed by server in MP.
/// </summary>
Confirmed,
/// <summary>
/// Entity sold by client in MP. Client has received at least one update from server after selling, but this entity wasn't yet confirmed.
/// </summary>
Unconfirmed,
/// <summary>
/// Entity sold by client in MP. Client hasn't yet received an update from server after selling.
/// </summary>
Local
}
public Item Item { get; }
public SellStatus Status { get; set; }
private SoldEntity(Item item, SellStatus status)
{
Item = item;
Status = status;
}
public static SoldEntity CreateInSinglePlayer(Item item) => new SoldEntity(item, SellStatus.Confirmed);
public static SoldEntity CreateInMultiPlayer(Item item) => new SoldEntity(item, SellStatus.Local);
}
private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>();
// The bag slot is intentionally left out since we want to be able to sell items from there
@@ -67,31 +37,6 @@ namespace Barotrauma
}
}
public IEnumerable<Item> GetSellableItemsFromSub()
{
if (Submarine.MainSub == null) { return new List<Item>(); }
var confirmedSoldEntities = GetConfirmedSoldEntities();
return Submarine.MainSub.GetItems(true).FindAll(item =>
{
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
if (item.GetRootInventoryOwner() is Character) { 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; }
return true;
}).Distinct();
static bool ItemAndAllContainersInteractable(Item item)
{
do
{
if (!item.IsPlayerTeamInteractable) { return false; }
item = item.Container;
} while (item != null);
return true;
}
}
private IEnumerable<SoldEntity> GetConfirmedSoldEntities()
{
// Only consider items which have been:
@@ -100,24 +45,6 @@ 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.SpawnedInCurrentOutpost) { 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();
@@ -125,15 +52,21 @@ namespace Barotrauma
OnItemsInBuyCrateChanged?.Invoke();
}
public void SetItemsInSubSellCrate(List<PurchasedItem> items)
{
ItemsInSellFromSubCrate.Clear();
ItemsInSellFromSubCrate.AddRange(items);
OnItemsInSellFromSubCrateChanged?.Invoke();
}
public void SetSoldItems(List<SoldItem> items)
{
SoldItems.Clear();
SoldItems.AddRange(items);
foreach (SoldEntity se in SoldEntities)
foreach (var se in SoldEntities)
{
if (se.Status == SoldEntity.SellStatus.Confirmed) { continue; }
if (SoldItems.Any(si => si.ID == se.Item.ID && si.ItemPrefab == se.Item.Prefab && (GameMain.Client == null || GameMain.Client.ID == si.SellerID)))
if (SoldItems.Any(si => Match(si, se, true)))
{
se.Status = SoldEntity.SellStatus.Confirmed;
}
@@ -142,13 +75,28 @@ namespace Barotrauma
se.Status = SoldEntity.SellStatus.Unconfirmed;
}
}
foreach (var si in SoldItems)
{
if (si.Origin != SoldItem.SellOrigin.Submarine) { continue; }
if (!(SoldEntities.FirstOrDefault(se => se.Item == null && Match(si, se, false)) is SoldEntity soldEntityMatch)) { continue; }
if (!(Entity.FindEntityByID(si.ID) is Item item)) { continue; }
soldEntityMatch.SetItem(item);
soldEntityMatch.Status = SoldEntity.SellStatus.Confirmed;
}
OnSoldItemsChanged?.Invoke();
static bool Match(SoldItem soldItem, SoldEntity soldEntity, bool matchId)
{
if (soldItem.ItemPrefab != soldEntity.ItemPrefab) { return false; }
if (matchId && (soldEntity.Item == null || soldItem.ID != soldEntity.Item.ID)) { return false; }
if (soldItem.Origin == SoldItem.SellOrigin.Character && GameMain.Client != null && soldItem.SellerID != GameMain.Client.ID) { return false; }
return true;
}
}
public void ModifyItemQuantityInSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
{
PurchasedItem itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
var itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemToSell != null)
{
itemToSell.Quantity += changeInQuantity;
@@ -186,72 +134,69 @@ namespace Barotrauma
public void SellItems(List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
{
var sellableItems = sellingMode switch
IEnumerable<Item> sellableItems;
try
{
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
Store.StoreTab.SellFromSub => GetSellableItemsFromSub(),
_ => throw new System.NotImplementedException(),
};
sellableItems = sellingMode switch
{
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
Store.StoreTab.SellSub => GetSellableItemsFromSub(),
_ => throw new NotImplementedException()
};
}
catch (NotImplementedException e)
{
DebugConsole.ShowError($"Error selling items: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
return;
}
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
var sellerId = GameMain.Client?.ID ?? 0;
byte sellerId = GameMain.Client?.ID ?? 0;
// Check all the prices before starting the transaction
// to make sure the modifiers stay the same for the whole transaction
Dictionary<ItemPrefab, int> sellValues = GetSellValuesAtCurrentLocation(itemsToSell.Select(i => i.ItemPrefab));
foreach (PurchasedItem item in itemsToSell)
{
var itemValue = item.Quantity * sellValues[item.ItemPrefab];
int itemValue = item.Quantity * sellValues[item.ItemPrefab];
// check if the store can afford the item
if (Location.StoreCurrentBalance < itemValue) { continue; }
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
if (matchingItems.Count() <= item.Quantity)
int count = Math.Min(item.Quantity, matchingItems.Count());
SoldItem.SellOrigin origin = sellingMode == Store.StoreTab.Sell ? SoldItem.SellOrigin.Character : SoldItem.SellOrigin.Submarine;
if (origin == SoldItem.SellOrigin.Character || GameMain.IsSingleplayer)
{
foreach (Item i in matchingItems)
for (int i = 0; i < count; i++)
{
SoldItems.Add(new SoldItem(i.Prefab, i.ID, canAddToRemoveQueue, sellerId));
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(i) : SoldEntity.CreateInMultiPlayer(i));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(i); }
var matchingItem = matchingItems.ElementAt(i);
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId, origin));
SoldEntities.Add(new SoldEntity(matchingItem, campaign.IsSinglePlayer ? SoldEntity.SellStatus.Confirmed : SoldEntity.SellStatus.Local));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
}
}
else
{
for (int i = 0; i < item.Quantity; i++)
// When selling from the sub in multiplayer, the server will determine the items that are sold
for (int i = 0; i < count; i++)
{
var matchingItem = matchingItems.ElementAt(i);
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId));
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(matchingItem) : SoldEntity.CreateInMultiPlayer(matchingItem));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
SoldItems.Add(new SoldItem(item.ItemPrefab, Entity.NullEntityID, canAddToRemoveQueue, sellerId, origin));
SoldEntities.Add(new SoldEntity(item.ItemPrefab, SoldEntity.SellStatus.Local));
}
}
// Exchange money
Location.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier);
// Remove from the sell crate
// TODO: Simplify duplicate logic?
if (sellingMode == Store.StoreTab.Sell && ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } inventoryItem)
if ((sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
{
inventoryItem.Quantity -= item.Quantity;
if (inventoryItem.Quantity < 1)
itemToSell.Quantity -= item.Quantity;
if (itemToSell.Quantity < 1)
{
ItemsInSellCrate.Remove(inventoryItem);
}
}
else if(sellingMode == Store.StoreTab.SellFromSub && ItemsInSellFromSubCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } subItem)
{
subItem.Quantity -= item.Quantity;
if (subItem.Quantity < 1)
{
ItemsInSellFromSubCrate.Remove(subItem);
(sellingMode == Store.StoreTab.Sell ? ItemsInSellCrate : ItemsInSellFromSubCrate)?.Remove(itemToSell);
}
}
}
OnSoldItemsChanged?.Invoke();
}

View File

@@ -83,7 +83,7 @@ namespace Barotrauma
partial void InitProjectSpecific()
{
guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUICanvas.Instance), null, Color.Transparent)
guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), null, Color.Transparent)
{
CanBeFocused = false
};
@@ -145,12 +145,14 @@ namespace Barotrauma
string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg);
// add to local history
ChatBox.ChatManager.Store(text);
WifiComponent headset = null;
ChatMessageType messageType =
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled, out headset)) ? ChatMessageType.Radio : ChatMessageType.Default;
AddSinglePlayerChatMessage(
Character.Controlled.Info.Name,
msg,
((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default,
msg, messageType,
Character.Controlled);
if (ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent headset))
if (messageType == ChatMessageType.Radio && headset != null)
{
Signal s = new Signal(msg, sender: Character.Controlled, source: headset.Item);
headset.TransmitSignal(s, sentFromChat: true);
@@ -302,7 +304,7 @@ namespace Barotrauma
/// </summary>
/// <param name="character">The character to remove</param>
/// <param name="removeInfo">If the character info is also removed, the character will not be visible in the round summary.</param>
public void RemoveCharacter(Character character, bool removeInfo = false)
public void RemoveCharacter(Character character, bool removeInfo = false, bool resetCrewListIndex = true)
{
if (character == null)
{
@@ -311,14 +313,15 @@ namespace Barotrauma
}
characters.Remove(character);
if (removeInfo) { characterInfos.Remove(character.Info); }
if (resetCrewListIndex) { ResetCrewListIndex(character); }
}
/// <summary>
/// Add character to the list without actually adding it to the crew
/// </summary>
public void AddCharacterToCrewList(Character character)
public GUIComponent AddCharacterToCrewList(Character character)
{
if (character == null) { return; }
if (character == null) { return null; }
var background = new GUIFrame(
new RectTransform(crewListEntrySize, parent: crewList.Content.RectTransform, anchor: Anchor.TopRight),
@@ -510,6 +513,8 @@ namespace Barotrauma
return true;
}
};
return background;
}
private void SetCharacterComponentTooltip(GUIComponent characterComponent)
@@ -549,13 +554,13 @@ namespace Barotrauma
if (characterInfos.Contains(revivedCharacter.Info)) { AddCharacter(revivedCharacter); }
}
public void KillCharacter(Character killedCharacter)
public void KillCharacter(Character killedCharacter, bool resetCrewListIndex = true)
{
if (crewList.Content.GetChildByUserData(killedCharacter) is GUIComponent characterComponent)
{
CoroutineManager.StartCoroutine(KillCharacterAnim(characterComponent));
}
RemoveCharacter(killedCharacter);
RemoveCharacter(killedCharacter, resetCrewListIndex: resetCrewListIndex);
}
private IEnumerable<CoroutineStatus> KillCharacterAnim(GUIComponent component)
@@ -601,9 +606,53 @@ namespace Barotrauma
{
if (crewList != this.crewList) { return; }
if (!(draggedElementData is Character)) { return; }
if (crewList.HasDraggedElementIndexChanged) { return; }
if (!IsSinglePlayer) { return; }
CharacterClicked(crewList.DraggedElement, draggedElementData);
if (crewList.HasDraggedElementIndexChanged)
{
UpdateCrewListIndices();
}
else
{
CharacterClicked(crewList.DraggedElement, draggedElementData);
}
}
private void ResetCrewListIndex(Character c)
{
if (c?.Info == null) { return; }
c.Info.CrewListIndex = -1;
UpdateCrewListIndices();
}
private void UpdateCrewListIndices()
{
if (crewList == null) { return; }
for (int i = 0; i < crewList.Content.CountChildren; i++)
{
var characterComponent = crewList.Content.GetChild(i);
if (!(characterComponent?.UserData is Character c)) { continue; }
if (c.Info == null) { continue; }
c.Info.CrewListIndex = i;
}
}
private void SortCrewList()
{
if (crewList == null) { return; }
crewList.Content.RectTransform.SortChildren((x, y) =>
{
var infoX = (x.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
var infoY = (y.GUIComponent.UserData as Character)?.Info?.CrewListIndex;
if (infoX.HasValue)
{
return infoY.HasValue ? infoX.Value.CompareTo(infoY.Value) : -1;
}
else
{
return infoY.HasValue ? 1 : 0;
}
});
UpdateCrewListIndices();
}
#endregion
@@ -718,7 +767,7 @@ namespace Barotrauma
/// Sets the character's current order (if it's close enough to receive messages from orderGiver) and
/// displays the order in the crew UI
/// </summary>
public void SetCharacterOrder(Character character, Order order, string option, int priority, Character orderGiver, Hull targetHull = null)
public void SetCharacterOrder(Character character, Order order, string option, int priority, Character orderGiver, Hull targetHull = null, bool isNewOrder = true)
{
if (order != null && order.TargetAllCharacters)
{
@@ -768,11 +817,11 @@ namespace Barotrauma
if (IsSinglePlayer)
{
orderGiver.Speak(order.GetChatMessage("", hull?.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order);
orderGiver.Speak(order.GetChatMessage("", hull?.DisplayName, givingOrderToSelf: character == orderGiver, isNewOrder: isNewOrder), ChatMessageType.Order);
}
else
{
OrderChatMessage msg = new OrderChatMessage(order, "", priority, order.IsReport ? hull : order.TargetEntity, null, orderGiver);
OrderChatMessage msg = new OrderChatMessage(order, "", priority, order.IsReport ? hull : order.TargetEntity, null, orderGiver, isNewOrder: isNewOrder);
GameMain.Client?.SendChatMessage(msg);
}
}
@@ -784,12 +833,12 @@ namespace Barotrauma
if (IsSinglePlayer)
{
character.SetOrder(order, option, priority, orderGiver, speak: orderGiver != character);
string message = order?.GetChatMessage(character.Name, orderGiver?.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option, priority: priority);
string message = order?.GetChatMessage(character.Name, orderGiver?.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option, isNewOrder: isNewOrder);
orderGiver?.Speak(message);
}
else if (orderGiver != null)
{
OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item, character, orderGiver);
OrderChatMessage msg = new OrderChatMessage(order, option, priority, order?.TargetSpatialEntity ?? order?.TargetItemComponent?.Item, character, orderGiver, isNewOrder: isNewOrder);
GameMain.Client?.SendChatMessage(msg);
}
}
@@ -1071,13 +1120,19 @@ namespace Barotrauma
var priority = Math.Max(CharacterInfo.HighestManualOrderPriority - orderList.Content.GetChildIndex(orderComponent), 1);
if (orderInfo.ManualPriority == priority) { return; }
var character = (Character)orderList.UserData;
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, priority, Character.Controlled);
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, priority, Character.Controlled, isNewOrder: false);
}
private string CreateOrderTooltip(Order orderPrefab, string option, Entity targetEntity)
{
if (orderPrefab == null) { return ""; }
if (!string.IsNullOrEmpty(option))
if (orderPrefab.DisplayGiverInTooltip && orderPrefab.OrderGiver != null)
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
new string[2] { orderPrefab.Name, orderPrefab.OrderGiver.DisplayName });
}
else if (!string.IsNullOrEmpty(option))
{
return TextManager.GetWithVariables("crewlistordericontooltip",
new string[2] { "[ordername]", "[orderoption]" },
@@ -1210,6 +1265,10 @@ namespace Barotrauma
DisableCommandUI();
Character.Controlled = character;
HintManager.OnChangeCharacter();
if (GameSession.TabMenuInstance != null && TabMenu.SelectedTab == TabMenu.InfoFrameTab.Talents)
{
GameSession.TabMenuInstance.SelectInfoFrameTab(TabMenu.SelectedTab);
}
}
private int TryAdjustIndex(int amount)
@@ -1566,10 +1625,28 @@ namespace Barotrauma
{
crewList.Select(character, force: true);
}
// Icon colors might change based on the target so we check if they need to be updated
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
{
foreach (var orderIcon in currentOrderIconList.Content.Children)
{
if (!(orderIcon.UserData is OrderInfo orderInfo)) { continue; }
if (!(orderInfo.Order is Order order)) { continue; }
if (order.ColoredWhenControllingGiver && order.OrderGiver != Character.Controlled)
{
orderIcon.Color = AIObjective.ObjectiveIconColor;
}
else
{
orderIcon.Color = order.Color;
}
}
}
// Only update the order highlights and objective icons here in singleplayer
// The server will let the clients know when they need to update in multiplayer
if (GameMain.IsSingleplayer && character.IsBot && character.AIController is HumanAIController controller &&
controller.ObjectiveManager is AIObjectiveManager objectiveManager)
{
// In multiplayer, these are set through character networking (the server lets the clients now when these are updated)
if (objectiveManager.CurrentObjective is AIObjective currentObjective)
{
if (objectiveManager.IsOrder(currentObjective))
@@ -1638,8 +1715,8 @@ namespace Barotrauma
bool foundMatch = false;
foreach (var orderIcon in currentOrderIconList.Content.Children)
{
var glowComponent = orderIcon.GetChildByUserData("glow");
if (glowComponent == null) { continue; }
if (!(orderIcon.GetChildByUserData("glow") is GUIComponent glowComponent)) { continue; }
glowComponent.Color = orderIcon.Color;
if (foundMatch)
{
glowComponent.Visible = false;
@@ -1684,11 +1761,11 @@ namespace Barotrauma
objectiveIconFrame.ClearChildren();
if (sprite != null)
{
var objectiveIcon = CreateNodeIcon(Vector2.One, objectiveIconFrame.RectTransform, sprite, Color.LightGray, tooltip: tooltip);
var objectiveIcon = CreateNodeIcon(Vector2.One, objectiveIconFrame.RectTransform, sprite, AIObjective.ObjectiveIconColor, tooltip: tooltip);
new GUIFrame(new RectTransform(new Vector2(1.5f), objectiveIcon.RectTransform, anchor: Anchor.Center), style: "OuterGlowCircular")
{
CanBeFocused = false,
Color = Color.LightGray
Color = AIObjective.ObjectiveIconColor
};
objectiveIconFrame.Visible = true;
}
@@ -1909,7 +1986,7 @@ namespace Barotrauma
ScaleCommandUI();
commandFrame = new GUIFrame(
new RectTransform(Vector2.One, GUICanvas.Instance, anchor: Anchor.Center),
new RectTransform(Vector2.One, GUI.Canvas, anchor: Anchor.Center),
style: null,
color: Color.Transparent);
background = new GUIImage(
@@ -2458,6 +2535,7 @@ namespace Barotrauma
{
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, dismissedOrderPrefab, -1));
}
shortcutNodes.RemoveAll(n => n.UserData is Order o && !IsOrderAvailable(o));
if (shortcutNodes.Count < 1) { return; }
shortcutCenterNode = new GUIFrame(new RectTransform(shortcutCenterNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), style: null)
{
@@ -2498,7 +2576,7 @@ namespace Barotrauma
private void CreateOrderNodes(OrderCategory orderCategory)
{
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport);
var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.IsReport && IsOrderAvailable(o));
Order order;
bool disableNode;
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
@@ -2582,15 +2660,12 @@ namespace Barotrauma
{
contextualOrders.Remove(pumpOrderInfo);
}
if (contextualOrders.None())
orderIdentifier = "cleanupitems";
if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)))
{
orderIdentifier = "cleanupitems";
if (contextualOrders.None(info => info.Order.Identifier.Equals(orderIdentifier)))
if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled))
{
if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled))
{
contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled), null));
}
contextualOrders.Add(new OrderInfo(new Order(Order.GetPrefab(orderIdentifier), itemContext, targetItem: null, Character.Controlled), null));
}
}
AddIgnoreOrder(itemContext);
@@ -2653,6 +2728,7 @@ namespace Barotrauma
contextualOrders.Add(new OrderInfo(Order.GetPrefab(orderIdentifier), null));
}
}
contextualOrders.RemoveAll(o => !IsOrderAvailable(o.Order));
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, contextualOrders.Count, MathHelper.ToRadians(90f + 180f / contextualOrders.Count));
bool disableNode = !CanCharacterBeHeard();
for (int i = 0; i < contextualOrders.Count; i++)
@@ -3411,6 +3487,20 @@ namespace Barotrauma
return character?.Info?.GetManualOrderPriority(order) ?? CharacterInfo.HighestManualOrderPriority;
}
private bool IsOrderAvailable(Order order)
{
if (order == null) { return false; }
switch (order.Identifier.ToLowerInvariant())
{
case "assaultenemy":
Character character = characterContext ?? Character.Controlled;
if (character?.Submarine == null) { return false; }
return character.Submarine.GetConnectedSubs().Any(s => s.TeamID != character.TeamID);
default:
return true;
}
}
#region Crew Member Assignment Logic
private bool CanOpenManualAssignment(GUIComponent node)
{
@@ -3487,7 +3577,7 @@ namespace Barotrauma
bool hasLeaks = Character.Controlled.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f);
ToggleReportButton("reportbreach", hasLeaks);
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled));
bool hasIntruders = Character.CharacterList.Any(c => c.CurrentHull == Character.Controlled.CurrentHull && AIObjectiveFightIntruders.IsValidTarget(c, Character.Controlled, false));
ToggleReportButton("reportintruders", hasIntruders);
foreach (GUIComponent reportButton in ReportButtonFrame.Children)
@@ -3523,16 +3613,6 @@ namespace Barotrauma
InitRound();
}
public void EndRound()
{
//remove characterinfos whose characters have been removed or killed
characterInfos.RemoveAll(c => c.Character == null || c.Character.Removed || c.CauseOfDeath != null);
characters.Clear();
crewList.ClearChildren();
GUIContextMenu.CurrentContextMenu = null;
}
public void Reset()
{
characters.Clear();
@@ -3543,12 +3623,14 @@ namespace Barotrauma
public void Save(XElement parentElement)
{
XElement element = new XElement("crew");
foreach (CharacterInfo ci in characterInfos)
for (int i = 0; i < characterInfos.Count; i++)
{
var ci = characterInfos[i];
var infoElement = ci.Save(element);
if (ci.InventoryData != null) { infoElement.Add(ci.InventoryData); }
if (ci.HealthData != null) { infoElement.Add(ci.HealthData); }
if (ci.OrderData != null) { infoElement.Add(ci.OrderData); }
infoElement.Add(new XAttribute("crewlistindex", ci.CrewListIndex));
if (ci.LastControlled) { infoElement.Add(new XAttribute("lastcontrolled", true)); }
}
SaveActiveOrders(element);

View File

@@ -97,17 +97,16 @@ namespace Barotrauma
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
public bool AllowedToManageCampaign()
public bool AllowedToManageCampaign(ClientPermissions permissions = ClientPermissions.ManageCampaign)
{
//allow ending the round if the client has permissions, is the owner, the only client in the server,
//allow managing the round if the client has permissions, is the owner, the only client in the server,
//or if no-one has management permissions
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.HasPermission(permissions) ||
GameMain.Client.ConnectedClients.Count == 1 ||
GameMain.Client.IsServerOwner ||
GameMain.Client.ConnectedClients.None(c =>
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageCampaign)));
GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions)));
}
public override void Draw(SpriteBatch spriteBatch)
@@ -242,6 +241,11 @@ namespace Barotrauma
{
ReadyCheckButton.RectTransform.ScreenSpaceOffset = endRoundButton.RectTransform.ScreenSpaceOffset;
ReadyCheckButton.DrawManually(spriteBatch);
if (ReadyCheck.ReadyCheckCooldown > DateTime.Now)
{
float progress = (ReadyCheck.ReadyCheckCooldown - DateTime.Now).Seconds / 60.0f;
ReadyCheckButton.Color = ToolBox.GradientLerp(progress, Color.White, GUI.Style.Red);
}
}
}
@@ -290,6 +294,9 @@ namespace Barotrauma
case InteractionType.Crew when GameMain.NetworkMember != null:
CampaignUI.CrewManagement.SendCrewState(false);
goto default;
case InteractionType.MedicalClinic:
CampaignUI.MedicalClinic.RequestLatestPending();
goto default;
default:
ShowCampaignUI = true;
CampaignUI.SelectTab(npc.CampaignInteractionType);
@@ -319,6 +326,8 @@ namespace Barotrauma
{
base.Update(deltaTime);
MedicalClinic?.Update(deltaTime);
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData is RoundSummary);

View File

@@ -97,7 +97,7 @@ namespace Barotrauma
partial void InitProjSpecific()
{
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUICanvas.Instance),
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUI.Canvas),
isHorizontal: true, childAnchor: Anchor.CenterRight)
{
CanBeFocused = false
@@ -108,7 +108,7 @@ namespace Barotrauma
buttonCenter = buttonHeight / 2,
screenMiddle = GameMain.GraphicsWidth / 2;
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle - buttonWidth / 2, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, buttonWidth, buttonHeight), GUICanvas.Instance),
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle - buttonWidth / 2, HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, buttonWidth, buttonHeight), GUI.Canvas),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{
Pulse = true,
@@ -145,7 +145,7 @@ namespace Barotrauma
int readyButtonHeight = buttonHeight;
int readyButtonWidth = (int) (GUI.Scale * 50);
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (buttonWidth / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, readyButtonWidth, readyButtonHeight), GUICanvas.Instance),
ReadyCheckButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle(screenMiddle + (buttonWidth / 2) + GUI.IntScale(16), HUDLayoutSettings.ButtonAreaTop.Center.Y - buttonCenter, readyButtonWidth, readyButtonHeight), GUI.Canvas),
style: "RepairBuyButton")
{
ToolTip = TextManager.Get("ReadyCheck.Tooltip"),
@@ -545,14 +545,21 @@ namespace Barotrauma
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, 100);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.ItemsInSellFromSubCrate.Count);
foreach (PurchasedItem pi in CargoManager.ItemsInSellFromSubCrate)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, 100);
msg.WriteRangedInteger(pi.Quantity, 0, CargoManager.MaxQuantity);
}
msg.Write((UInt16)CargoManager.SoldItems.Count);
@@ -562,6 +569,7 @@ namespace Barotrauma
msg.Write((UInt16)si.ID);
msg.Write(si.Removed);
msg.Write(si.SellerID);
msg.Write((byte)si.Origin);
}
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
@@ -640,6 +648,15 @@ namespace Barotrauma
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 subSellCrateItemCount = msg.ReadUInt16();
List<PurchasedItem> subSellCrateItems = new List<PurchasedItem>();
for (int i = 0; i < subSellCrateItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
subSellCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 purchasedItemCount = msg.ReadUInt16();
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
for (int i = 0; i < purchasedItemCount; i++)
@@ -657,7 +674,8 @@ namespace Barotrauma
UInt16 id = msg.ReadUInt16();
bool removed = msg.ReadBoolean();
byte sellerId = msg.ReadByte();
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId));
byte origin = msg.ReadByte();
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId, (SoldItem.SellOrigin)origin));
}
ushort pendingUpgradeCount = msg.ReadUInt16();
@@ -678,13 +696,9 @@ namespace Barotrauma
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
Item itemToRemove = Entity.FindEntityByID(itemToRemoveID) as Item;
string itemToInstallIdentifier = msg.ReadString();
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (itemToRemove == null) { continue; }
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
@@ -728,12 +742,11 @@ namespace Barotrauma
campaign.Map.SelectMission(selectedMissionIndices);
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.CargoManager.SetSoldItems(soldItems);
if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; }
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
{

View File

@@ -108,6 +108,9 @@ namespace Barotrauma
case "pets":
petsElement = subElement;
break;
case "stats":
LoadStats(subElement);
break;
}
}
@@ -167,7 +170,7 @@ namespace Barotrauma
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(450);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance),
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUI.Canvas),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{
Pulse = true,
@@ -412,6 +415,10 @@ namespace Barotrauma
break;
case TransitionType.ProgressToNextLocation:
Map.MoveToNextLocation();
TotalPassedLevels++;
break;
case TransitionType.ProgressToNextEmptyLocation:
TotalPassedLevels++;
break;
}
@@ -728,6 +735,7 @@ namespace Barotrauma
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
new XAttribute("cheatsenabled", CheatsEnabled));
modeElement.Add(Settings.Save());
modeElement.Add(SaveStats());
//save and remove all items that are in someone's inventory so they don't get included in the sub file as well
foreach (Character c in Character.CharacterList)

View File

@@ -220,12 +220,12 @@ namespace Barotrauma.Tutorials
SetHighlight(captain_navConsole.Item, true);
SetHighlight(captain_sonar.Item, true);
SetHighlight(captain_statusMonitor, true);
captain_navConsole.UseAutoDocking = false;
do
{
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
yield return new WaitForSeconds(1.0f, false);
} while (Submarine.MainSub.DockedTo.Any());
captain_navConsole.UseAutoDocking = false;
RemoveCompletedObjective(segments[4]);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Navigate to destination

View File

@@ -172,7 +172,7 @@ namespace Barotrauma
}
else
{
indicator.Visible = Character.Controlled.Info.GetAvailableTalentPoints() > 0;
indicator.Visible = Character.Controlled.Info.GetAvailableTalentPoints() > 0 && !Character.Controlled.HasUnlockedAllTalents();
}
}
}

View File

@@ -12,6 +12,8 @@ namespace Barotrauma
static class HintManager
{
private const string HintManagerFile = "hintmanager.xml";
public static bool Enabled => GameMain.Config != null && !GameMain.Config.DisableInGameHints;
private static HashSet<string> HintIdentifiers { get; set; }
private static Dictionary<string, HashSet<string>> HintTags { get; } = new Dictionary<string, HashSet<string>>();
private static Dictionary<string, (string identifier, string option)> HintOrders { get; } = new Dictionary<string, (string orderIdentifier, string orderOption)>();
@@ -666,6 +668,8 @@ namespace Barotrauma
ActiveHintMessageBox.InnerFrame.Flash(color: iconColor ?? Color.Orange, flashDuration: 0.75f);
onDisplay?.Invoke();
GameAnalyticsManager.AddDesignEvent($"HintManager:{GameMain.GameSession?.GameMode?.Preset?.Identifier ?? "none"}:HintDisplayed:{hintIdentifier}");
return true;
}

View File

@@ -0,0 +1,391 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma
{
internal partial class MedicalClinic
{
public enum RequestResult
{
Undecided,
Success,
Error,
Timeout
}
public readonly struct RequestAction<T>
{
public readonly Action<T> Callback;
public readonly DateTimeOffset Timeout;
public RequestAction(Action<T> callback, DateTimeOffset timeout)
{
Callback = callback;
Timeout = timeout;
}
}
public readonly struct AfflictionRequest
{
public readonly RequestResult Result;
public readonly ImmutableArray<NetAffliction> Afflictions;
public AfflictionRequest(RequestResult result, ImmutableArray<NetAffliction> afflictions)
{
Result = result;
Afflictions = afflictions;
}
}
public readonly struct PendingRequest
{
public readonly RequestResult Result;
public readonly ImmutableArray<NetCrewMember> CrewMembers;
public PendingRequest(RequestResult result, ImmutableArray<NetCrewMember> crewMembers)
{
Result = result;
CrewMembers = crewMembers;
}
}
public readonly struct CallbackOnlyRequest
{
public readonly RequestResult Result;
public CallbackOnlyRequest(RequestResult result)
{
Result = result;
}
}
public readonly struct HealRequest
{
public readonly RequestResult Result;
public readonly HealRequestResult HealResult;
public HealRequest(RequestResult result, HealRequestResult healResult)
{
Result = result;
HealResult = healResult;
}
}
private readonly List<RequestAction<AfflictionRequest>> afflictionRequests = new List<RequestAction<AfflictionRequest>>();
private readonly List<RequestAction<PendingRequest>> pendingHealRequests = new List<RequestAction<PendingRequest>>();
private readonly List<RequestAction<CallbackOnlyRequest>> clearAllRequests = new List<RequestAction<CallbackOnlyRequest>>();
private readonly List<RequestAction<HealRequest>> healAllRequests = new List<RequestAction<HealRequest>>();
private readonly List<RequestAction<CallbackOnlyRequest>> addRequests = new List<RequestAction<CallbackOnlyRequest>>();
private readonly List<RequestAction<CallbackOnlyRequest>> removeRequests = new List<RequestAction<CallbackOnlyRequest>>();
public void RequestAfflictions(CharacterInfo info, Action<AfflictionRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
#if DEBUG && LINUX
if (Screen.Selected is TestScreen)
{
onReceived.Invoke(new AfflictionRequest(RequestResult.Success, TestAfflictions.ToImmutableArray()));
return;
}
#endif
if (!(info is { Character: { CharacterHealth: { } health } }))
{
onReceived.Invoke(new AfflictionRequest(RequestResult.Error, ImmutableArray<NetAffliction>.Empty));
return;
}
ImmutableArray<NetAffliction> pendingAfflictions = GetAllAfflictions(health).ToImmutableArray();
onReceived.Invoke(new AfflictionRequest(RequestResult.Success, pendingAfflictions));
return;
}
afflictionRequests.Add(new RequestAction<AfflictionRequest>(onReceived, GetTimeout()));
SendAfflictionRequest(info);
}
public void RequestLatestPending(Action<PendingRequest> onReceived)
{
// no need to worry about syncing when there's only one pair of eyes capable of looking at the UI
if (GameMain.IsSingleplayer) { return; }
pendingHealRequests.Add(new RequestAction<PendingRequest>(onReceived, GetTimeout()));
SendPendingRequest();
}
public void Update(float deltaTime)
{
DateTimeOffset now = DateTimeOffset.Now;
UpdateQueue(afflictionRequests, now, onTimeout: callback => { callback(new AfflictionRequest(RequestResult.Timeout, ImmutableArray<NetAffliction>.Empty)); });
UpdateQueue(pendingHealRequests, now, onTimeout: callback => { callback(new PendingRequest(RequestResult.Timeout, ImmutableArray<NetCrewMember>.Empty)); });
UpdateQueue(healAllRequests, now, onTimeout: callback => { callback(new HealRequest(RequestResult.Timeout, HealRequestResult.Unknown)); });
UpdateQueue(clearAllRequests, now, onTimeout: CallbackOnlyTimeout);
UpdateQueue(addRequests, now, onTimeout: CallbackOnlyTimeout);
UpdateQueue(removeRequests, now, onTimeout: CallbackOnlyTimeout);
void CallbackOnlyTimeout(Action<CallbackOnlyRequest> callback) { callback(new CallbackOnlyRequest(RequestResult.Timeout)); }
}
public bool IsAfflictionPending(NetCrewMember character, NetAffliction affliction)
{
foreach (NetCrewMember crewMember in PendingHeals)
{
if (!crewMember.CharacterEquals(character)) { continue; }
return crewMember.Afflictions.Any(a => a.AfflictionEquals(affliction));
}
return false;
}
private static bool TryDequeue<T>(List<RequestAction<T>> requestQueue, out Action<T> result)
{
RequestAction<T>? first = requestQueue.FirstOrNull();
if (!(first is { } action))
{
result = _ => { };
return false;
}
requestQueue.Remove(action);
result = action.Callback;
return true;
}
private static void UpdateQueue<T>(List<RequestAction<T>> requestQueue, DateTimeOffset now, Action<Action<T>> onTimeout)
{
HashSet<RequestAction<T>>? removals = null;
foreach (RequestAction<T> action in requestQueue)
{
if (action.Timeout < now)
{
onTimeout.Invoke(action.Callback);
removals ??= new HashSet<RequestAction<T>>();
removals.Add(action);
}
}
if (removals is null) { return; }
foreach (RequestAction<T> action in removals)
{
requestQueue.Remove(action);
}
}
// if you have more than 5000 ping there are probably more important things to worry about but hey just in case
private static DateTimeOffset GetTimeout() => DateTimeOffset.Now.AddSeconds(5).AddMilliseconds(GetPing());
private static int GetPing()
{
if (GameMain.IsSingleplayer || !(GameMain.Client?.Name is { } ownName) || !(GameMain.NetworkMember?.ConnectedClients is { } clients)) { return 0; }
return (from client in clients where client.Name == ownName select client.Ping).FirstOrDefault();
}
public void HealAllButtonAction(Action<HealRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
HealRequestResult result = HealAllPending();
onReceived(new HealRequest(RequestResult.Success, HealAllPending()));
if (result == HealRequestResult.Success)
{
OnUpdate?.Invoke();
}
return;
}
if (campaign?.CampaignUI?.MedicalClinic is { } ui)
{
ui.ClosePopup();
}
healAllRequests.Add(new RequestAction<HealRequest>(onReceived, GetTimeout()));
ClientSend(null, NetworkHeader.HEAL_PENDING, DeliveryMethod.Reliable);
}
public void ClearAllButtonAction(Action<CallbackOnlyRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
ClearPendingHeals();
onReceived(new CallbackOnlyRequest(RequestResult.Success));
OnUpdate?.Invoke();
return;
}
clearAllRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
ClientSend(null, NetworkHeader.CLEAR_PENDING, DeliveryMethod.Reliable);
}
private void ClearRequstReceived()
{
ClearPendingHeals();
if (TryDequeue(clearAllRequests, out var callback))
{
callback(new CallbackOnlyRequest(RequestResult.Success));
}
OnUpdate?.Invoke();
}
private void HealRequestReceived(IReadMessage inc)
{
NetHealRequest request = INetSerializableStruct.Read<NetHealRequest>(inc);
if (request.Result == HealRequestResult.Success)
{
HealAllPending(force: true);
}
if (TryDequeue(healAllRequests, out var callback))
{
callback(new HealRequest(RequestResult.Success, request.Result));
}
OnUpdate?.Invoke();
}
public void AddPendingButtonAction(NetCrewMember crewMember, Action<CallbackOnlyRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
InsertPendingCrewMember(crewMember);
onReceived(new CallbackOnlyRequest(RequestResult.Success));
OnUpdate?.Invoke();
return;
}
addRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
ClientSend(crewMember, NetworkHeader.ADD_PENDING, DeliveryMethod.Reliable);
}
public void RemovePendingButtonAction(NetCrewMember crewMember, NetAffliction affliction, Action<CallbackOnlyRequest> onReceived)
{
if (GameMain.IsSingleplayer)
{
RemovePendingAffliction(crewMember, affliction);
onReceived(new CallbackOnlyRequest(RequestResult.Success));
OnUpdate?.Invoke();
return;
}
INetSerializableStruct removedAffliction = new NetRemovedAffliction
{
CrewMember = crewMember,
Affliction = affliction
};
removeRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
ClientSend(removedAffliction, NetworkHeader.REMOVE_PENDING, DeliveryMethod.Reliable);
}
private void NewAdditonReceived(IReadMessage inc, MessageFlag flag)
{
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
InsertPendingCrewMember(crewMember);
if (flag == MessageFlag.Response && TryDequeue(addRequests, out var callback))
{
callback(new CallbackOnlyRequest(RequestResult.Success));
}
OnUpdate?.Invoke();
}
private void NewRemovalReceived(IReadMessage inc, MessageFlag flag)
{
NetRemovedAffliction removed = INetSerializableStruct.Read<NetRemovedAffliction>(inc);
RemovePendingAffliction(removed.CrewMember, removed.Affliction);
if (flag == MessageFlag.Response && TryDequeue(removeRequests, out var callback))
{
callback(new CallbackOnlyRequest(RequestResult.Success));
}
OnUpdate?.Invoke();
}
private static void SendAfflictionRequest(CharacterInfo info)
{
INetSerializableStruct crewMember = new NetCrewMember
{
CharacterInfo = info,
Afflictions = Array.Empty<NetAffliction>()
};
ClientSend(crewMember, NetworkHeader.REQUEST_AFFLICTIONS, DeliveryMethod.Unreliable);
}
private static void SendPendingRequest()
{
ClientSend(null, NetworkHeader.REQUEST_PENDING, DeliveryMethod.Reliable);
}
private void AfflictionRequestReceived(IReadMessage inc)
{
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
if (TryDequeue(afflictionRequests, out var callback))
{
RequestResult result = crewMember.CharacterInfoID == 0 ? RequestResult.Error : RequestResult.Success;
callback(new AfflictionRequest(result, crewMember.Afflictions.ToImmutableArray()));
}
}
private void PendingRequestReceived(IReadMessage inc)
{
NetPendingCrew pendingCrew = INetSerializableStruct.Read<NetPendingCrew>(inc);
if (TryDequeue(pendingHealRequests, out var callback))
{
callback(new PendingRequest(RequestResult.Success, pendingCrew.CrewMembers.ToImmutableArray()));
}
}
private static IWriteMessage StartSending()
{
IWriteMessage writeMessage = new WriteOnlyMessage();
writeMessage.Write((byte)ClientPacketHeader.MEDICAL);
return writeMessage;
}
private static void ClientSend(INetSerializableStruct? netStruct, NetworkHeader header, DeliveryMethod deliveryMethod)
{
IWriteMessage msg = StartSending();
msg.Write((byte)header);
netStruct?.Write(msg);
GameMain.Client.ClientPeer?.Send(msg, deliveryMethod);
}
public void ClientRead(IReadMessage inc)
{
NetworkHeader header = (NetworkHeader)inc.ReadByte();
MessageFlag flag = (MessageFlag)inc.ReadByte();
switch (header)
{
case NetworkHeader.REQUEST_AFFLICTIONS:
AfflictionRequestReceived(inc);
break;
case NetworkHeader.REQUEST_PENDING:
PendingRequestReceived(inc);
break;
case NetworkHeader.ADD_PENDING:
NewAdditonReceived(inc, flag);
break;
case NetworkHeader.REMOVE_PENDING:
NewRemovalReceived(inc, flag);
break;
case NetworkHeader.HEAL_PENDING:
HealRequestReceived(inc);
break;
case NetworkHeader.CLEAR_PENDING:
ClearRequstReceived();
break;
}
}
}
}

View File

@@ -31,7 +31,7 @@ namespace Barotrauma
private GUIMessageBox? msgBox;
private GUIMessageBox? resultsBox;
public static DateTime lastReadyCheck = DateTime.MinValue;
public static DateTime ReadyCheckCooldown = DateTime.MinValue;
public static bool IsReadyCheck(GUIComponent? msgBox) => msgBox?.UserData as string == PromptData || msgBox?.UserData as string == ResultData;
@@ -273,10 +273,10 @@ namespace Barotrauma
public static void CreateReadyCheck()
{
if (lastReadyCheck < DateTime.Now)
if (ReadyCheckCooldown < DateTime.Now)
{
#if !DEBUG
lastReadyCheck = DateTime.Now.AddMinutes(1);
ReadyCheckCooldown = DateTime.Now.AddMinutes(1);
#endif
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte) ClientPacketHeader.READY_CHECK);
@@ -285,7 +285,7 @@ namespace Barotrauma
return;
}
GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, readyCheckPleaseWait((lastReadyCheck - DateTime.Now).Seconds), new[] { closeButton });
GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, readyCheckPleaseWait((ReadyCheckCooldown - DateTime.Now).Seconds), new[] { closeButton });
msgBox.Buttons[0].OnClicked = delegate
{
msgBox.Close();

View File

@@ -524,6 +524,7 @@ namespace Barotrauma
return true;
};
#if !OSX
var statisticsTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.045f), leftPanel.RectTransform), TextManager.Get("statisticsconsenttickbox"))
{
OnSelected = (GUITickBox tickBox) =>
@@ -562,6 +563,15 @@ namespace Barotrauma
statisticsTickBox.OnSelected = prevHandler;
statisticsTickBox.Enabled = GameAnalyticsManager.UserConsented != GameAnalyticsManager.Consent.Error;
});
#endif
foreach (var child in leftPanel.Children)
{
if (child is GUITextBlock textBlock)
{
textBlock.RectTransform.MinSize = new Point(textBlock.RectTransform.MinSize.X, (int)Math.Max(textBlock.RectTransform.MinSize.Y, textBlock.TextSize.Y));
}
}
// right panel --------------------------------------
@@ -597,6 +607,10 @@ namespace Barotrauma
OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; }
};
tabButtons[(int)tab].Text = ToolBox.LimitString(buttonText, tabButtons[(int)tab].Font, (int)(0.75f * tabWidth * tabButtonHolder.Rect.Width));
if (tabButtons[(int)tab].Text != buttonText)
{
tabButtons[(int)tab].ToolTip = buttonText;
}
}
/// Graphics tab --------------------------------------------------------------

View File

@@ -153,12 +153,6 @@ namespace Barotrauma
return container.Inventory;
}
protected override void PutItem(Item item, int i, Character user, bool removeItem = true, bool createNetworkEvent = true)
{
base.PutItem(item, i, user, removeItem, createNetworkEvent);
CreateSlots();
}
public override void CreateSlots()
{
visualSlots ??= new VisualSlot[capacity];
@@ -504,6 +498,13 @@ namespace Barotrauma
{
HUDLayoutSettings.InventoryTopY = visualSlots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale);
}
else
{
for (int i = 0; i < capacity; i++)
{
visualSlots[i].DrawOffset = Vector2.Zero;
}
}
}
protected override void ControlInput(Camera cam)
@@ -789,7 +790,7 @@ namespace Barotrauma
if (quickUseAction != QuickUseAction.Drop)
{
slot.QuickUseButtonToolTip = quickUseAction == QuickUseAction.None ?
"" : TextManager.GetWithVariable("QuickUseAction." + quickUseAction.ToString(), "[equippeditem]", item?.Name);
"" : TextManager.GetWithVariable("QuickUseAction." + quickUseAction.ToString(), "[equippeditem]", character.HeldItems.FirstOrDefault()?.Name ?? item?.Name);
if (PlayerInput.PrimaryMouseButtonDown()) { slot.EquipButtonState = GUIComponent.ComponentState.Pressed; }
if (PlayerInput.PrimaryMouseButtonClicked())
{
@@ -963,7 +964,9 @@ namespace Barotrauma
{
return QuickUseAction.TakeFromCharacter;
}
else if (character.HeldItems.Any(i => i.OwnInventory != null && i.OwnInventory.CanBePut(item)) && allowInventorySwap)
else if (character.HeldItems.Any(i =>
i.OwnInventory != null &&
(i.OwnInventory.CanBePut(item) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
{
return QuickUseAction.PutToEquippedItem;
}
@@ -1128,8 +1131,9 @@ namespace Barotrauma
case QuickUseAction.PutToEquippedItem:
foreach (Item heldItem in character.HeldItems)
{
if (heldItem.OwnInventory != null &&
heldItem.OwnInventory.TryPutItem(item, Character.Controlled))
if (heldItem.OwnInventory == null) { continue; }
if (heldItem.OwnInventory.TryPutItem(item, Character.Controlled) ||
(heldItem.OwnInventory.Capacity == 1 && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: true, allowCombine: false, user: Character.Controlled)))
{
success = true;
for (int j = 0; j < capacity; j++)

View File

@@ -28,8 +28,8 @@ namespace Barotrauma.Items.Components
}
case AreaShape.Circle:
Vector2 center = item.WorldPosition;
center.Y = -center.Y;
center += SpawnAreaOffset;
center.Y = -center.Y;
spriteBatch.DrawCircle(center, SpawnAreaRadius, 32, GUI.Style.Red, thickness: 4f);
if (MaximumAmountRangePadding > 0f)
@@ -51,8 +51,8 @@ namespace Barotrauma.Items.Components
}
case AreaShape.Circle:
Vector2 center = item.WorldPosition;
center.Y = -center.Y;
center += CrewAreaOffset;
center.Y = -center.Y;
spriteBatch.DrawCircle(center, CrewAreaRadius, 32, GUI.Style.Green);
break;
}

View File

@@ -256,6 +256,10 @@ namespace Barotrauma.Items.Components
{
targetHull.IncreaseSectionColorOrStrength(targetSections[i], color, sizeAdjustedSprayStrength * deltaTime, true, false);
}
if (GameMain.GameSession != null)
{
GameMain.GameSession.TimeSpentCleaning += deltaTime;
}
}
else
{
@@ -263,6 +267,10 @@ namespace Barotrauma.Items.Components
{
targetHull.CleanSection(targetSections[i], -sizeAdjustedSprayStrength * deltaTime, true);
}
if (GameMain.GameSession != null)
{
GameMain.GameSession.TimeSpentPainting += deltaTime;
}
}
Vector2 particleStartPos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);

View File

@@ -143,7 +143,7 @@ namespace Barotrauma.Items.Components
}
}
public GUIFrame GuiFrame { get; protected set; }
public GUIFrame GuiFrame { get; set; }
[Serialize(false, false)]
public bool AllowUIOverlap
@@ -390,10 +390,10 @@ namespace Barotrauma.Items.Components
if (SerializableProperties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
{
float newVolume = 0.0f;
float newVolume;
try
{
newVolume = (float)property.GetValue(this);
newVolume = property.GetFloatValue(this);
}
catch
{
@@ -554,7 +554,7 @@ namespace Barotrauma.Items.Components
color = GuiFrameSource.GetAttributeColor("color", Color.White);
}
string style = GuiFrameSource.Attribute("style") == null ? null : GuiFrameSource.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas.ItemComponentHolder, Anchor.Center), style, color);
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas, Anchor.Center), style, color);
DefaultLayout = GUILayoutSettings.Load(GuiFrameSource);
if (GuiFrame != null)
{

View File

@@ -76,6 +76,9 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false, description: "If true, the contained state indicator calculates how full the item is based on the total amount of items that can be stacked inside it, as opposed to how many of the inventory slots are occupied.")]
public bool ShowTotalStackCapacityInContainedStateIndicator { get; set; }
[Serialize(false, false, description: "Should the inventory of this item be kept open when the item is equipped by a character.")]
public bool KeepOpenWhenEquipped { get; set; }

View File

@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
}
}
partial void SetLightSourceTransform()
partial void SetLightSourceTransformProjSpecific()
{
if (ParentBody != null)
{

View File

@@ -6,21 +6,23 @@ namespace Barotrauma.Items.Components
{
partial class Controller : ItemComponent
{
private bool chatBoxOriginalState;
private bool isHUDsHidden;
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (focusTarget != null && character.ViewTarget == focusTarget)
{
foreach (ItemComponent ic in focusTarget.Components)
{
ic.DrawHUD(spriteBatch, character);
if (ic.ShouldDrawHUD(character))
{
ic.DrawHUD(spriteBatch, character);
}
}
}
}
private bool crewAreaOriginalState;
private bool chatBoxOriginalState;
private bool isHUDsHidden;
partial void HideHUDs(bool value)
{
if (isHUDsHidden == value) { return; }

View File

@@ -63,7 +63,7 @@ namespace Barotrauma.Items.Components
Stretch = true,
RelativeSpacing = 0.05f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelArea.RectTransform), TextManager.Get("uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, inputLabelArea.RectTransform), TextManager.Get("deconstructor.input", fallBackTag: "uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, inputLabelArea.RectTransform), style: "HorizontalLine");

View File

@@ -132,7 +132,7 @@ namespace Barotrauma.Items.Components
Stretch = true,
RelativeSpacing = 0.03f
};
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, separatorArea.RectTransform), TextManager.Get("uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
var inputLabel = new GUITextBlock(new RectTransform(Vector2.One, separatorArea.RectTransform), TextManager.Get("fabricator.input", fallBackTag: "uilabel.input"), font: GUI.SubHeadingFont) { Padding = Vector4.Zero };
inputLabel.RectTransform.Resize(new Point((int) inputLabel.Font.MeasureString(inputLabel.Text).X, inputLabel.RectTransform.Rect.Height));
new GUIFrame(new RectTransform(Vector2.One, separatorArea.RectTransform), style: "HorizontalLine");
@@ -302,6 +302,12 @@ namespace Barotrauma.Items.Components
}
HideEmptyItemListCategories();
if (selectedItem != null)
{
//reselect to recreate the info based on the new user's skills
SelectItem(character, selectedItem);
}
}
private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
@@ -343,13 +349,14 @@ namespace Barotrauma.Items.Components
foreach (Item it in availableItems)
{
if (it.ParentInventory == inputContainer.Inventory) { continue; }
var rootContainer = it.GetRootContainer();
if (rootContainer?.OwnInventory?.visualSlots == null) { continue; }
int availableSlotIndex = rootContainer.OwnInventory.FindIndex(it.Container == rootContainer ? it : it.Container);
var rootInventoryOwner = it.GetRootInventoryOwner();
Inventory rootInventory = (rootInventoryOwner as Item)?.OwnInventory as Inventory ?? (rootInventoryOwner as Character)?.Inventory;
if (rootInventory?.visualSlots == null) { continue; }
int availableSlotIndex = rootInventory.FindIndex((it.Container != rootInventoryOwner ? it.Container : it) ?? it);
if (availableSlotIndex < 0) { continue; }
if (rootContainer.OwnInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
if (rootInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
rootContainer.OwnInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
rootInventory.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);
@@ -406,9 +413,16 @@ namespace Barotrauma.Items.Components
{
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
}
else if(requiredItem.MaxCondition < 1.0f)
else if (requiredItem.MaxCondition < 1.0f)
{
toolTipText += " 0-" + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
if (requiredItem.MaxCondition <= 0.0f)
{
toolTipText += " " + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
}
else
{
toolTipText += " 0-" + (int)Math.Round(requiredItem.MaxCondition * 100) + "%";
}
}
else if (requiredItem.MaxCondition <= 0.0f)
{
@@ -524,16 +538,6 @@ namespace Barotrauma.Items.Components
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
var paddedReqFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), selectedItemReqsFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.03f };
/*var itemIcon = selectedItem.TargetItem.InventoryIcon ?? selectedItem.TargetItem.sprite;
if (itemIcon != null)
{
GUIImage img = new GUIImage(new RectTransform(new Point(40, 40), paddedFrame.RectTransform),
itemIcon, scaleToFit: true)
{
Color = selectedItem.TargetItem.InventoryIconColor
};
}*/
string itemName = GetRecipeNameAndAmount(selectedItem);
string name = itemName;
@@ -732,8 +736,6 @@ namespace Barotrauma.Items.Components
Character user = Entity.FindEntityByID(userID) as Character;
State = newState;
timeUntilReady = newTimeUntilReady;
if (newState == FabricatorState.Stopped || itemIndex == -1)
{
CancelFabricating();
@@ -747,6 +749,7 @@ namespace Barotrauma.Items.Components
SelectItem(user, fabricationRecipes[itemIndex]);
StartFabricating(fabricationRecipes[itemIndex], user);
}
timeUntilReady = newTimeUntilReady;
}
}
}

View File

@@ -507,7 +507,7 @@ namespace Barotrauma.Items.Components
{
Vector2 origin = weaponSprite.Origin;
float scale = parentWidth / Math.Max(weaponSprite.size.X, weaponSprite.size.Y);
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? GUI.Style.Red : GUI.Style.Green;
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? Color.DimGray : GUI.Style.Green;
weaponSprite.Draw(batch, center, color, origin, rotation, scale, it.SpriteEffects);
}
});
@@ -537,9 +537,9 @@ namespace Barotrauma.Items.Components
if (item.Submarine == null && displayedSubs.Count > 0 || // item not inside a sub anymore, but display is still showing subs
item.Submarine is { } itemSub &&
(
!displayedSubs.Contains(itemSub) || // current sub not displayed
itemSub.DockedTo.Any(s => !displayedSubs.Contains(s)) || // some of the docked subs not displayed
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s)) // displaying a sub that shouldn't be displayed
!displayedSubs.Contains(itemSub) || // current sub not displayed
itemSub.DockedTo.Any(s => !displayedSubs.Contains(s) && itemSub.ConnectedDockingPorts[s].IsLocked) || // some of the docked subs not displayed
displayedSubs.Any(s => s != itemSub && !itemSub.DockedTo.Contains(s)) // displaying a sub that shouldn't be displayed
) ||
prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight || // resolution changed
!submarineContainer.Children.Any()) // We lack a GUI
@@ -1092,6 +1092,12 @@ namespace Barotrauma.Items.Components
if (!(entity is Item it)) { continue; }
if (!electricalChildren.TryGetValue(miniMapGuiComponent, out GUIComponent component)) { continue; }
if (entity.Removed)
{
component.Visible = false;
continue;
}
if (item.Submarine == null || !hasPower)
{
component.Color = component.OutlineColor = NoPowerElectricalColor;
@@ -1117,7 +1123,7 @@ namespace Barotrauma.Items.Components
int current = (int)-powerTransfer.CurrPowerConsumption, load = (int)powerTransfer.PowerLoad;
line1 = TextManager.GetWithVariable("statusmonitor.junctionpower.tooltip", "[amount]", current.ToString(), fallBackTag: "statusmonitor.junctioncurrent.tooltip");
line2 = TextManager.GetWithVariable("statusmonitor.junctionload.tooltip", "[amount]", load.ToString());
line2 = TextManager.GetWithVariables("statusmonitor.junctionload.tooltip", new string[] { "[amount]", "[load]" }, new string[] { load.ToString(), load.ToString() });
}
string line3 = TextManager.GetWithVariable("statusmonitor.durability.tooltip", "[amount]", durability.ToString());
@@ -1329,7 +1335,7 @@ namespace Barotrauma.Items.Components
RectangleF entityRect = ScaleRectToUI(structure, parent, border);
Vector2 spriteScale = new Vector2(entityRect.Size.X / sprite.size.X, entityRect.Size.Y / sprite.size.Y);
sprite.Draw(spriteBatch, new Vector2(entityRect.Location.X + inflate, entityRect.Location.Y + inflate), structure.SpriteColor, Vector2.Zero, 0f, spriteScale, structure.SpriteEffects);
sprite.Draw(spriteBatch, new Vector2(entityRect.Location.X + inflate, entityRect.Location.Y + inflate), structure.SpriteColor, Vector2.Zero, 0f, spriteScale, sprite.effects ^ structure.SpriteEffects);
}
private static RectangleF ScaleRectToUI(MapEntity entity, RectangleF parentRect, RectangleF worldBorders)
@@ -1414,10 +1420,11 @@ namespace Barotrauma.Items.Components
Vector2 drawPos = new Vector2(frame.Rect.Right - sizeX, frame.Rect.Y - sizeY / 2f);
UISprite icon = GUI.Style.IconOverflowIndicator;
const int iconPadding = 4;
icon.Draw(spriteBatch, new Rectangle((int) drawPos.X - iconPadding, (int) drawPos.Y - iconPadding, (int) maxWidth + iconPadding * 2, (int) maxWidth + iconPadding * 2), Color.White, SpriteEffects.None);
if (icon != null)
{
const int iconPadding = 4;
icon.Draw(spriteBatch, new Rectangle((int) drawPos.X - iconPadding, (int) drawPos.Y - iconPadding, (int) maxWidth + iconPadding * 2, (int) maxWidth + iconPadding * 2), Color.White, SpriteEffects.None);
}
GUI.DrawString(spriteBatch, drawPos, text, GUI.Style.TextColor, font: GUI.SubHeadingFont);
}
break;
@@ -1712,5 +1719,20 @@ namespace Barotrauma.Items.Components
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
if (searchAutoComplete != null)
{
searchAutoComplete.RectTransform.Parent = null;
searchAutoComplete = null;
}
if (hullInfoFrame != null)
{
hullInfoFrame.RectTransform.Parent = null;
hullInfoFrame = null;
}
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Barotrauma.Items.Components
if (selectionUI == null)
{
selectionUI = new SubmarineSelection(true, null, GUICanvas.Instance.ItemComponentHolder);
selectionUI = new SubmarineSelection(true, null, GUI.Canvas);
}
GuiFrame = selectionUI.GuiFrame;
@@ -35,5 +35,15 @@ namespace Barotrauma.Items.Components
selectionUI?.Update();
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
if (selectionUI != null)
{
selectionUI.GuiFrame.RectTransform.Parent = null;
selectionUI = null;
}
}
}
}

View File

@@ -19,15 +19,6 @@ namespace Barotrauma.Items.Components
private readonly List<(Vector2 position, ParticleEmitter emitter)> pumpOutEmitters = new List<(Vector2 position, ParticleEmitter emitter)>();
private readonly List<(Vector2 position, ParticleEmitter emitter)> pumpInEmitters = new List<(Vector2 position, ParticleEmitter emitter)>();
public float CurrentBrokenVolume
{
get
{
if (item.ConditionPercentage > 10.0f || !IsActive) { return 0.0f; }
return (1.0f - item.ConditionPercentage / 10.0f) * 100.0f;
}
}
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
@@ -193,8 +184,6 @@ namespace Barotrauma.Items.Components
private readonly float flickerFrequency = 1;
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
pumpSpeedLockTimer -= deltaTime;
isActiveLockTimer -= deltaTime;
autoControlIndicator.Selected = IsAutoControlled;
PowerButton.Enabled = isActiveLockTimer <= 0.0f;
if (HasPower)

View File

@@ -83,9 +83,7 @@ namespace Barotrauma.Items.Components
private const float ConnectedSubUpdateInterval = 1.0f;
float connectedSubUpdateTimer;
//Vector2 = vector from the ping source to the position of the disruption
//float = strength of the disruption, between 0-1
private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>();
private readonly List<(Vector2 pos, float strength)> disruptedDirections = new List<(Vector2 pos, float strength)>();
private readonly Dictionary<object, CachedDistance> markerDistances = new Dictionary<object, CachedDistance>();
@@ -350,6 +348,27 @@ namespace Barotrauma.Items.Components
}
}
private Vector2 GetTransducerPos()
{
if (!UseTransducers || connectedTransducers.Count == 0)
{
//use the position of the sub if the item is static (no body) and inside a sub
return item.Submarine != null && item.body == null ? item.Submarine.WorldPosition : item.WorldPosition;
}
Vector2 transducerPosSum = Vector2.Zero;
foreach (ConnectedTransducer transducer in connectedTransducers)
{
if (transducer.Transducer.Item.Submarine != null && !CenterOnTransducers)
{
return transducer.Transducer.Item.Submarine.WorldPosition;
}
transducerPosSum += transducer.Transducer.Item.WorldPosition;
}
return transducerPosSum / connectedTransducers.Count;
}
public override void OnItemLoaded()
{
base.OnItemLoaded();
@@ -455,7 +474,20 @@ namespace Barotrauma.Items.Components
zoomSlider.OnMoved(zoomSlider, zoomSlider.BarScroll);
}
}
Vector2 transducerCenter = GetTransducerPos();
if (steering != null && steering.DockingModeEnabled && steering.ActiveDockingSource != null)
{
Vector2 worldFocusPos = (steering.ActiveDockingSource.Item.WorldPosition + steering.DockingTarget.Item.WorldPosition) / 2.0f;
DisplayOffset = Vector2.Lerp(DisplayOffset, worldFocusPos - transducerCenter, 0.1f);
}
else
{
DisplayOffset = Vector2.Lerp(DisplayOffset, Vector2.Zero, 0.1f);
}
transducerCenter += DisplayOffset;
float distort = MathHelper.Clamp(1.0f - item.Condition / item.MaxCondition, 0.0f, 1.0f);
for (int i = sonarBlips.Count - 1; i >= 0; i--)
{
@@ -502,8 +534,6 @@ namespace Barotrauma.Items.Components
return;
}
Vector2 transducerCenter = GetTransducerPos() + DisplayOffset;
if (Level.Loaded != null)
{
nearbyObjectUpdateTimer -= deltaTime;
@@ -829,8 +859,7 @@ namespace Barotrauma.Items.Components
}
}
Vector2 transducerCenter = GetTransducerPos();
Vector2 transducerCenter = GetTransducerPos();// + DisplayOffset;
if (sonarBlips.Count > 0)
{
@@ -840,7 +869,7 @@ namespace Barotrauma.Items.Components
foreach (SonarBlip sonarBlip in sonarBlips)
{
DrawBlip(spriteBatch, sonarBlip, transducerCenter, center, sonarBlip.FadeTimer / 2.0f * signalStrength, blipScale);
DrawBlip(spriteBatch, sonarBlip, transducerCenter + DisplayOffset, center, sonarBlip.FadeTimer / 2.0f * signalStrength, blipScale);
}
spriteBatch.End();
@@ -849,8 +878,8 @@ namespace Barotrauma.Items.Components
if (item.Submarine != null && !DetectSubmarineWalls)
{
DrawDockingPorts(spriteBatch, transducerCenter, signalStrength);
transducerCenter += DisplayOffset;
DrawDockingPorts(spriteBatch, transducerCenter, signalStrength);
DrawOwnSubmarineBorders(spriteBatch, transducerCenter, signalStrength);
}
else
@@ -1083,14 +1112,11 @@ namespace Barotrauma.Items.Components
{
DrawDockingIndicator(spriteBatch, steering, ref transducerCenter);
}
else
{
DisplayOffset = Vector2.Lerp(DisplayOffset, Vector2.Zero, 0.1f);
}
foreach (DockingPort dockingPort in DockingPort.List)
{
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
if (dockingPort.Item.HiddenInGame) { continue; }
if (dockingPort.Item.Submarine == null) { continue; }
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
@@ -1131,9 +1157,6 @@ namespace Barotrauma.Items.Components
Vector2 worldFocusPos = (steering.ActiveDockingSource.Item.WorldPosition + steering.DockingTarget.Item.WorldPosition) / 2.0f;
worldFocusPos.X = steering.DockingTarget.Item.WorldPosition.X;
DisplayOffset = Vector2.Lerp(DisplayOffset, worldFocusPos - transducerCenter, 0.1f);
transducerCenter += DisplayOffset;
Vector2 sourcePortDiff = (steering.ActiveDockingSource.Item.WorldPosition - transducerCenter) * scale;
Vector2 sourcePortPos = new Vector2(sourcePortDiff.X, -sourcePortDiff.Y);
Vector2 targetPortDiff = (steering.DockingTarget.Item.WorldPosition - transducerCenter) * scale;
@@ -1234,7 +1257,7 @@ namespace Barotrauma.Items.Components
Vector2 disruptionPos = new Vector2(levelObject.Position.X, levelObject.Position.Y);
float disruptionDist = Vector2.Distance(pingSource, disruptionPos);
disruptedDirections.Add(new Pair<Vector2, float>((disruptionPos - pingSource) / disruptionDist, disruptionStrength));
disruptedDirections.Add(((disruptionPos - pingSource) / disruptionDist, disruptionStrength));
CreateBlipsForDisruption(disruptionPos, disruptionStrength);
@@ -1246,7 +1269,7 @@ namespace Barotrauma.Items.Components
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
if (distSqr > worldPingRadiusSqr) { continue; }
float disruptionDist = (float)Math.Sqrt(distSqr);
disruptedDirections.Add(new Pair<Vector2, float>((aiTarget.WorldPosition - pingSource) / disruptionDist, aiTarget.SonarDisruption));
disruptedDirections.Add(((aiTarget.WorldPosition - pingSource) / disruptionDist, aiTarget.SonarDisruption));
CreateBlipsForDisruption(aiTarget.WorldPosition, disruption);
}
}
@@ -1461,10 +1484,10 @@ namespace Barotrauma.Items.Components
float transducerDist = transducerDiff.Length();
Vector2 pingDirection = transducerDiff / transducerDist;
bool disrupted = false;
foreach (Pair<Vector2, float> disruptDir in disruptedDirections)
foreach ((Vector2 disruptPos, float disruptStrength) in disruptedDirections)
{
float dot = Vector2.Dot(pingDirection, disruptDir.First);
if (dot > 1.0f - disruptDir.Second)
float dot = Vector2.Dot(pingDirection, disruptPos);
if (dot > 1.0f - disruptStrength)
{
disrupted = true;
break;
@@ -1670,13 +1693,12 @@ namespace Barotrauma.Items.Components
}
}
if (iconIdentifier == null || !targetIcons.ContainsKey(iconIdentifier))
if (iconIdentifier == null || !targetIcons.TryGetValue(iconIdentifier, out var iconInfo) || iconInfo.Item1 == null)
{
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X - 3, (int)markerPos.Y - 3, 6, 6), markerColor, thickness: 2);
}
else
{
var iconInfo = targetIcons[iconIdentifier];
iconInfo.Item1.Draw(spriteBatch, markerPos, iconInfo.Item2);
}
@@ -1689,7 +1711,10 @@ namespace Barotrauma.Items.Components
Vector2 textSize = GUI.SmallFont.MeasureString(wrappedLabel);
//flip the text to left side when the marker is on the left side or goes outside the right edge of the interface
if ((dir.X < 0.0f || labelPos.X + textSize.X + 10 > GuiFrame.Rect.X) && labelPos.X - textSize.X > 0) labelPos.X -= textSize.X + 10;
if (GuiFrame != null && (dir.X < 0.0f || labelPos.X + textSize.X + 10 > GuiFrame.Rect.X) && labelPos.X - textSize.X > 0)
{
labelPos.X -= textSize.X + 10;
}
GUI.DrawString(spriteBatch,
new Vector2(labelPos.X + 10, labelPos.Y),

View File

@@ -403,7 +403,7 @@ namespace Barotrauma.Items.Components
{
if (GameMain.Client == null)
{
item.SendSignal("1", "toggle_docking");
item.SendSignal(new Signal("1", sender: Character.Controlled), "toggle_docking");
}
else
{
@@ -682,7 +682,7 @@ namespace Barotrauma.Items.Components
enterOutpostPrompt?.Close();
}
}
else if (DockingSources.Any(d => d.Docked))
else if (connectedPorts.Any(d => d.Docked))
{
dockingButton.Text = undockText;
dockingContainer.Visible = true;
@@ -819,7 +819,7 @@ namespace Barotrauma.Items.Components
Connection dockingConnection = item.Connections?.FirstOrDefault(c => c.Name == "toggle_docking");
if (dockingConnection != null)
{
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection);
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection, ignoreInactiveRelays: true);
}
checkConnectedPortsTimer = CheckConnectedPortsInterval;
}

View File

@@ -10,6 +10,12 @@ namespace Barotrauma.Items.Components
private GUIProgressBar chargeIndicator;
private GUIScrollBar rechargeSpeedSlider;
[Serialize(0.0f, true)]
public float RechargeWarningIndicatorLow { get; set; }
[Serialize(0.0f, true)]
public float RechargeWarningIndicatorHigh { get; set; }
public Vector2 DrawSize
{
//use the extents of the item as the draw size
@@ -28,19 +34,38 @@ namespace Barotrauma.Items.Components
var upperArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), paddedFrame.RectTransform, Anchor.TopCenter), style: null);
var lowerArea = new GUIFrame(new RectTransform(new Vector2(1, 0.6f), paddedFrame.RectTransform, Anchor.BottomCenter), style: null);
string rechargeStr = TextManager.Get("PowerContainerRechargeRate");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), upperArea.RectTransform, Anchor.TopCenter),
"RechargeRate", textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.Center)
var rechargeRateContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), upperArea.RectTransform), style: null);
var rechargeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), rechargeRateContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get("rechargerate"), textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
string kW = TextManager.Get("kilowatt");
var rechargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), rechargeRateContainer.RectTransform, Anchor.CenterRight),
"", textColor: GUI.Style.TextColor, font: GUI.Font, textAlignment: Alignment.CenterRight)
{
TextGetter = () =>
{
return rechargeStr.Replace("[rate]", ((int)((rechargeSpeed / maxRechargeSpeed) * 100.0f)).ToString());
}
TextGetter = () => $"{(int)MathF.Round(currPowerConsumption)} {kW} ({(int)MathF.Round(RechargeRatio * 100)} %)"
};
if (rechargeText.TextSize.X > rechargeText.Rect.Width) { rechargeText.Font = GUI.SmallFont; }
rechargeSpeedSlider = new GUIScrollBar(new RectTransform(new Vector2(0.9f, 0.4f), upperArea.RectTransform, Anchor.BottomCenter),
barSize: 0.15f, style: "DeviceSlider")
var rechargeSliderContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.4f), upperArea.RectTransform, Anchor.BottomCenter));
if (RechargeWarningIndicatorLow > 0.0f || RechargeWarningIndicatorHigh > 0.0f)
{
var rechargeSliderFill = new GUICustomComponent(new RectTransform(new Vector2(0.95f, 0.9f), rechargeSliderContainer.RectTransform, Anchor.Center), (SpriteBatch sb, GUICustomComponent c) =>
{
if (RechargeWarningIndicatorLow > 0.0f)
{
float warningLow = c.Rect.Width * RechargeWarningIndicatorLow;
GUI.DrawRectangle(sb, new Vector2(c.Rect.X + warningLow, c.Rect.Y), new Vector2(c.Rect.Width - warningLow, c.Rect.Height), GUI.Style.Orange, isFilled: true);
}
if (RechargeWarningIndicatorHigh > 0.0f)
{
float warningHigh = c.Rect.Width * RechargeWarningIndicatorHigh;
GUI.DrawRectangle(sb, new Vector2(c.Rect.X + warningHigh, c.Rect.Y), new Vector2(c.Rect.Width - warningHigh, c.Rect.Height), GUI.Style.Red, isFilled: true);
}
});
}
rechargeSpeedSlider = new GUIScrollBar(new RectTransform(Vector2.One, rechargeSliderContainer.RectTransform, Anchor.Center),
barSize: 0.15f, style: "DeviceSliderSeeThrough")
{
Step = 0.1f,
OnMoved = (GUIScrollBar scrollBar, float barScroll) =>
@@ -61,17 +86,17 @@ namespace Barotrauma.Items.Components
// lower area --------------------------
var textArea = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), lowerArea.RectTransform), style: null);
var chargeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), textArea.RectTransform, Anchor.CenterLeft),
var chargeTextContainer = new GUIFrame(new RectTransform(new Vector2(1, 0.4f), lowerArea.RectTransform), style: null);
var chargeLabel = new GUITextBlock(new RectTransform(new Vector2(0.4f, 0.0f), chargeTextContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get("charge"), textColor: GUI.Style.TextColor, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
{
ToolTip = TextManager.Get("PowerTransferTipPower")
};
string kWmin = TextManager.Get("kilowattminute");
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), textArea.RectTransform, Anchor.CenterRight),
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), chargeTextContainer.RectTransform, Anchor.CenterRight),
"", textColor: GUI.Style.TextColor, font: GUI.Font, textAlignment: Alignment.CenterRight)
{
TextGetter = () => $"{(int)Math.Round(charge)}/{(int)capacity} {kWmin} ({(int)Math.Round(MathUtils.Percentage(charge, capacity))} %)"
TextGetter = () => $"{(int)MathF.Round(charge)}/{(int)capacity} {kWmin} ({(int)MathF.Round(MathUtils.Percentage(charge, capacity))} %)"
};
if (chargeText.TextSize.X > chargeText.Rect.Width) { chargeText.Font = GUI.SmallFont; }

View File

@@ -381,17 +381,17 @@ namespace Barotrauma.Items.Components
if (deteriorationTimer > 0.0f)
{
GUI.DrawString(spriteBatch,
new Vector2(item.WorldPosition.X, -item.WorldPosition.Y), "Deterioration delay " + ((int)deteriorationTimer) + (paused ? " [PAUSED]" : ""),
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), "Deterioration delay " + ((int)deteriorationTimer) + (paused ? " [PAUSED]" : ""),
paused ? Color.Cyan : Color.Lime, Color.Black * 0.5f);
}
else
{
GUI.DrawString(spriteBatch,
new Vector2(item.WorldPosition.X, -item.WorldPosition.Y), "Deteriorating at " + (int)(DeteriorationSpeed * 60.0f) + " units/min" + (paused ? " [PAUSED]" : ""),
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), "Deteriorating at " + (int)(DeteriorationSpeed * 60.0f) + " units/min" + (paused ? " [PAUSED]" : ""),
paused ? Color.Cyan : GUI.Style.Red, Color.Black * 0.5f);
}
GUI.DrawString(spriteBatch,
new Vector2(item.WorldPosition.X, -item.WorldPosition.Y + 20), "Condition: " + (int)item.Condition + "/" + (int)item.MaxCondition,
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + 20), "Condition: " + (int)item.Condition + "/" + (int)item.MaxCondition,
GUI.Style.Orange);
}
}
@@ -434,6 +434,7 @@ namespace Barotrauma.Items.Components
DeteriorateAlways = msg.ReadBoolean();
tinkeringDuration = msg.ReadSingle();
tinkeringStrength = msg.ReadSingle();
tinkeringPowersDevices = msg.ReadBoolean();
ushort currentFixerID = msg.ReadUInt16();
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;

View File

@@ -199,6 +199,26 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
snapped = msg.ReadBoolean();
if (!snapped)
{
UInt16 targetId = msg.ReadUInt16();
UInt16 sourceId = msg.ReadUInt16();
byte limbIndex = msg.ReadByte();
Item target = Entity.FindEntityByID(targetId) as Item;
if (target == null) { return; }
var source = Entity.FindEntityByID(sourceId);
if (source is Character sourceCharacter && limbIndex >= 0 && limbIndex < sourceCharacter.AnimController.Limbs.Length)
{
Limb sourceLimb = sourceCharacter.AnimController.Limbs[limbIndex];
Attach(sourceLimb, target);
}
else if (source is ISpatialEntity spatialEntity)
{
Attach(spatialEntity, target);
}
}
}
protected override void RemoveComponentSpecific()

View File

@@ -46,6 +46,7 @@ namespace Barotrauma.Items.Components
child.Enabled = buttonsEnabled;
child.Children.ForEach(c => c.Enabled = buttonsEnabled);
}
if (Container == null) { return; }
bool itemsContained = Container.Inventory.AllItems.Any();
if (itemsContained)
{
@@ -77,7 +78,7 @@ namespace Barotrauma.Items.Components
{
if (GameMain.IsSingleplayer)
{
SendSignal((int)userData);
SendSignal((int)userData, Character.Controlled);
}
else
{
@@ -98,6 +99,7 @@ namespace Barotrauma.Items.Components
partial void OnItemLoadedProjSpecific()
{
if (Container == null) { return; }
Container.AllowUIOverlap = true;
Container.Inventory.RectTransform = containerHolder.RectTransform;
}
@@ -109,7 +111,7 @@ namespace Barotrauma.Items.Components
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), isServerMessage: true);
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), sender: null, isServerMessage: true);
}
}
}

View File

@@ -23,7 +23,7 @@ namespace Barotrauma.Items.Components
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Character character)
{
if (DraggingConnected?.Item.Removed ?? false)
if (DraggingConnected?.Item?.Removed ?? true)
{
DraggingConnected = null;
}

View File

@@ -197,7 +197,7 @@ namespace Barotrauma.Items.Components
}
}
partial void UpdateProjSpecific()
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
{
bool elementVisibilityChanged = false;
int visibleElementCount = 0;
@@ -209,6 +209,7 @@ namespace Barotrauma.Items.Components
if (uiElement.Visible != visible)
{
uiElement.Visible = visible;
uiElement.IgnoreLayoutGroups = !uiElement.Visible;
elementVisibilityChanged = true;
}
}
@@ -223,6 +224,7 @@ namespace Barotrauma.Items.Components
uiElement.RectTransform.RelativeSize = new Vector2(1.0f, elementSize);
}
GuiFrame.Visible = visibleElementCount > 0;
uiElementContainer.Recalculate();
}
}

View File

@@ -12,9 +12,9 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
if (!editing || !MapEntity.SelectedList.Contains(item)) return;
if (!editing || !MapEntity.SelectedList.Contains(item)) { return; }
Vector2 pos = item.WorldPosition + detectOffset;
Vector2 pos = item.WorldPosition + TransformedDetectOffset;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, pos - new Vector2(rangeX, rangeY), new Vector2(rangeX, rangeY) * 2.0f, Color.Cyan * 0.5f, isFilled: false, thickness: 2);
}

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Items.Components
@@ -19,5 +20,10 @@ namespace Barotrauma.Items.Components
ShapeExtensions.DrawLine(spriteBatch, pos + Vector2.UnitX * range, pos - Vector2.UnitX * range, Color.Cyan * 0.5f, 2);
ShapeExtensions.DrawCircle(spriteBatch, pos, range, 32, Color.Cyan * 0.5f, 3);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
Channel = msg.ReadRangedInteger(MinChannel, MaxChannel);
}
}
}

View File

@@ -135,7 +135,13 @@ namespace Barotrauma.Items.Components
wireSprite = overrideSprite ?? defaultWireSprite;
}
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
Draw(spriteBatch, editing, Vector2.Zero, itemDepth);
}
public void Draw(SpriteBatch spriteBatch, bool editing, Vector2 offset, float itemDepth = -1)
{
if (sections.Count == 0 && !IsActive || Hidden)
{
@@ -156,6 +162,8 @@ namespace Barotrauma.Items.Components
drawOffset = sub.DrawPosition + sub.HiddenSubPosition;
}
drawOffset += offset;
float baseDepth = UseSpriteDepth ? item.SpriteDepth : wireSprite.Depth;
float depth = item.IsSelected ? 0.0f : SubEditorScreen.IsWiringMode() ? 0.02f : baseDepth + (item.ID % 100) * 0.000001f;// item.GetDrawDepth(wireSprite.Depth, wireSprite);
@@ -286,7 +294,6 @@ namespace Barotrauma.Items.Components
item.Color, depth, 0.3f);
}
public static void UpdateEditing(List<Wire> wires)
{
var doubleClicked = PlayerInput.DoubleClicked();
@@ -509,6 +516,31 @@ namespace Barotrauma.Items.Components
}
}
public override void Move(Vector2 amount)
{
//only used in the sub editor, hence only in the client project
if (!item.IsSelected) { return; }
Vector2 wireNodeOffset = item.Submarine == null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
for (int i = 0; i < nodes.Count; i++)
{
if (i == 0 || i == nodes.Count - 1)
{
if (connections[0]?.Item != null && !connections[0].Item.IsSelected &&
(Submarine.RectContains(connections[0].Item.Rect, nodes[i] + wireNodeOffset) || Submarine.RectContains(connections[0].Item.Rect, nodes[i] + wireNodeOffset - amount)))
{
continue;
}
else if (connections[1]?.Item != null && !connections[1].Item.IsSelected &&
(Submarine.RectContains(connections[1].Item.Rect, nodes[i] + wireNodeOffset) || Submarine.RectContains(connections[1].Item.Rect, nodes[i] + wireNodeOffset - amount)))
{
continue;
}
}
nodes[i] += amount;
}
UpdateSections();
}
public bool IsMouseOn()
{
if (GUI.MouseOn == null)

View File

@@ -301,7 +301,7 @@ namespace Barotrauma.Items.Components
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in allAfflictions)
{
if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) continue;
if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) { continue; }
if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab))
{
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;
@@ -314,7 +314,7 @@ namespace Barotrauma.Items.Components
foreach (AfflictionPrefab affliction in combinedAfflictionStrengths.Keys)
{
texts.Add(TextManager.AddPunctuation(':', affliction.Name, Math.Max(((int)combinedAfflictionStrengths[affliction]), 1).ToString() + " %"));
texts.Add(TextManager.AddPunctuation(':', affliction.Name, Math.Max((int)combinedAfflictionStrengths[affliction], 1).ToString() + " %"));
textColors.Add(Color.Lerp(GUI.Style.Orange, GUI.Style.Red, combinedAfflictionStrengths[affliction] / affliction.MaxStrength));
}
}

View File

@@ -580,14 +580,20 @@ namespace Barotrauma.Items.Components
private void GetAvailablePower(out float availableCharge, out float availableCapacity)
{
var batteries = item.GetConnectedComponents<PowerContainer>();
availableCharge = 0.0f;
availableCapacity = 0.0f;
foreach (PowerContainer battery in batteries)
if (item.Connections == null) { return; }
foreach (Connection c in item.Connections)
{
availableCharge += battery.Charge;
availableCapacity += battery.Capacity;
var recipients = c.Recipients;
foreach (Connection recipient in recipients)
{
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
var battery = recipient.Item?.GetComponent<PowerContainer>();
if (battery == null) { continue; }
availableCharge += battery.Charge;
availableCapacity += battery.Capacity;
}
}
}
@@ -647,7 +653,9 @@ namespace Barotrauma.Items.Components
bool readyToFire = reload <= 0.0f && charged && availableAmmo.Any(p => p != null);
if (ShowChargeIndicator && PowerConsumption > 0.0f)
{
powerIndicator.Color = charged ? GUI.Style.Green : GUI.Style.Red;
powerIndicator.Color = charged ?
(HasPowerToShoot() ? GUI.Style.Green : GUI.Style.Orange) :
GUI.Style.Red;
if (flashLowPower)
{
powerIndicator.BarSize = 1;

View File

@@ -10,7 +10,13 @@ namespace Barotrauma.Items.Components
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}";
string afflictionName =
AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, StringComparison.OrdinalIgnoreCase))?.Name ??
TextManager.Get($"afflictiontype.{afflictionIdentifier}", returnNull: true) ??
afflictionIdentifier;
description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {afflictionName}";
}
public override void AddTooltipInfo(ref string name, ref string description)
@@ -33,9 +39,9 @@ namespace Barotrauma.Items.Components
{
GetDamageModifierText(ref description, damageModifier, afflictionIdentifier);
}
foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionTypes)
foreach (string afflictionType in damageModifier.ParsedAfflictionTypes)
{
GetDamageModifierText(ref description, damageModifier, afflictionIdentifier);
GetDamageModifierText(ref description, damageModifier, afflictionType);
}
}
}

View File

@@ -1584,6 +1584,10 @@ namespace Barotrauma
{
containedState = item.Condition / item.MaxCondition;
}
else if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
{
containedState = itemContainer.Inventory.AllItems.Count() / (float)(itemContainer.GetMaxStackSize(0) * itemContainer.Capacity);
}
else
{
var containedItem = itemContainer.Inventory.slots[Math.Max(itemContainer.ContainedStateIndicatorSlot, 0)].FirstOrDefault();

View File

@@ -17,7 +17,7 @@ namespace Barotrauma
partial class Item : MapEntity, IDamageable, ISerializableEntity, IServerSerializable, IClientSerializable
{
public static bool ShowItems = true, ShowWires = true;
private readonly List<PosInfo> positionBuffer = new List<PosInfo>();
private readonly List<ItemComponent> activeHUDs = new List<ItemComponent>();
@@ -89,8 +89,8 @@ namespace Barotrauma
{
if (itemInUseWarning == null)
{
itemInUseWarning = new GUITextBlock(new RectTransform(new Point(10), GUI.Canvas), "",
textColor: GUI.Style.Orange, color: Color.Black,
itemInUseWarning = new GUITextBlock(new RectTransform(new Point(10), GUI.Canvas), "",
textColor: GUI.Style.Orange, color: Color.Black,
textAlignment: Alignment.Center, style: "OuterGlow");
}
return itemInUseWarning;
@@ -105,6 +105,9 @@ namespace Barotrauma
{
return false;
}
if (!SubEditorScreen.IsLayerVisible(this)) { return false;}
return parentInventory == null && (body == null || body.Enabled) && ShowItems;
}
}
@@ -154,7 +157,7 @@ namespace Barotrauma
if (containedSprite.UseWhenAttached)
{
activeContainedSprite = containedSprite;
activeSprite = containedSprite.Sprite;
activeSprite = containedSprite.Sprite;
UpdateSpriteStates(0.0f);
return;
}
@@ -196,7 +199,7 @@ namespace Barotrauma
{
brokenSprite.Sprite.EnsureLazyLoaded();
}
foreach (var decorativeSprite in ((ItemPrefab)prefab).DecorativeSprites)
{
decorativeSprite.Sprite.EnsureLazyLoaded();
@@ -255,7 +258,7 @@ namespace Barotrauma
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (!Visible || (!editing && HiddenInGame)) { return; }
if (!Visible || (!editing && HiddenInGame) || !SubEditorScreen.IsLayerVisible(this)) { return; }
if (editing)
{
@@ -265,7 +268,7 @@ namespace Barotrauma
}
else if (!ShowItems) { return; }
}
Color color = IsIncludedInSelection && editing ? GUI.Style.Blue : IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange * Math.Max(GetSpriteColor().A / (float) byte.MaxValue, 0.1f) : GetSpriteColor();
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
@@ -273,7 +276,7 @@ namespace Barotrauma
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
bool renderTransparent = isWiringMode && GetComponent<ConnectionPanel>() == null;
if (renderTransparent) { color *= 0.15f; }
BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f;
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
@@ -322,7 +325,7 @@ namespace Barotrauma
Vector2 size = new Vector2(rect.Width, rect.Height);
if (color.A > 0)
{
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
size, color: color,
textureScale: Vector2.One * Scale,
depth: depth);
@@ -336,11 +339,11 @@ namespace Barotrauma
}
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
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,
decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
size, color: color,
textureScale: Vector2.One * Scale,
@@ -380,7 +383,7 @@ namespace Barotrauma
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,
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
rotationRad + rot, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
@@ -440,11 +443,11 @@ namespace Barotrauma
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
foreach (var upgrade in Upgrades)
{
var upgradeSprites = GetUpgradeSprites(upgrade);
foreach (var decorativeSprite in upgradeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
@@ -456,7 +459,7 @@ namespace Barotrauma
rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
activeSprite.effects = oldEffects;
@@ -466,7 +469,7 @@ namespace Barotrauma
}
}
//use a backwards for loop because the drawable components may disable drawing,
//use a backwards for loop because the drawable components may disable drawing,
//causing them to be removed from the list
for (int i = drawableComponents.Count - 1; i >= 0; i--)
{
@@ -501,7 +504,7 @@ namespace Barotrauma
Vector2 drawPos = new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2));
Vector2 drawSize = new Vector2(MathF.Ceiling(rect.Width + Math.Abs(drawPos.X - (int)drawPos.X)), MathF.Ceiling(rect.Height + Math.Abs(drawPos.Y - (int)drawPos.Y)));
drawPos = new Vector2(MathF.Floor(drawPos.X), MathF.Floor(drawPos.Y));
GUI.DrawRectangle(spriteBatch, drawPos, drawSize,
GUI.DrawRectangle(spriteBatch, drawPos, drawSize,
Color.White, false, 0, thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
foreach (Rectangle t in Prefab.Triggers)
@@ -582,19 +585,25 @@ namespace Barotrauma
}
}
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
if (Prefab.DecorativeSpriteGroups.Count > 0)
{
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
}
foreach (var upgrade in Upgrades)
{
var upgradeSprites = GetUpgradeSprites(upgrade);
var upgradeSprites = GetUpgradeSprites(upgrade);
foreach (var decorativeSprite in upgradeSprites)
{
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = true;
foreach (var _ in decorativeSprite.IsActiveConditionals.Where(conditional => !ConditionalMatches(conditional)))
foreach (var conditional in decorativeSprite.IsActiveConditionals)
{
spriteState.IsActive = false;
break;
if (!ConditionalMatches(conditional))
{
spriteState.IsActive = false;
break;
}
}
}
}
@@ -696,8 +705,8 @@ namespace Barotrauma
foreach (string tag in ip.PreferredContainers.SelectMany(pc => pc.Primary)) { availableTags.Add(tag); }
foreach (string tag in ip.PreferredContainers.SelectMany(pc => pc.Secondary)) { availableTags.Add(tag); }
}
//remove identifiers from the available container tags
//(otherwise the list will include many irrelevant options,
//remove identifiers from the available container tags
//(otherwise the list will include many irrelevant options,
//e.g. "weldingtool" because a welding fuel tank can be placed inside the container, etc)
availableTags.RemoveWhere(t => MapEntityPrefab.List.Any(me => me.Identifier == t));
new GUIButton(new RectTransform(new Vector2(0.1f, 1), tagsField.RectTransform, Anchor.TopRight), "...")
@@ -749,7 +758,7 @@ namespace Barotrauma
{
me.FlipY(relativeToSub: false);
}
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
return true;
}
};
@@ -805,9 +814,9 @@ namespace Barotrauma
{
if (!ic.AllowInGameEditing) { continue; }
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 &&
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
{
continue;
continue;
}
}
else
@@ -869,7 +878,7 @@ namespace Barotrauma
textBox.Text = relatedItem.JoinedIdentifiers;
return true;
};
}
}
ic.CreateEditingHUD(componentEditor);
componentEditor.Recalculate();
@@ -892,7 +901,7 @@ namespace Barotrauma
return upgradeSprites;
}
public override bool AddUpgrade(Upgrade upgrade, bool createNetworkEvent = false)
{
if (upgrade.Prefab.IsWallUpgrade) { return false; }
@@ -949,7 +958,7 @@ namespace Barotrauma
//reset positions first
List<GUIComponent> elementsToMove = new List<GUIComponent>();
if (editingHUD != null && editingHUD.UserData == this &&
if (editingHUD != null && editingHUD.UserData == this &&
((HasInGameEditableProperties && Character.Controlled?.SelectedConstruction == this) || Screen.Selected == GameMain.SubEditorScreen))
{
elementsToMove.Add(editingHUD);
@@ -972,8 +981,8 @@ namespace Barotrauma
int disallowedPadding = (int)(50 * GUI.Scale);
disallowedAreas.Add(GameMain.GameSession.CrewManager.GetActiveCrewArea());
disallowedAreas.Add(new Rectangle(
HUDLayoutSettings.ChatBoxArea.X - disallowedPadding, HUDLayoutSettings.ChatBoxArea.Y,
HUDLayoutSettings.ChatBoxArea.Width + disallowedPadding, HUDLayoutSettings.ChatBoxArea.Height));
HUDLayoutSettings.ChatBoxArea.X - disallowedPadding, HUDLayoutSettings.ChatBoxArea.Y,
HUDLayoutSettings.ChatBoxArea.Width + disallowedPadding, HUDLayoutSettings.ChatBoxArea.Height));
}
if (Screen.Selected is SubEditorScreen editor)
@@ -985,8 +994,8 @@ namespace Barotrauma
GUI.PreventElementOverlap(elementsToMove, disallowedAreas,
new Rectangle(
0, 20,
GameMain.GraphicsWidth,
0, 20,
GameMain.GraphicsWidth,
HUDLayoutSettings.InventoryTopY > 0 ? HUDLayoutSettings.InventoryTopY - 40 : GameMain.GraphicsHeight - 80));
foreach (ItemComponent ic in activeHUDs)
@@ -995,7 +1004,7 @@ namespace Barotrauma
var linkUIToComponent = ic.GetLinkUIToComponent();
if (linkUIToComponent == null) { continue; }
if (linkUIToComponent == null) { continue; }
ic.GuiFrame.RectTransform.ScreenSpaceOffset = linkUIToComponent.GuiFrame.RectTransform.ScreenSpaceOffset;
}
@@ -1110,14 +1119,14 @@ namespace Barotrauma
}
}
}
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{
if (HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped))
{
DrawEditing(spriteBatch, cam);
}
foreach (ItemComponent ic in activeHUDs)
{
if (ic.CanBeSelected)
@@ -1138,9 +1147,9 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, debugInitialHudPositions[i], Color.Orange);
GUI.DrawRectangle(spriteBatch, ic.GuiFrame.Rect, Color.LightGreen);
GUI.DrawLine(spriteBatch, debugInitialHudPositions[i].Location.ToVector2(), ic.GuiFrame.Rect.Location.ToVector2(), Color.Orange);
i++;
}
}
}
}
@@ -1172,7 +1181,7 @@ namespace Barotrauma
}
texts.Add(new ColoredText(nameText, GUI.Style.TextColor, false, false));
if (CampaignInteractionType != CampaignMode.InteractionType.None)
if (CampaignMode.BlocksInteraction(CampaignInteractionType))
{
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameMain.Config.KeyBindText(InputType.Use)), Color.Cyan, false, false));
}
@@ -1262,7 +1271,7 @@ namespace Barotrauma
NetEntityEvent.Type eventType =
(NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
switch (eventType)
{
case NetEntityEvent.Type.ComponentState:
@@ -1323,7 +1332,7 @@ namespace Barotrauma
ItemComponent targetComponent = componentIndex < components.Count ? components[componentIndex] : null;
Character targetCharacter = FindEntityByID(targetCharacterID) as Character;
Limb targetLimb = targetCharacter != null && targetLimbID < targetCharacter.AnimController.Limbs.Length ?
Limb targetLimb = targetCharacter != null && targetLimbID < targetCharacter.AnimController.Limbs.Length ?
targetCharacter.AnimController.Limbs[targetLimbID] : null;
Entity useTarget = FindEntityByID(useTargetID);
@@ -1334,7 +1343,7 @@ namespace Barotrauma
else
{
targetComponent.ApplyStatusEffects(actionType, 1.0f, targetCharacter, targetLimb, useTarget, worldPosition: worldPosition);
}
}
}
break;
case NetEntityEvent.Type.ChangeProperty:
@@ -1346,7 +1355,7 @@ namespace Barotrauma
if (UpgradePrefab.Find(identifier) is { } upgradePrefab)
{
Upgrade upgrade = new Upgrade(this, upgradePrefab, level);
byte targetCount = msg.ReadByte();
for (int i = 0; i < targetCount; i++)
{
@@ -1360,7 +1369,7 @@ namespace Barotrauma
AddUpgrade(upgrade, false);
}
break;
break;
case NetEntityEvent.Type.Invalid:
break;
}
@@ -1394,7 +1403,7 @@ namespace Barotrauma
Character targetCharacter = FindEntityByID(characterID) as Character;
msg.Write(characterID);
msg.Write(targetCharacter == null ? (byte)255 : (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb));
msg.Write(targetCharacter == null ? (byte)255 : (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb));
break;
case NetEntityEvent.Type.ChangeProperty:
WritePropertyChange(msg, extraData, true);
@@ -1541,6 +1550,7 @@ namespace Barotrauma
Vector2 pos = Vector2.Zero;
Submarine sub = null;
float rotation = 0.0f;
int itemContainerIndex = -1;
int inventorySlotIndex = -1;
@@ -1552,7 +1562,7 @@ namespace Barotrauma
else
{
pos = new Vector2(msg.ReadSingle(), msg.ReadSingle());
rotation = msg.ReadRangedSingle(0, MathHelper.TwoPi, 8);
ushort subID = msg.ReadUInt16();
if (subID > 0)
{

View File

@@ -315,7 +315,7 @@ namespace Barotrauma.MapCreatures.Behavior
}
else
{
RemoveClaim(itemId);
RemoveClaim(item);
}
}
else

View File

@@ -2,7 +2,6 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
namespace Barotrauma
{
@@ -14,7 +13,7 @@ namespace Barotrauma
{
get
{
return ShowGaps;
return ShowGaps && SubEditorScreen.IsLayerVisible(this);
}
}
@@ -25,11 +24,14 @@ namespace Barotrauma
public override void Draw(SpriteBatch sb, bool editing, bool back = true)
{
float depth = (ID % 255) * 0.000001f;
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
{
Vector2 center = new Vector2(WorldRect.X + rect.Width / 2.0f, -(WorldRect.Y - rect.Height / 2.0f));
GUI.DrawLine(sb, center, center + new Vector2(flowForce.X, -flowForce.Y) / 10.0f, GUI.Style.Red);
GUI.DrawLine(sb, center + Vector2.One * 5.0f, center + new Vector2(lerpedFlowForce.X, -lerpedFlowForce.Y) / 10.0f + Vector2.One * 5.0f, GUI.Style.Orange);
if (FlowTargetHull != null)
{
DrawArrow(FlowTargetHull, IsHorizontal ? rect.Height: rect.Width, Math.Abs(lerpedFlowForce.Length()), Color.Red * 0.3f);
}
if (outsideCollisionBlocker.Enabled && Submarine != null)
{
@@ -42,12 +44,10 @@ namespace Barotrauma
}
}
if (!editing || !ShowGaps) { return; }
if (!editing || !ShowGaps || !SubEditorScreen.IsLayerVisible(this)) { return; }
Color clr = (open == 0.0f) ? GUI.Style.Red : Color.Cyan;
if (IsHighlighted) clr = Color.Gold;
float depth = (ID % 255) * 0.000001f;
if (IsHighlighted) { clr = Color.Gold; }
GUI.DrawRectangle(
sb, new Rectangle(WorldRect.X, -WorldRect.Y, rect.Width, rect.Height),
@@ -81,33 +81,38 @@ namespace Barotrauma
{
for (int i = 0; i < linkedTo.Count; i++)
{
Vector2 dir = IsHorizontal ?
new Vector2(Math.Sign(linkedTo[i].Rect.Center.X - rect.Center.X), 0.0f)
: new Vector2(0.0f, Math.Sign((rect.Y - rect.Height / 2.0f) - (linkedTo[i].Rect.Y - linkedTo[i].Rect.Height / 2.0f)));
Vector2 arrowPos = new Vector2(WorldRect.Center.X, -(WorldRect.Y - WorldRect.Height / 2));
arrowPos += new Vector2(dir.X * (WorldRect.Width / 2), dir.Y * (WorldRect.Height / 2));
float arrowWidth = 32.0f;
float arrowSize = 15.0f;
bool invalidDir = false;
if (dir == Vector2.Zero)
if (linkedTo[i] is Hull hull)
{
invalidDir = true;
dir = IsHorizontal ? Vector2.UnitX : Vector2.UnitY;
DrawArrow(hull, 32.0f, 15f, clr);
}
GUI.Arrow.Draw(sb,
arrowPos, invalidDir ? Color.Red : clr * 0.8f,
GUI.Arrow.Origin, MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
IsHorizontal ?
new Vector2(Math.Min(rect.Height, arrowWidth) / GUI.Arrow.size.X, arrowSize / GUI.Arrow.size.Y) :
new Vector2(Math.Min(rect.Width, arrowWidth) / GUI.Arrow.size.X, arrowSize / GUI.Arrow.size.Y),
SpriteEffects.None, depth);
}
}
void DrawArrow(Hull targetHull, float arrowWidth, float arrowLength, Color clr)
{
Vector2 dir = IsHorizontal ?
new Vector2(Math.Sign(targetHull.Rect.Center.X - rect.Center.X), 0.0f)
: new Vector2(0.0f, Math.Sign((rect.Y - rect.Height / 2.0f) - (targetHull.Rect.Y - targetHull.Rect.Height / 2.0f)));
Vector2 arrowPos = new Vector2(WorldRect.Center.X, -(WorldRect.Y - WorldRect.Height / 2));
arrowPos += new Vector2(dir.X * (WorldRect.Width / 2), dir.Y * (WorldRect.Height / 2));
bool invalidDir = false;
if (dir == Vector2.Zero)
{
invalidDir = true;
dir = IsHorizontal ? Vector2.UnitX : Vector2.UnitY;
}
GUI.Arrow.Draw(sb,
arrowPos, invalidDir ? Color.Red : clr * 0.8f,
GUI.Arrow.Origin, MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
IsHorizontal ?
new Vector2(Math.Min(rect.Height, arrowWidth) / GUI.Arrow.size.X, arrowLength / GUI.Arrow.size.Y) :
new Vector2(Math.Min(rect.Width, arrowWidth) / GUI.Arrow.size.X, arrowLength / GUI.Arrow.size.Y),
SpriteEffects.None, depth);
}
if (IsSelected)
{
GUI.DrawRectangle(sb,
@@ -128,8 +133,14 @@ namespace Barotrauma
{
//no flow particles between linked hulls (= rooms consisting of multiple hulls)
if (hull1.linkedTo.Contains(hull2)) { return; }
if (hull1.linkedTo.Any(h => h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2))) { return; }
if (hull2.linkedTo.Any(h => h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2))) { return; }
foreach (var linkedEntity in hull1.linkedTo)
{
if (linkedEntity is Hull h && h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
}
foreach (var linkedEntity in hull2.linkedTo)
{
if (linkedEntity is Hull h && h.linkedTo.Contains(hull1) && h.linkedTo.Contains(hull2)) { return; }
}
}
Vector2 pos = Position;
@@ -177,7 +188,7 @@ namespace Barotrauma
"bubbles",
(Submarine == null ? pos : pos + Submarine.Position),
velocity, 0, flowTargetHull);
particleTimer -= emitInterval;
}
}
@@ -202,9 +213,9 @@ namespace Barotrauma
"watersplash",
(Submarine == null ? pos : pos + Submarine.Position) - Vector2.UnitY * Rand.Range(0.0f, 10.0f),
velocity, 0, flowTargetHull);
if (particle != null)
{
if (particle.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(particle); }
particle.Size *= Math.Min(Math.Abs(flowForce.X / 500.0f), 5.0f);
}
}
@@ -232,13 +243,13 @@ namespace Barotrauma
}
else
{
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) return;
if (Math.Sign(flowTargetHull.Rect.Y - rect.Y) != Math.Sign(lerpedFlowForce.Y)) { return; }
float particlesPerSec = open * rect.Width * 0.3f * particleAmountMultiplier;
float particlesPerSec = Math.Max(open * rect.Width * 0.3f * particleAmountMultiplier, 20.0f);
float emitInterval = 1.0f / particlesPerSec;
while (particleTimer > emitInterval)
{
pos.X = Rand.Range(rect.X, rect.X + rect.Width);
pos.X = Rand.Range(rect.X, rect.X + rect.Width + 1);
Vector2 velocity = new Vector2(
lerpedFlowForce.X * Rand.Range(0.5f, 0.7f),
MathHelper.Clamp(lerpedFlowForce.Y, -500.0f, 1000.0f) * Rand.Range(0.5f, 0.7f));
@@ -246,17 +257,21 @@ namespace Barotrauma
if (flowTargetHull.WaterVolume < flowTargetHull.Volume * 0.95f)
{
var splash = GameMain.ParticleManager.CreateParticle(
"watersplash",
Submarine == null ? pos : pos + Submarine.Position,
velocity, 0, FlowTargetHull);
if (splash != null) splash.Size = splash.Size * MathHelper.Clamp(rect.Width / 50.0f, 0.8f, 4.0f);
"watersplash",
Submarine == null ? pos : pos + Submarine.Position,
velocity, 0, FlowTargetHull);
if (splash != null)
{
if (splash.CurrentHull == null) { GameMain.ParticleManager.RemoveParticle(splash); }
splash.Size *= MathHelper.Clamp(rect.Width / 50.0f, 1.5f, 4.0f);
}
}
if (Math.Abs(flowForce.Y) > 190.0f && Rand.Range(0.0f, 1.0f) < 0.3f && flowTargetHull.WaterVolume > flowTargetHull.Volume * 0.1f)
{
GameMain.ParticleManager.CreateParticle(
"bubbles",
Submarine == null ? pos : pos + Submarine.Position,
flowForce / 2.0f, 0, FlowTargetHull);
"bubbles",
Submarine == null ? pos : pos + Submarine.Position,
flowForce / 2.0f, 0, FlowTargetHull);
}
particleTimer -= emitInterval;
}

View File

@@ -34,14 +34,27 @@ namespace Barotrauma
private readonly List<RemoteDecal> remoteDecals = new List<RemoteDecal>();
private readonly HashSet<Decal> pendingDecalUpdates = new HashSet<Decal>();
private double lastAmbientLightEditTime;
private float drawSurface;
public float DrawSurface
{
get { return drawSurface; }
set
{
if (Math.Abs(drawSurface - value) < 0.00001f) { return; }
drawSurface = MathHelper.Clamp(value, rect.Y - rect.Height, rect.Y);
update = true;
}
}
public override bool SelectableInEditor
{
get
{
return ShowHulls;
return ShowHulls && SubEditorScreen.IsLayerVisible(this);
}
}
@@ -133,91 +146,101 @@ namespace Barotrauma
else
{
if (!entity.linkedTo.Contains(this)) { entity.linkedTo.Add(this); }
if (!linkedTo.Contains(this)) { linkedTo.Add(entity); }
if (!linkedTo.Contains(this)) { linkedTo.Add(entity); }
}
}
}
partial void UpdateProjSpecific(float deltaTime, Camera cam)
partial void UpdateProjSpecific(float deltaTime, Camera _)
{
serverUpdateDelay -= deltaTime;
if (serverUpdateDelay <= 0.0f)
{
ApplyRemoteState();
}
float waterDepth = WaterVolume / rect.Width;
//interpolate the position of the rendered surface towards the "target surface"
drawSurface = Math.Max(MathHelper.Lerp(
drawSurface,
rect.Y - rect.Height + waterDepth,
deltaTime * 10.0f), rect.Y - rect.Height);
if (networkUpdatePending)
if (GameMain.Client != null)
{
networkUpdateTimer += deltaTime;
if (networkUpdateTimer > 0.2f)
serverUpdateDelay -= deltaTime;
if (serverUpdateDelay <= 0.0f)
{
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
{
GameMain.NetworkMember?.CreateEntityEvent(this);
}
foreach (Decal decal in pendingDecalUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { decal });
}
foreach (int pendingSectionUpdate in pendingSectionUpdates)
{
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
}
pendingSectionUpdates.Clear();
networkUpdatePending = false;
networkUpdateTimer = 0.0f;
ApplyRemoteState();
}
}
if (!IdFreed)
{
if (EditWater)
if (networkUpdatePending)
{
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
if (Submarine.RectContains(WorldRect, position))
networkUpdateTimer += deltaTime;
if (networkUpdateTimer > 0.2f)
{
if (PlayerInput.PrimaryMouseButtonHeld())
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
{
WaterVolume += 1500.0f;
networkUpdatePending = true;
serverUpdateDelay = 0.5f;
GameMain.NetworkMember?.CreateEntityEvent(this);
}
else if (PlayerInput.SecondaryMouseButtonHeld())
foreach (Decal decal in pendingDecalUpdates)
{
WaterVolume -= 1500.0f;
networkUpdatePending = true;
serverUpdateDelay = 0.5f;
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { decal });
}
}
}
else if (EditFire)
{
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
if (Submarine.RectContains(WorldRect, position))
{
if (PlayerInput.PrimaryMouseButtonClicked())
foreach (int pendingSectionUpdate in pendingSectionUpdates)
{
new FireSource(position, this, isNetworkMessage: true);
networkUpdatePending = true;
serverUpdateDelay = 0.5f;
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
}
pendingSectionUpdates.Clear();
networkUpdatePending = false;
networkUpdateTimer = 0.0f;
}
}
}
if (waterVolume < 1.0f) { return; }
/*if (waterVolume < 1.0f) { return; }
for (int i = 1; i < waveY.Length - 1; i++)
{
float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
if (maxDelta > 1.0f && maxDelta > Rand.Range(1.0f, 10.0f))
if (maxDelta > 0.1f && maxDelta > Rand.Range(0.1f, 10.0f))
{
var particlePos = new Vector2(rect.X + WaveWidth * i, surface + waveY[i]);
if (Submarine != null) particlePos += Submarine.Position;
if (Submarine != null) { particlePos += Submarine.Position; }
GameMain.ParticleManager.CreateParticle("mist",
particlePos,
new Vector2(0.0f, -50.0f), 0.0f, this);
}
}*/
}
public static void UpdateCheats(float deltaTime, Camera cam)
{
bool primaryMouseButtonHeld = PlayerInput.PrimaryMouseButtonHeld();
bool secondaryMouseButtonHeld = PlayerInput.SecondaryMouseButtonHeld();
if (!primaryMouseButtonHeld && !secondaryMouseButtonHeld) { return; }
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
Hull hull = FindHull(position);
if (hull == null || hull.IdFreed) { return; }
if (EditWater)
{
if (primaryMouseButtonHeld)
{
hull.WaterVolume += 100000.0f * deltaTime;
hull.networkUpdatePending = true;
hull.serverUpdateDelay = 0.5f;
}
else if (secondaryMouseButtonHeld)
{
hull.WaterVolume -= 100000.0f * deltaTime;
hull.networkUpdatePending = true;
hull.serverUpdateDelay = 0.5f;
}
}
else if (EditFire)
{
if (primaryMouseButtonHeld)
{
new FireSource(position, hull, isNetworkMessage: true);
hull.networkUpdatePending = true;
hull.serverUpdateDelay = 0.5f;
}
}
}
@@ -243,7 +266,7 @@ namespace Barotrauma
return;
}
if (!ShowHulls && !GameMain.DebugDraw) { return; }
if ((!ShowHulls || !SubEditorScreen.IsLayerVisible(this)) && !GameMain.DebugDraw) { return; }
if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) { return; }
@@ -385,45 +408,42 @@ namespace Barotrauma
}
}
private static readonly Vector3[] corners = new Vector3[6];
private static readonly Vector2[] uvCoords = new Vector2[4];
private static readonly Vector3[] prevCorners = new Vector3[2];
private static readonly Vector2[] prevUVs = new Vector2[2];
private void UpdateVertices(Camera cam, EntityGrid entityGrid, WaterRenderer renderer)
{
Vector2 submarinePos = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
//if there's no more space in the buffer, don't render the water in the hull
//not an ideal solution, but this seems to only happen in cases where the missing
//not an ideal solution, but this seems to only happen in cases where the missing
//water is not very noticeable (e.g. zoomed very far out so that multiple subs and ruins are visible)
if (renderer.PositionInBuffer > renderer.vertices.Length - 6)
{
return;
}
if (!renderer.IndoorsVertices.ContainsKey(entityGrid))
{
renderer.IndoorsVertices[entityGrid] = new VertexPositionColorTexture[WaterRenderer.DefaultIndoorsBufferSize];
renderer.PositionInIndoorsBuffer[entityGrid] = 0;
}
//calculate where the surface should be based on the water volume
float top = rect.Y + submarinePos.Y;
float bottom = top - rect.Height;
float renderSurface = drawSurface + submarinePos.Y;
if (bottom > cam.WorldView.Y || top < cam.WorldView.Y - cam.WorldView.Height) return;
if (bottom > cam.WorldView.Y || top < cam.WorldView.Y - cam.WorldView.Height) { return; }
if (rect.X + submarinePos.X > cam.WorldView.Right || rect.Right + submarinePos.X < cam.WorldView.X) { return; }
Matrix transform = cam.Transform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
Matrix transform = cam.Transform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
if (!update)
{
// create the four corners of our triangle.
Vector3[] corners = new Vector3[4];
corners[0] = new Vector3(rect.X, rect.Y, 0.0f);
corners[1] = new Vector3(rect.X + rect.Width, rect.Y, 0.0f);
corners[2] = new Vector3(corners[1].X, rect.Y - rect.Height, 0.0f);
corners[3] = new Vector3(corners[0].X, corners[2].Y, 0.0f);
Vector2[] uvCoords = new Vector2[4];
for (int i = 0; i < 4; i++)
{
corners[i] += new Vector3(submarinePos, 0.0f);
@@ -443,6 +463,15 @@ namespace Barotrauma
return;
}
if (!renderer.IndoorsVertices.ContainsKey(entityGrid))
{
renderer.IndoorsVertices[entityGrid] = new VertexPositionColorTexture[WaterRenderer.DefaultIndoorsBufferSize];
}
if (!renderer.PositionInIndoorsBuffer.ContainsKey(entityGrid))
{
renderer.PositionInIndoorsBuffer[entityGrid] = 0;
}
float x = rect.X;
if (Submarine != null) { x += Submarine.DrawPosition.X; }
@@ -454,20 +483,15 @@ namespace Barotrauma
x += start * WaveWidth;
Vector3[] prevCorners = new Vector3[2];
Vector2[] prevUVs = new Vector2[2];
int width = WaveWidth;
for (int i = start; i < end; i++)
{
Vector3[] corners = new Vector3[6];
//top left
corners[0] = new Vector3(x, top, 0.0f);
//watersurface left
corners[3] = new Vector3(corners[0].X, renderSurface + waveY[i], 0.0f);
//top right
corners[1] = new Vector3(x + width, top, 0.0f);
//watersurface right
@@ -477,7 +501,7 @@ namespace Barotrauma
corners[4] = new Vector3(x, bottom, 0.0f);
//bottom right
corners[5] = new Vector3(x + width, bottom, 0.0f);
Vector2[] uvCoords = new Vector2[4];
for (int n = 0; n < 4; n++)
{
@@ -714,7 +738,7 @@ namespace Barotrauma
}
remoteBackgroundSections.Clear();
if (remoteDecals.Any())
if (remoteDecals.Count > 0)
{
decals.Clear();
foreach (RemoteDecal remoteDecal in remoteDecals)

View File

@@ -95,7 +95,7 @@ namespace Barotrauma
var prefab = ToolBox.SelectWeightedRandom(availablePrefabs, availablePrefabs.Select(p => p.GetCommonness(level.GenerationParams)).ToList(), Rand.RandSync.ClientOnly);
if (prefab == null) { break; }
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly);
int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax + 1, Rand.RandSync.ClientOnly);
List<BackgroundCreature> swarmMembers = new List<BackgroundCreature>();
for (int n = 0; n < amount; n++)
{

View File

@@ -51,7 +51,7 @@ namespace Barotrauma
public readonly WaterVertexData IndoorsSurfaceBottomColor = new WaterVertexData(0.2f, 0.1f, 0.9f, 1.0f);
public VertexPositionTexture[] vertices = new VertexPositionTexture[DefaultBufferSize];
public Dictionary<EntityGrid, VertexPositionColorTexture[]> IndoorsVertices = new Dictionary<EntityGrid, VertexPositionColorTexture[]>();// VertexPositionColorTexture[DefaultBufferSize * 2];
public Dictionary<EntityGrid, VertexPositionColorTexture[]> IndoorsVertices = new Dictionary<EntityGrid, VertexPositionColorTexture[]>();
public Effect WaterEffect
{
@@ -81,13 +81,17 @@ namespace Barotrauma
if (basicEffect == null)
{
basicEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
basicEffect.VertexColorEnabled = false;
basicEffect.TextureEnabled = true;
basicEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
{
VertexColorEnabled = false,
TextureEnabled = true
};
}
}
private readonly VertexPositionColorTexture[] tempVertices = new VertexPositionColorTexture[6];
private readonly Vector3[] tempCorners = new Vector3[4];
public void RenderWater(SpriteBatch spriteBatch, RenderTarget2D texture, Camera cam)
{
spriteBatch.GraphicsDevice.BlendState = BlendState.NonPremultiplied;
@@ -139,29 +143,26 @@ namespace Barotrauma
WaterEffect.CurrentTechnique.Passes[0].Apply();
VertexPositionColorTexture[] verts = new VertexPositionColorTexture[6];
Rectangle view = cam != null ? cam.WorldView : spriteBatch.GraphicsDevice.Viewport.Bounds;
var corners = new Vector3[4];
corners[0] = new Vector3(view.X, view.Y, 0.1f);
corners[1] = new Vector3(view.Right, view.Y, 0.1f);
corners[2] = new Vector3(view.Right, view.Y - view.Height, 0.1f);
corners[3] = new Vector3(view.X, view.Y - view.Height, 0.1f);
tempCorners[0] = new Vector3(view.X, view.Y, 0.1f);
tempCorners[1] = new Vector3(view.Right, view.Y, 0.1f);
tempCorners[2] = new Vector3(view.Right, view.Y - view.Height, 0.1f);
tempCorners[3] = new Vector3(view.X, view.Y - view.Height, 0.1f);
WaterVertexData backGroundColor = new WaterVertexData(0.1f, 0.1f, 0.5f, 1.0f);
verts[0] = new VertexPositionColorTexture(corners[0], backGroundColor, Vector2.Zero);
verts[1] = new VertexPositionColorTexture(corners[1], backGroundColor, Vector2.Zero);
verts[2] = new VertexPositionColorTexture(corners[2], backGroundColor, Vector2.Zero);
verts[3] = new VertexPositionColorTexture(corners[0], backGroundColor, Vector2.Zero);
verts[4] = new VertexPositionColorTexture(corners[2], backGroundColor, Vector2.Zero);
verts[5] = new VertexPositionColorTexture(corners[3], backGroundColor, Vector2.Zero);
tempVertices[0] = new VertexPositionColorTexture(tempCorners[0], backGroundColor, Vector2.Zero);
tempVertices[1] = new VertexPositionColorTexture(tempCorners[1], backGroundColor, Vector2.Zero);
tempVertices[2] = new VertexPositionColorTexture(tempCorners[2], backGroundColor, Vector2.Zero);
tempVertices[3] = new VertexPositionColorTexture(tempCorners[0], backGroundColor, Vector2.Zero);
tempVertices[4] = new VertexPositionColorTexture(tempCorners[2], backGroundColor, Vector2.Zero);
tempVertices[5] = new VertexPositionColorTexture(tempCorners[3], backGroundColor, Vector2.Zero);
spriteBatch.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, verts, 0, 2);
spriteBatch.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, tempVertices, 0, 2);
foreach (KeyValuePair<EntityGrid, VertexPositionColorTexture[]> subVerts in IndoorsVertices)
{
if (!PositionInIndoorsBuffer.ContainsKey(subVerts.Key) || PositionInIndoorsBuffer[subVerts.Key] == 0) continue;
if (!PositionInIndoorsBuffer.ContainsKey(subVerts.Key) || PositionInIndoorsBuffer[subVerts.Key] == 0) { continue; }
offset = WavePos;
if (subVerts.Key.Submarine != null) { offset -= subVerts.Key.Submarine.WorldPosition; }
@@ -207,11 +208,23 @@ namespace Barotrauma
basicEffect.CurrentTechnique.Passes[0].Apply();
}
private readonly List<EntityGrid> buffersToRemove = new List<EntityGrid>();
public void ResetBuffers()
{
PositionInBuffer = 0;
PositionInIndoorsBuffer.Clear();
IndoorsVertices.Clear();
buffersToRemove.Clear();
foreach (var buffer in IndoorsVertices.Keys)
{
if (buffer.Submarine?.Removed ?? false)
{
buffersToRemove.Add(buffer);
}
}
foreach (var bufferToRemove in buffersToRemove)
{
IndoorsVertices.Remove(bufferToRemove);
}
}
public void Dispose()

View File

@@ -517,7 +517,7 @@ namespace Barotrauma.Lights
private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub)
{
var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub);
if (fullChList == null) return;
if (fullChList == null) { return; }
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, ch.BoundingBox));
@@ -530,105 +530,121 @@ namespace Barotrauma.Lights
/// </summary>
private void CheckHullsInRange()
{
List<Submarine> subs = new List<Submarine>(Submarine.Loaded);
subs.Add(null);
foreach (Submarine sub in subs)
foreach (Submarine sub in Submarine.Loaded)
{
//find the list of convexhulls that belong to the sub
var chList = hullsInRange.Find(x => x.Submarine == sub);
CheckHullsInRange(sub);
}
//check convex hulls that aren't in any sub
CheckHullsInRange(null);
}
//not found -> create one
if (chList == null)
private void CheckHullsInRange(Submarine sub)
{
//find the list of convexhulls that belong to the sub
ConvexHullList chList = null;
foreach (var ch in hullsInRange)
{
if (ch.Submarine == sub)
{
chList = new ConvexHullList(sub);
hullsInRange.Add(chList);
NeedsRecalculation = true;
}
if (chList.List.Any(ch => ch.LastVertexChangeTime > lastRecalculationTime && !chList.IsHidden.Contains(ch)))
{
NeedsRecalculation = true;
}
Vector2 lightPos = position;
if (ParentSub == null)
{
//light and the convexhulls are both outside
if (sub == null)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, null);
}
}
//light is outside, convexhulls inside a sub
else
{
lightPos -= sub.Position;
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//only draw if the light overlaps with the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
continue;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
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);
}
}
//light and convexhull are inside different subs
else
{
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) continue;
lightPos -= (sub.Position - ParentSub.Position);
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//don't draw any shadows if the light doesn't overlap with the borders of the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
continue;
}
//recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
if (!diffToSub.TryGetValue(sub, out Vector2 prevDiff))
{
diffToSub.Add(sub, diff);
NeedsRecalculation = true;
}
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f * 5.0f)
{
diffToSub[sub] = diff;
NeedsRecalculation = true;
}
RefreshConvexHullList(chList, lightPos, sub);
}
chList = ch;
break;
}
}
//not found -> create one
if (chList == null)
{
chList = new ConvexHullList(sub);
hullsInRange.Add(chList);
NeedsRecalculation = true;
}
foreach (var ch in chList.List)
{
if (ch.LastVertexChangeTime > lastRecalculationTime && !chList.IsHidden.Contains(ch))
{
NeedsRecalculation = true;
break;
}
}
Vector2 lightPos = position;
if (ParentSub == null)
{
//light and the convexhulls are both outside
if (sub == null)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, null);
}
}
//light is outside, convexhulls inside a sub
else
{
lightPos -= sub.Position;
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//only draw if the light overlaps with the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) { NeedsRecalculation = true; }
chList.List.Clear();
return;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
else
{
//light is inside, convexhull outside
if (sub == null) { return; }
//light and convexhull are both inside the same sub
if (sub == ParentSub)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, sub);
}
}
//light and convexhull are inside different subs
else
{
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) { return; }
lightPos -= (sub.Position - ParentSub.Position);
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//don't draw any shadows if the light doesn't overlap with the borders of the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, subBorders))
{
if (chList.List.Count > 0) { NeedsRecalculation = true; }
chList.List.Clear();
return;
}
//recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
if (!diffToSub.TryGetValue(sub, out Vector2 prevDiff))
{
diffToSub.Add(sub, diff);
NeedsRecalculation = true;
}
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f * 5.0f)
{
diffToSub[sub] = diff;
NeedsRecalculation = true;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
}
private List<Vector2> FindRaycastHits()

View File

@@ -75,10 +75,12 @@ namespace Barotrauma
if (linkedTo.Contains(entity))
{
linkedTo.Remove(entity);
entity.linkedTo.Remove(this);
}
else
{
linkedTo.Add(entity);
if (!entity.linkedTo.Contains(this)) { entity.linkedTo.Add(this); }
}
}
}

View File

@@ -130,18 +130,31 @@ namespace Barotrauma
int tilesY = (int)Math.Ceiling(Height / tileSize.Y);
mapTiles = new Sprite[tilesX, tilesY];
tileDiscovered = new bool[tilesX, tilesY];
HashSet<Biome> missingBiomes = new HashSet<Biome>();
for (int x = 0; x < tilesX; x++)
{
for (int y = 0; y < tilesY; y++)
{
var biome = GetBiome(x * tileSize.X);
var tileList = generationParams.MapTiles.ContainsKey(biome.Identifier) ?
generationParams.MapTiles[biome.Identifier] :
generationParams.MapTiles.Values.First();
List<Sprite> tileList = null;
if (generationParams.MapTiles.ContainsKey(biome.Identifier))
{
tileList = generationParams.MapTiles[biome.Identifier];
}
else
{
tileList = generationParams.MapTiles.Values.First();
missingBiomes.Add(biome);
}
mapTiles[x, y] = tileList[x % tileList.Count];
}
}
foreach (var missingBiome in missingBiomes)
{
DebugConsole.ThrowError($"Could not find campaign map sprites for the biome \"{missingBiome.Identifier}\". Using the sprites of the first biome instead...");
}
RemoveFogOfWar(StartLocation);
GenerateLocationConnectionVisuals();
@@ -194,7 +207,11 @@ namespace Barotrauma
private void RemoveFogOfWar(Location location, bool removeFromAdjacentLocations = true)
{
if (location == null) { return; }
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
var mapTile = generationParams.MapTiles.Values.FirstOrDefault()?.FirstOrDefault();
if (mapTile == null) { return; }
Vector2 mapTileSize = mapTile.size * generationParams.MapTileScale;
int startX = (int)Math.Max(Math.Floor(location.MapPosition.X / mapTileSize.X - 0.25f), 0);
int startY = (int)Math.Max(Math.Floor(location.MapPosition.Y / mapTileSize.Y - 0.25f), 0);
int endX = (int)Math.Min(Math.Floor(location.MapPosition.X / mapTileSize.X + 0.25f), mapTiles.GetLength(0));

View File

@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Lights;
@@ -36,9 +37,6 @@ namespace Barotrauma
private static List<MapEntity> highlightedList = new List<MapEntity>();
// Test feature. Not yet saved.
public static Dictionary<MapEntity, HashSet<MapEntity>> SelectionGroups { get; private set; } = new Dictionary<MapEntity, HashSet<MapEntity>>();
private static float highlightTimer;
private static GUIListBox highlightedListBox;
@@ -197,7 +195,7 @@ namespace Barotrauma
{
Paste(cam.ScreenToWorld(PlayerInput.MousePosition));
}
else if (PlayerInput.KeyHit(Keys.G))
/*else if (PlayerInput.KeyHit(Keys.G))
{
if (SelectedList.Any())
{
@@ -217,7 +215,7 @@ namespace Barotrauma
}
}
}
}
}*/
}
}
@@ -360,14 +358,15 @@ namespace Barotrauma
{
if (highLightedEntity != null)
{
if (SelectionGroups.TryGetValue(highLightedEntity, out HashSet<MapEntity> group))
if (SubEditorScreen.IsLayerLinked(highLightedEntity)/*SelectionGroups.TryGetValue(highLightedEntity, out HashSet<MapEntity> group)*/)
{
foreach (MapEntity entity in group.Where(e => !newSelection.Contains(e)))
ImmutableHashSet<MapEntity> entitiesInSameLayer = SubEditorScreen.GetEntitiesInSameLayer(highLightedEntity);
foreach (MapEntity entity in entitiesInSameLayer.Where(e => !newSelection.Contains(e)))
{
newSelection.Add(entity);
}
foreach (MapEntity entity in group)
foreach (MapEntity entity in entitiesInSameLayer)
{
entity.IsIncludedInSelection = true;
}
@@ -769,34 +768,40 @@ namespace Barotrauma
switch (e)
{
case Item item:
{
if (item.FlippedX && item.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
if (item.flippedY && item.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
break;
}
{
if (item.FlippedX && item.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
if (item.flippedY && item.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
var wire = item.GetComponent<Wire>();
if (wire != null && wire.Item.body != null && !wire.Item.body.Enabled)
{
wire.Draw(spriteBatch, editing: false, new Vector2(moveAmount.X, -moveAmount.Y));
continue;
}
break;
}
case Structure structure:
{
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
break;
}
{
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) { spriteEffects ^= SpriteEffects.FlipHorizontally; }
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) { spriteEffects ^= SpriteEffects.FlipVertically; }
break;
}
case WayPoint wayPoint:
{
Vector2 drawPos = e.WorldPosition;
drawPos.Y = -drawPos.Y;
drawPos += moveAmount;
wayPoint.Draw(spriteBatch, drawPos);
continue;
}
{
Vector2 drawPos = e.WorldPosition;
drawPos.Y = -drawPos.Y;
drawPos += moveAmount;
wayPoint.Draw(spriteBatch, drawPos);
continue;
}
case LinkedSubmarine linkedSub:
{
var ma = moveAmount;
ma.Y = -ma.Y;
Vector2 lPos = linkedSub.Position;
lPos += ma;
linkedSub.Draw(spriteBatch, lPos, alpha: 0.5f);
break;
}
{
var ma = moveAmount;
ma.Y = -ma.Y;
Vector2 lPos = linkedSub.Position;
lPos += ma;
linkedSub.Draw(spriteBatch, lPos, alpha: 0.5f);
break;
}
}
e.prefab?.DrawPlacing(spriteBatch,
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size), e.Scale, spriteEffects);
@@ -1197,14 +1202,24 @@ namespace Barotrauma
Rectangle selectionRect = Submarine.AbsRect(pos, size);
foreach (MapEntity e in mapEntityList)
foreach (MapEntity entity in mapEntityList)
{
if (!e.SelectableInEditor) continue;
if (!entity.SelectableInEditor) { continue; }
if (Submarine.RectsOverlap(selectionRect, e.rect))
if (Submarine.RectsOverlap(selectionRect, entity.rect))
{
foundEntities.Add(e);
e.IsIncludedInSelection = true;
foundEntities.Add(entity);
entity.IsIncludedInSelection = true;
if (SubEditorScreen.IsLayerLinked(entity))
{
ImmutableHashSet<MapEntity> entitiesInSameLayer = SubEditorScreen.GetEntitiesInSameLayer(entity);
foreach (MapEntity layerEntity in entitiesInSameLayer.Where(e => !foundEntities.Contains(e)))
{
foundEntities.Add(layerEntity);
layerEntity.IsIncludedInSelection = true;
}
}
}
}

View File

@@ -30,6 +30,9 @@ namespace Barotrauma
{
return false;
}
if (!SubEditorScreen.IsLayerVisible(this)) { return false; }
return HasBody ? ShowWalls : ShowStructures;
}
}
@@ -244,8 +247,10 @@ namespace Barotrauma
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (prefab.sprite == null) { return; }
if (editing)
{
if (!SubEditorScreen.IsLayerVisible(this)) { return; }
if (!HasBody && !ShowStructures) { return; }
if (HasBody && !ShowWalls) { return; }
}
@@ -273,6 +278,7 @@ namespace Barotrauma
if (prefab.sprite == null) { return; }
if (editing)
{
if (!SubEditorScreen.IsLayerVisible(this)) { return; }
if (!HasBody && !ShowStructures) { return; }
if (HasBody && !ShowWalls) { return; }
}
@@ -285,13 +291,11 @@ namespace Barotrauma
//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;
Vector2 bodyPos = WorldPosition + BodyOffset * Scale;
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)));
@@ -465,7 +469,8 @@ namespace Barotrauma
public void UpdateSpriteStates(float deltaTime)
{
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
if (Prefab.DecorativeSpriteGroups.Count == 0) { return; }
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
foreach (int spriteGroup in Prefab.DecorativeSpriteGroups.Keys)
{
for (int i = 0; i < Prefab.DecorativeSpriteGroups[spriteGroup].Count; i++)

View File

@@ -25,14 +25,11 @@ namespace Barotrauma
public readonly bool Stream;
public readonly bool IgnoreMuffling;
public string Filename
{
get { return Sound?.Filename; }
}
public readonly string Filename;
public RoundSound(XElement element, Sound sound)
{
Filename = sound?.Filename;
Sound = sound;
Stream = sound.Stream;
Range = element.GetAttributeFloat("range", 1000.0f);

View File

@@ -55,9 +55,9 @@ namespace Barotrauma
if (closestSub != null && subsToMove.Contains(closestSub))
{
GameMain.GameScreen.Cam.Position += moveAmount;
if (GameMain.GameScreen.Cam.TargetPos != Vector2.Zero) GameMain.GameScreen.Cam.TargetPos += moveAmount;
if (GameMain.GameScreen.Cam.TargetPos != Vector2.Zero) { GameMain.GameScreen.Cam.TargetPos += moveAmount; }
if (Character.Controlled != null) Character.Controlled.CursorPosition += moveAmount;
if (Character.Controlled != null) { Character.Controlled.CursorPosition += moveAmount; }
}
}

View File

@@ -1,6 +1,6 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma
@@ -91,7 +91,7 @@ namespace Barotrauma
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font, bool includeTitle = true, bool includeClass = true, bool includeDescription = false)
{
float leftPanelWidth = 0.6f;
float rightPanelWidth = 0.4f;
float rightPanelWidth = 0.4f / leftPanelWidth;
string className = !HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{SubmarineClass}") : TextManager.Get("shuttle");
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
@@ -110,6 +110,17 @@ namespace Barotrauma
submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
}
if (Price > 0)
{
var priceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("subeditor.price"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), priceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", Price)), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
}
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
if (realWorldDimensions != Vector2.Zero)
{

View File

@@ -316,11 +316,11 @@ namespace Barotrauma
var srcRect = prefab.sprite.SourceRect;
SpriteEffects spriteEffects = SpriteEffects.None;
if (flippedX)
if (flippedX && ((prefab as ItemPrefab)?.CanSpriteFlipX ?? true))
{
spriteEffects |= SpriteEffects.FlipHorizontally;
}
if (flippedY)
if (flippedY && ((prefab as ItemPrefab)?.CanSpriteFlipY ?? true))
{
spriteEffects |= SpriteEffects.FlipVertically;
}
@@ -651,7 +651,11 @@ namespace Barotrauma
public void Dispose()
{
previewFrame = null;
if (previewFrame != null)
{
previewFrame.RectTransform.Parent = null;
previewFrame = null;
}
spriteRecorder?.Dispose();
isDisposed = true;
}

View File

@@ -90,14 +90,14 @@ namespace Barotrauma
{
GUI.DrawLine(spriteBatch,
drawPos,
new Vector2(ConnectedGap.WorldPosition.X, -ConnectedGap.WorldPosition.Y),
new Vector2(ConnectedGap.DrawPosition.X, -ConnectedGap.DrawPosition.Y),
GUI.Style.Green * 0.5f, width: 1);
}
if (Ladders != null)
{
GUI.DrawLine(spriteBatch,
drawPos,
new Vector2(Ladders.Item.WorldPosition.X, -Ladders.Item.WorldPosition.Y),
new Vector2(Ladders.Item.DrawPosition.X, -Ladders.Item.DrawPosition.Y),
GUI.Style.Green * 0.5f, width: 1);
}
@@ -146,6 +146,7 @@ namespace Barotrauma
private bool IsHidden()
{
if (!SubEditorScreen.IsLayerVisible(this)) { return false; }
if (spawnType == SpawnType.Path)
{
return (!GameMain.DebugDraw && !ShowWayPoints);
@@ -294,7 +295,7 @@ namespace Barotrauma
else
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), TextManager.Get("Spawnpoint"), font: GUI.LargeFont);
var spawnTypeContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), isHorizontal: true)
{
Stretch = true,
@@ -318,7 +319,10 @@ namespace Barotrauma
};
var descText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("IDCardDescription"), font: GUI.SmallFont);
TextManager.Get("IDCardDescription"), font: GUI.SmallFont)
{
ToolTip = TextManager.Get("IDCardDescriptionTooltip")
};
GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), descText.RectTransform, Anchor.CenterRight), IdCardDesc)
{
MaxTextLength = 150,
@@ -342,7 +346,10 @@ namespace Barotrauma
};
var idCardTagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("IDCardTags"), font: GUI.SmallFont);
TextManager.Get("IDCardTags"), font: GUI.SmallFont)
{
ToolTip = TextManager.Get("IDCardTagsTooltip")
};
propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), idCardTagsText.RectTransform, Anchor.CenterRight), string.Join(", ", idCardTags))
{
MaxTextLength = 60,
@@ -414,6 +421,6 @@ namespace Barotrauma
PositionEditingHUD();
return editingHUD;
}
}
}
}

View File

@@ -46,6 +46,13 @@ namespace Barotrauma.Networking
senderName = senderCharacter.Name;
}
}
Color? textColor = null;
if (msg.ReadBoolean())
{
textColor = msg.ReadColorR8G8B8A8();
}
msg.ReadPadBits();
switch (type)
@@ -78,7 +85,7 @@ namespace Barotrauma.Networking
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, targetRoom,
givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter,
orderOption: orderOption,
priority: orderMessageInfo.Priority);
isNewOrder: orderMessageInfo.IsNewOrder);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{
@@ -135,14 +142,18 @@ namespace Barotrauma.Networking
//only show the message box if the text differs from the text in the currently visible box
if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt)
{
new GUIMessageBox("", txt);
GUIMessageBox messageBox = new GUIMessageBox("", txt);
if (textColor != null) { messageBox.Text.TextColor = textColor.Value; }
}
break;
case ChatMessageType.ServerMessageBoxInGame:
new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
{
GUIMessageBox messageBox = new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
if (textColor != null) { messageBox.Text.TextColor = textColor.Value; }
}
break;
case ChatMessageType.Console:
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
DebugConsole.NewMessage(txt, textColor == null ? MessageColor[(int)ChatMessageType.Console] : textColor.Value);
break;
case ChatMessageType.ServerLog:
if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
@@ -152,7 +163,7 @@ namespace Barotrauma.Networking
GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
break;
default:
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType);
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType, textColor: textColor);
break;
}
LastID = id;

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -15,7 +16,11 @@ namespace Barotrauma
var entity = FindEntityByID(entityId);
if (entity != null)
{
DebugConsole.Log("Received entity removal message for \"" + entity.ToString() + "\".");
DebugConsole.Log($"Received entity removal message for \"{entity}\".");
if (entity is Item item && item.Container?.GetComponent<Deconstructor>() != null)
{
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + item.prefab.Identifier);
}
entity.Remove();
}
else
@@ -28,7 +33,11 @@ namespace Barotrauma
switch (message.ReadByte())
{
case (byte)SpawnableType.Item:
Item.ReadSpawnData(message, true);
var newItem = Item.ReadSpawnData(message, true);
if (newItem is Item item && item.Container?.GetComponent<Fabricator>() != null)
{
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + item.prefab.Identifier);
}
break;
case (byte)SpawnableType.Character:
Character.ReadSpawnData(message);

View File

@@ -859,7 +859,7 @@ namespace Barotrauma.Networking
}
break;
case ServerPacketHeader.STARTGAMEFINALIZE:
DebugConsole.Log("Received STARTGAMEFINALIZE packet.");
DebugConsole.NewMessage("Received STARTGAMEFINALIZE packet. Round init status: " + roundInitStatus);
if (roundInitStatus == RoundInitStatus.WaitingForStartGameFinalize)
{
//waiting for a save file
@@ -950,6 +950,9 @@ namespace Barotrauma.Networking
case ServerPacketHeader.CREW:
campaign?.ClientReadCrew(inc);
break;
case ServerPacketHeader.MEDICAL:
campaign?.MedicalClinic?.ClientRead(inc);
break;
case ServerPacketHeader.READY_CHECK:
ReadyCheck.ClientRead(inc);
break;
@@ -1121,9 +1124,9 @@ namespace Barotrauma.Networking
disconnectReason != DisconnectReason.InvalidVersion)
{
GameAnalyticsManager.AddErrorEventOnce(
"GameClient.HandleDisconnectMessage",
GameAnalyticsManager.ErrorSeverity.Debug,
"Client received a disconnect message. Reason: " + disconnectReason.ToString());
"GameClient.HandleDisconnectMessage",
GameAnalyticsManager.ErrorSeverity.Debug,
"Client received a disconnect message. Reason: " + disconnectReason.ToString());
}
if (disconnectReason == DisconnectReason.ServerFull)
@@ -1276,7 +1279,15 @@ namespace Barotrauma.Networking
private void ReadAchievement(IReadMessage inc)
{
string achievementIdentifier = inc.ReadString();
SteamAchievementManager.UnlockAchievement(achievementIdentifier);
int amount = inc.ReadInt32();
if (amount == 0)
{
SteamAchievementManager.UnlockAchievement(achievementIdentifier);
}
else
{
SteamAchievementManager.IncrementStat(achievementIdentifier, amount);
}
}
private void ReadTraitorMessage(IReadMessage inc)
@@ -1476,7 +1487,7 @@ namespace Barotrauma.Networking
serverSettings.LockAllDefaultWires = inc.ReadBoolean();
serverSettings.AllowRagdollButton = inc.ReadBoolean();
serverSettings.AllowLinkingWifiToChat = inc.ReadBoolean();
GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits();
GameMain.LightManager.LightingEnabled = true;
@@ -1488,6 +1499,8 @@ namespace Barotrauma.Networking
Task loadTask = null;
var roundSummary = (GUIMessageBox.MessageBoxes.Find(c => c?.UserData is RoundSummary)?.UserData) as RoundSummary;
bool isOutpost = false;
if (gameMode != GameModePreset.MultiPlayerCampaign)
{
string levelSeed = inc.ReadString();
@@ -1626,6 +1639,7 @@ namespace Barotrauma.Networking
{
GameMain.GameSession.StartRound(levelData, mirrorLevel);
}
isOutpost = levelData.Type == LevelData.LevelType.Outpost;
}
if (GameMain.Client?.ServerSettings?.Voting != null)
@@ -1745,8 +1759,7 @@ namespace Barotrauma.Networking
if (respawnAllowed)
{
bool isOutpost = GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign && Level.Loaded?.Type == LevelData.LevelType.Outpost;
respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle && !isOutpost ? GameMain.NetLobbyScreen.SelectedShuttle : null);
respawnManager = new RespawnManager(this, usingShuttle && !isOutpost ? GameMain.NetLobbyScreen.SelectedShuttle : null);
}
gameStarted = true;
@@ -1877,7 +1890,7 @@ namespace Barotrauma.Networking
if (int.TryParse(ownedIndexes[i], out int index))
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[index];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "owned"))
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.GameSession.OwnedSubmarines.Add(sub);
}
@@ -1893,7 +1906,7 @@ namespace Barotrauma.Networking
if (int.TryParse(ownedIndexes[i], out index))
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[index];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "owned"))
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.NetLobbyScreen.ServerOwnedSubmarines.Add(sub);
}
@@ -2095,13 +2108,6 @@ namespace Barotrauma.Networking
string selectShuttleName = inc.ReadString();
string selectShuttleHash = inc.ReadString();
UInt16 campaignSubmarineIndexCount = inc.ReadUInt16();
List<int> campaignSubIndices = new List<int>();
for (int i = 0; i< campaignSubmarineIndexCount; i++)
{
campaignSubIndices.Add(inc.ReadUInt16());
}
bool allowSubVoting = inc.ReadBoolean();
bool allowModeVoting = inc.ReadBoolean();
@@ -2162,16 +2168,11 @@ namespace Barotrauma.Networking
if (GameMain.Client.IsServerOwner) RequestSelectMode(modeIndex);
}
if (campaignSubIndices != null)
if (GameMain.NetLobbyScreen.SelectedMode == GameModePreset.MultiPlayerCampaign)
{
GameMain.NetLobbyScreen.CampaignSubmarines = new List<SubmarineInfo>();
foreach (UInt16 campaignSubIndex in campaignSubIndices)
foreach (SubmarineInfo sub in ServerSubmarines.Where(s => !ServerSettings.HiddenSubs.Contains(s.Name)))
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[campaignSubIndex];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, "campaign"))
{
GameMain.NetLobbyScreen.CampaignSubmarines.Add(sub);
}
GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Campaign);
}
}
@@ -2604,7 +2605,6 @@ namespace Barotrauma.Networking
NetLobbyScreen.FailedSubInfo failedCampaignSub = GameMain.NetLobbyScreen.FailedCampaignSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.Hash);
if (failedCampaignSub != default)
{
GameMain.NetLobbyScreen.CampaignSubmarines.Add(newSub);
GameMain.NetLobbyScreen.FailedCampaignSubs.Remove(failedCampaignSub);
}
@@ -2881,19 +2881,16 @@ namespace Barotrauma.Networking
public void SendCampaignState()
{
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
if (campaign == null)
if (!(GameMain.GameSession.GameMode is MultiPlayerCampaign campaign))
{
DebugConsole.ThrowError("Failed send campaign state to the server (no campaign active).\n" + Environment.StackTrace.CleanupStackTrace());
return;
}
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.SERVER_COMMAND);
msg.Write((UInt16)ClientPermissions.ManageCampaign);
campaign.ClientWrite(msg);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -3571,6 +3568,11 @@ namespace Barotrauma.Networking
case ClientNetError.MISSING_ENTITY:
outMsg.Write(eventID);
outMsg.Write(entityID);
outMsg.Write((byte)Submarine.Loaded.Count);
foreach (Submarine sub in Submarine.Loaded)
{
outMsg.Write(sub.Info.Name);
}
break;
}
clientPeer.Send(outMsg, DeliveryMethod.Reliable);

View File

@@ -100,7 +100,7 @@ namespace Barotrauma.Networking
if (!isActive) { return; }
if (steamId != hostSteamId) { return; }
Close($"SteamP2P connection failed: {error}");
OnDisconnectMessageReceived?.Invoke($"SteamP2P connection failed: {error}");
OnDisconnectMessageReceived?.Invoke($"{DisconnectReason.SteamP2PError}/SteamP2P connection failed: {error}");
}
private void OnP2PData(ulong steamId, byte[] data, int dataLength)
@@ -167,14 +167,14 @@ namespace Barotrauma.Networking
if (state == null)
{
Close("SteamP2P connection could not be established");
OnDisconnectMessageReceived?.Invoke("SteamP2P connection could not be established");
OnDisconnectMessageReceived?.Invoke(DisconnectReason.SteamP2PError.ToString());
}
else
{
if (state?.P2PSessionError != Steamworks.P2PSessionError.None)
{
Close($"SteamP2P error code: {state?.P2PSessionError}");
OnDisconnectMessageReceived?.Invoke($"SteamP2P error code: {state?.P2PSessionError}");
OnDisconnectMessageReceived?.Invoke($"{DisconnectReason.SteamP2PError}/SteamP2P error code: {state?.P2PSessionError}");
}
}
connectionStatusTimer = 1.0f;
@@ -210,7 +210,7 @@ namespace Barotrauma.Networking
if (timeout < 0.0)
{
Close("Timed out");
OnDisconnectMessageReceived?.Invoke("");
OnDisconnectMessageReceived?.Invoke(DisconnectReason.SteamP2PTimeOut.ToString());
return;
}
@@ -349,13 +349,19 @@ namespace Barotrauma.Networking
outMsg.Write((byte)PacketHeader.IsDisconnectMessage);
outMsg.Write(msg ?? "Disconnected");
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
try
{
Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
sentBytes += outMsg.LengthBytes;
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to send a disconnect message to the server using SteamP2P.", e);
}
Thread.Sleep(100);
Steamworks.SteamNetworking.ResetActions();
Steamworks.SteamNetworking.CloseP2PSessionWithUser(hostSteamId);
steamAuthTicket?.Cancel(); steamAuthTicket = null;

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