Merge branch 'faction-test'

# Conflicts:
#	Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs
#	Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs
#	Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs
#	Barotrauma/BarotraumaClient/ClientSource/Characters/Limb.cs
#	Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/HUDLayoutSettings.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/TalentMenu.cs
#	Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs
#	Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs
#	Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs
#	Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemContainer.cs
#	Barotrauma/BarotraumaClient/ClientSource/Items/Components/LightComponent.cs
#	Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs
#	Barotrauma/BarotraumaClient/ClientSource/Items/ItemPrefab.cs
#	Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs
#	Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs
#	Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs
#	Barotrauma/BarotraumaClient/ClientSource/Particles/ParticleEmitter.cs
#	Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs
#	Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/CharacterEditorScreen.cs
#	Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs
#	Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs
#	Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs
#	Barotrauma/BarotraumaClient/ClientSource/Screens/TestScreen.cs
#	Barotrauma/BarotraumaClient/LinuxClient.csproj
#	Barotrauma/BarotraumaClient/MacClient.csproj
#	Barotrauma/BarotraumaClient/WindowsClient.csproj
#	Barotrauma/BarotraumaServer/LinuxServer.csproj
#	Barotrauma/BarotraumaServer/MacServer.csproj
#	Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs
#	Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs
#	Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs
#	Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs
#	Barotrauma/BarotraumaServer/WindowsServer.csproj
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFightIntruders.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionMission.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionDataless/AbilityConditionHasPermanentStat.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityUnlockApprenticeshipTalentTree.cs
#	Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/TalentTree.cs
#	Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs
#	Barotrauma/BarotraumaShared/SharedSource/Enums.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/TagAction.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs
#	Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs
#	Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Factions.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs
#	Barotrauma/BarotraumaShared/SharedSource/GameSession/MedicalClinic.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Holdable.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Throwable.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs
#	Barotrauma/BarotraumaShared/SharedSource/Items/RelatedItem.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Biome.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelData.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/PriceInfo.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs
#	Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs
#	Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs
#	Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs
#	Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs
#	Barotrauma/BarotraumaShared/SharedSource/SteamAchievementManager.cs
#	Barotrauma/BarotraumaShared/SharedSource/Upgrades/UpgradePrefab.cs
#	Barotrauma/BarotraumaShared/SharedSource/Utils/Option/Option.cs
#	Barotrauma/BarotraumaShared/SharedSource/Utils/SerializableDateTime.cs
#	Barotrauma/BarotraumaShared/changelog.txt
#	Barotrauma/BarotraumaTest/SerializableDateTimeTests.cs
This commit is contained in:
Regalis11
2023-03-13 18:26:50 +02:00
335 changed files with 11054 additions and 5179 deletions
@@ -36,8 +36,8 @@ namespace Barotrauma
private float minZoom = 0.1f; private float minZoom = 0.1f;
public float MinZoom public float MinZoom
{ {
get { return minZoom;} get { return minZoom; }
set { minZoom = MathHelper.Clamp(value, 0.001f, 10.0f); } set { minZoom = MathHelper.Clamp(value, 0.001f, 10.0f); }
} }
private float maxZoom = 2.0f; private float maxZoom = 2.0f;
@@ -63,7 +63,7 @@ namespace Barotrauma
private float prevZoom; private float prevZoom;
public float Shake; public float Shake;
private Vector2 shakePosition; public Vector2 ShakePosition { get; private set; }
private float shakeTimer; private float shakeTimer;
private float globalZoomScale = 1.0f; private float globalZoomScale = 1.0f;
@@ -371,7 +371,7 @@ namespace Barotrauma
if (Shake < 0.01f) if (Shake < 0.01f)
{ {
shakePosition = Vector2.Zero; ShakePosition = Vector2.Zero;
shakeTimer = 0.0f; shakeTimer = 0.0f;
} }
else else
@@ -379,11 +379,11 @@ namespace Barotrauma
shakeTimer += deltaTime * 5.0f; 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); 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); Shake = MathHelper.Lerp(Shake, 0.0f, deltaTime * 2.0f);
} }
Translate(moveCam + shakePosition); Translate(moveCam + ShakePosition);
Freeze = false; Freeze = false;
} }
@@ -6,6 +6,8 @@ namespace Barotrauma
{ {
class CameraTransition class CameraTransition
{ {
private static List<CameraTransition> activeTransitions = new List<CameraTransition>();
public bool Running public bool Running
{ {
get; get;
@@ -19,6 +21,7 @@ namespace Barotrauma
private readonly float? endZoom; private readonly float? endZoom;
public readonly float WaitDuration; public readonly float WaitDuration;
public float EndWaitDuration = 0.1f;
public readonly float PanDuration; public readonly float PanDuration;
public readonly bool FadeOut; public readonly bool FadeOut;
public readonly bool LosFadeIn; public readonly bool LosFadeIn;
@@ -29,6 +32,8 @@ namespace Barotrauma
public bool AllowInterrupt = false; public bool AllowInterrupt = false;
public bool RemoveControlFromCharacter = true; public bool RemoveControlFromCharacter = true;
public bool RunWhilePaused = true;
public CameraTransition(ISpatialEntity targetEntity, Camera cam, Alignment? cameraStartPos, Alignment? cameraEndPos, bool fadeOut = true, bool losFadeIn = false, float waitDuration = 0f, float panDuration = 10.0f, float? startZoom = null, float? endZoom = null) public CameraTransition(ISpatialEntity targetEntity, Camera cam, Alignment? cameraStartPos, Alignment? cameraEndPos, bool fadeOut = true, bool losFadeIn = false, float waitDuration = 0f, float panDuration = 10.0f, float? startZoom = null, float? endZoom = null)
{ {
@@ -45,8 +50,19 @@ namespace Barotrauma
if (targetEntity == null) { return; } if (targetEntity == null) { return; }
Running = true; 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"); updateCoroutine = CoroutineManager.StartCoroutine(Update(targetEntity, cam), "CameraTransition");
activeTransitions.Add(this);
} }
public void Stop() public void Stop()
@@ -62,11 +78,13 @@ namespace Barotrauma
#endif #endif
} }
private float DeltaTime => CoroutineManager.Paused && !RunWhilePaused ? 0 : CoroutineManager.DeltaTime;
private IEnumerable<CoroutineStatus> Update(ISpatialEntity targetEntity, Camera cam) private IEnumerable<CoroutineStatus> Update(ISpatialEntity targetEntity, Camera cam)
{ {
if (targetEntity == null || (targetEntity is Entity e && e.Removed)) { yield return CoroutineStatus.Success; } if (targetEntity == null || (targetEntity is Entity e && e.Removed)) { yield return CoroutineStatus.Success; }
prevControlled = Character.Controlled; prevControlled ??= Character.Controlled;
if (RemoveControlFromCharacter) if (RemoveControlFromCharacter)
{ {
#if CLIENT #if CLIENT
@@ -80,6 +98,7 @@ namespace Barotrauma
float endZoom = this.endZoom ?? 0.5f; float endZoom = this.endZoom ?? 0.5f;
Vector2 initialCameraPos = cam.Position; Vector2 initialCameraPos = cam.Position;
Vector2? initialTargetPos = targetEntity?.WorldPosition; Vector2? initialTargetPos = targetEntity?.WorldPosition;
Vector2 endPos = cam.Position;
float timer = -WaitDuration; float timer = -WaitDuration;
@@ -137,13 +156,13 @@ namespace Barotrauma
{ {
startPos += targetEntity.WorldPosition - initialTargetPos.Value; startPos += targetEntity.WorldPosition - initialTargetPos.Value;
} }
Vector2 endPos = cameraEndPos.HasValue ? endPos = cameraEndPos.HasValue ?
new Vector2( new Vector2(
MathHelper.Lerp(minPos.X, maxPos.X, (cameraEndPos.Value.ToVector2().X + 1.0f) / 2.0f), 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)) : MathHelper.Lerp(maxPos.Y, minPos.Y, (cameraEndPos.Value.ToVector2().Y + 1.0f) / 2.0f)) :
prevControlled?.WorldPosition ?? targetEntity.WorldPosition; 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); cam.Translate(cameraPos - cam.Position);
#if CLIENT #if CLIENT
@@ -162,14 +181,21 @@ namespace Barotrauma
Lights.LightManager.ViewTarget = prevControlled ?? (targetEntity as Entity); Lights.LightManager.ViewTarget = prevControlled ?? (targetEntity as Entity);
} }
#endif #endif
timer += CoroutineManager.DeltaTime; timer += DeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
Running = false; float endTimer = 0.0f;
while (endTimer <= EndWaitDuration)
{
cam.Translate(endPos - cam.Position);
cam.Zoom = endZoom;
endTimer += DeltaTime;
yield return CoroutineStatus.Running;
}
yield return new WaitForSeconds(0.1f); Running = false;
#if CLIENT #if CLIENT
GUI.ScreenOverlayColor = Color.TransparentBlack; GUI.ScreenOverlayColor = Color.TransparentBlack;
@@ -99,12 +99,14 @@ namespace Barotrauma
float newAngularVelocity = Collider.AngularVelocity; float newAngularVelocity = Collider.AngularVelocity;
Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity); Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);
newVelocity = newVelocity.ClampLength(100.0f); if (Collider.BodyType == BodyType.Dynamic)
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; } {
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero; newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
Collider.LinearVelocity = newVelocity; overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
Collider.AngularVelocity = newAngularVelocity; Collider.LinearVelocity = newVelocity;
Collider.AngularVelocity = newAngularVelocity;
}
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition); float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
float errorTolerance = character.CanMove && !character.IsRagdolled ? 0.01f : 0.2f; float errorTolerance = character.CanMove && !character.IsRagdolled ? 0.01f : 0.2f;
@@ -109,6 +109,26 @@ namespace Barotrauma
set => grainStrength = Math.Max(0, value); 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> /// <summary>
/// Can be used to set camera shake from status effects /// Can be used to set camera shake from status effects
/// </summary> /// </summary>
@@ -278,6 +298,21 @@ namespace Barotrauma
{ {
keys[i].SetState(); keys[i].SetState();
} }
if (CharacterInventory.IsMouseOnInventory && CharacterHUD.ShouldDrawInventory(this))
{
ResetInputIfPrimaryMouse(InputType.Use);
ResetInputIfPrimaryMouse(InputType.Shoot);
ResetInputIfPrimaryMouse(InputType.Select);
void ResetInputIfPrimaryMouse(InputType inputType)
{
if (GameSettings.CurrentConfig.KeyMap.Bindings[inputType].MouseButton == MouseButton.PrimaryMouse)
{
keys[(int)inputType].Reset();
}
}
}
//if we were firing (= pressing the aim and shoot keys at the same time) //if we were firing (= pressing the aim and shoot keys at the same time)
//and the fire key is the same as Select or Use, reset the key to prevent accidentally selecting/using items //and the fire key is the same as Select or Use, reset the key to prevent accidentally selecting/using items
if (wasFiring && !keys[(int)InputType.Shoot].Held) if (wasFiring && !keys[(int)InputType.Shoot].Held)
@@ -296,8 +331,7 @@ namespace Barotrauma
float targetOffsetAmount = 0.0f; float targetOffsetAmount = 0.0f;
if (moveCam) if (moveCam)
{ {
if (NeedsAir && !IsProtectedFromPressure() && if (!IsProtectedFromPressure && (AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 0.0f))
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 0.0f))
{ {
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure; float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
if (pressure > 0.0f) if (pressure > 0.0f)
@@ -911,7 +945,7 @@ namespace Barotrauma
{ {
name += " " + TextManager.Get("Disguised"); name += " " + TextManager.Get("Disguised");
} }
else if (Info.Title != null) else if (Info.Title != null && TeamID != CharacterTeamType.Team1)
{ {
name += '\n' + Info.Title; name += '\n' + Info.Title;
} }
@@ -987,13 +1021,13 @@ namespace Barotrauma
} }
} }
if (CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible) if (Params.ShowHealthBar && CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
{ {
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f)); hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y); Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f), 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, Color.Lerp(GUIStyle.Red, GUIStyle.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha); new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
} }
@@ -13,9 +13,8 @@ namespace Barotrauma
{ {
const float BossHealthBarDuration = 120.0f; const float BossHealthBarDuration = 120.0f;
class BossHealthBar abstract class BossProgressBar
{ {
public readonly Character Character;
public float FadeTimer; public float FadeTimer;
public readonly GUIComponent TopContainer; public readonly GUIComponent TopContainer;
@@ -24,9 +23,18 @@ namespace Barotrauma
public readonly GUIProgressBar TopHealthBar; public readonly GUIProgressBar TopHealthBar;
public readonly GUIProgressBar SideHealthBar; public readonly GUIProgressBar SideHealthBar;
public BossHealthBar(Character character) public abstract bool Completed { get; }
public abstract bool Interrupted { get; }
public abstract float State { get; }
public abstract string NumberToDisplay { get; }
public abstract Color Color { get; }
public BossProgressBar(LocalizedString label)
{ {
Character = character;
FadeTimer = BossHealthBarDuration; FadeTimer = BossHealthBarDuration;
TopContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.18f, 0.03f), HUDFrame.RectTransform, Anchor.TopCenter) TopContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.18f, 0.03f), HUDFrame.RectTransform, Anchor.TopCenter)
@@ -34,7 +42,7 @@ namespace Barotrauma
MinSize = new Point(100, 50), MinSize = new Point(100, 50),
RelativeOffset = new Vector2(0.0f, 0.01f) RelativeOffset = new Vector2(0.0f, 0.01f)
}, isHorizontal: false, childAnchor: Anchor.TopCenter); }, 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) TopHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.6f), TopContainer.RectTransform)
{ {
MinSize = new Point(100, HUDLayoutSettings.HealthBarArea.Size.Y) MinSize = new Point(100, HUDLayoutSettings.HealthBarArea.Size.Y)
@@ -42,22 +50,95 @@ namespace Barotrauma
{ {
Color = GUIStyle.Red Color = GUIStyle.Red
}; };
CreateNumberText(TopHealthBar);
SideContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), bossHealthContainer.RectTransform) SideContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), bossHealthContainer.RectTransform)
{ {
MinSize = new Point(80, 60) MinSize = new Point(80, 60)
}, isHorizontal: false, childAnchor: Anchor.TopRight); }, 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") SideHealthBar = new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.7f), SideContainer.RectTransform), barSize: 0.0f, style: "CharacterHealthBar")
{ {
Color = GUIStyle.Red Color = GUIStyle.Red
}; };
CreateNumberText(SideHealthBar);
TopContainer.Visible = SideContainer.Visible = false; TopContainer.Visible = SideContainer.Visible = false;
TopContainer.CanBeFocused = false; TopContainer.CanBeFocused = false;
TopContainer.Children.ForEach(c => c.CanBeFocused = false); TopContainer.Children.ForEach(c => c.CanBeFocused = false);
SideContainer.CanBeFocused = false; SideContainer.CanBeFocused = false;
SideContainer.Children.ForEach(c => c.CanBeFocused = false); SideContainer.Children.ForEach(c => c.CanBeFocused = false);
void CreateNumberText(GUIComponent parent)
{
new GUITextBlock(new RectTransform(Vector2.One, parent.RectTransform)
{ AbsoluteOffset = new Point(2) },
string.Empty, textAlignment: Alignment.Center, textColor: GUIStyle.TextColorDark)
{
TextGetter = () => NumberToDisplay
};
new GUITextBlock(new RectTransform(Vector2.One, parent.RectTransform),
string.Empty, textAlignment: Alignment.Center, textColor: GUIStyle.TextColorBright)
{
TextGetter = () => NumberToDisplay
};
}
}
public abstract bool IsDuplicate(object targetObject);
}
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 override Color Color =>
Character.CharacterHealth.GetAfflictionStrength(AfflictionPrefab.PoisonType) > 0 || Character.CharacterHealth.GetAfflictionStrength(AfflictionPrefab.ParalysisType) > 0 ?
GUIStyle.HealthBarColorPoisoned : GUIStyle.Red;
public override string NumberToDisplay => string.Empty;
public BossHealthBar(Character character) : base(character.DisplayName)
{
Character = character;
}
public override bool IsDuplicate(object targetObject)
{
return targetObject is Character character && 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 || GameMain.GameSession?.Missions == null || !GameMain.GameSession.Missions.Contains(Mission);
public override Color Color => GUIStyle.Red;
public override string NumberToDisplay => Mission.Prefab.ShowProgressInNumbers ?
$"{Mission.State}/{Mission.Prefab.MaxProgressState}" :
string.Empty;
public MissionProgressBar(Mission mission) : base(mission.Prefab.ProgressBarLabel)
{
Mission = mission;
}
public override bool IsDuplicate(object targetObject)
{
return targetObject is Mission mission && Mission == mission;
} }
} }
@@ -69,7 +150,7 @@ namespace Barotrauma
private static readonly List<Item> brokenItems = new List<Item>(); private static readonly List<Item> brokenItems = new List<Item>();
private static float brokenItemsCheckTimer; private static float brokenItemsCheckTimer;
private static readonly List<BossHealthBar> bossHealthBars = new List<BossHealthBar>(); private static readonly List<BossProgressBar> bossProgressBars = new List<BossProgressBar>();
private static readonly Dictionary<Identifier, LocalizedString> cachedHudTexts = new Dictionary<Identifier, LocalizedString>(); private static readonly Dictionary<Identifier, LocalizedString> cachedHudTexts = new Dictionary<Identifier, LocalizedString>();
@@ -107,7 +188,7 @@ namespace Barotrauma
GameMain.GameSession?.Campaign != null && GameMain.GameSession?.Campaign != null &&
(GameMain.GameSession.Campaign.ShowCampaignUI || GameMain.GameSession.Campaign.ForceMapUI); (GameMain.GameSession.Campaign.ShowCampaignUI || GameMain.GameSession.Campaign.ForceMapUI);
private static bool ShouldDrawInventory(Character character) public static bool ShouldDrawInventory(Character character)
{ {
var controller = character.SelectedItem?.GetComponent<Controller>(); var controller = character.SelectedItem?.GetComponent<Controller>();
@@ -159,7 +240,7 @@ namespace Barotrauma
public static void Update(float deltaTime, Character character, Camera cam) public static void Update(float deltaTime, Character character, Camera cam)
{ {
UpdateBossHealthBars(deltaTime); UpdateBossProgressBars(deltaTime);
if (GUI.DisableHUD) if (GUI.DisableHUD)
{ {
@@ -623,7 +704,9 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No); GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No);
textPos.Y += GUIStyle.SubHeadingFont.MeasureString(focusName).Y; 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); 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; textPos.Y += GUIStyle.SubHeadingFont.MeasureString(character.FocusedCharacter.Info.Title.Value).Y;
@@ -661,27 +744,47 @@ namespace Barotrauma
} }
} }
public static void ShowBossHealthBar(Character character) public static void ShowBossHealthBar(Character character, float damage)
{ {
if (character == null || character.IsDead || character.Removed) { return; } if (character == null || character.IsDead || character.Removed) { return; }
if (bossProgressBars.Any(b => b.IsDuplicate(character))) { return; }
AddBossProgressBar(new BossHealthBar(character));
}
public static void ShowMissionProgressBar(Mission mission)
{
if (mission == null || mission.Completed || mission.Failed) { return; }
if (bossProgressBars.Any(b => b.IsDuplicate(mission))) { return; }
AddBossProgressBar(new MissionProgressBar(mission));
}
public static void ClearBossProgressBars()
{
for (int i = bossProgressBars.Count - 1; i>= 0; i--)
{
RemoveBossProgressBar(bossProgressBars[i]);
}
bossProgressBars.Clear();
}
private static void RemoveBossProgressBar(BossProgressBar progressBar)
{
progressBar.SideContainer.Parent?.RemoveChild(progressBar.SideContainer);
progressBar.TopContainer.Parent?.RemoveChild(progressBar.TopContainer);
bossProgressBars.Remove(progressBar);
}
private static void AddBossProgressBar(BossProgressBar progressBar)
{
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars; var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
if (healthBarMode == EnemyHealthBarMode.HideAll) if (healthBarMode == EnemyHealthBarMode.HideAll)
{ {
return; return;
} }
if (bossProgressBars.Count > 5)
var existingBar = bossHealthBars.Find(b => b.Character == character);
if (existingBar != null)
{ {
existingBar.FadeTimer = BossHealthBarDuration; BossProgressBar oldestHealthBar = bossProgressBars.First();
return; foreach (var bar in bossProgressBars)
}
if (bossHealthBars.Count > 5)
{
BossHealthBar oldestHealthBar = bossHealthBars.First();
foreach (var bar in bossHealthBars)
{ {
if (bar.TopHealthBar.BarSize < oldestHealthBar.TopHealthBar.BarSize) if (bar.TopHealthBar.BarSize < oldestHealthBar.TopHealthBar.BarSize)
{ {
@@ -690,62 +793,69 @@ namespace Barotrauma
} }
oldestHealthBar.FadeTimer = Math.Min(oldestHealthBar.FadeTimer, 1.0f); oldestHealthBar.FadeTimer = Math.Min(oldestHealthBar.FadeTimer, 1.0f);
} }
bossProgressBars.Add(progressBar);
bossHealthBars.Add(new BossHealthBar(character));
} }
public static void UpdateBossHealthBars(float deltaTime) public static void UpdateBossProgressBars(float deltaTime)
{ {
var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars; var healthBarMode = GameMain.NetworkMember?.ServerSettings.ShowEnemyHealthBars ?? GameSettings.CurrentConfig.ShowEnemyHealthBars;
for (int i = 0; i < bossHealthBars.Count; i++) for (int i = 0; i < bossProgressBars.Count; i++)
{ {
var bossHealthBar = bossHealthBars[i]; var bossHealthBar = bossProgressBars[i];
bool showTopBar = i == 0; bool showTopBar = i == bossProgressBars.Count - 1;
if (showTopBar != bossHealthBar.TopContainer.Visible) if (showTopBar && !bossHealthBar.TopContainer.Visible)
{ {
bossHealthContainer.Recalculate(); bossHealthBar.SideContainer.SetAsLastChild();
SetColor(bossHealthBar, bossHealthBar.SideContainer, 0);
} }
bossHealthBar.TopContainer.Visible = showTopBar; bossHealthBar.TopContainer.Visible = showTopBar;
bossHealthBar.SideContainer.Visible = !bossHealthBar.TopContainer.Visible; 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); 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)); SetColor(bossHealthBar, bossHealthBar.TopContainer, alpha);
if (c is GUITextBlock textBlock) }
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(bossHealthBar.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.Interrupted)
Color color = bossHealthBar.Character.CharacterHealth.GetAfflictionStrength("poison") > 0 || bossHealthBar.Character.CharacterHealth.GetAfflictionStrength("paralysis") > 0 ? GUIStyle.HealthBarColorPoisoned : GUIStyle.Red;
bossHealthBar.TopHealthBar.Color = bossHealthBar.SideHealthBar.Color = color;
if (bossHealthBar.Character.Removed || !bossHealthBar.Character.Enabled)
{ {
bossHealthBar.FadeTimer = Math.Min(bossHealthBar.FadeTimer, 1.0f); 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 = Math.Min(bossHealthBar.FadeTimer, 5.0f);
} }
bossHealthBar.FadeTimer -= deltaTime; bossHealthBar.FadeTimer -= deltaTime;
} }
for (int i = bossProgressBars.Count - 1; i >= 0 ; i--)
for (int i = bossHealthBars.Count - 1; i >= 0 ; i--)
{ {
var bossHealthBar = bossHealthBars[i]; var bossHealthBar = bossProgressBars[i];
if (bossHealthBar.FadeTimer <= 0 || healthBarMode == EnemyHealthBarMode.HideAll) if (bossHealthBar.FadeTimer <= 0 || healthBarMode == EnemyHealthBarMode.HideAll)
{ {
bossHealthBar.SideContainer.Parent?.RemoveChild(bossHealthBar.SideContainer); bossHealthBar.SideContainer.Parent?.RemoveChild(bossHealthBar.SideContainer);
bossHealthBar.TopContainer.Parent?.RemoveChild(bossHealthBar.TopContainer); bossHealthBar.TopContainer.Parent?.RemoveChild(bossHealthBar.TopContainer);
bossHealthBars.RemoveAt(i); bossProgressBars.RemoveAt(i);
bossHealthContainer.Recalculate(); bossHealthContainer.Recalculate();
} }
} }
@@ -540,37 +540,45 @@ namespace Barotrauma
string ragdollFile = inc.ReadString(); string ragdollFile = inc.ReadString();
Identifier npcId = inc.ReadIdentifier(); Identifier npcId = inc.ReadIdentifier();
Identifier factionId = inc.ReadIdentifier();
float minReputationToHire = 0.0f;
if (factionId != default)
{
minReputationToHire = inc.ReadSingle();
}
uint jobIdentifier = inc.ReadUInt32(); uint jobIdentifier = inc.ReadUInt32();
int variant = inc.ReadByte(); int variant = inc.ReadByte();
JobPrefab jobPrefab = null; JobPrefab jobPrefab = null;
Dictionary<Identifier, float> skillLevels = new Dictionary<Identifier, float>(); Dictionary<Identifier, float> skillLevels = new Dictionary<Identifier, float>();
if (jobIdentifier > 0) if (jobIdentifier > 0)
{ {
jobPrefab = JobPrefab.Prefabs.Find(jp => jp.UintIdentifier == jobIdentifier); jobPrefab = JobPrefab.Prefabs.Find(jp => jp.UintIdentifier == jobIdentifier);
if (jobPrefab == null)
{
throw new Exception($"Error while reading {nameof(CharacterInfo)} received from the server: could not find a job prefab with the identifier \"{jobIdentifier}\".");
}
foreach (SkillPrefab skillPrefab in jobPrefab.Skills.OrderBy(s => s.Identifier)) foreach (SkillPrefab skillPrefab in jobPrefab.Skills.OrderBy(s => s.Identifier))
{ {
float skillLevel = inc.ReadSingle(); float skillLevel = inc.ReadSingle();
skillLevels.Add(skillPrefab.Identifier, skillLevel); skillLevels.Add(skillPrefab.Identifier, skillLevel);
} }
} }
// TODO: animations
CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant, npcIdentifier: npcId) 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.RecreateHead(tagSet.ToImmutableHashSet(), hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
ch.Head.SkinColor = skinColor; ch.Head.SkinColor = skinColor;
ch.Head.HairColor = hairColor; ch.Head.HairColor = hairColor;
ch.Head.FacialHairColor = facialHairColor; ch.Head.FacialHairColor = facialHairColor;
ch.SetPersonalityTrait(); ch.SetPersonalityTrait();
if (ch.Job != null) ch.Job?.OverrideSkills(skillLevels);
{
ch.Job.OverrideSkills(skillLevels);
}
ch.ExperiencePoints = inc.ReadUInt16(); ch.ExperiencePoints = inc.ReadInt32();
ch.AdditionalTalentPoints = inc.ReadRangedInteger(0, MaxAdditionalTalentPoints); ch.AdditionalTalentPoints = inc.ReadRangedInteger(0, MaxAdditionalTalentPoints);
return ch; return ch;
} }
@@ -593,6 +593,7 @@ namespace Barotrauma
{ {
character.MerchantIdentifier = inc.ReadIdentifier(); character.MerchantIdentifier = inc.ReadIdentifier();
} }
character.Faction = inc.ReadIdentifier();
character.HumanPrefabHealthMultiplier = humanPrefabHealthMultiplier; character.HumanPrefabHealthMultiplier = humanPrefabHealthMultiplier;
character.Wallet.Balance = balance; character.Wallet.Balance = balance;
character.Wallet.RewardDistribution = rewardDistribution; character.Wallet.RewardDistribution = rewardDistribution;
@@ -87,6 +87,8 @@ namespace Barotrauma
/// Container for the icons above the health bar /// Container for the icons above the health bar
/// </summary> /// </summary>
private GUIComponent afflictionIconContainer; private GUIComponent afflictionIconContainer;
private float afflictionIconRefreshTimer;
const float AfflictionIconRefreshInterval = 1.0f;
private GUIButton showHiddenAfflictionsButton; private GUIButton showHiddenAfflictionsButton;
@@ -861,7 +863,7 @@ namespace Barotrauma
{ {
treatmentButton.ToolTip = treatmentButton.ToolTip =
RichString.Rich( RichString.Rich(
$"‖color:gui.green‖[{TextManager.Get(PlayerInput.MouseButtonsSwapped() ? "input.rightmouse" : "input.leftmouse")}] " $"‖color:gui.green‖[{PlayerInput.PrimaryMouseLabel}] "
+ $"{TextManager.Get("quickuseaction.usetreatment")}‖color:end‖" + '\n' + $"{TextManager.Get("quickuseaction.usetreatment")}‖color:end‖" + '\n'
+ treatmentButton.ToolTip.NestedStr); + treatmentButton.ToolTip.NestedStr);
} }
@@ -1157,15 +1159,20 @@ namespace Barotrauma
} }
} }
afflictionIconContainer.RectTransform.SortChildren((r1, r2) => afflictionIconRefreshTimer -= deltaTime;
if (afflictionIconRefreshTimer <= 0.0f)
{ {
if (r1.GUIComponent.UserData is not AfflictionPrefab prefab1) { return -1; } afflictionIconContainer.RectTransform.SortChildren((r1, r2) =>
if (r2.GUIComponent.UserData is not AfflictionPrefab prefab2) { return 1; } {
var index1 = statusIcons.IndexOf(s => s.Prefab == prefab1); if (r1.GUIComponent.UserData is not AfflictionPrefab prefab1) { return -1; }
var index2 = statusIcons.IndexOf(s => s.Prefab == prefab2); if (r2.GUIComponent.UserData is not AfflictionPrefab prefab2) { return 1; }
return index1.CompareTo(index2); var index1 = statusIcons.IndexOf(s => s.Prefab == prefab1);
}); var index2 = statusIcons.IndexOf(s => s.Prefab == prefab2);
(afflictionIconContainer as GUILayoutGroup).NeedsToRecalculate = true; return index1.CompareTo(index2);
});
(afflictionIconContainer as GUILayoutGroup).NeedsToRecalculate = true;
afflictionIconRefreshTimer = AfflictionIconRefreshInterval;
}
Rectangle hiddenAfflictionHoverArea = showHiddenAfflictionsButton.Rect; Rectangle hiddenAfflictionHoverArea = showHiddenAfflictionsButton.Rect;
foreach (GUIComponent child in hiddenAfflictionIconContainer.Children) foreach (GUIComponent child in hiddenAfflictionIconContainer.Children)
@@ -1983,6 +1990,7 @@ namespace Barotrauma
{ {
newAfflictions.Clear(); newAfflictions.Clear();
newPeriodicEffects.Clear(); newPeriodicEffects.Clear();
bool newAdded = false;
byte afflictionCount = inc.ReadByte(); byte afflictionCount = inc.ReadByte();
for (int i = 0; i < afflictionCount; i++) for (int i = 0; i < afflictionCount; i++)
{ {
@@ -2062,6 +2070,7 @@ namespace Barotrauma
{ {
existingAffliction = afflictionPrefab.Instantiate(strength); existingAffliction = afflictionPrefab.Instantiate(strength);
afflictions.Add(existingAffliction, limb); afflictions.Add(existingAffliction, limb);
newAdded = true;
} }
existingAffliction.SetStrength(strength); existingAffliction.SetStrength(strength);
if (existingAffliction == stunAffliction) if (existingAffliction == stunAffliction)
@@ -2088,6 +2097,11 @@ namespace Barotrauma
CalculateVitality(); CalculateVitality();
DisplayedVitality = Vitality; DisplayedVitality = Vitality;
if (newAdded)
{
MedicalClinic.OnAfflictionCountChanged(Character);
}
} }
partial void UpdateSkinTint() partial void UpdateSkinTint()
@@ -554,7 +554,7 @@ namespace Barotrauma
float damage = 0; float damage = 0;
foreach (var affliction in result.Afflictions) foreach (var affliction in result.Afflictions)
{ {
if (affliction.Prefab.DamageParticles && affliction.Prefab.AfflictionType == "damage") if (affliction.Prefab.DamageParticles && affliction.Prefab.AfflictionType == AfflictionPrefab.DamageType)
{ {
damage += affliction.GetVitalityDecrease(null); damage += affliction.GetVitalityDecrease(null);
} }
@@ -563,11 +563,11 @@ namespace Barotrauma
float bleedingDamageMultiplier = 1; float bleedingDamageMultiplier = 1;
foreach (DamageModifier damageModifier in result.AppliedDamageModifiers) foreach (DamageModifier damageModifier in result.AppliedDamageModifiers)
{ {
if (damageModifier.MatchesAfflictionType("damage")) if (damageModifier.MatchesAfflictionType(AfflictionPrefab.DamageType))
{ {
damageMultiplier *= damageModifier.DamageMultiplier; damageMultiplier *= damageModifier.DamageMultiplier;
} }
else if (damageModifier.MatchesAfflictionType("bleeding")) else if (damageModifier.MatchesAfflictionType(AfflictionPrefab.BleedingType))
{ {
bleedingDamageMultiplier *= damageModifier.DamageMultiplier; bleedingDamageMultiplier *= damageModifier.DamageMultiplier;
} }
@@ -599,7 +599,7 @@ namespace Barotrauma
{ {
if (damageModifier.DamageMultiplier > 0 && !string.IsNullOrWhiteSpace(damageModifier.DamageParticle)) if (damageModifier.DamageMultiplier > 0 && !string.IsNullOrWhiteSpace(damageModifier.DamageParticle))
{ {
overrideParticle = GameMain.ParticleManager?.FindPrefab(damageModifier.DamageParticle); overrideParticle = ParticleManager.FindPrefab(damageModifier.DamageParticle);
break; break;
} }
} }
@@ -646,7 +646,7 @@ namespace Barotrauma
dripParticleTimer += wetTimer * deltaTime * Mass * (wetTimer > 0.9f ? 50.0f : 5.0f); dripParticleTimer += wetTimer * deltaTime * Mass * (wetTimer > 0.9f ? 50.0f : 5.0f);
if (dripParticleTimer > 1.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( GameMain.ParticleManager.CreateParticle(
"waterdrop", "waterdrop",
WorldPosition + Rand.Vector(Rand.Range(0.0f, ConvertUnits.ToDisplayUnits(dropRadius))), WorldPosition + Rand.Vector(Rand.Range(0.0f, ConvertUnits.ToDisplayUnits(dropRadius))),
@@ -683,10 +683,10 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null, bool disableDeformations = false) 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(); var spriteParams = Params.GetSprite();
if (spriteParams == null) { return; } if (spriteParams == null) { return; }
float burn = spriteParams.IgnoreTint ? 0 : burnOverLayStrength;
float brightness = Math.Max(1.0f - burn, 0.2f);
Color clr = spriteParams.Color; Color clr = spriteParams.Color;
if (!spriteParams.IgnoreTint) if (!spriteParams.IgnoreTint)
{ {
@@ -727,7 +727,7 @@ namespace Barotrauma
} }
} }
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes"); float herpesStrength = character.CharacterHealth.GetAfflictionStrength(AfflictionPrefab.SpaceHerpesType);
bool hideLimb = Hide || bool hideLimb = Hide ||
OtherWearables.Any(w => w.HideLimb) || OtherWearables.Any(w => w.HideLimb) ||
@@ -639,7 +639,7 @@ namespace Barotrauma
{ {
if (Submarine.MainSub == null) { return; } if (Submarine.MainSub == null) { return; }
MapEntity.SelectedList.Clear(); MapEntity.SelectedList.Clear();
MapEntity.mapEntityList.ForEach(me => me.IsHighlighted = false); MapEntity.ClearHighlightedEntities();
WikiImage.Create(Submarine.MainSub); WikiImage.Create(Submarine.MainSub);
})); }));
@@ -752,7 +752,7 @@ namespace Barotrauma
state = !GameMain.LightManager.LosEnabled; state = !GameMain.LightManager.LosEnabled;
} }
GameMain.LightManager.LosEnabled = state; GameMain.LightManager.LosEnabled = state;
NewMessage("Line of sight effect " + (GameMain.LightManager.LosEnabled ? "enabled" : "disabled"), Color.White); NewMessage("Line of sight effect " + (GameMain.LightManager.LosEnabled ? "enabled" : "disabled"), Color.Yellow);
}); });
AssignRelayToServer("los", false); AssignRelayToServer("los", false);
@@ -763,7 +763,7 @@ namespace Barotrauma
state = !GameMain.LightManager.LightingEnabled; state = !GameMain.LightManager.LightingEnabled;
} }
GameMain.LightManager.LightingEnabled = state; GameMain.LightManager.LightingEnabled = state;
NewMessage("Lighting " + (GameMain.LightManager.LightingEnabled ? "enabled" : "disabled"), Color.White); NewMessage("Lighting " + (GameMain.LightManager.LightingEnabled ? "enabled" : "disabled"), Color.Yellow);
}); });
AssignRelayToServer("lighting|lights", false); AssignRelayToServer("lighting|lights", false);
@@ -781,7 +781,7 @@ namespace Barotrauma
hull.OriginalAmbientLight = null; hull.OriginalAmbientLight = null;
} }
} }
NewMessage("Restored all hull ambient lights", Color.White); NewMessage("Restored all hull ambient lights", Color.Yellow);
return; return;
} }
@@ -803,11 +803,11 @@ namespace Barotrauma
if (add) if (add)
{ {
NewMessage($"Set ambient light color to {color}.", Color.White); NewMessage($"Set ambient light color to {color}.", Color.Yellow);
} }
else else
{ {
NewMessage($"Increased ambient light by {color}.", Color.White); NewMessage($"Increased ambient light by {color}.", Color.Yellow);
} }
}); });
AssignRelayToServer("ambientlight", false); AssignRelayToServer("ambientlight", false);
@@ -1124,7 +1124,7 @@ namespace Barotrauma
state = !GameMain.DebugDraw; state = !GameMain.DebugDraw;
} }
GameMain.DebugDraw = state; GameMain.DebugDraw = state;
NewMessage("Debug draw mode " + (GameMain.DebugDraw ? "enabled" : "disabled"), Color.White); NewMessage("Debug draw mode " + (GameMain.DebugDraw ? "enabled" : "disabled"), Color.Yellow);
}); });
AssignRelayToServer("debugdraw", false); AssignRelayToServer("debugdraw", false);
@@ -1157,7 +1157,7 @@ namespace Barotrauma
state = !TextManager.DebugDraw; state = !TextManager.DebugDraw;
} }
TextManager.DebugDraw = state; TextManager.DebugDraw = state;
NewMessage("Localization debug draw mode " + (TextManager.DebugDraw ? "enabled" : "disabled"), Color.White); NewMessage("Localization debug draw mode " + (TextManager.DebugDraw ? "enabled" : "disabled"), Color.Yellow);
}); });
AssignRelayToServer("debugdraw", false); AssignRelayToServer("debugdraw", false);
@@ -1170,19 +1170,19 @@ namespace Barotrauma
var config = GameSettings.CurrentConfig; var config = GameSettings.CurrentConfig;
config.Audio.DisableVoiceChatFilters = state; config.Audio.DisableVoiceChatFilters = state;
GameSettings.SetCurrentConfig(config); GameSettings.SetCurrentConfig(config);
NewMessage("Voice chat filters " + (GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters ? "disabled" : "enabled"), Color.White); NewMessage("Voice chat filters " + (GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters ? "disabled" : "enabled"), Color.Yellow);
}); });
AssignRelayToServer("togglevoicechatfilters", false); AssignRelayToServer("togglevoicechatfilters", false);
commands.Add(new Command("fpscounter", "fpscounter: Toggle the FPS counter.", (string[] args) => commands.Add(new Command("fpscounter", "fpscounter: Toggle the FPS counter.", (string[] args) =>
{ {
GameMain.ShowFPS = !GameMain.ShowFPS; GameMain.ShowFPS = !GameMain.ShowFPS;
NewMessage("FPS counter " + (GameMain.DebugDraw ? "enabled" : "disabled"), Color.White); NewMessage("FPS counter " + (GameMain.DebugDraw ? "enabled" : "disabled"), Color.Yellow);
})); }));
commands.Add(new Command("showperf", "showperf: Toggle performance statistics on/off.", (string[] args) => commands.Add(new Command("showperf", "showperf: Toggle performance statistics on/off.", (string[] args) =>
{ {
GameMain.ShowPerf = !GameMain.ShowPerf; GameMain.ShowPerf = !GameMain.ShowPerf;
NewMessage("Performance statistics " + (GameMain.ShowPerf ? "enabled" : "disabled"), Color.White); NewMessage("Performance statistics " + (GameMain.ShowPerf ? "enabled" : "disabled"), Color.Yellow);
})); }));
AssignOnClientExecute("netstats", (string[] args) => AssignOnClientExecute("netstats", (string[] args) =>
@@ -1194,55 +1194,55 @@ namespace Barotrauma
commands.Add(new Command("hudlayoutdebugdraw|debugdrawhudlayout", "hudlayoutdebugdraw: Toggle the debug drawing mode of HUD layout areas on/off.", (string[] args) => commands.Add(new Command("hudlayoutdebugdraw|debugdrawhudlayout", "hudlayoutdebugdraw: Toggle the debug drawing mode of HUD layout areas on/off.", (string[] args) =>
{ {
HUDLayoutSettings.DebugDraw = !HUDLayoutSettings.DebugDraw; HUDLayoutSettings.DebugDraw = !HUDLayoutSettings.DebugDraw;
NewMessage("HUD layout debug draw mode " + (HUDLayoutSettings.DebugDraw ? "enabled" : "disabled"), Color.White); NewMessage("HUD layout debug draw mode " + (HUDLayoutSettings.DebugDraw ? "enabled" : "disabled"), Color.Yellow);
})); }));
commands.Add(new Command("interactdebugdraw|debugdrawinteract", "interactdebugdraw: Toggle the debug drawing mode of item interaction ranges on/off.", (string[] args) => commands.Add(new Command("interactdebugdraw|debugdrawinteract", "interactdebugdraw: Toggle the debug drawing mode of item interaction ranges on/off.", (string[] args) =>
{ {
Character.DebugDrawInteract = !Character.DebugDrawInteract; Character.DebugDrawInteract = !Character.DebugDrawInteract;
NewMessage("Interact debug draw mode " + (Character.DebugDrawInteract ? "enabled" : "disabled"), Color.White); NewMessage("Interact debug draw mode " + (Character.DebugDrawInteract ? "enabled" : "disabled"), Color.Yellow);
}, isCheat: true)); }, isCheat: true));
AssignOnExecute("togglehud|hud", (string[] args) => AssignOnExecute("togglehud|hud", (string[] args) =>
{ {
GUI.DisableHUD = !GUI.DisableHUD; GUI.DisableHUD = !GUI.DisableHUD;
GameMain.Instance.IsMouseVisible = !GameMain.Instance.IsMouseVisible; GameMain.Instance.IsMouseVisible = !GameMain.Instance.IsMouseVisible;
NewMessage(GUI.DisableHUD ? "Disabled HUD" : "Enabled HUD", Color.White); NewMessage(GUI.DisableHUD ? "Disabled HUD" : "Enabled HUD", Color.Yellow);
}); });
AssignRelayToServer("togglehud|hud", false); AssignRelayToServer("togglehud|hud", false);
AssignOnExecute("toggleupperhud", (string[] args) => AssignOnExecute("toggleupperhud", (string[] args) =>
{ {
GUI.DisableUpperHUD = !GUI.DisableUpperHUD; GUI.DisableUpperHUD = !GUI.DisableUpperHUD;
NewMessage(GUI.DisableUpperHUD ? "Disabled upper HUD" : "Enabled upper HUD", Color.White); NewMessage(GUI.DisableUpperHUD ? "Disabled upper HUD" : "Enabled upper HUD", Color.Yellow);
}); });
AssignRelayToServer("toggleupperhud", false); AssignRelayToServer("toggleupperhud", false);
AssignOnExecute("toggleitemhighlights", (string[] args) => AssignOnExecute("toggleitemhighlights", (string[] args) =>
{ {
GUI.DisableItemHighlights = !GUI.DisableItemHighlights; GUI.DisableItemHighlights = !GUI.DisableItemHighlights;
NewMessage(GUI.DisableItemHighlights ? "Disabled item highlights" : "Enabled item highlights", Color.White); NewMessage(GUI.DisableItemHighlights ? "Disabled item highlights" : "Enabled item highlights", Color.Yellow);
}); });
AssignRelayToServer("toggleitemhighlights", false); AssignRelayToServer("toggleitemhighlights", false);
AssignOnExecute("togglecharacternames", (string[] args) => AssignOnExecute("togglecharacternames", (string[] args) =>
{ {
GUI.DisableCharacterNames = !GUI.DisableCharacterNames; GUI.DisableCharacterNames = !GUI.DisableCharacterNames;
NewMessage(GUI.DisableCharacterNames ? "Disabled character names" : "Enabled character names", Color.White); NewMessage(GUI.DisableCharacterNames ? "Disabled character names" : "Enabled character names", Color.Yellow);
}); });
AssignRelayToServer("togglecharacternames", false); AssignRelayToServer("togglecharacternames", false);
AssignOnExecute("followsub", (string[] args) => AssignOnExecute("followsub", (string[] args) =>
{ {
Camera.FollowSub = !Camera.FollowSub; Camera.FollowSub = !Camera.FollowSub;
NewMessage(Camera.FollowSub ? "Set the camera to follow the closest submarine" : "Disabled submarine following.", Color.White); NewMessage(Camera.FollowSub ? "Set the camera to follow the closest submarine" : "Disabled submarine following.", Color.Yellow);
}); });
AssignRelayToServer("followsub", false); AssignRelayToServer("followsub", false);
AssignOnExecute("toggleaitargets|aitargets", (string[] args) => AssignOnExecute("toggleaitargets|aitargets", (string[] args) =>
{ {
AITarget.ShowAITargets = !AITarget.ShowAITargets; AITarget.ShowAITargets = !AITarget.ShowAITargets;
NewMessage(AITarget.ShowAITargets ? "Enabled AI target drawing" : "Disabled AI target drawing", Color.White); NewMessage(AITarget.ShowAITargets ? "Enabled AI target drawing" : "Disabled AI target drawing", Color.Yellow);
}); });
AssignRelayToServer("toggleaitargets|aitargets", false); AssignRelayToServer("toggleaitargets|aitargets", false);
@@ -1264,10 +1264,36 @@ namespace Barotrauma
GameMain.LightManager.LosEnabled = true; GameMain.LightManager.LosEnabled = true;
GameMain.LightManager.LosAlpha = 1f; GameMain.LightManager.LosAlpha = 1f;
} }
NewMessage(HumanAIController.debugai ? "AI debug info visible" : "AI debug info hidden", Color.White); NewMessage(HumanAIController.debugai ? "AI debug info visible" : "AI debug info hidden", Color.Yellow);
}); });
AssignRelayToServer("debugai", false); AssignRelayToServer("debugai", false);
AssignOnExecute("showmonsters", (string[] args) =>
{
CreatureMetrics.UnlockAll = true;
CreatureMetrics.Save();
NewMessage("All monsters are now visible in the character editor.", Color.Yellow);
if (Screen.Selected == GameMain.CharacterEditorScreen)
{
GameMain.CharacterEditorScreen.Deselect();
GameMain.CharacterEditorScreen.Select();
}
});
AssignRelayToServer("showmonsters", false);
AssignOnExecute("hidemonsters", (string[] args) =>
{
CreatureMetrics.UnlockAll = false;
CreatureMetrics.Save();
NewMessage("All monsters that haven't yet been encountered in the game are now hidden in the character editor.", Color.Yellow);
if (Screen.Selected == GameMain.CharacterEditorScreen)
{
GameMain.CharacterEditorScreen.Deselect();
GameMain.CharacterEditorScreen.Select();
}
});
AssignRelayToServer("hidemonsters", false);
AssignRelayToServer("water|editwater", false); AssignRelayToServer("water|editwater", false);
AssignRelayToServer("fire|editfire", false); AssignRelayToServer("fire|editfire", false);
@@ -2991,7 +3017,7 @@ namespace Barotrauma
ThrowError($"Could not find the location type \"{args[0]}\"."); ThrowError($"Could not find the location type \"{args[0]}\".");
return; return;
} }
GameMain.GameSession.Campaign.Map.CurrentLocation.ChangeType(locationType); GameMain.GameSession.Campaign.Map.CurrentLocation.ChangeType(GameMain.GameSession.Campaign, locationType);
}, },
() => () =>
{ {
@@ -83,6 +83,7 @@ namespace Barotrauma
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox; GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null); Debug.Assert(conversationList != null);
DisableButtons(conversationList.Content.GetAllChildren<GUIButton>(), selectedButton: null);
// gray out the last text block // gray out the last text block
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement) if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
{ {
@@ -269,14 +270,7 @@ namespace Barotrauma
if (actionInstance != null) if (actionInstance != null)
{ {
actionInstance.selectedOption = selectedOption; actionInstance.selectedOption = selectedOption;
foreach (GUIButton otherButton in optionButtons) DisableButtons(optionButtons, btn);
{
otherButton.CanBeFocused = false;
if (otherButton != btn)
{
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
btn.ExternalHighlight = true; btn.ExternalHighlight = true;
return true; return true;
} }
@@ -286,14 +280,7 @@ namespace Barotrauma
SendResponse(actionId.Value, selectedOption); SendResponse(actionId.Value, selectedOption);
btn.CanBeFocused = false; btn.CanBeFocused = false;
btn.ExternalHighlight = true; btn.ExternalHighlight = true;
foreach (GUIButton otherButton in optionButtons) DisableButtons(optionButtons, btn);
{
otherButton.CanBeFocused = false;
if (otherButton != btn)
{
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
return true; return true;
} }
//should not happen //should not happen
@@ -305,6 +292,18 @@ namespace Barotrauma
} }
} }
public static void SelectOption(ushort actionId, int option)
{
if (lastMessageBox.UserData is Pair<string, ushort> userData)
{
if (userData.Second != actionId) { return; }
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null);
DisableButtons(conversationList.Content.GetAllChildren<GUIButton>(), (btn) => btn.UserData is int i && i == option);
}
}
private static Tuple<Vector2, Point> GetSizes(DialogTypes dialogTypes) private static Tuple<Vector2, Point> GetSizes(DialogTypes dialogTypes)
{ {
return dialogTypes switch return dialogTypes switch
@@ -383,6 +382,30 @@ namespace Barotrauma
return buttons; return buttons;
} }
private static void DisableButtons(IEnumerable<GUIButton> buttons, GUIButton selectedButton)
{
DisableButtons(buttons, (btn) => btn == selectedButton);
}
private static void DisableButtons(IEnumerable<GUIButton> buttons, Func<GUIButton, bool> isSelectedButton)
{
foreach (GUIButton btn in buttons)
{
if (btn.CanBeFocused)
{
btn.CanBeFocused = false;
if (isSelectedButton(btn))
{
btn.Selected = true;
}
else
{
btn.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
}
}
private static void SendResponse(UInt16 actionId, int selectedOption) private static void SendResponse(UInt16 actionId, int selectedOption)
{ {
IWriteMessage outmsg = new WriteOnlyMessage(); IWriteMessage outmsg = new WriteOnlyMessage();
@@ -608,58 +608,90 @@ namespace Barotrauma
} }
break; break;
case NetworkEventType.CONVERSATION: case NetworkEventType.CONVERSATION:
UInt16 identifier = msg.ReadUInt16();
string eventSprite = msg.ReadString();
byte dialogType = msg.ReadByte();
bool continueConversation = msg.ReadBoolean();
UInt16 speakerId = msg.ReadUInt16();
string text = msg.ReadString();
bool fadeToBlack = msg.ReadBoolean();
byte optionCount = msg.ReadByte();
List<string> options = new List<string>();
for (int i = 0; i < optionCount; i++)
{ {
options.Add(msg.ReadString()); UInt16 identifier = msg.ReadUInt16();
} string eventSprite = msg.ReadString();
byte dialogType = msg.ReadByte();
byte endCount = msg.ReadByte(); bool continueConversation = msg.ReadBoolean();
int[] endings = new int[endCount]; UInt16 speakerId = msg.ReadUInt16();
for (int i = 0; i < endCount; i++) string text = msg.ReadString();
{ bool fadeToBlack = msg.ReadBoolean();
endings[i] = msg.ReadByte(); byte optionCount = msg.ReadByte();
} List<string> options = new List<string>();
for (int i = 0; i < optionCount; i++)
if (string.IsNullOrEmpty(text) && optionCount == 0)
{
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
{ {
if (mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction" && pair.Second == identifier) options.Add(msg.ReadString());
}
byte endCount = msg.ReadByte();
int[] endings = new int[endCount];
for (int i = 0; i < endCount; i++)
{
endings[i] = msg.ReadByte();
}
if (string.IsNullOrEmpty(text) && optionCount == 0)
{
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
{ {
(mb as GUIMessageBox)?.Close(); if (mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction" && pair.Second == identifier)
} {
}); (mb as GUIMessageBox)?.Close();
}
});
}
else
{
ConversationAction.CreateDialog(text, Entity.FindEntityByID(speakerId) as Character, options, endings, eventSprite, identifier, fadeToBlack, (ConversationAction.DialogTypes)dialogType, continueConversation);
}
if (Entity.FindEntityByID(speakerId) is Character speaker)
{
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
speaker.SetCustomInteract(null, null);
}
break;
} }
else case NetworkEventType.CONVERSATION_SELECTED_OPTION:
{ {
ConversationAction.CreateDialog(text, Entity.FindEntityByID(speakerId) as Character, options, endings, eventSprite, identifier, fadeToBlack, (ConversationAction.DialogTypes)dialogType, continueConversation); UInt16 identifier = msg.ReadUInt16();
int selectedOption = msg.ReadByte() - 1;
ConversationAction.SelectOption(identifier, selectedOption);
break;
} }
if (Entity.FindEntityByID(speakerId) is Character speaker)
{
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
speaker.SetCustomInteract(null, null);
}
break;
case NetworkEventType.MISSION: case NetworkEventType.MISSION:
Identifier missionIdentifier = msg.ReadIdentifier(); Identifier missionIdentifier = msg.ReadIdentifier();
int locationIndex = msg.ReadInt32();
int destinationIndex = msg.ReadInt32();
string missionName = msg.ReadString(); string missionName = msg.ReadString();
MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier); MissionPrefab? prefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == missionIdentifier);
if (prefab != null) if (prefab != null)
{ {
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", missionName), new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", missionName),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128)) Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
{ {
IconColor = prefab.IconColor IconColor = prefab.IconColor
}; };
if (GameMain.GameSession?.Map is { } map && locationIndex >= 0 && locationIndex < map.Locations.Count)
{
Location location = map.Locations[locationIndex];
map.Discover(location, checkTalents: false);
LocationConnection? connection = null;
if (destinationIndex != locationIndex && destinationIndex >= 0 && destinationIndex < map.Locations.Count)
{
Location destination = map.Locations[destinationIndex];
connection = map.Connections.FirstOrDefault(c => c.Locations.Contains(location) && c.Locations.Contains(destination));
}
if (connection != null)
{
location.UnlockMission(prefab, connection);
}
else
{
location.UnlockMission(prefab);
}
}
} }
break; break;
case NetworkEventType.UNLOCKPATH: case NetworkEventType.UNLOCKPATH:
@@ -8,7 +8,7 @@ namespace Barotrauma
public override int State public override int State
{ {
get { return base.State; } get { return base.State; }
protected set set
{ {
if (state != value) if (state != value)
{ {
@@ -45,7 +45,10 @@ namespace Barotrauma
{ {
requireRescue.Add(character); requireRescue.Add(character);
#if CLIENT #if CLIENT
GameMain.GameSession.CrewManager.AddCharacterToCrewList(character); if (allowOrderingRescuees)
{
GameMain.GameSession.CrewManager.AddCharacterToCrewList(character);
}
#endif #endif
} }
ushort itemCount = msg.ReadUInt16(); ushort itemCount = msg.ReadUInt16();
@@ -10,8 +10,7 @@ namespace Barotrauma
public override RichString GetMissionRewardText(Submarine sub) public override RichString GetMissionRewardText(Submarine sub)
{ {
LocalizedString rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub))); LocalizedString rewardText = GetRewardAmountText(sub);
LocalizedString retVal; LocalizedString retVal;
if (rewardPerCrate.HasValue) if (rewardPerCrate.HasValue)
{ {
@@ -0,0 +1,138 @@
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()
{
SoundPlayer.ForceMusicUpdate();
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)
{
RunWhilePaused = false,
EndWaitDuration = 3.0f
};
}
}, delay: 3.0f);
}
else if (Phase == MissionPhase.AllItemsDestroyed)
{
CoroutineManager.StartCoroutine(wakeUpCoroutine(), name: "EndMission.wakeUpCoroutine");
}
else if (Phase == MissionPhase.BossKilled)
{
if (!string.IsNullOrEmpty(endCinematicSound))
{
SoundPlayer.PlaySound(endCinematicSound);
}
CoroutineManager.Invoke(() =>
{
new CameraTransition(boss, GameMain.GameScreen.Cam, null, Alignment.Center, panDuration: 3, fadeOut: false, endZoom: 0.1f * GUI.yScale)
{
RunWhilePaused = false,
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)
{
RunWhilePaused = false,
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 (boss == null || boss.Removed) { return; }
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 partial class GoToMission : Mission
{ {
public override bool DisplayAsCompleted => false; public override bool DisplayAsCompleted => State >= Prefab.MaxProgressState;
public override bool DisplayAsFailed => false; public override bool DisplayAsFailed => false;
} }
} }
@@ -32,44 +32,73 @@ namespace Barotrauma
return ToolBox.GradientLerp(t, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red); return ToolBox.GradientLerp(t, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
} }
public virtual RichString GetMissionRewardText(Submarine sub) /// <summary>
/// Returns the amount of marks you get from the reward (e.g. "3,000 mk")
/// </summary>
protected LocalizedString GetRewardAmountText(Submarine sub)
{ {
LocalizedString rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub))); int baseReward = GetReward(sub);
return RichString.Rich(TextManager.GetWithVariable("missionreward", "[reward]", "‖color:gui.orange‖"+rewardText+"‖end‖")); int finalReward = GetFinalReward(sub);
string rewardAmountText = string.Format(CultureInfo.InvariantCulture, "{0:N0}", baseReward);
if (finalReward > baseReward)
{
rewardAmountText += $" + {string.Format(CultureInfo.InvariantCulture, "{0:N0}", finalReward - baseReward)}";
}
return TextManager.GetWithVariable("currencyformat", "[credits]", rewardAmountText);
} }
public RichString GetReputationRewardText(Location currLocation) /// <summary>
/// Returns the full reward text of the mission (e.g. "Reward: 2,000 mk" or "Reward: 500 mk x 2 (out of max 5) = 1,000 mk")
/// </summary>
public virtual RichString GetMissionRewardText(Submarine sub)
{
LocalizedString rewardText = GetRewardAmountText(sub);
return RichString.Rich(TextManager.GetWithVariable("missionreward", "[reward]", "‖color:gui.orange‖" + rewardText + "‖end‖"));
}
public RichString GetReputationRewardText()
{ {
List<LocalizedString> reputationRewardTexts = new List<LocalizedString>(); List<LocalizedString> reputationRewardTexts = new List<LocalizedString>();
foreach (var reputationReward in ReputationRewards) foreach (var reputationReward in ReputationRewards)
{ {
LocalizedString name = ""; FactionPrefab targetFactionPrefab;
if (reputationReward.Key == "location" )
if (reputationReward.Key == "location")
{ {
name = $"‖color:gui.orange‖{currLocation.Name}‖end‖"; targetFactionPrefab = OriginLocation.Faction?.Prefab;
} }
else else
{ {
var faction = FactionPrefab.Prefabs.Find(f => f.Identifier == reputationReward.Key); FactionPrefab.Prefabs.TryGet(reputationReward.Key, out targetFactionPrefab);
if (faction != null) }
{
name = $"‖color:{XMLExtensions.ColorToString(faction.IconColor)}‖{faction.Name}‖end‖"; if (targetFactionPrefab == null)
} {
else return string.Empty;
{
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 float totalReputationChange = reputationReward.Value;
if (GameMain.GameSession?.Campaign?.Factions.Find(f => f.Prefab == targetFactionPrefab) is Faction faction)
{
totalReputationChange = reputationReward.Value * faction.Reputation.GetReputationChangeMultiplier(reputationReward.Value);
}
LocalizedString name = $"‖color:{XMLExtensions.ToStringHex(targetFactionPrefab.IconColor)}‖{targetFactionPrefab.Name}‖end‖";
float normalizedValue = MathUtils.InverseLerp(-100.0f, 100.0f, totalReputationChange);
string formattedValue = ((int)Math.Round(totalReputationChange)).ToString("+#;-#;0"); //force plus sign for positive numbers
LocalizedString rewardText = TextManager.GetWithVariables( LocalizedString rewardText = TextManager.GetWithVariables(
"reputationformat", "reputationformat",
("[reputationname]", name), ("[reputationname]", name),
("[reputationvalue]", $"‖color:{XMLExtensions.ColorToString(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" )); ("[reputationvalue]", $"‖color:{XMLExtensions.ToStringHex(Reputation.GetReputationColor(normalizedValue))}‖{formattedValue}‖end‖" ));
reputationRewardTexts.Add(rewardText.Value); 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) partial void ShowMessageProjSpecific(int missionState)
@@ -107,6 +136,11 @@ namespace Barotrauma
}; };
} }
public Identifier GetOverrideMusicType()
{
return Prefab.GetOverrideMusicType(State);
}
public virtual void ClientRead(IReadMessage msg) public virtual void ClientRead(IReadMessage msg)
{ {
State = msg.ReadInt16(); State = msg.ReadInt16();
@@ -8,6 +8,7 @@ namespace Barotrauma
{ {
foreach (Mission mission in missions) 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) 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, IconColor = mission.Prefab.IconColor,
@@ -1,11 +1,16 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Xml.Linq; using System.Collections.Generic;
using System.Collections.Immutable;
namespace Barotrauma namespace Barotrauma
{ {
partial class MissionPrefab : PrefabWithUintIdentifier partial class MissionPrefab : PrefabWithUintIdentifier
{ {
private ImmutableArray<Sprite> portraits = new ImmutableArray<Sprite>();
public bool HasPortraits => portraits.Length > 0;
public Sprite Icon public Sprite Icon
{ {
get; get;
@@ -49,24 +54,57 @@ namespace Barotrauma
private Sprite hudIcon; private Sprite hudIcon;
private Color? hudIconColor; private Color? hudIconColor;
private ImmutableDictionary<int, Identifier> overrideMusicOnState;
partial void InitProjSpecific(ContentXElement element) partial void InitProjSpecific(ContentXElement element)
{ {
DisplayTargetHudIcons = element.GetAttributeBool("displaytargethudicons", false); DisplayTargetHudIcons = element.GetAttributeBool("displaytargethudicons", false);
HudIconMaxDistance = element.GetAttributeFloat("hudiconmaxdistance", 1000.0f); HudIconMaxDistance = element.GetAttributeFloat("hudiconmaxdistance", 1000.0f);
Dictionary<int, Identifier> overrideMusic = new Dictionary<int, Identifier>();
List<Sprite> portraits = new List<Sprite>();
foreach (var subElement in element.Elements()) foreach (var subElement in element.Elements())
{ {
string name = subElement.Name.ToString(); switch (subElement.Name.ToString().ToLowerInvariant())
if (name.Equals("icon", StringComparison.OrdinalIgnoreCase))
{ {
Icon = new Sprite(subElement); case "icon":
IconColor = subElement.GetAttributeColor("color", Color.White); Icon = new Sprite(subElement);
} IconColor = subElement.GetAttributeColor("color", Color.White);
else if (name.Equals("hudicon", StringComparison.OrdinalIgnoreCase)) break;
{ case "hudicon":
hudIcon = new Sprite(subElement); hudIcon = new Sprite(subElement);
hudIconColor = subElement.GetAttributeColor("color"); hudIconColor = subElement.GetAttributeColor("color");
break;
case "overridemusic":
overrideMusic.Add(
subElement.GetAttributeInt("state", 0),
subElement.GetAttributeIdentifier("type", Identifier.Empty));
break;
case "portrait":
var portrait = new Sprite(subElement, lazyLoad: true);
if (portrait != null)
{
portraits.Add(portrait);
}
break;
} }
} }
this.portraits = portraits.ToImmutableArray();
overrideMusicOnState = overrideMusic.ToImmutableDictionary();
}
public Identifier GetOverrideMusicType(int state)
{
if (overrideMusicOnState.TryGetValue(state, out Identifier id))
{
return id;
}
return Identifier.Empty;
}
public Sprite GetPortrait(int randomSeed)
{
if (portraits.Length == 0) { return null; }
return portraits[Math.Abs(randomSeed) % portraits.Length];
} }
partial void DisposeProjectSpecific() partial void DisposeProjectSpecific()
@@ -13,11 +13,12 @@ namespace Barotrauma
byte monsterCount = msg.ReadByte(); byte monsterCount = msg.ReadByte();
for (int i = 0; i < monsterCount; i++) for (int i = 0; i < monsterCount; i++)
{ {
monsters.Add(Character.ReadSpawnData(msg)); var monster = Character.ReadSpawnData(msg);
} if (monster == null)
if (monsters.Contains(null)) {
{ throw new System.Exception($"Error in MonsterMission.ClientReadInitial: failed to create a monster (mission: {Prefab.Identifier}, index: {i})");
throw new System.Exception("Error in MonsterMission.ClientReadInitial: monster list contains null (mission: " + Prefab.Identifier + ")"); }
monsters.Add(monster);
} }
if (monsters.Count != monsterCount) if (monsters.Count != monsterCount)
{ {
@@ -11,38 +11,59 @@ namespace Barotrauma
public override void ClientReadInitial(IReadMessage msg) public override void ClientReadInitial(IReadMessage msg)
{ {
base.ClientReadInitial(msg); base.ClientReadInitial(msg);
bool usedExistingItem = msg.ReadBoolean();
if (usedExistingItem) foreach (var target in targets)
{ {
ushort id = msg.ReadUInt16(); bool targetFound = msg.ReadBoolean();
item = Entity.FindEntityByID(id) as Item; if (!targetFound) { continue; }
if (item == null)
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); var state = (Target.RetrievalState)msg.ReadByte();
if (item == null) 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;
}
} }
} }
} }
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement; using PlayerBalanceElement = Barotrauma.CampaignUI.PlayerBalanceElement;
@@ -29,6 +28,8 @@ namespace Barotrauma
private Point resolutionWhenCreated; private Point resolutionWhenCreated;
private bool needsHireableRefresh;
private enum SortingMethod private enum SortingMethod
{ {
AlphabeticalAsc, AlphabeticalAsc,
@@ -50,6 +51,8 @@ namespace Barotrauma
campaignUI.Campaign.Map.OnLocationChanged.RegisterOverwriteExisting( campaignUI.Campaign.Map.OnLocationChanged.RegisterOverwriteExisting(
"CrewManagement.UpdateLocationView".ToIdentifier(), "CrewManagement.UpdateLocationView".ToIdentifier(),
(locationChangeInfo) => UpdateLocationView(locationChangeInfo.NewLocation, true, locationChangeInfo.PrevLocation)); (locationChangeInfo) => UpdateLocationView(locationChangeInfo.NewLocation, true, locationChangeInfo.PrevLocation));
Reputation.OnAnyReputationValueChanged.RegisterOverwriteExisting(
"CrewManagement.UpdateLocationView".ToIdentifier(), _ => needsHireableRefresh = true);
} }
public void RefreshPermissions() public void RefreshPermissions()
@@ -68,7 +71,13 @@ namespace Barotrauma
{ {
if (child.FindChild(c => c is GUIButton && c.UserData is CharacterInfo, true) is GUIButton buyButton) 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) if (sortingMethod == SortingMethod.AlphabeticalAsc)
{ {
list.Content.RectTransform.SortChildren((x, y) => list.Content.RectTransform.SortChildren((x, y) =>
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Name.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Name)); ((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Name.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Name));
} }
else if (sortingMethod == SortingMethod.JobAsc) else if (sortingMethod == SortingMethod.JobAsc)
{ {
SortCharacters(list, SortingMethod.AlphabeticalAsc); SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) => 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) else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
{ {
SortCharacters(list, SortingMethod.AlphabeticalAsc); SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) => list.Content.RectTransform.SortChildren((x, y) =>
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Salary.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Salary)); ((InfoSkill)x.GUIComponent.UserData).CharacterInfo.Salary.CompareTo(((InfoSkill)y.GUIComponent.UserData).CharacterInfo.Salary));
if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); } if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); }
} }
@@ -313,9 +325,26 @@ namespace Barotrauma
{ {
SortCharacters(list, SortingMethod.AlphabeticalAsc); SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) => list.Content.RectTransform.SortChildren((x, y) =>
CompareReputationRequirement(x.GUIComponent, y.GUIComponent) ??
((InfoSkill)x.GUIComponent.UserData).SkillLevel.CompareTo(((InfoSkill)y.GUIComponent.UserData).SkillLevel)); ((InfoSkill)x.GUIComponent.UserData).SkillLevel.CompareTo(((InfoSkill)y.GUIComponent.UserData).SkillLevel));
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); } 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 private readonly struct InfoSkill
@@ -367,12 +396,25 @@ namespace Barotrauma
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width); 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), 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 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; float width = 0.6f / 3;
if (characterInfo.Job != null && skill != null) if (characterInfo.Job != null && skill != null)
{ {
@@ -410,7 +452,7 @@ namespace Barotrauma
{ {
ClickSound = GUISoundType.Cart, ClickSound = GUISoundType.Cart,
UserData = characterInfo, UserData = characterInfo,
Enabled = HasPermission, Enabled = CanHire(characterInfo),
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo) OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
}; };
hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) => hireButton.OnAddedToGUIUpdateList += (GUIComponent btn) =>
@@ -426,10 +468,9 @@ namespace Barotrauma
else if (!btn.Enabled) else if (!btn.Enabled)
{ {
btn.ToolTip = string.Empty; btn.ToolTip = string.Empty;
btn.Enabled = HasPermission; btn.Enabled = CanHire(characterInfo);
} }
}; };
} }
else if (listBox == pendingList) else if (listBox == pendingList)
{ {
@@ -437,7 +478,7 @@ namespace Barotrauma
{ {
ClickSound = GUISoundType.Cart, ClickSound = GUISoundType.Cart,
UserData = characterInfo, UserData = characterInfo,
Enabled = HasPermission, Enabled = CanHire(characterInfo),
OnClicked = (b, o) => RemovePendingHire(o as 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); 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) 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")}")), ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", PlayerInput.PrimaryMouseLabel),
UserData = characterInfo, UserData = characterInfo,
OnClicked = CreateRenamingComponent 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) private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
@@ -488,13 +547,13 @@ namespace Barotrauma
Point absoluteOffset = new Point( Point absoluteOffset = new Point(
pivot == Pivot.TopLeft ? listBox.Parent.Parent.Rect.Right + 5 : listBox.Parent.Parent.Rect.Left - 5, pivot == Pivot.TopLeft ? listBox.Parent.Parent.Rect.Right + 5 : listBox.Parent.Parent.Rect.Left - 5,
characterFrame.Rect.Top); characterFrame.Rect.Top);
int frameSize = (int)(GUI.Scale * 300); Point frameSize = new Point(GUI.IntScale(300), GUI.IntScale(350));
if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize) < 0) if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize.Y) < 0)
{ {
pivot = listBox == hireableList ? Pivot.BottomLeft : Pivot.BottomRight; pivot = listBox == hireableList ? Pivot.BottomLeft : Pivot.BottomRight;
absoluteOffset.Y = characterFrame.Rect.Bottom; 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 AbsoluteOffset = absoluteOffset
}, style: "InnerFrame") }, style: "InnerFrame")
@@ -503,7 +562,8 @@ namespace Barotrauma
}; };
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), characterPreviewFrame.RectTransform, anchor: Anchor.Center)) 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 // Character info
@@ -545,9 +605,23 @@ namespace Barotrauma
blockHeight = 1.0f / characterSkills.Count(); blockHeight = 1.0f / characterSkills.Count();
foreach (Skill skill in characterSkills) 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); 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) private bool SelectCharacter(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
@@ -636,7 +710,7 @@ namespace Barotrauma
List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>(); List<CharacterInfo> nonDuplicateHires = new List<CharacterInfo>();
hires.ForEach(hireInfo => 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); nonDuplicateHires.Add(hireInfo);
} }
@@ -791,6 +865,16 @@ namespace Barotrauma
playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement); 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); (GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn);
if (highlightedFrame != null && highlightedInfo != null) if (highlightedFrame != null && highlightedInfo != null)
{ {
@@ -106,6 +106,11 @@ namespace Barotrauma
public static float VerticalAspectRatio => GameMain.GraphicsHeight / (float)GameMain.GraphicsWidth; public static float VerticalAspectRatio => GameMain.GraphicsHeight / (float)GameMain.GraphicsWidth;
public static float RelativeHorizontalAspectRatio => HorizontalAspectRatio / (ReferenceResolution.X / ReferenceResolution.Y); public static float RelativeHorizontalAspectRatio => HorizontalAspectRatio / (ReferenceResolution.X / ReferenceResolution.Y);
public static float RelativeVerticalAspectRatio => VerticalAspectRatio / (ReferenceResolution.Y / ReferenceResolution.X); public static float RelativeVerticalAspectRatio => VerticalAspectRatio / (ReferenceResolution.Y / ReferenceResolution.X);
/// <summary>
/// A horizontal scaling factor for low aspect ratios (small width relative to height)
/// </summary>
public static float AspectRatioAdjustment => HorizontalAspectRatio < 1.4f ? (1.0f - (1.4f - HorizontalAspectRatio)) : 1.0f;
public static bool IsUltrawide => HorizontalAspectRatio > 2.0f; public static bool IsUltrawide => HorizontalAspectRatio > 2.0f;
public static int UIWidth public static int UIWidth
@@ -140,13 +145,20 @@ namespace Barotrauma
public static Texture2D WhiteTexture => solidWhiteTexture; public static Texture2D WhiteTexture => solidWhiteTexture;
private static GUICursor MouseCursorSprites => GUIStyle.CursorSprite; private static GUICursor MouseCursorSprites => GUIStyle.CursorSprite;
private static bool debugDrawSounds, debugDrawEvents, debugDrawMetadata; private static bool debugDrawSounds, debugDrawEvents;
private static int debugDrawMetadataOffset;
private static readonly string[] ignoredMetadataInfo = { string.Empty, string.Empty, string.Empty, string.Empty }; 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; 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 PauseMenu { get; private set; }
public static GUIFrame SettingsMenuContainer { get; private set; } public static GUIFrame SettingsMenuContainer { get; private set; }
@@ -195,8 +207,9 @@ namespace Barotrauma
SettingsMenuOpen || SettingsMenuOpen ||
DebugConsole.IsOpen || DebugConsole.IsOpen ||
GameSession.IsTabMenuOpen || GameSession.IsTabMenuOpen ||
(GameMain.GameSession?.GameMode?.Paused ?? false) || GameMain.GameSession?.GameMode is { Paused: true } ||
CharacterHUD.IsCampaignInterfaceOpen; CharacterHUD.IsCampaignInterfaceOpen ||
GameMain.GameSession?.Campaign is { SlideshowPlayer: { Finished: false, Visible: true } };
} }
} }
@@ -533,18 +546,17 @@ namespace Barotrauma
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode) if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
{ {
// TODO: TEST THIS // TODO: TEST THIS
if (debugDrawMetadata) if (debugDrawMetaData.Enabled)
{ {
string text = "Ctrl+M to hide campaign metadata debug info\n\n" + 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+1 to {(debugDrawMetaData.FactionMetadata ? "hide" : "show")} faction reputations, \n" +
$"Ctrl+2 to {(string.IsNullOrWhiteSpace(ignoredMetadataInfo[1]) ? "hide" : "show")} faction reputations, \n" + $"Ctrl+2 to {(debugDrawMetaData.UpgradeLevels ? "hide" : "show")} upgrade levels, \n" +
$"Ctrl+3 to {(string.IsNullOrWhiteSpace(ignoredMetadataInfo[2]) ? "hide" : "show")} upgrade levels, \n" + $"Ctrl+3 to {(debugDrawMetaData.UpgradePrices ? "hide" : "show")} upgrade prices";
$"Ctrl+4 to {(string.IsNullOrWhiteSpace(ignoredMetadataInfo[3]) ? "hide" : "show")} upgrade prices";
Vector2 textSize = GUIStyle.SmallFont.MeasureString(text); Vector2 textSize = GUIStyle.SmallFont.MeasureString(text);
Vector2 pos = new Vector2(GameMain.GraphicsWidth - (textSize.X + 10), 300); Vector2 pos = new Vector2(GameMain.GraphicsWidth - (textSize.X + 10), 300);
DrawString(spriteBatch, pos, text, Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont); DrawString(spriteBatch, pos, text, Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
pos.Y += textSize.Y + 8; pos.Y += textSize.Y + 8;
campaignMode.CampaignMetadata?.DebugDraw(spriteBatch, pos, debugDrawMetadataOffset, ignoredMetadataInfo); campaignMode.CampaignMetadata?.DebugDraw(spriteBatch, pos, campaignMode, debugDrawMetaData);
} }
else else
{ {
@@ -684,37 +696,24 @@ namespace Barotrauma
} }
} }
public static void DrawBackgroundSprite(SpriteBatch spriteBatch, Sprite backgroundSprite, float aberrationStrength = 1.0f) public static void DrawBackgroundSprite(SpriteBatch spriteBatch, Sprite backgroundSprite, Color color, Rectangle? drawArea = null, SpriteEffects spriteEffects = SpriteEffects.None)
{ {
double aberrationT = (Timing.TotalTime * 0.5f); Rectangle area = drawArea ?? new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
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 scale = Math.Max(
(float)GameMain.GraphicsWidth / backgroundSprite.SourceRect.Width, (float)area.Width / backgroundSprite.SourceRect.Width,
(float)GameMain.GraphicsHeight / backgroundSprite.SourceRect.Height) * 1.1f; (float)area.Height / backgroundSprite.SourceRect.Height) * 1.1f;
float paddingX = backgroundSprite.SourceRect.Width * scale - GameMain.GraphicsWidth; float paddingX = backgroundSprite.SourceRect.Width * scale - area.Width;
float paddingY = backgroundSprite.SourceRect.Height * scale - GameMain.GraphicsHeight; float paddingY = backgroundSprite.SourceRect.Height * scale - area.Height;
double noiseT = (Timing.TotalTime * 0.02f); double noiseT = Timing.TotalTime * 0.02f;
Vector2 pos = new Vector2((float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0.5f) - 0.5f); Vector2 pos = new Vector2((float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0.5f) - 0.5f);
pos = new Vector2(pos.X * paddingX, pos.Y * paddingY); pos = new Vector2(pos.X * paddingX, pos.Y * paddingY);
spriteBatch.Draw(backgroundSprite.Texture, spriteBatch.Draw(backgroundSprite.Texture,
new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos, area.Center.ToVector2() + pos,
null, Color.White, 0.0f, backgroundSprite.size / 2, null, color, 0.0f, backgroundSprite.size / 2,
scale, SpriteEffects.None, 0.0f); scale, spriteEffects, 0.0f);
spriteBatch.End();
} }
#region Update list #region Update list
@@ -1206,48 +1205,37 @@ namespace Barotrauma
} }
if (PlayerInput.IsCtrlDown() && PlayerInput.KeyHit(Keys.M)) if (PlayerInput.IsCtrlDown() && PlayerInput.KeyHit(Keys.M))
{ {
debugDrawMetadata = !debugDrawMetadata; debugDrawMetaData.Enabled = !debugDrawMetaData.Enabled;
} }
if (debugDrawMetadata) if (debugDrawMetaData.Enabled)
{ {
if (PlayerInput.KeyHit(Keys.Up)) if (PlayerInput.KeyHit(Keys.Up))
{ {
debugDrawMetadataOffset--; debugDrawMetaData.Offset--;
} }
if (PlayerInput.KeyHit(Keys.Down)) if (PlayerInput.KeyHit(Keys.Down))
{ {
debugDrawMetadataOffset++; debugDrawMetaData.Offset++;
} }
if (PlayerInput.IsCtrlDown()) if (PlayerInput.IsCtrlDown())
{ {
if (PlayerInput.KeyHit(Keys.D1)) if (PlayerInput.KeyHit(Keys.D1))
{ {
ignoredMetadataInfo[0] = ignoredMetadataInfo[0] == string.Empty ? "reputation.location" : string.Empty; debugDrawMetaData.FactionMetadata = !debugDrawMetaData.FactionMetadata;
debugDrawMetadataOffset = 0; debugDrawMetaData.Offset = 0;
} }
if (PlayerInput.KeyHit(Keys.D2)) if (PlayerInput.KeyHit(Keys.D2))
{ {
ignoredMetadataInfo[1] = ignoredMetadataInfo[1] == string.Empty ? "reputation.faction" : string.Empty; debugDrawMetaData.UpgradeLevels = !debugDrawMetaData.UpgradeLevels;
debugDrawMetadataOffset = 0; debugDrawMetaData.Offset = 0;
} }
if (PlayerInput.KeyHit(Keys.D3)) if (PlayerInput.KeyHit(Keys.D3))
{ {
ignoredMetadataInfo[2] = ignoredMetadataInfo[2] == string.Empty ? "upgrade." : string.Empty; debugDrawMetaData.UpgradePrices = !debugDrawMetaData.UpgradePrices;
debugDrawMetadataOffset = 0; debugDrawMetaData.Offset = 0;
}
if (PlayerInput.KeyHit(Keys.D4))
{
ignoredMetadataInfo[3] = ignoredMetadataInfo[3] == string.Empty ? "upgradeprice." : string.Empty;
debugDrawMetadataOffset = 0;
} }
} }
} }
HandlePersistingElements(deltaTime); HandlePersistingElements(deltaTime);
@@ -2599,8 +2587,11 @@ namespace Barotrauma
public static void AddMessage(string message, Color color, float? lifeTime = null, bool playSound = true, GUIFont font = null) public static void AddMessage(string message, Color color, float? lifeTime = null, bool playSound = true, GUIFont font = null)
{ {
if (messages.Any(msg => msg.Text == message)) { return; } lock (mutex)
messages.Add(new GUIMessage(message, color, lifeTime ?? MathHelper.Clamp(message.Length / 5.0f, 3.0f, 10.0f), font ?? GUIStyle.LargeFont)); {
if (messages.Any(msg => msg.Text == message)) { return; }
messages.Add(new GUIMessage(message, color, lifeTime ?? MathHelper.Clamp(message.Length / 5.0f, 3.0f, 10.0f), font ?? GUIStyle.LargeFont));
}
if (playSound) { SoundPlayer.PlayUISound(GUISoundType.UIMessage); } if (playSound) { SoundPlayer.PlayUISound(GUISoundType.UIMessage); }
} }
@@ -2610,34 +2601,37 @@ namespace Barotrauma
var newMessage = new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, GUIStyle.Font, sub: sub); var newMessage = new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, GUIStyle.Font, sub: sub);
if (playSound) { SoundPlayer.PlayUISound(soundType); } if (playSound) { SoundPlayer.PlayUISound(soundType); }
bool overlapFound = true;
int tries = 0;
while (overlapFound)
{
overlapFound = false;
foreach (var otherMessage in messages)
{
float xDiff = otherMessage.Pos.X - newMessage.Pos.X;
if (Math.Abs(xDiff) > (newMessage.Size.X + otherMessage.Size.X) / 2) { continue; }
float yDiff = otherMessage.Pos.Y - newMessage.Pos.Y;
if (Math.Abs(yDiff) > (newMessage.Size.Y + otherMessage.Size.Y) / 2) { continue; }
Vector2 moveDir = -(new Vector2(xDiff, yDiff) + Rand.Vector(1.0f));
if (moveDir.LengthSquared() > 0.0001f)
{
moveDir = Vector2.Normalize(moveDir);
}
else
{
moveDir = Rand.Vector(1.0f);
}
moveDir.Y = -Math.Abs(moveDir.Y);
newMessage.Pos -= Vector2.UnitY * 10;
}
tries++;
if (tries > 20) { break; }
}
messages.Add(newMessage); lock (mutex)
{
bool overlapFound = true;
int tries = 0;
while (overlapFound)
{
overlapFound = false;
foreach (var otherMessage in messages)
{
float xDiff = otherMessage.Pos.X - newMessage.Pos.X;
if (Math.Abs(xDiff) > (newMessage.Size.X + otherMessage.Size.X) / 2) { continue; }
float yDiff = otherMessage.Pos.Y - newMessage.Pos.Y;
if (Math.Abs(yDiff) > (newMessage.Size.Y + otherMessage.Size.Y) / 2) { continue; }
Vector2 moveDir = -(new Vector2(xDiff, yDiff) + Rand.Vector(1.0f));
if (moveDir.LengthSquared() > 0.0001f)
{
moveDir = Vector2.Normalize(moveDir);
}
else
{
moveDir = Rand.Vector(1.0f);
}
moveDir.Y = -Math.Abs(moveDir.Y);
newMessage.Pos -= Vector2.UnitY * 10;
}
tries++;
if (tries > 20) { break; }
}
messages.Add(newMessage);
}
} }
public static void ClearMessages() public static void ClearMessages()
@@ -8,9 +8,7 @@ namespace Barotrauma
{ {
public class GUICanvas : RectTransform public class GUICanvas : RectTransform
{ {
private static readonly object mutex = new object(); protected GUICanvas() : base(Size, parent: null) { }
protected GUICanvas() : base(size, parent: null) { }
private static GUICanvas _instance; private static GUICanvas _instance;
public static GUICanvas Instance public static GUICanvas Instance
@@ -33,7 +31,7 @@ namespace Barotrauma
//GUICanvas stores the children as weak references, to allow elements that we no longer need to get garbage collected //GUICanvas stores the children as weak references, to allow elements that we no longer need to get garbage collected
private readonly List<WeakReference<RectTransform>> childrenWeakRef = new List<WeakReference<RectTransform>>(); private readonly List<WeakReference<RectTransform>> childrenWeakRef = new List<WeakReference<RectTransform>>();
private static Vector2 size => new Vector2(GameMain.GraphicsWidth / (float)GUI.UIWidth, 1f); private static Vector2 Size => new Vector2(GameMain.GraphicsWidth / (float)GUI.UIWidth, 1f);
protected override Rectangle NonScaledUIRect => UIRect; protected override Rectangle NonScaledUIRect => UIRect;
@@ -41,25 +39,27 @@ namespace Barotrauma
private static void OnChildrenChanged(RectTransform _) private static void OnChildrenChanged(RectTransform _)
{ {
lock (mutex) CrossThread.RequestExecutionOnMainThread(RefreshChildren);
}
private static void RefreshChildren()
{
//add weak reference if we don't have one yet
foreach (var child in _instance.Children)
{ {
//add weak reference if we don't have one yet if (!_instance.childrenWeakRef.Any(c => c.TryGetTarget(out var existingChild) && existingChild == child))
foreach (var child in _instance.Children)
{ {
if (!_instance.childrenWeakRef.Any(c => c.TryGetTarget(out var existingChild) && existingChild == child)) _instance.childrenWeakRef.Add(new WeakReference<RectTransform>(child));
{
_instance.childrenWeakRef.Add(new WeakReference<RectTransform>(child));
}
} }
//get rid of strong references }
_instance.children.Clear(); //get rid of strong references
//remove dead children _instance.children.Clear();
for (int i = _instance.childrenWeakRef.Count - 2; i >= 0; i--) //remove dead children
for (int i = _instance.childrenWeakRef.Count - 1; i >= 0; i--)
{
if (!_instance.childrenWeakRef[i].TryGetTarget(out var child) || child.Parent != _instance)
{ {
if (!_instance.childrenWeakRef[i].TryGetTarget(out var child) || child.Parent != _instance) _instance.childrenWeakRef.RemoveAt(i);
{
_instance.childrenWeakRef.RemoveAt(i);
}
} }
} }
} }
@@ -67,7 +67,7 @@ namespace Barotrauma
// Turn public, if there is a need to call this manually. // Turn public, if there is a need to call this manually.
private static void RecalculateSize() private static void RecalculateSize()
{ {
Vector2 recalculatedSize = size; Vector2 recalculatedSize = Size;
// Scale children that are supposed to encompass the whole screen so that they are properly scaled on ultrawide as well // Scale children that are supposed to encompass the whole screen so that they are properly scaled on ultrawide as well
for (int i = 0; i < Instance.childrenWeakRef.Count; i++) for (int i = 0; i < Instance.childrenWeakRef.Count; i++)
@@ -109,7 +109,7 @@ namespace Barotrauma
} }
} }
Instance.Resize(size, resizeChildren: true); Instance.Resize(Size, resizeChildren: true);
Instance.GetAllChildren().Select(c => c.GUIComponent as GUITextBlock).ForEach(t => t?.SetTextPos()); Instance.GetAllChildren().Select(c => c.GUIComponent as GUITextBlock).ForEach(t => t?.SetTextPos());
_instance.children.Clear(); _instance.children.Clear();
} }
@@ -244,18 +244,16 @@ namespace Barotrauma
return parentHierarchy.Last(); return parentHierarchy.Last();
} }
public void AddItem(LocalizedString text, object userData = null, LocalizedString toolTip = null) public GUIComponent AddItem(LocalizedString text, object userData = null, LocalizedString toolTip = null, Color? color = null, Color? textColor = null)
{ {
toolTip ??= ""; toolTip ??= "";
if (selectMultiple) if (selectMultiple)
{ {
var frame = new GUIFrame(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) var frame = new GUIFrame(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, style: "ListBoxElement", color: color)
{ IsFixedSize = false }, style: "ListBoxElement")
{ {
UserData = userData, UserData = userData,
ToolTip = toolTip ToolTip = toolTip
}; };
new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text) new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text)
{ {
UserData = userData, UserData = userData,
@@ -275,7 +273,7 @@ namespace Barotrauma
foreach (GUIComponent child in ListBox.Content.Children) foreach (GUIComponent child in ListBox.Content.Children)
{ {
var tickBox = child.GetChild<GUITickBox>(); var tickBox = child.GetChild<GUITickBox>();
if (tickBox.Selected) if (tickBox is { Selected: true })
{ {
selectedDataMultiple.Add(child.UserData); selectedDataMultiple.Add(child.UserData);
selectedIndexMultiple.Add(i); selectedIndexMultiple.Add(i);
@@ -289,11 +287,11 @@ namespace Barotrauma
return true; return true;
} }
}; };
return frame;
} }
else else
{ {
new GUITextBlock(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) return new GUITextBlock(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, text, style: "ListBoxElement", color: color, textColor: textColor)
{ IsFixedSize = false }, text, style: "ListBoxElement")
{ {
UserData = userData, UserData = userData,
ToolTip = toolTip ToolTip = toolTip
@@ -323,7 +321,7 @@ namespace Barotrauma
} }
else else
{ {
if (!(component is GUITextBlock textBlock)) if (component is not GUITextBlock textBlock)
{ {
textBlock = component.GetChild<GUITextBlock>(); textBlock = component.GetChild<GUITextBlock>();
if (textBlock is null && !AllowNonText) { return false; } if (textBlock is null && !AllowNonText) { return false; }
@@ -1059,6 +1059,7 @@ namespace Barotrauma
GUIComponent child = Content.GetChild(childIndex); GUIComponent child = Content.GetChild(childIndex);
if (child is null) { return; } if (child is null) { return; }
if (!child.Enabled) { return; }
bool wasSelected = true; bool wasSelected = true;
if (OnSelected != null) if (OnSelected != null)
@@ -268,7 +268,7 @@ namespace Barotrauma
Buttons.Clear(); 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); GUIStyle.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
@@ -313,7 +313,9 @@ namespace Barotrauma
break; break;
} }
RectTransform.MinSize = TextBox.RectTransform.MinSize; RectTransform.MinSize = new Point(
Math.Max(rectT.MinSize.X, TextBox.RectTransform.MinSize.X),
Math.Max(rectT.MinSize.Y, TextBox.RectTransform.MinSize.Y));
LayoutGroup.Recalculate(); LayoutGroup.Recalculate();
} }
@@ -438,7 +438,7 @@ namespace Barotrauma
} }
else else
{ {
if ((PlayerInput.LeftButtonClicked() || PlayerInput.RightButtonClicked()) && selected) if ((PlayerInput.PrimaryMouseButtonClicked() || PlayerInput.SecondaryMouseButtonClicked()) && selected)
{ {
if (!mouseHeldInside) { Deselect(); } if (!mouseHeldInside) { Deselect(); }
mouseHeldInside = false; mouseHeldInside = false;
@@ -143,14 +143,13 @@ namespace Barotrauma
} }
int healthBarHeight = (int)(50f * GUI.Scale); 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); HealthBarArea = new Rectangle(BottomRightInfoArea.Right - healthBarWidth + (int)Math.Floor(1 / GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + GUI.IntScale(10), healthBarWidth, healthBarHeight);
HealthBarAfflictionArea = 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; int messageAreaWidth = GameMain.GraphicsWidth / 3;
MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom + ButtonAreaTop.Height, messageAreaWidth, ButtonAreaTop.Height); MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom + ButtonAreaTop.Height, messageAreaWidth, ButtonAreaTop.Height);
bool isFourByThree = GUI.IsFourByThree(); int chatBoxWidth = (int)(475 * GUI.Scale * GUI.AspectRatioAdjustment);
int chatBoxWidth = !isFourByThree ? (int)(475 * GUI.Scale) : (int)(375 * GUI.Scale);
int chatBoxHeight = (int)Math.Max(GameMain.GraphicsHeight * 0.25f, 150); int chatBoxHeight = (int)Math.Max(GameMain.GraphicsHeight * 0.25f, 150);
ChatBoxArea = new Rectangle(Padding, GameMain.GraphicsHeight - Padding - chatBoxHeight, chatBoxWidth, chatBoxHeight); ChatBoxArea = new Rectangle(Padding, GameMain.GraphicsHeight - Padding - chatBoxHeight, chatBoxWidth, chatBoxHeight);
@@ -187,19 +186,26 @@ namespace Barotrauma
public static void Draw(SpriteBatch spriteBatch) public static void Draw(SpriteBatch spriteBatch)
{ {
DrawRectangle(ButtonAreaTop, Color.White * 0.5f); DrawRectangle(nameof(ButtonAreaTop), ButtonAreaTop, Color.White * 0.5f);
DrawRectangle(TutorialObjectiveListArea, GUIStyle.Blue * 0.5f); DrawRectangle(nameof(TutorialObjectiveListArea), TutorialObjectiveListArea, GUIStyle.Blue * 0.5f);
DrawRectangle(MessageAreaTop, GUIStyle.Orange * 0.5f); DrawRectangle(nameof(MessageAreaTop), MessageAreaTop, GUIStyle.Orange * 0.5f);
DrawRectangle(CrewArea, Color.Blue * 0.5f); DrawRectangle(nameof(CrewArea), CrewArea, Color.Blue * 0.5f);
DrawRectangle(ChatBoxArea, Color.Cyan * 0.5f); DrawRectangle(nameof(ChatBoxArea), ChatBoxArea, Color.Cyan * 0.5f);
DrawRectangle(HealthBarArea, Color.Red * 0.5f); DrawRectangle(nameof(HealthBarArea), HealthBarArea, Color.Red * 0.5f);
DrawRectangle(HealthBarAfflictionArea, Color.Red * 0.5f); DrawRectangle(nameof(HealthBarAfflictionArea), HealthBarAfflictionArea, Color.Red * 0.5f);
DrawRectangle(InventoryAreaLower, Color.Yellow * 0.5f); DrawRectangle(nameof(InventoryAreaLower), InventoryAreaLower, Color.Yellow * 0.5f);
DrawRectangle(HealthWindowAreaLeft, Color.Red * 0.5f); DrawRectangle(nameof(HealthWindowAreaLeft), HealthWindowAreaLeft, Color.Red * 0.5f);
DrawRectangle(BottomRightInfoArea, Color.Green * 0.5f); DrawRectangle(nameof(BottomRightInfoArea), BottomRightInfoArea, Color.Green * 0.5f);
DrawRectangle(ItemHUDArea, Color.Magenta * 0.3f); DrawRectangle(nameof(ItemHUDArea), ItemHUDArea, Color.Magenta * 0.3f);
void DrawRectangle(Rectangle r, Color c) => GUI.DrawRectangle(spriteBatch, r, c); void DrawRectangle(string label, Rectangle r, Color c)
{
if (!label.IsNullOrEmpty())
{
GUI.DrawString(spriteBatch, r.Location.ToVector2() + Vector2.One * 3, label, c, font: GUIStyle.SmallFont);
}
GUI.DrawRectangle(spriteBatch, r, c);
}
} }
} }
@@ -11,9 +11,9 @@ namespace Barotrauma
{ {
class LoadingScreen class LoadingScreen
{ {
private readonly Texture2D defaultBackgroundTexture, overlay; private readonly Sprite defaultBackgroundTexture, overlay;
private readonly SpriteSheet decorativeGraph, decorativeMap; private readonly SpriteSheet decorativeGraph, decorativeMap;
private Texture2D currentBackgroundTexture; private Sprite currentBackgroundTexture;
private readonly Sprite noiseSprite; private readonly Sprite noiseSprite;
private string randText = ""; private string randText = "";
@@ -24,6 +24,8 @@ namespace Barotrauma
private Video currSplashScreen; private Video currSplashScreen;
private DateTime videoStartTime; private DateTime videoStartTime;
private bool mirrorBackground;
public struct PendingSplashScreen public struct PendingSplashScreen
{ {
public string Filename; public string Filename;
@@ -112,12 +114,12 @@ namespace Barotrauma
public LoadingScreen(GraphicsDevice graphics) public LoadingScreen(GraphicsDevice graphics)
{ {
defaultBackgroundTexture = TextureLoader.FromFile("Content/Map/LocationPortraits/AlienRuins.png"); defaultBackgroundTexture = new Sprite("Content/Map/LocationPortraits/MainMenu1.png", Vector2.Zero);
decorativeMap = new SpriteSheet("Content/Map/MapHUD.png", 6, 5, Vector2.Zero, sourceRect: new Rectangle(0, 0, 2048, 640)); 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)); 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 = new Sprite("Content/UI/MainMenuVignette.png", Vector2.Zero);
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero); noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
DrawLoadingText = true; DrawLoadingText = true;
SetSelectedTip(TextManager.Get("LoadingScreenTip")); SetSelectedTip(TextManager.Get("LoadingScreenTip"));
@@ -138,35 +140,24 @@ namespace Barotrauma
DisableSplashScreen(); 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; drawn = true;
currentBackgroundTexture ??= defaultBackgroundTexture; currentBackgroundTexture ??= defaultBackgroundTexture;
float overlayScale = Math.Min(GameMain.GraphicsWidth / overlay.size.X, GameMain.GraphicsHeight / overlay.size.Y);
Rectangle drawArea = new Rectangle(
(int)(overlay.size.X * overlayScale / 2), 0,
(int)(GameMain.GraphicsWidth - overlay.size.X * overlayScale / 2), GameMain.GraphicsHeight);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState);
float scale = (GameMain.GraphicsWidth / (float)currentBackgroundTexture.Width) * 1.2f; GUI.DrawBackgroundSprite(spriteBatch, currentBackgroundTexture, Color.White, drawArea,
float paddingX = currentBackgroundTexture.Width * scale - GameMain.GraphicsWidth; spriteEffects: mirrorBackground ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
float paddingY = currentBackgroundTexture.Height * scale - GameMain.GraphicsHeight; overlay.Draw(spriteBatch, Vector2.Zero, scale: overlayScale);
double noiseT = (Timing.TotalTime * 0.02f);
Vector2 pos = new Vector2((float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0.5f) - 0.5f);
pos = new Vector2(pos.X * paddingX, pos.Y * paddingY);
spriteBatch.Draw(currentBackgroundTexture,
new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos,
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);
double noiseT = Timing.TotalTime * 0.02f;
float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0); float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0);
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f; float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f;
noiseSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight), noiseSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
@@ -174,10 +165,7 @@ namespace Barotrauma
color: Color.White * noiseStrength * 0.1f, color: Color.White * noiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale); textureScale: Vector2.One * noiseScale);
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f), Vector2 textPos = new Vector2((int)(GameMain.GraphicsWidth * 0.05f), (int)(GameMain.GraphicsHeight * 0.75f));
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
scale: GameMain.GraphicsHeight / 2000.0f);
if (WaitForLanguageSelection) if (WaitForLanguageSelection)
{ {
DrawLanguageSelectionPrompt(spriteBatch, graphics); DrawLanguageSelectionPrompt(spriteBatch, graphics);
@@ -215,16 +203,18 @@ namespace Barotrauma
#endif #endif
} }
} }
if (GUIStyle.LargeFont.HasValue) if (GUIStyle.LargeFont.HasValue)
{ {
GUIStyle.LargeFont.DrawString(spriteBatch, loadText.ToUpper(), 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); Color.White);
textPos.Y += GUIStyle.LargeFont.MeasureString(loadText.ToUpper()).Y * 1.2f;
} }
if (GUIStyle.Font.HasValue && selectedTip != null) 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'); string[] lines = wrappedTip.Split('\n');
float lineHeight = GUIStyle.Font.MeasureString(selectedTip).Y; float lineHeight = GUIStyle.Font.MeasureString(selectedTip).Y;
@@ -234,7 +224,8 @@ namespace Barotrauma
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < lines.Length; i++)
{ {
GUIStyle.Font.DrawStringWithColors(spriteBatch, lines[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); 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTip.RichTextData.Value, rtdOffset);
rtdOffset += lines[i].Length; rtdOffset += lines[i].Length;
} }
@@ -244,7 +235,8 @@ namespace Barotrauma
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < lines.Length; i++)
{ {
GUIStyle.Font.DrawString(spriteBatch, lines[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 +249,16 @@ namespace Barotrauma
Vector2 decorativeScale = new Vector2(GameMain.GraphicsHeight / 1080.0f); Vector2 decorativeScale = new Vector2(GameMain.GraphicsHeight / 1080.0f);
float noiseVal = (float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.25f, Timing.TotalTime * 0.5f, 0); float noiseVal = (float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.25f, Timing.TotalTime * 0.5f, 0);
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal), if (!WaitForLanguageSelection)
new Vector2(GameMain.GraphicsWidth * 0.001f, GameMain.GraphicsHeight * 0.24f), {
Color.White, Vector2.Zero, 0.0f, decorativeScale, SpriteEffects.FlipVertically); 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), decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal),
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.66f), new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.01f),
Color.White, decorativeMap.FrameSize.ToVector2(), 0.0f, decorativeScale); Color.White, new Vector2(decorativeMap.FrameSize.X, 0), 0.0f, decorativeScale, SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically);
if (noiseVal < 0.2f) if (noiseVal < 0.2f)
{ {
@@ -285,8 +280,9 @@ namespace Barotrauma
if (GUIStyle.LargeFont.HasValue) if (GUIStyle.LargeFont.HasValue)
{ {
Vector2 textSize = GUIStyle.LargeFont.MeasureString(randText);
GUIStyle.LargeFont.DrawString(spriteBatch, 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)); Color.White * (1.0f - noiseVal));
} }
@@ -312,8 +308,8 @@ namespace Barotrauma
languageSelectionCursor = new Sprite("Content/UI/cursor.png", Vector2.Zero); languageSelectionCursor = new Sprite("Content/UI/cursor.png", Vector2.Zero);
} }
Vector2 textPos = new Vector2(GameMain.GraphicsWidth / 2, GameMain.GraphicsHeight * 0.3f); 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); Vector2 textSpacing = new Vector2(0.0f, GameMain.GraphicsHeight * 0.5f / AvailableLanguages.Length);
foreach (LanguageIdentifier language in AvailableLanguages) foreach (LanguageIdentifier language in AvailableLanguages)
{ {
string localizedLanguageName = TextManager.GetTranslatedLanguageName(language); string localizedLanguageName = TextManager.GetTranslatedLanguageName(language);
@@ -321,10 +317,10 @@ namespace Barotrauma
Vector2 textSize = font.MeasureString(localizedLanguageName); Vector2 textSize = font.MeasureString(localizedLanguageName);
bool hover = bool hover =
Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 && PlayerInput.MousePosition.X > textPos.X && PlayerInput.MousePosition.X < textPos.X + textSize.X &&
Math.Abs(PlayerInput.MousePosition.Y - textPos.Y) < textSpacing.Y / 2; 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); hover ? Color.White : Color.White * 0.6f);
if (hover && PlayerInput.PrimaryMouseButtonClicked()) if (hover && PlayerInput.PrimaryMouseButtonClicked())
{ {
@@ -431,7 +427,12 @@ namespace Barotrauma
drawn = false; drawn = false;
LoadState = null; LoadState = null;
SetSelectedTip(TextManager.Get("LoadingScreenTip")); 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));
if (GameMain.GameSession?.GameMode?.Missions is { } missions && missions.Any(m => m.Prefab.HasPortraits))
{
currentBackgroundTexture = missions.Where(m => m.Prefab.HasPortraits).First().Prefab.GetPortrait(Rand.Int(int.MaxValue));
}
mirrorBackground = Rand.Range(0.0f, 1.0f) < 0.5f;
while (!drawn) while (!drawn)
{ {
@@ -143,33 +143,32 @@ namespace Barotrauma
{ {
public readonly MedicalClinic.NetAffliction Target; public readonly MedicalClinic.NetAffliction Target;
public readonly ImmutableArray<GUIComponent> ElementsToDisable; public readonly ImmutableArray<GUIComponent> ElementsToDisable;
public readonly GUIComponent TargetElement;
public PopupAffliction(ImmutableArray<GUIComponent> elementsToDisable, MedicalClinic.NetAffliction target) public PopupAffliction(ImmutableArray<GUIComponent> elementsToDisable, GUIComponent component, MedicalClinic.NetAffliction target)
{ {
Target = target; Target = target;
ElementsToDisable = elementsToDisable; ElementsToDisable = elementsToDisable;
TargetElement = component;
} }
} }
private readonly struct PopupAfflictionList private readonly struct PopupAfflictionList
{ {
public readonly MedicalClinic.NetCrewMember Target; public readonly MedicalClinic.NetCrewMember Target;
public readonly GUIListBox ListElement;
public readonly GUIButton TreatAllButton; public readonly GUIButton TreatAllButton;
public readonly List<PopupAffliction> Afflictions; public readonly HashSet<PopupAffliction> Afflictions;
public PopupAfflictionList(MedicalClinic.NetCrewMember crewMember, GUIButton treatAllButton) public PopupAfflictionList(MedicalClinic.NetCrewMember crewMember, GUIListBox listElement, GUIButton treatAllButton)
{ {
ListElement = listElement;
Target = crewMember; Target = crewMember;
TreatAllButton = treatAllButton; TreatAllButton = treatAllButton;
Afflictions = new List<PopupAffliction>(); Afflictions = new HashSet<PopupAffliction>();
} }
} }
// private enum SortMode
// {
// Severity
// }
private readonly MedicalClinic medicalClinic; private readonly MedicalClinic medicalClinic;
private readonly GUIComponent container; private readonly GUIComponent container;
private Point prevResolution; private Point prevResolution;
@@ -221,23 +220,22 @@ namespace Barotrauma
private void UpdatePopupAfflictions() private void UpdatePopupAfflictions()
{ {
if (selectedCrewAfflictionList is { } afflictionList) if (selectedCrewAfflictionList is not { } afflictionList) { return; }
{
foreach (PopupAffliction popupAffliction in afflictionList.Afflictions)
{
ToggleElements(ElementState.Enabled, popupAffliction.ElementsToDisable);
if (medicalClinic.IsAfflictionPending(afflictionList.Target, popupAffliction.Target))
{
ToggleElements(ElementState.Disabled, popupAffliction.ElementsToDisable);
}
}
afflictionList.TreatAllButton.Enabled = true; foreach (PopupAffliction popupAffliction in afflictionList.Afflictions)
if (afflictionList.Afflictions.All(a => medicalClinic.IsAfflictionPending(afflictionList.Target, a.Target))) {
ToggleElements(ElementState.Enabled, popupAffliction.ElementsToDisable);
if (medicalClinic.IsAfflictionPending(afflictionList.Target, popupAffliction.Target))
{ {
afflictionList.TreatAllButton.Enabled = false; ToggleElements(ElementState.Disabled, popupAffliction.ElementsToDisable);
} }
} }
afflictionList.TreatAllButton.Enabled = true;
if (afflictionList.Afflictions.All(a => medicalClinic.IsAfflictionPending(afflictionList.Target, a.Target)))
{
afflictionList.TreatAllButton.Enabled = false;
}
} }
private void UpdatePending() private void UpdatePending()
@@ -309,7 +307,7 @@ namespace Barotrauma
} }
} }
private void UpdateCrewPanel() public void UpdateCrewPanel()
{ {
if (crewHealList is not { } healList) { return; } if (crewHealList is not { } healList) { return; }
@@ -502,7 +500,7 @@ namespace Barotrauma
return true; return true;
} }
}; };
crewHealList = new CrewHealList(crewList, parent, treatAllButton); crewHealList = new CrewHealList(crewList, parent, treatAllButton);
void OnReceived(MedicalClinic.CallbackOnlyRequest obj) void OnReceived(MedicalClinic.CallbackOnlyRequest obj)
@@ -789,7 +787,7 @@ namespace Barotrauma
GUIListBox afflictionList = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), mainLayout.RectTransform)) { Visible = false }; GUIListBox afflictionList = new GUIListBox(new RectTransform(new Vector2(1f, 0.8f), mainLayout.RectTransform)) { Visible = false };
PopupAfflictionList popupAfflictionList = new PopupAfflictionList(crewMember, treatAllButton); PopupAfflictionList popupAfflictionList = new PopupAfflictionList(crewMember, afflictionList, treatAllButton);
selectedCrewElement = mainFrame; selectedCrewElement = mainFrame;
selectedCrewAfflictionList = popupAfflictionList; selectedCrewAfflictionList = popupAfflictionList;
@@ -810,9 +808,9 @@ namespace Barotrauma
List<GUIComponent> allComponents = new List<GUIComponent>(); List<GUIComponent> allComponents = new List<GUIComponent>();
foreach (MedicalClinic.NetAffliction affliction in request.Afflictions) foreach (MedicalClinic.NetAffliction affliction in request.Afflictions)
{ {
ImmutableArray<GUIComponent> createdComponents = CreatePopupAffliction(afflictionList.Content, crewMember, affliction); CreatedPopupAfflictionElement createdComponents = CreatePopupAffliction(afflictionList.Content, crewMember, affliction);
allComponents.AddRange(createdComponents); allComponents.AddRange(createdComponents.AllCreatedElements);
popupAfflictionList.Afflictions.Add(new PopupAffliction(createdComponents, affliction)); popupAfflictionList.Afflictions.Add(new PopupAffliction(createdComponents.AllCreatedElements, createdComponents.MainElement, affliction));
} }
allComponents.Add(treatAllButton); allComponents.Add(treatAllButton);
@@ -832,9 +830,11 @@ namespace Barotrauma
} }
} }
private ImmutableArray<GUIComponent> CreatePopupAffliction(GUIComponent parent, MedicalClinic.NetCrewMember crewMember, MedicalClinic.NetAffliction affliction) private readonly record struct CreatedPopupAfflictionElement(GUIComponent MainElement, ImmutableArray<GUIComponent> AllCreatedElements);
private CreatedPopupAfflictionElement CreatePopupAffliction(GUIComponent parent, MedicalClinic.NetCrewMember crewMember, MedicalClinic.NetAffliction affliction)
{ {
if (!(affliction.Prefab is { } prefab)) { return ImmutableArray<GUIComponent>.Empty; } ToolBox.ThrowIfNull(affliction.Prefab);
GUIFrame backgroundFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.33f), parent.RectTransform), style: "ListBoxElement"); GUIFrame backgroundFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.33f), parent.RectTransform), style: "ListBoxElement");
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), backgroundFrame.RectTransform, Anchor.BottomCenter), style: "HorizontalLine"); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), backgroundFrame.RectTransform, Anchor.BottomCenter), style: "HorizontalLine");
@@ -846,9 +846,9 @@ namespace Barotrauma
GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.33f), mainLayout.RectTransform), isHorizontal: true) { Stretch = true }; GUILayoutGroup topLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.33f), mainLayout.RectTransform), isHorizontal: true) { Stretch = true };
Color iconColor = CharacterHealth.GetAfflictionIconColor(prefab, affliction.Strength); Color iconColor = CharacterHealth.GetAfflictionIconColor(affliction.Prefab, affliction.Strength);
GUIImage icon = new GUIImage(new RectTransform(Vector2.One, topLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), prefab.Icon, scaleToFit: true) GUIImage icon = new GUIImage(new RectTransform(Vector2.One, topLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), affliction.Prefab.Icon, scaleToFit: true)
{ {
Color = iconColor, Color = iconColor,
DisabledColor = iconColor * 0.5f DisabledColor = iconColor * 0.5f
@@ -856,7 +856,7 @@ namespace Barotrauma
GUILayoutGroup topTextLayout = new GUILayoutGroup(new RectTransform(Vector2.One, topLayout.RectTransform), isHorizontal: true); GUILayoutGroup topTextLayout = new GUILayoutGroup(new RectTransform(Vector2.One, topLayout.RectTransform), isHorizontal: true);
GUITextBlock prefabBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), topTextLayout.RectTransform), prefab.Name, font: GUIStyle.SubHeadingFont); GUITextBlock prefabBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), topTextLayout.RectTransform), affliction.Prefab.Name, font: GUIStyle.SubHeadingFont);
Color textColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red, affliction.Strength / affliction.Prefab.MaxStrength); Color textColor = Color.Lerp(GUIStyle.Orange, GUIStyle.Red, affliction.Strength / affliction.Prefab.MaxStrength);
@@ -878,7 +878,7 @@ namespace Barotrauma
AutoScaleHorizontal = true AutoScaleHorizontal = true
}; };
EnsureTextDoesntOverflow(prefab.Name.Value, prefabBlock, prefabBlock.Rect, ImmutableArray.Create(mainLayout, topLayout, topTextLayout)); EnsureTextDoesntOverflow(affliction.Prefab.Name.Value, prefabBlock, prefabBlock.Rect, ImmutableArray.Create(mainLayout, topLayout, topTextLayout));
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.66f), mainLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft); GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.66f), mainLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
@@ -923,7 +923,7 @@ namespace Barotrauma
return true; return true;
}; };
return elementsToDisable; return new CreatedPopupAfflictionElement(backgroundFrame, elementsToDisable);
} }
private void AddPending(ImmutableArray<GUIComponent> elementsToDisable, MedicalClinic.NetCrewMember crewMember, ImmutableArray<MedicalClinic.NetAffliction> afflictions) private void AddPending(ImmutableArray<GUIComponent> elementsToDisable, MedicalClinic.NetCrewMember crewMember, ImmutableArray<MedicalClinic.NetAffliction> afflictions)
@@ -1033,11 +1033,53 @@ namespace Barotrauma
} }
} }
public void UpdateAfflictions(MedicalClinic.NetCrewMember crewMember)
{
if (selectedCrewAfflictionList is not { } afflictionList || !afflictionList.Target.CharacterEquals(crewMember)) { return; }
List<GUIComponent> allComponents = new List<GUIComponent>();
foreach (PopupAffliction existingAffliction in afflictionList.Afflictions.ToHashSet())
{
if (crewMember.Afflictions.None(received => received.AfflictionEquals(existingAffliction.Target)))
{
// remove from UI
existingAffliction.TargetElement.RectTransform.Parent = null;
afflictionList.Afflictions.Remove(existingAffliction);
}
else
{
allComponents.AddRange(existingAffliction.ElementsToDisable);
}
}
foreach (MedicalClinic.NetAffliction received in crewMember.Afflictions)
{
// we're not that concerned about updating the strength of the afflictions
if (afflictionList.Afflictions.Any(existing => existing.Target.AfflictionEquals(received))) { continue; }
CreatedPopupAfflictionElement createdComponents = CreatePopupAffliction(afflictionList.ListElement.Content, crewMember, received);
allComponents.AddRange(createdComponents.AllCreatedElements);
afflictionList.Afflictions.Add(new PopupAffliction(createdComponents.AllCreatedElements, createdComponents.MainElement, received));
}
allComponents.Add(afflictionList.TreatAllButton);
afflictionList.TreatAllButton.OnClicked = (_, _) =>
{
var afflictions = crewMember.Afflictions.Where(a => !medicalClinic.IsAfflictionPending(crewMember, a)).ToImmutableArray();
if (!afflictions.Any()) { return true; }
AddPending(allComponents.ToImmutableArray(), crewMember, afflictions);
return true;
};
UpdatePopupAfflictions();
}
public void ClosePopup() public void ClosePopup()
{ {
if (selectedCrewElement is { } popup) if (selectedCrewElement is { } popup)
{ {
popup.Parent?.RemoveChild(selectedCrewElement); popup.RectTransform.Parent = null;
} }
selectedCrewElement = null; selectedCrewElement = null;
@@ -1096,5 +1138,14 @@ namespace Barotrauma
refreshTimer = 0; refreshTimer = 0;
} }
} }
public void OnDeselected()
{
if (GameMain.NetworkMember is not null)
{
MedicalClinic.SendUnsubscribeRequest();
}
ClosePopup();
}
} }
} }
@@ -207,11 +207,12 @@ namespace Barotrauma
cargoManager.OnItemsInSellFromSubCrateChanged.RegisterOverwriteExisting(refreshStoreId, _ => needsSellingFromSubRefresh = true); cargoManager.OnItemsInSellFromSubCrateChanged.RegisterOverwriteExisting(refreshStoreId, _ => needsSellingFromSubRefresh = true);
} }
public void SelectStore(Identifier identifier) public void SelectStore(Character merchant)
{ {
Identifier storeIdentifier = merchant?.MerchantIdentifier ?? Identifier.Empty;
if (CurrentLocation?.Stores != null) if (CurrentLocation?.Stores != null)
{ {
if (!identifier.IsEmpty && CurrentLocation.GetStore(identifier) is { } store) if (!storeIdentifier.IsEmpty && CurrentLocation.GetStore(storeIdentifier) is { } store)
{ {
ActiveStore = store; ActiveStore = store;
if (storeNameBlock != null) if (storeNameBlock != null)
@@ -223,12 +224,13 @@ namespace Barotrauma
} }
storeNameBlock.SetRichText(storeName); storeNameBlock.SetRichText(storeName);
} }
ActiveStore.SetMerchantFaction(merchant.Faction);
} }
else else
{ {
ActiveStore = null; ActiveStore = null;
string errorId, msg; string errorId, msg;
if (identifier.IsEmpty) if (storeIdentifier.IsEmpty)
{ {
errorId = "Store.SelectStore:IdentifierEmpty"; errorId = "Store.SelectStore:IdentifierEmpty";
msg = $"Error selecting store at {CurrentLocation}: identifier is empty."; msg = $"Error selecting store at {CurrentLocation}: identifier is empty.";
@@ -236,7 +238,7 @@ namespace Barotrauma
else else
{ {
errorId = "Store.SelectStore:StoreDoesntExist"; errorId = "Store.SelectStore:StoreDoesntExist";
msg = $"Error selecting store with identifier \"{identifier}\" at {CurrentLocation}: store with the identifier doesn't exist at the location."; msg = $"Error selecting store with identifier \"{storeIdentifier}\" at {CurrentLocation}: store with the identifier doesn't exist at the location.";
} }
DebugConsole.LogError(msg); DebugConsole.LogError(msg);
GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg); GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
@@ -249,17 +251,17 @@ namespace Barotrauma
if (campaignUI.Campaign.Map == null) if (campaignUI.Campaign.Map == null)
{ {
errorId = "Store.SelectStore:MapNull"; errorId = "Store.SelectStore:MapNull";
msg = $"Error selecting store with identifier \"{identifier}\": Map is null."; msg = $"Error selecting store with identifier \"{storeIdentifier}\": Map is null.";
} }
else if (CurrentLocation == null) else if (CurrentLocation == null)
{ {
errorId = "Store.SelectStore:CurrentLocationNull"; errorId = "Store.SelectStore:CurrentLocationNull";
msg = $"Error selecting store with identifier \"{identifier}\": CurrentLocation is null."; msg = $"Error selecting store with identifier \"{storeIdentifier}\": CurrentLocation is null.";
} }
else if (CurrentLocation.Stores == null) else if (CurrentLocation.Stores == null)
{ {
errorId = "Store.SelectStore:StoresNull"; errorId = "Store.SelectStore:StoresNull";
msg = $"Error selecting store with identifier \"{identifier}\": CurrentLocation.Stores is null."; msg = $"Error selecting store with identifier \"{storeIdentifier}\": CurrentLocation.Stores is null.";
} }
if (!msg.IsNullOrEmpty()) if (!msg.IsNullOrEmpty())
{ {
@@ -406,11 +408,11 @@ namespace Barotrauma
TextScale = 1.1f, TextScale = 1.1f,
TextGetter = () => TextGetter = () =>
{ {
if (CurrentLocation != null) if (ActiveStore is not null)
{ {
Color textColor = GUIStyle.ColorReputationNeutral; Color textColor = GUIStyle.ColorReputationNeutral;
string sign = ""; string sign = "";
int reputationModifier = (int)MathF.Round((CurrentLocation.GetStoreReputationModifier(activeTab == StoreTab.Buy) - 1) * 100); int reputationModifier = (int)MathF.Round((ActiveStore.GetReputationModifier(activeTab == StoreTab.Buy) - 1) * 100);
if (reputationModifier > 0) if (reputationModifier > 0)
{ {
textColor = IsBuying ? GUIStyle.ColorReputationLow : GUIStyle.ColorReputationHigh; textColor = IsBuying ? GUIStyle.ColorReputationLow : GUIStyle.ColorReputationHigh;
@@ -727,7 +729,7 @@ namespace Barotrauma
ChangeStoreTab(StoreTab.Buy); ChangeStoreTab(StoreTab.Buy);
if (newLocation?.Reputation != null) if (newLocation?.Reputation != null)
{ {
CurrentLocation.Reputation.OnReputationValueChanged.RegisterOverwriteExisting("RefreshStore".ToIdentifier(), _ => { SetNeedsRefresh(); }); newLocation.Reputation.OnReputationValueChanged.RegisterOverwriteExisting("RefreshStore".ToIdentifier(), _ => { SetNeedsRefresh(); });
} }
} }
@@ -855,6 +857,28 @@ namespace Barotrauma
FilterStoreItems(category, searchBox.Text); 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; int prevDailySpecialCount, prevRequestedGoodsCount, prevSubRequestedGoodsCount;
private void RefreshStoreBuyList() private void RefreshStoreBuyList()
@@ -898,6 +922,7 @@ namespace Barotrauma
{ {
if (itemPrefab.CanBeBoughtFrom(ActiveStore, out PriceInfo priceInfo) && itemPrefab.CanCharacterBuy()) if (itemPrefab.CanBeBoughtFrom(ActiveStore, out PriceInfo priceInfo) && itemPrefab.CanCharacterBuy())
{ {
bool isDailySpecial = ActiveStore.DailySpecials.Contains(itemPrefab); bool isDailySpecial = ActiveStore.DailySpecials.Contains(itemPrefab);
var itemFrame = isDailySpecial ? var itemFrame = isDailySpecial ?
storeDailySpecialsGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) : storeDailySpecialsGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
@@ -922,7 +947,8 @@ namespace Barotrauma
SetOwnedText(itemFrame); SetOwnedText(itemFrame);
SetPriceGetters(itemFrame, true); SetPriceGetters(itemFrame, true);
} }
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0 && !GetTooLowReputation(priceInfo).HasValue);
existingItemFrames.Add(itemFrame); existingItemFrames.Add(itemFrame);
} }
} }
@@ -1317,6 +1343,8 @@ namespace Barotrauma
{ {
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY) 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 ? int sortResult = itemX.ItemPrefab.Name != itemY.ItemPrefab.Name ?
itemX.ItemPrefab.Name.CompareTo(itemY.ItemPrefab.Name) : itemX.ItemPrefab.Name.CompareTo(itemY.ItemPrefab.Name) :
itemX.ItemPrefab.Identifier.CompareTo(itemY.ItemPrefab.Identifier); itemX.ItemPrefab.Identifier.CompareTo(itemY.ItemPrefab.Identifier);
@@ -1345,6 +1373,8 @@ namespace Barotrauma
{ {
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY) 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( int sortResult = ActiveStore.GetAdjustedItemSellPrice(itemX.ItemPrefab).CompareTo(
ActiveStore.GetAdjustedItemSellPrice(itemY.ItemPrefab)); ActiveStore.GetAdjustedItemSellPrice(itemY.ItemPrefab));
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; } if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
@@ -1369,6 +1399,8 @@ namespace Barotrauma
{ {
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY) 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( int sortResult = ActiveStore.GetAdjustedItemBuyPrice(itemX.ItemPrefab).CompareTo(
ActiveStore.GetAdjustedItemBuyPrice(itemY.ItemPrefab)); ActiveStore.GetAdjustedItemBuyPrice(itemY.ItemPrefab));
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; } if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
@@ -1391,10 +1423,12 @@ namespace Barotrauma
specialsGroup.Recalculate(); 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) 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); return itemX.ItemPrefab.Category.CompareTo(itemY.ItemPrefab.Category);
} }
else else
@@ -1424,6 +1458,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) static int CompareByElement(RectTransform x, RectTransform y)
{ {
if (ShouldBeOnTop(x) || ShouldBeOnBottom(y)) if (ShouldBeOnTop(x) || ShouldBeOnBottom(y))
@@ -1753,7 +1800,7 @@ namespace Barotrauma
{ {
if (pi.ItemPrefab?.InventoryIcon != null) 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) else if (pi.ItemPrefab?.Sprite != null)
{ {
@@ -1858,7 +1905,7 @@ namespace Barotrauma
LocalizedString toolTip = string.Empty; LocalizedString toolTip = string.Empty;
if (purchasedItem.ItemPrefab != null) if (purchasedItem.ItemPrefab != null)
{ {
toolTip = purchasedItem.ItemPrefab.GetTooltip(); toolTip = purchasedItem.ItemPrefab.GetTooltip(Character.Controlled);
if (itemQuantity != null) if (itemQuantity != null)
{ {
if (itemQuantity.AllNonEmpty) if (itemQuantity.AllNonEmpty)
@@ -1871,6 +1918,23 @@ namespace Barotrauma
toolTip += $"\n{TextManager.GetWithVariable("campaignstore.ownedtotal", "[amount]", itemQuantity.Total.ToString())}"; 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 = RichString.Rich(toolTip); itemComponent.ToolTip = RichString.Rich(toolTip);
} }
@@ -15,8 +15,6 @@ namespace Barotrauma
private int pageCount; private int pageCount;
private readonly bool transferService, purchaseService; private readonly bool transferService, purchaseService;
private bool initialized; private bool initialized;
private int deliveryFee;
private string deliveryLocationName;
public GUIFrame GuiFrame; public GUIFrame GuiFrame;
private GUIFrame pageIndicatorHolder; private GUIFrame pageIndicatorHolder;
@@ -34,14 +32,13 @@ namespace Barotrauma
private readonly List<SubmarineInfo> subsToShow; private readonly List<SubmarineInfo> subsToShow;
private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage]; private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
private SubmarineInfo selectedSubmarine = null; private SubmarineInfo selectedSubmarine = null;
private LocalizedString purchaseAndSwitchText, purchaseOnlyText, deliveryText, selectedSubText, switchText, missingPreviewText, currencyName; private LocalizedString purchaseAndSwitchText, purchaseOnlyText, selectedSubText, switchText, missingPreviewText, currencyName;
private readonly RectTransform parent; private readonly RectTransform parent;
private readonly Action closeAction; private readonly Action closeAction;
private Sprite pageIndicator; private Sprite pageIndicator;
private readonly LocalizedString[] messageBoxOptions; private readonly LocalizedString[] messageBoxOptions;
public const int DeliveryFeePerDistanceTravelled = 1000;
public static bool ContentRefreshRequired = false; public static bool ContentRefreshRequired = false;
private static readonly Color indicatorColor = new Color(112, 149, 129); private static readonly Color indicatorColor = new Color(112, 149, 129);
@@ -108,14 +105,9 @@ namespace Barotrauma
{ {
initialized = true; initialized = true;
selectedSubText = TextManager.Get("selectedsub"); selectedSubText = TextManager.Get("selectedsub");
deliveryText = TextManager.Get("requestdeliverybutton");
switchText = TextManager.Get("switchtosubmarinebutton"); switchText = TextManager.Get("switchtosubmarinebutton");
purchaseAndSwitchText = TextManager.Get("purchaseandswitch"); purchaseAndSwitchText = TextManager.Get("purchaseandswitch");
purchaseOnlyText = TextManager.Get("purchase"); purchaseOnlyText = TextManager.Get("purchase");
if (transferService)
{
deliveryFee = CalculateDeliveryFee();
}
currencyName = TextManager.Get("credit").Value.ToLowerInvariant(); currencyName = TextManager.Get("credit").Value.ToLowerInvariant();
@@ -124,13 +116,6 @@ namespace Barotrauma
CreateGUI(); CreateGUI();
} }
private int CalculateDeliveryFee()
{
int distanceToOutpost = GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
deliveryLocationName = endLocation.Name;
return DeliveryFeePerDistanceTravelled * distanceToOutpost;
}
private void CreateGUI() private void CreateGUI()
{ {
createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
@@ -194,7 +179,7 @@ namespace Barotrauma
confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale"); confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale");
transferInfoFrameWidth -= confirmButtonAlt.RectTransform.RelativeSize.X; transferInfoFrameWidth -= confirmButtonAlt.RectTransform.RelativeSize.X;
} }
confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseService ? purchaseAndSwitchText : deliveryFee > 0 ? deliveryText : switchText, style: "GUIButtonFreeScale"); confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseService ? purchaseAndSwitchText : switchText, style: "GUIButtonFreeScale");
SetConfirmButtonState(false); SetConfirmButtonState(false);
transferInfoFrameWidth -= confirmButton.RectTransform.RelativeSize.X; transferInfoFrameWidth -= confirmButton.RectTransform.RelativeSize.X;
GUIFrame transferInfoFrame = new GUIFrame(new RectTransform(new Vector2(transferInfoFrameWidth, 1.0f), bottomContainer.RectTransform), style: null) GUIFrame transferInfoFrame = new GUIFrame(new RectTransform(new Vector2(transferInfoFrameWidth, 1.0f), bottomContainer.RectTransform), style: null)
@@ -406,22 +391,14 @@ namespace Barotrauma
if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay)) 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); submarineDisplays[i].submarineFee.Text = TextManager.GetWithVariable("price", "[amount]", amountString);
} }
else else
{ {
if (subToDisplay.Name != CurrentOrPendingSubmarine().Name) if (subToDisplay.Name != CurrentOrPendingSubmarine().Name)
{ {
if (deliveryFee > 0) submarineDisplays[i].submarineFee.Text = string.Empty;
{
LocalizedString amountString = TextManager.FormatCurrency(deliveryFee);
submarineDisplays[i].submarineFee.Text = TextManager.GetWithVariable("deliveryfee", "[amount]", amountString);
}
else
{
submarineDisplays[i].submarineFee.Text = string.Empty;
}
} }
else else
{ {
@@ -581,7 +558,7 @@ namespace Barotrauma
if (owned) if (owned)
{ {
confirmButton.Text = deliveryFee > 0 ? deliveryText : switchText; confirmButton.Text = switchText;
confirmButton.OnClicked = (button, userData) => confirmButton.OnClicked = (button, userData) =>
{ {
ShowTransferPrompt(); ShowTransferPrompt();
@@ -702,37 +679,12 @@ namespace Barotrauma
private void ShowTransferPrompt() private void ShowTransferPrompt()
{ {
if (!GameMain.GameSession.Campaign.CanAfford(deliveryFee) && deliveryFee > 0) var text = TextManager.GetWithVariables("switchsubmarinetext",
{ ("[submarinename1]", CurrentOrPendingSubmarine().DisplayName),
new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext", ("[submarinename2]", selectedSubmarine.DisplayName));
("[currencyname]", currencyName), text += GetItemTransferText();
("[submarinename]", selectedSubmarine.DisplayName), GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), text, messageBoxOptions);
("[location1]", deliveryLocationName),
("[location2]", GameMain.GameSession.Map.CurrentLocation.Name)));
return;
}
GUIMessageBox msgBox;
if (deliveryFee > 0)
{
msgBox = new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("deliveryrequesttext",
("[submarinename1]", selectedSubmarine.DisplayName),
("[location1]", deliveryLocationName),
("[location2]", GameMain.GameSession.Map.CurrentLocation.Name),
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName),
("[amount]", deliveryFee.ToString()),
("[currencyname]", currencyName)), messageBoxOptions);
msgBox.Buttons[0].ClickSound = GUISoundType.ConfirmTransaction;
}
else
{
var text = TextManager.GetWithVariables("switchsubmarinetext",
("[submarinename1]", CurrentOrPendingSubmarine().DisplayName),
("[submarinename2]", selectedSubmarine.DisplayName));
text += GetItemTransferText();
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), text, messageBoxOptions);
}
msgBox.Buttons[0].OnClicked = (applyButton, obj) => msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{ {
@@ -777,7 +729,7 @@ namespace Barotrauma
{ {
if (GameMain.Client == null) if (GameMain.Client == null)
{ {
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch, deliveryFee); GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch);
RefreshSubmarineDisplay(true); RefreshSubmarineDisplay(true);
} }
else else
@@ -797,7 +749,9 @@ namespace Barotrauma
private void ShowBuyPrompt(bool purchaseOnly) 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", new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext",
("[currencyname]", currencyName), ("[currencyname]", currencyName),
@@ -810,7 +764,7 @@ namespace Barotrauma
{ {
var text = TextManager.GetWithVariables("purchaseandswitchsubmarinetext", var text = TextManager.GetWithVariables("purchaseandswitchsubmarinetext",
("[submarinename1]", selectedSubmarine.DisplayName), ("[submarinename1]", selectedSubmarine.DisplayName),
("[amount]", selectedSubmarine.Price.ToString()), ("[amount]", price.ToString()),
("[currencyname]", currencyName), ("[currencyname]", currencyName),
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName)); ("[submarinename2]", CurrentOrPendingSubmarine().DisplayName));
text += GetItemTransferText(); text += GetItemTransferText();
@@ -854,7 +808,7 @@ namespace Barotrauma
if (GameMain.Client == null) if (GameMain.Client == null)
{ {
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine); GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch, 0); GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch);
RefreshSubmarineDisplay(true); RefreshSubmarineDisplay(true);
} }
else else
@@ -868,7 +822,7 @@ namespace Barotrauma
{ {
msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext", msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext",
("[submarinename]", selectedSubmarine.DisplayName), ("[submarinename]", selectedSubmarine.DisplayName),
("[amount]", selectedSubmarine.Price.ToString()), ("[amount]", price.ToString()),
("[currencyname]", currencyName)) + '\n' + TextManager.Get("submarineswitchinstruction"), messageBoxOptions); ("[currencyname]", currencyName)) + '\n' + TextManager.Get("submarineswitchinstruction"), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) => msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
@@ -836,7 +836,7 @@ namespace Barotrauma
Identifier eventIdentifier = new Identifier($"{nameof(CreateWalletCrewFrame)}.{character.ID}"); Identifier eventIdentifier = new Identifier($"{nameof(CreateWalletCrewFrame)}.{character.ID}");
campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e => campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
{ {
if (!(e.Owner is Some<Character> { Value: var owner }) || owner != character) { return; } if (!e.Owner.TryUnwrap(out var owner) || owner != character) { return; }
SetWalletText(walletBlock, e.Wallet, icon, largeIcon); SetWalletText(walletBlock, e.Wallet, icon, largeIcon);
}); });
registeredEvents.Add(eventIdentifier); registeredEvents.Add(eventIdentifier);
@@ -1502,27 +1502,9 @@ namespace Barotrauma
AbsoluteSpacing = GUI.IntScale(10) 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); Sprite portrait = location.Type.GetPortrait(location.PortraitId);
bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0; bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0;
int contentWidth = missionFrameContent.Rect.Width; int contentWidth = missionFrameContent.Rect.Width;
if (hasPortrait) if (hasPortrait)
{ {
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height; float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
@@ -1534,6 +1516,30 @@ namespace Barotrauma
portraitImage.RectTransform.NonScaledSize = new Point(Math.Min((int)(portraitImage.Rect.Size.Y * portraitAspectRatio), portraitImage.Rect.Width), portraitImage.Rect.Size.Y); 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) }); 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.ContentBackground.Color = Color.Transparent;
missionList.Spacing = GUI.IntScale(15); missionList.Spacing = GUI.IntScale(15);
@@ -1545,6 +1551,7 @@ namespace Barotrauma
foreach (Mission mission in GameMain.GameSession.Missions) 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); 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) 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)
{ {
@@ -1556,7 +1563,7 @@ namespace Barotrauma
descriptionText += "\n\n" + missionMessage; descriptionText += "\n\n" + missionMessage;
} }
RichString rewardText = mission.GetMissionRewardText(Submarine.MainSub); RichString rewardText = mission.GetMissionRewardText(Submarine.MainSub);
RichString reputationText = mission.GetReputationRewardText(mission.Locations[0]); RichString reputationText = mission.GetReputationRewardText();
Func<string, string> wrapMissionText(GUIFont font) Func<string, string> wrapMissionText(GUIFont font)
{ {
@@ -727,7 +727,7 @@ namespace Barotrauma
talentStages.Add(GetTalentState(character, button.Identifier, selectedTalents)); talentStages.Add(GetTalentState(character, button.Identifier, selectedTalents));
} }
TalentStages collectiveStage = talentStages.Any(static stage => stage is Locked) TalentStages collectiveStage = talentStages.All(static stage => stage is Locked)
? Locked ? Locked
: Available; : Available;
@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
@@ -77,6 +78,8 @@ namespace Barotrauma
private PlayerBalanceElement? playerBalanceElement; private PlayerBalanceElement? playerBalanceElement;
private static ImmutableHashSet<Character> characterList = ImmutableHashSet<Character>.Empty;
/// <summary> /// <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. /// 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. /// This is to prevent us from buying another upgrade before the server has given us the new prices and causing potential syncing issues.
@@ -102,6 +105,7 @@ namespace Barotrauma
public UpgradeStore(CampaignUI campaignUI, GUIComponent parent) public UpgradeStore(CampaignUI campaignUI, GUIComponent parent)
{ {
WaitForServerUpdate = false; WaitForServerUpdate = false;
characterList = GameSession.GetSessionCrewCharacters(CharacterType.Both);
this.campaignUI = campaignUI; this.campaignUI = campaignUI;
GUIFrame upgradeFrame = new GUIFrame(rectT(1, 1, parent, Anchor.Center), style: "OuterGlow", color: Color.Black * 0.7f) GUIFrame upgradeFrame = new GUIFrame(rectT(1, 1, parent, Anchor.Center), style: "OuterGlow", color: Color.Black * 0.7f)
{ {
@@ -130,6 +134,7 @@ namespace Barotrauma
private void RefreshAll() private void RefreshAll()
{ {
characterList = GameSession.GetSessionCrewCharacters(CharacterType.Both);
switch (selectedUpgradeTab) switch (selectedUpgradeTab)
{ {
case UpgradeTab.Repairs: case UpgradeTab.Repairs:
@@ -273,7 +278,7 @@ namespace Barotrauma
new GUITextBlock(rectT(1, 0, tooltipLayout), string.Empty) { UserData = "moreindicator" }; new GUITextBlock(rectT(1, 0, tooltipLayout), string.Empty) { UserData = "moreindicator" };
ItemInfoFrame.Children.ForEach(c => { c.CanBeFocused = false; c.Children.ForEach(c2 => c2.CanBeFocused = false); }); ItemInfoFrame.Children.ForEach(c => { c.CanBeFocused = false; c.Children.ForEach(c2 => c2.CanBeFocused = false); });
GUIFrame paddedLayout = new GUIFrame(rectT(0.95f, GUI.IsFourByThree() ? 0.98f : 0.95f, parent, Anchor.Center), style: null); GUIFrame paddedLayout = new GUIFrame(rectT(0.95f, 0.95f, parent, Anchor.Center), style: null);
mainStoreLayout = new GUILayoutGroup(rectT(1, 0.9f, paddedLayout, Anchor.BottomLeft), isHorizontal: true) { RelativeSpacing = 0.01f }; mainStoreLayout = new GUILayoutGroup(rectT(1, 0.9f, paddedLayout, Anchor.BottomLeft), isHorizontal: true) { RelativeSpacing = 0.01f };
topHeaderLayout = new GUILayoutGroup(rectT(1, 0.1f, paddedLayout, Anchor.TopLeft), isHorizontal: true); topHeaderLayout = new GUILayoutGroup(rectT(1, 0.1f, paddedLayout, Anchor.TopLeft), isHorizontal: true);
@@ -295,8 +300,8 @@ namespace Barotrauma
new GUITextBlock(rectT(1.0f, 1, locationLayout), TextManager.Get("UpgradeUI.AllSubmarinesInfo"), font: GUIStyle.SmallFont, wrap: true); 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 }; 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 upgradeButton = new GUIButton(rectT(0.5f, 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 }; GUIButton repairButton = new GUIButton(rectT(0.5f, 1f, categoryButtonLayout), TextManager.Get("UICategory.Maintenance"), style: "GUITabButton") { UserData = UpgradeTab.Repairs, Selected = selectedUpgradeTab == UpgradeTab.Repairs };
/* RIGHT HEADER LAYOUT /* RIGHT HEADER LAYOUT
* |---------------------------------------------------------------------------------------------------| * |---------------------------------------------------------------------------------------------------|
@@ -347,12 +352,15 @@ namespace Barotrauma
SelectTab(UpgradeTab.Upgrade); SelectTab(UpgradeTab.Upgrade);
var itemSwapPreview = new GUICustomComponent(new RectTransform(new Vector2(0.27f, 0.4f), mainStoreLayout.RectTransform, Anchor.TopLeft) { RelativeOffset = new Vector2(GUI.IsFourByThree() ? 0.5f : 0.47f, 0.0f) }, DrawItemSwapPreview) var itemSwapPreview = new GUICustomComponent(new RectTransform(new Vector2(0.25f, 0.4f), mainStoreLayout.RectTransform, Anchor.TopLeft)
{ RelativeOffset = new Vector2(0.52f * GUI.AspectRatioAdjustment, 0.0f) }, DrawItemSwapPreview)
{ {
IgnoreLayoutGroups = true, IgnoreLayoutGroups = true,
CanBeFocused = true CanBeFocused = true
}; };
GUITextBlock.AutoScaleAndNormalize(upgradeButton.TextBlock, repairButton.TextBlock);
#if DEBUG #if DEBUG
// creates a button that re-creates the UI // creates a button that re-creates the UI
CreateRefreshButton(); CreateRefreshButton();
@@ -725,7 +733,7 @@ namespace Barotrauma
if (storeLayout == null || mainStoreLayout == null) { return; } if (storeLayout == null || mainStoreLayout == null) { return; }
currentStoreLayout = CreateUpgradeCategoryList(rectT(1.0f, 1.5f, storeLayout)); currentStoreLayout = CreateUpgradeCategoryList(rectT(1.0f, 1.5f, storeLayout));
selectedUpgradeCategoryLayout = new GUIFrame(rectT(GUI.IsFourByThree() ? 0.3f : 0.25f, 1, mainStoreLayout), style: null) { CanBeFocused = false }; selectedUpgradeCategoryLayout = new GUIFrame(rectT(0.3f * GUI.AspectRatioAdjustment, 1, mainStoreLayout), style: null) { CanBeFocused = false };
RefreshUpgradeList(); RefreshUpgradeList();
@@ -956,7 +964,7 @@ namespace Barotrauma
bool isUninstallPending = item.Prefab.SwappableItem != null && item.PendingItemSwap?.Identifier == item.Prefab.SwappableItem.ReplacementOnUninstall; bool isUninstallPending = item.Prefab.SwappableItem != null && item.PendingItemSwap?.Identifier == item.Prefab.SwappableItem.ReplacementOnUninstall;
if (isUninstallPending) { canUninstall = false; } if (isUninstallPending) { canUninstall = false; }
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), currentOrPending.UpgradePreviewSprite, frames.Add(CreateUpgradeEntry(rectT(1f, 0.35f, parent.Content), currentOrPending.UpgradePreviewSprite,
item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : TextManager.GetWithVariable("upgrades.installeditem", "[itemname]", nameWithQuantity), item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : TextManager.GetWithVariable("upgrades.installeditem", "[itemname]", nameWithQuantity),
currentOrPending.Description, currentOrPending.Description,
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton").Frame); 0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton").Frame);
@@ -996,7 +1004,7 @@ namespace Barotrauma
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count(); int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count();
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description, frames.Add(CreateUpgradeEntry(rectT(1f, 0.35f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
price, replacement, price, replacement,
addBuyButton: true, addBuyButton: true,
addProgressBar: false, addProgressBar: false,
@@ -1102,7 +1110,7 @@ namespace Barotrauma
public static UpgradeFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true) public static UpgradeFrame 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)); return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
} }
@@ -1129,7 +1137,8 @@ namespace Barotrauma
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center); GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false }; var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false };
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(1f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout)); GUILayoutGroup textLayout = new GUILayoutGroup(rectT(1f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero }; var name = new GUITextBlock(rectT(1, 0.35f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
//name.RectTransform.MinSize = new Point(0, (int)name.TextSize.Y);
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout)); GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero }; var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
GUILayoutGroup? progressLayout = null; GUILayoutGroup? progressLayout = null;
@@ -1171,7 +1180,7 @@ namespace Barotrauma
materialCostList.Visible = false; materialCostList.Visible = false;
materialCostList.UserData = UpgradeStoreUserData.MaterialCostList; materialCostList.UserData = UpgradeStoreUserData.MaterialCostList;
var priceText = new GUITextBlock(rectT(0.2f, 1f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Right) var priceText = new GUITextBlock(rectT(0.2f, 1f, buyButtonLayout), formattedPrice, textAlignment: Alignment.CenterRight)
{ {
UserData = UpgradeStoreUserData.PriceLabel, UserData = UpgradeStoreUserData.PriceLabel,
//prices on swappable items are always visible, upgrade prices are enabled in UpdateUpgradeEntry for purchasable upgrades //prices on swappable items are always visible, upgrade prices are enabled in UpdateUpgradeEntry for purchasable upgrades
@@ -1258,7 +1267,7 @@ namespace Barotrauma
{ {
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody", LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
("[upgradename]", prefab.Name), ("[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, () => currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
{ {
if (GameMain.NetworkMember != null) if (GameMain.NetworkMember != null)
@@ -1673,7 +1682,7 @@ namespace Barotrauma
GUITextBlock priceLabel = (GUITextBlock)buttonParent.FindChild(UpgradeStoreUserData.PriceLabel, recursive: true); GUITextBlock priceLabel = (GUITextBlock)buttonParent.FindChild(UpgradeStoreUserData.PriceLabel, recursive: true);
priceLabel.Visible = true; 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 (!WaitForServerUpdate) if (!WaitForServerUpdate)
{ {
@@ -163,6 +163,7 @@ namespace Barotrauma
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, bool transferItems, VoteType type) private void SetSubmarineVotingText(Client starter, SubmarineInfo info, bool transferItems, VoteType type)
{ {
int price = info.GetPrice();
string name = starter.Name; string name = starter.Name;
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab; JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White; Color nameColor = prefab != null ? prefab.UIColor : Color.White;
@@ -177,35 +178,21 @@ namespace Barotrauma
text = TextManager.GetWithVariables(tag, text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString), ("[playername]", characterRichString),
("[submarinename]", submarineRichString), ("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()), ("[amount]", price.ToString()),
("[currencyname]", TextManager.Get("credit").ToLower())); ("[currencyname]", TextManager.Get("credit").ToLower()));
break; break;
case VoteType.PurchaseSub: case VoteType.PurchaseSub:
text = TextManager.GetWithVariables("submarinepurchasevote", text = TextManager.GetWithVariables("submarinepurchasevote",
("[playername]", characterRichString), ("[playername]", characterRichString),
("[submarinename]", submarineRichString), ("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()), ("[amount]", price.ToString()),
("[currencyname]", TextManager.Get("credit").ToLower())); ("[currencyname]", TextManager.Get("credit").ToLower()));
break; break;
case VoteType.SwitchSub: case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation); tag = transferItems ? "submarineswitchwithitemsnofeevote" : "submarineswitchnofeevote";
if (deliveryFee > 0) text = TextManager.GetWithVariables(tag,
{ ("[playername]", characterRichString),
tag = transferItems ? "submarineswitchwithitemsfeevote" : "submarineswitchfeevote"; ("[submarinename]", submarineRichString));
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[locationname]", endLocation.Name),
("[amount]", deliveryFee.ToString()),
("[currencyname]", TextManager.Get("credit").ToLower()));
}
else
{
tag = transferItems ? "submarineswitchwithitemsnofeevote" : "submarineswitchnofeevote";
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString));
}
break; break;
} }
votingOnText = RichString.Rich(text); votingOnText = RichString.Rich(text);
@@ -218,6 +205,7 @@ namespace Barotrauma
private LocalizedString GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, int yesVoteCount, int noVoteCount, bool votePassed) private LocalizedString GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, int yesVoteCount, int noVoteCount, bool votePassed)
{ {
int price = info.GetPrice();
LocalizedString result = string.Empty; LocalizedString result = string.Empty;
switch (type) switch (type)
@@ -225,7 +213,7 @@ namespace Barotrauma
case VoteType.PurchaseAndSwitchSub: case VoteType.PurchaseAndSwitchSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed", result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed",
("[submarinename]", info.DisplayName), ("[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()), ("[currencyname]", TextManager.Get("credit").ToLower()),
("[yesvotecount]", yesVoteCount.ToString()), ("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]" , noVoteCount.ToString())); ("[novotecount]" , noVoteCount.ToString()));
@@ -233,31 +221,16 @@ namespace Barotrauma
case VoteType.PurchaseSub: case VoteType.PurchaseSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed", result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed",
("[submarinename]", info.DisplayName), ("[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()), ("[currencyname]", TextManager.Get("credit").ToLower()),
("[yesvotecount]", yesVoteCount.ToString()), ("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]", noVoteCount.ToString())); ("[novotecount]", noVoteCount.ToString()));
break; break;
case VoteType.SwitchSub: case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation); result = TextManager.GetWithVariables(votePassed ? "submarineswitchnofeevotepassed" : "submarineswitchnofeevotefailed",
("[submarinename]", info.DisplayName),
if (deliveryFee > 0) ("[yesvotecount]", yesVoteCount.ToString()),
{ ("[novotecount]", noVoteCount.ToString()));
result = TextManager.GetWithVariables(votePassed ? "submarineswitchfeevotepassed" : "submarineswitchfeevotefailed",
("[submarinename]", info.DisplayName),
("[locationname]", endLocation.Name),
("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", deliveryFee)),
("[currencyname]", TextManager.Get("credit").ToLower()),
("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]", noVoteCount.ToString()));
}
else
{
result = TextManager.GetWithVariables(votePassed ? "submarineswitchnofeevotepassed" : "submarineswitchnofeevotefailed",
("[submarinename]", info.DisplayName),
("[yesvotecount]", yesVoteCount.ToString()),
("[novotecount]", noVoteCount.ToString()));
}
break; break;
default: default:
break; break;
@@ -232,9 +232,8 @@ namespace Barotrauma
} }
GameSettings.Init(); GameSettings.Init();
CreatureMetrics.Init();
Md5Hash.Cache.Load();
ConsoleArguments = args; ConsoleArguments = args;
try try
@@ -747,7 +746,7 @@ namespace Barotrauma
} }
else if (HasLoaded) else if (HasLoaded)
{ {
if (ConnectCommand is Some<ConnectCommand> { Value: var connectCommand }) if (ConnectCommand.TryUnwrap(out var connectCommand))
{ {
if (Client != null) if (Client != null)
{ {
@@ -1069,6 +1068,7 @@ namespace Barotrauma
public static void QuitToMainMenu(bool save) public static void QuitToMainMenu(bool save)
{ {
CreatureMetrics.Save();
if (save) if (save)
{ {
GUI.SetSavingIndicatorState(true); GUI.SetSavingIndicatorState(true);
@@ -1174,6 +1174,7 @@ namespace Barotrauma
protected override void OnExiting(object sender, EventArgs args) protected override void OnExiting(object sender, EventArgs args)
{ {
exiting = true; exiting = true;
CreatureMetrics.Save();
DebugConsole.NewMessage("Exiting..."); DebugConsole.NewMessage("Exiting...");
Client?.Quit(); Client?.Quit();
SteamManager.ShutDown(); SteamManager.ShutDown();
@@ -9,7 +9,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Steam;
namespace Barotrauma namespace Barotrauma
{ {
@@ -194,7 +193,7 @@ namespace Barotrauma
}; };
} }
var reports = OrderPrefab.Prefabs.Where(o => o.IsReport && o.SymbolSprite != null && !o.Hidden).ToArray(); var reports = OrderPrefab.Prefabs.Where(o => o.IsReport && o.SymbolSprite != null && !o.Hidden).OrderBy(o => o.Identifier).ToArray();
if (reports.None()) if (reports.None())
{ {
DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined."); DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined.");
@@ -1403,8 +1402,7 @@ namespace Barotrauma
bool hitDeselect = PlayerInput.KeyHit(InputType.Deselect) && bool hitDeselect = PlayerInput.KeyHit(InputType.Deselect) &&
(!PlayerInput.SecondaryMouseButtonClicked() || (!isMouseOnOptionNode && !isMouseOnShortcutNode)); (!PlayerInput.SecondaryMouseButtonClicked() || (!isMouseOnOptionNode && !isMouseOnShortcutNode));
bool isBoundToPrimaryMouse = GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Command].MouseButton is MouseButton mouseButton && bool isBoundToPrimaryMouse = GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Command].MouseButton == MouseButton.PrimaryMouse;
(mouseButton == MouseButton.PrimaryMouse || mouseButton == (PlayerInput.MouseButtonsSwapped() ? MouseButton.RightMouse : MouseButton.LeftMouse));
bool canToggleInterface = !isBoundToPrimaryMouse || bool canToggleInterface = !isBoundToPrimaryMouse ||
(!isMouseOnOptionNode && !isMouseOnShortcutNode && extraOptionNodes.None(n => GUI.IsMouseOn(n)) && !GUI.IsMouseOn(returnNode)); (!isMouseOnOptionNode && !isMouseOnShortcutNode && extraOptionNodes.None(n => GUI.IsMouseOn(n)) && !GUI.IsMouseOn(returnNode));
@@ -2796,8 +2794,8 @@ namespace Barotrauma
var orderName = GetOrderNameBasedOnContextuality(order); var orderName = GetOrderNameBasedOnContextuality(order);
var icon = CreateNodeIcon(Vector2.One, node.RectTransform, order.SymbolSprite, order.Color, var icon = CreateNodeIcon(Vector2.One, node.RectTransform, order.SymbolSprite, order.Color,
tooltip: !showAssignmentTooltip ? orderName : orderName + tooltip: !showAssignmentTooltip ? orderName : orderName +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + "\n" + PlayerInput.PrimaryMouseLabel + ": " + TextManager.Get("commandui.quickassigntooltip") +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip")); "\n" + PlayerInput.SecondaryMouseLabel + ": " + TextManager.Get("commandui.manualassigntooltip"));
if (disableNode) if (disableNode)
{ {
@@ -2999,8 +2997,8 @@ namespace Barotrauma
var showAssignmentTooltip = characterContext == null && !order.MustManuallyAssign && !order.TargetAllCharacters; var showAssignmentTooltip = characterContext == null && !order.MustManuallyAssign && !order.TargetAllCharacters;
icon = CreateNodeIcon(Vector2.One, node.RectTransform, sprite, order.Color, icon = CreateNodeIcon(Vector2.One, node.RectTransform, sprite, order.Color,
tooltip: characterContext != null ? optionName : optionName + tooltip: characterContext != null ? optionName : optionName +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + "\n" + PlayerInput.PrimaryMouseLabel + ": " + TextManager.Get("commandui.quickassigntooltip") +
"\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip")); "\n" + PlayerInput.SecondaryMouseLabel + ": " + TextManager.Get("commandui.manualassigntooltip"));
} }
if (!CanCharacterBeHeard()) if (!CanCharacterBeHeard())
{ {
@@ -1,9 +1,7 @@
using System; using Microsoft.Xna.Framework;
using System.Collections.Generic; using Microsoft.Xna.Framework.Graphics;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma namespace Barotrauma
{ {
@@ -11,21 +9,22 @@ namespace Barotrauma
{ {
private const int MaxDrawnElements = 12; 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; 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(keyStartsWith)).ToDictionary(i => i.Key, i => i.Value);
{
campaignData = campaignData.Where(pair => !pair.Key.StartsWith(ignored)).ToDictionary(i => i.Key, i => i.Value);
}
} }
int offset = 0;; int offset = 0;;
if (campaignData.Count > 0) if (campaignData.Count > 0)
{ {
offset = debugDrawMetadataOffset % campaignData.Count; offset = debugDrawMetaData.Offset % campaignData.Count;
if (offset < 0) { offset += campaignData.Count; } if (offset < 0) { offset += campaignData.Count; }
} }
@@ -72,7 +71,7 @@ namespace Barotrauma
} }
float y = infoRect.Bottom + 16; float y = infoRect.Bottom + 16;
if (Campaign.Factions != null) if (campaign.Factions != null)
{ {
const string factionHeader = "Reputations"; const string factionHeader = "Reputations";
Vector2 factionHeaderSize = GUIStyle.SubHeadingFont.MeasureString(factionHeader); Vector2 factionHeaderSize = GUIStyle.SubHeadingFont.MeasureString(factionHeader);
@@ -81,7 +80,7 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, factionPos, factionHeader, Color.White, font: GUIStyle.SubHeadingFont); GUI.DrawString(spriteBatch, factionPos, factionHeader, Color.White, font: GUIStyle.SubHeadingFont);
y += factionHeaderSize.Y + 8; y += factionHeaderSize.Y + 8;
foreach (Faction faction in Campaign.Factions) foreach (Faction faction in campaign.Factions)
{ {
LocalizedString name = faction.Prefab.Name; LocalizedString name = faction.Prefab.Name;
Vector2 nameSize = GUIStyle.SmallFont.MeasureString(name); Vector2 nameSize = GUIStyle.SmallFont.MeasureString(name);
@@ -94,20 +93,6 @@ namespace Barotrauma
y += 15; 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);
}
} }
} }
} }
@@ -13,7 +13,7 @@ namespace Barotrauma
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged) partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged)
{ {
if (Owner is Some<Character> { Value: var character }) if (Owner.TryUnwrap(out var character))
{ {
if (!character.IsPlayer) { return; } if (!character.IsPlayer) { return; }
} }
@@ -1,5 +1,4 @@
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking; using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
@@ -16,8 +15,6 @@ namespace Barotrauma
protected bool crewDead; protected bool crewDead;
protected Color overlayColor; protected Color overlayColor;
protected LocalizedString overlayText, overlayTextBottom;
protected Color overlayTextColor;
protected Sprite overlaySprite; protected Sprite overlaySprite;
private TransitionType prevCampaignUIAutoOpenType; private TransitionType prevCampaignUIAutoOpenType;
@@ -30,7 +27,13 @@ namespace Barotrauma
protected GUIFrame campaignUIContainer; protected GUIFrame campaignUIContainer;
public CampaignUI CampaignUI; public CampaignUI CampaignUI;
public static CancellationTokenSource StartRoundCancellationToken { get; private set; } public SlideshowPlayer SlideshowPlayer
{
get;
protected set;
}
private CancellationTokenSource startRoundCancellationToken;
public bool ForceMapUI public bool ForceMapUI
{ {
@@ -59,10 +62,19 @@ namespace Barotrauma
{ {
chatBox.ToggleOpen = wasChatBoxOpen; chatBox.ToggleOpen = wasChatBoxOpen;
} }
if (!value && CampaignUI?.SelectedTab == InteractionType.PurchaseSub) if (!value)
{ {
SubmarinePreview.Close(); switch (CampaignUI?.SelectedTab)
{
case InteractionType.PurchaseSub:
SubmarinePreview.Close();
break;
case InteractionType.MedicalClinic:
CampaignUI.MedicalClinic?.OnDeselected();
break;
}
} }
showCampaignUI = value; showCampaignUI = value;
} }
} }
@@ -77,6 +89,7 @@ namespace Barotrauma
{ {
foreach (Mission mission in Missions.ToList()) foreach (Mission mission in Missions.ToList())
{ {
if (!mission.Prefab.ShowStartMessage) { continue; }
new GUIMessageBox( new GUIMessageBox(
RichString.Rich(mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name), 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) RichString.Rich(mission.Description), Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon)
@@ -123,32 +136,10 @@ namespace Barotrauma
{ {
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), overlayColor, isFilled: true); 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")) if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
{ {
endRoundButton.Visible = false; endRoundButton.Visible = false;
@@ -188,6 +179,7 @@ namespace Barotrauma
case TransitionType.None: case TransitionType.None:
default: default:
if (Level.Loaded.Type == LevelData.LevelType.Outpost && 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))) (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]"); buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
@@ -259,11 +251,11 @@ namespace Barotrauma
GUI.ClearCursorWait(); GUI.ClearCursorWait();
StartRoundCancellationToken = new CancellationTokenSource(); startRoundCancellationToken = new CancellationTokenSource();
var loadTask = Task.Run(async () => var loadTask = Task.Run(async () =>
{ {
await Task.Yield(); await Task.Yield();
Rand.ThreadId = Thread.CurrentThread.ManagedThreadId; Rand.ThreadId = Environment.CurrentManagedThreadId;
try try
{ {
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost()); GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost());
@@ -273,7 +265,8 @@ namespace Barotrauma
roundSummaryScreen.LoadException = e; roundSummaryScreen.LoadException = e;
} }
Rand.ThreadId = 0; Rand.ThreadId = 0;
}, StartRoundCancellationToken.Token); startRoundCancellationToken = null;
}, startRoundCancellationToken.Token);
TaskPool.Add("AsyncCampaignStartRound", loadTask, (t) => TaskPool.Add("AsyncCampaignStartRound", loadTask, (t) =>
{ {
overlayColor = Color.Transparent; overlayColor = Color.Transparent;
@@ -283,6 +276,21 @@ namespace Barotrauma
return loadTask; return loadTask;
} }
public void CancelStartRound()
{
startRoundCancellationToken?.Cancel();
}
public void ThrowIfStartRoundCancellationRequested()
{
if (startRoundCancellationToken != null &&
startRoundCancellationToken.Token.IsCancellationRequested)
{
startRoundCancellationToken.Token.ThrowIfCancellationRequested();
startRoundCancellationToken = null;
}
}
protected SubmarineInfo GetPredefinedStartOutpost() protected SubmarineInfo GetPredefinedStartOutpost()
{ {
if (Map?.CurrentLocation?.Type?.GetForcedOutpostGenerationParams() is OutpostGenerationParams parameters && !parameters.OutpostFilePath.IsNullOrEmpty()) if (Map?.CurrentLocation?.Type?.GetForcedOutpostGenerationParams() is OutpostGenerationParams parameters && !parameters.OutpostFilePath.IsNullOrEmpty())
@@ -316,7 +324,7 @@ namespace Barotrauma
goto default; goto default;
default: default:
ShowCampaignUI = true; ShowCampaignUI = true;
CampaignUI.SelectTab(npc.CampaignInteractionType, storeIdentifier: npc.MerchantIdentifier); CampaignUI.SelectTab(npc.CampaignInteractionType, npc);
CampaignUI.UpgradeStore?.RequestRefresh(); CampaignUI.UpgradeStore?.RequestRefresh();
break; break;
} }
@@ -216,51 +216,35 @@ namespace Barotrauma
} }
Character prevControlled = Character.Controlled; Character prevControlled = Character.Controlled;
if (prevControlled?.AIController != null)
{
prevControlled.AIController.Enabled = false;
}
GUI.DisableHUD = true; GUI.DisableHUD = true;
if (IsFirstRound) 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(); prevControlled?.ClearInputs();
overlayColor = Color.LightGray; overlayColor = Color.LightGray;
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId); 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 outpost = GameMain.GameSession.Level.StartOutpost;
var borders = outpost.GetDockedBorders(); var borders = outpost.GetDockedBorders();
borders.Location += outpost.WorldPosition.ToPoint(); borders.Location += outpost.WorldPosition.ToPoint();
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2); GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
float startZoom = 0.8f / float startZoom = 0.8f /
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X); ((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, var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
null, null, null, null,
fadeOut: false, fadeOut: false,
@@ -272,16 +256,6 @@ namespace Barotrauma
AllowInterrupt = true, AllowInterrupt = true,
RemoveControlFromCharacter = false 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; overlayColor = Color.Transparent;
while (transition.Running) while (transition.Running)
{ {
@@ -409,6 +383,8 @@ namespace Barotrauma
base.Update(deltaTime); base.Update(deltaTime);
SlideshowPlayer?.UpdateManually(deltaTime);
if (PlayerInput.SecondaryMouseButtonClicked() || if (PlayerInput.SecondaryMouseButtonClicked() ||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{ {
@@ -442,7 +418,8 @@ namespace Barotrauma
CampaignUI.SelectTab(InteractionType.Map); 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?) //wasn't initially docked (sub doesn't have a docking port?)
// -> choose a destination when the sub is far enough from the start outpost // -> 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) public override void End(TransitionType transitionType = TransitionType.None)
{ {
base.End(transitionType); base.End(transitionType);
ForceMapUI = ShowCampaignUI = false; ForceMapUI = ShowCampaignUI = false;
SlideshowPlayer?.Finish();
// remove all event dialogue boxes // remove all event dialogue boxes
GUIMessageBox.MessageBoxes.ForEachMod(mb => GUIMessageBox.MessageBoxes.ForEachMod(mb =>
{ {
@@ -501,7 +484,8 @@ namespace Barotrauma
{ {
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox); GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
} }
CoroutineManager.StartCoroutine(DoEndCampaignCameraTransition(), "DoEndCampaignCameraTransition"); GameMain.CampaignEndScreen.Select();
GUI.DisableHUD = false;
GameMain.CampaignEndScreen.OnFinished = () => GameMain.CampaignEndScreen.OnFinished = () =>
{ {
GameMain.NetLobbyScreen.Select(); 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) public void ClientWrite(IWriteMessage msg)
{ {
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue); System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
@@ -844,8 +802,6 @@ namespace Barotrauma
{ {
DebugConsole.Log("Received campaign update (Reputation)"); DebugConsole.Log("Received campaign update (Reputation)");
UInt16 id = msg.ReadUInt16(); UInt16 id = msg.ReadUInt16();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>(); Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
byte factionsCount = msg.ReadByte(); byte factionsCount = msg.ReadByte();
for (int i = 0; i < factionsCount; i++) for (int i = 0; i < factionsCount; i++)
@@ -854,11 +810,6 @@ namespace Barotrauma
} }
if (ShouldApply(NetFlags.Reputation, id, requireUpToDateSave: true)) 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) foreach (var (identifier, rep) in factionReps)
{ {
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier); Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
@@ -871,6 +822,7 @@ namespace Barotrauma
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\"."); DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
} }
} }
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
} }
} }
if (requiredFlags.HasFlag(NetFlags.CharacterInfo)) if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
@@ -1010,25 +962,20 @@ namespace Barotrauma
foreach (NetWalletTransaction transaction in update.Transactions) foreach (NetWalletTransaction transaction in update.Transactions)
{ {
WalletInfo info = transaction.Info; WalletInfo info = transaction.Info;
switch (transaction.CharacterID) if (transaction.CharacterID.TryUnwrap(out var charID))
{ {
case Some<ushort> { Value: var charID }: Character targetCharacter = Character.CharacterList?.FirstOrDefault(c => c.ID == charID);
{ if (targetCharacter is null) { break; }
Character targetCharacter = Character.CharacterList?.FirstOrDefault(c => c.ID == charID); Wallet wallet = targetCharacter.Wallet;
if (targetCharacter is null) { break; }
Wallet wallet = targetCharacter.Wallet;
wallet.Balance = info.Balance; wallet.Balance = info.Balance;
wallet.RewardDistribution = info.RewardDistribution; wallet.RewardDistribution = info.RewardDistribution;
TryInvokeEvent(wallet, transaction.ChangedData, info); TryInvokeEvent(wallet, transaction.ChangedData, info);
break; }
} else
case None<ushort> _: {
{ Bank.Balance = info.Balance;
Bank.Balance = info.Balance; TryInvokeEvent(Bank, transaction.ChangedData, info);
TryInvokeEvent(Bank, transaction.ChangedData, info);
break;
}
} }
} }
@@ -1043,7 +990,7 @@ namespace Barotrauma
public override bool TryPurchase(Client client, int price) public override bool TryPurchase(Client client, int price)
{ {
if (!AllowedToManageCampaign(ClientPermissions.ManageCampaign)) if (!AllowedToManageCampaign(ClientPermissions.ManageMoney))
{ {
return PersonalWallet.TryDeduct(price); return PersonalWallet.TryDeduct(price);
} }
@@ -12,7 +12,13 @@ namespace Barotrauma
public override bool Paused 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) public override void UpdateWhilePaused(float deltaTime)
@@ -31,6 +37,8 @@ namespace Barotrauma
} }
} }
SlideshowPlayer?.UpdateManually(deltaTime);
CrewManager.ChatBox?.Update(deltaTime); CrewManager.ChatBox?.Update(deltaTime);
CrewManager.UpdateReports(); CrewManager.UpdateReports();
} }
@@ -77,9 +85,9 @@ namespace Barotrauma
/// </summary> /// </summary>
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings) private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings)
{ {
CampaignMetadata = new CampaignMetadata(this);
UpgradeManager = new UpgradeManager(this); UpgradeManager = new UpgradeManager(this);
Settings = settings; Settings = settings;
InitFactions();
map = new Map(this, mapSeed); map = new Map(this, mapSeed);
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs) foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{ {
@@ -89,7 +97,6 @@ namespace Barotrauma
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant)); CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant));
} }
} }
InitCampaignData();
InitUI(); InitUI();
} }
@@ -99,6 +106,17 @@ namespace Barotrauma
private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign, CampaignSettings.Empty) private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign, CampaignSettings.Empty)
{ {
IsFirstRound = false; IsFirstRound = false;
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "metadata":
CampaignMetadata.Load(subElement);
break;
}
}
InitFactions();
foreach (var subElement in element.Elements()) foreach (var subElement in element.Elements())
{ {
@@ -114,9 +132,6 @@ namespace Barotrauma
case "map": case "map":
map = Map.Load(this, subElement); map = Map.Load(this, subElement);
break; break;
case "metadata":
CampaignMetadata = new CampaignMetadata(this, subElement);
break;
case "cargo": case "cargo":
CargoManager.LoadPurchasedItems(subElement); CargoManager.LoadPurchasedItems(subElement);
break; break;
@@ -136,11 +151,8 @@ namespace Barotrauma
} }
} }
CampaignMetadata ??= new CampaignMetadata(this);
UpgradeManager ??= new UpgradeManager(this); UpgradeManager ??= new UpgradeManager(this);
InitCampaignData();
InitUI(); InitUI();
//backwards compatibility for saves made prior to the addition of personal wallets //backwards compatibility for saves made prior to the addition of personal wallets
@@ -265,7 +277,6 @@ namespace Barotrauma
private IEnumerable<CoroutineStatus> DoLoadInitialLevel(LevelData level, bool mirror) private IEnumerable<CoroutineStatus> DoLoadInitialLevel(LevelData level, bool mirror)
{ {
GameMain.GameSession.StartRound(level, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost()); GameMain.GameSession.StartRound(level, mirrorLevel: mirror, startOutpost: GetPredefinedStartOutpost());
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
@@ -296,34 +307,9 @@ namespace Barotrauma
if (IsFirstRound || showCampaignResetText) if (IsFirstRound || showCampaignResetText)
{ {
overlayColor = Color.LightGray; if (SlideshowPrefab.Prefabs.TryGet("campaignstart".ToIdentifier(), out var slideshow))
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 (timer > fadeInDuration) SlideshowPlayer = new SlideshowPlayer(GUICanvas.Instance, slideshow);
{
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;
} }
var outpost = GameMain.GameSession.Level.StartOutpost; var outpost = GameMain.GameSession.Level.StartOutpost;
var borders = outpost.GetDockedBorders(); var borders = outpost.GetDockedBorders();
@@ -331,7 +317,13 @@ namespace Barotrauma
GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2); GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
float startZoom = 0.8f / float startZoom = 0.8f /
((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X); ((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, var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
null, null, null, null,
fadeOut: false, fadeOut: false,
@@ -343,17 +335,6 @@ namespace Barotrauma
AllowInterrupt = true, AllowInterrupt = true,
RemoveControlFromCharacter = false 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) while (transition.Running)
{ {
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -440,61 +421,68 @@ namespace Barotrauma
TotalPassedLevels++; TotalPassedLevels++;
break; break;
case TransitionType.ProgressToNextEmptyLocation: case TransitionType.ProgressToNextEmptyLocation:
Map.Visit(Map.CurrentLocation);
TotalPassedLevels++; TotalPassedLevels++;
break; break;
case TransitionType.End:
EndCampaign();
IsFirstRound = true;
break;
} }
Map.ProgressWorld(transitionType, GameMain.GameSession.RoundDuration); Map.ProgressWorld(this, transitionType, GameMain.GameSession.RoundDuration);
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
transitionType == TransitionType.LeaveLocation ? Alignment.BottomCenter : Alignment.Center,
fadeOut: false,
panDuration: EndTransitionDuration);
GUI.ClearMessages(); 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 (transitionType != TransitionType.End)
if (success)
{ {
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine); var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
SaveUtil.SaveGame(GameMain.GameSession.SavePath); transitionType == TransitionType.LeaveLocation ? Alignment.BottomCenter : Alignment.Center,
} fadeOut: false,
else panDuration: EndTransitionDuration);
{
PendingSubmarineSwitch = null;
EnableRoundSummaryGameOverState();
}
CrewManager?.ClearCurrentOrders(); Location portraitLocation = Map.SelectedLocation ?? Map.CurrentLocation;
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
//-------------------------------------- float fadeOutDuration = endTransition.PanDuration;
float t = 0.0f;
SelectSummaryScreen(roundSummary, newLevel, mirror, () => while (t < fadeOutDuration || endTransition.Running)
{
GameMain.GameScreen.Select();
if (continueButton != null)
{ {
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; CrewManager?.ClearCurrentOrders();
GUI.ClearCursorWait();
overlayColor = Color.Transparent; SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
}); {
GameMain.GameScreen.Select();
if (continueButton != null)
{
continueButton.Visible = true;
}
GUI.DisableHUD = false;
GUI.ClearCursorWait();
overlayColor = Color.Transparent;
});
}
GUI.SetSavingIndicatorState(false); GUI.SetSavingIndicatorState(false);
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
@@ -502,7 +490,10 @@ namespace Barotrauma
protected override void EndCampaignProjSpecific() 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 = () => GameMain.CampaignEndScreen.OnFinished = () =>
{ {
showCampaignResetText = true; showCampaignResetText = true;
@@ -511,39 +502,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) public override void Update(float deltaTime)
{ {
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; } if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition") || gameOver) { return; }
base.Update(deltaTime); base.Update(deltaTime);
SlideshowPlayer?.UpdateManually(deltaTime);
Map?.Radiation?.UpdateRadiation(deltaTime); Map?.Radiation?.UpdateRadiation(deltaTime);
if (PlayerInput.SecondaryMouseButtonClicked() || if (PlayerInput.SecondaryMouseButtonClicked() ||
@@ -594,11 +560,19 @@ namespace Barotrauma
CampaignUI.SelectTab(InteractionType.Map); CampaignUI.SelectTab(InteractionType.Map);
} }
} }
else if (Level.Loaded.IsEndBiome)
{
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub);
if (transitionType == TransitionType.ProgressToNextLocation)
{
LoadNewLevel();
}
}
else else
{ {
//wasn't initially docked (sub doesn't have a docking port?) //wasn't initially docked (sub doesn't have a docking port?)
// -> choose a destination when the sub is far enough from the start outpost // -> 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; ForceMapUI = true;
CampaignUI.SelectTab(InteractionType.Map); CampaignUI.SelectTab(InteractionType.Map);
@@ -608,11 +582,11 @@ namespace Barotrauma
else else
{ {
var transitionType = GetAvailableTransition(out _, out Submarine leavingSub); 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)) Level.Loaded.EndOutpost != null && Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
{ {
LoadNewLevel(); LoadNewLevel();
@@ -585,7 +585,7 @@ namespace Barotrauma
if (!gap.IsRoomToRoom) if (!gap.IsRoomToRoom)
{ {
if (!IsWearingDivingSuit()) { continue; } if (!IsWearingDivingSuit()) { continue; }
if (Character.Controlled.IsProtectedFromPressure()) { continue; } if (Character.Controlled.IsProtectedFromPressure) { continue; }
if (DisplayHint("divingsuitwarning".ToIdentifier(), extendTextTag: false)) { return; } if (DisplayHint("divingsuitwarning".ToIdentifier(), extendTextTag: false)) { return; }
continue; continue;
} }
@@ -11,6 +11,8 @@ namespace Barotrauma
{ {
internal sealed partial class MedicalClinic internal sealed partial class MedicalClinic
{ {
private MedicalClinicUI? ui => campaign?.CampaignUI?.MedicalClinic;
public enum RequestResult public enum RequestResult
{ {
Undecided, Undecided,
@@ -303,6 +305,12 @@ namespace Barotrauma
} }
} }
private void AfflictionUpdateReceived(IReadMessage inc)
{
NetCrewMember crewMember = INetSerializableStruct.Read<NetCrewMember>(inc);
ui?.UpdateAfflictions(crewMember);
}
private void PendingRequestReceived(IReadMessage inc) private void PendingRequestReceived(IReadMessage inc)
{ {
var pendingCrew = INetSerializableStruct.Read<NetCollection<NetCrewMember>>(inc); var pendingCrew = INetSerializableStruct.Read<NetCollection<NetCrewMember>>(inc);
@@ -312,6 +320,10 @@ namespace Barotrauma
} }
} }
public static void SendUnsubscribeRequest() => ClientSend(null,
header: NetworkHeader.UNSUBSCRIBE_ME,
deliveryMethod: DeliveryMethod.Reliable);
private static IWriteMessage StartSending() private static IWriteMessage StartSending()
{ {
IWriteMessage writeMessage = new WriteOnlyMessage(); IWriteMessage writeMessage = new WriteOnlyMessage();
@@ -337,6 +349,9 @@ namespace Barotrauma
case NetworkHeader.REQUEST_AFFLICTIONS: case NetworkHeader.REQUEST_AFFLICTIONS:
AfflictionRequestReceived(inc); AfflictionRequestReceived(inc);
break; break;
case NetworkHeader.AFFLICTION_UPDATE:
AfflictionUpdateReceived(inc);
break;
case NetworkHeader.REQUEST_PENDING: case NetworkHeader.REQUEST_PENDING:
PendingRequestReceived(inc); PendingRequestReceived(inc);
break; break;
@@ -40,7 +40,7 @@ namespace Barotrauma
private void CreateMessageBox(string author) private void CreateMessageBox(string author)
{ {
Vector2 relativeSize = new Vector2(GUI.IsFourByThree() ? 0.3f : 0.2f, 0.15f); Vector2 relativeSize = new Vector2(0.3f * GUI.AspectRatioAdjustment, 0.15f);
Point minSize = new Point(300, 200); Point minSize = new Point(300, 200);
msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true }; msgBox = new GUIMessageBox(readyCheckHeader, readyCheckBody(author), new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable = true };
@@ -21,8 +21,7 @@ namespace Barotrauma
private readonly GameMode gameMode; private readonly GameMode gameMode;
private readonly float initialLocationReputation; private readonly Dictionary<Identifier, float> initialFactionReputations = new Dictionary<Identifier, float>();
private readonly Dictionary<Faction, float> initialFactionReputations = new Dictionary<Faction, float>();
public GUILayoutGroup ButtonArea { get; private set; } public GUILayoutGroup ButtonArea { get; private set; }
@@ -36,12 +35,11 @@ namespace Barotrauma
this.selectedMissions = selectedMissions.ToList(); this.selectedMissions = selectedMissions.ToList();
this.startLocation = startLocation; this.startLocation = startLocation;
this.endLocation = endLocation; this.endLocation = endLocation;
initialLocationReputation = startLocation?.Reputation?.Value ?? 0.0f;
if (gameMode is CampaignMode campaignMode) if (gameMode is CampaignMode campaignMode)
{ {
foreach (Faction faction in campaignMode.Factions) foreach (Faction faction in campaignMode.Factions)
{ {
initialFactionReputations.Add(faction, faction.Reputation.Value); initialFactionReputations.Add(faction.Prefab.Identifier, faction.Reputation.Value);
} }
} }
} }
@@ -214,11 +212,12 @@ namespace Barotrauma
Stretch = true 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) if (!selectedMissions.Any() && startLocation != null)
{ {
foreach (Mission mission in startLocation.SelectedMissions) foreach (Mission mission in startLocation.SelectedMissions)
{ {
if (!mission.Prefab.ShowInMenus) { continue; }
if (mission.Locations[0] == mission.Locations[1] || if (mission.Locations[0] == mission.Locations[1] ||
mission.Locations.Contains(campaignMode?.Map.SelectedLocation)) mission.Locations.Contains(campaignMode?.Map.SelectedLocation))
{ {
@@ -312,18 +311,27 @@ namespace Barotrauma
} }
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
RichString.Rich(missionMessage), wrap: true); RichString.Rich(missionMessage), wrap: true);
int reward = displayedMission.GetReward(Submarine.MainSub); if (selectedMissions.Contains(displayedMission) && displayedMission.Completed)
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0)
{ {
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub))); RichString reputationText = displayedMission.GetReputationRewardText();
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled) if (!reputationText.IsNullOrEmpty())
{ {
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(reward)); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), reputationText);
if (share > 0) }
int totalReward = displayedMission.GetFinalReward(Submarine.MainSub);
if (totalReward > 0)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled)
{ {
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share); var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(totalReward));
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}"))); if (share > 0)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString); {
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
}
} }
} }
} }
@@ -401,33 +409,17 @@ namespace Barotrauma
}; };
reputationList.ContentBackground.Color = Color.Transparent; reputationList.ContentBackground.Color = Color.Transparent;
if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
{
var iconStyle = GUIStyle.GetComponentStyle("LocationReputationIcon");
var locationFrame = CreateReputationElement(
reputationList.Content,
startLocation.Name,
startLocation.Reputation.Value, startLocation.Reputation.NormalizedValue, initialLocationReputation,
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)) foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
{ {
float initialReputation = faction.Reputation.Value; float initialReputation = faction.Reputation.Value;
if (initialFactionReputations.ContainsKey(faction)) if (!initialFactionReputations.TryGetValue(faction.Prefab.Identifier, out initialReputation))
{
initialReputation = initialFactionReputations[faction];
}
else
{ {
DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round)."); DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round).");
} }
var factionFrame = CreateReputationElement( var factionFrame = CreateReputationElement(
reputationList.Content, reputationList.Content,
faction.Prefab.Name, faction.Prefab.Name,
faction.Reputation.Value, faction.Reputation.NormalizedValue, initialReputation, faction.Reputation, initialReputation,
faction.Prefab.ShortDescription, faction.Prefab.Description, faction.Prefab.ShortDescription, faction.Prefab.Description,
faction.Prefab.Icon, faction.Prefab.BackgroundPortrait, faction.Prefab.IconColor); faction.Prefab.Icon, faction.Prefab.BackgroundPortrait, faction.Prefab.IconColor);
CreatePathUnlockElement(factionFrame, faction, null); CreatePathUnlockElement(factionFrame, faction, null);
@@ -455,52 +447,60 @@ namespace Barotrauma
void CreatePathUnlockElement(GUIComponent reputationFrame, Faction faction, Location location) void CreatePathUnlockElement(GUIComponent reputationFrame, Faction faction, Location location)
{ {
if (GameMain.GameSession?.Campaign?.Map != null) if (GameMain.GameSession?.Campaign?.Map == null) { return; }
IEnumerable<LocationConnection> connectionsBetweenBiomes =
GameMain.GameSession.Campaign.Map.Connections.Where(c => c.Locations[0].Biome != c.Locations[1].Biome);
foreach (LocationConnection connection in connectionsBetweenBiomes)
{ {
foreach (LocationConnection connection in GameMain.GameSession.Campaign.Map.Connections) if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
//don't show the "reputation required to unlock" text if another connection between the biomes has already been unlocked
if (connectionsBetweenBiomes.Where(c => !c.Locked).Any(c =>
(c.Locations[0].Biome == connection.Locations[0].Biome && c.Locations[1].Biome == connection.Locations[1].Biome) ||
(c.Locations[1].Biome == connection.Locations[0].Biome && c.Locations[0].Biome == connection.Locations[1].Biome)))
{ {
if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; } continue;
}
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1]; var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
var unlockEvent = var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == gateLocation.LevelData.Biome.Identifier) ??
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == Identifier.Empty);
if (unlockEvent == null) { continue; } 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.Faction) { continue; }
}
if (unlockEvent != null)
{
Reputation unlockReputation = gateLocation.Reputation;
Faction unlockFaction = null;
if (!unlockEvent.Faction.IsEmpty)
{ {
if (location == null || gateLocation != location) { continue; } unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
unlockReputation = unlockFaction?.Reputation;
} }
else float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
"lockedpathreputationrequirement",
("[reputation]", Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true)),
("[biomename]", $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖")));
var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUIStyle.TextColorNormal);
unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
unlockInfoPanel.UserData = "unlockinfo";
if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
{ {
if (faction == null || faction.Prefab.Identifier != unlockEvent.UnlockPathFaction) { continue; } unlockInfoPanel.Font = GUIStyle.SmallFont;
}
if (unlockEvent != null)
{
Reputation unlockReputation = gateLocation.Reputation;
Faction unlockFaction = null;
if (!string.IsNullOrEmpty(unlockEvent.UnlockPathFaction))
{
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.UnlockPathFaction);
unlockReputation = unlockFaction?.Reputation;
}
float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
"lockedpathreputationrequirement",
("[reputation]", Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true)),
("[biomename]", $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖")));
var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUIStyle.TextColorNormal);
unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
unlockInfoPanel.UserData = "unlockinfo";
if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
{
unlockInfoPanel.Font = GUIStyle.SmallFont;
}
} }
} }
} }
} }
} }
@@ -543,6 +543,11 @@ namespace Barotrauma
} }
} }
if (startLocation?.Biome != null && startLocation.Biome.IsEndBiome)
{
locationName ??= startLocation.Name;
}
if (textTag == null) { return ""; } if (textTag == null) { return ""; }
if (locationName == null) if (locationName == null)
@@ -680,7 +685,7 @@ namespace Barotrauma
} }
private GUIFrame CreateReputationElement(GUIComponent parent, private GUIFrame CreateReputationElement(GUIComponent parent,
LocalizedString name, float reputation, float normalizedReputation, float initialReputation, LocalizedString name, Reputation reputation, float initialReputation,
LocalizedString shortDescription, LocalizedString fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor) LocalizedString shortDescription, LocalizedString fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor)
{ {
var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), style: null); var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), style: null);
@@ -698,21 +703,22 @@ namespace Barotrauma
}; };
} }
var factionInfoHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), factionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft, isHorizontal: true) var factionInfoHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), factionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterRight, isHorizontal: true)
{ {
AbsoluteSpacing = GUI.IntScale(5), AbsoluteSpacing = GUI.IntScale(5),
Stretch = true Stretch = true
}; };
var factionIcon = new GUIImage(new RectTransform(Vector2.One * 0.7f, factionInfoHorizontal.RectTransform, scaleBasis: ScaleBasis.Smallest), icon, scaleToFit: true)
{
Color = iconColor
};
var factionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, factionInfoHorizontal.RectTransform)) var factionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, factionInfoHorizontal.RectTransform))
{ {
AbsoluteSpacing = GUI.IntScale(10), AbsoluteSpacing = GUI.IntScale(10),
Stretch = true Stretch = true
}; };
var factionIcon = new GUIImage(new RectTransform(Vector2.One * 0.7f, factionInfoHorizontal.RectTransform, scaleBasis: ScaleBasis.Smallest), icon, scaleToFit: true)
{
Color = iconColor
};
factionInfoHorizontal.Recalculate(); factionInfoHorizontal.Recalculate();
var header = new GUITextBlock(new RectTransform(new Point(factionTextContent.Rect.Width, GUI.IntScale(40)), factionTextContent.RectTransform), var header = new GUITextBlock(new RectTransform(new Point(factionTextContent.Rect.Width, GUI.IntScale(40)), factionTextContent.RectTransform),
@@ -733,24 +739,30 @@ namespace Barotrauma
factionTextContent.Recalculate(); factionTextContent.Recalculate();
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform), new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform),
onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, normalizedReputation)); onDraw: (sb, customComponent) => DrawReputationBar(sb, customComponent.Rect, reputation.NormalizedValue));
var reputationText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
string.Empty, textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont);
SetReputationText(reputationText);
reputation?.OnReputationValueChanged.RegisterOverwriteExisting("RefreshRoundSummary".ToIdentifier(), _ =>
{
SetReputationText(reputationText);
});
LocalizedString reputationText = Reputation.GetFormattedReputationText(normalizedReputation, reputation, addColorTags: true); void SetReputationText(GUITextBlock textBlock)
int reputationChange = (int)Math.Round(reputation - initialReputation);
if (Math.Abs(reputationChange) > 0)
{ {
string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}"; LocalizedString reputationText = Reputation.GetFormattedReputationText(reputation.NormalizedValue, reputation.Value, addColorTags: true);
string colorStr = XMLExtensions.ToStringHex(reputationChange > 0 ? GUIStyle.Green : GUIStyle.Red); int reputationChange = (int)Math.Round(reputation.Value - initialReputation);
var richText = RichString.Rich($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)"); if (Math.Abs(reputationChange) > 0)
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform), {
richText, string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}";
textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont); string colorStr = XMLExtensions.ToStringHex(reputationChange > 0 ? GUIStyle.Green : GUIStyle.Red);
} textBlock.Text = RichString.Rich($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)");
else }
{ else
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform), {
RichString.Rich(reputationText), textBlock.Text = RichString.Rich(reputationText);
textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont); }
} }
//spacing //spacing
@@ -63,7 +63,6 @@ namespace Barotrauma
public Vector2[] SlotPositions; public Vector2[] SlotPositions;
public static Point SlotSize; public static Point SlotSize;
public static int Spacing;
private Layout layout; private Layout layout;
public Layout CurrentLayout public Layout CurrentLayout
@@ -103,7 +102,7 @@ namespace Barotrauma
{ {
visualSlots ??= new VisualSlot[capacity]; visualSlots ??= new VisualSlot[capacity];
float multiplier = !GUI.IsFourByThree() ? UIScale : UIScale * 0.925f; float multiplier = UIScale * GUI.AspectRatioAdjustment;
for (int i = 0; i < capacity; i++) for (int i = 0; i < capacity; i++)
{ {
@@ -219,18 +218,11 @@ namespace Barotrauma
private void SetSlotPositions(Layout layout) private void SetSlotPositions(Layout layout)
{ {
bool isFourByThree = GUI.IsFourByThree(); int spacing = GUI.IntScale(5);
if (isFourByThree)
{
Spacing = (int)(5 * UIScale);
}
else
{
Spacing = (int)(8 * UIScale);
}
SlotSize = !isFourByThree ? (SlotSpriteSmall.size * UIScale).ToPoint() : (SlotSpriteSmall.size * UIScale * .925f).ToPoint(); SlotSize = (SlotSpriteSmall.size * UIScale * GUI.AspectRatioAdjustment).ToPoint();
int bottomOffset = SlotSize.Y + Spacing * 2 + ContainedIndicatorHeight; int bottomOffset = SlotSize.Y + spacing * 2 + ContainedIndicatorHeight;
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - spacing * 2 - (int)(UnequippedIndicator.size.Y * UIScale);
if (visualSlots == null) { CreateSlots(); } if (visualSlots == null) { CreateSlots(); }
if (visualSlots.None()) { return; } if (visualSlots.None()) { return; }
@@ -242,11 +234,11 @@ namespace Barotrauma
int personalSlotCount = SlotTypes.Count(s => PersonalSlots.HasFlag(s)); int personalSlotCount = SlotTypes.Count(s => PersonalSlots.HasFlag(s));
int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s) && s != InvSlotType.HealthInterface); int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s) && s != InvSlotType.HealthInterface);
int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (SlotSize.X + Spacing) / 2; int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (SlotSize.X + spacing) / 2;
int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - Spacing; int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - spacing;
//make sure the rightmost normal slot doesn't overlap with the personal slots //make sure the rightmost normal slot doesn't overlap with the personal slots
x -= Math.Max((x + normalSlotCount * (SlotSize.X + Spacing)) - (upperX - personalSlotCount * (SlotSize.X + Spacing)), 0); x -= Math.Max((x + normalSlotCount * (SlotSize.X + spacing)) - (upperX - personalSlotCount * (SlotSize.X + spacing)), 0);
int hideButtonSlotIndex = -1; int hideButtonSlotIndex = -1;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
@@ -254,7 +246,7 @@ namespace Barotrauma
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset);
upperX -= SlotSize.X + Spacing; upperX -= SlotSize.X + spacing;
personalSlotArea = (hideButtonSlotIndex == -1) ? personalSlotArea = (hideButtonSlotIndex == -1) ?
new Rectangle(SlotPositions[i].ToPoint(), SlotSize) : new Rectangle(SlotPositions[i].ToPoint(), SlotSize) :
Rectangle.Union(personalSlotArea, new Rectangle(SlotPositions[i].ToPoint(), SlotSize)); Rectangle.Union(personalSlotArea, new Rectangle(SlotPositions[i].ToPoint(), SlotSize));
@@ -263,7 +255,7 @@ namespace Barotrauma
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += SlotSize.X + Spacing; x += SlotSize.X + spacing;
} }
} }
} }
@@ -271,7 +263,7 @@ namespace Barotrauma
case Layout.Right: case Layout.Right:
{ {
int x = HUDLayoutSettings.InventoryAreaLower.Right; int x = HUDLayoutSettings.InventoryAreaLower.Right;
int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - Spacing; int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - spacing;
for (int i = 0; i < visualSlots.Length; i++) for (int i = 0; i < visualSlots.Length; i++)
{ {
if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; } if (HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
@@ -282,19 +274,18 @@ namespace Barotrauma
} }
else else
{ {
x -= SlotSize.X + Spacing; x -= SlotSize.X + spacing;
} }
} }
int lowerX = x; int lowerX = x;
int handSlotX = x; int handSlotX = x;
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - Spacing * 2 - (int)(!GUI.IsFourByThree() ? UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment : UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment * 2f);
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand)
{ {
SlotPositions[i] = new Vector2(handSlotX, personalSlotY); SlotPositions[i] = new Vector2(handSlotX, personalSlotY);
handSlotX += visualSlots[i].Rect.Width + Spacing; handSlotX += visualSlots[i].Rect.Width + spacing;
continue; continue;
} }
@@ -302,12 +293,12 @@ namespace Barotrauma
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY); SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
personalSlotX -= visualSlots[i].Rect.Width + Spacing; personalSlotX -= visualSlots[i].Rect.Width + spacing;
} }
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += visualSlots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + spacing;
} }
} }
@@ -316,7 +307,7 @@ namespace Barotrauma
{ {
if (!HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; } if (!HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; } if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; }
x -= visualSlots[i].Rect.Width + Spacing; x -= visualSlots[i].Rect.Width + spacing;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
} }
} }
@@ -325,7 +316,6 @@ namespace Barotrauma
{ {
int x = HUDLayoutSettings.InventoryAreaLower.X; int x = HUDLayoutSettings.InventoryAreaLower.X;
int personalSlotX = x; int personalSlotX = x;
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - Spacing * 2 - (int)(!GUI.IsFourByThree() ? UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment : UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment * 2f);
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
@@ -334,33 +324,33 @@ namespace Barotrauma
if (PersonalSlots.HasFlag(SlotTypes[i])) if (PersonalSlots.HasFlag(SlotTypes[i]))
{ {
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY); SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
personalSlotX += visualSlots[i].Rect.Width + Spacing; personalSlotX += visualSlots[i].Rect.Width + spacing;
} }
else else
{ {
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += visualSlots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + spacing;
} }
} }
int handSlotX = x - visualSlots[0].Rect.Width - Spacing; int handSlotX = x - visualSlots[0].Rect.Width - spacing;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
{ {
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand)
{ {
bool rightSlot = SlotTypes[i] == InvSlotType.RightHand; bool rightSlot = SlotTypes[i] == InvSlotType.RightHand;
SlotPositions[i] = new Vector2(rightSlot ? handSlotX : handSlotX - visualSlots[0].Rect.Width - Spacing, personalSlotY); SlotPositions[i] = new Vector2(rightSlot ? handSlotX : handSlotX - visualSlots[0].Rect.Width - spacing, personalSlotY);
continue; continue;
} }
if (!HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; } if (!HideSlot(i) || SlotTypes[i] == InvSlotType.HealthInterface) { continue; }
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += visualSlots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + spacing;
} }
} }
break; break;
case Layout.Center: case Layout.Center:
{ {
int columns = 5; int columns = 5;
int startX = (GameMain.GraphicsWidth / 2) - (SlotSize.X * columns + Spacing * (columns - 1)) / 2; int startX = (GameMain.GraphicsWidth / 2) - (SlotSize.X * columns + spacing * (columns - 1)) / 2;
int startY = GameMain.GraphicsHeight / 2 - (SlotSize.Y * 2); int startY = GameMain.GraphicsHeight / 2 - (SlotSize.Y * 2);
int x = startX, y = startY; int x = startX, y = startY;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
@@ -369,10 +359,10 @@ namespace Barotrauma
if (SlotTypes[i] == InvSlotType.Card || SlotTypes[i] == InvSlotType.Headset || SlotTypes[i] == InvSlotType.InnerClothes) if (SlotTypes[i] == InvSlotType.Card || SlotTypes[i] == InvSlotType.Headset || SlotTypes[i] == InvSlotType.InnerClothes)
{ {
SlotPositions[i] = new Vector2(x, y); SlotPositions[i] = new Vector2(x, y);
x += visualSlots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + spacing;
} }
} }
y += visualSlots[0].Rect.Height + Spacing + ContainedIndicatorHeight + visualSlots[0].EquipButtonRect.Height; y += visualSlots[0].Rect.Height + spacing + ContainedIndicatorHeight + visualSlots[0].EquipButtonRect.Height;
x = startX; x = startX;
int n = 0; int n = 0;
for (int i = 0; i < SlotPositions.Length; i++) for (int i = 0; i < SlotPositions.Length; i++)
@@ -381,12 +371,12 @@ namespace Barotrauma
if (SlotTypes[i] != InvSlotType.Card && SlotTypes[i] != InvSlotType.Headset && SlotTypes[i] != InvSlotType.InnerClothes) if (SlotTypes[i] != InvSlotType.Card && SlotTypes[i] != InvSlotType.Headset && SlotTypes[i] != InvSlotType.InnerClothes)
{ {
SlotPositions[i] = new Vector2(x, y); SlotPositions[i] = new Vector2(x, y);
x += visualSlots[i].Rect.Width + Spacing; x += visualSlots[i].Rect.Width + spacing;
n++; n++;
if (n >= columns) if (n >= columns)
{ {
x = startX; x = startX;
y += visualSlots[i].Rect.Height + Spacing + ContainedIndicatorHeight + visualSlots[i].EquipButtonRect.Height; y += visualSlots[i].Rect.Height + spacing + ContainedIndicatorHeight + visualSlots[i].EquipButtonRect.Height;
n = 0; n = 0;
} }
} }
@@ -402,7 +392,7 @@ namespace Barotrauma
{ {
if (SlotTypes[i] != InvSlotType.HealthInterface) { continue; } if (SlotTypes[i] != InvSlotType.HealthInterface) { continue; }
SlotPositions[i] = pos; SlotPositions[i] = pos;
pos.Y += visualSlots[i].Rect.Height + Spacing; pos.Y += visualSlots[i].Rect.Height + spacing;
} }
} }
@@ -641,7 +631,7 @@ namespace Barotrauma
{ {
slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ? slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ?
GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None; GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None;
if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld()) if (PlayerInput.PrimaryMouseButtonHeld() && PlayerInput.SecondaryMouseButtonHeld())
{ {
slot.EquipButtonState = GUIComponent.ComponentState.None; slot.EquipButtonState = GUIComponent.ComponentState.None;
} }
@@ -1018,7 +1008,47 @@ namespace Barotrauma
SoundPlayer.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail); SoundPlayer.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail);
} }
} }
public bool CanBeAutoMovedToCorrectSlots(Item item)
{
if (item == null) { return false; }
foreach (var allowedSlot in item.AllowedSlots)
{
InvSlotType slotsFree = InvSlotType.None;
for (int i = 0; i < slots.Length; i++)
{
if (allowedSlot.HasFlag(SlotTypes[i]) && slots[i].Empty()) { slotsFree |= SlotTypes[i]; }
}
if (allowedSlot == slotsFree) { return true; }
}
return false;
}
/// <summary>
/// Flash the slots the item is allowed to go in (not taking into account whether there's already something in those slots)
/// </summary>
public void FlashAllowedSlots(Item item, Color color)
{
if (item == null || visualSlots == null) { return; }
bool flashed = false;
foreach (var allowedSlot in item.AllowedSlots)
{
for (int i = 0; i < slots.Length; i++)
{
if (allowedSlot.HasFlag(SlotTypes[i]))
{
visualSlots[i].ShowBorderHighlight(color, 0.1f, 0.9f);
flashed = true;
}
}
}
if (flashed)
{
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
}
}
public void DrawOwn(SpriteBatch spriteBatch) public void DrawOwn(SpriteBatch spriteBatch)
{ {
if (!AccessibleWhenAlive && !character.IsDead && !AccessibleByOwner) { return; } if (!AccessibleWhenAlive && !character.IsDead && !AccessibleByOwner) { return; }
@@ -1106,40 +1136,24 @@ namespace Barotrauma
color *= 0.5f; color *= 0.5f;
} }
if (character.HasEquippedItem(slots[i].First())) Vector2 indicatorScale = new Vector2(
visualSlots[i].EquipButtonRect.Size.X / EquippedIndicator.size.X,
visualSlots[i].EquipButtonRect.Size.Y / EquippedIndicator.size.Y);
bool isEquipped = character.HasEquippedItem(slots[i].First());
var sprite = state switch
{ {
switch (state) GUIComponent.ComponentState.None
{ => isEquipped ? EquippedIndicator : UnequippedIndicator,
case GUIComponent.ComponentState.None: GUIComponent.ComponentState.Hover
EquippedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); => isEquipped ? EquippedHoverIndicator : UnequippedHoverIndicator,
break; GUIComponent.ComponentState.Pressed
case GUIComponent.ComponentState.Hover: or GUIComponent.ComponentState.Selected
EquippedHoverIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); or GUIComponent.ComponentState.HoverSelected
break; => isEquipped ? EquippedClickedIndicator : UnequippedClickedIndicator,
case GUIComponent.ComponentState.Pressed: _ => throw new NotImplementedException()
case GUIComponent.ComponentState.Selected: };
case GUIComponent.ComponentState.HoverSelected: sprite.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, indicatorScale);
EquippedClickedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break;
}
}
else
{
switch (state)
{
case GUIComponent.ComponentState.None:
UnequippedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break;
case GUIComponent.ComponentState.Hover:
UnequippedHoverIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break;
case GUIComponent.ComponentState.Pressed:
case GUIComponent.ComponentState.Selected:
case GUIComponent.ComponentState.HoverSelected:
UnequippedClickedIndicator.Draw(spriteBatch, visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment);
break;
}
}
} }
if (Locked) if (Locked)
@@ -182,7 +182,7 @@ namespace Barotrauma.Items.Components
if (brokenSprite == null) if (brokenSprite == null)
{ {
//broken doors turn black if no broken sprite has been configured //broken doors turn black if no broken sprite has been configured
color *= (item.Condition / item.MaxCondition); color = color.Multiply(item.Condition / item.MaxCondition);
color.A = 255; color.A = 255;
} }
@@ -1,13 +1,10 @@
using Barotrauma.Particles; using Barotrauma.Particles;
using Barotrauma.Sounds;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Barotrauma.IO;
using System.Text;
using System.Xml.Linq;
using Barotrauma.Sounds;
using System.Linq; using System.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
@@ -169,11 +166,11 @@ namespace Barotrauma.Items.Components
partial void LaunchProjSpecific() partial void LaunchProjSpecific()
{ {
Vector2 particlePos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos); Vector2 particlePos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
float rotation = -item.body.Rotation; float rotation = item.body.Rotation;
if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; } if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; }
foreach (ParticleEmitter emitter in particleEmitters) foreach (ParticleEmitter emitter in particleEmitters)
{ {
emitter.Emit(1.0f, particlePos, hullGuess: item.CurrentHull, angle: rotation, particleRotation: rotation); emitter.Emit(1.0f, particlePos, hullGuess: item.CurrentHull, angle: rotation, particleRotation: -rotation);
} }
} }
@@ -505,13 +505,14 @@ namespace Barotrauma.Items.Components
} }
ActionType type; ActionType type;
string typeStr = subElement.GetAttributeString("type", "");
try try
{ {
type = (ActionType)Enum.Parse(typeof(ActionType), subElement.GetAttributeString("type", ""), true); type = (ActionType)Enum.Parse(typeof(ActionType), typeStr, true);
} }
catch (Exception e) catch (Exception e)
{ {
DebugConsole.ThrowError("Invalid sound type in " + subElement + "!", e); DebugConsole.ThrowError($"Invalid sound type \"{typeStr}\" in item \"{item.Prefab.Identifier}\"!", e);
break; break;
} }
@@ -524,11 +525,13 @@ namespace Barotrauma.Items.Components
VolumeProperty = subElement.GetAttributeIdentifier("volumeproperty", "") 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) if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random)
{ {
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out SoundSelectionMode selectionMode); soundSelectionModes[type] = subElement.GetAttributeEnum("selectionmode", SoundSelectionMode.Random);
soundSelectionModes[type] = selectionMode;
} }
if (!sounds.TryGetValue(itemSound.Type, out List<ItemSound> soundList)) if (!sounds.TryGetValue(itemSound.Type, out List<ItemSound> soundList))
@@ -584,6 +587,8 @@ namespace Barotrauma.Items.Components
{ {
if (GuiFrame != null && GuiFrameSource.GetAttributeBool("draggable", true)) 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), var handle = new GUIDragHandle(new RectTransform(Vector2.One, GuiFrame.RectTransform, Anchor.Center),
GuiFrame.RectTransform, style: null) GuiFrame.RectTransform, style: null)
{ {
@@ -623,7 +628,7 @@ namespace Barotrauma.Items.Components
}; };
int buttonHeight = (int)(GUIStyle.ItemFrameMargin.Y * 0.4f); 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") style: "GUIButtonSettings")
{ {
OnClicked = (btn, userdata) => OnClicked = (btn, userdata) =>
@@ -648,6 +653,12 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
}; };
if (hideDragIcons)
{
dragIcon.Visible = false;
settingsIcon.Visible = false;
}
} }
} }
@@ -277,7 +277,8 @@ namespace Barotrauma.Items.Components
int ignoredItemCount = 0; int ignoredItemCount = 0;
var subContainableItems = AllSubContainableItems; var subContainableItems = AllSubContainableItems;
float capacity = GetMaxStackSize(targetSlot); float targetSlotCapacity = GetMaxStackSize(targetSlot);
float capacity = targetSlotCapacity * MainContainerCapacity;
if (subContainableItems != null) if (subContainableItems != null)
{ {
bool useMainContainerCapacity = true; bool useMainContainerCapacity = true;
@@ -299,15 +300,11 @@ namespace Barotrauma.Items.Components
} }
if (!useMainContainerCapacity) { break; } if (!useMainContainerCapacity) { break; }
} }
if (useMainContainerCapacity) if (!useMainContainerCapacity)
{
capacity *= MainContainerCapacity;
}
else
{ {
// Ignore all items in the main container. // Ignore all items in the main container.
ignoredItemCount = Inventory.AllItems.Count(it => subContainableItems.Any(ri => !ri.MatchesItem(it))); ignoredItemCount = Inventory.AllItems.Count(it => subContainableItems.Any(ri => !ri.MatchesItem(it)));
capacity *= Capacity - MainContainerCapacity; capacity = targetSlotCapacity * (Capacity - MainContainerCapacity);
} }
} }
int itemCount = Inventory.AllItems.Count() - ignoredItemCount; int itemCount = Inventory.AllItems.Count() - ignoredItemCount;
@@ -391,63 +388,60 @@ namespace Barotrauma.Items.Components
bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode(); bool isWiringMode = SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode();
int i = 0; int i = 0;
foreach (Item containedItem in Inventory.AllItems) foreach (DrawableContainedItem contained in drawableContainedItems)
{ {
Vector2 itemPos = currentItemPos; Vector2 itemPos = currentItemPos;
var relatedItem = FindContainableItem(containedItem);
if (relatedItem != null) if (contained.Item?.Sprite == null) { continue; }
if (contained.Hide) { continue; }
if (contained.ItemPos.HasValue)
{ {
if (relatedItem.Hide.HasValue && relatedItem.Hide.Value) { continue; } Vector2 pos = contained.ItemPos.Value;
if (relatedItem.ItemPos.HasValue) if (item.body != null)
{ {
Vector2 pos = relatedItem.ItemPos.Value; Matrix transform = Matrix.CreateRotationZ(item.body.DrawRotation);
if (item.body != null) 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)
{ {
Matrix transform = Matrix.CreateRotationZ(item.body.DrawRotation); itemPos.X = -itemPos.X;
pos.X *= item.body.Dir; itemPos.X += item.Rect.Width;
itemPos = Vector2.Transform(pos, transform) + item.body.DrawPosition;
} }
else if (item.FlippedY)
{ {
itemPos = pos; itemPos.Y = -itemPos.Y;
// This code is aped based on above. Not tested. itemPos.Y -= item.Rect.Height;
if (item.FlippedX) }
{ itemPos += new Vector2(item.Rect.X, item.Rect.Y);
itemPos.X = -itemPos.X; if (item.Submarine != null)
itemPos.X += item.Rect.Width; {
} itemPos += item.Submarine.DrawPosition;
if (item.FlippedY) }
{ if (Math.Abs(item.RotationRad) > 0.01f)
itemPos.Y = -itemPos.Y; {
itemPos.Y -= item.Rect.Height; Matrix transform = Matrix.CreateRotationZ(-item.RotationRad);
} itemPos = Vector2.Transform(itemPos - item.DrawPosition, transform) + item.DrawPosition;
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) if (AutoInteractWithContained)
{ {
containedItem.IsHighlighted = item.IsHighlighted; contained.Item.IsHighlighted = item.IsHighlighted;
item.IsHighlighted = false; item.IsHighlighted = false;
} }
Vector2 origin = containedItem.Sprite.Origin; Vector2 origin = contained.Item.Sprite.Origin;
if (item.FlippedX) { origin.X = containedItem.Sprite.SourceRect.Width - origin.X; } if (item.FlippedX) { origin.X = contained.Item.Sprite.SourceRect.Width - origin.X; }
if (item.FlippedY) { origin.Y = containedItem.Sprite.SourceRect.Height - origin.Y; } if (item.FlippedY) { origin.Y = contained.Item.Sprite.SourceRect.Height - origin.Y; }
float containedSpriteDepth = ContainedSpriteDepth < 0.0f ? containedItem.Sprite.Depth : ContainedSpriteDepth; float containedSpriteDepth = ContainedSpriteDepth < 0.0f ? contained.Item.Sprite.Depth : ContainedSpriteDepth;
if (i < containedSpriteDepths.Length) if (i < containedSpriteDepths.Length)
{ {
containedSpriteDepth = containedSpriteDepths[i]; containedSpriteDepth = containedSpriteDepths[i];
@@ -456,9 +450,9 @@ namespace Barotrauma.Items.Components
SpriteEffects spriteEffects = SpriteEffects.None; SpriteEffects spriteEffects = SpriteEffects.None;
float spriteRotation = ItemRotation; float spriteRotation = ItemRotation;
if (relatedItem != null && relatedItem.Rotation != 0) if (contained.Rotation != 0)
{ {
spriteRotation = relatedItem.Rotation; spriteRotation = contained.Rotation;
} }
if ((item.body != null && item.body.Dir == -1) || item.FlippedX) if ((item.body != null && item.body.Dir == -1) || item.FlippedX)
{ {
@@ -469,17 +463,17 @@ namespace Barotrauma.Items.Components
spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically; spriteEffects |= MathUtils.NearlyEqual(spriteRotation % 180, 90.0f) ? SpriteEffects.FlipHorizontally : SpriteEffects.FlipVertically;
} }
containedItem.Sprite.Draw( contained.Item.Sprite.Draw(
spriteBatch, spriteBatch,
new Vector2(itemPos.X, -itemPos.Y), new Vector2(itemPos.X, -itemPos.Y),
isWiringMode ? containedItem.GetSpriteColor(withHighlight: true) * 0.15f : containedItem.GetSpriteColor(withHighlight: true), isWiringMode ? contained.Item.GetSpriteColor(withHighlight: true) * 0.15f : contained.Item.GetSpriteColor(withHighlight: true),
origin, origin,
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation), -(contained.Item.body == null ? 0.0f : contained.Item.body.DrawRotation),
containedItem.Scale, contained.Item.Scale,
spriteEffects, spriteEffects,
depth: containedSpriteDepth); depth: containedSpriteDepth);
foreach (ItemContainer ic in containedItem.GetComponents<ItemContainer>()) foreach (ItemContainer ic in contained.Item.GetComponents<ItemContainer>())
{ {
if (ic.hideItems) { continue; } if (ic.hideItems) { continue; }
ic.DrawContainedItems(spriteBatch, containedSpriteDepth); ic.DrawContainedItems(spriteBatch, containedSpriteDepth);
@@ -14,6 +14,13 @@ namespace Barotrauma.Items.Components
private CoroutineHandle resetPredictionCoroutine; private CoroutineHandle resetPredictionCoroutine;
private float resetPredictionTimer; private float resetPredictionTimer;
/// <summary>
/// The current multiplier for the light color (usually equal to <see cref="lightBrightness"/>, but in the case of e.g. blinking lights the multiplier
/// doesn't go to 0 when the light turns off, because otherwise it'd take a while for it turn back on based on the lightBrightness which is interpolated
/// towards the current voltage).
/// </summary>
private float lightColorMultiplier;
public Vector2 DrawSize public Vector2 DrawSize
{ {
get { return new Vector2(Light.Range * 2, Light.Range * 2); } get { return new Vector2(Light.Range * 2, Light.Range * 2); }
@@ -27,21 +34,14 @@ namespace Barotrauma.Items.Components
Light.Position = ParentBody != null ? ParentBody.Position : item.Position; Light.Position = ParentBody != null ? ParentBody.Position : item.Position;
} }
partial void SetLightSourceState(bool enabled, float? brightness) partial void SetLightSourceState(bool enabled, float brightness)
{ {
if (Light == null) { return; } if (Light == null) { return; }
Light.Enabled = enabled; Light.Enabled = enabled;
if (brightness.HasValue) lightColorMultiplier = brightness;
{
lightBrightness = brightness.Value;
}
else
{
lightBrightness = enabled ? 1.0f : 0.0f;
}
if (enabled) if (enabled)
{ {
Light.Color = LightColor.Multiply(lightBrightness); Light.Color = LightColor.Multiply(lightColorMultiplier);
} }
} }
@@ -185,6 +185,7 @@ namespace Barotrauma.Items.Components
RefreshActivateButtonText(); RefreshActivateButtonText();
if (GameMain.Client != null) if (GameMain.Client != null)
{ {
pendingFabricatedItem = null;
item.CreateClientEvent(this); item.CreateClientEvent(this);
} }
return true; return true;
@@ -336,8 +337,11 @@ namespace Barotrauma.Items.Components
int calculatePlacement(FabricationRecipe recipe) int calculatePlacement(FabricationRecipe recipe)
{ {
if (recipe.RequiresRecipe && !AnyOneHasRecipeForItem(character, recipe.TargetItem))
{
return -2;
}
int placement = FabricationDegreeOfSuccess(character, recipe.RequiredSkills) >= 0.5f ? 0 : -1; int placement = FabricationDegreeOfSuccess(character, recipe.RequiredSkills) >= 0.5f ? 0 : -1;
placement += recipe.RequiresRecipe && !AnyOneHasRecipeForItem(character, recipe.TargetItem) ? -2 : 0;
return placement; return placement;
} }
@@ -298,7 +298,7 @@ namespace Barotrauma.Items.Components
} }
} }
OrderPrefab[] reports = OrderPrefab.Prefabs.Where(o => o.IsReport && o.SymbolSprite != null && !o.Hidden).ToArray(); OrderPrefab[] reports = OrderPrefab.Prefabs.Where(o => o.IsReport && o.SymbolSprite != null && !o.Hidden).OrderBy(o => o.Identifier).ToArray();
GUIFrame bottomFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.15f), paddedContainer.RectTransform, Anchor.BottomCenter) { MaxSize = new Point(int.MaxValue, GUI.IntScale(40)) }, style: null) GUIFrame bottomFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.15f), paddedContainer.RectTransform, Anchor.BottomCenter) { MaxSize = new Point(int.MaxValue, GUI.IntScale(40)) }, style: null)
{ {
@@ -452,7 +452,7 @@ namespace Barotrauma.Items.Components
foreach (var (entity, component) in electricalMapComponents) foreach (var (entity, component) in electricalMapComponents)
{ {
GUIComponent parent = component.RectComponent; GUIComponent parent = component.RectComponent;
if (!(entity is Item it )) { continue; } if (entity is not Item it ) { continue; }
Sprite? sprite = it.Prefab.UpgradePreviewSprite; Sprite? sprite = it.Prefab.UpgradePreviewSprite;
if (sprite is null) { continue; } if (sprite is null) { continue; }
@@ -476,7 +476,7 @@ namespace Barotrauma.Items.Components
{ {
if (!hullPointsOfInterest.Contains(entity)) { continue; } if (!hullPointsOfInterest.Contains(entity)) { continue; }
if (!(entity is Item it)) { continue; } if (entity is not Item it) { continue; }
const int borderMaxSize = 2; const int borderMaxSize = 2;
if (it.GetComponent<Door>() is { }) if (it.GetComponent<Door>() is { })
@@ -643,7 +643,7 @@ namespace Barotrauma.Items.Components
elementSize = GuiFrame.Rect.Size; elementSize = GuiFrame.Rect.Size;
} }
float distort = 1.0f - item.Condition / item.MaxCondition; float distort = item.Repairables.Any(r => r.IsBelowRepairThreshold) ? 1.0f - item.Condition / item.MaxCondition : 0.0f;
foreach (HullData hullData in hullDatas.Values) foreach (HullData hullData in hullDatas.Values)
{ {
hullData.DistortionTimer -= deltaTime; hullData.DistortionTimer -= deltaTime;
@@ -1130,7 +1130,7 @@ namespace Barotrauma.Items.Components
{ {
foreach (var (entity, miniMapGuiComponent) in electricalMapComponents) foreach (var (entity, miniMapGuiComponent) in electricalMapComponents)
{ {
if (!(entity is Item it)) { continue; } if (entity is not Item it) { continue; }
if (!electricalChildren.TryGetValue(miniMapGuiComponent, out GUIComponent? component)) { continue; } if (!electricalChildren.TryGetValue(miniMapGuiComponent, out GUIComponent? component)) { continue; }
if (entity.Removed) if (entity.Removed)
@@ -1220,7 +1220,7 @@ namespace Barotrauma.Items.Components
{ {
foreach (var (entity, component) in hullStatusComponents) foreach (var (entity, component) in hullStatusComponents)
{ {
if (!(entity is Hull hull)) { continue; } if (entity is not Hull hull) { continue; }
if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; } if (!hullDatas.TryGetValue(hull, out HullData? hullData) || hullData is null) { continue; }
if (hullData.Distort) { continue; } if (hullData.Distort) { continue; }
@@ -17,6 +17,7 @@ namespace Barotrauma.Items.Components
Default, Default,
Disruption, Disruption,
Destructible, Destructible,
Door,
LongRange LongRange
} }
@@ -110,6 +111,10 @@ namespace Barotrauma.Items.Components
BlipType.Destructible, BlipType.Destructible,
new Color[] { Color.TransparentBlack, new Color(74, 113, 75) * 0.8f, new Color(151, 236, 172) * 0.8f, new Color(153, 217, 234) * 0.8f } new Color[] { Color.TransparentBlack, new Color(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, BlipType.LongRange,
new Color[] { Color.TransparentBlack, Color.TransparentBlack, new Color(254, 68, 19) * 0.8f, Color.TransparentBlack } new Color[] { Color.TransparentBlack, Color.TransparentBlack, new Color(254, 68, 19) * 0.8f, Color.TransparentBlack }
@@ -975,7 +980,7 @@ namespace Barotrauma.Items.Components
if (GameMain.GameSession == null || Level.Loaded == null) { return; } if (GameMain.GameSession == null || Level.Loaded == null) { return; }
if (Level.Loaded.StartLocation != null) if (Level.Loaded.StartLocation?.Type is { ShowSonarMarker: true })
{ {
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
Level.Loaded.StartLocation.Name, Level.Loaded.StartLocation.Name,
@@ -985,7 +990,7 @@ namespace Barotrauma.Items.Components
displayScale, center, DisplayRadius); 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, DrawMarker(spriteBatch,
Level.Loaded.EndLocation.Name, Level.Loaded.EndLocation.Name,
@@ -1010,19 +1015,19 @@ namespace Barotrauma.Items.Components
int missionIndex = 0; int missionIndex = 0;
foreach (Mission mission in GameMain.GameSession.Missions) 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; if (!string.IsNullOrEmpty(label.Value))
foreach (Vector2 sonarPosition in mission.SonarPositions)
{ {
DrawMarker(spriteBatch, DrawMarker(spriteBatch,
mission.SonarLabel.Value, label.Value,
mission.SonarIconIdentifier, mission.SonarIconIdentifier,
"mission" + missionIndex + ":" + i, "mission" + missionIndex + ":" + i,
sonarPosition, transducerCenter, position, transducerCenter,
displayScale, center, DisplayRadius * 0.95f); displayScale, center, DisplayRadius * 0.95f);
i++;
} }
i++;
} }
missionIndex++; missionIndex++;
} }
@@ -1176,13 +1181,18 @@ namespace Barotrauma.Items.Components
if (dockingPort.Item.Submarine == null) { continue; } if (dockingPort.Item.Submarine == null) { continue; }
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; } if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's // docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; } if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine &&
!dockingPort.Item.Submarine.Info.IsOutpost && !dockingPort.Item.Submarine.Info.IsBeacon)
{
continue;
}
//don't show the docking ports of the opposing team on the sonar //don't show the docking ports of the opposing team on the sonar
if (item.Submarine != null && if (item.Submarine != null &&
item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle && item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
dockingPort.Item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle && dockingPort.Item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
dockingPort.Item.Submarine.Info.Type != SubmarineType.Outpost) !dockingPort.Item.Submarine.Info.IsOutpost &&
!dockingPort.Item.Submarine.Info.IsBeacon)
{ {
// specifically checking for friendlyNPC seems more logical here // specifically checking for friendlyNPC seems more logical here
if (dockingPort.Item.Submarine.TeamID != item.Submarine.TeamID && dockingPort.Item.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { continue; } if (dockingPort.Item.Submarine.TeamID != item.Submarine.TeamID && dockingPort.Item.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { continue; }
@@ -1348,6 +1358,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, private void Ping(Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius, float displayScale, float range, bool passive,
float pingStrength = 1.0f, AITarget needsToBeInSector = null) float pingStrength = 1.0f, AITarget needsToBeInSector = null)
{ {
@@ -1392,6 +1434,14 @@ namespace Barotrauma.Items.Components
if (connectedSubs.Contains(submarine)) { continue; } 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++) for (int i = 0; i < submarine.HullVertices.Count; i++)
{ {
Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]); Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]);
@@ -1608,6 +1658,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) private bool CheckBlipVisibility(SonarBlip blip, Vector2 transducerPos)
{ {
Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom; Vector2 pos = (blip.Position - transducerPos) * displayScale * zoom;
@@ -340,6 +340,10 @@ namespace Barotrauma.Items.Components
centerText = $"({TextManager.Get("Meter")})"; centerText = $"({TextManager.Get("Meter")})";
rightTextGetter = () => 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; float realWorldDepth = controlledSub == null ? -1000.0f : controlledSub.RealWorldDepth;
return ((int)realWorldDepth).ToString(); return ((int)realWorldDepth).ToString();
}; };
@@ -20,6 +20,7 @@ namespace Barotrauma.Items.Components
User = Entity.FindEntityByID(userId) as Character; User = Entity.FindEntityByID(userId) as Character;
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle()); Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
float rotation = msg.ReadSingle(); float rotation = msg.ReadSingle();
SpreadCounter = msg.ReadByte();
if (User != null) if (User != null)
{ {
Shoot(User, simPosition, simPosition, rotation, ignoredBodies: User.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: false); Shoot(User, simPosition, simPosition, rotation, ignoredBodies: User.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: false);
@@ -262,9 +262,11 @@ namespace Barotrauma.Items.Components
} }
} }
float conditionPercentage = item.Condition / (item.MaxCondition / item.MaxRepairConditionMultiplier) * 100f;
for (int i = 0; i < particleEmitters.Count; i++) for (int i = 0; i < particleEmitters.Count; i++)
{ {
if ((item.ConditionPercentage >= particleEmitterConditionRanges[i].X && item.ConditionPercentage <= particleEmitterConditionRanges[i].Y) || FakeBrokenTimer > 0.0f) if ((conditionPercentage >= particleEmitterConditionRanges[i].X && conditionPercentage <= particleEmitterConditionRanges[i].Y) || FakeBrokenTimer > 0.0f)
{ {
particleEmitters[i].Emit(deltaTime, item.WorldPosition, item.CurrentHull); particleEmitters[i].Emit(deltaTime, item.WorldPosition, item.CurrentHull);
} }
@@ -436,12 +438,16 @@ namespace Barotrauma.Items.Components
ushort currentFixerID = msg.ReadUInt16(); ushort currentFixerID = msg.ReadUInt16();
currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2); currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null; CurrentFixer = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;
item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer);
if (CurrentFixer == null) if (CurrentFixer is null)
{ {
qteTimer = QteDuration; qteTimer = QteDuration;
qteCooldown = 0.0f; qteCooldown = 0.0f;
} }
else
{
item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer);
}
} }
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null) public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
@@ -108,6 +108,7 @@ namespace Barotrauma.Items.Components
{ {
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f; startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
} }
startPos -= turret.GetRecoilOffset();
} }
else if (weapon != null) else if (weapon != null)
{ {
@@ -110,8 +110,8 @@ namespace Barotrauma.Items.Components
if (HighlightedWire != null) if (HighlightedWire != null)
{ {
HighlightedWire.Item.IsHighlighted = true; HighlightedWire.Item.IsHighlighted = true;
if (HighlightedWire.Connections[0] != null && HighlightedWire.Connections[0].Item != null) HighlightedWire.Connections[0].Item.IsHighlighted = true; if (HighlightedWire.Connections[0] != null && HighlightedWire.Connections[0].Item != null) { HighlightedWire.Connections[0].Item.IsHighlighted = true; }
if (HighlightedWire.Connections[1] != null && HighlightedWire.Connections[1].Item != null) HighlightedWire.Connections[1].Item.IsHighlighted = true; if (HighlightedWire.Connections[1] != null && HighlightedWire.Connections[1].Item != null) { HighlightedWire.Connections[1].Item.IsHighlighted = true; }
} }
} }
@@ -1,4 +1,5 @@
using Barotrauma.Networking; using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -24,7 +25,9 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific(XElement element) 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, ChildAnchor = Anchor.TopCenter,
RelativeSpacing = 0.02f, RelativeSpacing = 0.02f,
@@ -33,31 +36,34 @@ namespace Barotrauma.Items.Components
historyBox = new GUIListBox(new RectTransform(new Vector2(1, .9f), layoutGroup.RectTransform), style: null) historyBox = new GUIListBox(new RectTransform(new Vector2(1, .9f), layoutGroup.RectTransform), style: null)
{ {
AutoHideScrollBar = false AutoHideScrollBar = this.AutoHideScrollbar
}; };
CreateFillerBlock(); if (!Readonly)
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)
{ {
MaxTextLength = MaxMessageLength, CreateFillerBlock();
OverflowClip = true,
OnEnterPressed = (GUITextBox textBox, string text) => 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(); layoutGroup.Recalculate();
} }
@@ -101,7 +107,7 @@ namespace Barotrauma.Items.Components
GUITextBlock newBlock = new GUITextBlock( GUITextBlock newBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter), 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) textColor: color, wrap: true, font: UseMonospaceFont ? GUIStyle.MonospacedFont : GUIStyle.Font)
{ {
CanBeFocused = false CanBeFocused = false
@@ -123,7 +129,10 @@ namespace Barotrauma.Items.Components
historyBox.RecalculateChildren(); historyBox.RecalculateChildren();
historyBox.UpdateScrollBarSize(); historyBox.UpdateScrollBarSize();
historyBox.ScrollBar.BarScrollValue = 1; if (AutoScrollToBottom)
{
historyBox.ScrollBar.BarScrollValue = 1;
}
} }
public override bool Select(Character character) public override bool Select(Character character)
@@ -138,7 +147,7 @@ namespace Barotrauma.Items.Components
public override void AddToGUIUpdateList(int order = 0) public override void AddToGUIUpdateList(int order = 0)
{ {
base.AddToGUIUpdateList(order: order); base.AddToGUIUpdateList(order: order);
if (shouldSelectInputBox) if (shouldSelectInputBox && !Readonly)
{ {
inputBox.Select(); inputBox.Select();
shouldSelectInputBox = false; shouldSelectInputBox = false;
@@ -302,7 +302,18 @@ namespace Barotrauma.Items.Components
Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>(); Dictionary<AfflictionPrefab, float> combinedAfflictionStrengths = new Dictionary<AfflictionPrefab, float>();
foreach (Affliction affliction in allAfflictions) foreach (Affliction affliction in allAfflictions)
{ {
if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold || affliction.Strength <= 0.0f) { continue; } if (affliction.Strength <= 0f) { continue; }
if (affliction.Strength < affliction.Prefab.ShowInHealthScannerThreshold)
{
if (target.IsHuman || target.IsOnPlayerTeam || (affliction.Prefab.AfflictionType != AfflictionPrefab.PoisonType && affliction.Prefab.AfflictionType != AfflictionPrefab.ParalysisType))
{
// Always show the poisons on monsters, because poisoning bigger monsters require multiple doses.
// The solution is hacky, but didn't want to introduce an extra property for this.
// We also want to have a relatively high thershold for showing the poisons on the scanner on humans, so that it's not instantly clear that a target is poisoned and especially not which poison was used.
// Paralysis is treated like a poison but isn't technically a poison, so that we can have multiple afflictions that still are treated the same.
continue;
}
}
if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab)) if (combinedAfflictionStrengths.ContainsKey(affliction.Prefab))
{ {
combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength; combinedAfflictionStrengths[affliction.Prefab] += affliction.Strength;
@@ -333,15 +333,9 @@ namespace Barotrauma.Items.Components
crosshairPointerPos = PlayerInput.MousePosition; crosshairPointerPos = PlayerInput.MousePosition;
} }
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation) || !MathUtils.NearlyEqual(item.Scale, prevScale))
{
UpdateTransformedBarrelPos();
}
Vector2 drawPos = GetDrawPos();
public Vector2 GetRecoilOffset()
{
float recoilOffset = 0.0f; float recoilOffset = 0.0f;
if (Math.Abs(RecoilDistance) > 0.0f && recoilTimer > 0.0f) if (Math.Abs(RecoilDistance) > 0.0f && recoilTimer > 0.0f)
{ {
@@ -362,6 +356,17 @@ namespace Barotrauma.Items.Components
recoilOffset = RecoilDistance; recoilOffset = RecoilDistance;
} }
} }
return new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset;
}
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation) || !MathUtils.NearlyEqual(item.Scale, prevScale))
{
UpdateTransformedBarrelPos();
}
Vector2 drawPos = GetDrawPos();
railSprite?.Draw(spriteBatch, railSprite?.Draw(spriteBatch,
drawPos, drawPos,
@@ -370,7 +375,7 @@ namespace Barotrauma.Items.Components
SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth)); SpriteEffects.None, item.SpriteDepth + (railSprite.Depth - item.Sprite.Depth));
barrelSprite?.Draw(spriteBatch, barrelSprite?.Draw(spriteBatch,
drawPos - new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * recoilOffset * item.Scale, drawPos - GetRecoilOffset() * item.Scale,
item.SpriteColor, item.SpriteColor,
rotation + MathHelper.PiOver2, item.Scale, rotation + MathHelper.PiOver2, item.Scale,
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth)); SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
@@ -53,11 +53,11 @@ namespace Barotrauma
get get
{ {
// Returns a point off-screen, Rectangle.Empty places buttons in the top left of the screen // Returns a point off-screen, Rectangle.Empty places buttons in the top left of the screen
if (IsMoving) return offScreenRect; if (IsMoving) { return offScreenRect; }
int buttonDir = Math.Sign(SubInventoryDir); int buttonDir = Math.Sign(SubInventoryDir);
float sizeY = Inventory.UnequippedIndicator.size.Y * Inventory.UIScale * Inventory.IndicatorScaleAdjustment; float sizeY = Inventory.UnequippedIndicator.size.Y * Inventory.UIScale;
Vector2 equipIndicatorPos = new Vector2(Rect.Left, Rect.Center.Y + (Rect.Height / 2 + 15 * Inventory.UIScale) * buttonDir - sizeY / 2f); Vector2 equipIndicatorPos = new Vector2(Rect.Left, Rect.Center.Y + (Rect.Height / 2 + 15 * Inventory.UIScale) * buttonDir - sizeY / 2f);
equipIndicatorPos += DrawOffset; equipIndicatorPos += DrawOffset;
@@ -176,14 +176,6 @@ namespace Barotrauma
public static Sprite DraggableIndicator; public static Sprite DraggableIndicator;
public static Sprite UnequippedIndicator, UnequippedHoverIndicator, UnequippedClickedIndicator, EquippedIndicator, EquippedHoverIndicator, EquippedClickedIndicator; public static Sprite UnequippedIndicator, UnequippedHoverIndicator, UnequippedClickedIndicator, EquippedIndicator, EquippedHoverIndicator, EquippedClickedIndicator;
public static float IndicatorScaleAdjustment
{
get
{
return !GUI.IsFourByThree() ? 0.8f : 0.7f;
}
}
public static Inventory DraggingInventory; public static Inventory DraggingInventory;
public Inventory ReplacedBy; public Inventory ReplacedBy;
@@ -249,11 +241,11 @@ namespace Barotrauma
{ {
itemsInSlot = ParentInventory.GetItemsAt(SlotIndex); itemsInSlot = ParentInventory.GetItemsAt(SlotIndex);
} }
Tooltip = GetTooltip(Item, itemsInSlot); Tooltip = GetTooltip(Item, itemsInSlot, Character.Controlled);
tooltipDisplayedCondition = (int)Item.ConditionPercentage; tooltipDisplayedCondition = (int)Item.ConditionPercentage;
} }
private RichString GetTooltip(Item item, IEnumerable<Item> itemsInSlot) private static RichString GetTooltip(Item item, IEnumerable<Item> itemsInSlot, Character character)
{ {
if (item == null) { return null; } if (item == null) { return null; }
@@ -348,10 +340,12 @@ namespace Barotrauma
} }
if (itemsInSlot.Count() > 1) if (itemsInSlot.Count() > 1)
{ {
string colorStr = XMLExtensions.ColorToString(GUIStyle.Blue); toolTip += $"\n‖color:gui.blue‖[{GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.TakeOneFromInventorySlot)}] {TextManager.Get("inputtype.takeonefrominventoryslot")}‖color:end‖";
toolTip += $"\n‖color:{colorStr}‖[{GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.TakeOneFromInventorySlot)}] {TextManager.Get("inputtype.takeonefrominventoryslot")}‖color:end‖"; toolTip += $"\n‖color:gui.blue‖[{GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.TakeHalfFromInventorySlot)}] {TextManager.Get("inputtype.takehalffrominventoryslot")}‖color:end‖";
colorStr = XMLExtensions.ColorToString(GUIStyle.Blue); }
toolTip += $"\n‖color:{colorStr}‖[{GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.TakeHalfFromInventorySlot)}] {TextManager.Get("inputtype.takehalffrominventoryslot")}‖color:end‖"; if (item.Prefab.SkillRequirementHints != null && item.Prefab.SkillRequirementHints.Any())
{
toolTip += item.Prefab.GetSkillRequirementHints(character);
} }
return RichString.Rich(toolTip); return RichString.Rich(toolTip);
} }
@@ -576,7 +570,7 @@ namespace Barotrauma
} }
} }
if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld()) if (PlayerInput.PrimaryMouseButtonHeld() && PlayerInput.SecondaryMouseButtonHeld())
{ {
mouseOn = false; mouseOn = false;
} }
@@ -727,14 +721,7 @@ namespace Barotrauma
Rectangle subRect = slot.Rect; Rectangle subRect = slot.Rect;
Vector2 spacing; Vector2 spacing;
if (GUI.IsFourByThree()) spacing = new Vector2(10 * UIScale, (10 + UnequippedIndicator.size.Y) * UIScale * GUI.AspectRatioAdjustment);
{
spacing = new Vector2(5 * UIScale, (5 + UnequippedIndicator.size.Y) * UIScale);
}
else
{
spacing = new Vector2(10 * UIScale, (10 + UnequippedIndicator.size.Y) * UIScale);
}
int columns = MathHelper.Clamp((int)Math.Floor(Math.Sqrt(itemCapacity)), 1, container.SlotsPerRow); int columns = MathHelper.Clamp((int)Math.Floor(Math.Sqrt(itemCapacity)), 1, container.SlotsPerRow);
while (itemCapacity / columns * (subRect.Height + spacing.Y) > GameMain.GraphicsHeight * 0.5f) while (itemCapacity / columns * (subRect.Height + spacing.Y) > GameMain.GraphicsHeight * 0.5f)
@@ -1535,16 +1522,6 @@ namespace Barotrauma
{ {
Sprite slotSprite = slot.SlotSprite ?? SlotSpriteSmall; Sprite slotSprite = slot.SlotSprite ?? SlotSpriteSmall;
/*if (inventory != null && (CharacterInventory.PersonalSlots.HasFlag(type) || (inventory.isSubInventory && (inventory.Owner as Item) != null
&& (inventory.Owner as Item).AllowedSlots.Any(a => CharacterInventory.PersonalSlots.HasFlag(a)))))
{
slotColor = slot.IsHighlighted ? GUIStyle.EquipmentSlotColor : GUIStyle.EquipmentSlotColor * 0.8f;
}
else
{
slotColor = slot.IsHighlighted ? GUIStyle.InventorySlotColor : GUIStyle.InventorySlotColor * 0.8f;
}*/
if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; } if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; }
spriteBatch.Draw(slotSprite.Texture, rect, slotSprite.SourceRect, slotColor); spriteBatch.Draw(slotSprite.Texture, rect, slotSprite.SourceRect, slotColor);
@@ -1731,7 +1708,17 @@ namespace Barotrauma
slot.InventoryKeyIndex < GameSettings.CurrentConfig.InventoryKeyMap.Bindings.Length) slot.InventoryKeyIndex < GameSettings.CurrentConfig.InventoryKeyMap.Bindings.Length)
{ {
spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor); spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor);
GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2((int)(4.25f * UIScale), (int)Math.Ceiling(-1.5f * UIScale)), GameSettings.CurrentConfig.InventoryKeyMap.Bindings[slot.InventoryKeyIndex].Name, Color.Black, font: GUIStyle.HotkeyFont);
GUIStyle.HotkeyFont.DrawString(
spriteBatch,
GameSettings.CurrentConfig.InventoryKeyMap.Bindings[slot.InventoryKeyIndex].Name,
rect.Location.ToVector2() + new Vector2((int)(4.25f * UIScale), (int)Math.Ceiling(-1.5f * UIScale)),
Color.Black,
rotation: 0.0f,
origin: Vector2.Zero,
scale: Vector2.One * GUI.AspectRatioAdjustment,
SpriteEffects.None,
layerDepth: 0.0f);
} }
} }
@@ -23,7 +23,21 @@ namespace Barotrauma
private readonly List<SerializableEntityEditor> activeEditors = new List<SerializableEntityEditor>(); private readonly List<SerializableEntityEditor> activeEditors = new List<SerializableEntityEditor>();
public GUIComponentStyle IconStyle { get; private set; } = null;
private GUIComponentStyle iconStyle;
public GUIComponentStyle IconStyle
{
get { return iconStyle; }
private set
{
if (IconStyle != value)
{
iconStyle = value;
CheckIsHighlighted();
}
}
}
partial void AssignCampaignInteractionTypeProjSpecific(CampaignMode.InteractionType interactionType) partial void AssignCampaignInteractionTypeProjSpecific(CampaignMode.InteractionType interactionType)
{ {
if (interactionType == CampaignMode.InteractionType.None) if (interactionType == CampaignMode.InteractionType.None)
@@ -143,6 +157,18 @@ namespace Barotrauma
return color; return color;
} }
protected override void CheckIsHighlighted()
{
if (IsHighlighted || ExternalHighlight || IconStyle != null)
{
highlightedEntities.Add(this);
}
else
{
highlightedEntities.Remove(this);
}
}
public Color GetInventoryIconColor() public Color GetInventoryIconColor()
{ {
Color color = InventoryIconColor; Color color = InventoryIconColor;
@@ -281,7 +307,8 @@ namespace Barotrauma
cachedVisibleExtents = extents = new Rectangle(min.ToPoint(), max.ToPoint()); cachedVisibleExtents = extents = new Rectangle(min.ToPoint(), max.ToPoint());
} }
Vector2 worldPosition = WorldPosition; Vector2 worldPosition = WorldPosition + GetCollapseEffectOffset();
if (worldPosition.X + extents.X > worldView.Right || worldPosition.X + extents.Width < worldView.X) { return false; } if (worldPosition.X + extents.X > worldView.Right || worldPosition.X + extents.Width < worldView.X) { return false; }
if (worldPosition.Y + extents.Height < worldView.Y - worldView.Height || worldPosition.Y + extents.Y > worldView.Y) { return false; } if (worldPosition.Y + extents.Height < worldView.Y - worldView.Height || worldPosition.Y + extents.Y > worldView.Y) { return false; }
@@ -310,7 +337,9 @@ namespace Barotrauma
BrokenItemSprite fadeInBrokenSprite = null; BrokenItemSprite fadeInBrokenSprite = null;
float fadeInBrokenSpriteAlpha = 0.0f; float fadeInBrokenSpriteAlpha = 0.0f;
float displayCondition = FakeBroken ? 0.0f : ConditionPercentage; float displayCondition = FakeBroken ? 0.0f : ConditionPercentage;
Vector2 drawOffset = Vector2.Zero; Vector2 drawOffset = GetCollapseEffectOffset();
drawOffset.Y = -drawOffset.Y;
if (displayCondition < MaxCondition) if (displayCondition < MaxCondition)
{ {
for (int i = 0; i < Prefab.BrokenSprites.Length; i++) for (int i = 0; i < Prefab.BrokenSprites.Length; i++)
@@ -426,6 +455,8 @@ namespace Barotrauma
var holdable = GetComponent<Holdable>(); var holdable = GetComponent<Holdable>();
if (holdable != null && holdable.Picker?.AnimController != null) 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; } if (!back) { return; }
float depthStep = 0.000001f; float depthStep = 0.000001f;
if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == this) if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == this)
@@ -728,7 +759,7 @@ namespace Barotrauma
if (!lClick && !rClick) { return; } if (!lClick && !rClick) { return; }
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
var otherEntity = mapEntityList.FirstOrDefault(e => e != this && e.IsHighlighted && e.IsMouseOn(position)); var otherEntity = highlightedEntities.FirstOrDefault(e => e != this && e.IsMouseOn(position));
if (otherEntity != null) if (otherEntity != null)
{ {
if (linkedTo.Contains(otherEntity)) if (linkedTo.Contains(otherEntity))
@@ -1672,25 +1703,24 @@ namespace Barotrauma
bool hasIdCard = msg.ReadBoolean(); bool hasIdCard = msg.ReadBoolean();
string ownerName = "", ownerTags = ""; string ownerName = "", ownerTags = "";
int ownerBeardIndex = -1, ownerHairIndex = -1, ownerMoustacheIndex = -1, ownerFaceAttachmentIndex = -1; int ownerBeardIndex = -1, ownerHairIndex = -1, ownerMoustacheIndex = -1, ownerFaceAttachmentIndex = -1;
Color ownerHairColor = Microsoft.Xna.Framework.Color.White, Color ownerHairColor = Color.White,
ownerFacialHairColor = Microsoft.Xna.Framework.Color.White, ownerFacialHairColor = Color.White,
ownerSkinColor = Microsoft.Xna.Framework.Color.White; ownerSkinColor = Color.White;
Identifier ownerJobId = Identifier.Empty; Identifier ownerJobId = Identifier.Empty;
Vector2 ownerSheetIndex = Vector2.Zero; Vector2 ownerSheetIndex = Vector2.Zero;
int submarineSpecificId = 0;
if (hasIdCard) if (hasIdCard)
{ {
submarineSpecificId = msg.ReadInt32();
ownerName = msg.ReadString(); ownerName = msg.ReadString();
ownerTags = msg.ReadString(); ownerTags = msg.ReadString();
ownerBeardIndex = msg.ReadByte() - 1; ownerBeardIndex = msg.ReadByte() - 1;
ownerHairIndex = msg.ReadByte() - 1; ownerHairIndex = msg.ReadByte() - 1;
ownerMoustacheIndex = msg.ReadByte() - 1; ownerMoustacheIndex = msg.ReadByte() - 1;
ownerFaceAttachmentIndex = msg.ReadByte() - 1; ownerFaceAttachmentIndex = msg.ReadByte() - 1;
ownerHairColor = msg.ReadColorR8G8B8(); ownerHairColor = msg.ReadColorR8G8B8();
ownerFacialHairColor = msg.ReadColorR8G8B8(); ownerFacialHairColor = msg.ReadColorR8G8B8();
ownerSkinColor = msg.ReadColorR8G8B8(); ownerSkinColor = msg.ReadColorR8G8B8();
ownerJobId = msg.ReadIdentifier(); ownerJobId = msg.ReadIdentifier();
int x = msg.ReadByte(); int x = msg.ReadByte();
@@ -1794,6 +1824,7 @@ namespace Barotrauma
} }
foreach (IdCard idCard in item.GetComponents<IdCard>()) foreach (IdCard idCard in item.GetComponents<IdCard>())
{ {
idCard.SubmarineSpecificID = submarineSpecificId;
idCard.TeamID = (CharacterTeamType)teamID; idCard.TeamID = (CharacterTeamType)teamID;
idCard.OwnerName = ownerName; idCard.OwnerName = ownerName;
idCard.OwnerTags = ownerTags; idCard.OwnerTags = ownerTags;
@@ -254,7 +254,7 @@ namespace Barotrauma
if (!DefaultPrice.RequiresUnlock) { return true; } if (!DefaultPrice.RequiresUnlock) { return true; }
return Character.Controlled is not null && Character.Controlled.HasStoreAccessForItem(this); return Character.Controlled is not null && Character.Controlled.HasStoreAccessForItem(this);
} }
public LocalizedString GetTooltip() public LocalizedString GetTooltip(Character character)
{ {
LocalizedString tooltip = $"‖color:{XMLExtensions.ToStringHex(GUIStyle.TextColorBright)}‖{Name}‖color:end‖"; LocalizedString tooltip = $"‖color:{XMLExtensions.ToStringHex(GUIStyle.TextColorBright)}‖{Name}‖color:end‖";
if (!Description.IsNullOrEmpty()) if (!Description.IsNullOrEmpty())
@@ -265,6 +265,10 @@ namespace Barotrauma
{ {
Wearable.AddTooltipInfo(wearableDamageModifiers, wearableSkillModifiers, ref tooltip); Wearable.AddTooltipInfo(wearableDamageModifiers, wearableSkillModifiers, ref tooltip);
} }
if (SkillRequirementHints != null && SkillRequirementHints.Any())
{
tooltip += GetSkillRequirementHints(character);
}
return tooltip; return tooltip;
} }
@@ -376,5 +380,31 @@ namespace Barotrauma
Sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), SpriteColor * 0.8f); Sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), SpriteColor * 0.8f);
} }
} }
public LocalizedString GetSkillRequirementHints(Character character)
{
LocalizedString text = "";
if (SkillRequirementHints != null && SkillRequirementHints.Any() && character != null)
{
Color orange = GUIStyle.Orange;
// Reuse an existing, localized, text because it's what we want here: "Required skills:"
text = "\n\n" + $"‖color:{orange.ToStringHex()}‖{TextManager.Get("requiredrepairskills")}‖color:end‖";
foreach (var hint in SkillRequirementHints)
{
int skillLevel = (int)character.GetSkillLevel(hint.Skill);
Color levelColor = GUIStyle.Yellow;
if (skillLevel >= hint.Level)
{
levelColor = GUIStyle.Green;
}
else if (skillLevel < hint.Level / 2)
{
levelColor = GUIStyle.Red;
}
text += "\n" + hint.GetFormattedText(skillLevel, levelColor.ToStringHex());
}
}
return text;
}
} }
} }
@@ -129,20 +129,15 @@ namespace Barotrauma
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
foreach (MapEntity entity in mapEntityList) foreach (MapEntity entity in HighlightedEntities)
{ {
if (entity == this || !entity.IsHighlighted) { continue; } if (entity == this) { continue; }
if (!entity.IsMouseOn(position)) { continue; } if (!entity.IsMouseOn(position)) { continue; }
if (entity.linkedTo == null || !entity.Linkable) { continue; } if (entity.linkedTo == null || !entity.Linkable) { continue; }
if (entity.linkedTo.Contains(this) || linkedTo.Contains(entity) || rClick) if (entity.linkedTo.Contains(this) || linkedTo.Contains(entity) || rClick)
{ {
if (entity == this || !entity.IsHighlighted) { continue; } entity.linkedTo.Remove(this);
if (!entity.IsMouseOn(position)) { continue; } linkedTo.Remove(entity);
if (entity.linkedTo.Contains(this))
{
entity.linkedTo.Remove(this);
linkedTo.Remove(entity);
}
} }
else else
{ {
@@ -114,7 +114,11 @@ namespace Barotrauma
graphics.Clear(BackgroundColor); 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) public void DrawFront(SpriteBatch spriteBatch, Camera cam)
@@ -1,4 +1,4 @@
using FarseerPhysics; using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
@@ -71,9 +71,12 @@ namespace Barotrauma
{ {
private static BasicEffect wallEdgeEffect, wallCenterEffect; private static BasicEffect wallEdgeEffect, wallCenterEffect;
private Vector2 dustOffset; private Vector2 waterParticleOffset;
private Vector2 defaultDustVelocity; private Vector2 waterParticleVelocity;
private Vector2 dustVelocity;
private float flashCooldown;
private float flashTimer;
public Color FlashColor { get; private set; }
private readonly RasterizerState cullNone; private readonly RasterizerState cullNone;
@@ -81,10 +84,26 @@ namespace Barotrauma
private readonly List<LevelWallVertexBuffer> vertexBuffers = new List<LevelWallVertexBuffer>(); 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) public LevelRenderer(Level level)
{ {
defaultDustVelocity = Vector2.UnitY * 10.0f;
cullNone = new RasterizerState() { CullMode = CullMode.None }; cullNone = new RasterizerState() { CullMode = CullMode.None };
if (wallEdgeEffect == null) if (wallEdgeEffect == null)
@@ -120,12 +139,50 @@ namespace Barotrauma
level.GenerationParams.WallSprite.ReloadTexture(); level.GenerationParams.WallSprite.ReloadTexture();
wallCenterEffect.Texture = level.GenerationParams.WallSprite.Texture; wallCenterEffect.Texture = level.GenerationParams.WallSprite.Texture;
} }
public void Flash()
{
flashTimer = 1.0f;
}
public void Update(float deltaTime, Camera cam) 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 //calculate the sum of the forces of nearby level triggers
//and use it to move the dust texture and water distortion effect //and use it to move the water texture and water distortion effect
Vector2 currentDustVel = defaultDustVelocity; Vector2 currentWaterParticleVel = level.GenerationParams.WaterParticleVelocity;
foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects()) foreach (LevelObject levelObject in level.LevelObjectManager.GetVisibleObjects())
{ {
if (levelObject.Triggers == null) { continue; } if (levelObject.Triggers == null) { continue; }
@@ -139,21 +196,21 @@ namespace Barotrauma
objectMaxFlow = vel; objectMaxFlow = vel;
} }
} }
currentDustVel += objectMaxFlow; currentWaterParticleVel += objectMaxFlow;
} }
waterParticleVelocity = Vector2.Lerp(waterParticleVelocity, currentWaterParticleVel, deltaTime);
dustVelocity = Vector2.Lerp(dustVelocity, currentDustVel, deltaTime); WaterRenderer.Instance?.ScrollWater(waterParticleVelocity, deltaTime);
WaterRenderer.Instance?.ScrollWater(dustVelocity, deltaTime);
if (level.GenerationParams.WaterParticles != null) if (level.GenerationParams.WaterParticles != null)
{ {
Vector2 waterTextureSize = level.GenerationParams.WaterParticles.size * level.GenerationParams.WaterParticleScale; Vector2 waterTextureSize = level.GenerationParams.WaterParticles.size * level.GenerationParams.WaterParticleScale;
dustOffset += new Vector2(dustVelocity.X, -dustVelocity.Y) * level.GenerationParams.WaterParticleScale * deltaTime; waterParticleOffset += new Vector2(waterParticleVelocity.X, -waterParticleVelocity.Y) * level.GenerationParams.WaterParticleScale * deltaTime;
while (dustOffset.X <= -waterTextureSize.X) dustOffset.X += waterTextureSize.X; while (waterParticleOffset.X <= -waterTextureSize.X) { waterParticleOffset.X += waterTextureSize.X; }
while (dustOffset.X >= waterTextureSize.X) dustOffset.X -= waterTextureSize.X; while (waterParticleOffset.X >= waterTextureSize.X){ waterParticleOffset.X -= waterTextureSize.X; }
while (dustOffset.Y <= -waterTextureSize.Y) dustOffset.Y += waterTextureSize.Y; while (waterParticleOffset.Y <= -waterTextureSize.Y) { waterParticleOffset.Y += waterTextureSize.Y; }
while (dustOffset.Y >= waterTextureSize.Y) dustOffset.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); Rectangle srcRect = new Rectangle(0, 0, 2048, 2048);
Vector2 origin = new Vector2(cam.WorldView.X, -cam.WorldView.Y); 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 <= -srcRect.Width * textureScale) offset.X += srcRect.Width * textureScale;
while (offset.X > 0.0f) 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; while (offset.Y <= -srcRect.Height * textureScale) offset.Y += srcRect.Height * textureScale;
@@ -261,7 +318,7 @@ namespace Barotrauma
level.GenerationParams.WaterParticles.DrawTiled( level.GenerationParams.WaterParticles.DrawTiled(
spriteBatch, origin + offsetS, spriteBatch, origin + offsetS,
new Vector2(cam.WorldView.Width - offsetS.X, cam.WorldView.Height - offsetS.Y), 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(); spriteBatch.End();
@@ -2,7 +2,6 @@
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using SharpFont;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -12,28 +11,14 @@ namespace Barotrauma.Lights
{ {
class ConvexHullList class ConvexHullList
{ {
private List<ConvexHull> list;
public HashSet<ConvexHull> IsHidden;
public readonly Submarine Submarine; public readonly Submarine Submarine;
public List<ConvexHull> List public HashSet<ConvexHull> IsHidden = new HashSet<ConvexHull>();
{ public readonly List<ConvexHull> List = new List<ConvexHull>();
get { return list; }
set
{
Debug.Assert(value != null);
Debug.Assert(!list.Contains(null));
list = value;
IsHidden.RemoveWhere(ch => !list.Contains(ch));
}
}
public ConvexHullList(Submarine submarine) public ConvexHullList(Submarine submarine)
{ {
Submarine = submarine; Submarine = submarine;
list = new List<ConvexHull>();
IsHidden = new HashSet<ConvexHull>();
} }
} }
@@ -354,7 +339,7 @@ namespace Barotrauma.Lights
} }
} }
public bool IsSegmentAInB(Segment a, Segment b) public static bool IsSegmentAInB(Segment a, Segment b)
{ {
if (Vector2.DistanceSquared(a.Start.Pos, a.End.Pos) > Vector2.DistanceSquared(b.Start.Pos, b.End.Pos)) if (Vector2.DistanceSquared(a.Start.Pos, a.End.Pos) > Vector2.DistanceSquared(b.Start.Pos, b.End.Pos))
{ {
@@ -362,15 +347,16 @@ namespace Barotrauma.Lights
} }
Vector2 min = new Vector2(Math.Min(b.Start.Pos.X, b.End.Pos.X), Math.Min(b.Start.Pos.Y, b.End.Pos.Y)); Vector2 min = new Vector2(Math.Min(b.Start.Pos.X, b.End.Pos.X), Math.Min(b.Start.Pos.Y, b.End.Pos.Y));
Vector2 max = new Vector2(Math.Max(b.Start.Pos.X, b.End.Pos.X), Math.Max(b.Start.Pos.Y, b.End.Pos.Y));
min.X -= 1.0f; min.Y -= 1.0f; min.X -= 1.0f; min.Y -= 1.0f;
max.X += 1.0f; max.Y += 1.0f;
if (a.Start.Pos.X < min.X) { return false; } if (a.Start.Pos.X < min.X) { return false; }
if (a.Start.Pos.Y < min.Y) { return false; } if (a.Start.Pos.Y < min.Y) { return false; }
if (a.End.Pos.X < min.X) { return false; } if (a.End.Pos.X < min.X) { return false; }
if (a.End.Pos.Y < min.Y) { return false; } if (a.End.Pos.Y < min.Y) { return false; }
Vector2 max = new Vector2(Math.Max(b.Start.Pos.X, b.End.Pos.X), Math.Max(b.Start.Pos.Y, b.End.Pos.Y));
max.X += 1.0f; max.Y += 1.0f;
if (a.Start.Pos.X > max.X) { return false; } if (a.Start.Pos.X > max.X) { return false; }
if (a.Start.Pos.Y > max.Y) { return false; } if (a.Start.Pos.Y > max.Y) { return false; }
if (a.End.Pos.X > max.X) { return false; } if (a.End.Pos.X > max.X) { return false; }
@@ -628,7 +614,7 @@ namespace Barotrauma.Lights
{ {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
if (ignoreEdge[i] && ignoreEdges) continue; if (ignoreEdge[i] && ignoreEdges) { continue; }
Vector2 pos1 = vertices[i].WorldPos; Vector2 pos1 = vertices[i].WorldPos;
Vector2 pos2 = vertices[(i + 1) % 4].WorldPos; Vector2 pos2 = vertices[(i + 1) % 4].WorldPos;
@@ -449,9 +449,9 @@ namespace Barotrauma.Lights
{ {
highlightedEntities.Add(Character.Controlled.FocusedCharacter); highlightedEntities.Add(Character.Controlled.FocusedCharacter);
} }
foreach (Item item in Item.ItemList) foreach (MapEntity me in MapEntity.HighlightedEntities)
{ {
if ((item.IsHighlighted || item.IconStyle != null) && !highlightedEntities.Contains(item)) if (me is Item item && item != Character.Controlled.FocusedItem)
{ {
highlightedEntities.Add(item); highlightedEntities.Add(item);
} }
@@ -203,7 +203,7 @@ namespace Barotrauma.Lights
private VertexPositionColorTexture[] vertices; private VertexPositionColorTexture[] vertices;
private short[] indices; private short[] indices;
private readonly List<ConvexHullList> hullsInRange; private readonly List<ConvexHullList> convexHullsInRange;
public Texture2D texture; public Texture2D texture;
@@ -234,7 +234,7 @@ namespace Barotrauma.Lights
{ {
if (!needsRecalculation && value) if (!needsRecalculation && value)
{ {
foreach (ConvexHullList chList in hullsInRange) foreach (ConvexHullList chList in convexHullsInRange)
{ {
chList.IsHidden.Clear(); chList.IsHidden.Clear();
} }
@@ -474,7 +474,7 @@ namespace Barotrauma.Lights
public LightSource(Vector2 position, float range, Color color, Submarine submarine, bool addLight=true) public LightSource(Vector2 position, float range, Color color, Submarine submarine, bool addLight=true)
{ {
hullsInRange = new List<ConvexHullList>(); convexHullsInRange = new List<ConvexHullList>();
this.ParentSub = submarine; this.ParentSub = submarine;
this.position = position; this.position = position;
lightSourceParams = new LightSourceParams(range, color); lightSourceParams = new LightSourceParams(range, color);
@@ -515,19 +515,25 @@ namespace Barotrauma.Lights
/// </summary> /// </summary>
private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub) private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub)
{ {
var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); var fullChList = ConvexHull.HullLists.FirstOrDefault(chList => chList.Submarine == sub);
if (fullChList == null) { return; } if (fullChList == null) { return; }
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, ch.BoundingBox)); chList.List.Clear();
foreach (var convexHull in fullChList.List)
NeedsHullCheck = true; {
if (!convexHull.Enabled) { continue; }
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, convexHull.BoundingBox)) { continue; }
chList.List.Add(convexHull);
}
chList.IsHidden.RemoveWhere(ch => !chList.List.Contains(ch));
NeedsHullCheck = false;
} }
/// <summary> /// <summary>
/// Recheck which convex hulls are in range (if needed), /// Recheck which convex hulls are in range (if needed),
/// and check if we need to recalculate vertices due to changes in the convex hulls /// and check if we need to recalculate vertices due to changes in the convex hulls
/// </summary> /// </summary>
private void CheckHullsInRange() private void CheckConvexHullsInRange()
{ {
foreach (Submarine sub in Submarine.Loaded) foreach (Submarine sub in Submarine.Loaded)
{ {
@@ -540,21 +546,13 @@ namespace Barotrauma.Lights
private void CheckHullsInRange(Submarine sub) private void CheckHullsInRange(Submarine sub)
{ {
//find the list of convexhulls that belong to the sub //find the list of convexhulls that belong to the sub
ConvexHullList chList = null; ConvexHullList chList = convexHullsInRange.FirstOrDefault(chList => chList.Submarine == sub);
foreach (var ch in hullsInRange)
{
if (ch.Submarine == sub)
{
chList = ch;
break;
}
}
//not found -> create one //not found -> create one
if (chList == null) if (chList == null)
{ {
chList = new ConvexHullList(sub); chList = new ConvexHullList(sub);
hullsInRange.Add(chList); convexHullsInRange.Add(chList);
NeedsRecalculation = true; NeedsRecalculation = true;
} }
@@ -646,6 +644,10 @@ namespace Barotrauma.Lights
} }
} }
private static readonly List<Segment> visibleSegments = new List<Segment>();
private static readonly List<SegmentPoint> points = new List<SegmentPoint>();
private static readonly List<Vector2> output = new List<Vector2>();
private static readonly SegmentPoint[] boundaryCorners = new SegmentPoint[4];
private List<Vector2> FindRaycastHits() private List<Vector2> FindRaycastHits()
{ {
if (!CastShadows || Range < 1.0f || Color.A < 1) { return null; } if (!CastShadows || Range < 1.0f || Color.A < 1) { return null; }
@@ -653,12 +655,17 @@ namespace Barotrauma.Lights
Vector2 drawPos = position; Vector2 drawPos = position;
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; } if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
var hulls = new List<ConvexHull>(); visibleSegments.Clear();
foreach (ConvexHullList chList in hullsInRange) foreach (ConvexHullList chList in convexHullsInRange)
{ {
foreach (ConvexHull hull in chList.List) foreach (ConvexHull hull in chList.List)
{ {
if (!chList.IsHidden.Contains(hull)) { hulls.Add(hull); } if (!chList.IsHidden.Contains(hull))
{
//find convexhull segments that are close enough and facing towards the light source
hull.RefreshWorldPositions();
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
}
} }
foreach (ConvexHull hull in chList.List) foreach (ConvexHull hull in chList.List)
{ {
@@ -666,23 +673,13 @@ namespace Barotrauma.Lights
} }
} }
float bounds = TextureRange;
//find convexhull segments that are close enough and facing towards the light source
List<Segment> visibleSegments = new List<Segment>();
List<SegmentPoint> points = new List<SegmentPoint>();
foreach (ConvexHull hull in hulls)
{
hull.RefreshWorldPositions();
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from //add a square-shaped boundary to make sure we've got something to construct the triangles from
//even if there aren't enough hull segments around the light source //even if there aren't enough hull segments around the light source
//(might be more effective to calculate if we actually need these extra points) //(might be more effective to calculate if we actually need these extra points)
Vector2 drawOffset = Vector2.Zero; Vector2 drawOffset = Vector2.Zero;
float boundsExtended = bounds; float boundsExtended = TextureRange;
if (OverrideLightTexture != null) if (OverrideLightTexture != null)
{ {
float cosAngle = (float)Math.Cos(Rotation); float cosAngle = (float)Math.Cos(Rotation);
@@ -706,12 +703,12 @@ namespace Barotrauma.Lights
drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle; drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle;
} }
var boundaryCorners = new SegmentPoint[] { Vector2 boundsMin = drawPos + drawOffset + new Vector2(-boundsExtended, -boundsExtended);
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X + boundsExtended, drawPos.Y + drawOffset.Y + boundsExtended), null), Vector2 boundsMax = drawPos + drawOffset + new Vector2(boundsExtended, boundsExtended);
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X + boundsExtended, drawPos.Y + drawOffset.Y - boundsExtended), null), boundaryCorners[0] = new SegmentPoint(boundsMax, null);
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X - boundsExtended, drawPos.Y + drawOffset.Y - boundsExtended), null), boundaryCorners[1] = new SegmentPoint(new Vector2(boundsMax.X, boundsMin.Y), null);
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X - boundsExtended, drawPos.Y + drawOffset.Y + boundsExtended), null) boundaryCorners[2] = new SegmentPoint(boundsMin, null);
}; boundaryCorners[3] = new SegmentPoint(new Vector2(boundsMin.X, boundsMax.Y), null);
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
@@ -795,6 +792,7 @@ namespace Barotrauma.Lights
} }
} }
points.Clear();
//remove segments that fall out of bounds //remove segments that fall out of bounds
for (int i = 0; i < visibleSegments.Count; i++) for (int i = 0; i < visibleSegments.Count; i++)
{ {
@@ -814,7 +812,18 @@ namespace Barotrauma.Lights
} }
} }
visibleSegments = visibleSegments.OrderBy(s => MathUtils.LineToPointDistanceSquared(s.Start.WorldPos, s.End.WorldPos, drawPos)).ToList(); //remove points that are very close to each other
for (int i = 0; i < points.Count; i++)
{
for (int j = Math.Min(i + 4, points.Count-1); j > i; j--)
{
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < 6 &&
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < 6)
{
points.RemoveAt(j);
}
}
}
var compareCCW = new CompareSegmentPointCW(drawPos); var compareCCW = new CompareSegmentPointCW(drawPos);
try try
@@ -830,23 +839,12 @@ namespace Barotrauma.Lights
} }
DebugConsole.ThrowError(sb.ToString(), e); DebugConsole.ThrowError(sb.ToString(), e);
} }
visibleSegments.Sort((s1, s2) =>
MathUtils.LineToPointDistanceSquared(s1.Start.WorldPos, s1.End.WorldPos, drawPos)
.CompareTo(MathUtils.LineToPointDistanceSquared(s2.Start.WorldPos, s2.End.WorldPos, drawPos)));
List<Vector2> output = new List<Vector2>(); output.Clear();
//List<Pair<int, Vector2>> preOutput = new List<Pair<int, Vector2>>();
//remove points that are very close to each other
for (int i = 0; i < points.Count; i++)
{
for (int j = Math.Min(i + 4, points.Count-1); j > i; j--)
{
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < 6 &&
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < 6)
{
points.RemoveAt(j);
}
}
}
foreach (SegmentPoint p in points) foreach (SegmentPoint p in points)
{ {
Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos); Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos);
@@ -854,10 +852,10 @@ namespace Barotrauma.Lights
//do two slightly offset raycasts to hit the segment itself and whatever's behind it //do two slightly offset raycasts to hit the segment itself and whatever's behind it
var intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments); var intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
if (intersection1.index < 0) { return null; }
var intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments); var intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
if (intersection2.index < 0) { return null; }
if (intersection1.index < 0) return null;
if (intersection2.index < 0) return null;
Segment seg1 = visibleSegments[intersection1.index]; Segment seg1 = visibleSegments[intersection1.index];
Segment seg2 = visibleSegments[intersection2.index]; Segment seg2 = visibleSegments[intersection2.index];
@@ -869,7 +867,7 @@ namespace Barotrauma.Lights
//hit at the current segmentpoint -> place the segmentpoint into the list //hit at the current segmentpoint -> place the segmentpoint into the list
output.Add(p.WorldPos); output.Add(p.WorldPos);
foreach (ConvexHullList hullList in hullsInRange) foreach (ConvexHullList hullList in convexHullsInRange)
{ {
hullList.IsHidden.Remove(p.ConvexHull); hullList.IsHidden.Remove(p.ConvexHull);
hullList.IsHidden.Remove(seg1.ConvexHull); hullList.IsHidden.Remove(seg1.ConvexHull);
@@ -883,7 +881,7 @@ namespace Barotrauma.Lights
output.Add(isPoint1 ? p.WorldPos : intersection1.pos); output.Add(isPoint1 ? p.WorldPos : intersection1.pos);
output.Add(isPoint2 ? p.WorldPos : intersection2.pos); output.Add(isPoint2 ? p.WorldPos : intersection2.pos);
foreach (ConvexHullList hullList in hullsInRange) foreach (ConvexHullList hullList in convexHullsInRange)
{ {
hullList.IsHidden.Remove(p.ConvexHull); hullList.IsHidden.Remove(p.ConvexHull);
hullList.IsHidden.Remove(seg1.ConvexHull); hullList.IsHidden.Remove(seg1.ConvexHull);
@@ -911,7 +909,7 @@ namespace Barotrauma.Lights
return output; return output;
} }
private (int index, Vector2 pos) RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments) private static (int index, Vector2 pos) RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
{ {
Vector2? closestIntersection = null; Vector2? closestIntersection = null;
int segment = -1; int segment = -1;
@@ -936,13 +934,13 @@ namespace Barotrauma.Lights
//same for the x-axis //same for the x-axis
if (s.Start.WorldPos.X > s.End.WorldPos.X) if (s.Start.WorldPos.X > s.End.WorldPos.X)
{ {
if (s.Start.WorldPos.X < minX) continue; if (s.Start.WorldPos.X < minX) { continue; }
if (s.End.WorldPos.X > maxX) continue; if (s.End.WorldPos.X > maxX) { continue; }
} }
else else
{ {
if (s.End.WorldPos.X < minX) continue; if (s.End.WorldPos.X < minX) { continue; }
if (s.Start.WorldPos.X > maxX) continue; if (s.Start.WorldPos.X > maxX) { continue; }
} }
bool intersects; bool intersects;
@@ -1335,7 +1333,7 @@ namespace Barotrauma.Lights
return; return;
} }
CheckHullsInRange(); CheckConvexHullsInRange();
if (NeedsRecalculation && allowRecalculation) if (NeedsRecalculation && allowRecalculation)
{ {
@@ -1387,7 +1385,7 @@ namespace Barotrauma.Lights
public void Reset() public void Reset()
{ {
hullsInRange.Clear(); convexHullsInRange.Clear();
diffToSub.Clear(); diffToSub.Clear();
NeedsHullCheck = true; NeedsHullCheck = true;
NeedsRecalculation = true; NeedsRecalculation = true;
@@ -70,9 +70,9 @@ namespace Barotrauma
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
foreach (MapEntity entity in mapEntityList) foreach (MapEntity entity in HighlightedEntities)
{ {
if (entity == this || !entity.IsHighlighted || !(entity is Item) || !entity.IsMouseOn(position)) { continue; } if (entity == this|| entity is not Item || !entity.IsMouseOn(position)) { continue; }
if (((Item)entity).GetComponent<DockingPort>() == null) { continue; } if (((Item)entity).GetComponent<DockingPort>() == null) { continue; }
if (linkedTo.Contains(entity)) if (linkedTo.Contains(entity))
{ {
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
@@ -72,6 +73,8 @@ namespace Barotrauma
private RichString beaconStationActiveText, beaconStationInactiveText; private RichString beaconStationActiveText, beaconStationInactiveText;
private GUIComponent locationInfoOverlay;
/*private (Rectangle targetArea, string tip)? connectionTooltip; /*private (Rectangle targetArea, string tip)? connectionTooltip;
private string sanitizedConnectionTooltip; private string sanitizedConnectionTooltip;
private List<RichTextData> connectionTooltipRichTextData; private List<RichTextData> connectionTooltipRichTextData;
@@ -98,7 +101,7 @@ namespace Barotrauma
OnClicked = (btn, userData) => OnClicked = (btn, userData) =>
{ {
Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed)); Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed));
Generate(GameMain.GameSession.GameMode is CampaignMode campaign ? campaign.Settings : CampaignSettings.Empty); Generate(GameMain.GameSession?.Campaign);
InitProjectSpecific(); InitProjectSpecific();
return true; return true;
} }
@@ -186,7 +189,7 @@ namespace Barotrauma
private void LocationChanged(Location prevLocation, Location newLocation) private void LocationChanged(Location prevLocation, Location newLocation)
{ {
if (prevLocation == newLocation) return; if (prevLocation == newLocation) { return; }
//focus on starting location //focus on starting location
if (prevLocation != null) if (prevLocation != null)
{ {
@@ -210,11 +213,17 @@ namespace Barotrauma
currLocationIndicatorPos = CurrentLocation.MapPosition; currLocationIndicatorPos = CurrentLocation.MapPosition;
} }
RemoveFogOfWar(newLocation); if (newLocation.Visited)
{
RemoveFogOfWar(newLocation);
}
} }
partial void RemoveFogOfWarProjSpecific(Location location) => RemoveFogOfWar(location);
private void RemoveFogOfWar(Location location, bool removeFromAdjacentLocations = true) private void RemoveFogOfWar(Location location, bool removeFromAdjacentLocations = true)
{ {
if (mapTiles == null) { return; }
if (location == null) { return; } if (location == null) { return; }
var mapTile = generationParams.MapTiles.Values.FirstOrDefault().FirstOrDefault(); var mapTile = generationParams.MapTiles.Values.FirstOrDefault().FirstOrDefault();
@@ -252,27 +261,223 @@ namespace Barotrauma
return !tileDiscovered[MathHelper.Clamp(x, 0, tileDiscovered.Length), MathHelper.Clamp(y, 0, tileDiscovered.Length)]; 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) 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)] Vector2 textPos = pos + new Vector2(notification.Offset, -notification.TextSize.Y / 2);
.Replace("[previousname]", $"‖color:gui.orange‖{prevName}‖end‖")
.Replace("[name]", $"‖color:gui.orange‖{location.Name}‖end‖"); notification.Font.DrawStringWithColors(
location.LastTypeChangeMessage = msg; spriteBatch,
if (GameMain.Client != null) 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( mapNotifications.Add(new MapNotification(TextManager.Get(textTag).Value, GUIStyle.SubHeadingFont, mapNotifications, relatedLocation: null));
TextManager.Get("RadioAnnouncerName").Value,
msg,
Networking.ChatMessageType.Default,
sender: 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);
}
}
}
}
private void CreateLocationInfoOverlay(Location location)
{
locationInfoOverlay = new GUIFrame(new RectTransform(new Point(GUI.IntScale(350), GUI.IntScale(350)), GUI.Canvas), style: "GUIToolTip")
{
UserData = location
};
locationInfoOverlay.Color *= 0.8f;
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), locationInfoOverlay.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.02f
};
bool showReputation = hudVisibility > 0.0f && location.Type.HasOutpost && location.Reputation != null;
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.Name, font: GUIStyle.LargeFont) { Padding = Vector4.Zero };
if (!location.Type.Name.IsNullOrEmpty())
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
}
CreateSpacing(10);
if (!location.Type.Description.IsNullOrEmpty())
{
CreateTextWithIcon(location.Type.Description, location.Type.Sprite);
}
int highestSubTier = location.HighestSubmarineTierAvailable();
List<(SubmarineClass subClass, int tier)> overrideTiers = null;
if (location.CanHaveSubsForSale())
{
overrideTiers = new List<(SubmarineClass subClass, int tier)>();
foreach (SubmarineClass subClass in Enum.GetValues(typeof(SubmarineClass)))
{
if (subClass == SubmarineClass.Undefined) { continue; }
int highestClassTier = location.HighestSubmarineTierAvailable(subClass);
if (highestClassTier > 0 && highestClassTier > highestSubTier)
{
overrideTiers.Add((subClass, highestClassTier));
}
}
}
if (highestSubTier > 0)
{
CreateTextWithIcon(TextManager.GetWithVariable("advancedsub.all", "[tiernumber]", highestSubTier.ToString()), icon: null, style: "LocationOverlaySubmarineIcon");
}
if (overrideTiers != null)
{
foreach (var (subClass, tier) in overrideTiers)
{
CreateTextWithIcon(TextManager.GetWithVariable($"advancedsub.{subClass}", "[tiernumber]", tier.ToString()), icon: null, style: "LocationOverlaySubmarineIcon");
}
}
CreateSpacing(10);
void CreateTextWithIcon(LocalizedString text, Sprite icon, string style = null)
{
var textHolder = new GUILayoutGroup(new RectTransform(new Point(content.Rect.Width, (int)GUIStyle.Font.MeasureString(text).Y), content.RectTransform), isHorizontal: true)
{
Stretch = true,
CanBeFocused = true
};
var guiIcon =
style == null ?
new GUIImage(new RectTransform(Vector2.One * 1.25f, textHolder.RectTransform, scaleBasis: ScaleBasis.BothHeight), icon) :
new GUIImage(new RectTransform(Vector2.One * 1.25f, textHolder.RectTransform, scaleBasis: ScaleBasis.BothHeight), style);
var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.9f, 1.0f), textHolder.RectTransform), text);
textBlock.RectTransform.MinSize = new Point((int)textBlock.TextSize.X, 0);
textHolder.RectTransform.MinSize = new Point((int)textBlock.TextSize.X + guiIcon.Rect.Width, 0);
}
void CreateSpacing(int height)
{
new GUIFrame(new RectTransform(new Point(content.Rect.Width, GUI.IntScale(height)), content.RectTransform), style: null);
}
if (location.Faction != null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
RichString.Rich(TextManager.GetWithVariables("reputationgainnotification",
("[value]", string.Empty),
("[reputationname]", $"‖color:{XMLExtensions.ToStringHex(location.Faction.Prefab.IconColor)}‖{location.Faction.Prefab.Name}‖end‖"))))
{
Padding = Vector4.Zero
};
CreateSpacing(10);
var repBarHolder = new GUILayoutGroup(new RectTransform(new Point(content.Rect.Width, GUI.IntScale(25)), content.RectTransform), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.05f
};
new GUICustomComponent(new RectTransform(new Vector2(0.6f, 1.0f), repBarHolder.RectTransform), onDraw: (sb, component) =>
{
if (location.Reputation == null) { return; }
RoundSummary.DrawReputationBar(sb, component.Rect, location.Reputation.NormalizedValue);
});
new GUITextBlock(new RectTransform(new Vector2(0.4f, 1.0f), repBarHolder.RectTransform),
location.Reputation.GetFormattedReputationText(), textAlignment: Alignment.CenterRight);
new GUIImage(new RectTransform(new Vector2(0.25f, 0.5f), locationInfoOverlay.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.05f) },
location.Faction.Prefab.Icon, scaleToFit: true)
{
Color = location.Faction.Prefab.IconColor * 0.5f
};
CreateSpacing(20);
}
locationInfoOverlay.RectTransform.NonScaledSize =
new Point(
Math.Max(locationInfoOverlay.Rect.Width, (int)(content.Children.Max(c => c is GUITextBlock textBlock ? textBlock.TextSize.X : c.RectTransform.MinSize.X) * 1.2f)),
(int)(content.Children.Sum(c => c.Rect.Height) / content.RectTransform.RelativeSize.Y));
} }
partial void ClearAnimQueue() partial void ClearAnimQueue()
@@ -280,12 +485,13 @@ namespace Barotrauma
mapAnimQueue.Clear(); mapAnimQueue.Clear();
} }
public void Update(float deltaTime, GUICustomComponent mapContainer) public void Update(CampaignMode campaign, float deltaTime, GUICustomComponent mapContainer)
{ {
Rectangle rect = mapContainer.Rect; Rectangle rect = mapContainer.Rect;
var currentDisplayLocation = GameMain.GameSession?.Campaign?.GetCurrentDisplayLocation(); UpdateNotifications(deltaTime, mapContainer);
var currentDisplayLocation = campaign?.GetCurrentDisplayLocation();
if (currentDisplayLocation != null) if (currentDisplayLocation != null)
{ {
if (!currentDisplayLocation.Discovered) if (!currentDisplayLocation.Discovered)
@@ -345,10 +551,39 @@ namespace Barotrauma
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y); Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
Vector2 viewOffset = DrawOffset + drawOffsetNoise; Vector2 viewOffset = DrawOffset + drawOffsetNoise;
if (HighlightedLocation != null)
{
Vector2 highlightedLocationDrawPos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
if (locationInfoOverlay == null || locationInfoOverlay.UserData != HighlightedLocation)
{
CreateLocationInfoOverlay(HighlightedLocation);
}
Point offsetFromLocationIcon = new Point(GUI.IntScale(25));
var locationInfoRt = locationInfoOverlay.RectTransform;
if (locationInfoRt.Pivot == Pivot.BottomLeft || locationInfoRt.Pivot == Pivot.BottomRight)
{
offsetFromLocationIcon.Y = -offsetFromLocationIcon.Y;
}
if (locationInfoRt.Pivot == Pivot.TopRight || locationInfoRt.Pivot == Pivot.BottomRight)
{
offsetFromLocationIcon.X = -offsetFromLocationIcon.X;
}
locationInfoRt.ScreenSpaceOffset = highlightedLocationDrawPos.ToPoint() + offsetFromLocationIcon;
if (locationInfoOverlay.Rect.Bottom > rect.Bottom)
{
locationInfoRt.Pivot = Pivot.BottomLeft;
}
if (locationInfoOverlay.Rect.Right > rect.Right)
{
locationInfoRt.Pivot = locationInfoRt.Pivot == Pivot.TopLeft ? Pivot.TopRight : Pivot.BottomRight;
}
locationInfoOverlay?.AddToGUIUpdateList(order: 1);
}
float closestDist = 0.0f; float closestDist = 0.0f;
HighlightedLocation = null; HighlightedLocation = null;
if (GUI.MouseOn == null || GUI.MouseOn == mapContainer) if ((GUI.MouseOn == null || GUI.MouseOn == mapContainer))
{ {
for (int i = 0; i < Locations.Count; i++) for (int i = 0; i < Locations.Count; i++)
{ {
@@ -374,7 +609,7 @@ namespace Barotrauma
if (HighlightedLocation == null || dist < closestDist) if (HighlightedLocation == null || dist < closestDist)
{ {
closestDist = dist; closestDist = dist;
HighlightedLocation = location; HighlightedLocation = location;
} }
} }
} }
@@ -453,12 +688,13 @@ namespace Barotrauma
Level.Loaded.DebugSetEndLocation(null); Level.Loaded.DebugSetEndLocation(null);
Discover(CurrentLocation); Discover(CurrentLocation);
Visit(CurrentLocation);
OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation)); OnLocationChanged?.Invoke(new LocationChangeInfo(prevLocation, CurrentLocation));
SelectLocation(-1); SelectLocation(-1);
if (GameMain.Client == null) if (GameMain.Client == null)
{ {
CurrentLocation.CreateStores(); CurrentLocation.CreateStores();
ProgressWorld(); ProgressWorld(campaign);
Radiation?.OnStep(1); Radiation?.OnStep(1);
} }
else else
@@ -467,12 +703,6 @@ namespace Barotrauma
} }
} }
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift) && PlayerInput.PrimaryMouseButtonClicked() && HighlightedLocation != null)
{
int distance = DistanceToClosestLocationWithOutpost(HighlightedLocation, out Location foundLocation);
DebugConsole.NewMessage($"Distance to closest outpost from {HighlightedLocation.Name} to {foundLocation?.Name} is {distance}");
}
if (PlayerInput.PrimaryMouseButtonClicked() && HighlightedLocation == null) if (PlayerInput.PrimaryMouseButtonClicked() && HighlightedLocation == null)
{ {
SelectLocation(-1); SelectLocation(-1);
@@ -481,10 +711,10 @@ namespace Barotrauma
} }
} }
public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer) public void Draw(CampaignMode campaign, SpriteBatch spriteBatch, GUICustomComponent mapContainer)
{ {
tooltip = null; tooltip = null;
var currentDisplayLocation = GameMain.GameSession?.Campaign?.GetCurrentDisplayLocation(); var currentDisplayLocation = campaign?.GetCurrentDisplayLocation();
Rectangle rect = mapContainer.Rect; Rectangle rect = mapContainer.Rect;
@@ -501,6 +731,8 @@ namespace Barotrauma
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y); Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
float missionIconScale = generationParams.MissionIcon != null ? 18.0f / generationParams.MissionIcon.SourceRect.Width : 1.0f;
Rectangle prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle; Rectangle prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
spriteBatch.End(); spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, rect); spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, rect);
@@ -568,7 +800,9 @@ namespace Barotrauma
for (int i = 0; i < Locations.Count; i++) for (int i = 0; i < Locations.Count; i++)
{ {
Location location = Locations[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; Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite; Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
@@ -577,24 +811,54 @@ namespace Barotrauma
drawRect.X = (int)pos.X - drawRect.Width / 2; drawRect.X = (int)pos.X - drawRect.Width / 2;
drawRect.Y = (int)pos.Y - drawRect.Width / 2; drawRect.Y = (int)pos.Y - drawRect.Width / 2;
if (drawRect.X > rect.Right - GUI.IntScale(100) && generationParams.MissionIcon != null && location.AvailableMissions.Any())
{
Vector2 offScreenMissionIconPos = new Vector2(rect.Right - GUI.IntScale(50), drawRect.Center.Y);
generationParams.MissionIcon.Draw(spriteBatch,
offScreenMissionIconPos,
generationParams.IndicatorColor, scale: missionIconScale * zoom);
GUI.Arrow.Draw(spriteBatch,
offScreenMissionIconPos + Vector2.UnitX * generationParams.MissionIcon.size.X * missionIconScale * zoom,
generationParams.IndicatorColor, MathHelper.PiOver2, scale: 0.5f);
}
if (!rect.Intersects(drawRect)) { continue; } if (!rect.Intersects(drawRect)) { continue; }
Color color = location.Type.SpriteColor; Color color = location.Type.SpriteColor;
if (!location.Discovered) { color = Color.White; } if (!location.Visited) { color = Color.White; }
if (location.Connections.Find(c => c.Locations.Contains(currentDisplayLocation)) == null) if (location.Connections.Find(c => c.Locations.Contains(currentDisplayLocation)) == null)
{ {
color *= 0.5f; color *= 0.5f;
} }
float iconScale = location == currentDisplayLocation ? 1.2f : 1.0f; 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); 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 (location == currentDisplayLocation)
{ {
if (SelectedLocation != null) if (SelectedLocation != null)
@@ -626,7 +890,10 @@ namespace Barotrauma
{ {
Vector2 typeChangeIconPos = pos + new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom; Vector2 typeChangeIconPos = pos + new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
float typeChangeIconScale = 18.0f / generationParams.TypeChangeIcon.SourceRect.Width; 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 && if (Vector2.Distance(PlayerInput.MousePosition, typeChangeIconPos) < generationParams.TypeChangeIcon.SourceRect.Width * zoom &&
(tooltip == null || IsPreferredTooltip(typeChangeIconPos))) (tooltip == null || IsPreferredTooltip(typeChangeIconPos)))
{ {
@@ -635,14 +902,17 @@ namespace Barotrauma
} }
if (location != CurrentLocation && generationParams.MissionIcon != null) 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; 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); generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos)) 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))); tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
} }
} }
@@ -651,23 +921,19 @@ namespace Barotrauma
if (GameMain.DebugDraw) if (GameMain.DebugDraw)
{ {
Vector2 dPos = pos; Vector2 dPos = pos;
if (location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()) && location.Reputation != null) if (location == HighlightedLocation)
{ {
dPos.Y -= 80;
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;
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); if (PlayerInput.KeyDown(Keys.LeftShift))
bgRect.Inflate(8,8); {
Color barColor = ToolBox.GradientLerp(location.Reputation.NormalizedValue, Color.Red, Color.Yellow, Color.LightGreen); GUI.DrawString(spriteBatch, new Vector2(150,150), "Dist: " +
GUI.DrawRectangle(spriteBatch, bgRect, Color.Black * 0.8f, isFilled: true); GetDistanceToClosestLocationOrConnection(CurrentLocation, int.MaxValue, loc => loc == location), Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
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);
} }
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); GUI.DrawString(spriteBatch, dPos, $"Difficulty: {location.LevelData.Difficulty.FormatSingleDecimal()}", Color.White, Color.Black * 0.8f, 4, font: GUIStyle.SmallFont);
@@ -684,97 +950,6 @@ namespace Barotrauma
GUIComponent.DrawToolTip(spriteBatch, tooltip.Value.tip, tooltip.Value.targetArea); GUIComponent.DrawToolTip(spriteBatch, tooltip.Value.tip, tooltip.Value.targetArea);
drawRadiationTooltip = false; drawRadiationTooltip = false;
} }
else if (HighlightedLocation != null)
{
drawRadiationTooltip = false;
Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
pos.X += 50 * zoom;
pos.X = (int)pos.X;
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 descSize = HighlightedLocation.Type.Description.IsNullOrEmpty() ? Vector2.Zero : GUIStyle.SmallFont.MeasureString(HighlightedLocation.Type.Description);
Vector2 size = new Vector2(Math.Max(nameSize.X, Math.Max(typeSize.X, descSize.X)), nameSize.Y + typeSize.Y + descSize.Y);
int highestSubTier = HighlightedLocation.HighestSubmarineTierAvailable();
List<(SubmarineClass subClass, int tier)> overrideTiers = null;
if (HighlightedLocation.CanHaveSubsForSale())
{
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));
}
}
}
int subAvailabilityTextCount = (highestSubTier > 0 ? 1 : 0) + (overrideTiers?.Count ?? 0);
size.Y += subAvailabilityTextCount * GUIStyle.SmallFont.MeasureString(TextManager.Get("advancedsub.all")).Y;
bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
LocalizedString repLabelText = null, repValueText = null;
Vector2 repLabelSize = Vector2.Zero, repBarSize = Vector2.Zero;
if (showReputation)
{
repLabelText = TextManager.Get("reputation");
repLabelSize = GUIStyle.Font.MeasureString(repLabelText);
repBarSize = new Vector2(GUI.IntScale(200), repLabelSize.Y);
size.Y += 2 * repLabelSize.Y + GUI.IntScale(5) + repBarSize.Y;
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);
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);
DrawText(HighlightedLocation.Type.Name);
if (!HighlightedLocation.Type.Description.IsNullOrEmpty())
{
topLeftPos += new Vector2(0.0f, descSize.Y);
DrawText(HighlightedLocation.Type.Description, font: GUIStyle.SmallFont);
}
if (highestSubTier > 0)
{
DrawSubAvailabilityText("advancedsub.all", highestSubTier);
}
if (overrideTiers != null)
{
foreach (var (subClass, tier) in overrideTiers)
{
DrawSubAvailabilityText($"advancedsub.{subClass}", tier);
}
}
void DrawSubAvailabilityText(string tag, int tier)
{
topLeftPos += new Vector2(0.0f, typeSize.Y);
DrawText(TextManager.GetWithVariable(tag, "[tiernumber]", tier.ToString()), font: GUIStyle.SmallFont);
}
if (showReputation)
{
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) if (drawRadiationTooltip)
{ {
@@ -892,7 +1067,7 @@ namespace Barotrauma
} }
float a = 1.0f; float a = 1.0f;
if (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered) if (!connection.Locations[0].Visited && !connection.Locations[1].Visited)
{ {
if (IsInFogOfWar(connection.Locations[0])) if (IsInFogOfWar(connection.Locations[0]))
{ {
@@ -961,17 +1136,15 @@ namespace Barotrauma
if (connection.Locked) if (connection.Locked)
{ {
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1]; var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
var unlockEvent = var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == gateLocation.LevelData.Biome.Identifier) ??
EventPrefab.Prefabs.FirstOrDefault(ep => ep.UnlockPathEvent && ep.BiomeIdentifier == Identifier.Empty);
if (unlockEvent != null) if (unlockEvent != null)
{ {
Reputation unlockReputation = CurrentLocation.Reputation; Reputation unlockReputation = CurrentLocation.Reputation;
Faction unlockFaction = null; 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; unlockReputation = unlockFaction?.Reputation;
} }
@@ -1042,13 +1215,14 @@ namespace Barotrauma
private void DrawDecorativeHUD(SpriteBatch spriteBatch, Rectangle rect) private void DrawDecorativeHUD(SpriteBatch spriteBatch, Rectangle rect)
{ {
generationParams.DecorativeGraphSprite.Draw(spriteBatch, (int)((Timing.TotalTime * 5.0f) % generationParams.DecorativeGraphSprite.FrameCount), 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, GUI.DrawString(spriteBatch,
new Vector2(rect.Right - GUI.IntScale(170), rect.Y + GUI.IntScale(5)), 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); "JOVIAN FLUX " + ((cameraNoiseStrength + Rand.Range(-0.02f, 0.02f)) * 500), generationParams.IndicatorColor * hudVisibility, font: GUIStyle.SmallFont);
GUI.DrawString(spriteBatch, 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); "LAT " + (-DrawOffset.Y / 100.0f) + " LON " + (-DrawOffset.X / 100.0f), generationParams.IndicatorColor * hudVisibility, font: GUIStyle.SmallFont);
} }
@@ -36,7 +36,7 @@ namespace Barotrauma
public static List<MapEntity> CopiedList = new List<MapEntity>(); public static List<MapEntity> CopiedList = new List<MapEntity>();
private static List<MapEntity> highlightedList = new List<MapEntity>(); private static List<MapEntity> highlightedInEditorList = new List<MapEntity>();
private static float highlightTimer; private static float highlightTimer;
@@ -99,11 +99,24 @@ namespace Barotrauma
{ {
float depth = baseDepth float depth = baseDepth
//take texture into account to get entities with (roughly) the same base depth and texture to render consecutively to minimize texture swaps //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 + (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.000001f
+ ID % 100 * 0.000001f; + ID % 100 * 0.0000001f;
return Math.Min(depth, 1.0f); 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> /// <summary>
/// Update the selection logic in submarine editor /// Update the selection logic in submarine editor
/// </summary> /// </summary>
@@ -118,10 +131,7 @@ namespace Barotrauma
return; return;
} }
foreach (MapEntity e in mapEntityList) ClearHighlightedEntities();
{
e.isHighlighted = false;
}
if (DisableSelect) if (DisableSelect)
{ {
@@ -249,11 +259,10 @@ namespace Barotrauma
if (i == 0) highLightedEntity = e; if (i == 0) highLightedEntity = e;
} }
} }
UpdateHighlighting(highlightedEntities); UpdateHighlighting(highlightedEntities);
} }
if (highLightedEntity != null) highLightedEntity.isHighlighted = true; if (highLightedEntity != null) { highLightedEntity.IsHighlighted = true; }
} }
if (GUI.KeyboardDispatcher.Subscriber == null) if (GUI.KeyboardDispatcher.Subscriber == null)
@@ -275,7 +284,6 @@ namespace Barotrauma
if (startMovingPos != Vector2.Zero) if (startMovingPos != Vector2.Zero)
{ {
Item targetContainer = GetPotentialContainer(position, SelectedList); Item targetContainer = GetPotentialContainer(position, SelectedList);
if (targetContainer != null) { targetContainer.IsHighlighted = true; } if (targetContainer != null) { targetContainer.IsHighlighted = true; }
if (PlayerInput.PrimaryMouseButtonReleased()) if (PlayerInput.PrimaryMouseButtonReleased())
@@ -597,10 +605,10 @@ namespace Barotrauma
if (highlightedListBox != null) if (highlightedListBox != null)
{ {
if (GUI.MouseOn == highlightedListBox || highlightedListBox.IsParentOf(GUI.MouseOn)) return; if (GUI.MouseOn == highlightedListBox || highlightedListBox.IsParentOf(GUI.MouseOn)) return;
if (highlightedEntities.SequenceEqual(highlightedList)) return; if (highlightedEntities.SequenceEqual(highlightedInEditorList)) return;
} }
highlightedList = highlightedEntities; highlightedInEditorList = highlightedEntities;
highlightedListBox = new GUIListBox(new RectTransform(new Point(180, highlightedEntities.Count * 18 + 5), GUI.Canvas) highlightedListBox = new GUIListBox(new RectTransform(new Point(180, highlightedEntities.Count * 18 + 5), GUI.Canvas)
{ {
@@ -1083,7 +1091,7 @@ namespace Barotrauma
private void UpdateResizing(Camera cam) private void UpdateResizing(Camera cam)
{ {
isHighlighted = true; IsHighlighted = true;
int startX = ResizeHorizontal ? -1 : 0; int startX = ResizeHorizontal ? -1 : 0;
int StartY = ResizeVertical ? -1 : 0; int StartY = ResizeVertical ? -1 : 0;
@@ -1184,7 +1192,7 @@ namespace Barotrauma
private void DrawResizing(SpriteBatch spriteBatch, Camera cam) private void DrawResizing(SpriteBatch spriteBatch, Camera cam)
{ {
isHighlighted = true; IsHighlighted = true;
int startX = ResizeHorizontal ? -1 : 0; int startX = ResizeHorizontal ? -1 : 0;
int StartY = ResizeVertical ? -1 : 0; int StartY = ResizeVertical ? -1 : 0;
@@ -226,6 +226,9 @@ namespace Barotrauma
min.Y = Math.Min(worldPos.Y - decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * scale, min.Y); min.Y = Math.Min(worldPos.Y - decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * scale, min.Y);
max.Y = Math.Max(worldPos.Y + decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * scale, max.Y); max.Y = Math.Max(worldPos.Y + decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * scale, max.Y);
} }
Vector2 offset = GetCollapseEffectOffset();
min += offset;
max += offset;
if (min.X > worldView.Right || max.X < worldView.X) { return false; } if (min.X > worldView.Right || max.X < worldView.X) { return false; }
if (min.Y > worldView.Y || max.Y < worldView.Y - worldView.Height) { return false; } if (min.Y > worldView.Y || max.Y < worldView.Y - worldView.Height) { return false; }
@@ -295,6 +298,7 @@ namespace Barotrauma
if (isWiringMode) { color *= 0.15f; } if (isWiringMode) { color *= 0.15f; }
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition; Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
drawOffset += GetCollapseEffectOffset();
float depth = GetDrawDepth(); float depth = GetDrawDepth();
@@ -35,6 +35,13 @@ namespace Barotrauma
Rectangle camView = cam.WorldView; Rectangle camView = cam.WorldView;
camView = new Rectangle(camView.X - CullMargin, camView.Y + CullMargin, camView.Width + CullMargin * 2, camView.Height + CullMargin * 2); 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 && if (Math.Abs(camView.X - prevCullArea.X) < CullMoveThreshold &&
Math.Abs(camView.Y - prevCullArea.Y) < CullMoveThreshold && Math.Abs(camView.Y - prevCullArea.Y) < CullMoveThreshold &&
Math.Abs(camView.Right - prevCullArea.Right) < CullMoveThreshold && Math.Abs(camView.Right - prevCullArea.Right) < CullMoveThreshold &&
@@ -116,7 +116,7 @@ namespace Barotrauma
bool isMouseOnComponent = GUI.MouseOn == component; bool isMouseOnComponent = GUI.MouseOn == component;
camera.MoveCamera(deltaTime, allowZoom: isMouseOnComponent, followSub: false); camera.MoveCamera(deltaTime, allowZoom: isMouseOnComponent, followSub: false);
if (isMouseOnComponent && if (isMouseOnComponent &&
(PlayerInput.MidButtonHeld() || PlayerInput.LeftButtonHeld())) (PlayerInput.MidButtonHeld() || PlayerInput.PrimaryMouseButtonHeld()))
{ {
Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 60.0f / camera.Zoom; Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 60.0f / camera.Zoom;
moveSpeed.X = -moveSpeed.X; moveSpeed.X = -moveSpeed.X;
@@ -39,7 +39,7 @@ namespace Barotrauma
{ {
Color clr = CurrentHull == null ? Color.DodgerBlue : GUIStyle.Green; Color clr = CurrentHull == null ? Color.DodgerBlue : GUIStyle.Green;
if (spawnType != SpawnType.Path) { clr = Color.Gray; } if (spawnType != SpawnType.Path) { clr = Color.Gray; }
if (isObstructed) if (!IsTraversable)
{ {
clr = Color.Black; clr = Color.Black;
} }
@@ -84,7 +84,7 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
drawPos, drawPos,
new Vector2(e.DrawPosition.X, -e.DrawPosition.Y), new Vector2(e.DrawPosition.X, -e.DrawPosition.Y),
(isObstructed ? Color.Gray : GUIStyle.Green) * 0.7f, width: 5, depth: 0.002f); (IsTraversable ? GUIStyle.Green : Color.Gray) * 0.7f, width: 5, depth: 0.002f);
} }
if (ConnectedGap != null) if (ConnectedGap != null)
{ {
@@ -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, GUIStyle.SmallFont.DrawString(spriteBatch,
ID.ToString(), ID.ToString(),
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30), new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30),
@@ -170,9 +175,9 @@ namespace Barotrauma
if (PlayerInput.KeyDown(Keys.Space)) if (PlayerInput.KeyDown(Keys.Space))
{ {
foreach (MapEntity e in mapEntityList) foreach (MapEntity e in HighlightedEntities)
{ {
if (!(e is WayPoint) || e == this || !e.IsHighlighted) { continue; } if (e is not WayPoint || e == this) { continue; }
if (linkedTo.Contains(e)) if (linkedTo.Contains(e))
{ {
@@ -251,6 +256,7 @@ namespace Barotrauma
private bool ChangeSpawnType(GUIButton button, object obj) private bool ChangeSpawnType(GUIButton button, object obj)
{ {
var prevSpawnType = spawnType;
GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock; GUITextBlock spawnTypeText = button.Parent.GetChildByUserData("spawntypetext") as GUITextBlock;
var values = (SpawnType[])Enum.GetValues(typeof(SpawnType)); var values = (SpawnType[])Enum.GetValues(typeof(SpawnType));
int currIndex = values.IndexOf(spawnType); int currIndex = values.IndexOf(spawnType);
@@ -267,6 +273,7 @@ namespace Barotrauma
} }
spawnType = values[currIndex]; spawnType = values[currIndex];
spawnTypeText.Text = spawnType.ToString(); spawnTypeText.Text = spawnType.ToString();
if (spawnType == SpawnType.ExitPoint || prevSpawnType == SpawnType.ExitPoint) { CreateEditingHUD(); }
return true; return true;
} }
@@ -412,6 +419,28 @@ namespace Barotrauma
textBox.Text = string.Join(",", tags); textBox.Text = string.Join(",", tags);
textBox.Flash(GUIStyle.Green); 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( editingHUD.RectTransform.Resize(new Point(
@@ -332,7 +332,6 @@ namespace Barotrauma.Networking
FileSize = 0 FileSize = 0
}; };
Md5Hash.Cache.Remove(directTransfer.FilePath);
OnFinished(directTransfer); OnFinished(directTransfer);
} }
break; break;
@@ -414,7 +413,6 @@ namespace Barotrauma.Networking
{ {
finishedTransfers.Add((transferId, Timing.TotalTime)); finishedTransfers.Add((transferId, Timing.TotalTime));
StopTransfer(activeTransfer); StopTransfer(activeTransfer);
Md5Hash.Cache.Remove(activeTransfer.FilePath);
OnFinished(activeTransfer); OnFinished(activeTransfer);
} }
else else
@@ -326,7 +326,7 @@ namespace Barotrauma.Networking
return serverEndpoint switch return serverEndpoint switch
{ {
LidgrenEndpoint lidgrenEndpoint => new LidgrenClientPeer(lidgrenEndpoint, callbacks, ownerKey), LidgrenEndpoint lidgrenEndpoint => new LidgrenClientPeer(lidgrenEndpoint, callbacks, ownerKey),
SteamP2PEndpoint _ when ownerKey is Some<int> { Value: var key } => new SteamP2POwnerPeer(callbacks, key), SteamP2PEndpoint _ when ownerKey.TryUnwrap(out var key) => new SteamP2POwnerPeer(callbacks, key),
SteamP2PEndpoint steamP2PServerEndpoint when ownerKey.IsNone() => new SteamP2PClientPeer(steamP2PServerEndpoint, callbacks), SteamP2PEndpoint steamP2PServerEndpoint when ownerKey.IsNone() => new SteamP2PClientPeer(steamP2PServerEndpoint, callbacks),
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
@@ -988,7 +988,7 @@ namespace Barotrauma.Networking
GameMain.ModDownloadScreen.Reset(); GameMain.ModDownloadScreen.Reset();
ContentPackageManager.EnabledPackages.Restore(); ContentPackageManager.EnabledPackages.Restore();
CampaignMode.StartRoundCancellationToken?.Cancel(); GameMain.GameSession?.Campaign?.CancelStartRound();
if (SteamManager.IsInitialized) if (SteamManager.IsInitialized)
{ {
@@ -2623,7 +2623,13 @@ namespace Barotrauma.Networking
using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(msg)) using (var segmentTable = SegmentTableWriter<ClientNetSegment>.StartWriting(msg))
{ {
segmentTable.StartNewSegment(ClientNetSegment.Vote); segmentTable.StartNewSegment(ClientNetSegment.Vote);
Voting.ClientWrite(msg, voteType, data); bool succeeded = Voting.ClientWrite(msg, voteType, data);
if (!succeeded)
{
throw new Exception(
$"Failed to write vote of type {voteType}: " +
$"data was of invalid type {data?.GetType().Name ?? "NULL"}");
}
} }
ClientPeer.Send(msg, DeliveryMethod.Reliable); ClientPeer.Send(msg, DeliveryMethod.Reliable);
@@ -156,7 +156,7 @@ namespace Barotrauma.Networking
var packet = INetSerializableStruct.Read<ClientSteamTicketAndVersionPacket>(inc); var packet = INetSerializableStruct.Read<ClientSteamTicketAndVersionPacket>(inc);
packet.SteamAuthTicket.TryUnwrap(out byte[] ticket); packet.SteamAuthTicket.TryUnwrap(out var ticket);
Steamworks.BeginAuthResult authSessionStartState = SteamManager.StartAuthSession(ticket, steamId); Steamworks.BeginAuthResult authSessionStartState = SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK) if (authSessionStartState != Steamworks.BeginAuthResult.OK)
@@ -1,5 +1,6 @@
#nullable enable #nullable enable
using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -69,6 +70,9 @@ namespace Barotrauma.Networking
[Serialize(PlayStyle.Casual, IsPropertySaveable.Yes)] [Serialize(PlayStyle.Casual, IsPropertySaveable.Yes)]
public PlayStyle PlayStyle { get; set; } public PlayStyle PlayStyle { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public LanguageIdentifier Language { get; set; }
public Version GameVersion { get; set; } = new Version(0, 0, 0, 0); public Version GameVersion { get; set; } = new Version(0, 0, 0, 0);
@@ -281,7 +285,7 @@ namespace Barotrauma.Networking
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
float elementHeight = 0.075f; const float elementHeight = 0.075f;
// Spacing // Spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.025f), content.RectTransform), style: null); new GUIFrame(new RectTransform(new Vector2(1.0f, 0.025f), content.RectTransform), style: null);
@@ -294,6 +298,11 @@ namespace Barotrauma.Networking
serverMsg.Content.RectTransform.SizeChanged += () => { msgText.CalculateHeightFromText(); }; serverMsg.Content.RectTransform.SizeChanged += () => { msgText.CalculateHeightFromText(); };
msgText.RectTransform.SizeChanged += () => { serverMsg.UpdateScrollBarSize(); }; msgText.RectTransform.SizeChanged += () => { serverMsg.UpdateScrollBarSize(); };
var languageLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("Language"));
new GUITextBlock(new RectTransform(Vector2.One, languageLabel.RectTransform),
ServerLanguageOptions.Options.FirstOrNull(o => o.Identifier == Language)?.Label ?? TextManager.Get("Unknown"),
textAlignment: Alignment.Right);
var gameMode = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("GameMode")); var gameMode = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get("GameMode"));
new GUITextBlock(new RectTransform(Vector2.One, gameMode.RectTransform), new GUITextBlock(new RectTransform(Vector2.One, gameMode.RectTransform),
TextManager.Get(GameMode.IsEmpty ? "Unknown" : "GameMode." + GameMode).Fallback(GameMode.Value), TextManager.Get(GameMode.IsEmpty ? "Unknown" : "GameMode." + GameMode).Fallback(GameMode.Value),
@@ -363,7 +372,7 @@ namespace Barotrauma.Networking
packageText.Selected = true; packageText.Selected = true;
} }
//workshop download link found //workshop download link found
else if (package.Id is Some<ContentPackageId> { Value: var ugcId } && ugcId is SteamWorkshopId) else if (package.Id.TryUnwrap(out var ugcId) && ugcId is SteamWorkshopId)
{ {
packageText.ToolTip = TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", package.Name); packageText.ToolTip = TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", package.Name);
} }
@@ -417,6 +426,7 @@ namespace Barotrauma.Networking
GameMode = valueGetter("gamemode")?.ToIdentifier() ?? Identifier.Empty; GameMode = valueGetter("gamemode")?.ToIdentifier() ?? Identifier.Empty;
if (Enum.TryParse(valueGetter("traitors"), out YesNoMaybe traitorsEnabled)) { TraitorsEnabled = traitorsEnabled; } if (Enum.TryParse(valueGetter("traitors"), out YesNoMaybe traitorsEnabled)) { TraitorsEnabled = traitorsEnabled; }
if (Enum.TryParse(valueGetter("playstyle"), out PlayStyle playStyle)) { PlayStyle = playStyle; } if (Enum.TryParse(valueGetter("playstyle"), out PlayStyle playStyle)) { PlayStyle = playStyle; }
Language = valueGetter("language")?.ToLanguageIdentifier() ?? LanguageIdentifier.None;
ContentPackages = ExtractContentPackageInfo(valueGetter).ToImmutableArray(); ContentPackages = ExtractContentPackageInfo(valueGetter).ToImmutableArray();
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using Barotrauma.Steam;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -78,7 +79,7 @@ namespace Barotrauma.Networking
} }
} }
private Dictionary<Identifier, bool> tempMonsterEnabled; private Dictionary<Identifier, bool> tempMonsterEnabled;
partial void InitProjSpecific() partial void InitProjSpecific()
{ {
var properties = TypeDescriptor.GetProperties(GetType()).Cast<PropertyDescriptor>(); var properties = TypeDescriptor.GetProperties(GetType()).Cast<PropertyDescriptor>();
@@ -367,6 +368,15 @@ namespace Barotrauma.Networking
//*********************************************** //***********************************************
// Language
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverTab.RectTransform), TextManager.Get("Language"), font: GUIStyle.SubHeadingFont);
var languageDD = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.02f), serverTab.RectTransform));
foreach (var language in ServerLanguageOptions.Options)
{
languageDD.AddItem(language.Label, language.Identifier);
}
GetPropertyData(nameof(Language)).AssignGUIComponent(languageDD);
//changing server visibility on the fly is not supported in dedicated servers //changing server visibility on the fly is not supported in dedicated servers
if (GameMain.Client?.ClientPeer is not LidgrenClientPeer) if (GameMain.Client?.ClientPeer is not LidgrenClientPeer)
{ {
@@ -116,7 +116,9 @@ namespace Barotrauma.Networking
bool spectating = Character.Controlled == null; bool spectating = Character.Controlled == null;
float rangeMultiplier = spectating ? 2.0f : 1.0f; float rangeMultiplier = spectating ? 2.0f : 1.0f;
WifiComponent radio = null; WifiComponent radio = null;
var messageType = !client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) ? ChatMessageType.Radio : ChatMessageType.Default; var messageType =
!client.VoipQueue.ForceLocal && ChatMessage.CanUseRadio(client.Character, out radio) && ChatMessage.CanUseRadio(Character.Controlled) ?
ChatMessageType.Radio : ChatMessageType.Default;
client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]); client.Character.ShowSpeechBubble(1.25f, ChatMessage.MessageColor[(int)messageType]);
client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters; client.VoipSound.UseRadioFilter = messageType == ChatMessageType.Radio && !GameSettings.CurrentConfig.Audio.DisableVoiceChatFilters;
@@ -13,13 +13,11 @@ namespace Barotrauma
{ {
public SubmarineInfo SubmarineInfo { get; set; } public SubmarineInfo SubmarineInfo { get; set; }
public bool TransferItems { get; set; } public bool TransferItems { get; set; }
public int DeliveryFee { get; set; }
public SubmarineVoteInfo(SubmarineInfo submarineInfo, bool transferItems, int deliveryFee) public SubmarineVoteInfo(SubmarineInfo submarineInfo, bool transferItems)
{ {
SubmarineInfo = submarineInfo; SubmarineInfo = submarineInfo;
TransferItems = transferItems; TransferItems = transferItems;
DeliveryFee = deliveryFee;
} }
} }
@@ -128,64 +126,72 @@ namespace Barotrauma
UpdateVoteTexts(connectedClients, VoteType.Sub); UpdateVoteTexts(connectedClients, VoteType.Sub);
} }
public void ClientWrite(IWriteMessage msg, VoteType voteType, object data) /// <summary>
/// Returns true if the given data is valid for the given vote type,
/// returns false otherwise. If it returns false, the message must
/// be discarded or reset by the caller, as it is now malformed :)
/// </summary>
public bool ClientWrite(IWriteMessage msg, VoteType voteType, object data)
{ {
msg.WriteByte((byte)voteType); msg.WriteByte((byte)voteType);
switch (voteType) switch (voteType)
{ {
case VoteType.Sub: case VoteType.Sub:
if (!(data is SubmarineInfo sub)) { return; } if (data is not SubmarineInfo sub) { return false; }
msg.WriteInt32(sub.EqualityCheckVal); msg.WriteInt32(sub.EqualityCheckVal);
if (sub.EqualityCheckVal == 0) if (sub.EqualityCheckVal <= 0)
{ {
//sub doesn't exist client-side, use hash to let the server know which one we voted for //sub doesn't exist client-side, use hash to let the server know which one we voted for
msg.WriteString(sub.MD5Hash.StringRepresentation); msg.WriteString(sub.MD5Hash.StringRepresentation);
} }
break; break;
case VoteType.Mode: case VoteType.Mode:
if (!(data is GameModePreset gameMode)) { return; } if (data is not GameModePreset gameMode) { return false; }
msg.WriteIdentifier(gameMode.Identifier); msg.WriteIdentifier(gameMode.Identifier);
break; break;
case VoteType.EndRound: case VoteType.EndRound:
if (!(data is bool)) { return; } if (data is not bool endRound) { return false; }
msg.WriteBoolean((bool)data); msg.WriteBoolean(endRound);
break; break;
case VoteType.Kick: case VoteType.Kick:
if (!(data is Client votedClient)) { return; } if (data is not Client votedClient) { return false; }
msg.WriteByte(votedClient.SessionId); msg.WriteByte(votedClient.SessionId);
break; break;
case VoteType.StartRound: case VoteType.StartRound:
if (!(data is bool)) { return; } if (data is not bool startRound) { return false; }
msg.WriteBoolean((bool)data); msg.WriteBoolean(startRound);
break; break;
case VoteType.PurchaseAndSwitchSub: case VoteType.PurchaseAndSwitchSub:
case VoteType.PurchaseSub: case VoteType.PurchaseSub:
case VoteType.SwitchSub: case VoteType.SwitchSub:
if (data is (SubmarineInfo voteSub, bool transferItems)) switch (data)
{
//initiate sub vote
msg.WriteBoolean(true);
msg.WriteString(voteSub.Name);
msg.WriteBoolean(transferItems);
}
else
{ {
// vote case (SubmarineInfo voteSub, bool transferItems):
if (!(data is int)) { return; } //initiate sub vote
msg.WriteBoolean(false); msg.WriteBoolean(true);
msg.WriteInt32((int)data); msg.WriteString(voteSub.Name);
msg.WriteBoolean(transferItems);
break;
case int vote:
// vote
msg.WriteBoolean(false);
msg.WriteInt32(vote);
break;
default:
return false;
} }
break; break;
case VoteType.TransferMoney: case VoteType.TransferMoney:
if (!(data is int)) { return; } if (data is not int money) { return false; }
msg.WriteBoolean(false); //not initiating a vote msg.WriteBoolean(false); //not initiating a vote
msg.WriteInt32((int)data); msg.WriteInt32(money);
break; break;
} }
msg.WritePadBits(); msg.WritePadBits();
return true;
} }
public void ClientRead(IReadMessage inc) public void ClientRead(IReadMessage inc)
@@ -322,33 +328,34 @@ namespace Barotrauma
case VoteType.PurchaseAndSwitchSub: case VoteType.PurchaseAndSwitchSub:
case VoteType.SwitchSub: case VoteType.SwitchSub:
string subName2 = inc.ReadString(); string subName2 = inc.ReadString();
var submarineInfo = GameMain.GameSession.OwnedSubmarines.FirstOrDefault(s => s.Name == subName2) ?? GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2);
bool transferItems = inc.ReadBoolean(); bool transferItems = inc.ReadBoolean();
int deliveryFee = inc.ReadInt16(); if (GameMain.GameSession != null)
if (submarineInfo == null)
{ {
DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted"); var submarineInfo = GameMain.GameSession.OwnedSubmarines.FirstOrDefault(s => s.Name == subName2) ?? GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2);
return; if (submarineInfo == null)
{
DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
return;
}
submarineVoteInfo = new SubmarineVoteInfo(submarineInfo, transferItems);
} }
submarineVoteInfo = new SubmarineVoteInfo(submarineInfo, transferItems, deliveryFee);
break; break;
} }
GameMain.Client.VotingInterface?.EndVote(passed, yesClientCount, noClientCount); GameMain.Client.VotingInterface?.EndVote(passed, yesClientCount, noClientCount);
if (passed && submarineVoteInfo.SubmarineInfo is { } subInfo) if (passed && submarineVoteInfo.SubmarineInfo is { } subInfo)
{ {
switch (voteType) switch (voteType)
{ {
case VoteType.PurchaseAndSwitchSub: case VoteType.PurchaseAndSwitchSub:
GameMain.GameSession.PurchaseSubmarine(subInfo); GameMain.GameSession.PurchaseSubmarine(subInfo);
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems, 0); GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
break; break;
case VoteType.PurchaseSub: case VoteType.PurchaseSub:
GameMain.GameSession.PurchaseSubmarine(subInfo); GameMain.GameSession.PurchaseSubmarine(subInfo);
break; break;
case VoteType.SwitchSub: case VoteType.SwitchSub:
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems, submarineVoteInfo.DeliveryFee); GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
break; break;
} }
@@ -85,6 +85,9 @@ namespace Barotrauma.Particles
[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.")] [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; } 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)] [Editable, Serialize("1,1,1,1", IsPropertySaveable.Yes)]
public Color ColorMultiplier { get; set; } public Color ColorMultiplier { get; set; }
@@ -203,7 +206,7 @@ namespace Barotrauma.Particles
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax); 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) if (particle != null)
{ {
@@ -75,21 +75,21 @@ namespace Barotrauma.Particles
return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer, tracerPoints:tracerPoints); 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 (prefab == null || prefab.Sprites.Count == 0) { return null; }
if (particleCount >= MaxParticles) if (particleCount >= MaxParticles)
{ {
for (int i = 0; i < particleCount; i++) 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); RemoveParticle(i);
break; break;
} }
} }
if (particleCount >= MaxParticles) { return null; } if (particleCount >= MaxParticles) { return null; }
} }
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity); Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
@@ -109,26 +109,30 @@ namespace Barotrauma.Particles
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist); Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; } if (!prefab.DrawAlways)
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; } {
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(); } 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++; particleCount++;
return particles[particleCount - 1]; return particles[particleCount - 1];
} }
public List<ParticlePrefab> GetPrefabList() public static List<ParticlePrefab> GetPrefabList()
{ {
return ParticlePrefab.Prefabs.ToList(); 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) private void RemoveParticle(int index)
@@ -170,7 +174,7 @@ namespace Barotrauma.Particles
remove = true; 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?")] [Editable, Serialize(false, IsPropertySaveable.No, description: "Should the particle be always rendered on top of entities?")]
public bool DrawOnTop { get; private set; } 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.")] [Editable, Serialize(ParticleBlendState.AlphaBlend, IsPropertySaveable.No, description: "The type of blending to use when rendering the particle.")]
public ParticleBlendState BlendState { get; private set; } public ParticleBlendState BlendState { get; private set; }
@@ -79,13 +79,13 @@ namespace Barotrauma
new Vector2(DrawPosition.X, -DrawPosition.Y), new Vector2(DrawPosition.X, -DrawPosition.Y),
Color.Cyan, 0, 5); Color.Cyan, 0, 5);
} }
if (bodyShapeTexture == null && IsValidShape(radius, height, width)) if (bodyShapeTexture == null && IsValidShape(Radius, Height, Width))
{ {
switch (BodyShape) switch (BodyShape)
{ {
case Shape.Rectangle: 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) if (maxSize > 128.0f)
{ {
bodyShapeTextureScale = 128.0f / maxSize; bodyShapeTextureScale = 128.0f / maxSize;
@@ -96,14 +96,14 @@ namespace Barotrauma
} }
bodyShapeTexture = GUI.CreateRectangle( bodyShapeTexture = GUI.CreateRectangle(
(int)ConvertUnits.ToDisplayUnits(width * bodyShapeTextureScale), (int)ConvertUnits.ToDisplayUnits(Width * bodyShapeTextureScale),
(int)ConvertUnits.ToDisplayUnits(height * bodyShapeTextureScale)); (int)ConvertUnits.ToDisplayUnits(Height * bodyShapeTextureScale));
break; break;
} }
case Shape.Capsule: case Shape.Capsule:
case Shape.HorizontalCapsule: 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) if (maxSize > 128.0f)
{ {
bodyShapeTextureScale = 128.0f / maxSize; bodyShapeTextureScale = 128.0f / maxSize;
@@ -114,20 +114,20 @@ namespace Barotrauma
} }
bodyShapeTexture = GUI.CreateCapsule( bodyShapeTexture = GUI.CreateCapsule(
(int)ConvertUnits.ToDisplayUnits(radius * bodyShapeTextureScale), (int)ConvertUnits.ToDisplayUnits(Radius * bodyShapeTextureScale),
(int)ConvertUnits.ToDisplayUnits(Math.Max(height, width) * bodyShapeTextureScale)); (int)ConvertUnits.ToDisplayUnits(Math.Max(Height, Width) * bodyShapeTextureScale));
break; break;
} }
case Shape.Circle: 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 else
{ {
bodyShapeTextureScale = 1.0f; bodyShapeTextureScale = 1.0f;
} }
bodyShapeTexture = GUI.CreateCircle((int)ConvertUnits.ToDisplayUnits(radius * bodyShapeTextureScale)); bodyShapeTexture = GUI.CreateCircle((int)ConvertUnits.ToDisplayUnits(Radius * bodyShapeTextureScale));
break; break;
default: default:
throw new NotImplementedException(); throw new NotImplementedException();
@@ -11,15 +11,13 @@ namespace Barotrauma
public enum MouseButton public enum MouseButton
{ {
None = -1, None = -1,
LeftMouse = 0, PrimaryMouse = 0,
RightMouse = 1, SecondaryMouse = 1,
MiddleMouse = 2, MiddleMouse = 2,
MouseButton4 = 3, MouseButton4 = 3,
MouseButton5 = 4, MouseButton5 = 4,
MouseWheelUp = 5, MouseWheelUp = 5,
MouseWheelDown = 6, MouseWheelDown = 6
PrimaryMouse,
SecondaryMouse
} }
public class KeyOrMouse public class KeyOrMouse
@@ -65,10 +63,6 @@ namespace Barotrauma
return PlayerInput.PrimaryMouseButtonHeld(); return PlayerInput.PrimaryMouseButtonHeld();
case MouseButton.SecondaryMouse: case MouseButton.SecondaryMouse:
return PlayerInput.SecondaryMouseButtonHeld(); return PlayerInput.SecondaryMouseButtonHeld();
case MouseButton.LeftMouse:
return PlayerInput.LeftButtonHeld();
case MouseButton.RightMouse:
return PlayerInput.RightButtonHeld();
case MouseButton.MiddleMouse: case MouseButton.MiddleMouse:
return PlayerInput.MidButtonHeld(); return PlayerInput.MidButtonHeld();
case MouseButton.MouseButton4: case MouseButton.MouseButton4:
@@ -95,10 +89,6 @@ namespace Barotrauma
return PlayerInput.PrimaryMouseButtonClicked(); return PlayerInput.PrimaryMouseButtonClicked();
case MouseButton.SecondaryMouse: case MouseButton.SecondaryMouse:
return PlayerInput.SecondaryMouseButtonClicked(); return PlayerInput.SecondaryMouseButtonClicked();
case MouseButton.LeftMouse:
return PlayerInput.LeftButtonClicked();
case MouseButton.RightMouse:
return PlayerInput.RightButtonClicked();
case MouseButton.MiddleMouse: case MouseButton.MiddleMouse:
return PlayerInput.MidButtonClicked(); return PlayerInput.MidButtonClicked();
case MouseButton.MouseButton4: case MouseButton.MouseButton4:
@@ -218,11 +208,11 @@ namespace Barotrauma
switch (MouseButton) switch (MouseButton)
{ {
case MouseButton.PrimaryMouse: case MouseButton.PrimaryMouse:
return PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse"); return PlayerInput.PrimaryMouseLabel;
case MouseButton.SecondaryMouse: case MouseButton.SecondaryMouse:
return PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse"); return PlayerInput.SecondaryMouseLabel;
default: default:
return TextManager.Get("input." + MouseButton.ToString().ToLowerInvariant()); return TextManager.Get($"Input.{MouseButton}");
} }
} }
else else
@@ -270,6 +260,9 @@ namespace Barotrauma
} }
#endif #endif
public static readonly LocalizedString PrimaryMouseLabel = TextManager.Get($"Input.{(!MouseButtonsSwapped() ? "Left" : "Right")}Mouse");
public static readonly LocalizedString SecondaryMouseLabel = TextManager.Get($"Input.{(!MouseButtonsSwapped() ? "Right" : "Left")}Mouse");
public static Vector2 MousePosition public static Vector2 MousePosition
{ {
get { return new Vector2(mouseState.Position.X, mouseState.Position.Y); } get { return new Vector2(mouseState.Position.X, mouseState.Position.Y); }
@@ -317,120 +310,48 @@ namespace Barotrauma
} }
public static bool PrimaryMouseButtonHeld() public static bool PrimaryMouseButtonHeld()
{
if (MouseButtonsSwapped())
{
return RightButtonHeld();
}
return LeftButtonHeld();
}
public static bool PrimaryMouseButtonDown()
{
if (MouseButtonsSwapped())
{
return RightButtonDown();
}
return LeftButtonDown();
}
public static bool PrimaryMouseButtonReleased()
{
if (MouseButtonsSwapped())
{
return RightButtonReleased();
}
return LeftButtonReleased();
}
public static bool PrimaryMouseButtonClicked()
{
if (MouseButtonsSwapped())
{
return RightButtonClicked();
}
return LeftButtonClicked();
}
public static bool SecondaryMouseButtonHeld()
{
if (!MouseButtonsSwapped())
{
return RightButtonHeld();
}
return LeftButtonHeld();
}
public static bool SecondaryMouseButtonDown()
{
if (!MouseButtonsSwapped())
{
return RightButtonDown();
}
return LeftButtonDown();
}
public static bool SecondaryMouseButtonReleased()
{
if (!MouseButtonsSwapped())
{
return RightButtonReleased();
}
return LeftButtonReleased();
}
public static bool SecondaryMouseButtonClicked()
{
if (!MouseButtonsSwapped())
{
return RightButtonClicked();
}
return LeftButtonClicked();
}
public static bool LeftButtonHeld()
{ {
return AllowInput && mouseState.LeftButton == ButtonState.Pressed; return AllowInput && mouseState.LeftButton == ButtonState.Pressed;
} }
public static bool LeftButtonDown() public static bool PrimaryMouseButtonDown()
{ {
return AllowInput && return AllowInput &&
oldMouseState.LeftButton == ButtonState.Released && oldMouseState.LeftButton == ButtonState.Released &&
mouseState.LeftButton == ButtonState.Pressed; mouseState.LeftButton == ButtonState.Pressed;
} }
public static bool LeftButtonReleased() public static bool PrimaryMouseButtonReleased()
{ {
return AllowInput && mouseState.LeftButton == ButtonState.Released; return AllowInput && mouseState.LeftButton == ButtonState.Released;
} }
public static bool LeftButtonClicked() public static bool PrimaryMouseButtonClicked()
{ {
return (AllowInput && return (AllowInput &&
oldMouseState.LeftButton == ButtonState.Pressed oldMouseState.LeftButton == ButtonState.Pressed
&& mouseState.LeftButton == ButtonState.Released); && mouseState.LeftButton == ButtonState.Released);
} }
public static bool RightButtonHeld() public static bool SecondaryMouseButtonHeld()
{ {
return AllowInput && mouseState.RightButton == ButtonState.Pressed; return AllowInput && mouseState.RightButton == ButtonState.Pressed;
} }
public static bool RightButtonDown() public static bool SecondaryMouseButtonDown()
{ {
return AllowInput && return AllowInput &&
oldMouseState.RightButton == ButtonState.Released && oldMouseState.RightButton == ButtonState.Released &&
mouseState.RightButton == ButtonState.Pressed; mouseState.RightButton == ButtonState.Pressed;
} }
public static bool RightButtonReleased() public static bool SecondaryMouseButtonReleased()
{ {
return AllowInput && mouseState.RightButton == ButtonState.Released; return AllowInput && mouseState.RightButton == ButtonState.Released;
} }
public static bool RightButtonClicked() public static bool SecondaryMouseButtonClicked()
{ {
return (AllowInput && return (AllowInput &&
oldMouseState.RightButton == ButtonState.Pressed oldMouseState.RightButton == ButtonState.Pressed
@@ -7,17 +7,13 @@ namespace Barotrauma
{ {
class CampaignEndScreen : Screen class CampaignEndScreen : Screen
{ {
private Video video;
private readonly CreditsPlayer creditsPlayer; private readonly CreditsPlayer creditsPlayer;
private readonly Camera cam; private readonly Camera cam;
public Action OnFinished; public Action OnFinished;
private LocalizedString textOverlay; protected SlideshowPlayer slideshowPlayer;
private float textOverlayTimer;
private Vector2 textOverlaySize;
public CampaignEndScreen() public CampaignEndScreen()
{ {
@@ -27,43 +23,45 @@ namespace Barotrauma
ScrollBarEnabled = false, ScrollBarEnabled = false,
AllowMouseWheelScroll = false AllowMouseWheelScroll = false
}; };
new GUIButton(new RectTransform(new Vector2(0.1f), creditsPlayer.RectTransform, Anchor.BottomRight, maxSize: new Point(300, 50)) { AbsoluteOffset = new Point(GUI.IntScale(20)) }, creditsPlayer.CloseButton.OnClicked = (btn, userdata) =>
TextManager.Get("close"))
{ {
OnClicked = (btn, userdata) => creditsPlayer.Scroll = 1.0f;
{ return true;
creditsPlayer.Scroll = 1.0f;
return true;
}
}; };
cam = new Camera(); cam = new Camera();
} }
public override void Select() public override void Select()
{ {
base.Select(); base.Select();
if (SlideshowPrefab.Prefabs.TryGet("campaignending".ToIdentifier(), out var slideshow))
textOverlay = ToolBox.WrapText(TextManager.Get("campaignend1"), GameMain.GraphicsWidth / 3, GUIStyle.Font); {
textOverlaySize = GUIStyle.Font.MeasureString(textOverlay); slideshowPlayer = new SlideshowPlayer(GUICanvas.Instance, slideshow);
textOverlayTimer = 0.0f; }
video = Video.Load(GameMain.GraphicsDeviceManager.GraphicsDevice, GameMain.SoundManager, "Content/SplashScreens/Ending.webm");
video.Play();
creditsPlayer.Restart(); creditsPlayer.Restart();
creditsPlayer.Visible = false; creditsPlayer.Visible = false;
SteamAchievementManager.UnlockAchievement("campaigncompleted".ToIdentifier(), unlockClients: true); UnlockAchievement("campaigncompleted");
UnlockAchievement(
GameMain.GameSession is { Campaign.Settings.RadiationEnabled: true } ?
"campaigncompleted_radiationenabled" :
"campaigncompleted_radiationdisabled");
static void UnlockAchievement(string id)
{
SteamAchievementManager.UnlockAchievement(id.ToIdentifier(), unlockClients: true);
}
} }
public override void Deselect() public override void Deselect()
{ {
video?.Dispose();
video = null;
GUI.HideCursor = false; GUI.HideCursor = false;
SoundPlayer.OverrideMusicType = Identifier.Empty; SoundPlayer.OverrideMusicType = Identifier.Empty;
} }
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
slideshowPlayer?.UpdateManually((float)deltaTime);
if (creditsPlayer.Finished) if (creditsPlayer.Finished)
{ {
OnFinished?.Invoke(); OnFinished?.Invoke();
@@ -73,46 +71,18 @@ namespace Barotrauma
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) 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); graphics.Clear(Color.Black);
if (video.IsPlaying) SoundPlayer.OverrideMusicType = "ending".ToIdentifier();
if (slideshowPlayer != null && !slideshowPlayer.Finished)
{ {
GUI.HideCursor = !GUI.PauseMenuOpen; slideshowPlayer.DrawManually(spriteBatch);
spriteBatch.Draw(video.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
} }
else else
{ {
SoundPlayer.OverrideMusicType = "ending".ToIdentifier(); GUI.HideCursor = false;
float duration = 20.0f; creditsPlayer.Visible = true;
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;
}
} }
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable);
GUI.Draw(cam, spriteBatch); GUI.Draw(cam, spriteBatch);
spriteBatch.End(); spriteBatch.End();
} }
@@ -38,7 +38,6 @@ namespace Barotrauma
protected set; protected set;
} }
public CampaignSettings CurrentSettings = new CampaignSettings(element: null);
public GUIButton CampaignCustomizeButton { get; set; } public GUIButton CampaignCustomizeButton { get; set; }
public GUIMessageBox CampaignCustomizeSettings { get; set; } public GUIMessageBox CampaignCustomizeSettings { get; set; }
@@ -124,6 +123,7 @@ namespace Barotrauma
public struct CampaignSettingElements public struct CampaignSettingElements
{ {
public SettingValue<string> SelectedPreset;
public SettingValue<bool> TutorialEnabled; public SettingValue<bool> TutorialEnabled;
public SettingValue<bool> RadiationEnabled; public SettingValue<bool> RadiationEnabled;
public SettingValue<int> MaxMissionCount; public SettingValue<int> MaxMissionCount;
@@ -135,6 +135,7 @@ namespace Barotrauma
{ {
return new CampaignSettings(element: null) return new CampaignSettings(element: null)
{ {
PresetName = SelectedPreset.GetValue(),
TutorialEnabled = TutorialEnabled.GetValue(), TutorialEnabled = TutorialEnabled.GetValue(),
RadiationEnabled = RadiationEnabled.GetValue(), RadiationEnabled = RadiationEnabled.GetValue(),
MaxMissionCount = MaxMissionCount.GetValue(), MaxMissionCount = MaxMissionCount.GetValue(),
@@ -185,9 +186,13 @@ namespace Barotrauma
{ {
const float verticalSize = 0.14f; const float verticalSize = 0.14f;
bool loadingPreset = false;
GUILayoutGroup presetDropdownLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, verticalSize), parent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft); GUILayoutGroup presetDropdownLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, verticalSize), parent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), presetDropdownLayout.RectTransform), TextManager.Get("campaignsettingpreset")); new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), presetDropdownLayout.RectTransform), TextManager.Get("campaignsettingpreset"));
GUIDropDown presetDropdown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), presetDropdownLayout.RectTransform), elementCount: CampaignModePresets.List.Length); GUIDropDown presetDropdown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), presetDropdownLayout.RectTransform), elementCount: CampaignModePresets.List.Length + 1);
presetDropdown.AddItem(TextManager.Get("karmapreset.custom"), null);
presetDropdown.Select(0);
presetDropdownLayout.RectTransform.MinSize = new Point(0, presetDropdown.Rect.Height); presetDropdownLayout.RectTransform.MinSize = new Point(0, presetDropdown.Rect.Height);
@@ -195,21 +200,30 @@ namespace Barotrauma
{ {
string name = settings.PresetName; string name = settings.PresetName;
presetDropdown.AddItem(TextManager.Get($"preset.{name}").Fallback(name), settings); presetDropdown.AddItem(TextManager.Get($"preset.{name}").Fallback(name), settings);
if (settings.PresetName.Equals(prevSettings.PresetName, StringComparison.OrdinalIgnoreCase))
{
presetDropdown.SelectItem(settings);
}
} }
var presetValue = new SettingValue<string>(
get: () => presetDropdown.SelectedData is CampaignSettings settings ? settings.PresetName : string.Empty,
set: static _ => { }); // we do not need a way to set this value
GUIListBox settingsList = new GUIListBox(new RectTransform(new Vector2(1f, 1f - verticalSize), parent.RectTransform)) GUIListBox settingsList = new GUIListBox(new RectTransform(new Vector2(1f, 1f - verticalSize), parent.RectTransform))
{ {
Spacing = GUI.IntScale(5) Spacing = GUI.IntScale(5)
}; };
SettingValue<bool> tutorialEnabled = isSinglePlayer ? SettingValue<bool> tutorialEnabled = isSinglePlayer ?
CreateTickbox(settingsList.Content, TextManager.Get("CampaignOption.EnableTutorial"), TextManager.Get("campaignoption.enabletutorial.tooltip"), prevSettings.TutorialEnabled, verticalSize) : CreateTickbox(settingsList.Content, TextManager.Get("CampaignOption.EnableTutorial"), TextManager.Get("campaignoption.enabletutorial.tooltip"), prevSettings.TutorialEnabled, verticalSize, OnValuesChanged) :
new SettingValue<bool>(() => false, b => { }); new SettingValue<bool>(static () => false, static _ => { });
SettingValue<bool> radiationEnabled = CreateTickbox(settingsList.Content, TextManager.Get("CampaignOption.EnableRadiation"), TextManager.Get("campaignoption.enableradiation.tooltip"), prevSettings.RadiationEnabled, verticalSize); SettingValue<bool> radiationEnabled = CreateTickbox(settingsList.Content, TextManager.Get("CampaignOption.EnableRadiation"), TextManager.Get("campaignoption.enableradiation.tooltip"), prevSettings.RadiationEnabled, verticalSize, OnValuesChanged);
ImmutableArray<SettingCarouselElement<Identifier>> startingSetOptions = StartItemSet.Sets.OrderBy(s => s.Order).Select(set => new SettingCarouselElement<Identifier>(set.Identifier, $"startitemset.{set.Identifier}")).ToImmutableArray(); ImmutableArray<SettingCarouselElement<Identifier>> startingSetOptions = StartItemSet.Sets.OrderBy(s => s.Order).Select(set => new SettingCarouselElement<Identifier>(set.Identifier, $"startitemset.{set.Identifier}")).ToImmutableArray();
SettingCarouselElement<Identifier> prevStartingSet = startingSetOptions.FirstOrNull(element => element.Value == prevSettings.StartItemSet) ?? startingSetOptions[1]; SettingCarouselElement<Identifier> prevStartingSet = startingSetOptions.FirstOrNull(element => element.Value == prevSettings.StartItemSet) ?? startingSetOptions[1];
SettingValue<Identifier> startingSetInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("startitemset"), TextManager.Get("startitemsettooltip"), prevStartingSet, verticalSize, startingSetOptions); SettingValue<Identifier> startingSetInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("startitemset"), TextManager.Get("startitemsettooltip"), prevStartingSet, verticalSize, startingSetOptions, OnValuesChanged);
ImmutableArray<SettingCarouselElement<StartingBalanceAmount>> fundOptions = ImmutableArray.Create( ImmutableArray<SettingCarouselElement<StartingBalanceAmount>> fundOptions = ImmutableArray.Create(
new SettingCarouselElement<StartingBalanceAmount>(StartingBalanceAmount.Low, "startingfunds.low"), new SettingCarouselElement<StartingBalanceAmount>(StartingBalanceAmount.Low, "startingfunds.low"),
@@ -218,7 +232,7 @@ namespace Barotrauma
); );
SettingCarouselElement<StartingBalanceAmount> prevStartingFund = fundOptions.FirstOrNull(element => element.Value == prevSettings.StartingBalanceAmount) ?? fundOptions[1]; SettingCarouselElement<StartingBalanceAmount> prevStartingFund = fundOptions.FirstOrNull(element => element.Value == prevSettings.StartingBalanceAmount) ?? fundOptions[1];
SettingValue<StartingBalanceAmount> startingFundsInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("startingfundsdescription"), TextManager.Get("startingfundstooltip"), prevStartingFund, verticalSize, fundOptions); SettingValue<StartingBalanceAmount> startingFundsInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("startingfundsdescription"), TextManager.Get("startingfundstooltip"), prevStartingFund, verticalSize, fundOptions, OnValuesChanged);
ImmutableArray<SettingCarouselElement<GameDifficulty>> difficultyOptions = ImmutableArray.Create( ImmutableArray<SettingCarouselElement<GameDifficulty>> difficultyOptions = ImmutableArray.Create(
new SettingCarouselElement<GameDifficulty>(GameDifficulty.Easy, "difficulty.easy"), new SettingCarouselElement<GameDifficulty>(GameDifficulty.Easy, "difficulty.easy"),
@@ -228,30 +242,38 @@ namespace Barotrauma
); );
SettingCarouselElement<GameDifficulty> prevDifficulty = difficultyOptions.FirstOrNull(element => element.Value == prevSettings.Difficulty) ?? difficultyOptions[1]; SettingCarouselElement<GameDifficulty> prevDifficulty = difficultyOptions.FirstOrNull(element => element.Value == prevSettings.Difficulty) ?? difficultyOptions[1];
SettingValue<GameDifficulty> difficultyInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("leveldifficulty"), TextManager.Get("leveldifficultyexplanation"), prevDifficulty, verticalSize, difficultyOptions); SettingValue<GameDifficulty> difficultyInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("leveldifficulty"), TextManager.Get("leveldifficultyexplanation"), prevDifficulty, verticalSize, difficultyOptions, OnValuesChanged);
SettingValue<int> maxMissionCountInput = CreateGUINumberInputCarousel(settingsList.Content, TextManager.Get("maxmissioncount"), TextManager.Get("maxmissioncounttooltip"), SettingValue<int> maxMissionCountInput = CreateGUINumberInputCarousel(settingsList.Content, TextManager.Get("maxmissioncount"), TextManager.Get("maxmissioncounttooltip"),
prevSettings.MaxMissionCount, prevSettings.MaxMissionCount,
valueStep: 1, minValue: CampaignSettings.MinMissionCountLimit, maxValue: CampaignSettings.MaxMissionCountLimit, valueStep: 1, minValue: CampaignSettings.MinMissionCountLimit, maxValue: CampaignSettings.MaxMissionCountLimit,
verticalSize); verticalSize,
OnValuesChanged);
presetDropdown.OnSelected = (selected, o) => presetDropdown.OnSelected = (_, o) =>
{ {
if (o is CampaignSettings settings) if (o is not CampaignSettings settings) { return false; }
{
tutorialEnabled.SetValue(isSinglePlayer && settings.TutorialEnabled); loadingPreset = true;
radiationEnabled.SetValue(settings.RadiationEnabled); tutorialEnabled.SetValue(isSinglePlayer && settings.TutorialEnabled);
maxMissionCountInput.SetValue(settings.MaxMissionCount); radiationEnabled.SetValue(settings.RadiationEnabled);
startingFundsInput.SetValue(settings.StartingBalanceAmount); maxMissionCountInput.SetValue(settings.MaxMissionCount);
difficultyInput.SetValue(settings.Difficulty); startingFundsInput.SetValue(settings.StartingBalanceAmount);
startingSetInput.SetValue(settings.StartItemSet); difficultyInput.SetValue(settings.Difficulty);
return true; startingSetInput.SetValue(settings.StartItemSet);
} loadingPreset = false;
return false; return true;
}; };
void OnValuesChanged()
{
if (loadingPreset) { return; }
presetDropdown.Select(0);
}
return new CampaignSettingElements return new CampaignSettingElements
{ {
SelectedPreset = presetValue,
TutorialEnabled = tutorialEnabled, TutorialEnabled = tutorialEnabled,
RadiationEnabled = radiationEnabled, RadiationEnabled = radiationEnabled,
MaxMissionCount = maxMissionCountInput, MaxMissionCount = maxMissionCountInput,
@@ -261,7 +283,7 @@ namespace Barotrauma
}; };
// Create a number input with plus and minus buttons because for some reason the default GUINumberInput buttons don't work when in a GUIMessageBox // Create a number input with plus and minus buttons because for some reason the default GUINumberInput buttons don't work when in a GUIMessageBox
static SettingValue<int> CreateGUINumberInputCarousel(GUIComponent parent, LocalizedString description, LocalizedString tooltip, int defaultValue, int valueStep, int minValue, int maxValue, float verticalSize) static SettingValue<int> CreateGUINumberInputCarousel(GUIComponent parent, LocalizedString description, LocalizedString tooltip, int defaultValue, int valueStep, int minValue, int maxValue, float verticalSize, Action onChanged)
{ {
GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, horizontalSize: 0.55f, verticalSize: verticalSize); GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, horizontalSize: 0.55f, verticalSize: verticalSize);
@@ -286,9 +308,11 @@ namespace Barotrauma
minusButton.OnClicked = plusButton.OnClicked = ChangeValue; minusButton.OnClicked = plusButton.OnClicked = ChangeValue;
numberInput.OnValueChanged += _ => onChanged();
bool ChangeValue(GUIButton btn, object userData) bool ChangeValue(GUIButton btn, object userData)
{ {
if (!(userData is int change)) { return false; } if (userData is not int change) { return false; }
numberInput.IntValue += change; numberInput.IntValue += change;
return true; return true;
@@ -298,7 +322,7 @@ namespace Barotrauma
} }
static SettingValue<T> CreateSelectionCarousel<T>(GUIComponent parent, LocalizedString description, LocalizedString tooltip, SettingCarouselElement<T> defaultValue, float verticalSize, static SettingValue<T> CreateSelectionCarousel<T>(GUIComponent parent, LocalizedString description, LocalizedString tooltip, SettingCarouselElement<T> defaultValue, float verticalSize,
ImmutableArray<SettingCarouselElement<T>> options) ImmutableArray<SettingCarouselElement<T>> options, Action onChanged)
{ {
GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, horizontalSize: 0.55f, verticalSize: verticalSize); GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, horizontalSize: 0.55f, verticalSize: verticalSize);
@@ -349,6 +373,8 @@ namespace Barotrauma
return true; return true;
} }
numberInput.OnValueChanged += _ => onChanged();
void SetValue(int value) void SetValue(int value)
{ {
numberInput.IntValue = value; numberInput.IntValue = value;
@@ -358,7 +384,7 @@ namespace Barotrauma
return new SettingValue<T>(() => options[numberInput.IntValue].Value, t => SetValue(options.IndexOf(e => Equals(e.Value, t)))); return new SettingValue<T>(() => options[numberInput.IntValue].Value, t => SetValue(options.IndexOf(e => Equals(e.Value, t))));
} }
static SettingValue<bool> CreateTickbox(GUIComponent parent, LocalizedString description, LocalizedString tooltip, bool defaultValue, float verticalSize) static SettingValue<bool> CreateTickbox(GUIComponent parent, LocalizedString description, LocalizedString tooltip, bool defaultValue, float verticalSize, Action onChanged)
{ {
GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, 0.7f, verticalSize); GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, 0.7f, verticalSize);
GUILayoutGroup tickboxContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1.0f), inputContainer.RectTransform), childAnchor: Anchor.Center); GUILayoutGroup tickboxContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1.0f), inputContainer.RectTransform), childAnchor: Anchor.Center);
@@ -370,6 +396,13 @@ namespace Barotrauma
tickBox.Box.IgnoreLayoutGroups = true; tickBox.Box.IgnoreLayoutGroups = true;
tickBox.Box.RectTransform.SetPosition(Anchor.CenterRight); tickBox.Box.RectTransform.SetPosition(Anchor.CenterRight);
inputContainer.RectTransform.Parent.MinSize = new Point(0, tickBox.RectTransform.MinSize.Y); inputContainer.RectTransform.Parent.MinSize = new Point(0, tickBox.RectTransform.MinSize.Y);
tickBox.OnSelected += _ =>
{
onChanged();
return true;
};
return new SettingValue<bool>(() => tickBox.Selected, b => tickBox.Selected = b); return new SettingValue<bool>(() => tickBox.Selected, b => tickBox.Selected = b);
} }
@@ -194,7 +194,7 @@ namespace Barotrauma
{ {
TextGetter = () => TextGetter = () =>
{ {
int initialMoney = CurrentSettings.InitialMoney; int initialMoney = CampaignSettings.CurrentSettings.InitialMoney;
if (subList.SelectedData is SubmarineInfo subInfo) if (subList.SelectedData is SubmarineInfo subInfo)
{ {
initialMoney -= subInfo.Price; initialMoney -= subInfo.Price;
@@ -208,15 +208,15 @@ namespace Barotrauma
{ {
OnClicked = (tb, userdata) => OnClicked = (tb, userdata) =>
{ {
CreateCustomizeWindow(CurrentSettings, settings => CreateCustomizeWindow(CampaignSettings.CurrentSettings, settings =>
{ {
CampaignSettings prevSettings = CurrentSettings; CampaignSettings prevSettings = CampaignSettings.CurrentSettings;
CurrentSettings = settings; CampaignSettings.CurrentSettings = settings;
if (prevSettings.InitialMoney != settings.InitialMoney) if (prevSettings.InitialMoney != settings.InitialMoney)
{ {
object selectedData = subList.SelectedData; object selectedData = subList.SelectedData;
UpdateSubList(SubmarineInfo.SavedSubmarines); UpdateSubList(SubmarineInfo.SavedSubmarines);
if (selectedData is SubmarineInfo selectedSub && selectedSub.Price <= CurrentSettings.InitialMoney) if (selectedData is SubmarineInfo selectedSub && selectedSub.Price <= CampaignSettings.CurrentSettings.InitialMoney)
{ {
subList.Select(selectedData); subList.Select(selectedData);
} }
@@ -375,6 +375,7 @@ namespace Barotrauma
{ {
onClosed?.Invoke(elements.CreateSettings()); onClosed?.Invoke(elements.CreateSettings());
GameSettings.SaveCurrentConfig();
return CampaignCustomizeSettings.Close(button, o); return CampaignCustomizeSettings.Close(button, o);
}; };
} }
@@ -399,7 +400,7 @@ namespace Barotrauma
SubmarineInfo selectedSub = null; SubmarineInfo selectedSub = null;
if (!(subList.SelectedData is SubmarineInfo)) { return false; } if (subList.SelectedData is not SubmarineInfo) { return false; }
selectedSub = subList.SelectedData as SubmarineInfo; selectedSub = subList.SelectedData as SubmarineInfo;
if (selectedSub.SubmarineClass == SubmarineClass.Undefined) if (selectedSub.SubmarineClass == SubmarineClass.Undefined)
@@ -419,7 +420,7 @@ namespace Barotrauma
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Singleplayer, saveNameBox.Text); string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Singleplayer, saveNameBox.Text);
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled; bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
CampaignSettings settings = CurrentSettings; CampaignSettings settings = CampaignSettings.CurrentSettings;
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages) if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
{ {
@@ -476,7 +477,7 @@ namespace Barotrauma
{ {
foreach (GUIComponent child in subList.Content.Children) foreach (GUIComponent child in subList.Content.Children)
{ {
if (!(child.UserData is SubmarineInfo sub)) { return; } if (child.UserData is not SubmarineInfo sub) { return; }
child.Visible = string.IsNullOrEmpty(filter) || sub.DisplayName.Contains(filter.ToLower(), StringComparison.OrdinalIgnoreCase); child.Visible = string.IsNullOrEmpty(filter) || sub.DisplayName.Contains(filter.ToLower(), StringComparison.OrdinalIgnoreCase);
} }
} }
@@ -487,9 +488,9 @@ namespace Barotrauma
(subPreviewContainer.Parent as GUILayoutGroup)?.Recalculate(); (subPreviewContainer.Parent as GUILayoutGroup)?.Recalculate();
subPreviewContainer.ClearChildren(); subPreviewContainer.ClearChildren();
if (!(obj is SubmarineInfo sub)) { return true; } if (obj is not SubmarineInfo sub) { return true; }
#if !DEBUG #if !DEBUG
if (sub.Price > CurrentSettings.InitialMoney && !GameMain.DebugDraw) if (sub.Price > CampaignSettings.CurrentSettings.InitialMoney && !GameMain.DebugDraw)
{ {
SetPage(0); SetPage(0);
nextButton.Enabled = false; nextButton.Enabled = false;
@@ -551,7 +552,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), infoContainer.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), infoContainer.RectTransform),
TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", sub.Price)), textAlignment: Alignment.BottomRight, font: GUIStyle.SmallFont) TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", sub.Price)), textAlignment: Alignment.BottomRight, font: GUIStyle.SmallFont)
{ {
TextColor = sub.Price > CurrentSettings.InitialMoney ? GUIStyle.Red : textBlock.TextColor * 0.8f, TextColor = sub.Price > CampaignSettings.CurrentSettings.InitialMoney ? GUIStyle.Red : textBlock.TextColor * 0.8f,
ToolTip = textBlock.ToolTip ToolTip = textBlock.ToolTip
}; };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), infoContainer.RectTransform), new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), infoContainer.RectTransform),
@@ -563,7 +564,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (!GameMain.DebugDraw) if (!GameMain.DebugDraw)
{ {
if (sub.Price > CurrentSettings.InitialMoney || !sub.IsCampaignCompatible) if (sub.Price > CampaignSettings.CurrentSettings.InitialMoney || !sub.IsCampaignCompatible)
{ {
textBlock.CanBeFocused = false; textBlock.CanBeFocused = false;
textBlock.TextColor *= 0.5f; textBlock.TextColor *= 0.5f;
@@ -573,7 +574,7 @@ namespace Barotrauma
} }
if (SubmarineInfo.SavedSubmarines.Any()) if (SubmarineInfo.SavedSubmarines.Any())
{ {
var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CurrentSettings.InitialMoney).ToList(); var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CampaignSettings.CurrentSettings.InitialMoney).ToList();
if (validSubs.Count > 0) if (validSubs.Count > 0)
{ {
subList.Select(validSubs[Rand.Int(validSubs.Count)]); subList.Select(validSubs[Rand.Int(validSubs.Count)]);
@@ -81,11 +81,21 @@ namespace Barotrauma
tabs[(int)CampaignMode.InteractionType.Map] = CreateDefaultTabContainer(container, new Vector2(0.9f)); tabs[(int)CampaignMode.InteractionType.Map] = CreateDefaultTabContainer(container, new Vector2(0.9f));
var mapFrame = new GUIFrame(new RectTransform(Vector2.One, GetTabContainer(CampaignMode.InteractionType.Map).RectTransform, Anchor.TopLeft), color: Color.Black * 0.9f); var mapFrame = new GUIFrame(new RectTransform(Vector2.One, GetTabContainer(CampaignMode.InteractionType.Map).RectTransform, Anchor.TopLeft), color: Color.Black * 0.9f);
new GUICustomComponent(new RectTransform(Vector2.One, mapFrame.RectTransform), DrawMap, UpdateMap); var mapContainer = new GUICustomComponent(new RectTransform(Vector2.One, mapFrame.RectTransform), DrawMap, UpdateMap);
var notificationFrame = new GUIFrame(new RectTransform(new Point(mapContainer.Rect.Width, GUI.IntScale(40)), mapContainer.RectTransform, Anchor.BottomCenter), style: "ChatBox");
new GUIFrame(new RectTransform(Vector2.One, mapFrame.RectTransform), style: "InnerGlow", color: Color.Black * 0.9f) new GUIFrame(new RectTransform(Vector2.One, mapFrame.RectTransform), style: "InnerGlow", color: Color.Black * 0.9f)
{ {
CanBeFocused = false CanBeFocused = false
};
var notificationContainer = new GUICustomComponent(new RectTransform(new Vector2(0.98f, 1.0f), notificationFrame.RectTransform, Anchor.Center), DrawMapNotifications, null)
{
HideElementsOutsideFrame = true
}; };
var notificationHeader = new GUIImage(new RectTransform(new Vector2(0.1f, 1.0f), notificationFrame.RectTransform, Anchor.CenterLeft), style: "GUISlopedHeaderRight");
var text = new GUITextBlock(new RectTransform(Vector2.One, notificationHeader.RectTransform, Anchor.Center), TextManager.Get("breakingnews"), font: GUIStyle.LargeFont);
notificationHeader.RectTransform.MinSize = new Point((int)(text.TextSize.X * 1.3f), 0);
// crew tab ------------------------------------------------------------------------- // crew tab -------------------------------------------------------------------------
@@ -152,18 +162,23 @@ namespace Barotrauma
CreateUI(tabs[(int)CampaignMode.InteractionType.Map].Parent); CreateUI(tabs[(int)CampaignMode.InteractionType.Map].Parent);
} }
GameMain.GameSession?.Map?.Draw(spriteBatch, mapContainer); Campaign?.Map?.Draw(Campaign, spriteBatch, mapContainer);
}
private void DrawMapNotifications(SpriteBatch spriteBatch, GUICustomComponent notificationContainer)
{
Campaign?.Map?.DrawNotifications(spriteBatch, notificationContainer);
} }
private void UpdateMap(float deltaTime, GUICustomComponent mapContainer) private void UpdateMap(float deltaTime, GUICustomComponent mapContainer)
{ {
var map = GameMain.GameSession?.Map; var map = Campaign?.Map;
if (map == null) { return; } if (map == null) { return; }
if (selectedLocation != null && selectedLocation == GameMain.GameSession.Campaign.GetCurrentDisplayLocation()) if (selectedLocation != null && selectedLocation == Campaign.GetCurrentDisplayLocation())
{ {
map.SelectLocation(-1); map.SelectLocation(-1);
} }
map.Update(deltaTime, mapContainer); map.Update(Campaign, deltaTime, mapContainer);
foreach (GUITickBox tickBox in missionTickBoxes) foreach (GUITickBox tickBox in missionTickBoxes)
{ {
bool disable = hasMaxMissions && !tickBox.Selected; bool disable = hasMaxMissions && !tickBox.Selected;
@@ -260,14 +275,20 @@ namespace Barotrauma
if (connection?.LevelData != null) if (connection?.LevelData != null)
{ {
if (location.Faction?.Prefab != null)
{
var factionLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.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, textColor: location.Faction.Prefab.IconColor);
}
var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform), var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
TextManager.Get("Biome", "location"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft); TextManager.Get("Biome", "location"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), connection.Biome.DisplayName, textAlignment: Alignment.CenterRight); new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), connection.Biome.DisplayName, textAlignment: Alignment.CenterRight);
var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform), var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
TextManager.Get("LevelDifficulty"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft); TextManager.Get("LevelDifficulty"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), ((int)connection.LevelData.Difficulty) + " %", textAlignment: Alignment.CenterRight); new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), TextManager.GetWithVariable("percentageformat", "[value]", ((int)connection.LevelData.Difficulty).ToString()), textAlignment: Alignment.CenterRight);
if (connection.LevelData.HasBeaconStation) if (connection.LevelData.HasBeaconStation)
{ {
var beaconStationContent = new GUILayoutGroup(new RectTransform(biomeLabel.RectTransform.NonScaledSize, textContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft); var beaconStationContent = new GUILayoutGroup(new RectTransform(biomeLabel.RectTransform.NonScaledSize, textContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
@@ -328,12 +349,31 @@ namespace Barotrauma
if (connection != null && connection.Locations.Contains(currentDisplayLocation)) if (connection != null && connection.Locations.Contains(currentDisplayLocation))
{ {
List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList(); List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList();
if (!availableMissions.Contains(null)) { availableMissions.Insert(0, null); }
if (!availableMissions.Any()) { availableMissions.Insert(0, null); }
availableMissions.AddRange(location.AvailableMissions.Where(m => m.Locations[0] == m.Locations[1]));
missionList.Content.ClearChildren(); missionList.Content.ClearChildren();
bool isPrevMissionInNextLocation = false;
foreach (Mission mission in availableMissions) foreach (Mission mission in availableMissions)
{ {
bool isMissionInNextLocation = mission != null && location.AvailableMissions.Contains(mission);
if (isMissionInNextLocation && !isPrevMissionInNextLocation)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionList.Content.RectTransform), TextManager.Get("outpostmissions"),
textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont, wrap: true)
{
CanBeFocused = false
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionList.Content.RectTransform), style: "HorizontalLine")
{
CanBeFocused = false
};
}
isPrevMissionInNextLocation = isMissionInNextLocation;
var missionPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), missionList.Content.RectTransform), style: null) var missionPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), missionList.Content.RectTransform), style: null)
{ {
UserData = mission UserData = mission
@@ -347,45 +387,54 @@ namespace Barotrauma
var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUIStyle.SubHeadingFont, wrap: true); var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUIStyle.SubHeadingFont, wrap: true);
missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(15)); missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(15));
if (mission != null) if (mission == null)
{ {
var tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty) missionTextContent.RectTransform.MinSize = missionName.RectTransform.MinSize = new Point(0, GUI.IntScale(35));
missionTextContent.ChildAnchor = Anchor.CenterLeft;
}
else
{
GUITickBox tickBox = null;
if (!isMissionInNextLocation)
{ {
UserData = mission, tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty)
Selected = Campaign.Map.CurrentLocation?.SelectedMissions.Contains(mission) ?? false
};
tickBox.RectTransform.MinSize = new Point(tickBox.Rect.Height, 0);
tickBox.RectTransform.IsFixedSize = true;
tickBox.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap);
tickBox.OnSelected += (GUITickBox tb) =>
{
if (!CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap)) { return false; }
if (tb.Selected)
{ {
Campaign.Map.CurrentLocation.SelectMission(mission); UserData = mission,
} Selected = Campaign.Map.CurrentLocation?.SelectedMissions.Contains(mission) ?? false
else };
tickBox.RectTransform.MinSize = new Point(tickBox.Rect.Height, 0);
tickBox.RectTransform.IsFixedSize = true;
tickBox.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap);
tickBox.OnSelected += (GUITickBox tb) =>
{ {
Campaign.Map.CurrentLocation.DeselectMission(mission); if (!CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap)) { return false; }
}
foreach (GUITextBlock rewardText in missionRewardTexts) if (tb.Selected)
{ {
Mission otherMission = rewardText.UserData as Mission; Campaign.Map.CurrentLocation.SelectMission(mission);
rewardText.Text = otherMission.GetMissionRewardText(Submarine.MainSub); }
} else
{
Campaign.Map.CurrentLocation.DeselectMission(mission);
}
UpdateMaxMissions(connection.OtherLocation(currentDisplayLocation)); foreach (GUITextBlock rewardText in missionRewardTexts)
{
Mission otherMission = rewardText.UserData as Mission;
rewardText.Text = otherMission.GetMissionRewardText(Submarine.MainSub);
}
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending && UpdateMaxMissions(connection.OtherLocation(currentDisplayLocation));
CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap))
{ if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
GameMain.Client?.SendCampaignState(); CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap))
} {
return true; GameMain.Client?.SendCampaignState();
}; }
missionTickBoxes.Add(tickBox); return true;
};
missionTickBoxes.Add(tickBox);
}
GUILayoutGroup difficultyIndicatorGroup = null; GUILayoutGroup difficultyIndicatorGroup = null;
if (mission.Difficulty.HasValue) if (mission.Difficulty.HasValue)
@@ -410,7 +459,7 @@ namespace Barotrauma
float extraPadding = 0;// 0.8f * tickBox.Rect.Width; float extraPadding = 0;// 0.8f * tickBox.Rect.Width;
float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0; float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0;
missionName.Padding = new Vector4(missionName.Padding.X + tickBox.Rect.Width * 1.2f + extraPadding, missionName.Padding = new Vector4(missionName.Padding.X + (tickBox?.Rect.Width ?? 0) * 1.2f + extraPadding,
missionName.Padding.Y, missionName.Padding.Y,
missionName.Padding.Z + extraZPadding + extraPadding, missionName.Padding.Z + extraZPadding + extraPadding,
missionName.Padding.W); missionName.Padding.W);
@@ -425,9 +474,11 @@ namespace Barotrauma
}; };
missionRewardTexts.Add(rewardText); missionRewardTexts.Add(rewardText);
LocalizedString reputationText = mission.GetReputationRewardText(mission.Locations[0]); LocalizedString reputationText = mission.GetReputationRewardText();
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(reputationText), wrap: true); if (!reputationText.IsNullOrEmpty())
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(reputationText), wrap: true);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(mission.Description), wrap: true); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(mission.Description), wrap: true);
} }
missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(0)); missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(0));
@@ -487,7 +538,7 @@ namespace Barotrauma
OnClicked = (GUIButton btn, object obj) => OnClicked = (GUIButton btn, object obj) =>
{ {
if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null && if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null &&
missionList.Content.Children.Any(c => c.UserData is Mission)) missionList.Content.Children.Any(c => c.UserData is Mission mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation)))
{ {
var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") }); var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
noMissionVerification.Buttons[0].OnClicked = (btn, userdata) => noMissionVerification.Buttons[0].OnClicked = (btn, userdata) =>
@@ -520,7 +571,7 @@ namespace Barotrauma
//locationInfoPanel?.UpdateAuto(1.0f); //locationInfoPanel?.UpdateAuto(1.0f);
} }
public void SelectTab(CampaignMode.InteractionType tab, Identifier storeIdentifier = default) public void SelectTab(CampaignMode.InteractionType tab, Character npc = null)
{ {
if (Campaign.ShowCampaignUI || (Campaign.ForceMapUI && tab == CampaignMode.InteractionType.Map)) if (Campaign.ShowCampaignUI || (Campaign.ForceMapUI && tab == CampaignMode.InteractionType.Map))
{ {
@@ -541,7 +592,7 @@ namespace Barotrauma
switch (selectedTab) switch (selectedTab)
{ {
case CampaignMode.InteractionType.Store: case CampaignMode.InteractionType.Store:
Store.SelectStore(storeIdentifier); Store.SelectStore(npc);
break; break;
case CampaignMode.InteractionType.Crew: case CampaignMode.InteractionType.Crew:
CrewManagement.UpdateCrew(); CrewManagement.UpdateCrew();
@@ -87,26 +87,26 @@ namespace Barotrauma.CharacterEditor
private float spriteSheetZoom = 1; private float spriteSheetZoom = 1;
private float spriteSheetMinZoom = 0.25f; private float spriteSheetMinZoom = 0.25f;
private float spriteSheetMaxZoom = 1; private float spriteSheetMaxZoom = 1;
private int spriteSheetOffsetY = 20; private const int spriteSheetOffsetY = 20;
private int spriteSheetOffsetX = 30; private const int spriteSheetOffsetX = 30;
private bool hideBodySheet; private bool hideBodySheet;
private Color backgroundColor = new Color(0.2f, 0.2f, 0.2f, 1.0f); private Color backgroundColor = new Color(0.2f, 0.2f, 0.2f, 1.0f);
private Vector2 cameraOffset; private Vector2 cameraOffset;
private List<LimbJoint> selectedJoints = new List<LimbJoint>(); private readonly List<LimbJoint> selectedJoints = new List<LimbJoint>();
private List<Limb> selectedLimbs = new List<Limb>(); private readonly List<Limb> selectedLimbs = new List<Limb>();
private HashSet<Character> editedCharacters = new HashSet<Character>(); private readonly HashSet<Character> editedCharacters = new HashSet<Character>();
private bool isEndlessRunner; private bool isEndlessRunner;
private Rectangle spriteSheetRect; private Rectangle spriteSheetRect;
private Rectangle CalculateSpritesheetRectangle() => private Rectangle CalculateSpritesheetRectangle() =>
Textures == null || Textures.None() ? Rectangle.Empty : Textures == null || Textures.None() ? Rectangle.Empty :
new Rectangle( new Rectangle(
spriteSheetOffsetX, spriteSheetOffsetX,
spriteSheetOffsetY, spriteSheetOffsetY,
(int)(Textures.OrderByDescending(t => t.Width).First().Width * spriteSheetZoom), (int)(Textures.OrderByDescending(t => t.Width).First().Width * spriteSheetZoom),
(int)(Textures.Sum(t => t.Height) * spriteSheetZoom)); (int)(Textures.Sum(t => t.Height) * spriteSheetZoom));
private const string screenTextTag = "CharacterEditor."; private const string screenTextTag = "CharacterEditor.";
@@ -143,7 +143,7 @@ namespace Barotrauma.CharacterEditor
var humanSpeciesName = CharacterPrefab.HumanSpeciesName; var humanSpeciesName = CharacterPrefab.HumanSpeciesName;
if (humanSpeciesName.IsEmpty) if (humanSpeciesName.IsEmpty)
{ {
SpawnCharacter(AllSpecies.First()); SpawnCharacter(VisibleSpecies.First());
} }
else else
{ {
@@ -192,7 +192,7 @@ namespace Barotrauma.CharacterEditor
jointEndLimb = null; jointEndLimb = null;
anchor1Pos = null; anchor1Pos = null;
jointStartLimb = null; jointStartLimb = null;
allSpecies = null; visibleSpecies = null;
onlyShowSourceRectForSelectedLimbs = false; onlyShowSourceRectForSelectedLimbs = false;
unrestrictSpritesheet = false; unrestrictSpritesheet = false;
editedCharacters.Clear(); editedCharacters.Clear();
@@ -214,15 +214,12 @@ namespace Barotrauma.CharacterEditor
private void Reset(IEnumerable<Character> characters = null) private void Reset(IEnumerable<Character> characters = null)
{ {
if (characters == null) characters ??= editedCharacters;
{
characters = editedCharacters;
}
characters.ForEach(c => ResetParams(c)); characters.ForEach(c => ResetParams(c));
ResetVariables(); ResetVariables();
} }
private void ResetParams(Character character) private static void ResetParams(Character character)
{ {
character.Params.Reset(true); character.Params.Reset(true);
foreach (var animation in character.AnimController.AllAnimParams) foreach (var animation in character.AnimController.AllAnimParams)
@@ -719,7 +716,7 @@ namespace Barotrauma.CharacterEditor
cameraOffset = Vector2.Clamp(cameraOffset, min, max); cameraOffset = Vector2.Clamp(cameraOffset, min, max);
} }
Cam.Position = targetPos + cameraOffset; Cam.Position = targetPos + cameraOffset;
MapEntity.mapEntityList.ForEach(e => e.IsHighlighted = false); MapEntity.ClearHighlightedEntities();
// Update widgets // Update widgets
jointSelectionWidgets.Values.ForEach(w => w.Update((float)deltaTime)); jointSelectionWidgets.Values.ForEach(w => w.Update((float)deltaTime));
limbEditWidgets.Values.ForEach(w => w.Update((float)deltaTime)); limbEditWidgets.Values.ForEach(w => w.Update((float)deltaTime));
@@ -994,7 +991,7 @@ namespace Barotrauma.CharacterEditor
var collider = character.AnimController.Collider; var collider = character.AnimController.Collider;
var colliderDrawPos = SimToScreen(collider.SimPosition); var colliderDrawPos = SimToScreen(collider.SimPosition);
Vector2 forward = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation)); Vector2 forward = Vector2.Transform(Vector2.UnitY, Matrix.CreateRotationZ(collider.Rotation));
var endPos = SimToScreen(collider.SimPosition + forward * collider.radius); var endPos = SimToScreen(collider.SimPosition + forward * collider.Radius);
GUI.DrawLine(spriteBatch, colliderDrawPos, endPos, GUIStyle.Green); GUI.DrawLine(spriteBatch, colliderDrawPos, endPos, GUIStyle.Green);
GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + forward * 0.25f), Color.Blue); GUI.DrawLine(spriteBatch, colliderDrawPos, SimToScreen(collider.SimPosition + forward * 0.25f), Color.Blue);
Vector2 left = forward.Left(); Vector2 left = forward.Left();
@@ -1363,7 +1360,7 @@ namespace Barotrauma.CharacterEditor
private class WallGroup private class WallGroup
{ {
public readonly List<Structure> walls; public readonly List<Structure> walls;
public WallGroup(List<Structure> walls) public WallGroup(List<Structure> walls)
{ {
this.walls = walls; this.walls = walls;
@@ -1374,7 +1371,7 @@ namespace Barotrauma.CharacterEditor
var clones = new List<Structure>(); var clones = new List<Structure>();
walls.ForEachMod(w => clones.Add(w.Clone() as Structure)); walls.ForEachMod(w => clones.Add(w.Clone() as Structure));
return new WallGroup(clones); return new WallGroup(clones);
} }
} }
private void CloneWalls() private void CloneWalls()
@@ -1391,7 +1388,7 @@ namespace Barotrauma.CharacterEditor
else if (i == 2) else if (i == 2)
{ {
clones[i].walls[j].Move(new Vector2(-originalWall.walls[j].Rect.Width, 0)); clones[i].walls[j].Move(new Vector2(-originalWall.walls[j].Rect.Width, 0));
} }
} }
} }
} }
@@ -1404,8 +1401,8 @@ namespace Barotrauma.CharacterEditor
private WallGroup SelectLastClone(bool right) private WallGroup SelectLastClone(bool right)
{ {
var lastWall = right var lastWall = right
? clones.SelectMany(c => c.walls).OrderBy(w => w.Rect.Right).Last() ? clones.SelectMany(c => c.walls).OrderBy(w => w.Rect.Right).Last()
: clones.SelectMany(c => c.walls).OrderBy(w => w.Rect.Left).First(); : clones.SelectMany(c => c.walls).OrderBy(w => w.Rect.Left).First();
return clones.Where(c => c.walls.Contains(lastWall)).FirstOrDefault(); return clones.Where(c => c.walls.Contains(lastWall)).FirstOrDefault();
} }
@@ -1440,33 +1437,35 @@ namespace Barotrauma.CharacterEditor
private Identifier currentCharacterIdentifier; private Identifier currentCharacterIdentifier;
private Identifier selectedJob = Identifier.Empty; private Identifier selectedJob = Identifier.Empty;
private List<Identifier> allSpecies; private List<Identifier> visibleSpecies;
private List<Identifier> AllSpecies private List<Identifier> VisibleSpecies
{ {
get get
{ {
if (allSpecies == null) visibleSpecies ??= CharacterPrefab.Prefabs.Where(ShowCreature).OrderBy(p => p.Identifier).Select(p => p.Identifier).ToList();
{ return visibleSpecies;
#if DEBUG
allSpecies = CharacterPrefab.Prefabs.Keys.OrderBy(p => p).ToList();
#else
allSpecies = CharacterPrefab.Prefabs.Keys.Where(p => !p.Contains("variant")).OrderBy(p => p).ToList();
#endif
allSpecies.ForEach(f => DebugConsole.NewMessage(f.Value, Color.White));
}
return allSpecies;
} }
} }
private List<CharacterFile> vanillaCharacters; private bool ShowCreature(CharacterPrefab prefab)
private List<CharacterFile> VanillaCharacters {
Identifier speciesName = prefab.Identifier;
if (speciesName == CharacterPrefab.HumanSpeciesName) { return true; }
if (!VanillaCharacters.Contains(prefab.ContentFile))
{
// Always show all custom characters.
return true;
}
if (CreatureMetrics.UnlockAll) { return true; }
return CreatureMetrics.Unlocked.Contains(speciesName);
}
private IEnumerable<CharacterFile> vanillaCharacters;
private IEnumerable<CharacterFile> VanillaCharacters
{ {
get get
{ {
if (vanillaCharacters == null) vanillaCharacters ??= GameMain.VanillaContent.GetFiles<CharacterFile>();
{
vanillaCharacters = GameMain.VanillaContent.GetFiles<CharacterFile>().ToList();
}
return vanillaCharacters; return vanillaCharacters;
} }
} }
@@ -1475,7 +1474,7 @@ namespace Barotrauma.CharacterEditor
{ {
GetCurrentCharacterIndex(); GetCurrentCharacterIndex();
IncreaseIndex(); IncreaseIndex();
currentCharacterIdentifier = AllSpecies[characterIndex]; currentCharacterIdentifier = VisibleSpecies[characterIndex];
return currentCharacterIdentifier; return currentCharacterIdentifier;
} }
@@ -1483,19 +1482,19 @@ namespace Barotrauma.CharacterEditor
{ {
GetCurrentCharacterIndex(); GetCurrentCharacterIndex();
ReduceIndex(); ReduceIndex();
currentCharacterIdentifier = AllSpecies[characterIndex]; currentCharacterIdentifier = VisibleSpecies[characterIndex];
return currentCharacterIdentifier; return currentCharacterIdentifier;
} }
private void GetCurrentCharacterIndex() private void GetCurrentCharacterIndex()
{ {
characterIndex = AllSpecies.IndexOf(character.SpeciesName); characterIndex = VisibleSpecies.IndexOf(character.SpeciesName);
} }
private void IncreaseIndex() private void IncreaseIndex()
{ {
characterIndex++; characterIndex++;
if (characterIndex > AllSpecies.Count - 1) if (characterIndex > VisibleSpecies.Count - 1)
{ {
characterIndex = 0; characterIndex = 0;
} }
@@ -1506,7 +1505,7 @@ namespace Barotrauma.CharacterEditor
characterIndex--; characterIndex--;
if (characterIndex < 0) if (characterIndex < 0)
{ {
characterIndex = AllSpecies.Count - 1; characterIndex = VisibleSpecies.Count - 1;
} }
} }
@@ -1687,7 +1686,7 @@ namespace Barotrauma.CharacterEditor
XElement overrideElement = null; XElement overrideElement = null;
if (duplicate != null) if (duplicate != null)
{ {
allSpecies = null; visibleSpecies = null;
if (!File.Exists(configFilePath)) if (!File.Exists(configFilePath))
{ {
// If the file exists, we just want to overwrite it. // If the file exists, we just want to overwrite it.
@@ -1823,9 +1822,9 @@ namespace Barotrauma.CharacterEditor
AnimationParams.Create(fullPath, name, animType, type); AnimationParams.Create(fullPath, name, animType, type);
} }
} }
if (!AllSpecies.Contains(name)) if (!VisibleSpecies.Contains(name))
{ {
AllSpecies.Add(name); VisibleSpecies.Add(name);
} }
SpawnCharacter(name, ragdollParams); SpawnCharacter(name, ragdollParams);
limbPairEditing = false; limbPairEditing = false;
@@ -2678,23 +2677,33 @@ namespace Barotrauma.CharacterEditor
{ {
Stretch = true Stretch = true
}; };
// Character selection // Character selection
var characterLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), GetCharacterEditorTranslation("CharacterPanel"), font: GUIStyle.LargeFont); var characterLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), GetCharacterEditorTranslation("CharacterPanel"), font: GUIStyle.LargeFont);
var characterDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.2f), content.RectTransform) var characterDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.2f), content.RectTransform)
{ {
RelativeOffset = new Vector2(0, 0.2f) RelativeOffset = new Vector2(0, 0.2f)
}, elementCount: 8, style: null); }, elementCount: 8, style: null);
characterDropDown.ListBox.Color = new Color(characterDropDown.ListBox.Color.R, characterDropDown.ListBox.Color.G, characterDropDown.ListBox.Color.B, byte.MaxValue); characterDropDown.ListBox.Color = new Color(characterDropDown.ListBox.Color.R, characterDropDown.ListBox.Color.G, characterDropDown.ListBox.Color.B, byte.MaxValue);
foreach (var file in AllSpecies) foreach (CharacterPrefab prefab in CharacterPrefab.Prefabs.OrderByDescending(p => p.Identifier))
{ {
characterDropDown.AddItem(file.Value.CapitaliseFirstInvariant(), file); Identifier speciesName = prefab.Identifier;
if (ShowCreature(prefab))
{
characterDropDown.AddItem(speciesName.Value.CapitaliseFirstInvariant(), speciesName).SetAsFirstChild();
}
else if (!CreatureMetrics.Encountered.Contains(speciesName))
{
// Using a matching placeholder string here ("hidden").
var element = characterDropDown.AddItem(TextManager.Get("hiddensubmarines"), Identifier.Empty, textColor: Color.Gray * 0.75f);
element.SetAsLastChild();
element.Enabled = false;
}
} }
characterDropDown.SelectItem(currentCharacterIdentifier); characterDropDown.SelectItem(currentCharacterIdentifier);
characterDropDown.OnSelected = (component, data) => characterDropDown.OnSelected = (component, data) =>
{ {
Identifier characterIdentifier = (Identifier)data; Identifier characterIdentifier = (Identifier)data;
if (characterIdentifier.IsEmpty) { return true; }
try try
{ {
SpawnCharacter(characterIdentifier); SpawnCharacter(characterIdentifier);
@@ -2795,7 +2804,7 @@ namespace Barotrauma.CharacterEditor
saveAllButton.OnClicked += (button, userData) => saveAllButton.OnClicked += (button, userData) =>
{ {
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile)) if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
return false; return false;
@@ -2835,7 +2844,7 @@ namespace Barotrauma.CharacterEditor
box.Buttons[1].OnClicked += (b, d) => box.Buttons[1].OnClicked += (b, d) =>
{ {
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile)) if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
box.Close(); box.Close();
@@ -2973,7 +2982,7 @@ namespace Barotrauma.CharacterEditor
box.Buttons[1].OnClicked += (b, d) => box.Buttons[1].OnClicked += (b, d) =>
{ {
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile)) if (VanillaCharacters.Contains(CharacterPrefab.Prefabs[currentCharacterIdentifier].ContentFile))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
box.Close(); box.Close();
@@ -3212,7 +3221,7 @@ namespace Barotrauma.CharacterEditor
Wizard.Instance.CopyExisting(CharacterParams, RagdollParams, AnimParams); Wizard.Instance.CopyExisting(CharacterParams, RagdollParams, AnimParams);
} }
#region ToggleButtons #region ToggleButtons
private enum Direction private enum Direction
{ {
Left, Left,
@@ -4235,7 +4244,7 @@ namespace Barotrauma.CharacterEditor
int points = 1000; int points = 1000;
float GetAmplitude() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveAmplitude) * Cam.Zoom / amplitudeMultiplier; float GetAmplitude() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveAmplitude) * Cam.Zoom / amplitudeMultiplier;
float GetWaveLength() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveLength) * Cam.Zoom / lengthMultiplier; float GetWaveLength() => ConvertUnits.ToDisplayUnits(fishSwimParams.WaveLength) * Cam.Zoom / lengthMultiplier;
Vector2 GetRefPoint() => SimToScreen(collider.SimPosition) - GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(collider.radius) * 3 * Cam.Zoom; Vector2 GetRefPoint() => SimToScreen(collider.SimPosition) - GetScreenSpaceForward() * ConvertUnits.ToDisplayUnits(collider.Radius) * 3 * Cam.Zoom;
Vector2 GetDrawPos() => GetRefPoint() - GetScreenSpaceForward() * GetWaveLength(); Vector2 GetDrawPos() => GetRefPoint() - GetScreenSpaceForward() * GetWaveLength();
Vector2 GetDir() => GetRefPoint() - GetDrawPos(); Vector2 GetDir() => GetRefPoint() - GetDrawPos();
Vector2 GetStartPoint() => GetDrawPos() + GetDir() / 2; Vector2 GetStartPoint() => GetDrawPos() + GetDir() / 2;
@@ -5008,9 +5017,9 @@ namespace Barotrauma.CharacterEditor
// We want the collider to be slightly smaller than the source rect, because the source rect is usually a bit bigger than the graphic. // We want the collider to be slightly smaller than the source rect, because the source rect is usually a bit bigger than the graphic.
float multiplier = 0.9f; float multiplier = 0.9f;
l.body.SetSize(new Vector2(size.X, size.Y) * l.Scale * RagdollParams.TextureScale * multiplier); l.body.SetSize(new Vector2(size.X, size.Y) * l.Scale * RagdollParams.TextureScale * multiplier);
TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.radius / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale)); TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.Radius / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
TryUpdateLimbParam(l, "width", ConvertUnits.ToDisplayUnits(l.body.width / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale)); TryUpdateLimbParam(l, "width", ConvertUnits.ToDisplayUnits(l.body.Width / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
TryUpdateLimbParam(l, "height", ConvertUnits.ToDisplayUnits(l.body.height / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale)); TryUpdateLimbParam(l, "height", ConvertUnits.ToDisplayUnits(l.body.Height / l.Params.Scale / RagdollParams.LimbScale / RagdollParams.TextureScale));
} }
private void RecalculateOrigin(Limb l, Vector2? newOrigin = null) private void RecalculateOrigin(Limb l, Vector2? newOrigin = null)
@@ -1,6 +1,4 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -8,7 +6,7 @@ namespace Barotrauma
{ {
private GUIListBox listBox; private GUIListBox listBox;
private ContentXElement configElement; private readonly ContentXElement configElement;
private float scrollSpeed; private float scrollSpeed;
@@ -37,6 +35,8 @@ namespace Barotrauma
set { listBox.BarScroll = value; } set { listBox.BarScroll = value; }
} }
public readonly GUIButton CloseButton;
public CreditsPlayer(RectTransform rectT, string configFile) : base(null, rectT) public CreditsPlayer(RectTransform rectT, string configFile) : base(null, rectT)
{ {
@@ -51,6 +51,10 @@ namespace Barotrauma
configElement = doc.Root.FromPackage(ContentPackageManager.VanillaCorePackage); configElement = doc.Root.FromPackage(ContentPackageManager.VanillaCorePackage);
Load(); Load();
CloseButton = new GUIButton(new RectTransform(new Vector2(0.1f), RectTransform, Anchor.BottomRight, maxSize: new Point(GUI.IntScale(300), GUI.IntScale(50)))
{ AbsoluteOffset = new Point(GUI.IntScale(20), GUI.IntScale(20) + (Rect.Bottom - GameMain.GraphicsHeight)) },
TextManager.Get("close"));
} }
private void Load() private void Load()

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