Faction Test 100.4.0.0
This commit is contained in:
@@ -36,8 +36,8 @@ namespace Barotrauma
|
||||
private float minZoom = 0.1f;
|
||||
public float MinZoom
|
||||
{
|
||||
get { return minZoom;}
|
||||
set { minZoom = MathHelper.Clamp(value, 0.001f, 10.0f); }
|
||||
get { return minZoom; }
|
||||
set { minZoom = MathHelper.Clamp(value, 0.001f, 10.0f); }
|
||||
}
|
||||
|
||||
private float maxZoom = 2.0f;
|
||||
@@ -63,7 +63,7 @@ namespace Barotrauma
|
||||
private float prevZoom;
|
||||
|
||||
public float Shake;
|
||||
private Vector2 shakePosition;
|
||||
public Vector2 ShakePosition { get; private set; }
|
||||
private float shakeTimer;
|
||||
|
||||
private float globalZoomScale = 1.0f;
|
||||
@@ -371,7 +371,7 @@ namespace Barotrauma
|
||||
|
||||
if (Shake < 0.01f)
|
||||
{
|
||||
shakePosition = Vector2.Zero;
|
||||
ShakePosition = Vector2.Zero;
|
||||
shakeTimer = 0.0f;
|
||||
}
|
||||
else
|
||||
@@ -379,11 +379,11 @@ namespace Barotrauma
|
||||
shakeTimer += deltaTime * 5.0f;
|
||||
Vector2 noisePos = new Vector2((float)PerlinNoise.CalculatePerlin(shakeTimer, shakeTimer, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(shakeTimer, shakeTimer, 0.5f) - 0.5f);
|
||||
|
||||
shakePosition = noisePos * Shake * 2.0f;
|
||||
ShakePosition = noisePos * Shake * 2.0f;
|
||||
Shake = MathHelper.Lerp(Shake, 0.0f, deltaTime * 2.0f);
|
||||
}
|
||||
|
||||
Translate(moveCam + shakePosition);
|
||||
Translate(moveCam + ShakePosition);
|
||||
Freeze = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Barotrauma
|
||||
{
|
||||
class CameraTransition
|
||||
{
|
||||
private static List<CameraTransition> activeTransitions = new List<CameraTransition>();
|
||||
|
||||
public bool Running
|
||||
{
|
||||
get;
|
||||
@@ -19,6 +21,7 @@ namespace Barotrauma
|
||||
private readonly float? endZoom;
|
||||
|
||||
public readonly float WaitDuration;
|
||||
public float EndWaitDuration = 0.1f;
|
||||
public readonly float PanDuration;
|
||||
public readonly bool FadeOut;
|
||||
public readonly bool LosFadeIn;
|
||||
@@ -45,8 +48,19 @@ namespace Barotrauma
|
||||
if (targetEntity == null) { return; }
|
||||
|
||||
Running = true;
|
||||
CoroutineManager.StopCoroutines("CameraTransition");
|
||||
|
||||
prevControlled = Character.Controlled;
|
||||
activeTransitions.RemoveAll(a => !CoroutineManager.IsCoroutineRunning(a.updateCoroutine));
|
||||
foreach (var activeTransition in activeTransitions)
|
||||
{
|
||||
if (activeTransition.prevControlled != null)
|
||||
{
|
||||
prevControlled ??= activeTransition.prevControlled;
|
||||
}
|
||||
activeTransition.Stop();
|
||||
}
|
||||
updateCoroutine = CoroutineManager.StartCoroutine(Update(targetEntity, cam), "CameraTransition");
|
||||
activeTransitions.Add(this);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -66,7 +80,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetEntity == null || (targetEntity is Entity e && e.Removed)) { yield return CoroutineStatus.Success; }
|
||||
|
||||
prevControlled = Character.Controlled;
|
||||
prevControlled ??= Character.Controlled;
|
||||
if (RemoveControlFromCharacter)
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -80,6 +94,7 @@ namespace Barotrauma
|
||||
float endZoom = this.endZoom ?? 0.5f;
|
||||
Vector2 initialCameraPos = cam.Position;
|
||||
Vector2? initialTargetPos = targetEntity?.WorldPosition;
|
||||
Vector2 endPos = cam.Position;
|
||||
|
||||
float timer = -WaitDuration;
|
||||
|
||||
@@ -137,13 +152,13 @@ namespace Barotrauma
|
||||
{
|
||||
startPos += targetEntity.WorldPosition - initialTargetPos.Value;
|
||||
}
|
||||
Vector2 endPos = cameraEndPos.HasValue ?
|
||||
endPos = cameraEndPos.HasValue ?
|
||||
new Vector2(
|
||||
MathHelper.Lerp(minPos.X, maxPos.X, (cameraEndPos.Value.ToVector2().X + 1.0f) / 2.0f),
|
||||
MathHelper.Lerp(maxPos.Y, minPos.Y, (cameraEndPos.Value.ToVector2().Y + 1.0f) / 2.0f)) :
|
||||
prevControlled?.WorldPosition ?? targetEntity.WorldPosition;
|
||||
|
||||
Vector2 cameraPos = Vector2.SmoothStep(startPos, endPos, clampedTimer / PanDuration);
|
||||
Vector2 cameraPos = Vector2.SmoothStep(startPos, endPos, clampedTimer / PanDuration) + cam.ShakePosition;
|
||||
cam.Translate(cameraPos - cam.Position);
|
||||
|
||||
#if CLIENT
|
||||
@@ -164,9 +179,16 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
Running = false;
|
||||
float endTimer = 0.0f;
|
||||
while (endTimer <= EndWaitDuration)
|
||||
{
|
||||
cam.Translate(endPos - cam.Position);
|
||||
cam.Zoom = endZoom;
|
||||
endTimer += CoroutineManager.DeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
Running = false;
|
||||
|
||||
#if CLIENT
|
||||
GUI.ScreenOverlayColor = Color.TransparentBlack;
|
||||
|
||||
@@ -47,8 +47,8 @@ namespace Barotrauma
|
||||
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
|
||||
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
|
||||
}
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity} ({GetTargetMemory(SelectedAiTarget, false)?.Priority.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"({targetValue.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity}", GUIStyle.Red, Color.Black);
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"{targetValue.FormatZeroDecimal()} (M: {SelectedTargetMemory?.Priority.FormatZeroDecimal()}, P: {SelectedTargetingParams?.Priority.FormatZeroDecimal()})", GUIStyle.Red, Color.Black);
|
||||
}
|
||||
|
||||
/*GUIStyle.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, GUIStyle.Red);
|
||||
|
||||
@@ -99,12 +99,14 @@ namespace Barotrauma
|
||||
float newAngularVelocity = Collider.AngularVelocity;
|
||||
Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);
|
||||
|
||||
newVelocity = newVelocity.ClampLength(100.0f);
|
||||
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
|
||||
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
|
||||
|
||||
Collider.LinearVelocity = newVelocity;
|
||||
Collider.AngularVelocity = newAngularVelocity;
|
||||
if (Collider.BodyType == BodyType.Dynamic)
|
||||
{
|
||||
newVelocity = newVelocity.ClampLength(100.0f);
|
||||
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
|
||||
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
|
||||
Collider.LinearVelocity = newVelocity;
|
||||
Collider.AngularVelocity = newAngularVelocity;
|
||||
}
|
||||
|
||||
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
|
||||
float errorTolerance = character.CanMove ? 0.01f : 0.2f;
|
||||
@@ -442,8 +444,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb == null || limb.IsSevered || limb.ActiveSprite == null) { continue; }
|
||||
|
||||
if (limb == null || limb.IsSevered || limb.ActiveSprite == null || !limb.DoesFlip) { continue; }
|
||||
Vector2 spriteOrigin = limb.ActiveSprite.Origin;
|
||||
spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X;
|
||||
limb.ActiveSprite.Origin = spriteOrigin;
|
||||
@@ -468,7 +469,10 @@ namespace Barotrauma
|
||||
{
|
||||
var damageSound = character.GetSound(s => s.Type == CharacterSound.SoundType.Damage);
|
||||
float range = damageSound != null ? damageSound.Range * 2 : ConvertUnits.ToDisplayUnits(character.AnimController.Collider.GetSize().Length() * 10);
|
||||
SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: range);
|
||||
if (!limbJoint.Params.BreakSound.IsNullOrEmpty() && !limbJoint.Params.BreakSound.Equals("none", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
SoundPlayer.PlayDamageSound(limbJoint.Params.BreakSound, 1.0f, limbJoint.LimbA.body.DrawPosition, range: range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma
|
||||
|
||||
if (sound != null)
|
||||
{
|
||||
SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling);
|
||||
SoundPlayer.PlaySound(sound.Sound, worldPosition, sound.Volume, sound.Range, ignoreMuffling: sound.IgnoreMuffling, freqMult: sound.GetRandomFrequencyMultiplier());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,42 @@ namespace Barotrauma
|
||||
set => grainStrength = Math.Max(0, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects
|
||||
/// </summary>
|
||||
public float CollapseEffectStrength
|
||||
{
|
||||
get { return Level.Loaded?.Renderer?.CollapseEffectStrength ?? 0.0f; }
|
||||
set
|
||||
{
|
||||
if (Level.Loaded?.Renderer == null) { return; }
|
||||
if (Controlled == this)
|
||||
{
|
||||
float strength = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
Level.Loaded.Renderer.CollapseEffectStrength = strength;
|
||||
Level.Loaded.Renderer.CollapseEffectOrigin = Submarine?.WorldPosition ?? WorldPosition;
|
||||
Screen.Selected.Cam.Shake = Math.Max(MathF.Pow(strength, 3) * 100.0f, Screen.Selected.Cam.Shake);
|
||||
Screen.Selected.Cam.Rotation = strength * (PerlinNoise.GetPerlin((float)Timing.TotalTime * 0.01f, (float)Timing.TotalTime * 0.05f) - 0.5f);
|
||||
Level.Loaded.Renderer.ChromaticAberrationStrength = value * 50.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Can be used to set camera shake from status effects
|
||||
/// </summary>
|
||||
public float CameraShake
|
||||
{
|
||||
get { return Screen.Selected?.Cam?.Shake ?? 0.0f; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) { return; }
|
||||
if (Screen.Selected?.Cam != null)
|
||||
{
|
||||
Screen.Selected.Cam.Shake = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ParticleEmitter> bloodEmitters = new List<ParticleEmitter>();
|
||||
public IEnumerable<ParticleEmitter> BloodEmitters
|
||||
{
|
||||
@@ -907,7 +943,7 @@ namespace Barotrauma
|
||||
{
|
||||
name += " " + TextManager.Get("Disguised");
|
||||
}
|
||||
else if (Info.Title != null)
|
||||
else if (Info.Title != null && TeamID != CharacterTeamType.Team1)
|
||||
{
|
||||
name += '\n' + Info.Title;
|
||||
}
|
||||
@@ -965,14 +1001,31 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (IsDead) { return; }
|
||||
|
||||
if (CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
|
||||
|
||||
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
|
||||
if (healthBarMode != EnemyHealthBarMode.ShowAll)
|
||||
{
|
||||
if (Controlled == null)
|
||||
{
|
||||
if (!IsOnPlayerTeam) { return; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!HumanAIController.IsFriendly(Controlled, this) ||
|
||||
(AIController is HumanAIController humanAi && humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective && HumanAIController.IsFriendly(Controlled, combatObjective.Enemy)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
|
||||
{
|
||||
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
|
||||
|
||||
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
|
||||
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
|
||||
CharacterHealth.DisplayedVitality / MaxVitality,
|
||||
CharacterHealth.DisplayedVitality / MaxVitality,
|
||||
Color.Lerp(GUIStyle.Red, GUIStyle.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
|
||||
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Tutorials;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
@@ -14,9 +13,8 @@ namespace Barotrauma
|
||||
{
|
||||
const float BossHealthBarDuration = 120.0f;
|
||||
|
||||
class BossHealthBar
|
||||
abstract class BossProgressBar
|
||||
{
|
||||
public readonly Character Character;
|
||||
public float FadeTimer;
|
||||
|
||||
public readonly GUIComponent TopContainer;
|
||||
@@ -25,9 +23,14 @@ namespace Barotrauma
|
||||
public readonly GUIProgressBar TopHealthBar;
|
||||
public readonly GUIProgressBar SideHealthBar;
|
||||
|
||||
public BossHealthBar(Character character)
|
||||
public abstract bool Completed { get; }
|
||||
|
||||
public abstract bool Interrupted { get; }
|
||||
|
||||
public abstract float State { get; }
|
||||
|
||||
public BossProgressBar(LocalizedString label)
|
||||
{
|
||||
Character = character;
|
||||
FadeTimer = BossHealthBarDuration;
|
||||
|
||||
TopContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.18f, 0.03f), HUDFrame.RectTransform, Anchor.TopCenter)
|
||||
@@ -35,7 +38,7 @@ namespace Barotrauma
|
||||
MinSize = new Point(100, 50),
|
||||
RelativeOffset = new Vector2(0.0f, 0.01f)
|
||||
}, isHorizontal: false, childAnchor: Anchor.TopCenter);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), TopContainer.RectTransform), character.DisplayName, textAlignment: Alignment.Center, textColor: GUIStyle.Red);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), TopContainer.RectTransform), label, textAlignment: Alignment.Center, textColor: GUIStyle.Red);
|
||||
TopHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.6f), TopContainer.RectTransform)
|
||||
{
|
||||
MinSize = new Point(100, HUDLayoutSettings.HealthBarArea.Size.Y)
|
||||
@@ -48,7 +51,7 @@ namespace Barotrauma
|
||||
{
|
||||
MinSize = new Point(80, 60)
|
||||
}, isHorizontal: false, childAnchor: Anchor.TopRight);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), SideContainer.RectTransform), character.DisplayName, textAlignment: Alignment.CenterRight, textColor: GUIStyle.Red);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), SideContainer.RectTransform), label, textAlignment: Alignment.CenterRight, textColor: GUIStyle.Red);
|
||||
SideHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.7f), SideContainer.RectTransform), barSize: 0.0f, style: "CharacterHealthBar")
|
||||
{
|
||||
Color = GUIStyle.Red
|
||||
@@ -60,6 +63,50 @@ namespace Barotrauma
|
||||
SideContainer.CanBeFocused = false;
|
||||
SideContainer.Children.ForEach(c => c.CanBeFocused = false);
|
||||
}
|
||||
|
||||
public abstract bool IsDuplicate(BossProgressBar progressBar);
|
||||
}
|
||||
|
||||
class BossHealthBar : BossProgressBar
|
||||
{
|
||||
public readonly Character Character;
|
||||
|
||||
public override float State => Character.Vitality / Character.MaxVitality;
|
||||
|
||||
public override bool Completed => Character.IsDead;
|
||||
|
||||
public override bool Interrupted => Character.Removed || !Character.Enabled;
|
||||
|
||||
public BossHealthBar(Character character) : base(character.DisplayName)
|
||||
{
|
||||
Character = character;
|
||||
}
|
||||
|
||||
public override bool IsDuplicate(BossProgressBar progressBar)
|
||||
{
|
||||
return progressBar is BossHealthBar bossHealthBar && bossHealthBar.Character == Character;
|
||||
}
|
||||
}
|
||||
|
||||
class MissionProgressBar : BossProgressBar
|
||||
{
|
||||
public readonly Mission Mission;
|
||||
|
||||
public override float State => Mission.State / (float)Mission.Prefab.MaxProgressState;
|
||||
|
||||
public override bool Completed => Mission.State >= Mission.Prefab.MaxProgressState;
|
||||
|
||||
public override bool Interrupted => Mission.Failed;
|
||||
|
||||
public MissionProgressBar(Mission mission) : base(mission.Prefab.ProgressBarLabel)
|
||||
{
|
||||
Mission = mission;
|
||||
}
|
||||
|
||||
public override bool IsDuplicate(BossProgressBar progressBar)
|
||||
{
|
||||
return progressBar is MissionProgressBar missionProgressBar && missionProgressBar.Mission == Mission;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<ISpatialEntity, int> orderIndicatorCount = new Dictionary<ISpatialEntity, int>();
|
||||
@@ -70,7 +117,7 @@ namespace Barotrauma
|
||||
private static readonly List<Item> brokenItems = new List<Item>();
|
||||
private static float brokenItemsCheckTimer;
|
||||
|
||||
private static readonly List<BossHealthBar> bossHealthBars = new List<BossHealthBar>();
|
||||
private static readonly List<BossProgressBar> bossHealthBars = new List<BossProgressBar>();
|
||||
|
||||
private static readonly Dictionary<Identifier, LocalizedString> cachedHudTexts = new Dictionary<Identifier, LocalizedString>();
|
||||
|
||||
@@ -114,7 +161,7 @@ namespace Barotrauma
|
||||
return
|
||||
character?.Inventory != null &&
|
||||
!character.Removed && !character.IsKnockedDown &&
|
||||
(controller?.User != character || !controller.HideHUD) &&
|
||||
(controller?.User != character || !controller.HideHUD || Screen.Selected.IsEditor) &&
|
||||
!IsCampaignInterfaceOpen &&
|
||||
!ConversationAction.FadeScreenToBlack;
|
||||
}
|
||||
@@ -159,7 +206,7 @@ namespace Barotrauma
|
||||
|
||||
public static void Update(float deltaTime, Character character, Camera cam)
|
||||
{
|
||||
UpdateBossHealthBars(deltaTime);
|
||||
UpdateBossProgressBars(deltaTime);
|
||||
|
||||
if (GUI.DisableHUD)
|
||||
{
|
||||
@@ -307,7 +354,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!brokenItem.IsInteractable(character)) { continue; }
|
||||
float alpha = GetDistanceBasedIconAlpha(brokenItem);
|
||||
if (alpha <= 0.0f) continue;
|
||||
if (alpha <= 0.0f) { continue; }
|
||||
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUIStyle.BrokenIcon.Value.Sprite,
|
||||
Color.Lerp(GUIStyle.Red, GUIStyle.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
|
||||
}
|
||||
@@ -548,7 +595,7 @@ namespace Barotrauma
|
||||
if (CharacterHealth.OpenHealthWindow == character.SelectedCharacter.CharacterHealth)
|
||||
{
|
||||
character.SelectedCharacter.CharacterHealth.Alignment = Alignment.Left;
|
||||
character.SelectedCharacter.CharacterHealth.DrawStatusHUD(spriteBatch);
|
||||
//character.SelectedCharacter.CharacterHealth.DrawStatusHUD(spriteBatch);
|
||||
}
|
||||
}
|
||||
else if (character.Inventory != null)
|
||||
@@ -602,7 +649,9 @@ namespace Barotrauma
|
||||
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
|
||||
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(focusName).Y;
|
||||
|
||||
if (character.FocusedCharacter.Info?.Title != null && !character.FocusedCharacter.Info.Title.IsNullOrEmpty())
|
||||
if (character.FocusedCharacter.Info?.Title != null &&
|
||||
!character.FocusedCharacter.Info.Title.IsNullOrEmpty() &&
|
||||
character.FocusedCharacter.TeamID != CharacterTeamType.Team1)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.Info.Title, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
|
||||
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(character.FocusedCharacter.Info.Title.Value).Y;
|
||||
@@ -640,20 +689,41 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowBossHealthBar(Character character)
|
||||
public static void ShowBossHealthBar(Character character, float damage)
|
||||
{
|
||||
if (character == null || character.IsDead || character.Removed) { return; }
|
||||
AddBossProgressBar(new BossHealthBar(character), damage);
|
||||
}
|
||||
|
||||
var existingBar = bossHealthBars.Find(b => b.Character == character);
|
||||
if (existingBar != null)
|
||||
public static void ShowMissionProgressBar(Mission mission)
|
||||
{
|
||||
if (mission == null || mission.Completed || mission.Failed) { return; }
|
||||
AddBossProgressBar(new MissionProgressBar(mission));
|
||||
}
|
||||
|
||||
private static void AddBossProgressBar(BossProgressBar progressBar, float damage = 0.0f)
|
||||
{
|
||||
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
|
||||
if (healthBarMode == EnemyHealthBarMode.HideAll)
|
||||
{
|
||||
existingBar.FadeTimer = BossHealthBarDuration;
|
||||
return;
|
||||
}
|
||||
|
||||
var existingBar = bossHealthBars.Find(b => b.IsDuplicate(progressBar));
|
||||
if (existingBar != null)
|
||||
{
|
||||
existingBar.FadeTimer = BossHealthBarDuration;
|
||||
if (damage > 0)
|
||||
{
|
||||
// Show the most recent target at the top of the screen
|
||||
bossHealthBars.Remove(existingBar);
|
||||
bossHealthBars.Add(existingBar);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (bossHealthBars.Count > 5)
|
||||
{
|
||||
BossHealthBar oldestHealthBar = bossHealthBars.First();
|
||||
BossProgressBar oldestHealthBar = bossHealthBars.First();
|
||||
foreach (var bar in bossHealthBars)
|
||||
{
|
||||
if (bar.TopHealthBar.BarSize < oldestHealthBar.TopHealthBar.BarSize)
|
||||
@@ -663,54 +733,65 @@ namespace Barotrauma
|
||||
}
|
||||
oldestHealthBar.FadeTimer = Math.Min(oldestHealthBar.FadeTimer, 1.0f);
|
||||
}
|
||||
|
||||
bossHealthBars.Add(new BossHealthBar(character));
|
||||
bossHealthBars.Add(progressBar);
|
||||
}
|
||||
|
||||
public static void UpdateBossHealthBars(float deltaTime)
|
||||
public static void UpdateBossProgressBars(float deltaTime)
|
||||
{
|
||||
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
|
||||
|
||||
for (int i = 0; i < bossHealthBars.Count; i++)
|
||||
{
|
||||
var bossHealthBar = bossHealthBars[i];
|
||||
|
||||
bool showTopBar = i == 0;
|
||||
if (showTopBar != bossHealthBar.TopContainer.Visible)
|
||||
bool showTopBar = i == bossHealthBars.Count - 1;
|
||||
if (showTopBar && !bossHealthBar.TopContainer.Visible)
|
||||
{
|
||||
bossHealthContainer.Recalculate();
|
||||
bossHealthBar.SideContainer.SetAsLastChild();
|
||||
SetColor(bossHealthBar, bossHealthBar.SideContainer, 0);
|
||||
}
|
||||
|
||||
bossHealthBar.TopContainer.Visible = showTopBar;
|
||||
bossHealthBar.SideContainer.Visible = !bossHealthBar.TopContainer.Visible;
|
||||
|
||||
float health = bossHealthBar.Character.Vitality / bossHealthBar.Character.MaxVitality;
|
||||
|
||||
bossHealthBar.TopHealthBar.BarSize = bossHealthBar.SideHealthBar.BarSize = bossHealthBar.State;
|
||||
float alpha = Math.Min(bossHealthBar.FadeTimer, 1.0f);
|
||||
foreach (var c in bossHealthBar.SideContainer.GetAllChildren().Concat(bossHealthBar.TopContainer.GetAllChildren()))
|
||||
|
||||
if (bossHealthBar.TopContainer.Visible)
|
||||
{
|
||||
c.Color = new Color(c.Color, (byte)(alpha * 255));
|
||||
if (c is GUITextBlock textBlock)
|
||||
SetColor(bossHealthBar, bossHealthBar.TopContainer, alpha);
|
||||
}
|
||||
if (bossHealthBar.SideContainer.Visible)
|
||||
{
|
||||
SetColor(bossHealthBar, bossHealthBar.SideContainer, alpha);
|
||||
}
|
||||
|
||||
static void SetColor(BossProgressBar bossHealthBar, GUIComponent container, float alpha)
|
||||
{
|
||||
foreach (var component in container.GetAllChildren())
|
||||
{
|
||||
textBlock.TextColor = new Color(bossHealthBar.Character.IsDead ? Color.Gray : textBlock.TextColor, (byte)(alpha * 255));
|
||||
component.Color = new Color(component.Color, (byte)(alpha * 255));
|
||||
if (component is GUITextBlock textBlock)
|
||||
{
|
||||
textBlock.TextColor = new Color(bossHealthBar.Completed ? Color.Gray : textBlock.TextColor, (byte)(alpha * 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bossHealthBar.TopHealthBar.BarSize = bossHealthBar.SideHealthBar.BarSize = health;
|
||||
|
||||
if (bossHealthBar.Character.Removed || !bossHealthBar.Character.Enabled)
|
||||
if (bossHealthBar.Interrupted)
|
||||
{
|
||||
bossHealthBar.FadeTimer = Math.Min(bossHealthBar.FadeTimer, 1.0f);
|
||||
}
|
||||
else if (bossHealthBar.Character.IsDead)
|
||||
else if (bossHealthBar.Completed)
|
||||
{
|
||||
bossHealthBar.FadeTimer = Math.Min(bossHealthBar.FadeTimer, 5.0f);
|
||||
}
|
||||
bossHealthBar.FadeTimer -= deltaTime;
|
||||
}
|
||||
|
||||
for (int i = bossHealthBars.Count - 1; i >= 0 ; i--)
|
||||
{
|
||||
var bossHealthBar = bossHealthBars[i];
|
||||
if (bossHealthBar.FadeTimer <= 0)
|
||||
if (bossHealthBar.FadeTimer <= 0 || healthBarMode == EnemyHealthBarMode.HideAll)
|
||||
{
|
||||
bossHealthBar.SideContainer.Parent?.RemoveChild(bossHealthBar.SideContainer);
|
||||
bossHealthBar.TopContainer.Parent?.RemoveChild(bossHealthBar.TopContainer);
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma
|
||||
Color? nameColor = null;
|
||||
if (Job != null) { nameColor = Job.Prefab.UIColor; }
|
||||
|
||||
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), ToolBox.LimitString(Name, GUIStyle.Font, headerTextArea.Rect.Width), textColor: nameColor, font: GUIStyle.Font)
|
||||
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), headerTextArea.RectTransform), ToolBox.LimitString(Name, GUIStyle.Font, headerTextArea.Rect.Width), textColor: nameColor, font: GUIStyle.Font)
|
||||
{
|
||||
ForceUpperCase = ForceUpperCase.Yes,
|
||||
Padding = Vector4.Zero
|
||||
@@ -92,8 +92,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (Job != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform), Job.Name, textColor: Job.Prefab.UIColor, font: font)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), headerTextArea.RectTransform), Job.Name, textColor: Job.Prefab.UIColor, font: font)
|
||||
{
|
||||
Padding = Vector4.Zero
|
||||
};
|
||||
@@ -101,7 +101,7 @@ namespace Barotrauma
|
||||
|
||||
if (PersonalityTrait != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform),
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.25f), headerTextArea.RectTransform),
|
||||
TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), PersonalityTrait.DisplayName),
|
||||
font: font)
|
||||
{
|
||||
@@ -109,7 +109,23 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
if (Job != null && (Character == null || !Character.IsDead))
|
||||
GUIButton manageTalentButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.25f), headerTextArea.RectTransform),
|
||||
text: TextManager.Get("ClientPermission.ManageBotTalents"), style: "GUIButtonSmall")
|
||||
{
|
||||
Enabled = false,
|
||||
UserData = TalentMenu.ManageBotTalentsButtonUserData,
|
||||
TextBlock =
|
||||
{
|
||||
AutoScaleHorizontal = true
|
||||
}
|
||||
};
|
||||
|
||||
if (TalentMenu.CanManageTalents(this))
|
||||
{
|
||||
manageTalentButton.Enabled = true;
|
||||
}
|
||||
|
||||
if (Job != null && Character is not { IsDead: true })
|
||||
{
|
||||
var skillsArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.63f), paddedFrame.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter))
|
||||
{
|
||||
@@ -120,7 +136,7 @@ namespace Barotrauma
|
||||
skills.Sort((s1, s2) => -s1.Level.CompareTo(s2.Level));
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("skills"), string.Empty), font: font) { Padding = Vector4.Zero };
|
||||
|
||||
|
||||
foreach (Skill skill in skills)
|
||||
{
|
||||
Color textColor = Color.White * (0.5f + skill.Level / 200.0f);
|
||||
@@ -144,7 +160,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Character != null && Character.IsDead)
|
||||
else if (Character is { IsDead: true })
|
||||
{
|
||||
var deadArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.63f), paddedFrame.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter))
|
||||
{
|
||||
@@ -524,25 +540,33 @@ namespace Barotrauma
|
||||
|
||||
string ragdollFile = inc.ReadString();
|
||||
Identifier npcId = inc.ReadIdentifier();
|
||||
|
||||
Identifier factionId = inc.ReadIdentifier();
|
||||
float minReputationToHire = 0.0f;
|
||||
if (factionId != default)
|
||||
{
|
||||
minReputationToHire = inc.ReadSingle();
|
||||
}
|
||||
|
||||
uint jobIdentifier = inc.ReadUInt32();
|
||||
int variant = inc.ReadByte();
|
||||
|
||||
JobPrefab jobPrefab = null;
|
||||
Dictionary<Identifier, float> skillLevels = new Dictionary<Identifier, float>();
|
||||
if (jobIdentifier > 0)
|
||||
{
|
||||
{
|
||||
jobPrefab = JobPrefab.Prefabs.Find(jp => jp.UintIdentifier == jobIdentifier);
|
||||
foreach (SkillPrefab skillPrefab in jobPrefab.Skills.OrderBy(s => s.Identifier))
|
||||
{
|
||||
float skillLevel = inc.ReadSingle();
|
||||
skillLevels.Add(skillPrefab.Identifier, skillLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: animations
|
||||
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant, npcIdentifier: npcId)
|
||||
{
|
||||
ID = infoID
|
||||
ID = infoID,
|
||||
MinReputationToHire = (factionId, minReputationToHire)
|
||||
};
|
||||
ch.RecreateHead(tagSet.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||
ch.Head.SkinColor = skinColor;
|
||||
|
||||
@@ -501,7 +501,7 @@ namespace Barotrauma
|
||||
info?.ClearSavedStatValues(statType);
|
||||
for (int i = 0; i < savedStatValueCount; i++)
|
||||
{
|
||||
string statIdentifier = msg.ReadString();
|
||||
Identifier statIdentifier = msg.ReadIdentifier();
|
||||
float statValue = msg.ReadSingle();
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
|
||||
@@ -32,12 +32,6 @@ namespace Barotrauma
|
||||
|
||||
public static Sprite DamageOverlay => DamageOverlayPrefab.Prefabs.ActivePrefab.DamageOverlay;
|
||||
|
||||
private readonly static LocalizedString[] strengthTexts = new LocalizedString[]
|
||||
{
|
||||
TextManager.Get("AfflictionStrengthLow"),
|
||||
TextManager.Get("AfflictionStrengthMedium"),
|
||||
TextManager.Get("AfflictionStrengthHigh")
|
||||
};
|
||||
|
||||
private Point screenResolution;
|
||||
|
||||
@@ -89,11 +83,23 @@ namespace Barotrauma
|
||||
private int selectedLimbIndex = -1;
|
||||
private LimbHealth currentDisplayedLimb;
|
||||
|
||||
/// <summary>
|
||||
/// Container for the icons above the health bar
|
||||
/// </summary>
|
||||
private GUIComponent afflictionIconContainer;
|
||||
|
||||
private GUIButton showHiddenAfflictionsButton;
|
||||
|
||||
/// <summary>
|
||||
/// Container for passive afflictions that have been hidden from afflictionIconContainer
|
||||
/// </summary>
|
||||
private GUIComponent hiddenAfflictionIconContainer;
|
||||
|
||||
private GUIProgressBar healthWindowHealthBar;
|
||||
private GUIProgressBar healthWindowHealthBarShadow;
|
||||
|
||||
private GUITextBlock characterName;
|
||||
private GUIListBox afflictionIconContainer;
|
||||
private GUIListBox afflictionIconList;
|
||||
private GUILayoutGroup treatmentLayout;
|
||||
private GUIListBox recommendedTreatmentContainer;
|
||||
|
||||
@@ -331,7 +337,7 @@ namespace Barotrauma
|
||||
deadIndicator.AutoScaleHorizontal = true;
|
||||
}
|
||||
|
||||
afflictionIconContainer = new GUIListBox(new RectTransform(new Vector2(0.25f, 1.0f), characterIndicatorArea.RectTransform), style: null);
|
||||
afflictionIconList = new GUIListBox(new RectTransform(new Vector2(0.25f, 1.0f), characterIndicatorArea.RectTransform), style: null);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), healthWindowVerticalLayout.RectTransform),
|
||||
TextManager.Get("SuitableTreatments"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomCenter);
|
||||
@@ -379,6 +385,25 @@ namespace Barotrauma
|
||||
Enabled = true
|
||||
};
|
||||
|
||||
afflictionIconContainer = new GUILayoutGroup(
|
||||
HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAfflictionArea, GUI.Canvas),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
|
||||
showHiddenAfflictionsButton = new GUIButton(new RectTransform(new Point(afflictionIconContainer.Rect.Height), afflictionIconContainer.RectTransform), style: "GUIButtonCircular")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
hiddenAfflictionIconContainer = new GUILayoutGroup(
|
||||
HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.HealthBarAfflictionArea, GUI.Canvas),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
|
||||
UpdateAlignment();
|
||||
|
||||
SuicideButton = new GUIButton(new RectTransform(new Vector2(0.1f, 0.02f), GUI.Canvas, Anchor.TopCenter)
|
||||
@@ -596,7 +621,7 @@ namespace Barotrauma
|
||||
|
||||
public void UpdateHUD(float deltaTime)
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
if (GUI.DisableHUD) { return; }
|
||||
if (openHealthWindow != null)
|
||||
{
|
||||
if (openHealthWindow != Character.Controlled?.CharacterHealth && openHealthWindow != Character.Controlled?.SelectedCharacter?.CharacterHealth)
|
||||
@@ -700,6 +725,8 @@ namespace Barotrauma
|
||||
distortTimer = 0.0f;
|
||||
}
|
||||
|
||||
UpdateStatusHUD(deltaTime);
|
||||
|
||||
if (PlayerInput.KeyHit(InputType.Health) && GUI.KeyboardDispatcher.Subscriber == null &&
|
||||
Character.Controlled.AllowInput && !toggledThisFrame)
|
||||
{
|
||||
@@ -726,9 +753,9 @@ namespace Barotrauma
|
||||
OpenHealthWindow = null;
|
||||
}
|
||||
|
||||
foreach (GUIComponent afflictionIcon in afflictionIconContainer.Content.Children)
|
||||
foreach (GUIComponent afflictionIcon in afflictionIconList.Content.Children)
|
||||
{
|
||||
if (!(afflictionIcon.UserData is Affliction affliction)) { continue; }
|
||||
if (afflictionIcon.UserData is not Affliction affliction) { continue; }
|
||||
if (affliction.AppliedAsFailedTreatmentTime > Timing.TotalTime - 1.0 && afflictionIcon.FlashTimer <= 0.0f)
|
||||
{
|
||||
afflictionIcon.Flash(GUIStyle.Red);
|
||||
@@ -900,7 +927,7 @@ namespace Barotrauma
|
||||
|
||||
healthBarHolder.CanBeFocused = healthBar.CanBeFocused = healthBarShadow.CanBeFocused = !Character.ShouldLockHud();
|
||||
if (Character.AllowInput && UseHealthWindow && !Character.DisableHealthWindow && healthBar.Enabled && healthBar.CanBeFocused &&
|
||||
(GUI.IsMouseOn(healthBar) || highlightedAfflictionIcon != null) && Inventory.SelectedSlot == null)
|
||||
(GUI.IsMouseOn(healthBar) || GUI.MouseOn?.UserData is AfflictionPrefab) && Inventory.SelectedSlot == null)
|
||||
{
|
||||
healthBar.State = GUIComponent.ComponentState.Hover;
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
@@ -960,7 +987,12 @@ namespace Barotrauma
|
||||
}
|
||||
else if (Character.Controlled == Character && !CharacterHUD.IsCampaignInterfaceOpen)
|
||||
{
|
||||
healthBarHolder.AddToGUIUpdateList();
|
||||
healthBarHolder.AddToGUIUpdateList();
|
||||
afflictionIconContainer.AddToGUIUpdateList();
|
||||
if (hiddenAfflictionIconContainer.Visible)
|
||||
{
|
||||
hiddenAfflictionIconContainer.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
if (SuicideButton.Visible && Character == Character.Controlled)
|
||||
{
|
||||
@@ -989,7 +1021,7 @@ namespace Barotrauma
|
||||
if (affliction.Prefab.AfflictionOverlay != null)
|
||||
{
|
||||
Sprite ScreenAfflictionOverlay = affliction.Prefab.AfflictionOverlay;
|
||||
ScreenAfflictionOverlay?.Draw(spriteBatch, Vector2.Zero, Color.White * (affliction.GetAfflictionOverlayMultiplier()), Vector2.Zero, 0.0f,
|
||||
ScreenAfflictionOverlay?.Draw(spriteBatch, Vector2.Zero, Color.White * affliction.GetAfflictionOverlayMultiplier(), Vector2.Zero, 0.0f,
|
||||
new Vector2(GameMain.GraphicsWidth / DamageOverlay.size.X, GameMain.GraphicsHeight / DamageOverlay.size.Y));
|
||||
}
|
||||
}
|
||||
@@ -1021,94 +1053,134 @@ namespace Barotrauma
|
||||
// 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, LocalizedString NameToolTip)? highlightedAfflictionIcon = null;
|
||||
public void DrawStatusHUD(SpriteBatch spriteBatch)
|
||||
//private (Affliction Affliction, LocalizedString NameToolTip)? highlightedAfflictionIcon = null;
|
||||
|
||||
private readonly List<Affliction> statusIcons = new List<Affliction>();
|
||||
private readonly Dictionary<AfflictionPrefab, float> statusIconVisibleTime = new Dictionary<AfflictionPrefab, float>();
|
||||
private const float HideStatusIconDelay = 5.0f;
|
||||
|
||||
public void UpdateStatusHUD(float deltaTime)
|
||||
{
|
||||
highlightedAfflictionIcon = null;
|
||||
//Rectangle interactArea = healthBar.Rect;
|
||||
if (Character.Controlled?.SelectedCharacter == null && openHealthWindow == null)
|
||||
{
|
||||
var statusIcons = new List<(Affliction Affliction, LocalizedString Warning)>();
|
||||
statusIcons.Clear();
|
||||
if (Character.InPressure)
|
||||
{
|
||||
statusIcons.Add((pressureAffliction, TextManager.Get("PressureHUDWarning")));
|
||||
statusIcons.Add(pressureAffliction);
|
||||
}
|
||||
if (Character.CurrentHull != null && Character.OxygenAvailable < LowOxygenThreshold && oxygenLowAffliction.Strength < oxygenLowAffliction.Prefab.ShowIconThreshold)
|
||||
{
|
||||
statusIcons.Add((oxygenLowAffliction, TextManager.Get("OxygenHUDWarning")));
|
||||
statusIcons.Add(oxygenLowAffliction);
|
||||
}
|
||||
|
||||
foreach (Affliction affliction in currentDisplayedAfflictions)
|
||||
{
|
||||
statusIcons.Add((affliction, affliction.Prefab.Name));
|
||||
statusIcons.Add(affliction);
|
||||
}
|
||||
|
||||
Vector2 highlightedIconPos = Vector2.Zero;
|
||||
Rectangle afflictionArea = HUDLayoutSettings.AfflictionAreaLeft;
|
||||
|
||||
// Push the icons down since the portrait doesn't get rendered
|
||||
int spacing = GUI.IntScale(10);
|
||||
if (Character.ShouldLockHud())
|
||||
{
|
||||
afflictionArea.Y += HUDLayoutSettings.PortraitArea.Height;
|
||||
// Push the icons down since the portrait doesn't get rendered
|
||||
afflictionIconContainer.RectTransform.ScreenSpaceOffset = new Point(0, HUDLayoutSettings.PortraitArea.Height);
|
||||
hiddenAfflictionIconContainer.RectTransform.ScreenSpaceOffset = new Point(0, -hiddenAfflictionIconContainer.Rect.Height - spacing + HUDLayoutSettings.PortraitArea.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
afflictionIconContainer.RectTransform.ScreenSpaceOffset = new Point(0, 0);
|
||||
hiddenAfflictionIconContainer.RectTransform.ScreenSpaceOffset = new Point(0, -hiddenAfflictionIconContainer.Rect.Height - spacing);
|
||||
}
|
||||
//remove affliction icons for afflictions that no longer exist
|
||||
|
||||
bool horizontal = afflictionArea.Width > afflictionArea.Height;
|
||||
int iconSize = horizontal ? afflictionArea.Height : afflictionArea.Width;
|
||||
|
||||
Point pos = new Point(afflictionArea.Right - iconSize, afflictionArea.Top);
|
||||
RemoveNonExistentIcons(afflictionIconContainer);
|
||||
RemoveNonExistentIcons(hiddenAfflictionIconContainer);
|
||||
void RemoveNonExistentIcons(GUIComponent container)
|
||||
{
|
||||
for (int i = container.CountChildren - 1; i >= 0; i--)
|
||||
{
|
||||
var child = container.GetChild(i);
|
||||
if (child.UserData is not AfflictionPrefab afflictionPrefab) { continue; }
|
||||
if (!statusIcons.Any(s => s.Prefab == afflictionPrefab))
|
||||
{
|
||||
container.RemoveChild(child);
|
||||
statusIconVisibleTime.Remove(afflictionPrefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var statusIcon in statusIcons)
|
||||
{
|
||||
Affliction affliction = statusIcon.Affliction;
|
||||
Affliction affliction = statusIcon;
|
||||
AfflictionPrefab afflictionPrefab = affliction.Prefab;
|
||||
|
||||
Rectangle afflictionIconRect = new Rectangle(pos, new Point(iconSize));
|
||||
if (afflictionIconRect.Contains(PlayerInput.MousePosition) && !Character.ShouldLockHud() && GUI.MouseOn == null)
|
||||
if (!statusIconVisibleTime.ContainsKey(afflictionPrefab)) { statusIconVisibleTime.Add(afflictionPrefab, 0.0f); }
|
||||
statusIconVisibleTime[afflictionPrefab] += deltaTime;
|
||||
|
||||
var matchingIcon =
|
||||
afflictionIconContainer.GetChildByUserData(afflictionPrefab) ??
|
||||
hiddenAfflictionIconContainer.GetChildByUserData(afflictionPrefab);
|
||||
if (matchingIcon == null)
|
||||
{
|
||||
highlightedAfflictionIcon = statusIcon;
|
||||
highlightedIconPos = afflictionIconRect.Location.ToVector2();
|
||||
matchingIcon = new GUIButton(new RectTransform(new Point(afflictionIconContainer.Rect.Height), afflictionIconContainer.RectTransform), style: null)
|
||||
{
|
||||
UserData = afflictionPrefab,
|
||||
ToolTip = affliction.Prefab.Name,
|
||||
CanBeSelected = false
|
||||
};
|
||||
if (affliction == pressureAffliction)
|
||||
{
|
||||
matchingIcon.ToolTip = TextManager.Get("PressureHUDWarning");
|
||||
}
|
||||
else if (affliction == pressureAffliction)
|
||||
{
|
||||
matchingIcon.ToolTip = TextManager.Get("OxygenHUDWarning");
|
||||
}
|
||||
new GUIImage(new RectTransform(Vector2.One, matchingIcon.RectTransform, Anchor.BottomCenter), afflictionPrefab.Icon, scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
|
||||
if (affliction.DamagePerSecond > 1.0f)
|
||||
if (afflictionPrefab.HideIconAfterDelay && statusIconVisibleTime[afflictionPrefab] > HideStatusIconDelay)
|
||||
{
|
||||
Rectangle glowRect = afflictionIconRect;
|
||||
glowRect.Inflate((int)(20 * GUI.Scale), (int)(20 * GUI.Scale));
|
||||
var glow = GUIStyle.GetComponentStyle("OuterGlowCircular");
|
||||
glow.Sprites[GUIComponent.ComponentState.None][0].Draw(
|
||||
spriteBatch, glowRect,
|
||||
GUIStyle.Red * (float)((Math.Sin(affliction.DamagePerSecondTimer * MathHelper.TwoPi - MathHelper.PiOver2) + 1.0f) * 0.5f));
|
||||
matchingIcon.RectTransform.Parent = hiddenAfflictionIconContainer.RectTransform;
|
||||
}
|
||||
var image = matchingIcon.GetChild<GUIImage>();
|
||||
image.Color = GetAfflictionIconColor(afflictionPrefab, affliction);
|
||||
image.HoverColor = Color.Lerp(image.Color, Color.White, 0.5f);
|
||||
|
||||
float alphaMultiplier = highlightedAfflictionIcon == statusIcon ? 1f : 0.8f;
|
||||
|
||||
afflictionPrefab.Icon?.Draw(spriteBatch,
|
||||
pos.ToVector2(),
|
||||
/*highlightedIcon == statusIcon ? statusIcon.First.Prefab.IconColor : statusIcon.First.Prefab.IconColor * 0.8f,*/ // OLD IMPLEMENTATION
|
||||
GetAfflictionIconColor(afflictionPrefab, affliction) * alphaMultiplier,
|
||||
rotate: 0,
|
||||
scale: iconSize / afflictionPrefab.Icon.size.X);
|
||||
|
||||
if (horizontal)
|
||||
pos.X -= iconSize + (int)(5 * GUI.Scale);
|
||||
else
|
||||
pos.Y += iconSize + (int)(5 * GUI.Scale);
|
||||
if (affliction.DamagePerSecond > 1.0f && matchingIcon.FlashTimer <= 0.0f)
|
||||
{
|
||||
matchingIcon.Flash(useCircularFlash: true, flashDuration: 1.5f, flashRectInflate: Vector2.One * 15.0f * GUI.Scale);
|
||||
image.Pulsate(Vector2.One, Vector2.One * 1.2f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightedAfflictionIcon != null)
|
||||
afflictionIconContainer.RectTransform.SortChildren((r1, r2) =>
|
||||
{
|
||||
LocalizedString nameTooltip = highlightedAfflictionIcon.Value.NameToolTip;
|
||||
Vector2 offset = GUIStyle.Font.MeasureString(nameTooltip);
|
||||
if (r1.GUIComponent.UserData is not AfflictionPrefab prefab1) { return -1; }
|
||||
if (r2.GUIComponent.UserData is not AfflictionPrefab prefab2) { return 1; }
|
||||
var index1 = statusIcons.IndexOf(s => s.Prefab == prefab1);
|
||||
var index2 = statusIcons.IndexOf(s => s.Prefab == prefab2);
|
||||
return index1.CompareTo(index2);
|
||||
});
|
||||
(afflictionIconContainer as GUILayoutGroup).NeedsToRecalculate = true;
|
||||
|
||||
GUI.DrawString(spriteBatch,
|
||||
alignment == Alignment.Left ? highlightedIconPos + offset : highlightedIconPos - offset,
|
||||
nameTooltip,
|
||||
Color.White * 0.8f, Color.Black * 0.5f);
|
||||
Rectangle hiddenAfflictionHoverArea = showHiddenAfflictionsButton.Rect;
|
||||
foreach (GUIComponent child in hiddenAfflictionIconContainer.Children)
|
||||
{
|
||||
hiddenAfflictionHoverArea = Rectangle.Union(hiddenAfflictionHoverArea, child.Rect);
|
||||
}
|
||||
|
||||
afflictionIconContainer.Visible = true;
|
||||
hiddenAfflictionIconContainer.Visible =
|
||||
showHiddenAfflictionsButton.Rect.Contains(PlayerInput.MousePosition) ||
|
||||
(hiddenAfflictionIconContainer.Visible && hiddenAfflictionHoverArea.Contains(PlayerInput.MousePosition));
|
||||
showHiddenAfflictionsButton.Visible = hiddenAfflictionIconContainer.CountChildren > 0;
|
||||
showHiddenAfflictionsButton.IgnoreLayoutGroups = !showHiddenAfflictionsButton.Visible;
|
||||
showHiddenAfflictionsButton.Text = $"+{hiddenAfflictionIconContainer.CountChildren}";
|
||||
|
||||
if (Vitality > 0.0f)
|
||||
{
|
||||
float currHealth = healthBar.BarSize;
|
||||
@@ -1126,6 +1198,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
afflictionIconContainer.Visible = hiddenAfflictionIconContainer.Visible = false;
|
||||
if (Vitality > 0.0f)
|
||||
{
|
||||
float currHealth = healthWindowHealthBar.BarSize;
|
||||
@@ -1150,18 +1223,20 @@ namespace Barotrauma
|
||||
|
||||
public static Color GetAfflictionIconColor(AfflictionPrefab prefab, float afflictionStrength)
|
||||
{
|
||||
//use sqrt to make the color change rapidly when strength is low
|
||||
//(low strength is where seeing the severity of the affliction makes more difference - at high strengths the character is already unconscious or dead)
|
||||
float colorT = MathF.Sqrt(afflictionStrength / prefab.MaxStrength);
|
||||
// No specific colors, use generic
|
||||
if (prefab.IconColors == null)
|
||||
{
|
||||
if (prefab.IsBuff)
|
||||
{
|
||||
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUIStyle.BuffColorLow, GUIStyle.BuffColorMedium, GUIStyle.BuffColorHigh);
|
||||
return ToolBox.GradientLerp(colorT, GUIStyle.BuffColorLow, GUIStyle.BuffColorMedium, GUIStyle.BuffColorHigh);
|
||||
}
|
||||
|
||||
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, GUIStyle.DebuffColorLow, GUIStyle.DebuffColorMedium, GUIStyle.DebuffColorHigh);
|
||||
return ToolBox.GradientLerp(colorT, GUIStyle.DebuffColorLow, GUIStyle.DebuffColorMedium, GUIStyle.DebuffColorHigh);
|
||||
}
|
||||
|
||||
return ToolBox.GradientLerp(afflictionStrength / prefab.MaxStrength, prefab.IconColors);
|
||||
return ToolBox.GradientLerp(colorT, prefab.IconColors);
|
||||
}
|
||||
|
||||
public static Color GetAfflictionIconColor(Affliction affliction) => GetAfflictionIconColor(affliction.Prefab, affliction);
|
||||
@@ -1172,7 +1247,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (selectedLimb == null)
|
||||
{
|
||||
afflictionIconContainer.Content.ClearChildren();
|
||||
afflictionIconList.Content.ClearChildren();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1207,7 +1282,7 @@ namespace Barotrauma
|
||||
|
||||
private void CreateAfflictionInfos(IEnumerable<Affliction> afflictions)
|
||||
{
|
||||
afflictionIconContainer.ClearChildren();
|
||||
afflictionIconList.ClearChildren();
|
||||
displayedAfflictions.Clear();
|
||||
|
||||
Affliction mostSevereAffliction = SortAfflictionsBySeverity(afflictions, excludeBuffs: false).FirstOrDefault();
|
||||
@@ -1217,7 +1292,7 @@ namespace Barotrauma
|
||||
{
|
||||
displayedAfflictions.Add((affliction, affliction.Strength));
|
||||
|
||||
var frame = new GUIButton(new RectTransform(new Vector2(1.0f, 0.25f), afflictionIconContainer.Content.RectTransform), style: "ListBoxElement")
|
||||
var frame = new GUIButton(new RectTransform(new Vector2(1.0f, 0.25f), afflictionIconList.Content.RectTransform), style: "ListBoxElement")
|
||||
{
|
||||
UserData = affliction,
|
||||
OnClicked = SelectAffliction
|
||||
@@ -1275,7 +1350,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
buttonToSelect?.OnClicked(buttonToSelect, buttonToSelect.UserData);
|
||||
afflictionIconContainer.RecalculateChildren();
|
||||
afflictionIconList.RecalculateChildren();
|
||||
}
|
||||
|
||||
private void CreateRecommendedTreatments()
|
||||
@@ -1386,7 +1461,7 @@ namespace Barotrauma
|
||||
|
||||
recommendedTreatmentContainer.RecalculateChildren();
|
||||
|
||||
afflictionIconContainer.Content.RectTransform.SortChildren((r1, r2) =>
|
||||
afflictionIconList.Content.RectTransform.SortChildren((r1, r2) =>
|
||||
{
|
||||
var first = r1.GUIComponent.UserData as Affliction;
|
||||
var second = r2.GUIComponent.UserData as Affliction;
|
||||
@@ -1437,7 +1512,10 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
var description = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform),
|
||||
affliction.Prefab.Description, textAlignment: Alignment.TopLeft, wrap: true)
|
||||
affliction.Prefab.GetDescription(
|
||||
affliction.Strength,
|
||||
Character == Character.Controlled ? AfflictionPrefab.Description.TargetType.Self : AfflictionPrefab.Description.TargetType.OtherCharacter),
|
||||
textAlignment: Alignment.TopLeft, wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
@@ -1449,8 +1527,7 @@ namespace Barotrauma
|
||||
|
||||
Point nameDims = new Point(afflictionName.Rect.Width, (int)(GUIStyle.LargeFont.Size * 1.5f));
|
||||
|
||||
afflictionStrength.Text = strengthTexts[
|
||||
MathHelper.Clamp((int)Math.Floor((affliction.Strength / affliction.Prefab.MaxStrength) * strengthTexts.Length), 0, strengthTexts.Length - 1)];
|
||||
afflictionStrength.Text = affliction.GetStrengthText();
|
||||
|
||||
Vector2 strengthDims = GUIStyle.SubHeadingFont.MeasureString(afflictionStrength.Text);
|
||||
|
||||
@@ -1482,10 +1559,9 @@ namespace Barotrauma
|
||||
private bool SelectAffliction(GUIButton button, object userData)
|
||||
{
|
||||
bool selected = button.Selected;
|
||||
foreach (var child in afflictionIconContainer.Content.Children)
|
||||
foreach (var child in afflictionIconList.Content.Children)
|
||||
{
|
||||
GUIButton btn = child.GetChild<GUIButton>();
|
||||
if (btn != null)
|
||||
if (child is GUIButton btn)
|
||||
{
|
||||
btn.Selected = btn == button && !selected;
|
||||
}
|
||||
@@ -1516,7 +1592,7 @@ namespace Barotrauma
|
||||
afflictionEffectColor = GUIStyle.Green;
|
||||
}
|
||||
|
||||
var child = afflictionIconContainer.Content.FindChild(affliction);
|
||||
var child = afflictionIconList.Content.FindChild(affliction);
|
||||
|
||||
var afflictionStrengthPredictionBar = child.GetChild<GUILayoutGroup>().GetChildByUserData("afflictionstrengthprediction") as GUIProgressBar;
|
||||
afflictionStrengthPredictionBar.BarSize = 0.0f;
|
||||
@@ -1581,8 +1657,7 @@ namespace Barotrauma
|
||||
|
||||
var strengthText = labelContainer.GetChildByUserData("strength") as GUITextBlock;
|
||||
|
||||
strengthText.Text = strengthTexts[
|
||||
MathHelper.Clamp((int)Math.Floor((affliction.Strength / affliction.Prefab.MaxStrength) * strengthTexts.Length), 0, strengthTexts.Length - 1)];
|
||||
strengthText.Text = affliction.GetStrengthText();
|
||||
|
||||
strengthText.TextColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red,
|
||||
affliction.Strength / affliction.Prefab.MaxStrength);
|
||||
@@ -1836,14 +1911,14 @@ namespace Barotrauma
|
||||
i++;
|
||||
}
|
||||
|
||||
if (selectedLimbIndex > -1 && afflictionIconContainer.Content.CountChildren > 0)
|
||||
if (selectedLimbIndex > -1 && afflictionIconList.Content.CountChildren > 0)
|
||||
{
|
||||
LimbHealth limbHealth = limbHealths[selectedLimbIndex];
|
||||
if (limbHealth?.IndicatorSprite != null)
|
||||
{
|
||||
Rectangle selectedLimbArea = GetLimbHighlightArea(limbHealth, drawArea);
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(afflictionIconContainer.Rect.X, afflictionIconContainer.Rect.Y),
|
||||
new Vector2(afflictionIconList.Rect.X, afflictionIconList.Rect.Y),
|
||||
selectedLimbArea.Center.ToVector2(),
|
||||
Color.LightGray * 0.5f, width: 4);
|
||||
}
|
||||
|
||||
@@ -600,7 +600,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (damageModifier.DamageMultiplier > 0 && !string.IsNullOrWhiteSpace(damageModifier.DamageParticle))
|
||||
{
|
||||
overrideParticle = GameMain.ParticleManager?.FindPrefab(damageModifier.DamageParticle);
|
||||
overrideParticle = ParticleManager.FindPrefab(damageModifier.DamageParticle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -647,7 +647,7 @@ namespace Barotrauma
|
||||
dripParticleTimer += wetTimer * deltaTime * Mass * (wetTimer > 0.9f ? 50.0f : 5.0f);
|
||||
if (dripParticleTimer > 1.0f)
|
||||
{
|
||||
float dropRadius = body.BodyShape == PhysicsBody.Shape.Rectangle ? Math.Min(body.width, body.height) : body.radius;
|
||||
float dropRadius = body.BodyShape == PhysicsBody.Shape.Rectangle ? Math.Min(body.Width, body.Height) : body.Radius;
|
||||
GameMain.ParticleManager.CreateParticle(
|
||||
"waterdrop",
|
||||
WorldPosition + Rand.Vector(Rand.Range(0.0f, ConvertUnits.ToDisplayUnits(dropRadius))),
|
||||
@@ -684,10 +684,10 @@ namespace Barotrauma
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null, bool disableDeformations = false)
|
||||
{
|
||||
float brightness = Math.Max(1.0f - burnOverLayStrength, 0.2f);
|
||||
var spriteParams = Params.GetSprite();
|
||||
if (spriteParams == null) { return; }
|
||||
|
||||
float burn = spriteParams.IgnoreTint ? 0 : burnOverLayStrength;
|
||||
float brightness = Math.Max(1.0f - burn, 0.2f);
|
||||
Color clr = spriteParams.Color;
|
||||
if (!spriteParams.IgnoreTint)
|
||||
{
|
||||
|
||||
@@ -1136,6 +1136,17 @@ namespace Barotrauma
|
||||
});
|
||||
AssignRelayToServer("debugdraw", false);
|
||||
|
||||
AssignOnExecute("debugdrawlocalization", (string[] args) =>
|
||||
{
|
||||
if (args.None() || !bool.TryParse(args[0], out bool state))
|
||||
{
|
||||
state = !TextManager.DebugDraw;
|
||||
}
|
||||
TextManager.DebugDraw = state;
|
||||
NewMessage("Localization debug draw mode " + (TextManager.DebugDraw ? "enabled" : "disabled"), Color.White);
|
||||
});
|
||||
AssignRelayToServer("debugdraw", false);
|
||||
|
||||
AssignOnExecute("togglevoicechatfilters", (string[] args) =>
|
||||
{
|
||||
if (args.None() || !bool.TryParse(args[0], out bool state))
|
||||
@@ -1695,6 +1706,8 @@ namespace Barotrauma
|
||||
config.Language = language;
|
||||
GameSettings.SetCurrentConfig(config);
|
||||
}
|
||||
|
||||
HashSet<string> missingTexts = new HashSet<string>();
|
||||
|
||||
//key = text tag, value = list of languages the tag is missing from
|
||||
Dictionary<Identifier, HashSet<LanguageIdentifier>> missingTags = new Dictionary<Identifier, HashSet<LanguageIdentifier>>();
|
||||
@@ -1755,20 +1768,38 @@ namespace Barotrauma
|
||||
|
||||
foreach (Type itemComponentType in typeof(ItemComponent).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(ItemComponent))))
|
||||
{
|
||||
foreach (var property in itemComponentType.GetProperties())
|
||||
checkSerializableEntityType(itemComponentType);
|
||||
}
|
||||
checkSerializableEntityType(typeof(Item));
|
||||
checkSerializableEntityType(typeof(Hull));
|
||||
checkSerializableEntityType(typeof(Structure));
|
||||
|
||||
void checkSerializableEntityType(Type t)
|
||||
{
|
||||
foreach (var property in t.GetProperties())
|
||||
{
|
||||
if (!property.IsDefined(typeof(InGameEditable), false)) { continue; }
|
||||
if (!property.IsDefined(typeof(Editable), false)) { continue; }
|
||||
|
||||
string propertyTag = $"{property.DeclaringType.Name}.{property.Name}";
|
||||
|
||||
addIfMissingAll(language,
|
||||
if (addIfMissingAll(language,
|
||||
propertyTag.ToIdentifier(),
|
||||
property.Name.ToIdentifier(),
|
||||
$"sp.{propertyTag}.name".ToIdentifier());
|
||||
$"sp.{property.Name}.name".ToIdentifier(),
|
||||
$"sp.{propertyTag}.name".ToIdentifier()) && language == "English".ToLanguageIdentifier())
|
||||
{
|
||||
missingTexts.Add($"<sp.{propertyTag.ToLower()}.name>{property.Name.FormatCamelCaseWithSpaces()}</sp.{propertyTag.ToLower()}.name>");
|
||||
}
|
||||
|
||||
addIfMissingAll(language,
|
||||
var description = (property.GetCustomAttributes(true).First(a => a is Serialize) as Serialize).Description;
|
||||
|
||||
if (addIfMissingAll(language,
|
||||
$"sp.{propertyTag}.description".ToIdentifier(),
|
||||
$"{property.Name.ToIdentifier()}.description".ToIdentifier());
|
||||
$"sp.{property.Name}.description".ToIdentifier(),
|
||||
$"{property.Name.ToIdentifier()}.description".ToIdentifier()) && language == "English".ToLanguageIdentifier())
|
||||
{
|
||||
missingTexts.Add($"<sp.{propertyTag.ToLower()}.description>{description}</sp.{propertyTag.ToLower()}.description>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1792,7 +1823,18 @@ namespace Barotrauma
|
||||
|
||||
Identifier afflictionId = affliction.TranslationIdentifier;
|
||||
addIfMissing($"afflictionname.{afflictionId}".ToIdentifier(), language);
|
||||
addIfMissing($"afflictiondescription.{afflictionId}".ToIdentifier(), language);
|
||||
|
||||
if (affliction.Descriptions.Any())
|
||||
{
|
||||
foreach (var description in affliction.Descriptions)
|
||||
{
|
||||
addIfMissing(description.TextTag, language);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addIfMissing($"afflictiondescription.{afflictionId}".ToIdentifier(), language);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var talentTree in TalentTree.JobTalentTrees)
|
||||
@@ -1889,6 +1931,23 @@ namespace Barotrauma
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
SwapLanguage(TextManager.DefaultLanguage);
|
||||
|
||||
if (missingTexts.Any())
|
||||
{
|
||||
ShowQuestionPrompt("Dump the property names and descriptions missing from English to a new xml file? Y/N",
|
||||
(option) =>
|
||||
{
|
||||
if (option.ToLowerInvariant() == "y")
|
||||
{
|
||||
string path = "newtexts.txt";
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(path, missingTexts);
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(path));
|
||||
SwapLanguage(TextManager.DefaultLanguage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addIfMissing(Identifier tag, LanguageIdentifier language)
|
||||
{
|
||||
if (!tags[language].Contains(tag))
|
||||
@@ -1897,15 +1956,90 @@ namespace Barotrauma
|
||||
missingTags[tag].Add(language);
|
||||
}
|
||||
}
|
||||
void addIfMissingAll(LanguageIdentifier language, params Identifier[] potentialTags)
|
||||
bool addIfMissingAll(LanguageIdentifier language, params Identifier[] potentialTags)
|
||||
{
|
||||
if (!potentialTags.Any(t => tags[language].Contains(t)))
|
||||
{
|
||||
var tag = potentialTags.First();
|
||||
if (!missingTags.ContainsKey(tag)) { missingTags[tag] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[tag].Add(language);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
commands.Add(new Command("checkduplicateloca", "", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
ThrowError("Please specify a file path.");
|
||||
return;
|
||||
}
|
||||
XDocument doc1 = XMLExtensions.TryLoadXml(args[0]);
|
||||
if (doc1?.Root == null)
|
||||
{
|
||||
ThrowError($"Could not load the file \"{args[0]}\"");
|
||||
return;
|
||||
}
|
||||
List<(string tag, string text)> texts = new List<(string tag, string text)>();
|
||||
|
||||
bool duplicatesFound = false;
|
||||
foreach (XElement element in doc1.Root.Elements())
|
||||
{
|
||||
string tag = element.Name.ToString();
|
||||
string text = element.ElementInnerText();
|
||||
if (texts.Any(t => t.tag == tag))
|
||||
{
|
||||
ThrowError($"Duplicate tag \"{tag}\".");
|
||||
duplicatesFound = true;
|
||||
}
|
||||
}
|
||||
if (duplicatesFound)
|
||||
{
|
||||
ThrowError($"Aborting, please fix duplicate tags in the file and try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (XElement element in doc1.Root.Elements())
|
||||
{
|
||||
string tag = element.Name.ToString();
|
||||
string text = element.ElementInnerText();
|
||||
if (texts.Any(t => t.text == text))
|
||||
{
|
||||
if (tag.StartsWith("sp."))
|
||||
{
|
||||
string[] split = tag.Split('.');
|
||||
if (split.Length > 3)
|
||||
{
|
||||
texts.RemoveAll(t => t.text == text);
|
||||
string newTag = $"sp.{split[2]}.{split[3]}";
|
||||
texts.Add((newTag, text));
|
||||
NewMessage($"Duplicate text \"{tag}\", merging to \"{newTag}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage($"Duplicate text \"{tag}\", using existing one \"{texts.Find(t => t.text == text).tag}\".");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.Add((tag, text));
|
||||
ThrowError($"Duplicate text \"{tag}\". Could not determine if the text can be merged with an existing one, please check it manually.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
texts.Add((tag, text));
|
||||
}
|
||||
}
|
||||
|
||||
string filePath = "uniquetexts.xml";
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, texts.Select(t => $"<{t.tag}>{t.text}</{t.tag}>"));
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
}));
|
||||
|
||||
commands.Add(new Command("comparelocafiles", "comparelocafiles [file1] [file2]", (string[] args) =>
|
||||
@@ -2585,99 +2719,6 @@ namespace Barotrauma
|
||||
}));
|
||||
#endif
|
||||
|
||||
commands.Add(new Command("cleanbuild", "", (string[] args) =>
|
||||
{
|
||||
/*GameSettings.CurrentConfig.MusicVolume = 0.5f;
|
||||
GameSettings.CurrentConfig.SoundVolume = 0.5f;
|
||||
GameSettings.CurrentConfig.DynamicRangeCompressionEnabled = true;
|
||||
GameSettings.CurrentConfig.VoipAttenuationEnabled = true;
|
||||
NewMessage("Music and sound volume set to 0.5", Color.Green);
|
||||
|
||||
GameSettings.CurrentConfig.GraphicsWidth = 0;
|
||||
GameSettings.CurrentConfig.GraphicsHeight = 0;
|
||||
GameSettings.CurrentConfig.WindowMode = WindowMode.BorderlessWindowed;
|
||||
NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green);
|
||||
NewMessage("Fullscreen enabled", Color.Green);
|
||||
|
||||
GameSettings.CurrentConfig.VerboseLogging = false;
|
||||
|
||||
if (GameSettings.CurrentConfig.MasterServerUrl != "http://www.undertowgames.com/baromaster")
|
||||
{
|
||||
ThrowError("MasterServerUrl \"" + GameSettings.CurrentConfig.MasterServerUrl + "\"!");
|
||||
}
|
||||
|
||||
GameSettings.SaveCurrentConfig();*/
|
||||
throw new NotImplementedException();
|
||||
#warning TODO: reimplement
|
||||
|
||||
var saveFiles = Barotrauma.IO.Directory.GetFiles(SaveUtil.SaveFolder);
|
||||
|
||||
foreach (string saveFile in saveFiles)
|
||||
{
|
||||
Barotrauma.IO.File.Delete(saveFile);
|
||||
NewMessage("Deleted " + saveFile, Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.Directory.Exists(Barotrauma.IO.Path.Combine(SaveUtil.SaveFolder, "temp")))
|
||||
{
|
||||
Barotrauma.IO.Directory.Delete(Barotrauma.IO.Path.Combine(SaveUtil.SaveFolder, "temp"), true);
|
||||
NewMessage("Deleted temp save folder", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.Directory.Exists(ServerLog.SavePath))
|
||||
{
|
||||
var logFiles = Barotrauma.IO.Directory.GetFiles(ServerLog.SavePath);
|
||||
|
||||
foreach (string logFile in logFiles)
|
||||
{
|
||||
Barotrauma.IO.File.Delete(logFile);
|
||||
NewMessage("Deleted " + logFile, Color.Green);
|
||||
}
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists("filelist.xml"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("filelist.xml");
|
||||
NewMessage("Deleted filelist", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists("Data/bannedplayers.txt"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("Data/bannedplayers.txt");
|
||||
NewMessage("Deleted bannedplayers.txt", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists("Submarines/TutorialSub.sub"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("Submarines/TutorialSub.sub");
|
||||
|
||||
NewMessage("Deleted TutorialSub from the submarine folder", Color.Green);
|
||||
}
|
||||
|
||||
/*if (Barotrauma.IO.File.Exists(GameServer.SettingsFile))
|
||||
{
|
||||
Barotrauma.IO.File.Delete(GameServer.SettingsFile);
|
||||
NewMessage("Deleted server settings", Color.Green);
|
||||
}
|
||||
|
||||
if (Barotrauma.IO.File.Exists(GameServer.ClientPermissionsFile))
|
||||
{
|
||||
Barotrauma.IO.File.Delete(GameServer.ClientPermissionsFile);
|
||||
NewMessage("Deleted client permission file", Color.Green);
|
||||
}*/
|
||||
|
||||
if (Barotrauma.IO.File.Exists("crashreport.log"))
|
||||
{
|
||||
Barotrauma.IO.File.Delete("crashreport.log");
|
||||
NewMessage("Deleted crashreport.log", Color.Green);
|
||||
}
|
||||
|
||||
if (!Barotrauma.IO.File.Exists("Content/Map/TutorialSub.sub"))
|
||||
{
|
||||
ThrowError("TutorialSub.sub not found!");
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("reloadcorepackage", "", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 1)
|
||||
@@ -2925,7 +2966,7 @@ namespace Barotrauma
|
||||
ThrowError($"Could not find the location type \"{args[0]}\".");
|
||||
return;
|
||||
}
|
||||
GameMain.GameSession.Campaign.Map.CurrentLocation.ChangeType(locationType);
|
||||
GameMain.GameSession.Campaign.Map.CurrentLocation.ChangeType(GameMain.GameSession.Campaign, locationType);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace EventInput
|
||||
|
||||
public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
|
||||
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
|
||||
public delegate void EditingTextHandler(object sender, TextEditingEventArgs e);
|
||||
|
||||
public static class EventInput
|
||||
{
|
||||
@@ -88,6 +89,15 @@ namespace EventInput
|
||||
/// </summary>
|
||||
public static event KeyEventHandler KeyUp;
|
||||
|
||||
|
||||
#if !WINDOWS
|
||||
/// <summary>
|
||||
/// Raised when the user is editing text and IME is in progress.
|
||||
/// Windows build uses ImeSharp instead because SDL2's IME implementation is broken on Windows (https://github.com/libsdl-org/SDL/issues/2243)
|
||||
/// </summary>
|
||||
public static event EditingTextHandler EditingText;
|
||||
#endif
|
||||
|
||||
static bool initialized;
|
||||
|
||||
/// <summary>
|
||||
@@ -102,6 +112,9 @@ namespace EventInput
|
||||
}
|
||||
|
||||
window.TextInput += ReceiveInput;
|
||||
#if !WINDOWS
|
||||
window.TextEditing += ReceiveTextEditing;
|
||||
#endif
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
@@ -112,6 +125,13 @@ namespace EventInput
|
||||
KeyDown?.Invoke(sender, new KeyEventArgs(e.Key));
|
||||
}
|
||||
|
||||
#if !WINDOWS
|
||||
private static void ReceiveTextEditing(object sender, TextEditingEventArgs e)
|
||||
{
|
||||
EditingText?.Invoke(sender, e);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void OnCharEntered(char character)
|
||||
{
|
||||
CharEntered?.Invoke(null, new CharacterEventArgs(character, 0));
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
#if WINDOWS
|
||||
using System.Windows;
|
||||
#endif
|
||||
using Barotrauma;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
@@ -15,6 +11,11 @@ namespace EventInput
|
||||
void ReceiveCommandInput(char command);
|
||||
void ReceiveSpecialInput(Keys key);
|
||||
|
||||
#if !WINDOWS
|
||||
/// Windows build uses ImeSharp instead because SDL2's IME implementation is broken on Windows (https://github.com/libsdl-org/SDL/issues/2243)
|
||||
void ReceiveEditingInput(string text, int start);
|
||||
#endif
|
||||
|
||||
bool Selected { get; set; } //or Focused
|
||||
}
|
||||
|
||||
@@ -25,14 +26,26 @@ namespace EventInput
|
||||
EventInput.Initialize(window);
|
||||
EventInput.CharEntered += EventInput_CharEntered;
|
||||
EventInput.KeyDown += EventInput_KeyDown;
|
||||
}
|
||||
#if !WINDOWS
|
||||
EventInput.EditingText += EventInput_TextEditing;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SDL by default starts in a state where it accepts IME inputs
|
||||
* this is bad because this blocks keybinds since the IME thinks
|
||||
* it's typing in a text box and not forwarding keybinds to the game.
|
||||
*/
|
||||
TextInput.StopTextInput();
|
||||
}
|
||||
#if !WINDOWS
|
||||
public void EventInput_TextEditing(object sender, TextEditingEventArgs e)
|
||||
{
|
||||
_subscriber?.ReceiveEditingInput(e.Text, e.Start);
|
||||
}
|
||||
#endif
|
||||
public void EventInput_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (_subscriber == null)
|
||||
return;
|
||||
|
||||
_subscriber.ReceiveSpecialInput(e.KeyCode);
|
||||
_subscriber?.ReceiveSpecialInput(e.KeyCode);
|
||||
}
|
||||
|
||||
void EventInput_CharEntered(object sender, CharacterEventArgs e)
|
||||
@@ -74,12 +87,25 @@ namespace EventInput
|
||||
get { return _subscriber; }
|
||||
set
|
||||
{
|
||||
if (_subscriber == value) return;
|
||||
if (_subscriber != null)
|
||||
if (_subscriber == value) { return; }
|
||||
|
||||
if (_subscriber is GUITextBox)
|
||||
{
|
||||
TextInput.StopTextInput();
|
||||
_subscriber.Selected = false;
|
||||
}
|
||||
|
||||
if (value is GUITextBox box)
|
||||
{
|
||||
TextInput.SetTextInputRect(box.Rect);
|
||||
TextInput.StartTextInput();
|
||||
}
|
||||
|
||||
_subscriber = value;
|
||||
if (value != null)
|
||||
{
|
||||
value.Selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using Barotrauma.Tutorials;
|
||||
using Segment = Barotrauma.ObjectiveManager.Segment;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class CheckObjectiveAction : BinaryOptionAction
|
||||
{
|
||||
public enum CheckType
|
||||
{
|
||||
Added,
|
||||
Completed
|
||||
}
|
||||
|
||||
[Serialize(CheckType.Completed, IsPropertySaveable.Yes)]
|
||||
public CheckType Type { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier Identifier { get; set; }
|
||||
|
||||
partial void DetermineSuccessProjSpecific(ref bool success)
|
||||
{
|
||||
success = false;
|
||||
if (Identifier.IsEmpty)
|
||||
{
|
||||
success = ObjectiveManager.AllActiveObjectivesCompleted();
|
||||
}
|
||||
else if (ObjectiveManager.GetObjective(Identifier) is Segment segment)
|
||||
{
|
||||
success = Type switch
|
||||
{
|
||||
CheckType.Added => true,
|
||||
CheckType.Completed => segment.IsCompleted,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,7 +323,10 @@ namespace Barotrauma
|
||||
AlwaysOverrideCursor = true
|
||||
};
|
||||
|
||||
LocalizedString translatedText = TextManager.ParseInputTypes(TextManager.Get(text)).Fallback(text);
|
||||
LocalizedString translatedText = speaker?.DisplayName is not null ?
|
||||
TextManager.GetWithVariable(text, "[speakername]", speaker?.DisplayName) :
|
||||
TextManager.Get(text);
|
||||
translatedText = TextManager.ParseInputTypes(translatedText).Fallback(text);
|
||||
|
||||
if (speaker?.Info != null && drawChathead)
|
||||
{
|
||||
|
||||
@@ -11,11 +11,13 @@ partial class MessageBoxAction : EventAction
|
||||
if (Type == ActionType.Create || Type == ActionType.ConnectObjective)
|
||||
{
|
||||
CreateMessageBox();
|
||||
if (!ObjectiveTag.IsEmpty && GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
if (!ObjectiveTag.IsEmpty)
|
||||
{
|
||||
Identifier id = Identifier.IfEmpty(Text);
|
||||
var segment = Tutorial.Segment.CreateMessageBoxSegment(id, ObjectiveTag, CreateMessageBox);
|
||||
tutorialMode.Tutorial?.TriggerTutorialSegment(segment, connectObjective: Type == ActionType.ConnectObjective);
|
||||
var segment = ObjectiveManager.Segment.CreateMessageBoxSegment(id, ObjectiveTag, CreateMessageBox);
|
||||
segment.CanBeCompleted = ObjectiveCanBeCompleted;
|
||||
segment.ParentId = ParentObjectiveId;
|
||||
ObjectiveManager.TriggerTutorialSegment(segment, connectObjective: Type == ActionType.ConnectObjective);
|
||||
}
|
||||
}
|
||||
else if (Type == ActionType.Close)
|
||||
|
||||
@@ -1,50 +1,43 @@
|
||||
using Barotrauma.Tutorials;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
partial class TutorialSegmentAction : EventAction
|
||||
{
|
||||
private Tutorial.Segment segment;
|
||||
private ObjectiveManager.Segment segment;
|
||||
|
||||
partial void UpdateProjSpecific()
|
||||
{
|
||||
// Only need to create the segment when it's being triggered (otherwise the tutorial already has the segment instance)
|
||||
if (Type == SegmentActionType.Trigger)
|
||||
{
|
||||
segment = Tutorial.Segment.CreateInfoBoxSegment(Identifier, ObjectiveTag, AutoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No,
|
||||
new Tutorial.Segment.Text(TextTag, Width, Height, Anchor.Center),
|
||||
new Tutorial.Segment.Video(VideoFile, TextTag, Width, Height));
|
||||
segment = ObjectiveManager.Segment.CreateInfoBoxSegment(Identifier, ObjectiveTag, AutoPlayVideo ? Tutorials.AutoPlayVideo.Yes : Tutorials.AutoPlayVideo.No,
|
||||
new ObjectiveManager.Segment.Text(TextTag, Width, Height, Anchor.Center),
|
||||
new ObjectiveManager.Segment.Video(VideoFile, TextTag, Width, Height));
|
||||
}
|
||||
else if (Type == SegmentActionType.Add)
|
||||
{
|
||||
segment = Tutorial.Segment.CreateObjectiveSegment(Identifier, !ObjectiveTag.IsEmpty ? ObjectiveTag : Identifier);
|
||||
segment = ObjectiveManager.Segment.CreateObjectiveSegment(Identifier, !ObjectiveTag.IsEmpty ? ObjectiveTag : Identifier);
|
||||
}
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
if (segment is not null)
|
||||
{
|
||||
if (tutorialMode.Tutorial is Tutorial tutorial)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case SegmentActionType.Trigger:
|
||||
case SegmentActionType.Add:
|
||||
tutorial.TriggerTutorialSegment(segment);
|
||||
break;
|
||||
case SegmentActionType.Complete:
|
||||
tutorial.CompleteTutorialSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.Remove:
|
||||
tutorial.RemoveTutorialSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.CompleteAndRemove:
|
||||
tutorial.CompleteTutorialSegment(Identifier);
|
||||
tutorial.RemoveTutorialSegment(Identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
segment.CanBeCompleted = CanBeCompleted;
|
||||
segment.ParentId = ParentObjectiveId;
|
||||
}
|
||||
else
|
||||
switch (Type)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\": attempting to use TutorialSegmentAction during a non-Tutorial game mode!");
|
||||
case SegmentActionType.Trigger:
|
||||
case SegmentActionType.Add:
|
||||
ObjectiveManager.TriggerTutorialSegment(segment);
|
||||
break;
|
||||
case SegmentActionType.Complete:
|
||||
ObjectiveManager.CompleteTutorialSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.Remove:
|
||||
ObjectiveManager.RemoveTutorialSegment(Identifier);
|
||||
break;
|
||||
case SegmentActionType.CompleteAndRemove:
|
||||
ObjectiveManager.CompleteTutorialSegment(Identifier);
|
||||
ObjectiveManager.RemoveTutorialSegment(Identifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -651,6 +651,7 @@ namespace Barotrauma
|
||||
break;
|
||||
case NetworkEventType.MISSION:
|
||||
Identifier missionIdentifier = msg.ReadIdentifier();
|
||||
int locationIndex = msg.ReadInt32();
|
||||
string missionName = msg.ReadString();
|
||||
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
|
||||
if (prefab != null)
|
||||
@@ -660,6 +661,10 @@ namespace Barotrauma
|
||||
{
|
||||
IconColor = prefab.IconColor
|
||||
};
|
||||
if (GameMain.GameSession?.Map is { } map && locationIndex > 0 && locationIndex < map.Locations.Count)
|
||||
{
|
||||
map.Discover(map.Locations[locationIndex], checkTalents: false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetworkEventType.UNLOCKPATH:
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
public override int State
|
||||
{
|
||||
get { return base.State; }
|
||||
protected set
|
||||
set
|
||||
{
|
||||
if (state != value)
|
||||
{
|
||||
@@ -45,7 +45,10 @@ namespace Barotrauma
|
||||
{
|
||||
requireRescue.Add(character);
|
||||
#if CLIENT
|
||||
GameMain.GameSession.CrewManager.AddCharacterToCrewList(character);
|
||||
if (allowOrderingRescuees)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.AddCharacterToCrewList(character);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class EndMission : Mission
|
||||
{
|
||||
public override bool DisplayAsCompleted => false;
|
||||
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
partial void OnStateChangedProjSpecific()
|
||||
{
|
||||
if (Phase == MissionPhase.NoItemsDestroyed)
|
||||
{
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (boss != null && !boss.Removed)
|
||||
{
|
||||
new CameraTransition(boss, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: 8, fadeOut: false, startZoom: 1.0f, endZoom: 0.3f * GUI.yScale)
|
||||
{
|
||||
EndWaitDuration = 3.0f
|
||||
};
|
||||
}
|
||||
}, delay: 3.0f);
|
||||
}
|
||||
else if (Phase == MissionPhase.AllItemsDestroyed)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(wakeUpCoroutine(), name: "EndMission.wakeUpCoroutine");
|
||||
}
|
||||
else if (Phase == MissionPhase.BossKilled)
|
||||
{
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
new CameraTransition(boss, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: 3, fadeOut: false, endZoom: 0.1f * GUI.yScale)
|
||||
{
|
||||
EndWaitDuration = float.PositiveInfinity
|
||||
};
|
||||
}, delay: 3.0f);
|
||||
}
|
||||
|
||||
IEnumerable<CoroutineStatus> wakeUpCoroutine()
|
||||
{
|
||||
yield return new WaitForSeconds(wakeUpCinematicDelay);
|
||||
if (boss != null && !boss.Removed)
|
||||
{
|
||||
new CameraTransition(boss, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: 5.0f, fadeOut: false, losFadeIn: false, startZoom: 1.0f, endZoom: 0.4f * GUI.yScale)
|
||||
{
|
||||
EndWaitDuration = cameraWaitDuration
|
||||
};
|
||||
}
|
||||
yield return new WaitForSeconds(bossWakeUpDelay);
|
||||
if (boss != null && !boss.Removed)
|
||||
{
|
||||
foreach (var limb in boss.AnimController.Limbs)
|
||||
{
|
||||
if (!limb.FreezeBlinkState) { continue; }
|
||||
limb.FreezeBlinkState = false;
|
||||
if (limb.LightSource is Lights.LightSource light)
|
||||
{
|
||||
light.Enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific()
|
||||
{
|
||||
if (Phase is MissionPhase.Initial or MissionPhase.NoItemsDestroyed or MissionPhase.SomeItemsDestroyed)
|
||||
{
|
||||
// Put asleep.
|
||||
// Have to set the light every frame (or at least periodically), because light.Enabled is changed when Character.IsVisible changes (off/on screen). See GameScreen.Draw().
|
||||
foreach (var limb in boss.AnimController.Limbs)
|
||||
{
|
||||
if (limb.Params.BlinkFrequency > 0)
|
||||
{
|
||||
limb.FreezeBlinkState = true;
|
||||
limb.BlinkPhase = -limb.Params.BlinkHoldTime;
|
||||
if (limb.LightSource is Lights.LightSource light)
|
||||
{
|
||||
light.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.O))
|
||||
{
|
||||
State = 0;
|
||||
}
|
||||
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Y))
|
||||
{
|
||||
destructibleItems.ForEach(it => it.Condition = 0.0f);
|
||||
}
|
||||
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.U))
|
||||
{
|
||||
boss?.SetAllDamage(20000.0f, 0.0f, 0.0f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
|
||||
boss = Character.ReadSpawnData(msg);
|
||||
|
||||
byte minionCount = msg.ReadByte();
|
||||
List<Character> minionList = new List<Character>();
|
||||
for (int i = 0; i < minionCount; i++)
|
||||
{
|
||||
var minion = Character.ReadSpawnData(msg);
|
||||
if (minion == null)
|
||||
{
|
||||
throw new System.Exception($"Error in EndMission.ClientReadInitial: failed to create a minion (mission: {Prefab.Identifier}, index: {i})");
|
||||
}
|
||||
minionList.Add(minion);
|
||||
}
|
||||
minions = minionList.ToImmutableArray();
|
||||
if (minions.Length != minionCount)
|
||||
{
|
||||
throw new System.Exception("Error in EndMission.ClientReadInitial: minion count does not match the server count (" + minionCount + " != " + minions.Length + "mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
partial class GoToMission : Mission
|
||||
{
|
||||
public override bool DisplayAsCompleted => false;
|
||||
public override bool DisplayAsCompleted => State >= Prefab.MaxProgressState;
|
||||
public override bool DisplayAsFailed => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,33 +43,41 @@ namespace Barotrauma
|
||||
List<LocalizedString> reputationRewardTexts = new List<LocalizedString>();
|
||||
foreach (var reputationReward in ReputationRewards)
|
||||
{
|
||||
LocalizedString name = "";
|
||||
|
||||
if (reputationReward.Key == "location")
|
||||
FactionPrefab targetFaction;
|
||||
if (reputationReward.Key == "location" )
|
||||
{
|
||||
name = $"‖color:gui.orange‖{currLocation.Name}‖end‖";
|
||||
targetFaction = currLocation.Faction?.Prefab;
|
||||
}
|
||||
else
|
||||
{
|
||||
var faction = FactionPrefab.Prefabs.Find(f => f.Identifier == reputationReward.Key);
|
||||
if (faction != null)
|
||||
{
|
||||
name = $"‖color:{XMLExtensions.ColorToString(faction.IconColor)}‖{faction.Name}‖end‖";
|
||||
}
|
||||
else
|
||||
{
|
||||
name = TextManager.Get(reputationReward.Key);
|
||||
}
|
||||
FactionPrefab.Prefabs.TryGet(reputationReward.Key, out targetFaction);
|
||||
}
|
||||
|
||||
LocalizedString name;
|
||||
if (targetFaction != null)
|
||||
{
|
||||
name = $"‖color:{XMLExtensions.ToStringHex(targetFaction.IconColor)}‖{targetFaction.Name}‖end‖";
|
||||
}
|
||||
else
|
||||
{
|
||||
name = TextManager.Get(reputationReward.Key);
|
||||
}
|
||||
float normalizedValue = MathUtils.InverseLerp(-100.0f, 100.0f, reputationReward.Value);
|
||||
string formattedValue = ((int)reputationReward.Value).ToString("+#;-#;0"); //force plus sign for positive numbers
|
||||
LocalizedString rewardText = TextManager.GetWithVariables(
|
||||
"reputationformat",
|
||||
("[reputationname]", name),
|
||||
("[reputationvalue]", $"‖color:{XMLExtensions.ColorToString(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" ));
|
||||
("[reputationvalue]", $"‖color:{XMLExtensions.ToStringHex(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" ));
|
||||
reputationRewardTexts.Add(rewardText.Value);
|
||||
}
|
||||
return RichString.Rich(TextManager.AddPunctuation(':', TextManager.Get("reputation"), LocalizedString.Join(", ", reputationRewardTexts)));
|
||||
if (reputationRewardTexts.Any())
|
||||
{
|
||||
return RichString.Rich(TextManager.AddPunctuation(':', TextManager.Get("reputation"), LocalizedString.Join(", ", reputationRewardTexts)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
partial void ShowMessageProjSpecific(int missionState)
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Mission mission in missions)
|
||||
{
|
||||
if (!mission.Prefab.ShowStartMessage) { continue; }
|
||||
new GUIMessageBox(RichString.Rich(mission.Name), RichString.Rich(mission.Description), Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
|
||||
{
|
||||
IconColor = mission.Prefab.IconColor,
|
||||
|
||||
@@ -13,11 +13,12 @@ namespace Barotrauma
|
||||
byte monsterCount = msg.ReadByte();
|
||||
for (int i = 0; i < monsterCount; i++)
|
||||
{
|
||||
monsters.Add(Character.ReadSpawnData(msg));
|
||||
}
|
||||
if (monsters.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in MonsterMission.ClientReadInitial: monster list contains null (mission: " + Prefab.Identifier + ")");
|
||||
var monster = Character.ReadSpawnData(msg);
|
||||
if (monster == null)
|
||||
{
|
||||
throw new System.Exception($"Error in MonsterMission.ClientReadInitial: failed to create a monster (mission: {Prefab.Identifier}, index: {i})");
|
||||
}
|
||||
monsters.Add(monster);
|
||||
}
|
||||
if (monsters.Count != monsterCount)
|
||||
{
|
||||
|
||||
@@ -11,38 +11,59 @@ namespace Barotrauma
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
bool usedExistingItem = msg.ReadBoolean();
|
||||
if (usedExistingItem)
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
ushort id = msg.ReadUInt16();
|
||||
item = Entity.FindEntityByID(id) as Item;
|
||||
if (item == null)
|
||||
bool targetFound = msg.ReadBoolean();
|
||||
if (!targetFound) { continue; }
|
||||
|
||||
bool usedExistingItem = msg.ReadBoolean();
|
||||
if (usedExistingItem)
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: failed to find item " + id + " (mission: " + Prefab.Identifier + ")");
|
||||
ushort id = msg.ReadUInt16();
|
||||
target.Item = Entity.FindEntityByID(id) as Item;
|
||||
if (target.Item == null)
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: failed to find item " + id + " (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target.Item = Item.ReadSpawnData(msg);
|
||||
if (target.Item == null)
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: spawned item was null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
}
|
||||
|
||||
int executedEffectCount = msg.ReadByte();
|
||||
for (int i = 0; i < executedEffectCount; i++)
|
||||
{
|
||||
int listIndex = msg.ReadByte();
|
||||
int effectIndex = msg.ReadByte();
|
||||
var selectedEffect = target.StatusEffects[listIndex][effectIndex];
|
||||
target.Item.ApplyStatusEffect(selectedEffect, selectedEffect.type, deltaTime: 1.0f, worldPosition: target.Item.Position);
|
||||
}
|
||||
|
||||
if (target.Item.body != null)
|
||||
{
|
||||
target.Item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
public override void ClientRead(IReadMessage msg)
|
||||
{
|
||||
base.ClientRead(msg);
|
||||
int targetCount = msg.ReadByte();
|
||||
for (int i = 0; i < targetCount; i++)
|
||||
{
|
||||
item = Item.ReadSpawnData(msg);
|
||||
if (item == null)
|
||||
var state = (Target.RetrievalState)msg.ReadByte();
|
||||
if (i < targets.Count)
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: spawned item was null (mission: " + Prefab.Identifier + ")");
|
||||
targets[i].State = state;
|
||||
}
|
||||
}
|
||||
|
||||
int executedEffectCount = msg.ReadByte();
|
||||
for (int i = 0; i < executedEffectCount; i++)
|
||||
{
|
||||
int index1 = msg.ReadByte();
|
||||
int index2 = msg.ReadByte();
|
||||
var selectedEffect = statusEffects[index1][index2];
|
||||
item.ApplyStatusEffect(selectedEffect, selectedEffect.type, deltaTime: 1.0f, worldPosition: item.Position);
|
||||
}
|
||||
|
||||
if (item.body != null)
|
||||
{
|
||||
item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,7 +769,7 @@ namespace Barotrauma
|
||||
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
|
||||
{
|
||||
radio.Channel = channel;
|
||||
GameMain.Client?.CreateEntityEvent(radio.Item, new Item.ChangePropertyEventData(radio.SerializableProperties["channel".ToIdentifier()]));
|
||||
GameMain.Client?.CreateEntityEvent(radio.Item, new Item.ChangePropertyEventData(radio.SerializableProperties["channel".ToIdentifier()], radio));
|
||||
|
||||
if (setText)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement;
|
||||
|
||||
@@ -29,6 +28,8 @@ namespace Barotrauma
|
||||
|
||||
private Point resolutionWhenCreated;
|
||||
|
||||
private bool needsHireableRefresh;
|
||||
|
||||
private enum SortingMethod
|
||||
{
|
||||
AlphabeticalAsc,
|
||||
@@ -50,6 +51,8 @@ namespace Barotrauma
|
||||
campaignUI.Campaign.Map.OnLocationChanged.RegisterOverwriteExisting(
|
||||
"CrewManagement.UpdateLocationView".ToIdentifier(),
|
||||
(locationChangeInfo) => UpdateLocationView(locationChangeInfo.NewLocation, true, locationChangeInfo.PrevLocation));
|
||||
Reputation.OnAnyReputationValueChanged.RegisterOverwriteExisting(
|
||||
"CrewManagement.UpdateLocationView".ToIdentifier(), _ => needsHireableRefresh = true);
|
||||
}
|
||||
|
||||
public void RefreshPermissions()
|
||||
@@ -68,7 +71,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (child.FindChild(c => c is GUIButton && c.UserData is CharacterInfo, true) is GUIButton buyButton)
|
||||
{
|
||||
buyButton.Enabled = HasPermission;
|
||||
CharacterInfo characterInfo = buyButton.UserData as CharacterInfo;
|
||||
bool enoughReputationToHire = EnoughReputationToHire(characterInfo);
|
||||
buyButton.Enabled = HasPermission && enoughReputationToHire;
|
||||
foreach (GUITextBlock text in child.GetAllChildren<GUITextBlock>())
|
||||
{
|
||||
text.TextColor = new Color(text.TextColor, buyButton.Enabled ? 1.0f : 0.6f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,18 +303,21 @@ namespace Barotrauma
|
||||
if (sortingMethod == SortingMethod.AlphabeticalAsc)
|
||||
{
|
||||
list.Content.RectTransform.SortChildren((x, y) =>
|
||||
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
||||
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Name.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Name));
|
||||
}
|
||||
else if (sortingMethod == SortingMethod.JobAsc)
|
||||
{
|
||||
SortCharacters(list, SortingMethod.AlphabeticalAsc);
|
||||
list.Content.RectTransform.SortChildren((x, y) =>
|
||||
String.Compare(((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Job.Name.Value, ((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Job.Name.Value, StringComparison.Ordinal));
|
||||
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
||||
string.Compare(((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Job.Name.Value, ((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Job.Name.Value, StringComparison.Ordinal));
|
||||
}
|
||||
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
|
||||
{
|
||||
SortCharacters(list, SortingMethod.AlphabeticalAsc);
|
||||
list.Content.RectTransform.SortChildren((x, y) =>
|
||||
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
||||
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Salary.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Salary));
|
||||
if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); }
|
||||
}
|
||||
@@ -313,9 +325,26 @@ namespace Barotrauma
|
||||
{
|
||||
SortCharacters(list, SortingMethod.AlphabeticalAsc);
|
||||
list.Content.RectTransform.SortChildren((x, y) =>
|
||||
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
|
||||
((InfoSkill)x.GUIComponent.UserData).SkillLevel.CompareTo(((InfoSkill)y.GUIComponent.UserData).SkillLevel));
|
||||
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); }
|
||||
}
|
||||
|
||||
int? CompareReputationRequirement(GUIComponent c1, GUIComponent c2)
|
||||
{
|
||||
CharacterInfo info1 = ((InfoSkill)c1.UserData).CharacterInfo;
|
||||
CharacterInfo info2 = ((InfoSkill)c2.UserData).CharacterInfo;
|
||||
float requirement1 = EnoughReputationToHire(info1) ? 0 : info1.MinReputationToHire.reputation;
|
||||
float requirement2 = EnoughReputationToHire(info2) ? 0 : info2.MinReputationToHire.reputation;
|
||||
if (MathUtils.NearlyEqual(requirement1, 0.0f) && MathUtils.NearlyEqual(requirement2, 0.0f))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return requirement1.CompareTo(requirement2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct InfoSkill
|
||||
@@ -367,12 +396,25 @@ namespace Barotrauma
|
||||
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
|
||||
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
|
||||
characterInfo.Job.Name, textColor: Color.White, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft)
|
||||
characterInfo.Title ?? characterInfo.Job.Name, textColor: Color.White, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
jobBlock.Text = ToolBox.LimitString(jobBlock.Text, jobBlock.Font, jobBlock.Rect.Width);
|
||||
|
||||
if (!characterInfo.MinReputationToHire.factionId.IsEmpty)
|
||||
{
|
||||
var faction = campaign.Factions.Find(f => f.Prefab.Identifier == characterInfo.MinReputationToHire.factionId);
|
||||
if (faction != null)
|
||||
{
|
||||
jobBlock.TextColor = faction.Prefab.IconColor;
|
||||
}
|
||||
}
|
||||
var fullJobText = jobBlock.Text;
|
||||
jobBlock.Text = ToolBox.LimitString(fullJobText, jobBlock.Font, jobBlock.Rect.Width);
|
||||
if (jobBlock.Text != fullJobText)
|
||||
{
|
||||
jobBlock.ToolTip = fullJobText;
|
||||
jobBlock.CanBeFocused = true;
|
||||
}
|
||||
float width = 0.6f / 3;
|
||||
if (characterInfo.Job != null && skill != null)
|
||||
{
|
||||
@@ -410,7 +452,7 @@ namespace Barotrauma
|
||||
{
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = HasPermission,
|
||||
Enabled = CanHire(characterInfo),
|
||||
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
|
||||
};
|
||||
hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
|
||||
@@ -426,10 +468,9 @@ namespace Barotrauma
|
||||
else if (!btn.Enabled)
|
||||
{
|
||||
btn.ToolTip = string.Empty;
|
||||
btn.Enabled = HasPermission;
|
||||
btn.Enabled = CanHire(characterInfo);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
else if (listBox == pendingList)
|
||||
{
|
||||
@@ -437,7 +478,7 @@ namespace Barotrauma
|
||||
{
|
||||
ClickSound = GUISoundType.Cart,
|
||||
UserData = characterInfo,
|
||||
Enabled = HasPermission,
|
||||
Enabled = CanHire(characterInfo),
|
||||
OnClicked = (b, o) => RemovePendingHire(o as CharacterInfo)
|
||||
};
|
||||
}
|
||||
@@ -474,12 +515,30 @@ namespace Barotrauma
|
||||
size = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width, mainGroup.Rect.Height);
|
||||
new GUIButton(new RectTransform(size, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null)
|
||||
{
|
||||
Enabled = HasPermission,
|
||||
Enabled = CanHire(characterInfo),
|
||||
ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", TextManager.Get($"input.{(PlayerInput.MouseButtonsSwapped() ? "rightmouse" : "leftmouse")}")),
|
||||
UserData = characterInfo,
|
||||
OnClicked = CreateRenamingComponent
|
||||
};
|
||||
}
|
||||
|
||||
bool CanHire(CharacterInfo characterInfo)
|
||||
{
|
||||
if (!HasPermission) { return false; }
|
||||
return EnoughReputationToHire(characterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnoughReputationToHire(CharacterInfo characterInfo)
|
||||
{
|
||||
if (characterInfo.MinReputationToHire.factionId != Identifier.Empty)
|
||||
{
|
||||
if (campaign.GetReputation(characterInfo.MinReputationToHire.factionId) < characterInfo.MinReputationToHire.reputation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
|
||||
@@ -488,13 +547,13 @@ namespace Barotrauma
|
||||
Point absoluteOffset = new Point(
|
||||
pivot == Pivot.TopLeft ? listBox.Parent.Parent.Rect.Right + 5 : listBox.Parent.Parent.Rect.Left - 5,
|
||||
characterFrame.Rect.Top);
|
||||
int frameSize = (int)(GUI.Scale * 300);
|
||||
if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize) < 0)
|
||||
Point frameSize = new Point(GUI.IntScale(300), GUI.IntScale(350));
|
||||
if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize.Y) < 0)
|
||||
{
|
||||
pivot = listBox == hireableList ? Pivot.BottomLeft : Pivot.BottomRight;
|
||||
absoluteOffset.Y = characterFrame.Rect.Bottom;
|
||||
}
|
||||
characterPreviewFrame = new GUIFrame(new RectTransform(new Point(frameSize), parent: campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Parent.RectTransform, pivot: pivot)
|
||||
characterPreviewFrame = new GUIFrame(new RectTransform(frameSize, parent: campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Parent.RectTransform, pivot: pivot)
|
||||
{
|
||||
AbsoluteOffset = absoluteOffset
|
||||
}, style: "InnerFrame")
|
||||
@@ -503,7 +562,8 @@ namespace Barotrauma
|
||||
};
|
||||
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), characterPreviewFrame.RectTransform, anchor: Anchor.Center))
|
||||
{
|
||||
RelativeSpacing = 0.01f
|
||||
RelativeSpacing = 0.01f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
// Character info
|
||||
@@ -545,9 +605,23 @@ namespace Barotrauma
|
||||
blockHeight = 1.0f / characterSkills.Count();
|
||||
foreach (Skill skill in characterSkills)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillNameGroup.RectTransform), TextManager.Get("SkillName." + skill.Identifier));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillNameGroup.RectTransform), TextManager.Get("SkillName." + skill.Identifier), font: GUIStyle.SmallFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillLevelGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.Right);
|
||||
}
|
||||
|
||||
if (characterInfo.MinReputationToHire.reputation > 0.0f)
|
||||
{
|
||||
var repStr = TextManager.GetWithVariables(
|
||||
"campaignstore.reputationrequired",
|
||||
("[amount]", ((int)characterInfo.MinReputationToHire.reputation).ToString()),
|
||||
("[faction]", TextManager.Get("faction." + characterInfo.MinReputationToHire.factionId).Value));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), mainGroup.RectTransform),
|
||||
repStr, textColor: !EnoughReputationToHire(characterInfo) ? GUIStyle.Orange : GUIStyle.Green,
|
||||
font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.Center);
|
||||
}
|
||||
mainGroup.Recalculate();
|
||||
characterPreviewFrame.RectTransform.MinSize =
|
||||
new Point(0, (int)(mainGroup.Children.Sum(c => c.Rect.Height + mainGroup.Rect.Height * mainGroup.RelativeSpacing) / mainGroup.RectTransform.RelativeSize.Y));
|
||||
}
|
||||
|
||||
private bool SelectCharacter(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
|
||||
@@ -636,7 +710,7 @@ namespace Barotrauma
|
||||
List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
|
||||
hires.ForEach(hireInfo =>
|
||||
{
|
||||
if(campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
||||
if (campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName()))
|
||||
{
|
||||
nonDuplicateHires.Add(hireInfo);
|
||||
}
|
||||
@@ -791,6 +865,16 @@ namespace Barotrauma
|
||||
playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement);
|
||||
}
|
||||
|
||||
if (needsHireableRefresh)
|
||||
{
|
||||
RefreshCrewFrames(hireableList);
|
||||
if (sortingDropDown?.SelectedItemData != null)
|
||||
{
|
||||
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
|
||||
}
|
||||
needsHireableRefresh = false;
|
||||
}
|
||||
|
||||
(GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn);
|
||||
if (highlightedFrame != null && highlightedInfo != null)
|
||||
{
|
||||
|
||||
@@ -357,6 +357,17 @@ namespace Barotrauma
|
||||
string txt = directory;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
if (!txt.EndsWith("/")) { txt += "/"; }
|
||||
//get directory info
|
||||
DirectoryInfo dirInfo = new DirectoryInfo(directory);
|
||||
try
|
||||
{
|
||||
//this will throw an exception if the directory can't be opened
|
||||
Directory.GetDirectories(directory);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
|
||||
{
|
||||
UserData = ItemIsDirectory.Yes
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.CharacterEditor;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
@@ -50,6 +49,14 @@ namespace Barotrauma
|
||||
|
||||
static class GUI
|
||||
{
|
||||
// Controls where a line is drawn for given coords.
|
||||
public enum OutlinePosition
|
||||
{
|
||||
Default = 0, // Thickness is inside of top left and outside of bottom right coord
|
||||
Inside = 1, // Thickness is subtracted from the inside
|
||||
Centered = 2, // Thickness is centered on given coords
|
||||
Outside = 3, // Tickness is added to the outside
|
||||
}
|
||||
public static GUICanvas Canvas => GUICanvas.Instance;
|
||||
public static CursorState MouseCursor = CursorState.Default;
|
||||
|
||||
@@ -133,13 +140,20 @@ namespace Barotrauma
|
||||
public static Texture2D WhiteTexture => solidWhiteTexture;
|
||||
private static GUICursor MouseCursorSprites => GUIStyle.CursorSprite;
|
||||
|
||||
private static bool debugDrawSounds, debugDrawEvents, debugDrawMetadata;
|
||||
private static int debugDrawMetadataOffset;
|
||||
private static readonly string[] ignoredMetadataInfo = { string.Empty, string.Empty, string.Empty, string.Empty };
|
||||
private static bool debugDrawSounds, debugDrawEvents;
|
||||
|
||||
private static DebugDrawMetaData debugDrawMetaData;
|
||||
|
||||
public struct DebugDrawMetaData
|
||||
{
|
||||
public bool Enabled;
|
||||
public bool FactionMetadata, UpgradeLevels, UpgradePrices;
|
||||
public int Offset;
|
||||
}
|
||||
|
||||
public static GraphicsDevice GraphicsDevice => GameMain.Instance.GraphicsDevice;
|
||||
|
||||
private static List<GUIMessage> messages = new List<GUIMessage>();
|
||||
private static readonly List<GUIMessage> messages = new List<GUIMessage>();
|
||||
|
||||
public static GUIFrame PauseMenu { get; private set; }
|
||||
public static GUIFrame SettingsMenuContainer { get; private set; }
|
||||
@@ -188,8 +202,9 @@ namespace Barotrauma
|
||||
SettingsMenuOpen ||
|
||||
DebugConsole.IsOpen ||
|
||||
GameSession.IsTabMenuOpen ||
|
||||
(GameMain.GameSession?.GameMode?.Paused ?? false) ||
|
||||
CharacterHUD.IsCampaignInterfaceOpen;
|
||||
GameMain.GameSession?.GameMode is { Paused: true } ||
|
||||
CharacterHUD.IsCampaignInterfaceOpen ||
|
||||
GameMain.GameSession?.Campaign is { SlideshowPlayer: { Finished: false, Visible: true } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +263,17 @@ namespace Barotrauma
|
||||
ScreenChanged = false;
|
||||
}
|
||||
|
||||
updateList.ForEach(c => c.DrawAuto(spriteBatch));
|
||||
foreach (GUIComponent c in updateList)
|
||||
{
|
||||
c.DrawAuto(spriteBatch);
|
||||
}
|
||||
|
||||
// always draw IME preview on top of everything else
|
||||
foreach (GUIComponent c in updateList)
|
||||
{
|
||||
if (c is not GUITextBox box) { continue; }
|
||||
box.DrawIMEPreview(spriteBatch);
|
||||
}
|
||||
|
||||
if (ScreenOverlayColor.A > 0.0f)
|
||||
{
|
||||
@@ -516,18 +541,17 @@ namespace Barotrauma
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
|
||||
{
|
||||
// TODO: TEST THIS
|
||||
if (debugDrawMetadata)
|
||||
if (debugDrawMetaData.Enabled)
|
||||
{
|
||||
string text = "Ctrl+M to hide campaign metadata debug info\n\n" +
|
||||
$"Ctrl+1 to {(string.IsNullOrWhiteSpace(ignoredMetadataInfo[0]) ? "hide" : "show")} outpost reputations, \n" +
|
||||
$"Ctrl+2 to {(string.IsNullOrWhiteSpace(ignoredMetadataInfo[1]) ? "hide" : "show")} faction reputations, \n" +
|
||||
$"Ctrl+3 to {(string.IsNullOrWhiteSpace(ignoredMetadataInfo[2]) ? "hide" : "show")} upgrade levels, \n" +
|
||||
$"Ctrl+4 to {(string.IsNullOrWhiteSpace(ignoredMetadataInfo[3]) ? "hide" : "show")} upgrade prices";
|
||||
$"Ctrl+1 to {(debugDrawMetaData.FactionMetadata ? "hide" : "show")} faction reputations, \n" +
|
||||
$"Ctrl+2 to {(debugDrawMetaData.UpgradeLevels ? "hide" : "show")} upgrade levels, \n" +
|
||||
$"Ctrl+3 to {(debugDrawMetaData.UpgradePrices ? "hide" : "show")} upgrade prices";
|
||||
Vector2 textSize = GUIStyle.SmallFont.MeasureString(text);
|
||||
Vector2 pos = new Vector2(GameMain.GraphicsWidth - (textSize.X + 10), 300);
|
||||
DrawString(spriteBatch, pos, text, Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
|
||||
pos.Y += textSize.Y + 8;
|
||||
campaignMode.CampaignMetadata?.DebugDraw(spriteBatch, pos, debugDrawMetadataOffset, ignoredMetadataInfo);
|
||||
campaignMode.CampaignMetadata?.DebugDraw(spriteBatch, pos, campaignMode, debugDrawMetaData);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -582,6 +606,10 @@ namespace Barotrauma
|
||||
|
||||
GameMain.Client?.Draw(spriteBatch);
|
||||
|
||||
string factionWaterMark = "FACTION/ENDGAME TEST VERSION - please do not publicly share any material you see here!".ToUpper();
|
||||
DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth / 2, 100 * yScale) - GUIStyle.SubHeadingFont.MeasureString(factionWaterMark) / 2, factionWaterMark,
|
||||
GUIStyle.Red * 0.5f, font: GUIStyle.SubHeadingFont, backgroundColor: Color.Black * 0.5f, backgroundPadding: 10);
|
||||
|
||||
if (Character.Controlled?.Inventory != null)
|
||||
{
|
||||
if (Character.Controlled.Stun < 0.1f && !Character.Controlled.IsDead)
|
||||
@@ -667,21 +695,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawBackgroundSprite(SpriteBatch spriteBatch, Sprite backgroundSprite, float aberrationStrength = 1.0f)
|
||||
public static void DrawBackgroundSprite(SpriteBatch spriteBatch, Sprite backgroundSprite, Color color)
|
||||
{
|
||||
double aberrationT = (Timing.TotalTime * 0.5f);
|
||||
GameMain.GameScreen.PostProcessEffect.Parameters["blurDistance"].SetValue(0.001f * aberrationStrength);
|
||||
GameMain.GameScreen.PostProcessEffect.Parameters["chromaticAberrationStrength"].SetValue(new Vector3(-0.025f, -0.01f, -0.05f) *
|
||||
(float)(PerlinNoise.CalculatePerlin(aberrationT, aberrationT, 0) + 0.5f) * aberrationStrength);
|
||||
|
||||
Matrix.CreateOrthographicOffCenter(0, GameMain.GraphicsWidth, GameMain.GraphicsHeight, 0, 0, -1, out Matrix projection);
|
||||
|
||||
GameMain.GameScreen.PostProcessEffect.Parameters["MatrixTransform"].SetValue(projection);
|
||||
GameMain.GameScreen.PostProcessEffect.CurrentTechnique = GameMain.GameScreen.PostProcessEffect.Techniques["BlurChromaticAberration"];
|
||||
GameMain.GameScreen.PostProcessEffect.CurrentTechnique.Passes[0].Apply();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, effect: GameMain.GameScreen.PostProcessEffect);
|
||||
|
||||
float scale = Math.Max(
|
||||
(float)GameMain.GraphicsWidth / backgroundSprite.SourceRect.Width,
|
||||
(float)GameMain.GraphicsHeight / backgroundSprite.SourceRect.Height) * 1.1f;
|
||||
@@ -694,10 +709,8 @@ namespace Barotrauma
|
||||
|
||||
spriteBatch.Draw(backgroundSprite.Texture,
|
||||
new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos,
|
||||
null, Color.White, 0.0f, backgroundSprite.size / 2,
|
||||
null, color, 0.0f, backgroundSprite.size / 2,
|
||||
scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
#region Update list
|
||||
@@ -1189,48 +1202,37 @@ namespace Barotrauma
|
||||
}
|
||||
if (PlayerInput.IsCtrlDown() && PlayerInput.KeyHit(Keys.M))
|
||||
{
|
||||
debugDrawMetadata = !debugDrawMetadata;
|
||||
debugDrawMetaData.Enabled = !debugDrawMetaData.Enabled;
|
||||
}
|
||||
|
||||
if (debugDrawMetadata)
|
||||
if (debugDrawMetaData.Enabled)
|
||||
{
|
||||
if (PlayerInput.KeyHit(Keys.Up))
|
||||
{
|
||||
debugDrawMetadataOffset--;
|
||||
debugDrawMetaData.Offset--;
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.Down))
|
||||
{
|
||||
debugDrawMetadataOffset++;
|
||||
debugDrawMetaData.Offset++;
|
||||
}
|
||||
|
||||
if (PlayerInput.IsCtrlDown())
|
||||
{
|
||||
if (PlayerInput.KeyHit(Keys.D1))
|
||||
{
|
||||
ignoredMetadataInfo[0] = ignoredMetadataInfo[0] == string.Empty ? "reputation.location" : string.Empty;
|
||||
debugDrawMetadataOffset = 0;
|
||||
debugDrawMetaData.FactionMetadata = !debugDrawMetaData.FactionMetadata;
|
||||
debugDrawMetaData.Offset = 0;
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.D2))
|
||||
{
|
||||
ignoredMetadataInfo[1] = ignoredMetadataInfo[1] == string.Empty ? "reputation.faction" : string.Empty;
|
||||
debugDrawMetadataOffset = 0;
|
||||
debugDrawMetaData.UpgradeLevels = !debugDrawMetaData.UpgradeLevels;
|
||||
debugDrawMetaData.Offset = 0;
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.D3))
|
||||
{
|
||||
ignoredMetadataInfo[2] = ignoredMetadataInfo[2] == string.Empty ? "upgrade." : string.Empty;
|
||||
debugDrawMetadataOffset = 0;
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.D4))
|
||||
{
|
||||
ignoredMetadataInfo[3] = ignoredMetadataInfo[3] == string.Empty ? "upgradeprice." : string.Empty;
|
||||
debugDrawMetadataOffset = 0;
|
||||
debugDrawMetaData.UpgradePrices = !debugDrawMetaData.UpgradePrices;
|
||||
debugDrawMetaData.Offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HandlePersistingElements(deltaTime);
|
||||
@@ -1244,6 +1246,10 @@ namespace Barotrauma
|
||||
UpdateMessages(deltaTime);
|
||||
UpdateSavingIndicator(deltaTime);
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
GUITextBox.UpdateIME();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void UpdateGUIMessageBoxesOnly(float deltaTime)
|
||||
@@ -1350,7 +1356,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
#region Element drawing
|
||||
#region Element drawing
|
||||
|
||||
private static readonly List<float> usedIndicatorAngles = new List<float>();
|
||||
|
||||
@@ -1605,6 +1611,54 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2 position, Vector2 size, Vector2 origin, float rotation, Color clr, float depth = 0.0f, float thickness = 1, OutlinePosition outlinePos = OutlinePosition.Centered)
|
||||
{
|
||||
Vector2 topLeft = new Vector2(-origin.X, -origin.Y);
|
||||
Vector2 topRight = new Vector2(-origin.X + size.X, -origin.Y);
|
||||
Vector2 bottomLeft = new Vector2(-origin.X, -origin.Y + size.Y);
|
||||
Vector2 actualSize = size;
|
||||
|
||||
switch(outlinePos)
|
||||
{
|
||||
case OutlinePosition.Default:
|
||||
actualSize += new Vector2(thickness);
|
||||
break;
|
||||
case OutlinePosition.Centered:
|
||||
topLeft -= new Vector2(thickness * 0.5f);
|
||||
topRight -= new Vector2(thickness * 0.5f);
|
||||
bottomLeft -= new Vector2(thickness * 0.5f);
|
||||
actualSize += new Vector2(thickness);
|
||||
break;
|
||||
case OutlinePosition.Inside:
|
||||
topRight -= new Vector2(thickness, 0.0f);
|
||||
bottomLeft -= new Vector2(0.0f, thickness);
|
||||
break;
|
||||
case OutlinePosition.Outside:
|
||||
topLeft -= new Vector2(thickness);
|
||||
topRight -= new Vector2(0.0f, thickness);
|
||||
bottomLeft -= new Vector2(thickness, 0.0f);
|
||||
actualSize += new Vector2(thickness * 2.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
Matrix rotate = Matrix.CreateRotationZ(rotation);
|
||||
topLeft = Vector2.Transform(topLeft, rotate) + position;
|
||||
topRight = Vector2.Transform(topRight, rotate) + position;
|
||||
bottomLeft = Vector2.Transform(bottomLeft, rotate) + position;
|
||||
|
||||
Rectangle srcRect = new Rectangle(0, 0, 1, 1);
|
||||
sb.Draw(solidWhiteTexture, topLeft, srcRect, clr, rotation, Vector2.Zero, new Vector2(thickness, actualSize.Y), SpriteEffects.None, depth);
|
||||
sb.Draw(solidWhiteTexture, topLeft, srcRect, clr, rotation, Vector2.Zero, new Vector2(actualSize.X, thickness), SpriteEffects.None, depth);
|
||||
sb.Draw(solidWhiteTexture, topRight, srcRect, clr, rotation, Vector2.Zero, new Vector2(thickness, actualSize.Y), SpriteEffects.None, depth);
|
||||
sb.Draw(solidWhiteTexture, bottomLeft, srcRect, clr, rotation, Vector2.Zero, new Vector2(actualSize.X, thickness), SpriteEffects.None, depth);
|
||||
}
|
||||
|
||||
public static void DrawFilledRectangle(SpriteBatch sb, Vector2 position, Vector2 size, Vector2 pivot, float rotation, Color clr, float depth = 0.0f)
|
||||
{
|
||||
Rectangle srcRect = new Rectangle(0, 0, 1, 1);
|
||||
sb.Draw(solidWhiteTexture, position, srcRect, clr, rotation, (pivot/size), size, SpriteEffects.None, depth);
|
||||
}
|
||||
|
||||
public static void DrawFilledRectangle(SpriteBatch sb, RectangleF rect, Color clr, float depth = 0.0f)
|
||||
{
|
||||
DrawFilledRectangle(sb, rect.Location, rect.Size, clr, depth);
|
||||
@@ -1788,9 +1842,9 @@ namespace Barotrauma
|
||||
Vector2 pos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) - new Vector2(HUDLayoutSettings.Padding) - 2 * Scale * sheet.FrameSize.ToVector2();
|
||||
sheet.Draw(spriteBatch, (int)Math.Floor(savingIndicatorSpriteIndex), pos, savingIndicatorColor, origin: Vector2.Zero, rotate: 0.0f, scale: new Vector2(Scale));
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Element creation
|
||||
#region Element creation
|
||||
|
||||
public static Texture2D CreateCircle(int radius, bool filled = false)
|
||||
{
|
||||
@@ -2162,9 +2216,9 @@ namespace Barotrauma
|
||||
return msgBox;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Element positioning
|
||||
#region Element positioning
|
||||
private static List<T> CreateElements<T>(int count, RectTransform parent, Func<RectTransform, T> constructor,
|
||||
Vector2? relativeSize = null, Point? absoluteSize = null,
|
||||
Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, Point? minSize = null, Point? maxSize = null,
|
||||
@@ -2363,9 +2417,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Misc
|
||||
#region Misc
|
||||
public static void TogglePauseMenu()
|
||||
{
|
||||
if (Screen.Selected == GameMain.MainMenuScreen) { return; }
|
||||
@@ -2603,6 +2657,6 @@ namespace Barotrauma
|
||||
if (!isSavingIndicatorEnabled) { return; }
|
||||
timeUntilSavingIndicatorDisabled = delay;
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Barotrauma.IO;
|
||||
using RestSharp;
|
||||
using System.Net;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.Tutorials;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -736,7 +737,7 @@ namespace Barotrauma
|
||||
|
||||
public static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Vector2 pos)
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning) { return; }
|
||||
if (ObjectiveManager.ContentRunning) { return; }
|
||||
|
||||
int width = (int)(400 * GUI.Scale);
|
||||
int height = (int)(18 * GUI.Scale);
|
||||
@@ -759,7 +760,7 @@ namespace Barotrauma
|
||||
|
||||
public static void DrawToolTip(SpriteBatch spriteBatch, RichString toolTip, Rectangle targetElement)
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning) { return; }
|
||||
if (ObjectiveManager.ContentRunning) { return; }
|
||||
|
||||
int width = (int)(400 * GUI.Scale);
|
||||
int height = (int)(18 * GUI.Scale);
|
||||
|
||||
@@ -112,6 +112,10 @@ namespace Barotrauma
|
||||
public void ReceiveTextInput(string text) { }
|
||||
public void ReceiveCommandInput(char command) { }
|
||||
|
||||
#if !WINDOWS
|
||||
public void ReceiveEditingInput(string text, int start) { }
|
||||
#endif
|
||||
|
||||
public void ReceiveSpecialInput(Keys key)
|
||||
{
|
||||
switch (key)
|
||||
|
||||
@@ -1349,6 +1349,10 @@ namespace Barotrauma
|
||||
public void ReceiveTextInput(string text) { }
|
||||
public void ReceiveCommandInput(char command) { }
|
||||
|
||||
#if !WINDOWS
|
||||
public void ReceiveEditingInput(string text, int start) { }
|
||||
#endif
|
||||
|
||||
public void ReceiveSpecialInput(Keys key)
|
||||
{
|
||||
switch (key)
|
||||
|
||||
@@ -24,7 +24,8 @@ namespace Barotrauma
|
||||
InGame,
|
||||
Vote,
|
||||
Hint,
|
||||
Tutorial
|
||||
Tutorial,
|
||||
Warning // Keep this last so that it's always drawn in front
|
||||
}
|
||||
|
||||
private bool IsAnimated => type == Type.InGame || type == Type.Hint || type == Type.Tutorial;
|
||||
@@ -84,8 +85,8 @@ namespace Barotrauma
|
||||
|
||||
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
|
||||
|
||||
public GUIMessageBox(LocalizedString headerText, LocalizedString text, Vector2? relativeSize = null, Point? minSize = null)
|
||||
: this(headerText, text, new LocalizedString[] { "OK" }, relativeSize, minSize)
|
||||
public GUIMessageBox(LocalizedString headerText, LocalizedString text, Vector2? relativeSize = null, Point? minSize = null, Type type = Type.Default)
|
||||
: this(headerText, text, new LocalizedString[] { "OK" }, relativeSize, minSize, type: type)
|
||||
{
|
||||
this.Buttons[0].OnClicked = Close;
|
||||
}
|
||||
@@ -147,7 +148,7 @@ namespace Barotrauma
|
||||
Tag = tag.ToIdentifier();
|
||||
|
||||
#warning TODO: These should be broken into separate methods at least
|
||||
if (type == Type.Default || type == Type.Vote)
|
||||
if (type == Type.Default || type == Type.Vote || type == Type.Warning)
|
||||
{
|
||||
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
|
||||
|
||||
@@ -266,7 +267,7 @@ namespace Barotrauma
|
||||
Buttons.Clear();
|
||||
}
|
||||
|
||||
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
|
||||
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, textColor: GUIStyle.TextColorBright);
|
||||
GUIStyle.Apply(Header, "", this);
|
||||
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -285,8 +286,8 @@ namespace Barotrauma
|
||||
/// This is the new constructor.
|
||||
/// If the rectT height is set 0, the height is calculated from the text.
|
||||
/// </summary>
|
||||
public GUITextBlock(RectTransform rectT, RichString text, Color? textColor = null, GUIFont font = null,
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null)
|
||||
public GUITextBlock(RectTransform rectT, RichString text, Color? textColor = null, GUIFont font = null,
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null)
|
||||
: base(style, rectT)
|
||||
{
|
||||
if (color.HasValue)
|
||||
@@ -551,6 +552,8 @@ namespace Barotrauma
|
||||
|
||||
if (TextGetter != null) { Text = TextGetter(); }
|
||||
|
||||
string textToShow = Censor ? censoredText : (Wrap ? wrappedText.Value : text.SanitizedValue);
|
||||
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
if (overflowClipActive)
|
||||
{
|
||||
@@ -561,7 +564,7 @@ namespace Barotrauma
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
}
|
||||
|
||||
if (!text.IsNullOrEmpty())
|
||||
if (!string.IsNullOrEmpty(textToShow))
|
||||
{
|
||||
Vector2 pos = rect.Location.ToVector2() + textPos + TextOffset;
|
||||
if (RoundToNearestPixel)
|
||||
@@ -570,7 +573,8 @@ namespace Barotrauma
|
||||
pos.Y = (int)pos.Y;
|
||||
}
|
||||
|
||||
Color currentTextColor = State == ComponentState.Hover || State == ComponentState.HoverSelected ? HoverTextColor : TextColor;
|
||||
Color currentTextColor = State is ComponentState.Hover or ComponentState.HoverSelected ? HoverTextColor : TextColor;
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
currentTextColor = disabledTextColor;
|
||||
@@ -582,8 +586,14 @@ namespace Barotrauma
|
||||
|
||||
if (!HasColorHighlight)
|
||||
{
|
||||
string textToShow = Censor ? censoredText : (Wrap ? wrappedText.Value : text.SanitizedValue);
|
||||
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);
|
||||
if (TextManager.DebugDraw)
|
||||
{
|
||||
if (!text.NestedStr.Loaded || text.NestedStr.Language == LanguageIdentifier.None)
|
||||
{
|
||||
colorToShow = Color.Magenta;
|
||||
}
|
||||
}
|
||||
|
||||
if (Shadow)
|
||||
{
|
||||
@@ -597,10 +607,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (OverrideRichTextDataAlpha)
|
||||
{
|
||||
RichTextData.Value.ForEach(rt => rt.Alpha = currentTextColor.A / 255.0f);
|
||||
RichTextData?.ForEach(rt => rt.Alpha = currentTextColor.A / 255.0f);
|
||||
}
|
||||
Font.DrawStringWithColors(spriteBatch, Censor ? censoredText : (Wrap ? wrappedText : text.SanitizedString).Value, pos,
|
||||
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData.Value, alignment: textAlignment, forceUpperCase: ForceUpperCase);
|
||||
Font.DrawStringWithColors(spriteBatch, textToShow, pos,
|
||||
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData, alignment: textAlignment, forceUpperCase: ForceUpperCase);
|
||||
}
|
||||
|
||||
Strikethrough?.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X,
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma
|
||||
|
||||
public delegate void TextBoxEvent(GUITextBox sender, Keys key);
|
||||
|
||||
public class GUITextBox : GUIComponent, IKeyboardSubscriber
|
||||
public partial class GUITextBox : GUIComponent, IKeyboardSubscriber
|
||||
{
|
||||
public event TextBoxEvent OnSelected;
|
||||
public event TextBoxEvent OnDeselected;
|
||||
@@ -67,12 +67,12 @@ namespace Barotrauma
|
||||
private int selectionEndIndex;
|
||||
private bool IsLeftToRight => selectionStartIndex <= selectionEndIndex;
|
||||
|
||||
private GUICustomComponent caretAndSelectionRenderer;
|
||||
private readonly GUICustomComponent caretAndSelectionRenderer;
|
||||
|
||||
private bool mouseHeldInside;
|
||||
|
||||
private readonly Memento<string> memento = new Memento<string>();
|
||||
|
||||
|
||||
// Skip one update cycle, fixes Enter key instantly deselecting the chatbox
|
||||
private bool skipUpdate;
|
||||
|
||||
@@ -189,6 +189,7 @@ namespace Barotrauma
|
||||
base.Font = value;
|
||||
if (textBlock == null) { return; }
|
||||
textBlock.Font = value;
|
||||
imePreviewTextHandler.Font = Font;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +254,8 @@ namespace Barotrauma
|
||||
|
||||
public override bool PlaySoundOnSelect { get; set; } = true;
|
||||
|
||||
private readonly IMEPreviewTextHandler imePreviewTextHandler;
|
||||
|
||||
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, GUIFont font = null,
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false, bool createPenIcon = true)
|
||||
: base(style, rectT)
|
||||
@@ -264,6 +267,7 @@ namespace Barotrauma
|
||||
frame = new GUIFrame(new RectTransform(Vector2.One, rectT, Anchor.Center), style, color);
|
||||
GUIStyle.Apply(frame, style == "" ? "GUITextBox" : style);
|
||||
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text ?? "", textColor, font, textAlignment, wrap);
|
||||
imePreviewTextHandler = new IMEPreviewTextHandler(textBlock.Font);
|
||||
GUIStyle.Apply(textBlock, "", this);
|
||||
if (font != null) { textBlock.Font = font; }
|
||||
CaretEnabled = true;
|
||||
@@ -295,18 +299,17 @@ namespace Barotrauma
|
||||
textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - icon.Rect.Height - clearButtonWidth - icon.RectTransform.AbsoluteOffset.X * 2, int.MaxValue);
|
||||
}
|
||||
Font = textBlock.Font;
|
||||
|
||||
Enabled = true;
|
||||
|
||||
rectT.SizeChanged += () =>
|
||||
rectT.SizeChanged += () =>
|
||||
{
|
||||
if (icon != null) { textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - icon.Rect.Height - icon.RectTransform.AbsoluteOffset.X * 2, int.MaxValue); }
|
||||
caretPosDirty = true;
|
||||
caretPosDirty = true;
|
||||
};
|
||||
rectT.ScaleChanged += () =>
|
||||
{
|
||||
if (icon != null) { textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - icon.Rect.Height - icon.RectTransform.AbsoluteOffset.X * 2, int.MaxValue); }
|
||||
caretPosDirty = true;
|
||||
caretPosDirty = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -381,14 +384,16 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.KeyboardDispatcher.Subscriber = null;
|
||||
}
|
||||
|
||||
OnDeselected?.Invoke(this, Keys.None);
|
||||
imePreviewTextHandler.Reset();
|
||||
}
|
||||
|
||||
public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectOffset = null)
|
||||
{
|
||||
frame.Flash(color, flashDuration, useRectangleFlash, useCircularFlash, flashRectOffset);
|
||||
}
|
||||
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
if (!Visible) return;
|
||||
@@ -673,6 +678,18 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if !WINDOWS
|
||||
public void ReceiveEditingInput(string text, int start)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
if (start is 0) { imePreviewTextHandler.Reset(); }
|
||||
return;
|
||||
}
|
||||
|
||||
imePreviewTextHandler.UpdateText(text, start);
|
||||
}
|
||||
#endif
|
||||
|
||||
public void ReceiveSpecialInput(Keys key)
|
||||
{
|
||||
@@ -864,6 +881,24 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawIMEPreview(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!imePreviewTextHandler.HasText) { return; }
|
||||
|
||||
Vector2 imePosition = CaretScreenPos;
|
||||
int inflate = GUI.IntScale(3);
|
||||
|
||||
RectangleF rect = new RectangleF(imePosition, imePreviewTextHandler.TextSize);
|
||||
rect.Inflate(inflate, inflate);
|
||||
|
||||
RectangleF borderRect = rect;
|
||||
borderRect.Inflate(1, 1);
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, borderRect, Color.White, depth: 0.02f);
|
||||
GUI.DrawFilledRectangle(spriteBatch, rect, Color.Black, depth: 0.01f);
|
||||
Font.DrawString(spriteBatch, imePreviewTextHandler.PreviewText, imePosition, GUIStyle.Orange, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, 0, alignment: textBlock.TextAlignment, forceUpperCase: textBlock.ForceUpperCase);
|
||||
}
|
||||
|
||||
private void CalculateSelection()
|
||||
{
|
||||
string textDrawn = Censor ? textBlock.CensoredText : WrappedText;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using ImeSharp;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
/// <summary>
|
||||
/// A class for handling Input Method Editor (used for inputting e.g. Chinese and Japanese text)
|
||||
/// </summary>
|
||||
public partial class GUITextBox : GUIComponent
|
||||
{
|
||||
private static bool initialized;
|
||||
|
||||
public static GUIFrame IMEWindow { get; set; }
|
||||
public static GUITextBlock IMETextBlock { get; set; }
|
||||
|
||||
public static void UpdateIME()
|
||||
{
|
||||
if (!initialized) { InitializeIME(); }
|
||||
if (GUI.KeyboardDispatcher.Subscriber is GUITextBox { Selected: true })
|
||||
{
|
||||
IMEWindow?.AddToGUIUpdateList(order: 10);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializeIME()
|
||||
{
|
||||
InputMethod.Initialize(GameMain.Instance.Window.Hwnd, false);
|
||||
InputMethod.TextCompositionCallback = OnTextComposition;
|
||||
InputMethod.CommitTextCompositionCallback = OnCommitTextComposition;
|
||||
InputMethod.Enabled = true;
|
||||
IMEWindow = new GUIFrame(new RectTransform(new Point(GUI.IntScale(300), GUI.IntScale(300)), GUI.Canvas), "InnerFrame") { CanBeFocused = false, Visible = false };
|
||||
IMETextBlock = new GUITextBlock(new RectTransform(Vector2.One, IMEWindow.RectTransform), "") { CanBeFocused = false };
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private static void OnTextComposition(IMEString compositionText, int cursorPosition, IMEString[] candidateList, int candidatePageStart, int candidatePageSize, int candidateSelection)
|
||||
{
|
||||
if (GUI.KeyboardDispatcher.Subscriber is not GUITextBox { Selected: true } textBox) { return; }
|
||||
IMEWindow.Visible = true;
|
||||
string text = compositionText.ToString().Insert(cursorPosition, "|");
|
||||
if (candidateList != null)
|
||||
{
|
||||
text += "\n";
|
||||
for (int i = 0; i < candidatePageSize; i++)
|
||||
{
|
||||
string candidateStr = $"\t{candidatePageStart + i + 1} {candidateList[i]}";
|
||||
if (candidateSelection == i)
|
||||
{
|
||||
candidateStr = $" ‖color:{XMLExtensions.ToStringHex(Color.White)}‖{candidateStr}‖end‖";
|
||||
}
|
||||
candidateStr += "\n";
|
||||
text += candidateStr;
|
||||
}
|
||||
}
|
||||
IMETextBlock.Text = RichString.Rich(text);
|
||||
|
||||
IMEWindow.RectTransform.NonScaledSize = new Point(
|
||||
Math.Max(IMEWindow.Rect.Width, (int)IMETextBlock.TextSize.X + GUI.IntScale(32)),
|
||||
(int)IMETextBlock.TextSize.Y);
|
||||
|
||||
Point windowPos = new Point(textBox.Rect.X, textBox.Rect.Bottom);
|
||||
if (windowPos.Y + IMEWindow.Rect.Height > GameMain.GraphicsHeight)
|
||||
{
|
||||
windowPos.Y = textBox.Rect.Y - IMEWindow.Rect.Height;
|
||||
}
|
||||
IMEWindow.RectTransform.ScreenSpaceOffset = windowPos;
|
||||
}
|
||||
|
||||
private static void OnCommitTextComposition(string text)
|
||||
{
|
||||
if (IMEWindow.Visible)
|
||||
{
|
||||
foreach (char c in text)
|
||||
{
|
||||
if (!char.IsControl(c))
|
||||
{
|
||||
GUI.KeyboardDispatcher.Subscriber?.ReceiveTextInput(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
IMEWindow.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Barotrauma
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle AfflictionAreaLeft
|
||||
public static Rectangle HealthBarAfflictionArea
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
@@ -143,7 +143,7 @@ namespace Barotrauma
|
||||
}
|
||||
int healthBarHeight = (int)(50f * GUI.Scale);
|
||||
HealthBarArea = new Rectangle(BottomRightInfoArea.Right - healthBarWidth + (int)Math.Floor(1 / GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + GUI.IntScale(10), healthBarWidth, healthBarHeight);
|
||||
AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight);
|
||||
HealthBarAfflictionArea = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight);
|
||||
|
||||
|
||||
int messageAreaWidth = GameMain.GraphicsWidth / 3;
|
||||
@@ -173,7 +173,7 @@ namespace Barotrauma
|
||||
|
||||
int objectiveListAreaX = HealthWindowAreaLeft.Right + Padding;
|
||||
int objectiveListAreaY = ButtonAreaTop.Bottom + Padding;
|
||||
TutorialObjectiveListArea = new Rectangle(objectiveListAreaX, objectiveListAreaY, (GameMain.GraphicsWidth - Padding) - objectiveListAreaX, (AfflictionAreaLeft.Top - Padding) - objectiveListAreaY);
|
||||
TutorialObjectiveListArea = new Rectangle(objectiveListAreaX, objectiveListAreaY, (GameMain.GraphicsWidth - Padding) - objectiveListAreaX, (HealthBarAfflictionArea.Top - Padding) - objectiveListAreaY);
|
||||
|
||||
int votingAreaWidth = (int)(400 * GUI.Scale);
|
||||
int votingAreaX = GameMain.GraphicsWidth - Padding - votingAreaWidth;
|
||||
@@ -193,7 +193,7 @@ namespace Barotrauma
|
||||
DrawRectangle(CrewArea, Color.Blue * 0.5f);
|
||||
DrawRectangle(ChatBoxArea, Color.Cyan * 0.5f);
|
||||
DrawRectangle(HealthBarArea, Color.Red * 0.5f);
|
||||
DrawRectangle(AfflictionAreaLeft, Color.Red * 0.5f);
|
||||
DrawRectangle(HealthBarAfflictionArea, Color.Red * 0.5f);
|
||||
DrawRectangle(InventoryAreaLower, Color.Yellow * 0.5f);
|
||||
DrawRectangle(HealthWindowAreaLeft, Color.Red * 0.5f);
|
||||
DrawRectangle(BottomRightInfoArea, Color.Green * 0.5f);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public sealed class IMEPreviewTextHandler
|
||||
{
|
||||
public string PreviewText { get; private set; } = string.Empty;
|
||||
public Vector2 TextSize { get; private set; }
|
||||
public bool HasText => !string.IsNullOrEmpty(PreviewText);
|
||||
|
||||
// This has to be settable because for some reason we update the font of GUITextBox in some places
|
||||
public GUIFont Font { get; set; }
|
||||
|
||||
public IMEPreviewTextHandler(GUIFont font)
|
||||
{
|
||||
Font = font;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
TextSize = Vector2.Zero;
|
||||
PreviewText = string.Empty;
|
||||
}
|
||||
|
||||
public void UpdateText(string text, int start)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) && start is 0)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
int totalLength = start + text.Length;
|
||||
string newText = PreviewText;
|
||||
if (newText.Length > totalLength)
|
||||
{
|
||||
newText = newText[..totalLength];
|
||||
}
|
||||
|
||||
if (totalLength > newText.Length)
|
||||
{
|
||||
// this is required for some reason on Windows
|
||||
// my guess is that the order which TextEditing events come thru is not guaranteed
|
||||
newText = newText.PadRight(totalLength);
|
||||
}
|
||||
|
||||
newText = newText.Remove(start, text.Length).Insert(start, text);
|
||||
PreviewText = newText;
|
||||
TextSize = Font.MeasureString(PreviewText);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ namespace Barotrauma
|
||||
decorativeMap = new SpriteSheet("Content/Map/MapHUD.png", 6, 5, Vector2.Zero, sourceRect: new Rectangle(0, 0, 2048, 640));
|
||||
decorativeGraph = new SpriteSheet("Content/Map/MapHUD.png", 4, 10, Vector2.Zero, sourceRect: new Rectangle(1025, 1259, 1024, 732));
|
||||
|
||||
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
|
||||
overlay = TextureLoader.FromFile("Content/UI/MainMenuVignette.png");
|
||||
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
|
||||
DrawLoadingText = true;
|
||||
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
|
||||
@@ -138,13 +138,6 @@ namespace Barotrauma
|
||||
DisableSplashScreen();
|
||||
}
|
||||
}
|
||||
|
||||
var titleStyle = GUIStyle.GetComponentStyle("TitleText");
|
||||
Sprite titleSprite = null;
|
||||
if (!WaitForLanguageSelection && titleStyle != null && titleStyle.Sprites.ContainsKey(GUIComponent.ComponentState.None))
|
||||
{
|
||||
titleSprite = titleStyle.Sprites[GUIComponent.ComponentState.None].First()?.Sprite;
|
||||
}
|
||||
|
||||
drawn = true;
|
||||
|
||||
@@ -165,7 +158,7 @@ namespace Barotrauma
|
||||
null, Color.White, 0.0f, new Vector2(currentBackgroundTexture.Width / 2, currentBackgroundTexture.Height / 2),
|
||||
scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
spriteBatch.Draw(overlay, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f);
|
||||
spriteBatch.Draw(overlay, Vector2.Zero, null, Color.White, 0.0f, Vector2.Zero, Math.Min(GameMain.GraphicsWidth / (float)overlay.Width, GameMain.GraphicsHeight / (float)overlay.Height), SpriteEffects.None, 0.0f);
|
||||
|
||||
float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0);
|
||||
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f;
|
||||
@@ -174,10 +167,7 @@ namespace Barotrauma
|
||||
color: Color.White * noiseStrength * 0.1f,
|
||||
textureScale: Vector2.One * noiseScale);
|
||||
|
||||
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f),
|
||||
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
|
||||
scale: GameMain.GraphicsHeight / 2000.0f);
|
||||
|
||||
Vector2 textPos = new Vector2((int)(GameMain.GraphicsWidth * 0.05f), (int)(GameMain.GraphicsHeight * 0.75f));
|
||||
if (WaitForLanguageSelection)
|
||||
{
|
||||
DrawLanguageSelectionPrompt(spriteBatch, graphics);
|
||||
@@ -215,16 +205,18 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (GUIStyle.LargeFont.HasValue)
|
||||
{
|
||||
GUIStyle.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
|
||||
new Vector2(GameMain.GraphicsWidth / 2.0f - GUIStyle.LargeFont.MeasureString(loadText.ToUpper()).X / 2.0f, GameMain.GraphicsHeight * 0.75f),
|
||||
textPos,
|
||||
Color.White);
|
||||
textPos.Y += GUIStyle.LargeFont.MeasureString(loadText.ToUpper()).Y * 1.2f;
|
||||
}
|
||||
|
||||
if (GUIStyle.Font.HasValue && selectedTip != null)
|
||||
{
|
||||
string wrappedTip = ToolBox.WrapText(selectedTip.SanitizedValue, GameMain.GraphicsWidth * 0.5f, GUIStyle.Font.Value);
|
||||
string wrappedTip = ToolBox.WrapText(selectedTip.SanitizedValue, GameMain.GraphicsWidth * 0.3f, GUIStyle.Font.Value);
|
||||
string[] lines = wrappedTip.Split('\n');
|
||||
float lineHeight = GUIStyle.Font.MeasureString(selectedTip).Y;
|
||||
|
||||
@@ -234,7 +226,8 @@ namespace Barotrauma
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
GUIStyle.Font.DrawStringWithColors(spriteBatch, lines[i],
|
||||
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUIStyle.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White,
|
||||
new Vector2(textPos.X, (int)(textPos.Y + i * lineHeight)),
|
||||
Color.White,
|
||||
0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTip.RichTextData.Value, rtdOffset);
|
||||
rtdOffset += lines[i].Length;
|
||||
}
|
||||
@@ -244,7 +237,8 @@ namespace Barotrauma
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
GUIStyle.Font.DrawString(spriteBatch, lines[i],
|
||||
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUIStyle.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
|
||||
new Vector2(textPos.X, (int)(textPos.Y + i * lineHeight)),
|
||||
new Color(228, 217, 167, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,13 +251,16 @@ namespace Barotrauma
|
||||
Vector2 decorativeScale = new Vector2(GameMain.GraphicsHeight / 1080.0f);
|
||||
|
||||
float noiseVal = (float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.25f, Timing.TotalTime * 0.5f, 0);
|
||||
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal),
|
||||
new Vector2(GameMain.GraphicsWidth * 0.001f, GameMain.GraphicsHeight * 0.24f),
|
||||
Color.White, Vector2.Zero, 0.0f, decorativeScale, SpriteEffects.FlipVertically);
|
||||
if (!WaitForLanguageSelection)
|
||||
{
|
||||
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal),
|
||||
new Vector2(GameMain.GraphicsWidth * 0.001f, textPos.Y),
|
||||
Color.White, new Vector2(0, decorativeMap.FrameSize.Y), 0.0f, decorativeScale, SpriteEffects.FlipVertically);
|
||||
}
|
||||
|
||||
decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal),
|
||||
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.66f),
|
||||
Color.White, decorativeMap.FrameSize.ToVector2(), 0.0f, decorativeScale);
|
||||
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.01f),
|
||||
Color.White, new Vector2(decorativeMap.FrameSize.X, 0), 0.0f, decorativeScale, SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically);
|
||||
|
||||
if (noiseVal < 0.2f)
|
||||
{
|
||||
@@ -285,8 +282,9 @@ namespace Barotrauma
|
||||
|
||||
if (GUIStyle.LargeFont.HasValue)
|
||||
{
|
||||
Vector2 textSize = GUIStyle.LargeFont.MeasureString(randText);
|
||||
GUIStyle.LargeFont.DrawString(spriteBatch, randText,
|
||||
new Vector2(GameMain.GraphicsWidth - decorativeMap.FrameSize.X * decorativeScale.X * 0.8f, GameMain.GraphicsHeight * 0.57f),
|
||||
new Vector2(GameMain.GraphicsWidth * 0.95f - textSize.X, GameMain.GraphicsHeight * 0.06f),
|
||||
Color.White * (1.0f - noiseVal));
|
||||
}
|
||||
|
||||
@@ -312,8 +310,8 @@ namespace Barotrauma
|
||||
languageSelectionCursor = new Sprite("Content/UI/cursor.png", Vector2.Zero);
|
||||
}
|
||||
|
||||
Vector2 textPos = new Vector2(GameMain.GraphicsWidth / 2, GameMain.GraphicsHeight * 0.3f);
|
||||
Vector2 textSpacing = new Vector2(0.0f, (GameMain.GraphicsHeight * 0.5f) / AvailableLanguages.Length);
|
||||
Vector2 textPos = new Vector2((int)(GameMain.GraphicsWidth * 0.05f), (int)(GameMain.GraphicsHeight * 0.3f));
|
||||
Vector2 textSpacing = new Vector2(0.0f, GameMain.GraphicsHeight * 0.5f / AvailableLanguages.Length);
|
||||
foreach (LanguageIdentifier language in AvailableLanguages)
|
||||
{
|
||||
string localizedLanguageName = TextManager.GetTranslatedLanguageName(language);
|
||||
@@ -321,10 +319,10 @@ namespace Barotrauma
|
||||
|
||||
Vector2 textSize = font.MeasureString(localizedLanguageName);
|
||||
bool hover =
|
||||
Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 &&
|
||||
Math.Abs(PlayerInput.MousePosition.Y - textPos.Y) < textSpacing.Y / 2;
|
||||
PlayerInput.MousePosition.X > textPos.X && PlayerInput.MousePosition.X < textPos.X + textSize.X &&
|
||||
PlayerInput.MousePosition.Y > textPos.Y && PlayerInput.MousePosition.Y < textPos.Y + textSize.Y;
|
||||
|
||||
font.DrawString(spriteBatch, localizedLanguageName, textPos - textSize / 2,
|
||||
font.DrawString(spriteBatch, localizedLanguageName, textPos,
|
||||
hover ? Color.White : Color.White * 0.6f);
|
||||
if (hover && PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
@@ -431,7 +429,7 @@ namespace Barotrauma
|
||||
drawn = false;
|
||||
LoadState = null;
|
||||
SetSelectedTip(TextManager.Get("LoadingScreenTip"));
|
||||
currentBackgroundTexture = LocationType.Prefabs.GetRandomUnsynced()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
|
||||
currentBackgroundTexture = LocationType.Prefabs.Where(p => p.UsePortraitInRandomLoadingScreens).GetRandomUnsynced()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
|
||||
|
||||
while (!drawn)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement;
|
||||
namespace Barotrauma
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedVariable")]
|
||||
internal class MedicalClinicUI
|
||||
internal sealed class MedicalClinicUI
|
||||
{
|
||||
private enum ElementState
|
||||
{
|
||||
@@ -127,12 +127,14 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly GUIComponent Panel;
|
||||
public readonly GUIListBox HealList;
|
||||
public readonly GUIComponent TreatAllButton;
|
||||
public readonly List<CrewElement> HealElements;
|
||||
|
||||
public CrewHealList(GUIListBox healList, GUIComponent panel)
|
||||
public CrewHealList(GUIListBox healList, GUIComponent panel, GUIComponent treatAllButton)
|
||||
{
|
||||
Panel = panel;
|
||||
HealList = healList;
|
||||
TreatAllButton = treatAllButton;
|
||||
HealElements = new List<CrewElement>();
|
||||
}
|
||||
}
|
||||
@@ -179,7 +181,7 @@ namespace Barotrauma
|
||||
private PopupAfflictionList? selectedCrewAfflictionList;
|
||||
private bool isWaitingForServer;
|
||||
private const float refreshTimerMax = 3f;
|
||||
private float refreshTimer = 0;
|
||||
private float refreshTimer;
|
||||
|
||||
private PlayerBalanceElement? playerBalanceElement;
|
||||
|
||||
@@ -196,7 +198,7 @@ namespace Barotrauma
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(0.2f, 0.1f), parent.RectTransform, Anchor.TopCenter), "Recreate UI - NOT PRESENT IN RELEASE!")
|
||||
{
|
||||
OnClicked = (_, __) =>
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
parent.ClearChildren();
|
||||
CreateUI();
|
||||
@@ -254,7 +256,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatePendingHealElement(healList.HealList.Content, crewMember, healList, Array.Empty<MedicalClinic.NetAffliction>());
|
||||
CreatePendingHealElement(healList.HealList.Content, crewMember, healList, ImmutableArray<MedicalClinic.NetAffliction>.Empty);
|
||||
}
|
||||
|
||||
// check if there are elements that the crew doesn't have
|
||||
@@ -309,7 +311,7 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateCrewPanel()
|
||||
{
|
||||
if (!(crewHealList is { } healList)) { return; }
|
||||
if (crewHealList is not { } healList) { return; }
|
||||
|
||||
ImmutableArray<CharacterInfo> crew = MedicalClinic.GetCrewCharacters();
|
||||
|
||||
@@ -334,12 +336,21 @@ namespace Barotrauma
|
||||
healList.HealList.Content.RemoveChild(element.UIElement);
|
||||
}
|
||||
|
||||
IEnumerable<CrewElement> orderedList = healList.HealElements.OrderBy(element => element.Target.Character?.HealthPercentage ?? 100);
|
||||
IEnumerable<CrewElement> orderedList = healList.HealElements.OrderBy(static element => element.Target.Character?.HealthPercentage ?? 100);
|
||||
|
||||
foreach (CrewElement element in orderedList)
|
||||
{
|
||||
element.UIElement.SetAsLastChild();
|
||||
}
|
||||
|
||||
healList.TreatAllButton.Enabled = false;
|
||||
foreach (CrewElement element in healList.HealElements)
|
||||
{
|
||||
if (element.Afflictions.Count is 0) { continue; }
|
||||
|
||||
healList.TreatAllButton.Enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateAfflictionList(CrewElement healElement)
|
||||
@@ -350,7 +361,7 @@ namespace Barotrauma
|
||||
// sum up all the afflictions and their strengths
|
||||
Dictionary<AfflictionPrefab, float> afflictionAndStrength = new Dictionary<AfflictionPrefab, float>();
|
||||
|
||||
foreach (Affliction affliction in health.GetAllAfflictions().Where(a => MedicalClinic.IsHealable(a)))
|
||||
foreach (Affliction affliction in health.GetAllAfflictions().Where(MedicalClinic.IsHealable))
|
||||
{
|
||||
if (afflictionAndStrength.TryGetValue(affliction.Prefab, out float strength))
|
||||
{
|
||||
@@ -446,8 +457,8 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
GUILayoutGroup clinicLabelLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), clinicContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
GUIImage clinicIcon = new GUIImage(new RectTransform(Vector2.One, clinicLabelLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "CrewManagementHeaderIcon", scaleToFit: true);
|
||||
GUITextBlock clinicLabel = new GUITextBlock(new RectTransform(Vector2.One, clinicLabelLayout.RectTransform), TextManager.Get("medicalclinic.medicalclinic"), font: GUIStyle.LargeFont);
|
||||
new GUIImage(new RectTransform(Vector2.One, clinicLabelLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "CrewManagementHeaderIcon", scaleToFit: true);
|
||||
new GUITextBlock(new RectTransform(Vector2.One, clinicLabelLayout.RectTransform), TextManager.Get("medicalclinic.medicalclinic"), font: GUIStyle.LargeFont);
|
||||
|
||||
GUIFrame clinicBackground = new GUIFrame(new RectTransform(Vector2.One, clinicContent.RectTransform));
|
||||
|
||||
@@ -480,22 +491,24 @@ namespace Barotrauma
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
// GUILayoutGroup sortLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.05f), clinicContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
|
||||
// new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), sortLayout.RectTransform), TextManager.Get("campaignstore.sortby"), font: GUI.SubHeadingFont);
|
||||
|
||||
// GUIDropDown sortDropdown = new GUIDropDown(new RectTransform(new Vector2(0.3f, 1f), sortLayout.RectTransform));
|
||||
//
|
||||
// foreach (SortMode mode in Enum.GetValues(typeof(SortMode)).Cast<SortMode>())
|
||||
// {
|
||||
// sortDropdown.AddItem(TextManager.Get($"medicalclinic.sortmode.{mode}"), mode);
|
||||
// }
|
||||
//
|
||||
// sortDropdown.SelectItem(SortMode.Severity);
|
||||
|
||||
GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, clinicContainer.RectTransform));
|
||||
|
||||
crewHealList = new CrewHealList(crewList, parent);
|
||||
GUIButton treatAllButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), clinicContainer.RectTransform), TextManager.Get("medicalclinic.treateveryone"))
|
||||
{
|
||||
OnClicked = (_, _) =>
|
||||
{
|
||||
isWaitingForServer = true;
|
||||
medicalClinic.TreatAllButtonAction(OnReceived);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
crewHealList = new CrewHealList(crewList, parent, treatAllButton);
|
||||
|
||||
void OnReceived(MedicalClinic.CallbackOnlyRequest obj)
|
||||
{
|
||||
isWaitingForServer = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCrewEntry(GUIComponent parent, CrewHealList healList, CharacterInfo info, GUIComponent panel)
|
||||
@@ -525,9 +538,9 @@ namespace Barotrauma
|
||||
TextColor = GUIStyle.Red
|
||||
};
|
||||
|
||||
MedicalClinic.NetCrewMember member = new MedicalClinic.NetCrewMember { CharacterInfo = info, Afflictions = Array.Empty<MedicalClinic.NetAffliction>() };
|
||||
MedicalClinic.NetCrewMember member = new MedicalClinic.NetCrewMember(info);
|
||||
|
||||
crewBackground.OnClicked = (_, __) =>
|
||||
crewBackground.OnClicked = (_, _) =>
|
||||
{
|
||||
SelectCharacter(member, new Vector2(panel.Rect.Right, crewBackground.Rect.Top));
|
||||
return true;
|
||||
@@ -618,7 +631,7 @@ namespace Barotrauma
|
||||
pendingHealList = list;
|
||||
}
|
||||
|
||||
private void CreatePendingHealElement(GUIComponent parent, MedicalClinic.NetCrewMember crewMember, PendingHealList healList, MedicalClinic.NetAffliction[] afflictions)
|
||||
private void CreatePendingHealElement(GUIComponent parent, MedicalClinic.NetCrewMember crewMember, PendingHealList healList, ImmutableArray<MedicalClinic.NetAffliction> afflictions)
|
||||
{
|
||||
CharacterInfo? healInfo = crewMember.FindCharacterInfo(MedicalClinic.GetCrewCharacters());
|
||||
if (healInfo is null) { return; }
|
||||
@@ -803,7 +816,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
allComponents.Add(treatAllButton);
|
||||
treatAllButton.OnClicked = (_, __) =>
|
||||
treatAllButton.OnClicked = (_, _) =>
|
||||
{
|
||||
ImmutableArray<MedicalClinic.NetAffliction> afflictions = request.Afflictions.Where(a => !medicalClinic.IsAfflictionPending(crewMember, a)).ToImmutableArray();
|
||||
if (!afflictions.Any()) { return true; }
|
||||
@@ -845,9 +858,9 @@ namespace Barotrauma
|
||||
|
||||
GUITextBlock prefabBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), topTextLayout.RectTransform), prefab.Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
Color textColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red, (int)affliction.AfflictionSeverity / 2f);
|
||||
Color textColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red, affliction.Strength / affliction.Prefab.MaxStrength);
|
||||
|
||||
LocalizedString vitalityText = TextManager.GetWithVariable("medicalclinic.vitalitydifference", "[amount]", (-affliction.Strength).ToString());
|
||||
LocalizedString vitalityText = affliction.VitalityDecrease == 0 ? string.Empty : TextManager.GetWithVariable("medicalclinic.vitalitydifference", "[amount]", (-affliction.VitalityDecrease).ToString());
|
||||
GUITextBlock vitalityBlock = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), topTextLayout.RectTransform), vitalityText, textAlignment: Alignment.Center)
|
||||
{
|
||||
TextColor = textColor,
|
||||
@@ -856,7 +869,7 @@ namespace Barotrauma
|
||||
AutoScaleHorizontal = true
|
||||
};
|
||||
|
||||
LocalizedString severityText = TextManager.Get($"AfflictionStrength{affliction.AfflictionSeverity}");
|
||||
LocalizedString severityText = Affliction.GetStrengthText(affliction.Strength, affliction.Prefab.MaxStrength);
|
||||
GUITextBlock severityBlock = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1f), topTextLayout.RectTransform), severityText, textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
TextColor = textColor,
|
||||
@@ -873,9 +886,13 @@ namespace Barotrauma
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.6f), bottomTextLayout.RectTransform), prefab.Description, font: GUIStyle.SmallFont, wrap: true)
|
||||
LocalizedString description = affliction.Prefab.GetDescription(affliction.Strength, AfflictionPrefab.Description.TargetType.OtherCharacter);
|
||||
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.6f), bottomTextLayout.RectTransform),
|
||||
description,
|
||||
font: GUIStyle.SmallFont,
|
||||
wrap: true)
|
||||
{
|
||||
ToolTip = prefab.Description
|
||||
ToolTip = description
|
||||
};
|
||||
bool truncated = false;
|
||||
while (descriptionBlock.TextSize.Y > descriptionBlock.Rect.Height && descriptionBlock.WrappedText.Contains('\n'))
|
||||
@@ -919,10 +936,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
MedicalClinic.NetCrewMember newMember = new MedicalClinic.NetCrewMember
|
||||
MedicalClinic.NetCrewMember newMember = crewMember with
|
||||
{
|
||||
CharacterInfoID = crewMember.CharacterInfoID,
|
||||
Afflictions = Array.Empty<MedicalClinic.NetAffliction>()
|
||||
Afflictions = ImmutableArray<MedicalClinic.NetAffliction>.Empty
|
||||
};
|
||||
|
||||
existingMember = newMember;
|
||||
@@ -936,7 +952,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
existingMember.Afflictions = existingMember.Afflictions.Concat(afflictions).ToArray();
|
||||
existingMember.Afflictions = existingMember.Afflictions.Concat(afflictions).ToImmutableArray();
|
||||
ToggleElements(ElementState.Disabled, elementsToDisable);
|
||||
medicalClinic.AddPendingButtonAction(existingMember, request =>
|
||||
{
|
||||
|
||||
@@ -724,7 +724,7 @@ namespace Barotrauma
|
||||
ChangeStoreTab(StoreTab.Buy);
|
||||
if (newLocation?.Reputation != null)
|
||||
{
|
||||
CurrentLocation.Reputation.OnReputationValueChanged.RegisterOverwriteExisting("RefreshStore".ToIdentifier(), _ => { SetNeedsRefresh(); });
|
||||
newLocation.Reputation.OnReputationValueChanged.RegisterOverwriteExisting("RefreshStore".ToIdentifier(), _ => { SetNeedsRefresh(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,7 +745,7 @@ namespace Barotrauma
|
||||
} ?? Enumerable.Empty<PurchasedItem>();
|
||||
foreach (var button in itemCategoryButtons)
|
||||
{
|
||||
if (!(button.UserData is MapEntityCategory category))
|
||||
if (button.UserData is not MapEntityCategory category)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -852,6 +852,28 @@ namespace Barotrauma
|
||||
FilterStoreItems(category, searchBox.Text);
|
||||
}
|
||||
|
||||
private static KeyValuePair<Identifier, float>? GetReputationRequirement(PriceInfo priceInfo)
|
||||
{
|
||||
return GameMain.GameSession?.Campaign is not null
|
||||
? priceInfo.MinReputation.FirstOrNull()
|
||||
: null;
|
||||
}
|
||||
|
||||
private static KeyValuePair<Identifier, float>? GetTooLowReputation(PriceInfo priceInfo)
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign is CampaignMode campaign)
|
||||
{
|
||||
foreach (var minRep in priceInfo.MinReputation)
|
||||
{
|
||||
if (campaign.GetReputation(minRep.Key) < minRep.Value)
|
||||
{
|
||||
return minRep;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int prevDailySpecialCount, prevRequestedGoodsCount, prevSubRequestedGoodsCount;
|
||||
|
||||
private void RefreshStoreBuyList()
|
||||
@@ -892,8 +914,9 @@ namespace Barotrauma
|
||||
|
||||
void CreateOrUpdateItemFrame(ItemPrefab itemPrefab, int quantity)
|
||||
{
|
||||
if (itemPrefab.CanBeBoughtFrom(ActiveStore, out PriceInfo priceInfo))
|
||||
if (itemPrefab.CanBeBoughtFrom(ActiveStore, out PriceInfo priceInfo) && itemPrefab.CanCharacterBuy())
|
||||
{
|
||||
|
||||
bool isDailySpecial = ActiveStore.DailySpecials.Contains(itemPrefab);
|
||||
var itemFrame = isDailySpecial ?
|
||||
storeDailySpecialsGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
|
||||
@@ -918,7 +941,8 @@ namespace Barotrauma
|
||||
SetOwnedText(itemFrame);
|
||||
SetPriceGetters(itemFrame, true);
|
||||
}
|
||||
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
|
||||
|
||||
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0 && !GetTooLowReputation(priceInfo).HasValue);
|
||||
existingItemFrames.Add(itemFrame);
|
||||
}
|
||||
}
|
||||
@@ -1110,7 +1134,7 @@ namespace Barotrauma
|
||||
|
||||
private void SetPriceGetters(GUIComponent itemFrame, bool buying)
|
||||
{
|
||||
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
|
||||
if (itemFrame == null || itemFrame.UserData is not PurchasedItem pi) { return; }
|
||||
|
||||
if (itemFrame.FindChild("undiscountedprice", recursive: true) is GUITextBlock undiscountedPriceBlock)
|
||||
{
|
||||
@@ -1302,6 +1326,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
|
||||
{
|
||||
int reputationCompare = CompareByReputationRestriction(itemX, itemY);
|
||||
if (reputationCompare != 0) { return reputationCompare; }
|
||||
int sortResult = itemX.ItemPrefab.Name != itemY.ItemPrefab.Name ?
|
||||
itemX.ItemPrefab.Name.CompareTo(itemY.ItemPrefab.Name) :
|
||||
itemX.ItemPrefab.Identifier.CompareTo(itemY.ItemPrefab.Identifier);
|
||||
@@ -1330,6 +1356,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
|
||||
{
|
||||
int reputationCompare = CompareByReputationRestriction(itemX, itemY);
|
||||
if (reputationCompare != 0) { return reputationCompare; }
|
||||
int sortResult = ActiveStore.GetAdjustedItemSellPrice(itemX.ItemPrefab).CompareTo(
|
||||
ActiveStore.GetAdjustedItemSellPrice(itemY.ItemPrefab));
|
||||
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
|
||||
@@ -1354,6 +1382,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
|
||||
{
|
||||
int reputationCompare = CompareByReputationRestriction(itemX, itemY);
|
||||
if (reputationCompare != 0) { return reputationCompare; }
|
||||
int sortResult = ActiveStore.GetAdjustedItemBuyPrice(itemX.ItemPrefab).CompareTo(
|
||||
ActiveStore.GetAdjustedItemBuyPrice(itemY.ItemPrefab));
|
||||
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
|
||||
@@ -1376,10 +1406,12 @@ namespace Barotrauma
|
||||
specialsGroup.Recalculate();
|
||||
}
|
||||
|
||||
static int CompareByCategory(RectTransform x, RectTransform y)
|
||||
int CompareByCategory(RectTransform x, RectTransform y)
|
||||
{
|
||||
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
|
||||
{
|
||||
int reputationCompare = CompareByReputationRestriction(itemX, itemY);
|
||||
if (reputationCompare != 0) { return reputationCompare; }
|
||||
return itemX.ItemPrefab.Category.CompareTo(itemY.ItemPrefab.Category);
|
||||
}
|
||||
else
|
||||
@@ -1409,6 +1441,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
int CompareByReputationRestriction(PurchasedItem item1, PurchasedItem item2)
|
||||
{
|
||||
PriceInfo priceInfo1 = item1.ItemPrefab.GetPriceInfo(ActiveStore);
|
||||
PriceInfo priceInfo2 = item2.ItemPrefab.GetPriceInfo(ActiveStore);
|
||||
if (priceInfo1 != null && priceInfo2 != null)
|
||||
{
|
||||
var requiredReputation1 = GetTooLowReputation(priceInfo1)?.Value ?? 0.0f;
|
||||
var requiredReputation2 = GetTooLowReputation(priceInfo2)?.Value ?? 0.0f;
|
||||
return requiredReputation1.CompareTo(requiredReputation2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CompareByElement(RectTransform x, RectTransform y)
|
||||
{
|
||||
if (ShouldBeOnTop(x) || ShouldBeOnBottom(y))
|
||||
@@ -1667,8 +1712,8 @@ namespace Barotrauma
|
||||
{
|
||||
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 (!subItem.Components.All(c => c is not Holdable h || !h.Attachable || !h.Attached)) { continue; }
|
||||
if (!subItem.Components.All(c => c is not Wire w || w.Connections.All(c => c == null))) { continue; }
|
||||
if (!ItemAndAllContainersInteractable(subItem)) { continue; }
|
||||
AddOwnedItem(subItem);
|
||||
}
|
||||
@@ -1701,7 +1746,7 @@ namespace Barotrauma
|
||||
|
||||
void AddOwnedItem(Item item)
|
||||
{
|
||||
if (!(item?.Prefab.GetPriceInfo(ActiveStore) is PriceInfo priceInfo)) { return; }
|
||||
if (item?.Prefab.GetPriceInfo(ActiveStore) is not PriceInfo priceInfo) { return; }
|
||||
bool isNonEmpty = !priceInfo.DisplayNonEmpty || item.ConditionPercentage > 5.0f;
|
||||
if (OwnedItems.TryGetValue(item.Prefab, out ItemQuantity itemQuantity))
|
||||
{
|
||||
@@ -1729,14 +1774,14 @@ namespace Barotrauma
|
||||
|
||||
private void SetItemFrameStatus(GUIComponent itemFrame, bool enabled)
|
||||
{
|
||||
if (!(itemFrame?.UserData is PurchasedItem pi)) { return; }
|
||||
if (itemFrame?.UserData is not 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)
|
||||
{
|
||||
icon.Color = pi.ItemPrefab.InventoryIconColor * (enabled ? 1.0f: 0.5f);
|
||||
icon.Color = pi.ItemPrefab.InventoryIconColor * (enabled ? 1.0f : 0.5f);
|
||||
}
|
||||
else if (pi.ItemPrefab?.Sprite != null)
|
||||
{
|
||||
@@ -1841,11 +1886,7 @@ namespace Barotrauma
|
||||
LocalizedString toolTip = string.Empty;
|
||||
if (purchasedItem.ItemPrefab != null)
|
||||
{
|
||||
toolTip = purchasedItem.ItemPrefab.Name;
|
||||
if (!purchasedItem.ItemPrefab.Description.IsNullOrEmpty())
|
||||
{
|
||||
toolTip += $"\n{purchasedItem.ItemPrefab.Description}";
|
||||
}
|
||||
toolTip = purchasedItem.ItemPrefab.GetTooltip();
|
||||
if (itemQuantity != null)
|
||||
{
|
||||
if (itemQuantity.AllNonEmpty)
|
||||
@@ -1858,8 +1899,25 @@ namespace Barotrauma
|
||||
toolTip += $"\n{TextManager.GetWithVariable("campaignstore.ownedtotal", "[amount]", itemQuantity.Total.ToString())}";
|
||||
}
|
||||
}
|
||||
|
||||
PriceInfo priceInfo = purchasedItem.ItemPrefab.GetPriceInfo(ActiveStore);
|
||||
var campaign = GameMain.GameSession?.Campaign;
|
||||
if (priceInfo != null && campaign != null)
|
||||
{
|
||||
var requiredReputation = GetReputationRequirement(priceInfo);
|
||||
if (requiredReputation != null)
|
||||
{
|
||||
var repStr = TextManager.GetWithVariables(
|
||||
"campaignstore.reputationrequired",
|
||||
("[amount]", ((int)requiredReputation.Value.Value).ToString()),
|
||||
("[faction]", TextManager.Get("faction." + requiredReputation.Value.Key).Value));
|
||||
Color color = campaign.GetReputation(requiredReputation.Value.Key) < requiredReputation.Value.Value ?
|
||||
GUIStyle.Orange : GUIStyle.Green;
|
||||
toolTip += $"\n‖color:{color.ToStringHex()}‖{repStr}‖color:end‖";
|
||||
}
|
||||
}
|
||||
}
|
||||
itemComponent.ToolTip = toolTip;
|
||||
itemComponent.ToolTip = RichString.Rich(toolTip);
|
||||
}
|
||||
if (ownedLabel != null)
|
||||
{
|
||||
@@ -1995,11 +2053,23 @@ namespace Barotrauma
|
||||
int totalPrice = 0;
|
||||
foreach (var item in itemsToPurchase)
|
||||
{
|
||||
if (item?.ItemPrefab == null || !item.ItemPrefab.CanBeBoughtFrom(ActiveStore, out var priceInfo))
|
||||
if (item is null) { continue; }
|
||||
|
||||
if (item.ItemPrefab == null || !item.ItemPrefab.CanBeBoughtFrom(ActiveStore, out var priceInfo))
|
||||
{
|
||||
itemsToRemove.Add(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.ItemPrefab.DefaultPrice.RequiresUnlock)
|
||||
{
|
||||
if (!CargoManager.HasUnlockedStoreItem(item.ItemPrefab))
|
||||
{
|
||||
itemsToRemove.Add(item);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
totalPrice += item.Quantity * ActiveStore.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo);
|
||||
}
|
||||
itemsToRemove.ForEach(i => itemsToPurchase.Remove(i));
|
||||
@@ -2169,7 +2239,7 @@ namespace Barotrauma
|
||||
{
|
||||
needsRefresh = itemsToSellFromSub.Count != prevSubItems.Count ||
|
||||
itemsToSellFromSub.Sum(i => i.Quantity) != prevSubItems.Sum(i => i.Quantity) ||
|
||||
itemsToSellFromSub.Any(i => !(prevSubItems.FirstOrDefault(prev => prev.ItemPrefab == i.ItemPrefab) is PurchasedItem prev) || i.Quantity != prev.Quantity) ||
|
||||
itemsToSellFromSub.Any(i => prevSubItems.FirstOrDefault(prev => prev.ItemPrefab == i.ItemPrefab) is not PurchasedItem prev || i.Quantity != prev.Quantity) ||
|
||||
prevSubItems.Any(prev => itemsToSellFromSub.None(i => i.ItemPrefab == prev.ItemPrefab));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ namespace Barotrauma
|
||||
|
||||
if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay))
|
||||
{
|
||||
LocalizedString amountString = TextManager.FormatCurrency(subToDisplay.Price);
|
||||
LocalizedString amountString = TextManager.FormatCurrency(subToDisplay.GetPrice());
|
||||
submarineDisplays[i].submarineFee.Text = TextManager.GetWithVariable("price", "[amount]", amountString);
|
||||
}
|
||||
else
|
||||
@@ -472,7 +472,7 @@ namespace Barotrauma
|
||||
if (transferService)
|
||||
{
|
||||
subsToShow.AddRange(GameMain.GameSession.OwnedSubmarines);
|
||||
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
|
||||
subsToShow.Sort(ComparePrice);
|
||||
string currentSubName = CurrentOrPendingSubmarine().Name;
|
||||
int currentIndex = subsToShow.FindIndex(s => s.Name == currentSubName);
|
||||
if (currentIndex != -1)
|
||||
@@ -484,7 +484,11 @@ namespace Barotrauma
|
||||
{
|
||||
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));
|
||||
if (GameMain.GameSession.Campaign?.Map?.CurrentLocation is Location currentLocation)
|
||||
{
|
||||
subsToShow.RemoveAll(sub => !currentLocation.IsSubmarineAvailable(sub));
|
||||
}
|
||||
subsToShow.Sort(ComparePrice);
|
||||
}
|
||||
|
||||
if (transferService)
|
||||
@@ -492,10 +496,14 @@ namespace Barotrauma
|
||||
SetConfirmButtonState(selectedSubmarine != null && selectedSubmarine.Name != CurrentOrPendingSubmarine().Name);
|
||||
}
|
||||
|
||||
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
|
||||
pageCount = Math.Max(1, (int)Math.Ceiling(subsToShow.Count / (float)submarinesPerPage));
|
||||
UpdatePaging();
|
||||
ContentRefreshRequired = false;
|
||||
|
||||
static int ComparePrice(SubmarineInfo x, SubmarineInfo y)
|
||||
{
|
||||
return x.Price.CompareTo(y.Price) * 100 + x.Name.CompareTo(y.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private SubmarineInfo GetSubToDisplay(int index)
|
||||
@@ -673,7 +681,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign?.PendingSubmarineSwitch == null)
|
||||
{
|
||||
return Submarine.MainSub.Info;
|
||||
return Submarine.MainSub?.Info;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -789,7 +797,9 @@ namespace Barotrauma
|
||||
|
||||
private void ShowBuyPrompt(bool purchaseOnly)
|
||||
{
|
||||
if (!GameMain.GameSession.Campaign.CanAfford(selectedSubmarine.Price))
|
||||
int price = selectedSubmarine.GetPrice();
|
||||
|
||||
if (!GameMain.GameSession.Campaign.CanAfford(price))
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext",
|
||||
("[currencyname]", currencyName),
|
||||
@@ -802,7 +812,7 @@ namespace Barotrauma
|
||||
{
|
||||
var text = TextManager.GetWithVariables("purchaseandswitchsubmarinetext",
|
||||
("[submarinename1]", selectedSubmarine.DisplayName),
|
||||
("[amount]", selectedSubmarine.Price.ToString()),
|
||||
("[amount]", price.ToString()),
|
||||
("[currencyname]", currencyName),
|
||||
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName));
|
||||
text += GetItemTransferText();
|
||||
@@ -860,7 +870,7 @@ namespace Barotrauma
|
||||
{
|
||||
msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext",
|
||||
("[submarinename]", selectedSubmarine.DisplayName),
|
||||
("[amount]", selectedSubmarine.Price.ToString()),
|
||||
("[amount]", price.ToString()),
|
||||
("[currencyname]", currencyName)) + '\n' + TextManager.Get("submarineswitchinstruction"), messageBoxOptions);
|
||||
|
||||
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma
|
||||
private List<CharacterTeamType> teamIDs;
|
||||
private const string inLobbyString = "\u2022 \u2022 \u2022";
|
||||
|
||||
private GUIFrame pendingChangesFrame = null;
|
||||
public static GUIFrame PendingChangesFrame = null;
|
||||
|
||||
public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
|
||||
private bool isTransferMenuOpen;
|
||||
@@ -44,6 +44,7 @@ namespace Barotrauma
|
||||
private float transferMenuOpenState;
|
||||
private bool transferMenuStateCompleted;
|
||||
private readonly HashSet<Identifier> registeredEvents = new HashSet<Identifier>();
|
||||
private readonly TalentMenu talentMenu = new TalentMenu();
|
||||
|
||||
private class LinkedGUI
|
||||
{
|
||||
@@ -206,15 +207,8 @@ namespace Barotrauma
|
||||
transferMenuButton.RectTransform.AbsoluteOffset = new Point(0, -pos - transferMenu.Rect.Height);
|
||||
}
|
||||
GameSession.UpdateTalentNotificationIndicator(talentPointNotification);
|
||||
if (Character.Controlled?.Info is { } characterInfo && talentResetButton != null && talentApplyButton != null)
|
||||
{
|
||||
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentApplyButton.Flash(GUIStyle.Orange);
|
||||
}
|
||||
}
|
||||
|
||||
talentMenu?.Update();
|
||||
|
||||
if (SelectedTab != InfoFrameTab.Crew) { return; }
|
||||
if (linkedGUIList == null) { return; }
|
||||
@@ -325,11 +319,11 @@ namespace Barotrauma
|
||||
AbsoluteOffset = new Point(contentFrame.Rect.X, contentFrame.Rect.Bottom + GUI.IntScale(8))
|
||||
}, style: null);
|
||||
|
||||
pendingChangesFrame = new GUIFrame(new RectTransform(Vector2.One, bottomDisclaimerFrame.RectTransform, Anchor.Center), style: null);
|
||||
PendingChangesFrame = new GUIFrame(new RectTransform(Vector2.One, bottomDisclaimerFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
if (GameMain.NetLobbyScreen?.CampaignCharacterDiscarded ?? false)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
|
||||
NetLobbyScreen.CreateChangesPendingFrame(PendingChangesFrame);
|
||||
}
|
||||
|
||||
SetBalanceText(balanceText, campaignMode.Bank.Balance);
|
||||
@@ -403,7 +397,7 @@ namespace Barotrauma
|
||||
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
|
||||
break;
|
||||
case InfoFrameTab.Talents:
|
||||
CreateCharacterInfo(infoFrameHolder);
|
||||
talentMenu.CreateGUI(infoFrameHolder, Character.Controlled ?? GameMain.Client?.Character);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -957,16 +951,26 @@ namespace Barotrauma
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
if (GameMain.Client is null)
|
||||
{
|
||||
GUIComponent preview = character.Info.CreateInfoFrame(background, false, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUIComponent preview = character.Info.CreateInfoFrame(background, false, GetPermissionIcon(GameMain.Client.ConnectedClients.Find(c => c.Character == character)));
|
||||
|
||||
GameMain.Client.SelectCrewCharacter(character, preview);
|
||||
if (!character.IsBot && GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign) { CreateWalletFrame(background, character, mpCampaign); }
|
||||
}
|
||||
|
||||
if (background.FindChild(TalentMenu.ManageBotTalentsButtonUserData, recursive: true) is GUIButton { Enabled: true } talentButton)
|
||||
{
|
||||
talentButton.OnClicked = (button, o) =>
|
||||
{
|
||||
talentMenu.CreateGUI(infoFrameHolder, character);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (client != null)
|
||||
{
|
||||
@@ -1494,27 +1498,9 @@ namespace Barotrauma
|
||||
AbsoluteSpacing = GUI.IntScale(10)
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Name, font: GUIStyle.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
|
||||
TextManager.Get("Biome", "location"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), Level.Loaded.LevelData.Biome.DisplayName, textAlignment: Alignment.CenterRight);
|
||||
var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
|
||||
TextManager.Get("LevelDifficulty"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), ((int)Level.Loaded.LevelData.Difficulty) + " %", textAlignment: Alignment.CenterRight);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionFrameContent.RectTransform) { AbsoluteOffset = new Point(0, locationInfoContainer.Rect.Height + padding) }, style: "HorizontalLine")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
int locationInfoYOffset = locationInfoContainer.Rect.Height + padding * 2;
|
||||
|
||||
Sprite portrait = location.Type.GetPortrait(location.PortraitId);
|
||||
bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0;
|
||||
int contentWidth = missionFrameContent.Rect.Width;
|
||||
|
||||
if (hasPortrait)
|
||||
{
|
||||
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
|
||||
@@ -1526,6 +1512,30 @@ namespace Barotrauma
|
||||
portraitImage.RectTransform.NonScaledSize = new Point(Math.Min((int)(portraitImage.Rect.Size.Y * portraitAspectRatio), portraitImage.Rect.Width), portraitImage.Rect.Size.Y);
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Name, font: GUIStyle.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
if (location.Faction?.Prefab != null)
|
||||
{
|
||||
var factionLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
|
||||
TextManager.Get("Faction"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), factionLabel.RectTransform), location.Faction.Prefab.Name, textAlignment: Alignment.CenterRight);
|
||||
}
|
||||
var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
|
||||
TextManager.Get("Biome", "location"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), Level.Loaded.LevelData.Biome.DisplayName, textAlignment: Alignment.CenterRight);
|
||||
var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
|
||||
TextManager.Get("LevelDifficulty"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), TextManager.GetWithVariable("percentageformat", "[value]", ((int)Level.Loaded.LevelData.Difficulty).ToString()), textAlignment: Alignment.CenterRight);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionFrameContent.RectTransform) { AbsoluteOffset = new Point(0, locationInfoContainer.Rect.Height + padding) }, style: "HorizontalLine")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
int locationInfoYOffset = locationInfoContainer.Rect.Height + padding * 2;
|
||||
|
||||
|
||||
GUIListBox missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrameContent.Rect.Height - locationInfoYOffset), missionFrameContent.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
missionList.ContentBackground.Color = Color.Transparent;
|
||||
missionList.Spacing = GUI.IntScale(15);
|
||||
@@ -1537,6 +1547,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
if (!mission.Prefab.ShowInMenus) { continue; }
|
||||
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(iconSize + spacing, 0) }, false, childAnchor: Anchor.TopLeft)
|
||||
{
|
||||
@@ -1774,373 +1785,10 @@ namespace Barotrauma
|
||||
sub.Info.CreateSpecsWindow(specsListBox, GUIStyle.Font, includeTitle: false, includeClass: false, includeDescription: true);
|
||||
}
|
||||
}
|
||||
private Color unselectedColor = new Color(240, 255, 255, 225);
|
||||
private Color unselectableColor = new Color(100, 100, 100, 225);
|
||||
private Color pressedColor = new Color(60, 60, 60, 225);
|
||||
|
||||
private readonly List<(GUIButton button, GUIComponent icon)> talentButtons = new List<(GUIButton button, GUIComponent icon)>();
|
||||
private readonly List<(Identifier talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)> talentCornerIcons = new List<(Identifier talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)>();
|
||||
private List<Identifier> selectedTalents = new List<Identifier>();
|
||||
|
||||
private GUITextBlock experienceText;
|
||||
private GUIProgressBar experienceBar;
|
||||
private GUITextBlock talentPointText;
|
||||
private GUIListBox skillListBox;
|
||||
|
||||
private GUIButton talentApplyButton,
|
||||
talentResetButton;
|
||||
|
||||
private GUIImage talentPointNotification;
|
||||
|
||||
private readonly ImmutableDictionary<TalentTree.TalentTreeStageState, GUIComponentStyle> talentStageStyles = new Dictionary<TalentTree.TalentTreeStageState, GUIComponentStyle>
|
||||
{
|
||||
{ TalentTree.TalentTreeStageState.Invalid, GUIStyle.GetComponentStyle("TalentTreeLocked") },
|
||||
{ TalentTree.TalentTreeStageState.Locked, GUIStyle.GetComponentStyle("TalentTreeLocked") },
|
||||
{ TalentTree.TalentTreeStageState.Unlocked, GUIStyle.GetComponentStyle("TalentTreePurchased") },
|
||||
{ TalentTree.TalentTreeStageState.Available, GUIStyle.GetComponentStyle("TalentTreeUnlocked") },
|
||||
{ TalentTree.TalentTreeStageState.Highlighted, GUIStyle.GetComponentStyle("TalentTreeAvailable") },
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private readonly ImmutableDictionary<TalentTree.TalentTreeStageState, Color> talentStageBackgroundColors = new Dictionary<TalentTree.TalentTreeStageState, Color>
|
||||
{
|
||||
{ TalentTree.TalentTreeStageState.Invalid, new Color(48,48,48,255) },
|
||||
{ TalentTree.TalentTreeStageState.Locked, new Color(48,48,48,255) },
|
||||
{ TalentTree.TalentTreeStageState.Unlocked, new Color(24,37,31,255) },
|
||||
{ TalentTree.TalentTreeStageState.Available, new Color(50,47,33,255) },
|
||||
{ TalentTree.TalentTreeStageState.Highlighted, new Color(50,47,33,255) },
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private void CreateCharacterInfo(GUIFrame infoFrame)
|
||||
{
|
||||
infoFrame.ClearChildren();
|
||||
talentButtons.Clear();
|
||||
talentCornerIcons.Clear();
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
int padding = GUI.IntScale(15);
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUIFrame content = new GUIFrame(new RectTransform(new Vector2(0.98f), frame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUIFrame characterSettingsFrame = null;
|
||||
GUILayoutGroup characterLayout = null;
|
||||
if (!(GameMain.NetworkMember is null))
|
||||
{
|
||||
characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, frame.RectTransform), style: null) { Visible = false };
|
||||
characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
|
||||
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
|
||||
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null);
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
}
|
||||
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
CharacterInfo info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo;
|
||||
if (info == null) { return; }
|
||||
|
||||
Job job = info.Job;
|
||||
|
||||
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), content.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(10),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), contentLayout.RectTransform, Anchor.Center), isHorizontal: true);
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), topLayout.RectTransform), onDraw: (batch, component) =>
|
||||
{
|
||||
float posY = component.Rect.Center.Y - component.Rect.Width / 2;
|
||||
info.DrawPortrait(batch, new Vector2(component.Rect.X, posY), Vector2.Zero, component.Rect.Width, false, false);
|
||||
});
|
||||
|
||||
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), topLayout.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
if (!info.OmitJobInMenus)
|
||||
{
|
||||
nameBlock.TextColor = job.Prefab.UIColor;
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
|
||||
}
|
||||
|
||||
if (info.PersonalityTrait != null)
|
||||
{
|
||||
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), info.PersonalityTrait.DisplayName);
|
||||
Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString);
|
||||
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
|
||||
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
|
||||
}
|
||||
|
||||
IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
|
||||
if (talentsOutsideTree.Count() > 0)
|
||||
{
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), nameLayout.RectTransform), style: null);
|
||||
|
||||
GUILayoutGroup extraTalentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), nameLayout.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), extraTalentLayout.RectTransform, anchor: Anchor.Center), TextManager.Get("talentmenu.extratalents"), font: GUIStyle.SubHeadingFont);
|
||||
talentPointText.RectTransform.MaxSize = new Point(int.MaxValue, (int)talentPointText.TextSize.Y);
|
||||
|
||||
var extraTalentList = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.8f), extraTalentLayout.RectTransform, anchor: Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AutoHideScrollBar = false,
|
||||
ResizeContentToMakeSpaceForScrollBar = false
|
||||
};
|
||||
extraTalentList.ScrollBar.RectTransform.SetPosition(Anchor.BottomCenter, Pivot.TopCenter);
|
||||
extraTalentList.RectTransform.MinSize = new Point(0, GUI.IntScale(65));
|
||||
extraTalentLayout.Recalculate();
|
||||
extraTalentList.ForceLayoutRecalculation();
|
||||
|
||||
foreach (var extraTalent in talentsOutsideTree)
|
||||
{
|
||||
var img = new GUIImage(new RectTransform(new Point(extraTalentList.Content.Rect.Height), extraTalentList.Content.RectTransform), sprite: extraTalent.Icon, scaleToFit: true)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + extraTalent.Description),
|
||||
Color = GUIStyle.Green
|
||||
};
|
||||
img.RectTransform.SizeChanged += () =>
|
||||
{
|
||||
img.RectTransform.MaxSize = new Point(img.Rect.Height);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
GUILayoutGroup skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), topLayout.RectTransform), childAnchor: Anchor.TopRight)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillLayout.RectTransform), TextManager.Get("skills"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
|
||||
CreateSkillList(controlledCharacter, info, skillListBox);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
|
||||
|
||||
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||
|
||||
if (controlledCharacter == null)
|
||||
{
|
||||
talentTreeListBox.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
|
||||
|
||||
selectedTalents = info.GetUnlockedTalentsInTree().ToList();
|
||||
|
||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||
foreach (var subTree in talentTree.TalentSubTrees)
|
||||
{
|
||||
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
|
||||
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
|
||||
|
||||
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
|
||||
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
|
||||
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center));
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
|
||||
|
||||
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
|
||||
|
||||
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground")
|
||||
{
|
||||
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
|
||||
};
|
||||
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
|
||||
|
||||
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
|
||||
};
|
||||
|
||||
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
|
||||
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
|
||||
|
||||
if (subTree.TalentOptionStages.Length <= i) { continue; }
|
||||
|
||||
TalentOption talentOption = subTree.TalentOptionStages[i];
|
||||
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.7f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
|
||||
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
|
||||
foreach (Identifier talentId in talentOption.TalentIdentifiers.OrderBy(t => t))
|
||||
{
|
||||
if (!TalentPrefab.TalentPrefabs.TryGet(talentId, out TalentPrefab talent)) { continue; }
|
||||
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentOptionLayoutGroup.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
|
||||
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖" + "\n\n" + talent.Description),
|
||||
UserData = talent.Identifier,
|
||||
PressedColor = pressedColor,
|
||||
Enabled = controlledCharacter != null,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
// deselect other buttons in tier by removing their selected talents from pool
|
||||
foreach (GUIButton guiButton in talentOptionLayoutGroup.GetAllChildren<GUIButton>())
|
||||
{
|
||||
if (guiButton.UserData is Identifier otherTalentIdentifier && guiButton != button)
|
||||
{
|
||||
if (!controlledCharacter.HasTalent(otherTalentIdentifier))
|
||||
{
|
||||
selectedTalents.Remove(otherTalentIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
Identifier talentIdentifier = (Identifier)userData;
|
||||
|
||||
if (TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents))
|
||||
{
|
||||
if (!selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Add(talentIdentifier);
|
||||
}
|
||||
}
|
||||
else if (!controlledCharacter.HasTalent(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent;
|
||||
|
||||
GUIComponent iconImage;
|
||||
if (talent.Icon is null)
|
||||
{
|
||||
iconImage = new GUITextBlock(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), text: "???", font: GUIStyle.LargeFont, textAlignment: Alignment.Center, style: null)
|
||||
{
|
||||
OutlineColor = GUIStyle.Red,
|
||||
TextColor = GUIStyle.Red,
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
|
||||
{
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor * 0.5f,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
iconImage.Enabled = talentButton.Enabled;
|
||||
talentButtons.Add((talentButton, iconImage));
|
||||
}
|
||||
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
|
||||
}
|
||||
}
|
||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||
|
||||
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), contentLayout.RectTransform, Anchor.TopCenter), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.01f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), bottomLayout.RectTransform));
|
||||
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
|
||||
|
||||
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
|
||||
barSize: info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
|
||||
{
|
||||
IsHorizontal = true,
|
||||
};
|
||||
|
||||
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Shadow = true,
|
||||
ToolTip = TextManager.Get("experiencetooltip")
|
||||
};
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
|
||||
|
||||
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ResetTalentSelection
|
||||
};
|
||||
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ApplyTalentSelection,
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
|
||||
}
|
||||
|
||||
if (!(GameMain.NetworkMember is null))
|
||||
{
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
{
|
||||
IgnoreLayoutGroups = false
|
||||
};
|
||||
newCharacterBox.TextBlock.AutoScaleHorizontal = true;
|
||||
|
||||
newCharacterBox.OnClicked = (button, o) =>
|
||||
{
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
|
||||
{
|
||||
newCharacterBox.Text = TextManager.Get("settings");
|
||||
if (pendingChangesFrame != null)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
|
||||
}
|
||||
OpenMenu();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
return true;
|
||||
|
||||
void OpenMenu()
|
||||
{
|
||||
characterSettingsFrame!.Visible = true;
|
||||
content.Visible = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (!(characterLayout is null))
|
||||
{
|
||||
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomCenter);
|
||||
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
|
||||
characterSettingsFrame!.Visible = false;
|
||||
content.Visible = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
}
|
||||
|
||||
private void CreateSkillList(Character character, CharacterInfo info, GUIListBox parent)
|
||||
public static void CreateSkillList(Character character, CharacterInfo info, GUIListBox parent)
|
||||
{
|
||||
parent.Content.ClearChildren();
|
||||
List<GUITextBlock> skillNames = new List<GUITextBlock>();
|
||||
@@ -2154,10 +1802,10 @@ namespace Barotrauma
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.TopRight);
|
||||
|
||||
float modifiedSkillLevel = character?.GetSkillLevel(skill.Identifier) ?? skill.Level;
|
||||
float modifiedSkillLevel = MathF.Floor(character?.GetSkillLevel(skill.Identifier) ?? skill.Level);
|
||||
if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level)))
|
||||
{
|
||||
int skillChange = (int)MathF.Floor(modifiedSkillLevel - skill.Level);
|
||||
int skillChange = (int)MathF.Floor(modifiedSkillLevel - MathF.Floor(skill.Level));
|
||||
string stringColor = skillChange switch
|
||||
{
|
||||
> 0 => XMLExtensions.ToStringHex(GUIStyle.Green),
|
||||
@@ -2168,123 +1816,17 @@ namespace Barotrauma
|
||||
RichString changeText = RichString.Rich($"(‖color:{stringColor}‖{(skillChange > 0 ? "+" : string.Empty) + skillChange}‖color:end‖)");
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), changeText) { Padding = Vector4.Zero };
|
||||
}
|
||||
//skillContainer.Recalculate();
|
||||
skillContainer.Recalculate();
|
||||
}
|
||||
|
||||
parent.RecalculateChildren();
|
||||
GUITextBlock.AutoScaleAndNormalize(skillNames);
|
||||
}
|
||||
|
||||
private void UpdateTalentInfo()
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter?.Info == null) { return; }
|
||||
|
||||
if (SelectedTab != InfoFrameTab.Talents) { return; }
|
||||
|
||||
bool unlockedAllTalents = controlledCharacter.HasUnlockedAllTalents();
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
experienceText.Text = string.Empty;
|
||||
experienceBar.BarSize = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
experienceText.Text = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
|
||||
experienceBar.BarSize = controlledCharacter.Info.GetProgressTowardsNextLevel();
|
||||
}
|
||||
|
||||
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
|
||||
|
||||
string pointsLeft = controlledCharacter.Info.GetAvailableTalentPoints().ToString();
|
||||
|
||||
int talentCount = selectedTalents.Count - controlledCharacter.Info.GetUnlockedTalentsInTree().Count();
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
talentPointText.SetRichText($"‖color:{XMLExtensions.ToStringHex(Color.Gray)}‖{TextManager.Get("talentmenu.alltalentsunlocked")}‖color:end‖");
|
||||
}
|
||||
else if (talentCount > 0)
|
||||
{
|
||||
string pointsUsed = $"‖color:{XMLExtensions.ColorToString(GUIStyle.Red)}‖{-talentCount}‖color:end‖";
|
||||
LocalizedString localizedString = TextManager.GetWithVariables("talentmenu.points.spending", ("[amount]", pointsLeft), ("[used]", pointsUsed));
|
||||
talentPointText.SetRichText(localizedString);
|
||||
}
|
||||
else
|
||||
{
|
||||
talentPointText.SetRichText(TextManager.GetWithVariable("talentmenu.points", "[amount]", pointsLeft));
|
||||
}
|
||||
|
||||
foreach (var (talentTree, index, icon, frame, glow) in talentCornerIcons)
|
||||
{
|
||||
TalentTree.TalentTreeStageState state = TalentTree.GetTalentOptionStageState(controlledCharacter, talentTree, index, selectedTalents);
|
||||
GUIComponentStyle newStyle = talentStageStyles[state];
|
||||
icon.ApplyStyle(newStyle);
|
||||
icon.Color = newStyle.Color;
|
||||
frame.Color = talentStageBackgroundColors[state];
|
||||
glow.Visible = state == TalentTree.TalentTreeStageState.Highlighted;
|
||||
}
|
||||
|
||||
foreach (var talentButton in talentButtons)
|
||||
{
|
||||
Identifier talentIdentifier = (Identifier)talentButton.button.UserData;
|
||||
bool unselectable = !TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents) || controlledCharacter.HasTalent(talentIdentifier);
|
||||
Color newTalentColor = unselectable ? unselectableColor : unselectedColor;
|
||||
Color hoverColor = Color.White;
|
||||
|
||||
if (controlledCharacter.HasTalent(talentIdentifier))
|
||||
{
|
||||
newTalentColor = GUIStyle.Green;
|
||||
}
|
||||
else if (selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
newTalentColor = GUIStyle.Orange;
|
||||
hoverColor = Color.Lerp(GUIStyle.Orange, Color.White, 0.7f);
|
||||
}
|
||||
|
||||
talentButton.icon.Color = newTalentColor;
|
||||
talentButton.icon.HoverColor = hoverColor;
|
||||
}
|
||||
|
||||
CreateSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
|
||||
}
|
||||
|
||||
private void ApplyTalents(Character controlledCharacter)
|
||||
{
|
||||
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
|
||||
foreach (Identifier talent in selectedTalents)
|
||||
{
|
||||
controlledCharacter.GiveTalent(talent);
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(controlledCharacter, new Character.UpdateTalentsEventData());
|
||||
}
|
||||
}
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
UpdateTalentInfo();
|
||||
}
|
||||
|
||||
private bool ApplyTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
ApplyTalents(controlledCharacter);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter?.Info == null) { return false; }
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnExperienceChanged(Character character)
|
||||
{
|
||||
if (character != Character.Controlled) { return; }
|
||||
UpdateTalentInfo();
|
||||
talentMenu.UpdateTalentInfo();
|
||||
}
|
||||
|
||||
public void OnClose()
|
||||
|
||||
805
Barotrauma/BarotraumaClient/ClientSource/GUI/TalentMenu.cs
Normal file
805
Barotrauma/BarotraumaClient/ClientSource/GUI/TalentMenu.cs
Normal file
@@ -0,0 +1,805 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using static Barotrauma.TalentTree;
|
||||
using static Barotrauma.TalentTree.TalentStages;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal readonly record struct TalentShowCaseButton(ImmutableHashSet<TalentButton> Buttons,
|
||||
GUIComponent IconComponent);
|
||||
|
||||
internal readonly record struct TalentButton(GUIComponent IconComponent,
|
||||
TalentPrefab Prefab)
|
||||
{
|
||||
public Identifier Identifier => Prefab.Identifier;
|
||||
}
|
||||
|
||||
internal readonly record struct TalentCornerIcon(Identifier TalentTree,
|
||||
int Index,
|
||||
GUIImage IconComponent,
|
||||
GUIFrame BackgroundComponent,
|
||||
GUIFrame GlowComponent);
|
||||
|
||||
internal readonly struct TalentTreeStyle
|
||||
{
|
||||
public readonly GUIComponentStyle ComponentStyle;
|
||||
public readonly Color Color;
|
||||
|
||||
public TalentTreeStyle(string componentStyle, Color color)
|
||||
{
|
||||
ComponentStyle = GUIStyle.GetComponentStyle(componentStyle);
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TalentMenu
|
||||
{
|
||||
public const string ManageBotTalentsButtonUserData = "managebottalentsbutton";
|
||||
|
||||
private Character? character;
|
||||
private CharacterInfo? characterInfo;
|
||||
|
||||
private static readonly Color unselectedColor = new Color(240, 255, 255, 225),
|
||||
unselectableColor = new Color(100, 100, 100, 225),
|
||||
pressedColor = new Color(60, 60, 60, 225),
|
||||
lockedColor = new Color(48, 48, 48, 255),
|
||||
unlockedColor = new Color(24, 37, 31, 255),
|
||||
availableColor = new Color(50, 47, 33, 255);
|
||||
|
||||
private static readonly ImmutableDictionary<TalentStages, TalentTreeStyle> talentStageStyles =
|
||||
new Dictionary<TalentStages, TalentTreeStyle>
|
||||
{
|
||||
[Invalid] = new TalentTreeStyle("TalentTreeLocked", lockedColor),
|
||||
[Locked] = new TalentTreeStyle("TalentTreeLocked", lockedColor),
|
||||
[Unlocked] = new TalentTreeStyle("TalentTreePurchased", unlockedColor),
|
||||
[Available] = new TalentTreeStyle("TalentTreeUnlocked", availableColor),
|
||||
[Highlighted] = new TalentTreeStyle("TalentTreeAvailable", availableColor)
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private readonly HashSet<TalentButton> talentButtons = new HashSet<TalentButton>();
|
||||
private readonly HashSet<TalentShowCaseButton> talentShowCaseButtons = new HashSet<TalentShowCaseButton>();
|
||||
private readonly HashSet<GUIComponent> showCaseTalentFrames = new HashSet<GUIComponent>();
|
||||
private readonly HashSet<TalentCornerIcon> talentCornerIcons = new HashSet<TalentCornerIcon>();
|
||||
private HashSet<Identifier> selectedTalents = new HashSet<Identifier>();
|
||||
|
||||
private readonly Queue<Identifier> showCaseClosureQueue = new();
|
||||
|
||||
private GUIListBox? skillListBox;
|
||||
private GUITextBlock? talentPointText;
|
||||
private GUIProgressBar? experienceBar;
|
||||
private GUITextBlock? experienceText;
|
||||
private GUILayoutGroup? skillLayout;
|
||||
|
||||
private GUIButton? talentApplyButton,
|
||||
talentResetButton;
|
||||
|
||||
public void CreateGUI(GUIFrame parent, Character? targetCharacter)
|
||||
{
|
||||
parent.ClearChildren();
|
||||
talentButtons.Clear();
|
||||
talentShowCaseButtons.Clear();
|
||||
talentCornerIcons.Clear();
|
||||
showCaseTalentFrames.Clear();
|
||||
|
||||
character = targetCharacter;
|
||||
characterInfo = targetCharacter?.Info;
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
int padding = GUI.IntScale(15);
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(background.Rect.Width - padding, background.Rect.Height - padding), parent.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUIFrame content = new GUIFrame(new RectTransform(new Vector2(0.98f), frame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
GUILayoutGroup contentLayout = new GUILayoutGroup(new RectTransform(Vector2.One, content.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(10),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
if (characterInfo is null) { return; }
|
||||
|
||||
CreateStatPanel(contentLayout, characterInfo);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1f, 1f), contentLayout.RectTransform), style: "HorizontalLine");
|
||||
|
||||
if (JobTalentTrees.TryGet(characterInfo.Job.Prefab.Identifier, out TalentTree? talentTree))
|
||||
{
|
||||
CreateTalentMenu(contentLayout, characterInfo, talentTree!);
|
||||
}
|
||||
|
||||
CreateFooter(contentLayout, characterInfo);
|
||||
UpdateTalentInfo();
|
||||
|
||||
if (GameMain.NetworkMember != null && IsOwnCharacter(characterInfo))
|
||||
{
|
||||
CreateMultiplayerCharacterSettings(frame, content);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateMultiplayerCharacterSettings(GUIComponent parent, GUIComponent content)
|
||||
{
|
||||
if (skillLayout is null) { return; }
|
||||
|
||||
GUIFrame characterSettingsFrame = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null) { Visible = false };
|
||||
GUILayoutGroup characterLayout = new GUILayoutGroup(new RectTransform(Vector2.One, characterSettingsFrame.RectTransform));
|
||||
GUIFrame containerFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.9f), characterLayout.RectTransform), style: null);
|
||||
GUIFrame playerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.7f), containerFrame.RectTransform, Anchor.Center), style: null);
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), skillLayout.RectTransform, Anchor.BottomRight),
|
||||
text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"), style: "GUIButtonSmall")
|
||||
{
|
||||
IgnoreLayoutGroups = false,
|
||||
TextBlock =
|
||||
{
|
||||
AutoScaleHorizontal = true
|
||||
}
|
||||
};
|
||||
|
||||
newCharacterBox.OnClicked = (button, o) =>
|
||||
{
|
||||
if (!GameMain.NetLobbyScreen.CampaignCharacterDiscarded)
|
||||
{
|
||||
GameMain.NetLobbyScreen.TryDiscardCampaignCharacter(() =>
|
||||
{
|
||||
newCharacterBox.Text = TextManager.Get("settings");
|
||||
if (TabMenu.PendingChangesFrame != null)
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(TabMenu.PendingChangesFrame);
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenMenu();
|
||||
return true;
|
||||
|
||||
void OpenMenu()
|
||||
{
|
||||
characterSettingsFrame!.Visible = true;
|
||||
content.Visible = false;
|
||||
}
|
||||
};
|
||||
|
||||
GUILayoutGroup characterCloseButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), characterLayout.RectTransform), childAnchor: Anchor.BottomCenter);
|
||||
new GUIButton(new RectTransform(new Vector2(0.4f, 1f), characterCloseButtonLayout.RectTransform), TextManager.Get("ApplySettingsButton")) //TODO: Is this text appropriate for this circumstance for all languages?
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
GameMain.Client?.SendCharacterInfo(GameMain.Client.PendingName);
|
||||
characterSettingsFrame.Visible = false;
|
||||
content.Visible = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateStatPanel(GUIComponent parent, CharacterInfo info)
|
||||
{
|
||||
Job job = info.Job;
|
||||
|
||||
GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform, Anchor.Center), isHorizontal: true);
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), topLayout.RectTransform), onDraw: (batch, component) =>
|
||||
{
|
||||
info.DrawPortrait(batch, component.Rect.Location.ToVector2(), Vector2.Zero, component.Rect.Width, false, false);
|
||||
});
|
||||
|
||||
GUILayoutGroup nameLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1f), topLayout.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), info.Name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
if (!info.OmitJobInMenus)
|
||||
{
|
||||
nameBlock.TextColor = job.Prefab.UIColor;
|
||||
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), nameLayout.RectTransform), job.Name, font: GUIStyle.SmallFont) { TextColor = job.Prefab.UIColor };
|
||||
}
|
||||
|
||||
if (info.PersonalityTrait != null)
|
||||
{
|
||||
LocalizedString traitString = TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), info.PersonalityTrait.DisplayName);
|
||||
Vector2 traitSize = GUIStyle.SmallFont.MeasureString(traitString);
|
||||
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
|
||||
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
|
||||
}
|
||||
|
||||
ImmutableHashSet<TalentPrefab?> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(static e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e)).ToImmutableHashSet();
|
||||
if (talentsOutsideTree.Any())
|
||||
{
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), nameLayout.RectTransform), style: null);
|
||||
|
||||
GUILayoutGroup extraTalentLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.55f), nameLayout.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), extraTalentLayout.RectTransform, anchor: Anchor.Center), TextManager.Get("talentmenu.extratalents"), font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
AutoScaleVertical = true
|
||||
};
|
||||
talentPointText.RectTransform.MaxSize = new Point(int.MaxValue, (int)talentPointText.TextSize.Y);
|
||||
|
||||
var extraTalentList = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.7f), extraTalentLayout.RectTransform, anchor: Anchor.Center), isHorizontal: true)
|
||||
{
|
||||
AutoHideScrollBar = false,
|
||||
ResizeContentToMakeSpaceForScrollBar = false
|
||||
};
|
||||
extraTalentList.ScrollBar.RectTransform.SetPosition(Anchor.BottomCenter, Pivot.TopCenter);
|
||||
extraTalentLayout.Recalculate();
|
||||
extraTalentList.ForceLayoutRecalculation();
|
||||
|
||||
foreach (var extraTalent in talentsOutsideTree)
|
||||
{
|
||||
if (extraTalent is null) { continue; }
|
||||
GUIImage talentImg = new GUIImage(new RectTransform(Vector2.One, extraTalentList.Content.RectTransform, scaleBasis: ScaleBasis.BothHeight), sprite: extraTalent.Icon, scaleToFit: true)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{extraTalent.DisplayName}‖color:end‖" + "\n\n" + ToolBox.ExtendColorToPercentageSigns(extraTalent.Description.Value)),
|
||||
Color = GUIStyle.Green
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
skillLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1f), topLayout.RectTransform), childAnchor: Anchor.TopRight)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUITextBlock skillBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillLayout.RectTransform), TextManager.Get("skills"), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
|
||||
TabMenu.CreateSkillList(info.Character, info, skillListBox);
|
||||
}
|
||||
|
||||
private void CreateTalentMenu(GUIComponent parent, CharacterInfo info, TalentTree tree)
|
||||
{
|
||||
GUIListBox mainList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), parent.RectTransform, anchor: Anchor.TopCenter));
|
||||
|
||||
selectedTalents = info.GetUnlockedTalentsInTree().ToHashSet();
|
||||
|
||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||
foreach (var subTree in tree.TalentSubTrees)
|
||||
{
|
||||
GUIListBox talentList;
|
||||
GUIComponent talentParent;
|
||||
Vector2 treeSize;
|
||||
switch (subTree.Type)
|
||||
{
|
||||
case TalentTreeType.Primary:
|
||||
talentList = mainList;
|
||||
treeSize = new Vector2(1f, 0.5f);
|
||||
break;
|
||||
case TalentTreeType.Specialization:
|
||||
talentList = GetSpecializationList();
|
||||
treeSize = new Vector2(0.333f, 1f);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Invalid TalentTreeType \"{subTree.Type}\"");
|
||||
}
|
||||
talentParent = talentList.Content;
|
||||
|
||||
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(treeSize, talentParent.RectTransform), isHorizontal: false, childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
if (subTree.Type != TalentTreeType.Primary)
|
||||
{
|
||||
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.05f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter)
|
||||
{ MinSize = new Point(0, GUI.IntScale(30)) }, style: null);
|
||||
subtreeTitleFrame.RectTransform.IsFixedSize = true;
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
|
||||
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
|
||||
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center));
|
||||
}
|
||||
|
||||
int optionAmount = subTree.TalentOptionStages.Length;
|
||||
for (int i = 0; i < optionAmount; i++)
|
||||
{
|
||||
TalentOption option = subTree.TalentOptionStages[i];
|
||||
CreateTalentOption(subTreeLayoutGroup, subTree, i, option, info);
|
||||
}
|
||||
subTreeLayoutGroup.RectTransform.Resize(new Point(subTreeLayoutGroup.Rect.Width,
|
||||
subTreeLayoutGroup.Children.Sum(c => c.Rect.Height + subTreeLayoutGroup.AbsoluteSpacing)));
|
||||
subTreeLayoutGroup.RectTransform.MinSize = new Point(subTreeLayoutGroup.Rect.Width, subTreeLayoutGroup.Rect.Height);
|
||||
subTreeLayoutGroup.Recalculate();
|
||||
|
||||
if (subTree.Type == TalentTreeType.Specialization)
|
||||
{
|
||||
talentList.RectTransform.Resize(new Point(talentList.Rect.Width, Math.Max(subTreeLayoutGroup.Rect.Height, talentList.Rect.Height)));
|
||||
talentList.RectTransform.MinSize = new Point(0, talentList.Rect.Height);
|
||||
}
|
||||
}
|
||||
|
||||
var specializationList = GetSpecializationList();
|
||||
GetSpecializationList().RectTransform.Resize(new Point(specializationList.Content.Children.Sum(static c => c.Rect.Width), specializationList.Rect.Height));
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||
|
||||
GUIListBox GetSpecializationList()
|
||||
{
|
||||
if (mainList.Content.Children.LastOrDefault() is GUIListBox specList)
|
||||
{
|
||||
return specList;
|
||||
}
|
||||
|
||||
GUIListBox newSpecializationList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), mainList.Content.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||
return newSpecializationList;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info)
|
||||
{
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.01f), parent.RectTransform, anchor: Anchor.TopCenter)
|
||||
{ MinSize = new Point(0, GUI.IntScale(65)) }, style: null);
|
||||
|
||||
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
|
||||
|
||||
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center),
|
||||
style: "TalentBackground")
|
||||
{
|
||||
Color = talentStageStyles[Locked].Color
|
||||
};
|
||||
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
|
||||
|
||||
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = talentStageStyles[Locked].Color
|
||||
};
|
||||
|
||||
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
|
||||
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
|
||||
|
||||
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 0.9f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
|
||||
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
|
||||
HashSet<Identifier> talentOptionIdentifiers = talentOption.TalentIdentifiers.OrderBy(static t => t).ToHashSet();
|
||||
HashSet<TalentButton> buttonsToAdd = new();
|
||||
|
||||
Dictionary<GUILayoutGroup, ImmutableHashSet<Identifier>> showCaseTalentParents = new();
|
||||
Dictionary<Identifier, GUIComponent> showCaseTalentButtonsToAdd = new();
|
||||
|
||||
foreach (var (showCaseTalentIdentifier, talents) in talentOption.ShowCaseTalents)
|
||||
{
|
||||
talentOptionIdentifiers.Add(showCaseTalentIdentifier);
|
||||
Point parentSize = talentBackground.RectTransform.NonScaledSize;
|
||||
GUIFrame showCaseFrame = new GUIFrame(new RectTransform(new Point((int)(parentSize.X / 3f * (talents.Count - 1)), parentSize.Y)), style: "GUITooltip")
|
||||
{
|
||||
UserData = showCaseTalentIdentifier,
|
||||
IgnoreLayoutGroups = true,
|
||||
Visible = false
|
||||
};
|
||||
GUILayoutGroup showcaseCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.7f), showCaseFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
|
||||
GUILayoutGroup showcaseLayout = new GUILayoutGroup(new RectTransform(Vector2.One, showcaseCenterGroup.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
showCaseTalentParents.Add(showcaseLayout, talents);
|
||||
showCaseTalentFrames.Add(showCaseFrame);
|
||||
}
|
||||
|
||||
foreach (Identifier talentId in talentOptionIdentifiers)
|
||||
{
|
||||
if (!TalentPrefab.TalentPrefabs.TryGet(talentId, out TalentPrefab? talent)) { continue; }
|
||||
|
||||
bool isShowCaseTalent = talentOption.ShowCaseTalents.ContainsKey(talentId);
|
||||
GUIComponent talentParent = talentOptionLayoutGroup;
|
||||
|
||||
foreach (var (key, value) in showCaseTalentParents)
|
||||
{
|
||||
if (value.Contains(talentId))
|
||||
{
|
||||
talentParent = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentParent.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUIFrame croppedTalentFrame = new GUIFrame(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center, scaleBasis: ScaleBasis.BothHeight), style: null);
|
||||
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, croppedTalentFrame.RectTransform, anchor: Anchor.Center), style: null)
|
||||
{
|
||||
ToolTip = RichString.Rich($"‖color:{Color.White.ToStringHex()}‖{talent.DisplayName}‖color:end‖" + "\n\n" + ToolBox.ExtendColorToPercentageSigns(talent.Description.Value)),
|
||||
UserData = talent.Identifier,
|
||||
PressedColor = pressedColor,
|
||||
Enabled = info.Character != null,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (isShowCaseTalent)
|
||||
{
|
||||
foreach (GUIComponent component in showCaseTalentFrames)
|
||||
{
|
||||
if (component.UserData is Identifier showcaseIdentifier && showcaseIdentifier == talentId)
|
||||
{
|
||||
component.RectTransform.ScreenSpaceOffset = new Point((int)(button.Rect.Location.X - component.Rect.Width / 2f + button.Rect.Width / 2f), button.Rect.Location.Y - component.Rect.Height);
|
||||
component.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
component.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (character is null) { return false; }
|
||||
|
||||
if (talentOption.MaxChosenTalents is 1)
|
||||
{
|
||||
// deselect other buttons in tier by removing their selected talents from pool
|
||||
foreach (Identifier identifier in selectedTalents)
|
||||
{
|
||||
if (character.HasTalent(identifier) || identifier == talentId) { continue; }
|
||||
|
||||
if (talentOptionIdentifiers.Contains(identifier))
|
||||
{
|
||||
selectedTalents.Remove(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Identifier talentIdentifier = (Identifier)userData;
|
||||
|
||||
if (IsViableTalentForCharacter(info.Character, talentIdentifier, selectedTalents))
|
||||
{
|
||||
if (!selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Add(talentIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
}
|
||||
else if (!character.HasTalent(talentIdentifier))
|
||||
{
|
||||
selectedTalents.Remove(talentIdentifier);
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent;
|
||||
|
||||
GUIComponent iconImage;
|
||||
if (talent.Icon is null)
|
||||
{
|
||||
iconImage = new GUITextBlock(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), text: "???", font: GUIStyle.LargeFont, textAlignment: Alignment.Center, style: null)
|
||||
{
|
||||
OutlineColor = GUIStyle.Red,
|
||||
TextColor = GUIStyle.Red,
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
|
||||
{
|
||||
Color = talent.ColorOverride.TryUnwrap(out Color color) ? color : Color.White,
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor * 0.5f,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
|
||||
iconImage.Enabled = talentButton.Enabled;
|
||||
if (isShowCaseTalent)
|
||||
{
|
||||
showCaseTalentButtonsToAdd.Add(talentId, iconImage);
|
||||
continue;
|
||||
}
|
||||
|
||||
buttonsToAdd.Add(new TalentButton(iconImage, talent));
|
||||
}
|
||||
|
||||
foreach (TalentButton button in buttonsToAdd)
|
||||
{
|
||||
talentButtons.Add(button);
|
||||
}
|
||||
|
||||
foreach (var (key, value) in showCaseTalentButtonsToAdd)
|
||||
{
|
||||
HashSet<TalentButton> buttons = new();
|
||||
foreach (Identifier identifier in talentOption.ShowCaseTalents[key])
|
||||
{
|
||||
if (talentButtons.FirstOrNull(talentButton => talentButton.Identifier == identifier) is not { } button) { continue; }
|
||||
|
||||
buttons.Add(button);
|
||||
}
|
||||
|
||||
talentShowCaseButtons.Add(new TalentShowCaseButton(buttons.ToImmutableHashSet(), value));
|
||||
}
|
||||
|
||||
talentCornerIcons.Add(new TalentCornerIcon(subTree.Identifier, index, cornerIcon, talentBackground, talentBackgroundHighlight));
|
||||
}
|
||||
|
||||
private void CreateFooter(GUIComponent parent, CharacterInfo info)
|
||||
{
|
||||
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), parent.RectTransform, Anchor.TopCenter), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.01f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), bottomLayout.RectTransform));
|
||||
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
|
||||
|
||||
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
|
||||
barSize: info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
|
||||
{
|
||||
IsHorizontal = true,
|
||||
};
|
||||
|
||||
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Shadow = true,
|
||||
ToolTip = TextManager.Get("experiencetooltip")
|
||||
};
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight)
|
||||
{ AutoScaleVertical = true };
|
||||
|
||||
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ResetTalentSelection
|
||||
};
|
||||
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), bottomLayout.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ApplyTalentSelection,
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
|
||||
}
|
||||
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
if (characterInfo is null) { return false; }
|
||||
selectedTalents = characterInfo.GetUnlockedTalentsInTree().ToHashSet();
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ApplyTalents(Character controlledCharacter)
|
||||
{
|
||||
foreach (Identifier talent in CheckTalentSelection(controlledCharacter, selectedTalents))
|
||||
{
|
||||
controlledCharacter.GiveTalent(talent);
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(controlledCharacter, new Character.UpdateTalentsEventData());
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
}
|
||||
|
||||
private bool ApplyTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
if (character is null) { return false; }
|
||||
|
||||
ApplyTalents(character);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateTalentInfo()
|
||||
{
|
||||
if (character is null || characterInfo is null) { return; }
|
||||
|
||||
bool unlockedAllTalents = character.HasUnlockedAllTalents();
|
||||
|
||||
if (experienceBar is null || experienceText is null) { return; }
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
experienceText.Text = string.Empty;
|
||||
experienceBar.BarSize = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
experienceText.Text = $"{characterInfo.ExperiencePoints - characterInfo.GetExperienceRequiredForCurrentLevel()} / {characterInfo.GetExperienceRequiredToLevelUp() - characterInfo.GetExperienceRequiredForCurrentLevel()}";
|
||||
experienceBar.BarSize = characterInfo.GetProgressTowardsNextLevel();
|
||||
}
|
||||
|
||||
selectedTalents = CheckTalentSelection(character, selectedTalents).ToHashSet();
|
||||
|
||||
string pointsLeft = characterInfo.GetAvailableTalentPoints().ToString();
|
||||
|
||||
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
|
||||
if (unlockedAllTalents)
|
||||
{
|
||||
talentPointText?.SetRichText($"‖color:{Color.Gray.ToStringHex()}‖{TextManager.Get("talentmenu.alltalentsunlocked")}‖color:end‖");
|
||||
}
|
||||
else if (talentCount > 0)
|
||||
{
|
||||
string pointsUsed = $"‖color:{XMLExtensions.ToStringHex(GUIStyle.Red)}‖{-talentCount}‖color:end‖";
|
||||
LocalizedString localizedString = TextManager.GetWithVariables("talentmenu.points.spending", ("[amount]", pointsLeft), ("[used]", pointsUsed));
|
||||
talentPointText?.SetRichText(localizedString);
|
||||
}
|
||||
else
|
||||
{
|
||||
talentPointText?.SetRichText(TextManager.GetWithVariable("talentmenu.points", "[amount]", pointsLeft));
|
||||
}
|
||||
|
||||
foreach (TalentCornerIcon cornerIcon in talentCornerIcons)
|
||||
{
|
||||
TalentStages state = GetTalentOptionStageState(character, cornerIcon.TalentTree, cornerIcon.Index, selectedTalents);
|
||||
TalentTreeStyle style = talentStageStyles[state];
|
||||
GUIComponentStyle newStyle = style.ComponentStyle;
|
||||
cornerIcon.IconComponent.ApplyStyle(newStyle);
|
||||
cornerIcon.IconComponent.Color = newStyle.Color;
|
||||
cornerIcon.BackgroundComponent.Color = style.Color;
|
||||
cornerIcon.GlowComponent.Visible = state == Highlighted;
|
||||
}
|
||||
|
||||
foreach (TalentButton talentButton in talentButtons)
|
||||
{
|
||||
TalentStages stage = GetTalentState(character, talentButton.Identifier, selectedTalents);
|
||||
ApplyTalentIconColor(stage, talentButton.IconComponent, talentButton.Prefab.ColorOverride);
|
||||
}
|
||||
|
||||
foreach (TalentShowCaseButton showCaseTalentButton in talentShowCaseButtons)
|
||||
{
|
||||
TalentStages collectiveTalentStage = GetCollectiveTalentState(character, showCaseTalentButton.Buttons, selectedTalents);
|
||||
ApplyTalentIconColor(collectiveTalentStage, showCaseTalentButton.IconComponent, Option<Color>.None());
|
||||
}
|
||||
|
||||
if (skillListBox is null) { return; }
|
||||
|
||||
TabMenu.CreateSkillList(character, characterInfo, skillListBox);
|
||||
|
||||
static TalentStages GetTalentState(Character character, Identifier talentIdentifier, IReadOnlyCollection<Identifier> selectedTalents)
|
||||
{
|
||||
bool unselectable = !IsViableTalentForCharacter(character, talentIdentifier, selectedTalents) || character.HasTalent(talentIdentifier);
|
||||
TalentStages stage = unselectable ? Locked : Available;
|
||||
if (unselectable)
|
||||
{
|
||||
stage = Locked;
|
||||
}
|
||||
|
||||
if (character.HasTalent(talentIdentifier))
|
||||
{
|
||||
stage = Unlocked;
|
||||
}
|
||||
else if (selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
stage = Highlighted;
|
||||
}
|
||||
|
||||
return stage;
|
||||
}
|
||||
|
||||
static void ApplyTalentIconColor(TalentStages stage, GUIComponent component, Option<Color> colorOverride)
|
||||
{
|
||||
Color color = stage switch
|
||||
{
|
||||
Invalid => unselectableColor,
|
||||
Locked => unselectableColor,
|
||||
Unlocked => GetColorOrOverride(GUIStyle.Green, colorOverride),
|
||||
Highlighted => GetColorOrOverride(GUIStyle.Orange, colorOverride),
|
||||
Available => GetColorOrOverride(unselectedColor, colorOverride),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(stage), stage, null)
|
||||
};
|
||||
|
||||
component.Color = color;
|
||||
component.HoverColor = Color.Lerp(color, Color.White, 0.7f);
|
||||
|
||||
static Color GetColorOrOverride(Color color, Option<Color> colorOverride) => colorOverride.TryUnwrap(out Color overrideColor) ? overrideColor : color;
|
||||
}
|
||||
|
||||
// this could also be reused for setting colors for talentCornerIcons but that's for another time
|
||||
static TalentStages GetCollectiveTalentState(Character character, IReadOnlyCollection<TalentButton> buttons, IReadOnlyCollection<Identifier> selectedTalents)
|
||||
{
|
||||
HashSet<TalentStages> talentStages = new HashSet<TalentStages>();
|
||||
foreach (TalentButton button in buttons)
|
||||
{
|
||||
talentStages.Add(GetTalentState(character, button.Identifier, selectedTalents));
|
||||
}
|
||||
|
||||
TalentStages collectiveStage = talentStages.All(static stage => stage is Locked)
|
||||
? Locked
|
||||
: Available;
|
||||
|
||||
foreach (TalentStages stage in talentStages)
|
||||
{
|
||||
if (stage is Highlighted)
|
||||
{
|
||||
collectiveStage = Highlighted;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stage is Unlocked)
|
||||
{
|
||||
collectiveStage = Unlocked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return collectiveStage;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (characterInfo is null || talentResetButton is null || talentApplyButton is null) { return; }
|
||||
|
||||
int talentCount = selectedTalents.Count - characterInfo.GetUnlockedTalentsInTree().Count();
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentApplyButton.Flash(GUIStyle.Orange);
|
||||
}
|
||||
|
||||
while (showCaseClosureQueue.TryDequeue(out Identifier identifier))
|
||||
{
|
||||
foreach (GUIComponent component in showCaseTalentFrames)
|
||||
{
|
||||
if (component.UserData is Identifier showcaseIdentifier && showcaseIdentifier == identifier)
|
||||
{
|
||||
component.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mouseInteracted = PlayerInput.PrimaryMouseButtonClicked() || PlayerInput.SecondaryMouseButtonClicked() || PlayerInput.ScrollWheelSpeed != 0;
|
||||
bool keyboardInteracted = PlayerInput.KeyHit(Keys.Escape) || GameSettings.CurrentConfig.KeyMap.Bindings[InputType.InfoTab].IsHit();
|
||||
|
||||
foreach (GUIComponent component in showCaseTalentFrames)
|
||||
{
|
||||
if (component.UserData is not Identifier identifier) { continue; }
|
||||
|
||||
component.AddToGUIUpdateList(order: 1);
|
||||
if (!component.Visible) { continue; }
|
||||
|
||||
if (keyboardInteracted || (mouseInteracted && !component.Rect.Contains(PlayerInput.MousePosition)))
|
||||
{
|
||||
showCaseClosureQueue.Enqueue(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOwnCharacter(CharacterInfo? info)
|
||||
{
|
||||
if (info is null) { return false; }
|
||||
|
||||
CharacterInfo? ownCharacterInfo = Character.Controlled?.Info ?? GameMain.Client?.CharacterInfo;
|
||||
if (ownCharacterInfo is null) { return false; }
|
||||
|
||||
return info == ownCharacterInfo;
|
||||
}
|
||||
|
||||
public static bool CanManageTalents(CharacterInfo targetInfo)
|
||||
{
|
||||
// in singleplayer we can do whatever we want
|
||||
if (GameMain.IsSingleplayer) { return true; }
|
||||
|
||||
// always allow managing talents for own character
|
||||
if (IsOwnCharacter(targetInfo)) { return true; }
|
||||
|
||||
// don't allow controlling non-bot characters
|
||||
if (targetInfo.Character is not { IsBot: true }) { return false; }
|
||||
|
||||
// lastly check if we have the permission to do this
|
||||
return GameMain.Client is { } client && client.HasPermission(ClientPermissions.ManageBotTalents);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -78,6 +79,8 @@ namespace Barotrauma
|
||||
|
||||
private PlayerBalanceElement? playerBalanceElement;
|
||||
|
||||
private static ImmutableHashSet<Character> characterList = ImmutableHashSet<Character>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// While set to true any call to <see cref="RefreshUpgradeList"/> will cause the buy button to be disabled and to not update the prices.
|
||||
/// This is to prevent us from buying another upgrade before the server has given us the new prices and causing potential syncing issues.
|
||||
@@ -93,6 +96,7 @@ namespace Barotrauma
|
||||
public UpgradeStore(CampaignUI campaignUI, GUIComponent parent)
|
||||
{
|
||||
WaitForServerUpdate = false;
|
||||
characterList = GameSession.GetSessionCrewCharacters(CharacterType.Both);
|
||||
this.campaignUI = campaignUI;
|
||||
GUIFrame upgradeFrame = new GUIFrame(rectT(1, 1, parent, Anchor.Center), style: "OuterGlow", color: Color.Black * 0.7f)
|
||||
{
|
||||
@@ -121,6 +125,7 @@ namespace Barotrauma
|
||||
|
||||
private void RefreshAll()
|
||||
{
|
||||
characterList = GameSession.GetSessionCrewCharacters(CharacterType.Both);
|
||||
switch (selectedUpgradeTab)
|
||||
{
|
||||
case UpgradeTab.Repairs:
|
||||
@@ -278,10 +283,13 @@ namespace Barotrauma
|
||||
* | upgrades | maintenance | <- 1/3rd empty space |
|
||||
* |---------------------------------------------------------------------------------------------------|
|
||||
*/
|
||||
GUILayoutGroup leftLayout = new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout)) { RelativeSpacing = 0.05f };
|
||||
GUILayoutGroup leftLayout = new GUILayoutGroup(rectT(0.4f, 1, topHeaderLayout)) { RelativeSpacing = 0.05f };
|
||||
GUILayoutGroup locationLayout = new GUILayoutGroup(rectT(1, 0.5f, leftLayout), isHorizontal: true);
|
||||
GUIImage submarineIcon = new GUIImage(rectT(new Point(locationLayout.Rect.Height, locationLayout.Rect.Height), locationLayout), style: "SubmarineIcon", scaleToFit: true);
|
||||
new GUITextBlock(rectT(1.0f - submarineIcon.RectTransform.RelativeSize.X, 1, locationLayout), TextManager.Get("UpgradeUI.Title"), font: GUIStyle.LargeFont);
|
||||
var header = new GUITextBlock(rectT(1.0f - submarineIcon.RectTransform.RelativeSize.X, 1, locationLayout), TextManager.Get("UpgradeUI.Title"), font: GUIStyle.LargeFont);
|
||||
header.RectTransform.MaxSize = new Point((int)(header.TextSize.X + header.Padding.X + header.Padding.Z), int.MaxValue);
|
||||
new GUITextBlock(rectT(1.0f, 1, locationLayout), TextManager.Get("UpgradeUI.AllSubmarinesInfo"), font: GUIStyle.SmallFont, wrap: true);
|
||||
|
||||
categoryButtonLayout = new GUILayoutGroup(rectT(0.4f, 0.3f, leftLayout), isHorizontal: true) { Stretch = true };
|
||||
GUIButton upgradeButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Upgrades"), style: "GUITabButton") { UserData = UpgradeTab.Upgrade, Selected = selectedUpgradeTab == UpgradeTab.Upgrade };
|
||||
GUIButton repairButton = new GUIButton(rectT(1, 1f, categoryButtonLayout), TextManager.Get("UICategory.Maintenance"), style: "GUITabButton") { UserData = UpgradeTab.Repairs, Selected = selectedUpgradeTab == UpgradeTab.Repairs };
|
||||
@@ -433,8 +441,8 @@ namespace Barotrauma
|
||||
|
||||
Location location = Campaign.Map.CurrentLocation;
|
||||
|
||||
int hullRepairCost = Campaign.GetHullRepairCost();
|
||||
int itemRepairCost = Campaign.GetItemRepairCost();
|
||||
int hullRepairCost = CampaignMode.GetHullRepairCost();
|
||||
int itemRepairCost = CampaignMode.GetItemRepairCost();
|
||||
int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
|
||||
if (location != null)
|
||||
{
|
||||
@@ -1080,7 +1088,7 @@ namespace Barotrauma
|
||||
|
||||
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
|
||||
{
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
|
||||
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
|
||||
}
|
||||
|
||||
@@ -1222,7 +1230,7 @@ namespace Barotrauma
|
||||
{
|
||||
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
|
||||
("[upgradename]", prefab.Name),
|
||||
("[amount]", prefab.Price.GetBuyPrice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation).ToString()));
|
||||
("[amount]", prefab.Price.GetBuyPrice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation, characterList).ToString()));
|
||||
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
|
||||
{
|
||||
if (GameMain.NetworkMember != null)
|
||||
@@ -1639,7 +1647,7 @@ namespace Barotrauma
|
||||
|
||||
GUITextBlock priceLabel = textBlocks[0];
|
||||
priceLabel.Visible = true;
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
|
||||
|
||||
if (priceLabel != null && !WaitForServerUpdate)
|
||||
{
|
||||
|
||||
@@ -163,6 +163,7 @@ namespace Barotrauma
|
||||
|
||||
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, bool transferItems, VoteType type)
|
||||
{
|
||||
int price = info.GetPrice();
|
||||
string name = starter.Name;
|
||||
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
|
||||
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
|
||||
@@ -177,14 +178,14 @@ namespace Barotrauma
|
||||
text = TextManager.GetWithVariables(tag,
|
||||
("[playername]", characterRichString),
|
||||
("[submarinename]", submarineRichString),
|
||||
("[amount]", info.Price.ToString()),
|
||||
("[amount]", price.ToString()),
|
||||
("[currencyname]", TextManager.Get("credit").ToLower()));
|
||||
break;
|
||||
case VoteType.PurchaseSub:
|
||||
text = TextManager.GetWithVariables("submarinepurchasevote",
|
||||
("[playername]", characterRichString),
|
||||
("[submarinename]", submarineRichString),
|
||||
("[amount]", info.Price.ToString()),
|
||||
("[amount]", price.ToString()),
|
||||
("[currencyname]", TextManager.Get("credit").ToLower()));
|
||||
break;
|
||||
case VoteType.SwitchSub:
|
||||
@@ -218,6 +219,7 @@ namespace Barotrauma
|
||||
|
||||
private LocalizedString GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, int yesVoteCount, int noVoteCount, bool votePassed)
|
||||
{
|
||||
int price = info.GetPrice();
|
||||
LocalizedString result = string.Empty;
|
||||
|
||||
switch (type)
|
||||
@@ -225,7 +227,7 @@ namespace Barotrauma
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed",
|
||||
("[submarinename]", info.DisplayName),
|
||||
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", info.Price)),
|
||||
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", price)),
|
||||
("[currencyname]", TextManager.Get("credit").ToLower()),
|
||||
("[yesvotecount]", yesVoteCount.ToString()),
|
||||
("[novotecount]" , noVoteCount.ToString()));
|
||||
@@ -233,7 +235,7 @@ namespace Barotrauma
|
||||
case VoteType.PurchaseSub:
|
||||
result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed",
|
||||
("[submarinename]", info.DisplayName),
|
||||
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", info.Price)),
|
||||
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", price)),
|
||||
("[currencyname]", TextManager.Get("credit").ToLower()),
|
||||
("[yesvotecount]", yesVoteCount.ToString()),
|
||||
("[novotecount]", noVoteCount.ToString()));
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
Data = data,
|
||||
OnClick = (GUITextBlock component, GUITextBlock.ClickableArea area) =>
|
||||
{
|
||||
GameMain.Instance.ShowOpenUrlInWebBrowserPrompt("https://gameanalytics.com/privacy/");
|
||||
GameMain.ShowOpenUrlInWebBrowserPrompt("https://gameanalytics.com/privacy/");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Barotrauma.Networking;
|
||||
using Barotrauma.Particles;
|
||||
using Barotrauma.Steam;
|
||||
using Barotrauma.Transition;
|
||||
using Barotrauma.Tutorials;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -774,9 +775,9 @@ namespace Barotrauma
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
|
||||
}
|
||||
else if (GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning)
|
||||
else if (ObjectiveManager.ContentRunning)
|
||||
{
|
||||
tutorialMode.Tutorial.CloseActiveContentGUI();
|
||||
ObjectiveManager.CloseActiveContentGUI();
|
||||
}
|
||||
else if (GameSession.IsTabMenuOpen)
|
||||
{
|
||||
@@ -791,6 +792,10 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.TogglePauseMenu();
|
||||
}
|
||||
else if (GameSession?.Campaign is { ShowCampaignUI: true, ForceMapUI: false })
|
||||
{
|
||||
GameSession.Campaign.ShowCampaignUI = false;
|
||||
}
|
||||
//open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC
|
||||
else if ((Character.Controlled == null || !itemHudActive())
|
||||
&& CharacterHealth.OpenHealthWindow == null
|
||||
@@ -824,7 +829,7 @@ namespace Barotrauma
|
||||
Paused =
|
||||
(DebugConsole.IsOpen || DebugConsole.Paused ||
|
||||
GUI.PauseMenuOpen || GUI.SettingsMenuOpen ||
|
||||
(GameSession?.GameMode is TutorialMode tutoMode && tutoMode.Tutorial.ContentRunning)) &&
|
||||
(GameSession?.GameMode is TutorialMode && ObjectiveManager.ContentRunning)) &&
|
||||
(NetworkMember == null || !NetworkMember.GameStarted);
|
||||
if (GameSession?.GameMode != null && GameSession.GameMode.Paused)
|
||||
{
|
||||
@@ -858,8 +863,9 @@ namespace Barotrauma
|
||||
{
|
||||
Screen.Selected.Update(Timing.Step);
|
||||
}
|
||||
else if (GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial.ContentRunning)
|
||||
else if (ObjectiveManager.ContentRunning && GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
ObjectiveManager.VideoPlayer.Update();
|
||||
tutorialMode.Update((float)Timing.Step);
|
||||
}
|
||||
else
|
||||
@@ -1200,7 +1206,7 @@ namespace Barotrauma
|
||||
base.OnExiting(sender, args);
|
||||
}
|
||||
|
||||
public void ShowOpenUrlInWebBrowserPrompt(string url, string promptExtensionTag = null)
|
||||
public static void ShowOpenUrlInWebBrowserPrompt(string url, string promptExtensionTag = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url)) { return; }
|
||||
if (GUIMessageBox.VisibleBox?.UserData as string == "verificationprompt") { return; }
|
||||
@@ -1218,7 +1224,14 @@ namespace Barotrauma
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
{
|
||||
ToolBox.OpenFileWithShell(url);
|
||||
try
|
||||
{
|
||||
ToolBox.OpenFileWithShell(url);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to open the url {url}", e);
|
||||
}
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -11,21 +9,22 @@ namespace Barotrauma
|
||||
{
|
||||
private const int MaxDrawnElements = 12;
|
||||
|
||||
public void DebugDraw(SpriteBatch spriteBatch, Vector2 pos, int debugDrawMetadataOffset, string[] ignoredMetadataInfo)
|
||||
public void DebugDraw(SpriteBatch spriteBatch, Vector2 pos, CampaignMode campaign, GUI.DebugDrawMetaData debugDrawMetaData)
|
||||
{
|
||||
var campaignData = data;
|
||||
foreach (string ignored in ignoredMetadataInfo)
|
||||
if (!debugDrawMetaData.FactionMetadata) { removeData("reputation.faction"); }
|
||||
if (!debugDrawMetaData.UpgradeLevels) { removeData("upgrade."); }
|
||||
if (!debugDrawMetaData.UpgradePrices) { removeData("upgradeprice."); }
|
||||
|
||||
void removeData(string keyStartsWith)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ignored))
|
||||
{
|
||||
campaignData = campaignData.Where(pair => !pair.Key.StartsWith(ignored)).ToDictionary(i => i.Key, i => i.Value);
|
||||
}
|
||||
campaignData = campaignData.Where(pair => !pair.Key.StartsWith(keyStartsWith)).ToDictionary(i => i.Key, i => i.Value);
|
||||
}
|
||||
|
||||
int offset = 0;;
|
||||
if (campaignData.Count > 0)
|
||||
{
|
||||
offset = debugDrawMetadataOffset % campaignData.Count;
|
||||
offset = debugDrawMetaData.Offset % campaignData.Count;
|
||||
if (offset < 0) { offset += campaignData.Count; }
|
||||
}
|
||||
|
||||
@@ -72,7 +71,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float y = infoRect.Bottom + 16;
|
||||
if (Campaign.Factions != null)
|
||||
if (campaign.Factions != null)
|
||||
{
|
||||
const string factionHeader = "Reputations";
|
||||
Vector2 factionHeaderSize = GUIStyle.SubHeadingFont.MeasureString(factionHeader);
|
||||
@@ -81,7 +80,7 @@ namespace Barotrauma
|
||||
GUI.DrawString(spriteBatch, factionPos, factionHeader, Color.White, font: GUIStyle.SubHeadingFont);
|
||||
y += factionHeaderSize.Y + 8;
|
||||
|
||||
foreach (Faction faction in Campaign.Factions)
|
||||
foreach (Faction faction in campaign.Factions)
|
||||
{
|
||||
LocalizedString name = faction.Prefab.Name;
|
||||
Vector2 nameSize = GUIStyle.SmallFont.MeasureString(name);
|
||||
@@ -94,20 +93,6 @@ namespace Barotrauma
|
||||
y += 15;
|
||||
}
|
||||
}
|
||||
|
||||
Location location = Campaign.Map?.CurrentLocation;
|
||||
if (location?.Reputation != null)
|
||||
{
|
||||
string name = Campaign.Map?.CurrentLocation.Name;
|
||||
Vector2 nameSize = GUIStyle.SmallFont.MeasureString(name);
|
||||
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 264, y), name, Color.White, font: GUIStyle.SmallFont);
|
||||
y += nameSize.Y + 5;
|
||||
|
||||
float normalizedReputation = MathUtils.InverseLerp(location.Reputation.MinReputation, location.Reputation.MaxReputation, location.Reputation.Value);
|
||||
Color color = ToolBox.GradientLerp(normalizedReputation, Color.Red, Color.Yellow, Color.LightGreen);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, (int)(normalizedReputation * 255), 10), color, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, 256, 10), Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -15,8 +15,6 @@ namespace Barotrauma
|
||||
protected bool crewDead;
|
||||
|
||||
protected Color overlayColor;
|
||||
protected LocalizedString overlayText, overlayTextBottom;
|
||||
protected Color overlayTextColor;
|
||||
protected Sprite overlaySprite;
|
||||
|
||||
private TransitionType prevCampaignUIAutoOpenType;
|
||||
@@ -29,6 +27,12 @@ namespace Barotrauma
|
||||
protected GUIFrame campaignUIContainer;
|
||||
public CampaignUI CampaignUI;
|
||||
|
||||
public SlideshowPlayer SlideshowPlayer
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
public static CancellationTokenSource StartRoundCancellationToken { get; private set; }
|
||||
|
||||
public bool ForceMapUI
|
||||
@@ -76,6 +80,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Mission mission in Missions.ToList())
|
||||
{
|
||||
if (!mission.Prefab.ShowStartMessage) { continue; }
|
||||
new GUIMessageBox(
|
||||
RichString.Rich(mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name),
|
||||
RichString.Rich(mission.Description), Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
|
||||
@@ -86,6 +91,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOwner(Client client) => client != null && client.IsOwner;
|
||||
|
||||
/// <summary>
|
||||
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
|
||||
/// </summary>
|
||||
@@ -97,10 +104,8 @@ namespace Barotrauma
|
||||
return
|
||||
GameMain.Client.HasPermission(permissions) ||
|
||||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Client.ConnectedClients.Count == 1 ||
|
||||
GameMain.Client.IsServerOwner ||
|
||||
//allow managing if no-one with permissions is alive
|
||||
GameMain.Client.ConnectedClients.None(c => c.InGame && c.Character is { IsIncapacitated: false, IsDead: false } && (c.IsOwner || c.HasPermission(permissions)));
|
||||
AnyOneAllowedToManageCampaign(permissions);
|
||||
}
|
||||
|
||||
public static bool AllowedToManageWallets()
|
||||
@@ -127,32 +132,10 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), overlayColor, isFilled: true);
|
||||
}
|
||||
if (!overlayText.IsNullOrEmpty() && overlayTextColor.A > 0)
|
||||
{
|
||||
var backgroundSprite = GUIStyle.GetComponentStyle("CommandBackground").GetDefaultSprite();
|
||||
Vector2 centerPos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2;
|
||||
LocalizedString wrappedText = ToolBox.WrapText(overlayText, GameMain.GraphicsWidth / 3, GUIStyle.Font);
|
||||
Vector2 textSize = GUIStyle.Font.MeasureString(wrappedText);
|
||||
Vector2 textPos = centerPos - textSize / 2;
|
||||
backgroundSprite.Draw(spriteBatch,
|
||||
centerPos,
|
||||
Color.White * (overlayTextColor.A / 255.0f),
|
||||
origin: backgroundSprite.size / 2,
|
||||
rotate: 0.0f,
|
||||
scale: new Vector2(GameMain.GraphicsWidth / 2 / backgroundSprite.size.X, textSize.Y / backgroundSprite.size.Y * 1.5f));
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos + Vector2.One, wrappedText, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, textPos, wrappedText, overlayTextColor);
|
||||
|
||||
if (!overlayTextBottom.IsNullOrEmpty())
|
||||
{
|
||||
Vector2 bottomTextPos = centerPos + new Vector2(0.0f, textSize.Y / 2 + 40 * GUI.Scale) - GUIStyle.Font.MeasureString(overlayTextBottom) / 2;
|
||||
GUI.DrawString(spriteBatch, bottomTextPos + Vector2.One, overlayTextBottom.Value, Color.Black * (overlayTextColor.A / 255.0f));
|
||||
GUI.DrawString(spriteBatch, bottomTextPos, overlayTextBottom.Value, overlayTextColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SlideshowPlayer?.DrawManually(spriteBatch);
|
||||
|
||||
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
|
||||
{
|
||||
endRoundButton.Visible = false;
|
||||
@@ -192,6 +175,7 @@ namespace Barotrauma
|
||||
case TransitionType.None:
|
||||
default:
|
||||
if (Level.Loaded.Type == LevelData.LevelType.Outpost &&
|
||||
!Level.Loaded.IsEndBiome &&
|
||||
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags.Contains("airlock".ToIdentifier()) ?? false)))
|
||||
{
|
||||
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
|
||||
@@ -203,6 +187,10 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (Level.IsLoadedOutpost && !ObjectiveManager.AllActiveObjectivesCompleted())
|
||||
{
|
||||
endRoundButton.Visible = false;
|
||||
}
|
||||
|
||||
if (ReadyCheckButton != null) { ReadyCheckButton.Visible = endRoundButton.Visible; }
|
||||
|
||||
@@ -266,7 +254,7 @@ namespace Barotrauma
|
||||
Rand.ThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
try
|
||||
{
|
||||
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror);
|
||||
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -283,6 +271,18 @@ namespace Barotrauma
|
||||
return loadTask;
|
||||
}
|
||||
|
||||
protected SubmarineInfo GetPredefinedStartOutpost()
|
||||
{
|
||||
if (Map?.CurrentLocation?.Type?.GetForcedOutpostGenerationParams() is OutpostGenerationParams parameters && !parameters.OutpostFilePath.IsNullOrEmpty())
|
||||
{
|
||||
return new SubmarineInfo(parameters.OutpostFilePath.Value)
|
||||
{
|
||||
OutpostGenerationParams = parameters
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
partial void NPCInteractProjSpecific(Character npc, Character interactor)
|
||||
{
|
||||
if (npc == null || interactor == null) { return; }
|
||||
|
||||
@@ -216,51 +216,35 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Character prevControlled = Character.Controlled;
|
||||
if (prevControlled?.AIController != null)
|
||||
{
|
||||
prevControlled.AIController.Enabled = false;
|
||||
}
|
||||
GUI.DisableHUD = true;
|
||||
if (IsFirstRound)
|
||||
{
|
||||
Character.Controlled = null;
|
||||
if (SlideshowPrefab.Prefabs.TryGet("campaignstart".ToIdentifier(), out var slideshow))
|
||||
{
|
||||
SlideshowPlayer = new SlideshowPlayer(GUICanvas.Instance, slideshow);
|
||||
}
|
||||
|
||||
Character.Controlled = null;
|
||||
prevControlled?.ClearInputs();
|
||||
|
||||
overlayColor = Color.LightGray;
|
||||
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = TextManager.GetWithVariables("campaignstart",
|
||||
("xxxx", Map.CurrentLocation.Name), ("yyyy", TextManager.Get($"submarineclass.{Submarine.MainSub.Info.SubmarineClass}")));
|
||||
float fadeInDuration = 1.0f;
|
||||
float textDuration = 10.0f;
|
||||
float timer = 0.0f;
|
||||
while (timer < textDuration)
|
||||
{
|
||||
if (GameMain.GameSession == null || Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
GUI.DisableHUD = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
// Try to grab the controlled here to prevent inputs, assigned late on multiplayer
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
prevControlled = Character.Controlled;
|
||||
Character.Controlled = null;
|
||||
prevControlled.ClearInputs();
|
||||
}
|
||||
GameMain.GameScreen.Cam.Freeze = true;
|
||||
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
var outpost = GameMain.GameSession.Level.StartOutpost;
|
||||
var borders = outpost.GetDockedBorders();
|
||||
borders.Location += outpost.WorldPosition.ToPoint();
|
||||
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
|
||||
float startZoom = 0.8f /
|
||||
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
|
||||
GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
GameMain.GameScreen.Cam.Zoom = GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
while (SlideshowPlayer != null && !SlideshowPlayer.LastTextShown)
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = true;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
prevControlled ??= Character.Controlled;
|
||||
GameMain.LightManager.LosAlpha = 0.0f;
|
||||
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
@@ -272,16 +256,6 @@ namespace Barotrauma
|
||||
AllowInterrupt = true,
|
||||
RemoveControlFromCharacter = false
|
||||
};
|
||||
fadeInDuration = 1.0f;
|
||||
timer = 0.0f;
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = "";
|
||||
while (timer < fadeInDuration)
|
||||
{
|
||||
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
|
||||
timer += CoroutineManager.DeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.Transparent;
|
||||
while (transition.Running)
|
||||
{
|
||||
@@ -409,6 +383,8 @@ namespace Barotrauma
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked() ||
|
||||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
|
||||
{
|
||||
@@ -442,7 +418,8 @@ namespace Barotrauma
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
}
|
||||
}
|
||||
else
|
||||
//end biome is handled by the server (automatic transition without a map screen when the end of the level is reached)
|
||||
else if (!Level.Loaded.IsEndBiome)
|
||||
{
|
||||
//wasn't initially docked (sub doesn't have a docking port?)
|
||||
// -> choose a destination when the sub is far enough from the start outpost
|
||||
@@ -467,11 +444,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateWhilePaused(float deltaTime)
|
||||
{
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
}
|
||||
|
||||
public override void End(TransitionType transitionType = TransitionType.None)
|
||||
{
|
||||
base.End(transitionType);
|
||||
ForceMapUI = ShowCampaignUI = false;
|
||||
|
||||
SlideshowPlayer?.Finish();
|
||||
|
||||
// remove all event dialogue boxes
|
||||
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
|
||||
{
|
||||
@@ -501,7 +484,8 @@ namespace Barotrauma
|
||||
{
|
||||
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
|
||||
}
|
||||
CoroutineManager.StartCoroutine(DoEndCampaignCameraTransition(), "DoEndCampaignCameraTransition");
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
{
|
||||
GameMain.NetLobbyScreen.Select();
|
||||
@@ -510,32 +494,6 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> DoEndCampaignCameraTransition()
|
||||
{
|
||||
Character controlled = Character.Controlled;
|
||||
if (controlled != null)
|
||||
{
|
||||
controlled.AIController.Enabled = false;
|
||||
}
|
||||
|
||||
GUI.DisableHUD = true;
|
||||
ISpatialEntity endObject = Level.Loaded.LevelObjectManager.GetAllObjects().FirstOrDefault(obj => obj.Prefab.SpawnPos == LevelObjectPrefab.SpawnPosType.LevelEnd);
|
||||
var transition = new CameraTransition(endObject ?? Submarine.MainSub, GameMain.GameScreen.Cam,
|
||||
null, Alignment.Center,
|
||||
fadeOut: true,
|
||||
panDuration: 10,
|
||||
startZoom: null, endZoom: 0.2f);
|
||||
|
||||
while (transition.Running)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
|
||||
@@ -834,8 +792,6 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.Log("Received campaign update (Reputation)");
|
||||
UInt16 id = msg.ReadUInt16();
|
||||
float? reputation = null;
|
||||
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
|
||||
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
|
||||
byte factionsCount = msg.ReadByte();
|
||||
for (int i = 0; i < factionsCount; i++)
|
||||
@@ -844,11 +800,6 @@ namespace Barotrauma
|
||||
}
|
||||
if (ShouldApply(NetFlags.Reputation, id, requireUpToDateSave: true))
|
||||
{
|
||||
if (reputation.HasValue)
|
||||
{
|
||||
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
|
||||
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
|
||||
}
|
||||
foreach (var (identifier, rep) in factionReps)
|
||||
{
|
||||
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
|
||||
@@ -861,6 +812,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
|
||||
}
|
||||
}
|
||||
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
|
||||
}
|
||||
}
|
||||
if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
|
||||
|
||||
@@ -12,7 +12,13 @@ namespace Barotrauma
|
||||
|
||||
public override bool Paused
|
||||
{
|
||||
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition") || ShowCampaignUI && CampaignUI.SelectedTab == InteractionType.Map; }
|
||||
get
|
||||
{
|
||||
return
|
||||
ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition") ||
|
||||
ShowCampaignUI && CampaignUI.SelectedTab == InteractionType.Map ||
|
||||
(SlideshowPlayer != null && !SlideshowPlayer.LastTextShown);
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateWhilePaused(float deltaTime)
|
||||
@@ -31,6 +37,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
|
||||
CrewManager.ChatBox?.Update(deltaTime);
|
||||
CrewManager.UpdateReports();
|
||||
}
|
||||
@@ -77,9 +85,10 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings)
|
||||
{
|
||||
CampaignMetadata = new CampaignMetadata(this);
|
||||
CampaignMetadata = new CampaignMetadata();
|
||||
UpgradeManager = new UpgradeManager(this);
|
||||
Settings = settings;
|
||||
InitFactions();
|
||||
map = new Map(this, mapSeed);
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
{
|
||||
@@ -89,7 +98,6 @@ namespace Barotrauma
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
InitCampaignData();
|
||||
InitUI();
|
||||
}
|
||||
|
||||
@@ -100,6 +108,19 @@ namespace Barotrauma
|
||||
{
|
||||
IsFirstRound = false;
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "metadata":
|
||||
CampaignMetadata = new CampaignMetadata(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CampaignMetadata ??= new CampaignMetadata();
|
||||
InitFactions();
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -114,9 +135,6 @@ namespace Barotrauma
|
||||
case "map":
|
||||
map = Map.Load(this, subElement);
|
||||
break;
|
||||
case "metadata":
|
||||
CampaignMetadata = new CampaignMetadata(this, subElement);
|
||||
break;
|
||||
case "cargo":
|
||||
CargoManager.LoadPurchasedItems(subElement);
|
||||
break;
|
||||
@@ -136,11 +154,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
CampaignMetadata ??= new CampaignMetadata(this);
|
||||
UpgradeManager ??= new UpgradeManager(this);
|
||||
|
||||
InitCampaignData();
|
||||
|
||||
InitUI();
|
||||
|
||||
//backwards compatibility for saves made prior to the addition of personal wallets
|
||||
@@ -265,8 +280,7 @@ namespace Barotrauma
|
||||
|
||||
private IEnumerable<CoroutineStatus> DoLoadInitialLevel(LevelData level, bool mirror)
|
||||
{
|
||||
GameMain.GameSession.StartRound(level,
|
||||
mirrorLevel: mirror);
|
||||
GameMain.GameSession.StartRound(level, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost());
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
CoroutineManager.StartCoroutine(DoInitialCameraTransition(), "SinglePlayerCampaign.DoInitialCameraTransition");
|
||||
@@ -296,34 +310,9 @@ namespace Barotrauma
|
||||
|
||||
if (IsFirstRound || showCampaignResetText)
|
||||
{
|
||||
overlayColor = Color.LightGray;
|
||||
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = TextManager.GetWithVariables(showCampaignResetText ? "campaignend4" : "campaignstart",
|
||||
("xxxx", Map.CurrentLocation.Name),
|
||||
("yyyy", TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass)));
|
||||
LocalizedString pressAnyKeyText = TextManager.Get("pressanykey");
|
||||
float fadeInDuration = 2.0f;
|
||||
float textDuration = 10.0f;
|
||||
float timer = 0.0f;
|
||||
while (true)
|
||||
if (SlideshowPrefab.Prefabs.TryGet("campaignstart".ToIdentifier(), out var slideshow))
|
||||
{
|
||||
if (timer > fadeInDuration)
|
||||
{
|
||||
overlayTextBottom = pressAnyKeyText;
|
||||
if (PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0 || PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (GameMain.GameSession == null)
|
||||
{
|
||||
GUI.DisableHUD = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
SlideshowPlayer = new SlideshowPlayer(GUICanvas.Instance, slideshow);
|
||||
}
|
||||
var outpost = GameMain.GameSession.Level.StartOutpost;
|
||||
var borders = outpost.GetDockedBorders();
|
||||
@@ -331,7 +320,13 @@ namespace Barotrauma
|
||||
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
|
||||
float startZoom = 0.8f /
|
||||
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
|
||||
GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
GameMain.GameScreen.Cam.Zoom = GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
|
||||
while (SlideshowPlayer != null && !SlideshowPlayer.LastTextShown)
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = true;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
|
||||
null, null,
|
||||
fadeOut: false,
|
||||
@@ -343,17 +338,6 @@ namespace Barotrauma
|
||||
AllowInterrupt = true,
|
||||
RemoveControlFromCharacter = false
|
||||
};
|
||||
fadeInDuration = 1.0f;
|
||||
timer = 0.0f;
|
||||
overlayTextColor = Color.Transparent;
|
||||
overlayText = "";
|
||||
while (timer < fadeInDuration)
|
||||
{
|
||||
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
|
||||
timer += CoroutineManager.DeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.Transparent;
|
||||
while (transition.Running)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
@@ -407,6 +391,11 @@ namespace Barotrauma
|
||||
GUI.SetSavingIndicatorState(success);
|
||||
crewDead = false;
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Event history must be registered before ending the round or it will be cleared
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
}
|
||||
GameMain.GameSession.EndRound("", traitorResults, transitionType);
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
RoundSummary roundSummary = null;
|
||||
@@ -437,60 +426,65 @@ namespace Barotrauma
|
||||
case TransitionType.ProgressToNextEmptyLocation:
|
||||
TotalPassedLevels++;
|
||||
break;
|
||||
case TransitionType.End:
|
||||
EndCampaign();
|
||||
IsFirstRound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Map.ProgressWorld(transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
|
||||
Map.ProgressWorld(this, transitionType, (float)(Timing.TotalTime - GameMain.GameSession.RoundStartTime));
|
||||
|
||||
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
|
||||
GUI.ClearMessages();
|
||||
|
||||
//--------------------------------------
|
||||
if (transitionType != TransitionType.End)
|
||||
{
|
||||
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
|
||||
transitionType == TransitionType.LeaveLocation ? Alignment.BottomCenter : Alignment.Center,
|
||||
fadeOut: false,
|
||||
panDuration: EndTransitionDuration);
|
||||
|
||||
GUI.ClearMessages();
|
||||
|
||||
Location portraitLocation = Map.SelectedLocation ?? Map.CurrentLocation;
|
||||
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
|
||||
float fadeOutDuration = endTransition.PanDuration;
|
||||
float t = 0.0f;
|
||||
while (t < fadeOutDuration || endTransition.Running)
|
||||
{
|
||||
t += CoroutineManager.DeltaTime;
|
||||
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.White;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
if (success)
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
GameMain.GameSession.EventManager.RegisterEventHistory();
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingSubmarineSwitch = null;
|
||||
EnableRoundSummaryGameOverState();
|
||||
}
|
||||
|
||||
CrewManager?.ClearCurrentOrders();
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
|
||||
{
|
||||
GameMain.GameScreen.Select();
|
||||
if (continueButton != null)
|
||||
Location portraitLocation = Map.SelectedLocation ?? Map.CurrentLocation;
|
||||
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
|
||||
float fadeOutDuration = endTransition.PanDuration;
|
||||
float t = 0.0f;
|
||||
while (t < fadeOutDuration || endTransition.Running)
|
||||
{
|
||||
continueButton.Visible = true;
|
||||
t += CoroutineManager.DeltaTime;
|
||||
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.White;
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
if (success)
|
||||
{
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
PendingSubmarineSwitch = null;
|
||||
EnableRoundSummaryGameOverState();
|
||||
}
|
||||
|
||||
GUI.DisableHUD = false;
|
||||
GUI.ClearCursorWait();
|
||||
overlayColor = Color.Transparent;
|
||||
});
|
||||
CrewManager?.ClearCurrentOrders();
|
||||
|
||||
SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
|
||||
{
|
||||
GameMain.GameScreen.Select();
|
||||
if (continueButton != null)
|
||||
{
|
||||
continueButton.Visible = true;
|
||||
}
|
||||
|
||||
GUI.DisableHUD = false;
|
||||
GUI.ClearCursorWait();
|
||||
overlayColor = Color.Transparent;
|
||||
});
|
||||
}
|
||||
|
||||
GUI.SetSavingIndicatorState(false);
|
||||
yield return CoroutineStatus.Success;
|
||||
@@ -498,7 +492,10 @@ namespace Barotrauma
|
||||
|
||||
protected override void EndCampaignProjSpecific()
|
||||
{
|
||||
CoroutineManager.StartCoroutine(DoEndCampaignCameraTransition(), "DoEndCampaignCameraTransition");
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
GameMain.CampaignEndScreen.OnFinished = () =>
|
||||
{
|
||||
showCampaignResetText = true;
|
||||
@@ -507,39 +504,14 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> DoEndCampaignCameraTransition()
|
||||
{
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
Character.Controlled.AIController.Enabled = false;
|
||||
Character.Controlled = null;
|
||||
}
|
||||
GUI.DisableHUD = true;
|
||||
ISpatialEntity endObject = Level.Loaded.LevelObjectManager.GetAllObjects().FirstOrDefault(obj => obj.Prefab.SpawnPos == LevelObjectPrefab.SpawnPosType.LevelEnd);
|
||||
var transition = new CameraTransition(endObject ?? Submarine.MainSub, GameMain.GameScreen.Cam,
|
||||
null, Alignment.Center,
|
||||
fadeOut: true,
|
||||
panDuration: 10,
|
||||
startZoom: null, endZoom: 0.2f);
|
||||
|
||||
while (transition.Running)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
GameMain.CampaignEndScreen.Select();
|
||||
GUI.DisableHUD = false;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
|
||||
SlideshowPlayer?.UpdateManually(deltaTime);
|
||||
|
||||
Map?.Radiation?.UpdateRadiation(deltaTime);
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked() ||
|
||||
@@ -590,11 +562,19 @@ namespace Barotrauma
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
}
|
||||
}
|
||||
else if (Level.Loaded.IsEndBiome)
|
||||
{
|
||||
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub);
|
||||
if (transitionType == TransitionType.ProgressToNextLocation)
|
||||
{
|
||||
LoadNewLevel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//wasn't initially docked (sub doesn't have a docking port?)
|
||||
// -> choose a destination when the sub is far enough from the start outpost
|
||||
if (!Submarine.MainSub.AtStartExit)
|
||||
if (!Submarine.MainSub.AtStartExit && !Level.Loaded.StartOutpost.ExitPoints.Any())
|
||||
{
|
||||
ForceMapUI = true;
|
||||
CampaignUI.SelectTab(InteractionType.Map);
|
||||
@@ -604,11 +584,11 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub);
|
||||
if (transitionType == TransitionType.End)
|
||||
if (Level.Loaded.IsEndBiome && transitionType == TransitionType.ProgressToNextLocation)
|
||||
{
|
||||
EndCampaign();
|
||||
LoadNewLevel();
|
||||
}
|
||||
if (transitionType == TransitionType.ProgressToNextLocation &&
|
||||
else if (transitionType == TransitionType.ProgressToNextLocation &&
|
||||
Level.Loaded.EndOutpost != null && Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
|
||||
{
|
||||
LoadNewLevel();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -25,107 +23,9 @@ namespace Barotrauma.Tutorials
|
||||
#region Tutorial variables
|
||||
|
||||
public readonly Identifier Identifier;
|
||||
|
||||
public LocalizedString DisplayName { get; }
|
||||
|
||||
public bool ContentRunning { get; private set; }
|
||||
|
||||
private GUIComponent infoBox;
|
||||
private Action infoBoxClosedCallback;
|
||||
|
||||
private VideoPlayer videoPlayer;
|
||||
private Point screenResolution;
|
||||
private WindowMode windowMode;
|
||||
private float prevUIScale;
|
||||
|
||||
private GUILayoutGroup objectiveGroup;
|
||||
private readonly LocalizedString objectiveTextTranslated;
|
||||
|
||||
private readonly List<Segment> ActiveObjectives = new List<Segment>();
|
||||
private const float ObjectiveComponentAnimationTime = 1.5f;
|
||||
private Segment ActiveContentSegment { get; set; }
|
||||
|
||||
public class Segment
|
||||
{
|
||||
public readonly record struct Text(
|
||||
Identifier Tag,
|
||||
int Width = DefaultWidth,
|
||||
int Height = DefaultHeight,
|
||||
Anchor Anchor = Anchor.Center);
|
||||
|
||||
public readonly record struct Video(
|
||||
string FullPath,
|
||||
Identifier TextTag,
|
||||
int Width = DefaultWidth,
|
||||
int Height = DefaultHeight)
|
||||
{
|
||||
public string FileName => Path.GetFileName(FullPath.CleanUpPath());
|
||||
public string ContentPath => Path.GetDirectoryName(FullPath.CleanUpPath());
|
||||
}
|
||||
|
||||
private const int DefaultWidth = 450;
|
||||
private const int DefaultHeight = 80;
|
||||
|
||||
public GUIImage ObjectiveStateIndicator;
|
||||
public GUIButton ObjectiveButton;
|
||||
public GUITextBlock LinkedTextBlock;
|
||||
public LocalizedString ObjectiveText;
|
||||
|
||||
public readonly Identifier Id;
|
||||
public readonly Text TextContent;
|
||||
public readonly Video VideoContent;
|
||||
public readonly AutoPlayVideo AutoPlayVideo;
|
||||
|
||||
public Action OnClickObjective;
|
||||
|
||||
public TutorialSegmentType SegmentType { get; private set; }
|
||||
|
||||
public static Segment CreateInfoBoxSegment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag, autoPlayVideo, textContent, videoContent);
|
||||
}
|
||||
|
||||
public static Segment CreateMessageBoxSegment(Identifier id, Identifier objectiveTextTag, Action onClickObjective)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag, onClickObjective);
|
||||
}
|
||||
|
||||
public static Segment CreateObjectiveSegment(Identifier id, Identifier objectiveTextTag)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag);
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
AutoPlayVideo = autoPlayVideo;
|
||||
TextContent = textContent;
|
||||
VideoContent = videoContent;
|
||||
SegmentType = TutorialSegmentType.InfoBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, Action onClickObjective)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
OnClickObjective = onClickObjective;
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
SegmentType = TutorialSegmentType.Objective;
|
||||
}
|
||||
|
||||
public void ConnectMessageBox(Segment messageBoxSegment)
|
||||
{
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
OnClickObjective = messageBoxSegment.OnClickObjective;
|
||||
}
|
||||
}
|
||||
public LocalizedString Description { get; }
|
||||
|
||||
|
||||
private bool completed;
|
||||
public bool Completed
|
||||
@@ -163,6 +63,8 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
public readonly List<(Entity entity, Identifier iconStyle)> Icons = new List<(Entity entity, Identifier iconStyle)>();
|
||||
|
||||
public bool Paused { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tutorial Controls
|
||||
@@ -171,8 +73,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
Identifier = $"tutorial.{prefab.Identifier}".ToIdentifier();
|
||||
DisplayName = TextManager.Get(Identifier);
|
||||
objectiveTextTranslated = TextManager.Get("Tutorial.Objective");
|
||||
|
||||
Description = TextManager.Get($"tutorial.{prefab.Identifier}.description");
|
||||
TutorialPrefab = prefab;
|
||||
eventPrefab = EventSet.GetEventPrefab(prefab.EventIdentifier);
|
||||
}
|
||||
@@ -260,35 +161,26 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
tutorialCoroutine = CoroutineManager.StartCoroutine(UpdateState());
|
||||
|
||||
Initialize();
|
||||
GameMain.GameSession.CrewManager.AllowCharacterSwitch = TutorialPrefab.AllowCharacterSwitch;
|
||||
GameMain.GameSession.CrewManager.AutoHideCrewList();
|
||||
|
||||
if (Character.Controlled?.Inventory is CharacterInventory inventory)
|
||||
{
|
||||
foreach (Item item in inventory.AllItemsMod)
|
||||
{
|
||||
if (item.HasTag(TutorialPrefab.StartingItemTags)) { continue; }
|
||||
item.Unequip(Character.Controlled);
|
||||
Character.Controlled.Inventory.RemoveItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
GameMain.GameSession.CrewManager.AllowCharacterSwitch = TutorialPrefab.AllowCharacterSwitch;
|
||||
GameMain.GameSession.CrewManager.AutoHideCrewList();
|
||||
|
||||
if (Character.Controlled is Character character)
|
||||
{
|
||||
foreach (Item item in character.Inventory.AllItemsMod)
|
||||
{
|
||||
if (item.HasTag(TutorialPrefab.StartingItemTags)) { continue; }
|
||||
item.Unequip(character);
|
||||
character.Inventory.RemoveItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
videoPlayer = new VideoPlayer();
|
||||
GameMain.Instance.ShowLoading(Loading());
|
||||
ActiveObjectives.Clear();
|
||||
ActiveContentSegment = null;
|
||||
|
||||
CreateObjectiveFrame();
|
||||
ObjectiveManager.ResetObjectives();
|
||||
|
||||
// Setup doors: Clear all requirements, unless the door is setup as locked.
|
||||
foreach (var item in Item.ItemList)
|
||||
@@ -304,24 +196,8 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale || GameSettings.CurrentConfig.Graphics.DisplayMode != windowMode)
|
||||
{
|
||||
CreateObjectiveFrame();
|
||||
}
|
||||
if (ActiveObjectives.Count > 0)
|
||||
{
|
||||
objectiveGroup?.AddToGUIUpdateList(order: -1);
|
||||
}
|
||||
infoBox?.AddToGUIUpdateList(order: 100);
|
||||
videoPlayer?.AddToGUIUpdateList(order: 100);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
videoPlayer?.Update();
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
if (character.Oxygen < 1)
|
||||
@@ -342,8 +218,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
GUI.PreventPauseMenuToggle = false;
|
||||
}
|
||||
ContentRunning = false;
|
||||
infoBox = null;
|
||||
ObjectiveManager.ClearContent();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -374,18 +249,6 @@ namespace Barotrauma.Tutorials
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void CloseActiveContentGUI()
|
||||
{
|
||||
if (videoPlayer.IsPlaying)
|
||||
{
|
||||
videoPlayer.Stop();
|
||||
}
|
||||
else if (infoBox != null)
|
||||
{
|
||||
CloseInfoFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<CoroutineStatus> UpdateState()
|
||||
{
|
||||
while (GameMain.Instance.LoadingScreenOpen || Level.Loaded == null || Level.Loaded.Generating)
|
||||
@@ -432,13 +295,56 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return new WaitForSeconds(WaitBeforeFade);
|
||||
|
||||
Action onEnd = () => GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
|
||||
|
||||
TutorialPrefab nextTutorialPrefab = null;
|
||||
bool displayEndMessage =
|
||||
TutorialPrefab.EndMessage.EndType == TutorialPrefab.EndType.Restart ||
|
||||
(TutorialPrefab.EndMessage.EndType == TutorialPrefab.EndType.Continue && TutorialPrefab.Prefabs.TryGet(TutorialPrefab.EndMessage.NextTutorialIdentifier, out nextTutorialPrefab));
|
||||
|
||||
if (displayEndMessage)
|
||||
{
|
||||
Paused = true;
|
||||
var endingMessageBox = new GUIMessageBox(
|
||||
headerText: "",
|
||||
text: TextManager.Get($"{Identifier}.completed"),
|
||||
buttons: new LocalizedString[]
|
||||
{
|
||||
TextManager.Get(nextTutorialPrefab is null ? "restart" : "campaigncontinue"),
|
||||
TextManager.Get("pausemenuquit")
|
||||
});
|
||||
|
||||
endingMessageBox.Buttons[0].OnClicked += (_, _) =>
|
||||
{
|
||||
if (nextTutorialPrefab is null)
|
||||
{
|
||||
onEnd = () => Restart(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
onEnd = () =>
|
||||
{
|
||||
GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
|
||||
new Tutorial(nextTutorialPrefab).Start();
|
||||
};
|
||||
}
|
||||
return true;
|
||||
};
|
||||
endingMessageBox.Buttons[0].OnClicked += endingMessageBox.Close;
|
||||
endingMessageBox.Buttons[0].OnClicked += (_, _) => Paused = false;
|
||||
endingMessageBox.Buttons[1].OnClicked += endingMessageBox.Close;
|
||||
endingMessageBox.Buttons[1].OnClicked += (_, _) => Paused = false;
|
||||
}
|
||||
|
||||
while (Paused) { yield return CoroutineStatus.Running; }
|
||||
|
||||
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: FadeOutTime);
|
||||
Completed = true;
|
||||
|
||||
while (endCinematic.Running) { yield return CoroutineStatus.Running; }
|
||||
|
||||
Stop();
|
||||
GameMain.MainMenuScreen.ReturnToMainMenu(null, null);
|
||||
onEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,379 +356,15 @@ namespace Barotrauma.Tutorials
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TriggerTutorialSegment(Segment segment, bool connectObjective = false)
|
||||
{
|
||||
if (segment.SegmentType != TutorialSegmentType.InfoBox)
|
||||
{
|
||||
ActiveObjectives.Add(segment);
|
||||
AddToObjectiveList(segment, connectObjective);
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
ActiveContentSegment = segment;
|
||||
|
||||
var title = TextManager.Get(segment.Id);
|
||||
LocalizedString tutorialText = TextManager.GetFormatted(segment.TextContent.Tag);
|
||||
tutorialText = TextManager.ParseInputTypes(tutorialText);
|
||||
|
||||
switch (segment.AutoPlayVideo)
|
||||
{
|
||||
case AutoPlayVideo.Yes:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: LoadActiveContentVideo);
|
||||
break;
|
||||
case AutoPlayVideo.No:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: StopCurrentContentSegment,
|
||||
onVideoButtonClicked: LoadActiveContentVideo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void CompleteTutorialSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment)
|
||||
{
|
||||
DebugConsole.AddWarning($"Warning: tried to complete the tutorial segment \"{segmentId}\" in tutorial \"{Identifier}\" but it isn't active!");
|
||||
return;
|
||||
}
|
||||
if (GUIStyle.GetComponentStyle("ObjectiveIndicatorCompleted") is GUIComponentStyle style)
|
||||
{
|
||||
//return if already completed
|
||||
if (segment.ObjectiveStateIndicator.Style == style) { return; }
|
||||
segment.ObjectiveStateIndicator.ApplyStyle(style);
|
||||
}
|
||||
segment.ObjectiveStateIndicator.Parent.Flash(color: GUIStyle.Green, flashDuration: 0.35f, useRectangleFlash: true);
|
||||
segment.ObjectiveButton.OnClicked = null;
|
||||
segment.ObjectiveButton.CanBeFocused = false;
|
||||
GameAnalyticsManager.AddDesignEvent($"Tutorial:{Identifier}:{segmentId}:Completed");
|
||||
}
|
||||
|
||||
public void RemoveTutorialSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment)
|
||||
{
|
||||
DebugConsole.AddWarning($"Warning: tried to remove the tutorial segment \"{segmentId}\" in tutorial \"{Identifier}\" but it isn't active!");
|
||||
return;
|
||||
}
|
||||
segment.ObjectiveStateIndicator.FadeOut(ObjectiveComponentAnimationTime, false);
|
||||
segment.LinkedTextBlock.FadeOut(ObjectiveComponentAnimationTime, false);
|
||||
var parent = segment.LinkedTextBlock.Parent;
|
||||
parent.FadeOut(ObjectiveComponentAnimationTime, true, onRemove: () =>
|
||||
{
|
||||
ActiveObjectives.Remove(segment);
|
||||
objectiveGroup?.Recalculate();
|
||||
});
|
||||
parent.RectTransform.MoveOverTime(GetObjectiveHiddenPosition(parent.RectTransform), ObjectiveComponentAnimationTime);
|
||||
segment.ObjectiveButton.OnClicked = null;
|
||||
segment.ObjectiveButton.CanBeFocused = false;
|
||||
}
|
||||
|
||||
private Segment GetActiveObjective(Identifier id) => ActiveObjectives.FirstOrDefault(s => s.Id == id);
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (tutorialCoroutine != null)
|
||||
{
|
||||
CoroutineManager.StopCoroutines(tutorialCoroutine);
|
||||
}
|
||||
ContentRunning = false;
|
||||
infoBox = null;
|
||||
videoPlayer?.Remove();
|
||||
ObjectiveManager.ResetUI();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Objectives
|
||||
|
||||
/// <summary>
|
||||
/// Create the objective list that holds the objectives (called on start and on resolution change)
|
||||
/// </summary>
|
||||
private void CreateObjectiveFrame()
|
||||
{
|
||||
var objectiveListFrame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.TutorialObjectiveListArea, GUI.Canvas), style: null);
|
||||
objectiveGroup = new GUILayoutGroup(new RectTransform(Vector2.One, objectiveListFrame.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = (int)GUIStyle.Font.LineHeight
|
||||
};
|
||||
for (int i = 0; i < ActiveObjectives.Count; i++)
|
||||
{
|
||||
AddToObjectiveList(ActiveObjectives[i]);
|
||||
}
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
windowMode = GameSettings.CurrentConfig.Graphics.DisplayMode;
|
||||
prevUIScale = GUI.Scale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops content running and adds the active segment to the objective list
|
||||
/// </summary>
|
||||
private void StopCurrentContentSegment()
|
||||
{
|
||||
if (!ActiveContentSegment.ObjectiveText.IsNullOrEmpty())
|
||||
{
|
||||
ActiveObjectives.Add(ActiveContentSegment);
|
||||
AddToObjectiveList(ActiveContentSegment);
|
||||
}
|
||||
ContentRunning = false;
|
||||
ActiveContentSegment = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the segment to the objective list
|
||||
/// </summary>
|
||||
private void AddToObjectiveList(Segment segment, bool connectExisting = false)
|
||||
{
|
||||
if (connectExisting)
|
||||
{
|
||||
if (ActiveObjectives.Find(o => o.Id == segment.Id) is { } existingSegment)
|
||||
{
|
||||
existingSegment.ConnectMessageBox(segment);
|
||||
SetButtonBehavior(existingSegment);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var frameRt = new RectTransform(new Vector2(1.0f, 0.1f), objectiveGroup.RectTransform)
|
||||
{
|
||||
AbsoluteOffset = GetObjectiveHiddenPosition(),
|
||||
MinSize = new Point(0, objectiveGroup.AbsoluteSpacing)
|
||||
};
|
||||
var frame = new GUIFrame(frameRt, style: null)
|
||||
{
|
||||
CanBeFocused = true
|
||||
};
|
||||
objectiveGroup.Recalculate();
|
||||
|
||||
segment.LinkedTextBlock = new GUITextBlock(
|
||||
new RectTransform(new Point(frameRt.Rect.Width - objectiveGroup.AbsoluteSpacing, 0), frame.RectTransform, anchor: Anchor.TopRight),
|
||||
TextManager.ParseInputTypes(segment.ObjectiveText),
|
||||
wrap: true);
|
||||
|
||||
var size = new Point(segment.LinkedTextBlock.Rect.Width, segment.LinkedTextBlock.Rect.Height);
|
||||
segment.LinkedTextBlock.RectTransform.NonScaledSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.MinSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.MaxSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.IsFixedSize = true;
|
||||
frame.RectTransform.Resize(new Point(frame.Rect.Width, segment.LinkedTextBlock.RectTransform.Rect.Height), resizeChildren: false);
|
||||
frame.RectTransform.IsFixedSize = true;
|
||||
|
||||
var indicatorRt = new RectTransform(new Point(objectiveGroup.AbsoluteSpacing), frame.RectTransform, isFixedSize: true);
|
||||
segment.ObjectiveStateIndicator = new GUIImage(indicatorRt, "ObjectiveIndicatorIncomplete");
|
||||
|
||||
SetTransparent(segment.LinkedTextBlock);
|
||||
|
||||
segment.ObjectiveButton = new GUIButton(new RectTransform(Vector2.One, segment.LinkedTextBlock.RectTransform, Anchor.TopLeft, Pivot.TopLeft), style: null)
|
||||
{
|
||||
ToolTip = objectiveTextTranslated
|
||||
};
|
||||
SetButtonBehavior(segment);
|
||||
SetTransparent(segment.ObjectiveButton);
|
||||
|
||||
frameRt.MoveOverTime(new Point(0, frameRt.AbsoluteOffset.Y), ObjectiveComponentAnimationTime, onDoneMoving: () => objectiveGroup?.Recalculate());
|
||||
|
||||
static void SetTransparent(GUIComponent component) => component.Color = component.HoverColor = component.PressedColor = component.SelectedColor = Color.Transparent;
|
||||
|
||||
void SetButtonBehavior(Segment segment)
|
||||
{
|
||||
segment.ObjectiveButton.CanBeFocused = segment.SegmentType != TutorialSegmentType.Objective;
|
||||
segment.ObjectiveButton.OnClicked = (GUIButton btn, object userdata) =>
|
||||
{
|
||||
if (segment.SegmentType == TutorialSegmentType.InfoBox)
|
||||
{
|
||||
if (segment.AutoPlayVideo == AutoPlayVideo.Yes)
|
||||
{
|
||||
ReplaySegmentVideo(segment);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowSegmentText(segment);
|
||||
}
|
||||
}
|
||||
else if (segment.SegmentType == TutorialSegmentType.MessageBox)
|
||||
{
|
||||
segment.OnClickObjective?.Invoke();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaySegmentVideo(Segment segment)
|
||||
{
|
||||
if (ContentRunning) { return; }
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
LoadVideo(segment);
|
||||
}
|
||||
|
||||
private void ShowSegmentText(Segment segment)
|
||||
{
|
||||
if (ContentRunning) { return; }
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
ActiveContentSegment = segment;
|
||||
infoBox = CreateInfoFrame(
|
||||
TextManager.Get(segment.Id),
|
||||
TextManager.Get(segment.TextContent.Tag),
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: () => ContentRunning = false,
|
||||
onVideoButtonClicked: () => LoadVideo(segment));
|
||||
}
|
||||
|
||||
private Point GetObjectiveHiddenPosition(RectTransform rt = null)
|
||||
{
|
||||
return new Point(GameMain.GraphicsWidth - objectiveGroup.Rect.X, rt?.AbsoluteOffset.Y ?? 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region InfoFrame
|
||||
|
||||
private void CloseInfoFrame() => CloseInfoFrame(null, null);
|
||||
|
||||
private bool CloseInfoFrame(GUIButton button, object userData)
|
||||
{
|
||||
infoBox = null;
|
||||
infoBoxClosedCallback?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// Creates and displays a tutorial info box
|
||||
/// </summary>
|
||||
private GUIComponent CreateInfoFrame(LocalizedString title, LocalizedString text, int width = 300, int height = 80, Anchor anchor = Anchor.TopRight, bool hasButton = false, Action onInfoBoxClosed = null, Action onVideoButtonClicked = null)
|
||||
{
|
||||
if (hasButton)
|
||||
{
|
||||
height += 60;
|
||||
}
|
||||
|
||||
width = (int)(width * GUI.Scale);
|
||||
height = (int)(height * GUI.Scale);
|
||||
|
||||
LocalizedString wrappedText = ToolBox.WrapText(text, width, GUIStyle.Font);
|
||||
height += (int)GUIStyle.Font.MeasureString(wrappedText).Y;
|
||||
|
||||
if (title.Length > 0)
|
||||
{
|
||||
height += (int)GUIStyle.Font.MeasureString(title).Y + (int)(150 * GUI.Scale);
|
||||
}
|
||||
|
||||
var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), background.RectTransform, anchor));
|
||||
infoBlock.Flash(GUIStyle.Green);
|
||||
|
||||
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), infoBlock.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = 5
|
||||
};
|
||||
|
||||
if (title.Length > 0)
|
||||
{
|
||||
var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform),
|
||||
title, font: GUIStyle.LargeFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
|
||||
titleBlock.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
|
||||
text = RichString.Rich(text);
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true);
|
||||
|
||||
textBlock.RectTransform.IsFixedSize = true;
|
||||
infoBoxClosedCallback = onInfoBoxClosed;
|
||||
|
||||
if (hasButton)
|
||||
{
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), infoContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.1f
|
||||
};
|
||||
buttonContainer.RectTransform.IsFixedSize = true;
|
||||
|
||||
if (onVideoButtonClicked != null)
|
||||
{
|
||||
buttonContainer.Stretch = true;
|
||||
var videoButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("Video"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = (GUIButton button, object obj) =>
|
||||
{
|
||||
onVideoButtonClicked();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonContainer.Stretch = false;
|
||||
buttonContainer.ChildAnchor = Anchor.Center;
|
||||
}
|
||||
|
||||
var okButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("OK"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = CloseInfoFrame
|
||||
};
|
||||
}
|
||||
|
||||
infoBlock.RectTransform.NonScaledSize = new Point(infoBlock.Rect.Width, (int)(infoContent.Children.Sum(c => c.Rect.Height + infoContent.AbsoluteSpacing) / infoContent.RectTransform.RelativeSize.Y));
|
||||
|
||||
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
|
||||
|
||||
return background;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Video
|
||||
|
||||
private void LoadVideo(Segment segment)
|
||||
{
|
||||
videoPlayer ??= new VideoPlayer();
|
||||
if (segment.AutoPlayVideo == AutoPlayVideo.Yes)
|
||||
{
|
||||
videoPlayer.LoadContent(
|
||||
contentPath: segment.VideoContent.ContentPath,
|
||||
videoSettings: new VideoPlayer.VideoSettings(segment.VideoContent.FileName),
|
||||
textSettings: new VideoPlayer.TextSettings(segment.VideoContent.TextTag, segment.VideoContent.Width),
|
||||
contentId: segment.Id,
|
||||
startPlayback: true,
|
||||
objective: segment.ObjectiveText,
|
||||
onStop: StopCurrentContentSegment);
|
||||
}
|
||||
else
|
||||
{
|
||||
videoPlayer.LoadContent(
|
||||
contentPath: segment.VideoContent.ContentPath,
|
||||
videoSettings: new VideoPlayer.VideoSettings(segment.VideoContent.FileName),
|
||||
textSettings: null,
|
||||
contentId: segment.Id,
|
||||
startPlayback: true,
|
||||
objective: string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadActiveContentVideo() => LoadVideo(ActiveContentSegment);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Barotrauma
|
||||
{
|
||||
public Tutorial Tutorial;
|
||||
|
||||
public override bool Paused => Tutorial.Paused;
|
||||
|
||||
public TutorialMode(GameModePreset preset) : base(preset) { }
|
||||
|
||||
public override void Start()
|
||||
@@ -19,12 +21,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
base.AddToGUIUpdateList();
|
||||
Tutorial.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Tutorials;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -128,8 +129,9 @@ namespace Barotrauma
|
||||
if (GUI.DisableHUD) { return; }
|
||||
GameMode?.AddToGUIUpdateList();
|
||||
tabMenu?.AddToGUIUpdateList();
|
||||
ObjectiveManager.AddToGUIUpdateList();
|
||||
|
||||
if ((!(GameMode is CampaignMode campaign) || (!campaign.ForceMapUI && !campaign.ShowCampaignUI)) &&
|
||||
if ((GameMode is not CampaignMode campaign || (!campaign.ForceMapUI && !campaign.ShowCampaignUI)) &&
|
||||
!CoroutineManager.IsCoroutineRunning("LevelTransition") && !CoroutineManager.IsCoroutineRunning("SubmarineTransition"))
|
||||
{
|
||||
if (topLeftButtonGroup == null)
|
||||
@@ -223,6 +225,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
HintManager.Update();
|
||||
ObjectiveManager.VideoPlayer.Update();
|
||||
}
|
||||
|
||||
public void SetRespawnInfo(bool visible, string text, Color textColor, bool buttonsVisible, bool waitForNextRoundRespawn)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Tutorials;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -209,7 +210,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (item.CurrentHull == null) { continue; }
|
||||
if (item.GetComponent<Pump>() == null) { continue; }
|
||||
if (!item.HasTag("ballast")) { continue; }
|
||||
if (!item.HasTag("ballast") && !item.CurrentHull.RoomName.Contains("ballast", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
BallastHulls.Add(item.CurrentHull);
|
||||
}
|
||||
}
|
||||
@@ -383,6 +384,34 @@ namespace Barotrauma
|
||||
IgnoreReminder("tabmenu");
|
||||
}
|
||||
|
||||
public static void OnObtainedItem(Character character, Item item)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled || item == null) { return; }
|
||||
|
||||
if (DisplayHint($"onobtaineditem.{item.Prefab.Identifier}".ToIdentifier())) { return; }
|
||||
foreach (Identifier tag in item.GetTags())
|
||||
{
|
||||
if (DisplayHint($"onobtaineditem.{tag}".ToIdentifier())) { return; }
|
||||
}
|
||||
|
||||
if ((item.HasTag("geneticmaterial") && character.Inventory.FindItemByTag("geneticdevice".ToIdentifier(), recursive: true) != null) ||
|
||||
(item.HasTag("geneticdevice") && character.Inventory.FindItemByTag("geneticmaterial".ToIdentifier(), recursive: true) != null))
|
||||
{
|
||||
if (DisplayHint($"geneticmaterial.useinstructions".ToIdentifier())) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnStartDeconstructing(Character character, Deconstructor deconstructor)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled || deconstructor == null) { return; }
|
||||
if (deconstructor.InputContainer.Inventory.AllItems.All(it => it.GetComponent<GeneticMaterial>() is not null))
|
||||
{
|
||||
DisplayHint($"geneticmaterial.onrefiningorcombining".ToIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnStoleItem(Character character, Item item)
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
@@ -507,7 +536,7 @@ namespace Barotrauma
|
||||
if (!CanDisplayHints()) { return; }
|
||||
if (character != Character.Controlled) { return; }
|
||||
// Could make this more generic if there will ever be any other status effect related hints
|
||||
if (!(component is Repairable) || actionType != ActionType.OnFailure) { return; }
|
||||
if (component is not Repairable || actionType != ActionType.OnFailure) { return; }
|
||||
DisplayHint("onrepairfailed".ToIdentifier());
|
||||
}
|
||||
|
||||
@@ -563,7 +592,7 @@ namespace Barotrauma
|
||||
foreach (var me in gap.linkedTo)
|
||||
{
|
||||
if (me == Character.Controlled.CurrentHull) { continue; }
|
||||
if (!(me is Hull adjacentHull)) { continue; }
|
||||
if (me is not Hull adjacentHull) { continue; }
|
||||
if (!IsOnFriendlySub()) { continue; }
|
||||
if (IsWearingDivingSuit()) { continue; }
|
||||
if (adjacentHull.LethalPressure > 5.0f && DisplayHint("onadjacenthull.highpressure".ToIdentifier())) { return; }
|
||||
@@ -720,6 +749,7 @@ namespace Barotrauma
|
||||
if (requireControllingCharacter && Character.Controlled == null) { return false; }
|
||||
var gameMode = GameMain.GameSession?.GameMode;
|
||||
if (!(gameMode is CampaignMode || gameMode is MissionMode)) { return false; }
|
||||
if (ObjectiveManager.AnyObjectives) { return false; }
|
||||
if (requireGameScreen && Screen.Selected != GameMain.GameScreen) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class MedicalClinic
|
||||
internal sealed partial class MedicalClinic
|
||||
{
|
||||
public enum RequestResult
|
||||
{
|
||||
@@ -19,63 +19,11 @@ namespace Barotrauma
|
||||
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;
|
||||
}
|
||||
}
|
||||
public readonly record struct RequestAction<T>(Action<T> Callback, DateTimeOffset Timeout);
|
||||
public readonly record struct AfflictionRequest(RequestResult Result, ImmutableArray<NetAffliction> Afflictions);
|
||||
public readonly record struct PendingRequest(RequestResult Result, NetCollection<NetCrewMember> CrewMembers);
|
||||
public readonly record struct CallbackOnlyRequest(RequestResult Result);
|
||||
public readonly record struct HealRequest(RequestResult Result, HealRequestResult HealResult);
|
||||
|
||||
private readonly List<RequestAction<AfflictionRequest>> afflictionRequests = new List<RequestAction<AfflictionRequest>>();
|
||||
private readonly List<RequestAction<PendingRequest>> pendingHealRequests = new List<RequestAction<PendingRequest>>();
|
||||
@@ -96,7 +44,7 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(info is { Character: { CharacterHealth: { } health } }))
|
||||
if (info is not { Character.CharacterHealth: { } health })
|
||||
{
|
||||
onReceived.Invoke(new AfflictionRequest(RequestResult.Error, ImmutableArray<NetAffliction>.Empty));
|
||||
return;
|
||||
@@ -123,14 +71,14 @@ namespace Barotrauma
|
||||
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(afflictionRequests, now, onTimeout: static callback => { callback(new AfflictionRequest(RequestResult.Timeout, ImmutableArray<NetAffliction>.Empty)); });
|
||||
UpdateQueue(pendingHealRequests, now, onTimeout: static callback => { callback(new PendingRequest(RequestResult.Timeout, NetCollection<NetCrewMember>.Empty)); });
|
||||
UpdateQueue(healAllRequests, now, onTimeout: static 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)); }
|
||||
static void CallbackOnlyTimeout(Action<CallbackOnlyRequest> callback) { callback(new CallbackOnlyRequest(RequestResult.Timeout)); }
|
||||
}
|
||||
|
||||
public bool IsAfflictionPending(NetCrewMember character, NetAffliction affliction)
|
||||
@@ -148,9 +96,9 @@ namespace Barotrauma
|
||||
private static bool TryDequeue<T>(List<RequestAction<T>> requestQueue, out Action<T> result)
|
||||
{
|
||||
RequestAction<T>? first = requestQueue.FirstOrNull();
|
||||
if (!(first is { } action))
|
||||
if (first is not { } action)
|
||||
{
|
||||
result = _ => { };
|
||||
result = static _ => { };
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -191,11 +139,25 @@ namespace Barotrauma
|
||||
|
||||
private static int GetPing()
|
||||
{
|
||||
if (GameMain.IsSingleplayer || !(GameMain.Client?.Name is { } ownName) || !(GameMain.NetworkMember?.ConnectedClients is { } clients)) { return 0; }
|
||||
if (GameMain.IsSingleplayer || GameMain.Client?.Name is not { } ownName || GameMain.NetworkMember?.ConnectedClients is not { } clients) { return 0; }
|
||||
|
||||
return (from client in clients where client.Name == ownName select client.Ping).FirstOrDefault();
|
||||
}
|
||||
|
||||
public void TreatAllButtonAction(Action<CallbackOnlyRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
AddEverythingToPending();
|
||||
onReceived(new CallbackOnlyRequest(RequestResult.Success));
|
||||
OnUpdate?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
addRequests.Add(new RequestAction<CallbackOnlyRequest>(onReceived, GetTimeout()));
|
||||
ClientSend(null, NetworkHeader.ADD_EVERYTHING_TO_PENDING, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
public void HealAllButtonAction(Action<HealRequest> onReceived)
|
||||
{
|
||||
if (GameMain.IsSingleplayer)
|
||||
@@ -296,8 +258,11 @@ namespace Barotrauma
|
||||
|
||||
private void NewAdditonReceived(IReadMessage inc, MessageFlag flag)
|
||||
{
|
||||
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
|
||||
InsertPendingCrewMember(crewMember);
|
||||
var crewMembers = INetSerializableStruct.Read<NetCollection<NetCrewMember>>(inc);
|
||||
foreach (var crewMember in crewMembers)
|
||||
{
|
||||
InsertPendingCrewMember(crewMember);
|
||||
}
|
||||
if (flag == MessageFlag.Response && TryDequeue(addRequests, out var callback))
|
||||
{
|
||||
callback(new CallbackOnlyRequest(RequestResult.Success));
|
||||
@@ -318,11 +283,7 @@ namespace Barotrauma
|
||||
|
||||
private static void SendAfflictionRequest(CharacterInfo info)
|
||||
{
|
||||
INetSerializableStruct crewMember = new NetCrewMember
|
||||
{
|
||||
CharacterInfo = info,
|
||||
Afflictions = Array.Empty<NetAffliction>()
|
||||
};
|
||||
INetSerializableStruct crewMember = new NetCrewMember(info);
|
||||
|
||||
ClientSend(crewMember, NetworkHeader.REQUEST_AFFLICTIONS, DeliveryMethod.Unreliable);
|
||||
}
|
||||
@@ -337,17 +298,17 @@ namespace Barotrauma
|
||||
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
|
||||
if (TryDequeue(afflictionRequests, out var callback))
|
||||
{
|
||||
RequestResult result = crewMember.CharacterInfoID == 0 ? RequestResult.Error : RequestResult.Success;
|
||||
RequestResult result = crewMember.CharacterInfoID is 0 ? RequestResult.Error : RequestResult.Success;
|
||||
callback(new AfflictionRequest(result, crewMember.Afflictions.ToImmutableArray()));
|
||||
}
|
||||
}
|
||||
|
||||
private void PendingRequestReceived(IReadMessage inc)
|
||||
{
|
||||
NetPendingCrew pendingCrew = INetSerializableStruct.Read<NetPendingCrew>(inc);
|
||||
var pendingCrew = INetSerializableStruct.Read<NetCollection<NetCrewMember>>(inc);
|
||||
if (TryDequeue(pendingHealRequests, out var callback))
|
||||
{
|
||||
callback(new PendingRequest(RequestResult.Success, pendingCrew.CrewMembers.ToImmutableArray()));
|
||||
callback(new PendingRequest(RequestResult.Success, pendingCrew));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,599 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Tutorials;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
static class ObjectiveManager
|
||||
{
|
||||
public class Segment
|
||||
{
|
||||
public readonly record struct Text(
|
||||
Identifier Tag,
|
||||
int Width = DefaultWidth,
|
||||
int Height = DefaultHeight,
|
||||
Anchor Anchor = Anchor.Center);
|
||||
|
||||
public readonly record struct Video(
|
||||
string FullPath,
|
||||
Identifier TextTag,
|
||||
int Width = DefaultWidth,
|
||||
int Height = DefaultHeight)
|
||||
{
|
||||
public string FileName => Path.GetFileName(FullPath.CleanUpPath());
|
||||
public string ContentPath => Path.GetDirectoryName(FullPath.CleanUpPath());
|
||||
}
|
||||
|
||||
private const int DefaultWidth = 450;
|
||||
private const int DefaultHeight = 80;
|
||||
|
||||
public GUIImage ObjectiveStateIndicator;
|
||||
public GUIButton ObjectiveButton;
|
||||
public GUITextBlock LinkedTextBlock;
|
||||
public LocalizedString ObjectiveText;
|
||||
|
||||
public readonly Identifier Id;
|
||||
public readonly Text TextContent;
|
||||
public readonly Video VideoContent;
|
||||
public readonly AutoPlayVideo AutoPlayVideo;
|
||||
|
||||
public Action OnClickObjective;
|
||||
|
||||
public bool IsCompleted { get; set; }
|
||||
|
||||
public bool CanBeCompleted { get; set; }
|
||||
|
||||
public Identifier ParentId { get; set; }
|
||||
|
||||
public TutorialSegmentType SegmentType { get; private set; }
|
||||
|
||||
public static Segment CreateInfoBoxSegment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag, autoPlayVideo, textContent, videoContent);
|
||||
}
|
||||
|
||||
public static Segment CreateMessageBoxSegment(Identifier id, Identifier objectiveTextTag, Action onClickObjective)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag, onClickObjective);
|
||||
}
|
||||
|
||||
public static Segment CreateObjectiveSegment(Identifier id, Identifier objectiveTextTag)
|
||||
{
|
||||
return new Segment(id, objectiveTextTag);
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, AutoPlayVideo autoPlayVideo, Text textContent = default, Video videoContent = default)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
AutoPlayVideo = autoPlayVideo;
|
||||
TextContent = textContent;
|
||||
VideoContent = videoContent;
|
||||
SegmentType = TutorialSegmentType.InfoBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag, Action onClickObjective)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
OnClickObjective = onClickObjective;
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
}
|
||||
|
||||
private Segment(Identifier id, Identifier objectiveTextTag)
|
||||
{
|
||||
Id = id;
|
||||
ObjectiveText = TextManager.ParseInputTypes(TextManager.Get(objectiveTextTag));
|
||||
SegmentType = TutorialSegmentType.Objective;
|
||||
}
|
||||
|
||||
public void ConnectMessageBox(Segment messageBoxSegment)
|
||||
{
|
||||
SegmentType = TutorialSegmentType.MessageBox;
|
||||
OnClickObjective = messageBoxSegment.OnClickObjective;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly record struct ScreenSettings(
|
||||
Point ScreenResolution = default,
|
||||
float UiScale = default,
|
||||
WindowMode WindowMode = default)
|
||||
{
|
||||
public bool HaveChanged() =>
|
||||
GameMain.GraphicsWidth != ScreenResolution.X ||
|
||||
GameMain.GraphicsHeight != ScreenResolution.Y ||
|
||||
GUI.Scale != UiScale ||
|
||||
GameSettings.CurrentConfig.Graphics.DisplayMode != WindowMode;
|
||||
};
|
||||
|
||||
private const float ObjectiveComponentAnimationTime = 1.5f;
|
||||
|
||||
public static bool ContentRunning { get; private set; }
|
||||
|
||||
public static VideoPlayer VideoPlayer { get; } = new VideoPlayer();
|
||||
|
||||
private static Segment ActiveContentSegment { get; set; }
|
||||
|
||||
private readonly static List<Segment> activeObjectives = new List<Segment>();
|
||||
private static GUIComponent infoBox;
|
||||
private static Action infoBoxClosedCallback;
|
||||
private static ScreenSettings screenSettings;
|
||||
private static GUILayoutGroup objectiveGroup;
|
||||
private static LocalizedString objectiveTextTranslated;
|
||||
|
||||
public static void AddToGUIUpdateList()
|
||||
{
|
||||
if (screenSettings.HaveChanged())
|
||||
{
|
||||
CreateObjectiveFrame();
|
||||
}
|
||||
if (activeObjectives.Count > 0 && GameMain.GameSession?.Campaign is not { ShowCampaignUI: true })
|
||||
{
|
||||
objectiveGroup?.AddToGUIUpdateList(order: -1);
|
||||
}
|
||||
infoBox?.AddToGUIUpdateList(order: 100);
|
||||
VideoPlayer.AddToGUIUpdateList(order: 100);
|
||||
}
|
||||
|
||||
public static void TriggerTutorialSegment(Segment segment, bool connectObjective = false)
|
||||
{
|
||||
if (segment.SegmentType != TutorialSegmentType.InfoBox)
|
||||
{
|
||||
activeObjectives.Add(segment);
|
||||
AddToObjectiveList(segment, connectObjective);
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
ActiveContentSegment = segment;
|
||||
|
||||
var title = TextManager.Get(segment.Id);
|
||||
LocalizedString tutorialText = TextManager.GetFormatted(segment.TextContent.Tag);
|
||||
tutorialText = TextManager.ParseInputTypes(tutorialText);
|
||||
|
||||
switch (segment.AutoPlayVideo)
|
||||
{
|
||||
case AutoPlayVideo.Yes:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: LoadActiveContentVideo);
|
||||
break;
|
||||
case AutoPlayVideo.No:
|
||||
infoBox = CreateInfoFrame(
|
||||
title,
|
||||
tutorialText,
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: StopCurrentContentSegment,
|
||||
onVideoButtonClicked: LoadActiveContentVideo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompleteTutorialSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment || !segment.CanBeCompleted || segment.IsCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!MarkSegmentCompleted(segment))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent($"Tutorial:{tutorialMode.Tutorial?.Identifier}:{segmentId}:Completed");
|
||||
}
|
||||
else if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent($"Tutorial:CampaignMode:{segmentId}:Completed");
|
||||
campaign?.CampaignMetadata?.SetValue(segmentId, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool MarkSegmentCompleted(Segment segment, bool flash = true)
|
||||
{
|
||||
segment.IsCompleted = true;
|
||||
if (GUIStyle.GetComponentStyle("ObjectiveIndicatorCompleted") is GUIComponentStyle style)
|
||||
{
|
||||
if (segment.ObjectiveStateIndicator.Style == style)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
segment.ObjectiveStateIndicator.ApplyStyle(style);
|
||||
}
|
||||
if (flash)
|
||||
{
|
||||
segment.ObjectiveStateIndicator.Parent.Flash(color: GUIStyle.Green, flashDuration: 0.35f, useRectangleFlash: true);
|
||||
}
|
||||
segment.ObjectiveButton.OnClicked = null;
|
||||
segment.ObjectiveButton.CanBeFocused = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void RemoveTutorialSegment(Identifier segmentId)
|
||||
{
|
||||
if (GetActiveObjective(segmentId) is not Segment segment)
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode)
|
||||
{
|
||||
DebugConsole.AddWarning($"Warning: tried to remove the tutorial segment \"{segmentId}\" in tutorial \"{tutorialMode.Tutorial?.Identifier}\" but it isn't active!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
segment.ObjectiveStateIndicator.FadeOut(ObjectiveComponentAnimationTime, false);
|
||||
segment.LinkedTextBlock.FadeOut(ObjectiveComponentAnimationTime, false);
|
||||
var parent = segment.LinkedTextBlock.Parent;
|
||||
parent.FadeOut(ObjectiveComponentAnimationTime, true, onRemove: () =>
|
||||
{
|
||||
activeObjectives.Remove(segment);
|
||||
objectiveGroup?.Recalculate();
|
||||
});
|
||||
parent.RectTransform.MoveOverTime(GetObjectiveHiddenPosition(parent.RectTransform), ObjectiveComponentAnimationTime);
|
||||
segment.ObjectiveButton.OnClicked = null;
|
||||
segment.ObjectiveButton.CanBeFocused = false;
|
||||
}
|
||||
|
||||
public static void CloseActiveContentGUI()
|
||||
{
|
||||
if (VideoPlayer.IsPlaying)
|
||||
{
|
||||
VideoPlayer.Stop();
|
||||
}
|
||||
else if (infoBox != null)
|
||||
{
|
||||
CloseInfoFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearContent()
|
||||
{
|
||||
ContentRunning = false;
|
||||
infoBox = null;
|
||||
}
|
||||
|
||||
public static void ResetUI()
|
||||
{
|
||||
ContentRunning = false;
|
||||
infoBox = null;
|
||||
VideoPlayer.Remove();
|
||||
}
|
||||
|
||||
#region Objectives
|
||||
private static Segment GetActiveObjective(Identifier id) => activeObjectives.FirstOrDefault(s => s.Id == id);
|
||||
|
||||
public static void ResetObjectives()
|
||||
{
|
||||
activeObjectives.Clear();
|
||||
ActiveContentSegment = null;
|
||||
CreateObjectiveFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the objective list that holds the objectives (called on start and on resolution change)
|
||||
/// </summary>
|
||||
private static void CreateObjectiveFrame()
|
||||
{
|
||||
var objectiveListFrame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.TutorialObjectiveListArea, GUI.Canvas), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
objectiveGroup = new GUILayoutGroup(new RectTransform(Vector2.One, objectiveListFrame.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = (int)GUIStyle.Font.LineHeight
|
||||
};
|
||||
for (int i = 0; i < activeObjectives.Count; i++)
|
||||
{
|
||||
AddToObjectiveList(activeObjectives[i]);
|
||||
}
|
||||
screenSettings = new ScreenSettings(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Scale, GameSettings.CurrentConfig.Graphics.DisplayMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops content running and adds the active segment to the objective list
|
||||
/// </summary>
|
||||
private static void StopCurrentContentSegment()
|
||||
{
|
||||
if (!ActiveContentSegment.ObjectiveText.IsNullOrEmpty())
|
||||
{
|
||||
activeObjectives.Add(ActiveContentSegment);
|
||||
AddToObjectiveList(ActiveContentSegment);
|
||||
}
|
||||
ContentRunning = false;
|
||||
ActiveContentSegment = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the segment to the objective list
|
||||
/// </summary>
|
||||
private static void AddToObjectiveList(Segment segment, bool connectExisting = false)
|
||||
{
|
||||
if (connectExisting)
|
||||
{
|
||||
if (activeObjectives.Find(o => o.Id == segment.Id) is { } existingSegment)
|
||||
{
|
||||
existingSegment.ConnectMessageBox(segment);
|
||||
SetButtonBehavior(existingSegment);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var frameRt = new RectTransform(new Vector2(1.0f, 0.1f), objectiveGroup.RectTransform)
|
||||
{
|
||||
MinSize = new Point(0, objectiveGroup.AbsoluteSpacing)
|
||||
};
|
||||
Segment parentSegment = activeObjectives.FirstOrDefault(s => s.Id == segment.ParentId);
|
||||
if (parentSegment is not null)
|
||||
{
|
||||
// Add this child as the last child in case there are other existing children already
|
||||
int totalChildren = activeObjectives.Count(s => s.ParentId == segment.ParentId);
|
||||
int childIndex = activeObjectives.IndexOf(parentSegment) + totalChildren;
|
||||
if (objectiveGroup.RectTransform.GetChildIndex(frameRt) != childIndex)
|
||||
{
|
||||
frameRt.RepositionChildInHierarchy(childIndex);
|
||||
activeObjectives.Remove(segment);
|
||||
activeObjectives.Insert(childIndex, segment);
|
||||
}
|
||||
}
|
||||
frameRt.AbsoluteOffset = GetObjectiveHiddenPosition();
|
||||
|
||||
var frame = new GUIFrame(frameRt, style: null)
|
||||
{
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
objectiveGroup.Recalculate();
|
||||
|
||||
int textWidth = parentSegment is null ? frameRt.Rect.Width - objectiveGroup.AbsoluteSpacing
|
||||
: frameRt.Rect.Width - 2 * objectiveGroup.AbsoluteSpacing;
|
||||
segment.LinkedTextBlock = new GUITextBlock(
|
||||
new RectTransform(new Point(textWidth, 0), frame.RectTransform, anchor: Anchor.TopRight),
|
||||
TextManager.ParseInputTypes(segment.ObjectiveText),
|
||||
wrap: true);
|
||||
|
||||
var size = new Point(segment.LinkedTextBlock.Rect.Width, segment.LinkedTextBlock.Rect.Height);
|
||||
segment.LinkedTextBlock.RectTransform.NonScaledSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.MinSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.MaxSize = size;
|
||||
segment.LinkedTextBlock.RectTransform.IsFixedSize = true;
|
||||
frame.RectTransform.Resize(new Point(frame.Rect.Width, segment.LinkedTextBlock.RectTransform.Rect.Height), resizeChildren: false);
|
||||
frame.RectTransform.IsFixedSize = true;
|
||||
|
||||
var indicatorRt = new RectTransform(new Point(objectiveGroup.AbsoluteSpacing), frame.RectTransform, isFixedSize: true);
|
||||
if (parentSegment is not null)
|
||||
{
|
||||
indicatorRt.AbsoluteOffset = new Point(objectiveGroup.AbsoluteSpacing, 0);
|
||||
}
|
||||
segment.ObjectiveStateIndicator = new GUIImage(indicatorRt, "ObjectiveIndicatorIncomplete");
|
||||
|
||||
SetTransparent(segment.LinkedTextBlock);
|
||||
|
||||
objectiveTextTranslated ??= TextManager.Get("Tutorial.Objective");
|
||||
segment.ObjectiveButton = new GUIButton(new RectTransform(Vector2.One, segment.LinkedTextBlock.RectTransform, Anchor.TopLeft, Pivot.TopLeft), style: null)
|
||||
{
|
||||
ToolTip = objectiveTextTranslated
|
||||
};
|
||||
SetButtonBehavior(segment);
|
||||
SetTransparent(segment.ObjectiveButton);
|
||||
|
||||
frameRt.MoveOverTime(new Point(0, frameRt.AbsoluteOffset.Y), ObjectiveComponentAnimationTime, onDoneMoving: () => objectiveGroup?.Recalculate());
|
||||
|
||||
// Check if the objective has already been completed in the campaign
|
||||
if (!segment.IsCompleted && GameMain.GameSession?.Campaign?.CampaignMetadata is CampaignMetadata data && data.GetBoolean(segment.Id))
|
||||
{
|
||||
MarkSegmentCompleted(segment, flash: false);
|
||||
}
|
||||
|
||||
static void SetTransparent(GUIComponent component) => component.Color = component.HoverColor = component.PressedColor = component.SelectedColor = Color.Transparent;
|
||||
|
||||
void SetButtonBehavior(Segment segment)
|
||||
{
|
||||
segment.ObjectiveButton.CanBeFocused = segment.SegmentType != TutorialSegmentType.Objective;
|
||||
segment.ObjectiveButton.OnClicked = (GUIButton btn, object userdata) =>
|
||||
{
|
||||
if (segment.SegmentType == TutorialSegmentType.InfoBox)
|
||||
{
|
||||
if (segment.AutoPlayVideo == AutoPlayVideo.Yes)
|
||||
{
|
||||
ReplaySegmentVideo(segment);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowSegmentText(segment);
|
||||
}
|
||||
}
|
||||
else if (segment.SegmentType == TutorialSegmentType.MessageBox)
|
||||
{
|
||||
segment.OnClickObjective?.Invoke();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReplaySegmentVideo(Segment segment)
|
||||
{
|
||||
if (ContentRunning) { return; }
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
LoadVideo(segment);
|
||||
}
|
||||
|
||||
private static void ShowSegmentText(Segment segment)
|
||||
{
|
||||
if (ContentRunning) { return; }
|
||||
Inventory.DraggingItems.Clear();
|
||||
ContentRunning = true;
|
||||
ActiveContentSegment = segment;
|
||||
infoBox = CreateInfoFrame(
|
||||
TextManager.Get(segment.Id),
|
||||
TextManager.Get(segment.TextContent.Tag),
|
||||
segment.TextContent.Width,
|
||||
segment.TextContent.Height,
|
||||
segment.TextContent.Anchor,
|
||||
hasButton: true,
|
||||
onInfoBoxClosed: () => ContentRunning = false,
|
||||
onVideoButtonClicked: () => LoadVideo(segment));
|
||||
}
|
||||
|
||||
private static Point GetObjectiveHiddenPosition(RectTransform rt = null)
|
||||
{
|
||||
return new Point(GameMain.GraphicsWidth - objectiveGroup.Rect.X, rt?.AbsoluteOffset.Y ?? 0);
|
||||
}
|
||||
|
||||
public static Segment GetObjective(Identifier identifier)
|
||||
{
|
||||
return activeObjectives.FirstOrDefault(o => o.Id == identifier);
|
||||
}
|
||||
|
||||
public static bool AllActiveObjectivesCompleted()
|
||||
{
|
||||
return activeObjectives.None() || activeObjectives.All(o => !o.CanBeCompleted || o.IsCompleted);
|
||||
}
|
||||
|
||||
public static bool AnyObjectives => activeObjectives.Any();
|
||||
|
||||
#endregion
|
||||
|
||||
#region InfoFrame
|
||||
|
||||
private static void CloseInfoFrame() => CloseInfoFrame(null, null);
|
||||
|
||||
private static bool CloseInfoFrame(GUIButton button, object userData)
|
||||
{
|
||||
infoBox = null;
|
||||
infoBoxClosedCallback?.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// Creates and displays a tutorial info box
|
||||
/// </summary>
|
||||
private static GUIComponent CreateInfoFrame(LocalizedString title, LocalizedString text, int width = 300, int height = 80, Anchor anchor = Anchor.TopRight, bool hasButton = false, Action onInfoBoxClosed = null, Action onVideoButtonClicked = null)
|
||||
{
|
||||
if (hasButton)
|
||||
{
|
||||
height += 60;
|
||||
}
|
||||
|
||||
width = (int)(width * GUI.Scale);
|
||||
height = (int)(height * GUI.Scale);
|
||||
|
||||
LocalizedString wrappedText = ToolBox.WrapText(text, width, GUIStyle.Font);
|
||||
height += (int)GUIStyle.Font.MeasureString(wrappedText).Y;
|
||||
|
||||
if (title.Length > 0)
|
||||
{
|
||||
height += (int)GUIStyle.Font.MeasureString(title).Y + (int)(150 * GUI.Scale);
|
||||
}
|
||||
|
||||
var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), background.RectTransform, anchor));
|
||||
infoBlock.Flash(GUIStyle.Green);
|
||||
|
||||
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), infoBlock.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = 5
|
||||
};
|
||||
|
||||
if (title.Length > 0)
|
||||
{
|
||||
var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform),
|
||||
title, font: GUIStyle.LargeFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
|
||||
titleBlock.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
|
||||
text = RichString.Rich(text);
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true);
|
||||
|
||||
textBlock.RectTransform.IsFixedSize = true;
|
||||
infoBoxClosedCallback = onInfoBoxClosed;
|
||||
|
||||
if (hasButton)
|
||||
{
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), infoContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.1f
|
||||
};
|
||||
buttonContainer.RectTransform.IsFixedSize = true;
|
||||
|
||||
if (onVideoButtonClicked != null)
|
||||
{
|
||||
buttonContainer.Stretch = true;
|
||||
var videoButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("Video"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = (GUIButton button, object obj) =>
|
||||
{
|
||||
onVideoButtonClicked();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonContainer.Stretch = false;
|
||||
buttonContainer.ChildAnchor = Anchor.Center;
|
||||
}
|
||||
|
||||
var okButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("OK"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = CloseInfoFrame
|
||||
};
|
||||
}
|
||||
|
||||
infoBlock.RectTransform.NonScaledSize = new Point(infoBlock.Rect.Width, (int)(infoContent.Children.Sum(c => c.Rect.Height + infoContent.AbsoluteSpacing) / infoContent.RectTransform.RelativeSize.Y));
|
||||
|
||||
SoundPlayer.PlayUISound(GUISoundType.UIMessage);
|
||||
|
||||
return background;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Video
|
||||
|
||||
private static void LoadVideo(Segment segment)
|
||||
{
|
||||
if (segment.AutoPlayVideo == AutoPlayVideo.Yes)
|
||||
{
|
||||
VideoPlayer.LoadContent(
|
||||
contentPath: segment.VideoContent.ContentPath,
|
||||
videoSettings: new VideoPlayer.VideoSettings(segment.VideoContent.FileName),
|
||||
textSettings: new VideoPlayer.TextSettings(segment.VideoContent.TextTag, segment.VideoContent.Width),
|
||||
contentId: segment.Id,
|
||||
startPlayback: true,
|
||||
objective: segment.ObjectiveText,
|
||||
onStop: StopCurrentContentSegment);
|
||||
}
|
||||
else
|
||||
{
|
||||
VideoPlayer.LoadContent(
|
||||
contentPath: segment.VideoContent.ContentPath,
|
||||
videoSettings: new VideoPlayer.VideoSettings(segment.VideoContent.FileName),
|
||||
textSettings: null,
|
||||
contentId: segment.Id,
|
||||
startPlayback: true,
|
||||
objective: string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadActiveContentVideo() => LoadVideo(ActiveContentSegment);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -21,7 +21,6 @@ namespace Barotrauma
|
||||
|
||||
private readonly GameMode gameMode;
|
||||
|
||||
private readonly float initialLocationReputation;
|
||||
private readonly Dictionary<Faction, float> initialFactionReputations = new Dictionary<Faction, float>();
|
||||
|
||||
public GUILayoutGroup ButtonArea { get; private set; }
|
||||
@@ -36,7 +35,6 @@ namespace Barotrauma
|
||||
this.selectedMissions = selectedMissions.ToList();
|
||||
this.startLocation = startLocation;
|
||||
this.endLocation = endLocation;
|
||||
initialLocationReputation = startLocation?.Reputation?.Value ?? 0.0f;
|
||||
if (gameMode is CampaignMode campaignMode)
|
||||
{
|
||||
foreach (Faction faction in campaignMode.Factions)
|
||||
@@ -214,11 +212,12 @@ namespace Barotrauma
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions);
|
||||
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions.Where(m => m.Prefab.ShowInMenus));
|
||||
if (!selectedMissions.Any() && startLocation != null)
|
||||
{
|
||||
foreach (Mission mission in startLocation.SelectedMissions)
|
||||
{
|
||||
if (!mission.Prefab.ShowInMenus) { continue; }
|
||||
if (mission.Locations[0] == mission.Locations[1] ||
|
||||
mission.Locations.Contains(campaignMode?.Map.SelectedLocation))
|
||||
{
|
||||
@@ -401,7 +400,7 @@ namespace Barotrauma
|
||||
};
|
||||
reputationList.ContentBackground.Color = Color.Transparent;
|
||||
|
||||
if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
|
||||
/*if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
|
||||
{
|
||||
var iconStyle = GUIStyle.GetComponentStyle("LocationReputationIcon");
|
||||
var locationFrame = CreateReputationElement(
|
||||
@@ -411,7 +410,7 @@ namespace Barotrauma
|
||||
startLocation.Type.Name, "",
|
||||
iconStyle?.GetDefaultSprite(), startLocation.Type.GetPortrait(0), iconStyle?.Color ?? Color.White);
|
||||
CreatePathUnlockElement(locationFrame, null, startLocation);
|
||||
}
|
||||
}*/
|
||||
|
||||
foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
|
||||
{
|
||||
@@ -462,27 +461,25 @@ namespace Barotrauma
|
||||
if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
|
||||
|
||||
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
|
||||
var unlockEvent =
|
||||
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == gateLocation.LevelData.Biome.Identifier) ??
|
||||
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == Identifier.Empty);
|
||||
var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
|
||||
|
||||
if (unlockEvent == null) { continue; }
|
||||
if (string.IsNullOrEmpty(unlockEvent.UnlockPathFaction) || unlockEvent.UnlockPathFaction.Equals("location", StringComparison.OrdinalIgnoreCase))
|
||||
if (unlockEvent.Faction.IsEmpty)
|
||||
{
|
||||
if (location == null || gateLocation != location) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (faction == null || faction.Prefab.Identifier != unlockEvent.UnlockPathFaction) { continue; }
|
||||
if (faction == null || faction.Prefab.Identifier != unlockEvent.Faction) { continue; }
|
||||
}
|
||||
|
||||
if (unlockEvent != null)
|
||||
{
|
||||
Reputation unlockReputation = gateLocation.Reputation;
|
||||
Faction unlockFaction = null;
|
||||
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
|
||||
if (!unlockEvent.Faction.IsEmpty)
|
||||
{
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.UnlockPathFaction);
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
|
||||
unlockReputation = unlockFaction?.Reputation;
|
||||
}
|
||||
float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
|
||||
@@ -506,7 +503,7 @@ namespace Barotrauma
|
||||
|
||||
private LocalizedString GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
|
||||
{
|
||||
string locationName = Submarine.MainSub.AtEndExit ? endLocation?.Name : startLocation?.Name;
|
||||
string locationName = Submarine.MainSub is { AtEndExit: true } ? endLocation?.Name : startLocation?.Name;
|
||||
|
||||
string textTag;
|
||||
if (gameOver)
|
||||
@@ -543,6 +540,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (startLocation?.Biome != null && startLocation.Biome.IsEndBiome)
|
||||
{
|
||||
locationName ??= startLocation.Name;
|
||||
}
|
||||
|
||||
if (textTag == null) { return ""; }
|
||||
|
||||
if (locationName == null)
|
||||
|
||||
@@ -774,7 +774,6 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isEquippable = item.AllowedSlots.Any(s => s != InvSlotType.Any);
|
||||
var selectedContainer = character.SelectedItem?.GetComponent<ItemContainer>();
|
||||
|
||||
if (selectedContainer != null &&
|
||||
@@ -802,8 +801,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (character.HeldItems.Any(i =>
|
||||
i.OwnInventory != null &&
|
||||
/*disallow putting into equipped item if the item is equippable (equip as the quick action instead)*/
|
||||
((i.OwnInventory.CanBePut(item) && (allowInventorySwap || !isEquippable)) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
|
||||
(i.OwnInventory.CanBePut(item) || ((i.OwnInventory.Capacity == 1 || i.OwnInventory.Container.HasSubContainers) && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
|
||||
{
|
||||
return QuickUseAction.PutToEquippedItem;
|
||||
}
|
||||
@@ -977,7 +975,7 @@ namespace Barotrauma
|
||||
heldItem.OwnInventory.GetItemAt(0)?.Prefab == item.Prefab &&
|
||||
heldItem.OwnInventory.GetItemsAt(0).Count() > 1;
|
||||
if (heldItem.OwnInventory.TryPutItem(item, Character.Controlled) ||
|
||||
(heldItem.OwnInventory.Capacity == 1 && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: !disallowSwapping, allowCombine: false, user: Character.Controlled)))
|
||||
((heldItem.OwnInventory.Capacity == 1 || heldItem.OwnInventory.Container.HasSubContainers) && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: !disallowSwapping, allowCombine: false, user: Character.Controlled)))
|
||||
{
|
||||
success = true;
|
||||
for (int j = 0; j < capacity; j++)
|
||||
|
||||
@@ -25,14 +25,31 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Node node in nodes)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("swirlysmoke", node.WorldPosition, Vector2.Zero);
|
||||
|
||||
if (node.ParentIndex > -1)
|
||||
{
|
||||
Vector2 diff = nodes[node.ParentIndex].WorldPosition - node.WorldPosition;
|
||||
float dist = diff.Length();
|
||||
Vector2 normalizedDiff = diff / dist;
|
||||
for (float x = 0.0f; x < dist; x += 50.0f)
|
||||
{
|
||||
var spark = GameMain.ParticleManager.CreateParticle("ElectricShock", node.WorldPosition + normalizedDiff * x, Vector2.Zero);
|
||||
if (spark != null)
|
||||
{
|
||||
spark.Size *= 0.3f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawElectricity(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (timer <= 0.0f) { return; }
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (nodes[i].Length <= 1.0f) continue;
|
||||
if (nodes[i].Length <= 1.0f) { continue; }
|
||||
var node = nodes[i];
|
||||
electricitySprite.Draw(spriteBatch,
|
||||
(i + frameOffset) % electricitySprite.FrameCount,
|
||||
@@ -46,10 +63,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
for (int i = 1; i < nodes.Count; i++)
|
||||
{
|
||||
if (nodes[i].Length <= 1.0f) continue;
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(nodes[i].WorldPosition.X, -nodes[i].WorldPosition.Y), Vector2.One * 5, Color.LightCyan, isFilled: true);
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(nodes[i].WorldPosition.X, -nodes[i].WorldPosition.Y),
|
||||
new Vector2(nodes[nodes[i].ParentIndex].WorldPosition.X, -nodes[nodes[i].ParentIndex].WorldPosition.Y),
|
||||
Color.LightCyan,
|
||||
width: 3);
|
||||
|
||||
if (nodes[i].Length <= 1.0f) { continue; }
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(nodes[i].WorldPosition.X, -nodes[i].WorldPosition.Y), Vector2.One * 10, Color.LightCyan, isFilled: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +114,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (chargeSound != null)
|
||||
{
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling);
|
||||
if (chargeSoundChannel != null) chargeSoundChannel.Looping = true;
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling, freqMult: chargeSound.GetRandomFrequencyMultiplier());
|
||||
if (chargeSoundChannel != null) { chargeSoundChannel.Looping = true; }
|
||||
}
|
||||
}
|
||||
else if (chargeSoundChannel != null)
|
||||
|
||||
@@ -224,68 +224,60 @@ namespace Barotrauma.Items.Components
|
||||
if (character == null) { return false; }
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
if (targetSections.Count == 0) { return false; }
|
||||
Spray(deltaTime);
|
||||
if (targetSections.Count == 0) { return false; }
|
||||
Spray(character, deltaTime, applyColors: true);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//allow remote players to use the sprayer, but don't actually color the walls (we'll receive the data from the server)
|
||||
return character.IsRemotePlayer;
|
||||
Spray(character, deltaTime, applyColors: false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Spray(float deltaTime)
|
||||
public void Spray(Character user, float deltaTime, bool applyColors)
|
||||
{
|
||||
if (targetSections.Count == 0) { return; }
|
||||
|
||||
Item liquidItem = liquidContainer?.Inventory.FirstOrDefault();
|
||||
if (liquidItem == null) { return; }
|
||||
|
||||
bool isCleaning = false;
|
||||
liquidColors.TryGetValue(liquidItem.Prefab.Identifier, out color);
|
||||
|
||||
// Ethanol or other cleaning solvent
|
||||
if (color.A == 0) { isCleaning = true; }
|
||||
|
||||
float sizeAdjustedSprayStrength = SprayStrength / targetSections.Count;
|
||||
|
||||
if (!isCleaning)
|
||||
if (applyColors && targetSections.Any())
|
||||
{
|
||||
for (int i = 0; i < targetSections.Count; i++)
|
||||
// Ethanol or other cleaning solvent
|
||||
if (color.A == 0) { isCleaning = true; }
|
||||
float sizeAdjustedSprayStrength = SprayStrength / targetSections.Count;
|
||||
if (!isCleaning)
|
||||
{
|
||||
targetHull.IncreaseSectionColorOrStrength(targetSections[i], color, sizeAdjustedSprayStrength * deltaTime, true, false);
|
||||
for (int i = 0; i < targetSections.Count; i++)
|
||||
{
|
||||
targetHull.IncreaseSectionColorOrStrength(targetSections[i], color, sizeAdjustedSprayStrength * deltaTime, true, false);
|
||||
}
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
GameMain.GameSession.TimeSpentCleaning += deltaTime;
|
||||
}
|
||||
}
|
||||
if (GameMain.GameSession != null)
|
||||
else
|
||||
{
|
||||
GameMain.GameSession.TimeSpentCleaning += deltaTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < targetSections.Count; i++)
|
||||
{
|
||||
targetHull.CleanSection(targetSections[i], -sizeAdjustedSprayStrength * deltaTime, true);
|
||||
}
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
GameMain.GameSession.TimeSpentPainting += deltaTime;
|
||||
for (int i = 0; i < targetSections.Count; i++)
|
||||
{
|
||||
targetHull.CleanSection(targetSections[i], -sizeAdjustedSprayStrength * deltaTime, true);
|
||||
}
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
GameMain.GameSession.TimeSpentPainting += deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 particleStartPos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
|
||||
Vector2 particleEndPos = Vector2.Zero;
|
||||
for (int i = 0; i < targetSections.Count; i++)
|
||||
{
|
||||
particleEndPos += new Vector2(targetSections[i].Rect.Center.X, targetSections[i].Rect.Y - targetSections[i].Rect.Height / 2) + targetHull.Rect.Location.ToVector2();
|
||||
}
|
||||
particleEndPos /= targetSections.Count;
|
||||
if (targetHull?.Submarine != null)
|
||||
{
|
||||
particleEndPos += targetHull.Submarine.Position;
|
||||
}
|
||||
float dist = Vector2.Distance(particleStartPos, particleEndPos);
|
||||
|
||||
Vector2 particleEndPos = user.CursorWorldPosition;
|
||||
//the cursor position is not exact for remote players, we only know the direction they're aiming at but not the distance
|
||||
// -> use 50% range, looks good enough
|
||||
float dist = Math.Min(Vector2.Distance(particleStartPos, particleEndPos), Range * 0.5f);
|
||||
foreach (ParticleEmitter particleEmitter in particleEmitters)
|
||||
{
|
||||
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
|
||||
@@ -524,11 +524,13 @@ namespace Barotrauma.Items.Components
|
||||
VolumeProperty = subElement.GetAttributeIdentifier("volumeproperty", "")
|
||||
};
|
||||
|
||||
if (soundSelectionModes == null) soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>();
|
||||
if (soundSelectionModes == null)
|
||||
{
|
||||
soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>();
|
||||
}
|
||||
if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random)
|
||||
{
|
||||
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out SoundSelectionMode selectionMode);
|
||||
soundSelectionModes[type] = selectionMode;
|
||||
soundSelectionModes[type] = subElement.GetAttributeEnum("selectionmode", SoundSelectionMode.Random);
|
||||
}
|
||||
|
||||
if (!sounds.TryGetValue(itemSound.Type, out List<ItemSound> soundList))
|
||||
@@ -584,6 +586,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true))
|
||||
{
|
||||
bool hideDragIcons = GuiFrameSource.GetAttributeBool("hidedragicons", false);
|
||||
|
||||
var handle = new GUIDragHandle(new RectTransform(Vector2.One, GuiFrame.RectTransform, Anchor.Center),
|
||||
GuiFrame.RectTransform, style: null)
|
||||
{
|
||||
@@ -623,7 +627,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
|
||||
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f);
|
||||
new GUIButton(new RectTransform(new Point(buttonHeight), handle.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
|
||||
var settingsIcon = new GUIButton(new RectTransform(new Point(buttonHeight), handle.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(buttonHeight / 4), MinSize = new Point(buttonHeight) },
|
||||
style: "GUIButtonSettings")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
@@ -648,6 +652,12 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (hideDragIcons)
|
||||
{
|
||||
dragIcon.Visible = false;
|
||||
settingsIcon.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -309,21 +309,53 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Vector2 currentItemPos = transformedItemPos;
|
||||
|
||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||
if ((item.body != null && item.body.Dir == -1) || item.FlippedX)
|
||||
{
|
||||
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipVertically : SpriteEffects.FlipHorizontally;
|
||||
}
|
||||
if (item.FlippedY)
|
||||
{
|
||||
spriteEffects |= MathUtils.NearlyEqual(ItemRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
|
||||
}
|
||||
|
||||
bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
|
||||
|
||||
int i = 0;
|
||||
foreach (Item containedItem in Inventory.AllItems)
|
||||
{
|
||||
Vector2 itemPos = currentItemPos;
|
||||
var relatedItem = FindContainableItem(containedItem);
|
||||
if (relatedItem != null)
|
||||
{
|
||||
if (relatedItem.Hide.HasValue && relatedItem.Hide.Value) { continue; }
|
||||
if (relatedItem.ItemPos.HasValue)
|
||||
{
|
||||
Vector2 pos = relatedItem.ItemPos.Value;
|
||||
if (item.body != null)
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(item.body.DrawRotation);
|
||||
pos.X *= item.body.Dir;
|
||||
itemPos = Vector2.Transform(pos, transform) + item.body.DrawPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemPos = pos;
|
||||
// This code is aped based on above. Not tested.
|
||||
if (item.FlippedX)
|
||||
{
|
||||
itemPos.X = -itemPos.X;
|
||||
itemPos.X += item.Rect.Width;
|
||||
}
|
||||
if (item.FlippedY)
|
||||
{
|
||||
itemPos.Y = -itemPos.Y;
|
||||
itemPos.Y -= item.Rect.Height;
|
||||
}
|
||||
itemPos += new Vector2(item.Rect.X, item.Rect.Y);
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
itemPos += item.Submarine.DrawPosition;
|
||||
}
|
||||
if (Math.Abs(item.RotationRad) > 0.01f)
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(-item.RotationRad);
|
||||
itemPos = Vector2.Transform(itemPos - item.DrawPosition, transform) + item.DrawPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (containedItem?.Sprite == null) { continue; }
|
||||
|
||||
if (AutoInteractWithContained)
|
||||
@@ -343,19 +375,34 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
containedSpriteDepth = itemDepth + (containedSpriteDepth - (item.Sprite?.Depth ?? item.SpriteDepth)) / 10000.0f;
|
||||
|
||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||
float spriteRotation = ItemRotation;
|
||||
if (relatedItem != null && relatedItem.Rotation != 0)
|
||||
{
|
||||
spriteRotation = relatedItem.Rotation;
|
||||
}
|
||||
if ((item.body != null && item.body.Dir == -1) || item.FlippedX)
|
||||
{
|
||||
spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipVertically : SpriteEffects.FlipHorizontally;
|
||||
}
|
||||
if (item.FlippedY)
|
||||
{
|
||||
spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
|
||||
}
|
||||
|
||||
containedItem.Sprite.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(currentItemPos.X, -currentItemPos.Y),
|
||||
new Vector2(itemPos.X, -itemPos.Y),
|
||||
isWiringMode ? containedItem.GetSpriteColor(withHighlight: true) * 0.15f : containedItem.GetSpriteColor(withHighlight: true),
|
||||
origin,
|
||||
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation ),
|
||||
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation),
|
||||
containedItem.Scale,
|
||||
spriteEffects,
|
||||
depth: containedSpriteDepth);
|
||||
|
||||
foreach (ItemContainer ic in containedItem.GetComponents<ItemContainer>())
|
||||
{
|
||||
if (ic.hideItems) continue;
|
||||
if (ic.hideItems) { continue; }
|
||||
ic.DrawContainedItems(spriteBatch, containedSpriteDepth);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null)
|
||||
if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipX)
|
||||
{
|
||||
Light.LightSpriteEffect = Light.LightSpriteEffect == SpriteEffects.None ?
|
||||
SpriteEffects.FlipHorizontally : SpriteEffects.None;
|
||||
|
||||
@@ -1119,7 +1119,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (it.GetComponent<PowerContainer>() is { } battery)
|
||||
{
|
||||
int batteryCapacity = (int)(battery.Charge / battery.Capacity * 100f);
|
||||
int batteryCapacity = (int)(battery.Charge / battery.GetCapacity() * 100f);
|
||||
line2 = TextManager.GetWithVariable("statusmonitor.battery.tooltip", "[amount]", batteryCapacity.ToString());
|
||||
}
|
||||
else if (it.GetComponent<PowerTransfer>() is { } powerTransfer)
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Barotrauma.Items.Components
|
||||
Default,
|
||||
Disruption,
|
||||
Destructible,
|
||||
Door,
|
||||
LongRange
|
||||
}
|
||||
|
||||
@@ -110,6 +111,10 @@ namespace Barotrauma.Items.Components
|
||||
BlipType.Destructible,
|
||||
new Color[] { Color.TransparentBlack, new Color(74, 113, 75) * 0.8f, new Color(151, 236, 172) * 0.8f, new Color(153, 217, 234) * 0.8f }
|
||||
},
|
||||
{
|
||||
BlipType.Door,
|
||||
new Color[] { Color.TransparentBlack, new Color(73, 78, 86), new Color(66, 94, 100), new Color(47, 115, 58), new Color(255, 255, 255) }
|
||||
},
|
||||
{
|
||||
BlipType.LongRange,
|
||||
new Color[] { Color.TransparentBlack, Color.TransparentBlack, new Color(254, 68, 19) * 0.8f, Color.TransparentBlack }
|
||||
@@ -811,7 +816,7 @@ namespace Barotrauma.Items.Components
|
||||
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
|
||||
|
||||
float dist = (float)Math.Sqrt(distSqr);
|
||||
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500)
|
||||
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500 && t.IsWithinSector(transducerCenter))
|
||||
{
|
||||
Ping(t.WorldPosition, transducerCenter,
|
||||
Math.Min(t.SoundRange, range * 0.5f) * displayScale, 0, displayScale, Math.Min(t.SoundRange, range * 0.5f),
|
||||
@@ -971,7 +976,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
|
||||
if (Level.Loaded.StartLocation != null)
|
||||
if (Level.Loaded.StartLocation?.Type is { ShowSonarMarker: true })
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.StartLocation.Name,
|
||||
@@ -981,7 +986,7 @@ namespace Barotrauma.Items.Components
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
|
||||
if (Level.Loaded.EndLocation != null && Level.Loaded.Type == LevelData.LevelType.LocationConnection)
|
||||
if (Level.Loaded is { EndLocation.Type.ShowSonarMarker: true, Type: LevelData.LevelType.LocationConnection })
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
Level.Loaded.EndLocation.Name,
|
||||
@@ -1006,19 +1011,19 @@ namespace Barotrauma.Items.Components
|
||||
int missionIndex = 0;
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
if (!mission.SonarLabel.IsNullOrWhiteSpace())
|
||||
int i = 0;
|
||||
foreach ((LocalizedString label, Vector2 position) in mission.SonarLabels)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (Vector2 sonarPosition in mission.SonarPositions)
|
||||
if (!string.IsNullOrEmpty(label.Value))
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
mission.SonarLabel.Value,
|
||||
label.Value,
|
||||
mission.SonarIconIdentifier,
|
||||
"mission" + missionIndex + ":" + i,
|
||||
sonarPosition, transducerCenter,
|
||||
position, transducerCenter,
|
||||
displayScale, center, DisplayRadius * 0.95f);
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
missionIndex++;
|
||||
}
|
||||
@@ -1276,7 +1281,7 @@ namespace Barotrauma.Items.Components
|
||||
float indicatorSector = sector * 0.75f;
|
||||
float indicatorSectorLength = (float)(midLength / Math.Cos(indicatorSector));
|
||||
|
||||
bool withinSector =
|
||||
bool withinSector =
|
||||
(Math.Abs(diff.X) < steering.ActiveDockingSource.DistanceTolerance.X && Math.Abs(diff.Y) < steering.ActiveDockingSource.DistanceTolerance.Y) ||
|
||||
Vector2.Dot(normalizedDockingDir, MathUtils.RotatePoint(normalizedDockingDir, indicatorSector)) <
|
||||
Vector2.Dot(normalizedDockingDir, Vector2.Normalize(dockingDir));
|
||||
@@ -1344,6 +1349,38 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterExplosion(Explosion explosion, Vector2 worldPosition)
|
||||
{
|
||||
if (Character.Controlled?.SelectedItem != item) { return; }
|
||||
if (explosion.Attack.StructureDamage <= 0 && explosion.Attack.ItemDamage <= 0 && explosion.EmpStrength <= 0) { return; }
|
||||
Vector2 transducerCenter = GetTransducerPos();
|
||||
if (Vector2.DistanceSquared(worldPosition, transducerCenter) > range * range) { return; }
|
||||
int blipCount = MathHelper.Clamp((int)(explosion.Attack.Range / 100.0f), 0, 50);
|
||||
for (int i = 0; i < blipCount; i++)
|
||||
{
|
||||
sonarBlips.Add(new SonarBlip(
|
||||
worldPosition + Rand.Vector(Rand.Range(0.0f, explosion.Attack.Range)),
|
||||
1.0f,
|
||||
Rand.Range(0.5f, 1.0f),
|
||||
BlipType.Disruption));
|
||||
}
|
||||
if (explosion.EmpStrength > 0.0f)
|
||||
{
|
||||
int empBlipCount = MathHelper.Clamp((int)(blipCount * explosion.EmpStrength), 10, 50);
|
||||
for (int i = 0; i < empBlipCount; i++)
|
||||
{
|
||||
Vector2 dir = Rand.Vector(1.0f);
|
||||
var longRangeBlip = new SonarBlip(worldPosition, Rand.Range(1.9f, 2.1f), Rand.Range(1.0f, 1.5f), BlipType.LongRange)
|
||||
{
|
||||
Velocity = dir * MathUtils.Round(Rand.Range(4000.0f, 6000.0f), 1000.0f),
|
||||
Rotation = (float)Math.Atan2(-dir.Y, dir.X)
|
||||
};
|
||||
longRangeBlip.Size.Y *= 4.0f;
|
||||
sonarBlips.Add(longRangeBlip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Ping(Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius, float displayScale, float range, bool passive,
|
||||
float pingStrength = 1.0f)
|
||||
{
|
||||
@@ -1388,6 +1425,14 @@ namespace Barotrauma.Items.Components
|
||||
if (connectedSubs.Contains(submarine)) { continue; }
|
||||
}
|
||||
|
||||
Rectangle worldBorders = Submarine.MainSub.GetDockedBorders();
|
||||
worldBorders.Location += Submarine.MainSub.WorldPosition.ToPoint();
|
||||
if (Submarine.RectContains(worldBorders, pingSource))
|
||||
{
|
||||
CreateBlipsForSubmarineWalls(submarine, pingSource, transducerPos, pingRadius, prevPingRadius, range, passive);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < submarine.HullVertices.Count; i++)
|
||||
{
|
||||
Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]);
|
||||
@@ -1586,6 +1631,40 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateBlipsForSubmarineWalls(Submarine sub, Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius, float range, bool passive)
|
||||
{
|
||||
foreach (Structure structure in Structure.WallList)
|
||||
{
|
||||
if (structure.Submarine != sub) { continue; }
|
||||
CreateBlips(structure.IsHorizontal, structure.WorldPosition, structure.WorldRect);
|
||||
}
|
||||
foreach (var door in Door.DoorList)
|
||||
{
|
||||
if (door.Item.Submarine != sub || door.IsOpen) { continue; }
|
||||
CreateBlips(door.IsHorizontal, door.Item.WorldPosition, door.Item.WorldRect, BlipType.Door);
|
||||
}
|
||||
|
||||
void CreateBlips(bool isHorizontal, Vector2 worldPos, Rectangle worldRect, BlipType blipType = BlipType.Default)
|
||||
{
|
||||
Vector2 point1, point2;
|
||||
if (isHorizontal)
|
||||
{
|
||||
point1 = new Vector2(worldRect.X, worldPos.Y);
|
||||
point2 = new Vector2(worldRect.Right, worldPos.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
point1 = new Vector2(worldPos.X, worldRect.Y);
|
||||
point2 = new Vector2(worldPos.X, worldRect.Y - worldRect.Height);
|
||||
}
|
||||
CreateBlipsForLine(
|
||||
point1,
|
||||
point2,
|
||||
pingSource, transducerPos,
|
||||
pingRadius, prevPingRadius, 50.0f, 5.0f, range, 2.0f, passive, blipType);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckBlipVisibility(SonarBlip blip, Vector2 transducerPos)
|
||||
{
|
||||
Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom;
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool? swapDestinationOrder;
|
||||
|
||||
private GUIMessageBox enterOutpostPrompt;
|
||||
private GUIMessageBox enterOutpostPrompt, exitOutpostPrompt;
|
||||
|
||||
private bool levelStartSelected;
|
||||
public bool LevelStartSelected
|
||||
@@ -340,6 +340,10 @@ namespace Barotrauma.Items.Components
|
||||
centerText = $"({TextManager.Get("Meter")})";
|
||||
rightTextGetter = () =>
|
||||
{
|
||||
if (Level.Loaded is { IsEndBiome: true })
|
||||
{
|
||||
return Timing.TotalTime % 5.0f < 0.5f ? Rand.Range(-9000, 9000).ToString() : "ERROR";
|
||||
}
|
||||
float realWorldDepth = controlledSub == null ? -1000.0f : controlledSub.RealWorldDepth;
|
||||
return ((int)realWorldDepth).ToString();
|
||||
};
|
||||
@@ -382,15 +386,26 @@ namespace Barotrauma.Items.Components
|
||||
DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false)))
|
||||
{
|
||||
// Undocking from an outpost
|
||||
campaign.ShowCampaignUI = true;
|
||||
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
|
||||
return false;
|
||||
if (!ObjectiveManager.AllActiveObjectivesCompleted())
|
||||
{
|
||||
exitOutpostPrompt = new GUIMessageBox("",
|
||||
TextManager.GetWithVariable("CampaignExitTutorialOutpostPrompt", "[locationname]", campaign.Map.CurrentLocation.Name),
|
||||
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
|
||||
exitOutpostPrompt.Buttons[0].OnClicked += (_, _) =>
|
||||
{
|
||||
exitOutpostPrompt.Close();
|
||||
return OpenMap(campaign);
|
||||
};
|
||||
exitOutpostPrompt.Buttons[1].OnClicked += exitOutpostPrompt.Close;
|
||||
return false;
|
||||
}
|
||||
return OpenMap(campaign);
|
||||
}
|
||||
else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null &&
|
||||
!ActiveDockingSource.Docked && DockingTarget?.Item?.Submarine == Level.Loaded.StartOutpost && (DockingTarget?.Item?.Submarine?.Info.IsOutpost ?? false))
|
||||
{
|
||||
// Docking to an outpost
|
||||
var subsToLeaveBehind = campaign.GetSubsToLeaveBehind(Item.Submarine);
|
||||
var subsToLeaveBehind = CampaignMode.GetSubsToLeaveBehind(Item.Submarine);
|
||||
if (subsToLeaveBehind.Any())
|
||||
{
|
||||
enterOutpostPrompt = new GUIMessageBox(
|
||||
@@ -419,6 +434,14 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
bool OpenMap(CampaignMode campaign)
|
||||
{
|
||||
campaign.ShowCampaignUI = true;
|
||||
campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SendDockingSignal()
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
@@ -431,6 +454,7 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
dockingButton.Font = GUIStyle.SubHeadingFont;
|
||||
dockingButton.TextBlock.RectTransform.MaxSize = new Point((int)(dockingButton.Rect.Width * 0.7f), int.MaxValue);
|
||||
dockingButton.TextBlock.AutoScaleHorizontal = true;
|
||||
@@ -913,6 +937,7 @@ namespace Barotrauma.Items.Components
|
||||
maintainPosOriginIndicator?.Remove();
|
||||
steeringIndicator?.Remove();
|
||||
enterOutpostPrompt?.Close();
|
||||
exitOutpostPrompt?.Close();
|
||||
pathFinder = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -133,35 +133,43 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
{
|
||||
if (indicatorSize.X <= 1.0f || indicatorSize.Y <= 1.0f) { return; }
|
||||
Vector2 scaledIndicatorSize = indicatorSize * item.Scale;
|
||||
if (scaledIndicatorSize.X <= 2.0f || scaledIndicatorSize.Y <= 2.0f) { return; }
|
||||
|
||||
const float outlineThickness = 1.0f;
|
||||
Vector2 itemSize = new Vector2(item.Sprite.SourceRect.Width, item.Sprite.SourceRect.Height) * item.Scale;
|
||||
Vector2 indicatorPos = -itemSize / 2 + indicatorPosition * item.Scale;
|
||||
if (item.FlippedX && item.Prefab.CanSpriteFlipX) { indicatorPos.X = -indicatorPos.X - indicatorSize.X * item.Scale; }
|
||||
if (item.FlippedY && item.Prefab.CanSpriteFlipY) { indicatorPos.Y = -indicatorPos.Y - indicatorSize.Y * item.Scale; }
|
||||
Vector2 indicatorPos = -itemSize / 2.0f + indicatorPosition * item.Scale;
|
||||
Vector2 itemPosition = new Vector2(item.DrawPosition.X, -item.DrawPosition.Y);
|
||||
Vector2 flip = new Vector2(item.FlippedX && item.Prefab.CanSpriteFlipX ? -1.0f : 1.0f, item.FlippedY && item.Prefab.CanSpriteFlipY ? -1.0f : 1.0f);
|
||||
Matrix rotate = Matrix.CreateRotationZ(item.RotationRad);
|
||||
Vector2 center = Vector2.Transform((indicatorPos + (scaledIndicatorSize * 0.5f)) * flip, rotate) + itemPosition;
|
||||
|
||||
if (charge > 0 && capacity > 0)
|
||||
{
|
||||
float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f);
|
||||
Color indicatorColor = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
||||
if (!isHorizontal)
|
||||
Vector2 indicatorCenter = (indicatorPos + (scaledIndicatorSize * 0.5f)) * flip;
|
||||
Vector2 indicatorSize;
|
||||
|
||||
if (isHorizontal)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + ((indicatorSize.Y * item.Scale) * (1.0f - chargeRatio))) + indicatorPos,
|
||||
new Vector2(indicatorSize.X * item.Scale, (indicatorSize.Y * item.Scale) * chargeRatio), indicatorColor, true,
|
||||
depth: item.SpriteDepth - 0.00001f);
|
||||
float indicatorLength = (scaledIndicatorSize.X - outlineThickness * 2.0f) * chargeRatio;
|
||||
indicatorCenter.X += -scaledIndicatorSize.X * 0.5f + (flipIndicator ? scaledIndicatorSize.X - outlineThickness - indicatorLength * 0.5f : outlineThickness + indicatorLength * 0.5f);
|
||||
indicatorSize = new Vector2(indicatorLength, scaledIndicatorSize.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
|
||||
new Vector2((indicatorSize.X * item.Scale) * chargeRatio, indicatorSize.Y * item.Scale), indicatorColor, true,
|
||||
depth: item.SpriteDepth - 0.00001f);
|
||||
float indicatorLength = (scaledIndicatorSize.Y - outlineThickness * 2.0f) * chargeRatio;
|
||||
indicatorCenter.Y += -scaledIndicatorSize.Y * 0.5f + (flipIndicator ? outlineThickness + indicatorLength * 0.5f : scaledIndicatorSize.Y - outlineThickness - indicatorLength * 0.5f);
|
||||
indicatorSize = new Vector2(scaledIndicatorSize.X, indicatorLength);
|
||||
}
|
||||
|
||||
indicatorCenter = Vector2.Transform(indicatorCenter, rotate) + itemPosition;
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, indicatorCenter, indicatorSize, indicatorSize * 0.5f, item.RotationRad, indicatorColor, item.SpriteDepth - 0.00001f);
|
||||
}
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
|
||||
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.000015f);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, center, scaledIndicatorSize, scaledIndicatorSize * 0.5f, item.RotationRad, Color.Black, item.SpriteDepth - 0.000015f, outlineThickness, GUI.OutlinePosition.Inside);
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -24,7 +25,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
var layoutGroup = new GUILayoutGroup(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset })
|
||||
float marginMultiplier = element.GetAttributeFloat("marginmultiplier", 1.0f);
|
||||
|
||||
var layoutGroup = new GUILayoutGroup(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin.Multiply(marginMultiplier), GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset.Multiply(marginMultiplier) })
|
||||
{
|
||||
ChildAnchor = Anchor.TopCenter,
|
||||
RelativeSpacing = 0.02f,
|
||||
@@ -33,31 +36,34 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
historyBox = new GUIListBox(new RectTransform(new Vector2(1, .9f), layoutGroup.RectTransform), style: null)
|
||||
{
|
||||
AutoHideScrollBar = false
|
||||
AutoHideScrollBar = this.AutoHideScrollbar
|
||||
};
|
||||
|
||||
CreateFillerBlock();
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine");
|
||||
|
||||
inputBox = new GUITextBox(new RectTransform(new Vector2(1, .1f), layoutGroup.RectTransform), textColor: TextColor)
|
||||
if (!Readonly)
|
||||
{
|
||||
MaxTextLength = MaxMessageLength,
|
||||
OverflowClip = true,
|
||||
OnEnterPressed = (GUITextBox textBox, string text) =>
|
||||
CreateFillerBlock();
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine");
|
||||
|
||||
inputBox = new GUITextBox(new RectTransform(new Vector2(1, .1f), layoutGroup.RectTransform), textColor: TextColor)
|
||||
{
|
||||
if (GameMain.NetworkMember == null)
|
||||
MaxTextLength = MaxMessageLength,
|
||||
OverflowClip = true,
|
||||
OnEnterPressed = (GUITextBox textBox, string text) =>
|
||||
{
|
||||
SendOutput(text);
|
||||
if (GameMain.NetworkMember == null)
|
||||
{
|
||||
SendOutput(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this, new ClientEventData(text));
|
||||
}
|
||||
textBox.Text = string.Empty;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this, new ClientEventData(text));
|
||||
}
|
||||
textBox.Text = string.Empty;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
layoutGroup.Recalculate();
|
||||
}
|
||||
@@ -101,7 +107,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
GUITextBlock newBlock = new GUITextBlock(
|
||||
new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter),
|
||||
"> " + input,
|
||||
LineStartSymbol + TextManager.Get(input).Fallback(input),
|
||||
textColor: color, wrap: true, font: UseMonospaceFont ? GUIStyle.MonospacedFont : GUIStyle.Font)
|
||||
{
|
||||
CanBeFocused = false
|
||||
@@ -123,7 +129,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
historyBox.RecalculateChildren();
|
||||
historyBox.UpdateScrollBarSize();
|
||||
historyBox.ScrollBar.BarScrollValue = 1;
|
||||
if (AutoScrollToBottom)
|
||||
{
|
||||
historyBox.ScrollBar.BarScrollValue = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Select(Character character)
|
||||
@@ -138,7 +147,7 @@ namespace Barotrauma.Items.Components
|
||||
public override void AddToGUIUpdateList(int order = 0)
|
||||
{
|
||||
base.AddToGUIUpdateList(order: order);
|
||||
if (shouldSelectInputBox)
|
||||
if (shouldSelectInputBox && !Readonly)
|
||||
{
|
||||
inputBox.Select();
|
||||
shouldSelectInputBox = false;
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (target.Bleeding > 0.0f)
|
||||
{
|
||||
int bleedingTextIndex = MathHelper.Clamp((int)Math.Floor(target.Bleeding / 100.0f) * BleedingTexts.Length, 0, BleedingTexts.Length - 1);
|
||||
int bleedingTextIndex = MathHelper.Clamp((int)Math.Floor(target.Bleeding / 100.0f * BleedingTexts.Length), 0, BleedingTexts.Length - 1);
|
||||
texts.Add(BleedingTexts[bleedingTextIndex]);
|
||||
textColors.Add(Color.Lerp(GUIStyle.Orange, GUIStyle.Red, target.Bleeding / 100.0f));
|
||||
}
|
||||
|
||||
@@ -212,14 +212,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (moveSoundChannel == null && startMoveSound != null)
|
||||
{
|
||||
moveSoundChannel = SoundPlayer.PlaySound(startMoveSound.Sound, item.WorldPosition, startMoveSound.Volume, startMoveSound.Range, ignoreMuffling: startMoveSound.IgnoreMuffling);
|
||||
moveSoundChannel = SoundPlayer.PlaySound(startMoveSound.Sound, item.WorldPosition, startMoveSound.Volume, startMoveSound.Range, ignoreMuffling: startMoveSound.IgnoreMuffling, freqMult: startMoveSound.GetRandomFrequencyMultiplier());
|
||||
}
|
||||
else if (moveSoundChannel == null || !moveSoundChannel.IsPlaying)
|
||||
{
|
||||
if (moveSound != null)
|
||||
{
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, item.WorldPosition, moveSound.Volume, moveSound.Range, ignoreMuffling: moveSound.IgnoreMuffling);
|
||||
moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, item.WorldPosition, moveSound.Volume, moveSound.Range, ignoreMuffling: moveSound.IgnoreMuffling, freqMult: moveSound.GetRandomFrequencyMultiplier());
|
||||
if (moveSoundChannel != null) moveSoundChannel.Looping = true;
|
||||
}
|
||||
}
|
||||
@@ -231,7 +231,7 @@ namespace Barotrauma.Items.Components
|
||||
if (endMoveSound != null && moveSoundChannel.Sound != endMoveSound.Sound)
|
||||
{
|
||||
moveSoundChannel.FadeOutAndDispose();
|
||||
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, item.WorldPosition, endMoveSound.Volume, endMoveSound.Range, ignoreMuffling: endMoveSound.IgnoreMuffling);
|
||||
moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, item.WorldPosition, endMoveSound.Volume, endMoveSound.Range, ignoreMuffling: endMoveSound.IgnoreMuffling, freqMult: endMoveSound.GetRandomFrequencyMultiplier());
|
||||
if (moveSoundChannel != null) moveSoundChannel.Looping = false;
|
||||
}
|
||||
else if (!moveSoundChannel.IsPlaying)
|
||||
@@ -260,7 +260,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (chargeSound != null)
|
||||
{
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling);
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling, freqMult: chargeSound.GetRandomFrequencyMultiplier());
|
||||
if (chargeSoundChannel != null) chargeSoundChannel.Looping = true;
|
||||
}
|
||||
}
|
||||
@@ -581,7 +581,7 @@ namespace Barotrauma.Items.Components
|
||||
var battery = recipient.Item?.GetComponent<PowerContainer>();
|
||||
if (battery == null || battery.Item.Condition <= 0.0f) { continue; }
|
||||
availableCharge += battery.Charge;
|
||||
availableCapacity += battery.Capacity;
|
||||
availableCapacity += battery.GetCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
@@ -6,7 +7,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Wearable : Pickable, IServerSerializable
|
||||
{
|
||||
private void GetDamageModifierText(ref LocalizedString description, DamageModifier damageModifier, Identifier afflictionIdentifier)
|
||||
private static void GetDamageModifierText(ref LocalizedString description, DamageModifier damageModifier, Identifier afflictionIdentifier)
|
||||
{
|
||||
int roundedValue = (int)Math.Round((1 - damageModifier.DamageMultiplier * damageModifier.ProbabilityMultiplier) * 100);
|
||||
if (roundedValue == 0) { return; }
|
||||
@@ -19,8 +20,13 @@ namespace Barotrauma.Items.Components
|
||||
if (!description.IsNullOrWhiteSpace()) { description += '\n'; }
|
||||
description += $" ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {afflictionName}";
|
||||
}
|
||||
|
||||
|
||||
public override void AddTooltipInfo(ref LocalizedString name, ref LocalizedString description)
|
||||
{
|
||||
AddTooltipInfo(damageModifiers, SkillModifiers, ref description);
|
||||
}
|
||||
|
||||
public static void AddTooltipInfo(IReadOnlyList<DamageModifier> damageModifiers, IReadOnlyDictionary<Identifier, float> skillModifiers, ref LocalizedString description)
|
||||
{
|
||||
if (damageModifiers.Any())
|
||||
{
|
||||
@@ -41,9 +47,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SkillModifiers.Any())
|
||||
if (skillModifiers.Any())
|
||||
{
|
||||
foreach (var skillModifier in SkillModifiers)
|
||||
foreach (var skillModifier in skillModifiers)
|
||||
{
|
||||
string colorStr = XMLExtensions.ToStringHex(GUIStyle.Green);
|
||||
int roundedValue = (int)Math.Round(skillModifier.Value);
|
||||
|
||||
@@ -1610,20 +1610,26 @@ namespace Barotrauma
|
||||
}
|
||||
else if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
|
||||
{
|
||||
containedState = itemContainer.Inventory.AllItems.Count() / (float)(itemContainer.GetMaxStackSize(0) * itemContainer.Capacity);
|
||||
int ignoredItems = itemContainer.AllSubContainableItems == null ? 0 : itemContainer.AllSubContainableItems.Count;
|
||||
int itemCount = itemContainer.Inventory.AllItems.Count() - ignoredItems;
|
||||
containedState = itemCount / (float)(itemContainer.GetMaxStackSize(0) * itemContainer.MainContainerCapacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
var containedItem = itemContainer.Inventory.slots[Math.Max(itemContainer.ContainedStateIndicatorSlot, 0)].FirstOrDefault();
|
||||
|
||||
int targetSlot = Math.Max(itemContainer.ContainedStateIndicatorSlot, 0);
|
||||
var containedItem = itemContainer.Inventory.slots[targetSlot].FirstOrDefault();
|
||||
|
||||
containedState = itemContainer.Inventory.Capacity == 1 || itemContainer.ContainedStateIndicatorSlot > -1 ?
|
||||
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
|
||||
itemContainer.Inventory.slots.Count(i => !i.Empty()) / (float)itemContainer.Inventory.capacity;
|
||||
if (containedItem != null && itemContainer.Inventory.Capacity == 1)
|
||||
|
||||
if (containedItem != null && (itemContainer.Inventory.Capacity == 1 || itemContainer.HasSubContainers))
|
||||
{
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(0));
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(targetSlot));
|
||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||
{
|
||||
containedState = itemContainer.Inventory.slots[0].Items.Count / (float)maxStackSize;
|
||||
containedState = itemContainer.Inventory.slots[targetSlot].Items.Count / (float)maxStackSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +301,9 @@ namespace Barotrauma
|
||||
BrokenItemSprite fadeInBrokenSprite = null;
|
||||
float fadeInBrokenSpriteAlpha = 0.0f;
|
||||
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
|
||||
Vector2 drawOffset = Vector2.Zero;
|
||||
Vector2 drawOffset = GetCollapseEffectOffset();
|
||||
drawOffset.Y = -drawOffset.Y;
|
||||
|
||||
if (displayCondition < MaxCondition)
|
||||
{
|
||||
for (int i = 0; i < Prefab.BrokenSprites.Length; i++)
|
||||
@@ -417,6 +419,8 @@ namespace Barotrauma
|
||||
var holdable = GetComponent<Holdable>();
|
||||
if (holdable != null && holdable.Picker?.AnimController != null)
|
||||
{
|
||||
//don't draw the item on hands if it's also being worn
|
||||
if (GetComponent<Wearable>() is { IsActive: true }) { return; }
|
||||
if (!back) { return; }
|
||||
float depthStep = 0.000001f;
|
||||
if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == this)
|
||||
@@ -1415,6 +1419,15 @@ namespace Barotrauma
|
||||
case EventType.ChangeProperty:
|
||||
ReadPropertyChange(msg, false);
|
||||
break;
|
||||
case EventType.ItemStat:
|
||||
byte length = msg.ReadByte();
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
var statIdentifier = INetSerializableStruct.Read<ItemStatManager.TalentStatIdentifier>(msg);
|
||||
var statValue = msg.ReadSingle();
|
||||
StatManager.ApplyStat(statIdentifier, statValue);
|
||||
}
|
||||
break;
|
||||
case EventType.Upgrade:
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
byte level = msg.ReadByte();
|
||||
@@ -1654,25 +1667,24 @@ namespace Barotrauma
|
||||
bool hasIdCard = msg.ReadBoolean();
|
||||
string ownerName = "", ownerTags = "";
|
||||
int ownerBeardIndex = -1, ownerHairIndex = -1, ownerMoustacheIndex = -1, ownerFaceAttachmentIndex = -1;
|
||||
Color ownerHairColor = Microsoft.Xna.Framework.Color.White,
|
||||
ownerFacialHairColor = Microsoft.Xna.Framework.Color.White,
|
||||
ownerSkinColor = Microsoft.Xna.Framework.Color.White;
|
||||
Color ownerHairColor = Color.White,
|
||||
ownerFacialHairColor = Color.White,
|
||||
ownerSkinColor = Color.White;
|
||||
Identifier ownerJobId = Identifier.Empty;
|
||||
Vector2 ownerSheetIndex = Vector2.Zero;
|
||||
int submarineSpecificId = 0;
|
||||
if (hasIdCard)
|
||||
{
|
||||
submarineSpecificId = msg.ReadInt32();
|
||||
ownerName = msg.ReadString();
|
||||
ownerTags = msg.ReadString();
|
||||
|
||||
ownerTags = msg.ReadString();
|
||||
ownerBeardIndex = msg.ReadByte() - 1;
|
||||
ownerHairIndex = msg.ReadByte() - 1;
|
||||
ownerMoustacheIndex = msg.ReadByte() - 1;
|
||||
ownerFaceAttachmentIndex = msg.ReadByte() - 1;
|
||||
|
||||
ownerFaceAttachmentIndex = msg.ReadByte() - 1;
|
||||
ownerHairColor = msg.ReadColorR8G8B8();
|
||||
ownerFacialHairColor = msg.ReadColorR8G8B8();
|
||||
ownerSkinColor = msg.ReadColorR8G8B8();
|
||||
|
||||
ownerSkinColor = msg.ReadColorR8G8B8();
|
||||
ownerJobId = msg.ReadIdentifier();
|
||||
|
||||
int x = msg.ReadByte();
|
||||
@@ -1776,6 +1788,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (IdCard idCard in item.GetComponents<IdCard>())
|
||||
{
|
||||
idCard.SubmarineSpecificID = submarineSpecificId;
|
||||
idCard.TeamID = (CharacterTeamType)teamID;
|
||||
idCard.OwnerName = ownerName;
|
||||
idCard.OwnerTags = ownerTags;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
@@ -7,7 +7,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -73,6 +72,9 @@ namespace Barotrauma
|
||||
|
||||
public float UpgradePreviewScale = 1.0f;
|
||||
|
||||
private IReadOnlyList<DamageModifier> wearableDamageModifiers;
|
||||
private IReadOnlyDictionary<Identifier, float> wearableSkillModifiers;
|
||||
|
||||
//only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.No)]
|
||||
public Color InventoryIconColor { get; protected set; }
|
||||
@@ -101,6 +103,9 @@ namespace Barotrauma
|
||||
var containedSprites = new List<ContainedItemSprite>();
|
||||
var decorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
|
||||
|
||||
var wearableDamageModifiers = new List<DamageModifier>();
|
||||
var wearableSkillModifiers = new Dictionary<Identifier, float>();
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.LocalName.ToLowerInvariant())
|
||||
@@ -198,8 +203,33 @@ namespace Barotrauma
|
||||
containedSprites.Add(containedSprite);
|
||||
}
|
||||
break;
|
||||
case "wearable":
|
||||
foreach (ContentXElement wearableSubElement in subElement.Elements())
|
||||
{
|
||||
switch (wearableSubElement.Name.LocalName.ToLowerInvariant())
|
||||
{
|
||||
case "damagemodifier":
|
||||
wearableDamageModifiers.Add(new DamageModifier(wearableSubElement, Name.Value + ", Wearable", checkErrors: false));
|
||||
break;
|
||||
case "skillmodifier":
|
||||
Identifier skillIdentifier = wearableSubElement.GetAttributeIdentifier("skillidentifier", Identifier.Empty);
|
||||
float skillValue = wearableSubElement.GetAttributeFloat("skillvalue", 0f);
|
||||
if (wearableSkillModifiers.ContainsKey(skillIdentifier))
|
||||
{
|
||||
wearableSkillModifiers[skillIdentifier] += skillValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
wearableSkillModifiers.TryAdd(skillIdentifier, skillValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.wearableDamageModifiers = wearableDamageModifiers.ToImmutableList();
|
||||
this.wearableSkillModifiers = wearableSkillModifiers.ToImmutableDictionary();
|
||||
|
||||
UpgradeOverrideSprites = upgradeOverrideSprites.Select(kvp => (kvp.Key, kvp.Value.ToImmutableArray())).ToImmutableDictionary();
|
||||
BrokenSprites = brokenSprites.ToImmutableArray();
|
||||
@@ -208,6 +238,25 @@ namespace Barotrauma
|
||||
DecorativeSpriteGroups = decorativeSpriteGroups.Select(kvp => (kvp.Key, kvp.Value.ToImmutableArray())).ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public bool CanCharacterBuy()
|
||||
{
|
||||
if (!DefaultPrice.RequiresUnlock) { return true; }
|
||||
return Character.Controlled is not null && Character.Controlled.HasStoreAccessForItem(this);
|
||||
}
|
||||
public LocalizedString GetTooltip()
|
||||
{
|
||||
LocalizedString tooltip = $"‖color:{XMLExtensions.ToStringHex(GUIStyle.TextColorBright)}‖{Name}‖color:end‖";
|
||||
if (!Description.IsNullOrEmpty())
|
||||
{
|
||||
tooltip += $"\n{Description}";
|
||||
}
|
||||
if (wearableDamageModifiers.Any() || wearableSkillModifiers.Any())
|
||||
{
|
||||
Wearable.AddTooltipInfo(wearableDamageModifiers, wearableSkillModifiers, ref tooltip);
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public override void UpdatePlacing(Camera cam)
|
||||
{
|
||||
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
|
||||
@@ -313,15 +362,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 position = Submarine.MouseToWorldGrid(Screen.Selected.Cam, Submarine.MainSub);
|
||||
Vector2 placeSize = Size * Scale;
|
||||
if (placePosition != Vector2.Zero)
|
||||
{
|
||||
if (ResizeHorizontal) { placeSize.X = Math.Max(position.X - placePosition.X, placeSize.X); }
|
||||
if (ResizeVertical) { placeSize.Y = Math.Max(placePosition.Y - position.Y, placeSize.Y); }
|
||||
position = placePosition;
|
||||
}
|
||||
Sprite?.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, color: SpriteColor);
|
||||
Sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), SpriteColor * 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,11 @@ namespace Barotrauma
|
||||
|
||||
graphics.Clear(BackgroundColor);
|
||||
|
||||
renderer?.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
|
||||
if (renderer != null)
|
||||
{
|
||||
GameMain.LightManager.AmbientLight = GameMain.LightManager.AmbientLight.Add(renderer.FlashColor);
|
||||
renderer?.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawFront(SpriteBatch spriteBatch, Camera cam)
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (SoundChannels[i] == null || !SoundChannels[i].IsPlaying)
|
||||
{
|
||||
SoundChannels[i] = roundSound.Sound.Play(roundSound.Volume, roundSound.Range, soundPos);
|
||||
SoundChannels[i] = roundSound.Sound.Play(roundSound.Volume, roundSound.Range, roundSound.GetRandomFrequencyMultiplier(), soundPos);
|
||||
}
|
||||
SoundChannels[i].Position = new Vector3(soundPos.X, soundPos.Y, 0.0f);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
@@ -178,7 +179,7 @@ namespace Barotrauma
|
||||
activeSprite?.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(obj.Position.X, -obj.Position.Y) - camDiff * obj.Position.Z / 10000.0f,
|
||||
Color.Lerp(Color.White, Level.Loaded.BackgroundTextureColor, obj.Position.Z / 3000.0f),
|
||||
Color.Lerp(obj.Prefab.SpriteColor, obj.Prefab.SpriteColor.Multiply(Level.Loaded.BackgroundTextureColor), obj.Position.Z / 3000.0f),
|
||||
activeSprite.Origin,
|
||||
obj.CurrentRotation,
|
||||
obj.CurrentScale,
|
||||
@@ -200,7 +201,7 @@ namespace Barotrauma
|
||||
obj.ActivePrefab.DeformableSprite.Origin,
|
||||
obj.CurrentRotation,
|
||||
obj.CurrentScale,
|
||||
Color.Lerp(Color.White, Level.Loaded.BackgroundTextureColor, obj.Position.Z / 5000.0f));
|
||||
Color.Lerp(obj.Prefab.SpriteColor, obj.Prefab.SpriteColor.Multiply(Level.Loaded.BackgroundTextureColor), obj.Position.Z / 5000.0f));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FarseerPhysics;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
@@ -71,9 +71,12 @@ namespace Barotrauma
|
||||
{
|
||||
private static BasicEffect wallEdgeEffect, wallCenterEffect;
|
||||
|
||||
private Vector2 dustOffset;
|
||||
private Vector2 defaultDustVelocity;
|
||||
private Vector2 dustVelocity;
|
||||
private Vector2 waterParticleOffset;
|
||||
private Vector2 waterParticleVelocity;
|
||||
|
||||
private float flashCooldown;
|
||||
private float flashTimer;
|
||||
public Color FlashColor { get; private set; }
|
||||
|
||||
private readonly RasterizerState cullNone;
|
||||
|
||||
@@ -81,10 +84,26 @@ namespace Barotrauma
|
||||
|
||||
private readonly List<LevelWallVertexBuffer> vertexBuffers = new List<LevelWallVertexBuffer>();
|
||||
|
||||
private float chromaticAberrationStrength;
|
||||
public float ChromaticAberrationStrength
|
||||
{
|
||||
get { return chromaticAberrationStrength; }
|
||||
set { chromaticAberrationStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
public float CollapseEffectStrength
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public Vector2 CollapseEffectOrigin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public LevelRenderer(Level level)
|
||||
{
|
||||
defaultDustVelocity = Vector2.UnitY * 10.0f;
|
||||
|
||||
cullNone = new RasterizerState() { CullMode = CullMode.None };
|
||||
|
||||
if (wallEdgeEffect == null)
|
||||
@@ -120,12 +139,50 @@ namespace Barotrauma
|
||||
level.GenerationParams.WallSprite.ReloadTexture();
|
||||
wallCenterEffect.Texture = level.GenerationParams.WallSprite.Texture;
|
||||
}
|
||||
|
||||
public void Flash()
|
||||
{
|
||||
flashTimer = 1.0f;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (CollapseEffectStrength > 0.0f)
|
||||
{
|
||||
CollapseEffectStrength = Math.Max(0.0f, CollapseEffectStrength - deltaTime);
|
||||
}
|
||||
if (ChromaticAberrationStrength > 0.0f)
|
||||
{
|
||||
ChromaticAberrationStrength = Math.Max(0.0f, ChromaticAberrationStrength - deltaTime * 10.0f);
|
||||
}
|
||||
|
||||
if (level.GenerationParams.FlashInterval.Y > 0)
|
||||
{
|
||||
flashCooldown -= deltaTime;
|
||||
if (flashCooldown <= 0.0f)
|
||||
{
|
||||
flashTimer = 1.0f;
|
||||
if (level.GenerationParams.FlashSound != null)
|
||||
{
|
||||
level.GenerationParams.FlashSound.Play(1.0f, "default");
|
||||
}
|
||||
flashCooldown = Rand.Range(level.GenerationParams.FlashInterval.X, level.GenerationParams.FlashInterval.Y, Rand.RandSync.Unsynced);
|
||||
}
|
||||
if (flashTimer > 0.0f)
|
||||
{
|
||||
float brightness = flashTimer * 1.1f - PerlinNoise.GetPerlin((float)Timing.TotalTime, (float)Timing.TotalTime * 0.66f) * 0.1f;
|
||||
FlashColor = level.GenerationParams.FlashColor.Multiply(MathHelper.Clamp(brightness, 0.0f, 1.0f));
|
||||
flashTimer -= deltaTime * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
FlashColor = Color.TransparentBlack;
|
||||
}
|
||||
}
|
||||
|
||||
//calculate the sum of the forces of nearby level triggers
|
||||
//and use it to move the dust texture and water distortion effect
|
||||
Vector2 currentDustVel = defaultDustVelocity;
|
||||
//and use it to move the water texture and water distortion effect
|
||||
Vector2 currentWaterParticleVel = level.GenerationParams.WaterParticleVelocity;
|
||||
foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects())
|
||||
{
|
||||
if (levelObject.Triggers == null) { continue; }
|
||||
@@ -139,21 +196,21 @@ namespace Barotrauma
|
||||
objectMaxFlow = vel;
|
||||
}
|
||||
}
|
||||
currentDustVel += objectMaxFlow;
|
||||
currentWaterParticleVel += objectMaxFlow;
|
||||
}
|
||||
|
||||
waterParticleVelocity = Vector2.Lerp(waterParticleVelocity, currentWaterParticleVel, deltaTime);
|
||||
|
||||
dustVelocity = Vector2.Lerp(dustVelocity, currentDustVel, deltaTime);
|
||||
|
||||
WaterRenderer.Instance?.ScrollWater(dustVelocity, deltaTime);
|
||||
WaterRenderer.Instance?.ScrollWater(waterParticleVelocity, deltaTime);
|
||||
|
||||
if (level.GenerationParams.WaterParticles != null)
|
||||
{
|
||||
Vector2 waterTextureSize = level.GenerationParams.WaterParticles.size * level.GenerationParams.WaterParticleScale;
|
||||
dustOffset += new Vector2(dustVelocity.X, -dustVelocity.Y) * level.GenerationParams.WaterParticleScale * deltaTime;
|
||||
while (dustOffset.X <= -waterTextureSize.X) dustOffset.X += waterTextureSize.X;
|
||||
while (dustOffset.X >= waterTextureSize.X) dustOffset.X -= waterTextureSize.X;
|
||||
while (dustOffset.Y <= -waterTextureSize.Y) dustOffset.Y += waterTextureSize.Y;
|
||||
while (dustOffset.Y >= waterTextureSize.Y) dustOffset.Y -= waterTextureSize.Y;
|
||||
waterParticleOffset += new Vector2(waterParticleVelocity.X, -waterParticleVelocity.Y) * level.GenerationParams.WaterParticleScale * deltaTime;
|
||||
while (waterParticleOffset.X <= -waterTextureSize.X) { waterParticleOffset.X += waterTextureSize.X; }
|
||||
while (waterParticleOffset.X >= waterTextureSize.X){ waterParticleOffset.X -= waterTextureSize.X; }
|
||||
while (waterParticleOffset.Y <= -waterTextureSize.Y) { waterParticleOffset.Y += waterTextureSize.Y; }
|
||||
while (waterParticleOffset.Y >= waterTextureSize.Y) { waterParticleOffset.Y -= waterTextureSize.Y; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +291,7 @@ namespace Barotrauma
|
||||
|
||||
Rectangle srcRect = new Rectangle(0, 0, 2048, 2048);
|
||||
Vector2 origin = new Vector2(cam.WorldView.X, -cam.WorldView.Y);
|
||||
Vector2 offset = -origin + dustOffset;
|
||||
Vector2 offset = -origin + waterParticleOffset;
|
||||
while (offset.X <= -srcRect.Width * textureScale) offset.X += srcRect.Width * textureScale;
|
||||
while (offset.X > 0.0f) offset.X -= srcRect.Width * textureScale;
|
||||
while (offset.Y <= -srcRect.Height * textureScale) offset.Y += srcRect.Height * textureScale;
|
||||
@@ -261,7 +318,7 @@ namespace Barotrauma
|
||||
level.GenerationParams.WaterParticles.DrawTiled(
|
||||
spriteBatch, origin + offsetS,
|
||||
new Vector2(cam.WorldView.Width - offsetS.X, cam.WorldView.Height - offsetS.Y),
|
||||
color: Color.White * alpha, textureScale: new Vector2(texScale));
|
||||
color: level.GenerationParams.WaterParticleColor * alpha, textureScale: new Vector2(texScale));
|
||||
}
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -98,7 +99,7 @@ namespace Barotrauma
|
||||
OnClicked = (btn, userData) =>
|
||||
{
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed));
|
||||
Generate(GameMain.GameSession.GameMode is CampaignMode campaign ? campaign.Settings : CampaignSettings.Empty);
|
||||
Generate(GameMain.GameSession?.Campaign);
|
||||
InitProjectSpecific();
|
||||
return true;
|
||||
}
|
||||
@@ -186,7 +187,7 @@ namespace Barotrauma
|
||||
|
||||
private void LocationChanged(Location prevLocation, Location newLocation)
|
||||
{
|
||||
if (prevLocation == newLocation) return;
|
||||
if (prevLocation == newLocation) { return; }
|
||||
//focus on starting location
|
||||
if (prevLocation != null)
|
||||
{
|
||||
@@ -252,27 +253,104 @@ namespace Barotrauma
|
||||
return !tileDiscovered[MathHelper.Clamp(x, 0, tileDiscovered.Length), MathHelper.Clamp(y, 0, tileDiscovered.Length)];
|
||||
}
|
||||
|
||||
private class MapNotification
|
||||
{
|
||||
public readonly RichString Text;
|
||||
public readonly GUIFont Font;
|
||||
|
||||
public readonly Vector2 TextSize;
|
||||
|
||||
public int TimesShown;
|
||||
|
||||
public float Offset;
|
||||
|
||||
public readonly Location RelatedLocation;
|
||||
|
||||
public bool IsCurrentlyVisible;
|
||||
|
||||
public MapNotification(string text, GUIFont font, List<MapNotification> existingNotifications, Location relatedLocation)
|
||||
{
|
||||
Text = RichString.Rich(text);
|
||||
Font = font;
|
||||
TextSize = Font.MeasureString(Font.ForceUpperCase ? Text.SanitizedValue.ToUpper() : Text.SanitizedValue);
|
||||
if (existingNotifications.Any())
|
||||
{
|
||||
Offset = existingNotifications.Max(n => n.Offset + n.TextSize.X + GUI.IntScale(60));
|
||||
}
|
||||
RelatedLocation = relatedLocation;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<MapNotification> mapNotifications = new List<MapNotification>();
|
||||
|
||||
partial void ChangeLocationTypeProjSpecific(Location location, string prevName, LocationTypeChange change)
|
||||
{
|
||||
if (change.Messages.Any())
|
||||
var messages = change.GetMessages(location.Faction);
|
||||
if (!messages.Any()) { return; }
|
||||
|
||||
string msg = messages.GetRandom(Rand.RandSync.Unsynced)
|
||||
.Replace("[previousname]", $"‖color:gui.yellow‖{prevName}‖end‖")
|
||||
.Replace("[name]", $"‖color:gui.yellow‖{location.Name}‖end‖");
|
||||
location.LastTypeChangeMessage = msg;
|
||||
|
||||
mapNotifications.Add(new MapNotification(msg, GUIStyle.SubHeadingFont, mapNotifications, location));
|
||||
}
|
||||
|
||||
public void DrawNotifications(SpriteBatch spriteBatch, GUICustomComponent container)
|
||||
{
|
||||
Vector2 pos = new Vector2(container.Rect.Right, container.Rect.Center.Y);
|
||||
foreach (var notification in mapNotifications)
|
||||
{
|
||||
string msg = change.Messages[Rand.Range(0, change.Messages.Count)]
|
||||
.Replace("[previousname]", $"‖color:gui.orange‖{prevName}‖end‖")
|
||||
.Replace("[name]", $"‖color:gui.orange‖{location.Name}‖end‖");
|
||||
location.LastTypeChangeMessage = msg;
|
||||
if (GameMain.Client != null)
|
||||
Vector2 textPos = pos + new Vector2(notification.Offset, -notification.TextSize.Y / 2);
|
||||
|
||||
notification.Font.DrawStringWithColors(
|
||||
spriteBatch,
|
||||
notification.Text.SanitizedValue,
|
||||
textPos,
|
||||
Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0,
|
||||
notification.Text.RichTextData);
|
||||
|
||||
int margin = container.Rect.Width / 5;
|
||||
notification.IsCurrentlyVisible =
|
||||
textPos.X < container.Rect.Right - margin &&
|
||||
textPos.X + notification.TextSize.X > container.Rect.X + margin;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNotifications(float deltaTime, GUICustomComponent mapContainer)
|
||||
{
|
||||
if (mapNotifications.Count < 5)
|
||||
{
|
||||
int maxIndex = 1;
|
||||
while (TextManager.ContainsTag("randomnews" + maxIndex))
|
||||
{
|
||||
GameMain.Client.AddChatMessage(msg, Networking.ChatMessageType.Default, TextManager.Get("RadioAnnouncerName").Value);
|
||||
maxIndex++;
|
||||
}
|
||||
else
|
||||
string textTag = "randomnews" + Rand.Range(0, maxIndex);
|
||||
if (TextManager.ContainsTag(textTag))
|
||||
{
|
||||
GameMain.GameSession?.GameMode.CrewManager.AddSinglePlayerChatMessage(
|
||||
TextManager.Get("RadioAnnouncerName").Value,
|
||||
msg,
|
||||
Networking.ChatMessageType.Default,
|
||||
sender: null);
|
||||
mapNotifications.Add(new MapNotification(TextManager.Get(textTag).Value, GUIStyle.SubHeadingFont, mapNotifications, relatedLocation: null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = mapNotifications.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var notification = mapNotifications[i];
|
||||
notification.Offset -= deltaTime * 75.0f;
|
||||
if (notification.Offset < -notification.TextSize.X - mapContainer.Rect.Width)
|
||||
{
|
||||
notification.Offset = Math.Max(mapNotifications.Max(n => n.Offset + n.TextSize.X) + GUI.IntScale(60), 0);
|
||||
notification.TimesShown++;
|
||||
if (mapNotifications.Count > 5)
|
||||
{
|
||||
mapNotifications.RemoveAt(i);
|
||||
}
|
||||
else if (mapNotifications.Count > 3 && notification.TimesShown > 2)
|
||||
{
|
||||
mapNotifications.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void ClearAnimQueue()
|
||||
@@ -280,18 +358,19 @@ namespace Barotrauma
|
||||
mapAnimQueue.Clear();
|
||||
}
|
||||
|
||||
public void Update(float deltaTime, GUICustomComponent mapContainer)
|
||||
public void Update(CampaignMode campaign, float deltaTime, GUICustomComponent mapContainer)
|
||||
{
|
||||
Rectangle rect = mapContainer.Rect;
|
||||
|
||||
var currentDisplayLocation = GameMain.GameSession?.Campaign?.GetCurrentDisplayLocation();
|
||||
UpdateNotifications(deltaTime, mapContainer);
|
||||
|
||||
var currentDisplayLocation = campaign?.GetCurrentDisplayLocation();
|
||||
if (currentDisplayLocation != null)
|
||||
{
|
||||
if (!currentDisplayLocation.Discovered)
|
||||
{
|
||||
RemoveFogOfWar(currentDisplayLocation);
|
||||
currentDisplayLocation.Discover();
|
||||
Discover(currentDisplayLocation);
|
||||
if (currentDisplayLocation.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
|
||||
{
|
||||
furthestDiscoveredLocation = currentDisplayLocation;
|
||||
@@ -452,13 +531,13 @@ namespace Barotrauma
|
||||
Level.Loaded.DebugSetStartLocation(CurrentLocation);
|
||||
Level.Loaded.DebugSetEndLocation(null);
|
||||
|
||||
CurrentLocation.Discover();
|
||||
Discover(CurrentLocation);
|
||||
OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation));
|
||||
SelectLocation(-1);
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
CurrentLocation.CreateStores();
|
||||
ProgressWorld();
|
||||
ProgressWorld(campaign);
|
||||
Radiation?.OnStep(1);
|
||||
}
|
||||
else
|
||||
@@ -481,10 +560,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer)
|
||||
public void Draw(CampaignMode campaign, SpriteBatch spriteBatch, GUICustomComponent mapContainer)
|
||||
{
|
||||
tooltip = null;
|
||||
var currentDisplayLocation = GameMain.GameSession?.Campaign?.GetCurrentDisplayLocation();
|
||||
var currentDisplayLocation = campaign?.GetCurrentDisplayLocation();
|
||||
|
||||
Rectangle rect = mapContainer.Rect;
|
||||
|
||||
@@ -568,7 +647,9 @@ namespace Barotrauma
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
{
|
||||
Location location = Locations[i];
|
||||
if (IsInFogOfWar(location)) { continue; }
|
||||
if (!location.Discovered && IsInFogOfWar(location)) { continue; }
|
||||
bool isEndLocation = endLocations.Contains(location);
|
||||
if (!GameMain.DebugDraw && isEndLocation && location != endLocations.First()) { continue; }
|
||||
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
|
||||
|
||||
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
|
||||
@@ -587,14 +668,32 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float iconScale = location == currentDisplayLocation ? 1.2f : 1.0f;
|
||||
if (location == HighlightedLocation)
|
||||
if (location == HighlightedLocation) { iconScale *= 1.2f; }
|
||||
if (isEndLocation) { iconScale *= 2.0f; }
|
||||
|
||||
float notificationPulseAmount = 1.0f;
|
||||
float notificationColorLerp = 0.0f;
|
||||
if (mapNotifications.Any(n => n.RelatedLocation == location && n.IsCurrentlyVisible))
|
||||
{
|
||||
iconScale *= 1.2f;
|
||||
float sin = MathF.Sin((float)Timing.TotalTime * 2.0f);
|
||||
notificationPulseAmount = Math.Max(sin + 0.5f, 1.0f);
|
||||
notificationColorLerp = (notificationPulseAmount - 1.0f) * 4.0f;
|
||||
color = Color.Lerp(color, GUIStyle.Yellow, notificationColorLerp);
|
||||
iconScale *= notificationPulseAmount;
|
||||
}
|
||||
|
||||
locationSprite.Draw(spriteBatch, pos, color,
|
||||
locationSprite.Draw(spriteBatch, pos, color,
|
||||
scale: generationParams.LocationIconSize / locationSprite.size.X * iconScale * zoom);
|
||||
|
||||
if (location.Faction != null)
|
||||
{
|
||||
float factionIconScale = iconScale * 0.7f;
|
||||
Sprite factionIcon = location.Faction.Prefab.IconSmall ?? location.Faction.Prefab.Icon;
|
||||
Color factionIconColor = Color.Lerp(color, location.Faction.Prefab.IconColor, notificationColorLerp);
|
||||
factionIcon.Draw(spriteBatch, pos + new Vector2(-15, 15) * zoom, factionIconColor,
|
||||
scale: generationParams.LocationIconSize / factionIcon.size.X * factionIconScale * zoom);
|
||||
}
|
||||
|
||||
if (location == currentDisplayLocation)
|
||||
{
|
||||
if (SelectedLocation != null)
|
||||
@@ -626,7 +725,10 @@ namespace Barotrauma
|
||||
{
|
||||
Vector2 typeChangeIconPos = pos + new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
|
||||
float typeChangeIconScale = 18.0f / generationParams.TypeChangeIcon.SourceRect.Width;
|
||||
generationParams.TypeChangeIcon.Draw(spriteBatch, typeChangeIconPos, GUIStyle.Red, scale: typeChangeIconScale * zoom);
|
||||
Color iconColor = GUIStyle.Red;
|
||||
color = Color.Lerp(color, GUIStyle.Yellow, notificationColorLerp);
|
||||
iconScale *= notificationPulseAmount;
|
||||
generationParams.TypeChangeIcon.Draw(spriteBatch, typeChangeIconPos, iconColor, scale: typeChangeIconScale * zoom);
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, typeChangeIconPos) < generationParams.TypeChangeIcon.SourceRect.Width * zoom &&
|
||||
(tooltip == null || IsPreferredTooltip(typeChangeIconPos)))
|
||||
{
|
||||
@@ -635,14 +737,18 @@ namespace Barotrauma
|
||||
}
|
||||
if (location != CurrentLocation && generationParams.MissionIcon != null)
|
||||
{
|
||||
if ((CurrentLocation == currentDisplayLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location))) || location.AvailableMissions.Any(m => m.Prefab.Type == MissionType.GoTo))
|
||||
if ((CurrentLocation == currentDisplayLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location))) ||
|
||||
location.AvailableMissions.Any(m => m.Locations[0] == m.Locations[1]))
|
||||
{
|
||||
Vector2 missionIconPos = pos + new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
|
||||
float missionIconScale = 18.0f / generationParams.MissionIcon.SourceRect.Width;
|
||||
generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos))
|
||||
{
|
||||
var availableMissions = CurrentLocation.AvailableMissions.Where(m => m.Locations.Contains(location)).Concat(location.AvailableMissions.Where(m => m.Prefab.Type == MissionType.GoTo)).Distinct();
|
||||
var availableMissions = CurrentLocation.AvailableMissions
|
||||
.Where(m => m.Locations.Contains(location))
|
||||
.Concat(location.AvailableMissions.Where(m => m.Locations[0] == m.Locations[1]))
|
||||
.Distinct();
|
||||
tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
|
||||
}
|
||||
}
|
||||
@@ -651,23 +757,12 @@ namespace Barotrauma
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
Vector2 dPos = pos;
|
||||
if (location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()) && location.Reputation != null)
|
||||
if (location == HighlightedLocation)
|
||||
{
|
||||
dPos.Y += 48;
|
||||
string name = $"Reputation: {location.Name}";
|
||||
Vector2 nameSize = GUIStyle.SmallFont.MeasureString(name);
|
||||
GUI.DrawString(spriteBatch, dPos, name, Color.White, Color.Black * 0.8f, 4, font: GUIStyle.SmallFont);
|
||||
dPos.Y += nameSize.Y + 16;
|
||||
|
||||
Rectangle bgRect = new Rectangle((int)dPos.X, (int)dPos.Y, 256, 32);
|
||||
bgRect.Inflate(8,8);
|
||||
Color barColor = ToolBox.GradientLerp(location.Reputation.NormalizedValue, Color.Red, Color.Yellow, Color.LightGreen);
|
||||
GUI.DrawRectangle(spriteBatch, bgRect, Color.Black * 0.8f, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, (int)(location.Reputation.NormalizedValue * 255), 32), barColor, isFilled: true);
|
||||
string reputationValue = ((int)location.Reputation.Value).ToString();
|
||||
Vector2 repValueSize = GUIStyle.SubHeadingFont.MeasureString(reputationValue);
|
||||
GUI.DrawString(spriteBatch, dPos + (new Vector2(256, 32) / 2) - (repValueSize / 2), reputationValue, Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, 256, 32), Color.White);
|
||||
GUI.DrawString(spriteBatch, dPos + new Vector2(15, 32), "Faction: "+(location.Faction?.Prefab.Name ?? "none"), Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
|
||||
GUI.DrawString(spriteBatch, dPos + new Vector2(15, 50), "Secondary Faction: " + (location.SecondaryFaction?.Prefab.Name ?? "none"), Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
|
||||
dPos.Y += 48;
|
||||
}
|
||||
dPos.Y += 48;
|
||||
GUI.DrawString(spriteBatch, dPos, $"Difficulty: {location.LevelData.Difficulty.FormatSingleDecimal()}", Color.White, Color.Black * 0.8f, 4, font: GUIStyle.SmallFont);
|
||||
@@ -693,11 +788,27 @@ namespace Barotrauma
|
||||
pos.Y = (int)pos.Y;
|
||||
Vector2 nameSize = GUIStyle.LargeFont.MeasureString(HighlightedLocation.Name);
|
||||
Vector2 typeSize = HighlightedLocation.Type.Name.IsNullOrEmpty() ? Vector2.Zero : GUIStyle.Font.MeasureString(HighlightedLocation.Type.Name);
|
||||
Vector2 size = new Vector2(Math.Max(nameSize.X, typeSize.X), nameSize.Y + typeSize.Y);
|
||||
bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
|
||||
Vector2 factionSize = HighlightedLocation.Faction == null ? Vector2.Zero : GUIStyle.Font.MeasureString(HighlightedLocation.Faction.Prefab.Name);
|
||||
bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
|
||||
Vector2 descSize = HighlightedLocation.Type.Description.IsNullOrEmpty() ? Vector2.Zero : GUIStyle.SmallFont.MeasureString(HighlightedLocation.Type.Description);
|
||||
Vector2 size = new Vector2(Math.Max(factionSize.X, Math.Max(nameSize.X, Math.Max(typeSize.X, descSize.X))), nameSize.Y + factionSize.Y+ typeSize.Y + descSize.Y);
|
||||
|
||||
int highestSubTier = HighlightedLocation.HighestSubmarineTierAvailable();
|
||||
var overrideTiers = new List<(SubmarineClass subClass, int tier)>();
|
||||
foreach (SubmarineClass subClass in Enum.GetValues(typeof(SubmarineClass)))
|
||||
{
|
||||
if (subClass == SubmarineClass.Undefined) { continue; }
|
||||
int highestClassTier = HighlightedLocation.HighestSubmarineTierAvailable(subClass);
|
||||
if (highestClassTier > 0 && highestClassTier > highestSubTier)
|
||||
{
|
||||
overrideTiers.Add((subClass, highestClassTier));
|
||||
}
|
||||
}
|
||||
size.Y += ((highestSubTier > 0 ? 1 : 0) + overrideTiers.Count) * GUIStyle.SmallFont.MeasureString(TextManager.Get("advancedsub.all")).Y;
|
||||
|
||||
LocalizedString repLabelText = null, repValueText = null;
|
||||
Vector2 repLabelSize = Vector2.Zero, repBarSize = Vector2.Zero;
|
||||
if (showReputation)
|
||||
if (showReputation && HighlightedLocation.Reputation != null)
|
||||
{
|
||||
repLabelText = TextManager.Get("reputation");
|
||||
repLabelSize = GUIStyle.Font.MeasureString(repLabelText);
|
||||
@@ -706,21 +817,59 @@ namespace Barotrauma
|
||||
repValueText = HighlightedLocation.Reputation.GetFormattedReputationText(addColorTags: false);
|
||||
size.X = Math.Max(size.X, repBarSize.X + GUIStyle.Font.MeasureString(repValueText).X + GUI.IntScale(10));
|
||||
}
|
||||
|
||||
GUIStyle.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0].Draw(
|
||||
spriteBatch, new Rectangle((int)(pos.X - 60 * GUI.Scale), (int)(pos.Y - size.Y), (int)(size.X + 120 * GUI.Scale), (int)(size.Y * 2.2f)), Color.Black * hudVisibility);
|
||||
spriteBatch,
|
||||
new Rectangle(
|
||||
(int)(pos.X - 60 * GUI.Scale),
|
||||
(int)(pos.Y - size.Y),
|
||||
(int)(size.X + 120 * GUI.Scale),
|
||||
(int)(size.Y * 2.0f)),
|
||||
Color.Black * hudVisibility);
|
||||
|
||||
var topLeftPos = pos - new Vector2(0.0f, size.Y / 2);
|
||||
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Name, GUIStyle.TextColorNormal * hudVisibility * 1.5f, font: GUIStyle.LargeFont);
|
||||
topLeftPos += new Vector2(0.0f, nameSize.Y);
|
||||
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Type.Name, GUIStyle.TextColorNormal * hudVisibility * 1.5f);
|
||||
|
||||
if (!HighlightedLocation.Type.Name.IsNullOrEmpty())
|
||||
{
|
||||
DrawText(HighlightedLocation.Type.Name);
|
||||
topLeftPos += new Vector2(0.0f, typeSize.Y);
|
||||
}
|
||||
if (HighlightedLocation.Faction != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Faction.Prefab.Name, GUIStyle.TextColorNormal * hudVisibility * 1.5f);
|
||||
topLeftPos += new Vector2(0.0f, factionSize.Y);
|
||||
}
|
||||
if (!HighlightedLocation.Type.Description.IsNullOrEmpty())
|
||||
{
|
||||
DrawText(HighlightedLocation.Type.Description, font: GUIStyle.SmallFont);
|
||||
topLeftPos += new Vector2(0.0f, descSize.Y);
|
||||
}
|
||||
if (highestSubTier > 0)
|
||||
{
|
||||
DrawSubAvailabilityText("advancedsub.all", highestSubTier);
|
||||
}
|
||||
foreach (var (subClass, tier) in overrideTiers)
|
||||
{
|
||||
DrawSubAvailabilityText($"advancedsub.{subClass}", tier);
|
||||
}
|
||||
void DrawSubAvailabilityText(string tag, int tier)
|
||||
{
|
||||
DrawText(TextManager.GetWithVariable(tag, "[tiernumber]", tier.ToString()), font: GUIStyle.SmallFont);
|
||||
topLeftPos += new Vector2(0.0f, typeSize.Y);
|
||||
}
|
||||
if (showReputation)
|
||||
{
|
||||
topLeftPos += new Vector2(0.0f, typeSize.Y + repLabelSize.Y);
|
||||
GUI.DrawString(spriteBatch, topLeftPos, repLabelText.Value, GUIStyle.TextColorNormal * hudVisibility * 1.5f);
|
||||
//topLeftPos += new Vector2(0.0f, typeSize.Y + repLabelSize.Y);
|
||||
DrawText(repLabelText.Value);
|
||||
topLeftPos += new Vector2(0.0f, repLabelSize.Y + GUI.IntScale(10));
|
||||
Rectangle repBarRect = new Rectangle(new Point((int)topLeftPos.X, (int)topLeftPos.Y), new Point((int)repBarSize.X, (int)repBarSize.Y));
|
||||
RoundSummary.DrawReputationBar(spriteBatch, repBarRect, HighlightedLocation.Reputation.NormalizedValue);
|
||||
GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + GUI.IntScale(5), repBarRect.Top), repValueText.Value, Reputation.GetReputationColor(HighlightedLocation.Reputation.NormalizedValue));
|
||||
}
|
||||
|
||||
void DrawText(LocalizedString text, GUIFont font = null) => GUI.DrawString(spriteBatch, topLeftPos, text, GUIStyle.TextColorNormal * hudVisibility * 1.5f, font: font);
|
||||
}
|
||||
|
||||
if (drawRadiationTooltip)
|
||||
@@ -939,17 +1088,15 @@ namespace Barotrauma
|
||||
if (connection.Locked)
|
||||
{
|
||||
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
|
||||
var unlockEvent =
|
||||
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == gateLocation.LevelData.Biome.Identifier) ??
|
||||
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == Identifier.Empty);
|
||||
var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
|
||||
|
||||
if (unlockEvent != null)
|
||||
{
|
||||
Reputation unlockReputation = CurrentLocation.Reputation;
|
||||
Faction unlockFaction = null;
|
||||
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
|
||||
if (!unlockEvent.Faction.IsEmpty)
|
||||
{
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.UnlockPathFaction);
|
||||
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
|
||||
unlockReputation = unlockFaction?.Reputation;
|
||||
}
|
||||
|
||||
@@ -1020,13 +1167,14 @@ namespace Barotrauma
|
||||
private void DrawDecorativeHUD(SpriteBatch spriteBatch, Rectangle rect)
|
||||
{
|
||||
generationParams.DecorativeGraphSprite.Draw(spriteBatch, (int)((Timing.TotalTime * 5.0f) % generationParams.DecorativeGraphSprite.FrameCount),
|
||||
new Vector2(rect.Left, rect.Top), Color.White, Vector2.Zero, 0, Vector2.One * GUI.Scale);
|
||||
new Vector2(rect.X, rect.Bottom - (generationParams.DecorativeGraphSprite.FrameSize.Y + 30) * GUI.Scale),
|
||||
Color.White, Vector2.Zero, 0, Vector2.One * GUI.Scale, SpriteEffects.FlipVertically);
|
||||
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(rect.Right - GUI.IntScale(170), rect.Y + GUI.IntScale(5)),
|
||||
"JOVIAN FLUX " + ((cameraNoiseStrength + Rand.Range(-0.02f, 0.02f)) * 500), generationParams.IndicatorColor * hudVisibility, font: GUIStyle.SmallFont);
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(rect.X + GUI.IntScale(15), rect.Bottom - GUI.IntScale(25)),
|
||||
new Vector2(rect.X + GUI.IntScale(5), rect.Y + GUI.IntScale(5)),
|
||||
"LAT " + (-DrawOffset.Y / 100.0f) + " LON " + (-DrawOffset.X / 100.0f), generationParams.IndicatorColor * hudVisibility, font: GUIStyle.SmallFont);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,11 +99,24 @@ namespace Barotrauma
|
||||
{
|
||||
float depth = baseDepth
|
||||
//take texture into account to get entities with (roughly) the same base depth and texture to render consecutively to minimize texture swaps
|
||||
+ (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.00001f
|
||||
+ ID % 100 * 0.000001f;
|
||||
+ (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.000001f
|
||||
+ ID % 100 * 0.0000001f;
|
||||
return Math.Min(depth, 1.0f);
|
||||
}
|
||||
|
||||
protected Vector2 GetCollapseEffectOffset()
|
||||
{
|
||||
if (Level.Loaded?.Renderer?.CollapseEffectStrength is float collapseEffectStrength and > 0.0f && Submarine is not { Info.Type: SubmarineType.Player })
|
||||
{
|
||||
Vector2 noisePos = new Vector2(
|
||||
(float)PerlinNoise.GetPerlin((float)(Timing.TotalTime + ID) * 0.1f, (float)(Timing.TotalTime + ID) * 0.5f) - 0.5f,
|
||||
(float)PerlinNoise.GetPerlin((float)(Timing.TotalTime + ID) * 0.1f, (float)(Timing.TotalTime + ID) * 0.1f) - 0.5f);
|
||||
Vector2 offsetFromOrigin = Level.Loaded.Renderer.CollapseEffectOrigin - DrawPosition;
|
||||
return offsetFromOrigin * MathF.Pow(collapseEffectStrength, MathHelper.Lerp(1, 4, ID % 1000 / 1000.0f)) + (noisePos * 100.0f * collapseEffectStrength);
|
||||
}
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the selection logic in submarine editor
|
||||
/// </summary>
|
||||
|
||||
@@ -295,6 +295,7 @@ namespace Barotrauma
|
||||
if (isWiringMode) { color *= 0.15f; }
|
||||
|
||||
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
|
||||
drawOffset += GetCollapseEffectOffset();
|
||||
|
||||
float depth = GetDrawDepth();
|
||||
|
||||
|
||||
@@ -35,6 +35,13 @@ namespace Barotrauma
|
||||
Rectangle camView = cam.WorldView;
|
||||
camView = new Rectangle(camView.X - CullMargin, camView.Y + CullMargin, camView.Width + CullMargin * 2, camView.Height + CullMargin * 2);
|
||||
|
||||
if (Level.Loaded?.Renderer?.CollapseEffectStrength is > 0.0f)
|
||||
{
|
||||
//force everything to be visible when the collapse effect (which moves everything to a single point) is active
|
||||
camView = Rectangle.Union(AbsRect(camView.Location.ToVector2(), camView.Size.ToVector2()), new Rectangle(Point.Zero, Level.Loaded.Size));
|
||||
camView.Y += camView.Height;
|
||||
}
|
||||
|
||||
if (Math.Abs(camView.X - prevCullArea.X) < CullMoveThreshold &&
|
||||
Math.Abs(camView.Y - prevCullArea.Y) < CullMoveThreshold &&
|
||||
Math.Abs(camView.Right - prevCullArea.Right) < CullMoveThreshold &&
|
||||
|
||||
@@ -123,6 +123,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (spawnType == SpawnType.ExitPoint && ExitPointSize != Point.Zero)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, drawPos - ExitPointSize.ToVector2() / 2, ExitPointSize.ToVector2(), Color.Cyan, thickness: 5);
|
||||
}
|
||||
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch,
|
||||
ID.ToString(),
|
||||
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30),
|
||||
@@ -251,6 +256,7 @@ namespace Barotrauma
|
||||
|
||||
private bool ChangeSpawnType(GUIButton button, object obj)
|
||||
{
|
||||
var prevSpawnType = spawnType;
|
||||
GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock;
|
||||
var values = (SpawnType[])Enum.GetValues(typeof(SpawnType));
|
||||
int currIndex = values.IndexOf(spawnType);
|
||||
@@ -267,6 +273,7 @@ namespace Barotrauma
|
||||
}
|
||||
spawnType = values[currIndex];
|
||||
spawnTypeText.Text = spawnType.ToString();
|
||||
if (spawnType == SpawnType.ExitPoint || prevSpawnType == SpawnType.ExitPoint) { CreateEditingHUD(); }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -412,6 +419,28 @@ namespace Barotrauma
|
||||
textBox.Text = string.Join(",", tags);
|
||||
textBox.Flash(GUIStyle.Green);
|
||||
};
|
||||
|
||||
if (SpawnType == SpawnType.ExitPoint)
|
||||
{
|
||||
var sizeField = GUI.CreatePointField(ExitPointSize, GUI.IntScale(20), TextManager.Get("dimensions"), paddedFrame.RectTransform);
|
||||
GUINumberInput xField = null, yField = null;
|
||||
foreach (GUIComponent child in sizeField.GetAllChildren())
|
||||
{
|
||||
if (yField == null)
|
||||
{
|
||||
yField = child as GUINumberInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
xField = child as GUINumberInput;
|
||||
if (xField != null) { break; }
|
||||
}
|
||||
}
|
||||
xField.MinValueInt = 0;
|
||||
xField.OnValueChanged = (numberInput) => { ExitPointSize = new Point(numberInput.IntValue, ExitPointSize.Y); };
|
||||
yField.MinValueInt = 0;
|
||||
yField.OnValueChanged = (numberInput) => { ExitPointSize = new Point(ExitPointSize.X, numberInput.IntValue); };
|
||||
}
|
||||
}
|
||||
|
||||
editingHUD.RectTransform.Resize(new Point(
|
||||
|
||||
@@ -311,6 +311,12 @@ namespace Barotrauma.Networking
|
||||
CoroutineManager.StartCoroutine(WaitForStartingInfo(), "WaitForStartingInfo");
|
||||
}
|
||||
|
||||
public void SetLobbyPublic(bool isPublic)
|
||||
{
|
||||
GameMain.NetLobbyScreen.SetPublic(isPublic);
|
||||
SteamManager.SetLobbyPublic(isPublic);
|
||||
}
|
||||
|
||||
private ClientPeer CreateNetPeer()
|
||||
{
|
||||
Networking.ClientPeer.Callbacks callbacks = new ClientPeer.Callbacks(
|
||||
@@ -1319,6 +1325,7 @@ namespace Barotrauma.Networking
|
||||
ServerSettings.MaximumMoneyTransferRequest = inc.ReadInt32();
|
||||
bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle = inc.ReadBoolean();
|
||||
GameMain.LightManager.LosMode = (LosMode)inc.ReadByte();
|
||||
ServerSettings.ShowEnemyHealthBars = (EnemyHealthBarMode)inc.ReadByte();
|
||||
bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits();
|
||||
GameMain.LightManager.LightingEnabled = true;
|
||||
|
||||
@@ -2521,7 +2528,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!(entity is IClientSerializable clientSerializable))
|
||||
if (entity is not IClientSerializable clientSerializable)
|
||||
{
|
||||
throw new InvalidCastException($"Entity is not {nameof(IClientSerializable)}");
|
||||
}
|
||||
|
||||
@@ -277,16 +277,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ID = 0;
|
||||
|
||||
lastReceivedID = 0;
|
||||
|
||||
firstNewID = null;
|
||||
|
||||
events.Clear();
|
||||
eventLastSent.Clear();
|
||||
|
||||
MidRoundSyncingDone = false;
|
||||
|
||||
ClearSelf();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -297,6 +293,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ID = 0;
|
||||
events.Clear();
|
||||
if (thisClient != null)
|
||||
{
|
||||
thisClient.LastSentEntityEventID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
static class PingUtils
|
||||
{
|
||||
private static readonly Dictionary<IPAddress, int> activePings = new Dictionary<IPAddress, int>();
|
||||
private static readonly Dictionary<IPEndPoint, int> activePings = new Dictionary<IPEndPoint, int>();
|
||||
|
||||
private static bool steamPingInfoReady;
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
switch (serverInfo.Endpoint)
|
||||
{
|
||||
case LidgrenEndpoint { NetEndpoint: { Address: var address } }:
|
||||
case LidgrenEndpoint { NetEndpoint: var endPoint }:
|
||||
|
||||
GetIPAddressPing(serverInfo, address, onPingDiscovered);
|
||||
GetIPAddressPing(serverInfo, endPoint, onPingDiscovered);
|
||||
break;
|
||||
case SteamP2PEndpoint steamP2PEndpoint:
|
||||
TaskPool.Add($"EstimateSteamLobbyPing ({steamP2PEndpoint.StringRepresentation})",
|
||||
@@ -131,9 +131,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetIPAddressPing(ServerInfo serverInfo, IPAddress address, Action<ServerInfo> onPingDiscovered)
|
||||
private static void GetIPAddressPing(ServerInfo serverInfo, IPEndPoint endPoint, Action<ServerInfo> onPingDiscovered)
|
||||
{
|
||||
if (IPAddress.IsLoopback(address))
|
||||
if (IPAddress.IsLoopback(endPoint.Address))
|
||||
{
|
||||
serverInfo.Ping = Option<int>.Some(0);
|
||||
onPingDiscovered(serverInfo);
|
||||
@@ -142,24 +142,24 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
lock (activePings)
|
||||
{
|
||||
if (activePings.ContainsKey(address)) { return; }
|
||||
activePings.Add(address, activePings.Any() ? activePings.Values.Max() + 1 : 0);
|
||||
if (activePings.ContainsKey(endPoint)) { return; }
|
||||
activePings.Add(endPoint, activePings.Any() ? activePings.Values.Max() + 1 : 0);
|
||||
}
|
||||
serverInfo.Ping = Option<int>.None();
|
||||
TaskPool.Add($"PingServerAsync ({address})", PingServerAsync(address, 1000),
|
||||
TaskPool.Add($"PingServerAsync ({endPoint})", PingServerAsync(endPoint, 1000),
|
||||
rtt =>
|
||||
{
|
||||
if (!rtt.TryGetResult(out serverInfo.Ping)) { serverInfo.Ping = Option<int>.None(); }
|
||||
onPingDiscovered(serverInfo);
|
||||
lock (activePings)
|
||||
{
|
||||
activePings.Remove(address);
|
||||
activePings.Remove(endPoint);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<Option<int>> PingServerAsync(IPAddress ipAddress, int timeOut)
|
||||
private static async Task<Option<int>> PingServerAsync(IPEndPoint endPoint, int timeOut)
|
||||
{
|
||||
await Task.Yield();
|
||||
bool shouldGo = false;
|
||||
@@ -167,21 +167,21 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
lock (activePings)
|
||||
{
|
||||
shouldGo = activePings.Count(kvp => kvp.Value < activePings[ipAddress]) < 25;
|
||||
shouldGo = activePings.Count(kvp => kvp.Value < activePings[endPoint]) < 25;
|
||||
}
|
||||
await Task.Delay(25);
|
||||
}
|
||||
|
||||
if (ipAddress == null) { return Option<int>.None(); }
|
||||
if (endPoint?.Address == null) { return Option<int>.None(); }
|
||||
|
||||
//don't attempt to ping if the address is IPv6 and it's not supported
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) { return Option<int>.None(); }
|
||||
if (endPoint.Address.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6) { return Option<int>.None(); }
|
||||
|
||||
Ping ping = new Ping();
|
||||
byte[] buffer = new byte[32];
|
||||
try
|
||||
{
|
||||
PingReply pingReply = await ping.SendPingAsync(ipAddress, timeOut, buffer, new PingOptions(128, true));
|
||||
PingReply pingReply = await ping.SendPingAsync(endPoint.Address, timeOut, buffer, new PingOptions(128, true));
|
||||
|
||||
return pingReply.Status switch
|
||||
{
|
||||
@@ -191,9 +191,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + ipAddress, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
|
||||
GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + endPoint.Address, GameAnalyticsManager.ErrorSeverity.Warning, "Failed to ping a server - " + (ex?.InnerException?.Message ?? ex.Message));
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage("Failed to ping a server (" + ipAddress + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
|
||||
DebugConsole.NewMessage("Failed to ping a server (" + endPoint.Address + ") - " + (ex?.InnerException?.Message ?? ex.Message), Color.Red);
|
||||
#endif
|
||||
|
||||
return Option<int>.None();
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace Barotrauma.Networking
|
||||
MaxPlayers = incMsg.ReadByte();
|
||||
HasPassword = incMsg.ReadBoolean();
|
||||
IsPublic = incMsg.ReadBoolean();
|
||||
GameMain.NetLobbyScreen.SetPublic(IsPublic);
|
||||
GameMain.Client?.SetLobbyPublic(IsPublic);
|
||||
AllowFileTransfers = incMsg.ReadBoolean();
|
||||
incMsg.ReadPadBits();
|
||||
TickRate = incMsg.ReadRangedInteger(1, 60);
|
||||
@@ -367,6 +367,17 @@ namespace Barotrauma.Networking
|
||||
|
||||
//***********************************************
|
||||
|
||||
//changing server visibility on the fly is not supported in dedicated servers
|
||||
if (GameMain.Client?.ClientPeer is not LidgrenClientPeer)
|
||||
{
|
||||
var isPublic = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), serverTab.RectTransform),
|
||||
TextManager.Get("publicserver"))
|
||||
{
|
||||
ToolTip = TextManager.Get("publicservertooltip")
|
||||
};
|
||||
GetPropertyData(nameof(IsPublic)).AssignGUIComponent(isPublic);
|
||||
}
|
||||
|
||||
// Sub Selection
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverTab.RectTransform), TextManager.Get("ServerSettingsSubSelection"), font: GUIStyle.SubHeadingFont);
|
||||
var selectionFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.02f), serverTab.RectTransform), isHorizontal: true)
|
||||
@@ -475,9 +486,10 @@ namespace Barotrauma.Networking
|
||||
// game settings
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
var roundsTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.Center)) { };
|
||||
var roundsTab = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), settingsTabs[(int)SettingsTab.Rounds].RectTransform, Anchor.Center));
|
||||
var roundsContent = new GUIListBox(new RectTransform(Vector2.One, roundsTab.RectTransform, Anchor.Center), style: "GUIListBoxNoBorder").Content;
|
||||
|
||||
GUILayoutGroup playStyleLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), roundsTab.RectTransform));
|
||||
GUILayoutGroup playStyleLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), roundsContent.RectTransform));
|
||||
// Play Style Selection
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), playStyleLayout.RectTransform), TextManager.Get("ServerSettingsPlayStyle"), font: GUIStyle.SubHeadingFont);
|
||||
var playstyleList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), playStyleLayout.RectTransform))
|
||||
@@ -502,7 +514,7 @@ namespace Barotrauma.Networking
|
||||
GUITextBlock.AutoScaleAndNormalize(playStyleTickBoxes.Select(t => t.TextBlock));
|
||||
playstyleList.RectTransform.MinSize = new Point(0, (int)(playstyleList.Content.Children.First().Rect.Height * 2.0f + playstyleList.Padding.Y + playstyleList.Padding.W));
|
||||
|
||||
GUILayoutGroup sliderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), roundsTab.RectTransform))
|
||||
GUILayoutGroup sliderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.35f), roundsContent.RectTransform))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
@@ -608,7 +620,7 @@ namespace Barotrauma.Networking
|
||||
};
|
||||
slider.OnMoved(slider, slider.BarScroll);
|
||||
|
||||
GUILayoutGroup losModeLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.14f), roundsTab.RectTransform));
|
||||
GUILayoutGroup losModeLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.14f), roundsContent.RectTransform));
|
||||
|
||||
var losModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), losModeLayout.RectTransform),
|
||||
TextManager.Get("LosEffect"));
|
||||
@@ -629,7 +641,30 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
GetPropertyData(nameof(LosMode)).AssignGUIComponent(losModeRadioButtonGroup);
|
||||
|
||||
GUILayoutGroup numberLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), roundsTab.RectTransform))
|
||||
GUILayoutGroup healthBarModeLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.14f), roundsContent.RectTransform));
|
||||
|
||||
var healthBarModeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), healthBarModeLayout.RectTransform),
|
||||
TextManager.Get("ShowEnemyHealthBars"));
|
||||
|
||||
var healthBarModeRadioButtonLayout
|
||||
= new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), healthBarModeLayout.RectTransform),
|
||||
isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var healthBarModeRadioButtonGroup = new GUIRadioButtonGroup();
|
||||
EnemyHealthBarMode[] healthBarModeModes = Enum.GetValues<EnemyHealthBarMode>();
|
||||
for (int i = 0; i < healthBarModeModes.Length; i++)
|
||||
{
|
||||
var losTick = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), healthBarModeRadioButtonLayout.RectTransform),
|
||||
TextManager.Get($"ShowEnemyHealthBars.{healthBarModeModes[i]}"),
|
||||
font: GUIStyle.SmallFont, style: "GUIRadioButton");
|
||||
healthBarModeRadioButtonGroup.AddRadioButton(i, losTick);
|
||||
}
|
||||
GetPropertyData(nameof(ShowEnemyHealthBars)).AssignGUIComponent(healthBarModeRadioButtonGroup);
|
||||
|
||||
GUILayoutGroup numberLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), roundsContent.RectTransform))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
@@ -651,7 +686,7 @@ namespace Barotrauma.Networking
|
||||
var disableBotConversationsBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), numberLayout.RectTransform), TextManager.Get("ServerSettingsDisableBotConversations"));
|
||||
GetPropertyData(nameof(DisableBotConversations)).AssignGUIComponent(disableBotConversationsBox);
|
||||
|
||||
GUILayoutGroup buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), roundsTab.RectTransform), isHorizontal: true)
|
||||
GUILayoutGroup buttonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), roundsContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f
|
||||
|
||||
@@ -82,6 +82,12 @@ namespace Barotrauma.Particles
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool CopyEntityAngle { get; set; }
|
||||
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the entity heading direction be applied to the particle rotation? Only affects after flipping the texture and when CopyEntityAngle is true.")]
|
||||
public bool CopyEntityDir { get; set; }
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Only relevant for status effects. Makes the emitter copy the angle from the target of the effect instead of the entity applying the effect.")]
|
||||
public bool CopyTargetAngle { get; set; }
|
||||
|
||||
[Editable, Serialize("1,1,1,1", IsPropertySaveable.Yes)]
|
||||
public Color ColorMultiplier { get; set; }
|
||||
|
||||
@@ -200,7 +206,7 @@ namespace Barotrauma.Particles
|
||||
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);
|
||||
}
|
||||
|
||||
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, lifeTimeMultiplier: Prefab.Properties.LifeTimeMultiplier, tracerPoints: tracerPoints);
|
||||
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, lifeTimeMultiplier: Prefab.Properties.LifeTimeMultiplier, tracerPoints: tracerPoints);
|
||||
|
||||
if (particle != null)
|
||||
{
|
||||
|
||||
@@ -75,21 +75,21 @@ namespace Barotrauma.Particles
|
||||
return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer, tracerPoints:tracerPoints);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, float lifeTimeMultiplier = 1f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, float collisionIgnoreTimer = 0f, float lifeTimeMultiplier = 1f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
if (prefab == null || prefab.Sprites.Count == 0) { return null; }
|
||||
|
||||
if (particleCount >= MaxParticles)
|
||||
{
|
||||
for (int i = 0; i < particleCount; i++)
|
||||
{
|
||||
if (particles[i].Prefab.Priority < prefab.Priority)
|
||||
if (particles[i].Prefab.Priority < prefab.Priority ||
|
||||
(!particles[i].Prefab.DrawAlways && prefab.DrawAlways))
|
||||
{
|
||||
RemoveParticle(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (particleCount >= MaxParticles) { return null; }
|
||||
if (particleCount >= MaxParticles) { return null; }
|
||||
}
|
||||
|
||||
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
|
||||
@@ -109,26 +109,30 @@ namespace Barotrauma.Particles
|
||||
|
||||
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
|
||||
|
||||
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
|
||||
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
|
||||
if (!prefab.DrawAlways)
|
||||
{
|
||||
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
|
||||
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
|
||||
}
|
||||
|
||||
if (particles[particleCount] == null) { particles[particleCount] = new Particle(); }
|
||||
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, lifeTimeMultiplier, tracerPoints: tracerPoints);
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, prefab.DrawOnTop, collisionIgnoreTimer, lifeTimeMultiplier, tracerPoints: tracerPoints);
|
||||
|
||||
particleCount++;
|
||||
|
||||
return particles[particleCount - 1];
|
||||
}
|
||||
|
||||
public List<ParticlePrefab> GetPrefabList()
|
||||
public static List<ParticlePrefab> GetPrefabList()
|
||||
{
|
||||
return ParticlePrefab.Prefabs.ToList();
|
||||
}
|
||||
|
||||
public ParticlePrefab FindPrefab(string prefabName)
|
||||
public static ParticlePrefab FindPrefab(string prefabName)
|
||||
{
|
||||
return ParticlePrefab.Prefabs.Find(p => p.Identifier == prefabName);
|
||||
ParticlePrefab.Prefabs.TryGet(prefabName, out ParticlePrefab prefab);
|
||||
return prefab;
|
||||
}
|
||||
|
||||
private void RemoveParticle(int index)
|
||||
@@ -170,7 +174,7 @@ namespace Barotrauma.Particles
|
||||
remove = true;
|
||||
}
|
||||
|
||||
if (remove) RemoveParticle(i);
|
||||
if (remove) { RemoveParticle(i); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +185,9 @@ namespace Barotrauma.Particles
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the particle be always rendered on top of entities?")]
|
||||
public bool DrawOnTop { get; private set; }
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Draw the particle even when it's calculated to be outside of view (the formula doesn't take scales into account). ")]
|
||||
public bool DrawAlways { get; private set; }
|
||||
|
||||
[Editable, Serialize(ParticleBlendState.AlphaBlend, IsPropertySaveable.No, description: "The type of blending to use when rendering the particle.")]
|
||||
public ParticleBlendState BlendState { get; private set; }
|
||||
|
||||
|
||||
@@ -79,13 +79,13 @@ namespace Barotrauma
|
||||
new Vector2(DrawPosition.X, -DrawPosition.Y),
|
||||
Color.Cyan, 0, 5);
|
||||
}
|
||||
if (bodyShapeTexture == null && IsValidShape(radius, height, width))
|
||||
if (bodyShapeTexture == null && IsValidShape(Radius, Height, Width))
|
||||
{
|
||||
switch (BodyShape)
|
||||
{
|
||||
case Shape.Rectangle:
|
||||
{
|
||||
float maxSize = Math.Max(ConvertUnits.ToDisplayUnits(width), ConvertUnits.ToDisplayUnits(height));
|
||||
float maxSize = Math.Max(ConvertUnits.ToDisplayUnits(Width), ConvertUnits.ToDisplayUnits(Height));
|
||||
if (maxSize > 128.0f)
|
||||
{
|
||||
bodyShapeTextureScale = 128.0f / maxSize;
|
||||
@@ -96,14 +96,14 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bodyShapeTexture = GUI.CreateRectangle(
|
||||
(int)ConvertUnits.ToDisplayUnits(width * bodyShapeTextureScale),
|
||||
(int)ConvertUnits.ToDisplayUnits(height * bodyShapeTextureScale));
|
||||
(int)ConvertUnits.ToDisplayUnits(Width * bodyShapeTextureScale),
|
||||
(int)ConvertUnits.ToDisplayUnits(Height * bodyShapeTextureScale));
|
||||
break;
|
||||
}
|
||||
case Shape.Capsule:
|
||||
case Shape.HorizontalCapsule:
|
||||
{
|
||||
float maxSize = Math.Max(ConvertUnits.ToDisplayUnits(radius), ConvertUnits.ToDisplayUnits(Math.Max(height, width)));
|
||||
float maxSize = Math.Max(ConvertUnits.ToDisplayUnits(Radius), ConvertUnits.ToDisplayUnits(Math.Max(Height, Width)));
|
||||
if (maxSize > 128.0f)
|
||||
{
|
||||
bodyShapeTextureScale = 128.0f / maxSize;
|
||||
@@ -114,20 +114,20 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bodyShapeTexture = GUI.CreateCapsule(
|
||||
(int)ConvertUnits.ToDisplayUnits(radius * bodyShapeTextureScale),
|
||||
(int)ConvertUnits.ToDisplayUnits(Math.Max(height, width) * bodyShapeTextureScale));
|
||||
(int)ConvertUnits.ToDisplayUnits(Radius * bodyShapeTextureScale),
|
||||
(int)ConvertUnits.ToDisplayUnits(Math.Max(Height, Width) * bodyShapeTextureScale));
|
||||
break;
|
||||
}
|
||||
case Shape.Circle:
|
||||
if (ConvertUnits.ToDisplayUnits(radius) > 128.0f)
|
||||
if (ConvertUnits.ToDisplayUnits(Radius) > 128.0f)
|
||||
{
|
||||
bodyShapeTextureScale = 128.0f / ConvertUnits.ToDisplayUnits(radius);
|
||||
bodyShapeTextureScale = 128.0f / ConvertUnits.ToDisplayUnits(Radius);
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyShapeTextureScale = 1.0f;
|
||||
}
|
||||
bodyShapeTexture = GUI.CreateCircle((int)ConvertUnits.ToDisplayUnits(radius * bodyShapeTextureScale));
|
||||
bodyShapeTexture = GUI.CreateCircle((int)ConvertUnits.ToDisplayUnits(Radius * bodyShapeTextureScale));
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace Barotrauma
|
||||
switch (MouseButton)
|
||||
{
|
||||
case MouseButton.None:
|
||||
if (Key == Keys.None) { return false; }
|
||||
return PlayerInput.KeyDown(Key);
|
||||
case MouseButton.PrimaryMouse:
|
||||
return PlayerInput.PrimaryMouseButtonHeld();
|
||||
@@ -88,6 +89,7 @@ namespace Barotrauma
|
||||
switch (MouseButton)
|
||||
{
|
||||
case MouseButton.None:
|
||||
if (Key == Keys.None) { return false; }
|
||||
return PlayerInput.KeyHit(Key);
|
||||
case MouseButton.PrimaryMouse:
|
||||
return PlayerInput.PrimaryMouseButtonClicked();
|
||||
|
||||
@@ -112,10 +112,23 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("Barotrauma Client crash report (generated on " + DateTime.Now + ")");
|
||||
sb.AppendLine("\n");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! ");
|
||||
sb.AppendLine("\n");
|
||||
sb.AppendLine();
|
||||
|
||||
string dxgiErrorHelpText =
|
||||
#if WINDOWS
|
||||
GetDXGIErrorHelpText(game, exception);
|
||||
#else
|
||||
string.Empty;
|
||||
#endif
|
||||
if (!string.IsNullOrEmpty(dxgiErrorHelpText))
|
||||
{
|
||||
sb.AppendLine(dxgiErrorHelpText);
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -135,7 +148,7 @@ namespace Barotrauma
|
||||
XDocument newDoc = new XDocument(newElement);
|
||||
newDoc.Save(GameSettings.PlayerConfigPath);
|
||||
sb.AppendLine("To prevent further startup errors, installed mods will be disabled the next time you launch the game.");
|
||||
sb.AppendLine("\n");
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +161,7 @@ namespace Barotrauma
|
||||
{
|
||||
sb.AppendLine(exeHash.StringRepresentation);
|
||||
}
|
||||
sb.AppendLine("\n");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Game version " + GameMain.Version +
|
||||
" (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")");
|
||||
sb.AppendLine($"Graphics mode: {GameSettings.CurrentConfig.Graphics.Width}x{GameSettings.CurrentConfig.Graphics.Height} ({GameSettings.CurrentConfig.Graphics.DisplayMode})");
|
||||
@@ -171,7 +184,7 @@ namespace Barotrauma
|
||||
sb.AppendLine("Client (" + (GameMain.Client.GameStarted ? "Round had started)" : "Round hadn't been started)"));
|
||||
}
|
||||
|
||||
sb.AppendLine("\n");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("System info:");
|
||||
sb.AppendLine(" Operating system: " + System.Environment.OSVersion + (System.Environment.Is64BitOperatingSystem ? " 64 bit" : " x86"));
|
||||
|
||||
@@ -201,13 +214,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\n");
|
||||
sb.AppendLine("Exception: " + exception.Message + " (" + exception.GetType().ToString() + ")");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"Exception: {exception.Message} ({exception.GetType()})");
|
||||
#if WINDOWS
|
||||
if (exception is SharpDXException sharpDxException && ((uint)sharpDxException.HResult) == 0x887A0005)
|
||||
{
|
||||
var dxDevice = (SharpDX.Direct3D11.Device)game.GraphicsDevice.Handle;
|
||||
sb.AppendLine("Device removed reason: " + dxDevice.DeviceRemovedReason.ToString());
|
||||
var descriptor = ResultDescriptor.Find(dxDevice.DeviceRemovedReason)?.ApiCode ?? "UNKNOWN";
|
||||
sb.AppendLine($"Device removed reason: {descriptor} ({dxDevice.DeviceRemovedReason})");
|
||||
}
|
||||
#endif
|
||||
if (exception.TargetSite != null)
|
||||
@@ -219,7 +233,7 @@ namespace Barotrauma
|
||||
{
|
||||
sb.AppendLine("Stack trace: ");
|
||||
sb.AppendLine(exception.StackTrace.CleanupStackTrace());
|
||||
sb.AppendLine("\n");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (exception.InnerException != null)
|
||||
@@ -260,18 +274,43 @@ namespace Barotrauma
|
||||
|
||||
if (GameSettings.CurrentConfig.SaveDebugConsoleLogs
|
||||
|| GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.SaveLogs(); }
|
||||
|
||||
|
||||
string msg = string.Empty;
|
||||
if (GameAnalyticsManager.SendUserStatistics)
|
||||
{
|
||||
CrashMessageBox("A crash report (\"" + filePath + "\") was saved in the root folder of the game and sent to the developers.", filePath);
|
||||
msg = "A crash report (\"" + filePath + "\") was saved in the root folder of the game and sent to the developers.";
|
||||
}
|
||||
else
|
||||
{
|
||||
CrashMessageBox("A crash report (\"" + filePath + "\") was saved in the root folder of the game. The error was not sent to the developers because user statistics have been disabled, but" +
|
||||
" if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/", filePath);
|
||||
msg = "A crash report (\"" + filePath + "\") was saved in the root folder of the game. The error was not sent to the developers because user statistics have been disabled, but" +
|
||||
" if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/";
|
||||
}
|
||||
if (string.IsNullOrEmpty(dxgiErrorHelpText))
|
||||
{
|
||||
msg += "\n\n" + dxgiErrorHelpText;
|
||||
}
|
||||
CrashMessageBox(msg, filePath);
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
private static string GetDXGIErrorHelpText(GameMain game, Exception exception)
|
||||
{
|
||||
string text = string.Empty;
|
||||
if (exception is SharpDXException sharpDxException && ((uint)sharpDxException.HResult) == 0x887A0005)
|
||||
{
|
||||
var dxDevice = (SharpDX.Direct3D11.Device)game.GraphicsDevice.Handle;
|
||||
var descriptor = ResultDescriptor.Find(dxDevice.DeviceRemovedReason)?.ApiCode ?? "UNKNOWN";
|
||||
|
||||
text +=
|
||||
$"The crash was caused by the DirectX error {descriptor} ({dxDevice.DeviceRemovedReason}). " +
|
||||
"This is a common DirectX error that can be related to various different issues, such as outdated drivers, RAM problems or an overclocked or otherwise overstressed GPU. " +
|
||||
"There are several potential ways to fix the issue: ensuring your graphics drivers and DirectX installation are up-to-date, disabling overclocking and adjusting various GPU-specific settings. " +
|
||||
$"You may also be able to find potential solutions to the problem by using the error code {descriptor} ({dxDevice.DeviceRemovedReason}) and your GPU manufacturer as search terms.";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
#endif
|
||||
|
||||
private static IntPtr nvApi64Dll = IntPtr.Zero;
|
||||
private static void EnableNvOptimus()
|
||||
{
|
||||
@@ -287,11 +326,11 @@ namespace Barotrauma
|
||||
|
||||
private static void FreeNvOptimus()
|
||||
{
|
||||
#warning TODO: determine if we can do this safely
|
||||
#warning TODO: determine if we can do this safely
|
||||
//NativeLibrary.Free(nvApi64Dll);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,17 +7,13 @@ namespace Barotrauma
|
||||
{
|
||||
class CampaignEndScreen : Screen
|
||||
{
|
||||
private Video video;
|
||||
|
||||
private readonly CreditsPlayer creditsPlayer;
|
||||
|
||||
private readonly Camera cam;
|
||||
|
||||
public Action OnFinished;
|
||||
|
||||
private LocalizedString textOverlay;
|
||||
private float textOverlayTimer;
|
||||
private Vector2 textOverlaySize;
|
||||
protected SlideshowPlayer slideshowPlayer;
|
||||
|
||||
public CampaignEndScreen()
|
||||
{
|
||||
@@ -42,13 +38,10 @@ namespace Barotrauma
|
||||
public override void Select()
|
||||
{
|
||||
base.Select();
|
||||
|
||||
textOverlay = ToolBox.WrapText(TextManager.Get("campaignend1"), GameMain.GraphicsWidth / 3, GUIStyle.Font);
|
||||
textOverlaySize = GUIStyle.Font.MeasureString(textOverlay);
|
||||
textOverlayTimer = 0.0f;
|
||||
|
||||
video = Video.Load(GameMain.GraphicsDeviceManager.GraphicsDevice, GameMain.SoundManager, "Content/SplashScreens/Ending.webm");
|
||||
video.Play();
|
||||
if (SlideshowPrefab.Prefabs.TryGet("campaignending".ToIdentifier(), out var slideshow))
|
||||
{
|
||||
slideshowPlayer = new SlideshowPlayer(GUICanvas.Instance, slideshow);
|
||||
}
|
||||
creditsPlayer.Restart();
|
||||
creditsPlayer.Visible = false;
|
||||
SteamAchievementManager.UnlockAchievement("campaigncompleted".ToIdentifier(), unlockClients: true);
|
||||
@@ -56,14 +49,13 @@ namespace Barotrauma
|
||||
|
||||
public override void Deselect()
|
||||
{
|
||||
video?.Dispose();
|
||||
video = null;
|
||||
GUI.HideCursor = false;
|
||||
SoundPlayer.OverrideMusicType = Identifier.Empty;
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
slideshowPlayer?.UpdateManually((float)deltaTime);
|
||||
if (creditsPlayer.Finished)
|
||||
{
|
||||
OnFinished?.Invoke();
|
||||
@@ -73,46 +65,18 @@ namespace Barotrauma
|
||||
|
||||
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
||||
{
|
||||
spriteBatch.Begin();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable);
|
||||
graphics.Clear(Color.Black);
|
||||
if (video.IsPlaying)
|
||||
SoundPlayer.OverrideMusicType = "ending".ToIdentifier();
|
||||
if (slideshowPlayer != null && !slideshowPlayer.Finished)
|
||||
{
|
||||
GUI.HideCursor = !GUI.PauseMenuOpen;
|
||||
spriteBatch.Draw(video.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
|
||||
slideshowPlayer.DrawManually(spriteBatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
SoundPlayer.OverrideMusicType = "ending".ToIdentifier();
|
||||
float duration = 20.0f;
|
||||
float creditsDelay = 3.0f;
|
||||
if (textOverlayTimer < duration + creditsDelay)
|
||||
{
|
||||
float textAlpha;
|
||||
float fadeInTime = 5.0f, fadeOutTime = 3.0f;
|
||||
textOverlayTimer += (float)deltaTime;
|
||||
if (textOverlayTimer < fadeInTime)
|
||||
{
|
||||
textAlpha = textOverlayTimer / fadeInTime;
|
||||
}
|
||||
else if (textOverlayTimer > duration - fadeOutTime)
|
||||
{
|
||||
textAlpha = Math.Min((duration - textOverlayTimer) / fadeOutTime, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
textAlpha = 1.0f;
|
||||
}
|
||||
GUIStyle.Font.DrawString(spriteBatch, textOverlay, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 - textOverlaySize / 2, Color.White * textAlpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.HideCursor = false;
|
||||
creditsPlayer.Visible = true;
|
||||
}
|
||||
GUI.HideCursor = false;
|
||||
creditsPlayer.Visible = true;
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable);
|
||||
GUI.Draw(cam, spriteBatch);
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace Barotrauma
|
||||
|
||||
public struct CampaignSettingElements
|
||||
{
|
||||
public SettingValue<bool> TutorialEnabled;
|
||||
public SettingValue<bool> RadiationEnabled;
|
||||
public SettingValue<int> MaxMissionCount;
|
||||
public SettingValue<StartingBalanceAmount> StartingFunds;
|
||||
@@ -114,6 +115,7 @@ namespace Barotrauma
|
||||
{
|
||||
return new CampaignSettings(element: null)
|
||||
{
|
||||
TutorialEnabled = TutorialEnabled.GetValue(),
|
||||
RadiationEnabled = RadiationEnabled.GetValue(),
|
||||
MaxMissionCount = MaxMissionCount.GetValue(),
|
||||
StartingBalanceAmount = StartingFunds.GetValue(),
|
||||
@@ -159,7 +161,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected static CampaignSettingElements CreateCampaignSettingList(GUIComponent parent, CampaignSettings prevSettings)
|
||||
protected static CampaignSettingElements CreateCampaignSettingList(GUIComponent parent, CampaignSettings prevSettings, bool isSinglePlayer)
|
||||
{
|
||||
const float verticalSize = 0.14f;
|
||||
|
||||
@@ -180,6 +182,9 @@ namespace Barotrauma
|
||||
Spacing = GUI.IntScale(5)
|
||||
};
|
||||
|
||||
SettingValue<bool> tutorialEnabled = isSinglePlayer ?
|
||||
CreateTickbox(settingsList.Content, TextManager.Get("CampaignOption.EnableTutorial"), TextManager.Get("campaignoption.enabletutorial.tooltip"), prevSettings.TutorialEnabled, verticalSize) :
|
||||
new SettingValue<bool>(() => false, b => { });
|
||||
SettingValue<bool> radiationEnabled = CreateTickbox(settingsList.Content, TextManager.Get("CampaignOption.EnableRadiation"), TextManager.Get("campaignoption.enableradiation.tooltip"), prevSettings.RadiationEnabled, verticalSize);
|
||||
|
||||
ImmutableArray<SettingCarouselElement<Identifier>> startingSetOptions = StartItemSet.Sets.OrderBy(s => s.Order).Select(set => new SettingCarouselElement<Identifier>(set.Identifier, $"startitemset.{set.Identifier}")).ToImmutableArray();
|
||||
@@ -214,6 +219,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (o is CampaignSettings settings)
|
||||
{
|
||||
tutorialEnabled.SetValue(isSinglePlayer && settings.TutorialEnabled);
|
||||
radiationEnabled.SetValue(settings.RadiationEnabled);
|
||||
maxMissionCountInput.SetValue(settings.MaxMissionCount);
|
||||
startingFundsInput.SetValue(settings.StartingBalanceAmount);
|
||||
@@ -226,6 +232,7 @@ namespace Barotrauma
|
||||
|
||||
return new CampaignSettingElements
|
||||
{
|
||||
TutorialEnabled = tutorialEnabled,
|
||||
RadiationEnabled = radiationEnabled,
|
||||
MaxMissionCount = maxMissionCountInput,
|
||||
StartingFunds = startingFundsInput,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user